You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-04 12:00:25 +00:00
Implement Resource UIDs
* Most resource types now have unique identifiers. * Applies to text, binary and imported resources. * File formats reference both by text and UID (when available). UID always has priority. * Resource UIDs are 64 bits for better compatibility with the engine. * Can be represented and used textually, example `uuid://dapwmgsmnl28u`. * A special binary cache file is used and exported, containing the mappings. Example of how it looks: ```GDScript [gd_scene load_steps=2 format=3 uid="uid://dw86wq31afig2"] [ext_resource type="PackedScene" uid="uid://bt36ojelx8q6c" path="res://subscene.scn" id="1_t56hs"] ``` GDScript, shaders and other special resource files can't currently provide UIDs, but this should be doable with special keywords on the files. This will be reserved for future PRs.
This commit is contained in:
@@ -43,7 +43,7 @@
|
||||
|
||||
EditorFileSystem *EditorFileSystem::singleton = nullptr;
|
||||
//the name is the version, to keep compatibility with different versions of Godot
|
||||
#define CACHE_FILE_NAME "filesystem_cache6"
|
||||
#define CACHE_FILE_NAME "filesystem_cache7"
|
||||
|
||||
void EditorFileSystemDirectory::sort_files() {
|
||||
files.sort_custom<FileInfoSort>();
|
||||
@@ -116,7 +116,26 @@ String EditorFileSystemDirectory::get_file_path(int p_idx) const {
|
||||
|
||||
Vector<String> EditorFileSystemDirectory::get_file_deps(int p_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_idx, files.size(), Vector<String>());
|
||||
return files[p_idx]->deps;
|
||||
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) {
|
||||
//return proper dependency resoure from uid
|
||||
if (ResourceUID::get_singleton()->has_id(uid)) {
|
||||
dep = ResourceUID::get_singleton()->get_id_path(uid);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
deps.push_back(dep);
|
||||
}
|
||||
return deps;
|
||||
}
|
||||
|
||||
bool EditorFileSystemDirectory::get_file_import_is_valid(int p_idx) const {
|
||||
@@ -234,7 +253,7 @@ void EditorFileSystem::_scan_filesystem() {
|
||||
|
||||
} else {
|
||||
Vector<String> split = l.split("::");
|
||||
ERR_CONTINUE(split.size() != 8);
|
||||
ERR_CONTINUE(split.size() != 9);
|
||||
String name = split[0];
|
||||
String file;
|
||||
|
||||
@@ -243,15 +262,16 @@ void EditorFileSystem::_scan_filesystem() {
|
||||
|
||||
FileCache fc;
|
||||
fc.type = split[1];
|
||||
fc.modification_time = split[2].to_int();
|
||||
fc.import_modification_time = split[3].to_int();
|
||||
fc.import_valid = split[4].to_int() != 0;
|
||||
fc.import_group_file = split[5].strip_edges();
|
||||
fc.script_class_name = split[6].get_slice("<>", 0);
|
||||
fc.script_class_extends = split[6].get_slice("<>", 1);
|
||||
fc.script_class_icon_path = split[6].get_slice("<>", 2);
|
||||
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[7].strip_edges();
|
||||
String deps = split[8].strip_edges();
|
||||
if (deps.length()) {
|
||||
Vector<String> dp = deps.split("<>");
|
||||
for (int i = 0; i < dp.size(); i++) {
|
||||
@@ -368,6 +388,7 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo
|
||||
Vector<String> dest_files;
|
||||
String dest_md5 = "";
|
||||
int version = 0;
|
||||
bool found_uid = false;
|
||||
|
||||
while (true) {
|
||||
assign = Variant();
|
||||
@@ -395,6 +416,8 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo
|
||||
version = value;
|
||||
} else if (assign == "importer") {
|
||||
importer_name = value;
|
||||
} else if (assign == "uid") {
|
||||
found_uid = true;
|
||||
} else if (!p_only_imported_files) {
|
||||
if (assign == "source_file") {
|
||||
source_file = value;
|
||||
@@ -414,6 +437,10 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo
|
||||
return false; //keep mode, do not reimport
|
||||
}
|
||||
|
||||
if (!found_uid) {
|
||||
return true; //UUID not found, old format, reimport.
|
||||
}
|
||||
|
||||
Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name);
|
||||
|
||||
if (importer->get_format_version() > version) {
|
||||
@@ -582,6 +609,9 @@ bool EditorFileSystem::_update_scan_actions() {
|
||||
|
||||
if (reimports.size()) {
|
||||
reimport_files(reimports);
|
||||
} else {
|
||||
//reimport files will update the uid cache file so if nothing was reimported, update it manually
|
||||
ResourceUID::get_singleton()->update_cache();
|
||||
}
|
||||
|
||||
if (first_scan) {
|
||||
@@ -756,6 +786,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
|
||||
|
||||
if (fc && fc->modification_time == mt && fc->import_modification_time == import_mt && !_test_for_reimport(path, true)) {
|
||||
fi->type = fc->type;
|
||||
fi->uid = fc->uid;
|
||||
fi->deps = fc->deps;
|
||||
fi->modified_time = fc->modification_time;
|
||||
fi->import_modified_time = fc->import_modification_time;
|
||||
@@ -781,8 +812,14 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
|
||||
//note: I think this should not happen any longer..
|
||||
}
|
||||
|
||||
if (fc->uid == ResourceUID::INVALID_ID) {
|
||||
// imported files should always have a UUID, so attempt to fetch it.
|
||||
fi->uid = ResourceLoader::get_resource_uid(path);
|
||||
}
|
||||
|
||||
} else {
|
||||
fi->type = ResourceFormatImporter::get_singleton()->get_resource_type(path);
|
||||
fi->uid = ResourceFormatImporter::get_singleton()->get_resource_uid(path);
|
||||
fi->import_group_file = ResourceFormatImporter::get_singleton()->get_import_group_file(path);
|
||||
fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path);
|
||||
fi->modified_time = 0;
|
||||
@@ -799,6 +836,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
|
||||
if (fc && fc->modification_time == mt) {
|
||||
//not imported, so just update type if changed
|
||||
fi->type = fc->type;
|
||||
fi->uid = fc->uid;
|
||||
fi->modified_time = fc->modification_time;
|
||||
fi->deps = fc->deps;
|
||||
fi->import_modified_time = 0;
|
||||
@@ -809,6 +847,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
|
||||
} else {
|
||||
//new or modified time
|
||||
fi->type = ResourceLoader::get_resource_type(path);
|
||||
fi->uid = ResourceLoader::get_resource_uid(path);
|
||||
fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path);
|
||||
fi->deps = _get_dependencies(path);
|
||||
fi->modified_time = mt;
|
||||
@@ -817,6 +856,14 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
|
||||
ScriptLanguage *lang = ScriptServer::get_language(i);
|
||||
if (lang->supports_documentation() && fi->type == lang->get_type()) {
|
||||
@@ -1181,7 +1228,7 @@ void EditorFileSystem::_save_filesystem_cache(EditorFileSystemDirectory *p_dir,
|
||||
if (p_dir->files[i]->import_group_file != String()) {
|
||||
group_file_cache.insert(p_dir->files[i]->import_group_file);
|
||||
}
|
||||
String s = p_dir->files[i]->file + "::" + p_dir->files[i]->type + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid) + "::" + p_dir->files[i]->import_group_file + "::" + p_dir->files[i]->script_class_name + "<>" + p_dir->files[i]->script_class_extends + "<>" + p_dir->files[i]->script_class_icon_path;
|
||||
String s = p_dir->files[i]->file + "::" + p_dir->files[i]->type + "::" + itos(p_dir->files[i]->uid) + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid) + "::" + p_dir->files[i]->import_group_file + "::" + p_dir->files[i]->script_class_name + "<>" + p_dir->files[i]->script_class_extends + "<>" + p_dir->files[i]->script_class_icon_path;
|
||||
s += "::";
|
||||
for (int j = 0; j < p_dir->files[i]->deps.size(); j++) {
|
||||
if (j > 0) {
|
||||
@@ -1462,6 +1509,11 @@ void EditorFileSystem::update_file(const String &p_file) {
|
||||
//was removed
|
||||
_delete_internal_files(p_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);
|
||||
}
|
||||
}
|
||||
memdelete(fs->files[cpos]);
|
||||
fs->files.remove(cpos);
|
||||
}
|
||||
@@ -1472,6 +1524,7 @@ void EditorFileSystem::update_file(const String &p_file) {
|
||||
}
|
||||
|
||||
String type = ResourceLoader::get_resource_type(p_file);
|
||||
ResourceUID::ID uid = ResourceLoader::get_resource_uid(p_file);
|
||||
|
||||
if (cpos == -1) {
|
||||
// The file did not exist, it was added.
|
||||
@@ -1504,12 +1557,22 @@ void EditorFileSystem::update_file(const String &p_file) {
|
||||
}
|
||||
|
||||
fs->files[cpos]->type = type;
|
||||
fs->files[cpos]->uid = uid;
|
||||
fs->files[cpos]->script_class_name = _get_global_script_class(type, p_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(p_file);
|
||||
fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file);
|
||||
fs->files[cpos]->deps = _get_dependencies(p_file);
|
||||
fs->files[cpos]->import_valid = ResourceLoader::is_import_valid(p_file);
|
||||
|
||||
if (uid != ResourceUID::INVALID_ID) {
|
||||
if (ResourceUID::get_singleton()->has_id(uid)) {
|
||||
ResourceUID::get_singleton()->set_id(uid, p_file);
|
||||
} else {
|
||||
ResourceUID::get_singleton()->add_id(uid, p_file);
|
||||
}
|
||||
|
||||
ResourceUID::get_singleton()->update_cache();
|
||||
}
|
||||
// Update preview
|
||||
EditorResourcePreview::get_singleton()->check_for_invalidation(p_file);
|
||||
|
||||
@@ -1532,7 +1595,6 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector
|
||||
ERR_CONTINUE(file_importer_name == String());
|
||||
|
||||
if (importer_name != String() && importer_name != file_importer_name) {
|
||||
print_line("one importer '" + importer_name + "' the other '" + file_importer_name + "'.");
|
||||
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);
|
||||
}
|
||||
@@ -1702,6 +1764,8 @@ void EditorFileSystem::_reimport_file(const String &p_file, const Map<StringName
|
||||
params = *p_custom_options;
|
||||
}
|
||||
|
||||
ResourceUID::ID uid = ResourceUID::INVALID_ID;
|
||||
|
||||
if (FileAccess::exists(p_file + ".import")) {
|
||||
//use existing
|
||||
if (p_custom_options == nullptr) {
|
||||
@@ -1716,8 +1780,16 @@ void EditorFileSystem::_reimport_file(const String &p_file, const Map<StringName
|
||||
params[E->get()] = cf->get_value("params", E->get());
|
||||
}
|
||||
}
|
||||
if (p_custom_importer == String() && cf->has_section("remap")) {
|
||||
importer_name = cf->get_value("remap", "importer");
|
||||
|
||||
if (cf->has_section("remap")) {
|
||||
if (p_custom_importer == String()) {
|
||||
importer_name = cf->get_value("remap", "importer");
|
||||
}
|
||||
|
||||
if (cf->has_section_key("remap", "uid")) {
|
||||
String uidt = cf->get_value("remap", "uid");
|
||||
uid = ResourceUID::get_singleton()->text_to_id(uidt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1800,6 +1872,12 @@ void EditorFileSystem::_reimport_file(const String &p_file, const Map<StringName
|
||||
f->store_line("type=\"" + importer->get_resource_type() + "\"");
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
Vector<String> dest_paths;
|
||||
|
||||
if (err == OK) {
|
||||
@@ -1885,8 +1963,15 @@ void EditorFileSystem::_reimport_file(const String &p_file, const Map<StringName
|
||||
fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(p_file + ".import");
|
||||
fs->files[cpos]->deps = _get_dependencies(p_file);
|
||||
fs->files[cpos]->type = importer->get_resource_type();
|
||||
fs->files[cpos]->uid = uid;
|
||||
fs->files[cpos]->import_valid = ResourceLoader::is_import_valid(p_file);
|
||||
|
||||
if (ResourceUID::get_singleton()->has_id(uid)) {
|
||||
ResourceUID::get_singleton()->set_id(uid, p_file);
|
||||
} else {
|
||||
ResourceUID::get_singleton()->add_id(uid, p_file);
|
||||
}
|
||||
|
||||
//if file is currently up, maybe the source it was loaded from changed, so import math must be updated for it
|
||||
//to reload properly
|
||||
if (ResourceCache::has(p_file)) {
|
||||
@@ -1937,11 +2022,18 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) {
|
||||
Set<String> groups_to_reimport;
|
||||
|
||||
for (int i = 0; i < p_files.size(); i++) {
|
||||
String group_file = ResourceFormatImporter::get_singleton()->get_import_group_file(p_files[i]);
|
||||
String file = p_files[i];
|
||||
|
||||
if (group_file_cache.has(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);
|
||||
|
||||
if (group_file_cache.has(file)) {
|
||||
//maybe the file itself is a group!
|
||||
groups_to_reimport.insert(p_files[i]);
|
||||
groups_to_reimport.insert(file);
|
||||
//groups do not belong to grups
|
||||
group_file = String();
|
||||
} else if (group_file != String()) {
|
||||
@@ -1950,15 +2042,15 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) {
|
||||
} else {
|
||||
//it's a regular file
|
||||
ImportFile ifile;
|
||||
ifile.path = p_files[i];
|
||||
ResourceFormatImporter::get_singleton()->get_import_order_threads_and_importer(p_files[i], ifile.order, ifile.threaded, ifile.importer);
|
||||
ifile.path = file;
|
||||
ResourceFormatImporter::get_singleton()->get_import_order_threads_and_importer(file, ifile.order, ifile.threaded, ifile.importer);
|
||||
reimport_files.push_back(ifile);
|
||||
}
|
||||
|
||||
//group may have changed, so also update group reference
|
||||
EditorFileSystemDirectory *fs = nullptr;
|
||||
int cpos = -1;
|
||||
if (_find_file(p_files[i], &fs, cpos)) {
|
||||
if (_find_file(file, &fs, cpos)) {
|
||||
fs->files.write[cpos]->import_group_file = group_file;
|
||||
}
|
||||
}
|
||||
@@ -2023,6 +2115,8 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) {
|
||||
}
|
||||
}
|
||||
|
||||
ResourceUID::get_singleton()->update_cache(); //after reimporting, update the cache
|
||||
|
||||
_save_filesystem_cache();
|
||||
importing = false;
|
||||
if (!is_scanning()) {
|
||||
@@ -2108,6 +2202,30 @@ void EditorFileSystem::move_group_file(const String &p_path, const String &p_new
|
||||
}
|
||||
}
|
||||
|
||||
ResourceUID::ID EditorFileSystem::_resource_saver_get_resource_id_for_path(const String &p_path, bool p_generate) {
|
||||
if (!p_path.is_resource_file() || p_path.begins_with("res://.godot")) {
|
||||
//saved externally (configuration file) or internal file, do not assign an ID.
|
||||
return ResourceUID::INVALID_ID;
|
||||
}
|
||||
|
||||
EditorFileSystemDirectory *fs = nullptr;
|
||||
int cpos = -1;
|
||||
|
||||
if (!singleton->_find_file(p_path, &fs, cpos)) {
|
||||
if (p_generate) {
|
||||
return ResourceUID::get_singleton()->create_id(); //just create a new one, we will be notified of save anyway and fetch the right UUID at that time, to keep things simple.
|
||||
} else {
|
||||
return ResourceUID::INVALID_ID;
|
||||
}
|
||||
} else if (fs->files[cpos]->uid != ResourceUID::INVALID_ID) {
|
||||
return fs->files[cpos]->uid;
|
||||
} else if (p_generate) {
|
||||
return ResourceUID::get_singleton()->create_id(); //just create a new one, we will be notified of save anyway and fetch the right UUID at that time, to keep things simple.
|
||||
} else {
|
||||
return ResourceUID::INVALID_ID;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorFileSystem::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_filesystem"), &EditorFileSystem::get_filesystem);
|
||||
ClassDB::bind_method(D_METHOD("is_scanning"), &EditorFileSystem::is_scanning);
|
||||
@@ -2169,8 +2287,11 @@ EditorFileSystem::EditorFileSystem() {
|
||||
scan_changes_pending = false;
|
||||
revalidate_import_files = false;
|
||||
import_threads.init();
|
||||
ResourceUID::get_singleton()->clear(); //will be updated on scan
|
||||
ResourceSaver::set_get_resource_id_for_path(_resource_saver_get_resource_id_for_path);
|
||||
}
|
||||
|
||||
EditorFileSystem::~EditorFileSystem() {
|
||||
import_threads.finish();
|
||||
ResourceSaver::set_get_resource_id_for_path(nullptr);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user