Skip to content

Commit 6ad513b

Browse files
committed
Merge pull request godotengine#97090 from KoBeWi/secret_cloning_research
Don't rescan filesystem when duplicating
2 parents 9554e3c + 7377ca8 commit 6ad513b

File tree

3 files changed

+171
-82
lines changed

3 files changed

+171
-82
lines changed

editor/editor_file_system.cpp

Lines changed: 145 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,7 +1027,9 @@ void EditorFileSystem::scan() {
10271027
void EditorFileSystem::ScanProgress::increment() {
10281028
current++;
10291029
float ratio = current / MAX(hi, 1.0f);
1030-
progress->step(ratio * 1000.0f);
1030+
if (progress) {
1031+
progress->step(ratio * 1000.0f);
1032+
}
10311033
EditorFileSystem::singleton->scan_total = ratio;
10321034
}
10331035

@@ -1293,7 +1295,7 @@ void EditorFileSystem::_process_removed_files(const HashSet<String> &p_processed
12931295
}
12941296
}
12951297

1296-
void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanProgress &p_progress) {
1298+
void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanProgress &p_progress, bool p_recursive) {
12971299
uint64_t current_mtime = FileAccess::get_modified_time(p_dir->get_path());
12981300

12991301
bool updated_dir = false;
@@ -1487,7 +1489,9 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanPr
14871489
scan_actions.push_back(ia);
14881490
continue;
14891491
}
1490-
_scan_fs_changes(p_dir->get_subdir(i), p_progress);
1492+
if (p_recursive) {
1493+
_scan_fs_changes(p_dir->get_subdir(i), p_progress);
1494+
}
14911495
}
14921496

14931497
nb_files_total = MAX(nb_files_total + diff_nb_files, 0);
@@ -2912,6 +2916,96 @@ void EditorFileSystem::reimport_file_with_custom_parameters(const String &p_file
29122916
emit_signal(SNAME("resources_reimported"), reloads);
29132917
}
29142918

