diff options
103 files changed, 1196 insertions, 821 deletions
diff --git a/.github/actions/godot-deps/action.yml b/.github/actions/godot-deps/action.yml index eb9bdef1e7..bd9a1f55ed 100644 --- a/.github/actions/godot-deps/action.yml +++ b/.github/actions/godot-deps/action.yml @@ -27,6 +27,5 @@ runs: shell: bash run: | python -c "import sys; print(sys.version)" - python -m pip install wheel python -m pip install scons==${{ inputs.scons-version }} scons --version diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index cf653caa3d..bd4e2856e3 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -17,8 +17,8 @@ concurrency: jobs: build-linux: - # If unspecified, stay one LTS before latest to increase portability of Linux artifacts. - runs-on: ${{ matrix.os || 'ubuntu-22.04' }} + # Stay one LTS before latest to increase portability of Linux artifacts. + runs-on: ubuntu-22.04 name: ${{ matrix.name }} strategy: fail-fast: false @@ -61,8 +61,6 @@ jobs: artifact: false # Test our oldest supported SCons/Python versions on one arbitrary editor build. legacy-scons: true - # Python 3.6 unavailable on 22.04. - os: ubuntu-20.04 - name: Editor with ThreadSanitizer (target=editor, tests=yes, dev_build=yes, use_tsan=yes, use_llvm=yes, linker=lld) cache-name: linux-editor-thread-sanitizer @@ -132,8 +130,8 @@ jobs: uses: ./.github/actions/godot-deps with: # Sync with Ensure*Version in SConstruct. - python-version: 3.6 - scons-version: 3.1.2 + python-version: 3.8 + scons-version: 4.0 - name: Setup GCC problem matcher uses: ammaraskar/gcc-problem-matcher@master diff --git a/SConstruct b/SConstruct index ee34d421e0..5a3a8f49eb 100644 --- a/SConstruct +++ b/SConstruct @@ -1,8 +1,8 @@ #!/usr/bin/env python from misc.utility.scons_hints import * -EnsureSConsVersion(3, 1, 2) -EnsurePythonVersion(3, 6) +EnsureSConsVersion(4, 0) +EnsurePythonVersion(3, 8) # System import atexit @@ -59,7 +59,7 @@ import glsl_builders import methods import scu_builders from methods import print_error, print_warning -from platform_methods import architecture_aliases, architectures +from platform_methods import architecture_aliases, architectures, compatibility_platform_aliases if ARGUMENTS.get("target", "editor") == "editor": _helper_module("editor.editor_builders", "editor/editor_builders.py") @@ -350,27 +350,16 @@ if env["platform"] == "": if env["platform"] != "": print(f'Automatically detected platform: {env["platform"]}') -if env["platform"] == "osx": - # Deprecated alias kept for compatibility. - print_warning('Platform "osx" has been renamed to "macos" in Godot 4. Building for platform "macos".') - env["platform"] = "macos" - -if env["platform"] == "iphone": - # Deprecated alias kept for compatibility. - print_warning('Platform "iphone" has been renamed to "ios" in Godot 4. Building for platform "ios".') - env["platform"] = "ios" - -if env["platform"] in ["linux", "bsd", "x11"]: - if env["platform"] == "x11": - # Deprecated alias kept for compatibility. - print_warning('Platform "x11" has been renamed to "linuxbsd" in Godot 4. Building for platform "linuxbsd".') - # Alias for convenience. - env["platform"] = "linuxbsd" +# Deprecated aliases kept for compatibility. +if env["platform"] in compatibility_platform_aliases: + alias = env["platform"] + platform = compatibility_platform_aliases[alias] + print_warning(f'Platform "{alias}" has been renamed to "{platform}" in Godot 4. Building for platform "{platform}".') + env["platform"] = platform -if env["platform"] == "javascript": - # Deprecated alias kept for compatibility. - print_warning('Platform "javascript" has been renamed to "web" in Godot 4. Building for platform "web".') - env["platform"] = "web" +# Alias for convenience. +if env["platform"] in ["linux", "bsd"]: + env["platform"] = "linuxbsd" if env["platform"] not in platform_list: text = "The following platforms are available:\n\t{}\n".format("\n\t".join(platform_list)) @@ -1055,18 +1044,7 @@ if scons_cache_path is not None: CacheDir(scons_cache_path) print("Scons cache enabled... (path: '" + scons_cache_path + "')") -if env["vsproj"]: - env.vs_incs = [] - env.vs_srcs = [] - if env["compiledb"]: - if env.scons_version < (4, 0, 0): - # Generating the compilation DB (`compile_commands.json`) requires SCons 4.0.0 or later. - print_error( - "The `compiledb=yes` option requires SCons 4.0 or later, but your version is %s." % scons_raw_version - ) - Exit(255) - env.Tool("compilation_db") env.Alias("compiledb", env.CompilationDatabase()) diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 092177bc15..fa0e4037ba 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1408,7 +1408,7 @@ void ProjectSettings::_add_builtin_input_map() { } Dictionary action; - action["deadzone"] = Variant(0.2f); + action["deadzone"] = Variant(InputMap::DEFAULT_DEADZONE); action["events"] = events; String action_name = "input/" + E.key; diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index d125bad252..4733aaf220 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -1097,7 +1097,7 @@ JoyAxis InputEventJoypadMotion::get_axis() const { void InputEventJoypadMotion::set_axis_value(float p_value) { axis_value = p_value; - pressed = Math::abs(axis_value) >= 0.5f; + pressed = Math::abs(axis_value) >= InputMap::DEFAULT_DEADZONE; emit_changed(); } diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index 6378f18545..954df36f3e 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -42,7 +42,7 @@ InputMap *InputMap::singleton = nullptr; void InputMap::_bind_methods() { ClassDB::bind_method(D_METHOD("has_action", "action"), &InputMap::has_action); ClassDB::bind_method(D_METHOD("get_actions"), &InputMap::_get_actions); - ClassDB::bind_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::add_action, DEFVAL(0.2f)); + ClassDB::bind_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::add_action, DEFVAL(DEFAULT_DEADZONE)); ClassDB::bind_method(D_METHOD("erase_action", "action"), &InputMap::erase_action); ClassDB::bind_method(D_METHOD("action_set_deadzone", "action", "deadzone"), &InputMap::action_set_deadzone); @@ -305,7 +305,7 @@ void InputMap::load_from_project_settings() { String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length()); Dictionary action = GLOBAL_GET(pi.name); - float deadzone = action.has("deadzone") ? (float)action["deadzone"] : 0.2f; + float deadzone = action.has("deadzone") ? (float)action["deadzone"] : DEFAULT_DEADZONE; Array events = action["events"]; add_action(name, deadzone); diff --git a/core/input/input_map.h b/core/input/input_map.h index 45798490f7..2b2a025332 100644 --- a/core/input/input_map.h +++ b/core/input/input_map.h @@ -49,6 +49,8 @@ public: List<Ref<InputEvent>> inputs; }; + static constexpr float DEFAULT_DEADZONE = 0.2f; + private: static InputMap *singleton; @@ -74,7 +76,7 @@ public: bool has_action(const StringName &p_action) const; List<StringName> get_actions() const; - void add_action(const StringName &p_action, float p_deadzone = 0.2); + void add_action(const StringName &p_action, float p_deadzone = DEFAULT_DEADZONE); void erase_action(const StringName &p_action); float action_get_deadzone(const StringName &p_action); diff --git a/core/io/file_access.compat.inc b/core/io/file_access.compat.inc new file mode 100644 index 0000000000..ed16050126 --- /dev/null +++ b/core/io/file_access.compat.inc @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* file_access.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +Ref<FileAccess> FileAccess::_open_encrypted_bind_compat_98918(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key) { + return open_encrypted(p_path, p_mode_flags, p_key, Vector<uint8_t>()); +} + +void FileAccess::_bind_compatibility_methods() { + ClassDB::bind_compatibility_static_method("FileAccess", D_METHOD("open_encrypted", "path", "mode_flags", "key"), &FileAccess::_open_encrypted_bind_compat_98918); +} + +#endif // DISABLE_DEPRECATED diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index d8bf645a7d..dd826e626b 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "file_access.h" +#include "file_access.compat.inc" #include "core/config/project_settings.h" #include "core/crypto/crypto_core.h" @@ -124,7 +125,7 @@ Ref<FileAccess> FileAccess::_open(const String &p_path, ModeFlags p_mode_flags) return fa; } -Ref<FileAccess> FileAccess::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key) { +Ref<FileAccess> FileAccess::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key, const Vector<uint8_t> &p_iv) { Ref<FileAccess> fa = _open(p_path, p_mode_flags); if (fa.is_null()) { return fa; @@ -132,7 +133,7 @@ Ref<FileAccess> FileAccess::open_encrypted(const String &p_path, ModeFlags p_mod Ref<FileAccessEncrypted> fae; fae.instantiate(); - Error err = fae->open_and_parse(fa, p_key, (p_mode_flags == WRITE) ? FileAccessEncrypted::MODE_WRITE_AES256 : FileAccessEncrypted::MODE_READ); + Error err = fae->open_and_parse(fa, p_key, (p_mode_flags == WRITE) ? FileAccessEncrypted::MODE_WRITE_AES256 : FileAccessEncrypted::MODE_READ, true, p_iv); last_file_open_error = err; if (err) { return Ref<FileAccess>(); @@ -806,7 +807,7 @@ String FileAccess::get_sha256(const String &p_file) { void FileAccess::_bind_methods() { ClassDB::bind_static_method("FileAccess", D_METHOD("open", "path", "flags"), &FileAccess::_open); - ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted", "path", "mode_flags", "key"), &FileAccess::open_encrypted); + ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted", "path", "mode_flags", "key", "iv"), &FileAccess::open_encrypted, DEFVAL(Vector<uint8_t>())); ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted_with_pass", "path", "mode_flags", "pass"), &FileAccess::open_encrypted_pass); ClassDB::bind_static_method("FileAccess", D_METHOD("open_compressed", "path", "mode_flags", "compression_mode"), &FileAccess::open_compressed, DEFVAL(0)); ClassDB::bind_static_method("FileAccess", D_METHOD("get_open_error"), &FileAccess::get_open_error); diff --git a/core/io/file_access.h b/core/io/file_access.h index 7f5687fe03..88c8110a51 100644 --- a/core/io/file_access.h +++ b/core/io/file_access.h @@ -109,6 +109,12 @@ protected: static FileCloseFailNotify close_fail_notify; +#ifndef DISABLE_DEPRECATED + static Ref<FileAccess> _open_encrypted_bind_compat_98918(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key); + + static void _bind_compatibility_methods(); +#endif + private: static bool backup_save; thread_local static Error last_file_open_error; @@ -199,7 +205,7 @@ public: static Ref<FileAccess> create_for_path(const String &p_path); static Ref<FileAccess> open(const String &p_path, int p_mode_flags, Error *r_error = nullptr); /// Create a file access (for the current platform) this is the only portable way of accessing files. - static Ref<FileAccess> open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key); + static Ref<FileAccess> open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key, const Vector<uint8_t> &p_iv = Vector<uint8_t>()); static Ref<FileAccess> open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass); static Ref<FileAccess> open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode = COMPRESSION_FASTLZ); static Error get_open_error(); diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index 24be9ef230..ba26f2e07b 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -36,7 +36,7 @@ #include <stdio.h> -Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic) { +Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic, const Vector<uint8_t> &p_iv) { ERR_FAIL_COND_V_MSG(file.is_valid(), ERR_ALREADY_IN_USE, vformat("Can't open file while another file from path '%s' is open.", file->get_path_absolute())); ERR_FAIL_COND_V(p_key.size() != 32, ERR_INVALID_PARAMETER); @@ -49,6 +49,16 @@ Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<u writing = true; file = p_base; key = p_key; + if (p_iv.is_empty()) { + iv.resize(16); + CryptoCore::RandomGenerator rng; + ERR_FAIL_COND_V_MSG(rng.init(), FAILED, "Failed to initialize random number generator."); + Error err = rng.get_random_bytes(iv.ptrw(), 16); + ERR_FAIL_COND_V(err != OK, err); + } else { + ERR_FAIL_COND_V(p_iv.size() != 16, ERR_INVALID_PARAMETER); + iv = p_iv; + } } else if (p_mode == MODE_READ) { writing = false; @@ -63,10 +73,8 @@ Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<u p_base->get_buffer(md5d, 16); length = p_base->get_64(); - unsigned char iv[16]; - for (int i = 0; i < 16; i++) { - iv[i] = p_base->get_8(); - } + iv.resize(16); + p_base->get_buffer(iv.ptrw(), 16); base = p_base->get_position(); ERR_FAIL_COND_V(p_base->get_length() < base + length, ERR_FILE_CORRUPT); @@ -83,7 +91,7 @@ Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<u CryptoCore::AESContext ctx; ctx.set_encode_key(key.ptrw(), 256); // Due to the nature of CFB, same key schedule is used for both encryption and decryption! - ctx.decrypt_cfb(ds, iv, data.ptrw(), data.ptrw()); + ctx.decrypt_cfb(ds, iv.ptrw(), data.ptrw(), data.ptrw()); } data.resize(length); @@ -145,14 +153,9 @@ void FileAccessEncrypted::_close() { file->store_buffer(hash, 16); file->store_64(data.size()); + file->store_buffer(iv.ptr(), 16); - unsigned char iv[16]; - for (int i = 0; i < 16; i++) { - iv[i] = Math::rand() % 256; - file->store_8(iv[i]); - } - - ctx.encrypt_cfb(len, iv, compressed.ptrw(), compressed.ptrw()); + ctx.encrypt_cfb(len, iv.ptrw(), compressed.ptrw(), compressed.ptrw()); file->store_buffer(compressed.ptr(), compressed.size()); data.clear(); diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h index 5f8c803d60..63a8cab145 100644 --- a/core/io/file_access_encrypted.h +++ b/core/io/file_access_encrypted.h @@ -44,6 +44,7 @@ public: }; private: + Vector<uint8_t> iv; Vector<uint8_t> key; bool writing = false; Ref<FileAccess> file; @@ -57,9 +58,11 @@ private: void _close(); public: - Error open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic = true); + Error open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic = true, const Vector<uint8_t> &p_iv = Vector<uint8_t>()); Error open_and_parse_password(Ref<FileAccess> p_base, const String &p_key, Mode p_mode); + Vector<uint8_t> get_iv() const { return iv; } + virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file virtual bool is_open() const override; ///< true when file is open diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index bf44b3a2db..b957a43de2 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -205,7 +205,7 @@ Ref<FileAccess> PackedData::try_open_path(const String &p_path) { } bool PackedData::has_path(const String &p_path) { - return files.has(PathMD5(p_path.simplify_path().md5_buffer())); + return files.has(PathMD5(p_path.simplify_path().trim_prefix("res://").md5_buffer())); } bool PackedData::has_directory(const String &p_path) { diff --git a/core/io/image.cpp b/core/io/image.cpp index fbf37cbee7..fa4484bb63 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -2611,23 +2611,25 @@ Image::AlphaMode Image::detect_alpha() const { } Error Image::load(const String &p_path) { + String path = ResourceUID::ensure_path(p_path); #ifdef DEBUG_ENABLED - if (p_path.begins_with("res://") && ResourceLoader::exists(p_path)) { - WARN_PRINT(vformat("Loaded resource as image file, this will not work on export: '%s'. Instead, import the image file as an Image resource and load it normally as a resource.", p_path)); + if (path.begins_with("res://") && ResourceLoader::exists(path)) { + WARN_PRINT(vformat("Loaded resource as image file, this will not work on export: '%s'. Instead, import the image file as an Image resource and load it normally as a resource.", path)); } #endif - return ImageLoader::load_image(p_path, this); + return ImageLoader::load_image(ResourceUID::ensure_path(p_path), this); } Ref<Image> Image::load_from_file(const String &p_path) { + String path = ResourceUID::ensure_path(p_path); #ifdef DEBUG_ENABLED - if (p_path.begins_with("res://") && ResourceLoader::exists(p_path)) { - WARN_PRINT(vformat("Loaded resource as image file, this will not work on export: '%s'. Instead, import the image file as an Image resource and load it normally as a resource.", p_path)); + if (path.begins_with("res://") && ResourceLoader::exists(path)) { + WARN_PRINT(vformat("Loaded resource as image file, this will not work on export: '%s'. Instead, import the image file as an Image resource and load it normally as a resource.", path)); } #endif Ref<Image> image; image.instantiate(); - Error err = ImageLoader::load_image(p_path, image); + Error err = ImageLoader::load_image(path, image); if (err != OK) { ERR_FAIL_V_MSG(Ref<Image>(), vformat("Failed to load image. Error %d", err)); } diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp index c14121a53b..946bc524e5 100644 --- a/core/io/resource_uid.cpp +++ b/core/io/resource_uid.cpp @@ -34,6 +34,7 @@ #include "core/crypto/crypto_core.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" +#include "core/io/resource_loader.h" // These constants are off by 1, causing the 'z' and '9' characters never to be used. // This cannot be fixed without breaking compatibility; see GH-83843. @@ -139,6 +140,21 @@ void ResourceUID::remove_id(ID p_id) { unique_ids.erase(p_id); } +String ResourceUID::uid_to_path(const String &p_uid) { + return singleton->get_id_path(singleton->text_to_id(p_uid)); +} + +String ResourceUID::path_to_uid(const String &p_path) { + return singleton->id_to_text(ResourceLoader::get_resource_uid(p_path)); +} + +String ResourceUID::ensure_path(const String &p_uid_or_path) { + if (p_uid_or_path.begins_with("uid://")) { + return uid_to_path(p_uid_or_path); + } + return p_uid_or_path; +} + Error ResourceUID::save_to_cache() { String cache_file = get_cache_file(); if (!FileAccess::exists(cache_file)) { diff --git a/core/io/resource_uid.h b/core/io/resource_uid.h index e56b89f603..7b735d296a 100644 --- a/core/io/resource_uid.h +++ b/core/io/resource_uid.h @@ -73,6 +73,10 @@ public: String get_id_path(ID p_id) const; void remove_id(ID p_id); + static String uid_to_path(const String &p_uid); + static String path_to_uid(const String &p_path); + static String ensure_path(const String &p_uid_or_path); + Error load_from_cache(bool p_reset); Error save_to_cache(); Error update_cache(); diff --git a/core/templates/a_hash_map.h b/core/templates/a_hash_map.h index 29983ea268..6e3a978d50 100644 --- a/core/templates/a_hash_map.h +++ b/core/templates/a_hash_map.h @@ -622,10 +622,11 @@ public: } // Inserts an element without checking if it already exists. - void insert_new(const TKey &p_key, const TValue &p_value) { + Iterator insert_new(const TKey &p_key, const TValue &p_value) { DEV_ASSERT(!has(p_key)); uint32_t hash = _hash(p_key); - _insert_element(p_key, p_value, hash); + uint32_t pos = _insert_element(p_key, p_value, hash); + return Iterator(elements + pos, elements, elements + num_elements); } /* Array methods. */ diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index ce64bcc17c..1721134d08 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -2981,7 +2981,7 @@ Editing the property prompts the user for restarting the editor. </constant> <constant name="PROPERTY_USAGE_SCRIPT_VARIABLE" value="4096" enum="PropertyUsageFlags" is_bitfield="true"> - The property is a script variable which should be serialized and saved in the scene file. + The property is a script variable. [constant PROPERTY_USAGE_SCRIPT_VARIABLE] can be used to distinguish between exported script variables from built-in variables (which don't have this usage flag). By default, [constant PROPERTY_USAGE_SCRIPT_VARIABLE] is [b]not[/b] applied to variables that are created by overriding [method Object._get_property_list] in a script. </constant> <constant name="PROPERTY_USAGE_STORE_IF_NULL" value="8192" enum="PropertyUsageFlags" is_bitfield="true"> The property value of type [Object] will be stored even if its value is [code]null[/code]. diff --git a/doc/classes/AnimationNodeAnimation.xml b/doc/classes/AnimationNodeAnimation.xml index 70c3e5a26e..f4490bc167 100644 --- a/doc/classes/AnimationNodeAnimation.xml +++ b/doc/classes/AnimationNodeAnimation.xml @@ -12,6 +12,10 @@ <link title="Third Person Shooter (TPS) Demo">https://godotengine.org/asset-library/asset/2710</link> </tutorials> <members> + <member name="advance_on_start" type="bool" setter="set_advance_on_start" getter="is_advance_on_start" default="false"> + If [code]true[/code], on receiving a request to play an animation from the start, the first frame is not drawn, but only processed, and playback starts from the next frame. + See also the notes of [method AnimationPlayer.play]. + </member> <member name="animation" type="StringName" setter="set_animation" getter="get_animation" default="&"""> Animation to use as an output. It is one of the animations provided by [member AnimationTree.anim_player]. </member> diff --git a/doc/classes/FileAccess.xml b/doc/classes/FileAccess.xml index b782937a8a..5c89b64429 100644 --- a/doc/classes/FileAccess.xml +++ b/doc/classes/FileAccess.xml @@ -308,6 +308,7 @@ <param index="0" name="path" type="String" /> <param index="1" name="mode_flags" type="int" enum="FileAccess.ModeFlags" /> <param index="2" name="key" type="PackedByteArray" /> + <param index="3" name="iv" type="PackedByteArray" default="PackedByteArray()" /> <description> Creates a new [FileAccess] object and opens an encrypted file in write or read mode. You need to pass a binary key to encrypt/decrypt it. [b]Note:[/b] The provided key must be 32 bytes long. diff --git a/doc/classes/GeometryInstance3D.xml b/doc/classes/GeometryInstance3D.xml index f4075af186..0c45154c8a 100644 --- a/doc/classes/GeometryInstance3D.xml +++ b/doc/classes/GeometryInstance3D.xml @@ -39,8 +39,12 @@ <member name="extra_cull_margin" type="float" setter="set_extra_cull_margin" getter="get_extra_cull_margin" default="0.0"> The extra distance added to the GeometryInstance3D's bounding box ([AABB]) to increase its cull box. </member> - <member name="gi_lightmap_scale" type="int" setter="set_lightmap_scale" getter="get_lightmap_scale" enum="GeometryInstance3D.LightmapScale" default="0"> + <member name="gi_lightmap_scale" type="int" setter="set_lightmap_scale" getter="get_lightmap_scale" enum="GeometryInstance3D.LightmapScale" default="0" deprecated="Use [member gi_lightmap_texel_scale] instead."> + The texel density to use for lightmapping in [LightmapGI]. + </member> + <member name="gi_lightmap_texel_scale" type="float" setter="set_lightmap_texel_scale" getter="get_lightmap_texel_scale" default="1.0"> The texel density to use for lightmapping in [LightmapGI]. Greater scale values provide higher resolution in the lightmap, which can result in sharper shadows for lights that have both direct and indirect light baked. However, greater scale values will also increase the space taken by the mesh in the lightmap texture, which increases the memory, storage, and bake time requirements. When using a single mesh at different scales, consider adjusting this value to keep the lightmap texel density consistent across meshes. + For example, doubling [member gi_lightmap_texel_scale] doubles the lightmap texture resolution for this object [i]on each axis[/i], so it will [i]quadruple[/i] the texel count. </member> <member name="gi_mode" type="int" setter="set_gi_mode" getter="get_gi_mode" enum="GeometryInstance3D.GIMode" default="1" keywords="global_illumination_mode, light_bake_mode"> The global illumination mode to use for the whole geometry. To avoid inconsistent results, use a mode that matches the purpose of the mesh during gameplay (static/dynamic). @@ -111,19 +115,19 @@ <constant name="GI_MODE_DYNAMIC" value="2" enum="GIMode"> Dynamic global illumination mode. Use for dynamic objects that contribute to global illumination. This GI mode is only effective when using [VoxelGI], but it has a higher performance impact than [constant GI_MODE_STATIC]. When using other GI methods, this will act the same as [constant GI_MODE_DISABLED]. When using [LightmapGI], the object will receive indirect lighting using lightmap probes instead of using the baked lightmap texture. </constant> - <constant name="LIGHTMAP_SCALE_1X" value="0" enum="LightmapScale"> + <constant name="LIGHTMAP_SCALE_1X" value="0" enum="LightmapScale" deprecated="Use [member gi_lightmap_texel_scale] instead."> The standard texel density for lightmapping with [LightmapGI]. </constant> - <constant name="LIGHTMAP_SCALE_2X" value="1" enum="LightmapScale"> + <constant name="LIGHTMAP_SCALE_2X" value="1" enum="LightmapScale" deprecated="Use [member gi_lightmap_texel_scale] instead."> Multiplies texel density by 2× for lightmapping with [LightmapGI]. To ensure consistency in texel density, use this when scaling a mesh by a factor between 1.5 and 3.0. </constant> - <constant name="LIGHTMAP_SCALE_4X" value="2" enum="LightmapScale"> + <constant name="LIGHTMAP_SCALE_4X" value="2" enum="LightmapScale" deprecated="Use [member gi_lightmap_texel_scale] instead."> Multiplies texel density by 4× for lightmapping with [LightmapGI]. To ensure consistency in texel density, use this when scaling a mesh by a factor between 3.0 and 6.0. </constant> - <constant name="LIGHTMAP_SCALE_8X" value="3" enum="LightmapScale"> + <constant name="LIGHTMAP_SCALE_8X" value="3" enum="LightmapScale" deprecated="Use [member gi_lightmap_texel_scale] instead."> Multiplies texel density by 8× for lightmapping with [LightmapGI]. To ensure consistency in texel density, use this when scaling a mesh by a factor greater than 6.0. </constant> - <constant name="LIGHTMAP_SCALE_MAX" value="4" enum="LightmapScale"> + <constant name="LIGHTMAP_SCALE_MAX" value="4" enum="LightmapScale" deprecated="Use [member gi_lightmap_texel_scale] instead."> Represents the size of the [enum LightmapScale] enum. </constant> <constant name="VISIBILITY_RANGE_FADE_DISABLED" value="0" enum="VisibilityRangeFadeMode"> diff --git a/doc/classes/LightmapGI.xml b/doc/classes/LightmapGI.xml index 139bf6e4d1..0a492364ec 100644 --- a/doc/classes/LightmapGI.xml +++ b/doc/classes/LightmapGI.xml @@ -70,6 +70,7 @@ </member> <member name="texel_scale" type="float" setter="set_texel_scale" getter="get_texel_scale" default="1.0"> Scales the lightmap texel density of all meshes for the current bake. This is a multiplier that builds upon the existing lightmap texel size defined in each imported 3D scene, along with the per-mesh density multiplier (which is designed to be used when the same mesh is used at different scales). Lower values will result in faster bake times. + For example, doubling [member texel_scale] doubles the lightmap texture resolution for all objects [i]on each axis[/i], so it will [i]quadruple[/i] the texel count. </member> <member name="use_denoiser" type="bool" setter="set_use_denoiser" getter="is_using_denoiser" default="true"> If [code]true[/code], uses a GPU-based denoising algorithm on the generated lightmap. This eliminates most noise within the generated lightmap at the cost of longer bake times. File sizes are generally not impacted significantly by the use of a denoiser, although lossless compression may do a better job at compressing a denoised image. diff --git a/doc/classes/MainLoop.xml b/doc/classes/MainLoop.xml index 17cc0d78d3..2d88876d24 100644 --- a/doc/classes/MainLoop.xml +++ b/doc/classes/MainLoop.xml @@ -5,7 +5,7 @@ </brief_description> <description> [MainLoop] is the abstract base class for a Godot project's game loop. It is inherited by [SceneTree], which is the default game loop implementation used in Godot projects, though it is also possible to write and use one's own [MainLoop] subclass instead of the scene tree. - Upon the application start, a [MainLoop] implementation must be provided to the OS; otherwise, the application will exit. This happens automatically (and a [SceneTree] is created) unless a [MainLoop] [Script] is provided from the command line (with e.g. [code]godot -s my_loop.gd[/code]) or the "Main Loop Type" project setting is overwritten. + Upon the application start, a [MainLoop] implementation must be provided to the OS; otherwise, the application will exit. This happens automatically (and a [SceneTree] is created) unless a [MainLoop] [Script] is provided from the command line (with e.g. [code]godot -s my_loop.gd[/code]) or the [member ProjectSettings.application/run/main_loop_type] project setting is overwritten. Here is an example script implementing a simple [MainLoop]: [codeblocks] [gdscript] diff --git a/doc/classes/PhysicalBone3D.xml b/doc/classes/PhysicalBone3D.xml index eda9fd6af5..e2ad3db0a5 100644 --- a/doc/classes/PhysicalBone3D.xml +++ b/doc/classes/PhysicalBone3D.xml @@ -57,7 +57,7 @@ </methods> <members> <member name="angular_damp" type="float" setter="set_angular_damp" getter="get_angular_damp" default="0.0"> - Damps the body's rotation. By default, the body will use the [b]Default Angular Damp[/b] in [b]Project > Project Settings > Physics > 3d[/b] or any value override set by an [Area3D] the body is in. Depending on [member angular_damp_mode], you can set [member angular_damp] to be added to or to replace the body's damping value. + Damps the body's rotation. By default, the body will use the [member ProjectSettings.physics/3d/default_angular_damp] project setting or any value override set by an [Area3D] the body is in. Depending on [member angular_damp_mode], you can set [member angular_damp] to be added to or to replace the body's damping value. See [member ProjectSettings.physics/3d/default_angular_damp] for more details about damping. </member> <member name="angular_damp_mode" type="int" setter="set_angular_damp_mode" getter="get_angular_damp_mode" enum="PhysicalBone3D.DampMode" default="0"> diff --git a/doc/classes/Range.xml b/doc/classes/Range.xml index 820ff04b70..a76676489f 100644 --- a/doc/classes/Range.xml +++ b/doc/classes/Range.xml @@ -54,7 +54,7 @@ Minimum value. Range is clamped if [member value] is less than [member min_value]. </member> <member name="page" type="float" setter="set_page" getter="get_page" default="0.0"> - Page size. Used mainly for [ScrollBar]. ScrollBar's length is its size multiplied by [member page] over the difference between [member min_value] and [member max_value]. + Page size. Used mainly for [ScrollBar]. A [ScrollBar]'s grabber length is the [ScrollBar]'s size multiplied by [member page] over the difference between [member min_value] and [member max_value]. </member> <member name="ratio" type="float" setter="set_as_ratio" getter="get_as_ratio"> The value mapped between 0 and 1. diff --git a/doc/classes/ResourceImporterTexture.xml b/doc/classes/ResourceImporterTexture.xml index 0761702aa1..a39e48c7bf 100644 --- a/doc/classes/ResourceImporterTexture.xml +++ b/doc/classes/ResourceImporterTexture.xml @@ -90,9 +90,13 @@ <member name="process/size_limit" type="int" setter="" getter="" default="0"> If set to a value greater than [code]0[/code], the size of the texture is limited on import to a value smaller than or equal to the value specified here. For non-square textures, the size limit affects the longer dimension, with the shorter dimension scaled to preserve aspect ratio. Resizing is performed using cubic interpolation. This can be used to reduce memory usage without affecting the source images, or avoid issues with textures not displaying on mobile/web platforms (as these usually can't display textures larger than 4096×4096). + [b]Note:[/b] Even if this is set to [code]0[/code], import size is limited to the following dimensions for technical reasons. Depending on [member compress/mode], textures will be downsampled on import if necessary: + - [b]Lossy:[/b] 16383 pixels width or height, whichever is larger; + - [b]Basis Universal:[/b] 16384 pixels width or height, whichever is larger; + - [b]All other modes:[/b] 32768 pixels width or height, whichever is larger. </member> <member name="roughness/mode" type="int" setter="" getter="" default="0"> - The color channel to consider as a roughness map in this texture. Only effective if Roughness > Src Normal is not empty. + The color channel to consider as a roughness map in this texture. Only effective if [member roughness/src_normal] is not empty. </member> <member name="roughness/src_normal" type="String" setter="" getter="" default=""""> The path to the texture to consider as a normal map for roughness filtering on import. Specifying this can help decrease specular aliasing slightly in 3D. diff --git a/doc/classes/RigidBody2D.xml b/doc/classes/RigidBody2D.xml index 5661d1a276..1977b238e4 100644 --- a/doc/classes/RigidBody2D.xml +++ b/doc/classes/RigidBody2D.xml @@ -123,7 +123,7 @@ </methods> <members> <member name="angular_damp" type="float" setter="set_angular_damp" getter="get_angular_damp" default="0.0"> - Damps the body's rotation. By default, the body will use the [b]Default Angular Damp[/b] in [b]Project > Project Settings > Physics > 2d[/b] or any value override set by an [Area2D] the body is in. Depending on [member angular_damp_mode], you can set [member angular_damp] to be added to or to replace the body's damping value. + Damps the body's rotation. By default, the body will use the [member ProjectSettings.physics/2d/default_angular_damp] setting or any value override set by an [Area2D] the body is in. Depending on [member angular_damp_mode], you can set [member angular_damp] to be added to or to replace the body's damping value. See [member ProjectSettings.physics/2d/default_angular_damp] for more details about damping. </member> <member name="angular_damp_mode" type="int" setter="set_angular_damp_mode" getter="get_angular_damp_mode" enum="RigidBody2D.DampMode" default="0"> @@ -172,7 +172,7 @@ For a body that is always frozen, use [StaticBody2D] or [AnimatableBody2D] instead. </member> <member name="gravity_scale" type="float" setter="set_gravity_scale" getter="get_gravity_scale" default="1.0"> - Multiplies the gravity applied to the body. The body's gravity is calculated from the [b]Default Gravity[/b] value in [b]Project > Project Settings > Physics > 2d[/b] and/or any additional gravity vector applied by [Area2D]s. + Multiplies the gravity applied to the body. The body's gravity is calculated from the [member ProjectSettings.physics/2d/default_gravity] project setting and/or any additional gravity vector applied by [Area2D]s. </member> <member name="inertia" type="float" setter="set_inertia" getter="get_inertia" default="0.0"> The body's moment of inertia. This is like mass, but for rotation: it determines how much torque it takes to rotate the body. The moment of inertia is usually computed automatically from the mass and the shapes, but this property allows you to set a custom value. @@ -201,7 +201,7 @@ [/codeblocks] </member> <member name="linear_damp" type="float" setter="set_linear_damp" getter="get_linear_damp" default="0.0"> - Damps the body's movement. By default, the body will use the [b]Default Linear Damp[/b] in [b]Project > Project Settings > Physics > 2d[/b] or any value override set by an [Area2D] the body is in. Depending on [member linear_damp_mode], you can set [member linear_damp] to be added to or to replace the body's damping value. + Damps the body's movement. By default, the body will use the [member ProjectSettings.physics/2d/default_linear_damp] setting or any value override set by an [Area2D] the body is in. Depending on [member linear_damp_mode], you can set [member linear_damp] to be added to or to replace the body's damping value. See [member ProjectSettings.physics/2d/default_linear_damp] for more details about damping. </member> <member name="linear_damp_mode" type="int" setter="set_linear_damp_mode" getter="get_linear_damp_mode" enum="RigidBody2D.DampMode" default="0"> diff --git a/doc/classes/RigidBody3D.xml b/doc/classes/RigidBody3D.xml index 9a299ade57..de6d5cde3d 100644 --- a/doc/classes/RigidBody3D.xml +++ b/doc/classes/RigidBody3D.xml @@ -130,7 +130,7 @@ </methods> <members> <member name="angular_damp" type="float" setter="set_angular_damp" getter="get_angular_damp" default="0.0"> - Damps the body's rotation. By default, the body will use the [b]Default Angular Damp[/b] in [b]Project > Project Settings > Physics > 3d[/b] or any value override set by an [Area3D] the body is in. Depending on [member angular_damp_mode], you can set [member angular_damp] to be added to or to replace the body's damping value. + Damps the body's rotation. By default, the body will use the [member ProjectSettings.physics/3d/default_angular_damp] project setting or any value override set by an [Area3D] the body is in. Depending on [member angular_damp_mode], you can set [member angular_damp] to be added to or to replace the body's damping value. See [member ProjectSettings.physics/3d/default_angular_damp] for more details about damping. </member> <member name="angular_damp_mode" type="int" setter="set_angular_damp_mode" getter="get_angular_damp_mode" enum="RigidBody3D.DampMode" default="0"> @@ -208,7 +208,7 @@ [/codeblocks] </member> <member name="linear_damp" type="float" setter="set_linear_damp" getter="get_linear_damp" default="0.0"> - Damps the body's movement. By default, the body will use the [b]Default Linear Damp[/b] in [b]Project > Project Settings > Physics > 3d[/b] or any value override set by an [Area3D] the body is in. Depending on [member linear_damp_mode], you can set [member linear_damp] to be added to or to replace the body's damping value. + Damps the body's movement. By default, the body will use the [member ProjectSettings.physics/3d/default_linear_damp] project setting or any value override set by an [Area3D] the body is in. Depending on [member linear_damp_mode], you can set [member linear_damp] to be added to or to replace the body's damping value. See [member ProjectSettings.physics/3d/default_linear_damp] for more details about damping. </member> <member name="linear_damp_mode" type="int" setter="set_linear_damp_mode" getter="get_linear_damp_mode" enum="RigidBody3D.DampMode" default="0"> diff --git a/doc/classes/String.xml b/doc/classes/String.xml index e59734cf03..d0512b8e1c 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -248,7 +248,7 @@ <param index="1" name="placeholder" type="String" default=""{_}"" /> <description> Formats the string by replacing all occurrences of [param placeholder] with the elements of [param values]. - [param values] can be a [Dictionary], an [Array] or an [Object]. Any underscores in [param placeholder] will be replaced with the corresponding keys in advance. Array elements use their index as keys. + [param values] can be a [Dictionary], an [Array], or an [Object]. Any underscores in [param placeholder] will be replaced with the corresponding keys in advance. Array elements use their index as keys. [codeblock] # Prints "Waiting for Godot is a play by Samuel Beckett, and Godot Engine is named after it." var use_array_values = "Waiting for {0} is a play by {1}, and {0} Engine is named after it." @@ -265,7 +265,7 @@ [/codeblock] When passing an [Object], the property names from [method Object.get_property_list] are used as keys. [codeblock] - # Prints: Visible true, position (0, 0). + # Prints "Visible true, position (0, 0)" var node = Node2D.new() print("Visible {visible}, position {position}".format(node)) [/codeblock] diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml index 4982bc36a8..e561ef54fa 100644 --- a/doc/classes/StringName.xml +++ b/doc/classes/StringName.xml @@ -231,7 +231,7 @@ <param index="1" name="placeholder" type="String" default=""{_}"" /> <description> Formats the string by replacing all occurrences of [param placeholder] with the elements of [param values]. - [param values] can be a [Dictionary] or an [Array]. Any underscores in [param placeholder] will be replaced with the corresponding keys in advance. Array elements use their index as keys. + [param values] can be a [Dictionary], an [Array], or an [Object]. Any underscores in [param placeholder] will be replaced with the corresponding keys in advance. Array elements use their index as keys. [codeblock] # Prints "Waiting for Godot is a play by Samuel Beckett, and Godot Engine is named after it." var use_array_values = "Waiting for {0} is a play by {1}, and {0} Engine is named after it." @@ -246,6 +246,12 @@ print("User {} is {}.".format([42, "Godot"], "{}")) print("User {id} is {name}.".format([["id", 42], ["name", "Godot"]])) [/codeblock] + When passing an [Object], the property names from [method Object.get_property_list] are used as keys. + [codeblock] + # Prints "Visible true, position (0, 0)" + var node = Node2D.new() + print("Visible {visible}, position {position}".format(node)) + [/codeblock] See also the [url=$DOCS_URL/tutorials/scripting/gdscript/gdscript_format_string.html]GDScript format string[/url] tutorial. [b]Note:[/b] Each replacement is done sequentially for each element of [param values], [b]not[/b] all at once. This means that if any element is inserted and it contains another placeholder, it may be changed by the next replacement. While this can be very useful, it often causes unexpected results. If not necessary, make sure [param values]'s elements do not contain placeholders. [codeblock] diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp index 0cc89dfaca..0b1b0651c8 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.cpp +++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp @@ -2160,6 +2160,7 @@ RDD::FenceID RenderingDeviceDriverD3D12::fence_create() { Error RenderingDeviceDriverD3D12::fence_wait(FenceID p_fence) { FenceInfo *fence = (FenceInfo *)(p_fence.id); + fence->d3d_fence->SetEventOnCompletion(fence->fence_value, fence->event_handle); DWORD res = WaitForSingleObjectEx(fence->event_handle, INFINITE, FALSE); #ifdef PIX_ENABLED PIXNotifyWakeFromFenceSignal(fence->event_handle); @@ -2254,7 +2255,6 @@ Error RenderingDeviceDriverD3D12::command_queue_execute_and_present(CommandQueue FenceInfo *fence = (FenceInfo *)(p_cmd_fence.id); fence->fence_value++; command_queue->d3d_queue->Signal(fence->d3d_fence.Get(), fence->fence_value); - fence->d3d_fence->SetEventOnCompletion(fence->fence_value, fence->event_handle); } } diff --git a/drivers/metal/rendering_context_driver_metal.h b/drivers/metal/rendering_context_driver_metal.h index 7e0b09186d..1fdc255f93 100644 --- a/drivers/metal/rendering_context_driver_metal.h +++ b/drivers/metal/rendering_context_driver_metal.h @@ -107,6 +107,7 @@ public: uint32_t height = 0; DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED; bool needs_resize = false; + double present_minimum_duration = 0.0; Surface( #ifdef __OBJC__ @@ -123,6 +124,7 @@ public: virtual Error resize(uint32_t p_desired_framebuffer_count) = 0; virtual RDD::FramebufferID acquire_next_frame_buffer() = 0; virtual void present(MDCommandBuffer *p_cmd_buffer) = 0; + void set_max_fps(int p_max_fps) { present_minimum_duration = p_max_fps ? 1.0 / p_max_fps : 0.0; } }; #ifdef __OBJC__ diff --git a/drivers/metal/rendering_context_driver_metal.mm b/drivers/metal/rendering_context_driver_metal.mm index cf8c7e1c83..199ec25d79 100644 --- a/drivers/metal/rendering_context_driver_metal.mm +++ b/drivers/metal/rendering_context_driver_metal.mm @@ -172,7 +172,7 @@ public: count--; front = (front + 1) % frame_buffers.size(); - [p_cmd_buffer->get_command_buffer() presentDrawable:drawable]; + [p_cmd_buffer->get_command_buffer() presentDrawable:drawable afterMinimumDuration:present_minimum_duration]; } }; diff --git a/drivers/metal/rendering_device_driver_metal.h b/drivers/metal/rendering_device_driver_metal.h index e238de958e..09ab7601e9 100644 --- a/drivers/metal/rendering_device_driver_metal.h +++ b/drivers/metal/rendering_device_driver_metal.h @@ -220,6 +220,7 @@ public: virtual FramebufferID swap_chain_acquire_framebuffer(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, bool &r_resize_required) override final; virtual RenderPassID swap_chain_get_render_pass(SwapChainID p_swap_chain) override final; virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) override final; + virtual void swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) override final; virtual void swap_chain_free(SwapChainID p_swap_chain) override final; #pragma mark - Frame Buffer diff --git a/drivers/metal/rendering_device_driver_metal.mm b/drivers/metal/rendering_device_driver_metal.mm index d90f528a14..1dec02699e 100644 --- a/drivers/metal/rendering_device_driver_metal.mm +++ b/drivers/metal/rendering_device_driver_metal.mm @@ -982,6 +982,12 @@ RDD::DataFormat RenderingDeviceDriverMetal::swap_chain_get_format(SwapChainID p_ return swap_chain->data_format; } +void RenderingDeviceDriverMetal::swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) { + SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id); + RenderingContextDriverMetal::Surface *metal_surface = (RenderingContextDriverMetal::Surface *)(swap_chain->surface); + metal_surface->set_max_fps(p_max_fps); +} + void RenderingDeviceDriverMetal::swap_chain_free(SwapChainID p_swap_chain) { SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id); _swap_chain_release(swap_chain); diff --git a/drivers/unix/net_socket_unix.cpp b/drivers/unix/net_socket_unix.cpp index 3be615d9ad..3eaf1b2885 100644 --- a/drivers/unix/net_socket_unix.cpp +++ b/drivers/unix/net_socket_unix.cpp @@ -60,7 +60,7 @@ #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP #endif -size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type) { +size_t NetSocketUnix::_set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type) { memset(p_addr, 0, sizeof(struct sockaddr_storage)); if (p_ip_type == IP::TYPE_IPV6 || p_ip_type == IP::TYPE_ANY) { // IPv6 socket. @@ -95,7 +95,7 @@ size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const } } -void NetSocketPosix::_set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port) { +void NetSocketUnix::_set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port) { if (p_addr->ss_family == AF_INET) { struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr; if (r_ip) { @@ -115,21 +115,21 @@ void NetSocketPosix::_set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ } } -NetSocket *NetSocketPosix::_create_func() { - return memnew(NetSocketPosix); +NetSocket *NetSocketUnix::_create_func() { + return memnew(NetSocketUnix); } -void NetSocketPosix::make_default() { +void NetSocketUnix::make_default() { _create = _create_func; } -void NetSocketPosix::cleanup() { +void NetSocketUnix::cleanup() { } -NetSocketPosix::NetSocketPosix() { +NetSocketUnix::NetSocketUnix() { } -NetSocketPosix::~NetSocketPosix() { +NetSocketUnix::~NetSocketUnix() { close(); } @@ -140,7 +140,7 @@ NetSocketPosix::~NetSocketPosix() { #pragma GCC diagnostic ignored "-Wlogical-op" #endif -NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const { +NetSocketUnix::NetError NetSocketUnix::_get_socket_error() const { if (errno == EISCONN) { return ERR_NET_IS_CONNECTED; } @@ -167,7 +167,7 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const { #pragma GCC diagnostic pop #endif -bool NetSocketPosix::_can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const { +bool NetSocketUnix::_can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const { if (p_for_bind && !(p_ip.is_valid() || p_ip.is_wildcard())) { return false; } else if (!p_for_bind && !p_ip.is_valid()) { @@ -178,7 +178,7 @@ bool NetSocketPosix::_can_use_ip(const IPAddress &p_ip, const bool p_for_bind) c return !(_ip_type != IP::TYPE_ANY && !p_ip.is_wildcard() && _ip_type != type); } -_FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add) { +_FORCE_INLINE_ Error NetSocketUnix::_change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add) { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); ERR_FAIL_COND_V(!_can_use_ip(p_ip, false), ERR_INVALID_PARAMETER); @@ -232,7 +232,7 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IPAddress p_ip, Str return OK; } -void NetSocketPosix::_set_socket(int p_sock, IP::Type p_ip_type, bool p_is_stream) { +void NetSocketUnix::_set_socket(int p_sock, IP::Type p_ip_type, bool p_is_stream) { _sock = p_sock; _ip_type = p_ip_type; _is_stream = p_is_stream; @@ -240,13 +240,13 @@ void NetSocketPosix::_set_socket(int p_sock, IP::Type p_ip_type, bool p_is_strea _set_close_exec_enabled(true); } -void NetSocketPosix::_set_close_exec_enabled(bool p_enabled) { +void NetSocketUnix::_set_close_exec_enabled(bool p_enabled) { // Enable close on exec to avoid sharing with subprocesses. Off by default on Windows. int opts = fcntl(_sock, F_GETFD); fcntl(_sock, F_SETFD, opts | FD_CLOEXEC); } -Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) { +Error NetSocketUnix::open(Type p_sock_type, IP::Type &ip_type) { ERR_FAIL_COND_V(is_open(), ERR_ALREADY_IN_USE); ERR_FAIL_COND_V(ip_type > IP::TYPE_ANY || ip_type < IP::TYPE_NONE, ERR_INVALID_PARAMETER); @@ -299,7 +299,7 @@ Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) { return OK; } -void NetSocketPosix::close() { +void NetSocketUnix::close() { if (_sock != -1) { ::close(_sock); } @@ -309,7 +309,7 @@ void NetSocketPosix::close() { _is_stream = false; } -Error NetSocketPosix::bind(IPAddress p_addr, uint16_t p_port) { +Error NetSocketUnix::bind(IPAddress p_addr, uint16_t p_port) { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); ERR_FAIL_COND_V(!_can_use_ip(p_addr, true), ERR_INVALID_PARAMETER); @@ -326,7 +326,7 @@ Error NetSocketPosix::bind(IPAddress p_addr, uint16_t p_port) { return OK; } -Error NetSocketPosix::listen(int p_max_pending) { +Error NetSocketUnix::listen(int p_max_pending) { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); if (::listen(_sock, p_max_pending) != 0) { @@ -339,7 +339,7 @@ Error NetSocketPosix::listen(int p_max_pending) { return OK; } -Error NetSocketPosix::connect_to_host(IPAddress p_host, uint16_t p_port) { +Error NetSocketUnix::connect_to_host(IPAddress p_host, uint16_t p_port) { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); ERR_FAIL_COND_V(!_can_use_ip(p_host, false), ERR_INVALID_PARAMETER); @@ -367,7 +367,7 @@ Error NetSocketPosix::connect_to_host(IPAddress p_host, uint16_t p_port) { return OK; } -Error NetSocketPosix::poll(PollType p_type, int p_timeout) const { +Error NetSocketUnix::poll(PollType p_type, int p_timeout) const { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); struct pollfd pfd; @@ -401,7 +401,7 @@ Error NetSocketPosix::poll(PollType p_type, int p_timeout) const { return OK; } -Error NetSocketPosix::recv(uint8_t *p_buffer, int p_len, int &r_read) { +Error NetSocketUnix::recv(uint8_t *p_buffer, int p_len, int &r_read) { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); r_read = ::recv(_sock, p_buffer, p_len, 0); @@ -422,7 +422,7 @@ Error NetSocketPosix::recv(uint8_t *p_buffer, int p_len, int &r_read) { return OK; } -Error NetSocketPosix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek) { +Error NetSocketUnix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek) { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); struct sockaddr_storage from; @@ -460,7 +460,7 @@ Error NetSocketPosix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddr return OK; } -Error NetSocketPosix::send(const uint8_t *p_buffer, int p_len, int &r_sent) { +Error NetSocketUnix::send(const uint8_t *p_buffer, int p_len, int &r_sent) { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); int flags = 0; @@ -486,7 +486,7 @@ Error NetSocketPosix::send(const uint8_t *p_buffer, int p_len, int &r_sent) { return OK; } -Error NetSocketPosix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) { +Error NetSocketUnix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); struct sockaddr_storage addr; @@ -508,7 +508,7 @@ Error NetSocketPosix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP return OK; } -Error NetSocketPosix::set_broadcasting_enabled(bool p_enabled) { +Error NetSocketUnix::set_broadcasting_enabled(bool p_enabled) { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); // IPv6 has no broadcast support. if (_ip_type == IP::TYPE_IPV6) { @@ -523,7 +523,7 @@ Error NetSocketPosix::set_broadcasting_enabled(bool p_enabled) { return OK; } -void NetSocketPosix::set_blocking_enabled(bool p_enabled) { +void NetSocketUnix::set_blocking_enabled(bool p_enabled) { ERR_FAIL_COND(!is_open()); int ret = 0; @@ -539,7 +539,7 @@ void NetSocketPosix::set_blocking_enabled(bool p_enabled) { } } -void NetSocketPosix::set_ipv6_only_enabled(bool p_enabled) { +void NetSocketUnix::set_ipv6_only_enabled(bool p_enabled) { ERR_FAIL_COND(!is_open()); // This option is only available in IPv6 sockets. ERR_FAIL_COND(_ip_type == IP::TYPE_IPV4); @@ -550,7 +550,7 @@ void NetSocketPosix::set_ipv6_only_enabled(bool p_enabled) { } } -void NetSocketPosix::set_tcp_no_delay_enabled(bool p_enabled) { +void NetSocketUnix::set_tcp_no_delay_enabled(bool p_enabled) { ERR_FAIL_COND(!is_open()); ERR_FAIL_COND(!_is_stream); // Not TCP. @@ -560,7 +560,7 @@ void NetSocketPosix::set_tcp_no_delay_enabled(bool p_enabled) { } } -void NetSocketPosix::set_reuse_address_enabled(bool p_enabled) { +void NetSocketUnix::set_reuse_address_enabled(bool p_enabled) { ERR_FAIL_COND(!is_open()); int par = p_enabled ? 1 : 0; @@ -569,11 +569,11 @@ void NetSocketPosix::set_reuse_address_enabled(bool p_enabled) { } } -bool NetSocketPosix::is_open() const { +bool NetSocketUnix::is_open() const { return _sock != -1; } -int NetSocketPosix::get_available_bytes() const { +int NetSocketUnix::get_available_bytes() const { ERR_FAIL_COND_V(!is_open(), -1); int len; @@ -586,7 +586,7 @@ int NetSocketPosix::get_available_bytes() const { return len; } -Error NetSocketPosix::get_socket_address(IPAddress *r_ip, uint16_t *r_port) const { +Error NetSocketUnix::get_socket_address(IPAddress *r_ip, uint16_t *r_port) const { ERR_FAIL_COND_V(!is_open(), FAILED); struct sockaddr_storage saddr; @@ -600,7 +600,7 @@ Error NetSocketPosix::get_socket_address(IPAddress *r_ip, uint16_t *r_port) cons return OK; } -Ref<NetSocket> NetSocketPosix::accept(IPAddress &r_ip, uint16_t &r_port) { +Ref<NetSocket> NetSocketUnix::accept(IPAddress &r_ip, uint16_t &r_port) { Ref<NetSocket> out; ERR_FAIL_COND_V(!is_open(), out); @@ -615,17 +615,17 @@ Ref<NetSocket> NetSocketPosix::accept(IPAddress &r_ip, uint16_t &r_port) { _set_ip_port(&their_addr, &r_ip, &r_port); - NetSocketPosix *ns = memnew(NetSocketPosix); + NetSocketUnix *ns = memnew(NetSocketUnix); ns->_set_socket(fd, _ip_type, _is_stream); ns->set_blocking_enabled(false); return Ref<NetSocket>(ns); } -Error NetSocketPosix::join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) { +Error NetSocketUnix::join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) { return _change_multicast_group(p_multi_address, p_if_name, true); } -Error NetSocketPosix::leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) { +Error NetSocketUnix::leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) { return _change_multicast_group(p_multi_address, p_if_name, false); } diff --git a/drivers/unix/net_socket_unix.h b/drivers/unix/net_socket_unix.h index 22f7bfdd91..08c45f2ac3 100644 --- a/drivers/unix/net_socket_unix.h +++ b/drivers/unix/net_socket_unix.h @@ -37,7 +37,7 @@ #include <sys/socket.h> -class NetSocketPosix : public NetSocket { +class NetSocketUnix : public NetSocket { private: int _sock = -1; IP::Type _ip_type = IP::TYPE_NONE; @@ -93,8 +93,8 @@ public: virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override; virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override; - NetSocketPosix(); - ~NetSocketPosix() override; + NetSocketUnix(); + ~NetSocketUnix() override; }; #endif // UNIX_ENABLED && !UNIX_SOCKET_UNAVAILABLE diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index 92855d1a43..3976ae7d84 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -167,7 +167,9 @@ void OS_Unix::initialize_core() { DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA); DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM); - NetSocketPosix::make_default(); +#ifndef UNIX_SOCKET_UNAVAILABLE + NetSocketUnix::make_default(); +#endif IPUnix::make_default(); process_map = memnew((HashMap<ProcessID, ProcessInfo>)); @@ -176,7 +178,9 @@ void OS_Unix::initialize_core() { void OS_Unix::finalize_core() { memdelete(process_map); - NetSocketPosix::cleanup(); +#ifndef UNIX_SOCKET_UNAVAILABLE + NetSocketUnix::cleanup(); +#endif } Vector<String> OS_Unix::get_video_adapter_driver_info() const { diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp index 6eecd850f5..a86f72e0b9 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.cpp +++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -3032,13 +3032,10 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue, #if defined(SWAPPY_FRAME_PACING_ENABLED) if (swappy_frame_pacer_enable) { - const double max_fps = Engine::get_singleton()->get_max_fps(); - const uint64_t max_time = max_fps > 0 ? uint64_t((1000.0 * 1000.0 * 1000.0) / max_fps) : 0; - SwappyVk_initAndGetRefreshCycleDuration(get_jni_env(), static_cast<OS_Android *>(OS::get_singleton())->get_godot_java()->get_activity(), physical_device, vk_device, swap_chain->vk_swapchain, &swap_chain->refresh_duration); SwappyVk_setWindow(vk_device, swap_chain->vk_swapchain, static_cast<OS_Android *>(OS::get_singleton())->get_native_window()); - SwappyVk_setSwapIntervalNS(vk_device, swap_chain->vk_swapchain, MAX(swap_chain->refresh_duration, max_time)); + SwappyVk_setSwapIntervalNS(vk_device, swap_chain->vk_swapchain, swap_chain->refresh_duration); enum SwappyModes { PIPELINE_FORCED_ON, diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index cc81001efb..b5306154ba 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1516,6 +1516,9 @@ void EditorFileSystem::_delete_internal_files(const String &p_file) { } da->remove(p_file + ".import"); } + if (FileAccess::exists(p_file + ".uid")) { + DirAccess::remove_absolute(p_file + ".uid"); + } } int EditorFileSystem::_insert_actions_delete_files_directory(EditorFileSystemDirectory *p_dir) { diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp index b0c06475f8..0fc7052a2b 100644 --- a/editor/editor_help_search.cpp +++ b/editor/editor_help_search.cpp @@ -1166,7 +1166,7 @@ TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const if (p_matching_keyword.is_empty()) { item->set_text(0, p_doc->name); } else { - item->set_text(0, p_doc->name + " - " + TTR(vformat("Matches the \"%s\" keyword.", p_matching_keyword))); + item->set_text(0, p_doc->name + " - " + vformat(TTR("Matches the \"%s\" keyword."), p_matching_keyword)); } if (!term.is_empty()) { @@ -1272,7 +1272,7 @@ TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, cons text = p_class_name + "." + p_text; } if (!p_matching_keyword.is_empty()) { - text += " - " + TTR(vformat("Matches the \"%s\" keyword.", p_matching_keyword)); + text += " - " + vformat(TTR("Matches the \"%s\" keyword."), p_matching_keyword); } item->set_text(0, text); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index f8e23ecc9d..87f1f1b8a0 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -1454,6 +1454,16 @@ void EditorNode::save_resource_as(const Ref<Resource> &p_resource, const String file->popup_file_dialog(); } +void EditorNode::ensure_uid_file(const String &p_new_resource_path) { + if (ResourceLoader::exists(p_new_resource_path) && !ResourceLoader::has_custom_uid_support(p_new_resource_path) && !FileAccess::exists(p_new_resource_path + ".uid")) { + Ref<FileAccess> f = FileAccess::open(p_new_resource_path + ".uid", FileAccess::WRITE); + if (f.is_valid()) { + const ResourceUID::ID id = ResourceUID::get_singleton()->create_id(); + f->store_line(ResourceUID::get_singleton()->id_to_text(id)); + } + } +} + void EditorNode::_menu_option(int p_option) { _menu_option_confirm(p_option, false); } @@ -2173,6 +2183,12 @@ void EditorNode::_dialog_action(String p_file) { case RESOURCE_SAVE_AS: { ERR_FAIL_COND(saving_resource.is_null()); save_resource_in_path(saving_resource, p_file); + + if (current_menu_option == RESOURCE_SAVE_AS) { + // Create .uid file when making new Resource. + ensure_uid_file(p_file); + } + saving_resource = Ref<Resource>(); ObjectID current_id = editor_history.get_current(); Object *current_obj = current_id.is_valid() ? ObjectDB::get_instance(current_id) : nullptr; @@ -7690,24 +7706,28 @@ EditorNode::EditorNode() { disk_changed = memnew(ConfirmationDialog); { - disk_changed->set_title(TTR("Files have been modified on disk")); + disk_changed->set_title(TTR("Files have been modified outside Godot")); VBoxContainer *vbc = memnew(VBoxContainer); disk_changed->add_child(vbc); Label *dl = memnew(Label); - dl->set_text(TTR("The following files are newer on disk.\nWhat action should be taken?")); + dl->set_text(TTR("The following files are newer on disk:")); vbc->add_child(dl); disk_changed_list = memnew(Tree); vbc->add_child(disk_changed_list); disk_changed_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); + Label *what_action_label = memnew(Label); + what_action_label->set_text(TTR("What action should be taken?")); + vbc->add_child(what_action_label); + disk_changed->connect(SceneStringName(confirmed), callable_mp(this, &EditorNode::_reload_modified_scenes)); disk_changed->connect(SceneStringName(confirmed), callable_mp(this, &EditorNode::_reload_project_settings)); - disk_changed->set_ok_button_text(TTR("Discard local changes and reload")); + disk_changed->set_ok_button_text(TTR("Reload from disk")); - disk_changed->add_button(TTR("Keep local changes and overwrite"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); + disk_changed->add_button(TTR("Ignore external changes"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); disk_changed->connect("custom_action", callable_mp(this, &EditorNode::_resave_scenes)); } diff --git a/editor/editor_node.h b/editor/editor_node.h index 49c1699c28..4a283983c8 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -747,6 +747,7 @@ public: void save_resource_in_path(const Ref<Resource> &p_resource, const String &p_path); void save_resource(const Ref<Resource> &p_resource); void save_resource_as(const Ref<Resource> &p_resource, const String &p_at_path = String()); + void ensure_uid_file(const String &p_new_resource_path); void show_about() { _menu_option_confirm(HELP_ABOUT, false); } diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp index 6ca83c5e25..cddc8173cb 100644 --- a/editor/export/editor_export.cpp +++ b/editor/export/editor_export.cpp @@ -87,6 +87,8 @@ void EditorExport::_save() { config->set_value(section, "encryption_include_filters", preset->get_enc_in_filter()); config->set_value(section, "encryption_exclude_filters", preset->get_enc_ex_filter()); + config->set_value(section, "seed", preset->get_seed()); + config->set_value(section, "encrypt_pck", preset->get_enc_pck()); config->set_value(section, "encrypt_directory", preset->get_enc_directory()); config->set_value(section, "script_export_mode", preset->get_script_export_mode()); @@ -307,6 +309,9 @@ void EditorExport::load_config() { preset->set_script_export_mode(config->get_value(section, "script_export_mode", EditorExportPreset::MODE_SCRIPT_BINARY_TOKENS_COMPRESSED)); preset->set_patches(config->get_value(section, "patches", Vector<String>())); + if (config->has_section_key(section, "seed")) { + preset->set_seed(config->get_value(section, "seed")); + } if (config->has_section_key(section, "encrypt_pck")) { preset->set_enc_pck(config->get_value(section, "encrypt_pck")); } diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index 8b8fafcd32..91c9ff9807 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -216,7 +216,7 @@ void EditorExportPlatform::_unload_patches() { PackedData::get_singleton()->clear(); } -Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { +Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed) { ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export."); PackData *pd = (PackData *)p_userdata; @@ -247,10 +247,27 @@ Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_pa Ref<FileAccess> ftmp = pd->f; if (sd.encrypted) { + Vector<uint8_t> iv; + if (p_seed != 0) { + uint64_t seed = p_seed; + + const uint8_t *ptr = p_data.ptr(); + int64_t len = p_data.size(); + for (int64_t i = 0; i < len; i++) { + seed = ((seed << 5) + seed) ^ ptr[i]; + } + + RandomPCG rng = RandomPCG(seed, RandomPCG::DEFAULT_INC); + iv.resize(16); + for (int i = 0; i < 16; i++) { + iv.write[i] = rng.rand() % 256; + } + } + fae.instantiate(); ERR_FAIL_COND_V(fae.is_null(), ERR_SKIP); - Error err = fae->open_and_parse(ftmp, p_key, FileAccessEncrypted::MODE_WRITE_AES256, false); + Error err = fae->open_and_parse(ftmp, p_key, FileAccessEncrypted::MODE_WRITE_AES256, false, iv); ERR_FAIL_COND_V(err != OK, ERR_SKIP); ftmp = fae; } @@ -288,15 +305,15 @@ Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_pa return OK; } -Error EditorExportPlatform::_save_pack_patch_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { +Error EditorExportPlatform::_save_pack_patch_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed) { if (_check_hash(PackedData::get_singleton()->get_file_hash(p_path), p_data)) { return OK; } - return _save_pack_file(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key); + return _save_pack_file(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed); } -Error EditorExportPlatform::_save_zip_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { +Error EditorExportPlatform::_save_zip_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed) { ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export."); String path = p_path.replace_first("res://", ""); @@ -328,12 +345,12 @@ Error EditorExportPlatform::_save_zip_file(void *p_userdata, const String &p_pat return OK; } -Error EditorExportPlatform::_save_zip_patch_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { +Error EditorExportPlatform::_save_zip_patch_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed) { if (_check_hash(PackedData::get_singleton()->get_file_hash(p_path), p_data)) { return OK; } - return _save_zip_file(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key); + return _save_zip_file(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed); } Ref<ImageTexture> EditorExportPlatform::get_option_icon(int p_index) const { @@ -932,7 +949,7 @@ Vector<String> EditorExportPlatform::get_forced_export_files() { return files; } -Error EditorExportPlatform::_script_save_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { +Error EditorExportPlatform::_script_save_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed) { Callable cb = ((ScriptCallbackData *)p_userdata)->file_cb; ERR_FAIL_COND_V(!cb.is_valid(), FAILED); @@ -1043,8 +1060,10 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & Vector<String> enc_in_filters; Vector<String> enc_ex_filters; Vector<uint8_t> key; + uint64_t seed = 0; if (enc_pck) { + seed = p_preset->get_seed(); Vector<String> enc_in_split = p_preset->get_enc_in_filter().split(","); for (int i = 0; i < enc_in_split.size(); i++) { String f = enc_in_split[i].strip_edges(); @@ -1116,7 +1135,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & } } for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) { - err = p_save_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, 0, paths.size(), enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, 0, paths.size(), enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } @@ -1234,7 +1253,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & } for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) { - err = p_save_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } @@ -1266,7 +1285,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & if (importer_type == "keep") { // Just keep file as-is. Vector<uint8_t> array = FileAccess::get_file_as_bytes(path); - err = p_save_func(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; @@ -1309,13 +1328,13 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & sarr.resize(cs.size()); memcpy(sarr.ptrw(), cs.ptr(), sarr.size()); - err = p_save_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } // Now actual remapped file: sarr = FileAccess::get_file_as_bytes(export_path); - err = p_save_func(p_udata, export_path, sarr, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, export_path, sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } @@ -1345,14 +1364,14 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & if (remap == "path") { String remapped_path = config->get_value("remap", remap); Vector<uint8_t> array = FileAccess::get_file_as_bytes(remapped_path); - err = p_save_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); } else if (remap.begins_with("path.")) { String feature = remap.get_slice(".", 1); if (remap_features.has(feature)) { String remapped_path = config->get_value("remap", remap); Vector<uint8_t> array = FileAccess::get_file_as_bytes(remapped_path); - err = p_save_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); } else { // Remove paths if feature not enabled. config->erase_section_key("remap", remap); @@ -1378,7 +1397,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & sarr.resize(cs.size()); memcpy(sarr.ptrw(), cs.ptr(), sarr.size()); - err = p_save_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; @@ -1399,7 +1418,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & } Vector<uint8_t> array = FileAccess::get_file_as_bytes(export_path); - err = p_save_func(p_udata, export_path, array, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, export_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } @@ -1463,7 +1482,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & new_file.write[j] = utf8[j]; } - err = p_save_func(p_udata, from + ".remap", new_file, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, from + ".remap", new_file, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } @@ -1477,7 +1496,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & Vector<String> forced_export = get_forced_export_files(); for (int i = 0; i < forced_export.size(); i++) { Vector<uint8_t> array = FileAccess::get_file_as_bytes(forced_export[i]); - err = p_save_func(p_udata, forced_export[i], array, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, forced_export[i], array, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } @@ -1489,7 +1508,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & Vector<uint8_t> data = FileAccess::get_file_as_bytes(engine_cfb); DirAccess::remove_file_or_error(engine_cfb); - err = p_save_func(p_udata, "res://" + config_file, data, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, "res://" + config_file, data, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } @@ -1872,6 +1891,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b Ref<FileAccess> fhead = f; if (enc_pck && enc_directory) { + uint64_t seed = p_preset->get_seed(); String script_key = _get_script_encryption_key(p_preset); Vector<uint8_t> key; key.resize(32); @@ -1906,7 +1926,27 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b return ERR_CANT_CREATE; } - err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_WRITE_AES256, false); + Vector<uint8_t> iv; + if (seed != 0) { + for (int i = 0; i < pd.file_ofs.size(); i++) { + for (int64_t j = 0; j < pd.file_ofs[i].path_utf8.length(); j++) { + seed = ((seed << 5) + seed) ^ pd.file_ofs[i].path_utf8.get_data()[j]; + } + for (int64_t j = 0; j < pd.file_ofs[i].md5.size(); j++) { + seed = ((seed << 5) + seed) ^ pd.file_ofs[i].md5[j]; + } + seed = ((seed << 5) + seed) ^ pd.file_ofs[i].ofs; + seed = ((seed << 5) + seed) ^ pd.file_ofs[i].size; + } + + RandomPCG rng = RandomPCG(seed, RandomPCG::DEFAULT_INC); + iv.resize(16); + for (int i = 0; i < 16; i++) { + iv.write[i] = rng.rand() % 256; + } + } + + err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_WRITE_AES256, false, iv); if (err != OK) { add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), TTR("Can't open encrypted file to write.")); return ERR_CANT_CREATE; diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h index 919fb2915a..c7378ffec7 100644 --- a/editor/export/editor_export_platform.h +++ b/editor/export/editor_export_platform.h @@ -53,7 +53,7 @@ protected: static void _bind_methods(); public: - typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); + typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed); typedef Error (*EditorExportRemoveFunction)(void *p_userdata, const String &p_path); typedef Error (*EditorExportSaveSharedObject)(void *p_userdata, const SharedObject &p_so); @@ -114,14 +114,14 @@ private: static bool _check_hash(const uint8_t *p_hash, const Vector<uint8_t> &p_data); - static Error _save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); - static Error _save_pack_patch_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); + static Error _save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed); + static Error _save_pack_patch_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed); static Error _pack_add_shared_object(void *p_userdata, const SharedObject &p_so); static Error _remove_pack_file(void *p_userdata, const String &p_path); - static Error _save_zip_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); - static Error _save_zip_patch_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); + static Error _save_zip_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed); + static Error _save_zip_patch_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed); static Error _zip_add_shared_object(void *p_userdata, const SharedObject &p_so); struct ScriptCallbackData { @@ -129,7 +129,7 @@ private: Callable so_cb; }; - static Error _script_save_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); + static Error _script_save_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed); static Error _script_add_shared_object(void *p_userdata, const SharedObject &p_so); void _edit_files_with_filter(Ref<DirAccess> &da, const Vector<String> &p_filters, HashSet<String> &r_list, bool exclude); diff --git a/editor/export/editor_export_platform_pc.cpp b/editor/export/editor_export_platform_pc.cpp index 4eff096840..15d684cac5 100644 --- a/editor/export/editor_export_platform_pc.cpp +++ b/editor/export/editor_export_platform_pc.cpp @@ -223,13 +223,13 @@ Error EditorExportPlatformPC::export_project_data(const Ref<EditorExportPreset> if (err == OK) { err = da->copy_dir(src_path, target_path, -1, true); if (err != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("GDExtension"), TTR(vformat("Failed to copy shared object \"%s\".", src_path))); + add_message(EXPORT_MESSAGE_ERROR, TTR("GDExtension"), vformat(TTR("Failed to copy shared object \"%s\"."), src_path)); } } } else { err = da->copy(src_path, target_path); if (err != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("GDExtension"), TTR(vformat("Failed to copy shared object \"%s\".", src_path))); + add_message(EXPORT_MESSAGE_ERROR, TTR("GDExtension"), vformat(TTR("Failed to copy shared object \"%s\"."), src_path)); } if (err == OK) { err = sign_shared_object(p_preset, p_debug, target_path); diff --git a/editor/export/editor_export_preset.cpp b/editor/export/editor_export_preset.cpp index da7059b777..8ff5dd7551 100644 --- a/editor/export/editor_export_preset.cpp +++ b/editor/export/editor_export_preset.cpp @@ -451,6 +451,15 @@ String EditorExportPreset::get_enc_ex_filter() const { return enc_ex_filters; } +void EditorExportPreset::set_seed(uint64_t p_seed) { + seed = p_seed; + EditorExport::singleton->save_presets(); +} + +uint64_t EditorExportPreset::get_seed() const { + return seed; +} + void EditorExportPreset::set_enc_pck(bool p_enabled) { enc_pck = p_enabled; EditorExport::singleton->save_presets(); diff --git a/editor/export/editor_export_preset.h b/editor/export/editor_export_preset.h index af3a23fc50..4834a483eb 100644 --- a/editor/export/editor_export_preset.h +++ b/editor/export/editor_export_preset.h @@ -92,6 +92,7 @@ private: String enc_ex_filters; bool enc_pck = false; bool enc_directory = false; + uint64_t seed = 0; String script_key; int script_mode = MODE_SCRIPT_BINARY_TOKENS_COMPRESSED; @@ -165,6 +166,9 @@ public: void set_enc_ex_filter(const String &p_filter); String get_enc_ex_filter() const; + void set_seed(uint64_t p_seed); + uint64_t get_seed() const; + void set_enc_pck(bool p_enabled); bool get_enc_pck() const; diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index a3cd6523e9..8ae4b856a0 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -382,10 +382,16 @@ void ProjectExportDialog::_edit_preset(int p_index) { bool enc_pck_mode = current->get_enc_pck(); enc_pck->set_pressed(enc_pck_mode); + uint64_t seed = current->get_seed(); + if (!updating_seed) { + seed_input->set_text(itos(seed)); + } + enc_directory->set_disabled(!enc_pck_mode); enc_in_filters->set_editable(enc_pck_mode); enc_ex_filters->set_editable(enc_pck_mode); script_key->set_editable(enc_pck_mode); + seed_input->set_editable(enc_pck_mode); bool enc_directory_mode = current->get_enc_directory(); enc_directory->set_pressed(enc_directory_mode); @@ -591,6 +597,21 @@ void ProjectExportDialog::_enc_pck_changed(bool p_pressed) { _update_current_preset(); } +void ProjectExportDialog::_seed_input_changed(const String &p_text) { + if (updating) { + return; + } + + Ref<EditorExportPreset> current = get_current_preset(); + ERR_FAIL_COND(current.is_null()); + + current->set_seed(seed_input->get_text().to_int()); + + updating_seed = true; + _update_current_preset(); + updating_seed = false; +} + void ProjectExportDialog::_enc_directory_changed(bool p_pressed) { if (updating) { return; @@ -1623,6 +1644,10 @@ ProjectExportDialog::ProjectExportDialog() { sec_vb->add_child(script_key_error); sections->add_child(sec_scroll_container); + seed_input = memnew(LineEdit); + seed_input->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_seed_input_changed)); + sec_vb->add_margin_child(TTR("Initialization vector seed"), seed_input); + Label *sec_info = memnew(Label); sec_info->set_text(TTR("Note: Encryption key needs to be stored in the binary,\nyou need to build the export templates from source.")); sec_vb->add_child(sec_info); diff --git a/editor/export/project_export.h b/editor/export/project_export.h index bbf0d81228..68676bfc84 100644 --- a/editor/export/project_export.h +++ b/editor/export/project_export.h @@ -172,6 +172,7 @@ class ProjectExportDialog : public ConfirmationDialog { CheckButton *enc_directory = nullptr; LineEdit *enc_in_filters = nullptr; LineEdit *enc_ex_filters = nullptr; + LineEdit *seed_input = nullptr; OptionButton *script_mode = nullptr; @@ -192,9 +193,11 @@ class ProjectExportDialog : public ConfirmationDialog { bool updating_script_key = false; bool updating_enc_filters = false; + bool updating_seed = false; void _enc_pck_changed(bool p_pressed); void _enc_directory_changed(bool p_pressed); void _enc_filters_changed(const String &p_text); + void _seed_input_changed(const String &p_text); void _script_encryption_key_changed(const String &p_key); bool _validate_script_encryption_key(const String &p_key); diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp index ceff62723f..b2eeeebd7a 100644 --- a/editor/gui/editor_file_dialog.cpp +++ b/editor/gui/editor_file_dialog.cpp @@ -65,7 +65,7 @@ void EditorFileDialog::_native_popup() { } else if (access == ACCESS_USERDATA) { root = OS::get_singleton()->get_user_data_dir(); } - DisplayServer::get_singleton()->file_dialog_with_options_show(get_title(), ProjectSettings::get_singleton()->globalize_path(dir->get_text()), root, file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), filters, _get_options(), callable_mp(this, &EditorFileDialog::_native_dialog_cb)); + DisplayServer::get_singleton()->file_dialog_with_options_show(get_translated_title(), ProjectSettings::get_singleton()->globalize_path(dir->get_text()), root, file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), filters, _get_options(), callable_mp(this, &EditorFileDialog::_native_dialog_cb)); } void EditorFileDialog::popup(const Rect2i &p_rect) { diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp index c11da5dfdb..e89912d5bc 100644 --- a/editor/gui/scene_tree_editor.cpp +++ b/editor/gui/scene_tree_editor.cpp @@ -1098,8 +1098,19 @@ void SceneTreeEditor::rename_node(Node *p_node, const String &p_name, TreeItem * // Trim leading/trailing whitespace to prevent node names from containing accidental whitespace, which would make it more difficult to get the node via `get_node()`. new_name = new_name.strip_edges(); + if (new_name.is_empty() && p_node->get_owner() != nullptr && !p_node->get_scene_file_path().is_empty()) { + // If name is empty and node is root of an instance, revert to the original name. + const Ref<PackedScene> node_scene = ResourceLoader::load(p_node->get_scene_file_path()); + if (node_scene.is_valid()) { + const Ref<SceneState> &state = node_scene->get_state(); + if (state->get_node_count() > 0) { + new_name = state->get_node_name(0); // Root's name. + } + } + } + if (new_name.is_empty()) { - // If name is empty, fallback to class name. + // If name is still empty, fallback to class name. if (GLOBAL_GET("editor/naming/node_name_casing").operator int() != NAME_CASING_PASCAL_CASE) { new_name = Node::adjust_name_casing(p_node->get_class()); } else { diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 71ccef4752..8d6f4e0a70 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -241,7 +241,10 @@ void ResourceImporterTexture::get_import_options(const String &p_path, List<Impo r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/normal_map_invert_y"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/hdr_as_srgb"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/hdr_clamp_exposure"), false)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "process/size_limit", PROPERTY_HINT_RANGE, "0,4096,1"), 0)); + + // Maximum bound is the highest allowed value for lossy compression (the lowest common denominator). + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "process/size_limit", PROPERTY_HINT_RANGE, "0,16383,1"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "detect_3d/compress_to", PROPERTY_HINT_ENUM, "Disabled,VRAM Compressed,Basis Universal"), (p_preset == PRESET_DETECT) ? 1 : 0)); // Do path based customization only if a path was passed. @@ -454,7 +457,28 @@ Error ResourceImporterTexture::import(ResourceUID::ID p_source_id, const String const bool normal_map_invert_y = p_options["process/normal_map_invert_y"]; // Support for texture streaming is not implemented yet. const bool stream = false; - const int size_limit = p_options["process/size_limit"]; + + int size_limit = p_options["process/size_limit"]; + bool using_fallback_size_limit = false; + if (size_limit == 0) { + using_fallback_size_limit = true; + // If no size limit is defined, use a fallback size limit to prevent textures from looking incorrect or failing to import. + switch (compress_mode) { + case COMPRESS_LOSSY: + // Maximum WebP size on either axis. + size_limit = 16383; + break; + case COMPRESS_BASIS_UNIVERSAL: + // Maximum Basis Universal size on either axis. + size_limit = 16384; + break; + default: + // As of June 2024, no GPU can correctly display a texture larger than 32768 pixels on either axis. + size_limit = 32768; + break; + } + } + const bool hdr_as_srgb = p_options["process/hdr_as_srgb"]; if (hdr_as_srgb) { loader_flags |= ImageFormatLoader::FLAG_FORCE_LINEAR; @@ -523,11 +547,19 @@ Error ResourceImporterTexture::import(ResourceUID::ID p_source_id, const String int new_width = size_limit; int new_height = target_image->get_height() * new_width / target_image->get_width(); + if (using_fallback_size_limit) { + // Only warn if downsizing occurred when the user did not explicitly request it. + WARN_PRINT(vformat("%s: Texture was downsized on import as its width (%d pixels) exceeded the importable size limit (%d pixels).", p_source_file, target_image->get_width(), size_limit)); + } target_image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC); } else { int new_height = size_limit; int new_width = target_image->get_width() * new_height / target_image->get_height(); + if (using_fallback_size_limit) { + // Only warn if downsizing occurred when the user did not explicitly request it. + WARN_PRINT(vformat("%s: Texture was downsized on import as its height (%d pixels) exceeded the importable size limit (%d pixels).", p_source_file, target_image->get_height(), size_limit)); + } target_image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC); } diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 62793fbcb5..f91a052a24 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -1930,37 +1930,50 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) { // Drag resize handles if (drag_type == DRAG_NONE) { - if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() && ((b->is_alt_pressed() && b->is_command_or_control_pressed()) || tool == TOOL_SCALE)) { + if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() && + ((tool == TOOL_SELECT && b->is_alt_pressed() && b->is_command_or_control_pressed()) || tool == TOOL_SCALE)) { bool has_locked_items = false; List<CanvasItem *> selection = _get_edited_canvas_items(false, true, &has_locked_items); - if (selection.size() == 1) { + + // Remove non-movable nodes. + for (CanvasItem *ci : selection) { + if (!_is_node_movable(ci, true)) { + selection.erase(ci); + } + } + + if (!selection.is_empty()) { CanvasItem *ci = selection.front()->get(); - if (_is_node_movable(ci)) { - Transform2D xform = transform * ci->get_global_transform_with_canvas(); - Transform2D unscaled_transform = (xform * ci->get_transform().affine_inverse() * ci->_edit_get_transform()).orthonormalized(); - Transform2D simple_xform = viewport->get_transform() * unscaled_transform; + Transform2D edit_transform; + if (!Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y)) { + edit_transform = Transform2D(ci->_edit_get_rotation(), temp_pivot); + } else { + edit_transform = ci->_edit_get_transform(); + } - drag_type = DRAG_SCALE_BOTH; + Transform2D xform = transform * ci->get_global_transform_with_canvas(); + Transform2D unscaled_transform = (xform * ci->get_transform().affine_inverse() * edit_transform).orthonormalized(); + Transform2D simple_xform = viewport->get_transform() * unscaled_transform; - if (show_transformation_gizmos) { - Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE); - Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); - if (x_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) { - drag_type = DRAG_SCALE_X; - } - Rect2 y_handle_rect = Rect2(-5 * EDSCALE, scale_factor.y * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); - if (y_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) { - drag_type = DRAG_SCALE_Y; - } - } + drag_type = DRAG_SCALE_BOTH; - drag_from = transform.affine_inverse().xform(b->get_position()); - drag_selection = List<CanvasItem *>(); - drag_selection.push_back(ci); - _save_canvas_item_state(drag_selection); - return true; + if (show_transformation_gizmos) { + Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE); + Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); + if (x_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) { + drag_type = DRAG_SCALE_X; + } + Rect2 y_handle_rect = Rect2(-5 * EDSCALE, scale_factor.y * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); + if (y_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) { + drag_type = DRAG_SCALE_Y; + } } + + drag_from = transform.affine_inverse().xform(b->get_position()); + drag_selection = selection; + _save_canvas_item_state(drag_selection); + return true; } else { if (has_locked_items) { EditorToaster::get_singleton()->popup_str(TTR(locked_transform_warning), EditorToaster::SEVERITY_WARNING); @@ -1968,66 +1981,87 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) { return has_locked_items; } } - } - - if (drag_type == DRAG_SCALE_BOTH || drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) { + } else if (drag_type == DRAG_SCALE_BOTH || drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) { // Resize the node if (m.is_valid()) { _restore_canvas_item_state(drag_selection); - CanvasItem *ci = drag_selection.front()->get(); drag_to = transform.affine_inverse().xform(m->get_position()); - Transform2D parent_xform = ci->get_global_transform_with_canvas() * ci->get_transform().affine_inverse(); - Transform2D unscaled_transform = (transform * parent_xform * ci->_edit_get_transform()).orthonormalized(); - Transform2D simple_xform = (viewport->get_transform() * unscaled_transform).affine_inverse() * transform; - - bool uniform = m->is_shift_pressed(); - bool is_ctrl = m->is_command_or_control_pressed(); - - Point2 drag_from_local = simple_xform.xform(drag_from); - Point2 drag_to_local = simple_xform.xform(drag_to); - Point2 offset = drag_to_local - drag_from_local; + Size2 scale_max; + if (drag_type != DRAG_SCALE_BOTH) { + for (CanvasItem *ci : drag_selection) { + scale_max = scale_max.max(ci->_edit_get_scale()); + } + } - Size2 scale = ci->_edit_get_scale(); - Size2 original_scale = scale; - real_t ratio = scale.y / scale.x; - if (drag_type == DRAG_SCALE_BOTH) { - Size2 scale_factor = drag_to_local / drag_from_local; - if (uniform) { - scale *= (scale_factor.x + scale_factor.y) / 2.0; + for (CanvasItem *ci : drag_selection) { + Transform2D edit_transform; + bool using_temp_pivot = !Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y); + if (using_temp_pivot) { + edit_transform = Transform2D(ci->_edit_get_rotation(), temp_pivot); } else { - scale *= scale_factor; + edit_transform = ci->_edit_get_transform(); } - } else { - Size2 scale_factor = Vector2(offset.x, -offset.y) / SCALE_HANDLE_DISTANCE; - Size2 parent_scale = parent_xform.get_scale(); - scale_factor *= Vector2(1.0 / parent_scale.x, 1.0 / parent_scale.y); - if (drag_type == DRAG_SCALE_X) { - scale.x += scale_factor.x; + Transform2D parent_xform = ci->get_global_transform_with_canvas() * ci->get_transform().affine_inverse(); + Transform2D unscaled_transform = (transform * parent_xform * edit_transform).orthonormalized(); + Transform2D simple_xform = (viewport->get_transform() * unscaled_transform).affine_inverse() * transform; + + bool uniform = m->is_shift_pressed(); + bool is_ctrl = m->is_command_or_control_pressed(); + + Point2 drag_from_local = simple_xform.xform(drag_from); + Point2 drag_to_local = simple_xform.xform(drag_to); + Point2 offset = drag_to_local - drag_from_local; + + Size2 scale = ci->_edit_get_scale(); + Size2 original_scale = scale; + real_t ratio = scale.y / scale.x; + if (drag_type == DRAG_SCALE_BOTH) { + Size2 scale_factor = drag_to_local / drag_from_local; if (uniform) { - scale.y = scale.x * ratio; + scale *= (scale_factor.x + scale_factor.y) / 2.0; + } else { + scale *= scale_factor; } - } else if (drag_type == DRAG_SCALE_Y) { - scale.y -= scale_factor.y; - if (uniform) { - scale.x = scale.y / ratio; + } else { + Size2 scale_factor = Vector2(offset.x, -offset.y) / SCALE_HANDLE_DISTANCE; + Size2 parent_scale = parent_xform.get_scale(); + // Take into account the biggest scale, so all nodes are scaled uniformly. + scale_factor *= Vector2(1.0 / parent_scale.x, 1.0 / parent_scale.y) / (scale_max / original_scale); + + if (drag_type == DRAG_SCALE_X) { + scale.x += scale_factor.x; + if (uniform) { + scale.y = scale.x * ratio; + } + } else if (drag_type == DRAG_SCALE_Y) { + scale.y -= scale_factor.y; + if (uniform) { + scale.x = scale.y / ratio; + } } } - } - if (snap_scale && !is_ctrl) { - if (snap_relative) { - scale.x = original_scale.x * (roundf((scale.x / original_scale.x) / snap_scale_step) * snap_scale_step); - scale.y = original_scale.y * (roundf((scale.y / original_scale.y) / snap_scale_step) * snap_scale_step); - } else { - scale.x = roundf(scale.x / snap_scale_step) * snap_scale_step; - scale.y = roundf(scale.y / snap_scale_step) * snap_scale_step; + if (snap_scale && !is_ctrl) { + if (snap_relative) { + scale.x = original_scale.x * (Math::round((scale.x / original_scale.x) / snap_scale_step) * snap_scale_step); + scale.y = original_scale.y * (Math::round((scale.y / original_scale.y) / snap_scale_step) * snap_scale_step); + } else { + scale.x = Math::round(scale.x / snap_scale_step) * snap_scale_step; + scale.y = Math::round(scale.y / snap_scale_step) * snap_scale_step; + } + } + + ci->_edit_set_scale(scale); + + if (using_temp_pivot) { + Point2 ci_origin = ci->_edit_get_transform().get_origin(); + ci->_edit_set_position(ci_origin + (ci_origin - temp_pivot) * ((scale - original_scale) / original_scale)); } } - ci->_edit_set_scale(scale); return true; } @@ -2075,7 +2109,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { if (drag_type == DRAG_NONE) { //Start moving the nodes if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed()) { - if ((b->is_alt_pressed() && !b->is_command_or_control_pressed()) || tool == TOOL_MOVE) { + if ((tool == TOOL_SELECT && b->is_alt_pressed() && !b->is_command_or_control_pressed()) || tool == TOOL_MOVE) { bool has_locked_items = false; List<CanvasItem *> selection = _get_edited_canvas_items(false, true, &has_locked_items); @@ -2135,7 +2169,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { } Point2 drag_delta = drag_to - drag_from; - if (drag_selection.size() == 1 && (drag_type == DRAG_MOVE_X || drag_type == DRAG_MOVE_Y)) { + if (drag_type == DRAG_MOVE_X || drag_type == DRAG_MOVE_Y) { const CanvasItem *selected = drag_selection.front()->get(); Transform2D parent_xform = selected->get_global_transform_with_canvas() * selected->get_transform().affine_inverse(); Transform2D unscaled_transform = (transform * parent_xform * selected->_edit_get_transform()).orthonormalized(); @@ -3468,16 +3502,14 @@ void CanvasItemEditor::_draw_selection() { Ref<Texture2D> previous_position_icon = get_editor_theme_icon(SNAME("EditorPositionPrevious")); RID vp_ci = viewport->get_canvas_item(); - List<CanvasItem *> selection = _get_edited_canvas_items(true, false); - bool single = selection.size() == 1; + bool transform_tool = tool == TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_SCALE || tool == TOOL_ROTATE || tool == TOOL_EDIT_PIVOT; + for (CanvasItem *E : selection) { CanvasItem *ci = Object::cast_to<CanvasItem>(E); CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(ci); - bool item_locked = ci->has_meta("_edit_lock_"); - // Draw the previous position if we are dragging the node if (show_helpers && (drag_type == DRAG_MOVE || drag_type == DRAG_ROTATE || @@ -3502,6 +3534,7 @@ void CanvasItemEditor::_draw_selection() { } } + bool item_locked = ci->has_meta("_edit_lock_"); Transform2D xform = transform * ci->get_global_transform_with_canvas(); // Draw the selected items position / surrounding boxes @@ -3531,7 +3564,7 @@ void CanvasItemEditor::_draw_selection() { viewport->draw_set_transform_matrix(viewport->get_transform()); } - if (single && !item_locked && (tool == TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_SCALE || tool == TOOL_ROTATE || tool == TOOL_EDIT_PIVOT)) { //kind of sucks + if (single && !item_locked && transform_tool) { // Draw the pivot if (ci->_edit_use_pivot()) { // Draw the node's pivot @@ -3574,73 +3607,88 @@ void CanvasItemEditor::_draw_selection() { select_handle->draw(vp_ci, (ofs - (select_handle->get_size() / 2)).floor()); } } + } + } - // Draw the move handles - bool is_ctrl = Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL); - bool is_alt = Input::get_singleton()->is_key_pressed(Key::ALT); - if (tool == TOOL_MOVE && show_transformation_gizmos) { - if (_is_node_movable(ci)) { - Transform2D unscaled_transform = (xform * ci->get_transform().affine_inverse() * ci->_edit_get_transform()).orthonormalized(); - Transform2D simple_xform = viewport->get_transform() * unscaled_transform; + // Remove non-movable nodes. + for (CanvasItem *ci : selection) { + if (!_is_node_movable(ci, true)) { + selection.erase(ci); + } + } - Size2 move_factor = Size2(MOVE_HANDLE_DISTANCE, MOVE_HANDLE_DISTANCE); - viewport->draw_set_transform_matrix(simple_xform); + if (!selection.is_empty() && transform_tool && show_transformation_gizmos) { + CanvasItem *ci = selection.front()->get(); - Vector<Point2> points = { - Vector2(move_factor.x * EDSCALE, 5 * EDSCALE), - Vector2(move_factor.x * EDSCALE, -5 * EDSCALE), - Vector2((move_factor.x + 10) * EDSCALE, 0) - }; + Transform2D xform = transform * ci->get_global_transform_with_canvas(); + bool is_ctrl = Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL); + bool is_alt = Input::get_singleton()->is_key_pressed(Key::ALT); - viewport->draw_colored_polygon(points, get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor))); - viewport->draw_line(Point2(), Point2(move_factor.x * EDSCALE, 0), get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor)), Math::round(EDSCALE)); + // Draw the move handles. + if ((tool == TOOL_SELECT && is_alt && !is_ctrl) || tool == TOOL_MOVE) { + Transform2D unscaled_transform = (xform * ci->get_transform().affine_inverse() * ci->_edit_get_transform()).orthonormalized(); + Transform2D simple_xform = viewport->get_transform() * unscaled_transform; - points.clear(); - points.push_back(Vector2(5 * EDSCALE, move_factor.y * EDSCALE)); - points.push_back(Vector2(-5 * EDSCALE, move_factor.y * EDSCALE)); - points.push_back(Vector2(0, (move_factor.y + 10) * EDSCALE)); + Size2 move_factor = Size2(MOVE_HANDLE_DISTANCE, MOVE_HANDLE_DISTANCE); + viewport->draw_set_transform_matrix(simple_xform); - viewport->draw_colored_polygon(points, get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor))); - viewport->draw_line(Point2(), Point2(0, move_factor.y * EDSCALE), get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor)), Math::round(EDSCALE)); + Vector<Point2> points = { + Vector2(move_factor.x * EDSCALE, 5 * EDSCALE), + Vector2(move_factor.x * EDSCALE, -5 * EDSCALE), + Vector2((move_factor.x + 10) * EDSCALE, 0) + }; - viewport->draw_set_transform_matrix(viewport->get_transform()); - } - } + viewport->draw_colored_polygon(points, get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor))); + viewport->draw_line(Point2(), Point2(move_factor.x * EDSCALE, 0), get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor)), Math::round(EDSCALE)); - // Draw the rescale handles - if (show_transformation_gizmos && ((is_alt && is_ctrl) || tool == TOOL_SCALE || drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y)) { - if (_is_node_movable(ci)) { - Transform2D unscaled_transform = (xform * ci->get_transform().affine_inverse() * ci->_edit_get_transform()).orthonormalized(); - Transform2D simple_xform = viewport->get_transform() * unscaled_transform; + points.clear(); + points.push_back(Vector2(5 * EDSCALE, move_factor.y * EDSCALE)); + points.push_back(Vector2(-5 * EDSCALE, move_factor.y * EDSCALE)); + points.push_back(Vector2(0, (move_factor.y + 10) * EDSCALE)); - Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE); - bool uniform = Input::get_singleton()->is_key_pressed(Key::SHIFT); - Point2 offset = (simple_xform.affine_inverse().xform(drag_to) - simple_xform.affine_inverse().xform(drag_from)) * zoom; + viewport->draw_colored_polygon(points, get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor))); + viewport->draw_line(Point2(), Point2(0, move_factor.y * EDSCALE), get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor)), Math::round(EDSCALE)); - if (drag_type == DRAG_SCALE_X) { - scale_factor.x += offset.x; - if (uniform) { - scale_factor.y += offset.x; - } - } else if (drag_type == DRAG_SCALE_Y) { - scale_factor.y += offset.y; - if (uniform) { - scale_factor.x += offset.y; - } - } + viewport->draw_set_transform_matrix(viewport->get_transform()); + } - viewport->draw_set_transform_matrix(simple_xform); - Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); - viewport->draw_rect(x_handle_rect, get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor))); - viewport->draw_line(Point2(), Point2(scale_factor.x * EDSCALE, 0), get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor)), Math::round(EDSCALE)); + // Draw the rescale handles. + if ((tool == TOOL_SELECT && is_alt && is_ctrl) || tool == TOOL_SCALE || drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) { + Transform2D edit_transform; + if (!Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y)) { + edit_transform = Transform2D(ci->_edit_get_rotation(), temp_pivot); + } else { + edit_transform = ci->_edit_get_transform(); + } + Transform2D unscaled_transform = (xform * ci->get_transform().affine_inverse() * edit_transform).orthonormalized(); + Transform2D simple_xform = viewport->get_transform() * unscaled_transform; - Rect2 y_handle_rect = Rect2(-5 * EDSCALE, scale_factor.y * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); - viewport->draw_rect(y_handle_rect, get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor))); - viewport->draw_line(Point2(), Point2(0, scale_factor.y * EDSCALE), get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor)), Math::round(EDSCALE)); + Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE); + bool uniform = Input::get_singleton()->is_key_pressed(Key::SHIFT); + Point2 offset = (simple_xform.affine_inverse().xform(drag_to) - simple_xform.affine_inverse().xform(drag_from)) * zoom; - viewport->draw_set_transform_matrix(viewport->get_transform()); + if (drag_type == DRAG_SCALE_X) { + scale_factor.x += offset.x; + if (uniform) { + scale_factor.y += offset.x; + } + } else if (drag_type == DRAG_SCALE_Y) { + scale_factor.y += offset.y; + if (uniform) { + scale_factor.x += offset.y; } } + + viewport->draw_set_transform_matrix(simple_xform); + Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); + viewport->draw_rect(x_handle_rect, get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor))); + viewport->draw_line(Point2(), Point2(scale_factor.x * EDSCALE, 0), get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor)), Math::round(EDSCALE)); + + Rect2 y_handle_rect = Rect2(-5 * EDSCALE, scale_factor.y * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); + viewport->draw_rect(y_handle_rect, get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor))); + viewport->draw_line(Point2(), Point2(0, scale_factor.y * EDSCALE), get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor)), Math::round(EDSCALE)); + + viewport->draw_set_transform_matrix(viewport->get_transform()); } } @@ -5356,7 +5404,7 @@ CanvasItemEditor::CanvasItemEditor() { main_menu_hbox->add_child(pivot_button); pivot_button->set_toggle_mode(true); pivot_button->connect(SceneStringName(pressed), callable_mp(this, &CanvasItemEditor::_button_tool_select).bind(TOOL_EDIT_PIVOT)); - pivot_button->set_tooltip_text(TTR("Click to change object's rotation pivot.") + "\n" + TTR("Shift: Set temporary rotation pivot.") + "\n" + TTR("Click this button while holding Shift to put the temporary rotation pivot in the center of the selected nodes.")); + pivot_button->set_tooltip_text(TTR("Click to change object's pivot.") + "\n" + TTR("Shift: Set temporary pivot.") + "\n" + TTR("Click this button while holding Shift to put the temporary pivot in the center of the selected nodes.")); pan_button = memnew(Button); pan_button->set_theme_type_variation("FlatButton"); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 8c3979918d..49ecbac751 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -4391,28 +4391,28 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) { disk_changed = memnew(ConfirmationDialog); { - disk_changed->set_title(TTR("Files have been modified on disk")); + disk_changed->set_title(TTR("Files have been modified outside Godot")); VBoxContainer *vbc = memnew(VBoxContainer); disk_changed->add_child(vbc); Label *files_are_newer_label = memnew(Label); - files_are_newer_label->set_text(TTR("The following files are newer on disk.")); + files_are_newer_label->set_text(TTR("The following files are newer on disk:")); vbc->add_child(files_are_newer_label); - Label *what_action_label = memnew(Label); - what_action_label->set_text(TTR("What action should be taken?:")); - vbc->add_child(what_action_label); - disk_changed_list = memnew(Tree); vbc->add_child(disk_changed_list); disk_changed_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); disk_changed_list->set_v_size_flags(SIZE_EXPAND_FILL); + Label *what_action_label = memnew(Label); + what_action_label->set_text(TTR("What action should be taken?")); + vbc->add_child(what_action_label); + disk_changed->connect(SceneStringName(confirmed), callable_mp(this, &ScriptEditor::reload_scripts).bind(false)); - disk_changed->set_ok_button_text(TTR("Discard local changes and reload")); + disk_changed->set_ok_button_text(TTR("Reload from disk")); - disk_changed->add_button(TTR("Keep local changes and overwrite"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); + disk_changed->add_button(TTR("Ignore external changes"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); disk_changed->connect("custom_action", callable_mp(this, &ScriptEditor::_resave_scripts)); } diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 9c1befa144..ec0edc0c96 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -1985,6 +1985,67 @@ bool VisualShaderEditor::_update_preview_parameter_tree() { return found; } +void VisualShaderEditor::_preview_tools_menu_option(int p_idx) { + ShaderMaterial *src_mat = nullptr; + + if (p_idx == COPY_PARAMS_FROM_MATERIAL || p_idx == PASTE_PARAMS_TO_MATERIAL) { + for (int i = EditorNode::get_singleton()->get_editor_selection_history()->get_path_size() - 1; i >= 0; i--) { + Object *object = ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_selection_history()->get_path_object(i)); + ShaderMaterial *src_mat2; + if (!object) { + continue; + } + if (object->has_method("get_material_override")) { // Trying to get material from MeshInstance. + src_mat2 = Object::cast_to<ShaderMaterial>(object->call("get_material_override")); + } else if (object->has_method("get_material")) { // From CanvasItem/Node2D. + src_mat2 = Object::cast_to<ShaderMaterial>(object->call("get_material")); + } else { + src_mat2 = Object::cast_to<ShaderMaterial>(object); + } + + if (src_mat2 && src_mat2->get_shader().is_valid() && src_mat2->get_shader() == visual_shader) { + src_mat = src_mat2; + break; + } + } + } + + switch (p_idx) { + case COPY_PARAMS_FROM_MATERIAL: + if (src_mat) { + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Copy Preview Shader Parameters From Material")); + + List<PropertyInfo> params; + preview_material->get_shader()->get_shader_uniform_list(¶ms); + for (const PropertyInfo &E : params) { + undo_redo->add_do_method(visual_shader.ptr(), "_set_preview_shader_parameter", E.name, src_mat->get_shader_parameter(E.name)); + undo_redo->add_undo_method(visual_shader.ptr(), "_set_preview_shader_parameter", E.name, preview_material->get_shader_parameter(E.name)); + } + + undo_redo->commit_action(); + } + break; + case PASTE_PARAMS_TO_MATERIAL: + if (src_mat) { + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Paste Preview Shader Parameters To Material")); + + List<PropertyInfo> params; + preview_material->get_shader()->get_shader_uniform_list(¶ms); + for (const PropertyInfo &E : params) { + undo_redo->add_do_method(src_mat, "set_shader_parameter", E.name, preview_material->get_shader_parameter(E.name)); + undo_redo->add_undo_method(src_mat, "set_shader_parameter", E.name, src_mat->get_shader_parameter(E.name)); + } + + undo_redo->commit_action(); + } + break; + default: + break; + } +} + void VisualShaderEditor::_clear_preview_param() { selected_param_id = ""; current_prop = nullptr; @@ -5159,6 +5220,7 @@ void VisualShaderEditor::_notification(int p_what) { } tools->set_button_icon(get_editor_theme_icon(SNAME("Tools"))); + preview_tools->set_button_icon(get_editor_theme_icon(SNAME("Tools"))); if (is_visible_in_tree()) { _update_graph(); @@ -6555,11 +6617,21 @@ VisualShaderEditor::VisualShaderEditor() { VBoxContainer *params_vbox = memnew(VBoxContainer); preview_split->add_child(params_vbox); + HBoxContainer *filter_hbox = memnew(HBoxContainer); + params_vbox->add_child(filter_hbox); + param_filter = memnew(LineEdit); + filter_hbox->add_child(param_filter); param_filter->connect(SceneStringName(text_changed), callable_mp(this, &VisualShaderEditor::_param_filter_changed)); param_filter->set_h_size_flags(SIZE_EXPAND_FILL); param_filter->set_placeholder(TTR("Filter Parameters")); - params_vbox->add_child(param_filter); + + preview_tools = memnew(MenuButton); + filter_hbox->add_child(preview_tools); + preview_tools->set_tooltip_text(TTR("Options")); + preview_tools->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &VisualShaderEditor::_preview_tools_menu_option)); + preview_tools->get_popup()->add_item(TTR("Copy Parameters From Material"), COPY_PARAMS_FROM_MATERIAL); + preview_tools->get_popup()->add_item(TTR("Paste Parameters To Material"), PASTE_PARAMS_TO_MATERIAL); ScrollContainer *sc = memnew(ScrollContainer); sc->set_v_size_flags(SIZE_EXPAND_FILL); diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index d3dc2e7564..a8655b8141 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -276,6 +276,7 @@ class VisualShaderEditor : public ShaderEditor { bool shader_preview_showed = true; LineEdit *param_filter = nullptr; + MenuButton *preview_tools = nullptr; String selected_param_id; Tree *parameters = nullptr; HashMap<String, PropertyInfo> parameter_props; @@ -318,6 +319,11 @@ class VisualShaderEditor : public ShaderEditor { COLLAPSE_ALL }; + enum PreviewToolsMenuOptions { + COPY_PARAMS_FROM_MATERIAL, + PASTE_PARAMS_TO_MATERIAL, + }; + #ifdef MINGW_ENABLED #undef DELETE #endif @@ -367,6 +373,7 @@ class VisualShaderEditor : public ShaderEditor { void _show_add_varying_dialog(); void _show_remove_varying_dialog(); + void _preview_tools_menu_option(int p_idx); void _clear_preview_param(); void _update_preview_parameter_list(); bool _update_preview_parameter_tree(); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 30cf2030bc..eb5e8d2a72 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -34,8 +34,6 @@ #include "core/io/config_file.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" -#include "core/io/resource_saver.h" -#include "core/io/stream_peer_tls.h" #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/version.h" @@ -51,12 +49,9 @@ #include "editor/project_manager/project_list.h" #include "editor/project_manager/project_tag.h" #include "editor/project_manager/quick_settings_dialog.h" -#include "editor/themes/editor_icons.h" #include "editor/themes/editor_scale.h" #include "editor/themes/editor_theme_manager.h" #include "main/main.h" -#include "scene/gui/check_box.h" -#include "scene/gui/color_rect.h" #include "scene/gui/flow_container.h" #include "scene/gui/line_edit.h" #include "scene/gui/margin_container.h" @@ -64,7 +59,6 @@ #include "scene/gui/panel_container.h" #include "scene/gui/rich_text_label.h" #include "scene/gui/separator.h" -#include "scene/gui/texture_rect.h" #include "scene/main/window.h" #include "scene/theme/theme_db.h" #include "servers/display_server.h" @@ -1300,15 +1294,10 @@ ProjectManager::ProjectManager() { filter_option->connect(SceneStringName(item_selected), callable_mp(this, &ProjectManager::_on_order_option_changed)); hb->add_child(filter_option); - Vector<String> sort_filter_titles; - sort_filter_titles.push_back(TTR("Last Edited")); - sort_filter_titles.push_back(TTR("Name")); - sort_filter_titles.push_back(TTR("Path")); - sort_filter_titles.push_back(TTR("Tags")); - - for (int i = 0; i < sort_filter_titles.size(); i++) { - filter_option->add_item(sort_filter_titles[i]); - } + filter_option->add_item(TTR("Last Edited")); + filter_option->add_item(TTR("Name")); + filter_option->add_item(TTR("Path")); + filter_option->add_item(TTR("Tags")); } // Project list and its sidebar. diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 89c18143dc..97f1d5d641 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -31,6 +31,7 @@ #include "project_settings_editor.h" #include "core/config/project_settings.h" +#include "core/input/input_map.h" #include "editor/editor_inspector.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" @@ -390,7 +391,7 @@ void ProjectSettingsEditor::_action_added(const String &p_name) { Dictionary action; action["events"] = Array(); - action["deadzone"] = 0.2f; + action["deadzone"] = InputMap::DEFAULT_DEADZONE; EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Add Input Action")); diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index 8dd2fe8e4e..d38ff7af76 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -359,6 +359,7 @@ void ScriptCreateDialog::_create_new() { alert->popup_centered(); return; } + EditorNode::get_singleton()->ensure_uid_file(lpath); } emit_signal(SNAME("script_created"), scr); diff --git a/editor/shader_create_dialog.cpp b/editor/shader_create_dialog.cpp index 2bfe088e7f..e1c797633a 100644 --- a/editor/shader_create_dialog.cpp +++ b/editor/shader_create_dialog.cpp @@ -31,6 +31,7 @@ #include "shader_create_dialog.h" #include "core/config/project_settings.h" +#include "editor/editor_node.h" #include "editor/gui/editor_file_dialog.h" #include "editor/gui/editor_validation_panel.h" #include "editor/themes/editor_scale.h" @@ -240,6 +241,7 @@ void fog() { alert->popup_centered(); return; } + EditorNode::get_singleton()->ensure_uid_file(lpath); emit_signal(SNAME("shader_include_created"), shader_inc); } else { @@ -258,6 +260,7 @@ void fog() { alert->popup_centered(); return; } + EditorNode::get_singleton()->ensure_uid_file(lpath); } emit_signal(SNAME("shader_created"), shader); diff --git a/main/main.cpp b/main/main.cpp index d3c768946c..0a905f16ca 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2554,7 +2554,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph Engine::get_singleton()->set_physics_ticks_per_second(GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "physics/common/physics_ticks_per_second", PROPERTY_HINT_RANGE, "1,1000,1"), 60)); Engine::get_singleton()->set_max_physics_steps_per_frame(GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "physics/common/max_physics_steps_per_frame", PROPERTY_HINT_RANGE, "1,100,1"), 8)); Engine::get_singleton()->set_physics_jitter_fix(GLOBAL_DEF("physics/common/physics_jitter_fix", 0.5)); - Engine::get_singleton()->set_max_fps(GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/max_fps", PROPERTY_HINT_RANGE, "0,1000,1"), 0)); GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "audio/driver/output_latency", PROPERTY_HINT_RANGE, "1,100,1"), 15); // Use a safer default output_latency for web to avoid audio cracking on low-end devices, especially mobile. @@ -2575,10 +2574,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph OS::get_singleton()->set_environment("MVK_CONFIG_LOG_LEVEL", OS::get_singleton()->_verbose_stdout ? "3" : "1"); // 1 = Errors only, 3 = Info #endif - if (max_fps >= 0) { - Engine::get_singleton()->set_max_fps(max_fps); - } - if (frame_delay == 0) { frame_delay = GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/frame_delay_msec", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), 0); if (Engine::get_singleton()->is_editor_hint()) { @@ -3024,6 +3019,13 @@ Error Main::setup2(bool p_show_boot_logo) { OS::get_singleton()->benchmark_end_measure("Servers", "Display"); } + // Max FPS needs to be set after the DisplayServer is created. + Engine::get_singleton()->set_max_fps(GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/max_fps", PROPERTY_HINT_RANGE, "0,1000,1"), 0)); + + if (max_fps >= 0) { + Engine::get_singleton()->set_max_fps(max_fps); + } + #ifdef TOOLS_ENABLED // If the editor is running in windowed mode, ensure the window rect fits // the screen in case screen count or position has changed. diff --git a/methods.py b/methods.py index 64c3839718..d89185f585 100644 --- a/methods.py +++ b/methods.py @@ -409,8 +409,7 @@ def use_windows_spawn_fix(self, platform=None): "shell": False, "env": env, } - if sys.version_info >= (3, 7, 0): - popen_args["text"] = True + popen_args["text"] = True proc = subprocess.Popen(cmdline, **popen_args) _, err = proc.communicate() rv = proc.wait() @@ -593,23 +592,6 @@ def glob_recursive(pattern, node="."): return results -def add_to_vs_project(env, sources): - for x in sources: - fname = env.File(x).path if isinstance(x, str) else env.File(x)[0].path - pieces = fname.split(".") - if len(pieces) > 0: - basename = pieces[0] - basename = basename.replace("\\\\", "/") - if os.path.isfile(basename + ".h"): - env.vs_incs += [basename + ".h"] - elif os.path.isfile(basename + ".hpp"): - env.vs_incs += [basename + ".hpp"] - if os.path.isfile(basename + ".c"): - env.vs_srcs += [basename + ".c"] - elif os.path.isfile(basename + ".cpp"): - env.vs_srcs += [basename + ".cpp"] - - def precious_program(env, program, sources, **args): program = env.ProgramOriginal(program, sources, **args) env.Precious(program) @@ -1112,9 +1094,7 @@ def generate_vs_project(env, original_args, project_name="godot"): import json md5 = hashlib.md5( - json.dumps(headers + headers_dirs + sources + sources_dirs + others + others_dirs, sort_keys=True).encode( - "utf-8" - ) + json.dumps(sorted(headers + headers_dirs + sources + sources_dirs + others + others_dirs)).encode("utf-8") ).hexdigest() if os.path.exists(f"{project_name}.vcxproj.filters"): diff --git a/misc/extension_api_validation/4.3-stable.expected b/misc/extension_api_validation/4.3-stable.expected index 4f5ebc7a68..75e81b5ff4 100644 --- a/misc/extension_api_validation/4.3-stable.expected +++ b/misc/extension_api_validation/4.3-stable.expected @@ -115,3 +115,10 @@ GH-91201 Validate extension JSON: JSON file: Field was added in a way that breaks compatibility 'classes/OS/methods/read_string_from_stdin': arguments Added optional argument. Compatibility method registered. + + +GH-98918 +-------- +Validate extension JSON: Error: Field 'classes/FileAccess/methods/open_encrypted/arguments': size changed value in new API, from 3 to 4. + +Optional argument added to allow setting initialization vector. Compatibility method registered. diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp index caa7a79874..99e3b02dea 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -646,6 +646,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D Ref<InputEventKey> k = p_event; if (k.is_valid() && k->is_pressed() && !k->is_echo()) { + // Transform mode (toggle button): // If we are in Transform mode we pass the events to the 3D editor, // but if the Transform mode shortcut is pressed again, we go back to Selection mode. if (mode_buttons_group->get_pressed_button() == transform_mode_button) { @@ -656,7 +657,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D } return EditorPlugin::AFTER_GUI_INPUT_PASS; } - + // Tool modes and tool actions: for (BaseButton *b : viewport_shortcut_buttons) { if (b->is_disabled()) { continue; @@ -673,9 +674,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D return EditorPlugin::AFTER_GUI_INPUT_STOP; } } - } - - if (k.is_valid() && k->is_pressed() && !k->is_echo()) { + // Hard key actions: if (k->get_keycode() == Key::ESCAPE) { if (input_action == INPUT_PASTE) { _clear_clipboard_data(); @@ -692,7 +691,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D return EditorPlugin::AFTER_GUI_INPUT_STOP; } } - + // Options menu shortcuts: Ref<Shortcut> ed_shortcut = ED_GET_SHORTCUT("grid_map/previous_floor"); if (ed_shortcut.is_valid() && ed_shortcut->matches_event(p_event)) { accept_event(); @@ -1396,6 +1395,7 @@ GridMapEditor::GridMapEditor() { fill_action_button->connect(SceneStringName(pressed), callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_SELECTION_FILL)); action_buttons->add_child(fill_action_button); + viewport_shortcut_buttons.push_back(fill_action_button); move_action_button = memnew(Button); move_action_button->set_theme_type_variation("FlatButton"); @@ -1403,6 +1403,7 @@ GridMapEditor::GridMapEditor() { move_action_button->connect(SceneStringName(pressed), callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_SELECTION_CUT)); action_buttons->add_child(move_action_button); + viewport_shortcut_buttons.push_back(move_action_button); duplicate_action_button = memnew(Button); duplicate_action_button->set_theme_type_variation("FlatButton"); @@ -1410,6 +1411,7 @@ GridMapEditor::GridMapEditor() { duplicate_action_button->connect(SceneStringName(pressed), callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_SELECTION_DUPLICATE)); action_buttons->add_child(duplicate_action_button); + viewport_shortcut_buttons.push_back(duplicate_action_button); delete_action_button = memnew(Button); delete_action_button->set_theme_type_variation("FlatButton"); @@ -1417,6 +1419,7 @@ GridMapEditor::GridMapEditor() { delete_action_button->connect(SceneStringName(pressed), callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_SELECTION_CLEAR)); action_buttons->add_child(delete_action_button); + viewport_shortcut_buttons.push_back(delete_action_button); vsep = memnew(VSeparator); toolbar->add_child(vsep); @@ -1430,6 +1433,7 @@ GridMapEditor::GridMapEditor() { rotate_x_button->connect(SceneStringName(pressed), callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_CURSOR_ROTATE_X)); rotation_buttons->add_child(rotate_x_button); + viewport_shortcut_buttons.push_back(rotate_x_button); rotate_y_button = memnew(Button); rotate_y_button->set_theme_type_variation("FlatButton"); @@ -1437,6 +1441,7 @@ GridMapEditor::GridMapEditor() { rotate_y_button->connect(SceneStringName(pressed), callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_CURSOR_ROTATE_Y)); rotation_buttons->add_child(rotate_y_button); + viewport_shortcut_buttons.push_back(rotate_y_button); rotate_z_button = memnew(Button); rotate_z_button->set_theme_type_variation("FlatButton"); @@ -1444,6 +1449,7 @@ GridMapEditor::GridMapEditor() { rotate_z_button->connect(SceneStringName(pressed), callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_CURSOR_ROTATE_Z)); rotation_buttons->add_child(rotate_z_button); + viewport_shortcut_buttons.push_back(rotate_z_button); // Wide empty separation control. (like BoxContainer::add_spacer()) Control *c = memnew(Control); @@ -1456,9 +1462,9 @@ GridMapEditor::GridMapEditor() { floor->set_max(32767); floor->set_step(1); floor->set_tooltip_text( - TTR(vformat("Change Grid Floor:\nPrevious Plane (%s)\nNext Plane (%s)", + vformat(TTR("Change Grid Floor:\nPrevious Plane (%s)\nNext Plane (%s)"), ED_GET_SHORTCUT("grid_map/previous_floor")->get_as_text(), - ED_GET_SHORTCUT("grid_map/next_floor")->get_as_text()))); + ED_GET_SHORTCUT("grid_map/next_floor")->get_as_text())); toolbar->add_child(floor); floor->get_line_edit()->add_theme_constant_override("minimum_character_width", 2); floor->get_line_edit()->set_context_menu_enabled(false); diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index a6fd727290..1775541757 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -2036,8 +2036,9 @@ bool OpenXRAPI::poll_events() { if (local_floor_emulation.enabled) { local_floor_emulation.should_reset_floor_height = true; } - if (event->poseValid && xr_interface) { - xr_interface->on_pose_recentered(); + + if (xr_interface) { + xr_interface->on_reference_space_change_pending(); } } break; case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: { diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp index 8e0c672e58..68e04694e3 100644 --- a/modules/openxr/openxr_interface.cpp +++ b/modules/openxr/openxr_interface.cpp @@ -1134,6 +1134,12 @@ void OpenXRInterface::process() { if (head.is_valid()) { head->set_pose("default", head_transform, head_linear_velocity, head_angular_velocity, head_confidence); } + + if (reference_stage_changing) { + // Now that we have updated tracking information in our updated reference space, trigger our pose recentered signal. + emit_signal(SNAME("pose_recentered")); + reference_stage_changing = false; + } } void OpenXRInterface::pre_render() { @@ -1315,8 +1321,8 @@ void OpenXRInterface::on_state_exiting() { emit_signal(SNAME("instance_exiting")); } -void OpenXRInterface::on_pose_recentered() { - emit_signal(SNAME("pose_recentered")); +void OpenXRInterface::on_reference_space_change_pending() { + reference_stage_changing = true; } void OpenXRInterface::on_refresh_rate_changes(float p_new_rate) { diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h index f0ee0dc3c4..d1bf2aaf78 100644 --- a/modules/openxr/openxr_interface.h +++ b/modules/openxr/openxr_interface.h @@ -70,6 +70,7 @@ class OpenXRInterface : public XRInterface { private: OpenXRAPI *openxr_api = nullptr; bool initialized = false; + bool reference_stage_changing = false; XRInterface::TrackingStatus tracking_state; // At a minimum we need a tracker for our head @@ -207,7 +208,7 @@ public: void on_state_stopping(); void on_state_loss_pending(); void on_state_exiting(); - void on_pose_recentered(); + void on_reference_space_change_pending(); void on_refresh_rate_changes(float p_new_rate); void tracker_profile_changed(RID p_tracker, RID p_interaction_profile); diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 65916931c2..df3142ecbb 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -788,7 +788,7 @@ Error EditorExportPlatformAndroid::save_apk_so(void *p_userdata, const SharedObj return OK; } -Error EditorExportPlatformAndroid::save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { +Error EditorExportPlatformAndroid::save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed) { APKExportData *ed = static_cast<APKExportData *>(p_userdata); String dst_path = p_path.replace_first("res://", "assets/"); @@ -796,7 +796,7 @@ Error EditorExportPlatformAndroid::save_apk_file(void *p_userdata, const String return OK; } -Error EditorExportPlatformAndroid::ignore_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { +Error EditorExportPlatformAndroid::ignore_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed) { return OK; } diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h index 15e80f824d..23b6f9b193 100644 --- a/platform/android/export/export_plugin.h +++ b/platform/android/export/export_plugin.h @@ -142,9 +142,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { static Error save_apk_so(void *p_userdata, const SharedObject &p_so); - static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); + static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed); - static Error ignore_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); + static Error ignore_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed); static Error copy_gradle_so(void *p_userdata, const SharedObject &p_so); @@ -186,7 +186,7 @@ protected: void _notification(int p_what); public: - typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); + typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed); virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override; diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp index b2aed9a0dc..3603565805 100644 --- a/platform/android/export/gradle_export_util.cpp +++ b/platform/android/export/gradle_export_util.cpp @@ -169,7 +169,7 @@ Error store_string_at_path(const String &p_path, const String &p_data) { // It is used by the export_project_files method to save all the asset files into the gradle project. // It's functionality mirrors that of the method save_apk_file. // This method will be called ONLY when gradle build is enabled. -Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { +Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed) { CustomExportData *export_data = static_cast<CustomExportData *>(p_userdata); String dst_path = p_path.replace_first("res://", export_data->assets_directory + "/"); print_verbose("Saving project files from " + p_path + " into " + dst_path); diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h index a17fdf0e27..a528fd5211 100644 --- a/platform/android/export/gradle_export_util.h +++ b/platform/android/export/gradle_export_util.h @@ -93,7 +93,7 @@ Error store_string_at_path(const String &p_path, const String &p_data); // It is used by the export_project_files method to save all the asset files into the gradle project. // It's functionality mirrors that of the method save_apk_file. // This method will be called ONLY when gradle build is enabled. -Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); +Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed); // Creates strings.xml files inside the gradle project for different locales. Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name, const String &p_gradle_build_dir); diff --git a/platform/android/java/editor/build.gradle b/platform/android/java/editor/build.gradle index 45222ca3b0..276d74b75b 100644 --- a/platform/android/java/editor/build.gradle +++ b/platform/android/java/editor/build.gradle @@ -173,7 +173,7 @@ dependencies { implementation "androidx.window:window:1.3.0" implementation "androidx.core:core-splashscreen:$versions.splashscreenVersion" implementation "androidx.constraintlayout:constraintlayout:2.1.4" - implementation "org.bouncycastle:bcprov-jdk15to18:1.77" + implementation "org.bouncycastle:bcprov-jdk15to18:1.78" // Meta dependencies horizonosImplementation "org.godotengine:godot-openxr-vendors-meta:3.0.0-stable" diff --git a/platform/android/net_socket_android.cpp b/platform/android/net_socket_android.cpp index 8f0ee51fac..9ab7d6a04f 100644 --- a/platform/android/net_socket_android.cpp +++ b/platform/android/net_socket_android.cpp @@ -84,7 +84,7 @@ NetSocketAndroid::~NetSocketAndroid() { } void NetSocketAndroid::close() { - NetSocketPosix::close(); + NetSocketUnix::close(); if (wants_broadcast) { multicast_lock_release(); } @@ -96,7 +96,7 @@ void NetSocketAndroid::close() { } Error NetSocketAndroid::set_broadcasting_enabled(bool p_enabled) { - Error err = NetSocketPosix::set_broadcasting_enabled(p_enabled); + Error err = NetSocketUnix::set_broadcasting_enabled(p_enabled); if (err != OK) { return err; } @@ -115,7 +115,7 @@ Error NetSocketAndroid::set_broadcasting_enabled(bool p_enabled) { } Error NetSocketAndroid::join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) { - Error err = NetSocketPosix::join_multicast_group(p_multi_address, p_if_name); + Error err = NetSocketUnix::join_multicast_group(p_multi_address, p_if_name); if (err != OK) { return err; } @@ -129,7 +129,7 @@ Error NetSocketAndroid::join_multicast_group(const IPAddress &p_multi_address, c } Error NetSocketAndroid::leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) { - Error err = NetSocketPosix::leave_multicast_group(p_multi_address, p_if_name); + Error err = NetSocketUnix::leave_multicast_group(p_multi_address, p_if_name); if (err != OK) { return err; } diff --git a/platform/android/net_socket_android.h b/platform/android/net_socket_android.h index 452553ab1c..c33146d2d8 100644 --- a/platform/android/net_socket_android.h +++ b/platform/android/net_socket_android.h @@ -44,7 +44,7 @@ * the lock when broadcasting is enabled/disabled on a socket, or that socket * joins/leaves a multicast group. */ -class NetSocketAndroid : public NetSocketPosix { +class NetSocketAndroid : public NetSocketUnix { private: static jobject net_utils; static jclass cls; diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py index 2fd573da75..c8202b147d 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -279,16 +279,18 @@ def configure(env: "SConsEnvironment"): env.ParseConfig("pkg-config libwebp --cflags --libs") if not env["builtin_mbedtls"]: - # mbedTLS does not provide a pkgconfig config yet. See https://github.com/ARMmbed/mbedtls/issues/228 - env.Append(LIBS=["mbedtls", "mbedcrypto", "mbedx509"]) + # mbedTLS only provides a pkgconfig file since 3.6.0, but we still support 2.28.x, + # so fallback to manually specifying LIBS if it fails. + if os.system("pkg-config --exists mbedtls") == 0: # 0 means found + env.ParseConfig("pkg-config mbedtls mbedcrypto mbedx509 --cflags --libs") + else: + env.Append(LIBS=["mbedtls", "mbedcrypto", "mbedx509"]) if not env["builtin_wslay"]: env.ParseConfig("pkg-config libwslay --cflags --libs") if not env["builtin_miniupnpc"]: - # No pkgconfig file so far, hardcode default paths. - env.Prepend(CPPPATH=["/usr/include/miniupnpc"]) - env.Append(LIBS=["miniupnpc"]) + env.ParseConfig("pkg-config miniupnpc --cflags --libs") # On Linux wchar_t should be 32-bits # 16-bit library shouldn't be required due to compiler optimizations diff --git a/platform/web/detect.py b/platform/web/detect.py index 26bbbccffa..25a5bbe5a5 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -201,7 +201,7 @@ def configure(env: "SConsEnvironment"): sys.exit(255) env.Prepend(CPPPATH=["#platform/web"]) - env.Append(CPPDEFINES=["WEB_ENABLED", "UNIX_ENABLED"]) + env.Append(CPPDEFINES=["WEB_ENABLED", "UNIX_ENABLED", "UNIX_SOCKET_UNAVAILABLE"]) if env["opengl3"]: env.AppendUnique(CPPDEFINES=["GLES3_ENABLED"]) diff --git a/platform/web/package-lock.json b/platform/web/package-lock.json index a2e0fd3b27..7947fb96e4 100644 --- a/platform/web/package-lock.json +++ b/platform/web/package-lock.json @@ -9,14 +9,14 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@eslint/js": "^9.3.0", - "@html-eslint/eslint-plugin": "^0.24.1", - "@html-eslint/parser": "^0.24.1", - "@stylistic/eslint-plugin": "^2.1.0", - "eslint": "^9.3.0", + "@eslint/js": "^9.12.0", + "@html-eslint/eslint-plugin": "^0.27.0", + "@html-eslint/parser": "^0.27.0", + "@stylistic/eslint-plugin": "^2.9.0", + "eslint": "^9.12.0", "eslint-plugin-html": "^8.1.1", "espree": "^10.0.1", - "globals": "^15.3.0", + "globals": "^15.9.0", "jsdoc": "^4.0.3" } }, @@ -60,14 +60,40 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.6.0.tgz", + "integrity": "sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", @@ -104,28 +130,54 @@ } }, "node_modules/@eslint/js": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.3.0.tgz", - "integrity": "sha512-niBqk8iwv96+yuTwjM6bWg8ovzAPF9qkICsGtcoa5/dmqcEMfdwNAX7+/OHcJHc7wj7XqPxH98oAHytFYlw6Sw==", + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.12.0.tgz", + "integrity": "sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==", "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.1.tgz", + "integrity": "sha512-HFZ4Mp26nbWk9d/BpvP0YNL6W4UoZF0VFcTw/aPPA8RpOxeFQgK+ClABGgAUXs9Y/RGX/l1vOmrqz1MQt9MNuw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "levn": "^0.4.1" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@html-eslint/eslint-plugin": { - "version": "0.24.1", - "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.24.1.tgz", - "integrity": "sha512-JwNDQBrNIWEPcxgSpla/2jaUXyQCqL7Xp8CmON4Bk5qg8MwiDLXOgjylfVC+tN52i8JeHWMca34I9DqBGRj9Qg==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.27.0.tgz", + "integrity": "sha512-aAF14sgDKidMCCQpJ4kIhe+fwyAaAbvDlgVTIgd99F+HOWxokTTXDt39a3gewMBo76IeEHDaoizUDJQ/Vc7Mdg==", "dev": true, + "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@html-eslint/parser": { - "version": "0.24.1", - "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.24.1.tgz", - "integrity": "sha512-O13xX/+Ldh0P7VZMpDDYc3XtWiE1cYm5QhVJ0VB5i7D8Q69HrrGN+5BjS17vkCoLTz+3zWWIiJv4oFmyS5LReA==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.27.0.tgz", + "integrity": "sha512-F/A1M0jnDAYoRvJiiSC7pIBD9DAsf4EhbndbvEi81aozD/wI8WWXON50xZPUaGHCI1C+2syTVifxDz8MvDKaQA==", "dev": true, + "license": "MIT", "dependencies": { "es-html-parser": "^0.0.9" }, @@ -133,18 +185,28 @@ "node": ">=8.10.0" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "node_modules/@humanfs/core": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz", + "integrity": "sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==", "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.5", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.5.tgz", + "integrity": "sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" + "@humanfs/core": "^0.19.0", + "@humanwhocodes/retry": "^0.3.0" }, "engines": { - "node": ">=10.10.0" + "node": ">=18.18.0" } }, "node_modules/@humanwhocodes/module-importer": { @@ -160,17 +222,12 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "dev": true - }, "node_modules/@humanwhocodes/retry": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", - "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -227,50 +284,15 @@ } }, "node_modules/@stylistic/eslint-plugin": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.1.0.tgz", - "integrity": "sha512-cBBowKP2u/+uE5CzgH5w8pE9VKqcM7BXdIDPIbGt2rmLJGnA6MJPr9vYGaqgMoJFs7R/FzsMQerMvvEP40g2uw==", - "dev": true, - "dependencies": { - "@stylistic/eslint-plugin-js": "2.1.0", - "@stylistic/eslint-plugin-jsx": "2.1.0", - "@stylistic/eslint-plugin-plus": "2.1.0", - "@stylistic/eslint-plugin-ts": "2.1.0", - "@types/eslint": "^8.56.10" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "peerDependencies": { - "eslint": ">=8.40.0" - } - }, - "node_modules/@stylistic/eslint-plugin-js": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.1.0.tgz", - "integrity": "sha512-gdXUjGNSsnY6nPyqxu6lmDTtVrwCOjun4x8PUn0x04d5ucLI74N3MT1Q0UhdcOR9No3bo5PGDyBgXK+KmD787A==", - "dev": true, - "dependencies": { - "@types/eslint": "^8.56.10", - "acorn": "^8.11.3", - "eslint-visitor-keys": "^4.0.0", - "espree": "^10.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "peerDependencies": { - "eslint": ">=8.40.0" - } - }, - "node_modules/@stylistic/eslint-plugin-jsx": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-jsx/-/eslint-plugin-jsx-2.1.0.tgz", - "integrity": "sha512-mMD7S+IndZo2vxmwpHVTCwx2O1VdtE5tmpeNwgaEcXODzWV1WTWpnsc/PECQKIr/mkLPFWiSIqcuYNhQ/3l6AQ==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.9.0.tgz", + "integrity": "sha512-OrDyFAYjBT61122MIY1a3SfEgy3YCMgt2vL4eoPmvTwDBwyQhAXurxNQznlRD/jESNfYWfID8Ej+31LljvF7Xg==", "dev": true, + "license": "MIT", "dependencies": { - "@stylistic/eslint-plugin-js": "^2.1.0", - "@types/eslint": "^8.56.10", + "@typescript-eslint/utils": "^8.8.0", + "eslint-visitor-keys": "^4.1.0", + "espree": "^10.2.0", "estraverse": "^5.3.0", "picomatch": "^4.0.2" }, @@ -281,101 +303,19 @@ "eslint": ">=8.40.0" } }, - "node_modules/@stylistic/eslint-plugin-plus": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-plus/-/eslint-plugin-plus-2.1.0.tgz", - "integrity": "sha512-S5QAlgYXESJaSBFhBSBLZy9o36gXrXQwWSt6QkO+F0SrT9vpV5JF/VKoh+ojO7tHzd8Ckmyouq02TT9Sv2B0zQ==", - "dev": true, - "dependencies": { - "@types/eslint": "^8.56.10", - "@typescript-eslint/utils": "^7.8.0" - }, - "peerDependencies": { - "eslint": "*" - } - }, - "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/utils": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.10.0.tgz", - "integrity": "sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.10.0", - "@typescript-eslint/types": "7.10.0", - "@typescript-eslint/typescript-estree": "7.10.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - } - }, - "node_modules/@stylistic/eslint-plugin-ts": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-2.1.0.tgz", - "integrity": "sha512-2ioFibufHYBALx2TBrU4KXovCkN8qCqcb9yIHc0fyOfTaO5jw4d56WW7YRcF3Zgde6qFyXwAN6z/+w4pnmos1g==", - "dev": true, - "dependencies": { - "@stylistic/eslint-plugin-js": "2.1.0", - "@types/eslint": "^8.56.10", - "@typescript-eslint/utils": "^7.8.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "peerDependencies": { - "eslint": ">=8.40.0" - } - }, - "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/utils": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.10.0.tgz", - "integrity": "sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.10.0", - "@typescript-eslint/types": "7.10.0", - "@typescript-eslint/typescript-estree": "7.10.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - } - }, - "node_modules/@types/eslint": { - "version": "8.56.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", - "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/linkify-it": { "version": "5.0.0", @@ -400,16 +340,17 @@ "dev": true }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.10.0.tgz", - "integrity": "sha512-7L01/K8W/VGl7noe2mgH0K7BE29Sq6KAbVmxurj8GGaPDZXPr8EEQ2seOeAS+mEV9DnzxBQB6ax6qQQ5C6P4xg==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.11.0.tgz", + "integrity": "sha512-Uholz7tWhXmA4r6epo+vaeV7yjdKy5QFCERMjs1kMVsLRKIrSdM6o21W2He9ftp5PP6aWOVpD5zvrvuHZC0bMQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.10.0", - "@typescript-eslint/visitor-keys": "7.10.0" + "@typescript-eslint/types": "8.11.0", + "@typescript-eslint/visitor-keys": "8.11.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -417,12 +358,13 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.10.0.tgz", - "integrity": "sha512-7fNj+Ya35aNyhuqrA1E/VayQX9Elwr8NKZ4WueClR3KwJ7Xx9jcCdOrLW04h51de/+gNbyFMs+IDxh5xIwfbNg==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.11.0.tgz", + "integrity": "sha512-tn6sNMHf6EBAYMvmPUaKaVeYvhUsrE6x+bXQTxjQRp360h1giATU0WvgeEys1spbvb5R+VpNOZ+XJmjD8wOUHw==", "dev": true, + "license": "MIT", "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -430,22 +372,23 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.10.0.tgz", - "integrity": "sha512-LXFnQJjL9XIcxeVfqmNj60YhatpRLt6UhdlFwAkjNc6jSUlK8zQOl1oktAP8PlWFzPQC1jny/8Bai3/HPuvN5g==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.11.0.tgz", + "integrity": "sha512-yHC3s1z1RCHoCz5t06gf7jH24rr3vns08XXhfEqzYpd6Hll3z/3g23JRi0jM8A47UFKNc3u/y5KIMx8Ynbjohg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "7.10.0", - "@typescript-eslint/visitor-keys": "7.10.0", + "@typescript-eslint/types": "8.11.0", + "@typescript-eslint/visitor-keys": "8.11.0", "debug": "^4.3.4", - "globby": "^11.1.0", + "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -462,15 +405,17 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -481,17 +426,41 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@typescript-eslint/utils": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.11.0.tgz", + "integrity": "sha512-CYiX6WZcbXNJV7UNB4PLDIBtSdRmRI/nb0FMyqHPTQD1rMjA0foPLaPUV39C/MxkTd/QKSeX+Gb34PPsDVC35g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.11.0", + "@typescript-eslint/types": "8.11.0", + "@typescript-eslint/typescript-estree": "8.11.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.10.0.tgz", - "integrity": "sha512-9ntIVgsi6gg6FIq9xjEO4VQJvwOqA3jaBFQJ/6TK5AvEup2+cECI6Fh7QiBxmfMHXU0V0J4RyPeOU1VDNzl9cg==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.11.0.tgz", + "integrity": "sha512-EaewX6lxSjRJnc+99+dqzTeoDZUfyrA52d2/HRrkI830kgovWsmIiTfmr0NZorzqic7ga+1bS60lRBUgR3n/Bw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/types": "8.11.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -503,6 +472,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -511,10 +481,11 @@ } }, "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -547,15 +518,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -577,15 +539,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -613,6 +566,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -718,18 +672,6 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -816,28 +758,33 @@ } }, "node_modules/eslint": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.3.0.tgz", - "integrity": "sha512-5Iv4CsZW030lpUqHBapdPo3MJetAPtejVW8B84GIcIIv8+ohFaddXsrn1Gn8uD9ijDb+kcYKFUVmC8qG8B2ORQ==", + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.12.0.tgz", + "integrity": "sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.18.0", + "@eslint/core": "^0.6.0", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.3.0", - "@humanwhocodes/config-array": "^0.13.0", + "@eslint/js": "9.12.0", + "@eslint/plugin-kit": "^0.2.0", + "@humanfs/node": "^0.16.5", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.3.0", - "@nodelib/fs.walk": "^1.2.8", + "@humanwhocodes/retry": "^0.3.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.0.1", - "eslint-visitor-keys": "^4.0.0", - "espree": "^10.0.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.1.0", + "eslint-visitor-keys": "^4.1.0", + "espree": "^10.2.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", @@ -846,14 +793,11 @@ "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, "bin": { @@ -863,7 +807,15 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-plugin-html": { @@ -879,10 +831,11 @@ } }, "node_modules/eslint-scope": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", - "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz", + "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -895,10 +848,11 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", + "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -907,14 +861,15 @@ } }, "node_modules/espree": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", - "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", + "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.11.3", + "acorn": "^8.12.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.0.0" + "eslint-visitor-keys": "^4.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -940,6 +895,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -976,6 +932,7 @@ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -992,6 +949,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -1037,6 +995,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -1092,10 +1051,11 @@ } }, "node_modules/globals": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.3.0.tgz", - "integrity": "sha512-cCdyVjIUVTtX8ZsPkq1oCsOsLmGIswqnjZYMJJTGaNApj1yHtLSymKhwH51ttirREn75z3p4k051clwg7rvNKA==", + "version": "15.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz", + "integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -1103,26 +1063,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -1217,19 +1157,11 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1430,15 +1362,17 @@ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -1452,6 +1386,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -1572,20 +1507,12 @@ "node": ">=8" } }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/picomatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -1692,10 +1619,11 @@ } }, "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -1724,27 +1652,6 @@ "node": ">=8" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -1780,6 +1687,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -1792,6 +1700,7 @@ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=16" }, @@ -1812,10 +1721,11 @@ } }, "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, + "license": "Apache-2.0", "peer": true, "bin": { "tsc": "bin/tsc", diff --git a/platform/web/package.json b/platform/web/package.json index 588af2ff3b..bf61eb184c 100644 --- a/platform/web/package.json +++ b/platform/web/package.json @@ -11,14 +11,14 @@ "format": "npm run lint -- --fix" }, "devDependencies": { - "@eslint/js": "^9.3.0", - "@html-eslint/eslint-plugin": "^0.24.1", - "@html-eslint/parser": "^0.24.1", - "@stylistic/eslint-plugin": "^2.1.0", - "eslint": "^9.3.0", + "@eslint/js": "^9.12.0", + "@html-eslint/eslint-plugin": "^0.27.0", + "@html-eslint/parser": "^0.27.0", + "@stylistic/eslint-plugin": "^2.9.0", + "eslint": "^9.12.0", "eslint-plugin-html": "^8.1.1", "espree": "^10.0.1", - "globals": "^15.3.0", + "globals": "^15.9.0", "jsdoc": "^4.0.3" } } diff --git a/platform/windows/SCsub b/platform/windows/SCsub index eaa5ceff88..1ddefb9c33 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -83,16 +83,6 @@ if env["windows_subsystem"] == "gui": env_wrap.Depends(prog_wrap, prog) sources += common_win_wrap + res_wrap_obj -# Microsoft Visual Studio Project Generation -if env["vsproj"]: - env.vs_srcs += ["platform/windows/" + res_file] - env.vs_srcs += ["platform/windows/godot.natvis"] - for x in common_win: - env.vs_srcs += ["platform/windows/" + str(x)] - if env["windows_subsystem"] == "gui": - for x in common_win_wrap: - env.vs_srcs += ["platform/windows/" + str(x)] - if env["d3d12"]: dxc_target_aliases = { "x86_32": "x86", diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index a6eab1bd29..26dad095ad 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -4792,9 +4792,12 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA break; } - DisplayServer::WindowID receiving_window_id = _get_focused_window_or_popup(); - if (receiving_window_id == INVALID_WINDOW_ID) { - receiving_window_id = window_id; + DisplayServer::WindowID receiving_window_id = window_id; + if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { + receiving_window_id = _get_focused_window_or_popup(); + if (receiving_window_id == INVALID_WINDOW_ID) { + receiving_window_id = window_id; + } } const BitField<WinKeyModifierMask> &mods = _get_mods(); diff --git a/platform_methods.py b/platform_methods.py index 2c4eb0d1dd..201df3c0b5 100644 --- a/platform_methods.py +++ b/platform_methods.py @@ -8,6 +8,13 @@ import methods # NOTE: The multiprocessing module is not compatible with SCons due to conflict on cPickle +compatibility_platform_aliases = { + "osx": "macos", + "iphone": "ios", + "x11": "linuxbsd", + "javascript": "web", +} + # CPU architecture options. architectures = ["x86_32", "x86_64", "arm32", "arm64", "rv64", "ppc32", "ppc64", "wasm32"] architecture_aliases = { diff --git a/pyproject.toml b/pyproject.toml index a4bfd27816..403d9fd675 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,14 +1,14 @@ [tool.mypy] -ignore_missing_imports = true disallow_any_generics = true +explicit_package_bases = true +ignore_missing_imports = true +namespace_packages = true no_implicit_optional = true pretty = true show_column_numbers = true warn_redundant_casts = true warn_return_any = true warn_unreachable = true -namespace_packages = true -explicit_package_bases = true exclude = ["thirdparty/"] python_version = "3.8" @@ -16,7 +16,7 @@ python_version = "3.8" extend-exclude = ["thirdparty"] extend-include = ["SConstruct", "SCsub"] line-length = 120 -target-version = "py37" +target-version = "py38" [tool.ruff.lint] extend-select = [ @@ -42,50 +42,50 @@ section-order = [ ] [tool.codespell] -enable-colors = "" -write-changes = "" -check-hidden = "" +enable-colors = true +write-changes = true +check-hidden = true quiet-level = 3 -builtin = "clear,rare,en-GB_to_en-US" -skip = """\ - .mailmap, - *.desktop, - *.gitignore, - *.po, - *.pot, - *.rc, - AUTHORS.md, - COPYRIGHT.txt, - core/input/gamecontrollerdb.txt, - core/string/locales.h, - DONORS.md, - editor/project_converter_3_to_4.cpp, - platform/android/java/lib/src/com/*, - platform/web/package-lock.json -""" -ignore-words-list = """\ - breaked, - cancelled, - checkin, - colour, - curvelinear, - doubleclick, - expct, - findn, - gird, - hel, - inout, - labelin, - lod, - mis, - nd, - numer, - ot, - outin, - parm, - requestor, - te, - textin, - thirdparty, - vai -""" +builtin = ["clear", "rare", "en-GB_to_en-US"] +skip = [ + ".mailmap", + "*.desktop", + "*.gitignore", + "*.po", + "*.pot", + "*.rc", + "AUTHORS.md", + "COPYRIGHT.txt", + "core/input/gamecontrollerdb.txt", + "core/string/locales.h", + "DONORS.md", + "editor/project_converter_3_to_4.cpp", + "platform/android/java/lib/src/com/*", + "platform/web/package-lock.json", +] +ignore-words-list = [ + "breaked", + "cancelled", + "checkin", + "colour", + "curvelinear", + "doubleclick", + "expct", + "findn", + "gird", + "hel", + "inout", + "labelin", + "lod", + "mis", + "nd", + "numer", + "ot", + "outin", + "parm", + "requestor", + "te", + "textin", + "thirdparty", + "vai", +] diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index c658bfd799..cdbd95d930 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -333,9 +333,7 @@ void LightmapGI::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> &m mf.node_path = get_path_to(mi); mf.subindex = -1; mf.mesh = mesh; - - static const int lightmap_scale[GeometryInstance3D::LIGHTMAP_SCALE_MAX] = { 1, 2, 4, 8 }; - mf.lightmap_scale = lightmap_scale[mi->get_lightmap_scale()]; + mf.lightmap_scale = mi->get_lightmap_texel_scale(); Ref<Material> all_override = mi->get_material_override(); for (int i = 0; i < mesh->get_surface_count(); i++) { @@ -369,7 +367,7 @@ void LightmapGI::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> &m mf.xform = xf * mesh_xf; mf.node_path = get_path_to(s); mf.subindex = i / 2; - mf.lightmap_scale = 1; + mf.lightmap_scale = 1.0; mf.mesh = mesh; meshes.push_back(mf); diff --git a/scene/3d/lightmap_gi.h b/scene/3d/lightmap_gi.h index 0476061c60..faa8b84fa1 100644 --- a/scene/3d/lightmap_gi.h +++ b/scene/3d/lightmap_gi.h @@ -194,7 +194,7 @@ private: NodePath node_path; int32_t subindex = 0; Ref<Mesh> mesh; - int32_t lightmap_scale = 0; + float lightmap_scale = 0.0; Vector<Ref<Material>> overrides; }; diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index a59754c8cc..2c7a004dd0 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -454,14 +454,48 @@ AABB GeometryInstance3D::get_custom_aabb() const { return custom_aabb; } +void GeometryInstance3D::set_lightmap_texel_scale(float p_scale) { + lightmap_texel_scale = p_scale; +} + +float GeometryInstance3D::get_lightmap_texel_scale() const { + return lightmap_texel_scale; +} + +#ifndef DISABLE_DEPRECATED void GeometryInstance3D::set_lightmap_scale(LightmapScale p_scale) { ERR_FAIL_INDEX(p_scale, LIGHTMAP_SCALE_MAX); - lightmap_scale = p_scale; + switch (p_scale) { + case GeometryInstance3D::LIGHTMAP_SCALE_1X: + lightmap_texel_scale = 1.0f; + break; + case GeometryInstance3D::LIGHTMAP_SCALE_2X: + lightmap_texel_scale = 2.0f; + break; + case GeometryInstance3D::LIGHTMAP_SCALE_4X: + lightmap_texel_scale = 4.0f; + break; + case GeometryInstance3D::LIGHTMAP_SCALE_8X: + lightmap_texel_scale = 8.0f; + break; + case GeometryInstance3D::LIGHTMAP_SCALE_MAX: + break; // Can't happen, but silences warning. + } } GeometryInstance3D::LightmapScale GeometryInstance3D::get_lightmap_scale() const { - return lightmap_scale; + // Return closest approximation. + if (lightmap_texel_scale < 1.5f) { + return GeometryInstance3D::LIGHTMAP_SCALE_1X; + } else if (lightmap_texel_scale < 3.0f) { + return GeometryInstance3D::LIGHTMAP_SCALE_2X; + } else if (lightmap_texel_scale < 6.0f) { + return GeometryInstance3D::LIGHTMAP_SCALE_4X; + } + + return GeometryInstance3D::LIGHTMAP_SCALE_8X; } +#endif // DISABLE_DEPRECATED void GeometryInstance3D::set_gi_mode(GIMode p_mode) { switch (p_mode) { @@ -565,8 +599,13 @@ void GeometryInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_extra_cull_margin", "margin"), &GeometryInstance3D::set_extra_cull_margin); ClassDB::bind_method(D_METHOD("get_extra_cull_margin"), &GeometryInstance3D::get_extra_cull_margin); + ClassDB::bind_method(D_METHOD("set_lightmap_texel_scale", "scale"), &GeometryInstance3D::set_lightmap_texel_scale); + ClassDB::bind_method(D_METHOD("get_lightmap_texel_scale"), &GeometryInstance3D::get_lightmap_texel_scale); + +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("set_lightmap_scale", "scale"), &GeometryInstance3D::set_lightmap_scale); ClassDB::bind_method(D_METHOD("get_lightmap_scale"), &GeometryInstance3D::get_lightmap_scale); +#endif // DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("set_gi_mode", "mode"), &GeometryInstance3D::set_gi_mode); ClassDB::bind_method(D_METHOD("get_gi_mode"), &GeometryInstance3D::get_gi_mode); @@ -591,7 +630,10 @@ void GeometryInstance3D::_bind_methods() { ADD_GROUP("Global Illumination", "gi_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Static,Dynamic"), "set_gi_mode", "get_gi_mode"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, String::utf8("1×,2×,4×,8×")), "set_lightmap_scale", "get_lightmap_scale"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gi_lightmap_texel_scale", PROPERTY_HINT_RANGE, "0.01,10,0.0001,or_greater"), "set_lightmap_texel_scale", "get_lightmap_texel_scale"); +#ifndef DISABLE_DEPRECATED + ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, String::utf8("1×,2×,4×,8×"), PROPERTY_USAGE_NONE), "set_lightmap_scale", "get_lightmap_scale"); +#endif // DISABLE_DEPRECATED ADD_GROUP("Visibility Range", "visibility_range_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_visibility_range_begin", "get_visibility_range_begin"); diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 9b02c928b7..073fa74573 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -134,7 +134,7 @@ private: float extra_cull_margin = 0.0; AABB custom_aabb; - LightmapScale lightmap_scale = LIGHTMAP_SCALE_1X; + float lightmap_texel_scale = 1.0f; GIMode gi_mode = GI_MODE_STATIC; bool ignore_occlusion_culling = false; @@ -185,8 +185,13 @@ public: void set_gi_mode(GIMode p_mode); GIMode get_gi_mode() const; - void set_lightmap_scale(LightmapScale p_scale); + void set_lightmap_texel_scale(float p_scale); + float get_lightmap_texel_scale() const; + +#ifndef DISABLE_DEPRECATED + void set_lightmap_scale(GeometryInstance3D::LightmapScale p_scale); LightmapScale get_lightmap_scale() const; +#endif // DISABLE_DEPRECATED void set_instance_shader_parameter(const StringName &p_name, const Variant &p_value); Variant get_instance_shader_parameter(const StringName &p_name) const; @@ -203,8 +208,8 @@ public: }; VARIANT_ENUM_CAST(GeometryInstance3D::ShadowCastingSetting); -VARIANT_ENUM_CAST(GeometryInstance3D::LightmapScale); VARIANT_ENUM_CAST(GeometryInstance3D::GIMode); +VARIANT_ENUM_CAST(GeometryInstance3D::LightmapScale); VARIANT_ENUM_CAST(GeometryInstance3D::VisibilityRangeFadeMode); #endif // VISUAL_INSTANCE_3D_H diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index a2aef60417..e172286d05 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -93,7 +93,9 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::process(const AnimationMixer AnimationMixer::PlaybackInfo pi = p_playback_info; if (p_playback_info.seeked) { - pi.delta = get_node_time_info().position - p_playback_info.time; + if (p_playback_info.is_external_seeking) { + pi.delta = get_node_time_info().position - p_playback_info.time; + } } else { pi.time = get_node_time_info().position + (backward ? -p_playback_info.delta : p_playback_info.delta); } @@ -140,6 +142,12 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixe // 1. Progress for AnimationNode. bool will_end = Animation::is_greater_or_equal_approx(cur_time + cur_delta, cur_len); + bool is_started = p_seek && !p_is_external_seeking && Math::is_zero_approx(cur_time); + + // 1. Progress for AnimationNode. + if (is_started && advance_on_start) { + cur_time = cur_delta; + } if (cur_loop_mode != Animation::LOOP_NONE) { if (cur_loop_mode == Animation::LOOP_LINEAR) { if (!Math::is_zero_approx(cur_len)) { @@ -232,7 +240,7 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixe // We should use call_deferred since the track keys are still being processed. if (process_state->tree && !p_test_only) { // AnimationTree uses seek to 0 "internally" to process the first key of the animation, which is used as the start detection. - if (p_seek && !p_is_external_seeking && Math::is_zero_approx(cur_playback_time)) { + if (is_started) { process_state->tree->call_deferred(SNAME("emit_signal"), SceneStringName(animation_started), animation); } // Finished. @@ -282,6 +290,14 @@ bool AnimationNodeAnimation::is_backward() const { return backward; } +void AnimationNodeAnimation::set_advance_on_start(bool p_advance_on_start) { + advance_on_start = p_advance_on_start; +} + +bool AnimationNodeAnimation::is_advance_on_start() const { + return advance_on_start; +} + void AnimationNodeAnimation::set_use_custom_timeline(bool p_use_custom_timeline) { use_custom_timeline = p_use_custom_timeline; notify_property_list_changed(); @@ -331,6 +347,9 @@ void AnimationNodeAnimation::_bind_methods() { ClassDB::bind_method(D_METHOD("set_play_mode", "mode"), &AnimationNodeAnimation::set_play_mode); ClassDB::bind_method(D_METHOD("get_play_mode"), &AnimationNodeAnimation::get_play_mode); + ClassDB::bind_method(D_METHOD("set_advance_on_start", "advance_on_start"), &AnimationNodeAnimation::set_advance_on_start); + ClassDB::bind_method(D_METHOD("is_advance_on_start"), &AnimationNodeAnimation::is_advance_on_start); + ClassDB::bind_method(D_METHOD("set_use_custom_timeline", "use_custom_timeline"), &AnimationNodeAnimation::set_use_custom_timeline); ClassDB::bind_method(D_METHOD("is_using_custom_timeline"), &AnimationNodeAnimation::is_using_custom_timeline); @@ -348,6 +367,7 @@ void AnimationNodeAnimation::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation"), "set_animation", "get_animation"); ADD_PROPERTY(PropertyInfo(Variant::INT, "play_mode", PROPERTY_HINT_ENUM, "Forward,Backward"), "set_play_mode", "get_play_mode"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "advance_on_start"), "set_advance_on_start", "is_advance_on_start"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_custom_timeline"), "set_use_custom_timeline", "is_using_custom_timeline"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "timeline_length", PROPERTY_HINT_RANGE, "0.001,60,0.001,or_greater,or_less,hide_slider,suffix:s"), "set_timeline_length", "get_timeline_length"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stretch_time_scale"), "set_stretch_time_scale", "is_stretching_time_scale"); diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index 2add35d009..5c912f0095 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -38,6 +38,8 @@ class AnimationNodeAnimation : public AnimationRootNode { StringName animation; + bool advance_on_start = false; + bool use_custom_timeline = false; double timeline_length = 1.0; Animation::LoopMode loop_mode = Animation::LOOP_NONE; @@ -72,6 +74,9 @@ public: void set_backward(bool p_backward); bool is_backward() const; + void set_advance_on_start(bool p_advance_on_start); + bool is_advance_on_start() const; + void set_use_custom_timeline(bool p_use_custom_timeline); bool is_using_custom_timeline() const; diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp index 0fa6810d23..b40c677f6e 100644 --- a/scene/animation/animation_mixer.cpp +++ b/scene/animation/animation_mixer.cpp @@ -600,6 +600,22 @@ void AnimationMixer::_init_root_motion_cache() { root_motion_scale_accumulator = Vector3(1, 1, 1); } +void AnimationMixer::_create_track_num_to_track_cashe_for_animation(Ref<Animation> &p_animation) { + ERR_FAIL_COND(animation_track_num_to_track_cashe.has(p_animation)); + LocalVector<TrackCache *> &track_num_to_track_cashe = animation_track_num_to_track_cashe.insert_new(p_animation, LocalVector<TrackCache *>())->value; + const Vector<Animation::Track *> &tracks = p_animation->get_tracks(); + + track_num_to_track_cashe.resize(tracks.size()); + for (int i = 0; i < tracks.size(); i++) { + TrackCache **track_ptr = track_cache.getptr(tracks[i]->thash); + if (track_ptr == nullptr) { + track_num_to_track_cashe[i] = nullptr; + } else { + track_num_to_track_cashe[i] = *track_ptr; + } + } +} + bool AnimationMixer::_update_caches() { setup_pass++; @@ -928,20 +944,9 @@ bool AnimationMixer::_update_caches() { } animation_track_num_to_track_cashe.clear(); - LocalVector<TrackCache *> track_num_to_track_cashe; for (const StringName &E : sname_list) { Ref<Animation> anim = get_animation(E); - const Vector<Animation::Track *> tracks = anim->get_tracks(); - track_num_to_track_cashe.resize(tracks.size()); - for (int i = 0; i < tracks.size(); i++) { - TrackCache **track_ptr = track_cache.getptr(tracks[i]->thash); - if (track_ptr == nullptr) { - track_num_to_track_cashe[i] = nullptr; - } else { - track_num_to_track_cashe[i] = *track_ptr; - } - } - animation_track_num_to_track_cashe.insert(anim, track_num_to_track_cashe); + _create_track_num_to_track_cashe_for_animation(anim); } track_count = idx; @@ -1074,6 +1079,9 @@ void AnimationMixer::blend_capture(double p_delta) { capture_cache.remain -= p_delta * capture_cache.step; if (Animation::is_less_or_equal_approx(capture_cache.remain, 0)) { + if (capture_cache.animation.is_valid()) { + animation_track_num_to_track_cashe.erase(capture_cache.animation); + } capture_cache.clear(); return; } @@ -2205,6 +2213,9 @@ void AnimationMixer::capture(const StringName &p_name, double p_duration, Tween: capture_cache.step = 1.0 / p_duration; capture_cache.trans_type = p_trans_type; capture_cache.ease_type = p_ease_type; + if (capture_cache.animation.is_valid()) { + animation_track_num_to_track_cashe.erase(capture_cache.animation); + } capture_cache.animation.instantiate(); bool is_valid = false; @@ -2228,6 +2239,8 @@ void AnimationMixer::capture(const StringName &p_name, double p_duration, Tween: } if (!is_valid) { capture_cache.clear(); + } else { + _create_track_num_to_track_cashe_for_animation(capture_cache.animation); } } diff --git a/scene/animation/animation_mixer.h b/scene/animation/animation_mixer.h index 1906146c56..3769fa268c 100644 --- a/scene/animation/animation_mixer.h +++ b/scene/animation/animation_mixer.h @@ -321,6 +321,7 @@ protected: void _clear_playing_caches(); void _init_root_motion_cache(); bool _update_caches(); + void _create_track_num_to_track_cashe_for_animation(Ref<Animation> &p_animation); /* ---- Audio ---- */ AudioServer::PlaybackType playback_type; diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index d676e2acf4..f2871cda79 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -371,7 +371,9 @@ AnimationNode::NodeTimeInfo AnimationNode::process(const AnimationMixer::Playbac AnimationMixer::PlaybackInfo pi = p_playback_info; if (p_playback_info.seeked) { - pi.delta = get_node_time_info().position - p_playback_info.time; + if (p_playback_info.is_external_seeking) { + pi.delta = get_node_time_info().position - p_playback_info.time; + } } else { pi.time = get_node_time_info().position + p_playback_info.delta; } diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index eb25a8db1b..5a2a822ff0 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -91,7 +91,7 @@ public: if (Math::is_zero_approx(remain)) { return 0; } - return length - position; + return remain; } }; diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 9eda1a256f..c364888502 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -68,9 +68,9 @@ void FileDialog::_native_popup() { root = OS::get_singleton()->get_user_data_dir(); } if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE_EXTRA)) { - DisplayServer::get_singleton()->file_dialog_with_options_show(get_title(), ProjectSettings::get_singleton()->globalize_path(dir->get_text()), root, file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), filters, _get_options(), callable_mp(this, &FileDialog::_native_dialog_cb_with_options)); + DisplayServer::get_singleton()->file_dialog_with_options_show(get_translated_title(), ProjectSettings::get_singleton()->globalize_path(dir->get_text()), root, file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), filters, _get_options(), callable_mp(this, &FileDialog::_native_dialog_cb_with_options)); } else { - DisplayServer::get_singleton()->file_dialog_show(get_title(), ProjectSettings::get_singleton()->globalize_path(dir->get_text()), file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), filters, callable_mp(this, &FileDialog::_native_dialog_cb)); + DisplayServer::get_singleton()->file_dialog_show(get_translated_title(), ProjectSettings::get_singleton()->globalize_path(dir->get_text()), file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), filters, callable_mp(this, &FileDialog::_native_dialog_cb)); } } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 699277ba41..05e9b84bfa 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -7310,6 +7310,10 @@ void TextEdit::_paste_internal(int p_caret) { } String clipboard = DisplayServer::get_singleton()->clipboard_get(); + if (clipboard.is_empty()) { + // Nothing to paste. + return; + } // Paste a full line. Ignore '\r' characters that may have been added to the clipboard by the OS. if (get_caret_count() == 1 && !has_selection(0) && !cut_copy_line.is_empty() && cut_copy_line == clipboard.replace("\r", "")) { diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 1ee99099ec..e70407f36e 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -330,7 +330,7 @@ void Viewport::_sub_window_update(Window *p_window) { int close_h_ofs = p_window->theme_cache.close_h_offset; int close_v_ofs = p_window->theme_cache.close_v_offset; - TextLine title_text = TextLine(p_window->atr(p_window->get_title()), title_font, font_size); + TextLine title_text = TextLine(p_window->get_translated_title(), title_font, font_size); title_text.set_width(r.size.width - panel->get_minimum_size().x - close_h_ofs); title_text.set_direction(p_window->is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); int x = (r.size.width - title_text.get_size().x) / 2; diff --git a/scene/main/window.cpp b/scene/main/window.cpp index fc2fe4320b..05904fa8f9 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -303,6 +303,11 @@ String Window::get_title() const { return title; } +String Window::get_translated_title() const { + ERR_READ_THREAD_GUARD_V(String()); + return tr_title; +} + void Window::_settings_changed() { if (visible && initial_position != WINDOW_INITIAL_POSITION_ABSOLUTE && is_in_edited_scene_root()) { Size2 screen_size = Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height")); diff --git a/scene/main/window.h b/scene/main/window.h index 0994fc6012..a1d95ab91f 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -274,6 +274,7 @@ public: void set_title(const String &p_title); String get_title() const; + String get_translated_title() const; void set_initial_position(WindowInitialPosition p_initial_position); WindowInitialPosition get_initial_position() const; diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index cc67873b24..ab5de3cb7f 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -5272,14 +5272,13 @@ void RenderingDevice::_wait_for_transfer_worker(TransferWorker *p_transfer_worke p_transfer_worker->operations_processed = p_transfer_worker->operations_submitted; } - if (!p_transfer_worker->texture_barriers.is_empty()) { - MutexLock transfer_worker_lock(transfer_worker_pool_mutex); - _flush_barriers_for_transfer_worker(p_transfer_worker); - } + _flush_barriers_for_transfer_worker(p_transfer_worker); } void RenderingDevice::_flush_barriers_for_transfer_worker(TransferWorker *p_transfer_worker) { + // Caller must have already acquired the mutex for the worker. if (!p_transfer_worker->texture_barriers.is_empty()) { + MutexLock transfer_worker_lock(transfer_worker_pool_texture_barriers_mutex); for (uint32_t i = 0; i < p_transfer_worker->texture_barriers.size(); i++) { transfer_worker_pool_texture_barriers.push_back(p_transfer_worker->texture_barriers[i]); } @@ -5352,8 +5351,11 @@ void RenderingDevice::_submit_transfer_workers(RDD::CommandBufferID p_draw_comma } } } +} - if (p_draw_command_buffer && !transfer_worker_pool_texture_barriers.is_empty()) { +void RenderingDevice::_submit_transfer_barriers(RDD::CommandBufferID p_draw_command_buffer) { + MutexLock transfer_worker_lock(transfer_worker_pool_texture_barriers_mutex); + if (!transfer_worker_pool_texture_barriers.is_empty()) { driver->command_pipeline_barrier(p_draw_command_buffer, RDD::PIPELINE_STAGE_COPY_BIT, RDD::PIPELINE_STAGE_ALL_COMMANDS_BIT, {}, {}, transfer_worker_pool_texture_barriers); transfer_worker_pool_texture_barriers.clear(); } @@ -5953,6 +5955,7 @@ void RenderingDevice::_end_frame() { // The command buffer must be copied into a stack variable as the driver workarounds can change the command buffer in use. RDD::CommandBufferID command_buffer = frames[frame].command_buffer; _submit_transfer_workers(command_buffer); + _submit_transfer_barriers(command_buffer); draw_graph.end(RENDER_GRAPH_REORDER, RENDER_GRAPH_FULL_BARRIERS, command_buffer, frames[frame].command_buffer_pool); driver->command_buffer_end(command_buffer); diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 9939df976f..ccfe51043b 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -1285,6 +1285,7 @@ private: LocalVector<uint32_t> transfer_worker_pool_available_list; LocalVector<RDD::TextureBarrier> transfer_worker_pool_texture_barriers; BinaryMutex transfer_worker_pool_mutex; + BinaryMutex transfer_worker_pool_texture_barriers_mutex; ConditionVariable transfer_worker_pool_condition; TransferWorker *_acquire_transfer_worker(uint32_t p_transfer_size, uint32_t p_required_align, uint32_t &r_staging_offset); @@ -1299,6 +1300,7 @@ private: void _check_transfer_worker_vertex_array(VertexArray *p_vertex_array); void _check_transfer_worker_index_array(IndexArray *p_index_array); void _submit_transfer_workers(RDD::CommandBufferID p_draw_command_buffer = RDD::CommandBufferID()); + void _submit_transfer_barriers(RDD::CommandBufferID p_draw_command_buffer); void _wait_for_transfer_workers(); void _free_transfer_workers(); diff --git a/servers/rendering/rendering_device_graph.cpp b/servers/rendering/rendering_device_graph.cpp index ec2f336f3c..86b5f80e56 100644 --- a/servers/rendering/rendering_device_graph.cpp +++ b/servers/rendering/rendering_device_graph.cpp @@ -140,7 +140,7 @@ RDD::BarrierAccessBits RenderingDeviceGraph::_usage_to_access_bits(ResourceUsage #endif } -bool RenderingDeviceGraph::_check_command_intersection(ResourceTracker *p_resource_tracker, int32_t p_previous_command_index, int32_t p_command_index) const { +bool RenderingDeviceGraph::_check_command_intersection(ResourceTracker *p_resource_tracker, int32_t p_previous_command_index, int32_t p_command_index, bool &r_intersection_partial_coverage) const { if (p_resource_tracker->usage != RESOURCE_USAGE_ATTACHMENT_COLOR_READ_WRITE && p_resource_tracker->usage != RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE) { // We don't check possible intersections for usages that aren't consecutive color or depth writes. return true; @@ -155,6 +155,11 @@ bool RenderingDeviceGraph::_check_command_intersection(ResourceTracker *p_resour return true; } + if (!r_intersection_partial_coverage) { + // Indicate if this draw list only partially covers the region of the previous draw list. + r_intersection_partial_coverage = !current_draw_list_command.region.encloses(previous_draw_list_command.region); + } + // We check if the region used by both draw lists have an intersection. return previous_draw_list_command.region.intersects(current_draw_list_command.region); } @@ -471,7 +476,7 @@ void RenderingDeviceGraph::_add_command_to_graph(ResourceTracker **p_resource_tr resource_tracker->usage = new_resource_usage; } - bool command_intersection_failed = false; + bool intersection_partial_coverage = false; if (search_tracker->write_command_or_list_index >= 0) { if (search_tracker->write_command_list_enabled) { // Make this command adjacent to any commands that wrote to this resource and intersect with the slice if it applies. @@ -483,7 +488,7 @@ void RenderingDeviceGraph::_add_command_to_graph(ResourceTracker **p_resource_tr if (!resource_has_parent || search_tracker_rect.intersects(write_list_node.subresources)) { if (write_list_node.command_index == p_command_index) { ERR_FAIL_COND_MSG(!resource_has_parent, "Command can't have itself as a dependency."); - } else if (_check_command_intersection(resource_tracker, write_list_node.command_index, p_command_index)) { + } else if (_check_command_intersection(resource_tracker, write_list_node.command_index, p_command_index, intersection_partial_coverage)) { // Command is dependent on this command. Add this command to the adjacency list of the write command. _add_adjacent_command(write_list_node.command_index, p_command_index, r_command); @@ -499,8 +504,6 @@ void RenderingDeviceGraph::_add_command_to_graph(ResourceTracker **p_resource_tr write_list_index = write_list_node.next_list_index; continue; } - } else { - command_intersection_failed = true; } } @@ -511,16 +514,14 @@ void RenderingDeviceGraph::_add_command_to_graph(ResourceTracker **p_resource_tr // The index is just the latest command index that wrote to the resource. if (search_tracker->write_command_or_list_index == p_command_index) { ERR_FAIL_MSG("Command can't have itself as a dependency."); - } else if (_check_command_intersection(resource_tracker, search_tracker->write_command_or_list_index, p_command_index)) { + } else if (_check_command_intersection(resource_tracker, search_tracker->write_command_or_list_index, p_command_index, intersection_partial_coverage)) { _add_adjacent_command(search_tracker->write_command_or_list_index, p_command_index, r_command); - } else { - command_intersection_failed = true; } } } if (write_usage) { - if (resource_has_parent || command_intersection_failed) { + if (resource_has_parent || intersection_partial_coverage) { if (!search_tracker->write_command_list_enabled && search_tracker->write_command_or_list_index >= 0) { // Write command list was not being used but there was a write command recorded. Add a new node with the entire parent resource's subresources and the recorded command index to the list. const RDD::TextureSubresourceRange &tracker_subresources = search_tracker->texture_subresources; diff --git a/servers/rendering/rendering_device_graph.h b/servers/rendering/rendering_device_graph.h index 9ddd70bc80..452e1700b6 100644 --- a/servers/rendering/rendering_device_graph.h +++ b/servers/rendering/rendering_device_graph.h @@ -649,7 +649,7 @@ private: static bool _is_write_usage(ResourceUsage p_usage); static RDD::TextureLayout _usage_to_image_layout(ResourceUsage p_usage); static RDD::BarrierAccessBits _usage_to_access_bits(ResourceUsage p_usage); - bool _check_command_intersection(ResourceTracker *p_resource_tracker, int32_t p_previous_command_index, int32_t p_command_index) const; + bool _check_command_intersection(ResourceTracker *p_resource_tracker, int32_t p_previous_command_index, int32_t p_command_index, bool &r_intersection_partial_coverage) const; int32_t _add_to_command_list(int32_t p_command_index, int32_t p_list_index); void _add_adjacent_command(int32_t p_previous_command_index, int32_t p_command_index, RecordedCommand *r_command); int32_t _add_to_slice_read_list(int32_t p_command_index, Rect2i p_subresources, int32_t p_list_index); |