diff options
291 files changed, 2811 insertions, 1356 deletions
diff --git a/.editorconfig b/.editorconfig index 92ee947a82..4bb7553b16 100644 --- a/.editorconfig +++ b/.editorconfig @@ -21,3 +21,13 @@ indent_size = 4 [*.{yml,yaml}] indent_style = space indent_size = 2 + +# GDScript unit test files +[*.gd] +indent_style = tab +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.out] +insert_final_newline = true diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml index d8951ddb78..6c9d8d30f2 100644 --- a/.github/workflows/static_checks.yml +++ b/.github/workflows/static_checks.yml @@ -19,14 +19,14 @@ jobs: sudo rm -f /etc/apt/sources.list.d/* sudo cp -f misc/ci/sources.list /etc/apt/sources.list wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-13 main" + sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-15 main" sudo apt-get update - name: Install dependencies run: | - sudo apt-get install -qq dos2unix recode clang-format-13 libxml2-utils python3-pip moreutils + sudo apt-get install -qq dos2unix recode clang-format-15 libxml2-utils python3-pip moreutils sudo update-alternatives --remove-all clang-format || true - sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-13 100 + sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-15 100 sudo pip3 install black==22.3.0 pygments pytest==7.1.2 mypy==0.971 - name: File formatting checks (file_format.sh) diff --git a/core/core_constants.cpp b/core/core_constants.cpp index 504b343736..edb72f73cb 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -621,14 +621,6 @@ void register_global_constants() { BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_TYPE_STRING); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE); - BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_METHOD_OF_VARIANT_TYPE); - BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_METHOD_OF_BASE_TYPE); - BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_METHOD_OF_INSTANCE); - BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_METHOD_OF_SCRIPT); - BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE); - BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PROPERTY_OF_BASE_TYPE); - BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PROPERTY_OF_INSTANCE); - BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PROPERTY_OF_SCRIPT); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_OBJECT_TOO_BIG); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_PATH_VALID_TYPES); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_SAVE_FILE); @@ -646,10 +638,10 @@ void register_global_constants() { BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NONE); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_STORAGE); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_EDITOR); + BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_INTERNAL); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_CHECKABLE); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_CHECKED); - BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_INTERNATIONALIZED); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_GROUP); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_CATEGORY); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_SUBGROUP); @@ -663,7 +655,7 @@ void register_global_constants() { BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_CLASS_IS_ENUM); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NIL_IS_VARIANT); - BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_INTERNAL); + BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_ARRAY); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_HIGH_END_GFX); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT); @@ -673,10 +665,8 @@ void register_global_constants() { BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_EDITOR_BASIC_SETTING); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_READ_ONLY); - BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_ARRAY); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_DEFAULT); - BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_DEFAULT_INTL); BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NO_EDITOR); BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_NORMAL); diff --git a/core/doc_data.h b/core/doc_data.h index c547138619..1cf4e4f206 100644 --- a/core/doc_data.h +++ b/core/doc_data.h @@ -378,7 +378,6 @@ public: struct ClassDoc { String name; String inherits; - String category; // FIXME: Wrongly used by VisualScriptPropertySelector, should be removed. String brief_description; String description; Vector<TutorialDoc> tutorials; @@ -409,10 +408,6 @@ public: doc.inherits = p_dict["inherits"]; } - if (p_dict.has("category")) { - doc.category = p_dict["category"]; - } - if (p_dict.has("brief_description")) { doc.brief_description = p_dict["brief_description"]; } diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index c9e0c2c638..c256668af0 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -184,6 +184,22 @@ bool FileAccessCompressed::is_open() const { return f.is_valid(); } +String FileAccessCompressed::get_path() const { + if (f.is_valid()) { + return f->get_path(); + } else { + return ""; + } +} + +String FileAccessCompressed::get_path_absolute() const { + if (f.is_valid()) { + return f->get_path_absolute(); + } else { + return ""; + } +} + void FileAccessCompressed::seek(uint64_t p_position) { ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use."); diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h index 53b4887b90..136fcede06 100644 --- a/core/io/file_access_compressed.h +++ b/core/io/file_access_compressed.h @@ -73,6 +73,9 @@ public: 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 + virtual String get_path() const override; /// returns the path for the current open file + virtual String get_path_absolute() const override; /// returns the absolute path for the current open file + virtual void seek(uint64_t p_position) override; ///< seek to a given position virtual void seek_end(int64_t p_position = 0) override; ///< seek from the end of file virtual uint64_t get_position() const override; ///< get position in the file diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index ba6ad16ca8..45e1301930 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -1045,10 +1045,10 @@ void ResourceLoaderBinary::open(Ref<FileAccess> p_f, bool p_no_resources, bool p #ifdef TOOLS_ENABLED // Silence a warning that can happen during the initial filesystem scan due to cache being regenerated. if (ResourceLoader::get_resource_uid(res_path) != er.uid) { - WARN_PRINT(String(res_path + ": In external resource #" + itos(i) + ", invalid UUID: " + ResourceUID::get_singleton()->id_to_text(er.uid) + " - using text path instead: " + er.path).utf8().get_data()); + WARN_PRINT(String(res_path + ": In external resource #" + itos(i) + ", invalid UID: " + ResourceUID::get_singleton()->id_to_text(er.uid) + " - using text path instead: " + er.path).utf8().get_data()); } #else - WARN_PRINT(String(res_path + ": In external resource #" + itos(i) + ", invalid UUID: " + ResourceUID::get_singleton()->id_to_text(er.uid) + " - using text path instead: " + er.path).utf8().get_data()); + WARN_PRINT(String(res_path + ": In external resource #" + itos(i) + ", invalid UID: " + ResourceUID::get_singleton()->id_to_text(er.uid) + " - using text path instead: " + er.path).utf8().get_data()); #endif } } @@ -2209,12 +2209,130 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re return OK; } +Error ResourceFormatSaverBinaryInstance::set_uid(const String &p_path, ResourceUID::ID p_uid) { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, "Cannot open file '" + p_path + "'."); + + Ref<FileAccess> fw; + + local_path = p_path.get_base_dir(); + + uint8_t header[4]; + f->get_buffer(header, 4); + if (header[0] == 'R' && header[1] == 'S' && header[2] == 'C' && header[3] == 'C') { + // Compressed. + Ref<FileAccessCompressed> fac; + fac.instantiate(); + Error err = fac->open_after_magic(f); + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot open file '" + p_path + "'."); + f = fac; + + Ref<FileAccessCompressed> facw; + facw.instantiate(); + facw->configure("RSCC"); + err = facw->open_internal(p_path + ".uidren", FileAccess::WRITE); + ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, "Cannot create file '" + p_path + ".uidren'."); + + fw = facw; + + } else if (header[0] != 'R' || header[1] != 'S' || header[2] != 'R' || header[3] != 'C') { + // Not a binary resource. + return ERR_FILE_UNRECOGNIZED; + } else { + fw = FileAccess::open(p_path + ".uidren", FileAccess::WRITE); + ERR_FAIL_COND_V_MSG(fw.is_null(), ERR_CANT_CREATE, "Cannot create file '" + p_path + ".uidren'."); + + uint8_t magich[4] = { 'R', 'S', 'R', 'C' }; + fw->store_buffer(magich, 4); + } + + big_endian = f->get_32(); + bool use_real64 = f->get_32(); + f->set_big_endian(big_endian != 0); //read big endian if saved as big endian +#ifdef BIG_ENDIAN_ENABLED + fw->store_32(!big_endian); +#else + fw->store_32(big_endian); +#endif + fw->set_big_endian(big_endian != 0); + fw->store_32(use_real64); //use real64 + + uint32_t ver_major = f->get_32(); + uint32_t ver_minor = f->get_32(); + uint32_t ver_format = f->get_32(); + + if (ver_format < FORMAT_VERSION_CAN_RENAME_DEPS) { + fw.unref(); + + { + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + da->remove(p_path + ".uidren"); + } + + // Use the old approach. + + WARN_PRINT("This file is old, so it does not support UIDs, opening and resaving '" + p_path + "'."); + return ERR_UNAVAILABLE; + } + + if (ver_format > FORMAT_VERSION || ver_major > VERSION_MAJOR) { + ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, + vformat("File '%s' can't be loaded, as it uses a format version (%d) or engine version (%d.%d) which are not supported by your engine version (%s).", + local_path, ver_format, ver_major, ver_minor, VERSION_BRANCH)); + } + + // Since we're not actually converting the file contents, leave the version + // numbers in the file untouched. + fw->store_32(ver_major); + fw->store_32(ver_minor); + fw->store_32(ver_format); + + save_ustring(fw, get_ustring(f)); //type + + fw->store_64(f->get_64()); //metadata offset + + uint32_t flags = f->get_32(); + flags |= ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS; + f->get_64(); // Skip previous UID + + fw->store_32(flags); + fw->store_64(p_uid); + + //rest of file + uint8_t b = f->get_8(); + while (!f->eof_reached()) { + fw->store_8(b); + b = f->get_8(); + } + + f.unref(); + + bool all_ok = fw->get_error() == OK; + + if (!all_ok) { + return ERR_CANT_CREATE; + } + + fw.unref(); + + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + da->remove(p_path); + da->rename(p_path + ".uidren", p_path); + return OK; +} + Error ResourceFormatSaverBinary::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) { String local_path = ProjectSettings::get_singleton()->localize_path(p_path); ResourceFormatSaverBinaryInstance saver; return saver.save(local_path, p_resource, p_flags); } +Error ResourceFormatSaverBinary::set_uid(const String &p_path, ResourceUID::ID p_uid) { + String local_path = ProjectSettings::get_singleton()->localize_path(p_path); + ResourceFormatSaverBinaryInstance saver; + return saver.set_uid(local_path, p_uid); +} + bool ResourceFormatSaverBinary::recognize(const Ref<Resource> &p_resource) const { return true; //all recognized } diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h index 36613dbd58..2e8988005f 100644 --- a/core/io/resource_format_binary.h +++ b/core/io/resource_format_binary.h @@ -170,6 +170,7 @@ public: RESERVED_FIELDS = 11 }; Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0); + Error set_uid(const String &p_path, ResourceUID::ID p_uid); static void write_variant(Ref<FileAccess> f, const Variant &p_property, HashMap<Ref<Resource>, int> &resource_map, HashMap<Ref<Resource>, int> &external_resources, HashMap<StringName, int> &string_map, const PropertyInfo &p_hint = PropertyInfo()); }; @@ -177,6 +178,7 @@ class ResourceFormatSaverBinary : public ResourceFormatSaver { public: static ResourceFormatSaverBinary *singleton; virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0); + virtual Error set_uid(const String &p_path, ResourceUID::ID p_uid); virtual bool recognize(const Ref<Resource> &p_resource) const; virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const; diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index a4f4d705ee..dc1de6b9ce 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -31,6 +31,7 @@ #include "resource_importer.h" #include "core/config/project_settings.h" +#include "core/io/config_file.h" #include "core/os/os.h" #include "core/variant/variant_parser.h" @@ -484,3 +485,18 @@ void ResourceFormatImporter::add_importer(const Ref<ResourceImporter> &p_importe importers.push_back(p_importer); } } + +///// + +Error ResourceFormatImporterSaver::set_uid(const String &p_path, ResourceUID::ID p_uid) { + Ref<ConfigFile> cf; + cf.instantiate(); + Error err = cf->load(p_path + ".import"); + if (err != OK) { + return err; + } + cf->set_value("remap", "uid", ResourceUID::get_singleton()->id_to_text(p_uid)); + cf->save(p_path + ".import"); + + return OK; +} diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h index b104a9dffe..0089544caa 100644 --- a/core/io/resource_importer.h +++ b/core/io/resource_importer.h @@ -32,6 +32,7 @@ #define RESOURCE_IMPORTER_H #include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" class ResourceImporter; @@ -149,4 +150,11 @@ public: VARIANT_ENUM_CAST(ResourceImporter::ImportOrder); +class ResourceFormatImporterSaver : public ResourceFormatSaver { + GDCLASS(ResourceFormatImporterSaver, ResourceFormatSaver) + +public: + virtual Error set_uid(const String &p_path, ResourceUID::ID p_uid) override; +}; + #endif // RESOURCE_IMPORTER_H diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index de450a8420..9809b9a48f 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -47,6 +47,12 @@ Error ResourceFormatSaver::save(const Ref<Resource> &p_resource, const String &p return (Error)res; } +Error ResourceFormatSaver::set_uid(const String &p_path, ResourceUID::ID p_uid) { + Error err = ERR_FILE_UNRECOGNIZED; + GDVIRTUAL_CALL(_set_uid, p_path, p_uid, err); + return err; +} + bool ResourceFormatSaver::recognize(const Ref<Resource> &p_resource) const { bool success = false; GDVIRTUAL_CALL(_recognize, p_resource, success); @@ -85,6 +91,7 @@ bool ResourceFormatSaver::recognize_path(const Ref<Resource> &p_resource, const void ResourceFormatSaver::_bind_methods() { GDVIRTUAL_BIND(_save, "resource", "path", "flags"); + GDVIRTUAL_BIND(_set_uid, "path", "uid"); GDVIRTUAL_BIND(_recognize, "resource"); GDVIRTUAL_BIND(_get_recognized_extensions, "resource"); GDVIRTUAL_BIND(_recognize_path, "resource", "path"); @@ -146,6 +153,23 @@ Error ResourceSaver::save(const Ref<Resource> &p_resource, const String &p_path, return err; } +Error ResourceSaver::set_uid(const String &p_path, ResourceUID::ID p_uid) { + String path = p_path; + + ERR_FAIL_COND_V_MSG(path.is_empty(), ERR_INVALID_PARAMETER, "Can't update UID to empty path. Provide non-empty path."); + + Error err = ERR_FILE_UNRECOGNIZED; + + for (int i = 0; i < saver_count; i++) { + err = saver[i]->set_uid(path, p_uid); + if (err == OK) { + break; + } + } + + return err; +} + void ResourceSaver::set_save_callback(ResourceSavedCallback p_callback) { save_callback = p_callback; } diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h index f25463d71f..2043947963 100644 --- a/core/io/resource_saver.h +++ b/core/io/resource_saver.h @@ -42,12 +42,14 @@ protected: static void _bind_methods(); GDVIRTUAL3R(int64_t, _save, Ref<Resource>, String, uint32_t) + GDVIRTUAL2R(Error, _set_uid, String, ResourceUID::ID) GDVIRTUAL1RC(bool, _recognize, Ref<Resource>) GDVIRTUAL1RC(Vector<String>, _get_recognized_extensions, Ref<Resource>) GDVIRTUAL2RC(bool, _recognize_path, Ref<Resource>, String) public: virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0); + virtual Error set_uid(const String &p_path, ResourceUID::ID p_uid); virtual bool recognize(const Ref<Resource> &p_resource) const; virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const; virtual bool recognize_path(const Ref<Resource> &p_resource, const String &p_path) const; @@ -88,6 +90,8 @@ public: static void add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front = false); static void remove_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver); + static Error set_uid(const String &p_path, ResourceUID::ID p_uid); + static void set_timestamp_on_save(bool p_timestamp) { timestamp_on_save = p_timestamp; } static bool get_timestamp_on_save() { return timestamp_on_save; } diff --git a/core/object/object.cpp b/core/object/object.cpp index 0e82440fc0..2cb56dfe6c 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -1055,7 +1055,7 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) { //most likely object is not initialized yet, do not throw error. } else { - ERR_PRINT("Error calling from signal '" + String(p_name) + "' to callable: " + Variant::get_callable_error_text(c.callable, args, argc + c.callable.get_bound_arguments_count(), ce) + "."); + ERR_PRINT("Error calling from signal '" + String(p_name) + "' to callable: " + Variant::get_callable_error_text(c.callable, args, argc, ce) + "."); err = ERR_METHOD_NOT_FOUND; } } @@ -1594,25 +1594,6 @@ bool Object::is_blocking_signals() const { return _block_signals; } -void Object::get_translatable_strings(List<String> *p_strings) const { - List<PropertyInfo> plist; - get_property_list(&plist); - - for (const PropertyInfo &E : plist) { - if (!(E.usage & PROPERTY_USAGE_INTERNATIONALIZED)) { - continue; - } - - String text = get(E.name); - - if (text.is_empty()) { - continue; - } - - p_strings->push_back(text); - } -} - Variant::Type Object::get_static_property_type(const StringName &p_property, bool *r_valid) const { bool valid; Variant::Type t = ClassDB::get_property_type(get_class_name(), p_property, &valid); diff --git a/core/object/object.h b/core/object/object.h index 2ba86c9d31..f78c7c34fd 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -74,14 +74,6 @@ enum PropertyHint { PROPERTY_HINT_OBJECT_ID, PROPERTY_HINT_TYPE_STRING, ///< a type string, the hint is the base type to choose PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE, ///< so something else can provide this (used in scripts) - PROPERTY_HINT_METHOD_OF_VARIANT_TYPE, ///< a method of a type - PROPERTY_HINT_METHOD_OF_BASE_TYPE, ///< a method of a base type - PROPERTY_HINT_METHOD_OF_INSTANCE, ///< a method of an instance - PROPERTY_HINT_METHOD_OF_SCRIPT, ///< a method of a script & base - PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE, ///< a property of a type - PROPERTY_HINT_PROPERTY_OF_BASE_TYPE, ///< a property of a base type - PROPERTY_HINT_PROPERTY_OF_INSTANCE, ///< a property of an instance - PROPERTY_HINT_PROPERTY_OF_SCRIPT, ///< a property of a script & base PROPERTY_HINT_OBJECT_TOO_BIG, ///< object is too big to send PROPERTY_HINT_NODE_PATH_VALID_TYPES, PROPERTY_HINT_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog @@ -95,16 +87,15 @@ enum PropertyHint { PROPERTY_HINT_HIDE_QUATERNION_EDIT, /// Only Node3D::transform should hide the quaternion editor. PROPERTY_HINT_PASSWORD, PROPERTY_HINT_MAX, - // When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit }; enum PropertyUsageFlags { PROPERTY_USAGE_NONE = 0, PROPERTY_USAGE_STORAGE = 1 << 1, PROPERTY_USAGE_EDITOR = 1 << 2, - PROPERTY_USAGE_CHECKABLE = 1 << 3, // Used for editing global variables. - PROPERTY_USAGE_CHECKED = 1 << 4, // Used for editing global variables. - PROPERTY_USAGE_INTERNATIONALIZED = 1 << 5, // Hint for internationalized strings. + PROPERTY_USAGE_INTERNAL = 1 << 3, + PROPERTY_USAGE_CHECKABLE = 1 << 4, // Used for editing global variables. + PROPERTY_USAGE_CHECKED = 1 << 5, // Used for editing global variables. PROPERTY_USAGE_GROUP = 1 << 6, // Used for grouping props in the editor. PROPERTY_USAGE_CATEGORY = 1 << 7, PROPERTY_USAGE_SUBGROUP = 1 << 8, @@ -117,7 +108,7 @@ enum PropertyUsageFlags { PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE = 1 << 15, PROPERTY_USAGE_CLASS_IS_ENUM = 1 << 16, PROPERTY_USAGE_NIL_IS_VARIANT = 1 << 17, - PROPERTY_USAGE_INTERNAL = 1 << 18, + PROPERTY_USAGE_ARRAY = 1 << 18, // Used in the inspector to group properties as elements of an array. PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE = 1 << 19, // If the object is duplicated also this property will be duplicated. PROPERTY_USAGE_HIGH_END_GFX = 1 << 20, PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT = 1 << 21, @@ -127,10 +118,8 @@ enum PropertyUsageFlags { PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 25, // For Object properties, instantiate them when creating in editor. PROPERTY_USAGE_EDITOR_BASIC_SETTING = 1 << 26, //for project or editor settings, show when basic settings are selected. PROPERTY_USAGE_READ_ONLY = 1 << 27, // Mark a property as read-only in the inspector. - PROPERTY_USAGE_ARRAY = 1 << 28, // Used in the inspector to group properties as elements of an array. PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR, - PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNATIONALIZED, PROPERTY_USAGE_NO_EDITOR = PROPERTY_USAGE_STORAGE, }; @@ -894,8 +883,6 @@ public: Variant::Type get_static_property_type(const StringName &p_property, bool *r_valid = nullptr) const; Variant::Type get_static_property_type_indexed(const Vector<StringName> &p_path, bool *r_valid = nullptr) const; - virtual void get_translatable_strings(List<String> *p_strings) const; - virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const; // Translate message (internationalization). diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 81866ee7e4..700174bdae 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -84,6 +84,7 @@ static Ref<ResourceFormatSaverBinary> resource_saver_binary; static Ref<ResourceFormatLoaderBinary> resource_loader_binary; static Ref<ResourceFormatImporter> resource_format_importer; +static Ref<ResourceFormatImporterSaver> resource_format_importer_saver; static Ref<ResourceFormatLoaderImage> resource_format_image; static Ref<TranslationLoaderPO> resource_format_po; static Ref<ResourceFormatSaverCrypto> resource_format_saver_crypto; @@ -144,6 +145,9 @@ void register_core_types() { resource_format_importer.instantiate(); ResourceLoader::add_resource_format_loader(resource_format_importer); + resource_format_importer_saver.instantiate(); + ResourceSaver::add_resource_format_saver(resource_format_importer_saver); + resource_format_image.instantiate(); ResourceLoader::add_resource_format_loader(resource_format_image); @@ -389,6 +393,9 @@ void unregister_core_types() { ResourceLoader::remove_resource_format_loader(resource_format_importer); resource_format_importer.unref(); + ResourceSaver::remove_resource_format_saver(resource_format_importer_saver); + resource_format_importer_saver.unref(); + ResourceLoader::remove_resource_format_loader(resource_format_po); resource_format_po.unref(); diff --git a/core/variant/array.cpp b/core/variant/array.cpp index 0fecc2fe94..f8af78f3c1 100644 --- a/core/variant/array.cpp +++ b/core/variant/array.cpp @@ -54,16 +54,6 @@ void Array::_ref(const Array &p_from) const { ERR_FAIL_COND(!_fp); // should NOT happen. - if (unlikely(_fp->read_only != nullptr)) { - // If p_from is a read-only array, just copy the contents to avoid further modification. - _unref(); - _p = memnew(ArrayPrivate); - _p->refcount.init(); - _p->array = _fp->array; - _p->typed = _fp->typed; - return; - } - if (_fp == _p) { return; // whatever it is, nothing to do here move along } diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp index fd85fe78f6..2f2acc55a6 100644 --- a/core/variant/callable.cpp +++ b/core/variant/callable.cpp @@ -117,6 +117,7 @@ Callable Callable::bindv(const Array &p_arguments) { } Callable Callable::unbind(int p_argcount) const { + ERR_FAIL_COND_V_MSG(p_argcount <= 0, Callable(*this), "Amount of unbind() arguments must be 1 or greater."); return Callable(memnew(CallableCustomUnbind(*this, p_argcount))); } @@ -159,6 +160,27 @@ int Callable::get_bound_arguments_count() const { } } +void Callable::get_bound_arguments_ref(Vector<Variant> &r_arguments, int &r_argcount) const { + if (!is_null() && is_custom()) { + custom->get_bound_arguments(r_arguments, r_argcount); + } else { + r_arguments.clear(); + r_argcount = 0; + } +} + +Array Callable::get_bound_arguments() const { + Vector<Variant> arr; + int ac; + get_bound_arguments_ref(arr, ac); + Array ret; + ret.resize(arr.size()); + for (int i = 0; i < arr.size(); i++) { + ret[i] = arr[i]; + } + return ret; +} + CallableCustom *Callable::get_custom() const { ERR_FAIL_COND_V_MSG(!is_custom(), nullptr, vformat("Can't get custom on non-CallableCustom \"%s\".", operator String())); @@ -370,6 +392,11 @@ int CallableCustom::get_bound_arguments_count() const { return 0; } +void CallableCustom::get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const { + r_arguments = Vector<Variant>(); + r_argcount = 0; +} + CallableCustom::CallableCustom() { ref_count.init(); } diff --git a/core/variant/callable.h b/core/variant/callable.h index 10d291ac24..0abbb64c0b 100644 --- a/core/variant/callable.h +++ b/core/variant/callable.h @@ -108,6 +108,8 @@ public: StringName get_method() const; CallableCustom *get_custom() const; int get_bound_arguments_count() const; + void get_bound_arguments_ref(Vector<Variant> &r_arguments, int &r_argcount) const; // Internal engine use, the exposed one is below. + Array get_bound_arguments() const; uint32_t hash() const; @@ -149,6 +151,7 @@ public: virtual Error rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const; virtual const Callable *get_base_comparator() const; virtual int get_bound_arguments_count() const; + virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const; CallableCustom(); virtual ~CallableCustom() {} diff --git a/core/variant/callable_bind.cpp b/core/variant/callable_bind.cpp index 83035dc70d..5be91c6e11 100644 --- a/core/variant/callable_bind.cpp +++ b/core/variant/callable_bind.cpp @@ -91,6 +91,43 @@ int CallableCustomBind::get_bound_arguments_count() const { return callable.get_bound_arguments_count() + binds.size(); } +void CallableCustomBind::get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const { + Vector<Variant> sub_args; + int sub_count; + callable.get_bound_arguments_ref(sub_args, sub_count); + + if (sub_count == 0) { + r_arguments = binds; + r_argcount = binds.size(); + return; + } + + int new_count = sub_count + binds.size(); + r_argcount = new_count; + + if (new_count <= 0) { + // Removed more arguments than it adds. + r_arguments = Vector<Variant>(); + return; + } + + r_arguments.resize(new_count); + + if (sub_count > 0) { + for (int i = 0; i < sub_count; i++) { + r_arguments.write[i] = sub_args[i]; + } + for (int i = 0; i < binds.size(); i++) { + r_arguments.write[i + sub_count] = binds[i]; + } + r_argcount = new_count; + } else { + for (int i = 0; i < binds.size() + sub_count; i++) { + r_arguments.write[i] = binds[i - sub_count]; + } + } +} + void CallableCustomBind::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { const Variant **args = (const Variant **)alloca(sizeof(const Variant **) * (binds.size() + p_argcount)); for (int i = 0; i < p_argcount; i++) { @@ -172,6 +209,21 @@ int CallableCustomUnbind::get_bound_arguments_count() const { return callable.get_bound_arguments_count() - argcount; } +void CallableCustomUnbind::get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const { + Vector<Variant> sub_args; + int sub_count; + callable.get_bound_arguments_ref(sub_args, sub_count); + + r_argcount = sub_args.size() - argcount; + + if (argcount >= sub_args.size()) { + r_arguments = Vector<Variant>(); + } else { + sub_args.resize(sub_args.size() - argcount); + r_arguments = sub_args; + } +} + void CallableCustomUnbind::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { if (argcount > p_argcount) { r_call_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; diff --git a/core/variant/callable_bind.h b/core/variant/callable_bind.h index a79a521b5b..278ed335d0 100644 --- a/core/variant/callable_bind.h +++ b/core/variant/callable_bind.h @@ -52,6 +52,7 @@ public: virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; virtual const Callable *get_base_comparator() const override; virtual int get_bound_arguments_count() const override; + virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const override; Callable get_callable() { return callable; } Vector<Variant> get_binds() { return binds; } @@ -77,6 +78,7 @@ public: virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; virtual const Callable *get_base_comparator() const override; virtual int get_bound_arguments_count() const override; + virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const override; Callable get_callable() { return callable; } int get_unbinds() { return argcount; } diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp index c545109bd8..f87064a0d1 100644 --- a/core/variant/dictionary.cpp +++ b/core/variant/dictionary.cpp @@ -211,16 +211,6 @@ bool Dictionary::recursive_equal(const Dictionary &p_dictionary, int recursion_c } void Dictionary::_ref(const Dictionary &p_from) const { - if (unlikely(p_from._p->read_only != nullptr)) { - // If p_from is a read-only dictionary, just copy the contents to avoid further modification. - if (_p) { - _unref(); - } - _p = memnew(DictionaryPrivate); - _p->refcount.init(); - _p->variant_map = p_from._p->variant_map; - return; - } //make a copy first (thread safe) if (!p_from._p->refcount.ref()) { return; // couldn't copy diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index bff6656a88..ca42738b05 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -3654,7 +3654,22 @@ String Variant::get_call_error_text(Object *p_base, const StringName &p_method, } String Variant::get_callable_error_text(const Callable &p_callable, const Variant **p_argptrs, int p_argcount, const Callable::CallError &ce) { - return get_call_error_text(p_callable.get_object(), p_callable.get_method(), p_argptrs, p_argcount, ce); + Vector<Variant> binds; + int args_bound; + p_callable.get_bound_arguments_ref(binds, args_bound); + if (args_bound <= 0) { + return get_call_error_text(p_callable.get_object(), p_callable.get_method(), p_argptrs, MAX(0, p_argcount + args_bound), ce); + } else { + Vector<const Variant *> argptrs; + argptrs.resize(p_argcount + binds.size()); + for (int i = 0; i < p_argcount; i++) { + argptrs.write[i] = p_argptrs[i]; + } + for (int i = 0; i < binds.size(); i++) { + argptrs.write[i + p_argcount] = &binds[i]; + } + return get_call_error_text(p_callable.get_object(), p_callable.get_method(), (const Variant **)argptrs.ptr(), argptrs.size(), ce); + } } void Variant::register_types() { diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 6c06b63dab..05fb62ff12 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -2017,6 +2017,7 @@ static void _register_variant_builtin_methods() { bind_method(Callable, get_object_id, sarray(), varray()); bind_method(Callable, get_method, sarray(), varray()); bind_method(Callable, get_bound_arguments_count, sarray(), varray()); + bind_method(Callable, get_bound_arguments, sarray(), varray()); bind_method(Callable, hash, sarray(), varray()); bind_method(Callable, bindv, sarray("arguments"), varray()); bind_method(Callable, unbind, sarray("argcount"), varray()); diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index bf3ef7c7f9..41836650cd 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -2794,51 +2794,35 @@ </constant> <constant name="PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE" value="24" enum="PropertyHint"> </constant> - <constant name="PROPERTY_HINT_METHOD_OF_VARIANT_TYPE" value="25" enum="PropertyHint"> + <constant name="PROPERTY_HINT_OBJECT_TOO_BIG" value="25" enum="PropertyHint"> </constant> - <constant name="PROPERTY_HINT_METHOD_OF_BASE_TYPE" value="26" enum="PropertyHint"> + <constant name="PROPERTY_HINT_NODE_PATH_VALID_TYPES" value="26" enum="PropertyHint"> </constant> - <constant name="PROPERTY_HINT_METHOD_OF_INSTANCE" value="27" enum="PropertyHint"> + <constant name="PROPERTY_HINT_SAVE_FILE" value="27" enum="PropertyHint"> </constant> - <constant name="PROPERTY_HINT_METHOD_OF_SCRIPT" value="28" enum="PropertyHint"> + <constant name="PROPERTY_HINT_GLOBAL_SAVE_FILE" value="28" enum="PropertyHint"> </constant> - <constant name="PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE" value="29" enum="PropertyHint"> + <constant name="PROPERTY_HINT_INT_IS_OBJECTID" value="29" enum="PropertyHint"> </constant> - <constant name="PROPERTY_HINT_PROPERTY_OF_BASE_TYPE" value="30" enum="PropertyHint"> + <constant name="PROPERTY_HINT_INT_IS_POINTER" value="30" enum="PropertyHint"> </constant> - <constant name="PROPERTY_HINT_PROPERTY_OF_INSTANCE" value="31" enum="PropertyHint"> + <constant name="PROPERTY_HINT_ARRAY_TYPE" value="31" enum="PropertyHint"> </constant> - <constant name="PROPERTY_HINT_PROPERTY_OF_SCRIPT" value="32" enum="PropertyHint"> - </constant> - <constant name="PROPERTY_HINT_OBJECT_TOO_BIG" value="33" enum="PropertyHint"> - </constant> - <constant name="PROPERTY_HINT_NODE_PATH_VALID_TYPES" value="34" enum="PropertyHint"> - </constant> - <constant name="PROPERTY_HINT_SAVE_FILE" value="35" enum="PropertyHint"> - </constant> - <constant name="PROPERTY_HINT_GLOBAL_SAVE_FILE" value="36" enum="PropertyHint"> - </constant> - <constant name="PROPERTY_HINT_INT_IS_OBJECTID" value="37" enum="PropertyHint"> - </constant> - <constant name="PROPERTY_HINT_INT_IS_POINTER" value="38" enum="PropertyHint"> - </constant> - <constant name="PROPERTY_HINT_ARRAY_TYPE" value="39" enum="PropertyHint"> - </constant> - <constant name="PROPERTY_HINT_LOCALE_ID" value="40" enum="PropertyHint"> + <constant name="PROPERTY_HINT_LOCALE_ID" value="32" enum="PropertyHint"> Hints that a string property is a locale code. Editing it will show a locale dialog for picking language and country. </constant> - <constant name="PROPERTY_HINT_LOCALIZABLE_STRING" value="41" enum="PropertyHint"> + <constant name="PROPERTY_HINT_LOCALIZABLE_STRING" value="33" enum="PropertyHint"> Hints that a dictionary property is string translation map. Dictionary keys are locale codes and, values are translated strings. </constant> - <constant name="PROPERTY_HINT_NODE_TYPE" value="42" enum="PropertyHint"> + <constant name="PROPERTY_HINT_NODE_TYPE" value="34" enum="PropertyHint"> </constant> - <constant name="PROPERTY_HINT_HIDE_QUATERNION_EDIT" value="43" enum="PropertyHint"> + <constant name="PROPERTY_HINT_HIDE_QUATERNION_EDIT" value="35" enum="PropertyHint"> Hints that a quaternion property should disable the temporary euler editor. </constant> - <constant name="PROPERTY_HINT_PASSWORD" value="44" enum="PropertyHint"> + <constant name="PROPERTY_HINT_PASSWORD" value="36" enum="PropertyHint"> Hints that a string property is a password, and every character is replaced with the secret character. </constant> - <constant name="PROPERTY_HINT_MAX" value="45" enum="PropertyHint"> + <constant name="PROPERTY_HINT_MAX" value="37" enum="PropertyHint"> </constant> <constant name="PROPERTY_USAGE_NONE" value="0" enum="PropertyUsageFlags" is_bitfield="true"> The property is not stored, and does not display in the editor. This is the default for non-exported properties. @@ -2849,15 +2833,14 @@ <constant name="PROPERTY_USAGE_EDITOR" value="4" enum="PropertyUsageFlags" is_bitfield="true"> The property is shown in the [EditorInspector] (default). </constant> - <constant name="PROPERTY_USAGE_CHECKABLE" value="8" enum="PropertyUsageFlags" is_bitfield="true"> + <constant name="PROPERTY_USAGE_INTERNAL" value="8" enum="PropertyUsageFlags" is_bitfield="true"> + </constant> + <constant name="PROPERTY_USAGE_CHECKABLE" value="16" enum="PropertyUsageFlags" is_bitfield="true"> The property can be checked in the [EditorInspector]. </constant> - <constant name="PROPERTY_USAGE_CHECKED" value="16" enum="PropertyUsageFlags" is_bitfield="true"> + <constant name="PROPERTY_USAGE_CHECKED" value="32" enum="PropertyUsageFlags" is_bitfield="true"> The property is checked in the [EditorInspector]. </constant> - <constant name="PROPERTY_USAGE_INTERNATIONALIZED" value="32" enum="PropertyUsageFlags" is_bitfield="true"> - The property is a translatable string. - </constant> <constant name="PROPERTY_USAGE_GROUP" value="64" enum="PropertyUsageFlags" is_bitfield="true"> Used to group properties together in the editor. See [EditorInspector]. </constant> @@ -2888,7 +2871,8 @@ </constant> <constant name="PROPERTY_USAGE_NIL_IS_VARIANT" value="131072" enum="PropertyUsageFlags" is_bitfield="true"> </constant> - <constant name="PROPERTY_USAGE_INTERNAL" value="262144" enum="PropertyUsageFlags" is_bitfield="true"> + <constant name="PROPERTY_USAGE_ARRAY" value="262144" enum="PropertyUsageFlags" is_bitfield="true"> + The property is an array. </constant> <constant name="PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE" value="524288" enum="PropertyUsageFlags" is_bitfield="true"> If the property is a [Resource], a new copy of it is always created when calling [method Node.duplicate] or [method Resource.duplicate]. @@ -2911,15 +2895,9 @@ <constant name="PROPERTY_USAGE_READ_ONLY" value="134217728" enum="PropertyUsageFlags" is_bitfield="true"> The property is read-only in the [EditorInspector]. </constant> - <constant name="PROPERTY_USAGE_ARRAY" value="268435456" enum="PropertyUsageFlags" is_bitfield="true"> - The property is an array. - </constant> <constant name="PROPERTY_USAGE_DEFAULT" value="6" enum="PropertyUsageFlags" is_bitfield="true"> Default usage (storage, editor and network). </constant> - <constant name="PROPERTY_USAGE_DEFAULT_INTL" value="38" enum="PropertyUsageFlags" is_bitfield="true"> - Default usage for translatable strings (storage, editor, network and internationalized). - </constant> <constant name="PROPERTY_USAGE_NO_EDITOR" value="2" enum="PropertyUsageFlags" is_bitfield="true"> Default usage but without showing the property in the editor (storage, network). </constant> diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index ee21493434..21ccf79fe2 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -244,7 +244,7 @@ <param index="0" name="deep" type="bool" default="false" /> <description> Returns a copy of the array. - If [param deep] is [code]true[/code], a deep copy is performed: all nested arrays and dictionaries are duplicated and will not be shared with the original array. If [code]false[/code], a shallow copy is made and references to the original nested arrays and dictionaries are kept, so that modifying a sub-array or dictionary in the copy will also impact those referenced in the source array. + If [param deep] is [code]true[/code], a deep copy is performed: all nested arrays and dictionaries are duplicated and will not be shared with the original array. If [code]false[/code], a shallow copy is made and references to the original nested arrays and dictionaries are kept, so that modifying a sub-array or dictionary in the copy will also impact those referenced in the source array. Note that any [Object]-derived elements will be shallow copied regardless of the [param deep] setting. </description> </method> <method name="erase"> diff --git a/doc/classes/Callable.xml b/doc/classes/Callable.xml index a6fffae8b5..d1fdaef29c 100644 --- a/doc/classes/Callable.xml +++ b/doc/classes/Callable.xml @@ -107,6 +107,12 @@ Calls the method represented by this [Callable]. Unlike [method call], this method expects all arguments to be contained inside the [param arguments] [Array]. </description> </method> + <method name="get_bound_arguments" qualifiers="const"> + <return type="Array" /> + <description> + Return the bound arguments (as long as [method get_bound_arguments_count] is greater than zero), or empty (if [method get_bound_arguments_count] is less than or equal to zero). + </description> + </method> <method name="get_bound_arguments_count" qualifiers="const"> <return type="int" /> <description> diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index b9f3275dfe..08964cf21d 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -780,67 +780,13 @@ </method> <method name="set_drag_forwarding"> <return type="void" /> - <param index="0" name="target" type="Object" /> + <param index="0" name="drag_func" type="Callable" /> + <param index="1" name="can_drop_func" type="Callable" /> + <param index="2" name="drop_func" type="Callable" /> <description> - Forwards the handling of this control's drag and drop to [param target] object. - Forwarding can be implemented in the target object similar to the methods [method _get_drag_data], [method _can_drop_data], and [method _drop_data] but with two differences: - 1. The function name must be suffixed with [b]_fw[/b] - 2. The function must take an extra argument that is the control doing the forwarding - [codeblocks] - [gdscript] - # ThisControl.gd - extends Control - export(Control) var target_control - - func _ready(): - set_drag_forwarding(target_control) - - # TargetControl.gd - extends Control - - func _can_drop_data_fw(position, data, from_control): - return true - - func _drop_data_fw(position, data, from_control): - my_handle_data(data) # Your handler method. - - func _get_drag_data_fw(position, from_control): - set_drag_preview(my_preview) - return my_data() - [/gdscript] - [csharp] - // ThisControl.cs - public class ThisControl : Control - { - [Export] - public Control TargetControl { get; set; } - public override void _Ready() - { - SetDragForwarding(TargetControl); - } - } - - // TargetControl.cs - public class TargetControl : Control - { - public void CanDropDataFw(Vector2 position, object data, Control fromControl) - { - return true; - } - - public void DropDataFw(Vector2 position, object data, Control fromControl) - { - MyHandleData(data); // Your handler method. - } - - public void GetDragDataFw(Vector2 position, Control fromControl) - { - SetDragPreview(MyPreview); - return MyData(); - } - } - [/csharp] - [/codeblocks] + Forwards the handling of this control's [method _get_drag_data], [method _can_drop_data] and [method _drop_data] virtual functions to delegate callables. + For each argument, if not empty, the delegate callable is used, otherwise the local (virtual) function is used. + The function format for each callable should be exactly the same as the virtual functions described above. </description> </method> <method name="set_drag_preview"> diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 779c15c713..5da6cf8102 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -1525,7 +1525,7 @@ Makes the mouse cursor hidden if it is visible. </constant> <constant name="MOUSE_MODE_CAPTURED" value="2" enum="MouseMode"> - Captures the mouse. The mouse will be hidden and its position locked at the center of the screen. + Captures the mouse. The mouse will be hidden and its position locked at the center of the window manager's window. [b]Note:[/b] If you want to process the mouse's movement in this mode, you need to use [member InputEventMouseMotion.relative]. </constant> <constant name="MOUSE_MODE_CONFINED" value="3" enum="MouseMode"> diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index a2948697fb..a0d2d93a7d 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -397,7 +397,7 @@ Makes the mouse cursor hidden if it is visible. </constant> <constant name="MOUSE_MODE_CAPTURED" value="2" enum="MouseMode"> - Captures the mouse. The mouse will be hidden and its position locked at the center of the screen. + Captures the mouse. The mouse will be hidden and its position locked at the center of the window manager's window. [b]Note:[/b] If you want to process the mouse's movement in this mode, you need to use [member InputEventMouseMotion.relative]. </constant> <constant name="MOUSE_MODE_CONFINED" value="3" enum="MouseMode"> diff --git a/doc/classes/InputEventAction.xml b/doc/classes/InputEventAction.xml index 87e2b8ea20..3816847804 100644 --- a/doc/classes/InputEventAction.xml +++ b/doc/classes/InputEventAction.xml @@ -5,6 +5,7 @@ </brief_description> <description> Contains a generic action which can be targeted from several types of inputs. Actions can be created from the [b]Input Map[/b] tab in the [b]Project > Project Settings[/b] menu. See [method Node._input]. + [b]Note:[/b] Unlike the other [InputEvent] subclasses which map to unique physical events, this virtual one is not emitted by the engine. This class is useful to emit actions manually with [method Input.parse_input_event], which are then received in [method Node._input]. To check if a physical event matches an action from the Input Map, use [method InputEvent.is_action] and [method InputEvent.is_action_pressed]. </description> <tutorials> <link title="InputEvent: Actions">$DOCS_URL/tutorials/inputs/inputevent.html#actions</link> diff --git a/doc/classes/JavaScriptBridge.xml b/doc/classes/JavaScriptBridge.xml index 5e36b5cc80..340c296eef 100644 --- a/doc/classes/JavaScriptBridge.xml +++ b/doc/classes/JavaScriptBridge.xml @@ -46,6 +46,13 @@ If [param use_global_execution_context] is [code]true[/code], the code will be evaluated in the global execution context. Otherwise, it is evaluated in the execution context of a function within the engine's runtime environment. </description> </method> + <method name="force_fs_sync"> + <return type="void" /> + <description> + Force synchronization of the persistent file system (when enabled). + [b]Note:[/b] This is only useful for modules or extensions that can't use [FileAccess] to write files. + </description> + </method> <method name="get_interface"> <return type="JavaScriptObject" /> <param index="0" name="interface" type="String" /> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 0ccc4155b4..02fd6dae30 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -421,6 +421,12 @@ Returns the node's [Viewport]. </description> </method> + <method name="get_window" qualifiers="const"> + <return type="Window" /> + <description> + Returns the [Window] that contains this node. If the node is in the main window, this is equivalent to getting the root node ([code]get_tree().get_root()[/code]). + </description> + </method> <method name="has_node" qualifiers="const"> <return type="bool" /> <param index="0" name="path" type="NodePath" /> diff --git a/doc/classes/PhysicsDirectBodyState2D.xml b/doc/classes/PhysicsDirectBodyState2D.xml index eca6a1cbc7..a46de4c189 100644 --- a/doc/classes/PhysicsDirectBodyState2D.xml +++ b/doc/classes/PhysicsDirectBodyState2D.xml @@ -151,6 +151,13 @@ [b]Note:[/b] By default, this returns 0 unless bodies are configured to monitor contacts. See [member RigidBody2D.contact_monitor]. </description> </method> + <method name="get_contact_impulse" qualifiers="const"> + <return type="Vector2" /> + <param index="0" name="contact_idx" type="int" /> + <description> + Returns the impulse created by the contact. + </description> + </method> <method name="get_contact_local_normal" qualifiers="const"> <return type="Vector2" /> <param index="0" name="contact_idx" type="int" /> diff --git a/doc/classes/PhysicsDirectBodyState2DExtension.xml b/doc/classes/PhysicsDirectBodyState2DExtension.xml index 8fd34c1243..496cbf9136 100644 --- a/doc/classes/PhysicsDirectBodyState2DExtension.xml +++ b/doc/classes/PhysicsDirectBodyState2DExtension.xml @@ -130,6 +130,12 @@ <description> </description> </method> + <method name="_get_contact_impulse" qualifiers="virtual const"> + <return type="Vector2" /> + <param index="0" name="contact_idx" type="int" /> + <description> + </description> + </method> <method name="_get_contact_local_normal" qualifiers="virtual const"> <return type="Vector2" /> <param index="0" name="contact_idx" type="int" /> diff --git a/doc/classes/PhysicsDirectSpaceState2DExtension.xml b/doc/classes/PhysicsDirectSpaceState2DExtension.xml index 3235793853..fbbb98a959 100644 --- a/doc/classes/PhysicsDirectSpaceState2DExtension.xml +++ b/doc/classes/PhysicsDirectSpaceState2DExtension.xml @@ -87,5 +87,11 @@ <description> </description> </method> + <method name="is_body_excluded_from_query" qualifiers="const"> + <return type="bool" /> + <param index="0" name="body" type="RID" /> + <description> + </description> + </method> </methods> </class> diff --git a/doc/classes/PhysicsDirectSpaceState3DExtension.xml b/doc/classes/PhysicsDirectSpaceState3DExtension.xml index 98593012db..4297846e53 100644 --- a/doc/classes/PhysicsDirectSpaceState3DExtension.xml +++ b/doc/classes/PhysicsDirectSpaceState3DExtension.xml @@ -95,5 +95,11 @@ <description> </description> </method> + <method name="is_body_excluded_from_query" qualifiers="const"> + <return type="bool" /> + <param index="0" name="body" type="RID" /> + <description> + </description> + </method> </methods> </class> diff --git a/doc/classes/PhysicsServer2DExtension.xml b/doc/classes/PhysicsServer2DExtension.xml index 9bb11e0d89..7ae27c3f32 100644 --- a/doc/classes/PhysicsServer2DExtension.xml +++ b/doc/classes/PhysicsServer2DExtension.xml @@ -933,5 +933,17 @@ <description> </description> </method> + <method name="body_test_motion_is_excluding_body" qualifiers="const"> + <return type="bool" /> + <param index="0" name="body" type="RID" /> + <description> + </description> + </method> + <method name="body_test_motion_is_excluding_object" qualifiers="const"> + <return type="bool" /> + <param index="0" name="object" type="int" /> + <description> + </description> + </method> </methods> </class> diff --git a/doc/classes/PhysicsServer3DExtension.xml b/doc/classes/PhysicsServer3DExtension.xml index d45cb17510..5fe616b948 100644 --- a/doc/classes/PhysicsServer3DExtension.xml +++ b/doc/classes/PhysicsServer3DExtension.xml @@ -1284,5 +1284,17 @@ <description> </description> </method> + <method name="body_test_motion_is_excluding_body" qualifiers="const"> + <return type="bool" /> + <param index="0" name="body" type="RID" /> + <description> + </description> + </method> + <method name="body_test_motion_is_excluding_object" qualifiers="const"> + <return type="bool" /> + <param index="0" name="object" type="int" /> + <description> + </description> + </method> </methods> </class> diff --git a/doc/classes/ResourceFormatSaver.xml b/doc/classes/ResourceFormatSaver.xml index 1f2af6d157..b0c57bc7cb 100644 --- a/doc/classes/ResourceFormatSaver.xml +++ b/doc/classes/ResourceFormatSaver.xml @@ -43,5 +43,13 @@ Returns [constant OK] on success, or an [enum Error] constant in case of failure. </description> </method> + <method name="_set_uid" qualifiers="virtual"> + <return type="int" enum="Error" /> + <param index="0" name="path" type="String" /> + <param index="1" name="uid" type="int" /> + <description> + Sets a new UID for the resource at the given [param path]. Returns [constant OK] on success, or an [enum Error] constant in case of failure. + </description> + </method> </methods> </class> diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml index bd5b656e1a..bf19ebc23a 100644 --- a/doc/classes/SceneTree.xml +++ b/doc/classes/SceneTree.xml @@ -42,15 +42,15 @@ <description> Changes the running scene to the one at the given [param path], after loading it into a [PackedScene] and creating a new instance. Returns [constant OK] on success, [constant ERR_CANT_OPEN] if the [param path] cannot be loaded into a [PackedScene], or [constant ERR_CANT_CREATE] if that scene cannot be instantiated. - [b]Note:[/b] The scene change is deferred, which means that the new scene node is added on the next idle frame. You won't be able to access it immediately after the [method change_scene_to_file] call. + [b]Note:[/b] The scene change is deferred, which means that the new scene node is added on the next idle frame. This ensures that both scenes are never loaded at the same time, which can exhaust system resources if the scenes are too large or if running in a memory constrained environment. As such, you won't be able to access the loaded scene immediately after the [method change_scene_to_file] call. </description> </method> <method name="change_scene_to_packed"> <return type="int" enum="Error" /> <param index="0" name="packed_scene" type="PackedScene" /> <description> - Changes the running scene to a new instance of the given [PackedScene]. - Returns [constant OK] on success or [constant ERR_CANT_CREATE] if the scene cannot be instantiated. + Changes the running scene to a new instance of the given [PackedScene] (which must be valid). + Returns [constant OK] on success, [constant ERR_CANT_CREATE] if the scene cannot be instantiated, or [constant ERR_INVALID_PARAMETER] if the scene is invalid. [b]Note:[/b] The scene change is deferred, which means that the new scene node is added on the next idle frame. You won't be able to access it immediately after the [method change_scene_to_packed] call. </description> </method> @@ -209,6 +209,12 @@ Sets a custom [MultiplayerAPI] with the given [param root_path] (controlling also the relative subpaths), or override the default one if [param root_path] is empty. </description> </method> + <method name="unload_current_scene"> + <return type="void" /> + <description> + If a current scene is loaded, calling this method will unload it. + </description> + </method> </methods> <members> <member name="auto_accept_quit" type="bool" setter="set_auto_accept_quit" getter="is_auto_accept_quit" default="true"> diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index 584a2a2a7b..bfabd2d97d 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -52,6 +52,12 @@ The new item will be the [param idx]th child of parent, or it will be the last child if there are not enough siblings. </description> </method> + <method name="deselect_all"> + <return type="void" /> + <description> + Deselects all tree items (rows and columns). In [constant SELECT_MULTI] mode also removes selection cursor. + </description> + </method> <method name="edit_selected"> <return type="bool" /> <description> diff --git a/doc/classes/VisualShader.xml b/doc/classes/VisualShader.xml index 2d59810a5f..6bffcdef36 100644 --- a/doc/classes/VisualShader.xml +++ b/doc/classes/VisualShader.xml @@ -26,6 +26,7 @@ <param index="1" name="mode" type="int" enum="VisualShader.VaryingMode" /> <param index="2" name="type" type="int" enum="VisualShader.VaryingType" /> <description> + Adds a new varying value node to the shader. </description> </method> <method name="can_connect_nodes" qualifiers="const"> @@ -106,12 +107,14 @@ <return type="int" /> <param index="0" name="type" type="int" enum="VisualShader.Type" /> <description> + Returns next valid node ID that can be added to the shader graph. </description> </method> <method name="has_varying" qualifiers="const"> <return type="bool" /> <param index="0" name="name" type="String" /> <description> + Returns [code]true[/code] if the shader has a varying with the given [param name]. </description> </method> <method name="is_node_connection" qualifiers="const"> @@ -137,6 +140,7 @@ <return type="void" /> <param index="0" name="name" type="String" /> <description> + Removes a varying value node with the given [param name]. Prints an error if a node with this name is not found. </description> </method> <method name="replace_node"> @@ -181,16 +185,22 @@ A shader for light calculations. </constant> <constant name="TYPE_START" value="3" enum="Type"> + A function for the "start" stage of particle shader. </constant> <constant name="TYPE_PROCESS" value="4" enum="Type"> + A function for the "process" stage of particle shader. </constant> <constant name="TYPE_COLLIDE" value="5" enum="Type"> + A function for the "collide" stage (particle collision handler) of particle shader. </constant> <constant name="TYPE_START_CUSTOM" value="6" enum="Type"> + A function for the "start" stage of particle shader, with customized output. </constant> <constant name="TYPE_PROCESS_CUSTOM" value="7" enum="Type"> + A function for the "process" stage of particle shader, with customized output. </constant> <constant name="TYPE_SKY" value="8" enum="Type"> + A shader for 3D environment's sky. </constant> <constant name="TYPE_FOG" value="9" enum="Type"> A compute shader that runs for each froxel of the volumetric fog map. @@ -199,32 +209,46 @@ Represents the size of the [enum Type] enum. </constant> <constant name="VARYING_MODE_VERTEX_TO_FRAG_LIGHT" value="0" enum="VaryingMode"> + Varying is passed from [code]Vertex[/code] function to [code]Fragment[/code] and [code]Light[/code] functions. </constant> <constant name="VARYING_MODE_FRAG_TO_LIGHT" value="1" enum="VaryingMode"> + Varying is passed from [code]Fragment[/code] function to [code]Light[/code] function. </constant> <constant name="VARYING_MODE_MAX" value="2" enum="VaryingMode"> + Represents the size of the [enum VaryingMode] enum. </constant> <constant name="VARYING_TYPE_FLOAT" value="0" enum="VaryingType"> + Varying is of type [float]. </constant> <constant name="VARYING_TYPE_INT" value="1" enum="VaryingType"> + Varying is of type [int]. </constant> <constant name="VARYING_TYPE_UINT" value="2" enum="VaryingType"> + Varying is of type unsigned [int]. </constant> <constant name="VARYING_TYPE_VECTOR_2D" value="3" enum="VaryingType"> + Varying is of type [Vector2]. </constant> <constant name="VARYING_TYPE_VECTOR_3D" value="4" enum="VaryingType"> + Varying is of type [Vector3]. </constant> <constant name="VARYING_TYPE_VECTOR_4D" value="5" enum="VaryingType"> + Varying is of type [Vector4]. </constant> <constant name="VARYING_TYPE_BOOLEAN" value="6" enum="VaryingType"> + Varying is of type [bool]. </constant> <constant name="VARYING_TYPE_TRANSFORM" value="7" enum="VaryingType"> + Varying is of type [Transform3D]. </constant> <constant name="VARYING_TYPE_MAX" value="8" enum="VaryingType"> + Represents the size of the [enum VaryingType] enum. </constant> <constant name="NODE_ID_INVALID" value="-1"> + Denotes invalid [VisualShader] node. </constant> <constant name="NODE_ID_OUTPUT" value="0"> + Denotes output node of [VisualShader]. </constant> </constants> </class> diff --git a/doc/classes/VisualShaderNodeConstant.xml b/doc/classes/VisualShaderNodeConstant.xml index 213ab664ad..23b97a78c7 100644 --- a/doc/classes/VisualShaderNodeConstant.xml +++ b/doc/classes/VisualShaderNodeConstant.xml @@ -4,6 +4,7 @@ A base type for the constants within the visual shader graph. </brief_description> <description> + This is an abstract class. See the derived types for descriptions of the possible values. </description> <tutorials> </tutorials> diff --git a/doc/classes/VisualShaderNodeDistanceFade.xml b/doc/classes/VisualShaderNodeDistanceFade.xml index 8ea0857776..c707035253 100644 --- a/doc/classes/VisualShaderNodeDistanceFade.xml +++ b/doc/classes/VisualShaderNodeDistanceFade.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeDistanceFade" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader node representing distance fade effect. </brief_description> <description> + The distance fade effect fades out each pixel based on its distance to another object. </description> <tutorials> </tutorials> diff --git a/doc/classes/VisualShaderNodeIntParameter.xml b/doc/classes/VisualShaderNodeIntParameter.xml index 70335b0c77..1ee7e3f217 100644 --- a/doc/classes/VisualShaderNodeIntParameter.xml +++ b/doc/classes/VisualShaderNodeIntParameter.xml @@ -1,33 +1,45 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeIntParameter" inherits="VisualShaderNodeParameter" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader node for shader parameter (uniform) of type [int]. </brief_description> <description> + A [VisualShaderNodeParameter] of type [int]. Offers additional customization for range of accepted values. </description> <tutorials> </tutorials> <members> <member name="default_value" type="int" setter="set_default_value" getter="get_default_value" default="0"> + Default value of this parameter, which will be used if not set externally. [member default_value_enabled] must be enabled; defaults to [code]0[/code] otherwise. </member> <member name="default_value_enabled" type="bool" setter="set_default_value_enabled" getter="is_default_value_enabled" default="false"> + If [code]true[/code], the node will have a custom default value. </member> <member name="hint" type="int" setter="set_hint" getter="get_hint" enum="VisualShaderNodeIntParameter.Hint" default="0"> + Range hint of this node. Use it to customize valid parameter range. </member> <member name="max" type="int" setter="set_max" getter="get_max" default="100"> + The maximum value this parameter can take. [member hint] must be either [constant HINT_RANGE] or [constant HINT_RANGE_STEP] for this to take effect. </member> <member name="min" type="int" setter="set_min" getter="get_min" default="0"> + The minimum value this parameter can take. [member hint] must be either [constant HINT_RANGE] or [constant HINT_RANGE_STEP] for this to take effect. </member> <member name="step" type="int" setter="set_step" getter="get_step" default="1"> + The step between parameter's values. Forces the parameter to be a multiple of the given value. [member hint] must be [constant HINT_RANGE_STEP] for this to take effect. </member> </members> <constants> <constant name="HINT_NONE" value="0" enum="Hint"> + The parameter will not constrain its value. </constant> <constant name="HINT_RANGE" value="1" enum="Hint"> + The parameter's value must be within the specified [member min]/[member max] range. </constant> <constant name="HINT_RANGE_STEP" value="2" enum="Hint"> + The parameter's value must be within the specified range, with the given [member step] between values. </constant> <constant name="HINT_MAX" value="3" enum="Hint"> + Represents the size of the [enum Hint] enum. </constant> </constants> </class> diff --git a/doc/classes/VisualShaderNodeLinearSceneDepth.xml b/doc/classes/VisualShaderNodeLinearSceneDepth.xml index fa8c01ac0a..0dff2d780d 100644 --- a/doc/classes/VisualShaderNodeLinearSceneDepth.xml +++ b/doc/classes/VisualShaderNodeLinearSceneDepth.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeLinearSceneDepth" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader node that returns the depth value of the DEPTH_TEXTURE node in a linear space. </brief_description> <description> + This node can be used in fragment shaders. </description> <tutorials> </tutorials> diff --git a/doc/classes/VisualShaderNodeParameter.xml b/doc/classes/VisualShaderNodeParameter.xml index 55b10ac810..acc180a7ca 100644 --- a/doc/classes/VisualShaderNodeParameter.xml +++ b/doc/classes/VisualShaderNodeParameter.xml @@ -13,14 +13,18 @@ Name of the parameter, by which it can be accessed through the [ShaderMaterial] properties. </member> <member name="qualifier" type="int" setter="set_qualifier" getter="get_qualifier" enum="VisualShaderNodeParameter.Qualifier" default="0"> + Defines the scope of the parameter. </member> </members> <constants> <constant name="QUAL_NONE" value="0" enum="Qualifier"> + The parameter will be tied to the [ShaderMaterial] using this shader. </constant> <constant name="QUAL_GLOBAL" value="1" enum="Qualifier"> + The parameter will use a global value, defined in Project Settings. </constant> <constant name="QUAL_INSTANCE" value="2" enum="Qualifier"> + The parameter will be tied to the node with attached [ShaderMaterial] using this shader. </constant> <constant name="QUAL_MAX" value="3" enum="Qualifier"> Represents the size of the [enum Qualifier] enum. diff --git a/doc/classes/VisualShaderNodeParticleAccelerator.xml b/doc/classes/VisualShaderNodeParticleAccelerator.xml index f26362b336..7a197dd8cb 100644 --- a/doc/classes/VisualShaderNodeParticleAccelerator.xml +++ b/doc/classes/VisualShaderNodeParticleAccelerator.xml @@ -1,21 +1,27 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeParticleAccelerator" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader node that accelerates particles. </brief_description> <description> + Particle accelerator can be used in "process" step of particle shader. It will accelerate the particles. Connect it to the Velocity output port. </description> <tutorials> </tutorials> <members> <member name="mode" type="int" setter="set_mode" getter="get_mode" enum="VisualShaderNodeParticleAccelerator.Mode" default="0"> + Defines in what manner the particles will be accelerated. </member> </members> <constants> <constant name="MODE_LINEAR" value="0" enum="Mode"> + The particles will be accelerated based on their velocity. </constant> <constant name="MODE_RADIAL" value="1" enum="Mode"> + The particles will be accelerated towards or away from the center. </constant> <constant name="MODE_TANGENTIAL" value="2" enum="Mode"> + The particles will be accelerated tangentially to the radius vector from center to their position. </constant> <constant name="MODE_MAX" value="3" enum="Mode"> Represents the size of the [enum Mode] enum. diff --git a/doc/classes/VisualShaderNodeParticleBoxEmitter.xml b/doc/classes/VisualShaderNodeParticleBoxEmitter.xml index dbef4b806d..1bdb9d15bc 100644 --- a/doc/classes/VisualShaderNodeParticleBoxEmitter.xml +++ b/doc/classes/VisualShaderNodeParticleBoxEmitter.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeParticleBoxEmitter" inherits="VisualShaderNodeParticleEmitter" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader node that makes particles emitted in a box shape. </brief_description> <description> + [VisualShaderNodeParticleEmitter] that makes the particles emitted in box shape with the specified extents. </description> <tutorials> </tutorials> diff --git a/doc/classes/VisualShaderNodeParticleConeVelocity.xml b/doc/classes/VisualShaderNodeParticleConeVelocity.xml index c3b3621fbc..48da5b874b 100644 --- a/doc/classes/VisualShaderNodeParticleConeVelocity.xml +++ b/doc/classes/VisualShaderNodeParticleConeVelocity.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeParticleConeVelocity" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader node that makes particles move in a cone shape. </brief_description> <description> + This node can be used in "start" step of particle shader. It defines the initial velocity of the particles, making them move in cone shape starting from the center, with a given spread. </description> <tutorials> </tutorials> diff --git a/doc/classes/VisualShaderNodeParticleEmit.xml b/doc/classes/VisualShaderNodeParticleEmit.xml index c5e9d25ca1..3e52a74aed 100644 --- a/doc/classes/VisualShaderNodeParticleEmit.xml +++ b/doc/classes/VisualShaderNodeParticleEmit.xml @@ -1,25 +1,33 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeParticleEmit" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader node that forces to emit a particle from a sub-emitter. </brief_description> <description> + This node internally calls [code]emit_subparticle[/code] shader method. It will emit a particle from the configured sub-emitter and also allows to customize how its emitted. Requires a sub-emitter assigned to the particles node with this shader. </description> <tutorials> </tutorials> <members> <member name="flags" type="int" setter="set_flags" getter="get_flags" enum="VisualShaderNodeParticleEmit.EmitFlags" default="31"> + Flags used to override the properties defined in the sub-emitter's process material. </member> </members> <constants> <constant name="EMIT_FLAG_POSITION" value="1" enum="EmitFlags"> + If enabled, the particle starts with the position defined by this node. </constant> <constant name="EMIT_FLAG_ROT_SCALE" value="2" enum="EmitFlags"> + If enabled, the particle starts with the rotation and scale defined by this node. </constant> <constant name="EMIT_FLAG_VELOCITY" value="4" enum="EmitFlags"> + If enabled,the particle starts with the velocity defined by this node. </constant> <constant name="EMIT_FLAG_COLOR" value="8" enum="EmitFlags"> + If enabled, the particle starts with the color defined by this node. </constant> <constant name="EMIT_FLAG_CUSTOM" value="16" enum="EmitFlags"> + If enabled, the particle starts with the [code]CUSTOM[/code] data defined by this node. </constant> </constants> </class> diff --git a/doc/classes/VisualShaderNodeParticleEmitter.xml b/doc/classes/VisualShaderNodeParticleEmitter.xml index ddfd410708..abb5528d5f 100644 --- a/doc/classes/VisualShaderNodeParticleEmitter.xml +++ b/doc/classes/VisualShaderNodeParticleEmitter.xml @@ -4,6 +4,7 @@ A base class for particle emitters. </brief_description> <description> + Particle emitter nodes can be used in "start" step of particle shaders and they define the starting position of the particles. Connect them to the Position output port. </description> <tutorials> </tutorials> diff --git a/doc/classes/VisualShaderNodeParticleMeshEmitter.xml b/doc/classes/VisualShaderNodeParticleMeshEmitter.xml index 25dd925112..59dc74690e 100644 --- a/doc/classes/VisualShaderNodeParticleMeshEmitter.xml +++ b/doc/classes/VisualShaderNodeParticleMeshEmitter.xml @@ -1,17 +1,22 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeParticleMeshEmitter" inherits="VisualShaderNodeParticleEmitter" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader node that makes particles emitted in a shape defined by a [Mesh]. </brief_description> <description> + [VisualShaderNodeParticleEmitter] that makes the particles emitted in a shape of the assigned [member mesh]. It will emit from the mesh's surfaces, either all or only the specified one. </description> <tutorials> </tutorials> <members> <member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh"> + The [Mesh] that defines emission shape. </member> <member name="surface_index" type="int" setter="set_surface_index" getter="get_surface_index" default="0"> + Index of the surface that emits particles. [member use_all_surfaces] must be [code]false[/code] for this to take effect. </member> <member name="use_all_surfaces" type="bool" setter="set_use_all_surfaces" getter="is_use_all_surfaces" default="true"> + If [code]true[/code], the particles will emit from all surfaces of the mesh. </member> </members> </class> diff --git a/doc/classes/VisualShaderNodeParticleMultiplyByAxisAngle.xml b/doc/classes/VisualShaderNodeParticleMultiplyByAxisAngle.xml index a911dbf3b4..95d031807b 100644 --- a/doc/classes/VisualShaderNodeParticleMultiplyByAxisAngle.xml +++ b/doc/classes/VisualShaderNodeParticleMultiplyByAxisAngle.xml @@ -1,13 +1,16 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeParticleMultiplyByAxisAngle" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader helper node for multiplying position and rotation of particles. </brief_description> <description> + This node helps to multiply a position input vector by rotation using specific axis. Intended to work with emitters. </description> <tutorials> </tutorials> <members> <member name="degrees_mode" type="bool" setter="set_degrees_mode" getter="is_degrees_mode" default="true"> + If [code]true[/code], the angle will be interpreted in degrees instead of radians. </member> </members> </class> diff --git a/doc/classes/VisualShaderNodeParticleOutput.xml b/doc/classes/VisualShaderNodeParticleOutput.xml index 7542272e61..4a1e61b879 100644 --- a/doc/classes/VisualShaderNodeParticleOutput.xml +++ b/doc/classes/VisualShaderNodeParticleOutput.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeParticleOutput" inherits="VisualShaderNodeOutput" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + Visual shader node that defines output values for particle emitting. </brief_description> <description> + This node defines how particles are emitted. It allows to customize e.g. position and velocity. Available ports are different depending on which function this node is inside (start, process, collision) and whether custom data is enabled. </description> <tutorials> </tutorials> diff --git a/doc/classes/VisualShaderNodeParticleRandomness.xml b/doc/classes/VisualShaderNodeParticleRandomness.xml index a1f9ce040a..574ba63ba9 100644 --- a/doc/classes/VisualShaderNodeParticleRandomness.xml +++ b/doc/classes/VisualShaderNodeParticleRandomness.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeParticleRandomness" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + Visual shader node for randomizing particle values. </brief_description> <description> + Randomness node will output pseudo-random values of the given type based on the specified minimum and maximum values. </description> <tutorials> </tutorials> diff --git a/doc/classes/VisualShaderNodeParticleRingEmitter.xml b/doc/classes/VisualShaderNodeParticleRingEmitter.xml index bebce51d6b..e2ff44ed55 100644 --- a/doc/classes/VisualShaderNodeParticleRingEmitter.xml +++ b/doc/classes/VisualShaderNodeParticleRingEmitter.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeParticleRingEmitter" inherits="VisualShaderNodeParticleEmitter" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader node that makes particles emitted in a ring shape. </brief_description> <description> + [VisualShaderNodeParticleEmitter] that makes the particles emitted in ring shape with the specified inner and outer radii and height. </description> <tutorials> </tutorials> diff --git a/doc/classes/VisualShaderNodeParticleSphereEmitter.xml b/doc/classes/VisualShaderNodeParticleSphereEmitter.xml index ffbd384f1e..3d1e332c97 100644 --- a/doc/classes/VisualShaderNodeParticleSphereEmitter.xml +++ b/doc/classes/VisualShaderNodeParticleSphereEmitter.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeParticleSphereEmitter" inherits="VisualShaderNodeParticleEmitter" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader node that makes particles emitted in a sphere shape. </brief_description> <description> + [VisualShaderNodeParticleEmitter] that makes the particles emitted in sphere shape with the specified inner and outer radii. </description> <tutorials> </tutorials> diff --git a/doc/classes/VisualShaderNodeProximityFade.xml b/doc/classes/VisualShaderNodeProximityFade.xml index 25051eed71..8405fcef36 100644 --- a/doc/classes/VisualShaderNodeProximityFade.xml +++ b/doc/classes/VisualShaderNodeProximityFade.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeProximityFade" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader node representing proximity fade effect. </brief_description> <description> + The proximity fade effect fades out each pixel based on its distance to another object. </description> <tutorials> </tutorials> diff --git a/doc/classes/VisualShaderNodeRandomRange.xml b/doc/classes/VisualShaderNodeRandomRange.xml index adc83d808c..e1a5e0b266 100644 --- a/doc/classes/VisualShaderNodeRandomRange.xml +++ b/doc/classes/VisualShaderNodeRandomRange.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeRandomRange" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader node that generates a pseudo-random scalar. </brief_description> <description> + Random range node will output a pseudo-random scalar value in the specified range, based on the seed. The value is always the same for the given seed and range, so you should provide a changing input, e.g. by using time. </description> <tutorials> </tutorials> diff --git a/doc/classes/VisualShaderNodeRemap.xml b/doc/classes/VisualShaderNodeRemap.xml index 5a73a76e7f..73512bcb99 100644 --- a/doc/classes/VisualShaderNodeRemap.xml +++ b/doc/classes/VisualShaderNodeRemap.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeRemap" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader node for remap function. </brief_description> <description> + Remap will transform the input range into output range, e.g. you can change a [code]0..1[/code] value to [code]-2..2[/code] etc. See [method @GlobalScope.remap] for more details. </description> <tutorials> </tutorials> diff --git a/doc/classes/VisualShaderNodeTexture2DArrayParameter.xml b/doc/classes/VisualShaderNodeTexture2DArrayParameter.xml index 2afaa8e219..885325bc6c 100644 --- a/doc/classes/VisualShaderNodeTexture2DArrayParameter.xml +++ b/doc/classes/VisualShaderNodeTexture2DArrayParameter.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeTexture2DArrayParameter" inherits="VisualShaderNodeTextureParameter" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader node for shader parameter (uniform) of type [Texture2DArray]. </brief_description> <description> + This parameter allows to provide a collection of textures for the shader. You can use [VisualShaderNodeTexture2DArray] to extract the textures from array. </description> <tutorials> </tutorials> diff --git a/doc/classes/VisualShaderNodeTextureParameter.xml b/doc/classes/VisualShaderNodeTextureParameter.xml index ad21c4e990..333226dc58 100644 --- a/doc/classes/VisualShaderNodeTextureParameter.xml +++ b/doc/classes/VisualShaderNodeTextureParameter.xml @@ -51,27 +51,39 @@ Represents the size of the [enum ColorDefault] enum. </constant> <constant name="FILTER_DEFAULT" value="0" enum="TextureFilter"> + Sample the texture using the filter determined by the node this shader is attached to. </constant> <constant name="FILTER_NEAREST" value="1" enum="TextureFilter"> + The texture filter reads from the nearest pixel only. The simplest and fastest method of filtering, but the texture will look pixelized. </constant> <constant name="FILTER_LINEAR" value="2" enum="TextureFilter"> + The texture filter blends between the nearest four pixels. Use this for most cases where you want to avoid a pixelated style. </constant> <constant name="FILTER_NEAREST_MIPMAP" value="3" enum="TextureFilter"> + The texture filter reads from the nearest pixel in the nearest mipmap. This is the fastest way to read from textures with mipmaps. </constant> <constant name="FILTER_LINEAR_MIPMAP" value="4" enum="TextureFilter"> + The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps. Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels. </constant> <constant name="FILTER_NEAREST_MIPMAP_ANISOTROPIC" value="5" enum="TextureFilter"> + The texture filter reads from the nearest pixel, but selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. + [b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant FILTER_LINEAR_MIPMAP] is usually more appropriate. </constant> <constant name="FILTER_LINEAR_MIPMAP_ANISOTROPIC" value="6" enum="TextureFilter"> + The texture filter blends between the nearest 4 pixels and selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera. This is the slowest of the filtering options, but results in the highest quality texturing. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level]. + [b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant FILTER_LINEAR_MIPMAP] is usually more appropriate. </constant> <constant name="FILTER_MAX" value="7" enum="TextureFilter"> Represents the size of the [enum TextureFilter] enum. </constant> <constant name="REPEAT_DEFAULT" value="0" enum="TextureRepeat"> + Sample the texture using the repeat mode determined by the node this shader is attached to. </constant> <constant name="REPEAT_ENABLED" value="1" enum="TextureRepeat"> + Texture will repeat normally. </constant> <constant name="REPEAT_DISABLED" value="2" enum="TextureRepeat"> + Texture will not repeat. </constant> <constant name="REPEAT_MAX" value="3" enum="TextureRepeat"> Represents the size of the [enum TextureRepeat] enum. diff --git a/doc/classes/VisualShaderNodeUIntParameter.xml b/doc/classes/VisualShaderNodeUIntParameter.xml index 3b549c84f7..4c95e58962 100644 --- a/doc/classes/VisualShaderNodeUIntParameter.xml +++ b/doc/classes/VisualShaderNodeUIntParameter.xml @@ -1,15 +1,19 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeUIntParameter" inherits="VisualShaderNodeParameter" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader node for shader parameter (uniform) of type unsigned [int]. </brief_description> <description> + A [VisualShaderNodeParameter] of type unsigned [int]. Offers additional customization for range of accepted values. </description> <tutorials> </tutorials> <members> <member name="default_value" type="int" setter="set_default_value" getter="get_default_value" default="0"> + Default value of this parameter, which will be used if not set externally. [member default_value_enabled] must be enabled; defaults to [code]0[/code] otherwise. </member> <member name="default_value_enabled" type="bool" setter="set_default_value_enabled" getter="is_default_value_enabled" default="false"> + If [code]true[/code], the node will have a custom default value. </member> </members> </class> diff --git a/doc/classes/VisualShaderNodeUVFunc.xml b/doc/classes/VisualShaderNodeUVFunc.xml index 541991b790..b5143b647c 100644 --- a/doc/classes/VisualShaderNodeUVFunc.xml +++ b/doc/classes/VisualShaderNodeUVFunc.xml @@ -4,6 +4,7 @@ Contains functions to modify texture coordinates ([code]uv[/code]) to be used within the visual shader graph. </brief_description> <description> + UV functions are similar to [Vector2] functions, but the input port of this node uses the shader's UV value by default. </description> <tutorials> </tutorials> diff --git a/doc/classes/VisualShaderNodeUVPolarCoord.xml b/doc/classes/VisualShaderNodeUVPolarCoord.xml index 8582939db3..49f7f52bc1 100644 --- a/doc/classes/VisualShaderNodeUVPolarCoord.xml +++ b/doc/classes/VisualShaderNodeUVPolarCoord.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeUVPolarCoord" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader node that modifies the texture UV using polar coordinates. </brief_description> <description> + UV polar coord node will transform UV values into polar coordinates, with specified scale, zoom strength and repeat parameters. It can be used to create various swirl distortions. </description> <tutorials> </tutorials> diff --git a/doc/classes/VisualShaderNodeVarying.xml b/doc/classes/VisualShaderNodeVarying.xml index 0dbbd61f3a..0a5c5a70af 100644 --- a/doc/classes/VisualShaderNodeVarying.xml +++ b/doc/classes/VisualShaderNodeVarying.xml @@ -1,15 +1,19 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeVarying" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader node that represents a "varying" shader value. </brief_description> <description> + Varying values are shader variables that can be passed between shader functions, e.g. from Vertex shader to Fragment shader. </description> <tutorials> </tutorials> <members> <member name="varying_name" type="String" setter="set_varying_name" getter="get_varying_name" default=""[None]""> + Name of the variable. Must be unique. </member> <member name="varying_type" type="int" setter="set_varying_type" getter="get_varying_type" enum="VisualShader.VaryingType" default="0"> + Type of the variable. Determines where the variable can be accessed. </member> </members> </class> diff --git a/doc/classes/VisualShaderNodeVaryingGetter.xml b/doc/classes/VisualShaderNodeVaryingGetter.xml index de30b18d67..dea47ed3c1 100644 --- a/doc/classes/VisualShaderNodeVaryingGetter.xml +++ b/doc/classes/VisualShaderNodeVaryingGetter.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeVaryingGetter" inherits="VisualShaderNodeVarying" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader node that gets a value of a varying. </brief_description> <description> + Outputs a value of a varying defined in the shader. You need to first create a varying that can be used in the given function, e.g. varying getter in Fragment shader requires a varying with mode set to [constant VisualShader.VARYING_MODE_VERTEX_TO_FRAG_LIGHT]. </description> <tutorials> </tutorials> diff --git a/doc/classes/VisualShaderNodeVaryingSetter.xml b/doc/classes/VisualShaderNodeVaryingSetter.xml index 57ead3d82b..b305fdd3ef 100644 --- a/doc/classes/VisualShaderNodeVaryingSetter.xml +++ b/doc/classes/VisualShaderNodeVaryingSetter.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeVaryingSetter" inherits="VisualShaderNodeVarying" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A visual shader node that sets a value of a varying. </brief_description> <description> + Inputs a value to a varying defined in the shader. You need to first create a varying that can be used in the given function, e.g. varying setter in Fragment shader requires a varying with mode set to [constant VisualShader.VARYING_MODE_FRAG_TO_LIGHT]. </description> <tutorials> </tutorials> diff --git a/doc/classes/VisualShaderNodeVectorBase.xml b/doc/classes/VisualShaderNodeVectorBase.xml index d9c9f2d79c..68cf00e819 100644 --- a/doc/classes/VisualShaderNodeVectorBase.xml +++ b/doc/classes/VisualShaderNodeVectorBase.xml @@ -1,15 +1,16 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeVectorBase" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - A base type for the nodes using different vector types within the visual shader graph. + A base type for the nodes that perform vector operations within the visual shader graph. </brief_description> <description> + This is an abstract class. See the derived types for descriptions of the possible operations. </description> <tutorials> </tutorials> <members> <member name="op_type" type="int" setter="set_op_type" getter="get_op_type" enum="VisualShaderNodeVectorBase.OpType" default="1"> - A base type. + A vector type that this operation is performed on. </member> </members> <constants> diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 425ca1c516..6c833c8a26 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -36,7 +36,6 @@ #include "core/io/marshalls.h" #include "core/os/os.h" #include "core/templates/hashfuncs.h" -#include "core/version.h" #include "drivers/vulkan/vulkan_context.h" #include "thirdparty/misc/smolv.h" @@ -4574,7 +4573,7 @@ String RenderingDeviceVulkan::_shader_uniform_debug(RID p_shader, int p_set) { #define SHADER_BINARY_VERSION 3 String RenderingDeviceVulkan::shader_get_binary_cache_key() const { - return "Vulkan-SV" + itos(SHADER_BINARY_VERSION) + "-" + String(VERSION_NUMBER) + "-" + String(VERSION_HASH); + return "Vulkan-SV" + itos(SHADER_BINARY_VERSION); } struct RenderingDeviceVulkanShaderBinaryDataBinding { diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp index 8e55f3a1c3..712b11d7d7 100644 --- a/editor/action_map_editor.cpp +++ b/editor/action_map_editor.cpp @@ -578,7 +578,7 @@ ActionMapEditor::ActionMapEditor() { action_tree->connect("button_clicked", callable_mp(this, &ActionMapEditor::_tree_button_pressed)); main_vbox->add_child(action_tree); - action_tree->set_drag_forwarding(this); + action_tree->set_drag_forwarding_compat(this); // Adding event dialog event_config_dialog = memnew(InputEventConfigurationDialog); diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 8ae6638e80..89bb1f5ae6 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -1253,7 +1253,7 @@ void AnimationTimelineEdit::_anim_length_changed(double p_new_len) { editing = true; Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); - undo_redo->create_action(TTR("Change Animation Length")); + undo_redo->create_action(TTR("Change Animation Length"), UndoRedo::MERGE_ENDS); undo_redo->add_do_method(animation.ptr(), "set_length", p_new_len); undo_redo->add_undo_method(animation.ptr(), "set_length", animation->get_length()); undo_redo->commit_action(); diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index 76d70b09e6..6e701cb6bd 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -754,7 +754,7 @@ CreateDialog::CreateDialog() { favorites->connect("cell_selected", callable_mp(this, &CreateDialog::_favorite_selected)); favorites->connect("item_activated", callable_mp(this, &CreateDialog::_favorite_activated)); favorites->add_theme_constant_override("draw_guides", 1); - favorites->set_drag_forwarding(this); + favorites->set_drag_forwarding_compat(this); fav_vb->add_margin_child(TTR("Favorites:"), favorites, true); VBoxContainer *rec_vb = memnew(VBoxContainer); diff --git a/editor/editor_asset_installer.cpp b/editor/editor_asset_installer.cpp index 0258ac532f..a5bc90159a 100644 --- a/editor/editor_asset_installer.cpp +++ b/editor/editor_asset_installer.cpp @@ -120,7 +120,6 @@ void EditorAssetInstaller::open(const String &p_path, int p_depth) { // Mark C# support as unavailable. extension_guess["cs"] = tree->get_theme_icon(SNAME("ImportFail"), SNAME("EditorIcons")); } - extension_guess["vs"] = tree->get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons")); extension_guess["res"] = tree->get_theme_icon(SNAME("Resource"), SNAME("EditorIcons")); extension_guess["tres"] = tree->get_theme_icon(SNAME("Resource"), SNAME("EditorIcons")); diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index 5296749c18..7ef99d56ab 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -903,7 +903,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) { effects->connect("item_edited", callable_mp(this, &EditorAudioBus::_effect_edited)); effects->connect("cell_selected", callable_mp(this, &EditorAudioBus::_effect_selected)); effects->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true); - effects->set_drag_forwarding(this); + effects->set_drag_forwarding_compat(this); effects->connect("item_mouse_selected", callable_mp(this, &EditorAudioBus::_effect_rmb)); effects->set_allow_rmb_select(true); effects->set_focus_mode(FOCUS_CLICK); diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp index db251c857c..4001b849ff 100644 --- a/editor/editor_autoload_settings.cpp +++ b/editor/editor_autoload_settings.cpp @@ -935,7 +935,7 @@ EditorAutoloadSettings::EditorAutoloadSettings() { tree->set_select_mode(Tree::SELECT_MULTI); tree->set_allow_reselect(true); - tree->set_drag_forwarding(this); + tree->set_drag_forwarding_compat(this); tree->set_columns(4); tree->set_column_titles_visible(true); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 28424d53b1..4efc7c3055 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -433,7 +433,7 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo } if (!found_uid) { - return true; //UUID not found, old format, reimport. + return true; //UID not found, old format, reimport. } Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name); @@ -868,7 +868,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, Ref<DirAc } if (fc->uid == ResourceUID::INVALID_ID) { - // imported files should always have a UUID, so attempt to fetch it. + // imported files should always have a UID, so attempt to fetch it. fi->uid = ResourceLoader::get_resource_uid(path); } @@ -2319,14 +2319,14 @@ ResourceUID::ID EditorFileSystem::_resource_saver_get_resource_id_for_path(const } if (p_generate) { - return ResourceUID::get_singleton()->create_id(); // Just create a new one, we will be notified of save anyway and fetch the right UUID at that time, to keep things simple. + return ResourceUID::get_singleton()->create_id(); // Just create a new one, we will be notified of save anyway and fetch the right UID at that time, to keep things simple. } else { return ResourceUID::INVALID_ID; } } else if (fs->files[cpos]->uid != ResourceUID::INVALID_ID) { return fs->files[cpos]->uid; } else if (p_generate) { - return ResourceUID::get_singleton()->create_id(); // Just create a new one, we will be notified of save anyway and fetch the right UUID at that time, to keep things simple. + return ResourceUID::get_singleton()->create_id(); // Just create a new one, we will be notified of save anyway and fetch the right UID at that time, to keep things simple. } else { return ResourceUID::INVALID_ID; } diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 54d43bbf44..12aa44891d 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -2065,7 +2065,7 @@ void EditorInspectorArray::_setup() { ae.panel = memnew(PanelContainer); ae.panel->set_focus_mode(FOCUS_ALL); ae.panel->set_mouse_filter(MOUSE_FILTER_PASS); - ae.panel->set_drag_forwarding(this); + ae.panel->set_drag_forwarding_compat(this); ae.panel->set_meta("index", begin_array_index + i); ae.panel->set_tooltip_text(vformat(TTR("Element %d: %s%d*"), i, array_element_prefix, i)); ae.panel->connect("focus_entered", callable_mp((CanvasItem *)ae.panel, &PanelContainer::queue_redraw)); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 18bb3f3dbb..ab79031ad2 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -2336,7 +2336,7 @@ void EditorNode::_edit_current(bool p_skip_foreign) { if (main_plugin && !skip_main_plugin) { // Special case if use of external editor is true. Resource *current_res = Object::cast_to<Resource>(current_obj); - if (main_plugin->get_name() == "Script" && !current_obj->is_class("VisualScript") && current_res && !current_res->is_built_in() && (bool(EDITOR_GET("text_editor/external/use_external_editor")) || overrides_external_editor(current_obj))) { + if (main_plugin->get_name() == "Script" && current_res && !current_res->is_built_in() && (bool(EDITOR_GET("text_editor/external/use_external_editor")) || overrides_external_editor(current_obj))) { if (!changing_scene) { main_plugin->edit(current_obj); } @@ -6093,7 +6093,10 @@ EditorNode::EditorNode() { } // Define a minimum window size to prevent UI elements from overlapping or being cut off. - DisplayServer::get_singleton()->window_set_min_size(Size2(1024, 600) * EDSCALE); + Window *w = Object::cast_to<Window>(SceneTree::get_singleton()->get_root()); + if (w) { + w->set_min_size(Size2(1024, 600) * EDSCALE); + } FileDialog::set_default_show_hidden_files(EDITOR_GET("filesystem/file_dialog/show_hidden_files")); EditorFileDialog::set_default_show_hidden_files(EDITOR_GET("filesystem/file_dialog/show_hidden_files")); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 46aa798252..8ef394a59f 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -68,7 +68,7 @@ EditorPropertyNil::EditorPropertyNil() { void EditorPropertyText::_set_read_only(bool p_read_only) { text->set_editable(!p_read_only); -}; +} void EditorPropertyText::_text_submitted(const String &p_string) { if (updating) { @@ -133,7 +133,7 @@ EditorPropertyText::EditorPropertyText() { void EditorPropertyMultilineText::_set_read_only(bool p_read_only) { text->set_editable(!p_read_only); open_big_text->set_disabled(p_read_only); -}; +} void EditorPropertyMultilineText::_big_text_changed() { text->set_text(big_text->get_text()); @@ -236,7 +236,7 @@ EditorPropertyMultilineText::EditorPropertyMultilineText(bool p_expression) { void EditorPropertyTextEnum::_set_read_only(bool p_read_only) { option_button->set_disabled(p_read_only); edit_button->set_disabled(p_read_only); -}; +} void EditorPropertyTextEnum::_emit_changed_value(String p_string) { if (string_name) { @@ -450,7 +450,7 @@ EditorPropertyLocale::EditorPropertyLocale() { void EditorPropertyPath::_set_read_only(bool p_read_only) { path->set_editable(!p_read_only); path_edit->set_disabled(p_read_only); -}; +} void EditorPropertyPath::_path_selected(const String &p_path) { emit_changed(get_edited_property(), p_path); @@ -569,7 +569,7 @@ EditorPropertyPath::EditorPropertyPath() { HBoxContainer *path_hb = memnew(HBoxContainer); add_child(path_hb); path = memnew(LineEdit); - path->set_drag_forwarding(this); + path->set_drag_forwarding_compat(this); path->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE); path_hb->add_child(path); path->connect("text_submitted", callable_mp(this, &EditorPropertyPath::_path_selected)); @@ -588,7 +588,7 @@ EditorPropertyPath::EditorPropertyPath() { void EditorPropertyClassName::_set_read_only(bool p_read_only) { property->set_disabled(p_read_only); -}; +} void EditorPropertyClassName::setup(const String &p_base_type, const String &p_selected_type) { base_type = p_base_type; @@ -629,112 +629,11 @@ EditorPropertyClassName::EditorPropertyClassName() { add_child(dialog); } -///////////////////// MEMBER ///////////////////////// - -void EditorPropertyMember::_set_read_only(bool p_read_only) { - property->set_disabled(p_read_only); -}; - -void EditorPropertyMember::_property_selected(const String &p_selected) { - emit_changed(get_edited_property(), p_selected); - update_property(); -} - -void EditorPropertyMember::_property_select() { - if (!selector) { - selector = memnew(PropertySelector); - selector->connect("selected", callable_mp(this, &EditorPropertyMember::_property_selected)); - add_child(selector); - } - - String current = get_edited_object()->get(get_edited_property()); - - if (hint == MEMBER_METHOD_OF_VARIANT_TYPE) { - Variant::Type type = Variant::NIL; - for (int i = 0; i < Variant::VARIANT_MAX; i++) { - if (hint_text == Variant::get_type_name(Variant::Type(i))) { - type = Variant::Type(i); - } - } - if (type != Variant::NIL) { - selector->select_method_from_basic_type(type, current); - } - - } else if (hint == MEMBER_METHOD_OF_BASE_TYPE) { - selector->select_method_from_base_type(hint_text, current); - - } else if (hint == MEMBER_METHOD_OF_INSTANCE) { - Object *instance = ObjectDB::get_instance(ObjectID(hint_text.to_int())); - if (instance) { - selector->select_method_from_instance(instance, current); - } - - } else if (hint == MEMBER_METHOD_OF_SCRIPT) { - Object *obj = ObjectDB::get_instance(ObjectID(hint_text.to_int())); - if (Object::cast_to<Script>(obj)) { - selector->select_method_from_script(Object::cast_to<Script>(obj), current); - } - - } else if (hint == MEMBER_PROPERTY_OF_VARIANT_TYPE) { - Variant::Type type = Variant::NIL; - String tname = hint_text; - if (tname.contains(".")) { - tname = tname.get_slice(".", 0); - } - for (int i = 0; i < Variant::VARIANT_MAX; i++) { - if (tname == Variant::get_type_name(Variant::Type(i))) { - type = Variant::Type(Variant::Type(i)); - } - } - - if (type != Variant::NIL) { - selector->select_property_from_basic_type(type, current); - } - - } else if (hint == MEMBER_PROPERTY_OF_BASE_TYPE) { - selector->select_property_from_base_type(hint_text, current); - - } else if (hint == MEMBER_PROPERTY_OF_INSTANCE) { - Object *instance = ObjectDB::get_instance(ObjectID(hint_text.to_int())); - if (instance) { - selector->select_property_from_instance(instance, current); - } - - } else if (hint == MEMBER_PROPERTY_OF_SCRIPT) { - Object *obj = ObjectDB::get_instance(ObjectID(hint_text.to_int())); - if (Object::cast_to<Script>(obj)) { - selector->select_property_from_script(Object::cast_to<Script>(obj), current); - } - } -} - -void EditorPropertyMember::setup(Type p_hint, const String &p_hint_text) { - hint = p_hint; - hint_text = p_hint_text; -} - -void EditorPropertyMember::update_property() { - String full_path = get_edited_object()->get(get_edited_property()); - property->set_text(full_path); -} - -void EditorPropertyMember::_bind_methods() { -} - -EditorPropertyMember::EditorPropertyMember() { - selector = nullptr; - property = memnew(Button); - property->set_clip_text(true); - add_child(property); - add_focusable(property); - property->connect("pressed", callable_mp(this, &EditorPropertyMember::_property_select)); -} - ///////////////////// CHECK ///////////////////////// void EditorPropertyCheck::_set_read_only(bool p_read_only) { checkbox->set_disabled(p_read_only); -}; +} void EditorPropertyCheck::_checkbox_pressed() { emit_changed(get_edited_property(), checkbox->is_pressed()); @@ -761,7 +660,7 @@ EditorPropertyCheck::EditorPropertyCheck() { void EditorPropertyEnum::_set_read_only(bool p_read_only) { options->set_disabled(p_read_only); -}; +} void EditorPropertyEnum::_option_selected(int p_which) { int64_t val = options->get_item_metadata(p_which); @@ -820,7 +719,7 @@ void EditorPropertyFlags::_set_read_only(bool p_read_only) { for (CheckBox *check : flags) { check->set_disabled(p_read_only); } -}; +} void EditorPropertyFlags::_flag_toggled(int p_index) { uint32_t value = get_edited_object()->get(get_edited_property()); @@ -1186,7 +1085,7 @@ void EditorPropertyLayers::_notification(int p_what) { void EditorPropertyLayers::_set_read_only(bool p_read_only) { button->set_disabled(p_read_only); grid->set_read_only(p_read_only); -}; +} void EditorPropertyLayers::_grid_changed(uint32_t p_grid) { emit_changed(get_edited_property(), p_grid); @@ -1359,7 +1258,7 @@ EditorPropertyLayers::EditorPropertyLayers() { void EditorPropertyInteger::_set_read_only(bool p_read_only) { spin->set_read_only(p_read_only); -}; +} void EditorPropertyInteger::_value_changed(int64_t val) { if (setting) { @@ -1406,7 +1305,7 @@ EditorPropertyInteger::EditorPropertyInteger() { void EditorPropertyObjectID::_set_read_only(bool p_read_only) { edit->set_disabled(p_read_only); -}; +} void EditorPropertyObjectID::_edit_pressed() { emit_signal(SNAME("object_id_selected"), get_edited_property(), get_edited_object()->get(get_edited_property())); @@ -1496,7 +1395,7 @@ EditorPropertyCallable::EditorPropertyCallable() { void EditorPropertyFloat::_set_read_only(bool p_read_only) { spin->set_read_only(p_read_only); -}; +} void EditorPropertyFloat::_value_changed(double val) { if (setting) { @@ -1546,7 +1445,7 @@ EditorPropertyFloat::EditorPropertyFloat() { void EditorPropertyEasing::_set_read_only(bool p_read_only) { spin->set_read_only(p_read_only); -}; +} void EditorPropertyEasing::_drag_easing(const Ref<InputEvent> &p_ev) { if (is_read_only()) { @@ -1769,7 +1668,7 @@ void EditorPropertyVector2::_set_read_only(bool p_read_only) { for (int i = 0; i < 2; i++) { spin[i]->set_read_only(p_read_only); } -}; +} void EditorPropertyVector2::_value_changed(double val, const String &p_name) { if (setting) { @@ -1900,7 +1799,7 @@ void EditorPropertyRect2::_set_read_only(bool p_read_only) { for (int i = 0; i < 4; i++) { spin[i]->set_read_only(p_read_only); } -}; +} void EditorPropertyRect2::_value_changed(double val, const String &p_name) { if (setting) { @@ -2003,7 +1902,7 @@ void EditorPropertyVector3::_set_read_only(bool p_read_only) { for (int i = 0; i < 3; i++) { spin[i]->set_read_only(p_read_only); } -}; +} void EditorPropertyVector3::_value_changed(double val, const String &p_name) { if (setting) { @@ -2182,7 +2081,7 @@ void EditorPropertyVector2i::_set_read_only(bool p_read_only) { for (int i = 0; i < 2; i++) { spin[i]->set_read_only(p_read_only); } -}; +} void EditorPropertyVector2i::_value_changed(double val, const String &p_name) { if (setting) { @@ -2312,7 +2211,7 @@ void EditorPropertyRect2i::_set_read_only(bool p_read_only) { for (int i = 0; i < 4; i++) { spin[i]->set_read_only(p_read_only); } -}; +} void EditorPropertyRect2i::_value_changed(double val, const String &p_name) { if (setting) { @@ -2414,7 +2313,7 @@ void EditorPropertyVector3i::_set_read_only(bool p_read_only) { for (int i = 0; i < 3; i++) { spin[i]->set_read_only(p_read_only); } -}; +} void EditorPropertyVector3i::_value_changed(double val, const String &p_name) { if (setting) { @@ -2564,7 +2463,7 @@ void EditorPropertyPlane::_set_read_only(bool p_read_only) { for (int i = 0; i < 4; i++) { spin[i]->set_read_only(p_read_only); } -}; +} void EditorPropertyPlane::_value_changed(double val, const String &p_name) { if (setting) { @@ -2660,7 +2559,7 @@ void EditorPropertyQuaternion::_set_read_only(bool p_read_only) { for (int i = 0; i < 3; i++) { euler[i]->set_read_only(p_read_only); } -}; +} void EditorPropertyQuaternion::_edit_custom_value() { if (edit_button->is_pressed()) { @@ -2873,7 +2772,7 @@ void EditorPropertyVector4::_set_read_only(bool p_read_only) { for (int i = 0; i < 4; i++) { spin[i]->set_read_only(p_read_only); } -}; +} void EditorPropertyVector4::_value_changed(double val, const String &p_name) { if (setting) { @@ -2963,7 +2862,7 @@ void EditorPropertyVector4i::_set_read_only(bool p_read_only) { for (int i = 0; i < 4; i++) { spin[i]->set_read_only(p_read_only); } -}; +} void EditorPropertyVector4i::_value_changed(double val, const String &p_name) { if (setting) { @@ -3052,7 +2951,7 @@ void EditorPropertyAABB::_set_read_only(bool p_read_only) { for (int i = 0; i < 6; i++) { spin[i]->set_read_only(p_read_only); } -}; +} void EditorPropertyAABB::_value_changed(double val, const String &p_name) { if (setting) { @@ -3135,7 +3034,7 @@ void EditorPropertyTransform2D::_set_read_only(bool p_read_only) { for (int i = 0; i < 6; i++) { spin[i]->set_read_only(p_read_only); } -}; +} void EditorPropertyTransform2D::_value_changed(double val, const String &p_name) { if (setting) { @@ -3226,7 +3125,7 @@ void EditorPropertyBasis::_set_read_only(bool p_read_only) { for (int i = 0; i < 9; i++) { spin[i]->set_read_only(p_read_only); } -}; +} void EditorPropertyBasis::_value_changed(double val, const String &p_name) { if (setting) { @@ -3316,7 +3215,7 @@ void EditorPropertyTransform3D::_set_read_only(bool p_read_only) { for (int i = 0; i < 12; i++) { spin[i]->set_read_only(p_read_only); } -}; +} void EditorPropertyTransform3D::_value_changed(double val, const String &p_name) { if (setting) { @@ -3414,7 +3313,7 @@ void EditorPropertyProjection::_set_read_only(bool p_read_only) { for (int i = 0; i < 12; i++) { spin[i]->set_read_only(p_read_only); } -}; +} void EditorPropertyProjection::_value_changed(double val, const String &p_name) { if (setting) { @@ -3517,7 +3416,7 @@ EditorPropertyProjection::EditorPropertyProjection() { void EditorPropertyColor::_set_read_only(bool p_read_only) { picker->set_disabled(p_read_only); -}; +} void EditorPropertyColor::_color_changed(const Color &p_color) { // Cancel the color change if the current color is identical to the new one. @@ -3787,7 +3686,7 @@ EditorPropertyNodePath::EditorPropertyNodePath() { assign->set_h_size_flags(SIZE_EXPAND_FILL); assign->set_clip_text(true); assign->connect("pressed", callable_mp(this, &EditorPropertyNodePath::_node_assign)); - assign->set_drag_forwarding(this); + assign->set_drag_forwarding_compat(this); hbc->add_child(assign); clear = memnew(Button); @@ -3819,7 +3718,7 @@ EditorPropertyRID::EditorPropertyRID() { void EditorPropertyResource::_set_read_only(bool p_read_only) { resource_picker->set_editable(!p_read_only); -}; +} void EditorPropertyResource::_resource_selected(const Ref<Resource> &p_resource, bool p_inspect) { if (p_resource->is_built_in() && !p_resource->get_path().is_empty()) { @@ -3928,12 +3827,10 @@ void EditorPropertyResource::_resource_changed(const Ref<Resource> &p_resource) } } - // Make visual script the correct type. - Ref<Script> s = p_resource; - // The bool is_script applies only to an object's main script. // Changing the value of Script-type exported variables of the main script should not trigger saving/reloading properties. bool is_script = false; + Ref<Script> s = p_resource; if (get_edited_object() && s.is_valid() && get_edited_property() == CoreStringNames::get_singleton()->_script) { is_script = true; InspectorDock::get_singleton()->store_script_properties(get_edited_object()); @@ -4489,45 +4386,6 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ editor->set_save_mode(); } return editor; - } else if (p_hint == PROPERTY_HINT_METHOD_OF_VARIANT_TYPE || - p_hint == PROPERTY_HINT_METHOD_OF_BASE_TYPE || - p_hint == PROPERTY_HINT_METHOD_OF_INSTANCE || - p_hint == PROPERTY_HINT_METHOD_OF_SCRIPT || - p_hint == PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE || - p_hint == PROPERTY_HINT_PROPERTY_OF_BASE_TYPE || - p_hint == PROPERTY_HINT_PROPERTY_OF_INSTANCE || - p_hint == PROPERTY_HINT_PROPERTY_OF_SCRIPT) { - EditorPropertyMember *editor = memnew(EditorPropertyMember); - - EditorPropertyMember::Type type = EditorPropertyMember::MEMBER_METHOD_OF_BASE_TYPE; - switch (p_hint) { - case PROPERTY_HINT_METHOD_OF_BASE_TYPE: - type = EditorPropertyMember::MEMBER_METHOD_OF_BASE_TYPE; - break; - case PROPERTY_HINT_METHOD_OF_INSTANCE: - type = EditorPropertyMember::MEMBER_METHOD_OF_INSTANCE; - break; - case PROPERTY_HINT_METHOD_OF_SCRIPT: - type = EditorPropertyMember::MEMBER_METHOD_OF_SCRIPT; - break; - case PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE: - type = EditorPropertyMember::MEMBER_PROPERTY_OF_VARIANT_TYPE; - break; - case PROPERTY_HINT_PROPERTY_OF_BASE_TYPE: - type = EditorPropertyMember::MEMBER_PROPERTY_OF_BASE_TYPE; - break; - case PROPERTY_HINT_PROPERTY_OF_INSTANCE: - type = EditorPropertyMember::MEMBER_PROPERTY_OF_INSTANCE; - break; - case PROPERTY_HINT_PROPERTY_OF_SCRIPT: - type = EditorPropertyMember::MEMBER_PROPERTY_OF_SCRIPT; - break; - default: { - } - } - editor->setup(type, p_hint_text); - return editor; - } else { EditorPropertyText *editor = memnew(EditorPropertyText); if (p_hint == PROPERTY_HINT_PLACEHOLDER_TEXT) { diff --git a/editor/editor_properties.h b/editor/editor_properties.h index 03e318b706..a255af30ee 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -203,40 +203,6 @@ public: EditorPropertyClassName(); }; -class EditorPropertyMember : public EditorProperty { - GDCLASS(EditorPropertyMember, EditorProperty); - -public: - enum Type { - MEMBER_METHOD_OF_VARIANT_TYPE, ///< a method of a type - MEMBER_METHOD_OF_BASE_TYPE, ///< a method of a base type - MEMBER_METHOD_OF_INSTANCE, ///< a method of an instance - MEMBER_METHOD_OF_SCRIPT, ///< a method of a script & base - MEMBER_PROPERTY_OF_VARIANT_TYPE, ///< a property of a type - MEMBER_PROPERTY_OF_BASE_TYPE, ///< a property of a base type - MEMBER_PROPERTY_OF_INSTANCE, ///< a property of an instance - MEMBER_PROPERTY_OF_SCRIPT, ///< a property of a script & base - }; - -private: - Type hint; - PropertySelector *selector = nullptr; - Button *property = nullptr; - String hint_text; - - void _property_selected(const String &p_selected); - void _property_select(); - -protected: - virtual void _set_read_only(bool p_read_only) override; - static void _bind_methods(); - -public: - void setup(Type p_hint, const String &p_hint_text); - virtual void update_property() override; - EditorPropertyMember(); -}; - class EditorPropertyCheck : public EditorProperty { GDCLASS(EditorPropertyCheck, EditorProperty); CheckBox *checkbox = nullptr; diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index 10f46283e6..ed667aa7c8 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -715,7 +715,7 @@ EditorPropertyArray::EditorPropertyArray() { edit->set_clip_text(true); edit->connect("pressed", callable_mp(this, &EditorPropertyArray::_edit_pressed)); edit->set_toggle_mode(true); - edit->set_drag_forwarding(this); + edit->set_drag_forwarding_compat(this); edit->connect("draw", callable_mp(this, &EditorPropertyArray::_button_draw)); add_child(edit); add_focusable(edit); diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index 94152dfd49..6b733e11f7 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -950,7 +950,7 @@ EditorResourcePicker::EditorResourcePicker(bool p_hide_assign_button_controls) { assign_button->set_flat(true); assign_button->set_h_size_flags(SIZE_EXPAND_FILL); assign_button->set_clip_text(true); - assign_button->set_drag_forwarding(this); + assign_button->set_drag_forwarding_compat(this); add_child(assign_button); assign_button->connect("pressed", callable_mp(this, &EditorResourcePicker::_resource_selected)); assign_button->connect("draw", callable_mp(this, &EditorResourcePicker::_button_draw)); diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp index 5985af21cd..2881302775 100644 --- a/editor/editor_settings_dialog.cpp +++ b/editor/editor_settings_dialog.cpp @@ -793,7 +793,7 @@ EditorSettingsDialog::EditorSettingsDialog() { shortcuts->connect("item_activated", callable_mp(this, &EditorSettingsDialog::_shortcut_cell_double_clicked)); tab_shortcuts->add_child(shortcuts); - shortcuts->set_drag_forwarding(this); + shortcuts->set_drag_forwarding_compat(this); // Adding event dialog shortcut_editor = memnew(InputEventConfigurationDialog); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 96c8aa6fb5..d8252bed9c 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -133,25 +133,26 @@ void EditorColorMap::create() { add_conversion_color_pair("#0e71fc", "#0350bd"); // New Autotile add_conversion_color_pair("#c6ced4", "#828f9b"); // New Atlas - // Visual script - add_conversion_color_pair("#41ecad", "#25e3a0"); // VisualScript variant - add_conversion_color_pair("#6f91f0", "#6d8eeb"); // VisualScript bool - add_conversion_color_pair("#5abbef", "#4fb2e9"); // VisualScript int - add_conversion_color_pair("#35d4f4", "#27ccf0"); // VisualScript float - add_conversion_color_pair("#4593ec", "#4690e7"); // VisualScript String - add_conversion_color_pair("#ac73f1", "#ad76ee"); // VisualScript Vector2 - add_conversion_color_pair("#f1738f", "#ee758e"); // VisualScript Rect2 - add_conversion_color_pair("#de66f0", "#dc6aed"); // VisualScript Vector3 - add_conversion_color_pair("#b9ec41", "#96ce1a"); // VisualScript Transform2D - add_conversion_color_pair("#f74949", "#f77070"); // VisualScript Plane - add_conversion_color_pair("#ec418e", "#ec69a3"); // VisualScript Quat - add_conversion_color_pair("#ee5677", "#ee7991"); // VisualScript AABB - add_conversion_color_pair("#e1ec41", "#b2bb19"); // VisualScript Basis - add_conversion_color_pair("#f68f45", "#f49047"); // VisualScript Transform - add_conversion_color_pair("#417aec", "#6993ec"); // VisualScript NodePath - add_conversion_color_pair("#41ec80", "#2ce573"); // VisualScript RID - add_conversion_color_pair("#55f3e3", "#12d5c3"); // VisualScript Object - add_conversion_color_pair("#54ed9e", "#57e99f"); // VisualScript Dictionary + // Variant types + add_conversion_color_pair("#41ecad", "#25e3a0"); // Variant + add_conversion_color_pair("#6f91f0", "#6d8eeb"); // bool + add_conversion_color_pair("#5abbef", "#4fb2e9"); // int + add_conversion_color_pair("#35d4f4", "#27ccf0"); // float + add_conversion_color_pair("#4593ec", "#4690e7"); // String + add_conversion_color_pair("#ac73f1", "#ad76ee"); // Vector2 + add_conversion_color_pair("#f1738f", "#ee758e"); // Rect2 + add_conversion_color_pair("#de66f0", "#dc6aed"); // Vector3 + add_conversion_color_pair("#b9ec41", "#96ce1a"); // Transform2D + add_conversion_color_pair("#f74949", "#f77070"); // Plane + add_conversion_color_pair("#ec418e", "#ec69a3"); // Quaternion + add_conversion_color_pair("#ee5677", "#ee7991"); // AABB + add_conversion_color_pair("#e1ec41", "#b2bb19"); // Basis + add_conversion_color_pair("#f68f45", "#f49047"); // Transform3D + add_conversion_color_pair("#417aec", "#6993ec"); // NodePath + add_conversion_color_pair("#41ec80", "#2ce573"); // RID + add_conversion_color_pair("#55f3e3", "#12d5c3"); // Object + add_conversion_color_pair("#54ed9e", "#57e99f"); // Dictionary + // Visual shaders add_conversion_color_pair("#77ce57", "#67c046"); // Vector funcs add_conversion_color_pair("#ea686c", "#d95256"); // Vector transforms diff --git a/editor/event_listener_line_edit.cpp b/editor/event_listener_line_edit.cpp index 3a99c4e264..274fe34c52 100644 --- a/editor/event_listener_line_edit.cpp +++ b/editor/event_listener_line_edit.cpp @@ -168,8 +168,8 @@ void EventListenerLineEdit::clear_event() { } } -void EventListenerLineEdit::set_allowed_input_types(int input_types) { - allowed_input_types = input_types; +void EventListenerLineEdit::set_allowed_input_types(int p_type_masks) { + allowed_input_types = p_type_masks; } int EventListenerLineEdit::get_allowed_input_types() const { diff --git a/editor/event_listener_line_edit.h b/editor/event_listener_line_edit.h index 0679733b6a..aa0cc91d47 100644 --- a/editor/event_listener_line_edit.h +++ b/editor/event_listener_line_edit.h @@ -67,7 +67,7 @@ public: Ref<InputEvent> get_event() const; void clear_event(); - void set_allowed_input_types(int input_types); + void set_allowed_input_types(int p_type_masks); int get_allowed_input_types() const; void grab_focus(); diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index 3e06633cc8..df5d2dcd29 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -1023,7 +1023,7 @@ ProjectExportDialog::ProjectExportDialog() { mc->set_v_size_flags(Control::SIZE_EXPAND_FILL); presets = memnew(ItemList); // TODO: Must reimplement drag forwarding. - //presets->set_drag_forwarding(this); + //presets->set_drag_forwarding_compat(this); mc->add_child(presets); presets->connect("item_selected", callable_mp(this, &ProjectExportDialog::_edit_preset)); duplicate_preset = memnew(Button); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index b5193fb75c..6eed3e3e61 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -3122,7 +3122,7 @@ FileSystemDock::FileSystemDock() { tree = memnew(Tree); tree->set_hide_root(true); - tree->set_drag_forwarding(this); + tree->set_drag_forwarding_compat(this); tree->set_allow_rmb_select(true); tree->set_select_mode(Tree::SELECT_MULTI); tree->set_custom_minimum_size(Size2(0, 15 * EDSCALE)); @@ -3159,7 +3159,7 @@ FileSystemDock::FileSystemDock() { files = memnew(ItemList); files->set_v_size_flags(SIZE_EXPAND_FILL); files->set_select_mode(ItemList::SELECT_MULTI); - files->set_drag_forwarding(this); + files->set_drag_forwarding_compat(this); files->connect("item_clicked", callable_mp(this, &FileSystemDock::_file_list_item_clicked)); files->connect("gui_input", callable_mp(this, &FileSystemDock::_file_list_gui_input)); files->connect("multi_selected", callable_mp(this, &FileSystemDock::_file_multi_selected)); diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp index b137f6f668..08d4bfff4a 100644 --- a/editor/input_event_configuration_dialog.cpp +++ b/editor/input_event_configuration_dialog.cpp @@ -515,6 +515,7 @@ Ref<InputEvent> InputEventConfigurationDialog::get_event() const { void InputEventConfigurationDialog::set_allowed_input_types(int p_type_masks) { allowed_input_types = p_type_masks; + event_listener->set_allowed_input_types(p_type_masks); } InputEventConfigurationDialog::InputEventConfigurationDialog() { diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index 0ebc780604..f680993026 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -425,6 +425,11 @@ void AnimationNodeBlendTreeEditor::_connection_from_empty(const String &p_to, in } } +void AnimationNodeBlendTreeEditor::_popup_hide() { + to_node = ""; + to_slot = -1; +} + void AnimationNodeBlendTreeEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, const StringName &p_which) { updating = true; Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); @@ -1096,6 +1101,7 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { add_node->set_text(TTR("Add Node...")); graph->get_zoom_hbox()->move_child(add_node, 0); add_node->get_popup()->connect("id_pressed", callable_mp(this, &AnimationNodeBlendTreeEditor::_add_node)); + add_node->get_popup()->connect("popup_hide", callable_mp(this, &AnimationNodeBlendTreeEditor::_popup_hide), CONNECT_DEFERRED); add_node->connect("about_to_popup", callable_mp(this, &AnimationNodeBlendTreeEditor::_update_options_menu).bind(false)); add_node->set_disabled(read_only); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h index b471d47df6..afb3394238 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.h +++ b/editor/plugins/animation_blend_tree_editor_plugin.h @@ -121,6 +121,7 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { void _popup_request(const Vector2 &p_position); void _connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position); void _connection_from_empty(const String &p_to, int p_to_slot, const Vector2 &p_release_position); + void _popup_hide(); void _property_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing); diff --git a/editor/plugins/control_editor_plugin.cpp b/editor/plugins/control_editor_plugin.cpp index 0fff16bf24..ad3e861b79 100644 --- a/editor/plugins/control_editor_plugin.cpp +++ b/editor/plugins/control_editor_plugin.cpp @@ -727,6 +727,7 @@ void ControlEditorToolbar::_anchors_preset_selected(int p_preset) { for (Node *E : selection) { Control *control = Object::cast_to<Control>(E); if (control) { + undo_redo->add_do_property(control, "layout_mode", LayoutMode::LAYOUT_MODE_ANCHORS); undo_redo->add_do_property(control, "anchors_preset", preset); undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state()); } diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 942b6b51cf..4fe1a6c034 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -4921,7 +4921,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p c->add_child(viewport); surface = memnew(Control); - surface->set_drag_forwarding(this); + surface->set_drag_forwarding_compat(this); add_child(surface); surface->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); surface->set_clip_contents(true); diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp index 27cad1cb7e..2a5529e229 100644 --- a/editor/plugins/resource_preloader_editor_plugin.cpp +++ b/editor/plugins/resource_preloader_editor_plugin.cpp @@ -379,7 +379,7 @@ ResourcePreloaderEditor::ResourcePreloaderEditor() { tree->set_column_expand(1, true); tree->set_v_size_flags(SIZE_EXPAND_FILL); - tree->set_drag_forwarding(this); + tree->set_drag_forwarding_compat(this); vbc->add_child(tree); dialog = memnew(AcceptDialog); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 628ed44aa1..caa42b677c 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -223,7 +223,6 @@ void ScriptEditorBase::_bind_methods() { ADD_SIGNAL(MethodInfo("request_open_script_at_line", PropertyInfo(Variant::OBJECT, "script"), PropertyInfo(Variant::INT, "line"))); ADD_SIGNAL(MethodInfo("request_save_history")); ADD_SIGNAL(MethodInfo("go_to_help", PropertyInfo(Variant::STRING, "what"))); - // TODO: This signal is no use for VisualScript. ADD_SIGNAL(MethodInfo("search_in_files_requested", PropertyInfo(Variant::STRING, "text"))); ADD_SIGNAL(MethodInfo("replace_in_files_requested", PropertyInfo(Variant::STRING, "text"))); ADD_SIGNAL(MethodInfo("go_to_method", PropertyInfo(Variant::OBJECT, "script"), PropertyInfo(Variant::STRING, "method"))); @@ -2205,8 +2204,7 @@ bool ScriptEditor::edit(const Ref<Resource> &p_resource, int p_line, int p_col, if (use_external_editor && (EditorDebuggerNode::get_singleton()->get_dump_stack_script() != p_resource || EditorDebuggerNode::get_singleton()->get_debug_with_external_editor()) && - p_resource->get_path().is_resource_file() && - !p_resource->is_class("VisualScript")) { + p_resource->get_path().is_resource_file()) { String path = EDITOR_GET("text_editor/external/exec_path"); String flags = EDITOR_GET("text_editor/external/exec_flags"); @@ -2305,21 +2303,20 @@ bool ScriptEditor::edit(const Ref<Resource> &p_resource, int p_line, int p_col, se->set_edited_resource(p_resource); - if (!p_resource->is_class("VisualScript")) { - bool highlighter_set = false; - for (int i = 0; i < syntax_highlighters.size(); i++) { - Ref<EditorSyntaxHighlighter> highlighter = syntax_highlighters[i]->_create(); - if (highlighter.is_null()) { - continue; - } - se->add_syntax_highlighter(highlighter); + // Syntax highlighting. + bool highlighter_set = false; + for (int i = 0; i < syntax_highlighters.size(); i++) { + Ref<EditorSyntaxHighlighter> highlighter = syntax_highlighters[i]->_create(); + if (highlighter.is_null()) { + continue; + } + se->add_syntax_highlighter(highlighter); - if (scr != nullptr && !highlighter_set) { - PackedStringArray languages = highlighter->_get_supported_languages(); - if (languages.has(scr->get_language()->get_name())) { - se->set_syntax_highlighter(highlighter); - highlighter_set = true; - } + if (scr != nullptr && !highlighter_set) { + PackedStringArray languages = highlighter->_get_supported_languages(); + if (languages.has(scr->get_language()->get_name())) { + se->set_syntax_highlighter(highlighter); + highlighter_set = true; } } } @@ -3672,7 +3669,7 @@ ScriptEditor::ScriptEditor() { _sort_list_on_update = true; script_list->connect("item_clicked", callable_mp(this, &ScriptEditor::_script_list_clicked), CONNECT_DEFERRED); script_list->set_allow_rmb_select(true); - script_list->set_drag_forwarding(this); + script_list->set_drag_forwarding_compat(this); context_menu = memnew(PopupMenu); add_child(context_menu); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 9406701ade..4d525cc5a9 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -2167,7 +2167,7 @@ ScriptTextEditor::ScriptTextEditor() { connection_info_dialog = memnew(ConnectionInfoDialog); - code_editor->get_text_editor()->set_drag_forwarding(this); + code_editor->get_text_editor()->set_drag_forwarding_compat(this); } ScriptTextEditor::~ScriptTextEditor() { diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index da6a1ea0af..a822584619 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -451,7 +451,7 @@ ShaderEditorPlugin::ShaderEditorPlugin() { vb->add_child(shader_list); shader_list->connect("item_selected", callable_mp(this, &ShaderEditorPlugin::_shader_selected)); shader_list->connect("item_clicked", callable_mp(this, &ShaderEditorPlugin::_shader_list_clicked)); - shader_list->set_drag_forwarding(this); + shader_list->set_drag_forwarding_compat(this); main_split->add_child(vb); vb->set_custom_minimum_size(Size2(200, 300) * EDSCALE); diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index 0c8fc08795..956150ec69 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -813,7 +813,7 @@ void Skeleton3DEditor::create_editors() { joint_tree->set_v_size_flags(SIZE_EXPAND_FILL); joint_tree->set_h_size_flags(SIZE_EXPAND_FILL); joint_tree->set_allow_rmb_select(true); - joint_tree->set_drag_forwarding(this); + joint_tree->set_drag_forwarding_compat(this); s_con->add_child(joint_tree); pose_editor = memnew(BoneTransformEditor(skeleton)); diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 66892372cf..7603a5e55d 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -1408,7 +1408,7 @@ SpriteFramesEditor::SpriteFramesEditor() { frame_list->set_max_columns(0); frame_list->set_icon_mode(ItemList::ICON_MODE_TOP); frame_list->set_max_text_lines(2); - frame_list->set_drag_forwarding(this); + frame_list->set_drag_forwarding_compat(this); frame_list->connect("gui_input", callable_mp(this, &SpriteFramesEditor::_frame_list_gui_input)); frame_list->connect("item_selected", callable_mp(this, &SpriteFramesEditor::_frame_list_item_selected)); diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index f3f0542b9b..ad5881c76f 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -650,7 +650,7 @@ TextEditor::TextEditor() { goto_line_dialog = memnew(GotoLineDialog); add_child(goto_line_dialog); - code_editor->get_text_editor()->set_drag_forwarding(this); + code_editor->get_text_editor()->set_drag_forwarding_compat(this); } TextEditor::~TextEditor() { diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index e7dc5d825b..dd4daa45b7 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -2236,7 +2236,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { scene_tiles_list = memnew(ItemList); scene_tiles_list->set_h_size_flags(Control::SIZE_EXPAND_FILL); scene_tiles_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); - scene_tiles_list->set_drag_forwarding(this); + scene_tiles_list->set_drag_forwarding_compat(this); scene_tiles_list->set_select_mode(ItemList::SELECT_MULTI); scene_tiles_list->connect("multi_selected", callable_mp(this, &TileMapEditorTilesPlugin::_scenes_list_multi_selected)); scene_tiles_list->connect("empty_clicked", callable_mp(this, &TileMapEditorTilesPlugin::_scenes_list_lmb_empty_clicked)); diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index d269a7c0ec..b44cb18dc7 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -728,7 +728,7 @@ TileSetEditor::TileSetEditor() { sources_list->add_user_signal(MethodInfo("sort_request")); sources_list->connect("sort_request", callable_mp(this, &TileSetEditor::_update_sources_list).bind(-1)); sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); - sources_list->set_drag_forwarding(this); + sources_list->set_drag_forwarding_compat(this); split_container_left_side->add_child(sources_list); HBoxContainer *sources_bottom_actions = memnew(HBoxContainer); diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp index 5af338caa0..0ff8788626 100644 --- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp @@ -509,7 +509,7 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() { scene_tiles_list = memnew(ItemList); scene_tiles_list->set_h_size_flags(SIZE_EXPAND_FILL); scene_tiles_list->set_v_size_flags(SIZE_EXPAND_FILL); - scene_tiles_list->set_drag_forwarding(this); + scene_tiles_list->set_drag_forwarding_compat(this); scene_tiles_list->connect("item_selected", callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_tile_inspector).unbind(1)); scene_tiles_list->connect("item_selected", callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_action_buttons).unbind(1)); scene_tiles_list->connect("item_activated", callable_mp(this, &TileSetScenesCollectionSourceEditor::_scenes_list_item_activated)); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 4cf397ffd0..46ec3bcdd1 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -4915,7 +4915,7 @@ VisualShaderEditor::VisualShaderEditor() { graph->set_h_size_flags(SIZE_EXPAND_FILL); graph->set_show_zoom_label(true); add_child(graph); - graph->set_drag_forwarding(this); + graph->set_drag_forwarding_compat(this); float graph_minimap_opacity = EDITOR_GET("editors/visual_editors/minimap_opacity"); graph->set_minimap_opacity(graph_minimap_opacity); float graph_lines_curvature = EDITOR_GET("editors/visual_editors/lines_curvature"); @@ -5146,7 +5146,7 @@ VisualShaderEditor::VisualShaderEditor() { members = memnew(Tree); members_vb->add_child(members); - members->set_drag_forwarding(this); + members->set_drag_forwarding_compat(this); members->set_h_size_flags(SIZE_EXPAND_FILL); members->set_v_size_flags(SIZE_EXPAND_FILL); members->set_hide_root(true); diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index 22d118cd90..8396d50a0e 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -122,9 +122,6 @@ static const char *enum_renames[][2] = { { "JOINT_SLIDER", "JOINT_TYPE_SLIDER" }, // PhysicsServer3D { "KEY_CONTROL", "KEY_CTRL" }, // Globals { "LOOP_PING_PONG", "LOOP_PINGPONG" }, // AudioStreamWAV - { "MATH_RAND", "MATH_RANDF_RANGE" }, // VisualScriptBuiltinFunc - { "MATH_RANDOM", "MATH_RANDI_RANGE" }, // VisualScriptBuiltinFunc - { "MATH_STEPIFY", "MATH_STEP_DECIMALS" }, // VisualScriptBuiltinFunc { "MODE_KINEMATIC", "FREEZE_MODE_KINEMATIC" }, // RigidBody { "MODE_OPEN_ANY", "FILE_MODE_OPEN_ANY" }, // FileDialog { "MODE_OPEN_DIR", "FILE_MODE_OPEN_DIR" }, // FileDialog @@ -239,6 +236,7 @@ static const char *gdscript_function_renames[][2] = { { "body_add_torque", "body_apply_torque" }, // PhysicsServer2D { "bumpmap_to_normalmap", "bump_map_to_normal_map" }, // Image { "can_be_hidden", "_can_be_hidden" }, // EditorNode3DGizmoPlugin + { "can_drop_data", "_can_drop_data" }, // Control { "can_drop_data_fw", "_can_drop_data_fw" }, // ScriptEditor { "can_generate_small_preview", "_can_generate_small_preview" }, // EditorResourcePreviewGenerator { "can_instance", "can_instantiate" }, // PackedScene, Script @@ -265,6 +263,7 @@ static const char *gdscript_function_renames[][2] = { { "delete_char_at_cursor", "delete_char_at_caret" }, // LineEdit { "deselect_items", "deselect_all" }, // FileDialog { "disable_plugin", "_disable_plugin" }, // EditorPlugin + { "drop_data", "_drop_data" }, // Control { "drop_data_fw", "_drop_data_fw" }, // ScriptEditor { "exclude_polygons_2d", "exclude_polygons" }, // Geometry2D { "find_node", "find_child" }, // Node @@ -694,6 +693,7 @@ static const char *csharp_function_renames[][2] = { { "BindChildNodeToBone", "SetBoneChildren" }, // Skeleton3D { "BumpmapToNormalmap", "BumpMapToNormalMap" }, // Image { "CanBeHidden", "_CanBeHidden" }, // EditorNode3DGizmoPlugin + { "CanDropData", "_CanDropData" }, // Control { "CanDropDataFw", "_CanDropDataFw" }, // ScriptEditor { "CanGenerateSmallPreview", "_CanGenerateSmallPreview" }, // EditorResourcePreviewGenerator { "CanInstance", "CanInstantiate" }, // PackedScene, Script @@ -717,6 +717,7 @@ static const char *csharp_function_renames[][2] = { { "DampedStringJointSetParam", "DampedSpringJointSetParam" }, // PhysicsServer2D { "DeleteCharAtCursor", "DeleteCharAtCaret" }, // LineEdit { "DeselectItems", "DeselectAll" }, // FileDialog + { "DropData", "_DropData" }, // Control { "DropDataFw", "_DropDataFw" }, // ScriptEditor { "ExcludePolygons2d", "ExcludePolygons" }, // Geometry2D { "FindScancodeFromString", "FindKeycodeFromString" }, // OS diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index dc755cab41..b4ffc7d78d 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -3008,7 +3008,10 @@ ProjectManager::ProjectManager() { SceneTree::get_singleton()->get_root()->connect("files_dropped", callable_mp(this, &ProjectManager::_files_dropped)); // Define a minimum window size to prevent UI elements from overlapping or being cut off. - DisplayServer::get_singleton()->window_set_min_size(Size2(520, 350) * EDSCALE); + Window *w = Object::cast_to<Window>(SceneTree::get_singleton()->get_root()); + if (w) { + w->set_min_size(Size2(520, 350) * EDSCALE); + } // Resize the bootsplash window based on Editor display scale EDSCALE. float scale_factor = MAX(1, EDSCALE); diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index dad584ea92..2f50172f54 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -1415,7 +1415,7 @@ SceneTreeEditor::SceneTreeEditor(bool p_label, bool p_can_rename, bool p_can_ope add_child(tree); - tree->set_drag_forwarding(this); + tree->set_drag_forwarding_compat(this); if (p_can_rename) { tree->set_allow_rmb_select(true); tree->connect("item_mouse_selected", callable_mp(this, &SceneTreeEditor::_rmb_select)); diff --git a/main/main.cpp b/main/main.cpp index d21574b0e3..190cdf3151 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2769,10 +2769,6 @@ bool Main::start() { DisplayServer::get_singleton()->window_set_title(appname); #endif - // Define a very small minimum window size to prevent bugs such as GH-37242. - // It can still be overridden by the user in a script. - DisplayServer::get_singleton()->window_set_min_size(Size2i(64, 64)); - bool snap_controls = GLOBAL_GET("gui/common/snap_controls_to_pixels"); sml->get_root()->set_snap_controls_to_pixels(snap_controls); diff --git a/methods.py b/methods.py index 99a59b49e3..ee88401671 100644 --- a/methods.py +++ b/methods.py @@ -111,6 +111,10 @@ def get_version_info(module_version_string="", silent=False): head = open(os.path.join(gitfolder, "HEAD"), "r", encoding="utf8").readline().strip() if head.startswith("ref: "): ref = head[5:] + # If this directory is a Git worktree instead of a root clone. + parts = gitfolder.split("/") + if len(parts) > 2 and parts[-2] == "worktrees": + gitfolder = "/".join(parts[0:-2]) head = os.path.join(gitfolder, ref) packedrefs = os.path.join(gitfolder, "packed-refs") if os.path.isfile(head): diff --git a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj index e717b501f4..16e58172b2 100644 --- a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj +++ b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj @@ -328,7 +328,11 @@ "$(PROJECT_DIR)/**", ); PRODUCT_BUNDLE_IDENTIFIER = $bundle_identifier; - PRODUCT_NAME = "$(TARGET_NAME)"; + INFOPLIST_KEY_CFBundleDisplayName = "$name"; + PRODUCT_NAME = "$binary"; + EXECUTABLE_NAME = "$binary"; + MARKETING_VERSION = $short_version; + CURRENT_PROJECT_VERSION = $version; PROVISIONING_PROFILE = "$provisioning_profile_uuid_debug"; TARGETED_DEVICE_FAMILY = "$targeted_device_family"; VALID_ARCHS = "arm64 x86_64"; @@ -360,7 +364,11 @@ "$(PROJECT_DIR)/**", ); PRODUCT_BUNDLE_IDENTIFIER = $bundle_identifier; - PRODUCT_NAME = "$(TARGET_NAME)"; + INFOPLIST_KEY_CFBundleDisplayName = "$name"; + PRODUCT_NAME = "$binary"; + EXECUTABLE_NAME = "$binary"; + MARKETING_VERSION = $short_version; + CURRENT_PROJECT_VERSION = $version; PROVISIONING_PROFILE = "$provisioning_profile_uuid_release"; TARGETED_DEVICE_FAMILY = "$targeted_device_family"; VALID_ARCHS = "arm64 x86_64"; diff --git a/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist b/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist index b88dfae5b2..ee5f1d35ae 100644 --- a/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist +++ b/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist @@ -5,9 +5,9 @@ <key>CFBundleDevelopmentRegion</key> <string>en</string> <key>CFBundleDisplayName</key> - <string>$name</string> + <string>$(INFOPLIST_KEY_CFBundleDisplayName)</string> <key>CFBundleExecutable</key> - <string>$binary</string> + <string>$(EXECUTABLE_NAME)</string> <key>CFBundleIcons</key> <dict/> <key>CFBundleIcons~ipad</key> @@ -17,15 +17,15 @@ <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundleName</key> - <string>$name</string> + <string>$(PRODUCT_NAME)</string> <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> - <string>$short_version</string> + <string>$(MARKETING_VERSION)</string> <key>CFBundleSignature</key> <string>$signature</string> <key>CFBundleVersion</key> - <string>$version</string> + <string>$(CURRENT_PROJECT_VERSION)</string> <key>ITSAppUsesNonExemptEncryption</key> <false /> <key>LSRequiresIPhoneOS</key> diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 11daf739d2..7bde4e7c4b 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -42,6 +42,9 @@ #include "gdscript_utility_functions.h" #include "scene/resources/packed_scene.h" +#define UNNAMED_ENUM "<anonymous enum>" +#define ENUM_SEPARATOR "::" + static MethodInfo info_from_utility_func(const StringName &p_function) { ERR_FAIL_COND_V(!Variant::has_utility_function(p_function), MethodInfo()); @@ -106,13 +109,26 @@ static GDScriptParser::DataType make_native_meta_type(const StringName &p_class_ return type; } -static GDScriptParser::DataType make_native_enum_type(const StringName &p_native_class, const StringName &p_enum_name) { +// In enum types, native_type is used to store the class (native or otherwise) that the enum belongs to. +// This disambiguates between similarly named enums in base classes or outer classes +static GDScriptParser::DataType make_enum_type(const StringName &p_enum_name, const String &p_base_name, const bool p_meta = false) { GDScriptParser::DataType type; type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; type.kind = GDScriptParser::DataType::ENUM; - type.builtin_type = Variant::INT; + type.builtin_type = p_meta ? Variant::DICTIONARY : Variant::INT; + type.enum_type = p_enum_name; type.is_constant = true; - type.is_meta_type = true; + type.is_meta_type = p_meta; + + // For enums, native_type is only used to check compatibility in is_type_compatible() + // We can set anything readable here for error messages, as long as it uniquely identifies the type of the enum + type.native_type = p_base_name + ENUM_SEPARATOR + p_enum_name; + + return type; +} + +static GDScriptParser::DataType make_native_enum_type(const StringName &p_enum_name, const StringName &p_native_class, const bool p_meta = true) { + GDScriptParser::DataType type = make_enum_type(p_enum_name, p_native_class, p_meta); List<StringName> enum_values; ClassDB::get_enum_constants(p_native_class, p_enum_name, &enum_values); @@ -134,6 +150,19 @@ static GDScriptParser::DataType make_builtin_meta_type(Variant::Type p_type) { return type; } +static StringName enum_get_value_name(const GDScriptParser::DataType p_type, int64_t p_val) { + // Check that an enum has a given value, not key. + // Make sure that implicit conversion to int64_t is sensible before calling! + HashMap<StringName, int64_t>::ConstIterator i = p_type.enum_values.begin(); + while (i) { + if (i->value == p_val) { + return i->key; + } + ++i; + } + return StringName(); +} + bool GDScriptAnalyzer::has_member_name_conflict_in_script_class(const StringName &p_member_name, const GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_member) { if (p_class->members_indices.has(p_member_name)) { int index = p_class->members_indices[p_member_name]; @@ -192,6 +221,7 @@ Error GDScriptAnalyzer::check_native_member_name_conflict(const StringName &p_me } Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::ClassNode *p_class_node, const StringName &p_member_name, const GDScriptParser::Node *p_member_node) { + // TODO check outer classes for static members only const GDScriptParser::DataType *current_data_type = &p_class_node->base_type; while (current_data_type && current_data_type->kind == GDScriptParser::DataType::Kind::CLASS) { GDScriptParser::ClassNode *current_class_node = current_data_type->class_type; @@ -220,9 +250,13 @@ Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::C } void GDScriptAnalyzer::get_class_node_current_scope_classes(GDScriptParser::ClassNode *p_node, List<GDScriptParser::ClassNode *> *p_list) { + ERR_FAIL_NULL(p_node); + ERR_FAIL_NULL(p_list); + if (p_list->find(p_node) != nullptr) { return; } + p_list->push_back(p_node); // TODO: Try to solve class inheritance if not yet resolving. @@ -591,12 +625,17 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type result = ref->get_parser()->head->get_datatype(); } else if (ClassDB::has_enum(parser->current_class->base_type.native_type, first)) { // Native enum in current class. - result = make_native_enum_type(parser->current_class->base_type.native_type, first); + result = make_native_enum_type(first, parser->current_class->base_type.native_type); } else { // Classes in current scope. List<GDScriptParser::ClassNode *> script_classes; + bool found = false; get_class_node_current_scope_classes(parser->current_class, &script_classes); for (GDScriptParser::ClassNode *script_class : script_classes) { + if (found) { + break; + } + if (script_class->identifier && script_class->identifier->name == first) { result = script_class->get_datatype(); break; @@ -608,14 +647,17 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type switch (member.type) { case GDScriptParser::ClassNode::Member::CLASS: result = member.get_datatype(); + found = true; break; case GDScriptParser::ClassNode::Member::ENUM: result = member.get_datatype(); + found = true; break; case GDScriptParser::ClassNode::Member::CONSTANT: if (member.get_datatype().is_meta_type) { result = member.get_datatype(); result.is_meta_type = false; + found = true; break; } else if (Ref<Script>(member.constant->initializer->reduced_value).is_valid()) { Ref<GDScript> gdscript = member.constant->initializer->reduced_value; @@ -636,6 +678,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type result.native_type = script->get_instance_base_type(); result.is_meta_type = false; } + found = true; break; } [[fallthrough]]; @@ -667,15 +710,17 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type } } else if (result.kind == GDScriptParser::DataType::NATIVE) { // Only enums allowed for native. - if (!ClassDB::has_enum(result.native_type, p_type->type_chain[1]->name)) { - push_error(vformat(R"(Could not find nested type "%s" under base "%s".)", p_type->type_chain[1]->name, result.to_string()), p_type->type_chain[1]); - return bad_type; - } - if (p_type->type_chain.size() > 2) { - push_error(R"(Enums cannot contain nested types.)", p_type->type_chain[2]); + if (ClassDB::has_enum(result.native_type, p_type->type_chain[1]->name)) { + if (p_type->type_chain.size() > 2) { + push_error(R"(Enums cannot contain nested types.)", p_type->type_chain[2]); + return bad_type; + } else { + result = make_native_enum_type(p_type->type_chain[1]->name, result.native_type); + } + } else { + push_error(vformat(R"(Could not find type "%s" in "%s".)", p_type->type_chain[1]->name, first), p_type->type_chain[1]); return bad_type; } - result = make_native_enum_type(result.native_type, p_type->type_chain[1]->name); } else { push_error(vformat(R"(Could not find nested type "%s" under base "%s".)", p_type->type_chain[1]->name, result.to_string()), p_type->type_chain[1]); return bad_type; @@ -804,15 +849,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, check_class_member_name_conflict(p_class, member.m_enum->identifier->name, member.m_enum); member.m_enum->set_datatype(resolving_datatype); - - GDScriptParser::DataType enum_type; - enum_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; - enum_type.kind = GDScriptParser::DataType::ENUM; - enum_type.builtin_type = Variant::DICTIONARY; - enum_type.enum_type = member.m_enum->identifier->name; - enum_type.native_type = p_class->fqcn + "." + member.m_enum->identifier->name; - enum_type.is_meta_type = true; - enum_type.is_constant = true; + GDScriptParser::DataType enum_type = make_enum_type(member.m_enum->identifier->name, p_class->fqcn, true); const GDScriptParser::EnumNode *prev_enum = current_enum; current_enum = member.m_enum; @@ -846,6 +883,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, current_enum = prev_enum; + dictionary.set_read_only(true); member.m_enum->set_datatype(enum_type); member.m_enum->dictionary = dictionary; @@ -892,11 +930,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, // Also update the original references. member.enum_value.parent_enum->values.set(member.enum_value.index, member.enum_value); - GDScriptParser::DataType datatype; - datatype.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; - datatype.kind = GDScriptParser::DataType::BUILTIN; - datatype.builtin_type = Variant::INT; - member.enum_value.identifier->set_datatype(datatype); + member.enum_value.identifier->set_datatype(make_enum_type(UNNAMED_ENUM, p_class->fqcn, false)); } break; case GDScriptParser::ClassNode::Member::CLASS: check_class_member_name_conflict(p_class, member.m_class->identifier->name, member.m_class); @@ -1330,7 +1364,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * } } else { if (p_function->return_type != nullptr) { - p_function->set_datatype(resolve_datatype(p_function->return_type)); + p_function->set_datatype(type_from_metatype(resolve_datatype(p_function->return_type))); } else { // In case the function is not typed, we can safely assume it's a Variant, so it's okay to mark as "inferred" here. // It's not "undetected" to not mix up with unknown functions. @@ -1364,7 +1398,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * if (!valid) { // Compute parent signature as a string to show in the error message. - String parent_signature = function_name.operator String() + "("; + String parent_signature = String(function_name) + "("; int j = 0; for (const GDScriptParser::DataType &par_type : parameters_types) { if (j > 0) { @@ -1507,9 +1541,9 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi if (is_constant) { if (p_assignable->initializer->type == GDScriptParser::Node::ARRAY) { - const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer)); + const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer), true); } else if (p_assignable->initializer->type == GDScriptParser::Node::DICTIONARY) { - const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_assignable->initializer)); + const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_assignable->initializer), true); } if (!p_assignable->initializer->is_constant) { push_error(vformat(R"(Assigned value for %s "%s" isn't a constant expression.)", p_kind, p_assignable->identifier->name), p_assignable->initializer); @@ -2063,6 +2097,10 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig GDScriptParser::DataType assignee_type = p_assignment->assignee->get_datatype(); + if (assignee_type.is_constant || (p_assignment->assignee->type == GDScriptParser::Node::SUBSCRIPT && static_cast<GDScriptParser::SubscriptNode *>(p_assignment->assignee)->base->is_constant)) { + push_error("Cannot assign a new value to a constant.", p_assignment->assignee); + } + // Check if assigned value is an array literal, so we can make it a typed array too if appropriate. if (assignee_type.has_container_element_type() && p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY) { update_array_literal_element_type(assignee_type, static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value)); @@ -2070,24 +2108,22 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig GDScriptParser::DataType assigned_value_type = p_assignment->assigned_value->get_datatype(); - if (assignee_type.is_constant) { - push_error("Cannot assign a new value to a constant.", p_assignment->assignee); - } - bool compatible = true; GDScriptParser::DataType op_type = assigned_value_type; - if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) { + if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE && !op_type.is_variant()) { op_type = get_operation_type(p_assignment->variant_op, assignee_type, assigned_value_type, compatible, p_assignment->assigned_value); } p_assignment->set_datatype(op_type); - if (assignee_type.is_hard_type() && !assignee_type.is_variant() && op_type.is_hard_type()) { + // If Assignee is a variant, then you can assign anything + // When the assigned value has a known type, further checks are possible. + if (assignee_type.is_hard_type() && !assignee_type.is_variant() && op_type.is_hard_type() && !op_type.is_variant()) { if (compatible) { compatible = is_type_compatible(assignee_type, op_type, true, p_assignment->assigned_value); if (!compatible) { // Try reverse test since it can be a masked subtype. if (!is_type_compatible(op_type, assignee_type, true)) { - push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value); + push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value); } else { // TODO: Add warning. mark_node_unsafe(p_assignment); @@ -2141,7 +2177,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig if (!id_type.is_hard_type()) { id_type.kind = GDScriptParser::DataType::VARIANT; id_type.type_source = GDScriptParser::DataType::UNDETECTED; - identifier->variable_source->set_datatype(id_type); + identifier->bind_source->set_datatype(id_type); } } break; default: @@ -2368,7 +2404,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a switch (err.error) { case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: - push_error(vformat(R"(Invalid argument for %s constructor: argument %d should be %s but is %s.)", Variant::get_type_name(builtin_type), err.argument + 1, + push_error(vformat(R"(Invalid argument for %s constructor: argument %d should be "%s" but is "%s".)", Variant::get_type_name(builtin_type), err.argument + 1, Variant::get_type_name(Variant::Type(err.expected)), p_call->arguments[err.argument]->get_datatype().to_string()), p_call->arguments[err.argument]); break; @@ -2484,7 +2520,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a switch (err.error) { case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: { PropertyInfo wrong_arg = function_info.arguments[err.argument]; - push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", function_name, err.argument + 1, + push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*", function_name, err.argument + 1, type_from_property(wrong_arg).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()), p_call->arguments[err.argument]); } break; @@ -2537,7 +2573,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a expected_type_name = Variant::get_type_name((Variant::Type)err.expected); } - push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", function_name, err.argument + 1, + push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*", function_name, err.argument + 1, expected_type_name, p_call->arguments[err.argument]->get_datatype().to_string()), p_call->arguments[err.argument]); } break; @@ -2683,8 +2719,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a } else { bool found = false; - // Check if the name exists as something else. - if (!p_call->is_super && callee_type != GDScriptParser::Node::NONE) { + // Enums do not have functions other than the built-in dictionary ones. + if (base_type.kind == GDScriptParser::DataType::ENUM && base_type.is_meta_type) { + push_error(vformat(R"*(Enums only have Dictionary built-in methods. Function "%s()" does not exist for enum "%s".)*", p_call->function_name, base_type.enum_type), p_call->callee); + } else if (!p_call->is_super && callee_type != GDScriptParser::Node::NONE) { // Check if the name exists as something else. GDScriptParser::IdentifierNode *callee_id; if (callee_type == GDScriptParser::Node::IDENTIFIER) { callee_id = static_cast<GDScriptParser::IdentifierNode *>(p_call->callee); @@ -2714,7 +2752,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string(); push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee); } else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::NATIVE && base_type.is_meta_type)) { - push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.native_type.operator String()), p_call); + push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.native_type), p_call); } } @@ -2742,19 +2780,48 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) { GDScriptParser::DataType op_type = p_cast->operand->get_datatype(); if (!op_type.is_variant()) { bool valid = false; + bool more_informative_error = false; if (op_type.kind == GDScriptParser::DataType::ENUM && cast_type.kind == GDScriptParser::DataType::ENUM) { - // Enum types are compatible between each other, so it's a safe cast. - valid = true; + // Enum casts are compatible when value from operand exists in target enum + if (p_cast->operand->is_constant && p_cast->operand->reduced) { + if (enum_get_value_name(cast_type, p_cast->operand->reduced_value) != StringName()) { + valid = true; + } else { + valid = false; + more_informative_error = true; + push_error(vformat(R"(Invalid cast. Enum "%s" does not have value corresponding to "%s.%s" (%d).)", + cast_type.to_string(), op_type.enum_type, + enum_get_value_name(op_type, p_cast->operand->reduced_value), // Can never be null + p_cast->operand->reduced_value.operator uint64_t()), + p_cast->cast_type); + } + } else { + // Can't statically tell whether int has a corresponding enum value. Valid but dangerous! + mark_node_unsafe(p_cast); + valid = true; + } } else if (op_type.kind == GDScriptParser::DataType::BUILTIN && op_type.builtin_type == Variant::INT && cast_type.kind == GDScriptParser::DataType::ENUM) { - // Convertint int to enum is always valid. - valid = true; + // Int assignment to enum not valid when exact int assigned is known but is not an enum value + if (p_cast->operand->is_constant && p_cast->operand->reduced) { + if (enum_get_value_name(cast_type, p_cast->operand->reduced_value) != StringName()) { + valid = true; + } else { + valid = false; + more_informative_error = true; + push_error(vformat(R"(Invalid cast. Enum "%s" does not have enum value %d.)", cast_type.to_string(), p_cast->operand->reduced_value.operator uint64_t()), p_cast->cast_type); + } + } else { + // Can't statically tell whether int has a corresponding enum value. Valid but dangerous! + mark_node_unsafe(p_cast); + valid = true; + } } else if (op_type.kind == GDScriptParser::DataType::BUILTIN && cast_type.kind == GDScriptParser::DataType::BUILTIN) { valid = Variant::can_convert(op_type.builtin_type, cast_type.builtin_type); } else if (op_type.kind != GDScriptParser::DataType::BUILTIN && cast_type.kind != GDScriptParser::DataType::BUILTIN) { valid = is_type_compatible(cast_type, op_type) || is_type_compatible(op_type, cast_type); } - if (!valid) { + if (!valid && !more_informative_error) { push_error(vformat(R"(Invalid cast. Cannot convert from "%s" to "%s".)", op_type.to_string(), cast_type.to_string()), p_cast->cast_type); } } @@ -2852,6 +2919,18 @@ GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const Str } } +void GDScriptAnalyzer::reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype) { + ERR_FAIL_NULL(p_identifier); + + p_identifier->set_datatype(p_identifier_datatype); + Error err = OK; + GDScript *scr = GDScriptCache::get_full_script(p_identifier_datatype.script_path, err).ptr(); + ERR_FAIL_COND_MSG(err != OK, "Error while getting full script."); + scr = scr->find_class(p_identifier_datatype.class_type->fqcn); + p_identifier->reduced_value = scr; + p_identifier->is_constant = true; +} + void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType *p_base) { if (!p_identifier->get_datatype().has_no_type()) { return; @@ -2869,26 +2948,14 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod if (base.kind == GDScriptParser::DataType::ENUM) { if (base.is_meta_type) { if (base.enum_values.has(name)) { + p_identifier->set_datatype(type_from_metatype(base)); p_identifier->is_constant = true; p_identifier->reduced_value = base.enum_values[name]; - - GDScriptParser::DataType result; - result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; - result.kind = GDScriptParser::DataType::ENUM; - result.is_constant = true; - result.builtin_type = Variant::INT; - result.native_type = base.native_type; - result.enum_type = base.enum_type; - result.enum_values = base.enum_values; - p_identifier->set_datatype(result); return; - } else { - // Consider as a Dictionary, so it can be anything. - // This will be evaluated in the next if block. - base.kind = GDScriptParser::DataType::BUILTIN; - base.builtin_type = Variant::DICTIONARY; - base.is_meta_type = false; } + + // Enum does not have this value, return. + return; } else { push_error(R"(Cannot get property from enum value.)", p_identifier); return; @@ -2942,102 +3009,91 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod } GDScriptParser::ClassNode *base_class = base.class_type; + List<GDScriptParser::ClassNode *> script_classes; + bool is_base = true; - // TODO: Switch current class/function/suite here to avoid misrepresenting identifiers (in recursive reduce calls). - while (base_class != nullptr) { - if (base_class->identifier && base_class->identifier->name == name) { - p_identifier->set_datatype(base_class->get_datatype()); + if (base_class != nullptr) { + get_class_node_current_scope_classes(base_class, &script_classes); + } + + for (GDScriptParser::ClassNode *script_class : script_classes) { + if (p_base == nullptr && script_class->identifier && script_class->identifier->name == name) { + reduce_identifier_from_base_set_class(p_identifier, script_class->get_datatype()); return; } - if (base_class->has_member(name)) { - resolve_class_member(base_class, name, p_identifier); + if (script_class->has_member(name)) { + resolve_class_member(script_class, name, p_identifier); - GDScriptParser::ClassNode::Member member = base_class->get_member(name); - p_identifier->set_datatype(member.get_datatype()); + GDScriptParser::ClassNode::Member member = script_class->get_member(name); switch (member.type) { - case GDScriptParser::ClassNode::Member::CONSTANT: + case GDScriptParser::ClassNode::Member::CONSTANT: { + p_identifier->set_datatype(member.get_datatype()); p_identifier->is_constant = true; p_identifier->reduced_value = member.constant->initializer->reduced_value; p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT; p_identifier->constant_source = member.constant; - break; - case GDScriptParser::ClassNode::Member::ENUM_VALUE: + return; + } + + case GDScriptParser::ClassNode::Member::ENUM_VALUE: { + p_identifier->set_datatype(member.get_datatype()); p_identifier->is_constant = true; p_identifier->reduced_value = member.enum_value.value; p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT; - break; - case GDScriptParser::ClassNode::Member::ENUM: + return; + } + + case GDScriptParser::ClassNode::Member::ENUM: { + p_identifier->set_datatype(member.get_datatype()); p_identifier->is_constant = true; p_identifier->reduced_value = member.m_enum->dictionary; p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT; - break; - case GDScriptParser::ClassNode::Member::VARIABLE: - p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE; - p_identifier->variable_source = member.variable; - member.variable->usages += 1; - break; - case GDScriptParser::ClassNode::Member::SIGNAL: - p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_SIGNAL; - break; - case GDScriptParser::ClassNode::Member::FUNCTION: - p_identifier->set_datatype(make_callable_type(member.function->info)); - break; - case GDScriptParser::ClassNode::Member::CLASS: - if (p_base != nullptr && p_base->is_constant) { - p_identifier->is_constant = true; - p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT; - - Error err = OK; - GDScript *scr = GDScriptCache::get_full_script(base.script_path, err).ptr(); - ERR_FAIL_COND_MSG(err != OK, "Error while getting subscript full script."); - scr = scr->find_class(p_identifier->get_datatype().class_type->fqcn); - p_identifier->reduced_value = scr; + return; + } + + case GDScriptParser::ClassNode::Member::VARIABLE: { + if (is_base && !base.is_meta_type) { + p_identifier->set_datatype(member.get_datatype()); + p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE; + p_identifier->variable_source = member.variable; + member.variable->usages += 1; + return; } - break; - default: - break; // Type already set. - } - return; - } - // Check outer constants. - // TODO: Allow outer static functions. - if (base_class->outer != nullptr) { - List<GDScriptParser::ClassNode *> script_classes; - get_class_node_current_scope_classes(base_class->outer, &script_classes); - for (GDScriptParser::ClassNode *script_class : script_classes) { - if (script_class->has_member(name)) { - resolve_class_member(script_class, name, p_identifier); - - GDScriptParser::ClassNode::Member member = script_class->get_member(name); - switch (member.type) { - case GDScriptParser::ClassNode::Member::CONSTANT: - // TODO: Make sure loops won't cause problem. And make special error message for those. - p_identifier->set_datatype(member.get_datatype()); - p_identifier->is_constant = true; - p_identifier->reduced_value = member.constant->initializer->reduced_value; - return; - case GDScriptParser::ClassNode::Member::ENUM_VALUE: - p_identifier->set_datatype(member.get_datatype()); - p_identifier->is_constant = true; - p_identifier->reduced_value = member.enum_value.value; - return; - case GDScriptParser::ClassNode::Member::ENUM: - p_identifier->set_datatype(member.get_datatype()); - p_identifier->is_constant = true; - p_identifier->reduced_value = member.m_enum->dictionary; - return; - case GDScriptParser::ClassNode::Member::CLASS: - p_identifier->set_datatype(member.get_datatype()); - return; - default: - break; + } break; + + case GDScriptParser::ClassNode::Member::SIGNAL: { + if (is_base && !base.is_meta_type) { + p_identifier->set_datatype(member.get_datatype()); + p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_SIGNAL; + return; + } + } break; + + case GDScriptParser::ClassNode::Member::FUNCTION: { + if (is_base && !base.is_meta_type) { + p_identifier->set_datatype(make_callable_type(member.function->info)); + return; } + } break; + + case GDScriptParser::ClassNode::Member::CLASS: { + reduce_identifier_from_base_set_class(p_identifier, member.get_datatype()); + return; + } + + default: { + // Do nothing } } } - base_class = base_class->base_type.class_type; + if (is_base) { + is_base = script_class->base_type.class_type != nullptr; + if (!is_base && p_base != nullptr) { + break; + } + } } // Check native members. No need for native class recursion because Node exposes all Object's properties. @@ -3067,35 +3123,39 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod return; } if (ClassDB::has_enum(native, name)) { - p_identifier->set_datatype(make_native_enum_type(native, name)); + p_identifier->set_datatype(make_native_enum_type(name, native)); p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT; return; } bool valid = false; + int64_t int_constant = ClassDB::get_integer_constant(native, name, &valid); if (valid) { p_identifier->is_constant = true; p_identifier->reduced_value = int_constant; - p_identifier->set_datatype(type_from_variant(int_constant, p_identifier)); p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT; - return; + + // Check whether this constant, which exists, belongs to an enum + StringName enum_name = ClassDB::get_integer_constant_enum(native, name); + if (enum_name != StringName()) { + p_identifier->set_datatype(make_native_enum_type(enum_name, native, false)); + } else { + p_identifier->set_datatype(type_from_variant(int_constant, p_identifier)); + } } } } void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_identifier, bool can_be_builtin) { - // TODO: This is opportunity to further infer types. + // TODO: This is an opportunity to further infer types. - // Check if we are inside and enum. This allows enum values to access other elements of the same enum. + // Check if we are inside an enum. This allows enum values to access other elements of the same enum. if (current_enum) { for (int i = 0; i < current_enum->values.size(); i++) { const GDScriptParser::EnumNode::Value &element = current_enum->values[i]; if (element.identifier->name == p_identifier->name) { - GDScriptParser::DataType type; - type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; - type.kind = element.parent_enum->identifier ? GDScriptParser::DataType::ENUM : GDScriptParser::DataType::BUILTIN; - type.builtin_type = Variant::INT; - type.is_constant = true; + StringName enum_name = current_enum->identifier->name ? current_enum->identifier->name : UNNAMED_ENUM; + GDScriptParser::DataType type = make_enum_type(enum_name, parser->current_class->fqcn, false); if (element.parent_enum->identifier) { type.enum_type = element.parent_enum->identifier->name; } @@ -3164,18 +3224,20 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident } if (found_source) { - if ((p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) && parser->current_function && parser->current_function->is_static) { + bool source_is_variable = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE; + bool source_is_signal = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_SIGNAL; + if ((source_is_variable || source_is_signal) && parser->current_function && parser->current_function->is_static) { // Get the parent function above any lambda. GDScriptParser::FunctionNode *parent_function = parser->current_function; while (parent_function->source_lambda) { parent_function = parent_function->source_lambda->parent_function; } - push_error(vformat(R"*(Cannot access instance variable "%s" from the static function "%s()".)*", p_identifier->name, parent_function->identifier->name), p_identifier); + push_error(vformat(R"*(Cannot access %s "%s" from the static function "%s()".)*", source_is_signal ? "signal" : "instance variable", p_identifier->name, parent_function->identifier->name), p_identifier); } if (!lambda_stack.is_empty()) { - // If the identifier is a member variable (including the native class properties), we consider the lambda to be using `self`, so we keep a reference to the current instance. - if (p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) { + // If the identifier is a member variable (including the native class properties) or a signal, we consider the lambda to be using `self`, so we keep a reference to the current instance. + if (source_is_variable || source_is_signal) { mark_lambda_use_self(); return; // No need to capture. } @@ -3411,9 +3473,9 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri reduce_expression(p_subscript->base); if (p_subscript->base->type == GDScriptParser::Node::ARRAY) { - const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_subscript->base)); + const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_subscript->base), false); } else if (p_subscript->base->type == GDScriptParser::Node::DICTIONARY) { - const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_subscript->base)); + const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_subscript->base), false); } } @@ -3473,12 +3535,12 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri Variant value = p_subscript->base->reduced_value.get(p_subscript->index->reduced_value, &valid); if (!valid) { push_error(vformat(R"(Cannot get index "%s" from "%s".)", p_subscript->index->reduced_value, p_subscript->base->reduced_value), p_subscript->index); + result_type.kind = GDScriptParser::DataType::VARIANT; } else { p_subscript->is_constant = true; p_subscript->reduced_value = value; result_type = type_from_variant(value, p_subscript); } - result_type.kind = GDScriptParser::DataType::VARIANT; } else { GDScriptParser::DataType base_type = p_subscript->base->get_datatype(); GDScriptParser::DataType index_type = p_subscript->index->get_datatype(); @@ -3738,20 +3800,17 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op) p_unary_op->set_datatype(result); } -void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array) { - bool all_is_constant = true; - +void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array, bool p_is_const) { for (int i = 0; i < p_array->elements.size(); i++) { GDScriptParser::ExpressionNode *element = p_array->elements[i]; if (element->type == GDScriptParser::Node::ARRAY) { - const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element)); + const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element), p_is_const); } else if (element->type == GDScriptParser::Node::DICTIONARY) { - const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element)); + const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element), p_is_const); } - all_is_constant = all_is_constant && element->is_constant; - if (!all_is_constant) { + if (!element->is_constant) { return; } } @@ -3761,24 +3820,24 @@ void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array) { for (int i = 0; i < p_array->elements.size(); i++) { array[i] = p_array->elements[i]->reduced_value; } + if (p_is_const) { + array.set_read_only(true); + } p_array->is_constant = true; p_array->reduced_value = array; } -void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary) { - bool all_is_constant = true; - +void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary, bool p_is_const) { for (int i = 0; i < p_dictionary->elements.size(); i++) { const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i]; if (element.value->type == GDScriptParser::Node::ARRAY) { - const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element.value)); + const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element.value), p_is_const); } else if (element.value->type == GDScriptParser::Node::DICTIONARY) { - const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element.value)); + const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element.value), p_is_const); } - all_is_constant = all_is_constant && element.key->is_constant && element.value->is_constant; - if (!all_is_constant) { + if (!element.key->is_constant || !element.value->is_constant) { return; } } @@ -3788,6 +3847,9 @@ void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_d const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i]; dict[element.key->reduced_value] = element.value->reduced_value; } + if (p_is_const) { + dict.set_read_only(true); + } p_dictionary->is_constant = true; p_dictionary->reduced_value = dict; } @@ -3865,12 +3927,13 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va return result; } -GDScriptParser::DataType GDScriptAnalyzer::type_from_metatype(const GDScriptParser::DataType &p_meta_type) const { +GDScriptParser::DataType GDScriptAnalyzer::type_from_metatype(const GDScriptParser::DataType &p_meta_type) { GDScriptParser::DataType result = p_meta_type; result.is_meta_type = false; - result.is_constant = false; if (p_meta_type.kind == GDScriptParser::DataType::ENUM) { result.builtin_type = Variant::INT; + } else { + result.is_constant = false; } return result; } @@ -3928,11 +3991,12 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo r_default_arg_count = 0; StringName function_name = p_function; + bool was_enum = false; if (p_base_type.kind == GDScriptParser::DataType::ENUM) { + was_enum = true; if (p_base_type.is_meta_type) { // Enum type can be treated as a dictionary value. p_base_type.kind = GDScriptParser::DataType::BUILTIN; - p_base_type.builtin_type = Variant::DICTIONARY; p_base_type.is_meta_type = false; } else { push_error("Cannot call function on enum value.", p_source); @@ -3955,6 +4019,10 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo if (E.name == p_function) { function_signature_from_info(E, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg); r_static = Variant::is_builtin_method_static(p_base_type.builtin_type, function_name); + // Cannot use non-const methods on enums. + if (!r_static && was_enum && !(E.flags & METHOD_FLAG_CONST)) { + push_error(vformat(R"*(Cannot call non-const Dictionary function "%s()" on enum "%s".)*", p_function, p_base_type.enum_type), p_source); + } return true; } } @@ -4102,7 +4170,7 @@ bool GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p // Supertypes are acceptable for dynamic compliance, but it's unsafe. mark_node_unsafe(p_call); if (!is_type_compatible(arg_type, par_type)) { - push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", + push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*", p_call->function_name, i + 1, par_type.to_string(), arg_type.to_string()), p_call->arguments[i]); valid = false; diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index a7f8e3b556..ecae0b4629 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -102,12 +102,12 @@ class GDScriptAnalyzer { void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op); void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op); - void const_fold_array(GDScriptParser::ArrayNode *p_array); - void const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary); + void const_fold_array(GDScriptParser::ArrayNode *p_array, bool p_is_const); + void const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary, bool p_is_const); // Helpers. GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source); - GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type) const; + static GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type); GDScriptParser::DataType type_from_property(const PropertyInfo &p_property) const; GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source); bool get_function_signature(GDScriptParser::Node *p_source, bool p_is_constructor, GDScriptParser::DataType base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg); @@ -123,6 +123,7 @@ class GDScriptAnalyzer { void mark_lambda_use_self(); bool class_exists(const StringName &p_class) const; Ref<GDScriptParserRef> get_parser_for(const String &p_path); + static void reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype); #ifdef DEBUG_ENABLED bool is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context); #endif diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 640d7dca2f..beed6e90d2 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -213,7 +213,7 @@ static bool _have_exact_arguments(const MethodBind *p_method, const Vector<GDScr } GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root, bool p_initializer, const GDScriptCodeGenerator::Address &p_index_addr) { - if (p_expression->is_constant) { + if (p_expression->is_constant && !p_expression->get_datatype().is_meta_type) { return codegen.add_constant(p_expression->reduced_value); } diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index dcf17b9fe7..0a1ae46927 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -995,9 +995,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT); GDScriptParser::DataType base_type = p_base.type; - bool _static = base_type.is_meta_type; - if (_static && base_type.kind != GDScriptParser::DataType::BUILTIN) { + if (base_type.is_meta_type && base_type.kind != GDScriptParser::DataType::BUILTIN && base_type.kind != GDScriptParser::DataType::ENUM) { ScriptLanguage::CodeCompletionOption option("new", ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, ScriptLanguage::LOCATION_LOCAL); option.insert_text += "("; r_result.insert(option.display, option); @@ -1006,7 +1005,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base while (!base_type.has_no_type()) { switch (base_type.kind) { case GDScriptParser::DataType::CLASS: { - _find_identifiers_in_class(base_type.class_type, p_only_functions, _static, false, r_result, p_recursion_depth + 1); + _find_identifiers_in_class(base_type.class_type, p_only_functions, base_type.is_meta_type, false, r_result, p_recursion_depth + 1); // This already finds all parent identifiers, so we are done. base_type = GDScriptParser::DataType(); } break; @@ -1014,7 +1013,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base Ref<Script> scr = base_type.script_type; if (scr.is_valid()) { if (!p_only_functions) { - if (!_static) { + if (!base_type.is_meta_type) { List<PropertyInfo> members; scr->get_script_property_list(&members); for (const PropertyInfo &E : members) { @@ -1090,7 +1089,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base r_result.insert(option.display, option); } - if (!_static || Engine::get_singleton()->has_singleton(type)) { + if (!base_type.is_meta_type || Engine::get_singleton()->has_singleton(type)) { List<PropertyInfo> pinfo; ClassDB::get_property_list(type, &pinfo); for (const PropertyInfo &E : pinfo) { @@ -1107,7 +1106,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base } } - bool only_static = _static && !Engine::get_singleton()->has_singleton(type); + bool only_static = base_type.is_meta_type && !Engine::get_singleton()->has_singleton(type); List<MethodInfo> methods; ClassDB::get_method_list(type, &methods, false, true); @@ -1129,6 +1128,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base } return; } break; + case GDScriptParser::DataType::ENUM: case GDScriptParser::DataType::BUILTIN: { Callable::CallError err; Variant tmp; @@ -1156,6 +1156,10 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base List<MethodInfo> methods; tmp.get_method_list(&methods); for (const MethodInfo &E : methods) { + if (base_type.kind == GDScriptParser::DataType::ENUM && base_type.is_meta_type && !(E.flags & METHOD_FLAG_CONST)) { + // Enum types are static and cannot change, therefore we skip non-const dictionary methods. + continue; + } ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); if (E.arguments.size()) { option.insert_text += "("; @@ -1364,6 +1368,9 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, if (p_expression->is_constant) { // Already has a value, so just use that. r_type = _type_from_variant(p_expression->reduced_value); + if (p_expression->get_datatype().kind == GDScriptParser::DataType::ENUM) { + r_type.type = p_expression->get_datatype(); + } found = true; } else { switch (p_expression->type) { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index add8a02e55..a6b8537074 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -1299,16 +1299,18 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() { EnumNode::Value item; GDScriptParser::IdentifierNode *identifier = parse_identifier(); #ifdef DEBUG_ENABLED - for (MethodInfo &info : gdscript_funcs) { - if (info.name == identifier->name) { + if (!named) { // Named enum identifiers do not shadow anything since you can only access them with NamedEnum.ENUM_VALUE + for (MethodInfo &info : gdscript_funcs) { + if (info.name == identifier->name) { + push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "built-in function"); + } + } + if (Variant::has_utility_function(identifier->name)) { push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "built-in function"); + } else if (ClassDB::class_exists(identifier->name)) { + push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "global class"); } } - if (Variant::has_utility_function(identifier->name)) { - push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "built-in function"); - } else if (ClassDB::class_exists(identifier->name)) { - push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "global class"); - } #endif item.identifier = identifier; item.parent_enum = enum_node; @@ -4092,8 +4094,11 @@ String GDScriptParser::DataType::to_string() const { } return native_type.operator String(); } - case ENUM: - return enum_type.operator String() + " (enum)"; + case ENUM: { + // native_type contains either the native class defining the enum + // or the fully qualified class name of the script defining the enum + return String(native_type).get_file(); // Remove path, keep filename + } case RESOLVING: case UNRESOLVED: return "<unresolved type>"; diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 65eace8088..4bb02c4ea3 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -185,8 +185,8 @@ public: case BUILTIN: return builtin_type == p_other.builtin_type; case NATIVE: - case ENUM: - return native_type == p_other.native_type && enum_type == p_other.enum_type; + case ENUM: // Enums use native_type to identify the enum and its base class. + return native_type == p_other.native_type; case SCRIPT: return script_type == p_other.script_type; case CLASS: diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_enum.gd b/modules/gdscript/tests/scripts/analyzer/errors/assign_enum.gd new file mode 100644 index 0000000000..8123fc53d9 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_enum.gd @@ -0,0 +1,3 @@ +enum { V } +func test(): + V = 1 diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_enum.out b/modules/gdscript/tests/scripts/analyzer/errors/assign_enum.out new file mode 100644 index 0000000000..5275183da2 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_enum.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot assign a new value to a constant. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.gd b/modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.gd new file mode 100644 index 0000000000..da2b13d690 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.gd @@ -0,0 +1,3 @@ +enum NamedEnum { V } +func test(): + NamedEnum.V = 1 diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.out b/modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.out new file mode 100644 index 0000000000..5275183da2 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot assign a new value to a constant. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.gd b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.gd new file mode 100644 index 0000000000..71616ea3af --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.gd @@ -0,0 +1,5 @@ +enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 } +enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2, OTHER_ENUM_VALUE_3 } + +func test(): + print(MyOtherEnum.OTHER_ENUM_VALUE_3 as MyEnum) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.out b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.out new file mode 100644 index 0000000000..3a8d2a205a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Invalid cast. Enum "cast_enum_bad_enum.gd::MyEnum" does not have value corresponding to "MyOtherEnum.OTHER_ENUM_VALUE_3" (2). diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.gd b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.gd new file mode 100644 index 0000000000..60a31fb318 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.gd @@ -0,0 +1,4 @@ +enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 } + +func test(): + print(2 as MyEnum) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.out b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.out new file mode 100644 index 0000000000..bc0d8b7834 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Invalid cast. Enum "cast_enum_bad_int.gd::MyEnum" does not have enum value 2. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.gd b/modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.gd new file mode 100644 index 0000000000..b8603dd4ca --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.gd @@ -0,0 +1,5 @@ +const array: Array = [0] + +func test(): + var key: int = 0 + array[key] = 0 diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.out b/modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.out new file mode 100644 index 0000000000..5275183da2 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot assign a new value to a constant. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.gd b/modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.gd new file mode 100644 index 0000000000..9b5112b788 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.gd @@ -0,0 +1,5 @@ +const dictionary := {} + +func test(): + var key: int = 0 + dictionary[key] = 0 diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.out b/modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.out new file mode 100644 index 0000000000..5275183da2 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot assign a new value to a constant. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.gd new file mode 100644 index 0000000000..87fbe1229c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.gd @@ -0,0 +1,5 @@ +const base := [0] + +func test(): + var sub := base[0] + if sub is String: pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.out b/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.out new file mode 100644 index 0000000000..54c190cf8a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Expression is of type "int" so it can't be of type "String". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.gd new file mode 100644 index 0000000000..2940c03515 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.gd @@ -0,0 +1,4 @@ +enum Enum {V1, V2} + +func test(): + Enum.clear() diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.out new file mode 100644 index 0000000000..9ca86eca9c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot call non-const Dictionary function "clear()" on enum "Enum". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.gd new file mode 100644 index 0000000000..a66e2714d9 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.gd @@ -0,0 +1,4 @@ +enum Enum {V1, V2} + +func test(): + var bad = Enum.V3 diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.out new file mode 100644 index 0000000000..ddbdc17a42 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot find member "V3" in base "enum_bad_value.gd::Enum". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out index fde7e92f8c..02c4633586 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Cannot assign a value of type "MyOtherEnum (enum)" to a target of type "MyEnum (enum)". +Value of type "enum_class_var_assign_with_wrong_enum_type.gd::MyOtherEnum" cannot be assigned to a variable of type "enum_class_var_assign_with_wrong_enum_type.gd::MyEnum". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out index 6fa2682d0a..441cccbf7b 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Cannot assign a value of type MyOtherEnum (enum) to variable "class_var" with specified type MyEnum (enum). +Cannot assign a value of type enum_class_var_init_with_wrong_enum_type.gd::MyOtherEnum to variable "class_var" with specified type enum_class_var_init_with_wrong_enum_type.gd::MyEnum. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.gd new file mode 100644 index 0000000000..2c7dfafd06 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.gd @@ -0,0 +1,5 @@ +enum Enum {V1, V2} + +func test(): + var Enum2 = Enum + Enum2.clear() diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.out new file mode 100644 index 0000000000..9ca86eca9c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot call non-const Dictionary function "clear()" on enum "Enum". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.gd new file mode 100644 index 0000000000..62ac1c3108 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.gd @@ -0,0 +1,8 @@ +enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 } +enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 } + +func enum_func(e : MyEnum) -> void: + print(e) + +func test(): + enum_func(MyOtherEnum.OTHER_ENUM_VALUE_1) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out new file mode 100644 index 0000000000..e85f7d6f9f --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Invalid argument for "enum_func()" function: argument 1 should be "enum_function_parameter_wrong_type.gd::MyEnum" but is "enum_function_parameter_wrong_type.gd::MyOtherEnum". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.gd new file mode 100644 index 0000000000..18b3ffb0fc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.gd @@ -0,0 +1,8 @@ +enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 } +enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 } + +func enum_func() -> MyEnum: + return MyOtherEnum.OTHER_ENUM_VALUE_1 + +func test(): + print(enum_func()) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out new file mode 100644 index 0000000000..f7ea3267fa --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot return value of type "enum_function_return_wrong_type.gd::MyOtherEnum" because the function return type is "enum_function_return_wrong_type.gd::MyEnum". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.gd new file mode 100644 index 0000000000..2b006f1f69 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.gd @@ -0,0 +1,10 @@ +enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 } + +class InnerClass: + enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 } + +func test(): + var local_var: MyEnum = MyEnum.ENUM_VALUE_1 + print(local_var) + local_var = InnerClass.MyEnum.ENUM_VALUE_2 + print(local_var) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out new file mode 100644 index 0000000000..38df5a0cd8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Value of type "enum_local_var_assign_outer_with_wrong_enum_type.gd::InnerClass::MyEnum" cannot be assigned to a variable of type "enum_local_var_assign_outer_with_wrong_enum_type.gd::MyEnum". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out index fde7e92f8c..2adcbd9edf 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Cannot assign a value of type "MyOtherEnum (enum)" to a target of type "MyEnum (enum)". +Value of type "enum_local_var_assign_with_wrong_enum_type.gd::MyOtherEnum" cannot be assigned to a variable of type "enum_local_var_assign_with_wrong_enum_type.gd::MyEnum". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out index 07fb19f1ff..331113dd30 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Cannot assign a value of type MyOtherEnum (enum) to variable "local_var" with specified type MyEnum (enum). +Cannot assign a value of type enum_local_var_init_with_wrong_enum_type.gd::MyOtherEnum to variable "local_var" with specified type enum_local_var_init_with_wrong_enum_type.gd::MyEnum. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.gd new file mode 100644 index 0000000000..744c2e47ce --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.gd @@ -0,0 +1,2 @@ +func test(): + var _bad = TileSet.TileShape.THIS_DOES_NOT_EXIST diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.out new file mode 100644 index 0000000000..49f041a2dd --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot find member "THIS_DOES_NOT_EXIST" in base "TileSet::TileShape". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.gd new file mode 100644 index 0000000000..81d5d59ae8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.gd @@ -0,0 +1,7 @@ +enum MyEnum { VALUE_A, VALUE_B, VALUE_C = 42 } + +func test(): + const P = preload("../features/enum_value_from_parent.gd") + var local_var: MyEnum + local_var = P.VALUE_B + print(local_var) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out new file mode 100644 index 0000000000..6298c026b4 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Value of type "enum_value_from_parent.gd::<anonymous enum>" cannot be assigned to a variable of type "enum_preload_unnamed_assign_to_named.gd::MyEnum". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.gd new file mode 100644 index 0000000000..96904c297a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.gd @@ -0,0 +1,8 @@ +class A: + enum { V } + +class B extends A: + enum { V } + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.out new file mode 100644 index 0000000000..7961a1a481 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +The member "V" already exists in parent class A. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_outer_enum.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_outer_enum.gd new file mode 100644 index 0000000000..f3f3b5ffeb --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_outer_enum.gd @@ -0,0 +1,7 @@ +enum { V } + +class InnerClass: + enum { V } + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_outer_enum.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_outer_enum.out new file mode 100644 index 0000000000..c9706003e1 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_outer_enum.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Name "V" is already used as a class enum value. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.gd new file mode 100644 index 0000000000..7e749db6b5 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.gd @@ -0,0 +1,7 @@ +enum { ENUM_VALUE_1, ENUM_VALUE_2 } + +enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 } + +func test(): + var local_var: MyEnum = ENUM_VALUE_1 + print(local_var) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out new file mode 100644 index 0000000000..b70121ed81 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot assign a value of type enum_unnamed_assign_to_named.gd::<anonymous enum> to variable "local_var" with specified type enum_unnamed_assign_to_named.gd::MyEnum. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.gd b/modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.gd new file mode 100644 index 0000000000..e1bed94406 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.gd @@ -0,0 +1,2 @@ +func test(): + TileSet.this_does_not_exist # Does not exist diff --git a/modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.out b/modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.out new file mode 100644 index 0000000000..06180c3a55 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot find member "this_does_not_exist" in base "TileSet". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.gd new file mode 100644 index 0000000000..1cf3870a8e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.gd @@ -0,0 +1,8 @@ +class Outer: + const OUTER_CONST: = 0 + class Inner: + pass + +func test() -> void: + var type: = Outer.Inner + print(type.OUTER_CONST) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.out b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.out new file mode 100644 index 0000000000..73a54d7820 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> analyzer/errors/outer_class_constants.gd +>> 8 +>> Invalid get index 'OUTER_CONST' (on base: 'GDScript'). diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.gd new file mode 100644 index 0000000000..c1074df915 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.gd @@ -0,0 +1,9 @@ +class Outer: + const OUTER_CONST: = 0 + class Inner: + pass + +func test() -> void: + var type: = Outer.Inner + var type_v: Variant = type + print(type_v.OUTER_CONST) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.out b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.out new file mode 100644 index 0000000000..92e7b9316e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> analyzer/errors/outer_class_constants_as_variant.gd +>> 9 +>> Invalid get index 'OUTER_CONST' (on base: 'GDScript'). diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.gd new file mode 100644 index 0000000000..2631c3c500 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.gd @@ -0,0 +1,8 @@ +class Outer: + const OUTER_CONST: = 0 + class Inner: + pass + +func test() -> void: + var instance: = Outer.Inner.new() + print(instance.OUTER_CONST) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.out b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.out new file mode 100644 index 0000000000..892f8e2c3f --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> analyzer/errors/outer_class_instance_constants.gd +>> 8 +>> Invalid get index 'OUTER_CONST' (on base: 'RefCounted (Inner)'). diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.gd new file mode 100644 index 0000000000..cba788381e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.gd @@ -0,0 +1,9 @@ +class Outer: + const OUTER_CONST: = 0 + class Inner: + pass + +func test() -> void: + var instance: = Outer.Inner.new() + var instance_v: Variant = instance + print(instance_v.OUTER_CONST) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.out b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.out new file mode 100644 index 0000000000..8257e74f57 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> analyzer/errors/outer_class_instance_constants_as_variant.gd +>> 9 +>> Invalid get index 'OUTER_CONST' (on base: 'RefCounted (Inner)'). diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd index 65c0d9dabc..200c352223 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd +++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd @@ -1,12 +1,12 @@ class A: - class B: - func test(): - print(A.B.D) + class B: + func test(): + print(A.B.D) class C: - class D: - pass + class D: + pass func test(): - var inst = A.B.new() - inst.test() + var inst = A.B.new() + inst.test() diff --git a/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.gd new file mode 100644 index 0000000000..4e75ded96a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.gd @@ -0,0 +1,6 @@ +enum LocalNamed { VALUE_A, VALUE_B, VALUE_C = 42 } + +func test(): + const P = preload("../features/enum_from_outer.gd") + var x : LocalNamed + x = P.Named.VALUE_A diff --git a/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out new file mode 100644 index 0000000000..5e3c446bf6 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Value of type "enum_from_outer.gd::Named" cannot be assigned to a variable of type "preload_enum_error.gd::LocalNamed". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out index bbadf1ce27..bf776029b9 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Cannot assign a value of type "String" to a target of type "int". +Value of type "String" cannot be assigned to a variable of type "int". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd index 63587942f7..393b66c9f0 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd +++ b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd @@ -1,2 +1,2 @@ func test() -> void: - return null + return null diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd index 0ee4e7ea36..6be2730bab 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd +++ b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd @@ -1,4 +1,4 @@ func test() -> void: - var a - a = 1 - return a + var a + a = 1 + return a diff --git a/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.gd b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.gd index 7881a0feb6..a94487d989 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.gd @@ -11,4 +11,3 @@ func test() -> void: Extend.InnerClass.InnerInnerClass.test_a_b_c(A.new(), B.new(), C.new()) Extend.InnerClass.InnerInnerClass.test_enum(C.TestEnum.HELLO_WORLD) Extend.InnerClass.InnerInnerClass.test_a_prime(A.APrime.new()) - diff --git a/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd index 30e7deb05a..7c846c59bd 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd @@ -1,19 +1,19 @@ class A: - var x = 3 + var x = 3 class B: - var x = 4 + var x = 4 class C: - var x = 5 + var x = 5 class Test: - var a = A.new() - var b: B = B.new() - var c := C.new() + var a = A.new() + var b: B = B.new() + var c := C.new() func test(): - var test_instance := Test.new() - prints(test_instance.a.x) - prints(test_instance.b.x) - prints(test_instance.c.x) + var test_instance := Test.new() + prints(test_instance.a.x) + prints(test_instance.b.x) + prints(test_instance.c.x) diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_access_types.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_access_types.gd new file mode 100644 index 0000000000..9bc08f2dc5 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_access_types.gd @@ -0,0 +1,29 @@ +class_name EnumAccessOuterClass + +class InnerClass: + enum MyEnum { V0, V2, V1 } + + static func print_enums(): + print("Inner - Inner") + print(MyEnum.V0, MyEnum.V1, MyEnum.V2) + print(InnerClass.MyEnum.V0, InnerClass.MyEnum.V1, InnerClass.MyEnum.V2) + print(EnumAccessOuterClass.InnerClass.MyEnum.V0, EnumAccessOuterClass.InnerClass.MyEnum.V1, EnumAccessOuterClass.InnerClass.MyEnum.V2) + + print("Inner - Outer") + print(EnumAccessOuterClass.MyEnum.V0, EnumAccessOuterClass.MyEnum.V1, EnumAccessOuterClass.MyEnum.V2) + + +enum MyEnum { V0, V1, V2 } + +func print_enums(): + print("Outer - Outer") + print(MyEnum.V0, MyEnum.V1, MyEnum.V2) + print(EnumAccessOuterClass.MyEnum.V0, EnumAccessOuterClass.MyEnum.V1, EnumAccessOuterClass.MyEnum.V2) + + print("Outer - Inner") + print(InnerClass.MyEnum.V0, InnerClass.MyEnum.V1, InnerClass.MyEnum.V2) + print(EnumAccessOuterClass.InnerClass.MyEnum.V0, EnumAccessOuterClass.InnerClass.MyEnum.V1, EnumAccessOuterClass.InnerClass.MyEnum.V2) + +func test(): + print_enums() + InnerClass.print_enums() diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_access_types.out b/modules/gdscript/tests/scripts/analyzer/features/enum_access_types.out new file mode 100644 index 0000000000..02e2e2b396 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_access_types.out @@ -0,0 +1,13 @@ +GDTEST_OK +Outer - Outer +012 +012 +Outer - Inner +021 +021 +Inner - Inner +021 +021 +021 +Inner - Outer +012 diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.gd new file mode 100644 index 0000000000..3076e7069f --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.gd @@ -0,0 +1,13 @@ +enum Enum {V1, V2} + +func test(): + var enumAsDict : Dictionary = Enum.duplicate() + var enumAsVariant = Enum.duplicate() + print(Enum.has("V1")) + print(enumAsDict.has("V1")) + print(enumAsVariant.has("V1")) + enumAsDict.clear() + enumAsVariant.clear() + print(Enum.has("V1")) + print(enumAsDict.has("V1")) + print(enumAsVariant.has("V1")) diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.out b/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.out new file mode 100644 index 0000000000..a41924d0c9 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.out @@ -0,0 +1,7 @@ +GDTEST_OK +true +true +true +true +false +false diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_from_base.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_from_base.gd new file mode 100644 index 0000000000..b3f9941903 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_from_base.gd @@ -0,0 +1,13 @@ +class A: + enum Named { VALUE_A, VALUE_B, VALUE_C = 42 } + +class B extends A: + var a = Named.VALUE_A + var b = Named.VALUE_B + var c = Named.VALUE_C + +func test(): + var test_instance = B.new() + prints("a", test_instance.a, test_instance.a == A.Named.VALUE_A) + prints("b", test_instance.b, test_instance.b == A.Named.VALUE_B) + prints("c", test_instance.c, test_instance.c == B.Named.VALUE_C) diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.out b/modules/gdscript/tests/scripts/analyzer/features/enum_from_base.out index c160839da3..c160839da3 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.out +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_from_base.out diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.gd index 5f57c5b8c2..4d6852a9be 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.gd @@ -1,5 +1,3 @@ -extends Node - enum Named { VALUE_A, VALUE_B, VALUE_C = 42 } class Test: diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.out b/modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.out new file mode 100644 index 0000000000..c160839da3 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.out @@ -0,0 +1,4 @@ +GDTEST_OK +a 0 true +b 1 true +c 42 true diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd new file mode 100644 index 0000000000..8a4e89d0d6 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd @@ -0,0 +1,112 @@ +class_name EnumFunctionTypecheckOuterClass + +enum MyEnum { V0, V1, V2 } + +class InnerClass: + enum MyEnum { V0, V2, V1 } + + func inner_inner_no_class(e : MyEnum) -> MyEnum: + print(e) + return e + + func inner_inner_class(e : InnerClass.MyEnum) -> InnerClass.MyEnum: + print(e) + return e + + func inner_inner_class_class(e : EnumFunctionTypecheckOuterClass.InnerClass.MyEnum) -> EnumFunctionTypecheckOuterClass.InnerClass.MyEnum: + print(e) + return e + + func inner_outer(e : EnumFunctionTypecheckOuterClass.MyEnum) -> EnumFunctionTypecheckOuterClass.MyEnum: + print(e) + return e + + func test(): + var _d + print("Inner") + + var o := EnumFunctionTypecheckOuterClass.new() + + _d = o.outer_outer_no_class(EnumFunctionTypecheckOuterClass.MyEnum.V1) + print() + _d = o.outer_outer_class(EnumFunctionTypecheckOuterClass.MyEnum.V1) + print() + _d = o.outer_inner_class(MyEnum.V1) + _d = o.outer_inner_class(InnerClass.MyEnum.V1) + _d = o.outer_inner_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1) + print() + _d = o.outer_inner_class_class(MyEnum.V1) + _d = o.outer_inner_class_class(InnerClass.MyEnum.V1) + _d = o.outer_inner_class_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1) + print() + print() + + + _d = inner_inner_no_class(MyEnum.V1) + _d = inner_inner_no_class(InnerClass.MyEnum.V1) + _d = inner_inner_no_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1) + print() + _d = inner_inner_class(MyEnum.V1) + _d = inner_inner_class(InnerClass.MyEnum.V1) + _d = inner_inner_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1) + print() + _d = inner_inner_class_class(MyEnum.V1) + _d = inner_inner_class_class(InnerClass.MyEnum.V1) + _d = inner_inner_class_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1) + print() + _d = inner_outer(EnumFunctionTypecheckOuterClass.MyEnum.V1) + print() + print() + + +func outer_outer_no_class(e : MyEnum) -> MyEnum: + print(e) + return e + +func outer_outer_class(e : EnumFunctionTypecheckOuterClass.MyEnum) -> EnumFunctionTypecheckOuterClass.MyEnum: + print(e) + return e + +func outer_inner_class(e : InnerClass.MyEnum) -> InnerClass.MyEnum: + print(e) + return e + +func outer_inner_class_class(e : EnumFunctionTypecheckOuterClass.InnerClass.MyEnum) -> EnumFunctionTypecheckOuterClass.InnerClass.MyEnum: + print(e) + return e + +func test(): + var _d + print("Outer") + + _d = outer_outer_no_class(MyEnum.V1) + _d = outer_outer_no_class(EnumFunctionTypecheckOuterClass.MyEnum.V1) + print() + _d = outer_outer_class(MyEnum.V1) + _d = outer_outer_class(EnumFunctionTypecheckOuterClass.MyEnum.V1) + print() + _d = outer_inner_class(InnerClass.MyEnum.V1) + _d = outer_inner_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1) + print() + _d = outer_inner_class_class(InnerClass.MyEnum.V1) + _d = outer_inner_class_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1) + print() + print() + + var i := EnumFunctionTypecheckOuterClass.InnerClass.new() + + _d = i.inner_inner_no_class(InnerClass.MyEnum.V1) + _d = i.inner_inner_no_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1) + print() + _d = i.inner_inner_class(InnerClass.MyEnum.V1) + _d = i.inner_inner_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1) + print() + _d = i.inner_inner_class_class(InnerClass.MyEnum.V1) + _d = i.inner_inner_class_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1) + print() + _d = i.inner_outer(MyEnum.V1) + _d = i.inner_outer(EnumFunctionTypecheckOuterClass.MyEnum.V1) + print() + print() + + i.test() diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.out b/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.out new file mode 100644 index 0000000000..2e3ce1aa10 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.out @@ -0,0 +1,55 @@ +GDTEST_OK +Outer +1 +1 + +1 +1 + +2 +2 + +2 +2 + + +2 +2 + +2 +2 + +2 +2 + +1 +1 + + +Inner +1 + +1 + +2 +2 +2 + +2 +2 +2 + + +2 +2 +2 + +2 +2 +2 + +2 +2 +2 + +1 diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.gd new file mode 100644 index 0000000000..b97d9bbb6b --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.gd @@ -0,0 +1,16 @@ +const A := 1 +enum { B } +enum NamedEnum { C } + +class Parent: + const D := 2 + enum { E } + enum NamedEnum2 { F } + +class Child extends Parent: + enum TestEnum { A, B, C, D, E, F, Node, Object, Child, Parent} + +func test(): + print(A, B, NamedEnum.C, Parent.D, Parent.E, Parent.NamedEnum2.F) + print(Child.TestEnum.A, Child.TestEnum.B, Child.TestEnum.C, Child.TestEnum.D, Child.TestEnum.E, Child.TestEnum.F) + print(Child.TestEnum.Node, Child.TestEnum.Object, Child.TestEnum.Child, Child.TestEnum.Parent) diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.out b/modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.out new file mode 100644 index 0000000000..864ba2a549 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.out @@ -0,0 +1,4 @@ +GDTEST_OK +100200 +012345 +6789 diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.gd new file mode 100644 index 0000000000..6a0a1e1969 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.gd @@ -0,0 +1,19 @@ +func print_enum(e : TileSet.TileShape) -> TileSet.TileShape: + print(e) + return e + +func test(): + var v : TileSet.TileShape + v = TileSet.TILE_SHAPE_SQUARE + v = print_enum(v) + v = print_enum(TileSet.TILE_SHAPE_SQUARE) + v = TileSet.TileShape.TILE_SHAPE_SQUARE + v = print_enum(v) + v = print_enum(TileSet.TileShape.TILE_SHAPE_SQUARE) + + v = TileSet.TILE_SHAPE_ISOMETRIC + v = print_enum(v) + v = print_enum(TileSet.TILE_SHAPE_ISOMETRIC) + v = TileSet.TileShape.TILE_SHAPE_ISOMETRIC + v = print_enum(v) + v = print_enum(TileSet.TileShape.TILE_SHAPE_ISOMETRIC) diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.out b/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.out new file mode 100644 index 0000000000..1126dcc6ec --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.out @@ -0,0 +1,9 @@ +GDTEST_OK +0 +0 +0 +0 +1 +1 +1 +1 diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.gd new file mode 100644 index 0000000000..b05ae82048 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.gd @@ -0,0 +1,86 @@ +class_name EnumTypecheckOuterClass + +enum MyEnum { V0, V1, V2 } + +class InnerClass: + enum MyEnum { V0, V2, V1 } + + static func test_inner_from_inner(): + print("Inner - Inner") + var e1 : MyEnum + var e2 : InnerClass.MyEnum + var e3 : EnumTypecheckOuterClass.InnerClass.MyEnum + + print("Self ", e1, e2, e3) + e1 = MyEnum.V1 + e2 = MyEnum.V1 + e3 = MyEnum.V1 + print("MyEnum ", e1, e2, e3) + e1 = InnerClass.MyEnum.V1 + e2 = InnerClass.MyEnum.V1 + e3 = InnerClass.MyEnum.V1 + print("Inner.MyEnum ", e1, e2, e3) + e1 = EnumTypecheckOuterClass.InnerClass.MyEnum.V1 + e2 = EnumTypecheckOuterClass.InnerClass.MyEnum.V1 + e3 = EnumTypecheckOuterClass.InnerClass.MyEnum.V1 + print("Outer.Inner.MyEnum ", e1, e2, e3) + + e1 = e2 + e1 = e3 + e2 = e1 + e2 = e3 + e3 = e1 + e3 = e2 + + print() + + static func test_outer_from_inner(): + print("Inner - Outer") + var e : EnumTypecheckOuterClass.MyEnum + + e = EnumTypecheckOuterClass.MyEnum.V1 + print("Outer.MyEnum ", e) + + print() + +func test_outer_from_outer(): + print("Outer - Outer") + var e1 : MyEnum + var e2 : EnumTypecheckOuterClass.MyEnum + + print("Self ", e1, e2) + e1 = MyEnum.V1 + e2 = MyEnum.V1 + print("Outer ", e1, e2) + e1 = EnumTypecheckOuterClass.MyEnum.V1 + e2 = EnumTypecheckOuterClass.MyEnum.V1 + print("Outer.MyEnum ", e1, e2) + + e1 = e2 + e2 = e1 + + print() + +func test_inner_from_outer(): + print("Outer - Inner") + var e1 : InnerClass.MyEnum + var e2 : EnumTypecheckOuterClass.InnerClass.MyEnum + + print("Inner ", e1, e2) + e1 = InnerClass.MyEnum.V1 + e2 = InnerClass.MyEnum.V1 + print("Outer.Inner ", e1, e2) + e1 = EnumTypecheckOuterClass.InnerClass.MyEnum.V1 + e2 = EnumTypecheckOuterClass.InnerClass.MyEnum.V1 + print("Outer.Inner.MyEnum ", e1, e2) + + e1 = e2 + e2 = e1 + + print() + +func test(): + test_outer_from_outer() + test_inner_from_outer() + InnerClass.test_outer_from_inner() + InnerClass.test_inner_from_inner() diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.out b/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.out new file mode 100644 index 0000000000..3b2dcade26 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.out @@ -0,0 +1,19 @@ +GDTEST_OK +Outer - Outer +Self 00 +Outer 11 +Outer.MyEnum 11 + +Outer - Inner +Inner 00 +Outer.Inner 22 +Outer.Inner.MyEnum 22 + +Inner - Outer +Outer.MyEnum 1 + +Inner - Inner +Self 000 +MyEnum 222 +Inner.MyEnum 222 +Outer.Inner.MyEnum 222 diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd index 757744b6f1..0c740935b9 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd @@ -2,5 +2,5 @@ const External = preload("external_enum_as_constant_external.notest.gd") const MyEnum = External.MyEnum func test(): - print(MyEnum.WAITING == 0) - print(MyEnum.GODOT == 1) + print(MyEnum.WAITING == 0) + print(MyEnum.GODOT == 1) diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd index 7c090844d0..24c1e41aab 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd @@ -1,4 +1,4 @@ enum MyEnum { - WAITING, - GODOT + WAITING, + GODOT } diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_class.gd b/modules/gdscript/tests/scripts/analyzer/features/lookup_class.gd new file mode 100644 index 0000000000..541da78332 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_class.gd @@ -0,0 +1,50 @@ +# Inner-outer class lookup +class A: + const Q: = "right one" + +class X: + const Q: = "wrong one" + +class Y extends X: + class B extends A: + static func check() -> void: + print(Q) + +# External class lookup +const External: = preload("lookup_class_external.notest.gd") + +class Internal extends External.A: + static func check() -> void: + print(TARGET) + + class E extends External.E: + static func check() -> void: + print(TARGET) + print(WAITING) + +# Variable lookup +class C: + var Q := 'right one' + +class D: + const Q := 'wrong one' + +class E extends D: + class F extends C: + func check() -> void: + print(Q) + +# Test +func test() -> void: + # Inner-outer class lookup + Y.B.check() + print("---") + + # External class lookup + Internal.check() + Internal.E.check() + print("---") + + # Variable lookup + var f: = E.F.new() + f.check() diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_class.out b/modules/gdscript/tests/scripts/analyzer/features/lookup_class.out new file mode 100644 index 0000000000..a0983c1438 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_class.out @@ -0,0 +1,8 @@ +GDTEST_OK +right one +--- +wrong +right +godot +--- +right one diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_class_external.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/lookup_class_external.notest.gd new file mode 100644 index 0000000000..a2904e20a8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_class_external.notest.gd @@ -0,0 +1,15 @@ +class A: + const TARGET: = "wrong" + + class B: + const TARGET: = "wrong" + const WAITING: = "godot" + + class D extends C: + pass + +class C: + const TARGET: = "right" + +class E extends A.B.D: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.gd b/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.gd new file mode 100644 index 0000000000..26cf6c7322 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.gd @@ -0,0 +1,41 @@ +signal hello + +func get_signal() -> Signal: + return hello + +class A: + signal hello + + func get_signal() -> Signal: + return hello + + class B: + signal hello + + func get_signal() -> Signal: + return hello + +class C extends A.B: + func get_signal() -> Signal: + return hello + +func test(): + var a: = A.new() + var b: = A.B.new() + var c: = C.new() + + var hello_a_result: = hello == a.get_signal() + var hello_b_result: = hello == b.get_signal() + var hello_c_result: = hello == c.get_signal() + var a_b_result: = a.get_signal() == b.get_signal() + var a_c_result: = a.get_signal() == c.get_signal() + var b_c_result: = b.get_signal() == c.get_signal() + var c_c_result: = c.get_signal() == c.get_signal() + + print("hello == A.hello? %s" % hello_a_result) + print("hello == A.B.hello? %s" % hello_b_result) + print("hello == C.hello? %s" % hello_c_result) + print("A.hello == A.B.hello? %s" % a_b_result) + print("A.hello == C.hello? %s" % a_c_result) + print("A.B.hello == C.hello? %s" % b_c_result) + print("C.hello == C.hello? %s" % c_c_result) diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.out b/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.out new file mode 100644 index 0000000000..6b0d32eaf8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.out @@ -0,0 +1,8 @@ +GDTEST_OK +hello == A.hello? false +hello == A.B.hello? false +hello == C.hello? false +A.hello == A.B.hello? false +A.hello == C.hello? false +A.B.hello == C.hello? false +C.hello == C.hello? true diff --git a/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd index c9caef7d7c..95f04421d1 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd @@ -1,5 +1,5 @@ func variant() -> Variant: - return 'variant' + return 'variant' func test(): - print(variant()) + print(variant()) diff --git a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd index ada6030132..179e454073 100644 --- a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd +++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd @@ -3,4 +3,4 @@ class_name HelloWorld func test(): - pass + pass diff --git a/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd b/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd index 92dfb2366d..816783f239 100644 --- a/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd +++ b/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd @@ -1,2 +1,2 @@ func test(): - var dictionary = { hello = "world",, } + var dictionary = { hello = "world",, } diff --git a/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd b/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd index 4608c778aa..7a745bd995 100644 --- a/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd +++ b/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd @@ -1,4 +1,4 @@ func test(): - match 1: - [[[var a]]], 2: - pass + match 1: + [[[var a]]], 2: + pass diff --git a/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd index 43b513045b..a7197bf68f 100644 --- a/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd +++ b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd @@ -1,34 +1,34 @@ func foo(x): - match x: - 1 + 1: - print("1+1") - [1,2,[1,{1:2,2:var z,..}]]: - print("[1,2,[1,{1:2,2:var z,..}]]") - print(z) - 1 if true else 2: - print("1 if true else 2") - 1 < 2: - print("1 < 2") - 1 or 2 and 1: - print("1 or 2 and 1") - 6 | 1: - print("1 | 1") - 1 >> 1: - print("1 >> 1") - 1, 2 or 3, 4: - print("1, 2 or 3, 4") - _: - print("wildcard") + match x: + 1 + 1: + print("1+1") + [1,2,[1,{1:2,2:var z,..}]]: + print("[1,2,[1,{1:2,2:var z,..}]]") + print(z) + 1 if true else 2: + print("1 if true else 2") + 1 < 2: + print("1 < 2") + 1 or 2 and 1: + print("1 or 2 and 1") + 6 | 1: + print("1 | 1") + 1 >> 1: + print("1 >> 1") + 1, 2 or 3, 4: + print("1, 2 or 3, 4") + _: + print("wildcard") func test(): - foo(6 | 1) - foo(1 >> 1) - foo(2) - foo(1) - foo(1+1) - foo(1 < 2) - foo([2, 1]) - foo(4) - foo([1, 2, [1, {1 : 2, 2:3}]]) - foo([1, 2, [1, {1 : 2, 2:[1,3,5, "123"], 4:2}]]) - foo([1, 2, [1, {1 : 2}]]) + foo(6 | 1) + foo(1 >> 1) + foo(2) + foo(1) + foo(1+1) + foo(1 < 2) + foo([2, 1]) + foo(4) + foo([1, 2, [1, {1 : 2, 2:3}]]) + foo([1, 2, [1, {1 : 2, 2:[1,3,5, "123"], 4:2}]]) + foo([1, 2, [1, {1 : 2}]]) diff --git a/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd b/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd index 2b46f1e88a..c959c6c6af 100644 --- a/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd +++ b/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd @@ -1,27 +1,27 @@ func foo(x): - match x: - 1: - print("1") - 2: - print("2") - [1, 2]: - print("[1, 2]") - 3 or 4: - print("3 or 4") - 4: - print("4") - {1 : 2, 2 : 3}: - print("{1 : 2, 2 : 3}") - _: - print("wildcard") + match x: + 1: + print("1") + 2: + print("2") + [1, 2]: + print("[1, 2]") + 3 or 4: + print("3 or 4") + 4: + print("4") + {1 : 2, 2 : 3}: + print("{1 : 2, 2 : 3}") + _: + print("wildcard") func test(): - foo(0) - foo(1) - foo(2) - foo([1, 2]) - foo(3) - foo(4) - foo([4,4]) - foo({1 : 2, 2 : 3}) - foo({1 : 2, 4 : 3}) + foo(0) + foo(1) + foo(2) + foo([1, 2]) + foo(3) + foo(4) + foo([4,4]) + foo({1 : 2, 2 : 3}) + foo({1 : 2, 4 : 3}) diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_callable.gd b/modules/gdscript/tests/scripts/parser/features/lambda_callable.gd index c3b2506156..17d00bce3c 100644 --- a/modules/gdscript/tests/scripts/parser/features/lambda_callable.gd +++ b/modules/gdscript/tests/scripts/parser/features/lambda_callable.gd @@ -1,4 +1,4 @@ func test(): - var my_lambda = func(x): - print(x) - my_lambda.call("hello") + var my_lambda = func(x): + print(x) + my_lambda.call("hello") diff --git a/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd b/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd index 377dd25e9e..75857fb8ff 100644 --- a/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd +++ b/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd @@ -1,43 +1,43 @@ func foo(x): - match x: - {"key1": "value1", "key2": "value2"}: - print('{"key1": "value1", "key2": "value2"}') - {"key1": "value1", "key2"}: - print('{"key1": "value1", "key2"}') - {"key1", "key2": "value2"}: - print('{"key1", "key2": "value2"}') - {"key1", "key2"}: - print('{"key1", "key2"}') - {"key1": "value1"}: - print('{"key1": "value1"}') - {"key1"}: - print('{"key1"}') - _: - print("wildcard") + match x: + {"key1": "value1", "key2": "value2"}: + print('{"key1": "value1", "key2": "value2"}') + {"key1": "value1", "key2"}: + print('{"key1": "value1", "key2"}') + {"key1", "key2": "value2"}: + print('{"key1", "key2": "value2"}') + {"key1", "key2"}: + print('{"key1", "key2"}') + {"key1": "value1"}: + print('{"key1": "value1"}') + {"key1"}: + print('{"key1"}') + _: + print("wildcard") func bar(x): - match x: - {0}: - print("0") - {1}: - print("1") - {2}: - print("2") - _: - print("wildcard") + match x: + {0}: + print("0") + {1}: + print("1") + {2}: + print("2") + _: + print("wildcard") func test(): - foo({"key1": "value1", "key2": "value2"}) - foo({"key1": "value1", "key2": ""}) - foo({"key1": "", "key2": "value2"}) - foo({"key1": "", "key2": ""}) - foo({"key1": "value1"}) - foo({"key1": ""}) - foo({"key1": "value1", "key2": "value2", "key3": "value3"}) - foo({"key1": "value1", "key3": ""}) - foo({"key2": "value2"}) - foo({"key3": ""}) - bar({0: "0"}) - bar({1: "1"}) - bar({2: "2"}) - bar({3: "3"}) + foo({"key1": "value1", "key2": "value2"}) + foo({"key1": "value1", "key2": ""}) + foo({"key1": "", "key2": "value2"}) + foo({"key1": "", "key2": ""}) + foo({"key1": "value1"}) + foo({"key1": ""}) + foo({"key1": "value1", "key2": "value2", "key3": "value3"}) + foo({"key1": "value1", "key3": ""}) + foo({"key2": "value2"}) + foo({"key3": ""}) + bar({0: "0"}) + bar({1: "1"}) + bar({2: "2"}) + bar({3: "3"}) diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd index dbe223f5f5..a278ea1154 100644 --- a/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd +++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd @@ -1,26 +1,26 @@ func foo(x): - match x: - 1, [2]: - print('1, [2]') - _: - print('wildcard') + match x: + 1, [2]: + print('1, [2]') + _: + print('wildcard') func bar(x): - match x: - [1], [2], [3]: - print('[1], [2], [3]') - [4]: - print('[4]') - _: - print('wildcard') + match x: + [1], [2], [3]: + print('[1], [2], [3]') + [4]: + print('[4]') + _: + print('wildcard') func test(): - foo(1) - foo([2]) - foo(2) - bar([1]) - bar([2]) - bar([3]) - bar([4]) - bar([5]) + foo(1) + foo([2]) + foo(2) + bar([1]) + bar([2]) + bar([3]) + bar([4]) + bar([5]) diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd b/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd index a0ae7fb17c..0a71f33c25 100644 --- a/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd +++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd @@ -1,6 +1,6 @@ func test(): - match [1, 2, 3]: - [var a, var b, var c]: - print(a == 1) - print(b == 2) - print(c == 3) + match [1, 2, 3]: + [var a, var b, var c]: + print(a == 1) + print(b == 2) + print(c == 3) diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.gd b/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.gd new file mode 100644 index 0000000000..a5ecaba38d --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.gd @@ -0,0 +1,6 @@ +const array: Array = [{}] + +func test(): + var dictionary := array[0] + var key: int = 0 + dictionary[key] = 0 diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out b/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out new file mode 100644 index 0000000000..2a97eaea44 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> runtime/errors/constant_array_is_deep.gd +>> 6 +>> Invalid set index '0' (on base: 'Dictionary') with value of type 'int' diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.gd b/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.gd new file mode 100644 index 0000000000..3e71cd0518 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.gd @@ -0,0 +1,4 @@ +const array: Array = [0] + +func test(): + array.push_back(0) diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.out b/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.out new file mode 100644 index 0000000000..ba3e1c46c6 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.out @@ -0,0 +1,7 @@ +GDTEST_RUNTIME_ERROR +>> ERROR +>> on function: push_back() +>> core/variant/array.cpp +>> 253 +>> Condition "_p->read_only" is true. +>> Array is in read-only state. diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.gd b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.gd new file mode 100644 index 0000000000..7b350e81ad --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.gd @@ -0,0 +1,4 @@ +const dictionary := {} + +func test(): + dictionary.erase(0) diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.out b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.out new file mode 100644 index 0000000000..3e7ca11a4f --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.out @@ -0,0 +1,7 @@ +GDTEST_RUNTIME_ERROR +>> ERROR +>> on function: erase() +>> core/variant/dictionary.cpp +>> 177 +>> Condition "_p->read_only" is true. Returning: false +>> Dictionary is in read-only state. diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.gd b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.gd new file mode 100644 index 0000000000..4763210a7f --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.gd @@ -0,0 +1,6 @@ +const dictionary := {0: [0]} + +func test(): + var array := dictionary[0] + var key: int = 0 + array[key] = 0 diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out new file mode 100644 index 0000000000..c807db6b0c --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> runtime/errors/constant_dictionary_is_deep.gd +>> 6 +>> Invalid set index '0' (on base: 'Array') with value of type 'int' diff --git a/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd b/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd index 9b64084fa6..bd38259cec 100644 --- a/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd +++ b/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd @@ -9,7 +9,7 @@ func test(): array_sname.push_back(&"godot") print("String in Array: ", "godot" in array_sname) - # Not equal because the values are different types. + # Not equal because the values are different types. print("Arrays not equal: ", array_str != array_sname) var string_array: Array[String] = [] diff --git a/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd b/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd index 1f15026f17..94bac1974f 100644 --- a/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd +++ b/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd @@ -13,5 +13,5 @@ func test(): print("String gets StringName: ", stringname_dict.get("abc")) stringname_dict[&"abc"] = 42 - # They compare equal because StringName keys are converted to String. + # They compare equal because StringName keys are converted to String. print("String Dictionary == StringName Dictionary: ", string_dict == stringname_dict) diff --git a/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd index f33ba7dffd..252e100bda 100644 --- a/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd +++ b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd @@ -3,23 +3,23 @@ var a: int = 1 func shadow_regular_assignment(a: Variant, b: Variant) -> void: - print(a) - print(self.a) - a = b - print(a) - print(self.a) + print(a) + print(self.a) + a = b + print(a) + print(self.a) var v := Vector2(0.0, 0.0) func shadow_subscript_assignment(v: Vector2, x: float) -> void: - print(v) - print(self.v) - v.x += x - print(v) - print(self.v) + print(v) + print(self.v) + v.x += x + print(v) + print(self.v) func test(): - shadow_regular_assignment('a', 'b') - shadow_subscript_assignment(Vector2(1.0, 1.0), 5.0) + shadow_regular_assignment('a', 'b') + shadow_subscript_assignment(Vector2(1.0, 1.0), 5.0) diff --git a/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd new file mode 100644 index 0000000000..af3f3cb941 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd @@ -0,0 +1,9 @@ +# https://github.com/godotengine/godot/issues/71172 + +func test(): + @warning_ignore(narrowing_conversion) + var foo: int = 0.0 + print(typeof(foo) == TYPE_INT) + var dict : Dictionary = {"a":0.0} + foo = dict.get("a") + print(typeof(foo) == TYPE_INT) diff --git a/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.out b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.out new file mode 100644 index 0000000000..9d111a8322 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.out @@ -0,0 +1,3 @@ +GDTEST_OK +true +true diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs index 7f1231dbcb..5fb29b86da 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs @@ -74,27 +74,19 @@ namespace Godot.SourceGenerators ObjectId = 22, TypeString = 23, NodePathToEditedNode = 24, - MethodOfVariantType = 25, - MethodOfBaseType = 26, - MethodOfInstance = 27, - MethodOfScript = 28, - PropertyOfVariantType = 29, - PropertyOfBaseType = 30, - PropertyOfInstance = 31, - PropertyOfScript = 32, - ObjectTooBig = 33, - NodePathValidTypes = 34, - SaveFile = 35, - GlobalSaveFile = 36, - IntIsObjectid = 37, - IntIsPointer = 38, - ArrayType = 39, - LocaleId = 40, - LocalizableString = 41, - NodeType = 42, - HideQuaternionEdit = 43, - Password = 44, - Max = 45 + ObjectTooBig = 25, + NodePathValidTypes = 26, + SaveFile = 27, + GlobalSaveFile = 28, + IntIsObjectid = 29, + IntIsPointer = 30, + ArrayType = 31, + LocaleId = 32, + LocalizableString = 33, + NodeType = 34, + HideQuaternionEdit = 35, + Password = 36, + Max = 37 } [Flags] @@ -103,9 +95,9 @@ namespace Godot.SourceGenerators None = 0, Storage = 2, Editor = 4, - Checkable = 8, - Checked = 16, - Internationalized = 32, + Internal = 8, + Checkable = 16, + Checked = 32, Group = 64, Category = 128, Subgroup = 256, @@ -118,7 +110,7 @@ namespace Godot.SourceGenerators ScriptDefaultValue = 32768, ClassIsEnum = 65536, NilIsVariant = 131072, - Internal = 262144, + Array = 262144, DoNotShareOnDuplicate = 524288, HighEndGfx = 1048576, NodePathFromSceneRoot = 2097152, @@ -128,9 +120,7 @@ namespace Godot.SourceGenerators EditorInstantiateObject = 33554432, EditorBasicSetting = 67108864, ReadOnly = 134217728, - Array = 268435456, Default = 6, - DefaultIntl = 38, NoEditor = 2 } diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index e40b17cecb..c0d88553ad 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -3806,7 +3806,7 @@ void BindingsGenerator::_populate_global_constants() { if (enum_name != StringName()) { EnumInterface ienum(enum_name); - // TODO: ienum.is_flags is always false for core constants since they don't seem to support bitfield enums + ienum.is_flags = CoreConstants::is_global_constant_bitfield(i); List<EnumInterface>::Element *enum_match = global_enums.find(ienum); if (enum_match) { enum_match->get().constants.push_back(iconstant); diff --git a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml index a3ca2d6486..b6a31cf542 100644 --- a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml +++ b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml @@ -5,20 +5,12 @@ </brief_description> <description> Spawnable scenes can be configured in the editor or through code (see [method add_spawnable_scene]). - Also supports custom node spawns through [method spawn], calling [method _spawn_custom] on all peers. + Also supports custom node spawns through [method spawn], calling [member spawn_function] on all peers. Internally, [MultiplayerSpawner] uses [method MultiplayerAPI.object_configuration_add] to notify spawns passing the spawned node as the [code]object[/code] and itself as the [code]configuration[/code], and [method MultiplayerAPI.object_configuration_remove] to notify despawns in a similar way. </description> <tutorials> </tutorials> <methods> - <method name="_spawn_custom" qualifiers="virtual"> - <return type="Node" /> - <param index="0" name="data" type="Variant" /> - <description> - Method called on all peers when a custom spawn was requested by the authority using [method spawn]. Should return a [Node] that is not in the scene tree. - [b]Note:[/b] Spawned nodes should [b]not[/b] be added to the scene with [method Node.add_child]. This is done automatically. - </description> - </method> <method name="add_spawnable_scene"> <return type="void" /> <param index="0" name="path" type="String" /> @@ -49,12 +41,16 @@ <return type="Node" /> <param index="0" name="data" type="Variant" default="null" /> <description> - Requests a custom spawn, with [code]data[/code] passed to [method _spawn_custom] on all peers. Returns the locally spawned node instance already inside the scene tree, and added as a child of the node pointed by [member spawn_path]. + Requests a custom spawn, with [code]data[/code] passed to [member spawn_function] on all peers. Returns the locally spawned node instance already inside the scene tree, and added as a child of the node pointed by [member spawn_path]. [b]Note:[/b] Spawnable scenes are spawned automatically. [method spawn] is only needed for custom spawns. </description> </method> </methods> <members> + <member name="spawn_function" type="Callable" setter="set_spawn_function" getter="get_spawn_function"> + Method called on all peers when for every custom [method spawn] requested by the authority. Will receive the [code]data[/code] parameter, and should return a [Node] that is not in the scene tree. + [b]Note:[/b] The returned node should [b]not[/b] be added to the scene with [method Node.add_child]. This is done automatically. + </member> <member name="spawn_limit" type="int" setter="set_spawn_limit" getter="get_spawn_limit" default="0"> Maximum nodes that is allowed to be spawned by this spawner. Includes both spawnable scenes and custom spawns. When set to [code]0[/code] (the default), there is no limit. diff --git a/modules/multiplayer/doc_classes/SceneMultiplayer.xml b/modules/multiplayer/doc_classes/SceneMultiplayer.xml index e4e2b4f631..a688c5fd79 100644 --- a/modules/multiplayer/doc_classes/SceneMultiplayer.xml +++ b/modules/multiplayer/doc_classes/SceneMultiplayer.xml @@ -79,6 +79,7 @@ </member> <member name="server_relay" type="bool" setter="set_server_relay_enabled" getter="is_server_relay_enabled" default="true"> Enable or disable the server feature that notifies clients of other peers' connection/disconnection, and relays messages between them. When this option is [code]false[/code], clients won't be automatically notified of other peers and won't be able to send them packets through the server. + [b]Note:[/b] Changing this option while other peers are connected may lead to unexpected behaviors. [b]Note:[/b] Support for this feature may depend on the current [MultiplayerPeer] configuration. See [method MultiplayerPeer.is_server_relay_supported]. </member> </members> diff --git a/modules/multiplayer/editor/replication_editor.cpp b/modules/multiplayer/editor/replication_editor.cpp index dbf1eecf0e..4ab41cfcb0 100644 --- a/modules/multiplayer/editor/replication_editor.cpp +++ b/modules/multiplayer/editor/replication_editor.cpp @@ -250,7 +250,7 @@ ReplicationEditor::ReplicationEditor() { tree->add_child(drop_label); drop_label->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); - tree->set_drag_forwarding(this); + tree->set_drag_forwarding_compat(this); } void ReplicationEditor::_bind_methods() { diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp index 52b874d280..7ed69a84d0 100644 --- a/modules/multiplayer/multiplayer_spawner.cpp +++ b/modules/multiplayer/multiplayer_spawner.cpp @@ -93,13 +93,6 @@ PackedStringArray MultiplayerSpawner::get_configuration_warnings() const { if (spawn_path.is_empty() || !has_node(spawn_path)) { warnings.push_back(RTR("A valid NodePath must be set in the \"Spawn Path\" property in order for MultiplayerSpawner to be able to spawn Nodes.")); } - bool has_scenes = get_spawnable_scene_count() > 0; - // Can't check if method is overridden in placeholder scripts. - bool has_placeholder_script = get_script_instance() && get_script_instance()->is_placeholder(); - if (!has_scenes && !GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom) && !has_placeholder_script) { - warnings.push_back(RTR("A list of PackedScenes must be set in the \"Auto Spawn List\" property in order for MultiplayerSpawner to automatically spawn them remotely when added as child of \"spawn_path\".")); - warnings.push_back(RTR("Alternatively, a Script implementing the function \"_spawn_custom\" must be set for this MultiplayerSpawner, and \"spawn\" must be called explicitly in code.")); - } return warnings; } @@ -162,7 +155,9 @@ void MultiplayerSpawner::_bind_methods() { ClassDB::bind_method(D_METHOD("set_spawn_limit", "limit"), &MultiplayerSpawner::set_spawn_limit); ADD_PROPERTY(PropertyInfo(Variant::INT, "spawn_limit", PROPERTY_HINT_RANGE, "0,1024,1,or_greater"), "set_spawn_limit", "get_spawn_limit"); - GDVIRTUAL_BIND(_spawn_custom, "data"); + ClassDB::bind_method(D_METHOD("get_spawn_function"), &MultiplayerSpawner::get_spawn_function); + ClassDB::bind_method(D_METHOD("set_spawn_function", "spawn_function"), &MultiplayerSpawner::set_spawn_function); + ADD_PROPERTY(PropertyInfo(Variant::CALLABLE, "spawn_function", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_spawn_function", "get_spawn_function"); ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("spawned", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); @@ -183,7 +178,7 @@ void MultiplayerSpawner::_update_spawn_node() { Node *node = spawn_path.is_empty() && is_inside_tree() ? nullptr : get_node_or_null(spawn_path); if (node) { spawn_node = node->get_instance_id(); - if (get_spawnable_scene_count() && !GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom)) { + if (get_spawnable_scene_count()) { node->connect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added)); } } else { @@ -298,23 +293,26 @@ Node *MultiplayerSpawner::instantiate_scene(int p_id) { Node *MultiplayerSpawner::instantiate_custom(const Variant &p_data) { ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!"); - Node *node = nullptr; - if (GDVIRTUAL_CALL(_spawn_custom, p_data, node)) { - return node; - } - ERR_FAIL_V_MSG(nullptr, "Method '_spawn_custom' is not implemented on this peer."); + ERR_FAIL_COND_V_MSG(!spawn_function.is_valid(), nullptr, "Custom spawn requires a valid 'spawn_function'."); + const Variant *argv[1] = { &p_data }; + Variant ret; + Callable::CallError ce; + spawn_function.callp(argv, 1, ret, ce); + ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, nullptr, "Failed to call spawn function."); + ERR_FAIL_COND_V_MSG(ret.get_type() != Variant::OBJECT, nullptr, "The spawn function must return a Node."); + return Object::cast_to<Node>(ret.operator Object *()); } Node *MultiplayerSpawner::spawn(const Variant &p_data) { ERR_FAIL_COND_V(!is_inside_tree() || !get_multiplayer()->has_multiplayer_peer() || !is_multiplayer_authority(), nullptr); ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!"); - ERR_FAIL_COND_V_MSG(!GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom), nullptr, "Custom spawn requires the '_spawn_custom' virtual method to be implemented via script."); + ERR_FAIL_COND_V_MSG(!spawn_function.is_valid(), nullptr, "Custom spawn requires the 'spawn_function' property to be a valid callable."); Node *parent = get_spawn_node(); ERR_FAIL_COND_V_MSG(!parent, nullptr, "Cannot find spawn node."); Node *node = instantiate_custom(p_data); - ERR_FAIL_COND_V_MSG(!node, nullptr, "The '_spawn_custom' implementation must return a valid Node."); + ERR_FAIL_COND_V_MSG(!node, nullptr, "The 'spawn_function' callable must return a valid node."); _track(node, p_data); parent->add_child(node, true); diff --git a/modules/multiplayer/multiplayer_spawner.h b/modules/multiplayer/multiplayer_spawner.h index 3793c2d111..8d401a6818 100644 --- a/modules/multiplayer/multiplayer_spawner.h +++ b/modules/multiplayer/multiplayer_spawner.h @@ -71,6 +71,7 @@ private: ObjectID spawn_node; HashMap<ObjectID, SpawnInfo> tracked_nodes; uint32_t spawn_limit = 0; + Callable spawn_function; void _update_spawn_node(); void _track(Node *p_node, const Variant &p_argument, int p_scene_id = INVALID_ID); @@ -106,6 +107,8 @@ public: void set_spawn_path(const NodePath &p_path); uint32_t get_spawn_limit() const { return spawn_limit; } void set_spawn_limit(uint32_t p_limit) { spawn_limit = p_limit; } + void set_spawn_function(Callable p_spawn_function) { spawn_function = p_spawn_function; } + Callable get_spawn_function() const { return spawn_function; } const Variant get_spawn_argument(const ObjectID &p_id) const; int find_spawnable_scene_index_from_object(const ObjectID &p_id) const; @@ -114,8 +117,6 @@ public: Node *instantiate_custom(const Variant &p_data); Node *instantiate_scene(int p_idx); - GDVIRTUAL1R(Node *, _spawn_custom, const Variant &); - MultiplayerSpawner() {} }; diff --git a/modules/multiplayer/scene_multiplayer.cpp b/modules/multiplayer/scene_multiplayer.cpp index 0a580f782f..01fc1b5275 100644 --- a/modules/multiplayer/scene_multiplayer.cpp +++ b/modules/multiplayer/scene_multiplayer.cpp @@ -610,7 +610,6 @@ Error SceneMultiplayer::object_configuration_remove(Object *p_obj, Variant p_con } void SceneMultiplayer::set_server_relay_enabled(bool p_enabled) { - ERR_FAIL_COND_MSG(multiplayer_peer.is_valid() && multiplayer_peer->get_connection_status() != MultiplayerPeer::CONNECTION_DISCONNECTED, "Cannot change the server relay option while the multiplayer peer is active."); server_relay = p_enabled; } diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub index fefee9bb24..3b39967ba4 100644 --- a/modules/openxr/SCsub +++ b/modules/openxr/SCsub @@ -101,6 +101,7 @@ env_openxr.add_source_files(module_obj, "extensions/openxr_huawei_controller_ext env_openxr.add_source_files(module_obj, "extensions/openxr_hand_tracking_extension.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_fb_passthrough_extension_wrapper.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_fb_display_refresh_rate_extension.cpp") +env_openxr.add_source_files(module_obj, "extensions/openxr_pico_controller_extension.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_wmr_controller_extension.cpp") env.modules_sources += module_obj diff --git a/modules/openxr/action_map/openxr_action_map.cpp b/modules/openxr/action_map/openxr_action_map.cpp index e3ff1b4382..669c395b3e 100644 --- a/modules/openxr/action_map/openxr_action_map.cpp +++ b/modules/openxr/action_map/openxr_action_map.cpp @@ -307,6 +307,31 @@ void OpenXRActionMap::create_default_action_sets() { profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic"); add_interaction_profile(profile); + // Create our Pico 4 / Neo 3 controller profile + profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/pico/neo3_controller"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); + profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); + profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); + profile->add_new_binding(select_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click"); // system click may not be available + profile->add_new_binding(menu_button, "/user/hand/left/input/back/click,/user/hand/right/input/back/click"); // right hand back click may not be available + profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand + profile->add_new_binding(ax_touch, "/user/hand/left/input/x/touch,/user/hand/right/input/a/touch"); + profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand + profile->add_new_binding(by_touch, "/user/hand/left/input/y/touch,/user/hand/right/input/b/touch"); + profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); + profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); // should be converted to boolean + profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch"); + profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // should be converted to boolean + profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); + // primary on our pico controller is our thumbstick + profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick"); + profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click"); + profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch"); + // pico controller has no secondary input + profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic"); + add_interaction_profile(profile); + // Create our Valve index controller profile profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/valve/index_controller"); profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); diff --git a/modules/openxr/extensions/openxr_pico_controller_extension.cpp b/modules/openxr/extensions/openxr_pico_controller_extension.cpp new file mode 100644 index 0000000000..f2fcf22ce2 --- /dev/null +++ b/modules/openxr/extensions/openxr_pico_controller_extension.cpp @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* openxr_pico_controller_extension.cpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#include "openxr_pico_controller_extension.h" +#include "../action_map/openxr_interaction_profile_meta_data.h" + +// Pico controllers are not part of the OpenXR spec at the time of writing this +// code. We'll hardcode the extension name that is used internally, verified by +// tests on the Pico 4. Note that later versions of the Pico 4 and OpenXR +// runtime on Pico might use a different, standardized extension name. +#ifndef XR_PICO_CONTROLLER_INTERACTION_EXTENSION_NAME +#define XR_PICO_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_PICO_controller_interaction" +#endif + +HashMap<String, bool *> OpenXRPicoControllerExtension::get_requested_extensions() { + HashMap<String, bool *> request_extensions; + + request_extensions[XR_PICO_CONTROLLER_INTERACTION_EXTENSION_NAME] = &available; + + return request_extensions; +} + +bool OpenXRPicoControllerExtension::is_available() { + return available; +} + +void OpenXRPicoControllerExtension::on_register_metadata() { + OpenXRInteractionProfileMetaData *metadata = OpenXRInteractionProfileMetaData::get_singleton(); + ERR_FAIL_NULL(metadata); + + // Pico controller (Pico 4 and Pico Neo 3 controllers) + metadata->register_interaction_profile("Pico controller", "/interaction_profiles/pico/neo3_controller", XR_PICO_CONTROLLER_INTERACTION_EXTENSION_NAME); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Palm pose", "/user/hand/left", "/user/hand/left/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Palm pose", "/user/hand/right", "/user/hand/right/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE); + + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Menu click", "/user/hand/left", "/user/hand/left/input/back/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Screenshot click", "/user/hand/right", "/user/hand/right/input/back/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "System click", "/user/hand/left", "/user/hand/left/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "System click", "/user/hand/right", "/user/hand/right/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "X click", "/user/hand/left", "/user/hand/left/input/x/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "X touch", "/user/hand/left", "/user/hand/left/input/x/touch", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Y click", "/user/hand/left", "/user/hand/left/input/y/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Y touch", "/user/hand/left", "/user/hand/left/input/y/touch", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "A touch", "/user/hand/right", "/user/hand/right/input/a/touch", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "B touch", "/user/hand/right", "/user/hand/right/input/b/touch", "", OpenXRAction::OPENXR_ACTION_BOOL); + + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Trigger", "/user/hand/left", "/user/hand/left/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Trigger touch", "/user/hand/left", "/user/hand/left/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Trigger", "/user/hand/right", "/user/hand/right/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Trigger touch", "/user/hand/right", "/user/hand/right/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL); + + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Squeeze", "/user/hand/left", "/user/hand/left/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Squeeze", "/user/hand/right", "/user/hand/right/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT); + + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick", "/user/hand/left", "/user/hand/left/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick click", "/user/hand/left", "/user/hand/left/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick touch", "/user/hand/left", "/user/hand/left/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick", "/user/hand/right", "/user/hand/right/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick click", "/user/hand/right", "/user/hand/right/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick touch", "/user/hand/right", "/user/hand/right/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL); + + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC); + metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Haptic output", "/user/hand/right", "/user/hand/right/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC); +} diff --git a/modules/openxr/extensions/openxr_pico_controller_extension.h b/modules/openxr/extensions/openxr_pico_controller_extension.h new file mode 100644 index 0000000000..a2a1e2f3d3 --- /dev/null +++ b/modules/openxr/extensions/openxr_pico_controller_extension.h @@ -0,0 +1,48 @@ +/**************************************************************************/ +/* openxr_pico_controller_extension.h */ +/**************************************************************************/ +/* 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 OPENXR_PICO_CONTROLLER_EXTENSION_H +#define OPENXR_PICO_CONTROLLER_EXTENSION_H + +#include "openxr_extension_wrapper.h" + +class OpenXRPicoControllerExtension : public OpenXRExtensionWrapper { +public: + virtual HashMap<String, bool *> get_requested_extensions() override; + + bool is_available(); + + virtual void on_register_metadata() override; + +private: + bool available = false; +}; + +#endif // OPENXR_PICO_CONTROLLER_EXTENSION_H diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 550ed8052e..33b3c2967f 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -217,7 +217,7 @@ bool OpenXRAPI::is_top_level_path_supported(const String &p_toplevel_path) { String required_extension = OpenXRInteractionProfileMetaData::get_singleton()->get_top_level_extension(p_toplevel_path); // If unsupported is returned we likely have a misspelled interaction profile path in our action map. Always output that as an error. - ERR_FAIL_COND_V_MSG(required_extension == XR_PATH_UNSUPPORTED_NAME, false, "OpenXR: Unsupported interaction profile " + p_toplevel_path); + ERR_FAIL_COND_V_MSG(required_extension == XR_PATH_UNSUPPORTED_NAME, false, "OpenXR: Unsupported toplevel path " + p_toplevel_path); if (required_extension == "") { // no extension needed, core top level are always "supported", they just won't be used if not really supported diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp index 56c31883e6..4e2fe3dab5 100644 --- a/modules/openxr/register_types.cpp +++ b/modules/openxr/register_types.cpp @@ -53,6 +53,7 @@ #include "extensions/openxr_htc_vive_tracker_extension.h" #include "extensions/openxr_huawei_controller_extension.h" #include "extensions/openxr_palm_pose_extension.h" +#include "extensions/openxr_pico_controller_extension.h" #include "extensions/openxr_wmr_controller_extension.h" static OpenXRAPI *openxr_api = nullptr; @@ -92,6 +93,7 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) { // register our other extensions OpenXRAPI::register_extension_wrapper(memnew(OpenXRPalmPoseExtension)); + OpenXRAPI::register_extension_wrapper(memnew(OpenXRPicoControllerExtension)); OpenXRAPI::register_extension_wrapper(memnew(OpenXRCompositionLayerDepthExtension)); OpenXRAPI::register_extension_wrapper(memnew(OpenXRHTCControllerExtension)); OpenXRAPI::register_extension_wrapper(memnew(OpenXRHTCViveTrackerExtension)); diff --git a/platform/web/api/api.cpp b/platform/web/api/api.cpp index eb3a199ae1..a630e3d866 100644 --- a/platform/web/api/api.cpp +++ b/platform/web/api/api.cpp @@ -73,6 +73,7 @@ void JavaScriptBridge::_bind_methods() { ClassDB::bind_method(D_METHOD("download_buffer", "buffer", "name", "mime"), &JavaScriptBridge::download_buffer, DEFVAL("application/octet-stream")); ClassDB::bind_method(D_METHOD("pwa_needs_update"), &JavaScriptBridge::pwa_needs_update); ClassDB::bind_method(D_METHOD("pwa_update"), &JavaScriptBridge::pwa_update); + ClassDB::bind_method(D_METHOD("force_fs_sync"), &JavaScriptBridge::force_fs_sync); ADD_SIGNAL(MethodInfo("pwa_update_available")); } @@ -111,6 +112,8 @@ bool JavaScriptBridge::pwa_needs_update() const { Error JavaScriptBridge::pwa_update() { return ERR_UNAVAILABLE; } +void JavaScriptBridge::force_fs_sync() { +} void JavaScriptBridge::download_buffer(Vector<uint8_t> p_arr, const String &p_name, const String &p_mime) { } #endif diff --git a/platform/web/api/javascript_bridge_singleton.h b/platform/web/api/javascript_bridge_singleton.h index bcf1ed653a..456fa6b313 100644 --- a/platform/web/api/javascript_bridge_singleton.h +++ b/platform/web/api/javascript_bridge_singleton.h @@ -61,6 +61,7 @@ public: void download_buffer(Vector<uint8_t> p_arr, const String &p_name, const String &p_mime = "application/octet-stream"); bool pwa_needs_update() const; Error pwa_update(); + void force_fs_sync(); static JavaScriptBridge *get_singleton(); JavaScriptBridge(); diff --git a/platform/web/javascript_bridge_singleton.cpp b/platform/web/javascript_bridge_singleton.cpp index 308ca2d5de..dba630404f 100644 --- a/platform/web/javascript_bridge_singleton.cpp +++ b/platform/web/javascript_bridge_singleton.cpp @@ -361,6 +361,11 @@ void JavaScriptBridge::download_buffer(Vector<uint8_t> p_arr, const String &p_na bool JavaScriptBridge::pwa_needs_update() const { return OS_Web::get_singleton()->pwa_needs_update(); } + Error JavaScriptBridge::pwa_update() { return OS_Web::get_singleton()->pwa_update(); } + +void JavaScriptBridge::force_fs_sync() { + OS_Web::get_singleton()->force_fs_sync(); +} diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp index cb304ce7ac..e12f62f4ad 100644 --- a/platform/web/os_web.cpp +++ b/platform/web/os_web.cpp @@ -196,6 +196,12 @@ void OS_Web::update_pwa_state_callback() { } } +void OS_Web::force_fs_sync() { + if (is_userfs_persistent()) { + idb_needs_sync = true; + } +} + Error OS_Web::pwa_update() { return godot_js_pwa_update() ? FAILED : OK; } diff --git a/platform/web/os_web.h b/platform/web/os_web.h index c8fdea2ee0..70d8af9db9 100644 --- a/platform/web/os_web.h +++ b/platform/web/os_web.h @@ -69,6 +69,7 @@ public: bool pwa_needs_update() const { return pwa_is_waiting; } Error pwa_update(); + void force_fs_sync(); void initialize_joypads() override; diff --git a/platform/web/package-lock.json b/platform/web/package-lock.json index e1428546c6..4399e8243c 100644 --- a/platform/web/package-lock.json +++ b/platform/web/package-lock.json @@ -1529,9 +1529,9 @@ "dev": true }, "node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "dependencies": { "minimist": "^1.2.0" @@ -3662,9 +3662,9 @@ "dev": true }, "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "requires": { "minimist": "^1.2.0" diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 39be51879d..afd4763d74 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -1430,8 +1430,8 @@ void CPUParticles2D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_min", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_less"), "set_param_min", "get_param_min", PARAM_ANIM_SPEED); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_max", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_less"), "set_param_max", "get_param_max", PARAM_ANIM_SPEED); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_SPEED); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,1,0.0001"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,1,0.0001"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_OFFSET); BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY); diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index f24ed805c3..b9a161e476 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -1616,8 +1616,8 @@ void CPUParticles3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_min", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_less"), "set_param_min", "get_param_min", PARAM_ANIM_SPEED); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_max", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_less"), "set_param_max", "get_param_max", PARAM_ANIM_SPEED); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_SPEED); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,1,0.0001"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,1,0.0001"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_OFFSET); BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY); diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index e73bd460ed..e8b51ceb92 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -368,7 +368,7 @@ void SoftBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "parent_collision_ignore", PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE, "Parent collision object"), "set_parent_collision_ignore", "get_parent_collision_ignore"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "parent_collision_ignore", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CollisionObject3D"), "set_parent_collision_ignore", "get_parent_collision_ignore"); ADD_PROPERTY(PropertyInfo(Variant::INT, "simulation_precision", PROPERTY_HINT_RANGE, "1,100,1"), "set_simulation_precision", "get_simulation_precision"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "total_mass", PROPERTY_HINT_RANGE, "0.01,10000,1"), "set_total_mass", "get_total_mass"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_linear_stiffness", "get_linear_stiffness"); diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 46764b64b2..1e07a53642 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -572,7 +572,7 @@ void Button::_bind_methods() { ClassDB::bind_method(D_METHOD("set_expand_icon", "enabled"), &Button::set_expand_icon); ClassDB::bind_method(D_METHOD("is_expand_icon"), &Button::is_expand_icon); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_button_icon", "get_button_icon"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text"); diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 944363bcb0..b5846cb692 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -642,7 +642,7 @@ inline int ColorPicker::_get_preset_size() { void ColorPicker::_add_preset_button(int p_size, const Color &p_color) { ColorPresetButton *btn_preset_new = memnew(ColorPresetButton(p_color, p_size)); btn_preset_new->set_tooltip_text(vformat(RTR("Color: #%s\nLMB: Apply color\nRMB: Remove preset"), p_color.to_html(p_color.a < 1))); - btn_preset_new->set_drag_forwarding(this); + btn_preset_new->set_drag_forwarding_compat(this); btn_preset_new->set_button_group(preset_group); preset_container->add_child(btn_preset_new); btn_preset_new->set_pressed(true); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 8460728448..4188946494 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -864,6 +864,14 @@ void Control::_set_layout_mode(LayoutMode p_mode) { } } +void Control::_update_layout_mode() { + LayoutMode computed_layout = _get_layout_mode(); + if (data.stored_layout_mode != computed_layout) { + data.stored_layout_mode = computed_layout; + notify_property_list_changed(); + } +} + Control::LayoutMode Control::_get_layout_mode() const { Node *parent_node = get_parent_control(); // In these modes the property is read-only. @@ -1813,33 +1821,53 @@ bool Control::is_focus_owner_in_shortcut_context() const { // Drag and drop handling. -void Control::set_drag_forwarding(Object *p_target) { - if (p_target) { - data.drag_owner = p_target->get_instance_id(); +void Control::set_drag_forwarding_compat(Object *p_base) { + if (p_base != nullptr) { + data.forward_drag = Callable(p_base, "_get_drag_data_fw").bind(this); + data.forward_can_drop = Callable(p_base, "_can_drop_data_fw").bind(this); + data.forward_drop = Callable(p_base, "_drop_data_fw").bind(this); + } else { - data.drag_owner = ObjectID(); + data.forward_drag = Callable(); + data.forward_can_drop = Callable(); + data.forward_drop = Callable(); } } +void Control::set_drag_forwarding(const Callable &p_drag, const Callable &p_can_drop, const Callable &p_drop) { + data.forward_drag = p_drag; + data.forward_can_drop = p_can_drop; + data.forward_drop = p_drop; +} + Variant Control::get_drag_data(const Point2 &p_point) { - if (data.drag_owner.is_valid()) { - Object *obj = ObjectDB::get_instance(data.drag_owner); - if (obj) { - return obj->call("_get_drag_data_fw", p_point, this); + Variant ret; + if (data.forward_drag.is_valid()) { + Variant p = p_point; + const Variant *vp[1] = { &p }; + Callable::CallError ce; + data.forward_drag.callp((const Variant **)vp, 1, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_FAIL_V_MSG(Variant(), "Error calling forwarded method from 'get_drag_data': " + Variant::get_callable_error_text(data.forward_drag, (const Variant **)vp, 1, ce) + "."); } + return ret; } - Variant dd; - GDVIRTUAL_CALL(_get_drag_data, p_point, dd); - return dd; + GDVIRTUAL_CALL(_get_drag_data, p_point, ret); + return ret; } bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const { - if (data.drag_owner.is_valid()) { - Object *obj = ObjectDB::get_instance(data.drag_owner); - if (obj) { - return obj->call("_can_drop_data_fw", p_point, p_data, this); + if (data.forward_can_drop.is_valid()) { + Variant ret; + Variant p = p_point; + const Variant *vp[2] = { &p, &p_data }; + Callable::CallError ce; + data.forward_can_drop.callp((const Variant **)vp, 2, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_FAIL_V_MSG(Variant(), "Error calling forwarded method from 'can_drop_data': " + Variant::get_callable_error_text(data.forward_can_drop, (const Variant **)vp, 2, ce) + "."); } + return ret; } bool ret = false; @@ -1848,12 +1876,16 @@ bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const } void Control::drop_data(const Point2 &p_point, const Variant &p_data) { - if (data.drag_owner.is_valid()) { - Object *obj = ObjectDB::get_instance(data.drag_owner); - if (obj) { - obj->call("_drop_data_fw", p_point, p_data, this); - return; + if (data.forward_drop.is_valid()) { + Variant ret; + Variant p = p_point; + const Variant *vp[2] = { &p, &p_data }; + Callable::CallError ce; + data.forward_drop.callp((const Variant **)vp, 2, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_FAIL_MSG("Error calling forwarded method from 'drop_data': " + Variant::get_callable_error_text(data.forward_drop, (const Variant **)vp, 2, ce) + "."); } + return; } GDVIRTUAL_CALL(_drop_data, p_point, p_data); @@ -2870,6 +2902,8 @@ void Control::_notification(int p_notification) { data.parent_window = Object::cast_to<Window>(parent_node); data.theme_owner->assign_theme_on_parented(this); + + _update_layout_mode(); } break; case NOTIFICATION_UNPARENTED: { @@ -3189,7 +3223,7 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("grab_click_focus"), &Control::grab_click_focus); - ClassDB::bind_method(D_METHOD("set_drag_forwarding", "target"), &Control::set_drag_forwarding); + ClassDB::bind_method(D_METHOD("set_drag_forwarding", "drag_func", "can_drop_func", "drop_func"), &Control::set_drag_forwarding); ClassDB::bind_method(D_METHOD("set_drag_preview", "control"), &Control::set_drag_preview); ClassDB::bind_method(D_METHOD("is_drag_successful"), &Control::is_drag_successful); diff --git a/scene/gui/control.h b/scene/gui/control.h index 9705dd62db..22a37dd89e 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -168,7 +168,9 @@ private: Control *parent_control = nullptr; Window *parent_window = nullptr; CanvasItem *parent_canvas_item = nullptr; - ObjectID drag_owner; + Callable forward_drag; + Callable forward_can_drop; + Callable forward_drop; // Positioning and sizing. @@ -280,6 +282,7 @@ private: void _compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]); void _set_layout_mode(LayoutMode p_mode); + void _update_layout_mode(); LayoutMode _get_layout_mode() const; LayoutMode _get_default_layout_mode() const; void _set_anchors_layout_preset(int p_preset); @@ -499,7 +502,8 @@ public: // Drag and drop handling. - virtual void set_drag_forwarding(Object *p_target); + virtual void set_drag_forwarding(const Callable &p_drag, const Callable &p_can_drop, const Callable &p_drop); + virtual void set_drag_forwarding_compat(Object *p_base); virtual Variant get_drag_data(const Point2 &p_point); virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const; virtual void drop_data(const Point2 &p_point, const Variant &p_data); diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index 1d5e6320dd..4365db2ea2 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -378,7 +378,7 @@ void AcceptDialog::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "ok_button_text"), "set_ok_button_text", "get_ok_button_text"); ADD_GROUP("Dialog", "dialog_"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "dialog_text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "dialog_text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_hide_on_ok"), "set_hide_on_ok", "get_hide_on_ok"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_close_on_escape"), "set_close_on_escape", "get_close_on_escape"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_autowrap"), "set_autowrap", "has_autowrap"); diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 81e5de1886..cafea83f16 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -949,7 +949,7 @@ void Label::_bind_methods() { ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &Label::set_structured_text_bidi_override_options); ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &Label::get_structured_text_bidi_override_options); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "label_settings", PROPERTY_HINT_RESOURCE_TYPE, "LabelSettings"), "set_label_settings", "get_label_settings"); ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_vertical_alignment", "get_vertical_alignment"); diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp index d262959e8a..224e405d41 100644 --- a/scene/gui/menu_bar.cpp +++ b/scene/gui/menu_bar.cpp @@ -842,27 +842,6 @@ String MenuBar::get_tooltip(const Point2 &p_pos) const { } } -void MenuBar::get_translatable_strings(List<String> *p_strings) const { - Vector<PopupMenu *> popups = _get_popups(); - for (int i = 0; i < popups.size(); i++) { - PopupMenu *pm = popups[i]; - - if (!pm->has_meta("_menu_name") && !pm->has_meta("_menu_tooltip")) { - continue; - } - - String name = pm->get_meta("_menu_name"); - if (!name.is_empty()) { - p_strings->push_back(name); - } - - String tooltip = pm->get_meta("_menu_tooltip"); - if (!tooltip.is_empty()) { - p_strings->push_back(tooltip); - } - } -} - MenuBar::MenuBar() { set_process_shortcut_input(true); } diff --git a/scene/gui/menu_bar.h b/scene/gui/menu_bar.h index 306fcc01ab..3436978a0e 100644 --- a/scene/gui/menu_bar.h +++ b/scene/gui/menu_bar.h @@ -170,7 +170,6 @@ public: PopupMenu *get_menu_popup(int p_menu) const; - virtual void get_translatable_strings(List<String> *p_strings) const override; virtual String get_tooltip(const Point2 &p_pos) const override; MenuBar(); diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index f21b5f43c6..027c97b383 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -519,10 +519,6 @@ void OptionButton::show_popup() { popup->popup(); } -void OptionButton::get_translatable_strings(List<String> *p_strings) const { - popup->get_translatable_strings(p_strings); -} - void OptionButton::_validate_property(PropertyInfo &p_property) const { if (p_property.name == "text" || p_property.name == "icon") { p_property.usage = PROPERTY_USAGE_NONE; diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h index 9e409356e2..d8e2f3209d 100644 --- a/scene/gui/option_button.h +++ b/scene/gui/option_button.h @@ -125,8 +125,6 @@ public: PopupMenu *get_popup() const; void show_popup(); - virtual void get_translatable_strings(List<String> *p_strings) const override; - OptionButton(const String &p_text = String()); ~OptionButton(); }; diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 4fc0e5b05e..4e8a44dd63 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -1841,14 +1841,6 @@ void PopupMenu::set_parent_rect(const Rect2 &p_rect) { parent_rect = p_rect; } -void PopupMenu::get_translatable_strings(List<String> *p_strings) const { - for (int i = 0; i < items.size(); i++) { - if (!items[i].xl_text.is_empty()) { - p_strings->push_back(items[i].xl_text); - } - } -} - void PopupMenu::add_autohide_area(const Rect2 &p_area) { autohide_areas.push_back(p_area); } diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 94bb93c867..bcc02a890f 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -284,8 +284,6 @@ public: virtual String get_tooltip(const Point2 &p_pos) const; - virtual void get_translatable_strings(List<String> *p_strings) const override; - void add_autohide_area(const Rect2 &p_area); void clear_autohide_areas(); diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 0d7b055ce8..3457cfa94f 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -803,22 +803,6 @@ Ref<Texture2D> TabContainer::get_tab_button_icon(int p_tab) const { return tab_bar->get_tab_button_icon(p_tab); } -void TabContainer::get_translatable_strings(List<String> *p_strings) const { - Vector<Control *> controls = _get_tab_controls(); - for (int i = 0; i < controls.size(); i++) { - Control *c = controls[i]; - - if (!c->has_meta("_tab_name")) { - continue; - } - - String name = c->get_meta("_tab_name"); - if (!name.is_empty()) { - p_strings->push_back(name); - } - } -} - Size2 TabContainer::get_minimum_size() const { Size2 ms; @@ -991,7 +975,7 @@ void TabContainer::_bind_methods() { TabContainer::TabContainer() { tab_bar = memnew(TabBar); - tab_bar->set_drag_forwarding(this); + tab_bar->set_drag_forwarding_compat(this); add_child(tab_bar, false, INTERNAL_MODE_FRONT); tab_bar->set_anchors_and_offsets_preset(Control::PRESET_TOP_WIDE); tab_bar->connect("tab_changed", callable_mp(this, &TabContainer::_on_tab_changed)); diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index 4a0205c2f4..3020e1fada 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -147,8 +147,6 @@ public: virtual Size2 get_minimum_size() const override; - virtual void get_translatable_strings(List<String> *p_strings) const override; - void set_popup(Node *p_popup); Popup *get_popup() const; diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index ace3edfcb0..3458b87b8d 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -4850,7 +4850,11 @@ void Tree::_do_incr_search(const String &p_add) { return; } - item->select(col); + if (select_mode == SELECT_MULTI) { + item->set_as_cursor(col); + } else { + item->select(col); + } ensure_cursor_is_visible(); } @@ -5180,6 +5184,7 @@ void Tree::_bind_methods() { ClassDB::bind_method(D_METHOD("get_pressed_button"), &Tree::get_pressed_button); ClassDB::bind_method(D_METHOD("set_select_mode", "mode"), &Tree::set_select_mode); ClassDB::bind_method(D_METHOD("get_select_mode"), &Tree::get_select_mode); + ClassDB::bind_method(D_METHOD("deselect_all"), &Tree::deselect_all); ClassDB::bind_method(D_METHOD("set_columns", "amount"), &Tree::set_columns); ClassDB::bind_method(D_METHOD("get_columns"), &Tree::get_columns); diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index a04c299705..3f98b540fc 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -195,7 +195,15 @@ void CanvasItem::_top_level_raise_self() { } void CanvasItem::_enter_canvas() { - if ((!Object::cast_to<CanvasItem>(get_parent())) || top_level) { + // Resolves to nullptr if the node is toplevel. + CanvasItem *parent_item = get_parent_item(); + + if (parent_item) { + canvas_layer = parent_item->canvas_layer; + RenderingServer::get_singleton()->canvas_item_set_parent(canvas_item, parent_item->get_canvas_item()); + RenderingServer::get_singleton()->canvas_item_set_draw_index(canvas_item, get_index()); + RenderingServer::get_singleton()->canvas_item_set_visibility_layer(canvas_item, visibility_layer); + } else { Node *n = this; canvas_layer = nullptr; @@ -231,13 +239,6 @@ void CanvasItem::_enter_canvas() { } get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE | SceneTree::GROUP_CALL_DEFERRED, canvas_group, SNAME("_top_level_raise_self")); - - } else { - CanvasItem *parent = get_parent_item(); - canvas_layer = parent->canvas_layer; - RenderingServer::get_singleton()->canvas_item_set_parent(canvas_item, parent->get_canvas_item()); - RenderingServer::get_singleton()->canvas_item_set_draw_index(canvas_item, get_index()); - RenderingServer::get_singleton()->canvas_item_set_visibility_layer(canvas_item, visibility_layer); } pending_update = false; @@ -320,8 +321,7 @@ void CanvasItem::_notification(int p_what) { if (canvas_group != StringName()) { get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE | SceneTree::GROUP_CALL_DEFERRED, canvas_group, "_top_level_raise_self"); } else { - CanvasItem *p = get_parent_item(); - ERR_FAIL_COND(!p); + ERR_FAIL_COND_MSG(!get_parent_item(), "Moved child is in incorrect state (no canvas group, no canvas item parent)."); RenderingServer::get_singleton()->canvas_item_set_draw_index(canvas_item, get_index()); } } break; diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 3544ec4c83..eb57ccfef1 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -39,6 +39,7 @@ #include "scene/animation/tween.h" #include "scene/debugger/scene_debugger.h" #include "scene/main/multiplayer_api.h" +#include "scene/main/window.h" #include "scene/resources/packed_scene.h" #include "scene/scene_string_names.h" #include "viewport.h" @@ -1467,6 +1468,14 @@ Node *Node::find_parent(const String &p_pattern) const { return nullptr; } +Window *Node::get_window() const { + Viewport *vp = get_viewport(); + if (vp) { + return vp->get_base_window(); + } + return nullptr; +} + bool Node::is_ancestor_of(const Node *p_node) const { ERR_FAIL_NULL_V(p_node, false); Node *p = p_node->data.parent; @@ -2858,6 +2867,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("set_physics_process_internal", "enable"), &Node::set_physics_process_internal); ClassDB::bind_method(D_METHOD("is_physics_processing_internal"), &Node::is_physics_processing_internal); + ClassDB::bind_method(D_METHOD("get_window"), &Node::get_window); ClassDB::bind_method(D_METHOD("get_tree"), &Node::get_tree); ClassDB::bind_method(D_METHOD("create_tween"), &Node::create_tween); diff --git a/scene/main/node.h b/scene/main/node.h index 398465c3cd..dbdcca6170 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -37,6 +37,7 @@ #include "scene/main/scene_tree.h" class Viewport; +class Window; class SceneState; class Tween; class PropertyTweener; @@ -321,6 +322,8 @@ public: Node *get_parent() const; Node *find_parent(const String &p_pattern) const; + Window *get_window() const; + _FORCE_INLINE_ SceneTree *get_tree() const { ERR_FAIL_COND_V(!data.tree, nullptr); return data.tree; diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 73d85a0660..b5c587c0aa 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -1131,11 +1131,11 @@ Error SceneTree::change_scene_to_file(const String &p_path) { } Error SceneTree::change_scene_to_packed(const Ref<PackedScene> &p_scene) { - Node *new_scene = nullptr; - if (p_scene.is_valid()) { - new_scene = p_scene->instantiate(); - ERR_FAIL_COND_V(!new_scene, ERR_CANT_CREATE); - } + ERR_FAIL_COND_V_MSG(p_scene.is_null(), ERR_INVALID_PARAMETER, "Can't change to a null scene. Use unload_current_scene() if you wish to unload it."); + + Node *new_scene = p_scene->instantiate(); + new_scene = p_scene->instantiate(); + ERR_FAIL_COND_V(!new_scene, ERR_CANT_CREATE); call_deferred(SNAME("_change_scene"), new_scene); return OK; @@ -1147,6 +1147,13 @@ Error SceneTree::reload_current_scene() { return change_scene_to_file(fname); } +void SceneTree::unload_current_scene() { + if (current_scene) { + memdelete(current_scene); + current_scene = nullptr; + } +} + void SceneTree::add_current_scene(Node *p_current) { current_scene = p_current; root->add_child(p_current); @@ -1297,6 +1304,7 @@ void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("change_scene_to_packed", "packed_scene"), &SceneTree::change_scene_to_packed); ClassDB::bind_method(D_METHOD("reload_current_scene"), &SceneTree::reload_current_scene); + ClassDB::bind_method(D_METHOD("unload_current_scene"), &SceneTree::unload_current_scene); ClassDB::bind_method(D_METHOD("_change_scene"), &SceneTree::_change_scene); @@ -1396,6 +1404,7 @@ SceneTree::SceneTree() { // Create with mainloop. root = memnew(Window); + root->set_min_size(Size2i(64, 64)); // Define a very small minimum window size to prevent bugs such as GH-37242. root->set_process_mode(Node::PROCESS_MODE_PAUSABLE); root->set_name("root"); root->set_title(GLOBAL_GET("application/config/name")); diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 77cfe0a04a..fc185b4f37 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -361,6 +361,7 @@ public: Error change_scene_to_file(const String &p_path); Error change_scene_to_packed(const Ref<PackedScene> &p_scene); Error reload_current_scene(); + void unload_current_scene(); Ref<SceneTreeTimer> create_timer(double p_delay_sec, bool p_process_always = true, bool p_process_in_physics = false, bool p_ignore_time_scale = false); Ref<Tween> create_tween(); diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp index a7f48b92fe..7ae154ea1d 100644 --- a/scene/resources/particle_process_material.cpp +++ b/scene/resources/particle_process_material.cpp @@ -1756,8 +1756,8 @@ void ParticleProcessMaterial::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_min", PROPERTY_HINT_RANGE, "0,16,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_ANIM_SPEED); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_max", PROPERTY_HINT_RANGE, "0,16,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_ANIM_SPEED); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_SPEED); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,16,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,16,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,1,0.0001"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,1,0.0001"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_OFFSET); ADD_GROUP("Sub Emitter", "sub_emitter_"); diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index ade8875935..80b9ff3f38 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -449,10 +449,10 @@ Error ResourceLoaderText::load() { #ifdef TOOLS_ENABLED // Silence a warning that can happen during the initial filesystem scan due to cache being regenerated. if (ResourceLoader::get_resource_uid(path) != uid) { - WARN_PRINT(String(res_path + ":" + itos(lines) + " - ext_resource, invalid UUID: " + uidt + " - using text path instead: " + path).utf8().get_data()); + WARN_PRINT(String(res_path + ":" + itos(lines) + " - ext_resource, invalid UID: " + uidt + " - using text path instead: " + path).utf8().get_data()); } #else - WARN_PRINT(String(res_path + ":" + itos(lines) + " - ext_resource, invalid UUID: " + uidt + " - using text path instead: " + path).utf8().get_data()); + WARN_PRINT(String(res_path + ":" + itos(lines) + " - ext_resource, invalid UID: " + uidt + " - using text path instead: " + path).utf8().get_data()); #endif } } @@ -601,14 +601,14 @@ Error ResourceLoaderText::load() { resource_current++; + if (progress && resources_total > 0) { + *progress = resource_current / float(resources_total); + } + int_resources[id] = res; //always assign int resources - if (do_assign) { - if (cache_mode == ResourceFormatLoader::CACHE_MODE_IGNORE) { - res->set_path(path); - } else { - res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); - res->set_scene_unique_id(id); - } + if (do_assign && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { + res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); + res->set_scene_unique_id(id); } Dictionary missing_resource_properties; @@ -663,10 +663,6 @@ Error ResourceLoaderText::load() { if (!missing_resource_properties.is_empty()) { res->set_meta(META_MISSING_RESOURCES, missing_resource_properties); } - - if (progress && resources_total > 0) { - *progress = resource_current / float(resources_total); - } } while (true) { @@ -716,8 +712,6 @@ Error ResourceLoaderText::load() { resource = Ref<Resource>(r); } - resource_current++; - Dictionary missing_resource_properties; while (true) { @@ -770,6 +764,12 @@ Error ResourceLoaderText::load() { } } + resource_current++; + + if (progress && resources_total > 0) { + *progress = resource_current / float(resources_total); + } + if (missing_resource) { missing_resource->set_recording_properties(false); } @@ -779,9 +779,6 @@ Error ResourceLoaderText::load() { } error = OK; - if (progress && resources_total > 0) { - *progress = resource_current / float(resources_total); - } return error; } @@ -2237,6 +2234,35 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso return OK; } +Error ResourceLoaderText::set_uid(Ref<FileAccess> p_f, ResourceUID::ID p_uid) { + open(p_f, true); + ERR_FAIL_COND_V(error != OK, error); + ignore_resource_parsing = true; + + Ref<FileAccess> fw; + + fw = FileAccess::open(local_path + ".uidren", FileAccess::WRITE); + if (is_scene) { + fw->store_string("[gd_scene load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + " uid=\"" + ResourceUID::get_singleton()->id_to_text(p_uid) + "\"]"); + } else { + fw->store_string("[gd_resource type=\"" + res_type + "\" load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + " uid=\"" + ResourceUID::get_singleton()->id_to_text(p_uid) + "\"]"); + } + + uint8_t c = f->get_8(); + while (!f->eof_reached()) { + fw->store_8(c); + c = f->get_8(); + } + + bool all_ok = fw->get_error() == OK; + + if (!all_ok) { + return ERR_CANT_CREATE; + } + + return OK; +} + Error ResourceFormatSaverText::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) { if (p_path.ends_with(".tscn") && !Ref<PackedScene>(p_resource).is_valid()) { return ERR_FILE_UNRECOGNIZED; @@ -2246,6 +2272,35 @@ Error ResourceFormatSaverText::save(const Ref<Resource> &p_resource, const Strin return saver.save(p_path, p_resource, p_flags); } +Error ResourceFormatSaverText::set_uid(const String &p_path, ResourceUID::ID p_uid) { + String lc = p_path.to_lower(); + if (!lc.ends_with(".tscn") && !lc.ends_with(".tres")) { + return ERR_FILE_UNRECOGNIZED; + } + + String local_path = ProjectSettings::get_singleton()->localize_path(p_path); + Error err = OK; + { + Ref<FileAccess> fo = FileAccess::open(p_path, FileAccess::READ); + if (fo.is_null()) { + ERR_FAIL_V(ERR_CANT_OPEN); + } + + ResourceLoaderText loader; + loader.local_path = local_path; + loader.res_path = loader.local_path; + err = loader.set_uid(fo, p_uid); + } + + if (err == OK) { + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + da->remove(local_path); + da->rename(local_path + ".uidren", local_path); + } + + return err; +} + bool ResourceFormatSaverText::recognize(const Ref<Resource> &p_resource) const { return true; // All resources recognized! } diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h index f96511fb74..0f95e2fbfd 100644 --- a/scene/resources/resource_format_text.h +++ b/scene/resources/resource_format_text.h @@ -106,6 +106,7 @@ class ResourceLoaderText { VariantParser::ResourceParser rp; friend class ResourceFormatLoaderText; + friend class ResourceFormatSaverText; Error error = OK; @@ -117,6 +118,7 @@ public: void set_local_path(const String &p_local_path); Ref<Resource> get_resource(); Error load(); + Error set_uid(Ref<FileAccess> p_f, ResourceUID::ID p_uid); int get_stage() const; int get_stage_count() const; void set_translation_remapped(bool p_remapped); @@ -195,6 +197,7 @@ class ResourceFormatSaverText : public ResourceFormatSaver { public: static ResourceFormatSaverText *singleton; virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0); + virtual Error set_uid(const String &p_path, ResourceUID::ID p_uid); virtual bool recognize(const Ref<Resource> &p_resource) const; virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const; diff --git a/servers/extensions/physics_server_2d_extension.cpp b/servers/extensions/physics_server_2d_extension.cpp index a0c082ee44..2dbb59a323 100644 --- a/servers/extensions/physics_server_2d_extension.cpp +++ b/servers/extensions/physics_server_2d_extension.cpp @@ -37,6 +37,8 @@ bool PhysicsDirectSpaceState2DExtension::is_body_excluded_from_query(const RID & thread_local const HashSet<RID> *PhysicsDirectSpaceState2DExtension::exclude = nullptr; void PhysicsDirectSpaceState2DExtension::_bind_methods() { + ClassDB::bind_method(D_METHOD("is_body_excluded_from_query", "body"), &PhysicsDirectSpaceState2DExtension::is_body_excluded_from_query); + GDVIRTUAL_BIND(_intersect_ray, "from", "to", "collision_mask", "collide_with_bodies", "collide_with_areas", "hit_from_inside", "result"); GDVIRTUAL_BIND(_intersect_point, "position", "canvas_instance_id", "collision_mask", "collide_with_bodies", "collide_with_areas", "results", "max_results"); GDVIRTUAL_BIND(_intersect_shape, "shape_rid", "transform", "motion", "margin", "collision_mask", "collide_with_bodies", "collide_with_areas", "result", "max_results"); @@ -101,6 +103,7 @@ void PhysicsDirectBodyState2DExtension::_bind_methods() { GDVIRTUAL_BIND(_get_contact_collider_object, "contact_idx"); GDVIRTUAL_BIND(_get_contact_collider_shape, "contact_idx"); GDVIRTUAL_BIND(_get_contact_collider_velocity_at_position, "contact_idx"); + GDVIRTUAL_BIND(_get_contact_impulse, "contact_idx"); GDVIRTUAL_BIND(_get_step); GDVIRTUAL_BIND(_integrate_forces); @@ -202,6 +205,9 @@ void PhysicsServer2DExtension::_bind_methods() { /* BODY API */ + ClassDB::bind_method(D_METHOD("body_test_motion_is_excluding_body", "body"), &PhysicsServer2DExtension::body_test_motion_is_excluding_body); + ClassDB::bind_method(D_METHOD("body_test_motion_is_excluding_object", "object"), &PhysicsServer2DExtension::body_test_motion_is_excluding_object); + GDVIRTUAL_BIND(_body_create); GDVIRTUAL_BIND(_body_set_space, "body", "space"); diff --git a/servers/extensions/physics_server_2d_extension.h b/servers/extensions/physics_server_2d_extension.h index c4970f6398..0008653f66 100644 --- a/servers/extensions/physics_server_2d_extension.h +++ b/servers/extensions/physics_server_2d_extension.h @@ -100,6 +100,7 @@ public: EXBIND1RC(Object *, get_contact_collider_object, int) EXBIND1RC(int, get_contact_collider_shape, int) EXBIND1RC(Vector2, get_contact_collider_velocity_at_position, int) + EXBIND1RC(Vector2, get_contact_impulse, int) EXBIND0RC(real_t, get_step) EXBIND0(integrate_forces) diff --git a/servers/extensions/physics_server_3d_extension.cpp b/servers/extensions/physics_server_3d_extension.cpp index 788ece721b..0d8476fd68 100644 --- a/servers/extensions/physics_server_3d_extension.cpp +++ b/servers/extensions/physics_server_3d_extension.cpp @@ -37,6 +37,8 @@ bool PhysicsDirectSpaceState3DExtension::is_body_excluded_from_query(const RID & thread_local const HashSet<RID> *PhysicsDirectSpaceState3DExtension::exclude = nullptr; void PhysicsDirectSpaceState3DExtension::_bind_methods() { + ClassDB::bind_method(D_METHOD("is_body_excluded_from_query", "body"), &PhysicsDirectSpaceState3DExtension::is_body_excluded_from_query); + GDVIRTUAL_BIND(_intersect_ray, "from", "to", "collision_mask", "collide_with_bodies", "collide_with_areas", "hit_from_inside", "hit_back_faces", "result"); GDVIRTUAL_BIND(_intersect_point, "position", "collision_mask", "collide_with_bodies", "collide_with_areas", "results", "max_results"); GDVIRTUAL_BIND(_intersect_shape, "shape_rid", "transform", "motion", "margin", "collision_mask", "collide_with_bodies", "collide_with_areas", "result_count", "max_results"); @@ -206,6 +208,9 @@ void PhysicsServer3DExtension::_bind_methods() { /* BODY API */ + ClassDB::bind_method(D_METHOD("body_test_motion_is_excluding_body", "body"), &PhysicsServer3DExtension::body_test_motion_is_excluding_body); + ClassDB::bind_method(D_METHOD("body_test_motion_is_excluding_object", "object"), &PhysicsServer3DExtension::body_test_motion_is_excluding_object); + GDVIRTUAL_BIND(_body_create); GDVIRTUAL_BIND(_body_set_space, "body", "space"); diff --git a/servers/physics_2d/godot_body_2d.h b/servers/physics_2d/godot_body_2d.h index 34da64dac9..71dc826604 100644 --- a/servers/physics_2d/godot_body_2d.h +++ b/servers/physics_2d/godot_body_2d.h @@ -132,6 +132,7 @@ class GodotBody2D : public GodotCollisionObject2D { ObjectID collider_instance_id; RID collider; Vector2 collider_velocity_at_pos; + Vector2 impulse; }; Vector<Contact> contacts; //no contacts by default @@ -190,7 +191,7 @@ public: _FORCE_INLINE_ int get_max_contacts_reported() const { return contacts.size(); } _FORCE_INLINE_ bool can_report_contacts() const { return !contacts.is_empty(); } - _FORCE_INLINE_ void add_contact(const Vector2 &p_local_pos, const Vector2 &p_local_normal, real_t p_depth, int p_local_shape, const Vector2 &p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID &p_collider, const Vector2 &p_collider_velocity_at_pos); + _FORCE_INLINE_ void add_contact(const Vector2 &p_local_pos, const Vector2 &p_local_normal, real_t p_depth, int p_local_shape, const Vector2 &p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID &p_collider, const Vector2 &p_collider_velocity_at_pos, const Vector2 &p_impulse); _FORCE_INLINE_ void add_exception(const RID &p_exception) { exceptions.insert(p_exception); } _FORCE_INLINE_ void remove_exception(const RID &p_exception) { exceptions.erase(p_exception); } @@ -340,7 +341,7 @@ public: //add contact inline -void GodotBody2D::add_contact(const Vector2 &p_local_pos, const Vector2 &p_local_normal, real_t p_depth, int p_local_shape, const Vector2 &p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID &p_collider, const Vector2 &p_collider_velocity_at_pos) { +void GodotBody2D::add_contact(const Vector2 &p_local_pos, const Vector2 &p_local_normal, real_t p_depth, int p_local_shape, const Vector2 &p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID &p_collider, const Vector2 &p_collider_velocity_at_pos, const Vector2 &p_impulse) { int c_max = contacts.size(); if (c_max == 0) { @@ -380,6 +381,7 @@ void GodotBody2D::add_contact(const Vector2 &p_local_pos, const Vector2 &p_local c[idx].collider_instance_id = p_collider_instance_id; c[idx].collider = p_collider; c[idx].collider_velocity_at_pos = p_collider_velocity_at_pos; + c[idx].impulse = p_impulse; } #endif // GODOT_BODY_2D_H diff --git a/servers/physics_2d/godot_body_direct_state_2d.cpp b/servers/physics_2d/godot_body_direct_state_2d.cpp index 35092b631a..2fa933ce17 100644 --- a/servers/physics_2d/godot_body_direct_state_2d.cpp +++ b/servers/physics_2d/godot_body_direct_state_2d.cpp @@ -210,6 +210,11 @@ Vector2 GodotPhysicsDirectBodyState2D::get_contact_collider_velocity_at_position return body->contacts[p_contact_idx].collider_velocity_at_pos; } +Vector2 GodotPhysicsDirectBodyState2D::get_contact_impulse(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2()); + return body->contacts[p_contact_idx].impulse; +} + PhysicsDirectSpaceState2D *GodotPhysicsDirectBodyState2D::get_space_state() { return body->get_space()->get_direct_state(); } diff --git a/servers/physics_2d/godot_body_direct_state_2d.h b/servers/physics_2d/godot_body_direct_state_2d.h index c01ee62920..545d52ad23 100644 --- a/servers/physics_2d/godot_body_direct_state_2d.h +++ b/servers/physics_2d/godot_body_direct_state_2d.h @@ -92,8 +92,8 @@ public: virtual Vector2 get_contact_collider_position(int p_contact_idx) const override; virtual ObjectID get_contact_collider_id(int p_contact_idx) const override; virtual int get_contact_collider_shape(int p_contact_idx) const override; - virtual Vector2 get_contact_collider_velocity_at_position(int p_contact_idx) const override; + virtual Vector2 get_contact_impulse(int p_contact_idx) const override; virtual PhysicsDirectSpaceState2D *get_space_state() override; diff --git a/servers/physics_2d/godot_body_pair_2d.cpp b/servers/physics_2d/godot_body_pair_2d.cpp index 474367def0..40dbb4fcf4 100644 --- a/servers/physics_2d/godot_body_pair_2d.cpp +++ b/servers/physics_2d/godot_body_pair_2d.cpp @@ -434,21 +434,6 @@ bool GodotBodyPair2D::pre_solve(real_t p_step) { c.rA = global_A - A->get_center_of_mass(); c.rB = global_B - B->get_center_of_mass() - offset_B; - if (A->can_report_contacts()) { - Vector2 crB(-B->get_angular_velocity() * c.rB.y, B->get_angular_velocity() * c.rB.x); - A->add_contact(global_A + offset_A, -c.normal, depth, shape_A, global_B + offset_A, shape_B, B->get_instance_id(), B->get_self(), crB + B->get_linear_velocity()); - } - - if (B->can_report_contacts()) { - Vector2 crA(-A->get_angular_velocity() * c.rA.y, A->get_angular_velocity() * c.rA.x); - B->add_contact(global_B + offset_A, c.normal, depth, shape_B, global_A + offset_A, shape_A, A->get_instance_id(), A->get_self(), crA + A->get_linear_velocity()); - } - - if (report_contacts_only) { - collided = false; - continue; - } - // Precompute normal mass, tangent mass, and bias. real_t rnA = c.rA.dot(c.normal); real_t rnB = c.rB.dot(c.normal); @@ -466,11 +451,28 @@ bool GodotBodyPair2D::pre_solve(real_t p_step) { c.bias = -bias * inv_dt * MIN(0.0f, -depth + max_penetration); c.depth = depth; + Vector2 P = c.acc_normal_impulse * c.normal + c.acc_tangent_impulse * tangent; + + c.acc_impulse -= P; + + if (A->can_report_contacts()) { + Vector2 crB(-B->get_angular_velocity() * c.rB.y, B->get_angular_velocity() * c.rB.x); + A->add_contact(global_A + offset_A, -c.normal, depth, shape_A, global_B + offset_A, shape_B, B->get_instance_id(), B->get_self(), crB + B->get_linear_velocity(), c.acc_impulse); + } + + if (B->can_report_contacts()) { + Vector2 crA(-A->get_angular_velocity() * c.rA.y, A->get_angular_velocity() * c.rA.x); + B->add_contact(global_B + offset_A, c.normal, depth, shape_B, global_A + offset_A, shape_A, A->get_instance_id(), A->get_self(), crA + A->get_linear_velocity(), c.acc_impulse); + } + + if (report_contacts_only) { + collided = false; + continue; + } + #ifdef ACCUMULATE_IMPULSES { // Apply normal + friction impulse - Vector2 P = c.acc_normal_impulse * c.normal + c.acc_tangent_impulse * tangent; - if (collide_A) { A->apply_impulse(-P, c.rA + A->get_center_of_mass()); } @@ -581,6 +583,7 @@ void GodotBodyPair2D::solve(real_t p_step) { if (collide_B) { B->apply_impulse(j, c.rB + B->get_center_of_mass()); } + c.acc_impulse -= j; } } diff --git a/servers/physics_2d/godot_body_pair_2d.h b/servers/physics_2d/godot_body_pair_2d.h index 7e7a9839c1..4e9bfa6022 100644 --- a/servers/physics_2d/godot_body_pair_2d.h +++ b/servers/physics_2d/godot_body_pair_2d.h @@ -59,6 +59,7 @@ class GodotBodyPair2D : public GodotConstraint2D { Vector2 position; Vector2 normal; Vector2 local_A, local_B; + Vector2 acc_impulse; // accumulated impulse real_t acc_normal_impulse = 0.0; // accumulated normal impulse (Pn) real_t acc_tangent_impulse = 0.0; // accumulated tangent impulse (Pt) real_t acc_bias_impulse = 0.0; // accumulated normal impulse for position bias (Pnb) diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp index 4973b9970a..214de27b35 100644 --- a/servers/physics_server_2d.cpp +++ b/servers/physics_server_2d.cpp @@ -126,6 +126,7 @@ void PhysicsDirectBodyState2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_contact_collider_object", "contact_idx"), &PhysicsDirectBodyState2D::get_contact_collider_object); ClassDB::bind_method(D_METHOD("get_contact_collider_shape", "contact_idx"), &PhysicsDirectBodyState2D::get_contact_collider_shape); ClassDB::bind_method(D_METHOD("get_contact_collider_velocity_at_position", "contact_idx"), &PhysicsDirectBodyState2D::get_contact_collider_velocity_at_position); + ClassDB::bind_method(D_METHOD("get_contact_impulse", "contact_idx"), &PhysicsDirectBodyState2D::get_contact_impulse); ClassDB::bind_method(D_METHOD("get_step"), &PhysicsDirectBodyState2D::get_step); ClassDB::bind_method(D_METHOD("integrate_forces"), &PhysicsDirectBodyState2D::integrate_forces); ClassDB::bind_method(D_METHOD("get_space_state"), &PhysicsDirectBodyState2D::get_space_state); diff --git a/servers/physics_server_2d.h b/servers/physics_server_2d.h index aac4d9d69e..836ab5bd76 100644 --- a/servers/physics_server_2d.h +++ b/servers/physics_server_2d.h @@ -99,6 +99,7 @@ public: virtual Object *get_contact_collider_object(int p_contact_idx) const; virtual int get_contact_collider_shape(int p_contact_idx) const = 0; virtual Vector2 get_contact_collider_velocity_at_position(int p_contact_idx) const = 0; + virtual Vector2 get_contact_impulse(int p_contact_idx) const = 0; virtual real_t get_step() const = 0; virtual void integrate_forces(); diff --git a/servers/rendering/renderer_rd/effects/luminance.cpp b/servers/rendering/renderer_rd/effects/luminance.cpp new file mode 100644 index 0000000000..7462282932 --- /dev/null +++ b/servers/rendering/renderer_rd/effects/luminance.cpp @@ -0,0 +1,255 @@ +/**************************************************************************/ +/* luminance.cpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#include "luminance.h" +#include "../framebuffer_cache_rd.h" +#include "../uniform_set_cache_rd.h" +#include "servers/rendering/renderer_rd/storage_rd/material_storage.h" + +using namespace RendererRD; + +Luminance::Luminance(bool p_prefer_raster_effects) { + prefer_raster_effects = p_prefer_raster_effects; + + if (prefer_raster_effects) { + Vector<String> luminance_reduce_modes; + luminance_reduce_modes.push_back("\n#define FIRST_PASS\n"); // LUMINANCE_REDUCE_FRAGMENT_FIRST + luminance_reduce_modes.push_back("\n"); // LUMINANCE_REDUCE_FRAGMENT + luminance_reduce_modes.push_back("\n#define FINAL_PASS\n"); // LUMINANCE_REDUCE_FRAGMENT_FINAL + + luminance_reduce_raster.shader.initialize(luminance_reduce_modes); + luminance_reduce_raster.shader_version = luminance_reduce_raster.shader.version_create(); + + for (int i = 0; i < LUMINANCE_REDUCE_FRAGMENT_MAX; i++) { + luminance_reduce_raster.pipelines[i].setup(luminance_reduce_raster.shader.version_get_shader(luminance_reduce_raster.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0); + } + } else { + // Initialize luminance_reduce + Vector<String> luminance_reduce_modes; + luminance_reduce_modes.push_back("\n#define READ_TEXTURE\n"); + luminance_reduce_modes.push_back("\n"); + luminance_reduce_modes.push_back("\n#define WRITE_LUMINANCE\n"); + + luminance_reduce.shader.initialize(luminance_reduce_modes); + luminance_reduce.shader_version = luminance_reduce.shader.version_create(); + + for (int i = 0; i < LUMINANCE_REDUCE_MAX; i++) { + luminance_reduce.pipelines[i] = RD::get_singleton()->compute_pipeline_create(luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, i)); + } + + for (int i = 0; i < LUMINANCE_REDUCE_FRAGMENT_MAX; i++) { + luminance_reduce_raster.pipelines[i].clear(); + } + } +} + +Luminance::~Luminance() { + if (prefer_raster_effects) { + luminance_reduce_raster.shader.version_free(luminance_reduce_raster.shader_version); + } else { + luminance_reduce.shader.version_free(luminance_reduce.shader_version); + } +} + +void Luminance::LuminanceBuffers::set_prefer_raster_effects(bool p_prefer_raster_effects) { + prefer_raster_effects = p_prefer_raster_effects; +} + +void Luminance::LuminanceBuffers::configure(RenderSceneBuffersRD *p_render_buffers) { + Size2i internal_size = p_render_buffers->get_internal_size(); + int w = internal_size.x; + int h = internal_size.y; + + while (true) { + w = MAX(w / 8, 1); + h = MAX(h / 8, 1); + + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R32_SFLOAT; + tf.width = w; + tf.height = h; + + bool final = w == 1 && h == 1; + + if (prefer_raster_effects) { + tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; + } else { + tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT; + if (final) { + tf.usage_bits |= RD::TEXTURE_USAGE_SAMPLING_BIT; + } + } + + RID texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + reduce.push_back(texture); + + if (final) { + current = RD::get_singleton()->texture_create(tf, RD::TextureView()); + break; + } + } +} + +void Luminance::LuminanceBuffers::free_data() { + for (int i = 0; i < reduce.size(); i++) { + RD::get_singleton()->free(reduce[i]); + } + reduce.clear(); + + if (current.is_valid()) { + RD::get_singleton()->free(current); + current = RID(); + } +} + +Ref<Luminance::LuminanceBuffers> Luminance::get_luminance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers) { + if (p_render_buffers->has_custom_data(RB_LUMINANCE_BUFFERS)) { + return p_render_buffers->get_custom_data(RB_LUMINANCE_BUFFERS); + } + + Ref<LuminanceBuffers> buffers; + buffers.instantiate(); + buffers->set_prefer_raster_effects(prefer_raster_effects); + buffers->configure(p_render_buffers.ptr()); + + p_render_buffers->set_custom_data(RB_LUMINANCE_BUFFERS, buffers); + + return buffers; +} + +RID Luminance::get_current_luminance_buffer(Ref<RenderSceneBuffersRD> p_render_buffers) { + if (p_render_buffers->has_custom_data(RB_LUMINANCE_BUFFERS)) { + Ref<LuminanceBuffers> buffers = p_render_buffers->get_custom_data(RB_LUMINANCE_BUFFERS); + return buffers->current; + } + + return RID(); +} + +void Luminance::luminance_reduction(RID p_source_texture, const Size2i p_source_size, Ref<LuminanceBuffers> p_luminance_buffers, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set) { + UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); + ERR_FAIL_NULL(uniform_set_cache); + MaterialStorage *material_storage = MaterialStorage::get_singleton(); + ERR_FAIL_NULL(material_storage); + + // setup our uniforms + RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + + if (prefer_raster_effects) { + LuminanceReduceRasterPushConstant push_constant; + memset(&push_constant, 0, sizeof(LuminanceReduceRasterPushConstant)); + + push_constant.max_luminance = p_max_luminance; + push_constant.min_luminance = p_min_luminance; + push_constant.exposure_adjust = p_adjust; + + for (int i = 0; i < p_luminance_buffers->reduce.size(); i++) { + push_constant.source_size[0] = i == 0 ? p_source_size.x : push_constant.dest_size[0]; + push_constant.source_size[1] = i == 0 ? p_source_size.y : push_constant.dest_size[1]; + push_constant.dest_size[0] = MAX(push_constant.source_size[0] / 8, 1); + push_constant.dest_size[1] = MAX(push_constant.source_size[1] / 8, 1); + + bool final = !p_set && (push_constant.dest_size[0] == 1) && (push_constant.dest_size[1] == 1); + LuminanceReduceRasterMode mode = final ? LUMINANCE_REDUCE_FRAGMENT_FINAL : (i == 0 ? LUMINANCE_REDUCE_FRAGMENT_FIRST : LUMINANCE_REDUCE_FRAGMENT); + RID shader = luminance_reduce_raster.shader.version_get_shader(luminance_reduce_raster.shader_version, mode); + + RID framebuffer = FramebufferCacheRD::get_singleton()->get_cache(p_luminance_buffers->reduce[i]); + + RD::Uniform u_source_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, i == 0 ? p_source_texture : p_luminance_buffers->reduce[i - 1] })); + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, luminance_reduce_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0); + if (final) { + RD::Uniform u_current_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_luminance_buffers->current })); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_current_texture), 1); + } + RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); + + RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(LuminanceReduceRasterPushConstant)); + + RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_end(); + } + } else { + LuminanceReducePushConstant push_constant; + memset(&push_constant, 0, sizeof(LuminanceReducePushConstant)); + + push_constant.source_size[0] = p_source_size.x; + push_constant.source_size[1] = p_source_size.y; + push_constant.max_luminance = p_max_luminance; + push_constant.min_luminance = p_min_luminance; + push_constant.exposure_adjust = p_adjust; + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + + for (int i = 0; i < p_luminance_buffers->reduce.size(); i++) { + RID shader; + + if (i == 0) { + shader = luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, LUMINANCE_REDUCE_READ); + RD::Uniform u_source_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_texture })); + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE_READ]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0); + } else { + RD::get_singleton()->compute_list_add_barrier(compute_list); //needs barrier, wait until previous is done + + if (i == p_luminance_buffers->reduce.size() - 1 && !p_set) { + shader = luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, LUMINANCE_REDUCE_WRITE); + RD::Uniform u_current_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_luminance_buffers->current })); + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE_WRITE]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_current_texture), 2); + } else { + shader = luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, LUMINANCE_REDUCE); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE]); + } + + RD::Uniform u_source_texture(RD::UNIFORM_TYPE_IMAGE, 0, p_luminance_buffers->reduce[i - 1]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0); + } + + RD::Uniform u_reduce_texture(RD::UNIFORM_TYPE_IMAGE, 0, p_luminance_buffers->reduce[i]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_reduce_texture), 1); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(LuminanceReducePushConstant)); + + RD::get_singleton()->compute_list_dispatch_threads(compute_list, push_constant.source_size[0], push_constant.source_size[1], 1); + + push_constant.source_size[0] = MAX(push_constant.source_size[0] / 8, 1); + push_constant.source_size[1] = MAX(push_constant.source_size[1] / 8, 1); + } + + RD::get_singleton()->compute_list_end(); + } + + SWAP(p_luminance_buffers->current, p_luminance_buffers->reduce.write[p_luminance_buffers->reduce.size() - 1]); +} diff --git a/servers/rendering/renderer_rd/effects/luminance.h b/servers/rendering/renderer_rd/effects/luminance.h new file mode 100644 index 0000000000..0f343fceab --- /dev/null +++ b/servers/rendering/renderer_rd/effects/luminance.h @@ -0,0 +1,120 @@ +/**************************************************************************/ +/* luminance.h */ +/**************************************************************************/ +/* 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 LUMINANCE_RD_H +#define LUMINANCE_RD_H + +#include "servers/rendering/renderer_rd/pipeline_cache_rd.h" +#include "servers/rendering/renderer_rd/shaders/effects/luminance_reduce.glsl.gen.h" +#include "servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl.gen.h" +#include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h" +#include "servers/rendering/renderer_scene_render.h" + +#include "servers/rendering_server.h" + +#define RB_LUMINANCE_BUFFERS SNAME("luminance_buffers") + +namespace RendererRD { + +class Luminance { +private: + bool prefer_raster_effects; + + enum LuminanceReduceMode { + LUMINANCE_REDUCE_READ, + LUMINANCE_REDUCE, + LUMINANCE_REDUCE_WRITE, + LUMINANCE_REDUCE_MAX + }; + + struct LuminanceReducePushConstant { + int32_t source_size[2]; + float max_luminance; + float min_luminance; + float exposure_adjust; + float pad[3]; + }; + + struct LuminanceReduce { + LuminanceReduceShaderRD shader; + RID shader_version; + RID pipelines[LUMINANCE_REDUCE_MAX]; + } luminance_reduce; + + enum LuminanceReduceRasterMode { + LUMINANCE_REDUCE_FRAGMENT_FIRST, + LUMINANCE_REDUCE_FRAGMENT, + LUMINANCE_REDUCE_FRAGMENT_FINAL, + LUMINANCE_REDUCE_FRAGMENT_MAX + }; + + struct LuminanceReduceRasterPushConstant { + int32_t source_size[2]; + int32_t dest_size[2]; + float exposure_adjust; + float min_luminance; + float max_luminance; + uint32_t pad1; + }; + + struct LuminanceReduceFragment { + LuminanceReduceRasterShaderRD shader; + RID shader_version; + PipelineCacheRD pipelines[LUMINANCE_REDUCE_FRAGMENT_MAX]; + } luminance_reduce_raster; + +public: + class LuminanceBuffers : public RenderBufferCustomDataRD { + GDCLASS(LuminanceBuffers, RenderBufferCustomDataRD); + + private: + bool prefer_raster_effects; + + public: + Vector<RID> reduce; + RID current; + + virtual void configure(RenderSceneBuffersRD *p_render_buffers) override; + virtual void free_data() override; + + void set_prefer_raster_effects(bool p_prefer_raster_effects); + }; + + Ref<LuminanceBuffers> get_luminance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers); + RID get_current_luminance_buffer(Ref<RenderSceneBuffersRD> p_render_buffers); + void luminance_reduction(RID p_source_texture, const Size2i p_source_size, Ref<LuminanceBuffers> p_luminance_buffers, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false); + + Luminance(bool p_prefer_raster_effects); + ~Luminance(); +}; + +} // namespace RendererRD + +#endif // LUMINANCE_RD_H diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp index 6d15d5c77b..b7a1396f9c 100644 --- a/servers/rendering/renderer_rd/effects_rd.cpp +++ b/servers/rendering/renderer_rd/effects_rd.cpp @@ -55,36 +55,13 @@ RID EffectsRD::_get_uniform_set_from_image(RID p_image) { u.append_id(p_image); uniforms.push_back(u); //any thing with the same configuration (one texture in binding 0 for set 0), is good - RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, 0), 1); + RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, roughness_limiter.shader.version_get_shader(roughness_limiter.shader_version, 0), 1); image_to_uniform_set_cache[p_image] = uniform_set; return uniform_set; } -RID EffectsRD::_get_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps) { - if (texture_to_uniform_set_cache.has(p_texture)) { - RID uniform_set = texture_to_uniform_set_cache[p_texture]; - if (RD::get_singleton()->uniform_set_is_valid(uniform_set)) { - return uniform_set; - } - } - - Vector<RD::Uniform> uniforms; - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; - u.binding = 0; - u.append_id(p_use_mipmaps ? default_mipmap_sampler : default_sampler); - u.append_id(p_texture); - uniforms.push_back(u); - // anything with the same configuration (one texture in binding 0 for set 0), is good - RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, luminance_reduce_raster.shader.version_get_shader(luminance_reduce_raster.shader_version, 0), 0); - - texture_to_uniform_set_cache[p_texture] = uniform_set; - - return uniform_set; -} - RID EffectsRD::_get_compute_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps) { if (texture_to_compute_uniform_set_cache.has(p_texture)) { RID uniform_set = texture_to_compute_uniform_set_cache[p_texture]; @@ -101,86 +78,13 @@ RID EffectsRD::_get_compute_uniform_set_from_texture(RID p_texture, bool p_use_m u.append_id(p_texture); uniforms.push_back(u); //any thing with the same configuration (one texture in binding 0 for set 0), is good - RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, 0), 0); + RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, roughness_limiter.shader.version_get_shader(roughness_limiter.shader_version, 0), 0); texture_to_compute_uniform_set_cache[p_texture] = uniform_set; return uniform_set; } -void EffectsRD::luminance_reduction(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set) { - ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use compute version of luminance reduction with the mobile renderer."); - - luminance_reduce.push_constant.source_size[0] = p_source_size.x; - luminance_reduce.push_constant.source_size[1] = p_source_size.y; - luminance_reduce.push_constant.max_luminance = p_max_luminance; - luminance_reduce.push_constant.min_luminance = p_min_luminance; - luminance_reduce.push_constant.exposure_adjust = p_adjust; - - RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); - - for (int i = 0; i < p_reduce.size(); i++) { - if (i == 0) { - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE_READ]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_texture), 0); - } else { - RD::get_singleton()->compute_list_add_barrier(compute_list); //needs barrier, wait until previous is done - - if (i == p_reduce.size() - 1 && !p_set) { - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE_WRITE]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_prev_luminance), 2); - } else { - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE]); - } - - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_reduce[i - 1]), 0); - } - - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_reduce[i]), 1); - - RD::get_singleton()->compute_list_set_push_constant(compute_list, &luminance_reduce.push_constant, sizeof(LuminanceReducePushConstant)); - - RD::get_singleton()->compute_list_dispatch_threads(compute_list, luminance_reduce.push_constant.source_size[0], luminance_reduce.push_constant.source_size[1], 1); - - luminance_reduce.push_constant.source_size[0] = MAX(luminance_reduce.push_constant.source_size[0] / 8, 1); - luminance_reduce.push_constant.source_size[1] = MAX(luminance_reduce.push_constant.source_size[1] / 8, 1); - } - - RD::get_singleton()->compute_list_end(); -} - -void EffectsRD::luminance_reduction_raster(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, Vector<RID> p_fb, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set) { - ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use raster version of luminance reduction with the clustered renderer."); - ERR_FAIL_COND_MSG(p_reduce.size() != p_fb.size(), "Incorrect frame buffer account for luminance reduction."); - - luminance_reduce_raster.push_constant.max_luminance = p_max_luminance; - luminance_reduce_raster.push_constant.min_luminance = p_min_luminance; - luminance_reduce_raster.push_constant.exposure_adjust = p_adjust; - - for (int i = 0; i < p_reduce.size(); i++) { - luminance_reduce_raster.push_constant.source_size[0] = i == 0 ? p_source_size.x : luminance_reduce_raster.push_constant.dest_size[0]; - luminance_reduce_raster.push_constant.source_size[1] = i == 0 ? p_source_size.y : luminance_reduce_raster.push_constant.dest_size[1]; - luminance_reduce_raster.push_constant.dest_size[0] = MAX(luminance_reduce_raster.push_constant.source_size[0] / 8, 1); - luminance_reduce_raster.push_constant.dest_size[1] = MAX(luminance_reduce_raster.push_constant.source_size[1] / 8, 1); - - bool final = !p_set && (luminance_reduce_raster.push_constant.dest_size[0] == 1) && (luminance_reduce_raster.push_constant.dest_size[1] == 1); - LuminanceReduceRasterMode mode = final ? LUMINANCE_REDUCE_FRAGMENT_FINAL : (i == 0 ? LUMINANCE_REDUCE_FRAGMENT_FIRST : LUMINANCE_REDUCE_FRAGMENT); - - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_fb[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, luminance_reduce_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_fb[i]))); - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(i == 0 ? p_source_texture : p_reduce[i - 1]), 0); - if (final) { - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_prev_luminance), 1); - } - RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); - - RD::get_singleton()->draw_list_set_push_constant(draw_list, &luminance_reduce_raster.push_constant, sizeof(LuminanceReduceRasterPushConstant)); - - RD::get_singleton()->draw_list_draw(draw_list, true); - RD::get_singleton()->draw_list_end(); - } -} - void EffectsRD::roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve) { roughness_limiter.push_constant.screen_size[0] = p_size.x; roughness_limiter.push_constant.screen_size[1] = p_size.y; @@ -270,39 +174,6 @@ void EffectsRD::sort_buffer(RID p_uniform_set, int p_size) { EffectsRD::EffectsRD(bool p_prefer_raster_effects) { prefer_raster_effects = p_prefer_raster_effects; - if (prefer_raster_effects) { - Vector<String> luminance_reduce_modes; - luminance_reduce_modes.push_back("\n#define FIRST_PASS\n"); // LUMINANCE_REDUCE_FRAGMENT_FIRST - luminance_reduce_modes.push_back("\n"); // LUMINANCE_REDUCE_FRAGMENT - luminance_reduce_modes.push_back("\n#define FINAL_PASS\n"); // LUMINANCE_REDUCE_FRAGMENT_FINAL - - luminance_reduce_raster.shader.initialize(luminance_reduce_modes); - memset(&luminance_reduce_raster.push_constant, 0, sizeof(LuminanceReduceRasterPushConstant)); - luminance_reduce_raster.shader_version = luminance_reduce_raster.shader.version_create(); - - for (int i = 0; i < LUMINANCE_REDUCE_FRAGMENT_MAX; i++) { - luminance_reduce_raster.pipelines[i].setup(luminance_reduce_raster.shader.version_get_shader(luminance_reduce_raster.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0); - } - } else { - // Initialize luminance_reduce - Vector<String> luminance_reduce_modes; - luminance_reduce_modes.push_back("\n#define READ_TEXTURE\n"); - luminance_reduce_modes.push_back("\n"); - luminance_reduce_modes.push_back("\n#define WRITE_LUMINANCE\n"); - - luminance_reduce.shader.initialize(luminance_reduce_modes); - - luminance_reduce.shader_version = luminance_reduce.shader.version_create(); - - for (int i = 0; i < LUMINANCE_REDUCE_MAX; i++) { - luminance_reduce.pipelines[i] = RD::get_singleton()->compute_pipeline_create(luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, i)); - } - - for (int i = 0; i < LUMINANCE_REDUCE_FRAGMENT_MAX; i++) { - luminance_reduce_raster.pipelines[i].clear(); - } - } - if (!prefer_raster_effects) { // Initialize roughness limiter Vector<String> shader_modes; @@ -368,11 +239,6 @@ EffectsRD::~EffectsRD() { RD::get_singleton()->free(default_mipmap_sampler); RD::get_singleton()->free(index_buffer); //array gets freed as dependency - if (prefer_raster_effects) { - luminance_reduce_raster.shader.version_free(luminance_reduce_raster.shader_version); - } else { - luminance_reduce.shader.version_free(luminance_reduce.shader_version); - } if (!prefer_raster_effects) { roughness_limiter.shader.version_free(roughness_limiter.shader_version); } diff --git a/servers/rendering/renderer_rd/effects_rd.h b/servers/rendering/renderer_rd/effects_rd.h index bbe240b241..45198e5fc5 100644 --- a/servers/rendering/renderer_rd/effects_rd.h +++ b/servers/rendering/renderer_rd/effects_rd.h @@ -33,8 +33,6 @@ #include "core/math/projection.h" #include "servers/rendering/renderer_rd/pipeline_cache_rd.h" -#include "servers/rendering/renderer_rd/shaders/luminance_reduce.glsl.gen.h" -#include "servers/rendering/renderer_rd/shaders/luminance_reduce_raster.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/roughness_limiter.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/sort.glsl.gen.h" #include "servers/rendering/renderer_scene_render.h" @@ -45,51 +43,6 @@ class EffectsRD { private: bool prefer_raster_effects; - enum LuminanceReduceMode { - LUMINANCE_REDUCE_READ, - LUMINANCE_REDUCE, - LUMINANCE_REDUCE_WRITE, - LUMINANCE_REDUCE_MAX - }; - - struct LuminanceReducePushConstant { - int32_t source_size[2]; - float max_luminance; - float min_luminance; - float exposure_adjust; - float pad[3]; - }; - - struct LuminanceReduce { - LuminanceReducePushConstant push_constant; - LuminanceReduceShaderRD shader; - RID shader_version; - RID pipelines[LUMINANCE_REDUCE_MAX]; - } luminance_reduce; - - enum LuminanceReduceRasterMode { - LUMINANCE_REDUCE_FRAGMENT_FIRST, - LUMINANCE_REDUCE_FRAGMENT, - LUMINANCE_REDUCE_FRAGMENT_FINAL, - LUMINANCE_REDUCE_FRAGMENT_MAX - }; - - struct LuminanceReduceRasterPushConstant { - int32_t source_size[2]; - int32_t dest_size[2]; - float exposure_adjust; - float min_luminance; - float max_luminance; - uint32_t pad1; - }; - - struct LuminanceReduceFragment { - LuminanceReduceRasterPushConstant push_constant; - LuminanceReduceRasterShaderRD shader; - RID shader_version; - PipelineCacheRD pipelines[LUMINANCE_REDUCE_FRAGMENT_MAX]; - } luminance_reduce_raster; - struct RoughnessLimiterPushConstant { int32_t screen_size[2]; float curve; @@ -164,15 +117,11 @@ private: RBMap<TextureSamplerPair, RID> texture_sampler_to_compute_uniform_set_cache; RID _get_uniform_set_from_image(RID p_texture); - RID _get_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps = false); RID _get_compute_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps = false); public: bool get_prefer_raster_effects(); - void luminance_reduction(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false); - void luminance_reduction_raster(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, Vector<RID> p_fb, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false); - void roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve); void sort_buffer(RID p_uniform_set, int p_size); diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index d426c4fc2e..1d45db8eaf 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -249,57 +249,6 @@ Ref<RenderSceneBuffers> RendererSceneRenderRD::render_buffers_create() { return rb; } -void RendererSceneRenderRD::_allocate_luminance_textures(Ref<RenderSceneBuffersRD> rb) { - ERR_FAIL_COND(!rb->luminance.current.is_null()); - - Size2i internal_size = rb->get_internal_size(); - int w = internal_size.x; - int h = internal_size.y; - - while (true) { - w = MAX(w / 8, 1); - h = MAX(h / 8, 1); - - RD::TextureFormat tf; - tf.format = RD::DATA_FORMAT_R32_SFLOAT; - tf.width = w; - tf.height = h; - - bool final = w == 1 && h == 1; - - if (_render_buffers_can_be_storage()) { - tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT; - if (final) { - tf.usage_bits |= RD::TEXTURE_USAGE_SAMPLING_BIT; - } - } else { - tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; - } - - RID texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); - - rb->luminance.reduce.push_back(texture); - if (!_render_buffers_can_be_storage()) { - Vector<RID> fb; - fb.push_back(texture); - - rb->luminance.fb.push_back(RD::get_singleton()->framebuffer_create(fb)); - } - - if (final) { - rb->luminance.current = RD::get_singleton()->texture_create(tf, RD::TextureView()); - - if (!_render_buffers_can_be_storage()) { - Vector<RID> fb; - fb.push_back(rb->luminance.current); - - rb->luminance.current_fb = RD::get_singleton()->framebuffer_create(fb); - } - break; - } - } -} - void RendererSceneRenderRD::_render_buffers_copy_screen_texture(const RenderDataRD *p_render_data) { Ref<RenderSceneBuffersRD> rb = p_render_data->render_buffers; ERR_FAIL_COND(rb.is_null()); @@ -443,9 +392,9 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende RENDER_TIMESTAMP("Auto exposure"); RD::get_singleton()->draw_command_begin_label("Auto exposure"); - if (rb->luminance.current.is_null()) { - _allocate_luminance_textures(rb); - } + + Ref<RendererRD::Luminance::LuminanceBuffers> luminance_buffers = luminance->get_luminance_buffers(rb); + uint64_t auto_exposure_version = RSG::camera_attributes->camera_attributes_get_auto_exposure_version(p_render_data->camera_attributes); bool set_immediate = auto_exposure_version != rb->get_auto_exposure_version(); rb->set_auto_exposure_version(auto_exposure_version); @@ -453,16 +402,9 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende double step = RSG::camera_attributes->camera_attributes_get_auto_exposure_adjust_speed(p_render_data->camera_attributes) * time_step; float auto_exposure_min_sensitivity = RSG::camera_attributes->camera_attributes_get_auto_exposure_min_sensitivity(p_render_data->camera_attributes); float auto_exposure_max_sensitivity = RSG::camera_attributes->camera_attributes_get_auto_exposure_max_sensitivity(p_render_data->camera_attributes); - if (can_use_storage) { - RendererCompositorRD::singleton->get_effects()->luminance_reduction(internal_texture, internal_size, rb->luminance.reduce, rb->luminance.current, auto_exposure_min_sensitivity, auto_exposure_max_sensitivity, step, set_immediate); - } else { - RendererCompositorRD::singleton->get_effects()->luminance_reduction_raster(internal_texture, internal_size, rb->luminance.reduce, rb->luminance.fb, rb->luminance.current, auto_exposure_min_sensitivity, auto_exposure_max_sensitivity, step, set_immediate); - } + luminance->luminance_reduction(internal_texture, internal_size, luminance_buffers, auto_exposure_min_sensitivity, auto_exposure_max_sensitivity, step, set_immediate); + // Swap final reduce with prev luminance. - SWAP(rb->luminance.current, rb->luminance.reduce.write[rb->luminance.reduce.size() - 1]); - if (!can_use_storage) { - SWAP(rb->luminance.current_fb, rb->luminance.fb.write[rb->luminance.fb.size() - 1]); - } auto_exposure_scale = RSG::camera_attributes->camera_attributes_get_auto_exposure_scale(p_render_data->camera_attributes); @@ -496,8 +438,8 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende if (i == 0) { RID luminance_texture; - if (RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes) && rb->luminance.current.is_valid()) { - luminance_texture = rb->luminance.current; + if (RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes)) { + luminance_texture = luminance->get_current_luminance_buffer(rb); // this will return and empty RID if we don't have an auto exposure buffer } RID source = rb->get_internal_texture(l); RID dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, l, i); @@ -530,9 +472,9 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende RendererRD::ToneMapper::TonemapSettings tonemap; - if (can_use_effects && RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes) && rb->luminance.current.is_valid()) { + tonemap.exposure_texture = luminance->get_current_luminance_buffer(rb); + if (can_use_effects && RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes) && tonemap.exposure_texture.is_valid()) { tonemap.use_auto_exposure = true; - tonemap.exposure_texture = rb->luminance.current; tonemap.auto_exposure_scale = auto_exposure_scale; } else { tonemap.exposure_texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_WHITE); @@ -746,10 +688,11 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(Ref<RenderSceneBuffersRD> } if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SCENE_LUMINANCE) { - if (p_render_buffers->luminance.current.is_valid()) { + RID luminance_texture = luminance->get_current_luminance_buffer(p_render_buffers); + if (luminance_texture.is_valid()) { Size2i rtsize = texture_storage->render_target_get_size(render_target); - copy_effects->copy_to_fb_rect(p_render_buffers->luminance.current, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize / 8), false, true); + copy_effects->copy_to_fb_rect(luminance_texture, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize / 8), false, true); } } @@ -1334,6 +1277,7 @@ void RendererSceneRenderRD::init() { bool can_use_vrs = is_vrs_supported(); bokeh_dof = memnew(RendererRD::BokehDOF(!can_use_storage)); copy_effects = memnew(RendererRD::CopyEffects(!can_use_storage)); + luminance = memnew(RendererRD::Luminance(!can_use_storage)); tone_mapper = memnew(RendererRD::ToneMapper); if (can_use_vrs) { vrs = memnew(RendererRD::VRS); @@ -1354,6 +1298,9 @@ RendererSceneRenderRD::~RendererSceneRenderRD() { if (copy_effects) { memdelete(copy_effects); } + if (luminance) { + memdelete(luminance); + } if (tone_mapper) { memdelete(tone_mapper); } diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h index 54f068c314..6fa2f7a570 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h @@ -38,6 +38,7 @@ #include "servers/rendering/renderer_rd/effects/bokeh_dof.h" #include "servers/rendering/renderer_rd/effects/copy_effects.h" #include "servers/rendering/renderer_rd/effects/fsr.h" +#include "servers/rendering/renderer_rd/effects/luminance.h" #include "servers/rendering/renderer_rd/effects/tone_mapper.h" #include "servers/rendering/renderer_rd/effects/vrs.h" #include "servers/rendering/renderer_rd/environment/fog.h" @@ -105,6 +106,7 @@ protected: RendererRD::ForwardIDStorage *forward_id_storage = nullptr; RendererRD::BokehDOF *bokeh_dof = nullptr; RendererRD::CopyEffects *copy_effects = nullptr; + RendererRD::Luminance *luminance = nullptr; RendererRD::ToneMapper *tone_mapper = nullptr; RendererRD::FSR *fsr = nullptr; RendererRD::VRS *vrs = nullptr; @@ -180,9 +182,6 @@ private: /* RENDER BUFFERS */ - // TODO move into effects/luminance.h/cpp - void _allocate_luminance_textures(Ref<RenderSceneBuffersRD> rb); - /* GI */ bool screen_space_roughness_limiter = false; float screen_space_roughness_limiter_amount = 0.25; diff --git a/servers/rendering/renderer_rd/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp index ab64ae1055..2d5263a3e2 100644 --- a/servers/rendering/renderer_rd/shader_rd.cpp +++ b/servers/rendering/renderer_rd/shader_rd.cpp @@ -33,6 +33,7 @@ #include "core/io/compression.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" +#include "core/version.h" #include "renderer_compositor_rd.h" #include "servers/rendering/rendering_device.h" #include "thirdparty/misc/smolv.h" @@ -116,6 +117,10 @@ void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, con } StringBuilder tohash; + tohash.append("[GodotVersionNumber]"); + tohash.append(VERSION_NUMBER); + tohash.append("[GodotVersionHash]"); + tohash.append(VERSION_HASH); tohash.append("[SpirvCacheKey]"); tohash.append(RenderingDevice::get_singleton()->shader_get_spirv_cache_key()); tohash.append("[BinaryCacheKey]"); diff --git a/servers/rendering/renderer_rd/shaders/luminance_reduce.glsl b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce.glsl index 0ee4cf6e31..0ee4cf6e31 100644 --- a/servers/rendering/renderer_rd/shaders/luminance_reduce.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce.glsl diff --git a/servers/rendering/renderer_rd/shaders/luminance_reduce_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl index 29ebd74a90..29ebd74a90 100644 --- a/servers/rendering/renderer_rd/shaders/luminance_reduce_raster.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl diff --git a/servers/rendering/renderer_rd/shaders/luminance_reduce_raster_inc.glsl b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster_inc.glsl index b8860f6518..b8860f6518 100644 --- a/servers/rendering/renderer_rd/shaders/luminance_reduce_raster_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster_inc.glsl diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp index a953bac433..d67a848a40 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp @@ -94,28 +94,6 @@ void RenderSceneBuffersRD::cleanup() { free_named_texture(E.value); } named_textures.clear(); - - // old stuff, to be re-evaluated... - - for (int i = 0; i < luminance.fb.size(); i++) { - RD::get_singleton()->free(luminance.fb[i]); - } - luminance.fb.clear(); - - for (int i = 0; i < luminance.reduce.size(); i++) { - RD::get_singleton()->free(luminance.reduce[i]); - } - luminance.reduce.clear(); - - if (luminance.current_fb.is_valid()) { - RD::get_singleton()->free(luminance.current_fb); - luminance.current_fb = RID(); - } - - if (luminance.current.is_valid()) { - RD::get_singleton()->free(luminance.current); - luminance.current = RID(); - } } void RenderSceneBuffersRD::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa_3d, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) { diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h index 1bd542500c..ff946f410f 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h @@ -213,15 +213,6 @@ public: // 2 full size, 2 half size WeightBuffers weight_buffers[4]; // Only used in raster - - struct Luminance { - Vector<RID> reduce; - RID current; - - // used only on mobile renderer - Vector<RID> fb; - RID current_fb; - } luminance; }; #endif // RENDER_SCENE_BUFFERS_RD_H |