2919+
Error EditorFileSystem::_copy_file(const String &p_from, const String &p_to) {
2920+
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
2921+
if (FileAccess::exists(p_from + ".import")) {
2922+
Error err = da->copy(p_from, p_to);
2923+
if (err != OK) {
2924+
return err;
2925+
}
2926+
2927+
// Remove uid from .import file to avoid conflict.
2928+
Ref<ConfigFile> cfg;
2929+
cfg.instantiate();
2930+
cfg->load(p_from + ".import");
2931+
cfg->erase_section_key("remap", "uid");
2932+
err = cfg->save(p_to + ".import");
2933+
if (err != OK) {
2934+
return err;
2935+
}
2936+
} else if (ResourceLoader::get_resource_uid(p_from) == ResourceUID::INVALID_ID) {
2937+
// Files which do not use an uid can just be copied.
2938+
Error err = da->copy(p_from, p_to);
2939+
if (err != OK) {
2940+
return err;
2941+
}
2942+
} else {
2943+
// Load the resource and save it again in the new location (this generates a new UID).
2944+
Error err;
2945+
Ref<Resource> res = ResourceLoader::load(p_from, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
2946+
if (err == OK && res.is_valid()) {
2947+
err = ResourceSaver::save(res, p_to, ResourceSaver::FLAG_COMPRESS);
2948+
if (err != OK) {
2949+
return err;
2950+
}
2951+
} else if (err != OK) {
2952+
// When loading files like text files the error is OK but the resource is still null.
2953+
// We can ignore such files.
2954+
return err;
2955+
}
2956+
}
2957+
return OK;
2958+
}
2959+
2960+
bool EditorFileSystem::_copy_directory(const String &p_from, const String &p_to, List<CopiedFile> *p_files) {
2961+
Ref<DirAccess> old_dir = DirAccess::open(p_from);
2962+
ERR_FAIL_COND_V(old_dir.is_null(), false);
2963+
2964+
Error err = make_dir_recursive(p_to);
2965+
if (err != OK && err != ERR_ALREADY_EXISTS) {
2966+
return false;
2967+
}
2968+
2969+
bool success = true;
2970+
old_dir->set_include_navigational(false);
2971+
old_dir->list_dir_begin();
2972+
2973+
for (String F = old_dir->_get_next(); !F.is_empty(); F = old_dir->_get_next()) {
2974+
if (old_dir->current_is_dir()) {
2975+
success = _copy_directory(p_from.path_join(F), p_to.path_join(F), p_files) && success;
2976+
} else if (F.get_extension() != "import") {
2977+
CopiedFile copy;
2978+
copy.from = p_from.path_join(F);
2979+
copy.to = p_to.path_join(F);
2980+
p_files->push_back(copy);
2981+
}
2982+
}
2983+
return success;
2984+
}
2985+
2986+
void EditorFileSystem::_queue_refresh_filesystem() {
2987+
if (refresh_queued) {
2988+
return;
2989+
}
2990+
refresh_queued = true;
2991+
get_tree()->connect(SNAME("process_frame"), callable_mp(this, &EditorFileSystem::_refresh_filesystem), CONNECT_ONE_SHOT);
2992+
}
2993+
2994+
void EditorFileSystem::_refresh_filesystem() {
2995+
for (const ObjectID &id : folders_to_sort) {
2996+
EditorFileSystemDirectory *dir = Object::cast_to<EditorFileSystemDirectory>(ObjectDB::get_instance(id));
2997+
if (dir) {
2998+
dir->subdirs.sort_custom<DirectoryComparator>();
2999+
}
3000+
}
3001+
folders_to_sort.clear();
3002+
3003+
_update_scan_actions();
3004+
3005+
emit_signal(SNAME("filesystem_changed"));
3006+
refresh_queued = false;
3007+
}
3008+
29153009
void EditorFileSystem::_reimport_thread(uint32_t p_index, ImportThreadData *p_import_data) {
29163010
int current_max = p_import_data->reimport_from + int(p_index);
29173011
p_import_data->max_index.exchange_if_greater(current_max);
@@ -3235,10 +3329,9 @@ Error EditorFileSystem::make_dir_recursive(const String &p_path, const String &p
32353329
const String path = da->get_current_dir();
32363330
EditorFileSystemDirectory *parent = get_filesystem_path(path);
32373331
ERR_FAIL_NULL_V(parent, ERR_FILE_NOT_FOUND);
3332+
folders_to_sort.insert(parent->get_instance_id());
32383333

32393334
const PackedStringArray folders = p_path.trim_prefix(path).trim_suffix("/").split("/");
3240-
bool first = true;
3241-
32423335
for (const String &folder : folders) {
32433336
const int current = parent->find_dir_index(folder);
32443337
if (current > -1) {
@@ -3250,18 +3343,59 @@ Error EditorFileSystem::make_dir_recursive(const String &p_path, const String &p
32503343
efd->parent = parent;
32513344
efd->name = folder;
32523345
parent->subdirs.push_back(efd);
3253-
3254-
if (first) {
3255-
parent->subdirs.sort_custom<DirectoryComparator>();
3256-
first = false;
3257-
}
32583346
parent = efd;
32593347
}
32603348

3261-
emit_signal(SNAME("filesystem_changed"));
3349+
_queue_refresh_filesystem();
32623350
return OK;
32633351
}
32643352

3353+
Error EditorFileSystem::copy_file(const String &p_from, const String &p_to) {
3354+
_copy_file(p_from, p_to);
3355+
3356+
EditorFileSystemDirectory *parent = get_filesystem_path(p_to.get_base_dir());
3357+
ERR_FAIL_NULL_V(parent, ERR_FILE_NOT_FOUND);
3358+
3359+
ScanProgress sp;
3360+
_scan_fs_changes(parent, sp, false);
3361+
3362+
_queue_refresh_filesystem();
3363+
return OK;
3364+
}
3365+
3366+
Error EditorFileSystem::copy_directory(const String &p_from, const String &p_to) {
3367+
List<CopiedFile> files;
3368+
bool success = _copy_directory(p_from, p_to, &files);
3369+
3370+
EditorProgress *ep = nullptr;
3371+
if (files.size() > 10) {
3372+
ep = memnew(EditorProgress("_copy_files", TTR("Copying files..."), files.size()));
3373+
}
3374+
3375+
int i = 0;
3376+
for (const CopiedFile &F : files) {
3377+
if (_copy_file(F.from, F.to) != OK) {
3378+
success = false;
3379+
}
3380+
if (ep) {
3381+
ep->step(F.from.get_file(), i++, false);
3382+
}
3383+
}
3384+
memdelete_notnull(ep);
3385+
3386+
EditorFileSystemDirectory *efd = get_filesystem_path(p_to);
3387+
ERR_FAIL_NULL_V(efd, FAILED);
3388+
ERR_FAIL_NULL_V(efd->get_parent(), FAILED);
3389+
3390+
folders_to_sort.insert(efd->get_parent()->get_instance_id());
3391+
3392+
ScanProgress sp;
3393+
_scan_fs_changes(efd, sp);
3394+
3395+
_queue_refresh_filesystem();
3396+
return success ? OK : FAILED;
3397+
}
3398+
32653399
ResourceUID::ID EditorFileSystem::_resource_saver_get_resource_id_for_path(const String &p_path, bool p_generate) {
32663400
if (!p_path.is_resource_file() || p_path.begins_with(ProjectSettings::get_singleton()->get_project_data_path())) {
32673401
// Saved externally (configuration file) or internal file, do not assign an ID.

editor/editor_file_system.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ class EditorFileSystem : public Node {
239239

240240
bool _find_file(const String &p_file, EditorFileSystemDirectory **r_d, int &r_file_pos) const;
241241

242-
void _scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanProgress &p_progress);
242+
void _scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanProgress &p_progress, bool p_recursive = true);
243243

244244
void _delete_internal_files(const String &p_file);
245245
int _insert_actions_delete_files_directory(EditorFileSystemDirectory *p_dir);
@@ -324,6 +324,19 @@ class EditorFileSystem : public Node {
324324
HashSet<String> group_file_cache;
325325
HashMap<String, String> file_icon_cache;
326326

327+
struct CopiedFile {
328+
String from;
329+
String to;
330+
};
331+
332+
bool refresh_queued = false;
333+
HashSet<ObjectID> folders_to_sort;
334+
335+
Error _copy_file(const String &p_from, const String &p_to);
336+
bool _copy_directory(const String &p_from, const String &p_to, List<CopiedFile> *p_files);
337+
void _queue_refresh_filesystem();
338+
void _refresh_filesystem();
339+
327340
struct ImportThreadData {
328341
const ImportFile *reimport_files;
329342
int reimport_from;
@@ -378,6 +391,8 @@ class EditorFileSystem : public Node {
378391
void move_group_file(const String &p_path, const String &p_new_path);
379392

380393
Error make_dir_recursive(const String &p_path, const String &p_base_path = String());
394+
Error copy_file(const String &p_from, const String &p_to);
395+
Error copy_directory(const String &p_from, const String &p_to);
381396

382397
static bool _should_skip_directory(const String &p_path);
383398

editor/filesystem_dock.cpp

Lines changed: 10 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,76 +1491,22 @@ void FileSystemDock::_try_duplicate_item(const FileOrFolder &p_item, const Strin
14911491
EditorNode::get_singleton()->add_io_error(TTR("Cannot move a folder into itself.") + "\n" + old_path + "\n");
14921492
return;
14931493
}
1494-
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
14951494

14961495
if (p_item.is_file) {
14971496
print_verbose("Duplicating " + old_path + " -> " + new_path);
14981497

14991498
// Create the directory structure.
1500-
da->make_dir_recursive(new_path.get_base_dir());
1501-
1502-
if (FileAccess::exists(old_path + ".import")) {
1503-
Error err = da->copy(old_path, new_path);
1504-
if (err != OK) {
1505-
EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + "\n" + old_path + ": " + error_names[err] + "\n");
1506-
return;
1507-
}
1508-
1509-
// Remove uid from .import file to avoid conflict.
1510-
Ref<ConfigFile> cfg;
1511-
cfg.instantiate();
1512-
cfg->load(old_path + ".import");
1513-
cfg->erase_section_key("remap", "uid");
1514-
err = cfg->save(new_path + ".import");
1515-
if (err != OK) {
1516-
EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + "\n" + old_path + ".import: " + error_names[err] + "\n");
1517-
return;
1518-
}
1519-
} else {
1520-
// Files which do not use an uid can just be copied.
1521-
if (ResourceLoader::get_resource_uid(old_path) == ResourceUID::INVALID_ID) {
1522-
Error err = da->copy(old_path, new_path);
1523-
if (err != OK) {
1524-
EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + "\n" + old_path + ": " + error_names[err] + "\n");
1525-
}
1526-
return;
1527-
}
1499+
EditorFileSystem::get_singleton()->make_dir_recursive(p_new_path.get_base_dir());
15281500

1529-
// Load the resource and save it again in the new location (this generates a new UID).
1530-
Error err;
1531-
Ref<Resource> res = ResourceLoader::load(old_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
1532-
if (err == OK && res.is_valid()) {
1533-
err = ResourceSaver::save(res, new_path, ResourceSaver::FLAG_COMPRESS);
1534-
if (err != OK) {
1535-
EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + " " + vformat(TTR("Failed to save resource at %s: %s"), new_path, error_names[err]));
1536-
}
1537-
} else if (err != OK) {
1538-
// When loading files like text files the error is OK but the resource is still null.
1539-
// We can ignore such files.
1540-
EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + " " + vformat(TTR("Failed to load resource at %s: %s"), new_path, error_names[err]));
1541-
}
1501+
Error err = EditorFileSystem::get_singleton()->copy_file(old_path, new_path);
1502+
if (err != OK) {
1503+
EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + "\n" + old_path + ": " + error_names[err] + "\n");
15421504
}
15431505
} else {
1544-
da->make_dir(new_path);
1545-
1546-
// Recursively duplicate all files inside the folder.
1547-
Ref<DirAccess> old_dir = DirAccess::open(old_path);
1548-
ERR_FAIL_COND(old_dir.is_null());
1549-
1550-
Ref<FileAccess> file_access = FileAccess::create(FileAccess::ACCESS_RESOURCES);
1551-
old_dir->set_include_navigational(false);
1552-
old_dir->list_dir_begin();
1553-
for (String f = old_dir->_get_next(); !f.is_empty(); f = old_dir->_get_next()) {
1554-
if (f.get_extension() == "import") {
1555-
continue;
1556-
}
1557-
if (file_access->file_exists(old_path + f)) {
1558-
_try_duplicate_item(FileOrFolder(old_path + f, true), new_path + f);
1559-
} else if (da->dir_exists(old_path + f)) {
1560-
_try_duplicate_item(FileOrFolder(old_path + f, false), new_path + f);
1561-
}
1506+
Error err = EditorFileSystem::get_singleton()->copy_directory(old_path, new_path);
1507+
if (err != OK) {
1508+
EditorNode::get_singleton()->add_io_error(TTR("Error duplicating directory:") + "\n" + old_path + "\n");
15621509
}
1563-
old_dir->list_dir_end();
15641510
}
15651511
}
15661512

@@ -1866,21 +1812,15 @@ void FileSystemDock::_rename_operation_confirm() {
18661812
}
18671813

18681814
void FileSystemDock::_duplicate_operation_confirm(const String &p_path) {
1869-
String base_dir = p_path.trim_suffix("/").get_base_dir();
1870-
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
1871-
if (!da->dir_exists(base_dir)) {
1872-
Error err = da->make_dir_recursive(base_dir);
1873-
1815+
const String base_dir = p_path.trim_suffix("/").get_base_dir();
1816+
if (!DirAccess::dir_exists_absolute(base_dir)) {
1817+
Error err = EditorFileSystem::get_singleton()->make_dir_recursive(base_dir);
18741818
if (err != OK) {
18751819
EditorNode::get_singleton()->show_warning(vformat(TTR("Could not create base directory: %s"), error_names[err]));
18761820
return;
18771821
}
18781822
}
18791823
_try_duplicate_item(to_duplicate, p_path);
1880-
1881-
// Rescan everything.
1882-
print_verbose("FileSystem: calling rescan.");
1883-
_rescan();
18841824
}
18851825

18861826
void FileSystemDock::_overwrite_dialog_action(bool p_overwrite) {

0 commit comments

Comments
 (0)