Merge pull request #50786 from reduz/implement-resource-uids

Implement Resource UIDs
This commit is contained in:
Rémi Verschelde 2021-07-24 17:18:12 +02:00 committed by GitHub
commit 2b1e6e303e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 900 additions and 126 deletions

View file

@ -31,6 +31,7 @@
#ifndef RESOURCE_H
#define RESOURCE_H
#include "core/io/resource_uid.h"
#include "core/object/class_db.h"
#include "core/object/ref_counted.h"
#include "core/templates/safe_refcount.h"

View file

@ -816,13 +816,18 @@ String ResourceLoaderBinary::get_unicode_string() {
}
void ResourceLoaderBinary::get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types) {
open(p_f);
open(p_f, false, true);
if (error) {
return;
}
for (int i = 0; i < external_resources.size(); i++) {
String dep = external_resources[i].path;
String dep;
if (external_resources[i].uid != ResourceUID::INVALID_ID) {
dep = ResourceUID::get_singleton()->id_to_text(external_resources[i].uid);
} else {
dep = external_resources[i].path;
}
if (p_add_types && external_resources[i].type != String()) {
dep += "::" + external_resources[i].type;
@ -832,7 +837,7 @@ void ResourceLoaderBinary::get_dependencies(FileAccess *p_f, List<String> *p_dep
}
}
void ResourceLoaderBinary::open(FileAccess *p_f) {
void ResourceLoaderBinary::open(FileAccess *p_f, bool p_no_resources, bool p_keep_uuid_paths) {
error = OK;
f = p_f;
@ -891,10 +896,24 @@ void ResourceLoaderBinary::open(FileAccess *p_f) {
if (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_NAMED_SCENE_IDS) {
using_named_scene_ids = true;
}
for (int i = 0; i < 13; i++) {
if (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS) {
using_uids = true;
}
if (using_uids) {
uid = f->get_64();
} else {
uid = ResourceUID::INVALID_ID;
}
for (int i = 0; i < 5; i++) {
f->get_32(); //skip a few reserved fields
}
if (p_no_resources) {
return;
}
uint32_t string_table_size = f->get_32();
string_map.resize(string_table_size);
for (uint32_t i = 0; i < string_table_size; i++) {
@ -908,8 +927,18 @@ void ResourceLoaderBinary::open(FileAccess *p_f) {
for (uint32_t i = 0; i < ext_resources_size; i++) {
ExtResource er;
er.type = get_unicode_string();
er.path = get_unicode_string();
if (using_uids) {
er.uid = f->get_64();
if (!p_keep_uuid_paths && er.uid != ResourceUID::INVALID_ID) {
if (ResourceUID::get_singleton()->has_id(er.uid)) {
// If a UID is found and the path is valid, it will be used, otherwise, it falls back to the path.
er.path = ResourceUID::get_singleton()->get_id_path(er.uid);
} else {
WARN_PRINT(String(res_path + ": In external resouce #" + itos(i) + ", invalid UUID: " + ResourceUID::get_singleton()->id_to_text(er.uid) + " - using text path instead: " + er.path).utf8().get_data());
}
}
}
external_resources.push_back(er);
}
@ -1173,8 +1202,15 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
uint64_t importmd_ofs = f->get_64();
fw->store_64(0); //metadata offset
for (int i = 0; i < 14; i++) {
fw->store_32(0);
uint32_t flags = f->get_32();
bool using_uids = (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS);
uint64_t uid_data = f->get_64();
fw->store_32(flags);
fw->store_64(uid_data);
for (int i = 0; i < 5; i++) {
f->store_32(0); // reserved
f->get_32();
}
@ -1195,6 +1231,16 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
String type = get_ustring(f);
String path = get_ustring(f);
if (using_uids) {
ResourceUID::ID uid = f->get_64();
if (uid != ResourceUID::INVALID_ID) {
if (ResourceUID::get_singleton()->has_id(uid)) {
// If a UID is found and the path is valid, it will be used, otherwise, it falls back to the path.
path = ResourceUID::get_singleton()->get_id_path(uid);
}
}
}
bool relative = false;
if (!path.begins_with("res://")) {
path = local_path.plus_file(path).simplify_path();
@ -1206,6 +1252,8 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
path = np;
}
String full_path = path;
if (relative) {
//restore relative
path = local_path.path_to_file(path);
@ -1213,6 +1261,11 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
save_ustring(fw, type);
save_ustring(fw, path);
if (using_uids) {
ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(full_path);
f->store_64(uid);
}
}
int64_t size_diff = (int64_t)fw->get_position() - (int64_t)f->get_position();
@ -1268,6 +1321,28 @@ String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const
return ClassDB::get_compatibility_remapped_class(r);
}
ResourceUID::ID ResourceFormatLoaderBinary::get_resource_uid(const String &p_path) const {
String ext = p_path.get_extension().to_lower();
if (!ClassDB::is_resource_extension(ext)) {
return ResourceUID::INVALID_ID;
}
FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
if (!f) {
return ResourceUID::INVALID_ID; //could not read
}
ResourceLoaderBinary loader;
loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
loader.res_path = loader.local_path;
//loader.set_local_path( Globals::get_singleton()->localize_path(p_path) );
loader.open(f, true);
if (loader.error != OK) {
return ResourceUID::INVALID_ID; //could not read
}
return loader.uid;
}
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
@ -1824,8 +1899,10 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
save_unicode_string(f, p_resource->get_class());
f->store_64(0); //offset to import metadata
f->store_32(FORMAT_FLAG_NAMED_SCENE_IDS);
for (int i = 0; i < 13; i++) {
f->store_32(FORMAT_FLAG_NAMED_SCENE_IDS | FORMAT_FLAG_UIDS);
ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(p_path, true);
f->store_64(uid);
for (int i = 0; i < 5; i++) {
f->store_32(0); // reserved
}
@ -1891,6 +1968,8 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
String path = save_order[i]->get_path();
path = relative_paths ? local_path.path_to_file(path) : path;
save_unicode_string(f, path);
ResourceUID::ID ruid = ResourceSaver::get_resource_id_for_path(save_order[i]->get_path(), false);
f->store_64(ruid);
}
// save internal resource table
f->store_32(saved_resources.size()); //amount of internal resources

View file

@ -47,6 +47,8 @@ class ResourceLoaderBinary {
uint64_t importmd_ofs = 0;
ResourceUID::ID uid = ResourceUID::INVALID_ID;
Vector<char> str_buf;
List<RES> resource_cache;
@ -57,10 +59,12 @@ class ResourceLoaderBinary {
struct ExtResource {
String path;
String type;
ResourceUID::ID uid = ResourceUID::INVALID_ID;
RES cache;
};
bool using_named_scene_ids = false;
bool using_uids = false;
bool use_sub_threads = false;
float *progress = nullptr;
Vector<ExtResource> external_resources;
@ -94,7 +98,7 @@ public:
void set_translation_remapped(bool p_remapped);
void set_remaps(const Map<String, String> &p_remaps) { remaps = p_remaps; }
void open(FileAccess *p_f);
void open(FileAccess *p_f, bool p_no_resources = false, bool p_keep_uuid_paths = false);
String recognize(FileAccess *p_f);
void get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types);
@ -109,6 +113,7 @@ public:
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
virtual Error rename_dependencies(const String &p_path, const Map<String, String> &p_map);
};
@ -157,7 +162,8 @@ class ResourceFormatSaverBinaryInstance {
public:
enum {
FORMAT_FLAG_NAMED_SCENE_IDS = 1
FORMAT_FLAG_NAMED_SCENE_IDS = 1,
FORMAT_FLAG_UIDS = 2,
};
Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
static void write_variant(FileAccess *f, const Variant &p_property, Map<RES, int> &resource_map, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint = PropertyInfo());

View file

@ -93,6 +93,8 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy
r_path_and_type.type = ClassDB::get_compatibility_remapped_class(value);
} else if (assign == "importer") {
r_path_and_type.importer = value;
} else if (assign == "uid") {
r_path_and_type.uid = ResourceUID::get_singleton()->text_to_id(value);
} else if (assign == "group_file") {
r_path_and_type.group_file = value;
} else if (assign == "metadata") {
@ -336,6 +338,17 @@ String ResourceFormatImporter::get_resource_type(const String &p_path) const {
return pat.type;
}
ResourceUID::ID ResourceFormatImporter::get_resource_uid(const String &p_path) const {
PathAndType pat;
Error err = _get_path_and_type(p_path, pat);
if (err != OK) {
return ResourceUID::INVALID_ID;
}
return pat.uid;
}
Variant ResourceFormatImporter::get_resource_metadata(const String &p_path) const {
PathAndType pat;
Error err = _get_path_and_type(p_path, pat);

View file

@ -42,6 +42,7 @@ class ResourceFormatImporter : public ResourceFormatLoader {
String importer;
String group_file;
Variant metadata;
uint64_t uid = ResourceUID::INVALID_ID;
};
Error _get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool *r_valid = nullptr) const;
@ -63,6 +64,8 @@ public:
virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
virtual Variant get_resource_metadata(const String &p_path) const;
virtual bool is_import_valid(const String &p_path) const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);

View file

@ -84,6 +84,14 @@ String ResourceFormatLoader::get_resource_type(const String &p_path) const {
return "";
}
ResourceUID::ID ResourceFormatLoader::get_resource_uid(const String &p_path) const {
if (get_script_instance() && get_script_instance()->has_method("_get_resource_uid")) {
return get_script_instance()->call("_get_resource_uid", p_path);
}
return ResourceUID::INVALID_ID;
}
void ResourceFormatLoader::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const {
if (p_type == "" || handles_type(p_type)) {
get_recognized_extensions(p_extensions);
@ -270,13 +278,18 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
thread_load_mutex->unlock();
}
Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, ResourceFormatLoader::CacheMode p_cache_mode, const String &p_source_resource) {
String local_path;
if (p_path.is_rel_path()) {
local_path = "res://" + p_path;
static String _validate_local_path(const String &p_path) {
ResourceUID::ID uid = ResourceUID::get_singleton()->text_to_id(p_path);
if (uid != ResourceUID::INVALID_ID) {
return ResourceUID::get_singleton()->get_id_path(uid);
} else if (p_path.is_rel_path()) {
return "res://" + p_path;
} else {
local_path = ProjectSettings::get_singleton()->localize_path(p_path);
return ProjectSettings::get_singleton()->localize_path(p_path);
}
}
Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, ResourceFormatLoader::CacheMode p_cache_mode, const String &p_source_resource) {
String local_path = _validate_local_path(p_path);
thread_load_mutex->lock();
@ -399,12 +412,7 @@ float ResourceLoader::_dependency_get_progress(const String &p_path) {
}
ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const String &p_path, float *r_progress) {
String local_path;
if (p_path.is_rel_path()) {
local_path = "res://" + p_path;
} else {
local_path = ProjectSettings::get_singleton()->localize_path(p_path);
}
String local_path = _validate_local_path(p_path);
thread_load_mutex->lock();
if (!thread_load_tasks.has(local_path)) {
@ -424,12 +432,7 @@ ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const
}
RES ResourceLoader::load_threaded_get(const String &p_path, Error *r_error) {
String local_path;
if (p_path.is_rel_path()) {
local_path = "res://" + p_path;
} else {
local_path = ProjectSettings::get_singleton()->localize_path(p_path);
}
String local_path = _validate_local_path(p_path);
thread_load_mutex->lock();
if (!thread_load_tasks.has(local_path)) {
@ -510,12 +513,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, Resour
*r_error = ERR_CANT_OPEN;
}
String local_path;
if (p_path.is_rel_path()) {
local_path = "res://" + p_path;
} else {
local_path = ProjectSettings::get_singleton()->localize_path(p_path);
}
String local_path = _validate_local_path(p_path);
if (p_cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
thread_load_mutex->lock();
@ -612,12 +610,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, Resour
}
bool ResourceLoader::exists(const String &p_path, const String &p_type_hint) {
String local_path;
if (p_path.is_rel_path()) {
local_path = "res://" + p_path;
} else {
local_path = ProjectSettings::get_singleton()->localize_path(p_path);
}
String local_path = _validate_local_path(p_path);
if (ResourceCache::has(local_path)) {
return true; // If cached, it probably exists
@ -677,14 +670,7 @@ void ResourceLoader::remove_resource_format_loader(Ref<ResourceFormatLoader> p_f
}
int ResourceLoader::get_import_order(const String &p_path) {
String path = _path_remap(p_path);
String local_path;
if (path.is_rel_path()) {
local_path = "res://" + path;
} else {
local_path = ProjectSettings::get_singleton()->localize_path(path);
}
String local_path = _path_remap(_validate_local_path(p_path));
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path)) {
@ -702,14 +688,7 @@ int ResourceLoader::get_import_order(const String &p_path) {
}
String ResourceLoader::get_import_group_file(const String &p_path) {
String path = _path_remap(p_path);
String local_path;
if (path.is_rel_path()) {
local_path = "res://" + path;
} else {
local_path = ProjectSettings::get_singleton()->localize_path(path);
}
String local_path = _path_remap(_validate_local_path(p_path));
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path)) {
@ -727,14 +706,7 @@ String ResourceLoader::get_import_group_file(const String &p_path) {
}
bool ResourceLoader::is_import_valid(const String &p_path) {
String path = _path_remap(p_path);
String local_path;
if (path.is_rel_path()) {
local_path = "res://" + path;
} else {
local_path = ProjectSettings::get_singleton()->localize_path(path);
}
String local_path = _path_remap(_validate_local_path(p_path));
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path)) {
@ -752,14 +724,7 @@ bool ResourceLoader::is_import_valid(const String &p_path) {
}
bool ResourceLoader::is_imported(const String &p_path) {
String path = _path_remap(p_path);
String local_path;
if (path.is_rel_path()) {
local_path = "res://" + path;
} else {
local_path = ProjectSettings::get_singleton()->localize_path(path);
}
String local_path = _path_remap(_validate_local_path(p_path));
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path)) {
@ -777,14 +742,7 @@ bool ResourceLoader::is_imported(const String &p_path) {
}
void ResourceLoader::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
String path = _path_remap(p_path);
String local_path;
if (path.is_rel_path()) {
local_path = "res://" + path;
} else {
local_path = ProjectSettings::get_singleton()->localize_path(path);
}
String local_path = _path_remap(_validate_local_path(p_path));
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path)) {
@ -800,14 +758,7 @@ void ResourceLoader::get_dependencies(const String &p_path, List<String> *p_depe
}
Error ResourceLoader::rename_dependencies(const String &p_path, const Map<String, String> &p_map) {
String path = _path_remap(p_path);
String local_path;
if (path.is_rel_path()) {
local_path = "res://" + path;
} else {
local_path = ProjectSettings::get_singleton()->localize_path(path);
}
String local_path = _path_remap(_validate_local_path(p_path));
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path)) {
@ -825,12 +776,7 @@ Error ResourceLoader::rename_dependencies(const String &p_path, const Map<String
}
String ResourceLoader::get_resource_type(const String &p_path) {
String local_path;
if (p_path.is_rel_path()) {
local_path = "res://" + p_path;
} else {
local_path = ProjectSettings::get_singleton()->localize_path(p_path);
}
String local_path = _validate_local_path(p_path);
for (int i = 0; i < loader_count; i++) {
String result = loader[i]->get_resource_type(local_path);
@ -842,6 +788,19 @@ String ResourceLoader::get_resource_type(const String &p_path) {
return "";
}
ResourceUID::ID ResourceLoader::get_resource_uid(const String &p_path) {
String local_path = _validate_local_path(p_path);
for (int i = 0; i < loader_count; i++) {
ResourceUID::ID id = loader[i]->get_resource_uid(local_path);
if (id != ResourceUID::INVALID_ID) {
return id;
}
}
return ResourceUID::INVALID_ID;
}
String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_remapped) {
String new_path = p_path;

View file

@ -56,6 +56,7 @@ public:
virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
virtual Error rename_dependencies(const String &p_path, const Map<String, String> &p_map);
virtual bool is_import_valid(const String &p_path) const { return true; }
@ -157,6 +158,7 @@ public:
static void add_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader, bool p_at_front = false);
static void remove_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader);
static String get_resource_type(const String &p_path);
static ResourceUID::ID get_resource_uid(const String &p_path);
static void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
static Error rename_dependencies(const String &p_path, const Map<String, String> &p_map);
static bool is_import_valid(const String &p_path);

View file

@ -39,6 +39,7 @@ Ref<ResourceFormatSaver> ResourceSaver::saver[MAX_SAVERS];
int ResourceSaver::saver_count = 0;
bool ResourceSaver::timestamp_on_save = false;
ResourceSavedCallback ResourceSaver::save_callback = nullptr;
ResourceSaverGetResourceIDForPath ResourceSaver::save_get_id_for_path = nullptr;
Error ResourceFormatSaver::save(const String &p_path, const RES &p_resource, uint32_t p_flags) {
if (get_script_instance() && get_script_instance()->has_method("_save")) {
@ -258,3 +259,14 @@ void ResourceSaver::remove_custom_savers() {
remove_resource_format_saver(custom_savers[i]);
}
}
ResourceUID::ID ResourceSaver::get_resource_id_for_path(const String &p_path, bool p_generate) {
if (save_get_id_for_path) {
return save_get_id_for_path(p_path, p_generate);
}
return ResourceUID::INVALID_ID;
}
void ResourceSaver::set_get_resource_id_for_path(ResourceSaverGetResourceIDForPath p_callback) {
save_get_id_for_path = p_callback;
}

View file

@ -48,6 +48,7 @@ public:
};
typedef void (*ResourceSavedCallback)(Ref<Resource> p_resource, const String &p_path);
typedef ResourceUID::ID (*ResourceSaverGetResourceIDForPath)(const String &p_path, bool p_generate);
class ResourceSaver {
enum {
@ -58,6 +59,7 @@ class ResourceSaver {
static int saver_count;
static bool timestamp_on_save;
static ResourceSavedCallback save_callback;
static ResourceSaverGetResourceIDForPath save_get_id_for_path;
static Ref<ResourceFormatSaver> _find_custom_resource_format_saver(String path);
@ -80,7 +82,10 @@ public:
static void set_timestamp_on_save(bool p_timestamp) { timestamp_on_save = p_timestamp; }
static bool get_timestamp_on_save() { return timestamp_on_save; }
static ResourceUID::ID get_resource_id_for_path(const String &p_path, bool p_generate = false);
static void set_save_callback(ResourceSavedCallback p_callback);
static void set_get_resource_id_for_path(ResourceSaverGetResourceIDForPath p_callback);
static bool add_custom_resource_format_saver(String script_path);
static void remove_custom_resource_format_saver(String script_path);

262
core/io/resource_uid.cpp Normal file
View file

@ -0,0 +1,262 @@
/*************************************************************************/
/* resource_uid.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
#include "resource_uid.h"
#include "core/crypto/crypto.h"
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
static constexpr uint32_t char_count = ('z' - 'a');
static constexpr uint32_t base = char_count + ('9' - '0');
const char *ResourceUID::CACHE_FILE = "res://.godot/uid_cache.bin";
String ResourceUID::id_to_text(ID p_id) const {
if (p_id < 0) {
return "uid://<invalid>";
}
String txt;
while (p_id) {
uint32_t c = p_id % base;
if (c < char_count) {
txt = String::chr('a' + c) + txt;
} else {
txt = String::chr('0' + (c - char_count)) + txt;
}
p_id /= base;
}
return "uid://" + txt;
}
ResourceUID::ID ResourceUID::text_to_id(const String &p_text) const {
if (!p_text.begins_with("uid://") || p_text == "uid://<invalid>") {
return INVALID_ID;
}
uint32_t l = p_text.length();
uint64_t uid = 0;
for (uint32_t i = 6; i < l; i++) {
uid *= base;
uint32_t c = p_text[i];
if (c >= 'a' && c <= 'z') {
uid += c - 'a';
} else if (c >= '0' && c <= '9') {
uid += c - '0' + char_count;
} else {
return INVALID_ID;
}
}
return ID(uid & 0x7FFFFFFFFFFFFFFF);
}
ResourceUID::ID ResourceUID::create_id() const {
mutex.lock();
if (crypto.is_null()) {
crypto = Ref<Crypto>(Crypto::create());
}
mutex.unlock();
while (true) {
PackedByteArray bytes = crypto->generate_random_bytes(8);
ERR_FAIL_COND_V(bytes.size() != 8, INVALID_ID);
const uint64_t *ptr64 = (const uint64_t *)bytes.ptr();
ID id = int64_t((*ptr64) & 0x7FFFFFFFFFFFFFFF);
mutex.lock();
bool exists = unique_ids.has(id);
mutex.unlock();
if (!exists) {
return id;
}
}
}
bool ResourceUID::has_id(ID p_id) const {
MutexLock l(mutex);
return unique_ids.has(p_id);
}
void ResourceUID::add_id(ID p_id, const String &p_path) {
MutexLock l(mutex);
ERR_FAIL_COND(unique_ids.has(p_id));
Cache c;
c.cs = p_path.utf8();
unique_ids[p_id] = c;
changed = true;
}
void ResourceUID::set_id(ID p_id, const String &p_path) {
MutexLock l(mutex);
ERR_FAIL_COND(!unique_ids.has(p_id));
CharString cs = p_path.utf8();
if (strcmp(cs.ptr(), unique_ids[p_id].cs.ptr()) != 0) {
unique_ids[p_id].cs = cs;
unique_ids[p_id].saved_to_cache = false; //changed
changed = true;
}
}
String ResourceUID::get_id_path(ID p_id) const {
MutexLock l(mutex);
ERR_FAIL_COND_V(!unique_ids.has(p_id), String());
const CharString &cs = unique_ids[p_id].cs;
String s(cs.ptr());
return s;
}
void ResourceUID::remove_id(ID p_id) {
MutexLock l(mutex);
ERR_FAIL_COND(!unique_ids.has(p_id));
unique_ids.erase(p_id);
}
Error ResourceUID::save_to_cache() {
if (!FileAccess::exists(CACHE_FILE)) {
DirAccessRef d = DirAccess::create(DirAccess::ACCESS_RESOURCES);
d->make_dir_recursive(String(CACHE_FILE).get_base_dir()); //ensure base dir exists
}
FileAccessRef f = FileAccess::open(CACHE_FILE, FileAccess::WRITE);
if (!f) {
return ERR_CANT_OPEN;
}
MutexLock l(mutex);
f->store_32(unique_ids.size());
cache_entries = 0;
for (OrderedHashMap<ID, Cache>::Element E = unique_ids.front(); E; E = E.next()) {
f->store_64(E.key());
uint32_t s = E.get().cs.length();
f->store_32(s);
f->store_buffer((const uint8_t *)E.get().cs.ptr(), s);
E.get().saved_to_cache = true;
cache_entries++;
}
changed = false;
return OK;
}
Error ResourceUID::load_from_cache() {
FileAccessRef f = FileAccess::open(CACHE_FILE, FileAccess::READ);
if (!f) {
return ERR_CANT_OPEN;
}
MutexLock l(mutex);
unique_ids.clear();
uint32_t entry_count = f->get_32();
for (uint32_t i = 0; i < entry_count; i++) {
int64_t id = f->get_64();
int32_t len = f->get_32();
Cache c;
c.cs.resize(len + 1);
ERR_FAIL_COND_V(c.cs.size() != len + 1, ERR_FILE_CORRUPT); // out of memory
c.cs[len] = 0;
int32_t rl = f->get_buffer((uint8_t *)c.cs.ptrw(), len);
ERR_FAIL_COND_V(rl != len, ERR_FILE_CORRUPT);
c.saved_to_cache = true;
unique_ids[id] = c;
}
cache_entries = entry_count;
changed = false;
return OK;
}
Error ResourceUID::update_cache() {
if (!changed) {
return OK;
}
if (cache_entries == 0) {
return save_to_cache();
}
MutexLock l(mutex);
FileAccess *f = nullptr;
for (OrderedHashMap<ID, Cache>::Element E = unique_ids.front(); E; E = E.next()) {
if (!E.get().saved_to_cache) {
if (f == nullptr) {
f = FileAccess::open(CACHE_FILE, FileAccess::READ_WRITE); //append
if (!f) {
return ERR_CANT_OPEN;
}
f->seek_end();
}
f->store_64(E.key());
uint32_t s = E.get().cs.length();
f->store_32(s);
f->store_buffer((const uint8_t *)E.get().cs.ptr(), s);
E.get().saved_to_cache = true;
cache_entries++;
}
}
if (f != nullptr) {
f->seek(0);
f->store_32(cache_entries); //update amount of entries
f->close();
memdelete(f);
}
changed = false;
return OK;
}
void ResourceUID::clear() {
cache_entries = 0;
unique_ids.clear();
changed = false;
}
void ResourceUID::_bind_methods() {
ClassDB::bind_method(D_METHOD("id_to_text", "id"), &ResourceUID::id_to_text);
ClassDB::bind_method(D_METHOD("text_to_id", "text_id"), &ResourceUID::text_to_id);
ClassDB::bind_method(D_METHOD("create_id"), &ResourceUID::create_id);
ClassDB::bind_method(D_METHOD("has_id", "id"), &ResourceUID::has_id);
ClassDB::bind_method(D_METHOD("add_id", "id", "path"), &ResourceUID::add_id);
ClassDB::bind_method(D_METHOD("set_id", "id", "path"), &ResourceUID::set_id);
ClassDB::bind_method(D_METHOD("get_id_path", "id"), &ResourceUID::get_id_path);
ClassDB::bind_method(D_METHOD("remove_id", "id", "path"), &ResourceUID::remove_id);
BIND_CONSTANT(INVALID_ID)
}
ResourceUID *ResourceUID::singleton = nullptr;
ResourceUID::ResourceUID() {
ERR_FAIL_COND(singleton != nullptr);
singleton = this;
}
ResourceUID::~ResourceUID() {
}

89
core/io/resource_uid.h Normal file
View file

@ -0,0 +1,89 @@
/*************************************************************************/
/* resource_uid.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
#ifndef RESOURCE_UUID_H
#define RESOURCE_UUID_H
#include "core/object/ref_counted.h"
#include "core/string/string_name.h"
#include "core/templates/ordered_hash_map.h"
class Crypto;
class ResourceUID : public Object {
GDCLASS(ResourceUID, Object)
public:
typedef int64_t ID;
enum {
INVALID_ID = -1
};
static const char *CACHE_FILE;
private:
mutable Ref<Crypto> crypto;
Mutex mutex;
struct Cache {
CharString cs;
bool saved_to_cache = false;
};
OrderedHashMap<ID, Cache> unique_ids; //unique IDs and utf8 paths (less memory used)
static ResourceUID *singleton;
uint32_t cache_entries = 0;
bool changed = false;
protected:
static void _bind_methods();
public:
String id_to_text(ID p_id) const;
ID text_to_id(const String &p_text) const;
ID create_id() const;
bool has_id(ID p_id) const;
void add_id(ID p_id, const String &p_path);
void set_id(ID p_id, const String &p_path);
String get_id_path(ID p_id) const;
void remove_id(ID p_id);
Error load_from_cache();
Error save_to_cache();
Error update_cache();
void clear();
static ResourceUID *get_singleton() { return singleton; }
ResourceUID();
~ResourceUID();
};
#endif // RESOURCEUUID_H

View file

@ -1495,6 +1495,10 @@ void ClassDB::get_resource_base_extensions(List<String> *p_extensions) {
}
}
bool ClassDB::is_resource_extension(const StringName &p_extension) {
return resource_base_extensions.has(p_extension);
}
void ClassDB::get_extensions_for_type(const StringName &p_class, List<String> *p_extensions) {
const StringName *K = nullptr;

View file

@ -396,6 +396,7 @@ public:
static void add_resource_base_extension(const StringName &p_extension, const StringName &p_class);
static void get_resource_base_extensions(List<String> *p_extensions);
static void get_extensions_for_type(const StringName &p_class, List<String> *p_extensions);
static bool is_resource_extension(const StringName &p_extension);
static void add_compatibility_class(const StringName &p_class, const StringName &p_fallback);

View file

@ -56,6 +56,7 @@
#include "core/io/pck_packer.h"
#include "core/io/resource_format_binary.h"
#include "core/io/resource_importer.h"
#include "core/io/resource_uid.h"
#include "core/io/stream_peer_ssl.h"
#include "core/io/tcp_server.h"
#include "core/io/translation_loader_po.h"
@ -102,6 +103,8 @@ static NativeExtensionManager *native_extension_manager = nullptr;
extern void register_global_constants();
extern void unregister_global_constants();
static ResourceUID *resource_uid = nullptr;
void register_core_types() {
//consistency check
static_assert(sizeof(Callable) <= 16);
@ -225,6 +228,10 @@ void register_core_types() {
GDREGISTER_VIRTUAL_CLASS(NativeExtensionManager);
GDREGISTER_VIRTUAL_CLASS(ResourceUID);
resource_uid = memnew(ResourceUID);
native_extension_manager = memnew(NativeExtensionManager);
ip = IP::create();
@ -286,6 +293,7 @@ void register_core_singletons() {
Engine::get_singleton()->add_singleton(Engine::Singleton("EngineDebugger", _EngineDebugger::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("Time", Time::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("NativeExtensionManager", NativeExtensionManager::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceUID", ResourceUID::get_singleton()));
}
void register_core_extensions() {
@ -304,6 +312,8 @@ void unregister_core_types() {
native_extension_manager->deinitialize_extensions(NativeExtension::INITIALIZATION_LEVEL_CORE);
memdelete(native_extension_manager);
memdelete(resource_uid);
memdelete(_resource_loader);
memdelete(_resource_saver);
memdelete(_os);

View file

@ -1259,6 +1259,8 @@
<member name="ResourceSaver" type="ResourceSaver" setter="" getter="">
The [ResourceSaver] singleton.
</member>
<member name="ResourceUID" type="ResourceUID" setter="" getter="">
</member>
<member name="TextServerManager" type="TextServerManager" setter="" getter="">
The [TextServerManager] singleton.
</member>

View file

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ResourceUID" inherits="Object" version="4.0">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
<methods>
<method name="add_id">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="path" type="String">
</argument>
<description>
</description>
</method>
<method name="create_id" qualifiers="const">
<return type="int">
</return>
<description>
</description>
</method>
<method name="get_id_path" qualifiers="const">
<return type="String">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
</description>
</method>
<method name="has_id" qualifiers="const">
<return type="bool">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
</description>
</method>
<method name="id_to_text" qualifiers="const">
<return type="String">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
</description>
</method>
<method name="set_id">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="path" type="String">
</argument>
<description>
</description>
</method>
<method name="text_to_id" qualifiers="const">
<return type="int">
</return>
<argument index="0" name="text_id" type="String">
</argument>
<description>
</description>
</method>
</methods>
<constants>
<constant name="INVALID_ID" value="-1">
</constant>
</constants>
</class>

View file

@ -111,7 +111,7 @@ Error FileAccessUnix::_open(const String &p_path, int p_mode_flags) {
}
}
if (is_backup_save_enabled() && (p_mode_flags & WRITE) && !(p_mode_flags & READ)) {
if (is_backup_save_enabled() && (p_mode_flags == WRITE)) {
save_path = path;
path = path + ".tmp";
}

View file

@ -108,7 +108,7 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) {
}
#endif
if (is_backup_save_enabled() && p_mode_flags & WRITE && !(p_mode_flags & READ)) {
if (is_backup_save_enabled() && p_mode_flags == WRITE) {
save_path = path;
path = path + ".tmp";
}

View file

@ -178,6 +178,15 @@ void DependencyEditor::_update_list() {
path = n;
type = "Resource";
}
ResourceUID::ID uid = ResourceUID::get_singleton()->text_to_id(path);
if (uid != ResourceUID::INVALID_ID) {
// dependency is in uid format, obtain proper path
ERR_CONTINUE(!ResourceUID::get_singleton()->has_id(uid));
path = ResourceUID::get_singleton()->get_id_path(uid);
}
String name = path.get_file();
Ref<Texture2D> icon = EditorNode::get_singleton()->get_class_icon(type);

View file

@ -1048,6 +1048,13 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
return err;
}
}
if (FileAccess::exists(ResourceUID::CACHE_FILE)) {
Vector<uint8_t> array = FileAccess::get_file_as_array(ResourceUID::CACHE_FILE);
err = p_func(p_udata, ResourceUID::CACHE_FILE, array, idx, total, enc_in_filters, enc_ex_filters, key);
if (err != OK) {
return err;
}
}
// Store text server data if it is supported.
if (TS->has_feature(TextServer::FEATURE_USE_SUPPORT_DATA)) {

View file

@ -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) {
@ -580,6 +607,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) {
@ -754,6 +784,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;
@ -779,8 +810,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;
@ -797,6 +834,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;
@ -807,6 +845,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;
@ -815,6 +854,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()) {
@ -1179,7 +1226,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) {
@ -1460,6 +1507,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);
}
@ -1470,6 +1522,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.
@ -1502,12 +1555,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);
@ -1530,7 +1593,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);
}
@ -1699,6 +1761,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) {
@ -1713,8 +1777,16 @@ void EditorFileSystem::_reimport_file(const String &p_file, const Map<StringName
params[E] = cf->get_value("params", E);
}
}
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);
}
}
}
}
@ -1797,6 +1869,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) {
@ -1882,8 +1960,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)) {
@ -1934,11 +2019,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()) {
@ -1947,15 +2039,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;
}
}
@ -2020,6 +2112,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()) {
@ -2104,6 +2198,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);
@ -2165,8 +2283,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);
}

View file

@ -55,6 +55,7 @@ class EditorFileSystemDirectory : public Object {
struct FileInfo {
String file;
StringName type;
ResourceUID::ID uid = ResourceUID::INVALID_ID;
uint64_t modified_time = 0;
uint64_t import_modified_time = 0;
bool import_valid = false;
@ -159,6 +160,7 @@ class EditorFileSystem : public Node {
/* Used for reading the filesystem cache file */
struct FileCache {
String type;
ResourceUID::ID uid = ResourceUID::INVALID_ID;
uint64_t modification_time = 0;
uint64_t import_modification_time = 0;
Vector<String> deps;
@ -251,6 +253,8 @@ class EditorFileSystem : public Node {
void _reimport_thread(uint32_t p_index, ImportThreadData *p_import_data);
static ResourceUID::ID _resource_saver_get_resource_id_for_path(const String &p_path, bool p_generate);
protected:
void _notification(int p_what);
static void _bind_methods();

View file

@ -1089,6 +1089,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
// Initialize user data dir.
OS::get_singleton()->ensure_user_data_dir();
ResourceUID::get_singleton()->load_from_cache(); // load UUIDs from cache.
GLOBAL_DEF("memory/limits/multithreaded_server/rid_pool_prealloc", 60);
ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/multithreaded_server/rid_pool_prealloc",
PropertyInfo(Variant::INT,

View file

@ -413,6 +413,17 @@ Error ResourceLoaderText::load() {
String type = next_tag.fields["type"];
String id = next_tag.fields["id"];
if (next_tag.fields.has("uid")) {
String uidt = next_tag.fields["uid"];
ResourceUID::ID uid = ResourceUID::get_singleton()->text_to_id(uidt);
if (uid != ResourceUID::INVALID_ID && ResourceUID::get_singleton()->has_id(uid)) {
// If a UID is found and the path is valid, it will be used, otherwise, it falls back to the path.
path = ResourceUID::get_singleton()->get_id_path(uid);
} else {
WARN_PRINT(String(res_path + ":" + itos(lines) + " - ext_resource, invalid UUID: " + uidt + " - using text path instead: " + path).utf8().get_data());
}
}
if (path.find("://") == -1 && path.is_rel_path()) {
// path is relative to file being loaded, so convert to a resource path
path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path));
@ -746,7 +757,18 @@ void ResourceLoaderText::get_dependencies(FileAccess *p_f, List<String> *p_depen
String path = next_tag.fields["path"];
String type = next_tag.fields["type"];
if (path.find("://") == -1 && path.is_rel_path()) {
bool using_uid = false;
if (next_tag.fields.has("uid")) {
//if uid exists, return uid in text format, not the path
String uidt = next_tag.fields["uid"];
ResourceUID::ID uid = ResourceUID::get_singleton()->text_to_id(uidt);
if (uid != ResourceUID::INVALID_ID) {
path = ResourceUID::get_singleton()->id_to_text(uid);
using_uid = true;
}
}
if (!using_uid && path.find("://") == -1 && path.is_rel_path()) {
// path is relative to file being loaded, so convert to a resource path
path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path));
}
@ -819,6 +841,14 @@ Error ResourceLoaderText::rename_dependencies(FileAccess *p_f, const String &p_p
String id = next_tag.fields["id"];
String type = next_tag.fields["type"];
if (next_tag.fields.has("uid")) {
String uidt = next_tag.fields["uid"];
ResourceUID::ID uid = ResourceUID::get_singleton()->text_to_id(uidt);
if (uid != ResourceUID::INVALID_ID && ResourceUID::get_singleton()->has_id(uid)) {
// If a UID is found and the path is valid, it will be used, otherwise, it falls back to the path.
path = ResourceUID::get_singleton()->get_id_path(uid);
}
}
bool relative = false;
if (!path.begins_with("res://")) {
path = base_path.plus_file(path).simplify_path();
@ -835,7 +865,14 @@ Error ResourceLoaderText::rename_dependencies(FileAccess *p_f, const String &p_p
path = base_path.path_to_file(path);
}
fw->store_line("[ext_resource path=\"" + path + "\" type=\"" + type + "\" id=\"" + id + "\"]");
String s = "[ext_resource type=\"" + type + "\"";
ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(path);
if (uid != ResourceUID::INVALID_ID) {
s += " uid=\"" + ResourceUID::get_singleton()->id_to_text(uid) + "\"";
}
s += " path=\"" + path + "\" id=\"" + id + "\"]";
fw->store_line(s); // Bundled.
tag_end = f->get_position();
}
@ -921,6 +958,12 @@ void ResourceLoaderText::open(FileAccess *p_f, bool p_skip_first_tag) {
return;
}
if (tag.fields.has("uid")) {
res_uid = ResourceUID::get_singleton()->text_to_id(tag.fields["uid"]);
} else {
res_uid = ResourceUID::INVALID_ID;
}
if (tag.fields.has("load_steps")) {
resources_total = tag.fields["load_steps"];
} else {
@ -976,7 +1019,12 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path)
bs_save_unicode_string(wf.f, is_scene ? "PackedScene" : resource_type);
wf->store_64(0); //offset to import metadata, this is no longer used
for (int i = 0; i < 14; i++) {
f->store_32(ResourceFormatSaverBinaryInstance::FORMAT_FLAG_NAMED_SCENE_IDS | ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS);
f->store_64(res_uid);
for (int i = 0; i < 5; i++) {
wf->store_32(0); // reserved
}
@ -1018,9 +1066,15 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path)
String path = next_tag.fields["path"];
String type = next_tag.fields["type"];
String id = next_tag.fields["id"];
ResourceUID::ID uid = ResourceUID::INVALID_ID;
if (next_tag.fields.has("uid")) {
String uidt = next_tag.fields["uid"];
uid = ResourceUID::get_singleton()->text_to_id(uidt);
}
bs_save_unicode_string(wf.f, type);
bs_save_unicode_string(wf.f, path);
wf.f->store_64(uid);
int lindex = dummy_read.external_resources.size();
Ref<DummyResource> dr;
@ -1257,6 +1311,32 @@ String ResourceLoaderText::recognize(FileAccess *p_f) {
return tag.fields["type"];
}
ResourceUID::ID ResourceLoaderText::get_uid(FileAccess *p_f) {
error = OK;
lines = 1;
f = p_f;
stream.f = f;
ignore_resource_parsing = true;
VariantParser::Tag tag;
Error err = VariantParser::parse_tag(&stream, lines, error_text, tag);
if (err) {
_printerr();
return ResourceUID::INVALID_ID;
}
if (tag.fields.has("uid")) { //field is optional
String uidt = tag.fields["uid"];
return ResourceUID::get_singleton()->text_to_id(uidt);
}
return ResourceUID::INVALID_ID;
}
/////////////////////
RES ResourceFormatLoaderText::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
@ -1277,7 +1357,6 @@ RES ResourceFormatLoaderText::load(const String &p_path, const String &p_origina
loader.local_path = ProjectSettings::get_singleton()->localize_path(path);
loader.progress = r_progress;
loader.res_path = loader.local_path;
//loader.set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) );
loader.open(f);
err = loader.load();
if (r_error) {
@ -1330,11 +1409,28 @@ String ResourceFormatLoaderText::get_resource_type(const String &p_path) const {
ResourceLoaderText loader;
loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
loader.res_path = loader.local_path;
//loader.set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) );
String r = loader.recognize(f);
return ClassDB::get_compatibility_remapped_class(r);
}
ResourceUID::ID ResourceFormatLoaderText::get_resource_uid(const String &p_path) const {
String ext = p_path.get_extension().to_lower();
if (ext != "tscn" && ext != "tres") {
return ResourceUID::INVALID_ID;
}
FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
if (!f) {
return ResourceUID::INVALID_ID; //could not read
}
ResourceLoaderText loader;
loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
loader.res_path = loader.local_path;
return loader.get_uid(f);
}
void ResourceFormatLoaderText::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
if (!f) {
@ -1344,7 +1440,6 @@ void ResourceFormatLoaderText::get_dependencies(const String &p_path, List<Strin
ResourceLoaderText loader;
loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
loader.res_path = loader.local_path;
//loader.set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) );
loader.get_dependencies(f, p_dependencies, p_add_types);
}
@ -1357,7 +1452,6 @@ Error ResourceFormatLoaderText::rename_dependencies(const String &p_path, const
ResourceLoaderText loader;
loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
loader.res_path = loader.local_path;
//loader.set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) );
return loader.rename_dependencies(f, p_path, p_map);
}
@ -1373,7 +1467,6 @@ Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path,
const String &path = p_src_path;
loader.local_path = ProjectSettings::get_singleton()->localize_path(path);
loader.res_path = loader.local_path;
//loader.set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) );
loader.open(f);
return loader.save_as_binary(f, p_dst_path);
}
@ -1548,6 +1641,12 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
}
title += "format=" + itos(FORMAT_VERSION) + "";
ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(local_path, true);
if (uid != ResourceUID::INVALID_ID) {
title += " uid=\"" + ResourceUID::get_singleton()->id_to_text(uid) + "\"";
}
f->store_string(title);
f->store_line("]\n"); // One empty line.
}
@ -1612,7 +1711,14 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
for (int i = 0; i < sorted_er.size(); i++) {
String p = sorted_er[i].resource->get_path();
f->store_string("[ext_resource path=\"" + p + "\" type=\"" + sorted_er[i].resource->get_save_class() + "\" id=\"" + sorted_er[i].id + "\"]\n"); // Bundled.
String s = "[ext_resource type=\"" + sorted_er[i].resource->get_save_class() + "\"";
ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(p, false);
if (uid != ResourceUID::INVALID_ID) {
s += " uid=\"" + ResourceUID::get_singleton()->id_to_text(uid) + "\"";
}
s += " path=\"" + p + "\" id=\"" + sorted_er[i].id + "\"]\n";
f->store_string(s); // Bundled.
}
if (external_resources.size()) {

View file

@ -74,6 +74,8 @@ class ResourceLoaderText {
mutable int lines = 0;
ResourceUID::ID res_uid = ResourceUID::INVALID_ID;
Map<String, String> remaps;
static Error _parse_sub_resources(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return reinterpret_cast<ResourceLoaderText *>(p_self)->_parse_sub_resource(p_stream, r_res, line, r_err_str); }
@ -120,6 +122,7 @@ public:
void open(FileAccess *p_f, bool p_skip_first_tag = false);
String recognize(FileAccess *p_f);
ResourceUID::ID get_uid(FileAccess *p_f);
void get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types);
Error rename_dependencies(FileAccess *p_f, const String &p_path, const Map<String, String> &p_map);
@ -136,6 +139,7 @@ public:
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
virtual Error rename_dependencies(const String &p_path, const Map<String, String> &p_map);