2023-01-05 13:25:55 +01:00
/**************************************************************************/
/* editor_file_system.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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-05 00:50:27 +01:00
2014-02-10 02:10:30 +01:00
# include "editor_file_system.h"
2017-01-16 08:04:19 +01:00
2020-11-07 23:33:38 +01:00
# include "core/config/project_settings.h"
2022-12-07 12:11:28 +01:00
# include "core/extension/gdextension_manager.h"
2021-06-11 14:51:48 +02:00
# include "core/io/file_access.h"
2019-02-12 13:30:56 +01:00
# include "core/io/resource_importer.h"
2018-09-11 18:13:45 +02:00
# include "core/io/resource_loader.h"
# include "core/io/resource_saver.h"
2022-07-23 19:12:41 +02:00
# include "core/object/worker_thread_pool.h"
2018-09-11 18:13:45 +02:00
# include "core/os/os.h"
2020-11-07 23:33:38 +01:00
# include "core/variant/variant_parser.h"
2022-11-19 12:45:49 +01:00
# include "editor/editor_help.h"
2022-02-12 02:46:22 +01:00
# include "editor/editor_node.h"
2022-07-29 02:36:26 +02:00
# include "editor/editor_paths.h"
2022-02-12 02:46:22 +01:00
# include "editor/editor_resource_preview.h"
# include "editor/editor_settings.h"
2024-05-24 07:30:16 +02:00
# include "editor/project_settings_editor.h"
2022-05-12 10:20:12 +02:00
# include "scene/resources/packed_scene.h"
2014-02-10 02:10:30 +01:00
2020-04-02 01:20:12 +02:00
EditorFileSystem * EditorFileSystem : : singleton = nullptr ;
2019-02-26 22:43:37 +01:00
//the name is the version, to keep compatibility with different versions of Godot
2023-01-19 19:12:25 +01:00
# define CACHE_FILE_NAME "filesystem_cache8"
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int EditorFileSystemDirectory : : find_file_index ( const String & p_file ) const {
for ( int i = 0 ; i < files . size ( ) ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( files [ i ] - > file = = p_file ) {
2016-01-05 14:36:24 +01:00
return i ;
2020-05-14 16:41:43 +02:00
}
2016-01-05 14:36:24 +01:00
}
return - 1 ;
}
2020-05-14 14:29:06 +02:00
2017-03-05 16:44:50 +01:00
int EditorFileSystemDirectory : : find_dir_index ( const String & p_dir ) const {
for ( int i = 0 ; i < subdirs . size ( ) ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( subdirs [ i ] - > name = = p_dir ) {
2016-01-05 14:36:24 +01:00
return i ;
2020-05-14 16:41:43 +02:00
}
2016-01-05 14:36:24 +01:00
}
return - 1 ;
}
2021-02-27 21:43:26 +01:00
void EditorFileSystemDirectory : : force_update ( ) {
// We set modified_time to 0 to force `EditorFileSystem::_scan_fs_changes` to search changes in the directory
modified_time = 0 ;
}
2014-02-10 02:10:30 +01:00
int EditorFileSystemDirectory : : get_subdir_count ( ) const {
return subdirs . size ( ) ;
}
2017-03-05 16:44:50 +01:00
EditorFileSystemDirectory * EditorFileSystemDirectory : : get_subdir ( int p_idx ) {
2020-04-02 01:20:12 +02:00
ERR_FAIL_INDEX_V ( p_idx , subdirs . size ( ) , nullptr ) ;
2014-02-10 02:10:30 +01:00
return subdirs [ p_idx ] ;
}
2017-03-05 16:44:50 +01:00
int EditorFileSystemDirectory : : get_file_count ( ) const {
2014-02-10 02:10:30 +01:00
return files . size ( ) ;
}
2017-03-05 16:44:50 +01:00
String EditorFileSystemDirectory : : get_file ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , files . size ( ) , " " ) ;
2014-02-10 02:10:30 +01:00
2016-01-05 14:36:24 +01:00
return files [ p_idx ] - > file ;
2014-02-10 02:10:30 +01:00
}
String EditorFileSystemDirectory : : get_path ( ) const {
String p ;
2017-03-05 16:44:50 +01:00
const EditorFileSystemDirectory * d = this ;
while ( d - > parent ) {
2022-08-30 02:34:01 +02:00
p = d - > name . path_join ( p ) ;
2017-03-05 16:44:50 +01:00
d = d - > parent ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
return " res:// " + p ;
2014-02-10 02:10:30 +01:00
}
String EditorFileSystemDirectory : : get_file_path ( int p_idx ) const {
String file = get_file ( p_idx ) ;
2017-03-05 16:44:50 +01:00
const EditorFileSystemDirectory * d = this ;
while ( d - > parent ) {
2022-08-30 02:34:01 +02:00
file = d - > name . path_join ( file ) ;
2017-03-05 16:44:50 +01:00
d = d - > parent ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
return " res:// " + file ;
2014-02-10 02:10:30 +01:00
}
2015-08-24 01:15:56 +02:00
Vector < String > EditorFileSystemDirectory : : get_file_deps ( int p_idx ) const {
2017-03-05 16:44:50 +01:00
ERR_FAIL_INDEX_V ( p_idx , files . size ( ) , Vector < String > ( ) ) ;
2021-07-23 21:01:18 +02:00
Vector < String > deps ;
for ( int i = 0 ; i < files [ p_idx ] - > deps . size ( ) ; i + + ) {
String dep = files [ p_idx ] - > deps [ i ] ;
int sep_idx = dep . find ( " :: " ) ; //may contain type information, unwanted
if ( sep_idx ! = - 1 ) {
dep = dep . substr ( 0 , sep_idx ) ;
}
ResourceUID : : ID uid = ResourceUID : : get_singleton ( ) - > text_to_id ( dep ) ;
if ( uid ! = ResourceUID : : INVALID_ID ) {
Fix various typos
Follow-up typos found via `codespell -q 3 -S ./thirdparty,*.po,./DONORS.md -L ackward,ang,ans,ba,beng,cas,childs,childrens,dof,doubleclick,fave,findn,hist,inout,leapyear,lod,nd,numer,ois,ony,paket,seeked,sinc,switchs,te,uint`
2021-07-25 13:18:25 +02:00
//return proper dependency resource from uid
2021-07-23 21:01:18 +02:00
if ( ResourceUID : : get_singleton ( ) - > has_id ( uid ) ) {
dep = ResourceUID : : get_singleton ( ) - > get_id_path ( uid ) ;
} else {
continue ;
}
}
deps . push_back ( dep ) ;
}
return deps ;
2015-08-24 01:15:56 +02:00
}
2016-05-27 19:18:40 +02:00
2017-08-30 01:17:34 +02:00
bool EditorFileSystemDirectory : : get_file_import_is_valid ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , files . size ( ) , false ) ;
return files [ p_idx ] - > import_valid ;
}
2020-10-22 23:38:41 +02:00
uint64_t EditorFileSystemDirectory : : get_file_modified_time ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , files . size ( ) , 0 ) ;
return files [ p_idx ] - > modified_time ;
}
2023-11-16 14:53:09 +01:00
uint64_t EditorFileSystemDirectory : : get_file_import_modified_time ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , files . size ( ) , 0 ) ;
return files [ p_idx ] - > import_modified_time ;
}
2018-07-16 00:29:00 +02:00
String EditorFileSystemDirectory : : get_file_script_class_name ( int p_idx ) const {
return files [ p_idx ] - > script_class_name ;
}
String EditorFileSystemDirectory : : get_file_script_class_extends ( int p_idx ) const {
return files [ p_idx ] - > script_class_extends ;
}
2018-07-29 05:36:43 +02:00
String EditorFileSystemDirectory : : get_file_script_class_icon_path ( int p_idx ) const {
return files [ p_idx ] - > script_class_icon_path ;
}
2024-06-14 06:13:45 +02:00
String EditorFileSystemDirectory : : get_file_icon_path ( int p_idx ) const {
return files [ p_idx ] - > icon_path ;
}
2015-08-24 01:15:56 +02:00
StringName EditorFileSystemDirectory : : get_file_type ( int p_idx ) const {
2017-03-05 16:44:50 +01:00
ERR_FAIL_INDEX_V ( p_idx , files . size ( ) , " " ) ;
2016-01-05 14:36:24 +01:00
return files [ p_idx ] - > type ;
2014-02-10 02:10:30 +01:00
}
2023-01-19 19:12:25 +01:00
StringName EditorFileSystemDirectory : : get_file_resource_script_class ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , files . size ( ) , " " ) ;
return files [ p_idx ] - > resource_script_class ;
}
2014-02-10 02:10:30 +01:00
String EditorFileSystemDirectory : : get_name ( ) {
return name ;
}
EditorFileSystemDirectory * EditorFileSystemDirectory : : get_parent ( ) {
return parent ;
}
void EditorFileSystemDirectory : : _bind_methods ( ) {
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_subdir_count " ) , & EditorFileSystemDirectory : : get_subdir_count ) ;
2017-08-09 13:19:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_subdir " , " idx " ) , & EditorFileSystemDirectory : : get_subdir ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_file_count " ) , & EditorFileSystemDirectory : : get_file_count ) ;
ClassDB : : bind_method ( D_METHOD ( " get_file " , " idx " ) , & EditorFileSystemDirectory : : get_file ) ;
ClassDB : : bind_method ( D_METHOD ( " get_file_path " , " idx " ) , & EditorFileSystemDirectory : : get_file_path ) ;
ClassDB : : bind_method ( D_METHOD ( " get_file_type " , " idx " ) , & EditorFileSystemDirectory : : get_file_type ) ;
2018-07-16 00:29:00 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_file_script_class_name " , " idx " ) , & EditorFileSystemDirectory : : get_file_script_class_name ) ;
ClassDB : : bind_method ( D_METHOD ( " get_file_script_class_extends " , " idx " ) , & EditorFileSystemDirectory : : get_file_script_class_extends ) ;
2017-08-30 01:17:34 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_file_import_is_valid " , " idx " ) , & EditorFileSystemDirectory : : get_file_import_is_valid ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_name " ) , & EditorFileSystemDirectory : : get_name ) ;
ClassDB : : bind_method ( D_METHOD ( " get_path " ) , & EditorFileSystemDirectory : : get_path ) ;
2017-08-09 13:19:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_parent " ) , & EditorFileSystemDirectory : : get_parent ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " find_file_index " , " name " ) , & EditorFileSystemDirectory : : find_file_index ) ;
ClassDB : : bind_method ( D_METHOD ( " find_dir_index " , " name " ) , & EditorFileSystemDirectory : : find_dir_index ) ;
2014-02-10 02:10:30 +01:00
}
EditorFileSystemDirectory : : EditorFileSystemDirectory ( ) {
2017-03-05 16:44:50 +01:00
modified_time = 0 ;
2020-04-02 01:20:12 +02:00
parent = nullptr ;
2014-02-10 02:10:30 +01:00
}
EditorFileSystemDirectory : : ~ EditorFileSystemDirectory ( ) {
2024-05-24 07:30:16 +02:00
for ( EditorFileSystemDirectory : : FileInfo * fi : files ) {
memdelete ( fi ) ;
2016-01-05 14:36:24 +01:00
}
2024-05-24 07:30:16 +02:00
for ( EditorFileSystemDirectory * dir : subdirs ) {
memdelete ( dir ) ;
}
}
EditorFileSystem : : ScannedDirectory : : ~ ScannedDirectory ( ) {
for ( ScannedDirectory * dir : subdirs ) {
memdelete ( dir ) ;
}
}
void EditorFileSystem : : _first_scan_filesystem ( ) {
Ref < DirAccess > d = DirAccess : : create ( DirAccess : : ACCESS_RESOURCES ) ;
first_scan_root_dir = memnew ( ScannedDirectory ) ;
first_scan_root_dir - > full_path = " res:// " ;
HashSet < String > existing_class_names ;
nb_files_total = _scan_new_dir ( first_scan_root_dir , d ) ;
// This loads the global class names from the scripts and ensures that even if the
// global_script_class_cache.cfg was missing or invalid, the global class names are valid in ScriptServer.
_first_scan_process_scripts ( first_scan_root_dir , existing_class_names ) ;
// Removing invalid global class to prevent having invalid paths in ScriptServer.
_remove_invalid_global_class_names ( existing_class_names ) ;
// Now that all the global class names should be loaded, create autoloads and plugins.
// This is done after loading the global class names because autoloads and plugins can use
// global class names.
ProjectSettingsEditor : : get_singleton ( ) - > init_autoloads ( ) ;
EditorNode : : get_singleton ( ) - > init_plugins ( ) ;
}
void EditorFileSystem : : _first_scan_process_scripts ( const ScannedDirectory * p_scan_dir , HashSet < String > & p_existing_class_names ) {
for ( ScannedDirectory * scan_sub_dir : p_scan_dir - > subdirs ) {
_first_scan_process_scripts ( scan_sub_dir , p_existing_class_names ) ;
}
for ( const String & scan_file : p_scan_dir - > files ) {
String path = p_scan_dir - > full_path . path_join ( scan_file ) ;
String type = ResourceLoader : : get_resource_type ( path ) ;
if ( ClassDB : : is_parent_class ( type , SNAME ( " Script " ) ) ) {
String script_class_extends ;
String script_class_icon_path ;
String script_class_name = _get_global_script_class ( type , path , & script_class_extends , & script_class_icon_path ) ;
_register_global_class_script ( path , path , type , script_class_name , script_class_extends , script_class_icon_path ) ;
if ( ! script_class_name . is_empty ( ) ) {
p_existing_class_names . insert ( script_class_name ) ;
}
}
2014-02-10 02:10:30 +01:00
}
}
2016-01-05 14:36:24 +01:00
void EditorFileSystem : : _scan_filesystem ( ) {
2024-05-24 07:30:16 +02:00
// On the first scan, the first_scan_root_dir is created in _first_scan_filesystem.
ERR_FAIL_COND ( ! scanning | | new_filesystem | | ( first_scan & & ! first_scan_root_dir ) ) ;
2014-02-10 02:10:30 +01:00
//read .fscache
String cpath ;
sources_changed . clear ( ) ;
2016-01-05 14:36:24 +01:00
file_cache . clear ( ) ;
2014-02-10 02:10:30 +01:00
2017-07-19 22:00:46 +02:00
String project = ProjectSettings : : get_singleton ( ) - > get_resource_path ( ) ;
2015-06-06 14:44:38 +02:00
2022-08-30 02:34:01 +02:00
String fscache = EditorPaths : : get_singleton ( ) - > get_project_settings_dir ( ) . path_join ( CACHE_FILE_NAME ) ;
2022-04-12 09:12:40 +02:00
{
Ref < FileAccess > f = FileAccess : : open ( fscache , FileAccess : : READ ) ;
bool first = true ;
if ( f . is_valid ( ) ) {
//read the disk cache
while ( ! f - > eof_reached ( ) ) {
String l = f - > get_line ( ) . strip_edges ( ) ;
if ( first ) {
if ( first_scan ) {
// only use this on first scan, afterwards it gets ignored
// this is so on first reimport we synchronize versions, then
// we don't care until editor restart. This is for usability mainly so
// your workflow is not killed after changing a setting by forceful reimporting
// everything there is.
filesystem_settings_version_for_import = l . strip_edges ( ) ;
if ( filesystem_settings_version_for_import ! = ResourceFormatImporter : : get_singleton ( ) - > get_import_settings_hash ( ) ) {
revalidate_import_files = true ;
}
2019-02-26 22:43:37 +01:00
}
2022-04-12 09:12:40 +02:00
first = false ;
continue ;
}
if ( l . is_empty ( ) ) {
continue ;
2019-02-26 22:43:37 +01:00
}
2014-02-10 02:10:30 +01:00
2022-04-12 09:12:40 +02:00
if ( l . begins_with ( " :: " ) ) {
Vector < String > split = l . split ( " :: " ) ;
ERR_CONTINUE ( split . size ( ) ! = 3 ) ;
2023-11-18 23:40:56 +01:00
const String & name = split [ 1 ] ;
2014-02-10 02:10:30 +01:00
2022-04-12 09:12:40 +02:00
cpath = name ;
2014-02-10 02:10:30 +01:00
2022-04-12 09:12:40 +02:00
} else {
2023-10-10 02:50:20 +02:00
// The last section (deps) may contain the same splitter, so limit the maxsplit to 8 to get the complete deps.
Vector < String > split = l . split ( " :: " , true , 8 ) ;
2023-06-16 13:12:56 +02:00
ERR_CONTINUE ( split . size ( ) < 9 ) ;
2022-04-12 09:12:40 +02:00
String name = split [ 0 ] ;
String file ;
file = name ;
2022-08-30 02:34:01 +02:00
name = cpath . path_join ( name ) ;
2022-04-12 09:12:40 +02:00
FileCache fc ;
fc . type = split [ 1 ] ;
2024-05-06 16:20:20 +02:00
if ( fc . type . contains ( " / " ) ) {
2024-07-03 21:25:16 +02:00
fc . type = split [ 1 ] . get_slice ( " / " , 0 ) ;
fc . resource_script_class = split [ 1 ] . get_slice ( " / " , 1 ) ;
2023-01-19 19:12:25 +01:00
}
2022-04-12 09:12:40 +02:00
fc . uid = split [ 2 ] . to_int ( ) ;
fc . modification_time = split [ 3 ] . to_int ( ) ;
fc . import_modification_time = split [ 4 ] . to_int ( ) ;
fc . import_valid = split [ 5 ] . to_int ( ) ! = 0 ;
fc . import_group_file = split [ 6 ] . strip_edges ( ) ;
fc . script_class_name = split [ 7 ] . get_slice ( " <> " , 0 ) ;
fc . script_class_extends = split [ 7 ] . get_slice ( " <> " , 1 ) ;
fc . script_class_icon_path = split [ 7 ] . get_slice ( " <> " , 2 ) ;
String deps = split [ 8 ] . strip_edges ( ) ;
if ( deps . length ( ) ) {
Vector < String > dp = deps . split ( " <> " ) ;
for ( int i = 0 ; i < dp . size ( ) ; i + + ) {
2023-11-18 23:40:56 +01:00
const String & path = dp [ i ] ;
2022-04-12 09:12:40 +02:00
fc . deps . push_back ( path ) ;
}
2015-08-24 01:15:56 +02:00
}
2022-04-12 09:12:40 +02:00
file_cache [ name ] = fc ;
}
2014-02-10 02:10:30 +01:00
}
}
}
2024-05-15 16:26:33 +02:00
const String update_cache = EditorPaths : : get_singleton ( ) - > get_project_settings_dir ( ) . path_join ( " filesystem_update4 " ) ;
if ( first_scan & & FileAccess : : exists ( update_cache ) ) {
2017-08-17 22:02:43 +02:00
{
2022-03-23 10:08:58 +01:00
Ref < FileAccess > f2 = FileAccess : : open ( update_cache , FileAccess : : READ ) ;
2019-02-12 21:10:08 +01:00
String l = f2 - > get_line ( ) . strip_edges ( ) ;
2021-12-09 10:42:46 +01:00
while ( ! l . is_empty ( ) ) {
2024-05-15 16:26:33 +02:00
dep_update_list . insert ( l ) ;
file_cache . erase ( l ) ; // Erase cache for this, so it gets updated.
2019-02-12 21:10:08 +01:00
l = f2 - > get_line ( ) . strip_edges ( ) ;
2017-08-17 22:02:43 +02:00
}
}
2024-05-15 16:26:33 +02:00
Ref < DirAccess > d = DirAccess : : create ( DirAccess : : ACCESS_RESOURCES ) ;
d - > remove ( update_cache ) ; // Bye bye update cache.
2017-08-17 22:02:43 +02:00
}
2017-03-05 16:44:50 +01:00
EditorProgressBG scan_progress ( " efs " , " ScanFS " , 1000 ) ;
2016-01-05 14:36:24 +01:00
ScanProgress sp ;
2024-05-24 07:30:16 +02:00
sp . hi = nb_files_total ;
2017-03-05 16:44:50 +01:00
sp . progress = & scan_progress ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
new_filesystem = memnew ( EditorFileSystemDirectory ) ;
2020-04-02 01:20:12 +02:00
new_filesystem - > parent = nullptr ;
2014-02-10 02:10:30 +01:00
2024-05-24 07:30:16 +02:00
ScannedDirectory * sd ;
// On the first scan, the first_scan_root_dir is created in _first_scan_filesystem.
if ( first_scan ) {
sd = first_scan_root_dir ;
} else {
Ref < DirAccess > d = DirAccess : : create ( DirAccess : : ACCESS_RESOURCES ) ;
sd = memnew ( ScannedDirectory ) ;
sd - > full_path = " res:// " ;
nb_files_total = _scan_new_dir ( sd , d ) ;
}
_process_file_system ( sd , new_filesystem , sp ) ;
2014-02-10 02:10:30 +01:00
2024-05-24 07:30:16 +02:00
dep_update_list . clear ( ) ;
2016-01-05 14:36:24 +01:00
file_cache . clear ( ) ; //clear caches, no longer needed
2014-02-10 02:10:30 +01:00
2024-05-24 07:30:16 +02:00
if ( first_scan ) {
memdelete ( first_scan_root_dir ) ;
first_scan_root_dir = nullptr ;
} else {
2019-02-26 22:43:37 +01:00
//on the first scan this is done from the main thread after re-importing
_save_filesystem_cache ( ) ;
2018-03-10 20:47:10 +01:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
scanning = false ;
2014-02-10 02:10:30 +01:00
}
2017-02-01 13:45:45 +01:00
void EditorFileSystem : : _save_filesystem_cache ( ) {
2019-04-19 20:54:33 +02:00
group_file_cache . clear ( ) ;
2022-08-30 02:34:01 +02:00
String fscache = EditorPaths : : get_singleton ( ) - > get_project_settings_dir ( ) . path_join ( CACHE_FILE_NAME ) ;
2014-02-10 02:10:30 +01:00
2022-03-23 10:08:58 +01:00
Ref < FileAccess > f = FileAccess : : open ( fscache , FileAccess : : WRITE ) ;
ERR_FAIL_COND_MSG ( f . is_null ( ) , " Cannot create file ' " + fscache + " '. Check user write permissions. " ) ;
2019-10-31 15:14:49 +01:00
f - > store_line ( filesystem_settings_version_for_import ) ;
_save_filesystem_cache ( filesystem , f ) ;
2017-02-01 13:45:45 +01:00
}
2014-02-10 02:10:30 +01:00
void EditorFileSystem : : _thread_func ( void * _userdata ) {
2017-03-05 16:44:50 +01:00
EditorFileSystem * sd = ( EditorFileSystem * ) _userdata ;
2016-01-05 14:36:24 +01:00
sd - > _scan_filesystem ( ) ;
}
2017-11-24 17:39:41 +01:00
bool EditorFileSystem : : _test_for_reimport ( const String & p_path , bool p_only_imported_files ) {
2020-05-14 16:41:43 +02:00
if ( ! reimport_on_missing_imported_files & & p_only_imported_files ) {
2017-11-24 17:39:41 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2017-11-24 17:39:41 +01:00
2019-02-27 13:36:23 +01:00
if ( ! FileAccess : : exists ( p_path + " .import " ) ) {
2017-11-24 17:39:41 +01:00
return true ;
}
2019-02-27 13:36:23 +01:00
Error err ;
2022-03-23 10:08:58 +01:00
Ref < FileAccess > f = FileAccess : : open ( p_path + " .import " , FileAccess : : READ , & err ) ;
2019-02-27 13:36:23 +01:00
2024-07-31 13:48:42 +02:00
if ( f . is_null ( ) ) { // No import file, reimport.
2019-02-27 13:36:23 +01:00
return true ;
}
2017-11-24 17:39:41 +01:00
VariantParser : : StreamFile stream ;
stream . f = f ;
String assign ;
Variant value ;
VariantParser : : Tag next_tag ;
int lines = 0 ;
String error_text ;
List < String > to_check ;
2020-12-02 02:40:47 +01:00
String importer_name ;
2018-03-01 09:39:40 +01:00
String source_file = " " ;
String source_md5 = " " ;
2017-12-27 19:21:18 +01:00
Vector < String > dest_files ;
2018-03-01 09:39:40 +01:00
String dest_md5 = " " ;
2020-12-02 02:40:47 +01:00
int version = 0 ;
2021-07-23 21:01:18 +02:00
bool found_uid = false ;
2017-11-24 17:39:41 +01:00
while ( true ) {
assign = Variant ( ) ;
next_tag . fields . clear ( ) ;
next_tag . name = String ( ) ;
2020-04-02 01:20:12 +02:00
err = VariantParser : : parse_tag_assign_eof ( & stream , lines , error_text , next_tag , assign , value , nullptr , true ) ;
2017-11-24 17:39:41 +01:00
if ( err = = ERR_FILE_EOF ) {
break ;
} else if ( err ! = OK ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " ResourceFormatImporter::load - ' " + p_path + " .import: " + itos ( lines ) + " ' error ' " + error_text + " '. " ) ;
2024-07-31 13:48:42 +02:00
// Parse error, skip and let user attempt manual reimport to avoid reimport loop.
return false ;
2017-11-24 17:39:41 +01:00
}
2021-12-09 10:42:46 +01:00
if ( ! assign . is_empty ( ) ) {
2024-07-31 13:48:42 +02:00
if ( assign = = " valid " & & value . operator bool ( ) = = false ) {
// Invalid import (failed previous import), skip and let user attempt manual reimport to avoid reimport loop.
return false ;
}
2017-11-24 17:39:41 +01:00
if ( assign . begins_with ( " path " ) ) {
to_check . push_back ( value ) ;
} else if ( assign = = " files " ) {
Array fa = value ;
for ( int i = 0 ; i < fa . size ( ) ; i + + ) {
to_check . push_back ( fa [ i ] ) ;
}
2020-12-02 02:40:47 +01:00
} else if ( assign = = " importer_version " ) {
version = value ;
} else if ( assign = = " importer " ) {
importer_name = value ;
2021-07-23 21:01:18 +02:00
} else if ( assign = = " uid " ) {
found_uid = true ;
2017-12-27 19:21:18 +01:00
} else if ( ! p_only_imported_files ) {
2018-03-01 09:39:40 +01:00
if ( assign = = " source_file " ) {
2017-12-27 19:21:18 +01:00
source_file = value ;
} else if ( assign = = " dest_files " ) {
dest_files = value ;
}
2017-11-24 17:39:41 +01:00
}
} else if ( next_tag . name ! = " remap " & & next_tag . name ! = " deps " ) {
break ;
}
}
2024-07-31 13:48:42 +02:00
if ( ! ResourceFormatImporter : : get_singleton ( ) - > are_import_settings_valid ( p_path ) ) {
// Reimport settings are out of sync with project settings, reimport.
return true ;
}
2024-02-05 11:10:37 +01:00
if ( importer_name = = " keep " | | importer_name = = " skip " ) {
2021-03-22 20:41:47 +01:00
return false ; //keep mode, do not reimport
}
2021-07-23 21:01:18 +02:00
if ( ! found_uid ) {
2022-12-05 19:01:59 +01:00
return true ; //UID not found, old format, reimport.
2021-07-23 21:01:18 +02:00
}
2020-12-02 02:40:47 +01:00
Ref < ResourceImporter > importer = ResourceFormatImporter : : get_singleton ( ) - > get_importer_by_name ( importer_name ) ;
2021-09-10 03:54:18 +02:00
if ( importer . is_null ( ) ) {
return true ; // the importer has possibly changed, try to reimport.
}
2020-12-02 02:40:47 +01:00
if ( importer - > get_format_version ( ) > version ) {
return true ; // version changed, reimport
}
2018-09-13 03:38:39 +02:00
// Read the md5's from a separate file (so the import parameters aren't dependent on the file version
2018-03-01 09:39:40 +01:00
String base_path = ResourceFormatImporter : : get_singleton ( ) - > get_import_base_path ( p_path ) ;
2022-03-23 10:08:58 +01:00
Ref < FileAccess > md5s = FileAccess : : open ( base_path + " .md5 " , FileAccess : : READ , & err ) ;
if ( md5s . is_null ( ) ) { // No md5's stored for this resource
2018-03-01 09:39:40 +01:00
return true ;
}
VariantParser : : StreamFile md5_stream ;
md5_stream . f = md5s ;
while ( true ) {
assign = Variant ( ) ;
next_tag . fields . clear ( ) ;
next_tag . name = String ( ) ;
2020-04-02 01:20:12 +02:00
err = VariantParser : : parse_tag_assign_eof ( & md5_stream , lines , error_text , next_tag , assign , value , nullptr , true ) ;
2018-03-01 09:39:40 +01:00
if ( err = = ERR_FILE_EOF ) {
break ;
} else if ( err ! = OK ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " ResourceFormatImporter::load - ' " + p_path + " .import.md5: " + itos ( lines ) + " ' error ' " + error_text + " '. " ) ;
2018-03-01 09:39:40 +01:00
return false ; // parse error
}
2021-12-09 10:42:46 +01:00
if ( ! assign . is_empty ( ) ) {
2018-03-01 09:39:40 +01:00
if ( ! p_only_imported_files ) {
if ( assign = = " source_md5 " ) {
source_md5 = value ;
} else if ( assign = = " dest_md5 " ) {
dest_md5 = value ;
}
}
}
}
2017-11-24 17:39:41 +01:00
//imported files are gone, reimport
2021-07-24 15:46:25 +02:00
for ( const String & E : to_check ) {
2021-07-16 05:45:57 +02:00
if ( ! FileAccess : : exists ( E ) ) {
2017-11-24 17:39:41 +01:00
return true ;
}
}
//check source md5 matching
if ( ! p_only_imported_files ) {
2021-12-09 10:42:46 +01:00
if ( ! source_file . is_empty ( ) & & source_file ! = p_path ) {
2017-12-27 19:21:18 +01:00
return true ; //file was moved, reimport
}
2021-12-09 10:42:46 +01:00
if ( source_md5 . is_empty ( ) ) {
2017-11-24 17:39:41 +01:00
return true ; //lacks md5, so just reimport
}
String md5 = FileAccess : : get_md5 ( p_path ) ;
if ( md5 ! = source_md5 ) {
return true ;
}
2017-12-27 19:21:18 +01:00
2021-12-09 10:42:46 +01:00
if ( dest_files . size ( ) & & ! dest_md5 . is_empty ( ) ) {
2017-12-27 19:21:18 +01:00
md5 = FileAccess : : get_multiple_md5 ( dest_files ) ;
if ( md5 ! = dest_md5 ) {
return true ;
}
}
2017-11-24 17:39:41 +01:00
}
return false ; //nothing changed
}
2024-02-15 17:25:58 +01:00
bool EditorFileSystem : : _scan_import_support ( const Vector < String > & reimports ) {
2022-03-31 22:00:17 +02:00
if ( import_support_queries . size ( ) = = 0 ) {
return false ;
}
2022-05-13 15:04:37 +02:00
HashMap < String , int > import_support_test ;
2022-03-31 22:00:17 +02:00
Vector < bool > import_support_tested ;
import_support_tested . resize ( import_support_queries . size ( ) ) ;
for ( int i = 0 ; i < import_support_queries . size ( ) ; i + + ) {
import_support_tested . write [ i ] = false ;
if ( import_support_queries [ i ] - > is_active ( ) ) {
Vector < String > extensions = import_support_queries [ i ] - > get_file_extensions ( ) ;
for ( int j = 0 ; j < extensions . size ( ) ; j + + ) {
import_support_test . insert ( extensions [ j ] , i ) ;
}
}
}
if ( import_support_test . size ( ) = = 0 ) {
return false ; //well nothing to do
}
for ( int i = 0 ; i < reimports . size ( ) ; i + + ) {
2023-06-22 16:33:13 +02:00
HashMap < String , int > : : Iterator E = import_support_test . find ( reimports [ i ] . get_extension ( ) . to_lower ( ) ) ;
2022-03-31 22:00:17 +02:00
if ( E ) {
2022-05-13 15:04:37 +02:00
import_support_tested . write [ E - > value ] = true ;
2022-03-31 22:00:17 +02:00
}
}
for ( int i = 0 ; i < import_support_tested . size ( ) ; i + + ) {
if ( import_support_tested [ i ] ) {
if ( import_support_queries . write [ i ] - > query ( ) ) {
return true ;
}
}
}
return false ;
}
2016-01-05 14:36:24 +01:00
bool EditorFileSystem : : _update_scan_actions ( ) {
sources_changed . clear ( ) ;
2024-05-24 07:30:16 +02:00
// We need to update the script global class names before the reimports to be sure that
// all the importer classes that depends on class names will work.
_update_script_classes ( ) ;
2017-03-05 16:44:50 +01:00
bool fs_changed = false ;
2016-01-05 14:36:24 +01:00
2017-02-01 13:45:45 +01:00
Vector < String > reimports ;
2018-11-21 01:47:48 +01:00
Vector < String > reloads ;
2017-02-01 13:45:45 +01:00
2021-07-24 15:46:25 +02:00
for ( const ItemAction & ia : scan_actions ) {
2017-03-05 16:44:50 +01:00
switch ( ia . action ) {
2016-01-05 14:36:24 +01:00
case ItemAction : : ACTION_NONE : {
} break ;
case ItemAction : : ACTION_DIR_ADD : {
2017-03-05 16:44:50 +01:00
int idx = 0 ;
for ( int i = 0 ; i < ia . dir - > subdirs . size ( ) ; i + + ) {
2024-02-02 16:50:23 +01:00
if ( ia . new_dir - > name . filenocasecmp_to ( ia . dir - > subdirs [ i ] - > name ) < 0 ) {
2016-01-05 14:36:24 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2016-01-05 14:36:24 +01:00
idx + + ;
}
2017-03-05 16:44:50 +01:00
if ( idx = = ia . dir - > subdirs . size ( ) ) {
2016-01-05 14:36:24 +01:00
ia . dir - > subdirs . push_back ( ia . new_dir ) ;
} else {
2017-03-05 16:44:50 +01:00
ia . dir - > subdirs . insert ( idx , ia . new_dir ) ;
2016-01-05 14:36:24 +01:00
}
2017-03-05 16:44:50 +01:00
fs_changed = true ;
2016-01-05 14:36:24 +01:00
} break ;
case ItemAction : : ACTION_DIR_REMOVE : {
ERR_CONTINUE ( ! ia . dir - > parent ) ;
ia . dir - > parent - > subdirs . erase ( ia . dir ) ;
2017-03-05 16:44:50 +01:00
memdelete ( ia . dir ) ;
fs_changed = true ;
2016-01-05 14:36:24 +01:00
} break ;
case ItemAction : : ACTION_FILE_ADD : {
2017-03-05 16:44:50 +01:00
int idx = 0 ;
for ( int i = 0 ; i < ia . dir - > files . size ( ) ; i + + ) {
2024-02-02 16:50:23 +01:00
if ( ia . new_file - > file . filenocasecmp_to ( ia . dir - > files [ i ] - > file ) < 0 ) {
2016-01-05 14:36:24 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2016-01-05 14:36:24 +01:00
idx + + ;
}
2017-03-05 16:44:50 +01:00
if ( idx = = ia . dir - > files . size ( ) ) {
2016-01-05 14:36:24 +01:00
ia . dir - > files . push_back ( ia . new_file ) ;
} else {
2017-03-05 16:44:50 +01:00
ia . dir - > files . insert ( idx , ia . new_file ) ;
2016-01-05 14:36:24 +01:00
}
2017-03-05 16:44:50 +01:00
fs_changed = true ;
2016-01-05 14:36:24 +01:00
2023-01-18 17:32:28 +01:00
if ( ClassDB : : is_parent_class ( ia . new_file - > type , SNAME ( " Script " ) ) ) {
2024-05-24 07:30:16 +02:00
_queue_update_script_class ( ia . dir - > get_file_path ( idx ) , ia . new_file - > type , ia . new_file - > script_class_name , ia . new_file - > script_class_extends , ia . new_file - > script_class_icon_path ) ;
2023-01-18 17:32:28 +01:00
}
2022-05-12 10:20:12 +02:00
if ( ia . new_file - > type = = SNAME ( " PackedScene " ) ) {
_queue_update_scene_groups ( ia . dir - > get_file_path ( idx ) ) ;
}
2023-01-18 17:32:28 +01:00
2016-01-05 14:36:24 +01:00
} break ;
case ItemAction : : ACTION_FILE_REMOVE : {
int idx = ia . dir - > find_file_index ( ia . file ) ;
2017-03-05 16:44:50 +01:00
ERR_CONTINUE ( idx = = - 1 ) ;
2023-01-18 17:32:28 +01:00
2024-05-24 07:30:16 +02:00
String script_class_name = ia . dir - > files [ idx ] - > script_class_name ;
2023-01-18 17:32:28 +01:00
if ( ClassDB : : is_parent_class ( ia . dir - > files [ idx ] - > type , SNAME ( " Script " ) ) ) {
2024-05-24 07:30:16 +02:00
_queue_update_script_class ( ia . dir - > get_file_path ( idx ) , " " , " " , " " , " " ) ;
2023-01-18 17:32:28 +01:00
}
2022-05-12 10:20:12 +02:00
if ( ia . dir - > files [ idx ] - > type = = SNAME ( " PackedScene " ) ) {
_queue_update_scene_groups ( ia . dir - > get_file_path ( idx ) ) ;
}
2023-01-18 17:32:28 +01:00
2017-08-15 01:13:14 +02:00
_delete_internal_files ( ia . dir - > files [ idx ] - > file ) ;
2017-03-05 16:44:50 +01:00
memdelete ( ia . dir - > files [ idx ] ) ;
2021-07-04 00:17:03 +02:00
ia . dir - > files . remove_at ( idx ) ;
2016-01-05 14:36:24 +01:00
2024-05-24 07:30:16 +02:00
// Restore another script with the same global class name if it exists.
if ( ! script_class_name . is_empty ( ) ) {
EditorFileSystemDirectory : : FileInfo * old_fi = nullptr ;
String old_file = _get_file_by_class_name ( filesystem , script_class_name , old_fi ) ;
if ( ! old_file . is_empty ( ) & & old_fi ) {
_queue_update_script_class ( old_file , old_fi - > type , old_fi - > script_class_name , old_fi - > script_class_extends , old_fi - > script_class_icon_path ) ;
}
}
2017-03-05 16:44:50 +01:00
fs_changed = true ;
2016-01-05 14:36:24 +01:00
} break ;
2017-11-24 17:39:41 +01:00
case ItemAction : : ACTION_FILE_TEST_REIMPORT : {
2016-01-05 14:36:24 +01:00
int idx = ia . dir - > find_file_index ( ia . file ) ;
2017-03-05 16:44:50 +01:00
ERR_CONTINUE ( idx = = - 1 ) ;
2016-01-05 14:36:24 +01:00
String full_path = ia . dir - > get_file_path ( idx ) ;
2023-11-16 14:53:09 +01:00
bool need_reimport = _test_for_reimport ( full_path , false ) ;
2024-07-24 12:07:59 +02:00
// Workaround GH-94416 for the Android editor for now.
// `import_mt` seems to always be 0 and force a reimport on any fs scan.
# ifndef ANDROID_ENABLED
2023-11-16 14:53:09 +01:00
if ( ! need_reimport & & FileAccess : : exists ( full_path + " .import " ) ) {
uint64_t import_mt = ia . dir - > get_file_import_modified_time ( idx ) ;
if ( import_mt ! = FileAccess : : get_modified_time ( full_path + " .import " ) ) {
need_reimport = true ;
}
}
2024-07-24 12:07:59 +02:00
# endif
2023-11-16 14:53:09 +01:00
if ( need_reimport ) {
2017-11-24 17:39:41 +01:00
//must reimport
reimports . push_back ( full_path ) ;
2022-10-19 08:42:51 +02:00
Vector < String > dependencies = _get_dependencies ( full_path ) ;
2024-06-30 03:38:04 +02:00
for ( const String & dep : dependencies ) {
const String & dependency_path = dep . contains ( " :: " ) ? dep . get_slice ( " :: " , 0 ) : dep ;
if ( import_extensions . has ( dep . get_extension ( ) ) ) {
2022-10-19 08:42:51 +02:00
reimports . push_back ( dependency_path ) ;
}
}
2017-11-24 17:39:41 +01:00
} else {
//must not reimport, all was good
//update modified times, to avoid reimport
ia . dir - > files [ idx ] - > modified_time = FileAccess : : get_modified_time ( full_path ) ;
ia . dir - > files [ idx ] - > import_modified_time = FileAccess : : get_modified_time ( full_path + " .import " ) ;
}
2016-01-05 14:36:24 +01:00
2017-03-05 16:44:50 +01:00
fs_changed = true ;
2016-01-05 14:36:24 +01:00
} break ;
2018-11-21 01:47:48 +01:00
case ItemAction : : ACTION_FILE_RELOAD : {
int idx = ia . dir - > find_file_index ( ia . file ) ;
ERR_CONTINUE ( idx = = - 1 ) ;
String full_path = ia . dir - > get_file_path ( idx ) ;
2024-05-24 07:30:16 +02:00
const EditorFileSystemDirectory : : FileInfo * fi = ia . dir - > files [ idx ] ;
if ( ClassDB : : is_parent_class ( fi - > type , SNAME ( " Script " ) ) ) {
_queue_update_script_class ( full_path , fi - > type , fi - > script_class_name , fi - > script_class_extends , fi - > script_class_icon_path ) ;
2023-01-18 17:32:28 +01:00
}
2024-05-24 07:30:16 +02:00
if ( fi - > type = = SNAME ( " PackedScene " ) ) {
2022-05-12 10:20:12 +02:00
_queue_update_scene_groups ( full_path ) ;
}
2023-01-18 17:32:28 +01:00
2018-11-21 01:47:48 +01:00
reloads . push_back ( full_path ) ;
} break ;
2016-01-05 14:36:24 +01:00
}
}
2021-08-20 20:32:56 +02:00
if ( _scan_extensions ( ) ) {
//needs editor restart
//extensions also may provide filetypes to be imported, so they must run before importing
2022-03-31 22:00:17 +02:00
if ( EditorNode : : immediate_confirmation_dialog ( TTR ( " Some extensions need the editor to restart to take effect. " ) , first_scan ? TTR ( " Restart " ) : TTR ( " Save & Restart " ) , TTR ( " Continue " ) ) ) {
2021-08-20 20:32:56 +02:00
if ( ! first_scan ) {
EditorNode : : get_singleton ( ) - > save_all_scenes ( ) ;
}
EditorNode : : get_singleton ( ) - > restart_editor ( ) ;
//do not import
return true ;
}
}
2022-03-31 22:00:17 +02:00
2017-02-01 13:45:45 +01:00
if ( reimports . size ( ) ) {
2022-03-31 22:00:17 +02:00
if ( _scan_import_support ( reimports ) ) {
return true ;
}
2017-02-01 13:45:45 +01:00
reimport_files ( reimports ) ;
2021-07-23 21:01:18 +02:00
} else {
//reimport files will update the uid cache file so if nothing was reimported, update it manually
ResourceUID : : get_singleton ( ) - > update_cache ( ) ;
2017-02-01 13:45:45 +01:00
}
2018-11-21 01:47:48 +01:00
2019-02-26 22:43:37 +01:00
if ( first_scan ) {
//only on first scan this is valid and updated, then settings changed.
revalidate_import_files = false ;
filesystem_settings_version_for_import = ResourceFormatImporter : : get_singleton ( ) - > get_import_settings_hash ( ) ;
_save_filesystem_cache ( ) ;
}
2024-05-24 07:30:16 +02:00
// Moving the processing of pending updates before the resources_reload event to be sure all global class names
// are updated. Script.cpp listens on resources_reload and reloads updated scripts.
_process_update_pending ( ) ;
2018-11-21 01:47:48 +01:00
if ( reloads . size ( ) ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " resources_reload " ) , reloads ) ;
2018-11-21 01:47:48 +01:00
}
2016-01-05 14:36:24 +01:00
scan_actions . clear ( ) ;
return fs_changed ;
2014-02-10 02:10:30 +01:00
}
void EditorFileSystem : : scan ( ) {
2020-05-14 16:41:43 +02:00
if ( false /*&& bool(Globals::get_singleton()->get("debug/disable_scan"))*/ ) {
2017-03-05 16:44:50 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2021-01-19 13:29:41 +01:00
if ( scanning | | scanning_changes | | thread . is_started ( ) ) {
2014-02-10 02:10:30 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2024-05-24 07:30:16 +02:00
// The first scan must be on the main thread because, after the first scan and update
// of global class names, we load the plugins and autoloads. These need to
// be added on the main thread because they are nodes, and we need to wait for them
// to be loaded to continue the scan and reimportations.
if ( first_scan ) {
_first_scan_filesystem ( ) ;
}
2017-02-01 13:45:45 +01:00
_update_extensions ( ) ;
2014-02-10 02:10:30 +01:00
if ( ! use_threads ) {
2017-03-05 16:44:50 +01:00
scanning = true ;
scan_total = 0 ;
2016-01-05 14:36:24 +01:00
_scan_filesystem ( ) ;
2020-05-14 16:41:43 +02:00
if ( filesystem ) {
2014-02-10 02:10:30 +01:00
memdelete ( filesystem ) ;
2020-05-14 16:41:43 +02:00
}
2017-01-14 12:26:56 +01:00
//file_type_cache.clear();
2017-03-05 16:44:50 +01:00
filesystem = new_filesystem ;
2020-04-02 01:20:12 +02:00
new_filesystem = nullptr ;
2016-01-05 14:36:24 +01:00
_update_scan_actions ( ) ;
2024-06-14 06:13:45 +02:00
// Update all icons so they are loaded for the FileSystemDock.
_update_files_icon_path ( ) ;
2024-05-24 07:30:16 +02:00
scanning = false ;
// Set first_scan to false before the signals so the function doing_first_scan can return false
// in editor_node to start the export if needed.
first_scan = false ;
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " filesystem_changed " ) ) ;
emit_signal ( SNAME ( " sources_changed " ) , sources_changed . size ( ) > 0 ) ;
2014-02-10 02:10:30 +01:00
} else {
2021-01-19 13:29:41 +01:00
ERR_FAIL_COND ( thread . is_started ( ) ) ;
2014-02-10 02:10:30 +01:00
set_process ( true ) ;
Thread : : Settings s ;
2017-03-05 16:44:50 +01:00
scanning = true ;
scan_total = 0 ;
s . priority = Thread : : PRIORITY_LOW ;
2021-01-19 13:29:41 +01:00
thread . start ( _thread_func , this , s ) ;
2014-02-10 02:10:30 +01:00
//tree->hide();
//progress->show();
}
}
2024-05-24 07:30:16 +02:00
void EditorFileSystem : : ScanProgress : : increment ( ) {
current + + ;
float ratio = current / MAX ( hi , 1.0f ) ;
progress - > step ( ratio * 1000.0f ) ;
2017-03-05 16:44:50 +01:00
EditorFileSystem : : singleton - > scan_total = ratio ;
2016-01-05 14:36:24 +01:00
}
2024-05-24 07:30:16 +02:00
int EditorFileSystem : : _scan_new_dir ( ScannedDirectory * p_dir , Ref < DirAccess > & da ) {
2016-01-05 14:36:24 +01:00
List < String > dirs ;
List < String > files ;
String cd = da - > get_current_dir ( ) ;
da - > list_dir_begin ( ) ;
while ( true ) {
2019-07-25 11:09:57 +02:00
String f = da - > get_next ( ) ;
2021-12-09 10:42:46 +01:00
if ( f . is_empty ( ) ) {
2016-01-05 14:36:24 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2016-01-05 14:36:24 +01:00
2020-05-14 16:41:43 +02:00
if ( da - > current_is_hidden ( ) ) {
2020-02-10 09:19:10 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2020-02-10 09:19:10 +01:00
2019-07-25 11:09:57 +02:00
if ( da - > current_is_dir ( ) ) {
2020-05-14 16:41:43 +02:00
if ( f . begins_with ( " . " ) ) { // Ignore special and . / ..
2016-01-05 14:36:24 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2016-01-05 14:36:24 +01:00
2022-08-30 02:34:01 +02:00
if ( _should_skip_directory ( cd . path_join ( f ) ) ) {
2017-08-06 16:43:48 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2016-01-05 14:36:24 +01:00
dirs . push_back ( f ) ;
} else {
files . push_back ( f ) ;
}
}
da - > list_dir_end ( ) ;
2024-02-02 16:50:23 +01:00
dirs . sort_custom < FileNoCaseComparator > ( ) ;
files . sort_custom < FileNoCaseComparator > ( ) ;
2016-01-05 14:36:24 +01:00
2024-05-24 07:30:16 +02:00
int nb_files_total_scan = 0 ;
2016-06-28 13:26:07 +02:00
2024-05-24 07:30:16 +02:00
for ( List < String > : : Element * E = dirs . front ( ) ; E ; E = E - > next ( ) ) {
2017-03-05 16:44:50 +01:00
if ( da - > change_dir ( E - > get ( ) ) = = OK ) {
2016-06-28 13:26:07 +02:00
String d = da - > get_current_dir ( ) ;
2016-01-05 14:36:24 +01:00
2017-03-05 16:44:50 +01:00
if ( d = = cd | | ! d . begins_with ( cd ) ) {
2016-06-28 13:26:07 +02:00
da - > change_dir ( cd ) ; //avoid recursion
} else {
2024-05-24 07:30:16 +02:00
ScannedDirectory * sd = memnew ( ScannedDirectory ) ;
sd - > name = E - > get ( ) ;
sd - > full_path = p_dir - > full_path . path_join ( sd - > name ) ;
2016-01-05 14:36:24 +01:00
2024-05-24 07:30:16 +02:00
nb_files_total_scan + = _scan_new_dir ( sd , da ) ;
2016-01-05 14:36:24 +01:00
2024-05-24 07:30:16 +02:00
p_dir - > subdirs . push_back ( sd ) ;
2016-06-28 13:26:07 +02:00
da - > change_dir ( " .. " ) ;
}
2016-01-05 14:36:24 +01:00
} else {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Cannot go into subdir ' " + E - > get ( ) + " '. " ) ;
2016-01-05 14:36:24 +01:00
}
2024-05-24 07:30:16 +02:00
}
2016-01-05 14:36:24 +01:00
2024-05-24 07:30:16 +02:00
p_dir - > files = files ;
nb_files_total_scan + = files . size ( ) ;
return nb_files_total_scan ;
}
void EditorFileSystem : : _process_file_system ( const ScannedDirectory * p_scan_dir , EditorFileSystemDirectory * p_dir , ScanProgress & p_progress ) {
p_dir - > modified_time = FileAccess : : get_modified_time ( p_scan_dir - > full_path ) ;
for ( ScannedDirectory * scan_sub_dir : p_scan_dir - > subdirs ) {
EditorFileSystemDirectory * sub_dir = memnew ( EditorFileSystemDirectory ) ;
sub_dir - > parent = p_dir ;
sub_dir - > name = scan_sub_dir - > name ;
p_dir - > subdirs . push_back ( sub_dir ) ;
_process_file_system ( scan_sub_dir , sub_dir , p_progress ) ;
2016-01-05 14:36:24 +01:00
}
2024-05-24 07:30:16 +02:00
for ( const String & scan_file : p_scan_dir - > files ) {
String ext = scan_file . get_extension ( ) . to_lower ( ) ;
2017-02-01 13:45:45 +01:00
if ( ! valid_extensions . has ( ext ) ) {
2024-05-24 07:30:16 +02:00
p_progress . increment ( ) ;
2016-01-05 14:36:24 +01:00
continue ; //invalid
2017-02-01 13:45:45 +01:00
}
2016-01-05 14:36:24 +01:00
2024-05-24 07:30:16 +02:00
String path = p_scan_dir - > full_path . path_join ( scan_file ) ;
2016-01-05 14:36:24 +01:00
2024-05-24 07:30:16 +02:00
EditorFileSystemDirectory : : FileInfo * fi = memnew ( EditorFileSystemDirectory : : FileInfo ) ;
fi - > file = scan_file ;
p_dir - > files . push_back ( fi ) ;
2016-01-05 14:36:24 +01:00
FileCache * fc = file_cache . getptr ( path ) ;
uint64_t mt = FileAccess : : get_modified_time ( path ) ;
2017-02-01 13:45:45 +01:00
if ( import_extensions . has ( ext ) ) {
2017-03-05 16:44:50 +01:00
//is imported
uint64_t import_mt = 0 ;
if ( FileAccess : : exists ( path + " .import " ) ) {
import_mt = FileAccess : : get_modified_time ( path + " .import " ) ;
2017-02-01 13:45:45 +01:00
}
2016-01-05 14:36:24 +01:00
2017-11-24 17:39:41 +01:00
if ( fc & & fc - > modification_time = = mt & & fc - > import_modification_time = = import_mt & & ! _test_for_reimport ( path , true ) ) {
2017-03-05 16:44:50 +01:00
fi - > type = fc - > type ;
2023-01-19 19:12:25 +01:00
fi - > resource_script_class = fc - > resource_script_class ;
2021-07-23 21:01:18 +02:00
fi - > uid = fc - > uid ;
2017-08-17 22:02:43 +02:00
fi - > deps = fc - > deps ;
2017-03-05 16:44:50 +01:00
fi - > modified_time = fc - > modification_time ;
fi - > import_modified_time = fc - > import_modification_time ;
2019-02-26 22:43:37 +01:00
2017-08-30 00:50:58 +02:00
fi - > import_valid = fc - > import_valid ;
2018-07-16 00:29:00 +02:00
fi - > script_class_name = fc - > script_class_name ;
2019-04-19 20:54:33 +02:00
fi - > import_group_file = fc - > import_group_file ;
2018-07-16 00:29:00 +02:00
fi - > script_class_extends = fc - > script_class_extends ;
2018-07-29 05:36:43 +02:00
fi - > script_class_icon_path = fc - > script_class_icon_path ;
2018-07-16 00:29:00 +02:00
2019-02-26 22:43:37 +01:00
if ( revalidate_import_files & & ! ResourceFormatImporter : : get_singleton ( ) - > are_import_settings_valid ( path ) ) {
ItemAction ia ;
ia . action = ItemAction : : ACTION_FILE_TEST_REIMPORT ;
ia . dir = p_dir ;
2024-05-24 07:30:16 +02:00
ia . file = fi - > file ;
2019-02-26 22:43:37 +01:00
scan_actions . push_back ( ia ) ;
}
2021-12-09 10:42:46 +01:00
if ( fc - > type . is_empty ( ) ) {
2017-08-15 21:27:15 +02:00
fi - > type = ResourceLoader : : get_resource_type ( path ) ;
2023-01-19 19:12:25 +01:00
fi - > resource_script_class = ResourceLoader : : get_resource_script_class ( path ) ;
2019-04-19 20:54:33 +02:00
fi - > import_group_file = ResourceLoader : : get_import_group_file ( path ) ;
2017-08-15 21:27:15 +02:00
//there is also the chance that file type changed due to reimport, must probably check this somehow here (or kind of note it for next time in another file?)
2017-08-17 22:02:43 +02:00
//note: I think this should not happen any longer..
2017-08-15 21:27:15 +02:00
}
2017-02-01 13:45:45 +01:00
2021-07-23 21:01:18 +02:00
if ( fc - > uid = = ResourceUID : : INVALID_ID ) {
2022-12-05 19:01:59 +01:00
// imported files should always have a UID, so attempt to fetch it.
2021-07-23 21:01:18 +02:00
fi - > uid = ResourceLoader : : get_resource_uid ( path ) ;
}
2017-02-01 13:45:45 +01:00
} else {
2017-03-05 16:44:50 +01:00
fi - > type = ResourceFormatImporter : : get_singleton ( ) - > get_resource_type ( path ) ;
2021-07-23 21:01:18 +02:00
fi - > uid = ResourceFormatImporter : : get_singleton ( ) - > get_resource_uid ( path ) ;
2019-04-19 20:54:33 +02:00
fi - > import_group_file = ResourceFormatImporter : : get_singleton ( ) - > get_import_group_file ( path ) ;
2018-07-29 05:36:43 +02:00
fi - > script_class_name = _get_global_script_class ( fi - > type , path , & fi - > script_class_extends , & fi - > script_class_icon_path ) ;
2017-03-05 16:44:50 +01:00
fi - > modified_time = 0 ;
fi - > import_modified_time = 0 ;
2022-07-20 23:51:29 +02:00
fi - > import_valid = fi - > type = = " TextFile " ? true : ResourceLoader : : is_import_valid ( path ) ;
2016-01-05 14:36:24 +01:00
ItemAction ia ;
2017-11-24 17:39:41 +01:00
ia . action = ItemAction : : ACTION_FILE_TEST_REIMPORT ;
2017-03-05 16:44:50 +01:00
ia . dir = p_dir ;
2024-05-24 07:30:16 +02:00
ia . file = fi - > file ;
2016-01-05 14:36:24 +01:00
scan_actions . push_back ( ia ) ;
}
2016-05-27 19:18:40 +02:00
} else {
2017-08-17 22:02:43 +02:00
if ( fc & & fc - > modification_time = = mt ) {
//not imported, so just update type if changed
2017-03-05 16:44:50 +01:00
fi - > type = fc - > type ;
2023-01-19 19:12:25 +01:00
fi - > resource_script_class = fc - > resource_script_class ;
2021-07-23 21:01:18 +02:00
fi - > uid = fc - > uid ;
2017-03-05 16:44:50 +01:00
fi - > modified_time = fc - > modification_time ;
2017-08-17 22:02:43 +02:00
fi - > deps = fc - > deps ;
2017-03-05 16:44:50 +01:00
fi - > import_modified_time = 0 ;
2017-08-30 00:50:58 +02:00
fi - > import_valid = true ;
2018-07-16 00:29:00 +02:00
fi - > script_class_name = fc - > script_class_name ;
fi - > script_class_extends = fc - > script_class_extends ;
2018-07-29 05:36:43 +02:00
fi - > script_class_icon_path = fc - > script_class_icon_path ;
2024-05-24 07:30:16 +02:00
if ( first_scan & & ClassDB : : is_parent_class ( fi - > type , SNAME ( " Script " ) ) ) {
bool update_script = false ;
String old_class_name = fi - > script_class_name ;
fi - > script_class_name = _get_global_script_class ( fi - > type , path , & fi - > script_class_extends , & fi - > script_class_icon_path ) ;
if ( old_class_name ! = fi - > script_class_name ) {
update_script = true ;
} else if ( ! fi - > script_class_name . is_empty ( ) & & ( ! ScriptServer : : is_global_class ( fi - > script_class_name ) | | ScriptServer : : get_global_class_path ( fi - > script_class_name ) ! = path ) ) {
// This script has a class name but is not in the global class names or the path of the class has changed.
update_script = true ;
}
if ( update_script ) {
_queue_update_script_class ( path , fi - > type , fi - > script_class_name , fi - > script_class_extends , fi - > script_class_icon_path ) ;
}
}
2017-02-01 13:45:45 +01:00
} else {
2017-08-17 22:02:43 +02:00
//new or modified time
2017-03-05 16:44:50 +01:00
fi - > type = ResourceLoader : : get_resource_type ( path ) ;
2023-01-19 19:12:25 +01:00
fi - > resource_script_class = ResourceLoader : : get_resource_script_class ( path ) ;
2021-09-23 23:09:15 +02:00
if ( fi - > type = = " " & & textfile_extensions . has ( ext ) ) {
fi - > type = " TextFile " ;
}
2021-07-23 21:01:18 +02:00
fi - > uid = ResourceLoader : : get_resource_uid ( path ) ;
2018-07-29 05:36:43 +02:00
fi - > script_class_name = _get_global_script_class ( fi - > type , path , & fi - > script_class_extends , & fi - > script_class_icon_path ) ;
2017-08-17 22:02:43 +02:00
fi - > deps = _get_dependencies ( path ) ;
2017-03-05 16:44:50 +01:00
fi - > modified_time = mt ;
fi - > import_modified_time = 0 ;
2017-08-30 00:50:58 +02:00
fi - > import_valid = true ;
2023-01-19 13:52:15 +01:00
2024-05-15 16:26:33 +02:00
// Files in dep_update_list are forced for rescan to update dependencies. They don't need other updates.
if ( ! dep_update_list . has ( path ) ) {
if ( ClassDB : : is_parent_class ( fi - > type , SNAME ( " Script " ) ) ) {
2024-05-24 07:30:16 +02:00
_queue_update_script_class ( path , fi - > type , fi - > script_class_name , fi - > script_class_extends , fi - > script_class_icon_path ) ;
2024-05-15 16:26:33 +02:00
}
if ( fi - > type = = SNAME ( " PackedScene " ) ) {
_queue_update_scene_groups ( path ) ;
}
2022-05-12 10:20:12 +02:00
}
2017-02-01 13:45:45 +01:00
}
2016-01-05 14:36:24 +01:00
}
2021-07-23 21:01:18 +02:00
if ( fi - > uid ! = ResourceUID : : INVALID_ID ) {
if ( ResourceUID : : get_singleton ( ) - > has_id ( fi - > uid ) ) {
ResourceUID : : get_singleton ( ) - > set_id ( fi - > uid , path ) ;
} else {
ResourceUID : : get_singleton ( ) - > add_id ( fi - > uid , path ) ;
}
}
2024-05-24 07:30:16 +02:00
p_progress . increment ( ) ;
2016-01-05 14:36:24 +01:00
}
}
2024-05-24 07:30:16 +02:00
void EditorFileSystem : : _scan_fs_changes ( EditorFileSystemDirectory * p_dir , ScanProgress & p_progress ) {
2016-01-05 14:36:24 +01:00
uint64_t current_mtime = FileAccess : : get_modified_time ( p_dir - > get_path ( ) ) ;
2017-03-05 16:44:50 +01:00
bool updated_dir = false ;
2017-02-01 13:45:45 +01:00
String cd = p_dir - > get_path ( ) ;
2024-05-24 07:30:16 +02:00
int diff_nb_files = 0 ;
2016-01-05 14:36:24 +01:00
2019-04-27 16:50:26 +02:00
if ( current_mtime ! = p_dir - > modified_time | | using_fat32_or_exfat ) {
2017-03-05 16:44:50 +01:00
updated_dir = true ;
p_dir - > modified_time = current_mtime ;
2016-01-05 14:36:24 +01:00
//ooooops, dir changed, see what's going on
2023-10-06 12:52:10 +02:00
//first mark everything as verified
2016-01-05 14:36:24 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < p_dir - > files . size ( ) ; i + + ) {
p_dir - > files [ i ] - > verified = false ;
2016-01-05 14:36:24 +01:00
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < p_dir - > subdirs . size ( ) ; i + + ) {
p_dir - > get_subdir ( i ) - > verified = false ;
2016-01-05 14:36:24 +01:00
}
2024-05-24 07:30:16 +02:00
diff_nb_files - = p_dir - > files . size ( ) ;
2016-01-05 14:36:24 +01:00
//then scan files and directories and check what's different
2022-03-23 10:08:58 +01:00
Ref < DirAccess > da = DirAccess : : create ( DirAccess : : ACCESS_RESOURCES ) ;
2021-02-27 21:43:26 +01:00
Error ret = da - > change_dir ( cd ) ;
ERR_FAIL_COND_MSG ( ret ! = OK , " Cannot change to ' " + cd + " ' folder. " ) ;
2017-02-01 13:45:45 +01:00
2016-01-05 14:36:24 +01:00
da - > list_dir_begin ( ) ;
while ( true ) {
2019-07-25 11:09:57 +02:00
String f = da - > get_next ( ) ;
2021-12-09 10:42:46 +01:00
if ( f . is_empty ( ) ) {
2016-01-05 14:36:24 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2016-01-05 14:36:24 +01:00
2020-05-14 16:41:43 +02:00
if ( da - > current_is_hidden ( ) ) {
2020-02-10 09:19:10 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2020-02-10 09:19:10 +01:00
2019-07-25 11:09:57 +02:00
if ( da - > current_is_dir ( ) ) {
2020-05-14 16:41:43 +02:00
if ( f . begins_with ( " . " ) ) { // Ignore special and . / ..
2016-01-05 14:36:24 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2016-01-05 14:36:24 +01:00
2017-04-29 17:56:51 +02:00
int idx = p_dir - > find_dir_index ( f ) ;
2017-03-05 16:44:50 +01:00
if ( idx = = - 1 ) {
2024-05-24 07:30:16 +02:00
String dir_path = cd . path_join ( f ) ;
if ( _should_skip_directory ( dir_path ) ) {
2017-08-06 16:43:48 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2016-01-05 14:36:24 +01:00
2024-05-24 07:30:16 +02:00
ScannedDirectory sd ;
sd . name = f ;
sd . full_path = dir_path ;
2016-01-05 14:36:24 +01:00
2024-05-24 07:30:16 +02:00
EditorFileSystemDirectory * efd = memnew ( EditorFileSystemDirectory ) ;
2017-03-05 16:44:50 +01:00
efd - > parent = p_dir ;
2017-04-29 17:56:51 +02:00
efd - > name = f ;
2024-05-24 07:30:16 +02:00
2022-03-23 10:08:58 +01:00
Ref < DirAccess > d = DirAccess : : create ( DirAccess : : ACCESS_RESOURCES ) ;
2024-05-24 07:30:16 +02:00
d - > change_dir ( dir_path ) ;
int nb_files_dir = _scan_new_dir ( & sd , d ) ;
p_progress . hi + = nb_files_dir ;
diff_nb_files + = nb_files_dir ;
_process_file_system ( & sd , efd , p_progress ) ;
2016-01-05 14:36:24 +01:00
ItemAction ia ;
2017-03-05 16:44:50 +01:00
ia . action = ItemAction : : ACTION_DIR_ADD ;
ia . dir = p_dir ;
2017-04-29 17:56:51 +02:00
ia . file = f ;
2017-03-05 16:44:50 +01:00
ia . new_dir = efd ;
2016-01-05 14:36:24 +01:00
scan_actions . push_back ( ia ) ;
} else {
2017-03-05 16:44:50 +01:00
p_dir - > subdirs [ idx ] - > verified = true ;
2016-01-05 14:36:24 +01:00
}
} else {
2017-04-29 17:56:51 +02:00
String ext = f . get_extension ( ) . to_lower ( ) ;
2020-05-14 16:41:43 +02:00
if ( ! valid_extensions . has ( ext ) ) {
2016-01-05 14:36:24 +01:00
continue ; //invalid
2020-05-14 16:41:43 +02:00
}
2016-01-05 14:36:24 +01:00
2017-04-29 17:56:51 +02:00
int idx = p_dir - > find_file_index ( f ) ;
2016-01-05 14:36:24 +01:00
2017-03-05 16:44:50 +01:00
if ( idx = = - 1 ) {
2016-01-05 14:36:24 +01:00
//never seen this file, add actition to add it
2017-03-05 16:44:50 +01:00
EditorFileSystemDirectory : : FileInfo * fi = memnew ( EditorFileSystemDirectory : : FileInfo ) ;
2017-04-29 17:56:51 +02:00
fi - > file = f ;
2016-01-05 14:36:24 +01:00
2022-08-30 02:34:01 +02:00
String path = cd . path_join ( fi - > file ) ;
2017-03-05 16:44:50 +01:00
fi - > modified_time = FileAccess : : get_modified_time ( path ) ;
fi - > import_modified_time = 0 ;
fi - > type = ResourceLoader : : get_resource_type ( path ) ;
2023-01-19 19:12:25 +01:00
fi - > resource_script_class = ResourceLoader : : get_resource_script_class ( path ) ;
2021-09-23 23:09:15 +02:00
if ( fi - > type = = " " & & textfile_extensions . has ( ext ) ) {
fi - > type = " TextFile " ;
}
2018-07-29 05:36:43 +02:00
fi - > script_class_name = _get_global_script_class ( fi - > type , path , & fi - > script_class_extends , & fi - > script_class_icon_path ) ;
2022-07-20 23:51:29 +02:00
fi - > import_valid = fi - > type = = " TextFile " ? true : ResourceLoader : : is_import_valid ( path ) ;
2019-04-19 20:54:33 +02:00
fi - > import_group_file = ResourceLoader : : get_import_group_file ( path ) ;
2016-01-05 14:36:24 +01:00
{
ItemAction ia ;
2017-03-05 16:44:50 +01:00
ia . action = ItemAction : : ACTION_FILE_ADD ;
ia . dir = p_dir ;
2017-04-29 17:56:51 +02:00
ia . file = f ;
2017-03-05 16:44:50 +01:00
ia . new_file = fi ;
2016-01-05 14:36:24 +01:00
scan_actions . push_back ( ia ) ;
2017-02-01 13:45:45 +01:00
}
if ( import_extensions . has ( ext ) ) {
//if it can be imported, and it was added, it needs to be reimported
2016-01-05 14:36:24 +01:00
ItemAction ia ;
2017-11-24 17:39:41 +01:00
ia . action = ItemAction : : ACTION_FILE_TEST_REIMPORT ;
2017-03-05 16:44:50 +01:00
ia . dir = p_dir ;
2017-04-29 17:56:51 +02:00
ia . file = f ;
2016-01-05 14:36:24 +01:00
scan_actions . push_back ( ia ) ;
}
2024-05-24 07:30:16 +02:00
diff_nb_files + + ;
2016-01-05 14:36:24 +01:00
} else {
2017-03-05 16:44:50 +01:00
p_dir - > files [ idx ] - > verified = true ;
2016-01-05 14:36:24 +01:00
}
}
}
2016-06-28 14:47:03 +02:00
2016-01-05 14:36:24 +01:00
da - > list_dir_end ( ) ;
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < p_dir - > files . size ( ) ; i + + ) {
2016-01-05 14:36:24 +01:00
if ( updated_dir & & ! p_dir - > files [ i ] - > verified ) {
//this file was removed, add action to remove it
ItemAction ia ;
2017-03-05 16:44:50 +01:00
ia . action = ItemAction : : ACTION_FILE_REMOVE ;
ia . dir = p_dir ;
ia . file = p_dir - > files [ i ] - > file ;
2016-01-05 14:36:24 +01:00
scan_actions . push_back ( ia ) ;
2024-05-24 07:30:16 +02:00
diff_nb_files - - ;
2016-01-05 14:36:24 +01:00
continue ;
}
2016-07-03 18:15:15 +02:00
2022-08-30 02:34:01 +02:00
String path = cd . path_join ( p_dir - > files [ i ] - > file ) ;
2018-11-21 01:47:48 +01:00
2017-02-01 13:45:45 +01:00
if ( import_extensions . has ( p_dir - > files [ i ] - > file . get_extension ( ) . to_lower ( ) ) ) {
//check here if file must be imported or not
uint64_t mt = FileAccess : : get_modified_time ( path ) ;
2017-03-05 16:44:50 +01:00
bool reimport = false ;
2017-02-01 13:45:45 +01:00
2017-03-05 16:44:50 +01:00
if ( mt ! = p_dir - > files [ i ] - > modified_time ) {
reimport = true ; //it was modified, must be reimported.
} else if ( ! FileAccess : : exists ( path + " .import " ) ) {
reimport = true ; //no .import file, obviously reimport
2017-02-01 13:45:45 +01:00
} else {
2017-03-05 16:44:50 +01:00
uint64_t import_mt = FileAccess : : get_modified_time ( path + " .import " ) ;
if ( import_mt ! = p_dir - > files [ i ] - > import_modified_time ) {
reimport = true ;
2017-11-24 17:39:41 +01:00
} else if ( _test_for_reimport ( path , true ) ) {
2017-03-05 16:44:50 +01:00
reimport = true ;
2017-02-01 13:45:45 +01:00
}
}
if ( reimport ) {
ItemAction ia ;
2017-11-24 17:39:41 +01:00
ia . action = ItemAction : : ACTION_FILE_TEST_REIMPORT ;
2017-03-05 16:44:50 +01:00
ia . dir = p_dir ;
ia . file = p_dir - > files [ i ] - > file ;
2017-02-01 13:45:45 +01:00
scan_actions . push_back ( ia ) ;
}
2018-11-21 01:47:48 +01:00
} else if ( ResourceCache : : has ( path ) ) { //test for potential reload
uint64_t mt = FileAccess : : get_modified_time ( path ) ;
if ( mt ! = p_dir - > files [ i ] - > modified_time ) {
p_dir - > files [ i ] - > modified_time = mt ; //save new time, but test for reload
ItemAction ia ;
ia . action = ItemAction : : ACTION_FILE_RELOAD ;
ia . dir = p_dir ;
ia . file = p_dir - > files [ i ] - > file ;
scan_actions . push_back ( ia ) ;
}
2014-02-10 02:10:30 +01:00
}
2024-05-24 07:30:16 +02:00
p_progress . increment ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < p_dir - > subdirs . size ( ) ; i + + ) {
2021-10-08 15:15:05 +02:00
if ( ( updated_dir & & ! p_dir - > subdirs [ i ] - > verified ) | | _should_skip_directory ( p_dir - > subdirs [ i ] - > get_path ( ) ) ) {
2024-05-24 07:30:16 +02:00
// Add all the files of the folder to be sure _update_scan_actions process the removed files
// for global class names.
diff_nb_files + = _insert_actions_delete_files_directory ( p_dir - > subdirs [ i ] ) ;
2021-10-08 15:15:05 +02:00
//this directory was removed or ignored, add action to remove it
2016-01-05 14:36:24 +01:00
ItemAction ia ;
2017-03-05 16:44:50 +01:00
ia . action = ItemAction : : ACTION_DIR_REMOVE ;
ia . dir = p_dir - > subdirs [ i ] ;
2016-01-05 14:36:24 +01:00
scan_actions . push_back ( ia ) ;
continue ;
}
2017-03-05 16:44:50 +01:00
_scan_fs_changes ( p_dir - > get_subdir ( i ) , p_progress ) ;
2014-02-10 02:10:30 +01:00
}
2024-05-24 07:30:16 +02:00
nb_files_total = MAX ( nb_files_total + diff_nb_files , 0 ) ;
2014-02-10 02:10:30 +01:00
}
2024-02-15 17:25:58 +01:00
void EditorFileSystem : : _delete_internal_files ( const String & p_file ) {
2017-08-15 01:13:14 +02:00
if ( FileAccess : : exists ( p_file + " .import " ) ) {
List < String > paths ;
ResourceFormatImporter : : get_singleton ( ) - > get_internal_resource_path_list ( p_file , & paths ) ;
2022-03-23 10:08:58 +01:00
Ref < DirAccess > da = DirAccess : : create ( DirAccess : : ACCESS_RESOURCES ) ;
2021-07-24 15:46:25 +02:00
for ( const String & E : paths ) {
2021-07-16 05:45:57 +02:00
da - > remove ( E ) ;
2017-08-15 01:13:14 +02:00
}
da - > remove ( p_file + " .import " ) ;
}
}
2024-05-24 07:30:16 +02:00
int EditorFileSystem : : _insert_actions_delete_files_directory ( EditorFileSystemDirectory * p_dir ) {
int nb_files = 0 ;
for ( EditorFileSystemDirectory : : FileInfo * fi : p_dir - > files ) {
ItemAction ia ;
ia . action = ItemAction : : ACTION_FILE_REMOVE ;
ia . dir = p_dir ;
ia . file = fi - > file ;
scan_actions . push_back ( ia ) ;
nb_files + + ;
}
for ( EditorFileSystemDirectory * sub_dir : p_dir - > subdirs ) {
nb_files + = _insert_actions_delete_files_directory ( sub_dir ) ;
}
return nb_files ;
}
2014-02-10 02:10:30 +01:00
void EditorFileSystem : : _thread_func_sources ( void * _userdata ) {
2017-03-05 16:44:50 +01:00
EditorFileSystem * efs = ( EditorFileSystem * ) _userdata ;
2014-02-10 02:10:30 +01:00
if ( efs - > filesystem ) {
2017-03-05 16:44:50 +01:00
EditorProgressBG pr ( " sources " , TTR ( " ScanSources " ) , 1000 ) ;
2016-01-05 14:36:24 +01:00
ScanProgress sp ;
2017-03-05 16:44:50 +01:00
sp . progress = & pr ;
2024-05-24 07:30:16 +02:00
sp . hi = efs - > nb_files_total ;
2017-03-05 16:44:50 +01:00
efs - > _scan_fs_changes ( efs - > filesystem , sp ) ;
2014-02-10 02:10:30 +01:00
}
2024-02-09 13:47:13 +01:00
efs - > scanning_changes_done . set ( ) ;
2014-02-10 02:10:30 +01:00
}
2024-05-24 07:30:16 +02:00
void EditorFileSystem : : _remove_invalid_global_class_names ( const HashSet < String > & p_existing_class_names ) {
List < StringName > global_classes ;
ScriptServer : : get_global_class_list ( & global_classes ) ;
for ( const StringName & class_name : global_classes ) {
if ( ! p_existing_class_names . has ( class_name ) ) {
ScriptServer : : remove_global_class ( class_name ) ;
}
}
}
String EditorFileSystem : : _get_file_by_class_name ( EditorFileSystemDirectory * p_dir , const String & p_class_name , EditorFileSystemDirectory : : FileInfo * & r_file_info ) {
for ( EditorFileSystemDirectory : : FileInfo * fi : p_dir - > files ) {
if ( fi - > script_class_name = = p_class_name ) {
r_file_info = fi ;
return p_dir - > get_path ( ) . path_join ( fi - > file ) ;
}
}
for ( EditorFileSystemDirectory * sub_dir : p_dir - > subdirs ) {
String file = _get_file_by_class_name ( sub_dir , p_class_name , r_file_info ) ;
if ( ! file . is_empty ( ) ) {
return file ;
}
}
r_file_info = nullptr ;
return " " ;
}
2017-02-01 13:45:45 +01:00
void EditorFileSystem : : scan_changes ( ) {
2020-02-10 09:19:10 +01:00
if ( first_scan | | // Prevent a premature changes scan from inhibiting the first full scan
2021-01-19 13:29:41 +01:00
scanning | | scanning_changes | | thread . is_started ( ) ) {
2020-02-10 09:19:10 +01:00
scan_changes_pending = true ;
set_process ( true ) ;
2014-02-10 02:10:30 +01:00
return ;
2020-02-10 09:19:10 +01:00
}
2014-02-10 02:10:30 +01:00
2017-02-01 13:45:45 +01:00
_update_extensions ( ) ;
2014-02-10 02:10:30 +01:00
sources_changed . clear ( ) ;
2017-03-05 16:44:50 +01:00
scanning_changes = true ;
2024-02-09 13:47:13 +01:00
scanning_changes_done . clear ( ) ;
2014-02-10 02:10:30 +01:00
if ( ! use_threads ) {
2016-01-05 14:36:24 +01:00
if ( filesystem ) {
2017-03-05 16:44:50 +01:00
EditorProgressBG pr ( " sources " , TTR ( " ScanSources " ) , 1000 ) ;
2016-01-05 14:36:24 +01:00
ScanProgress sp ;
2017-03-05 16:44:50 +01:00
sp . progress = & pr ;
2024-05-24 07:30:16 +02:00
sp . hi = nb_files_total ;
2017-03-05 16:44:50 +01:00
scan_total = 0 ;
_scan_fs_changes ( filesystem , sp ) ;
2024-05-24 07:30:16 +02:00
if ( _update_scan_actions ( ) ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " filesystem_changed " ) ) ;
2020-05-14 16:41:43 +02:00
}
2016-01-05 14:36:24 +01:00
}
2017-03-05 16:44:50 +01:00
scanning_changes = false ;
2024-02-09 13:47:13 +01:00
scanning_changes_done . set ( ) ;
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " sources_changed " ) , sources_changed . size ( ) > 0 ) ;
2014-02-10 02:10:30 +01:00
} else {
2021-01-19 13:29:41 +01:00
ERR_FAIL_COND ( thread_sources . is_started ( ) ) ;
2014-02-10 02:10:30 +01:00
set_process ( true ) ;
2017-03-05 16:44:50 +01:00
scan_total = 0 ;
2014-02-10 02:10:30 +01:00
Thread : : Settings s ;
2017-03-05 16:44:50 +01:00
s . priority = Thread : : PRIORITY_LOW ;
2021-01-19 13:29:41 +01:00
thread_sources . start ( _thread_func_sources , this , s ) ;
2014-02-10 02:10:30 +01:00
}
}
void EditorFileSystem : : _notification ( int p_what ) {
2017-03-05 16:44:50 +01:00
switch ( p_what ) {
2014-11-06 01:20:42 +01:00
case NOTIFICATION_EXIT_TREE : {
2021-01-19 13:29:41 +01:00
Thread & active_thread = thread . is_started ( ) ? thread : thread_sources ;
if ( use_threads & & active_thread . is_started ( ) ) {
2017-03-05 16:44:50 +01:00
while ( scanning ) {
2014-02-10 02:10:30 +01:00
OS : : get_singleton ( ) - > delay_usec ( 1000 ) ;
}
2021-01-19 13:29:41 +01:00
active_thread . wait_to_finish ( ) ;
2019-11-07 09:44:15 +01:00
WARN_PRINT ( " Scan thread aborted... " ) ;
2014-02-10 02:10:30 +01:00
set_process ( false ) ;
}
2020-05-14 16:41:43 +02:00
if ( filesystem ) {
2014-02-10 02:10:30 +01:00
memdelete ( filesystem ) ;
2020-05-14 16:41:43 +02:00
}
if ( new_filesystem ) {
2016-01-05 14:36:24 +01:00
memdelete ( new_filesystem ) ;
2020-05-14 16:41:43 +02:00
}
2020-04-02 01:20:12 +02:00
filesystem = nullptr ;
new_filesystem = nullptr ;
2014-02-10 02:10:30 +01:00
} break ;
2022-02-16 00:52:32 +01:00
2014-02-10 02:10:30 +01:00
case NOTIFICATION_PROCESS : {
if ( use_threads ) {
2023-02-13 11:54:32 +01:00
/** This hack exists because of the EditorProgress nature
* of processing events recursively . This needs to be rewritten
* at some point entirely , but in the meantime the following
* hack prevents deadlock on import .
*/
static bool prevent_recursive_process_hack = false ;
if ( prevent_recursive_process_hack ) {
break ;
}
prevent_recursive_process_hack = true ;
bool done_importing = false ;
2017-02-01 13:45:45 +01:00
if ( scanning_changes ) {
2024-02-09 13:47:13 +01:00
if ( scanning_changes_done . is_set ( ) ) {
2014-02-10 02:10:30 +01:00
set_process ( false ) ;
2023-04-27 18:34:30 +02:00
if ( thread_sources . is_started ( ) ) {
thread_sources . wait_to_finish ( ) ;
}
2023-01-19 13:52:15 +01:00
bool changed = _update_scan_actions ( ) ;
2024-05-24 07:30:16 +02:00
// Set first_scan to false before the signals so the function doing_first_scan can return false
// in editor_node to start the export if needed.
first_scan = false ;
2023-01-19 13:52:15 +01:00
if ( changed ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " filesystem_changed " ) ) ;
2020-05-14 16:41:43 +02:00
}
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " sources_changed " ) , sources_changed . size ( ) > 0 ) ;
2023-02-13 11:54:32 +01:00
scanning_changes = false ; // Changed to false here to prevent recursive triggering of scan thread.
done_importing = true ;
2014-02-10 02:10:30 +01:00
}
2021-01-19 13:29:41 +01:00
} else if ( ! scanning & & thread . is_started ( ) ) {
2014-02-10 02:10:30 +01:00
set_process ( false ) ;
2020-05-14 16:41:43 +02:00
if ( filesystem ) {
2014-02-10 02:10:30 +01:00
memdelete ( filesystem ) ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
filesystem = new_filesystem ;
2020-04-02 01:20:12 +02:00
new_filesystem = nullptr ;
2021-01-19 13:29:41 +01:00
thread . wait_to_finish ( ) ;
2016-01-05 14:36:24 +01:00
_update_scan_actions ( ) ;
2024-06-14 06:13:45 +02:00
// Update all icons so they are loaded for the FileSystemDock.
_update_files_icon_path ( ) ;
2024-05-24 07:30:16 +02:00
// Set first_scan to false before the signals so the function doing_first_scan can return false
// in editor_node to start the export if needed.
first_scan = false ;
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " filesystem_changed " ) ) ;
emit_signal ( SNAME ( " sources_changed " ) , sources_changed . size ( ) > 0 ) ;
2014-02-10 02:10:30 +01:00
}
2020-02-10 09:19:10 +01:00
2023-02-13 11:54:32 +01:00
if ( done_importing & & scan_changes_pending ) {
2020-02-10 09:19:10 +01:00
scan_changes_pending = false ;
scan_changes ( ) ;
}
2023-02-13 11:54:32 +01:00
prevent_recursive_process_hack = false ;
2014-02-10 02:10:30 +01:00
}
} break ;
}
}
bool EditorFileSystem : : is_scanning ( ) const {
2024-05-24 07:30:16 +02:00
return scanning | | scanning_changes | | first_scan ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 14:29:06 +02:00
2014-02-10 02:10:30 +01:00
float EditorFileSystem : : get_scanning_progress ( ) const {
2016-01-05 14:36:24 +01:00
return scan_total ;
2014-02-10 02:10:30 +01:00
}
EditorFileSystemDirectory * EditorFileSystem : : get_filesystem ( ) {
return filesystem ;
}
2022-03-23 10:08:58 +01:00
void EditorFileSystem : : _save_filesystem_cache ( EditorFileSystemDirectory * p_dir , Ref < FileAccess > p_file ) {
2020-05-14 16:41:43 +02:00
if ( ! p_dir ) {
2014-02-10 02:10:30 +01:00
return ; //none
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
p_file - > store_line ( " :: " + p_dir - > get_path ( ) + " :: " + String : : num ( p_dir - > modified_time ) ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < p_dir - > files . size ( ) ; i + + ) {
2024-03-08 22:51:31 +01:00
const EditorFileSystemDirectory : : FileInfo * file_info = p_dir - > files [ i ] ;
if ( ! file_info - > import_group_file . is_empty ( ) ) {
group_file_cache . insert ( file_info - > import_group_file ) ;
2019-04-19 20:54:33 +02:00
}
2023-01-19 19:12:25 +01:00
2024-03-08 22:51:31 +01:00
String type = file_info - > type ;
if ( file_info - > resource_script_class ) {
type + = " / " + String ( file_info - > resource_script_class ) ;
2015-08-24 01:15:56 +02:00
}
2024-03-08 22:51:31 +01:00
PackedStringArray cache_string ;
cache_string . append ( file_info - > file ) ;
cache_string . append ( type ) ;
cache_string . append ( itos ( file_info - > uid ) ) ;
cache_string . append ( itos ( file_info - > modified_time ) ) ;
cache_string . append ( itos ( file_info - > import_modified_time ) ) ;
cache_string . append ( itos ( file_info - > import_valid ) ) ;
cache_string . append ( file_info - > import_group_file ) ;
cache_string . append ( String ( " <> " ) . join ( { file_info - > script_class_name , file_info - > script_class_extends , file_info - > script_class_icon_path } ) ) ;
cache_string . append ( String ( " <> " ) . join ( file_info - > deps ) ) ;
p_file - > store_line ( String ( " :: " ) . join ( cache_string ) ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < p_dir - > subdirs . size ( ) ; i + + ) {
_save_filesystem_cache ( p_dir - > subdirs [ i ] , p_file ) ;
2014-02-10 02:10:30 +01:00
}
}
2017-03-05 16:44:50 +01:00
bool EditorFileSystem : : _find_file ( const String & p_file , EditorFileSystemDirectory * * r_d , int & r_file_pos ) const {
2014-02-10 02:10:30 +01:00
//todo make faster
2020-05-14 16:41:43 +02:00
if ( ! filesystem | | scanning ) {
2016-01-05 14:36:24 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-07-19 22:00:46 +02:00
String f = ProjectSettings : : get_singleton ( ) - > localize_path ( p_file ) ;
2014-02-10 02:10:30 +01:00
2024-04-05 22:38:48 +02:00
// Note: Only checks if base directory is case sensitive.
Ref < DirAccess > dir = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
bool fs_case_sensitive = dir - > is_case_sensitive ( " res:// " ) ;
2020-05-14 16:41:43 +02:00
if ( ! f . begins_with ( " res:// " ) ) {
2016-01-05 14:36:24 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
f = f . substr ( 6 , f . length ( ) ) ;
f = f . replace ( " \\ " , " / " ) ;
2014-02-10 02:10:30 +01:00
2016-01-05 14:36:24 +01:00
Vector < String > path = f . split ( " / " ) ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( path . size ( ) = = 0 ) {
2016-01-05 14:36:24 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
String file = path [ path . size ( ) - 1 ] ;
path . resize ( path . size ( ) - 1 ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
EditorFileSystemDirectory * fs = filesystem ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < path . size ( ) ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( path [ i ] . begins_with ( " . " ) ) {
2017-02-04 15:12:03 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int idx = - 1 ;
for ( int j = 0 ; j < fs - > get_subdir_count ( ) ; j + + ) {
2024-04-05 22:38:48 +02:00
if ( fs_case_sensitive ) {
if ( fs - > get_subdir ( j ) - > get_name ( ) = = path [ i ] ) {
idx = j ;
break ;
}
} else {
if ( fs - > get_subdir ( j ) - > get_name ( ) . to_lower ( ) = = path [ i ] . to_lower ( ) ) {
idx = j ;
break ;
}
2016-01-05 14:36:24 +01:00
}
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( idx = = - 1 ) {
2024-07-16 14:40:55 +02:00
// Only create a missing directory in memory when it exists on disk.
if ( ! dir - > dir_exists ( fs - > get_path ( ) . path_join ( path [ i ] ) ) ) {
return false ;
}
2017-03-05 16:44:50 +01:00
EditorFileSystemDirectory * efsd = memnew ( EditorFileSystemDirectory ) ;
2017-06-16 22:32:46 +02:00
2017-03-05 16:44:50 +01:00
efsd - > name = path [ i ] ;
2017-06-16 22:32:46 +02:00
efsd - > parent = fs ;
2017-03-05 16:44:50 +01:00
int idx2 = 0 ;
for ( int j = 0 ; j < fs - > get_subdir_count ( ) ; j + + ) {
2024-02-02 16:50:23 +01:00
if ( efsd - > name . filenocasecmp_to ( fs - > get_subdir ( j ) - > get_name ( ) ) < 0 ) {
2016-01-05 14:36:24 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2016-01-05 14:36:24 +01:00
idx2 + + ;
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( idx2 = = fs - > get_subdir_count ( ) ) {
2016-01-05 14:36:24 +01:00
fs - > subdirs . push_back ( efsd ) ;
2020-05-14 16:41:43 +02:00
} else {
2017-03-05 16:44:50 +01:00
fs - > subdirs . insert ( idx2 , efsd ) ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
fs = efsd ;
2016-01-05 14:36:24 +01:00
} else {
2017-03-05 16:44:50 +01:00
fs = fs - > get_subdir ( idx ) ;
2016-01-05 14:36:24 +01:00
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
int cpos = - 1 ;
for ( int i = 0 ; i < fs - > files . size ( ) ; i + + ) {
if ( fs - > files [ i ] - > file = = file ) {
cpos = i ;
2016-01-05 14:36:24 +01:00
break ;
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
r_file_pos = cpos ;
* r_d = fs ;
2014-02-10 02:10:30 +01:00
2019-06-26 15:08:25 +02:00
return cpos ! = - 1 ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
String EditorFileSystem : : get_file_type ( const String & p_file ) const {
2020-04-02 01:20:12 +02:00
EditorFileSystemDirectory * fs = nullptr ;
2017-03-05 16:44:50 +01:00
int cpos = - 1 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( ! _find_file ( p_file , & fs , cpos ) ) {
return " " ;
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
return fs - > files [ cpos ] - > type ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
EditorFileSystemDirectory * EditorFileSystem : : find_file ( const String & p_file , int * r_index ) const {
2020-05-14 16:41:43 +02:00
if ( ! filesystem | | scanning ) {
2020-04-02 01:20:12 +02:00
return nullptr ;
2020-05-14 16:41:43 +02:00
}
2016-05-27 19:18:40 +02:00
2020-04-02 01:20:12 +02:00
EditorFileSystemDirectory * fs = nullptr ;
2017-03-05 16:44:50 +01:00
int cpos = - 1 ;
if ( ! _find_file ( p_file , & fs , cpos ) ) {
2020-04-02 01:20:12 +02:00
return nullptr ;
2016-05-27 19:18:40 +02:00
}
2020-05-14 16:41:43 +02:00
if ( r_index ) {
2017-03-05 16:44:50 +01:00
* r_index = cpos ;
2020-05-14 16:41:43 +02:00
}
2016-05-27 19:18:40 +02:00
return fs ;
}
2017-03-05 16:44:50 +01:00
EditorFileSystemDirectory * EditorFileSystem : : get_filesystem_path ( const String & p_path ) {
2020-05-14 16:41:43 +02:00
if ( ! filesystem | | scanning ) {
2020-04-02 01:20:12 +02:00
return nullptr ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-07-19 22:00:46 +02:00
String f = ProjectSettings : : get_singleton ( ) - > localize_path ( p_path ) ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( ! f . begins_with ( " res:// " ) ) {
2020-04-02 01:20:12 +02:00
return nullptr ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
f = f . substr ( 6 , f . length ( ) ) ;
f = f . replace ( " \\ " , " / " ) ;
2021-12-09 10:42:46 +01:00
if ( f . is_empty ( ) ) {
2017-03-05 16:44:50 +01:00
return filesystem ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( f . ends_with ( " / " ) ) {
2017-03-05 16:44:50 +01:00
f = f . substr ( 0 , f . length ( ) - 1 ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
Vector < String > path = f . split ( " / " ) ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( path . size ( ) = = 0 ) {
2020-04-02 01:20:12 +02:00
return nullptr ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
EditorFileSystemDirectory * fs = filesystem ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < path . size ( ) ; i + + ) {
int idx = - 1 ;
for ( int j = 0 ; j < fs - > get_subdir_count ( ) ; j + + ) {
if ( fs - > get_subdir ( j ) - > get_name ( ) = = path [ i ] ) {
idx = j ;
break ;
}
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( idx = = - 1 ) {
2020-04-02 01:20:12 +02:00
return nullptr ;
2017-03-05 16:44:50 +01:00
} else {
fs = fs - > get_subdir ( idx ) ;
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
return fs ;
2014-02-10 02:10:30 +01:00
}
2017-08-17 22:02:43 +02:00
void EditorFileSystem : : _save_late_updated_files ( ) {
//files that already existed, and were modified, need re-scanning for dependencies upon project restart. This is done via saving this special file
2022-08-30 02:34:01 +02:00
String fscache = EditorPaths : : get_singleton ( ) - > get_project_settings_dir ( ) . path_join ( " filesystem_update4 " ) ;
2022-03-23 10:08:58 +01:00
Ref < FileAccess > f = FileAccess : : open ( fscache , FileAccess : : WRITE ) ;
ERR_FAIL_COND_MSG ( f . is_null ( ) , " Cannot create file ' " + fscache + " '. Check user write permissions. " ) ;
2022-05-19 01:43:40 +02:00
for ( const String & E : late_update_files ) {
f - > store_line ( E ) ;
2017-08-17 22:02:43 +02:00
}
}
Vector < String > EditorFileSystem : : _get_dependencies ( const String & p_path ) {
2023-05-20 11:45:02 +02:00
// Avoid error spam on first opening of a not yet imported project by treating the following situation
// as a benign one, not letting the file open error happen: the resource is of an importable type but
// it has not been imported yet.
if ( ResourceFormatImporter : : get_singleton ( ) - > recognize_path ( p_path ) ) {
const String & internal_path = ResourceFormatImporter : : get_singleton ( ) - > get_internal_resource_path ( p_path ) ;
if ( ! internal_path . is_empty ( ) & & ! FileAccess : : exists ( internal_path ) ) { // If path is empty (error), keep the code flow to the error.
return Vector < String > ( ) ;
}
}
2017-08-17 22:02:43 +02:00
List < String > deps ;
ResourceLoader : : get_dependencies ( p_path , & deps ) ;
Vector < String > ret ;
2021-07-24 15:46:25 +02:00
for ( const String & E : deps ) {
2021-07-16 05:45:57 +02:00
ret . push_back ( E ) ;
2017-08-17 22:02:43 +02:00
}
return ret ;
}
2018-07-29 05:36:43 +02:00
String EditorFileSystem : : _get_global_script_class ( const String & p_type , const String & p_path , String * r_extends , String * r_icon_path ) const {
2018-07-16 00:29:00 +02:00
for ( int i = 0 ; i < ScriptServer : : get_language_count ( ) ; i + + ) {
if ( ScriptServer : : get_language ( i ) - > handles_global_class_type ( p_type ) ) {
String global_name ;
String extends ;
2018-07-29 05:36:43 +02:00
String icon_path ;
2018-07-16 00:29:00 +02:00
2018-07-29 05:36:43 +02:00
global_name = ScriptServer : : get_language ( i ) - > get_global_class_name ( p_path , & extends , & icon_path ) ;
2018-07-16 00:29:00 +02:00
* r_extends = extends ;
2018-07-29 05:36:43 +02:00
* r_icon_path = icon_path ;
2018-07-16 00:29:00 +02:00
return global_name ;
}
}
* r_extends = String ( ) ;
2018-07-29 05:36:43 +02:00
* r_icon_path = String ( ) ;
2018-07-16 00:29:00 +02:00
return String ( ) ;
}
2024-06-14 06:13:45 +02:00
void EditorFileSystem : : _update_file_icon_path ( EditorFileSystemDirectory : : FileInfo * file_info ) {
String icon_path ;
if ( file_info - > script_class_icon_path . is_empty ( ) & & ! file_info - > deps . is_empty ( ) ) {
2024-06-30 03:38:04 +02:00
const String & script_dep = file_info - > deps [ 0 ] ; // Assuming the first dependency is a script.
const String & script_path = script_dep . contains ( " :: " ) ? script_dep . get_slice ( " :: " , 2 ) : script_dep ;
2024-06-14 06:13:45 +02:00
if ( ! script_path . is_empty ( ) ) {
String * cached = file_icon_cache . getptr ( script_path ) ;
if ( cached ) {
icon_path = * cached ;
} else {
if ( ClassDB : : is_parent_class ( ResourceLoader : : get_resource_type ( script_path ) , SNAME ( " Script " ) ) ) {
int script_file ;
EditorFileSystemDirectory * efsd = find_file ( script_path , & script_file ) ;
if ( efsd ) {
icon_path = efsd - > files [ script_file ] - > script_class_icon_path ;
}
}
file_icon_cache . insert ( script_path , icon_path ) ;
}
}
}
file_info - > icon_path = icon_path ;
}
void EditorFileSystem : : _update_files_icon_path ( EditorFileSystemDirectory * edp ) {
if ( ! edp ) {
edp = filesystem ;
file_icon_cache . clear ( ) ;
}
for ( EditorFileSystemDirectory * sub_dir : edp - > subdirs ) {
_update_files_icon_path ( sub_dir ) ;
}
for ( EditorFileSystemDirectory : : FileInfo * fi : edp - > files ) {
_update_file_icon_path ( fi ) ;
}
}
2023-01-18 17:32:28 +01:00
void EditorFileSystem : : _update_script_classes ( ) {
2024-05-24 07:30:16 +02:00
if ( update_script_paths . is_empty ( ) ) {
return ;
}
2023-01-18 17:32:28 +01:00
update_script_mutex . lock ( ) ;
2024-05-24 07:30:16 +02:00
for ( const KeyValue < String , ScriptInfo > & E : update_script_paths ) {
_register_global_class_script ( E . key , E . key , E . value . type , E . value . script_class_name , E . value . script_class_extends , E . value . script_class_icon_path ) ;
2023-01-19 13:52:15 +01:00
}
2024-05-24 07:30:16 +02:00
update_script_paths . clear ( ) ;
update_script_mutex . unlock ( ) ;
ScriptServer : : save_global_classes ( ) ;
EditorNode : : get_editor_data ( ) . script_class_save_icon_paths ( ) ;
emit_signal ( " script_classes_updated " ) ;
// Rescan custom loaders and savers.
// Doing the following here because the `filesystem_changed` signal fires multiple times and isn't always followed by script classes update.
// So I thought it's better to do this when script classes really get updated
ResourceLoader : : remove_custom_loaders ( ) ;
ResourceLoader : : add_custom_loaders ( ) ;
ResourceSaver : : remove_custom_savers ( ) ;
ResourceSaver : : add_custom_savers ( ) ;
}
void EditorFileSystem : : _update_script_documentation ( ) {
if ( update_script_paths_documentation . is_empty ( ) ) {
return ;
}
update_script_mutex . lock ( ) ;
for ( const String & path : update_script_paths_documentation ) {
2023-01-19 13:52:15 +01:00
int index = - 1 ;
EditorFileSystemDirectory * efd = find_file ( path , & index ) ;
2018-07-16 00:29:00 +02:00
2023-01-21 04:19:54 +01:00
if ( ! efd | | index < 0 ) {
// The file was removed
continue ;
}
2023-01-18 17:32:28 +01:00
for ( int i = 0 ; i < ScriptServer : : get_language_count ( ) ; i + + ) {
ScriptLanguage * lang = ScriptServer : : get_language ( i ) ;
if ( lang - > supports_documentation ( ) & & efd - > files [ index ] - > type = = lang - > get_type ( ) ) {
Ref < Script > scr = ResourceLoader : : load ( path ) ;
if ( scr . is_null ( ) ) {
continue ;
}
Vector < DocData : : ClassDoc > docs = scr - > get_documentation ( ) ;
for ( int j = 0 ; j < docs . size ( ) ; j + + ) {
EditorHelp : : get_doc_data ( ) - > add_doc ( docs [ j ] ) ;
}
}
}
2020-05-14 16:41:43 +02:00
}
2018-07-16 00:29:00 +02:00
2024-05-24 07:30:16 +02:00
update_script_paths_documentation . clear ( ) ;
2023-01-18 17:32:28 +01:00
update_script_mutex . unlock ( ) ;
2018-07-16 00:29:00 +02:00
}
2024-05-24 07:30:16 +02:00
void EditorFileSystem : : _process_update_pending ( ) {
_update_script_classes ( ) ;
// Parse documentation second, as it requires the class names to be loaded
// because _update_script_documentation loads the scripts completely.
_update_script_documentation ( ) ;
_update_pending_scene_groups ( ) ;
2023-01-19 13:52:15 +01:00
}
2024-05-24 07:30:16 +02:00
void EditorFileSystem : : _queue_update_script_class ( const String & p_path , const String & p_type , const String & p_script_class_name , const String & p_script_class_extends , const String & p_script_class_icon_path ) {
2023-01-18 17:32:28 +01:00
update_script_mutex . lock ( ) ;
2024-05-24 07:30:16 +02:00
ScriptInfo si ;
si . type = p_type ;
si . script_class_name = p_script_class_name ;
si . script_class_extends = p_script_class_extends ;
si . script_class_icon_path = p_script_class_icon_path ;
update_script_paths . insert ( p_path , si ) ;
update_script_paths_documentation . insert ( p_path ) ;
2023-01-18 17:32:28 +01:00
update_script_mutex . unlock ( ) ;
2018-07-16 00:29:00 +02:00
}
2022-05-12 10:20:12 +02:00
void EditorFileSystem : : _update_scene_groups ( ) {
2024-05-24 07:30:16 +02:00
if ( update_scene_paths . is_empty ( ) ) {
return ;
}
2024-04-17 10:59:53 +02:00
EditorProgress * ep = nullptr ;
2024-07-21 19:45:09 +02:00
if ( update_scene_paths . size ( ) > 20 ) {
2024-04-17 10:59:53 +02:00
ep = memnew ( EditorProgress ( " update_scene_groups " , TTR ( " Update Scene Groups " ) , update_scene_paths . size ( ) ) ) ;
}
2024-03-21 12:20:12 +01:00
int step_count = 0 ;
2022-05-12 10:20:12 +02:00
2024-03-21 12:20:12 +01:00
update_scene_mutex . lock ( ) ;
2022-05-12 10:20:12 +02:00
for ( const String & path : update_scene_paths ) {
ProjectSettings : : get_singleton ( ) - > remove_scene_groups_cache ( path ) ;
int index = - 1 ;
EditorFileSystemDirectory * efd = find_file ( path , & index ) ;
if ( ! efd | | index < 0 ) {
// The file was removed.
continue ;
}
2024-06-29 00:40:34 +02:00
const HashSet < StringName > scene_groups = PackedScene : : get_scene_groups ( path ) ;
2022-05-12 10:20:12 +02:00
if ( ! scene_groups . is_empty ( ) ) {
ProjectSettings : : get_singleton ( ) - > add_scene_groups_cache ( path , scene_groups ) ;
}
2024-03-21 12:20:12 +01:00
2024-04-17 10:59:53 +02:00
if ( ep ) {
ep - > step ( TTR ( " Updating Scene Groups... " ) , step_count + + ) ;
}
2022-05-12 10:20:12 +02:00
}
2024-04-17 10:59:53 +02:00
memdelete_notnull ( ep ) ;
2022-05-12 10:20:12 +02:00
update_scene_paths . clear ( ) ;
update_scene_mutex . unlock ( ) ;
ProjectSettings : : get_singleton ( ) - > save_scene_groups_cache ( ) ;
}
void EditorFileSystem : : _update_pending_scene_groups ( ) {
if ( ! FileAccess : : exists ( ProjectSettings : : get_singleton ( ) - > get_scene_groups_cache_path ( ) ) ) {
_get_all_scenes ( get_filesystem ( ) , update_scene_paths ) ;
_update_scene_groups ( ) ;
} else if ( ! update_scene_paths . is_empty ( ) ) {
_update_scene_groups ( ) ;
}
}
void EditorFileSystem : : _queue_update_scene_groups ( const String & p_path ) {
update_scene_mutex . lock ( ) ;
update_scene_paths . insert ( p_path ) ;
update_scene_mutex . unlock ( ) ;
}
void EditorFileSystem : : _get_all_scenes ( EditorFileSystemDirectory * p_dir , HashSet < String > & r_list ) {
for ( int i = 0 ; i < p_dir - > get_file_count ( ) ; i + + ) {
if ( p_dir - > get_file_type ( i ) = = SNAME ( " PackedScene " ) ) {
r_list . insert ( p_dir - > get_file_path ( i ) ) ;
}
}
for ( int i = 0 ; i < p_dir - > get_subdir_count ( ) ; i + + ) {
_get_all_scenes ( p_dir - > get_subdir ( i ) , r_list ) ;
}
}
2017-03-05 16:44:50 +01:00
void EditorFileSystem : : update_file ( const String & p_file ) {
2023-07-07 20:51:46 +02:00
ERR_FAIL_COND ( p_file . is_empty ( ) ) ;
2024-06-08 04:54:47 +02:00
update_files ( { p_file } ) ;
}
2014-11-12 15:23:23 +01:00
2024-06-08 04:54:47 +02:00
void EditorFileSystem : : update_files ( const Vector < String > & p_script_paths ) {
2024-06-14 15:00:42 +02:00
bool updated = false ;
2024-06-14 06:13:45 +02:00
bool update_files_icon_cache = false ;
Vector < EditorFileSystemDirectory : : FileInfo * > files_to_update_icon_path ;
2024-06-08 04:54:47 +02:00
for ( const String & file : p_script_paths ) {
ERR_CONTINUE ( file . is_empty ( ) ) ;
EditorFileSystemDirectory * fs = nullptr ;
int cpos = - 1 ;
if ( ! _find_file ( file , & fs , cpos ) ) {
if ( ! fs ) {
2024-06-11 02:03:18 +02:00
continue ;
2024-06-08 04:54:47 +02:00
}
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2024-06-08 04:54:47 +02:00
if ( ! FileAccess : : exists ( file ) ) {
//was removed
_delete_internal_files ( file ) ;
if ( cpos ! = - 1 ) { // Might've never been part of the editor file system (*.* files deleted in Open dialog).
if ( fs - > files [ cpos ] - > uid ! = ResourceUID : : INVALID_ID ) {
if ( ResourceUID : : get_singleton ( ) - > has_id ( fs - > files [ cpos ] - > uid ) ) {
ResourceUID : : get_singleton ( ) - > remove_id ( fs - > files [ cpos ] - > uid ) ;
}
2021-07-23 21:01:18 +02:00
}
2024-06-08 04:54:47 +02:00
if ( ClassDB : : is_parent_class ( fs - > files [ cpos ] - > type , SNAME ( " Script " ) ) ) {
2024-05-24 07:30:16 +02:00
_queue_update_script_class ( file , fs - > files [ cpos ] - > type , " " , " " , " " ) ;
2024-06-14 06:13:45 +02:00
if ( ! fs - > files [ cpos ] - > script_class_icon_path . is_empty ( ) ) {
update_files_icon_cache = true ;
}
2024-06-08 04:54:47 +02:00
}
if ( fs - > files [ cpos ] - > type = = SNAME ( " PackedScene " ) ) {
_queue_update_scene_groups ( file ) ;
}
memdelete ( fs - > files [ cpos ] ) ;
fs - > files . remove_at ( cpos ) ;
2024-06-14 15:00:42 +02:00
updated = true ;
2022-05-12 10:20:12 +02:00
}
2024-06-11 02:03:18 +02:00
} else {
String type = ResourceLoader : : get_resource_type ( file ) ;
if ( type . is_empty ( ) & & textfile_extensions . has ( file . get_extension ( ) ) ) {
type = " TextFile " ;
}
String script_class = ResourceLoader : : get_resource_script_class ( file ) ;
2023-01-18 17:32:28 +01:00
2024-06-11 02:03:18 +02:00
ResourceUID : : ID uid = ResourceLoader : : get_resource_uid ( file ) ;
2014-02-10 02:10:30 +01:00
2024-06-11 02:03:18 +02:00
if ( cpos = = - 1 ) {
// The file did not exist, it was added.
int idx = 0 ;
String file_name = file . get_file ( ) ;
2014-02-10 02:10:30 +01:00
2024-06-11 02:03:18 +02:00
for ( int i = 0 ; i < fs - > files . size ( ) ; i + + ) {
if ( file . filenocasecmp_to ( fs - > files [ i ] - > file ) < 0 ) {
break ;
}
idx + + ;
2024-06-08 04:54:47 +02:00
}
2014-02-10 02:10:30 +01:00
2024-06-11 02:03:18 +02:00
EditorFileSystemDirectory : : FileInfo * fi = memnew ( EditorFileSystemDirectory : : FileInfo ) ;
fi - > file = file_name ;
fi - > import_modified_time = 0 ;
fi - > import_valid = type = = " TextFile " ? true : ResourceLoader : : is_import_valid ( file ) ;
2014-02-10 02:10:30 +01:00
2024-06-11 02:03:18 +02:00
if ( idx = = fs - > files . size ( ) ) {
fs - > files . push_back ( fi ) ;
} else {
fs - > files . insert ( idx , fi ) ;
}
cpos = idx ;
2024-06-08 04:54:47 +02:00
} else {
2024-06-11 02:03:18 +02:00
//the file exists and it was updated, and was not added in this step.
//this means we must force upon next restart to scan it again, to get proper type and dependencies
late_update_files . insert ( file ) ;
_save_late_updated_files ( ) ; //files need to be updated in the re-scan
2024-06-08 04:54:47 +02:00
}
2021-07-23 21:01:18 +02:00
2024-06-14 06:13:45 +02:00
const String old_script_class_icon_path = fs - > files [ cpos ] - > script_class_icon_path ;
2024-05-24 07:30:16 +02:00
const String old_class_name = fs - > files [ cpos ] - > script_class_name ;
2024-06-11 02:03:18 +02:00
fs - > files [ cpos ] - > type = type ;
fs - > files [ cpos ] - > resource_script_class = script_class ;
fs - > files [ cpos ] - > uid = uid ;
fs - > files [ cpos ] - > script_class_name = _get_global_script_class ( type , file , & fs - > files [ cpos ] - > script_class_extends , & fs - > files [ cpos ] - > script_class_icon_path ) ;
fs - > files [ cpos ] - > import_group_file = ResourceLoader : : get_import_group_file ( file ) ;
fs - > files [ cpos ] - > modified_time = FileAccess : : get_modified_time ( file ) ;
fs - > files [ cpos ] - > deps = _get_dependencies ( file ) ;
fs - > files [ cpos ] - > import_valid = type = = " TextFile " ? true : ResourceLoader : : is_import_valid ( file ) ;
if ( uid ! = ResourceUID : : INVALID_ID ) {
if ( ResourceUID : : get_singleton ( ) - > has_id ( uid ) ) {
ResourceUID : : get_singleton ( ) - > set_id ( uid , file ) ;
} else {
ResourceUID : : get_singleton ( ) - > add_id ( uid , file ) ;
}
2018-03-10 14:21:17 +01:00
2024-06-11 02:03:18 +02:00
ResourceUID : : get_singleton ( ) - > update_cache ( ) ;
2024-06-08 04:54:47 +02:00
}
2024-06-11 02:03:18 +02:00
// Update preview
EditorResourcePreview : : get_singleton ( ) - > check_for_invalidation ( file ) ;
2024-06-08 04:54:47 +02:00
2024-06-11 02:03:18 +02:00
if ( ClassDB : : is_parent_class ( fs - > files [ cpos ] - > type , SNAME ( " Script " ) ) ) {
2024-05-24 07:30:16 +02:00
_queue_update_script_class ( file , fs - > files [ cpos ] - > type , fs - > files [ cpos ] - > script_class_name , fs - > files [ cpos ] - > script_class_extends , fs - > files [ cpos ] - > script_class_icon_path ) ;
2024-06-11 02:03:18 +02:00
}
if ( fs - > files [ cpos ] - > type = = SNAME ( " PackedScene " ) ) {
_queue_update_scene_groups ( file ) ;
}
2024-05-24 07:30:16 +02:00
2024-06-14 06:13:45 +02:00
if ( fs - > files [ cpos ] - > type = = SNAME ( " Resource " ) ) {
files_to_update_icon_path . push_back ( fs - > files [ cpos ] ) ;
} else if ( old_script_class_icon_path ! = fs - > files [ cpos ] - > script_class_icon_path ) {
update_files_icon_cache = true ;
}
2024-05-24 07:30:16 +02:00
// Restore another script as the global class name if multiple scripts had the same old class name.
if ( ! old_class_name . is_empty ( ) & & fs - > files [ cpos ] - > script_class_name ! = old_class_name & & ClassDB : : is_parent_class ( type , SNAME ( " Script " ) ) ) {
EditorFileSystemDirectory : : FileInfo * old_fi = nullptr ;
String old_file = _get_file_by_class_name ( filesystem , old_class_name , old_fi ) ;
if ( ! old_file . is_empty ( ) & & old_fi ) {
_queue_update_script_class ( old_file , old_fi - > type , old_fi - > script_class_name , old_fi - > script_class_extends , old_fi - > script_class_icon_path ) ;
}
}
2024-06-14 15:00:42 +02:00
updated = true ;
2024-06-08 04:54:47 +02:00
}
2022-05-12 10:20:12 +02:00
}
2023-01-18 17:32:28 +01:00
2024-06-14 15:00:42 +02:00
if ( updated ) {
2024-06-14 06:13:45 +02:00
if ( update_files_icon_cache ) {
_update_files_icon_path ( ) ;
} else {
for ( EditorFileSystemDirectory : : FileInfo * fi : files_to_update_icon_path ) {
_update_file_icon_path ( fi ) ;
}
}
2024-07-04 04:10:05 +02:00
if ( ! is_scanning ( ) ) {
_process_update_pending ( ) ;
call_deferred ( SNAME ( " emit_signal " ) , " filesystem_changed " ) ; //update later
}
2024-06-14 15:00:42 +02:00
}
2014-02-10 02:10:30 +01:00
}
2022-05-19 17:00:06 +02:00
HashSet < String > EditorFileSystem : : get_valid_extensions ( ) const {
2022-01-16 19:08:40 +01:00
return valid_extensions ;
}
2024-05-24 07:30:16 +02:00
void EditorFileSystem : : _register_global_class_script ( const String & p_search_path , const String & p_target_path , const String & p_type , const String & p_script_class_name , const String & p_script_class_extends , const String & p_script_class_icon_path ) {
2024-05-01 00:26:42 +02:00
ScriptServer : : remove_global_class_by_path ( p_search_path ) ; // First remove, just in case it changed
2024-05-24 07:30:16 +02:00
if ( p_script_class_name . is_empty ( ) ) {
2024-05-01 00:26:42 +02:00
return ;
}
2024-05-24 07:30:16 +02:00
String lang ;
for ( int j = 0 ; j < ScriptServer : : get_language_count ( ) ; j + + ) {
if ( ScriptServer : : get_language ( j ) - > handles_global_class_type ( p_type ) ) {
lang = ScriptServer : : get_language ( j ) - > get_name ( ) ;
break ;
2024-05-01 00:26:42 +02:00
}
2024-05-24 07:30:16 +02:00
}
if ( lang . is_empty ( ) ) {
return ; // No lang found that can handle this global class
}
2024-05-01 00:26:42 +02:00
2024-05-24 07:30:16 +02:00
ScriptServer : : add_global_class ( p_script_class_name , p_script_class_extends , lang , p_target_path ) ;
EditorNode : : get_editor_data ( ) . script_class_set_icon_path ( p_script_class_name , p_script_class_icon_path ) ;
EditorNode : : get_editor_data ( ) . script_class_set_name ( p_target_path , p_script_class_name ) ;
}
void EditorFileSystem : : register_global_class_script ( const String & p_search_path , const String & p_target_path ) {
int index_file ;
EditorFileSystemDirectory * efsd = find_file ( p_search_path , & index_file ) ;
if ( efsd ) {
const EditorFileSystemDirectory : : FileInfo * fi = efsd - > files [ index_file ] ;
EditorFileSystem : : get_singleton ( ) - > _register_global_class_script ( p_search_path , p_target_path , fi - > type , fi - > script_class_name , fi - > script_class_extends , fi - > script_class_icon_path ) ;
} else {
ScriptServer : : remove_global_class_by_path ( p_search_path ) ;
2024-05-01 00:26:42 +02:00
}
}
2019-04-19 20:54:33 +02:00
Error EditorFileSystem : : _reimport_group ( const String & p_group_file , const Vector < String > & p_files ) {
String importer_name ;
2022-05-13 15:04:37 +02:00
HashMap < String , HashMap < StringName , Variant > > source_file_options ;
2022-11-05 20:05:47 +01:00
HashMap < String , ResourceUID : : ID > uids ;
2022-05-13 15:04:37 +02:00
HashMap < String , String > base_paths ;
2019-04-19 20:54:33 +02:00
for ( int i = 0 ; i < p_files . size ( ) ; i + + ) {
Ref < ConfigFile > config ;
2021-06-18 00:03:09 +02:00
config . instantiate ( ) ;
2019-04-19 20:54:33 +02:00
Error err = config - > load ( p_files [ i ] + " .import " ) ;
ERR_CONTINUE ( err ! = OK ) ;
ERR_CONTINUE ( ! config - > has_section_key ( " remap " , " importer " ) ) ;
String file_importer_name = config - > get_value ( " remap " , " importer " ) ;
2021-12-09 10:42:46 +01:00
ERR_CONTINUE ( file_importer_name . is_empty ( ) ) ;
2019-04-19 20:54:33 +02:00
2021-12-09 10:42:46 +01:00
if ( ! importer_name . is_empty ( ) & & importer_name ! = file_importer_name ) {
2019-04-19 20:54:33 +02:00
EditorNode : : get_singleton ( ) - > show_warning ( vformat ( TTR ( " There are multiple importers for different types pointing to file %s, import aborted " ) , p_group_file ) ) ;
ERR_FAIL_V ( ERR_FILE_CORRUPT ) ;
}
2022-11-05 20:05:47 +01:00
ResourceUID : : ID uid = ResourceUID : : INVALID_ID ;
if ( config - > has_section_key ( " remap " , " uid " ) ) {
String uidt = config - > get_value ( " remap " , " uid " ) ;
uid = ResourceUID : : get_singleton ( ) - > text_to_id ( uidt ) ;
}
uids [ p_files [ i ] ] = uid ;
2022-05-13 15:04:37 +02:00
source_file_options [ p_files [ i ] ] = HashMap < StringName , Variant > ( ) ;
2019-04-19 20:54:33 +02:00
importer_name = file_importer_name ;
2024-02-05 11:10:37 +01:00
if ( importer_name = = " keep " | | importer_name = = " skip " ) {
2021-03-22 20:41:47 +01:00
continue ; //do nothing
}
2019-04-19 20:54:33 +02:00
Ref < ResourceImporter > importer = ResourceFormatImporter : : get_singleton ( ) - > get_importer_by_name ( importer_name ) ;
ERR_FAIL_COND_V ( ! importer . is_valid ( ) , ERR_FILE_CORRUPT ) ;
List < ResourceImporter : : ImportOption > options ;
2021-11-14 18:02:38 +01:00
importer - > get_import_options ( p_files [ i ] , & options ) ;
2019-04-19 20:54:33 +02:00
//set default values
2021-07-24 15:46:25 +02:00
for ( const ResourceImporter : : ImportOption & E : options ) {
2021-07-16 05:45:57 +02:00
source_file_options [ p_files [ i ] ] [ E . option . name ] = E . default_value ;
2019-04-19 20:54:33 +02:00
}
if ( config - > has_section ( " params " ) ) {
List < String > sk ;
config - > get_section_keys ( " params " , & sk ) ;
2021-07-24 15:46:25 +02:00
for ( const String & param : sk ) {
2019-04-19 20:54:33 +02:00
Variant value = config - > get_value ( " params " , param ) ;
Fix various typos
Found via `codespell -q 3 -S ./thirdparty,*.po,./DONORS.md -L ackward,ang,ans,ba,beng,cas,childs,childrens,dof,doubleclick,expct,fave,findn,gird,hist,inh,inout,leapyear,lod,nd,numer,ois,ony,paket,ro,seeked,sinc,switchs,te,uint,varn,vew`
2022-01-02 19:47:52 +01:00
//override with whatever is in file
2019-04-19 20:54:33 +02:00
source_file_options [ p_files [ i ] ] [ param ] = value ;
}
}
base_paths [ p_files [ i ] ] = ResourceFormatImporter : : get_singleton ( ) - > get_import_base_path ( p_files [ i ] ) ;
}
2024-02-05 11:10:37 +01:00
if ( importer_name = = " keep " | | importer_name = = " skip " ) {
2021-03-22 20:41:47 +01:00
return OK ; // (do nothing)
}
2021-12-09 10:42:46 +01:00
ERR_FAIL_COND_V ( importer_name . is_empty ( ) , ERR_UNCONFIGURED ) ;
2019-04-19 20:54:33 +02:00
Ref < ResourceImporter > importer = ResourceFormatImporter : : get_singleton ( ) - > get_importer_by_name ( importer_name ) ;
Error err = importer - > import_group_file ( p_group_file , source_file_options , base_paths ) ;
//all went well, overwrite config files with proper remaps and md5s
2022-05-13 15:04:37 +02:00
for ( const KeyValue < String , HashMap < StringName , Variant > > & E : source_file_options ) {
2021-08-09 22:13:42 +02:00
const String & file = E . key ;
2019-04-19 20:54:33 +02:00
String base_path = ResourceFormatImporter : : get_singleton ( ) - > get_import_base_path ( file ) ;
Vector < String > dest_paths ;
2022-11-05 20:05:47 +01:00
ResourceUID : : ID uid = uids [ file ] ;
2022-04-12 09:12:40 +02:00
{
Ref < FileAccess > f = FileAccess : : open ( file + " .import " , FileAccess : : WRITE ) ;
ERR_FAIL_COND_V_MSG ( f . is_null ( ) , ERR_FILE_CANT_OPEN , " Cannot open import file ' " + file + " .import'. " ) ;
//write manually, as order matters ([remap] has to go first for performance).
f - > store_line ( " [remap] " ) ;
f - > store_line ( " " ) ;
f - > store_line ( " importer= \" " + importer - > get_importer_name ( ) + " \" " ) ;
int version = importer - > get_format_version ( ) ;
if ( version > 0 ) {
f - > store_line ( " importer_version= " + itos ( version ) ) ;
}
if ( ! importer - > get_resource_type ( ) . is_empty ( ) ) {
f - > store_line ( " type= \" " + importer - > get_resource_type ( ) + " \" " ) ;
}
2019-04-19 20:54:33 +02:00
2022-11-05 20:05:47 +01:00
if ( uid = = ResourceUID : : INVALID_ID ) {
uid = ResourceUID : : get_singleton ( ) - > create_id ( ) ;
}
f - > store_line ( " uid= \" " + ResourceUID : : get_singleton ( ) - > id_to_text ( uid ) + " \" " ) ; // Store in readable format.
2022-04-12 09:12:40 +02:00
if ( err = = OK ) {
String path = base_path + " . " + importer - > get_save_extension ( ) ;
f - > store_line ( " path= \" " + path + " \" " ) ;
dest_paths . push_back ( path ) ;
}
2019-04-19 20:54:33 +02:00
2022-04-12 09:12:40 +02:00
f - > store_line ( " group_file= " + Variant ( p_group_file ) . get_construct_string ( ) ) ;
2019-04-19 20:54:33 +02:00
2022-04-12 09:12:40 +02:00
if ( err = = OK ) {
f - > store_line ( " valid=true " ) ;
} else {
f - > store_line ( " valid=false " ) ;
2019-04-19 20:54:33 +02:00
}
2022-04-12 09:12:40 +02:00
f - > store_line ( " [deps] \n " ) ;
2019-04-19 20:54:33 +02:00
2022-04-12 09:12:40 +02:00
f - > store_line ( " " ) ;
2019-04-19 20:54:33 +02:00
2022-04-12 09:12:40 +02:00
f - > store_line ( " source_file= " + Variant ( file ) . get_construct_string ( ) ) ;
if ( dest_paths . size ( ) ) {
Array dp ;
for ( int i = 0 ; i < dest_paths . size ( ) ; i + + ) {
dp . push_back ( dest_paths [ i ] ) ;
}
f - > store_line ( " dest_files= " + Variant ( dp ) . get_construct_string ( ) + " \n " ) ;
}
f - > store_line ( " [params] " ) ;
f - > store_line ( " " ) ;
//store options in provided order, to avoid file changing. Order is also important because first match is accepted first.
List < ResourceImporter : : ImportOption > options ;
importer - > get_import_options ( file , & options ) ;
//set default values
for ( const ResourceImporter : : ImportOption & F : options ) {
String base = F . option . name ;
Variant v = F . default_value ;
if ( source_file_options [ file ] . has ( base ) ) {
v = source_file_options [ file ] [ base ] ;
}
String value ;
VariantWriter : : write_to_string ( v , value ) ;
f - > store_line ( base + " = " + value ) ;
2019-04-19 20:54:33 +02:00
}
}
// Store the md5's of the various files. These are stored separately so that the .import files can be version controlled.
2022-04-12 09:12:40 +02:00
{
Ref < FileAccess > md5s = FileAccess : : open ( base_path + " .md5 " , FileAccess : : WRITE ) ;
ERR_FAIL_COND_V_MSG ( md5s . is_null ( ) , ERR_FILE_CANT_OPEN , " Cannot open MD5 file ' " + base_path + " .md5'. " ) ;
2019-04-19 20:54:33 +02:00
2022-04-12 09:12:40 +02:00
md5s - > store_line ( " source_md5= \" " + FileAccess : : get_md5 ( file ) + " \" " ) ;
if ( dest_paths . size ( ) ) {
md5s - > store_line ( " dest_md5= \" " + FileAccess : : get_multiple_md5 ( dest_paths ) + " \" \n " ) ;
}
2019-04-19 20:54:33 +02:00
}
2020-04-02 01:20:12 +02:00
EditorFileSystemDirectory * fs = nullptr ;
2019-04-19 20:54:33 +02:00
int cpos = - 1 ;
bool found = _find_file ( file , & fs , cpos ) ;
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_V_MSG ( ! found , ERR_UNCONFIGURED , " Can't find file ' " + file + " '. " ) ;
2019-04-19 20:54:33 +02:00
//update modified times, to avoid reimport
fs - > files [ cpos ] - > modified_time = FileAccess : : get_modified_time ( file ) ;
fs - > files [ cpos ] - > import_modified_time = FileAccess : : get_modified_time ( file + " .import " ) ;
fs - > files [ cpos ] - > deps = _get_dependencies ( file ) ;
2022-11-05 20:05:47 +01:00
fs - > files [ cpos ] - > uid = uid ;
2019-04-19 20:54:33 +02:00
fs - > files [ cpos ] - > type = importer - > get_resource_type ( ) ;
2021-09-23 23:09:15 +02:00
if ( fs - > files [ cpos ] - > type = = " " & & textfile_extensions . has ( file . get_extension ( ) ) ) {
fs - > files [ cpos ] - > type = " TextFile " ;
}
2019-04-19 20:54:33 +02:00
fs - > files [ cpos ] - > import_valid = err = = OK ;
2022-11-05 20:05:47 +01:00
if ( ResourceUID : : get_singleton ( ) - > has_id ( uid ) ) {
ResourceUID : : get_singleton ( ) - > set_id ( uid , file ) ;
} else {
ResourceUID : : get_singleton ( ) - > add_id ( uid , file ) ;
}
2019-04-19 20:54:33 +02:00
//if file is currently up, maybe the source it was loaded from changed, so import math must be updated for it
//to reload properly
2022-06-22 13:46:46 +02:00
Ref < Resource > r = ResourceCache : : get_ref ( file ) ;
2019-04-19 20:54:33 +02:00
2022-06-22 13:46:46 +02:00
if ( r . is_valid ( ) ) {
2021-12-09 10:42:46 +01:00
if ( ! r - > get_import_path ( ) . is_empty ( ) ) {
2019-04-19 20:54:33 +02:00
String dst_path = ResourceFormatImporter : : get_singleton ( ) - > get_internal_resource_path ( file ) ;
r - > set_import_path ( dst_path ) ;
r - > set_import_last_modified_time ( 0 ) ;
}
}
EditorResourcePreview : : get_singleton ( ) - > check_for_invalidation ( file ) ;
}
return err ;
}
2023-02-02 22:41:04 +01:00
Error EditorFileSystem : : _reimport_file ( const String & p_file , const HashMap < StringName , Variant > & p_custom_options , const String & p_custom_importer , Variant * p_generator_parameters ) {
2024-02-27 13:49:39 +01:00
print_verbose ( vformat ( " EditorFileSystem: Importing file: %s " , p_file ) ) ;
uint64_t start_time = OS : : get_singleton ( ) - > get_ticks_msec ( ) ;
2020-04-02 01:20:12 +02:00
EditorFileSystemDirectory * fs = nullptr ;
2017-03-05 16:44:50 +01:00
int cpos = - 1 ;
bool found = _find_file ( p_file , & fs , cpos ) ;
2023-01-31 15:59:52 +01:00
ERR_FAIL_COND_V_MSG ( ! found , ERR_FILE_NOT_FOUND , " Can't find file ' " + p_file + " '. " ) ;
2017-02-01 13:45:45 +01:00
//try to obtain existing params
2023-02-02 22:41:04 +01:00
HashMap < StringName , Variant > params = p_custom_options ;
2021-03-19 13:57:52 +01:00
String importer_name ; //empty by default though
2021-12-09 10:42:46 +01:00
if ( ! p_custom_importer . is_empty ( ) ) {
2021-03-19 13:57:52 +01:00
importer_name = p_custom_importer ;
}
2017-02-01 13:45:45 +01:00
2021-07-23 21:01:18 +02:00
ResourceUID : : ID uid = ResourceUID : : INVALID_ID ;
2023-02-02 22:41:04 +01:00
Variant generator_parameters ;
if ( p_generator_parameters ) {
generator_parameters = * p_generator_parameters ;
}
2021-07-23 21:01:18 +02:00
2017-03-05 16:44:50 +01:00
if ( FileAccess : : exists ( p_file + " .import " ) ) {
2017-07-23 23:48:05 +02:00
//use existing
2023-02-02 22:41:04 +01:00
Ref < ConfigFile > cf ;
cf . instantiate ( ) ;
Error err = cf - > load ( p_file + " .import " ) ;
if ( err = = OK ) {
if ( cf - > has_section ( " params " ) ) {
List < String > sk ;
cf - > get_section_keys ( " params " , & sk ) ;
for ( const String & E : sk ) {
if ( ! params . has ( E ) ) {
2021-07-16 05:45:57 +02:00
params [ E ] = cf - > get_value ( " params " , E ) ;
2021-03-19 13:57:52 +01:00
}
}
2023-02-02 22:41:04 +01:00
}
2021-07-23 21:01:18 +02:00
2023-02-02 22:41:04 +01:00
if ( cf - > has_section ( " remap " ) ) {
if ( p_custom_importer . is_empty ( ) ) {
importer_name = cf - > get_value ( " remap " , " importer " ) ;
}
2021-07-23 21:01:18 +02:00
2023-02-02 22:41:04 +01:00
if ( cf - > has_section_key ( " remap " , " uid " ) ) {
String uidt = cf - > get_value ( " remap " , " uid " ) ;
uid = ResourceUID : : get_singleton ( ) - > text_to_id ( uidt ) ;
}
2023-01-31 15:59:52 +01:00
2023-02-02 22:41:04 +01:00
if ( ! p_generator_parameters ) {
if ( cf - > has_section_key ( " remap " , " generator_parameters " ) ) {
generator_parameters = cf - > get_value ( " remap " , " generator_parameters " ) ;
2023-01-31 15:59:52 +01:00
}
2018-08-28 20:24:58 +02:00
}
2017-02-01 13:45:45 +01:00
}
}
}
2024-02-05 11:10:37 +01:00
if ( importer_name = = " keep " | | importer_name = = " skip " ) {
2021-03-22 20:41:47 +01:00
//keep files, do nothing.
fs - > files [ cpos ] - > modified_time = FileAccess : : get_modified_time ( p_file ) ;
fs - > files [ cpos ] - > import_modified_time = FileAccess : : get_modified_time ( p_file + " .import " ) ;
fs - > files [ cpos ] - > deps . clear ( ) ;
fs - > files [ cpos ] - > type = " " ;
fs - > files [ cpos ] - > import_valid = false ;
EditorResourcePreview : : get_singleton ( ) - > check_for_invalidation ( p_file ) ;
2023-01-31 15:59:52 +01:00
return OK ;
2021-03-22 20:41:47 +01:00
}
2017-02-01 13:45:45 +01:00
Ref < ResourceImporter > importer ;
2017-07-23 23:48:05 +02:00
bool load_default = false ;
2017-02-01 13:45:45 +01:00
//find the importer
2021-12-09 10:42:46 +01:00
if ( ! importer_name . is_empty ( ) ) {
2017-02-01 13:45:45 +01:00
importer = ResourceFormatImporter : : get_singleton ( ) - > get_importer_by_name ( importer_name ) ;
}
if ( importer . is_null ( ) ) {
//not found by name, find by extension
importer = ResourceFormatImporter : : get_singleton ( ) - > get_importer_by_extension ( p_file . get_extension ( ) ) ;
2017-07-23 23:48:05 +02:00
load_default = true ;
2017-02-01 13:45:45 +01:00
if ( importer . is_null ( ) ) {
2023-01-31 15:59:52 +01:00
ERR_FAIL_V_MSG ( ERR_FILE_CANT_OPEN , " BUG: File queued for import, but can't be imported, importer for type ' " + importer_name + " ' not found. " ) ;
2017-02-01 13:45:45 +01:00
}
}
2023-10-31 23:55:15 +01:00
if ( FileAccess : : exists ( p_file + " .import " ) ) {
// We only want to handle compat for existing files, not new ones.
importer - > handle_compatibility_options ( params ) ;
}
2017-02-01 13:45:45 +01:00
//mix with default params, in case a parameter is missing
List < ResourceImporter : : ImportOption > opts ;
2021-11-14 18:02:38 +01:00
importer - > get_import_options ( p_file , & opts ) ;
2021-07-24 15:46:25 +02:00
for ( const ResourceImporter : : ImportOption & E : opts ) {
2021-07-16 05:45:57 +02:00
if ( ! params . has ( E . option . name ) ) { //this one is not present
params [ E . option . name ] = E . default_value ;
2017-02-01 13:45:45 +01:00
}
}
2017-10-05 20:34:34 +02:00
if ( load_default & & ProjectSettings : : get_singleton ( ) - > has_setting ( " importer_defaults/ " + importer - > get_importer_name ( ) ) ) {
2017-07-23 23:48:05 +02:00
//use defaults if exist
2022-10-18 16:43:37 +02:00
Dictionary d = GLOBAL_GET ( " importer_defaults/ " + importer - > get_importer_name ( ) ) ;
2017-07-23 23:48:05 +02:00
List < Variant > v ;
d . get_key_list ( & v ) ;
2021-07-24 15:46:25 +02:00
for ( const Variant & E : v ) {
2021-07-16 05:45:57 +02:00
params [ E ] = d [ E ] ;
2017-07-23 23:48:05 +02:00
}
}
2017-02-01 13:45:45 +01:00
//finally, perform import!!
String base_path = ResourceFormatImporter : : get_singleton ( ) - > get_import_base_path ( p_file ) ;
List < String > import_variants ;
2017-02-02 00:41:05 +01:00
List < String > gen_files ;
2022-09-29 11:53:28 +02:00
Variant meta ;
Error err = importer - > import ( p_file , base_path , params , & import_variants , & gen_files , & meta ) ;
2017-02-01 13:45:45 +01:00
2024-07-14 19:51:29 +02:00
// As import is complete, save the .import file.
2017-02-01 13:45:45 +01:00
2022-04-12 09:12:40 +02:00
Vector < String > dest_paths ;
{
Ref < FileAccess > f = FileAccess : : open ( p_file + " .import " , FileAccess : : WRITE ) ;
2023-01-31 15:59:52 +01:00
ERR_FAIL_COND_V_MSG ( f . is_null ( ) , ERR_FILE_CANT_OPEN , " Cannot open file from path ' " + p_file + " .import'. " ) ;
2021-07-23 21:01:18 +02:00
2024-07-14 19:51:29 +02:00
// Write manually, as order matters ([remap] has to go first for performance).
2022-04-12 09:12:40 +02:00
f - > store_line ( " [remap] " ) ;
f - > store_line ( " " ) ;
f - > store_line ( " importer= \" " + importer - > get_importer_name ( ) + " \" " ) ;
int version = importer - > get_format_version ( ) ;
if ( version > 0 ) {
f - > store_line ( " importer_version= " + itos ( version ) ) ;
}
if ( ! importer - > get_resource_type ( ) . is_empty ( ) ) {
f - > store_line ( " type= \" " + importer - > get_resource_type ( ) + " \" " ) ;
}
2021-07-23 21:01:18 +02:00
2022-04-12 09:12:40 +02:00
if ( uid = = ResourceUID : : INVALID_ID ) {
uid = ResourceUID : : get_singleton ( ) - > create_id ( ) ;
}
2017-12-27 19:21:18 +01:00
2024-07-14 19:51:29 +02:00
f - > store_line ( " uid= \" " + ResourceUID : : get_singleton ( ) - > id_to_text ( uid ) + " \" " ) ; // Store in readable format.
2017-08-30 00:50:58 +02:00
2022-04-12 09:12:40 +02:00
if ( err = = OK ) {
if ( importer - > get_save_extension ( ) . is_empty ( ) ) {
//no path
} else if ( import_variants . size ( ) ) {
//import with variants
for ( const String & E : import_variants ) {
String path = base_path . c_escape ( ) + " . " + E + " . " + importer - > get_save_extension ( ) ;
f - > store_line ( " path. " + E + " = \" " + path + " \" " ) ;
dest_paths . push_back ( path ) ;
}
} else {
String path = base_path + " . " + importer - > get_save_extension ( ) ;
f - > store_line ( " path= \" " + path + " \" " ) ;
2017-12-27 19:21:18 +01:00
dest_paths . push_back ( path ) ;
2017-08-30 00:50:58 +02:00
}
2022-04-12 09:12:40 +02:00
2017-08-30 00:50:58 +02:00
} else {
2022-04-12 09:12:40 +02:00
f - > store_line ( " valid=false " ) ;
2017-02-01 13:45:45 +01:00
}
2017-08-30 00:50:58 +02:00
2022-09-29 11:53:28 +02:00
if ( meta ! = Variant ( ) ) {
f - > store_line ( " metadata= " + meta . get_construct_string ( ) ) ;
2022-04-12 09:12:40 +02:00
}
2017-02-01 13:45:45 +01:00
2023-02-02 22:41:04 +01:00
if ( generator_parameters ! = Variant ( ) ) {
f - > store_line ( " generator_parameters= " + generator_parameters . get_construct_string ( ) ) ;
2023-01-31 15:59:52 +01:00
}
2022-04-12 09:12:40 +02:00
f - > store_line ( " " ) ;
2019-02-26 22:43:37 +01:00
2022-04-12 09:12:40 +02:00
f - > store_line ( " [deps] \n " ) ;
2017-08-17 22:02:43 +02:00
2022-04-12 09:12:40 +02:00
if ( gen_files . size ( ) ) {
Array genf ;
for ( const String & E : gen_files ) {
genf . push_back ( E ) ;
dest_paths . push_back ( E ) ;
}
2017-11-24 17:39:41 +01:00
2022-04-12 09:12:40 +02:00
String value ;
VariantWriter : : write_to_string ( genf , value ) ;
f - > store_line ( " files= " + value ) ;
f - > store_line ( " " ) ;
2017-02-02 00:41:05 +01:00
}
2022-04-12 09:12:40 +02:00
f - > store_line ( " source_file= " + Variant ( p_file ) . get_construct_string ( ) ) ;
2017-02-02 00:41:05 +01:00
2022-04-12 09:12:40 +02:00
if ( dest_paths . size ( ) ) {
Array dp ;
for ( int i = 0 ; i < dest_paths . size ( ) ; i + + ) {
dp . push_back ( dest_paths [ i ] ) ;
}
2024-07-14 19:51:29 +02:00
f - > store_line ( " dest_files= " + Variant ( dp ) . get_construct_string ( ) ) ;
2017-12-27 19:21:18 +01:00
}
2024-07-14 19:51:29 +02:00
f - > store_line ( " " ) ;
2017-12-27 19:21:18 +01:00
2022-04-12 09:12:40 +02:00
f - > store_line ( " [params] " ) ;
f - > store_line ( " " ) ;
2017-02-01 13:45:45 +01:00
2024-07-14 19:51:29 +02:00
// Store options in provided order, to avoid file changing. Order is also important because first match is accepted first.
2017-02-06 04:38:39 +01:00
2022-04-12 09:12:40 +02:00
for ( const ResourceImporter : : ImportOption & E : opts ) {
String base = E . option . name ;
String value ;
VariantWriter : : write_to_string ( params [ base ] , value ) ;
f - > store_line ( base + " = " + value ) ;
}
2017-02-01 13:45:45 +01:00
}
2018-03-01 09:39:40 +01:00
// Store the md5's of the various files. These are stored separately so that the .import files can be version controlled.
2022-04-12 09:12:40 +02:00
{
Ref < FileAccess > md5s = FileAccess : : open ( base_path + " .md5 " , FileAccess : : WRITE ) ;
2023-01-31 15:59:52 +01:00
ERR_FAIL_COND_V_MSG ( md5s . is_null ( ) , ERR_FILE_CANT_OPEN , " Cannot open MD5 file ' " + base_path + " .md5'. " ) ;
2019-09-25 10:28:50 +02:00
2022-04-12 09:12:40 +02:00
md5s - > store_line ( " source_md5= \" " + FileAccess : : get_md5 ( p_file ) + " \" " ) ;
if ( dest_paths . size ( ) ) {
md5s - > store_line ( " dest_md5= \" " + FileAccess : : get_multiple_md5 ( dest_paths ) + " \" \n " ) ;
}
2018-03-01 09:39:40 +01:00
}
2023-12-08 14:47:28 +01:00
// Update cpos, newly created files could've changed the index of the reimported p_file.
_find_file ( p_file , & fs , cpos ) ;
2024-07-14 19:51:29 +02:00
// Update modified times, to avoid reimport.
2017-02-01 13:45:45 +01:00
fs - > files [ cpos ] - > modified_time = FileAccess : : get_modified_time ( p_file ) ;
2017-03-05 16:44:50 +01:00
fs - > files [ cpos ] - > import_modified_time = FileAccess : : get_modified_time ( p_file + " .import " ) ;
2017-08-17 22:02:43 +02:00
fs - > files [ cpos ] - > deps = _get_dependencies ( p_file ) ;
fs - > files [ cpos ] - > type = importer - > get_resource_type ( ) ;
2021-07-23 21:01:18 +02:00
fs - > files [ cpos ] - > uid = uid ;
2022-07-20 23:51:29 +02:00
fs - > files [ cpos ] - > import_valid = fs - > files [ cpos ] - > type = = " TextFile " ? true : ResourceLoader : : is_import_valid ( p_file ) ;
2017-02-06 04:38:39 +01:00
2021-07-23 21:01:18 +02:00
if ( ResourceUID : : get_singleton ( ) - > has_id ( uid ) ) {
ResourceUID : : get_singleton ( ) - > set_id ( uid , p_file ) ;
} else {
ResourceUID : : get_singleton ( ) - > add_id ( uid , p_file ) ;
}
2024-07-14 19:51:29 +02:00
// If file is currently up, maybe the source it was loaded from changed, so import math must be updated for it
// to reload properly.
2022-06-22 13:46:46 +02:00
Ref < Resource > r = ResourceCache : : get_ref ( p_file ) ;
if ( r . is_valid ( ) ) {
2021-12-09 10:42:46 +01:00
if ( ! r - > get_import_path ( ) . is_empty ( ) ) {
2017-02-06 04:38:39 +01:00
String dst_path = ResourceFormatImporter : : get_singleton ( ) - > get_internal_resource_path ( p_file ) ;
r - > set_import_path ( dst_path ) ;
r - > set_import_last_modified_time ( 0 ) ;
}
}
2017-11-12 04:48:00 +01:00
EditorResourcePreview : : get_singleton ( ) - > check_for_invalidation ( p_file ) ;
2023-01-31 15:59:52 +01:00
2024-02-27 13:49:39 +01:00
print_verbose ( vformat ( " EditorFileSystem: \" %s \" import took %d ms. " , p_file , OS : : get_singleton ( ) - > get_ticks_msec ( ) - start_time ) ) ;
2024-07-14 19:51:29 +02:00
ERR_FAIL_COND_V_MSG ( err ! = OK , ERR_FILE_UNRECOGNIZED , " Error importing ' " + p_file + " '. " ) ;
2023-01-31 15:59:52 +01:00
return OK ;
2017-02-01 13:45:45 +01:00
}
2022-05-19 17:00:06 +02:00
void EditorFileSystem : : _find_group_files ( EditorFileSystemDirectory * efd , HashMap < String , Vector < String > > & group_files , HashSet < String > & groups_to_reimport ) {
2019-04-19 20:54:33 +02:00
int fc = efd - > files . size ( ) ;
const EditorFileSystemDirectory : : FileInfo * const * files = efd - > files . ptr ( ) ;
for ( int i = 0 ; i < fc ; i + + ) {
if ( groups_to_reimport . has ( files [ i ] - > import_group_file ) ) {
if ( ! group_files . has ( files [ i ] - > import_group_file ) ) {
group_files [ files [ i ] - > import_group_file ] = Vector < String > ( ) ;
}
group_files [ files [ i ] - > import_group_file ] . push_back ( efd - > get_file_path ( i ) ) ;
}
}
for ( int i = 0 ; i < efd - > get_subdir_count ( ) ; i + + ) {
_find_group_files ( efd - > get_subdir ( i ) , group_files , groups_to_reimport ) ;
}
}
2022-05-13 15:04:37 +02:00
void EditorFileSystem : : reimport_file_with_custom_parameters ( const String & p_file , const String & p_importer , const HashMap < StringName , Variant > & p_custom_params ) {
2024-07-08 20:28:43 +02:00
Vector < String > reloads ;
reloads . append ( p_file ) ;
// Emit the resource_reimporting signal for the single file before the actual importation.
emit_signal ( SNAME ( " resources_reimporting " ) , reloads ) ;
2023-02-02 22:41:04 +01:00
_reimport_file ( p_file , p_custom_params , p_importer ) ;
2023-02-09 23:24:08 +01:00
// Emit the resource_reimported signal for the single file we just reimported.
emit_signal ( SNAME ( " resources_reimported " ) , reloads ) ;
2021-03-19 13:57:52 +01:00
}
2021-03-25 00:44:13 +01:00
void EditorFileSystem : : _reimport_thread ( uint32_t p_index , ImportThreadData * p_import_data ) {
2023-11-24 07:58:41 +01:00
int current_max = p_import_data - > reimport_from + int ( p_index ) ;
p_import_data - > max_index . exchange_if_greater ( current_max ) ;
_reimport_file ( p_import_data - > reimport_files [ current_max ] . path ) ;
2021-03-25 00:44:13 +01:00
}
2017-03-05 16:44:50 +01:00
void EditorFileSystem : : reimport_files ( const Vector < String > & p_files ) {
2023-02-13 11:54:32 +01:00
ERR_FAIL_COND_MSG ( importing , " Attempted to call reimport_files() recursively, this is not allowed. " ) ;
2017-03-05 16:44:50 +01:00
importing = true ;
2017-09-21 01:59:19 +02:00
2022-11-05 20:05:47 +01:00
Vector < String > reloads ;
2023-01-31 15:59:52 +01:00
EditorProgress pr ( " reimport " , TTR ( " (Re) Importing Assets " ), p_files.size()) ;
2021-03-25 00:44:13 +01:00
Vector < ImportFile > reimport_files ;
2022-05-19 17:00:06 +02:00
HashSet < String > groups_to_reimport ;
2017-09-21 01:59:19 +02:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < p_files . size ( ) ; i + + ) {
2021-07-23 21:01:18 +02:00
String file = p_files [ i ] ;
ResourceUID : : ID uid = ResourceUID : : get_singleton ( ) - > text_to_id ( file ) ;
if ( uid ! = ResourceUID : : INVALID_ID & & ResourceUID : : get_singleton ( ) - > has_id ( uid ) ) {
file = ResourceUID : : get_singleton ( ) - > get_id_path ( uid ) ;
}
String group_file = ResourceFormatImporter : : get_singleton ( ) - > get_import_group_file ( file ) ;
2019-04-19 20:54:33 +02:00
2021-07-23 21:01:18 +02:00
if ( group_file_cache . has ( file ) ) {
2022-11-05 20:05:47 +01:00
// Maybe the file itself is a group!
2021-07-23 21:01:18 +02:00
groups_to_reimport . insert ( file ) ;
2022-11-05 20:05:47 +01:00
// Groups do not belong to groups.
group_file = String ( ) ;
} else if ( groups_to_reimport . has ( file ) ) {
// Groups do not belong to groups.
2019-04-19 20:54:33 +02:00
group_file = String ( ) ;
2021-12-09 10:42:46 +01:00
} else if ( ! group_file . is_empty ( ) ) {
2022-11-05 20:05:47 +01:00
// It's a group file, add group to import and skip this file.
2019-04-19 20:54:33 +02:00
groups_to_reimport . insert ( group_file ) ;
} else {
2022-11-05 20:05:47 +01:00
// It's a regular file.
2019-04-19 20:54:33 +02:00
ImportFile ifile ;
2021-07-23 21:01:18 +02:00
ifile . path = file ;
ResourceFormatImporter : : get_singleton ( ) - > get_import_order_threads_and_importer ( file , ifile . order , ifile . threaded , ifile . importer ) ;
2022-11-05 20:05:47 +01:00
reloads . push_back ( file ) ;
2021-03-25 00:44:13 +01:00
reimport_files . push_back ( ifile ) ;
2019-04-19 20:54:33 +02:00
}
2022-11-05 20:05:47 +01:00
// Group may have changed, so also update group reference.
2020-04-02 01:20:12 +02:00
EditorFileSystemDirectory * fs = nullptr ;
2019-04-19 20:54:33 +02:00
int cpos = - 1 ;
2021-07-23 21:01:18 +02:00
if ( _find_file ( file , & fs , cpos ) ) {
2019-04-19 20:54:33 +02:00
fs - > files . write [ cpos ] - > import_group_file = group_file ;
}
2017-09-21 01:59:19 +02:00
}
2021-03-25 00:44:13 +01:00
reimport_files . sort ( ) ;
2017-09-21 01:59:19 +02:00
2024-07-08 20:28:43 +02:00
// Emit the resource_reimporting signal for the single file before the actual importation.
emit_signal ( SNAME ( " resources_reimporting " ) , reloads ) ;
2023-12-01 19:39:09 +01:00
# ifdef THREADS_ENABLED
2022-09-29 11:53:28 +02:00
bool use_multiple_threads = GLOBAL_GET ( " editor/import/use_multiple_threads " ) ;
2023-12-01 19:39:09 +01:00
# else
bool use_multiple_threads = false ;
# endif
2021-03-25 00:44:13 +01:00
int from = 0 ;
for ( int i = 0 ; i < reimport_files . size ( ) ; i + + ) {
2022-11-05 20:05:47 +01:00
if ( groups_to_reimport . has ( reimport_files [ i ] . path ) ) {
2024-05-24 17:32:33 +02:00
from = i + 1 ;
2022-11-05 20:05:47 +01:00
continue ;
}
2022-09-29 11:53:28 +02:00
if ( use_multiple_threads & & reimport_files [ i ] . threaded ) {
2024-05-24 17:32:33 +02:00
if ( i + 1 = = reimport_files . size ( ) | | reimport_files [ i + 1 ] . importer ! = reimport_files [ from ] . importer | | groups_to_reimport . has ( reimport_files [ i + 1 ] . path ) ) {
2021-03-25 00:44:13 +01:00
if ( from - i = = 0 ) {
2022-11-05 20:05:47 +01:00
// Single file, do not use threads.
2021-03-25 00:44:13 +01:00
pr . step ( reimport_files [ i ] . path . get_file ( ) , i ) ;
_reimport_file ( reimport_files [ i ] . path ) ;
} else {
Ref < ResourceImporter > importer = ResourceFormatImporter : : get_singleton ( ) - > get_importer_by_name ( reimport_files [ from ] . importer ) ;
2024-05-24 17:32:33 +02:00
if ( importer . is_null ( ) ) {
ERR_PRINT ( vformat ( " Invalid importer for \" %s \" . " , reimport_files [ from ] . importer ) ) ;
from = i + 1 ;
continue ;
}
2021-03-25 00:44:13 +01:00
importer - > import_threaded_begin ( ) ;
2022-09-29 11:53:28 +02:00
ImportThreadData tdata ;
2023-11-24 07:58:41 +01:00
tdata . max_index . set ( from ) ;
2022-09-29 11:53:28 +02:00
tdata . reimport_from = from ;
tdata . reimport_files = reimport_files . ptr ( ) ;
2021-03-25 00:44:13 +01:00
2022-09-29 11:53:28 +02:00
WorkerThreadPool : : GroupID group_task = WorkerThreadPool : : get_singleton ( ) - > add_template_group_task ( this , & EditorFileSystem : : _reimport_thread , & tdata , i - from + 1 , - 1 , false , vformat ( TTR ( " Import resources of type: %s " ) , reimport_files [ from ] . importer ) ) ;
2021-03-25 00:44:13 +01:00
int current_index = from - 1 ;
do {
2023-11-24 07:58:41 +01:00
if ( current_index < tdata . max_index . get ( ) ) {
current_index = tdata . max_index . get ( ) ;
2021-03-25 00:44:13 +01:00
pr . step ( reimport_files [ current_index ] . path . get_file ( ) , current_index ) ;
}
OS : : get_singleton ( ) - > delay_usec ( 1 ) ;
2022-07-23 19:12:41 +02:00
} while ( ! WorkerThreadPool : : get_singleton ( ) - > is_group_task_completed ( group_task ) ) ;
2021-03-25 00:44:13 +01:00
2022-07-23 19:12:41 +02:00
WorkerThreadPool : : get_singleton ( ) - > wait_for_group_task_completion ( group_task ) ;
2021-03-25 00:44:13 +01:00
importer - > import_threaded_end ( ) ;
}
from = i + 1 ;
}
} else {
pr . step ( reimport_files [ i ] . path . get_file ( ) , i ) ;
_reimport_file ( reimport_files [ i ] . path ) ;
2024-05-24 17:32:33 +02:00
// We need to increment the counter, maybe the next file is multithreaded
// and doesn't have the same importer.
from = i + 1 ;
2021-03-25 00:44:13 +01:00
}
2017-02-01 13:45:45 +01:00
}
2022-11-05 20:05:47 +01:00
// Reimport groups.
from = reimport_files . size ( ) ;
2019-04-19 20:54:33 +02:00
if ( groups_to_reimport . size ( ) ) {
2022-05-13 15:04:37 +02:00
HashMap < String , Vector < String > > group_files ;
2019-04-19 20:54:33 +02:00
_find_group_files ( filesystem , group_files , groups_to_reimport ) ;
2021-08-09 22:13:42 +02:00
for ( const KeyValue < String , Vector < String > > & E : group_files ) {
2022-11-05 20:05:47 +01:00
pr . step ( E . key . get_file ( ) , from + + ) ;
2021-08-09 22:13:42 +02:00
Error err = _reimport_group ( E . key , E . value ) ;
2022-11-05 20:05:47 +01:00
reloads . push_back ( E . key ) ;
reloads . append_array ( E . value ) ;
2019-04-19 20:54:33 +02:00
if ( err = = OK ) {
2021-08-09 22:13:42 +02:00
_reimport_file ( E . key ) ;
2019-04-19 20:54:33 +02:00
}
}
}
2022-11-05 20:05:47 +01:00
ResourceUID : : get_singleton ( ) - > update_cache ( ) ; // After reimporting, update the cache.
2021-07-23 21:01:18 +02:00
2017-02-01 13:45:45 +01:00
_save_filesystem_cache ( ) ;
2024-05-24 07:30:16 +02:00
_process_update_pending ( ) ;
2017-03-05 16:44:50 +01:00
importing = false ;
2017-02-06 04:38:39 +01:00
if ( ! is_scanning ( ) ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " filesystem_changed " ) ) ;
2017-02-06 04:38:39 +01:00
}
2017-08-31 23:57:03 +02:00
2022-11-05 20:05:47 +01:00
emit_signal ( SNAME ( " resources_reimported " ) , reloads ) ;
2017-02-01 13:45:45 +01:00
}
2014-02-10 02:10:30 +01:00
2023-02-02 22:41:04 +01:00
Error EditorFileSystem : : reimport_append ( const String & p_file , const HashMap < StringName , Variant > & p_custom_options , const String & p_custom_importer , Variant p_generator_parameters ) {
2023-01-31 15:59:52 +01:00
ERR_FAIL_COND_V_MSG ( ! importing , ERR_INVALID_PARAMETER , " Can only append files to import during a current reimport process. " ) ;
2024-07-08 20:28:43 +02:00
Vector < String > reloads ;
reloads . append ( p_file ) ;
// Emit the resource_reimporting signal for the single file before the actual importation.
emit_signal ( SNAME ( " resources_reimporting " ) , reloads ) ;
2023-12-25 09:58:38 +01:00
Error ret = _reimport_file ( p_file , p_custom_options , p_custom_importer , & p_generator_parameters ) ;
// Emit the resource_reimported signal for the single file we just reimported.
emit_signal ( SNAME ( " resources_reimported " ) , reloads ) ;
return ret ;
2023-01-31 15:59:52 +01:00
}
2018-10-05 04:00:02 +02:00
Error EditorFileSystem : : _resource_import ( const String & p_path ) {
Vector < String > files ;
files . push_back ( p_path ) ;
singleton - > update_file ( p_path ) ;
singleton - > reimport_files ( files ) ;
return OK ;
}
2021-02-22 06:34:27 +01:00
bool EditorFileSystem : : _should_skip_directory ( const String & p_path ) {
2021-10-13 22:56:18 +02:00
String project_data_path = ProjectSettings : : get_singleton ( ) - > get_project_data_path ( ) ;
if ( p_path = = project_data_path | | p_path . begins_with ( project_data_path + " / " ) ) {
2021-09-10 17:32:29 +02:00
return true ;
}
2022-08-30 02:34:01 +02:00
if ( FileAccess : : exists ( p_path . path_join ( " project.godot " ) ) ) {
2023-05-15 02:04:58 +02:00
// Skip if another project inside this.
2023-11-12 16:21:49 +01:00
if ( EditorFileSystem : : get_singleton ( ) - > first_scan ) {
WARN_PRINT_ONCE ( vformat ( " Detected another project.godot at %s. The folder will be ignored. " , p_path ) ) ;
}
2021-02-22 06:34:27 +01:00
return true ;
}
2022-08-30 02:34:01 +02:00
if ( FileAccess : : exists ( p_path . path_join ( " .gdignore " ) ) ) {
2023-05-15 02:04:58 +02:00
// Skip if a `.gdignore` file is inside this.
2021-02-22 06:34:27 +01:00
return true ;
}
return false ;
}
2019-04-19 20:54:33 +02:00
bool EditorFileSystem : : is_group_file ( const String & p_path ) const {
return group_file_cache . has ( p_path ) ;
}
void EditorFileSystem : : _move_group_files ( EditorFileSystemDirectory * efd , const String & p_group_file , const String & p_new_location ) {
int fc = efd - > files . size ( ) ;
EditorFileSystemDirectory : : FileInfo * const * files = efd - > files . ptrw ( ) ;
for ( int i = 0 ; i < fc ; i + + ) {
if ( files [ i ] - > import_group_file = = p_group_file ) {
files [ i ] - > import_group_file = p_new_location ;
Ref < ConfigFile > config ;
2021-06-18 00:03:09 +02:00
config . instantiate ( ) ;
2019-04-19 20:54:33 +02:00
String path = efd - > get_file_path ( i ) + " .import " ;
Error err = config - > load ( path ) ;
if ( err ! = OK ) {
continue ;
}
if ( config - > has_section_key ( " remap " , " group_file " ) ) {
config - > set_value ( " remap " , " group_file " , p_new_location ) ;
}
List < String > sk ;
config - > get_section_keys ( " params " , & sk ) ;
2021-07-24 15:46:25 +02:00
for ( const String & param : sk ) {
2019-04-19 20:54:33 +02:00
//not very clean, but should work
String value = config - > get_value ( " params " , param ) ;
if ( value = = p_group_file ) {
config - > set_value ( " params " , param , p_new_location ) ;
}
}
config - > save ( path ) ;
}
}
for ( int i = 0 ; i < efd - > get_subdir_count ( ) ; i + + ) {
_move_group_files ( efd - > get_subdir ( i ) , p_group_file , p_new_location ) ;
}
}
void EditorFileSystem : : move_group_file ( const String & p_path , const String & p_new_path ) {
if ( get_filesystem ( ) ) {
_move_group_files ( get_filesystem ( ) , p_path , p_new_path ) ;
if ( group_file_cache . has ( p_path ) ) {
group_file_cache . erase ( p_path ) ;
group_file_cache . insert ( p_new_path ) ;
}
}
}
2021-07-23 21:01:18 +02:00
ResourceUID : : ID EditorFileSystem : : _resource_saver_get_resource_id_for_path ( const String & p_path , bool p_generate ) {
2021-09-10 17:32:29 +02:00
if ( ! p_path . is_resource_file ( ) | | p_path . begins_with ( ProjectSettings : : get_singleton ( ) - > get_project_data_path ( ) ) ) {
2022-10-09 13:06:15 +02:00
// Saved externally (configuration file) or internal file, do not assign an ID.
2021-07-23 21:01:18 +02:00
return ResourceUID : : INVALID_ID ;
}
EditorFileSystemDirectory * fs = nullptr ;
int cpos = - 1 ;
if ( ! singleton - > _find_file ( p_path , & fs , cpos ) ) {
2022-10-09 13:06:15 +02:00
// Fallback to ResourceLoader if filesystem cache fails (can happen during scanning etc.).
ResourceUID : : ID fallback = ResourceLoader : : get_resource_uid ( p_path ) ;
if ( fallback ! = ResourceUID : : INVALID_ID ) {
return fallback ;
}
2021-07-23 21:01:18 +02:00
if ( p_generate ) {
2022-12-05 19:01:59 +01:00
return ResourceUID : : get_singleton ( ) - > create_id ( ) ; // Just create a new one, we will be notified of save anyway and fetch the right UID at that time, to keep things simple.
2021-07-23 21:01:18 +02:00
} else {
return ResourceUID : : INVALID_ID ;
}
} else if ( fs - > files [ cpos ] - > uid ! = ResourceUID : : INVALID_ID ) {
return fs - > files [ cpos ] - > uid ;
} else if ( p_generate ) {
2022-12-05 19:01:59 +01:00
return ResourceUID : : get_singleton ( ) - > create_id ( ) ; // Just create a new one, we will be notified of save anyway and fetch the right UID at that time, to keep things simple.
2021-07-23 21:01:18 +02:00
} else {
return ResourceUID : : INVALID_ID ;
}
}
2022-05-19 17:00:06 +02:00
static void _scan_extensions_dir ( EditorFileSystemDirectory * d , HashSet < String > & extensions ) {
2021-08-20 20:32:56 +02:00
int fc = d - > get_file_count ( ) ;
for ( int i = 0 ; i < fc ; i + + ) {
2022-12-07 12:11:28 +01:00
if ( d - > get_file_type ( i ) = = SNAME ( " GDExtension " ) ) {
2021-08-20 20:32:56 +02:00
extensions . insert ( d - > get_file_path ( i ) ) ;
}
}
int dc = d - > get_subdir_count ( ) ;
for ( int i = 0 ; i < dc ; i + + ) {
_scan_extensions_dir ( d - > get_subdir ( i ) , extensions ) ;
}
}
bool EditorFileSystem : : _scan_extensions ( ) {
EditorFileSystemDirectory * d = get_filesystem ( ) ;
2022-05-19 17:00:06 +02:00
HashSet < String > extensions ;
2022-03-31 22:00:17 +02:00
2021-08-20 20:32:56 +02:00
_scan_extensions_dir ( d , extensions ) ;
//verify against loaded extensions
Vector < String > extensions_added ;
Vector < String > extensions_removed ;
for ( const String & E : extensions ) {
2022-12-07 12:11:28 +01:00
if ( ! GDExtensionManager : : get_singleton ( ) - > is_extension_loaded ( E ) ) {
2021-08-20 20:32:56 +02:00
extensions_added . push_back ( E ) ;
}
}
2022-12-07 12:11:28 +01:00
Vector < String > loaded_extensions = GDExtensionManager : : get_singleton ( ) - > get_loaded_extensions ( ) ;
2021-08-20 20:32:56 +02:00
for ( int i = 0 ; i < loaded_extensions . size ( ) ; i + + ) {
if ( ! extensions . has ( loaded_extensions [ i ] ) ) {
extensions_removed . push_back ( loaded_extensions [ i ] ) ;
}
}
2022-12-07 12:11:28 +01:00
String extension_list_config_file = GDExtension : : get_extension_list_config_file ( ) ;
2021-08-20 20:32:56 +02:00
if ( extensions . size ( ) ) {
if ( extensions_added . size ( ) | | extensions_removed . size ( ) ) { //extensions were added or removed
2022-03-23 10:08:58 +01:00
Ref < FileAccess > f = FileAccess : : open ( extension_list_config_file , FileAccess : : WRITE ) ;
2021-08-20 20:32:56 +02:00
for ( const String & E : extensions ) {
f - > store_line ( E ) ;
}
}
} else {
2021-09-10 17:32:29 +02:00
if ( loaded_extensions . size ( ) | | FileAccess : : exists ( extension_list_config_file ) ) { //extensions were removed
2022-03-23 10:08:58 +01:00
Ref < DirAccess > da = DirAccess : : create ( DirAccess : : ACCESS_RESOURCES ) ;
2021-09-10 17:32:29 +02:00
da - > remove ( extension_list_config_file ) ;
2021-08-20 20:32:56 +02:00
}
}
bool needs_restart = false ;
for ( int i = 0 ; i < extensions_added . size ( ) ; i + + ) {
2022-12-07 12:11:28 +01:00
GDExtensionManager : : LoadStatus st = GDExtensionManager : : get_singleton ( ) - > load_extension ( extensions_added [ i ] ) ;
2023-10-06 12:34:36 +02:00
if ( st = = GDExtensionManager : : LOAD_STATUS_NEEDS_RESTART ) {
2021-08-20 20:32:56 +02:00
needs_restart = true ;
}
}
for ( int i = 0 ; i < extensions_removed . size ( ) ; i + + ) {
2022-12-07 12:11:28 +01:00
GDExtensionManager : : LoadStatus st = GDExtensionManager : : get_singleton ( ) - > unload_extension ( extensions_removed [ i ] ) ;
2023-10-06 12:34:36 +02:00
if ( st = = GDExtensionManager : : LOAD_STATUS_NEEDS_RESTART ) {
2021-08-20 20:32:56 +02:00
needs_restart = true ;
}
}
return needs_restart ;
}
2014-02-10 02:10:30 +01:00
void EditorFileSystem : : _bind_methods ( ) {
2017-08-09 13:19:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_filesystem " ) , & EditorFileSystem : : get_filesystem ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " is_scanning " ) , & EditorFileSystem : : is_scanning ) ;
ClassDB : : bind_method ( D_METHOD ( " get_scanning_progress " ) , & EditorFileSystem : : get_scanning_progress ) ;
ClassDB : : bind_method ( D_METHOD ( " scan " ) , & EditorFileSystem : : scan ) ;
ClassDB : : bind_method ( D_METHOD ( " scan_sources " ) , & EditorFileSystem : : scan_changes ) ;
ClassDB : : bind_method ( D_METHOD ( " update_file " , " path " ) , & EditorFileSystem : : update_file ) ;
2017-08-09 13:19:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_filesystem_path " , " path " ) , & EditorFileSystem : : get_filesystem_path ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_file_type " , " path " ) , & EditorFileSystem : : get_file_type ) ;
2022-05-13 19:35:12 +02:00
ClassDB : : bind_method ( D_METHOD ( " reimport_files " , " files " ) , & EditorFileSystem : : reimport_files ) ;
2017-03-05 16:44:50 +01:00
ADD_SIGNAL ( MethodInfo ( " filesystem_changed " ) ) ;
2022-12-28 13:56:53 +01:00
ADD_SIGNAL ( MethodInfo ( " script_classes_updated " ) ) ;
2017-03-05 16:44:50 +01:00
ADD_SIGNAL ( MethodInfo ( " sources_changed " , PropertyInfo ( Variant : : BOOL , " exist " ) ) ) ;
2024-07-08 20:28:43 +02:00
ADD_SIGNAL ( MethodInfo ( " resources_reimporting " , PropertyInfo ( Variant : : PACKED_STRING_ARRAY , " resources " ) ) ) ;
2020-02-17 22:06:54 +01:00
ADD_SIGNAL ( MethodInfo ( " resources_reimported " , PropertyInfo ( Variant : : PACKED_STRING_ARRAY , " resources " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " resources_reload " , PropertyInfo ( Variant : : PACKED_STRING_ARRAY , " resources " ) ) ) ;
2014-02-10 02:10:30 +01:00
}
2017-02-01 13:45:45 +01:00
void EditorFileSystem : : _update_extensions ( ) {
valid_extensions . clear ( ) ;
import_extensions . clear ( ) ;
2021-09-23 23:09:15 +02:00
textfile_extensions . clear ( ) ;
2017-02-01 13:45:45 +01:00
List < String > extensionsl ;
2017-03-05 16:44:50 +01:00
ResourceLoader : : get_recognized_extensions_for_type ( " " , & extensionsl ) ;
2021-07-24 15:46:25 +02:00
for ( const String & E : extensionsl ) {
2021-07-16 05:45:57 +02:00
valid_extensions . insert ( E ) ;
2017-02-01 13:45:45 +01:00
}
2022-10-18 16:43:37 +02:00
const Vector < String > textfile_ext = ( ( String ) ( EDITOR_GET ( " docks/filesystem/textfile_extensions " ) ) ) . split ( " , " , false ) ;
2021-09-23 23:09:15 +02:00
for ( const String & E : textfile_ext ) {
if ( valid_extensions . has ( E ) ) {
continue ;
}
valid_extensions . insert ( E ) ;
textfile_extensions . insert ( E ) ;
}
2017-02-01 13:45:45 +01:00
extensionsl . clear ( ) ;
ResourceFormatImporter : : get_singleton ( ) - > get_recognized_extensions ( & extensionsl ) ;
2021-07-24 15:46:25 +02:00
for ( const String & E : extensionsl ) {
2021-07-16 05:45:57 +02:00
import_extensions . insert ( E ) ;
2017-02-01 13:45:45 +01:00
}
}
2016-07-03 18:15:15 +02:00
2022-03-31 22:00:17 +02:00
void EditorFileSystem : : add_import_format_support_query ( Ref < EditorFileSystemImportFormatSupportQuery > p_query ) {
2024-05-06 16:20:20 +02:00
ERR_FAIL_COND ( import_support_queries . has ( p_query ) ) ;
2022-03-31 22:00:17 +02:00
import_support_queries . push_back ( p_query ) ;
}
void EditorFileSystem : : remove_import_format_support_query ( Ref < EditorFileSystemImportFormatSupportQuery > p_query ) {
import_support_queries . erase ( p_query ) ;
}
2014-02-10 02:10:30 +01:00
EditorFileSystem : : EditorFileSystem ( ) {
2023-12-01 19:39:09 +01:00
# ifdef THREADS_ENABLED
use_threads = true ;
# endif
2018-10-05 04:00:02 +02:00
ResourceLoader : : import = _resource_import ;
2023-03-10 15:09:56 +01:00
reimport_on_missing_imported_files = GLOBAL_GET ( " editor/import/reimport_missing_imported_files " ) ;
2017-03-05 16:44:50 +01:00
singleton = this ;
filesystem = memnew ( EditorFileSystemDirectory ) ; //like, empty
2020-04-02 01:20:12 +02:00
filesystem - > parent = nullptr ;
2014-02-10 02:10:30 +01:00
2020-04-02 01:20:12 +02:00
new_filesystem = nullptr ;
2014-02-10 02:10:30 +01:00
2019-04-27 16:50:26 +02:00
// This should probably also work on Unix and use the string it returns for FAT32 or exFAT
2022-03-23 10:08:58 +01:00
Ref < DirAccess > da = DirAccess : : create ( DirAccess : : ACCESS_RESOURCES ) ;
2019-04-27 16:50:26 +02:00
using_fat32_or_exfat = ( da - > get_filesystem_type ( ) = = " FAT32 " | | da - > get_filesystem_type ( ) = = " exFAT " ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
scan_total = 0 ;
2023-12-18 15:46:56 +01:00
callable_mp ( ResourceUID : : get_singleton ( ) , & ResourceUID : : clear ) . call_deferred ( ) ; // Will be updated on scan.
2021-07-23 21:01:18 +02:00
ResourceSaver : : set_get_resource_id_for_path ( _resource_saver_get_resource_id_for_path ) ;
2014-02-10 02:10:30 +01:00
}
EditorFileSystem : : ~ EditorFileSystem ( ) {
2021-07-23 21:01:18 +02:00
ResourceSaver : : set_get_resource_id_for_path ( nullptr ) ;
2014-02-10 02:10:30 +01:00
}