diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 8941c071b589..e892957fb841 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1180,6 +1180,18 @@ Override for [member filesystem/import/fbx2gltf/enabled] on the Web where FBX2glTF can't easily be accessed from Godot. + + If [code]true[/code], disables the use of inline text resource UIDs in addons. This is useful for compatibility with old addons that concerning about file's md5. + + + If [code]true[/code], inline text resource UIDs will be used for [code].gd[/code] files. + + + if [code]true[/code], inline text resource UIDs will be used for [code].shader[/code] files. + + + if [code]true[/code], inline text resource UIDs will be used for [code].shaderinc[/code] files. + If [code]true[/code], [Control]s will always show if they're focused, even if said focus was gained via mouse/touch input. diff --git a/editor/file_system/editor_file_system.cpp b/editor/file_system/editor_file_system.cpp index 6d7ef2690146..972b9e973518 100644 --- a/editor/file_system/editor_file_system.cpp +++ b/editor/file_system/editor_file_system.cpp @@ -1364,6 +1364,18 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir, } f->store_line(ResourceUID::get_singleton()->id_to_text(fi->uid)); } + } else { + if (fi->uid == ResourceUID::INVALID_ID) { + ResourceUID::ID new_uid = ResourceUID::get_singleton()->create_id_for_path(path); + if (ResourceSaver::set_uid(path, new_uid) == OK) { + fi->uid = new_uid; + ResourceUID::get_singleton()->add_id(new_uid, path); + } + } else { + if (ResourceLoader::get_resource_uid(path) != fi->uid) { + ResourceSaver::set_uid(path, fi->uid); + } + } } } @@ -2468,6 +2480,10 @@ void EditorFileSystem::update_files(const Vector &p_script_paths) { fi->import_valid = (type == "TextFile" || type == "OtherFile") ? true : ResourceLoader::is_import_valid(file); if (uid != ResourceUID::INVALID_ID) { + if (ResourceLoader::get_resource_uid(file) != uid) { + ResourceSaver::set_uid(file, uid); + } + if (ResourceUID::get_singleton()->has_id(uid)) { ResourceUID::get_singleton()->set_id(uid, file); } else { @@ -2484,6 +2500,12 @@ void EditorFileSystem::update_files(const Vector &p_script_paths) { f->store_line(ResourceUID::get_singleton()->id_to_text(id)); fi->uid = id; } + } else { + ResourceUID::ID new_uid = ResourceUID::get_singleton()->create_id_for_path(file); + if (ResourceSaver::set_uid(file, new_uid) == OK) { + fi->uid = new_uid; + ResourceUID::get_singleton()->add_id(new_uid, file); + } } } diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index b85fd8e74767..d55971a4f361 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -40,6 +40,7 @@ #ifdef TOOLS_ENABLED #include "editor/gdscript_docgen.h" +#include "editor/script/script_editor_plugin.h" #endif #ifdef TESTS_ENABLED @@ -2961,6 +2962,8 @@ GDScriptLanguage::GDScriptLanguage() { track_call_stack = GLOBAL_DEF_RST("debug/settings/gdscript/always_track_call_stacks", false); track_locals = GLOBAL_DEF_RST("debug/settings/gdscript/always_track_local_variables", false); + GLOBAL_DEF("filesystem/inline_text_resource_uids/gdscript", true); + #ifdef DEBUG_ENABLED track_call_stack = true; track_locals = track_locals || EngineDebugger::is_active(); @@ -3137,6 +3140,93 @@ void ResourceFormatLoaderGDScript::get_classes_used(const String &p_path, HashSe } } +#define UID_COMMENT_PREFIX "# uid://" +#define UID_COMMENT_SUFFIX "This line is generated, don't modify or remove it." +static ResourceUID::ID extract_uid_from_line(const String &p_line) { + Vector splits = p_line.strip_edges().substr(2).split(" ", false, 1); + if (splits.is_empty()) { + return ResourceUID::INVALID_ID; + } + return ResourceUID::get_singleton()->text_to_id(splits[0]); +} + +ResourceUID::ID ResourceFormatLoaderGDScript::get_resource_uid(const String &p_path) const { + int64_t uid = ResourceUID::INVALID_ID; + + if (FileAccess::exists(p_path + ".uid")) { + Ref file = FileAccess::open(p_path + ".uid", FileAccess::READ); + if (file.is_valid()) { + uid = ResourceUID::get_singleton()->text_to_id(file->get_line()); + } + } else { + const String extension = p_path.get_extension().to_lower(); + if (extension == "gd") { + Ref file = FileAccess::open(p_path, FileAccess::READ); + if (file.is_valid()) { + while (!file->eof_reached()) { + String line = file->get_line().strip_edges(); + if (!line.is_empty()) { + if (line.begins_with(UID_COMMENT_PREFIX)) { + uid = extract_uid_from_line(line); + } + break; + } + } + } + } + } + + return uid; +} + +bool ResourceFormatLoaderGDScript::has_custom_uid_support() const { + return GLOBAL_GET("filesystem/inline_text_resource_uids/gdscript"); +} + +String ResourceFormatSaverGDScript::add_uid_to_source(const String &p_source, const String &p_path, ResourceUID::ID p_uid) const { + bool need_update = false; + Vector lines = p_source.split("\n"); + bool uid_comment_valid = false; + for (Vector::Size i = 0; i < lines.size(); i++) { + const String &line = lines[i].strip_edges(); + if (line.begins_with(UID_COMMENT_PREFIX)) { + ResourceUID::ID uid = extract_uid_from_line(line); + if (uid == ResourceUID::INVALID_ID || p_uid != uid) { + uid = p_uid == ResourceUID::INVALID_ID ? ResourceSaver::get_resource_id_for_path(p_path, true) : p_uid; + lines.set(i, vformat("# %s %s", ResourceUID::get_singleton()->id_to_text(uid), UID_COMMENT_SUFFIX)); + if (ResourceUID::get_singleton()->has_id(uid)) { + ResourceUID::get_singleton()->set_id(uid, p_path); + } else { + ResourceUID::get_singleton()->add_id(uid, p_path); + } + need_update = true; + } + + uid_comment_valid = true; + break; + } else if (!line.strip_edges().is_empty()) { + break; + } + } + + if (!uid_comment_valid) { + ResourceUID::ID uid = p_uid == ResourceUID::INVALID_ID ? ResourceSaver::get_resource_id_for_path(p_path, true) : p_uid; + lines.insert(0, vformat("# %s %s", ResourceUID::get_singleton()->id_to_text(uid), UID_COMMENT_SUFFIX)); + if (ResourceUID::get_singleton()->has_id(uid)) { + ResourceUID::get_singleton()->set_id(uid, p_path); + } else { + ResourceUID::get_singleton()->add_id(uid, p_path); + } + need_update = true; + } + + if (need_update) { + return String("\n").join(lines); + } else { + return p_source; + } +} + Error ResourceFormatSaverGDScript::save(const Ref &p_resource, const String &p_path, uint32_t p_flags) { Ref sqscr = p_resource; ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER); @@ -3149,6 +3239,20 @@ Error ResourceFormatSaverGDScript::save(const Ref &p_resource, const S ERR_FAIL_COND_V_MSG(err, err, "Cannot save GDScript file '" + p_path + "'."); + if (!FileAccess::exists(p_path + ".uid")) { + if (p_path.begins_with("res://addons/") && GLOBAL_GET("filesystem/inline_text_resource_uids/compatibility/no_inline_text_resource_uids_in_addons")) { + Ref f = FileAccess::open(p_path + ".uid", FileAccess::WRITE); + if (f.is_valid()) { + ResourceUID::ID uid = ResourceUID::get_singleton()->create_id_for_path(p_path); + f->store_line(ResourceUID::get_singleton()->id_to_text(uid)); + f->close(); + } + } else { + source = add_uid_to_source(source, p_path); + sqscr->set_source_code(source); + } + } + file->store_string(source); if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { return ERR_CANT_CREATE; @@ -3159,6 +3263,14 @@ Error ResourceFormatSaverGDScript::save(const Ref &p_resource, const S GDScriptLanguage::get_singleton()->reload_tool_script(p_resource, true); } +#ifdef TOOLS_ENABLED + if (ScriptEditor *script_editor = ScriptEditor::get_singleton()) { + if (script_editor->get_open_scripts().has(sqscr)) { + script_editor->reload_scripts(true); + } + } +#endif // TOOLS_ENABLED + return OK; } @@ -3171,3 +3283,35 @@ void ResourceFormatSaverGDScript::get_recognized_extensions(const Ref bool ResourceFormatSaverGDScript::recognize(const Ref &p_resource) const { return Object::cast_to(*p_resource) != nullptr; } + +Error ResourceFormatSaverGDScript::set_uid(const String &p_path, ResourceUID::ID p_uid) { + if (FileAccess::exists(p_path + ".uid") || (p_path.begins_with("res://addons/") && GLOBAL_GET("filesystem/inline_text_resource_uids/compatibility/no_inline_text_resource_uids_in_addons"))) { + Ref f = FileAccess::open(p_path + ".uid", FileAccess::WRITE); + if (f.is_valid()) { + f->store_line(ResourceUID::get_singleton()->id_to_text(p_uid)); + return OK; + } else { + return FileAccess::get_open_error(); + } + } else if (p_path.get_extension().to_lower() == "gd") { + Error err = OK; + String source = FileAccess::get_file_as_string(p_path, &err); + if (err != OK) { + return err; + } + + source = add_uid_to_source(source, p_path, p_uid); + + Ref f = FileAccess::open(p_path, FileAccess::WRITE); + if (f.is_valid()) { + f->store_string(source); + err = OK; + } else { + err = FileAccess::get_open_error(); + } + + return err; + } + + return ERR_FILE_UNRECOGNIZED; +} diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index f9013d0bf996..4491e734ed56 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -685,13 +685,21 @@ class ResourceFormatLoaderGDScript : public ResourceFormatLoader { virtual String get_resource_type(const String &p_path) const override; virtual void get_dependencies(const String &p_path, List *p_dependencies, bool p_add_types = false) override; virtual void get_classes_used(const String &p_path, HashSet *r_classes) override; + + virtual ResourceUID::ID get_resource_uid(const String &p_path) const override; + virtual bool has_custom_uid_support() const override; }; class ResourceFormatSaverGDScript : public ResourceFormatSaver { GDSOFTCLASS(ResourceFormatSaverGDScript, ResourceFormatSaver); +private: + String add_uid_to_source(const String &p_source, const String &p_path, ResourceUID::ID p_uid = ResourceUID::INVALID_ID) const; + public: virtual Error save(const Ref &p_resource, const String &p_path, uint32_t p_flags = 0) override; virtual void get_recognized_extensions(const Ref &p_resource, List *p_extensions) const override; virtual bool recognize(const Ref &p_resource) const override; + + virtual Error set_uid(const String &p_path, ResourceUID::ID p_uid) override; }; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 58fadf663a11..90628a459236 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -398,6 +398,8 @@ void register_scene_types() { resource_loader_shader.instantiate(); ResourceLoader::add_resource_format_loader(resource_loader_shader, true); + + GLOBAL_DEF("filesystem/inline_text_resource_uids/shader", true); } if constexpr (GD_IS_CLASS_ENABLED(ShaderInclude)) { @@ -406,8 +408,12 @@ void register_scene_types() { resource_loader_shader_include.instantiate(); ResourceLoader::add_resource_format_loader(resource_loader_shader_include, true); + + GLOBAL_DEF("filesystem/inline_text_resource_uids/shader_include", true); } + GLOBAL_DEF("filesystem/inline_text_resource_uids/compatibility/no_inline_text_resource_uids_in_addons", false); + OS::get_singleton()->yield(); // may take time to init GDREGISTER_CLASS(Object); diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index dc07ce7bb5ec..a9116f47419b 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -31,6 +31,7 @@ #include "shader.h" #include "shader.compat.inc" +#include "core/config/project_settings.h" #include "core/io/file_access.h" #include "scene/main/scene_tree.h" #include "servers/rendering/rendering_server.h" @@ -40,6 +41,9 @@ #ifdef TOOLS_ENABLED #include "editor/doc/editor_help.h" +#include "editor/editor_node.h" +#include "editor/shader/shader_editor_plugin.h" +#include "editor/shader/text_shader_editor.h" #include "modules/modules_enabled.gen.h" // For regex. #ifdef MODULE_REGEX_ENABLED @@ -345,6 +349,93 @@ String ResourceFormatLoaderShader::get_resource_type(const String &p_path) const return ""; } +#define UID_COMMENT_PREFIX "// uid://" +#define UID_COMMENT_SUFFIX "This line is generated, don't modify or remove it." +static ResourceUID::ID extract_uid_from_line(const String &p_line) { + Vector splits = p_line.strip_edges().substr(3).split(" ", false, 1); + if (splits.is_empty()) { + return ResourceUID::INVALID_ID; + } + return ResourceUID::get_singleton()->text_to_id(splits[0]); +} + +ResourceUID::ID ResourceFormatLoaderShader::get_resource_uid(const String &p_path) const { + int64_t uid = ResourceUID::INVALID_ID; + + if (FileAccess::exists(p_path + ".uid")) { + Ref file = FileAccess::open(p_path + ".uid", FileAccess::READ); + if (file.is_valid()) { + uid = ResourceUID::get_singleton()->text_to_id(file->get_line()); + } + } else { + const String extension = p_path.get_extension().to_lower(); + if (extension == "gdshader") { + Ref file = FileAccess::open(p_path, FileAccess::READ); + if (file.is_valid()) { + while (!file->eof_reached()) { + String line = file->get_line().strip_edges(); + if (!line.is_empty()) { + if (line.begins_with(UID_COMMENT_PREFIX)) { + uid = extract_uid_from_line(line); + } + break; + } + } + } + } + } + + return uid; +} + +bool ResourceFormatLoaderShader::has_custom_uid_support() const { + return GLOBAL_GET("filesystem/inline_text_resource_uids/shader"); +} + +String ResourceFormatSaverShader::add_uid_to_source(const String &p_source, const String &p_path, ResourceUID::ID p_uid) const { + bool need_update = false; + Vector lines = p_source.split("\n"); + bool uid_comment_valid = false; + for (Vector::Size i = 0; i < lines.size(); i++) { + const String &line = lines[i].strip_edges(); + if (line.begins_with(UID_COMMENT_PREFIX)) { + ResourceUID::ID uid = extract_uid_from_line(line); + if (uid == ResourceUID::INVALID_ID || p_uid != uid) { + uid = p_uid == ResourceUID::INVALID_ID ? ResourceSaver::get_resource_id_for_path(p_path, true) : p_uid; + lines.set(i, vformat("// %s %s", ResourceUID::get_singleton()->id_to_text(uid), UID_COMMENT_SUFFIX)); + if (ResourceUID::get_singleton()->has_id(uid)) { + ResourceUID::get_singleton()->set_id(uid, p_path); + } else { + ResourceUID::get_singleton()->add_id(uid, p_path); + } + need_update = true; + } + + uid_comment_valid = true; + break; + } else if (!line.strip_edges().is_empty()) { + break; + } + } + + if (!uid_comment_valid) { + ResourceUID::ID uid = p_uid == ResourceUID::INVALID_ID ? ResourceSaver::get_resource_id_for_path(p_path, true) : p_uid; + lines.insert(0, vformat("// %s %s", ResourceUID::get_singleton()->id_to_text(uid), UID_COMMENT_SUFFIX)); + if (ResourceUID::get_singleton()->has_id(uid)) { + ResourceUID::get_singleton()->set_id(uid, p_path); + } else { + ResourceUID::get_singleton()->add_id(uid, p_path); + } + need_update = true; + } + + if (need_update) { + return String("\n").join(lines); + } else { + return p_source; + } +} + Error ResourceFormatSaverShader::save(const Ref &p_resource, const String &p_path, uint32_t p_flags) { Ref shader = p_resource; ERR_FAIL_COND_V(shader.is_null(), ERR_INVALID_PARAMETER); @@ -356,6 +447,43 @@ Error ResourceFormatSaverShader::save(const Ref &p_resource, const Str ERR_FAIL_COND_V_MSG(err, err, "Cannot save shader '" + p_path + "'."); + if (!FileAccess::exists(p_path + ".uid")) { + if (p_path.begins_with("res://addons/") && GLOBAL_GET("filesystem/inline_text_resource_uids/compatibility/no_inline_text_resource_uids_in_addons")) { + Ref f = FileAccess::open(p_path + ".uid", FileAccess::WRITE); + if (f.is_valid()) { + ResourceUID::ID uid = ResourceUID::get_singleton()->create_id_for_path(p_path); + f->store_line(ResourceUID::get_singleton()->id_to_text(uid)); + f->close(); + } + } else { + source = add_uid_to_source(source, p_path); + shader->set_code(source); +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + if (ShaderEditorPlugin *shader_editor_plugin = Object::cast_to(EditorNode::get_editor_data().get_editor_by_name("Shader"))) { + if (TextShaderEditor *text_shader_editor = Object::cast_to(shader_editor_plugin->get_shader_editor(shader))) { + CodeEdit *te = text_shader_editor->get_code_editor()->get_text_editor(); + int column = te->get_caret_column(); + int row = te->get_caret_line(); + int h = te->get_h_scroll(); + int v = te->get_v_scroll(); + + te->set_text(source); + te->set_caret_line(row); + te->set_caret_column(column); + te->set_h_scroll(h); + te->set_v_scroll(v); + + te->tag_saved_version(); + + text_shader_editor->get_code_editor()->update_line_and_column(); + } + } + } +#endif // TOOLS_ENABLED + } + } + file->store_string(source); if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { return ERR_CANT_CREATE; @@ -375,3 +503,35 @@ void ResourceFormatSaverShader::get_recognized_extensions(const Ref &p bool ResourceFormatSaverShader::recognize(const Ref &p_resource) const { return p_resource->get_class_name() == "Shader"; //only shader, not inherited } + +Error ResourceFormatSaverShader::set_uid(const String &p_path, ResourceUID::ID p_uid) { + if (FileAccess::exists(p_path + ".uid") || (p_path.begins_with("res://addons/") && GLOBAL_GET("filesystem/inline_text_resource_uids/compatibility/no_inline_text_resource_uids_in_addons"))) { + Ref f = FileAccess::open(p_path + ".uid", FileAccess::WRITE); + if (f.is_valid()) { + f->store_line(ResourceUID::get_singleton()->id_to_text(p_uid)); + return OK; + } else { + return FileAccess::get_open_error(); + } + } else if (p_path.get_extension().to_lower() == "gdshader") { + Error err = OK; + String source = FileAccess::get_file_as_string(p_path, &err); + if (err != OK) { + return err; + } + + source = add_uid_to_source(source, p_path, p_uid); + + Ref f = FileAccess::open(p_path, FileAccess::WRITE); + if (f.is_valid()) { + f->store_string(source); + err = OK; + } else { + err = FileAccess::get_open_error(); + } + + return err; + } + + return ERR_FILE_UNRECOGNIZED; +} diff --git a/scene/resources/shader.h b/scene/resources/shader.h index ff8841173cd8..c9dbbf7797f9 100644 --- a/scene/resources/shader.h +++ b/scene/resources/shader.h @@ -113,13 +113,21 @@ class ResourceFormatLoaderShader : public ResourceFormatLoader { virtual void get_recognized_extensions(List *p_extensions) const override; virtual bool handles_type(const String &p_type) const override; virtual String get_resource_type(const String &p_path) const override; + + virtual ResourceUID::ID get_resource_uid(const String &p_path) const override; + virtual bool has_custom_uid_support() const override; }; class ResourceFormatSaverShader : public ResourceFormatSaver { GDSOFTCLASS(ResourceFormatSaverShader, ResourceFormatSaver); +private: + String add_uid_to_source(const String &p_source, const String &p_path, ResourceUID::ID p_uid = ResourceUID::INVALID_ID) const; + public: virtual Error save(const Ref &p_resource, const String &p_path, uint32_t p_flags = 0) override; virtual void get_recognized_extensions(const Ref &p_resource, List *p_extensions) const override; virtual bool recognize(const Ref &p_resource) const override; + + virtual Error set_uid(const String &p_path, ResourceUID::ID p_uid) override; }; diff --git a/scene/resources/shader_include.cpp b/scene/resources/shader_include.cpp index b03396bd6fe5..75352cf688b8 100644 --- a/scene/resources/shader_include.cpp +++ b/scene/resources/shader_include.cpp @@ -30,9 +30,16 @@ #include "shader_include.h" +#include "core/config/project_settings.h" #include "core/io/file_access.h" #include "servers/rendering/shader_preprocessor.h" +#ifdef TOOLS_ENABLED +#include "editor/editor_node.h" +#include "editor/shader/shader_editor_plugin.h" +#include "editor/shader/text_shader_editor.h" +#endif // TOOLS_ENABLED + void ShaderInclude::_dependency_changed() { emit_changed(); } @@ -128,7 +135,93 @@ String ResourceFormatLoaderShaderInclude::get_resource_type(const String &p_path return ""; } +#define UID_COMMENT_PREFIX "// uid://" +#define UID_COMMENT_SUFFIX "This line is generated, don't modify or remove it." +static ResourceUID::ID extract_uid_from_line(const String &p_line) { + Vector splits = p_line.strip_edges().substr(3).split(" ", false, 1); + if (splits.is_empty()) { + return ResourceUID::INVALID_ID; + } + return ResourceUID::get_singleton()->text_to_id(splits[0]); +} + +ResourceUID::ID ResourceFormatLoaderShaderInclude::get_resource_uid(const String &p_path) const { + int64_t uid = ResourceUID::INVALID_ID; + + if (FileAccess::exists(p_path + ".uid")) { + Ref file = FileAccess::open(p_path + ".uid", FileAccess::READ); + if (file.is_valid()) { + uid = ResourceUID::get_singleton()->text_to_id(file->get_line()); + } + } else { + const String extension = p_path.get_extension().to_lower(); + if (extension == "gdshaderinc") { + Ref file = FileAccess::open(p_path, FileAccess::READ); + if (file.is_valid()) { + while (!file->eof_reached()) { + String line = file->get_line().strip_edges(); + if (!line.is_empty()) { + if (line.begins_with(UID_COMMENT_PREFIX)) { + uid = extract_uid_from_line(line); + } + break; + } + } + } + } + } + + return uid; +} + +bool ResourceFormatLoaderShaderInclude::has_custom_uid_support() const { + return GLOBAL_GET("filesystem/inline_text_resource_uids/shader_include"); +} + // ResourceFormatSaverShaderInclude +String ResourceFormatSaverShaderInclude::add_uid_to_source(const String &p_source, const String &p_path, ResourceUID::ID p_uid) const { + bool need_update = false; + Vector lines = p_source.split("\n"); + bool uid_comment_valid = false; + for (Vector::Size i = 0; i < lines.size(); i++) { + const String &line = lines[i].strip_edges(); + if (line.begins_with(UID_COMMENT_PREFIX)) { + ResourceUID::ID uid = extract_uid_from_line(line); + if (uid == ResourceUID::INVALID_ID || p_uid != uid) { + uid = p_uid == ResourceUID::INVALID_ID ? ResourceSaver::get_resource_id_for_path(p_path, true) : p_uid; + lines.set(i, vformat("// %s %s", ResourceUID::get_singleton()->id_to_text(uid), UID_COMMENT_SUFFIX)); + if (ResourceUID::get_singleton()->has_id(uid)) { + ResourceUID::get_singleton()->set_id(uid, p_path); + } else { + ResourceUID::get_singleton()->add_id(uid, p_path); + } + need_update = true; + } + + uid_comment_valid = true; + break; + } else if (!line.strip_edges().is_empty()) { + break; + } + } + + if (!uid_comment_valid) { + ResourceUID::ID uid = p_uid == ResourceUID::INVALID_ID ? ResourceSaver::get_resource_id_for_path(p_path, true) : p_uid; + lines.insert(0, vformat("// %s %s", ResourceUID::get_singleton()->id_to_text(uid), UID_COMMENT_SUFFIX)); + if (ResourceUID::get_singleton()->has_id(uid)) { + ResourceUID::get_singleton()->set_id(uid, p_path); + } else { + ResourceUID::get_singleton()->add_id(uid, p_path); + } + need_update = true; + } + + if (need_update) { + return String("\n").join(lines); + } else { + return p_source; + } +} Error ResourceFormatSaverShaderInclude::save(const Ref &p_resource, const String &p_path, uint32_t p_flags) { Ref shader_inc = p_resource; @@ -141,6 +234,43 @@ Error ResourceFormatSaverShaderInclude::save(const Ref &p_resource, co ERR_FAIL_COND_V_MSG(error, error, "Cannot save shader include '" + p_path + "'."); + if (!FileAccess::exists(p_path + ".uid")) { + if (p_path.begins_with("res://addons/") && GLOBAL_GET("filesystem/inline_text_resource_uids/compatibility/no_inline_text_resource_uids_in_addons")) { + Ref f = FileAccess::open(p_path + ".uid", FileAccess::WRITE); + if (f.is_valid()) { + ResourceUID::ID uid = ResourceUID::get_singleton()->create_id_for_path(p_path); + f->store_line(ResourceUID::get_singleton()->id_to_text(uid)); + f->close(); + } + } else { + source = add_uid_to_source(source, p_path); + shader_inc->set_code(source); +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + if (ShaderEditorPlugin *shader_editor_plugin = Object::cast_to(EditorNode::get_editor_data().get_editor_by_name("Shader"))) { + if (TextShaderEditor *text_shader_editor = Object::cast_to(shader_editor_plugin->get_shader_editor(shader_inc))) { + CodeEdit *te = text_shader_editor->get_code_editor()->get_text_editor(); + int column = te->get_caret_column(); + int row = te->get_caret_line(); + int h = te->get_h_scroll(); + int v = te->get_v_scroll(); + + te->set_text(source); + te->set_caret_line(row); + te->set_caret_column(column); + te->set_h_scroll(h); + te->set_v_scroll(v); + + te->tag_saved_version(); + + text_shader_editor->get_code_editor()->update_line_and_column(); + } + } + } +#endif // TOOLS_ENABLED + } + } + file->store_string(source); if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { return ERR_CANT_CREATE; @@ -159,3 +289,35 @@ void ResourceFormatSaverShaderInclude::get_recognized_extensions(const Ref &p_resource) const { return p_resource->get_class_name() == "ShaderInclude"; //only shader, not inherited } + +Error ResourceFormatSaverShaderInclude::set_uid(const String &p_path, ResourceUID::ID p_uid) { + if (FileAccess::exists(p_path + ".uid") || (p_path.begins_with("res://addons/") && GLOBAL_GET("filesystem/inline_text_resource_uids/compatibility/no_inline_text_resource_uids_in_addons"))) { + Ref f = FileAccess::open(p_path + ".uid", FileAccess::WRITE); + if (f.is_valid()) { + f->store_line(ResourceUID::get_singleton()->id_to_text(p_uid)); + return OK; + } else { + return FileAccess::get_open_error(); + } + } else if (p_path.get_extension().to_lower() == "gdshaderinc") { + Error err = OK; + String source = FileAccess::get_file_as_string(p_path, &err); + if (err != OK) { + return err; + } + + source = add_uid_to_source(source, p_path, p_uid); + + Ref f = FileAccess::open(p_path, FileAccess::WRITE); + if (f.is_valid()) { + f->store_string(source); + err = OK; + } else { + err = FileAccess::get_open_error(); + } + + return err; + } + + return ERR_FILE_UNRECOGNIZED; +} diff --git a/scene/resources/shader_include.h b/scene/resources/shader_include.h index 0930f5b47597..4b4139a29ccf 100644 --- a/scene/resources/shader_include.h +++ b/scene/resources/shader_include.h @@ -63,13 +63,21 @@ class ResourceFormatLoaderShaderInclude : public ResourceFormatLoader { virtual void get_recognized_extensions(List *p_extensions) const override; virtual bool handles_type(const String &p_type) const override; virtual String get_resource_type(const String &p_path) const override; + + virtual ResourceUID::ID get_resource_uid(const String &p_path) const override; + virtual bool has_custom_uid_support() const override; }; class ResourceFormatSaverShaderInclude : public ResourceFormatSaver { GDSOFTCLASS(ResourceFormatSaverShaderInclude, ResourceFormatSaver); +private: + String add_uid_to_source(const String &p_source, const String &p_path, ResourceUID::ID p_uid = ResourceUID::INVALID_ID) const; + public: virtual Error save(const Ref &p_resource, const String &p_path, uint32_t p_flags = 0) override; virtual void get_recognized_extensions(const Ref &p_resource, List *p_extensions) const override; virtual bool recognize(const Ref &p_resource) const override; + + virtual Error set_uid(const String &p_path, ResourceUID::ID p_uid) override; };