diff options
196 files changed, 2146 insertions, 499 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index d1fafec50d..9dea66914f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -45,7 +45,8 @@ body: label: Issue description description: | Describe your issue briefly. What doesn't work, and how do you expect it to work instead? - You can include images or videos with drag and drop, and format code blocks or logs with <code>```</code> tags. + You can include images or videos with drag and drop, and format code blocks or logs with <code>```</code> tags, on separate lines before and after the text. (Use <code>```gdscript</code> to add GDScript syntax highlighting.) + Please do not add code examples or error messages as screenshots, but as text, this helps searching for issues and testing the code. If you are reporting a bug in the editor interface, like the script editor, please provide both a screenshot *and* the text of the code to help with testing. validations: required: true diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index 0b2d5e41cf..3b9b1f9094 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -168,6 +168,18 @@ ScriptLanguage *ScriptServer::get_language(int p_idx) { return _languages[p_idx]; } +ScriptLanguage *ScriptServer::get_language_for_extension(const String &p_extension) { + MutexLock lock(languages_mutex); + + for (int i = 0; i < _language_count; i++) { + if (_languages[i] && _languages[i]->get_extension() == p_extension) { + return _languages[i]; + } + } + + return nullptr; +} + Error ScriptServer::register_language(ScriptLanguage *p_language) { MutexLock lock(languages_mutex); ERR_FAIL_NULL_V(p_language, ERR_INVALID_PARAMETER); diff --git a/core/object/script_language.h b/core/object/script_language.h index bb714d5bc3..294231a3e7 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -75,6 +75,7 @@ public: static bool is_scripting_enabled(); _FORCE_INLINE_ static int get_language_count() { return _language_count; } static ScriptLanguage *get_language(int p_idx); + static ScriptLanguage *get_language_for_extension(const String &p_extension); static Error register_language(ScriptLanguage *p_language); static Error unregister_language(const ScriptLanguage *p_language); diff --git a/doc/classes/EditorProperty.xml b/doc/classes/EditorProperty.xml index 4fd288f16d..535515165b 100644 --- a/doc/classes/EditorProperty.xml +++ b/doc/classes/EditorProperty.xml @@ -72,6 +72,9 @@ <member name="checked" type="bool" setter="set_checked" getter="is_checked" default="false"> Used by the inspector, set to [code]true[/code] when the property is checked. </member> + <member name="configuration_warning" type="String" setter="set_configuration_warning" getter="get_configuration_warning" default=""""> + Used by the inspector, set to show a configuration warning on the property. + </member> <member name="deletable" type="bool" setter="set_deletable" getter="is_deletable" default="false"> Used by the inspector, set to [code]true[/code] when the property can be deleted by the user. </member> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 4f09a0c616..f00bc594cb 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -37,9 +37,13 @@ </description> </method> <method name="_get_configuration_warnings" qualifiers="virtual const"> - <return type="PackedStringArray" /> + <return type="Array" /> <description> The elements in the array returned from this method are displayed as warnings in the Scene dock if the script that overrides it is a [code]tool[/code] script. + Each array element must either be a [String] or a [Dictionary]. + A dictionary element must contain a key [code]message[/code] of type [String] which is shown in the user interface. + The dictionary may optionally contain a key [code]property[/code] of type [NodePath], which also shows this warning in the inspector on the corresponding property. + If a string is found in the returned array, it is converted to an equivalent dictionary with the [code]message[/code] field set. Returning an empty array produces no warnings. Call [method update_configuration_warnings] when the warnings need to be updated for this node. [codeblock] diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml index 844782eb9a..8db19ec957 100644 --- a/doc/classes/StringName.xml +++ b/doc/classes/StringName.xml @@ -5,7 +5,7 @@ </brief_description> <description> [StringName]s are immutable strings designed for general-purpose representation of unique names (also called "string interning"). Two [StringName]s with the same value are the same object. Comparing them is extremely fast compared to regular [String]s. - You will usually just pass a [String] to methods expecting a [StringName] and it will be automatically converted, but you may occasionally want to construct a [StringName] ahead of time with the [StringName] constructor or, in GDScript, the literal syntax [code]&"example"[/code]. + You will usually pass a [String] to methods expecting a [StringName] and it will be automatically converted (often at compile time), but in rare cases you can construct a [StringName] ahead of time with the [StringName] constructor or, in GDScript, the literal syntax [code]&"example"[/code]. Manually constructing a [StringName] allows you to control when the conversion from [String] occurs or to use the literal and prevent conversions entirely. See also [NodePath], which is a similar concept specifically designed to store pre-parsed scene tree paths. All of [String]'s methods are available in this class too. They convert the [StringName] into a string, and they also return a string. This is highly inefficient and should only be used if the string is desired. [b]Note:[/b] In C#, an explicit conversion to [code]System.String[/code] is required to use the methods listed on this page. Use the [code]ToString()[/code] method to cast a [StringName] to a string, and then use the equivalent methods in [code]System.String[/code] or [code]StringExtensions[/code]. diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index fc4c8ea4b7..bc6f3c2bbf 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -1371,7 +1371,17 @@ void EditorHelp::_update_doc() { } // Enums - if (enums.size()) { + bool has_enums = enums.size() && !cd.is_script_doc; + if (enums.size() && !has_enums) { + for (KeyValue<String, DocData::EnumDoc> &E : cd.enums) { + if (E.key.begins_with("_") && E.value.description.strip_edges().is_empty()) { + continue; + } + has_enums = true; + break; + } + } + if (has_enums) { section_line.push_back(Pair<String, int>(TTR("Enumerations"), class_desc->get_paragraph_count() - 2)); _push_title_font(); class_desc->add_text(TTR("Enumerations")); @@ -1381,8 +1391,17 @@ void EditorHelp::_update_doc() { class_desc->add_newline(); for (KeyValue<String, Vector<DocData::ConstantDoc>> &E : enums) { - enum_line[E.key] = class_desc->get_paragraph_count() - 2; + String key = E.key; + if ((key.get_slice_count(".") > 1) && (key.get_slice(".", 0) == edited_class)) { + key = key.get_slice(".", 1); + } + if (cd.enums.has(key)) { + if (cd.is_script_doc && cd.enums[key].description.strip_edges().is_empty() && E.key.begins_with("_")) { + continue; + } + } + enum_line[E.key] = class_desc->get_paragraph_count() - 2; _push_code_font(); class_desc->push_color(theme_cache.title_color); @@ -1393,27 +1412,19 @@ void EditorHelp::_update_doc() { } class_desc->pop(); - String e = E.key; - if ((e.get_slice_count(".") > 1) && (e.get_slice(".", 0) == edited_class)) { - e = e.get_slice(".", 1); - } - class_desc->push_color(theme_cache.headline_color); - class_desc->add_text(e); + class_desc->add_text(key); class_desc->pop(); class_desc->push_color(theme_cache.symbol_color); class_desc->add_text(":"); class_desc->pop(); - if (cd.enums.has(e)) { - if (cd.enums[e].is_deprecated) { - DEPRECATED_DOC_TAG; - } - - if (cd.enums[e].is_experimental) { - EXPERIMENTAL_DOC_TAG; - } + if (cd.enums[key].is_deprecated) { + DEPRECATED_DOC_TAG; + } + if (cd.enums[key].is_experimental) { + EXPERIMENTAL_DOC_TAG; } _pop_code_font(); @@ -1422,11 +1433,11 @@ void EditorHelp::_update_doc() { class_desc->add_newline(); // Enum description. - if (e != "@unnamed_enums" && cd.enums.has(e) && !cd.enums[e].description.strip_edges().is_empty()) { + if (key != "@unnamed_enums" && cd.enums.has(key) && !cd.enums[key].description.strip_edges().is_empty()) { class_desc->push_color(theme_cache.text_color); _push_normal_font(); class_desc->push_indent(1); - _add_text(cd.enums[e].description); + _add_text(cd.enums[key].description); class_desc->pop(); _pop_normal_font(); class_desc->pop(); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index b97729db7b..4501c52bc8 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -214,6 +214,22 @@ void EditorProperty::_notification(int p_what) { text_size -= close->get_width() + 4 * EDSCALE; } } + + if (!configuration_warning.is_empty() && !read_only) { + Ref<Texture2D> warning; + + warning = get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons")); + + rect.size.x -= warning->get_width() + get_theme_constant(SNAME("hseparator"), SNAME("Tree")); + + if (is_layout_rtl()) { + rect.position.x += warning->get_width() + get_theme_constant(SNAME("hseparator"), SNAME("Tree")); + } + + if (no_children) { + text_size -= warning->get_width() + 4 * EDSCALE; + } + } } //set children @@ -399,6 +415,38 @@ void EditorProperty::_notification(int p_what) { } else { delete_rect = Rect2(); } + + if (!configuration_warning.is_empty() && !read_only) { + Ref<Texture2D> warning; + + StringName warning_icon; + Node *node = Object::cast_to<Node>(object); + if (node) { + const int warning_num = node->get_configuration_warnings_of_property(property_path).size(); + warning_icon = Node::get_configuration_warning_icon(warning_num); + } else { + // This shouldn't happen, but let's not crash over an icon. + warning_icon = "NodeWarning"; + } + warning = get_theme_icon(warning_icon, SNAME("EditorIcons")); + + ofs -= warning->get_width() + get_theme_constant(SNAME("hseparator"), SNAME("Tree")); + + Color color2(1, 1, 1); + if (configuration_warning_hover) { + color2.r *= 1.2; + color2.g *= 1.2; + color2.b *= 1.2; + } + configuration_warning_rect = Rect2(ofs, ((size.height - warning->get_height()) / 2), warning->get_width(), warning->get_height()); + if (rtl) { + draw_texture(warning, Vector2(size.width - configuration_warning_rect.position.x - warning->get_width(), configuration_warning_rect.position.y), color2); + } else { + draw_texture(warning, configuration_warning_rect.position, color2); + } + } else { + configuration_warning_rect = Rect2(); + } } break; } } @@ -674,6 +722,12 @@ void EditorProperty::gui_input(const Ref<InputEvent> &p_event) { check_hover = new_check_hover; queue_redraw(); } + + bool new_configuration_warning_hover = configuration_warning_rect.has_point(mpos) && !button_left; + if (new_configuration_warning_hover != configuration_warning_hover) { + configuration_warning_hover = new_configuration_warning_hover; + queue_redraw(); + } } Ref<InputEventMouseButton> mb = p_event; @@ -730,6 +784,16 @@ void EditorProperty::gui_input(const Ref<InputEvent> &p_event) { queue_redraw(); emit_signal(SNAME("property_checked"), property, checked); } + + if (configuration_warning_rect.has_point(mpos)) { + if (warning_dialog == nullptr) { + warning_dialog = memnew(AcceptDialog); + add_child(warning_dialog); + warning_dialog->set_title(TTR("Node Configuration Warning!")); + } + warning_dialog->set_text(configuration_warning); + warning_dialog->popup_centered(); + } } else if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) { accept_event(); _update_popup(); @@ -855,6 +919,16 @@ float EditorProperty::get_name_split_ratio() const { return split_ratio; } +void EditorProperty::set_configuration_warning(const String &p_configuration_warning) { + configuration_warning = p_configuration_warning; + queue_redraw(); + queue_sort(); +} + +String EditorProperty::get_configuration_warning() const { + return configuration_warning; +} + void EditorProperty::set_object_and_property(Object *p_object, const StringName &p_property) { object = p_object; property = p_property; @@ -911,6 +985,15 @@ void EditorProperty::_update_pin_flags() { } } +void EditorProperty::_update_configuration_warnings() { + Node *node = Object::cast_to<Node>(object); + if (node) { + const PackedStringArray warnings = node->get_configuration_warnings_as_strings(true, property_path); + const String warning_lines = String("\n").join(warnings); + set_configuration_warning(warning_lines); + } +} + Control *EditorProperty::make_custom_tooltip(const String &p_text) const { EditorHelpBit *tooltip = nullptr; @@ -980,6 +1063,9 @@ void EditorProperty::_bind_methods() { ClassDB::bind_method(D_METHOD("set_deletable", "deletable"), &EditorProperty::set_deletable); ClassDB::bind_method(D_METHOD("is_deletable"), &EditorProperty::is_deletable); + ClassDB::bind_method(D_METHOD("set_configuration_warning", "configuration_warning"), &EditorProperty::set_configuration_warning); + ClassDB::bind_method(D_METHOD("get_configuration_warning"), &EditorProperty::get_configuration_warning); + ClassDB::bind_method(D_METHOD("get_edited_property"), &EditorProperty::get_edited_property); ClassDB::bind_method(D_METHOD("get_edited_object"), &EditorProperty::get_edited_object); @@ -997,6 +1083,7 @@ void EditorProperty::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_warning"), "set_draw_warning", "is_draw_warning"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keying"), "set_keying", "is_keying"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deletable"), "set_deletable", "is_deletable"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "configuration_warning"), "set_configuration_warning", "get_configuration_warning"); ADD_SIGNAL(MethodInfo("property_changed", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::STRING_NAME, "field"), PropertyInfo(Variant::BOOL, "changing"))); ADD_SIGNAL(MethodInfo("multiple_properties_changed", PropertyInfo(Variant::PACKED_STRING_ARRAY, "properties"), PropertyInfo(Variant::ARRAY, "value"))); @@ -3314,6 +3401,7 @@ void EditorInspector::update_tree() { ep->set_keying(keying); ep->set_read_only(property_read_only || all_read_only); ep->set_deletable(deletable_properties || p.name.begins_with("metadata/")); + ep->_update_configuration_warnings(); } current_vbox->add_child(editors[i].property_editor); @@ -3960,6 +4048,12 @@ void EditorInspector::_node_removed(Node *p_node) { } } +void EditorInspector::_warning_changed(Node *p_node) { + if (p_node == object) { + update_tree_pending = true; + } +} + void EditorInspector::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { @@ -3971,6 +4065,7 @@ void EditorInspector::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { if (!sub_inspector) { get_tree()->connect("node_removed", callable_mp(this, &EditorInspector::_node_removed)); + get_tree()->connect("node_configuration_warning_changed", callable_mp(this, &EditorInspector::_warning_changed)); } } break; @@ -3981,6 +4076,7 @@ void EditorInspector::_notification(int p_what) { case NOTIFICATION_EXIT_TREE: { if (!sub_inspector) { get_tree()->disconnect("node_removed", callable_mp(this, &EditorInspector::_node_removed)); + get_tree()->disconnect("node_configuration_warning_changed", callable_mp(this, &EditorInspector::_warning_changed)); } edit(nullptr); } break; diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 2dcad88840..d8b3e32d7d 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -76,6 +76,7 @@ private: String doc_path; bool internal = false; bool has_doc_tooltip = false; + AcceptDialog *warning_dialog = nullptr; int property_usage; @@ -98,6 +99,8 @@ private: bool check_hover = false; Rect2 delete_rect; bool delete_hover = false; + Rect2 configuration_warning_rect; + bool configuration_warning_hover = false; bool can_revert = false; bool can_pin = false; @@ -121,12 +124,15 @@ private: Control *bottom_editor = nullptr; PopupMenu *menu = nullptr; + String configuration_warning; + HashMap<StringName, Variant> cache; GDVIRTUAL0(_update_property) GDVIRTUAL1(_set_read_only, bool) void _update_pin_flags(); + void _update_configuration_warnings(); protected: void _notification(int p_what); @@ -203,6 +209,9 @@ public: void set_name_split_ratio(float p_ratio); float get_name_split_ratio() const; + void set_configuration_warning(const String &p_configuration_warning); + String get_configuration_warning() const; + void set_object_and_property(Object *p_object, const StringName &p_property); virtual Control *make_custom_tooltip(const String &p_text) const override; @@ -532,6 +541,7 @@ class EditorInspector : public ScrollContainer { void _object_id_selected(const String &p_path, ObjectID p_id); void _node_removed(Node *p_node); + void _warning_changed(Node *p_node); HashMap<StringName, int> per_array_page; void _page_change_request(int p_new_page, const StringName &p_array_prefix); diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index 1702277ebc..623aa3f45c 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -129,6 +129,8 @@ void EditorResourcePreview::_preview_ready(const String &p_path, int p_hash, con void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<ImageTexture> &r_small_texture, const QueueItem &p_item, const String &cache_base, Dictionary &p_metadata) { String type; + uint64_t started_at = OS::get_singleton()->get_ticks_usec(); + if (p_item.resource.is_valid()) { type = p_item.resource->get_class(); } else { @@ -138,6 +140,10 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref< if (type.is_empty()) { r_texture = Ref<ImageTexture>(); r_small_texture = Ref<ImageTexture>(); + + if (is_print_verbose_enabled()) { + print_line(vformat("Generated '%s' preview in %d usec", p_item.path, OS::get_singleton()->get_ticks_usec() - started_at)); + } return; //could not guess type } @@ -196,6 +202,10 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref< _write_preview_cache(f, thumbnail_size, has_small_texture, FileAccess::get_modified_time(p_item.path), FileAccess::get_md5(p_item.path), p_metadata); } } + + if (is_print_verbose_enabled()) { + print_line(vformat("Generated '%s' preview in %d usec", p_item.path, OS::get_singleton()->get_ticks_usec() - started_at)); + } } const Dictionary EditorResourcePreview::get_preview_metadata(const String &p_path) const { diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp index 670fd0a06d..aeb4966169 100644 --- a/editor/export/editor_export.cpp +++ b/editor/export/editor_export.cpp @@ -85,6 +85,7 @@ void EditorExport::_save() { config->set_value(section, "encryption_exclude_filters", preset->get_enc_ex_filter()); config->set_value(section, "encrypt_pck", preset->get_enc_pck()); config->set_value(section, "encrypt_directory", preset->get_enc_directory()); + config->set_value(section, "script_export_mode", preset->get_script_export_mode()); credentials->set_value(section, "script_encryption_key", preset->get_script_encryption_key()); String option_section = "preset." + itos(i) + ".options"; @@ -269,6 +270,7 @@ void EditorExport::load_config() { preset->set_include_filter(config->get_value(section, "include_filter")); preset->set_exclude_filter(config->get_value(section, "exclude_filter")); preset->set_export_path(config->get_value(section, "export_path", "")); + preset->set_script_export_mode(config->get_value(section, "script_export_mode", EditorExportPreset::MODE_SCRIPT_BINARY_TOKENS_COMPRESSED)); if (config->has_section_key(section, "encrypt_pck")) { preset->set_enc_pck(config->get_value(section, "encrypt_pck")); diff --git a/editor/export/editor_export_preset.cpp b/editor/export/editor_export_preset.cpp index b941170b7b..478ef98037 100644 --- a/editor/export/editor_export_preset.cpp +++ b/editor/export/editor_export_preset.cpp @@ -323,6 +323,15 @@ String EditorExportPreset::get_script_encryption_key() const { return script_key; } +void EditorExportPreset::set_script_export_mode(int p_mode) { + script_mode = p_mode; + EditorExport::singleton->save_presets(); +} + +int EditorExportPreset::get_script_export_mode() const { + return script_mode; +} + Variant EditorExportPreset::get_or_env(const StringName &p_name, const String &p_env_var, bool *r_valid) const { const String from_env = OS::get_singleton()->get_environment(p_env_var); if (!from_env.is_empty()) { diff --git a/editor/export/editor_export_preset.h b/editor/export/editor_export_preset.h index 025e7603f3..effce48111 100644 --- a/editor/export/editor_export_preset.h +++ b/editor/export/editor_export_preset.h @@ -54,6 +54,12 @@ public: MODE_FILE_REMOVE, }; + enum ScriptExportMode { + MODE_SCRIPT_TEXT, + MODE_SCRIPT_BINARY_TOKENS, + MODE_SCRIPT_BINARY_TOKENS_COMPRESSED, + }; + private: Ref<EditorExportPlatform> platform; ExportFilter export_filter = EXPORT_ALL_RESOURCES; @@ -84,6 +90,7 @@ private: bool enc_directory = false; String script_key; + int script_mode = MODE_SCRIPT_BINARY_TOKENS_COMPRESSED; protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -152,6 +159,9 @@ public: void set_script_encryption_key(const String &p_key); String get_script_encryption_key() const; + void set_script_export_mode(int p_mode); + int get_script_export_mode() const; + Variant get_or_env(const StringName &p_name, const String &p_env_var, bool *r_valid = nullptr) const; // Return the preset's version number, or fall back to the diff --git a/editor/export/export_template_manager.cpp b/editor/export/export_template_manager.cpp index 1491adee52..b3951d2c28 100644 --- a/editor/export/export_template_manager.cpp +++ b/editor/export/export_template_manager.cpp @@ -146,6 +146,9 @@ void ExportTemplateManager::_download_template(const String &p_url, bool p_skip_ install_options_vb->hide(); download_progress_hb->show(); + download_progress_bar->show(); + download_progress_bar->set_indeterminate(true); + _set_current_progress_status(TTR("Starting the download...")); download_templates->set_download_file(EditorPaths::get_singleton()->get_cache_dir().path_join("tmp_templates.tpz")); @@ -159,6 +162,7 @@ void ExportTemplateManager::_download_template(const String &p_url, bool p_skip_ Error err = download_templates->request(p_url); if (err != OK) { _set_current_progress_status(TTR("Error requesting URL:") + " " + p_url, true); + download_progress_hb->hide(); return; } @@ -357,10 +361,10 @@ bool ExportTemplateManager::_humanize_http_status(HTTPRequest *p_request, String } void ExportTemplateManager::_set_current_progress_status(const String &p_status, bool p_error) { - download_progress_bar->hide(); download_progress_label->set_text(p_status); if (p_error) { + download_progress_bar->hide(); download_progress_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), EditorStringName(Editor))); } else { download_progress_label->add_theme_color_override("font_color", get_theme_color(SNAME("font_color"), SNAME("Label"))); @@ -369,6 +373,7 @@ void ExportTemplateManager::_set_current_progress_status(const String &p_status, void ExportTemplateManager::_set_current_progress_value(float p_value, const String &p_status) { download_progress_bar->show(); + download_progress_bar->set_indeterminate(false); download_progress_bar->set_value(p_value); download_progress_label->set_text(p_status); } @@ -955,6 +960,7 @@ ExportTemplateManager::ExportTemplateManager() { download_progress_bar->set_max(1); download_progress_bar->set_value(0); download_progress_bar->set_step(0.01); + download_progress_bar->set_editor_preview_indeterminate(true); download_progress_hb->add_child(download_progress_bar); download_progress_label = memnew(Label); diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index 63bd87e6cc..ff1fa3470e 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -383,6 +383,9 @@ void ProjectExportDialog::_edit_preset(int p_index) { script_key_error->hide(); } + int script_export_mode = current->get_script_export_mode(); + script_mode->select(script_export_mode); + updating = false; } @@ -582,6 +585,19 @@ bool ProjectExportDialog::_validate_script_encryption_key(const String &p_key) { return is_valid; } +void ProjectExportDialog::_script_export_mode_changed(int p_mode) { + if (updating) { + return; + } + + Ref<EditorExportPreset> current = get_current_preset(); + ERR_FAIL_COND(current.is_null()); + + current->set_script_export_mode(p_mode); + + _update_current_preset(); +} + void ProjectExportDialog::_duplicate_preset() { Ref<EditorExportPreset> current = get_current_preset(); if (current.is_null()) { @@ -1328,7 +1344,7 @@ ProjectExportDialog::ProjectExportDialog() { feature_vb->add_margin_child(TTR("Feature List:"), custom_feature_display, true); sections->add_child(feature_vb); - // Script export parameters. + // Encryption export parameters. VBoxContainer *sec_vb = memnew(VBoxContainer); sec_vb->set_name(TTR("Encryption")); @@ -1373,6 +1389,20 @@ ProjectExportDialog::ProjectExportDialog() { sec_more_info->connect("pressed", callable_mp(this, &ProjectExportDialog::_open_key_help_link)); sec_vb->add_child(sec_more_info); + // Script export parameters. + + VBoxContainer *script_vb = memnew(VBoxContainer); + script_vb->set_name(TTR("Scripts")); + + script_mode = memnew(OptionButton); + script_vb->add_margin_child(TTR("GDScript Export Mode:"), script_mode); + script_mode->add_item(TTR("Text (easier debugging)"), (int)EditorExportPreset::MODE_SCRIPT_TEXT); + script_mode->add_item(TTR("Binary tokens (faster loading)"), (int)EditorExportPreset::MODE_SCRIPT_BINARY_TOKENS); + script_mode->add_item(TTR("Compressed binary tokens (smaller files)"), (int)EditorExportPreset::MODE_SCRIPT_BINARY_TOKENS_COMPRESSED); + script_mode->connect("item_selected", callable_mp(this, &ProjectExportDialog::_script_export_mode_changed)); + + sections->add_child(script_vb); + sections->connect("tab_changed", callable_mp(this, &ProjectExportDialog::_tab_changed)); // Disable by default. diff --git a/editor/export/project_export.h b/editor/export/project_export.h index 1a359b08da..0fe7ecc2a8 100644 --- a/editor/export/project_export.h +++ b/editor/export/project_export.h @@ -160,6 +160,8 @@ class ProjectExportDialog : public ConfirmationDialog { LineEdit *enc_in_filters = nullptr; LineEdit *enc_ex_filters = nullptr; + OptionButton *script_mode = nullptr; + void _open_export_template_manager(); void _export_pck_zip(); @@ -183,6 +185,8 @@ class ProjectExportDialog : public ConfirmationDialog { void _script_encryption_key_changed(const String &p_key); bool _validate_script_encryption_key(const String &p_key); + void _script_export_mode_changed(int p_mode); + void _open_key_help_link(); void _tab_changed(int); diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp index 1f35da322a..6c8e8b75ef 100644 --- a/editor/gui/scene_tree_editor.cpp +++ b/editor/gui/scene_tree_editor.cpp @@ -132,32 +132,13 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i } undo_redo->commit_action(); } else if (p_id == BUTTON_WARNING) { - const PackedStringArray warnings = n->get_configuration_warnings(); - + const PackedStringArray warnings = n->get_configuration_warnings_as_strings(true); if (warnings.is_empty()) { return; } - // Improve looks on tooltip, extra spacing on non-bullet point newlines. - const String bullet_point = U"• "; - String all_warnings; - for (const String &w : warnings) { - all_warnings += "\n" + bullet_point + w; - } - - // Limit the line width while keeping some padding. - // It is not efficient, but it does not have to be. - const PackedInt32Array boundaries = TS->string_get_word_breaks(all_warnings, "", 80); - PackedStringArray lines; - for (int i = 0; i < boundaries.size(); i += 2) { - const int start = boundaries[i]; - const int end = boundaries[i + 1]; - const String line = all_warnings.substr(start, end - start); - lines.append(line); - } - all_warnings = String("\n").join(lines).indent(" ").replace(U" •", U"\n•").substr(2); // We don't want the first two newlines. - - warning->set_text(all_warnings); + const String warning_lines = String("\n").join(warnings); + warning->set_text(warning_lines); warning->popup_centered(); } else if (p_id == BUTTON_SIGNALS) { @@ -294,29 +275,12 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { if (can_rename) { //should be can edit.. - const PackedStringArray warnings = p_node->get_configuration_warnings(); + const PackedStringArray warnings = p_node->get_configuration_warnings_as_strings(false); const int num_warnings = warnings.size(); if (num_warnings > 0) { - String warning_icon; - if (num_warnings == 1) { - warning_icon = SNAME("NodeWarning"); - } else if (num_warnings <= 3) { - warning_icon = vformat("NodeWarnings%d", num_warnings); - } else { - warning_icon = SNAME("NodeWarnings4Plus"); - } - - // Improve looks on tooltip, extra spacing on non-bullet point newlines. - const String bullet_point = U"• "; - String all_warnings; - for (const String &w : warnings) { - all_warnings += "\n\n" + bullet_point + w.replace("\n", "\n "); - } - if (num_warnings == 1) { - all_warnings.remove_at(0); // With only one warning, two newlines do not look great. - } - - item->add_button(0, get_editor_theme_icon(warning_icon), BUTTON_WARNING, false, TTR("Node configuration warning:") + all_warnings); + const StringName warning_icon = Node::get_configuration_warning_icon(num_warnings); + const String warning_lines = String("\n\n").join(warnings); + item->add_button(0, get_editor_theme_icon(warning_icon), BUTTON_WARNING, false, TTR("Node configuration warning:") + "\n\n" + warning_lines); } if (p_node->is_unique_name_in_owner()) { diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 4477884b7a..8edd569a87 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -384,6 +384,10 @@ void EditorAssetLibraryItemDownload::_http_download_completed(int p_status, int } break; } + // Make the progress bar invisible but don't reflow other Controls around it. + progress->set_modulate(Color(0, 0, 0, 0)); + progress->set_indeterminate(false); + if (!error_text.is_empty()) { download_error->set_text(TTR("Asset Download Error:") + "\n" + error_text); download_error->popup_centered(); @@ -394,8 +398,6 @@ void EditorAssetLibraryItemDownload::_http_download_completed(int p_status, int install_button->set_disabled(false); status->set_text(TTR("Ready to install!")); - // Make the progress bar invisible but don't reflow other Controls around it. - progress->set_modulate(Color(0, 0, 0, 0)); set_process(false); @@ -436,13 +438,13 @@ void EditorAssetLibraryItemDownload::_notification(int p_what) { if (cstatus == HTTPClient::STATUS_BODY) { if (download->get_body_size() > 0) { + progress->set_indeterminate(false); status->set_text(vformat( TTR("Downloading (%s / %s)..."), String::humanize_size(download->get_downloaded_bytes()), String::humanize_size(download->get_body_size()))); } else { - // Total file size is unknown, so it cannot be displayed. - progress->set_modulate(Color(0, 0, 0, 0)); + progress->set_indeterminate(true); status->set_text(vformat( TTR("Downloading...") + " (%s)", String::humanize_size(download->get_downloaded_bytes()))); @@ -508,6 +510,7 @@ void EditorAssetLibraryItemDownload::_make_request() { if (err != OK) { status->set_text(TTR("Error making request")); } else { + progress->set_indeterminate(true); set_process(true); } } @@ -548,6 +551,7 @@ EditorAssetLibraryItemDownload::EditorAssetLibraryItemDownload() { status = memnew(Label(TTR("Idle"))); vb->add_child(status); progress = memnew(ProgressBar); + progress->set_editor_preview_indeterminate(true); vb->add_child(progress); HBoxContainer *hb2 = memnew(HBoxContainer); diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 90bd117543..0019922f9c 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -464,6 +464,17 @@ bool EditorScriptPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Script"); } +Ref<Texture2D> EditorScriptPreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const { + Error err; + String code = FileAccess::get_file_as_string(p_path, &err); + if (err != OK) { + return Ref<Texture2D>(); + } + + ScriptLanguage *lang = ScriptServer::get_language_for_extension(p_path.get_extension()); + return _generate_from_source_code(lang, code, p_size, p_metadata); +} + Ref<Texture2D> EditorScriptPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const { Ref<Script> scr = p_from; if (scr.is_null()) { @@ -471,18 +482,24 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const Ref<Resource> &p_from, } String code = scr->get_source_code().strip_edges(); - if (code.is_empty()) { + return _generate_from_source_code(scr->get_language(), code, p_size, p_metadata); +} + +Ref<Texture2D> EditorScriptPreviewPlugin::_generate_from_source_code(const ScriptLanguage *p_language, const String &p_source_code, const Size2 &p_size, Dictionary &p_metadata) const { + if (p_source_code.is_empty()) { return Ref<Texture2D>(); } List<String> kwors; - scr->get_language()->get_reserved_words(&kwors); + if (p_language) { + p_language->get_reserved_words(&kwors); + } HashSet<String> control_flow_keywords; HashSet<String> keywords; for (const String &E : kwors) { - if (scr->get_language()->is_control_flow_keyword(E)) { + if (p_language && p_language->is_control_flow_keyword(E)) { control_flow_keywords.insert(E); } else { keywords.insert(E); @@ -505,7 +522,7 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const Ref<Resource> &p_from, if (bg_color.a == 0) { bg_color = Color(0, 0, 0, 0); } - bg_color.a = MAX(bg_color.a, 0.2); // some background + bg_color.a = MAX(bg_color.a, 0.2); // Ensure we have some background, regardless of the text editor setting. img->fill(bg_color); @@ -519,14 +536,14 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const Ref<Resource> &p_from, bool in_keyword = false; bool in_comment = false; bool in_doc_comment = false; - for (int i = 0; i < code.length(); i++) { - char32_t c = code[i]; + for (int i = 0; i < p_source_code.length(); i++) { + char32_t c = p_source_code[i]; if (c > 32) { if (col < thumbnail_size) { Color color = text_color; if (c == '#') { - if (i < code.length() - 1 && code[i + 1] == '#') { + if (i < p_source_code.length() - 1 && p_source_code[i + 1] == '#') { in_doc_comment = true; } else { in_comment = true; @@ -539,17 +556,17 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const Ref<Resource> &p_from, color = doc_comment_color; } else { if (is_symbol(c)) { - //make symbol a little visible + // Make symbol a little visible. color = symbol_color; in_control_flow_keyword = false; in_keyword = false; } else if (!prev_is_text && is_ascii_identifier_char(c)) { int pos = i; - while (is_ascii_identifier_char(code[pos])) { + while (is_ascii_identifier_char(p_source_code[pos])) { pos++; } - String word = code.substr(i, pos - i); + String word = p_source_code.substr(i, pos - i); if (control_flow_keywords.has(word)) { in_control_flow_keyword = true; } else if (keywords.has(word)) { diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h index f3786d8413..fa7015f423 100644 --- a/editor/plugins/editor_preview_plugins.h +++ b/editor/plugins/editor_preview_plugins.h @@ -34,6 +34,8 @@ #include "core/templates/safe_refcount.h" #include "editor/editor_resource_preview.h" +class ScriptLanguage; + void post_process_preview(Ref<Image> p_image); class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator { @@ -112,9 +114,12 @@ public: class EditorScriptPreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorScriptPreviewPlugin, EditorResourcePreviewGenerator); + Ref<Texture2D> _generate_from_source_code(const ScriptLanguage *p_language, const String &p_source_code, const Size2 &p_size, Dictionary &p_metadata) const; + public: virtual bool handles(const String &p_type) const override; virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override; + virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const override; EditorScriptPreviewPlugin(); }; diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected index 04e046ec93..9b1aacc2e9 100644 --- a/misc/extension_api_validation/4.2-stable.expected +++ b/misc/extension_api_validation/4.2-stable.expected @@ -82,3 +82,25 @@ Validate extension JSON: Error: Field 'classes/GPUParticles3D/properties/process Validate extension JSON: Error: Field 'classes/Sky/properties/sky_material': type changed value in new API, from "ShaderMaterial,PanoramaSkyMaterial,ProceduralSkyMaterial,PhysicalSkyMaterial" to "PanoramaSkyMaterial,ProceduralSkyMaterial,PhysicalSkyMaterial,ShaderMaterial". Property hints reordered to improve editor usability. The types allowed are still the same as before. No adjustments should be necessary. + + +GH-68420 +-------- +Validate extension JSON: Error: Field 'classes/Node/methods/_get_configuration_warnings/return_value': type changed value in new API, from "PackedStringArray" to "Array". + +Allow configuration warnings to refer to a property. Compatibility method registered. + + +GH-86907 +-------- + +Validate extension JSON: Error: Field 'classes/AudioStreamPlayer/methods/is_autoplay_enabled': is_const changed value in new API, from false to true. +Validate extension JSON: Error: Field 'classes/AudioStreamPlayer2D/methods/is_autoplay_enabled': is_const changed value in new API, from false to true. +Validate extension JSON: Error: Field 'classes/AudioStreamPlayer3D/methods/is_autoplay_enabled': is_const changed value in new API, from false to true. +Validate extension JSON: Error: Field 'classes/GLTFBufferView/methods/get_buffer': is_const changed value in new API, from false to true. +Validate extension JSON: Error: Field 'classes/GLTFBufferView/methods/get_byte_length': is_const changed value in new API, from false to true. +Validate extension JSON: Error: Field 'classes/GLTFBufferView/methods/get_byte_offset': is_const changed value in new API, from false to true. +Validate extension JSON: Error: Field 'classes/GLTFBufferView/methods/get_byte_stride': is_const changed value in new API, from false to true. +Validate extension JSON: Error: Field 'classes/GLTFBufferView/methods/get_indices': is_const changed value in new API, from false to true. + +Change AudioStreamPlayer* is_autoplay_enabled and GLTFBufferView getters to be const. diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index e78c113c6c..7a576d5292 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -35,6 +35,7 @@ #include "gdscript_compiler.h" #include "gdscript_parser.h" #include "gdscript_rpc_callable.h" +#include "gdscript_tokenizer_buffer.h" #include "gdscript_warning.h" #ifdef TOOLS_ENABLED @@ -740,7 +741,12 @@ Error GDScript::reload(bool p_keep_state) { valid = false; GDScriptParser parser; - Error err = parser.parse(source, path, false); + Error err; + if (!binary_tokens.is_empty()) { + err = parser.parse_binary(binary_tokens, path); + } else { + err = parser.parse(source, path, false); + } if (err) { if (EngineDebugger::is_active()) { GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message); @@ -1050,6 +1056,19 @@ Error GDScript::load_source_code(const String &p_path) { return OK; } +void GDScript::set_binary_tokens_source(const Vector<uint8_t> &p_binary_tokens) { + binary_tokens = p_binary_tokens; +} + +const Vector<uint8_t> &GDScript::get_binary_tokens_source() const { + return binary_tokens; +} + +Vector<uint8_t> GDScript::get_as_binary_tokens() const { + GDScriptTokenizerBuffer tokenizer; + return tokenizer.parse_code_string(source, GDScriptTokenizerBuffer::COMPRESS_NONE); +} + const HashMap<StringName, GDScriptFunction *> &GDScript::debug_get_member_functions() const { return member_functions; } @@ -2805,6 +2824,7 @@ Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const Str void ResourceFormatLoaderGDScript::get_recognized_extensions(List<String> *p_extensions) const { p_extensions->push_back("gd"); + p_extensions->push_back("gdc"); } bool ResourceFormatLoaderGDScript::handles_type(const String &p_type) const { @@ -2813,7 +2833,7 @@ bool ResourceFormatLoaderGDScript::handles_type(const String &p_type) const { String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) const { String el = p_path.get_extension().to_lower(); - if (el == "gd") { + if (el == "gd" || el == "gdc") { return "GDScript"; } return ""; diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 2da9b89eb9..56a8deb905 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -176,6 +176,7 @@ private: bool clearing = false; //exported members String source; + Vector<uint8_t> binary_tokens; String path; bool path_valid = false; // False if using default path. StringName local_name; // Inner class identifier or `class_name`. @@ -296,6 +297,10 @@ public: String get_script_path() const; Error load_source_code(const String &p_path); + void set_binary_tokens_source(const Vector<uint8_t> &p_binary_tokens); + const Vector<uint8_t> &get_binary_tokens_source() const; + Vector<uint8_t> get_as_binary_tokens() const; + bool get_property_default_value(const StringName &p_property, Variant &r_value) const override; virtual void get_script_method_list(List<MethodInfo> *p_list) const override; diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index 76f4e69ab9..ef783ab564 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -67,10 +67,15 @@ Error GDScriptParserRef::raise_status(Status p_new_status) { while (p_new_status > status) { switch (status) { - case EMPTY: + case EMPTY: { status = PARSED; - result = parser->parse(GDScriptCache::get_source_code(path), path, false); - break; + String remapped_path = ResourceLoader::path_remap(path); + if (remapped_path.get_extension().to_lower() == "gdc") { + result = parser->parse_binary(GDScriptCache::get_binary_tokens(remapped_path), path); + } else { + result = parser->parse(GDScriptCache::get_source_code(remapped_path), path, false); + } + } break; case PARSED: { status = INHERITANCE_SOLVED; Error inheritance_result = get_analyzer()->resolve_inheritance(); @@ -205,7 +210,8 @@ Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptP return ref; } } else { - if (!FileAccess::exists(p_path)) { + String remapped_path = ResourceLoader::path_remap(p_path); + if (!FileAccess::exists(remapped_path)) { r_error = ERR_FILE_NOT_FOUND; return ref; } @@ -239,6 +245,20 @@ String GDScriptCache::get_source_code(const String &p_path) { return source; } +Vector<uint8_t> GDScriptCache::get_binary_tokens(const String &p_path) { + Vector<uint8_t> buffer; + Error err = OK; + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); + ERR_FAIL_COND_V_MSG(err != OK, buffer, "Failed to open binary GDScript file '" + p_path + "'."); + + uint64_t len = f->get_length(); + buffer.resize(len); + uint64_t read = f->get_buffer(buffer.ptrw(), buffer.size()); + ERR_FAIL_COND_V_MSG(read != len, Vector<uint8_t>(), "Failed to read binary GDScript file '" + p_path + "'."); + + return buffer; +} + Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, Error &r_error, const String &p_owner) { MutexLock lock(singleton->mutex); if (!p_owner.is_empty()) { @@ -251,10 +271,20 @@ Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, Error &r_e return singleton->shallow_gdscript_cache[p_path]; } + String remapped_path = ResourceLoader::path_remap(p_path); + Ref<GDScript> script; script.instantiate(); script->set_path(p_path, true); - r_error = script->load_source_code(p_path); + if (remapped_path.get_extension().to_lower() == "gdc") { + Vector<uint8_t> buffer = get_binary_tokens(remapped_path); + if (buffer.is_empty()) { + r_error = ERR_FILE_CANT_READ; + } + script->set_binary_tokens_source(buffer); + } else { + r_error = script->load_source_code(remapped_path); + } if (r_error) { return Ref<GDScript>(); // Returns null and does not cache when the script fails to load. @@ -294,9 +324,18 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro } if (p_update_from_disk) { - r_error = script->load_source_code(p_path); - if (r_error) { - return script; + if (p_path.get_extension().to_lower() == "gdc") { + Vector<uint8_t> buffer = get_binary_tokens(p_path); + if (buffer.is_empty()) { + r_error = ERR_FILE_CANT_READ; + return script; + } + script->set_binary_tokens_source(buffer); + } else { + r_error = script->load_source_code(p_path); + if (r_error) { + return script; + } } } diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h index 0a0f403e44..0754e9feb6 100644 --- a/modules/gdscript/gdscript_cache.h +++ b/modules/gdscript/gdscript_cache.h @@ -99,6 +99,7 @@ public: static void remove_script(const String &p_path); static Ref<GDScriptParserRef> get_parser(const String &p_path, GDScriptParserRef::Status status, Error &r_error, const String &p_owner = String()); static String get_source_code(const String &p_path); + static Vector<uint8_t> get_binary_tokens(const String &p_path); static Ref<GDScript> get_shallow_script(const String &p_path, Error &r_error, const String &p_owner = String()); static Ref<GDScript> get_full_script(const String &p_path, Error &r_error, const String &p_owner = String(), bool p_update_from_disk = false); static Ref<GDScript> get_cached_script(const String &p_path); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 78f9c0846f..1a574b3f3e 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -210,7 +210,7 @@ bool GDScriptLanguage::supports_documentation() const { } int GDScriptLanguage::find_function(const String &p_function, const String &p_code) const { - GDScriptTokenizer tokenizer; + GDScriptTokenizerText tokenizer; tokenizer.set_source_code(p_code); int indent = 0; GDScriptTokenizer::Token current = tokenizer.scan(); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 129d62cabd..3ba6e4d160 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -31,6 +31,7 @@ #include "gdscript_parser.h" #include "gdscript.h" +#include "gdscript_tokenizer_buffer.h" #include "core/config/project_settings.h" #include "core/io/file_access.h" @@ -226,7 +227,7 @@ void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node if (!for_completion || (!p_force && completion_context.type != COMPLETION_NONE)) { return; } - if (previous.cursor_place != GDScriptTokenizer::CURSOR_MIDDLE && previous.cursor_place != GDScriptTokenizer::CURSOR_END && current.cursor_place == GDScriptTokenizer::CURSOR_NONE) { + if (previous.cursor_place != GDScriptTokenizerText::CURSOR_MIDDLE && previous.cursor_place != GDScriptTokenizerText::CURSOR_END && current.cursor_place == GDScriptTokenizerText::CURSOR_NONE) { return; } CompletionContext context; @@ -234,7 +235,7 @@ void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node context.current_class = current_class; context.current_function = current_function; context.current_suite = current_suite; - context.current_line = tokenizer.get_cursor_line(); + context.current_line = tokenizer->get_cursor_line(); context.current_argument = p_argument; context.node = p_node; completion_context = context; @@ -244,7 +245,7 @@ void GDScriptParser::make_completion_context(CompletionType p_type, Variant::Typ if (!for_completion || (!p_force && completion_context.type != COMPLETION_NONE)) { return; } - if (previous.cursor_place != GDScriptTokenizer::CURSOR_MIDDLE && previous.cursor_place != GDScriptTokenizer::CURSOR_END && current.cursor_place == GDScriptTokenizer::CURSOR_NONE) { + if (previous.cursor_place != GDScriptTokenizerText::CURSOR_MIDDLE && previous.cursor_place != GDScriptTokenizerText::CURSOR_END && current.cursor_place == GDScriptTokenizerText::CURSOR_NONE) { return; } CompletionContext context; @@ -252,7 +253,7 @@ void GDScriptParser::make_completion_context(CompletionType p_type, Variant::Typ context.current_class = current_class; context.current_function = current_function; context.current_suite = current_suite; - context.current_line = tokenizer.get_cursor_line(); + context.current_line = tokenizer->get_cursor_line(); context.builtin_type = p_builtin_type; completion_context = context; } @@ -265,7 +266,7 @@ void GDScriptParser::push_completion_call(Node *p_call) { call.call = p_call; call.argument = 0; completion_call_stack.push_back(call); - if (previous.cursor_place == GDScriptTokenizer::CURSOR_MIDDLE || previous.cursor_place == GDScriptTokenizer::CURSOR_END || current.cursor_place == GDScriptTokenizer::CURSOR_BEGINNING) { + if (previous.cursor_place == GDScriptTokenizerText::CURSOR_MIDDLE || previous.cursor_place == GDScriptTokenizerText::CURSOR_END || current.cursor_place == GDScriptTokenizerText::CURSOR_BEGINNING) { completion_call = call; } } @@ -328,17 +329,21 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_ source = source.replace_first(String::chr(0xFFFF), String()); } - tokenizer.set_source_code(source); - tokenizer.set_cursor_position(cursor_line, cursor_column); + GDScriptTokenizerText *text_tokenizer = memnew(GDScriptTokenizerText); + text_tokenizer->set_source_code(source); + + tokenizer = text_tokenizer; + + tokenizer->set_cursor_position(cursor_line, cursor_column); script_path = p_script_path.simplify_path(); - current = tokenizer.scan(); + current = tokenizer->scan(); // Avoid error or newline as the first token. // The latter can mess with the parser when opening files filled exclusively with comments and newlines. while (current.type == GDScriptTokenizer::Token::ERROR || current.type == GDScriptTokenizer::Token::NEWLINE) { if (current.type == GDScriptTokenizer::Token::ERROR) { push_error(current.literal); } - current = tokenizer.scan(); + current = tokenizer->scan(); } #ifdef DEBUG_ENABLED @@ -359,6 +364,9 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_ parse_program(); pop_multiline(); + memdelete(text_tokenizer); + tokenizer = nullptr; + #ifdef DEBUG_ENABLED if (multiline_stack.size() > 0) { ERR_PRINT("Parser bug: Imbalanced multiline stack."); @@ -372,6 +380,41 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_ } } +Error GDScriptParser::parse_binary(const Vector<uint8_t> &p_binary, const String &p_script_path) { + GDScriptTokenizerBuffer *buffer_tokenizer = memnew(GDScriptTokenizerBuffer); + Error err = buffer_tokenizer->set_code_buffer(p_binary); + + if (err) { + memdelete(buffer_tokenizer); + return err; + } + + tokenizer = buffer_tokenizer; + script_path = p_script_path; + current = tokenizer->scan(); + // Avoid error or newline as the first token. + // The latter can mess with the parser when opening files filled exclusively with comments and newlines. + while (current.type == GDScriptTokenizer::Token::ERROR || current.type == GDScriptTokenizer::Token::NEWLINE) { + if (current.type == GDScriptTokenizer::Token::ERROR) { + push_error(current.literal); + } + current = tokenizer->scan(); + } + + push_multiline(false); // Keep one for the whole parsing. + parse_program(); + pop_multiline(); + + memdelete(buffer_tokenizer); + tokenizer = nullptr; + + if (errors.is_empty()) { + return OK; + } else { + return ERR_PARSE_ERROR; + } +} + GDScriptTokenizer::Token GDScriptParser::advance() { lambda_ended = false; // Empty marker since we're past the end in any case. @@ -379,16 +422,16 @@ GDScriptTokenizer::Token GDScriptParser::advance() { ERR_FAIL_COND_V_MSG(current.type == GDScriptTokenizer::Token::TK_EOF, current, "GDScript parser bug: Trying to advance past the end of stream."); } if (for_completion && !completion_call_stack.is_empty()) { - if (completion_call.call == nullptr && tokenizer.is_past_cursor()) { + if (completion_call.call == nullptr && tokenizer->is_past_cursor()) { completion_call = completion_call_stack.back()->get(); passed_cursor = true; } } previous = current; - current = tokenizer.scan(); + current = tokenizer->scan(); while (current.type == GDScriptTokenizer::Token::ERROR) { push_error(current.literal); - current = tokenizer.scan(); + current = tokenizer->scan(); } if (previous.type != GDScriptTokenizer::Token::DEDENT) { // `DEDENT` belongs to the next non-empty line. for (Node *n : nodes_in_progress) { @@ -457,11 +500,11 @@ void GDScriptParser::synchronize() { void GDScriptParser::push_multiline(bool p_state) { multiline_stack.push_back(p_state); - tokenizer.set_multiline_mode(p_state); + tokenizer->set_multiline_mode(p_state); if (p_state) { // Consume potential whitespace tokens already waiting in line. while (current.type == GDScriptTokenizer::Token::NEWLINE || current.type == GDScriptTokenizer::Token::INDENT || current.type == GDScriptTokenizer::Token::DEDENT) { - current = tokenizer.scan(); // Don't call advance() here, as we don't want to change the previous token. + current = tokenizer->scan(); // Don't call advance() here, as we don't want to change the previous token. } } } @@ -469,7 +512,7 @@ void GDScriptParser::push_multiline(bool p_state) { void GDScriptParser::pop_multiline() { ERR_FAIL_COND_MSG(multiline_stack.size() == 0, "Parser bug: trying to pop from multiline stack without available value."); multiline_stack.pop_back(); - tokenizer.set_multiline_mode(multiline_stack.size() > 0 ? multiline_stack.back()->get() : false); + tokenizer->set_multiline_mode(multiline_stack.size() > 0 ? multiline_stack.back()->get() : false); } bool GDScriptParser::is_statement_end_token() const { @@ -588,7 +631,7 @@ void GDScriptParser::parse_program() { complete_extents(head); #ifdef TOOLS_ENABLED - const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments(); + const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer->get_comments(); int line = MIN(max_script_doc_line, head->end_line); while (line > 0) { if (comments.has(line) && comments[line].new_line && comments[line].comment.begins_with("##")) { @@ -597,6 +640,7 @@ void GDScriptParser::parse_program() { } line--; } + #endif // TOOLS_ENABLED if (!check(GDScriptTokenizer::Token::TK_EOF)) { @@ -793,7 +837,7 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)(b if (has_comment(member->start_line, true)) { // Inline doc comment. member->doc_data = parse_class_doc_comment(member->start_line, true); - } else if (has_comment(doc_comment_line, true) && tokenizer.get_comments()[doc_comment_line].new_line) { + } else if (has_comment(doc_comment_line, true) && tokenizer->get_comments()[doc_comment_line].new_line) { // Normal doc comment. Don't check `min_member_doc_line` because a class ends parsing after its members. // This may not work correctly for cases like `var a; class B`, but it doesn't matter in practice. member->doc_data = parse_class_doc_comment(doc_comment_line); @@ -802,7 +846,7 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)(b if (has_comment(member->start_line, true)) { // Inline doc comment. member->doc_data = parse_doc_comment(member->start_line, true); - } else if (doc_comment_line >= min_member_doc_line && has_comment(doc_comment_line, true) && tokenizer.get_comments()[doc_comment_line].new_line) { + } else if (doc_comment_line >= min_member_doc_line && has_comment(doc_comment_line, true) && tokenizer->get_comments()[doc_comment_line].new_line) { // Normal doc comment. member->doc_data = parse_doc_comment(doc_comment_line); } @@ -1357,7 +1401,7 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum(bool p_is_static) { if (i == enum_node->values.size() - 1 || enum_node->values[i + 1].line > enum_value_line) { doc_data = parse_doc_comment(enum_value_line, true); } - } else if (doc_comment_line >= min_enum_value_doc_line && has_comment(doc_comment_line, true) && tokenizer.get_comments()[doc_comment_line].new_line) { + } else if (doc_comment_line >= min_enum_value_doc_line && has_comment(doc_comment_line, true) && tokenizer->get_comments()[doc_comment_line].new_line) { // Normal doc comment. doc_data = parse_doc_comment(doc_comment_line); } @@ -2346,6 +2390,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode IdentifierNode *identifier = alloc_node<IdentifierNode>(); complete_extents(identifier); identifier->name = previous.get_identifier(); + if (identifier->name.operator String().is_empty()) { + print_line("Empty identifier found."); + } identifier->suite = current_suite; if (current_suite != nullptr && current_suite->has_local(identifier->name)) { @@ -3050,7 +3097,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre // Allow for trailing comma. break; } - bool use_identifier_completion = current.cursor_place == GDScriptTokenizer::CURSOR_END || current.cursor_place == GDScriptTokenizer::CURSOR_MIDDLE; + bool use_identifier_completion = current.cursor_place == GDScriptTokenizerText::CURSOR_END || current.cursor_place == GDScriptTokenizerText::CURSOR_MIDDLE; ExpressionNode *argument = parse_expression(false); if (argument == nullptr) { push_error(R"(Expected expression as the function argument.)"); @@ -3220,7 +3267,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p // Reset the multiline stack since we don't want the multiline mode one in the lambda body. push_multiline(false); if (multiline_context) { - tokenizer.push_expression_indented_block(); + tokenizer->push_expression_indented_block(); } push_multiline(true); // For the parameters. @@ -3267,9 +3314,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p if (multiline_context) { // If we're in multiline mode, we want to skip the spurious DEDENT and NEWLINE tokens. while (check(GDScriptTokenizer::Token::DEDENT) || check(GDScriptTokenizer::Token::INDENT) || check(GDScriptTokenizer::Token::NEWLINE)) { - current = tokenizer.scan(); // Not advance() since we don't want to change the previous token. + current = tokenizer->scan(); // Not advance() since we don't want to change the previous token. } - tokenizer.pop_expression_indented_block(); + tokenizer->pop_expression_indented_block(); } current_function = previous_function; @@ -3518,20 +3565,20 @@ static String _process_doc_line(const String &p_line, const String &p_text, cons } bool GDScriptParser::has_comment(int p_line, bool p_must_be_doc) { - bool has_comment = tokenizer.get_comments().has(p_line); + bool has_comment = tokenizer->get_comments().has(p_line); // If there are no comments or if we don't care whether the comment // is a docstring, we have our result. if (!p_must_be_doc || !has_comment) { return has_comment; } - return tokenizer.get_comments()[p_line].comment.begins_with("##"); + return tokenizer->get_comments()[p_line].comment.begins_with("##"); } GDScriptParser::MemberDocData GDScriptParser::parse_doc_comment(int p_line, bool p_single_line) { ERR_FAIL_COND_V(!has_comment(p_line, true), MemberDocData()); - const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments(); + const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer->get_comments(); int line = p_line; if (!p_single_line) { @@ -3580,7 +3627,7 @@ GDScriptParser::MemberDocData GDScriptParser::parse_doc_comment(int p_line, bool GDScriptParser::ClassDocData GDScriptParser::parse_class_doc_comment(int p_line, bool p_single_line) { ERR_FAIL_COND_V(!has_comment(p_line, true), ClassDocData()); - const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments(); + const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer->get_comments(); int line = p_line; if (!p_single_line) { @@ -5027,6 +5074,9 @@ void GDScriptParser::TreePrinter::print_function(FunctionNode *p_function, const for (const AnnotationNode *E : p_function->annotations) { print_annotation(E); } + if (p_function->is_static) { + push_text("Static "); + } push_text(p_context); push_text(" "); if (p_function->identifier) { @@ -5371,6 +5421,9 @@ void GDScriptParser::TreePrinter::print_variable(VariableNode *p_variable) { print_annotation(E); } + if (p_variable->is_static) { + push_text("Static "); + } push_text("Variable "); print_identifier(p_variable->identifier); diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 11c5e51b9a..c064a2d0f4 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1336,7 +1336,7 @@ private: HashSet<int> unsafe_lines; #endif - GDScriptTokenizer tokenizer; + GDScriptTokenizer *tokenizer = nullptr; GDScriptTokenizer::Token previous; GDScriptTokenizer::Token current; @@ -1540,6 +1540,7 @@ private: public: Error parse(const String &p_source_code, const String &p_script_path, bool p_for_completion); + Error parse_binary(const Vector<uint8_t> &p_binary, const String &p_script_path); ClassNode *get_tree() const { return head; } bool is_tool() const { return _is_tool; } ClassNode *find_class(const String &p_qualified_name) const; diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index 29cf7bc6ca..2940af585d 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -256,7 +256,7 @@ String GDScriptTokenizer::get_token_name(Token::Type p_token_type) { return token_names[p_token_type]; } -void GDScriptTokenizer::set_source_code(const String &p_source_code) { +void GDScriptTokenizerText::set_source_code(const String &p_source_code) { source = p_source_code; if (source.is_empty()) { _source = U""; @@ -270,34 +270,34 @@ void GDScriptTokenizer::set_source_code(const String &p_source_code) { position = 0; } -void GDScriptTokenizer::set_cursor_position(int p_line, int p_column) { +void GDScriptTokenizerText::set_cursor_position(int p_line, int p_column) { cursor_line = p_line; cursor_column = p_column; } -void GDScriptTokenizer::set_multiline_mode(bool p_state) { +void GDScriptTokenizerText::set_multiline_mode(bool p_state) { multiline_mode = p_state; } -void GDScriptTokenizer::push_expression_indented_block() { +void GDScriptTokenizerText::push_expression_indented_block() { indent_stack_stack.push_back(indent_stack); } -void GDScriptTokenizer::pop_expression_indented_block() { - ERR_FAIL_COND(indent_stack_stack.size() == 0); +void GDScriptTokenizerText::pop_expression_indented_block() { + ERR_FAIL_COND(indent_stack_stack.is_empty()); indent_stack = indent_stack_stack.back()->get(); indent_stack_stack.pop_back(); } -int GDScriptTokenizer::get_cursor_line() const { +int GDScriptTokenizerText::get_cursor_line() const { return cursor_line; } -int GDScriptTokenizer::get_cursor_column() const { +int GDScriptTokenizerText::get_cursor_column() const { return cursor_column; } -bool GDScriptTokenizer::is_past_cursor() const { +bool GDScriptTokenizerText::is_past_cursor() const { if (line < cursor_line) { return false; } @@ -310,7 +310,7 @@ bool GDScriptTokenizer::is_past_cursor() const { return true; } -char32_t GDScriptTokenizer::_advance() { +char32_t GDScriptTokenizerText::_advance() { if (unlikely(_is_at_end())) { return '\0'; } @@ -329,11 +329,11 @@ char32_t GDScriptTokenizer::_advance() { return _peek(-1); } -void GDScriptTokenizer::push_paren(char32_t p_char) { +void GDScriptTokenizerText::push_paren(char32_t p_char) { paren_stack.push_back(p_char); } -bool GDScriptTokenizer::pop_paren(char32_t p_expected) { +bool GDScriptTokenizerText::pop_paren(char32_t p_expected) { if (paren_stack.is_empty()) { return false; } @@ -343,13 +343,13 @@ bool GDScriptTokenizer::pop_paren(char32_t p_expected) { return actual == p_expected; } -GDScriptTokenizer::Token GDScriptTokenizer::pop_error() { +GDScriptTokenizer::Token GDScriptTokenizerText::pop_error() { Token error = error_stack.back()->get(); error_stack.pop_back(); return error; } -GDScriptTokenizer::Token GDScriptTokenizer::make_token(Token::Type p_type) { +GDScriptTokenizer::Token GDScriptTokenizerText::make_token(Token::Type p_type) { Token token(p_type); token.start_line = start_line; token.end_line = line; @@ -408,35 +408,35 @@ GDScriptTokenizer::Token GDScriptTokenizer::make_token(Token::Type p_type) { return token; } -GDScriptTokenizer::Token GDScriptTokenizer::make_literal(const Variant &p_literal) { +GDScriptTokenizer::Token GDScriptTokenizerText::make_literal(const Variant &p_literal) { Token token = make_token(Token::LITERAL); token.literal = p_literal; return token; } -GDScriptTokenizer::Token GDScriptTokenizer::make_identifier(const StringName &p_identifier) { +GDScriptTokenizer::Token GDScriptTokenizerText::make_identifier(const StringName &p_identifier) { Token identifier = make_token(Token::IDENTIFIER); identifier.literal = p_identifier; return identifier; } -GDScriptTokenizer::Token GDScriptTokenizer::make_error(const String &p_message) { +GDScriptTokenizer::Token GDScriptTokenizerText::make_error(const String &p_message) { Token error = make_token(Token::ERROR); error.literal = p_message; return error; } -void GDScriptTokenizer::push_error(const String &p_message) { +void GDScriptTokenizerText::push_error(const String &p_message) { Token error = make_error(p_message); error_stack.push_back(error); } -void GDScriptTokenizer::push_error(const Token &p_error) { +void GDScriptTokenizerText::push_error(const Token &p_error) { error_stack.push_back(p_error); } -GDScriptTokenizer::Token GDScriptTokenizer::make_paren_error(char32_t p_paren) { +GDScriptTokenizer::Token GDScriptTokenizerText::make_paren_error(char32_t p_paren) { if (paren_stack.is_empty()) { return make_error(vformat("Closing \"%c\" doesn't have an opening counterpart.", p_paren)); } @@ -445,7 +445,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::make_paren_error(char32_t p_paren) { return error; } -GDScriptTokenizer::Token GDScriptTokenizer::check_vcs_marker(char32_t p_test, Token::Type p_double_type) { +GDScriptTokenizer::Token GDScriptTokenizerText::check_vcs_marker(char32_t p_test, Token::Type p_double_type) { const char32_t *next = _current + 1; int chars = 2; // Two already matched. @@ -469,7 +469,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::check_vcs_marker(char32_t p_test, To } } -GDScriptTokenizer::Token GDScriptTokenizer::annotation() { +GDScriptTokenizer::Token GDScriptTokenizerText::annotation() { if (is_unicode_identifier_start(_peek())) { _advance(); // Consume start character. } else { @@ -550,7 +550,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::annotation() { #define MAX_KEYWORD_LENGTH 10 #ifdef DEBUG_ENABLED -void GDScriptTokenizer::make_keyword_list() { +void GDScriptTokenizerText::make_keyword_list() { #define KEYWORD_LINE(keyword, token_type) keyword, #define KEYWORD_GROUP_IGNORE(group) keyword_list = { @@ -561,7 +561,7 @@ void GDScriptTokenizer::make_keyword_list() { } #endif // DEBUG_ENABLED -GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() { +GDScriptTokenizer::Token GDScriptTokenizerText::potential_identifier() { bool only_ascii = _peek(-1) < 128; // Consume all identifier characters. @@ -611,7 +611,9 @@ GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() { static_assert(keyword_length <= MAX_KEYWORD_LENGTH, "There's a keyword longer than the defined maximum length"); \ static_assert(keyword_length >= MIN_KEYWORD_LENGTH, "There's a keyword shorter than the defined minimum length"); \ if (keyword_length == len && name == keyword) { \ - return make_token(token_type); \ + Token kw = make_token(token_type); \ + kw.literal = name; \ + return kw; \ } \ } @@ -646,7 +648,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() { #undef MIN_KEYWORD_LENGTH #undef KEYWORDS -void GDScriptTokenizer::newline(bool p_make_token) { +void GDScriptTokenizerText::newline(bool p_make_token) { // Don't overwrite previous newline, nor create if we want a line continuation. if (p_make_token && !pending_newline && !line_continuation) { Token newline(Token::NEWLINE); @@ -667,7 +669,7 @@ void GDScriptTokenizer::newline(bool p_make_token) { leftmost_column = 1; } -GDScriptTokenizer::Token GDScriptTokenizer::number() { +GDScriptTokenizer::Token GDScriptTokenizerText::number() { int base = 10; bool has_decimal = false; bool has_exponent = false; @@ -868,7 +870,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() { } } -GDScriptTokenizer::Token GDScriptTokenizer::string() { +GDScriptTokenizer::Token GDScriptTokenizerText::string() { enum StringType { STRING_REGULAR, STRING_NAME, @@ -1154,7 +1156,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() { return make_literal(string); } -void GDScriptTokenizer::check_indent() { +void GDScriptTokenizerText::check_indent() { ERR_FAIL_COND_MSG(column != 1, "Checking tokenizer indentation in the middle of a line."); if (_is_at_end()) { @@ -1323,13 +1325,13 @@ void GDScriptTokenizer::check_indent() { } } -String GDScriptTokenizer::_get_indent_char_name(char32_t ch) { +String GDScriptTokenizerText::_get_indent_char_name(char32_t ch) { ERR_FAIL_COND_V(ch != ' ' && ch != '\t', String(&ch, 1).c_escape()); return ch == ' ' ? "space" : "tab"; } -void GDScriptTokenizer::_skip_whitespace() { +void GDScriptTokenizerText::_skip_whitespace() { if (pending_indents != 0) { // Still have some indent/dedent tokens to give. return; @@ -1391,7 +1393,7 @@ void GDScriptTokenizer::_skip_whitespace() { } } -GDScriptTokenizer::Token GDScriptTokenizer::scan() { +GDScriptTokenizer::Token GDScriptTokenizerText::scan() { if (has_error()) { return pop_error(); } @@ -1453,6 +1455,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() { if (_peek() != '\n') { return make_error("Expected new line after \"\\\"."); } + continuation_lines.push_back(line); _advance(); newline(false); line_continuation = true; @@ -1673,7 +1676,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() { } } -GDScriptTokenizer::GDScriptTokenizer() { +GDScriptTokenizerText::GDScriptTokenizerText() { #ifdef TOOLS_ENABLED if (EditorSettings::get_singleton()) { tab_size = EditorSettings::get_singleton()->get_setting("text_editor/behavior/indent/size"); diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index a64aaf6820..5d76375173 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -181,14 +181,13 @@ public: bool can_precede_bin_op() const; bool is_identifier() const; bool is_node_name() const; - StringName get_identifier() const { return source; } + StringName get_identifier() const { return literal; } Token(Type p_type) { type = p_type; } - Token() { - } + Token() {} }; #ifdef TOOLS_ENABLED @@ -203,12 +202,26 @@ public: new_line = p_new_line; } }; - const HashMap<int, CommentData> &get_comments() const { - return comments; - } + virtual const HashMap<int, CommentData> &get_comments() const = 0; #endif // TOOLS_ENABLED -private: + static String get_token_name(Token::Type p_token_type); + + virtual int get_cursor_line() const = 0; + virtual int get_cursor_column() const = 0; + virtual void set_cursor_position(int p_line, int p_column) = 0; + virtual void set_multiline_mode(bool p_state) = 0; + virtual bool is_past_cursor() const = 0; + virtual void push_expression_indented_block() = 0; // For lambdas, or blocks inside expressions. + virtual void pop_expression_indented_block() = 0; // For lambdas, or blocks inside expressions. + virtual bool is_text() = 0; + + virtual Token scan() = 0; + + virtual ~GDScriptTokenizer() {} +}; + +class GDScriptTokenizerText : public GDScriptTokenizer { String source; const char32_t *_source = nullptr; const char32_t *_current = nullptr; @@ -235,6 +248,7 @@ private: char32_t indent_char = '\0'; int position = 0; int length = 0; + Vector<int> continuation_lines; #ifdef DEBUG_ENABLED Vector<String> keyword_list; #endif // DEBUG_ENABLED @@ -275,20 +289,28 @@ private: Token annotation(); public: - Token scan(); - void set_source_code(const String &p_source_code); - int get_cursor_line() const; - int get_cursor_column() const; - void set_cursor_position(int p_line, int p_column); - void set_multiline_mode(bool p_state); - bool is_past_cursor() const; - static String get_token_name(Token::Type p_token_type); - void push_expression_indented_block(); // For lambdas, or blocks inside expressions. - void pop_expression_indented_block(); // For lambdas, or blocks inside expressions. + const Vector<int> &get_continuation_lines() const { return continuation_lines; } + + virtual int get_cursor_line() const override; + virtual int get_cursor_column() const override; + virtual void set_cursor_position(int p_line, int p_column) override; + virtual void set_multiline_mode(bool p_state) override; + virtual bool is_past_cursor() const override; + virtual void push_expression_indented_block() override; // For lambdas, or blocks inside expressions. + virtual void pop_expression_indented_block() override; // For lambdas, or blocks inside expressions. + virtual bool is_text() override { return true; } + +#ifdef TOOLS_ENABLED + virtual const HashMap<int, CommentData> &get_comments() const override { + return comments; + } +#endif // TOOLS_ENABLED + + virtual Token scan() override; - GDScriptTokenizer(); + GDScriptTokenizerText(); }; #endif // GDSCRIPT_TOKENIZER_H diff --git a/modules/gdscript/gdscript_tokenizer_buffer.cpp b/modules/gdscript/gdscript_tokenizer_buffer.cpp new file mode 100644 index 0000000000..db523ea941 --- /dev/null +++ b/modules/gdscript/gdscript_tokenizer_buffer.cpp @@ -0,0 +1,493 @@ +/**************************************************************************/ +/* gdscript_tokenizer_buffer.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 "gdscript_tokenizer_buffer.h" + +#include "core/io/compression.h" +#include "core/io/marshalls.h" + +#define TOKENIZER_VERSION 100 + +int GDScriptTokenizerBuffer::_token_to_binary(const Token &p_token, Vector<uint8_t> &r_buffer, int p_start, HashMap<StringName, uint32_t> &r_identifiers_map, HashMap<Variant, uint32_t, VariantHasher, VariantComparator> &r_constants_map) { + int pos = p_start; + + int token_type = p_token.type & TOKEN_MASK; + + switch (p_token.type) { + case GDScriptTokenizer::Token::ANNOTATION: + case GDScriptTokenizer::Token::IDENTIFIER: { + // Add identifier to map. + int identifier_pos; + StringName id = p_token.get_identifier(); + if (r_identifiers_map.has(id)) { + identifier_pos = r_identifiers_map[id]; + } else { + identifier_pos = r_identifiers_map.size(); + r_identifiers_map[id] = identifier_pos; + } + token_type |= identifier_pos << TOKEN_BITS; + } break; + case GDScriptTokenizer::Token::ERROR: + case GDScriptTokenizer::Token::LITERAL: { + // Add literal to map. + int constant_pos; + if (r_constants_map.has(p_token.literal)) { + constant_pos = r_constants_map[p_token.literal]; + } else { + constant_pos = r_constants_map.size(); + r_constants_map[p_token.literal] = constant_pos; + } + token_type |= constant_pos << TOKEN_BITS; + } break; + default: + break; + } + + // Encode token. + int token_len; + if (token_type & TOKEN_MASK) { + token_len = 8; + r_buffer.resize(pos + token_len); + encode_uint32(token_type | TOKEN_BYTE_MASK, &r_buffer.write[pos]); + pos += 4; + } else { + token_len = 5; + r_buffer.resize(pos + token_len); + r_buffer.write[pos] = token_type; + pos++; + } + encode_uint32(p_token.start_line, &r_buffer.write[pos]); + return token_len; +} + +GDScriptTokenizer::Token GDScriptTokenizerBuffer::_binary_to_token(const uint8_t *p_buffer) { + Token token; + const uint8_t *b = p_buffer; + + uint32_t token_type = decode_uint32(b); + token.type = (Token::Type)(token_type & TOKEN_MASK); + if (token_type & TOKEN_BYTE_MASK) { + b += 4; + } else { + b++; + } + token.start_line = decode_uint32(b); + token.end_line = token.start_line; + + token.literal = token.get_name(); + if (token.type == Token::CONST_NAN) { + token.literal = String("NAN"); // Special case since name and notation are different. + } + + switch (token.type) { + case GDScriptTokenizer::Token::ANNOTATION: + case GDScriptTokenizer::Token::IDENTIFIER: { + // Get name from map. + int identifier_pos = token_type >> TOKEN_BITS; + if (unlikely(identifier_pos >= identifiers.size())) { + Token error; + error.type = Token::ERROR; + error.literal = "Identifier index out of bounds."; + return error; + } + token.literal = identifiers[identifier_pos]; + } break; + case GDScriptTokenizer::Token::ERROR: + case GDScriptTokenizer::Token::LITERAL: { + // Get literal from map. + int constant_pos = token_type >> TOKEN_BITS; + if (unlikely(constant_pos >= constants.size())) { + Token error; + error.type = Token::ERROR; + error.literal = "Constant index out of bounds."; + return error; + } + token.literal = constants[constant_pos]; + } break; + default: + break; + } + + return token; +} + +Error GDScriptTokenizerBuffer::set_code_buffer(const Vector<uint8_t> &p_buffer) { + const uint8_t *buf = p_buffer.ptr(); + ERR_FAIL_COND_V(p_buffer.size() < 12 || p_buffer[0] != 'G' || p_buffer[1] != 'D' || p_buffer[2] != 'S' || p_buffer[3] != 'C', ERR_INVALID_DATA); + + int version = decode_uint32(&buf[4]); + ERR_FAIL_COND_V_MSG(version > TOKENIZER_VERSION, ERR_INVALID_DATA, "Binary GDScript is too recent! Please use a newer engine version."); + + int decompressed_size = decode_uint32(&buf[8]); + + Vector<uint8_t> contents; + if (decompressed_size == 0) { + contents = p_buffer.slice(12); + } else { + contents.resize(decompressed_size); + int result = Compression::decompress(contents.ptrw(), contents.size(), &buf[12], p_buffer.size() - 12, Compression::MODE_ZSTD); + ERR_FAIL_COND_V_MSG(result != decompressed_size, ERR_INVALID_DATA, "Error decompressing GDScript tokenizer buffer."); + } + + int total_len = contents.size(); + buf = contents.ptr(); + uint32_t identifier_count = decode_uint32(&buf[0]); + uint32_t constant_count = decode_uint32(&buf[4]); + uint32_t token_line_count = decode_uint32(&buf[8]); + uint32_t token_count = decode_uint32(&buf[16]); + + const uint8_t *b = &buf[20]; + total_len -= 20; + + identifiers.resize(identifier_count); + for (uint32_t i = 0; i < identifier_count; i++) { + uint32_t len = decode_uint32(b); + total_len -= 4; + ERR_FAIL_COND_V((len * 4u) > (uint32_t)total_len, ERR_INVALID_DATA); + b += 4; + Vector<uint32_t> cs; + cs.resize(len); + for (uint32_t j = 0; j < len; j++) { + uint8_t tmp[4]; + for (uint32_t k = 0; k < 4; k++) { + tmp[k] = b[j * 4 + k] ^ 0xb6; + } + cs.write[j] = decode_uint32(tmp); + } + + String s(reinterpret_cast<const char32_t *>(cs.ptr()), len); + b += len * 4; + total_len -= len * 4; + identifiers.write[i] = s; + } + + constants.resize(constant_count); + for (uint32_t i = 0; i < constant_count; i++) { + Variant v; + int len; + Error err = decode_variant(v, b, total_len, &len, false); + if (err) { + return err; + } + b += len; + total_len -= len; + constants.write[i] = v; + } + + for (uint32_t i = 0; i < token_line_count; i++) { + ERR_FAIL_COND_V(total_len < 8, ERR_INVALID_DATA); + uint32_t token_index = decode_uint32(b); + b += 4; + uint32_t line = decode_uint32(b); + b += 4; + total_len -= 8; + token_lines[token_index] = line; + } + for (uint32_t i = 0; i < token_line_count; i++) { + ERR_FAIL_COND_V(total_len < 8, ERR_INVALID_DATA); + uint32_t token_index = decode_uint32(b); + b += 4; + uint32_t column = decode_uint32(b); + b += 4; + total_len -= 8; + token_columns[token_index] = column; + } + + tokens.resize(token_count); + for (uint32_t i = 0; i < token_count; i++) { + int token_len = 5; + if ((*b) & TOKEN_BYTE_MASK) { + token_len = 8; + } + ERR_FAIL_COND_V(total_len < token_len, ERR_INVALID_DATA); + Token token = _binary_to_token(b); + b += token_len; + ERR_FAIL_INDEX_V(token.type, Token::TK_MAX, ERR_INVALID_DATA); + tokens.write[i] = token; + total_len -= token_len; + } + + ERR_FAIL_COND_V(total_len > 0, ERR_INVALID_DATA); + + return OK; +} + +Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code, CompressMode p_compress_mode) { + HashMap<StringName, uint32_t> identifier_map; + HashMap<Variant, uint32_t, VariantHasher, VariantComparator> constant_map; + Vector<uint8_t> token_buffer; + HashMap<uint32_t, uint32_t> token_lines; + HashMap<uint32_t, uint32_t> token_columns; + + GDScriptTokenizerText tokenizer; + tokenizer.set_source_code(p_code); + tokenizer.set_multiline_mode(true); // Ignore whitespace tokens. + Token current = tokenizer.scan(); + int token_pos = 0; + int last_token_line = 0; + int token_counter = 0; + + while (current.type != Token::TK_EOF) { + int token_len = _token_to_binary(current, token_buffer, token_pos, identifier_map, constant_map); + token_pos += token_len; + if (token_counter > 0 && current.start_line > last_token_line) { + token_lines[token_counter] = current.start_line; + token_columns[token_counter] = current.start_column; + } + last_token_line = current.end_line; + + current = tokenizer.scan(); + token_counter++; + } + + // Reverse maps. + Vector<StringName> rev_identifier_map; + rev_identifier_map.resize(identifier_map.size()); + for (const KeyValue<StringName, uint32_t> &E : identifier_map) { + rev_identifier_map.write[E.value] = E.key; + } + Vector<Variant> rev_constant_map; + rev_constant_map.resize(constant_map.size()); + for (const KeyValue<Variant, uint32_t> &E : constant_map) { + rev_constant_map.write[E.value] = E.key; + } + HashMap<uint32_t, uint32_t> rev_token_lines; + for (const KeyValue<uint32_t, uint32_t> &E : token_lines) { + rev_token_lines[E.value] = E.key; + } + + // Remove continuation lines from map. + for (int line : tokenizer.get_continuation_lines()) { + if (rev_token_lines.has(line + 1)) { + token_lines.erase(rev_token_lines[line + 1]); + token_columns.erase(rev_token_lines[line + 1]); + } + } + + Vector<uint8_t> contents; + contents.resize(20); + encode_uint32(identifier_map.size(), &contents.write[0]); + encode_uint32(constant_map.size(), &contents.write[4]); + encode_uint32(token_lines.size(), &contents.write[8]); + encode_uint32(token_counter, &contents.write[16]); + + int buf_pos = 20; + + // Save identifiers. + for (const StringName &id : rev_identifier_map) { + String s = id.operator String(); + int len = s.length(); + + contents.resize(buf_pos + (len + 1) * 4); + + encode_uint32(len, &contents.write[buf_pos]); + buf_pos += 4; + + for (int i = 0; i < len; i++) { + uint8_t tmp[4]; + encode_uint32(s[i], tmp); + + for (int b = 0; b < 4; b++) { + contents.write[buf_pos + b] = tmp[b] ^ 0xb6; + } + + buf_pos += 4; + } + } + + // Save constants. + for (const Variant &v : rev_constant_map) { + int len; + // Objects cannot be constant, never encode objects. + Error err = encode_variant(v, nullptr, len, false); + ERR_FAIL_COND_V_MSG(err != OK, Vector<uint8_t>(), "Error when trying to encode Variant."); + contents.resize(buf_pos + len); + encode_variant(v, &contents.write[buf_pos], len, false); + buf_pos += len; + } + + // Save lines and columns. + contents.resize(buf_pos + token_lines.size() * 16); + for (const KeyValue<uint32_t, uint32_t> &e : token_lines) { + encode_uint32(e.key, &contents.write[buf_pos]); + buf_pos += 4; + encode_uint32(e.value, &contents.write[buf_pos]); + buf_pos += 4; + } + for (const KeyValue<uint32_t, uint32_t> &e : token_columns) { + encode_uint32(e.key, &contents.write[buf_pos]); + buf_pos += 4; + encode_uint32(e.value, &contents.write[buf_pos]); + buf_pos += 4; + } + + // Store tokens. + contents.append_array(token_buffer); + + Vector<uint8_t> buf; + + // Save header. + buf.resize(12); + buf.write[0] = 'G'; + buf.write[1] = 'D'; + buf.write[2] = 'S'; + buf.write[3] = 'C'; + encode_uint32(TOKENIZER_VERSION, &buf.write[4]); + + switch (p_compress_mode) { + case COMPRESS_NONE: + encode_uint32(0u, &buf.write[8]); + buf.append_array(contents); + break; + + case COMPRESS_ZSTD: { + encode_uint32(contents.size(), &buf.write[8]); + Vector<uint8_t> compressed; + int max_size = Compression::get_max_compressed_buffer_size(contents.size(), Compression::MODE_ZSTD); + compressed.resize(max_size); + + int compressed_size = Compression::compress(compressed.ptrw(), contents.ptr(), contents.size(), Compression::MODE_ZSTD); + ERR_FAIL_COND_V_MSG(compressed_size < 0, Vector<uint8_t>(), "Error compressing GDScript tokenizer buffer."); + compressed.resize(compressed_size); + + buf.append_array(compressed); + } break; + } + + return buf; +} + +int GDScriptTokenizerBuffer::get_cursor_line() const { + return 0; +} + +int GDScriptTokenizerBuffer::get_cursor_column() const { + return 0; +} + +void GDScriptTokenizerBuffer::set_cursor_position(int p_line, int p_column) { +} + +void GDScriptTokenizerBuffer::set_multiline_mode(bool p_state) { + multiline_mode = p_state; +} + +bool GDScriptTokenizerBuffer::is_past_cursor() const { + return false; +} + +void GDScriptTokenizerBuffer::push_expression_indented_block() { + indent_stack_stack.push_back(indent_stack); +} + +void GDScriptTokenizerBuffer::pop_expression_indented_block() { + ERR_FAIL_COND(indent_stack_stack.is_empty()); + indent_stack = indent_stack_stack.back()->get(); + indent_stack_stack.pop_back(); +} + +GDScriptTokenizer::Token GDScriptTokenizerBuffer::scan() { + // Add final newline. + if (current >= tokens.size() && !last_token_was_newline) { + Token newline; + newline.type = Token::NEWLINE; + newline.start_line = current_line; + newline.end_line = current_line; + last_token_was_newline = true; + return newline; + } + + // Resolve pending indentation change. + if (pending_indents > 0) { + pending_indents--; + Token indent; + indent.type = Token::INDENT; + indent.start_line = current_line; + indent.end_line = current_line; + return indent; + } else if (pending_indents < 0) { + pending_indents++; + Token dedent; + dedent.type = Token::DEDENT; + dedent.start_line = current_line; + dedent.end_line = current_line; + return dedent; + } + + if (current >= tokens.size()) { + if (!indent_stack.is_empty()) { + pending_indents -= indent_stack.size(); + indent_stack.clear(); + return scan(); + } + Token eof; + eof.type = Token::TK_EOF; + return eof; + }; + + if (!last_token_was_newline && token_lines.has(current)) { + current_line = token_lines[current]; + uint32_t current_column = token_columns[current]; + + // Check if there's a need to indent/dedent. + if (!multiline_mode) { + uint32_t previous_indent = 0; + if (!indent_stack.is_empty()) { + previous_indent = indent_stack.back()->get(); + } + if (current_column - 1 > previous_indent) { + pending_indents++; + indent_stack.push_back(current_column - 1); + } else { + while (current_column - 1 < previous_indent) { + pending_indents--; + indent_stack.pop_back(); + if (indent_stack.is_empty()) { + break; + } + previous_indent = indent_stack.back()->get(); + } + } + + Token newline; + newline.type = Token::NEWLINE; + newline.start_line = current_line; + newline.end_line = current_line; + last_token_was_newline = true; + + return newline; + } + } + + last_token_was_newline = false; + + Token token = tokens[current++]; + return token; +} diff --git a/modules/gdscript/gdscript_tokenizer_buffer.h b/modules/gdscript/gdscript_tokenizer_buffer.h new file mode 100644 index 0000000000..55df66e50f --- /dev/null +++ b/modules/gdscript/gdscript_tokenizer_buffer.h @@ -0,0 +1,93 @@ +/**************************************************************************/ +/* gdscript_tokenizer_buffer.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 GDSCRIPT_TOKENIZER_BUFFER_H +#define GDSCRIPT_TOKENIZER_BUFFER_H + +#include "gdscript_tokenizer.h" + +class GDScriptTokenizerBuffer : public GDScriptTokenizer { +public: + enum CompressMode { + COMPRESS_NONE, + COMPRESS_ZSTD, + }; + + enum { + TOKEN_BYTE_MASK = 0x80, + TOKEN_BITS = 8, + TOKEN_MASK = (1 << (TOKEN_BITS - 1)) - 1, + }; + + Vector<StringName> identifiers; + Vector<Variant> constants; + Vector<int> continuation_lines; + HashMap<int, int> token_lines; + HashMap<int, int> token_columns; + Vector<Token> tokens; + int current = 0; + uint32_t current_line = 1; + + bool multiline_mode = false; + List<int> indent_stack; + List<List<int>> indent_stack_stack; // For lambdas, which require manipulating the indentation point. + int pending_indents = 0; + bool last_token_was_newline = false; + +#ifdef TOOLS_ENABLED + HashMap<int, CommentData> dummy; +#endif // TOOLS_ENABLED + + static int _token_to_binary(const Token &p_token, Vector<uint8_t> &r_buffer, int p_start, HashMap<StringName, uint32_t> &r_identifiers_map, HashMap<Variant, uint32_t, VariantHasher, VariantComparator> &r_constants_map); + Token _binary_to_token(const uint8_t *p_buffer); + +public: + Error set_code_buffer(const Vector<uint8_t> &p_buffer); + static Vector<uint8_t> parse_code_string(const String &p_code, CompressMode p_compress_mode); + + virtual int get_cursor_line() const override; + virtual int get_cursor_column() const override; + virtual void set_cursor_position(int p_line, int p_column) override; + virtual void set_multiline_mode(bool p_state) override; + virtual bool is_past_cursor() const override; + virtual void push_expression_indented_block() override; // For lambdas, or blocks inside expressions. + virtual void pop_expression_indented_block() override; // For lambdas, or blocks inside expressions. + virtual bool is_text() override { return false; }; + +#ifdef TOOLS_ENABLED + virtual const HashMap<int, CommentData> &get_comments() const override { + return dummy; + } +#endif // TOOLS_ENABLED + + virtual Token scan() override; +}; + +#endif // GDSCRIPT_TOKENIZER_BUFFER_H diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index 0f8648e9a3..ad7af34bf1 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -191,7 +191,7 @@ void ExtendGDScriptParser::update_symbols() { void ExtendGDScriptParser::update_document_links(const String &p_code) { document_links.clear(); - GDScriptTokenizer scr_tokenizer; + GDScriptTokenizerText scr_tokenizer; Ref<FileAccess> fs = FileAccess::create(FileAccess::ACCESS_RESOURCES); scr_tokenizer.set_source_code(p_code); while (true) { diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index 605e82be6e..5ff1c78ac9 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -34,6 +34,7 @@ #include "gdscript_analyzer.h" #include "gdscript_cache.h" #include "gdscript_tokenizer.h" +#include "gdscript_tokenizer_buffer.h" #include "gdscript_utility_functions.h" #ifdef TOOLS_ENABLED @@ -83,18 +84,33 @@ class EditorExportGDScript : public EditorExportPlugin { public: virtual void _export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) override { - String script_key; + int script_mode = EditorExportPreset::MODE_SCRIPT_BINARY_TOKENS_COMPRESSED; const Ref<EditorExportPreset> &preset = get_export_preset(); if (preset.is_valid()) { - script_key = preset->get_script_encryption_key().to_lower(); + script_mode = preset->get_script_export_mode(); } - if (!p_path.ends_with(".gd")) { + if (!p_path.ends_with(".gd") || script_mode == EditorExportPreset::MODE_SCRIPT_TEXT) { return; } + Vector<uint8_t> file = FileAccess::get_file_as_bytes(p_path); + if (file.is_empty()) { + return; + } + + String source; + source.parse_utf8(reinterpret_cast<const char *>(file.ptr()), file.size()); + GDScriptTokenizerBuffer::CompressMode compress_mode = script_mode == EditorExportPreset::MODE_SCRIPT_BINARY_TOKENS_COMPRESSED ? GDScriptTokenizerBuffer::COMPRESS_ZSTD : GDScriptTokenizerBuffer::COMPRESS_NONE; + file = GDScriptTokenizerBuffer::parse_code_string(source, compress_mode); + if (file.is_empty()) { + return; + } + + add_file(p_path.get_basename() + ".gdc", file, true); + return; } @@ -185,6 +201,10 @@ void test_tokenizer() { GDScriptTests::test(GDScriptTests::TestType::TEST_TOKENIZER); } +void test_tokenizer_buffer() { + GDScriptTests::test(GDScriptTests::TestType::TEST_TOKENIZER_BUFFER); +} + void test_parser() { GDScriptTests::test(GDScriptTests::TestType::TEST_PARSER); } @@ -198,6 +218,7 @@ void test_bytecode() { } REGISTER_TEST_COMMAND("gdscript-tokenizer", &test_tokenizer); +REGISTER_TEST_COMMAND("gdscript-tokenizer-buffer", &test_tokenizer_buffer); REGISTER_TEST_COMMAND("gdscript-parser", &test_parser); REGISTER_TEST_COMMAND("gdscript-compiler", &test_compiler); REGISTER_TEST_COMMAND("gdscript-bytecode", &test_bytecode); diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp index 4d93a6fc18..a0329eb8d2 100644 --- a/modules/gdscript/tests/gdscript_test_runner.cpp +++ b/modules/gdscript/tests/gdscript_test_runner.cpp @@ -34,6 +34,7 @@ #include "../gdscript_analyzer.h" #include "../gdscript_compiler.h" #include "../gdscript_parser.h" +#include "../gdscript_tokenizer_buffer.h" #include "core/config/project_settings.h" #include "core/core_globals.h" @@ -131,10 +132,11 @@ void finish_language() { StringName GDScriptTestRunner::test_function_name; -GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_language, bool p_print_filenames) { +GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_language, bool p_print_filenames, bool p_use_binary_tokens) { test_function_name = StaticCString::create("test"); do_init_languages = p_init_language; print_filenames = p_print_filenames; + binary_tokens = p_use_binary_tokens; source_dir = p_source_dir; if (!source_dir.ends_with("/")) { @@ -277,6 +279,9 @@ bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) { if (next.ends_with(".notest.gd")) { next = dir->get_next(); continue; + } else if (binary_tokens && next.ends_with(".textonly.gd")) { + next = dir->get_next(); + continue; } else if (next.get_extension().to_lower() == "gd") { #ifndef DEBUG_ENABLED // On release builds, skip tests marked as debug only. @@ -299,6 +304,9 @@ bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) { ERR_FAIL_V_MSG(false, "Could not find output file for " + next); } GDScriptTest test(current_dir.path_join(next), current_dir.path_join(out_file), source_dir); + if (binary_tokens) { + test.set_tokenizer_mode(GDScriptTest::TOKENIZER_BUFFER); + } tests.push_back(test); } } @@ -321,24 +329,65 @@ bool GDScriptTestRunner::make_tests() { return make_tests_for_dir(dir->get_current_dir()); } -bool GDScriptTestRunner::generate_class_index() { +static bool generate_class_index_recursive(const String &p_dir) { + Error err = OK; + Ref<DirAccess> dir(DirAccess::open(p_dir, &err)); + + if (err != OK) { + return false; + } + + String current_dir = dir->get_current_dir(); + + dir->list_dir_begin(); + String next = dir->get_next(); + StringName gdscript_name = GDScriptLanguage::get_singleton()->get_name(); - for (int i = 0; i < tests.size(); i++) { - GDScriptTest test = tests[i]; - String base_type; + while (!next.is_empty()) { + if (dir->current_is_dir()) { + if (next == "." || next == ".." || next == "completion" || next == "lsp") { + next = dir->get_next(); + continue; + } + if (!generate_class_index_recursive(current_dir.path_join(next))) { + return false; + } + } else { + if (!next.ends_with(".gd")) { + next = dir->get_next(); + continue; + } + String base_type; + String source_file = current_dir.path_join(next); + String class_name = GDScriptLanguage::get_singleton()->get_global_class_name(source_file, &base_type); + if (class_name.is_empty()) { + next = dir->get_next(); + continue; + } + ERR_FAIL_COND_V_MSG(ScriptServer::is_global_class(class_name), false, + "Class name '" + class_name + "' from " + source_file + " is already used in " + ScriptServer::get_global_class_path(class_name)); - String class_name = GDScriptLanguage::get_singleton()->get_global_class_name(test.get_source_file(), &base_type); - if (class_name.is_empty()) { - continue; + ScriptServer::add_global_class(class_name, base_type, gdscript_name, source_file); } - ERR_FAIL_COND_V_MSG(ScriptServer::is_global_class(class_name), false, - "Class name '" + class_name + "' from " + test.get_source_file() + " is already used in " + ScriptServer::get_global_class_path(class_name)); - ScriptServer::add_global_class(class_name, base_type, gdscript_name, test.get_source_file()); + next = dir->get_next(); } + + dir->list_dir_end(); + return true; } +bool GDScriptTestRunner::generate_class_index() { + Error err = OK; + Ref<DirAccess> dir(DirAccess::open(source_dir, &err)); + + ERR_FAIL_COND_V_MSG(err != OK, false, "Could not open specified test directory."); + + source_dir = dir->get_current_dir() + "/"; // Make it absolute path. + return generate_class_index_recursive(dir->get_current_dir()); +} + GDScriptTest::GDScriptTest(const String &p_source_path, const String &p_output_path, const String &p_base_dir) { source_file = p_source_path; output_file = p_output_path; @@ -484,7 +533,15 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) { Ref<GDScript> script; script.instantiate(); script->set_path(source_file); - err = script->load_source_code(source_file); + if (tokenizer_mode == TOKENIZER_TEXT) { + err = script->load_source_code(source_file); + } else { + String code = FileAccess::get_file_as_string(source_file, &err); + if (!err) { + Vector<uint8_t> buffer = GDScriptTokenizerBuffer::parse_code_string(code, GDScriptTokenizerBuffer::COMPRESS_ZSTD); + script->set_binary_tokens_source(buffer); + } + } if (err != OK) { enable_stdout(); result.status = GDTEST_LOAD_ERROR; @@ -494,7 +551,11 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) { // Test parsing. GDScriptParser parser; - err = parser.parse(script->get_source_code(), source_file, false); + if (tokenizer_mode == TOKENIZER_TEXT) { + err = parser.parse(script->get_source_code(), source_file, false); + } else { + err = parser.parse_binary(script->get_binary_tokens_source(), source_file); + } if (err != OK) { enable_stdout(); result.status = GDTEST_PARSER_ERROR; @@ -583,7 +644,14 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) { add_print_handler(&_print_handler); add_error_handler(&_error_handler); - script->reload(); + err = script->reload(); + if (err) { + enable_stdout(); + result.status = GDTEST_LOAD_ERROR; + result.output = ""; + result.passed = false; + ERR_FAIL_V_MSG(result, "\nCould not reload script: '" + source_file + "'"); + } // Create object instance for test. Object *obj = ClassDB::instantiate(script->get_native()->get_name()); diff --git a/modules/gdscript/tests/gdscript_test_runner.h b/modules/gdscript/tests/gdscript_test_runner.h index b1190604ad..57e3ac86f9 100644 --- a/modules/gdscript/tests/gdscript_test_runner.h +++ b/modules/gdscript/tests/gdscript_test_runner.h @@ -62,6 +62,11 @@ public: bool passed; }; + enum TokenizerMode { + TOKENIZER_TEXT, + TOKENIZER_BUFFER, + }; + private: struct ErrorHandlerData { TestResult *result = nullptr; @@ -79,6 +84,8 @@ private: PrintHandlerList _print_handler; ErrorHandlerList _error_handler; + TokenizerMode tokenizer_mode = TOKENIZER_TEXT; + void enable_stdout(); void disable_stdout(); bool check_output(const String &p_output) const; @@ -96,6 +103,9 @@ public: const String get_source_relative_filepath() const { return source_file.trim_prefix(base_dir); } const String &get_output_file() const { return output_file; } + void set_tokenizer_mode(TokenizerMode p_tokenizer_mode) { tokenizer_mode = p_tokenizer_mode; } + TokenizerMode get_tokenizer_mode() const { return tokenizer_mode; } + GDScriptTest(const String &p_source_path, const String &p_output_path, const String &p_base_dir); GDScriptTest() : GDScriptTest(String(), String(), String()) {} // Needed to use in Vector. @@ -108,6 +118,7 @@ class GDScriptTestRunner { bool is_generating = false; bool do_init_languages = false; bool print_filenames; // Whether filenames should be printed when generated/running tests + bool binary_tokens; // Test with buffer tokenizer. bool make_tests(); bool make_tests_for_dir(const String &p_dir); @@ -120,7 +131,7 @@ public: int run_tests(); bool generate_outputs(); - GDScriptTestRunner(const String &p_source_dir, bool p_init_language, bool p_print_filenames = false); + GDScriptTestRunner(const String &p_source_dir, bool p_init_language, bool p_print_filenames = false, bool p_use_binary_tokens = false); ~GDScriptTestRunner(); }; diff --git a/modules/gdscript/tests/gdscript_test_runner_suite.h b/modules/gdscript/tests/gdscript_test_runner_suite.h index 5fd7d942d2..5acf436e42 100644 --- a/modules/gdscript/tests/gdscript_test_runner_suite.h +++ b/modules/gdscript/tests/gdscript_test_runner_suite.h @@ -38,12 +38,10 @@ namespace GDScriptTests { TEST_SUITE("[Modules][GDScript]") { - // GDScript 2.0 is still under heavy construction. - // Allow the tests to fail, but do not ignore errors during development. - // Update the scripts and expected output as needed. TEST_CASE("Script compilation and runtime") { bool print_filenames = OS::get_singleton()->get_cmdline_args().find("--print-filenames") != nullptr; - GDScriptTestRunner runner("modules/gdscript/tests/scripts", true, print_filenames); + bool use_binary_tokens = OS::get_singleton()->get_cmdline_args().find("--use-binary-tokens") != nullptr; + GDScriptTestRunner runner("modules/gdscript/tests/scripts", true, print_filenames, use_binary_tokens); int fail_count = runner.run_tests(); INFO("Make sure `*.out` files have expected results."); REQUIRE_MESSAGE(fail_count == 0, "All GDScript tests should pass."); diff --git a/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.gd b/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.textonly.gd index 9ad77f1432..9ad77f1432 100644 --- a/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.gd +++ b/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.textonly.gd diff --git a/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.out b/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.textonly.out index 31bed2dbc7..31bed2dbc7 100644 --- a/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.out +++ b/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.textonly.out diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_if.gd b/modules/gdscript/tests/scripts/parser/features/multiline_if.gd index 86152f4543..7b82d9b1da 100644 --- a/modules/gdscript/tests/scripts/parser/features/multiline_if.gd +++ b/modules/gdscript/tests/scripts/parser/features/multiline_if.gd @@ -9,6 +9,7 @@ func test(): # Alternatively, backslashes can be used. if 1 == 1 \ + \ and 2 == 2 and \ 3 == 3: pass diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp index 467bedc4b2..f6965cf7cf 100644 --- a/modules/gdscript/tests/test_gdscript.cpp +++ b/modules/gdscript/tests/test_gdscript.cpp @@ -34,6 +34,7 @@ #include "../gdscript_compiler.h" #include "../gdscript_parser.h" #include "../gdscript_tokenizer.h" +#include "../gdscript_tokenizer_buffer.h" #include "core/config/project_settings.h" #include "core/io/file_access.h" @@ -50,7 +51,7 @@ namespace GDScriptTests { static void test_tokenizer(const String &p_code, const Vector<String> &p_lines) { - GDScriptTokenizer tokenizer; + GDScriptTokenizerText tokenizer; tokenizer.set_source_code(p_code); int tab_size = 4; @@ -107,6 +108,53 @@ static void test_tokenizer(const String &p_code, const Vector<String> &p_lines) print_line(current.get_name()); // Should be EOF } +static void test_tokenizer_buffer(const Vector<uint8_t> &p_buffer, const Vector<String> &p_lines); + +static void test_tokenizer_buffer(const String &p_code, const Vector<String> &p_lines) { + Vector<uint8_t> binary = GDScriptTokenizerBuffer::parse_code_string(p_code, GDScriptTokenizerBuffer::COMPRESS_NONE); + test_tokenizer_buffer(binary, p_lines); +} + +static void test_tokenizer_buffer(const Vector<uint8_t> &p_buffer, const Vector<String> &p_lines) { + GDScriptTokenizerBuffer tokenizer; + tokenizer.set_code_buffer(p_buffer); + + int tab_size = 4; +#ifdef TOOLS_ENABLED + if (EditorSettings::get_singleton()) { + tab_size = EditorSettings::get_singleton()->get_setting("text_editor/behavior/indent/size"); + } +#endif // TOOLS_ENABLED + String tab = String(" ").repeat(tab_size); + + GDScriptTokenizer::Token current = tokenizer.scan(); + while (current.type != GDScriptTokenizer::Token::TK_EOF) { + StringBuilder token; + token += " --> "; // Padding for line number. + + for (int l = current.start_line; l <= current.end_line && l <= p_lines.size(); l++) { + print_line(vformat("%04d %s", l, p_lines[l - 1]).replace("\t", tab)); + } + + token += current.get_name(); + + if (current.type == GDScriptTokenizer::Token::ERROR || current.type == GDScriptTokenizer::Token::LITERAL || current.type == GDScriptTokenizer::Token::IDENTIFIER || current.type == GDScriptTokenizer::Token::ANNOTATION) { + token += "("; + token += Variant::get_type_name(current.literal.get_type()); + token += ") "; + token += current.literal; + } + + print_line(token.as_string()); + + print_line("-------------------------------------------------------"); + + current = tokenizer.scan(); + } + + print_line(current.get_name()); // Should be EOF +} + static void test_parser(const String &p_code, const String &p_script_path, const Vector<String> &p_lines) { GDScriptParser parser; Error err = parser.parse(p_code, p_script_path, false); @@ -119,7 +167,7 @@ static void test_parser(const String &p_code, const String &p_script_path, const } GDScriptAnalyzer analyzer(&parser); - analyzer.analyze(); + err = analyzer.analyze(); if (err != OK) { const List<GDScriptParser::ParserError> &errors = parser.get_errors(); @@ -212,7 +260,7 @@ void test(TestType p_type) { } String test = cmdlargs.back()->get(); - if (!test.ends_with(".gd")) { + if (!test.ends_with(".gd") && !test.ends_with(".gdc")) { print_line("This test expects a path to a GDScript file as its last parameter. Got: " + test); return; } @@ -255,6 +303,13 @@ void test(TestType p_type) { case TEST_TOKENIZER: test_tokenizer(code, lines); break; + case TEST_TOKENIZER_BUFFER: + if (test.ends_with(".gdc")) { + test_tokenizer_buffer(buf, lines); + } else { + test_tokenizer_buffer(code, lines); + } + break; case TEST_PARSER: test_parser(code, test, lines); break; diff --git a/modules/gdscript/tests/test_gdscript.h b/modules/gdscript/tests/test_gdscript.h index b39dfe2b5a..32f278d5ce 100644 --- a/modules/gdscript/tests/test_gdscript.h +++ b/modules/gdscript/tests/test_gdscript.h @@ -39,6 +39,7 @@ namespace GDScriptTests { enum TestType { TEST_TOKENIZER, + TEST_TOKENIZER_BUFFER, TEST_PARSER, TEST_COMPILER, TEST_BYTECODE, diff --git a/modules/gltf/structures/gltf_buffer_view.compat.inc b/modules/gltf/structures/gltf_buffer_view.compat.inc new file mode 100644 index 0000000000..db2600a071 --- /dev/null +++ b/modules/gltf/structures/gltf_buffer_view.compat.inc @@ -0,0 +1,61 @@ +/**************************************************************************/ +/* gltf_buffer_view.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +GLTFBufferIndex GLTFBufferView::_get_buffer_bind_compat_86907() { + return get_buffer(); +} + +int GLTFBufferView::_get_byte_offset_bind_compat_86907() { + return get_byte_offset(); +} + +int GLTFBufferView::_get_byte_length_bind_compat_86907() { + return get_byte_length(); +} + +int GLTFBufferView::_get_byte_stride_bind_compat_86907() { + return get_byte_stride(); +} + +bool GLTFBufferView::_get_indices_bind_compat_86907() { + return get_indices(); +} + +void GLTFBufferView::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("get_buffer"), &GLTFBufferView::_get_buffer_bind_compat_86907); + ClassDB::bind_compatibility_method(D_METHOD("get_byte_offset"), &GLTFBufferView::_get_byte_offset_bind_compat_86907); + ClassDB::bind_compatibility_method(D_METHOD("get_byte_length"), &GLTFBufferView::_get_byte_length_bind_compat_86907); + ClassDB::bind_compatibility_method(D_METHOD("get_byte_stride"), &GLTFBufferView::_get_byte_stride_bind_compat_86907); + ClassDB::bind_compatibility_method(D_METHOD("get_indices"), &GLTFBufferView::_get_indices_bind_compat_86907); +} + +#endif // DISABLE_DEPRECATED diff --git a/modules/gltf/structures/gltf_buffer_view.cpp b/modules/gltf/structures/gltf_buffer_view.cpp index 8588de0752..997c219bf0 100644 --- a/modules/gltf/structures/gltf_buffer_view.cpp +++ b/modules/gltf/structures/gltf_buffer_view.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "gltf_buffer_view.h" +#include "gltf_buffer_view.compat.inc" #include "../gltf_state.h" @@ -53,7 +54,7 @@ void GLTFBufferView::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "indices"), "set_indices", "get_indices"); // bool } -GLTFBufferIndex GLTFBufferView::get_buffer() { +GLTFBufferIndex GLTFBufferView::get_buffer() const { return buffer; } @@ -61,7 +62,7 @@ void GLTFBufferView::set_buffer(GLTFBufferIndex p_buffer) { buffer = p_buffer; } -int GLTFBufferView::get_byte_offset() { +int GLTFBufferView::get_byte_offset() const { return byte_offset; } @@ -69,7 +70,7 @@ void GLTFBufferView::set_byte_offset(int p_byte_offset) { byte_offset = p_byte_offset; } -int GLTFBufferView::get_byte_length() { +int GLTFBufferView::get_byte_length() const { return byte_length; } @@ -77,7 +78,7 @@ void GLTFBufferView::set_byte_length(int p_byte_length) { byte_length = p_byte_length; } -int GLTFBufferView::get_byte_stride() { +int GLTFBufferView::get_byte_stride() const { return byte_stride; } @@ -85,7 +86,7 @@ void GLTFBufferView::set_byte_stride(int p_byte_stride) { byte_stride = p_byte_stride; } -bool GLTFBufferView::get_indices() { +bool GLTFBufferView::get_indices() const { return indices; } diff --git a/modules/gltf/structures/gltf_buffer_view.h b/modules/gltf/structures/gltf_buffer_view.h index e4b7168130..1c7bd5c5c7 100644 --- a/modules/gltf/structures/gltf_buffer_view.h +++ b/modules/gltf/structures/gltf_buffer_view.h @@ -49,20 +49,29 @@ private: protected: static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + GLTFBufferIndex _get_buffer_bind_compat_86907(); + int _get_byte_offset_bind_compat_86907(); + int _get_byte_length_bind_compat_86907(); + int _get_byte_stride_bind_compat_86907(); + bool _get_indices_bind_compat_86907(); + static void _bind_compatibility_methods(); +#endif // DISABLE_DEPRECATED + public: - GLTFBufferIndex get_buffer(); + GLTFBufferIndex get_buffer() const; void set_buffer(GLTFBufferIndex p_buffer); - int get_byte_offset(); + int get_byte_offset() const; void set_byte_offset(int p_byte_offset); - int get_byte_length(); + int get_byte_length() const; void set_byte_length(int p_byte_length); - int get_byte_stride(); + int get_byte_stride() const; void set_byte_stride(int p_byte_stride); - bool get_indices(); + bool get_indices() const; void set_indices(bool p_indices); Vector<uint8_t> load_buffer_view_data(const Ref<GLTFState> p_state) const; diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp index 264a2e9c8e..fecefc7e71 100644 --- a/modules/multiplayer/multiplayer_spawner.cpp +++ b/modules/multiplayer/multiplayer_spawner.cpp @@ -87,8 +87,8 @@ void MultiplayerSpawner::_get_property_list(List<PropertyInfo> *p_list) const { } #endif -PackedStringArray MultiplayerSpawner::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array MultiplayerSpawner::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); 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.")); diff --git a/modules/multiplayer/multiplayer_spawner.h b/modules/multiplayer/multiplayer_spawner.h index 0e94b781ea..6cd2946df7 100644 --- a/modules/multiplayer/multiplayer_spawner.h +++ b/modules/multiplayer/multiplayer_spawner.h @@ -91,7 +91,7 @@ protected: void _get_property_list(List<PropertyInfo> *p_list) const; #endif public: - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; Node *get_spawn_node() const { return spawn_node.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(spawn_node)) : nullptr; diff --git a/modules/multiplayer/multiplayer_synchronizer.cpp b/modules/multiplayer/multiplayer_synchronizer.cpp index 02e3a11964..16df9da78e 100644 --- a/modules/multiplayer/multiplayer_synchronizer.cpp +++ b/modules/multiplayer/multiplayer_synchronizer.cpp @@ -143,8 +143,8 @@ bool MultiplayerSynchronizer::update_inbound_sync_time(uint16_t p_network_time) return true; } -PackedStringArray MultiplayerSynchronizer::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array MultiplayerSynchronizer::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (root_path.is_empty() || !has_node(root_path)) { warnings.push_back(RTR("A valid NodePath must be set in the \"Root Path\" property in order for MultiplayerSynchronizer to be able to synchronize properties.")); diff --git a/modules/multiplayer/multiplayer_synchronizer.h b/modules/multiplayer/multiplayer_synchronizer.h index 192d7a5920..f5f3993f0d 100644 --- a/modules/multiplayer/multiplayer_synchronizer.h +++ b/modules/multiplayer/multiplayer_synchronizer.h @@ -91,7 +91,7 @@ public: bool update_outbound_sync_time(uint64_t p_usec); bool update_inbound_sync_time(uint16_t p_network_time); - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; void set_replication_interval(double p_interval); double get_replication_interval() const; diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm index 56542aff37..000215ac46 100644 --- a/platform/macos/os_macos.mm +++ b/platform/macos/os_macos.mm @@ -356,8 +356,11 @@ Error OS_MacOS::shell_show_in_file_manager(String p_path, bool p_open_folder) { Error OS_MacOS::shell_open(String p_uri) { NSString *string = [NSString stringWithUTF8String:p_uri.utf8().get_data()]; NSURL *uri = [[NSURL alloc] initWithString:string]; - // Escape special characters in filenames if (!uri || !uri.scheme || [uri.scheme isEqual:@"file"]) { + // No scheme set, assume "file://" and escape special characters. + if (!p_uri.begins_with("file://")) { + string = [NSString stringWithUTF8String:("file://" + p_uri).utf8().get_data()]; + } uri = [[NSURL alloc] initWithString:[string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]]]; } [[NSWorkspace sharedWorkspace] openURL:uri]; diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index b3a20edea8..9e343780c5 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -568,8 +568,8 @@ StringName AnimatedSprite2D::get_animation() const { return animation; } -PackedStringArray AnimatedSprite2D::get_configuration_warnings() const { - PackedStringArray warnings = Node2D::get_configuration_warnings(); +Array AnimatedSprite2D::get_configuration_warnings() const { + Array warnings = Node2D::get_configuration_warnings(); if (frames.is_null()) { warnings.push_back(RTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite2D to display frames.")); } diff --git a/scene/2d/animated_sprite_2d.h b/scene/2d/animated_sprite_2d.h index ac53bd26ee..1f70f1af81 100644 --- a/scene/2d/animated_sprite_2d.h +++ b/scene/2d/animated_sprite_2d.h @@ -125,7 +125,7 @@ public: void set_flip_v(bool p_flip); bool is_flipped_v() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; AnimatedSprite2D(); diff --git a/scene/2d/audio_stream_player_2d.compat.inc b/scene/2d/audio_stream_player_2d.compat.inc new file mode 100644 index 0000000000..6f541087a6 --- /dev/null +++ b/scene/2d/audio_stream_player_2d.compat.inc @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* audio_stream_player_2d.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +bool AudioStreamPlayer2D::_is_autoplay_enabled_bind_compat_86907() { + return is_autoplay_enabled(); +} + +void AudioStreamPlayer2D::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("is_autoplay_enabled"), &AudioStreamPlayer2D::_is_autoplay_enabled_bind_compat_86907); +} + +#endif // DISABLE_DEPRECATED diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index f6e6eb8b17..4762ae28f8 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "audio_stream_player_2d.h" +#include "audio_stream_player_2d.compat.inc" #include "core/config/project_settings.h" #include "scene/2d/area_2d.h" @@ -251,7 +252,7 @@ void AudioStreamPlayer2D::set_autoplay(bool p_enable) { internal->autoplay = p_enable; } -bool AudioStreamPlayer2D::is_autoplay_enabled() { +bool AudioStreamPlayer2D::is_autoplay_enabled() const { return internal->autoplay; } diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h index 3552735cd7..3a3e6510c8 100644 --- a/scene/2d/audio_stream_player_2d.h +++ b/scene/2d/audio_stream_player_2d.h @@ -89,6 +89,11 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; +#ifndef DISABLE_DEPRECATED + bool _is_autoplay_enabled_bind_compat_86907(); + static void _bind_compatibility_methods(); +#endif // DISABLE_DEPRECATED + public: void set_stream(Ref<AudioStream> p_stream); Ref<AudioStream> get_stream() const; @@ -109,7 +114,7 @@ public: StringName get_bus() const; void set_autoplay(bool p_enable); - bool is_autoplay_enabled(); + bool is_autoplay_enabled() const; void set_max_distance(float p_pixels); float get_max_distance() const; diff --git a/scene/2d/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp index 2c5c6a1a16..f87880afd7 100644 --- a/scene/2d/canvas_modulate.cpp +++ b/scene/2d/canvas_modulate.cpp @@ -113,8 +113,8 @@ Color CanvasModulate::get_color() const { return color; } -PackedStringArray CanvasModulate::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array CanvasModulate::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (is_in_canvas && is_visible_in_tree()) { List<Node *> nodes; diff --git a/scene/2d/canvas_modulate.h b/scene/2d/canvas_modulate.h index 08ded52e23..05b126e889 100644 --- a/scene/2d/canvas_modulate.h +++ b/scene/2d/canvas_modulate.h @@ -54,7 +54,7 @@ public: void set_color(const Color &p_color); Color get_color() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; CanvasModulate(); ~CanvasModulate(); diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index 4e5852984b..e36eda573c 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -582,8 +582,8 @@ void CollisionObject2D::_update_pickable() { } } -PackedStringArray CollisionObject2D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array CollisionObject2D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (shapes.is_empty()) { warnings.push_back(RTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape2D or CollisionPolygon2D as a child to define its shape.")); diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h index 780793f289..25ae9af8d5 100644 --- a/scene/2d/collision_object_2d.h +++ b/scene/2d/collision_object_2d.h @@ -166,7 +166,7 @@ public: void set_pickable(bool p_enabled); bool is_pickable() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; _FORCE_INLINE_ RID get_rid() const { return rid; } diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp index d2f71eca9d..cfbdf65776 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -232,8 +232,8 @@ bool CollisionPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, doubl } #endif -PackedStringArray CollisionPolygon2D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array CollisionPolygon2D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (!Object::cast_to<CollisionObject2D>(get_parent())) { warnings.push_back(RTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, CharacterBody2D, etc. to give them a shape.")); diff --git a/scene/2d/collision_polygon_2d.h b/scene/2d/collision_polygon_2d.h index f1ee30babe..cbedc718d8 100644 --- a/scene/2d/collision_polygon_2d.h +++ b/scene/2d/collision_polygon_2d.h @@ -77,7 +77,7 @@ public: void set_polygon(const Vector<Point2> &p_polygon); Vector<Point2> get_polygon() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; void set_disabled(bool p_disabled); bool is_disabled() const; diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp index ee413c7bc2..2c6e675527 100644 --- a/scene/2d/collision_shape_2d.cpp +++ b/scene/2d/collision_shape_2d.cpp @@ -173,8 +173,8 @@ bool CollisionShape2D::_edit_is_selected_on_click(const Point2 &p_point, double return shape->_edit_is_selected_on_click(p_point, p_tolerance); } -PackedStringArray CollisionShape2D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array CollisionShape2D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); CollisionObject2D *col_object = Object::cast_to<CollisionObject2D>(get_parent()); if (col_object == nullptr) { diff --git a/scene/2d/collision_shape_2d.h b/scene/2d/collision_shape_2d.h index 3e13dd698c..18f16269c0 100644 --- a/scene/2d/collision_shape_2d.h +++ b/scene/2d/collision_shape_2d.h @@ -80,7 +80,7 @@ public: void set_debug_color(const Color &p_color); Color get_debug_color() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; CollisionShape2D(); }; diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index e04e6d7dce..c06b172b38 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -246,8 +246,8 @@ bool CPUParticles2D::get_fractional_delta() const { return fractional_delta; } -PackedStringArray CPUParticles2D::get_configuration_warnings() const { - PackedStringArray warnings = Node2D::get_configuration_warnings(); +Array CPUParticles2D::get_configuration_warnings() const { + Array warnings = Node2D::get_configuration_warnings(); CanvasItemMaterial *mat = Object::cast_to<CanvasItemMaterial>(get_material().ptr()); diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h index 3f858c3277..7108764bbe 100644 --- a/scene/2d/cpu_particles_2d.h +++ b/scene/2d/cpu_particles_2d.h @@ -282,7 +282,7 @@ public: void set_gravity(const Vector2 &p_gravity); Vector2 get_gravity() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; void restart(); diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index bc39513c03..d34e339448 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -327,8 +327,8 @@ float GPUParticles2D::get_interp_to_end() const { return interp_to_end_factor; } -PackedStringArray GPUParticles2D::get_configuration_warnings() const { - PackedStringArray warnings = Node2D::get_configuration_warnings(); +Array GPUParticles2D::get_configuration_warnings() const { + Array warnings = Node2D::get_configuration_warnings(); if (process_material.is_null()) { warnings.push_back(RTR("A material to process the particles is not assigned, so no behavior is imprinted.")); diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h index 58996b0327..ba10e54251 100644 --- a/scene/2d/gpu_particles_2d.h +++ b/scene/2d/gpu_particles_2d.h @@ -161,7 +161,7 @@ public: void set_amount_ratio(float p_ratio); float get_amount_ratio() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; void set_sub_emitter(const NodePath &p_path); NodePath get_sub_emitter() const; diff --git a/scene/2d/joint_2d.cpp b/scene/2d/joint_2d.cpp index 41b121a482..dd9a721240 100644 --- a/scene/2d/joint_2d.cpp +++ b/scene/2d/joint_2d.cpp @@ -212,8 +212,8 @@ bool Joint2D::get_exclude_nodes_from_collision() const { return exclude_from_collision; } -PackedStringArray Joint2D::get_configuration_warnings() const { - PackedStringArray warnings = Node2D::get_configuration_warnings(); +Array Joint2D::get_configuration_warnings() const { + Array warnings = Node2D::get_configuration_warnings(); if (!warning.is_empty()) { warnings.push_back(warning); diff --git a/scene/2d/joint_2d.h b/scene/2d/joint_2d.h index 5ff75a77a1..6a3777d3f1 100644 --- a/scene/2d/joint_2d.h +++ b/scene/2d/joint_2d.h @@ -62,7 +62,7 @@ protected: _FORCE_INLINE_ bool is_configured() const { return configured; } public: - virtual PackedStringArray get_configuration_warnings() const override; + virtual Array get_configuration_warnings() const override; void set_node_a(const NodePath &p_node_a); NodePath get_node_a() const; diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp index c03786caef..a8aecd8257 100644 --- a/scene/2d/light_2d.cpp +++ b/scene/2d/light_2d.cpp @@ -401,11 +401,14 @@ Vector2 PointLight2D::get_texture_offset() const { return texture_offset; } -PackedStringArray PointLight2D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array PointLight2D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (!texture.is_valid()) { - warnings.push_back(RTR("A texture with the shape of the light must be supplied to the \"Texture\" property.")); + Dictionary texture_warning; + texture_warning["message"] = RTR("A texture with the shape of the light must be supplied."); + texture_warning["property"] = "texture"; + warnings.push_back(texture_warning); } return warnings; diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h index 3c1171deae..79b49b4c69 100644 --- a/scene/2d/light_2d.h +++ b/scene/2d/light_2d.h @@ -174,7 +174,7 @@ public: void set_texture_scale(real_t p_scale); real_t get_texture_scale() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; PointLight2D(); }; diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp index 61f5d5cd46..f5da96d4f9 100644 --- a/scene/2d/light_occluder_2d.cpp +++ b/scene/2d/light_occluder_2d.cpp @@ -247,8 +247,8 @@ int LightOccluder2D::get_occluder_light_mask() const { return mask; } -PackedStringArray LightOccluder2D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array LightOccluder2D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (!occluder_polygon.is_valid()) { warnings.push_back(RTR("An occluder polygon must be set (or drawn) for this occluder to take effect.")); diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h index dd3130394e..b8add2e498 100644 --- a/scene/2d/light_occluder_2d.h +++ b/scene/2d/light_occluder_2d.h @@ -105,7 +105,7 @@ public: void set_as_sdf_collision(bool p_enable); bool is_set_as_sdf_collision() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; LightOccluder2D(); ~LightOccluder2D(); diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index fee774fe2e..3b222e27c4 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -624,8 +624,8 @@ void NavigationAgent2D::_avoidance_done(Vector3 p_new_velocity) { emit_signal(SNAME("velocity_computed"), safe_velocity); } -PackedStringArray NavigationAgent2D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array NavigationAgent2D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (!Object::cast_to<Node2D>(get_parent())) { warnings.push_back(RTR("The NavigationAgent2D can be used only under a Node2D inheriting parent node.")); diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h index 0e46086a81..7a8d008e90 100644 --- a/scene/2d/navigation_agent_2d.h +++ b/scene/2d/navigation_agent_2d.h @@ -200,7 +200,7 @@ public: void _avoidance_done(Vector3 p_new_velocity); - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; void set_avoidance_layers(uint32_t p_layers); uint32_t get_avoidance_layers() const; diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp index 04ba550888..be224eb08b 100644 --- a/scene/2d/navigation_link_2d.cpp +++ b/scene/2d/navigation_link_2d.cpp @@ -337,8 +337,8 @@ void NavigationLink2D::set_travel_cost(real_t p_travel_cost) { NavigationServer2D::get_singleton()->link_set_travel_cost(link, travel_cost); } -PackedStringArray NavigationLink2D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array NavigationLink2D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (start_position.is_equal_approx(end_position)) { warnings.push_back(RTR("NavigationLink2D start position should be different than the end position to be useful.")); diff --git a/scene/2d/navigation_link_2d.h b/scene/2d/navigation_link_2d.h index 2929691c04..7d8b375b6c 100644 --- a/scene/2d/navigation_link_2d.h +++ b/scene/2d/navigation_link_2d.h @@ -93,7 +93,7 @@ public: void set_travel_cost(real_t p_travel_cost); real_t get_travel_cost() const { return travel_cost; } - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; NavigationLink2D(); ~NavigationLink2D(); diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index 9a689c0f21..38ca1d98ef 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -275,8 +275,8 @@ void NavigationRegion2D::_navigation_map_changed(RID p_map) { } #endif // DEBUG_ENABLED -PackedStringArray NavigationRegion2D::get_configuration_warnings() const { - PackedStringArray warnings = Node2D::get_configuration_warnings(); +Array NavigationRegion2D::get_configuration_warnings() const { + Array warnings = Node2D::get_configuration_warnings(); if (is_visible_in_tree() && is_inside_tree()) { if (!navigation_polygon.is_valid()) { diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h index 0719f786fd..f7ae3c5741 100644 --- a/scene/2d/navigation_region_2d.h +++ b/scene/2d/navigation_region_2d.h @@ -113,7 +113,7 @@ public: void set_avoidance_layer_value(int p_layer_number, bool p_value); bool get_avoidance_layer_value(int p_layer_number) const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; void bake_navigation_polygon(bool p_on_thread); void _bake_finished(Ref<NavigationPolygon> p_navigation_polygon); diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp index 3dd0d7b61c..44cfc1deb2 100644 --- a/scene/2d/parallax_layer.cpp +++ b/scene/2d/parallax_layer.cpp @@ -137,8 +137,8 @@ void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, real_t p_s _update_mirroring(); } -PackedStringArray ParallaxLayer::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array ParallaxLayer::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (!Object::cast_to<ParallaxBackground>(get_parent())) { warnings.push_back(RTR("ParallaxLayer node only works when set as child of a ParallaxBackground node.")); diff --git a/scene/2d/parallax_layer.h b/scene/2d/parallax_layer.h index 22fa0dd51c..dbf9386198 100644 --- a/scene/2d/parallax_layer.h +++ b/scene/2d/parallax_layer.h @@ -59,7 +59,7 @@ public: void set_base_offset_and_scale(const Point2 &p_offset, real_t p_scale); - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; ParallaxLayer(); }; diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index 282d14da5d..4fce49dc57 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -287,8 +287,8 @@ void PathFollow2D::_validate_property(PropertyInfo &p_property) const { } } -PackedStringArray PathFollow2D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array PathFollow2D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (is_visible_in_tree() && is_inside_tree()) { if (!Object::cast_to<Path2D>(get_parent())) { diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h index bfd5cde5e9..616903c788 100644 --- a/scene/2d/path_2d.h +++ b/scene/2d/path_2d.h @@ -106,7 +106,7 @@ public: void set_cubic_interpolation_enabled(bool p_enabled); bool is_cubic_interpolation_enabled() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; PathFollow2D() {} }; diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp index 64cf56fa85..12a3e6da21 100644 --- a/scene/2d/physical_bone_2d.cpp +++ b/scene/2d/physical_bone_2d.cpp @@ -106,8 +106,8 @@ void PhysicalBone2D::_find_joint_child() { } } -PackedStringArray PhysicalBone2D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array PhysicalBone2D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (!parent_skeleton) { warnings.push_back(RTR("A PhysicalBone2D only works with a Skeleton2D or another PhysicalBone2D as a parent node!")); diff --git a/scene/2d/physical_bone_2d.h b/scene/2d/physical_bone_2d.h index e585f2c0ed..1930ce1aba 100644 --- a/scene/2d/physical_bone_2d.h +++ b/scene/2d/physical_bone_2d.h @@ -79,7 +79,7 @@ public: void set_follow_bone_when_simulating(bool p_follow); bool get_follow_bone_when_simulating() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; PhysicalBone2D(); ~PhysicalBone2D(); diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index af9008f452..7e3118330e 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -928,10 +928,10 @@ void RigidBody2D::_notification(int p_what) { #endif } -PackedStringArray RigidBody2D::get_configuration_warnings() const { +Array RigidBody2D::get_configuration_warnings() const { Transform2D t = get_transform(); - PackedStringArray warnings = CollisionObject2D::get_configuration_warnings(); + Array warnings = CollisionObject2D::get_configuration_warnings(); if (ABS(t.columns[0].length() - 1.0) > 0.05 || ABS(t.columns[1].length() - 1.0) > 0.05) { warnings.push_back(RTR("Size changes to RigidBody2D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 62636b02f4..4a7cbb0cae 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -312,7 +312,7 @@ public: TypedArray<Node2D> get_colliding_bodies() const; //function for script - virtual PackedStringArray get_configuration_warnings() const override; + virtual Array get_configuration_warnings() const override; RigidBody2D(); ~RigidBody2D(); diff --git a/scene/2d/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp index 5ea5098475..b3926a4638 100644 --- a/scene/2d/remote_transform_2d.cpp +++ b/scene/2d/remote_transform_2d.cpp @@ -200,8 +200,8 @@ void RemoteTransform2D::force_update_cache() { _update_cache(); } -PackedStringArray RemoteTransform2D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array RemoteTransform2D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (!has_node(remote_node) || !Object::cast_to<Node2D>(get_node(remote_node))) { warnings.push_back(RTR("Path property must point to a valid Node2D node to work.")); diff --git a/scene/2d/remote_transform_2d.h b/scene/2d/remote_transform_2d.h index 997fd8fc69..fe7289380a 100644 --- a/scene/2d/remote_transform_2d.h +++ b/scene/2d/remote_transform_2d.h @@ -70,7 +70,7 @@ public: void force_update_cache(); - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; RemoteTransform2D(); }; diff --git a/scene/2d/shape_cast_2d.cpp b/scene/2d/shape_cast_2d.cpp index 90d80d7549..332e5eeb3e 100644 --- a/scene/2d/shape_cast_2d.cpp +++ b/scene/2d/shape_cast_2d.cpp @@ -402,8 +402,8 @@ Array ShapeCast2D::_get_collision_result() const { return ret; } -PackedStringArray ShapeCast2D::get_configuration_warnings() const { - PackedStringArray warnings = Node2D::get_configuration_warnings(); +Array ShapeCast2D::get_configuration_warnings() const { + Array warnings = Node2D::get_configuration_warnings(); if (shape.is_null()) { warnings.push_back(RTR("This node cannot interact with other objects unless a Shape2D is assigned.")); diff --git a/scene/2d/shape_cast_2d.h b/scene/2d/shape_cast_2d.h index a577c351fd..da7c58bacc 100644 --- a/scene/2d/shape_cast_2d.h +++ b/scene/2d/shape_cast_2d.h @@ -118,7 +118,7 @@ public: void remove_exception(const CollisionObject2D *p_node); void clear_exceptions(); - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; ShapeCast2D(); }; diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 69e0414855..432b8e914e 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -415,8 +415,8 @@ int Bone2D::get_index_in_skeleton() const { return skeleton_index; } -PackedStringArray Bone2D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array Bone2D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (!skeleton) { if (parent_bone) { warnings.push_back(RTR("This Bone2D chain should end at a Skeleton2D node.")); diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h index 6a36a31552..1128ebb7c6 100644 --- a/scene/2d/skeleton_2d.h +++ b/scene/2d/skeleton_2d.h @@ -78,7 +78,7 @@ public: void apply_rest(); Transform2D get_skeleton_rest() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; void set_autocalculate_length_and_angle(bool p_autocalculate); bool get_autocalculate_length_and_angle() const; diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 24eefce99d..f40ee1f506 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -1103,8 +1103,8 @@ void TileMap::draw_cells_outline(Control *p_control, const RBSet<Vector2i> &p_ce #undef DRAW_SIDE_IF_NEEDED } -PackedStringArray TileMap::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array TileMap::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); // Retrieve the set of Z index values with a Y-sorted layer. RBSet<int> y_sorted_z_index; diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index f2b481cdd1..22efbf018f 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -232,7 +232,7 @@ public: GDVIRTUAL3(_tile_data_runtime_update, int, Vector2i, TileData *); // Configuration warnings. - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; TileMap(); ~TileMap(); diff --git a/scene/3d/audio_stream_player_3d.compat.inc b/scene/3d/audio_stream_player_3d.compat.inc new file mode 100644 index 0000000000..11f82b51f7 --- /dev/null +++ b/scene/3d/audio_stream_player_3d.compat.inc @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* audio_stream_player_3d.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +bool AudioStreamPlayer3D::_is_autoplay_enabled_bind_compat_86907() { + return is_autoplay_enabled(); +} + +void AudioStreamPlayer3D::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("is_autoplay_enabled"), &AudioStreamPlayer3D::_is_autoplay_enabled_bind_compat_86907); +} + +#endif // DISABLE_DEPRECATED diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 49ae59e2a4..1fe6cb718c 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "audio_stream_player_3d.h" +#include "audio_stream_player_3d.compat.inc" #include "core/config/project_settings.h" #include "scene/3d/area_3d.h" @@ -571,7 +572,7 @@ void AudioStreamPlayer3D::set_autoplay(bool p_enable) { internal->autoplay = p_enable; } -bool AudioStreamPlayer3D::is_autoplay_enabled() { +bool AudioStreamPlayer3D::is_autoplay_enabled() const { return internal->autoplay; } diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h index 3cc1efaf67..61005dc249 100644 --- a/scene/3d/audio_stream_player_3d.h +++ b/scene/3d/audio_stream_player_3d.h @@ -121,6 +121,11 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; +#ifndef DISABLE_DEPRECATED + bool _is_autoplay_enabled_bind_compat_86907(); + static void _bind_compatibility_methods(); +#endif // DISABLE_DEPRECATED + public: void set_stream(Ref<AudioStream> p_stream); Ref<AudioStream> get_stream() const; @@ -150,7 +155,7 @@ public: int get_max_polyphony() const; void set_autoplay(bool p_enable); - bool is_autoplay_enabled(); + bool is_autoplay_enabled() const; void set_max_distance(float p_metres); float get_max_distance() const; diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index 5683fb7306..3cab128be5 100644 --- a/scene/3d/bone_attachment_3d.cpp +++ b/scene/3d/bone_attachment_3d.cpp @@ -87,8 +87,8 @@ void BoneAttachment3D::_get_property_list(List<PropertyInfo> *p_list) const { } } -PackedStringArray BoneAttachment3D::get_configuration_warnings() const { - PackedStringArray warnings = Node3D::get_configuration_warnings(); +Array BoneAttachment3D::get_configuration_warnings() const { + Array warnings = Node3D::get_configuration_warnings(); if (use_external_skeleton) { if (external_skeleton_node_cache.is_null()) { diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h index 1bf44c2756..13fdccab3c 100644 --- a/scene/3d/bone_attachment_3d.h +++ b/scene/3d/bone_attachment_3d.h @@ -71,7 +71,8 @@ public: virtual void notify_skeleton_bones_renamed(Node *p_base_scene, Skeleton3D *p_skeleton, Dictionary p_rename_map); #endif // TOOLS_ENABLED - virtual PackedStringArray get_configuration_warnings() const override; +public: + virtual Array get_configuration_warnings() const override; void set_bone_name(const String &p_name); String get_bone_name() const; diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index 0cfe0f8cb7..32799cacfe 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -728,8 +728,8 @@ bool CollisionObject3D::get_capture_input_on_drag() const { return capture_input_on_drag; } -PackedStringArray CollisionObject3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array CollisionObject3D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (shapes.is_empty()) { warnings.push_back(RTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape3D or CollisionPolygon3D as a child to define its shape.")); diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h index b51423f021..dc3c9269e4 100644 --- a/scene/3d/collision_object_3d.h +++ b/scene/3d/collision_object_3d.h @@ -173,7 +173,7 @@ public: _FORCE_INLINE_ RID get_rid() const { return rid; } - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; CollisionObject3D(); ~CollisionObject3D(); diff --git a/scene/3d/collision_polygon_3d.cpp b/scene/3d/collision_polygon_3d.cpp index 9c1a7181aa..29cec43011 100644 --- a/scene/3d/collision_polygon_3d.cpp +++ b/scene/3d/collision_polygon_3d.cpp @@ -168,8 +168,8 @@ void CollisionPolygon3D::set_margin(real_t p_margin) { } } -PackedStringArray CollisionPolygon3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array CollisionPolygon3D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (!Object::cast_to<CollisionObject3D>(get_parent())) { warnings.push_back(RTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node.\nPlease only use it as a child of Area3D, StaticBody3D, RigidBody3D, CharacterBody3D, etc. to give them a shape.")); diff --git a/scene/3d/collision_polygon_3d.h b/scene/3d/collision_polygon_3d.h index 61290a7947..715ea721cf 100644 --- a/scene/3d/collision_polygon_3d.h +++ b/scene/3d/collision_polygon_3d.h @@ -74,7 +74,7 @@ public: real_t get_margin() const; void set_margin(real_t p_margin); - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; CollisionPolygon3D(); }; diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp index 61941a0e53..30692660f6 100644 --- a/scene/3d/collision_shape_3d.cpp +++ b/scene/3d/collision_shape_3d.cpp @@ -118,8 +118,8 @@ void CollisionShape3D::resource_changed(Ref<Resource> res) { } #endif -PackedStringArray CollisionShape3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array CollisionShape3D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); CollisionObject3D *col_object = Object::cast_to<CollisionObject3D>(get_parent()); if (col_object == nullptr) { diff --git a/scene/3d/collision_shape_3d.h b/scene/3d/collision_shape_3d.h index bc0e70f8ac..f66f88648e 100644 --- a/scene/3d/collision_shape_3d.h +++ b/scene/3d/collision_shape_3d.h @@ -64,7 +64,7 @@ public: void set_disabled(bool p_disabled); bool is_disabled() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; CollisionShape3D(); ~CollisionShape3D(); diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 3dc82cfb97..fa8599a0a2 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -193,8 +193,8 @@ bool CPUParticles3D::get_fractional_delta() const { return fractional_delta; } -PackedStringArray CPUParticles3D::get_configuration_warnings() const { - PackedStringArray warnings = GeometryInstance3D::get_configuration_warnings(); +Array CPUParticles3D::get_configuration_warnings() const { + Array warnings = GeometryInstance3D::get_configuration_warnings(); bool mesh_found = false; bool anim_material_found = false; diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h index a5bc7dddb9..e05bd65fba 100644 --- a/scene/3d/cpu_particles_3d.h +++ b/scene/3d/cpu_particles_3d.h @@ -302,7 +302,7 @@ public: void set_gravity(const Vector3 &p_gravity); Vector3 get_gravity() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; void restart(); diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp index 6878df21d8..acd52da8b0 100644 --- a/scene/3d/decal.cpp +++ b/scene/3d/decal.cpp @@ -162,8 +162,8 @@ void Decal::_validate_property(PropertyInfo &p_property) const { } } -PackedStringArray Decal::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array Decal::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") { warnings.push_back(RTR("Decals are only available when using the Forward+ or Mobile rendering backends.")); diff --git a/scene/3d/decal.h b/scene/3d/decal.h index 171b52815a..3bc6664afd 100644 --- a/scene/3d/decal.h +++ b/scene/3d/decal.h @@ -69,7 +69,7 @@ protected: #endif // DISABLE_DEPRECATED public: - virtual PackedStringArray get_configuration_warnings() const override; + virtual Array get_configuration_warnings() const override; void set_size(const Vector3 &p_size); Vector3 get_size() const; diff --git a/scene/3d/fog_volume.cpp b/scene/3d/fog_volume.cpp index 12ca1888c4..1c17a733e9 100644 --- a/scene/3d/fog_volume.cpp +++ b/scene/3d/fog_volume.cpp @@ -117,8 +117,8 @@ AABB FogVolume::get_aabb() const { return AABB(); } -PackedStringArray FogVolume::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array FogVolume::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); Ref<Environment> environment = get_viewport()->find_world_3d()->get_environment(); diff --git a/scene/3d/fog_volume.h b/scene/3d/fog_volume.h index f7e861e3d0..a185cdbd94 100644 --- a/scene/3d/fog_volume.h +++ b/scene/3d/fog_volume.h @@ -66,7 +66,7 @@ public: Ref<Material> get_material() const; virtual AABB get_aabb() const override; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; FogVolume(); ~FogVolume(); diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index 16813b9017..6670c2c5f6 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -296,8 +296,8 @@ bool GPUParticles3D::get_interpolate() const { return interpolate; } -PackedStringArray GPUParticles3D::get_configuration_warnings() const { - PackedStringArray warnings = GeometryInstance3D::get_configuration_warnings(); +Array GPUParticles3D::get_configuration_warnings() const { + Array warnings = GeometryInstance3D::get_configuration_warnings(); bool meshes_found = false; bool anim_material_found = false; diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h index 0c9f2c1378..1697749d9b 100644 --- a/scene/3d/gpu_particles_3d.h +++ b/scene/3d/gpu_particles_3d.h @@ -164,7 +164,7 @@ public: void set_draw_pass_mesh(int p_pass, const Ref<Mesh> &p_mesh); Ref<Mesh> get_draw_pass_mesh(int p_pass) const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; void set_sub_emitter(const NodePath &p_path); NodePath get_sub_emitter() const; diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index cbc75801b0..f5d5795a27 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -527,8 +527,8 @@ Ref<Image> GPUParticlesCollisionSDF3D::bake() { return ret; } -PackedStringArray GPUParticlesCollisionSDF3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array GPUParticlesCollisionSDF3D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (bake_mask == 0) { warnings.push_back(RTR("The Bake Mask has no bits enabled, which means baking will not produce any collision for this GPUParticlesCollisionSDF3D.\nTo resolve this, enable at least one bit in the Bake Mask property.")); diff --git a/scene/3d/gpu_particles_collision_3d.h b/scene/3d/gpu_particles_collision_3d.h index 1649320069..a4d8b15f9f 100644 --- a/scene/3d/gpu_particles_collision_3d.h +++ b/scene/3d/gpu_particles_collision_3d.h @@ -170,7 +170,7 @@ protected: #endif // DISABLE_DEPRECATED public: - virtual PackedStringArray get_configuration_warnings() const override; + virtual Array get_configuration_warnings() const override; void set_thickness(float p_thickness); float get_thickness() const; diff --git a/scene/3d/joint_3d.cpp b/scene/3d/joint_3d.cpp index 1e4e50182c..d76838d434 100644 --- a/scene/3d/joint_3d.cpp +++ b/scene/3d/joint_3d.cpp @@ -198,8 +198,8 @@ bool Joint3D::get_exclude_nodes_from_collision() const { return exclude_from_collision; } -PackedStringArray Joint3D::get_configuration_warnings() const { - PackedStringArray warnings = Node3D::get_configuration_warnings(); +Array Joint3D::get_configuration_warnings() const { + Array warnings = Node3D::get_configuration_warnings(); if (!warning.is_empty()) { warnings.push_back(warning); diff --git a/scene/3d/joint_3d.h b/scene/3d/joint_3d.h index 527aed4079..bf2519db12 100644 --- a/scene/3d/joint_3d.h +++ b/scene/3d/joint_3d.h @@ -63,7 +63,7 @@ protected: _FORCE_INLINE_ bool is_configured() const { return configured; } public: - virtual PackedStringArray get_configuration_warnings() const override; + virtual Array get_configuration_warnings() const override; void set_node_a(const NodePath &p_node_a); NodePath get_node_a() const; diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index 7b70986adc..bc74ff2c0f 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -169,8 +169,8 @@ AABB Light3D::get_aabb() const { return AABB(); } -PackedStringArray Light3D::get_configuration_warnings() const { - PackedStringArray warnings = VisualInstance3D::get_configuration_warnings(); +Array Light3D::get_configuration_warnings() const { + Array warnings = VisualInstance3D::get_configuration_warnings(); if (!get_scale().is_equal_approx(Vector3(1, 1, 1))) { warnings.push_back(RTR("A light's scale does not affect the visual size of the light.")); @@ -596,8 +596,8 @@ OmniLight3D::ShadowMode OmniLight3D::get_shadow_mode() const { return shadow_mode; } -PackedStringArray OmniLight3D::get_configuration_warnings() const { - PackedStringArray warnings = Light3D::get_configuration_warnings(); +Array OmniLight3D::get_configuration_warnings() const { + Array warnings = Light3D::get_configuration_warnings(); if (!has_shadow() && get_projector().is_valid()) { warnings.push_back(RTR("Projector texture only works with shadows active.")); @@ -628,8 +628,8 @@ OmniLight3D::OmniLight3D() : set_shadow_mode(SHADOW_CUBE); } -PackedStringArray SpotLight3D::get_configuration_warnings() const { - PackedStringArray warnings = Light3D::get_configuration_warnings(); +Array SpotLight3D::get_configuration_warnings() const { + Array warnings = Light3D::get_configuration_warnings(); if (has_shadow() && get_param(PARAM_SPOT_ANGLE) >= 90.0) { warnings.push_back(RTR("A SpotLight3D with an angle wider than 90 degrees cannot cast shadows.")); diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h index d6eca8d8b6..b242493c28 100644 --- a/scene/3d/light_3d.h +++ b/scene/3d/light_3d.h @@ -147,7 +147,7 @@ public: Color get_correlated_color() const; virtual AABB get_aabb() const override; - virtual PackedStringArray get_configuration_warnings() const override; + virtual Array get_configuration_warnings() const override; Light3D(); ~Light3D(); @@ -217,7 +217,7 @@ public: void set_shadow_mode(ShadowMode p_mode); ShadowMode get_shadow_mode() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; OmniLight3D(); }; @@ -231,7 +231,7 @@ protected: static void _bind_methods(); public: - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; SpotLight3D(); }; diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 86ff6d15dd..008a09f141 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -1528,8 +1528,8 @@ Ref<CameraAttributes> LightmapGI::get_camera_attributes() const { return camera_attributes; } -PackedStringArray LightmapGI::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array LightmapGI::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") { warnings.push_back(RTR("Lightmap can only be baked from a device that supports the RD backends. Lightmap baking may fail.")); diff --git a/scene/3d/lightmap_gi.h b/scene/3d/lightmap_gi.h index 765e4a731d..8d96c1e909 100644 --- a/scene/3d/lightmap_gi.h +++ b/scene/3d/lightmap_gi.h @@ -302,7 +302,7 @@ public: BakeError bake(Node *p_from_node, String p_image_data_path = "", Lightmapper::BakeStepFunc p_bake_step = nullptr, void *p_bake_userdata = nullptr); - virtual PackedStringArray get_configuration_warnings() const override; + virtual Array get_configuration_warnings() const override; LightmapGI(); }; diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index eb52d4540e..ff0869b4ea 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -690,8 +690,8 @@ void NavigationAgent3D::_avoidance_done(Vector3 p_new_velocity) { emit_signal(SNAME("velocity_computed"), safe_velocity); } -PackedStringArray NavigationAgent3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array NavigationAgent3D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (!Object::cast_to<Node3D>(get_parent())) { warnings.push_back(RTR("The NavigationAgent3D can be used only under a Node3D inheriting parent node.")); diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h index 4eaed83149..5f7b249b7f 100644 --- a/scene/3d/navigation_agent_3d.h +++ b/scene/3d/navigation_agent_3d.h @@ -221,7 +221,7 @@ public: void _avoidance_done(Vector3 p_new_velocity); - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; void set_avoidance_layers(uint32_t p_layers); uint32_t get_avoidance_layers() const; diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp index dc776ebea2..c535393273 100644 --- a/scene/3d/navigation_link_3d.cpp +++ b/scene/3d/navigation_link_3d.cpp @@ -458,8 +458,8 @@ void NavigationLink3D::set_travel_cost(real_t p_travel_cost) { NavigationServer3D::get_singleton()->link_set_travel_cost(link, travel_cost); } -PackedStringArray NavigationLink3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array NavigationLink3D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (start_position.is_equal_approx(end_position)) { warnings.push_back(RTR("NavigationLink3D start position should be different than the end position to be useful.")); diff --git a/scene/3d/navigation_link_3d.h b/scene/3d/navigation_link_3d.h index 1867082811..771d470711 100644 --- a/scene/3d/navigation_link_3d.h +++ b/scene/3d/navigation_link_3d.h @@ -99,7 +99,7 @@ public: void set_travel_cost(real_t p_travel_cost); real_t get_travel_cost() const { return travel_cost; } - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; }; #endif // NAVIGATION_LINK_3D_H diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 753bc0434b..de6c6e6f11 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -270,8 +270,8 @@ bool NavigationRegion3D::is_baking() const { return NavigationServer3D::get_singleton()->is_baking_navigation_mesh(navigation_mesh); } -PackedStringArray NavigationRegion3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array NavigationRegion3D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (is_visible_in_tree() && is_inside_tree()) { if (!navigation_mesh.is_valid()) { diff --git a/scene/3d/navigation_region_3d.h b/scene/3d/navigation_region_3d.h index ea0a0cd391..33140d4d71 100644 --- a/scene/3d/navigation_region_3d.h +++ b/scene/3d/navigation_region_3d.h @@ -107,7 +107,7 @@ public: void _bake_finished(Ref<NavigationMesh> p_navigation_mesh); bool is_baking() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; NavigationRegion3D(); ~NavigationRegion3D(); diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp index a76488e479..0f56bfad52 100644 --- a/scene/3d/occluder_instance_3d.cpp +++ b/scene/3d/occluder_instance_3d.cpp @@ -693,8 +693,8 @@ OccluderInstance3D::BakeError OccluderInstance3D::bake_scene(Node *p_from_node, return BAKE_ERROR_OK; } -PackedStringArray OccluderInstance3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array OccluderInstance3D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (!bool(GLOBAL_GET("rendering/occlusion_culling/use_occlusion_culling"))) { warnings.push_back(RTR("Occlusion culling is disabled in the Project Settings, which means occlusion culling won't be performed in the root viewport.\nTo resolve this, open the Project Settings and enable Rendering > Occlusion Culling > Use Occlusion Culling.")); diff --git a/scene/3d/occluder_instance_3d.h b/scene/3d/occluder_instance_3d.h index f607877e8f..cf20655c2c 100644 --- a/scene/3d/occluder_instance_3d.h +++ b/scene/3d/occluder_instance_3d.h @@ -181,7 +181,7 @@ protected: static void _bind_methods(); public: - virtual PackedStringArray get_configuration_warnings() const override; + virtual Array get_configuration_warnings() const override; enum BakeError { BAKE_ERROR_OK, diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index e38375d339..1bab02e9b4 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -291,8 +291,8 @@ void PathFollow3D::_validate_property(PropertyInfo &p_property) const { } } -PackedStringArray PathFollow3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array PathFollow3D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (is_visible_in_tree() && is_inside_tree()) { if (!Object::cast_to<Path3D>(get_parent())) { diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h index 6116e98054..fe44b73e14 100644 --- a/scene/3d/path_3d.h +++ b/scene/3d/path_3d.h @@ -122,7 +122,7 @@ public: void set_cubic_interpolation_enabled(bool p_enabled); bool is_cubic_interpolation_enabled() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; PathFollow3D() {} }; diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 7d08d767c7..d184aec2ab 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -995,8 +995,8 @@ TypedArray<Node3D> RigidBody3D::get_colliding_bodies() const { return ret; } -PackedStringArray RigidBody3D::get_configuration_warnings() const { - PackedStringArray warnings = CollisionObject3D::get_configuration_warnings(); +Array RigidBody3D::get_configuration_warnings() const { + Array warnings = CollisionObject3D::get_configuration_warnings(); Vector3 scale = get_transform().get_basis().get_scale(); if (ABS(scale.x - 1.0) > 0.05 || ABS(scale.y - 1.0) > 0.05 || ABS(scale.z - 1.0) > 0.05) { diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index e8373d5907..63fec2c585 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -328,7 +328,7 @@ public: void set_constant_torque(const Vector3 &p_torque); Vector3 get_constant_torque() const; - virtual PackedStringArray get_configuration_warnings() const override; + virtual Array get_configuration_warnings() const override; RigidBody3D(); ~RigidBody3D(); diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp index b4dd6d09be..89368a7309 100644 --- a/scene/3d/reflection_probe.cpp +++ b/scene/3d/reflection_probe.cpp @@ -190,8 +190,8 @@ AABB ReflectionProbe::get_aabb() const { return aabb; } -PackedStringArray ReflectionProbe::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array ReflectionProbe::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") { warnings.push_back(RTR("ReflectionProbes are not supported when using the GL Compatibility backend yet. Support will be added in a future release.")); diff --git a/scene/3d/reflection_probe.h b/scene/3d/reflection_probe.h index 425fbb5bc2..2561b2a922 100644 --- a/scene/3d/reflection_probe.h +++ b/scene/3d/reflection_probe.h @@ -122,7 +122,7 @@ public: virtual AABB get_aabb() const override; - virtual PackedStringArray get_configuration_warnings() const override; + virtual Array get_configuration_warnings() const override; ReflectionProbe(); ~ReflectionProbe(); diff --git a/scene/3d/remote_transform_3d.cpp b/scene/3d/remote_transform_3d.cpp index 8d6e717132..bb41015219 100644 --- a/scene/3d/remote_transform_3d.cpp +++ b/scene/3d/remote_transform_3d.cpp @@ -200,8 +200,8 @@ void RemoteTransform3D::force_update_cache() { _update_cache(); } -PackedStringArray RemoteTransform3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array RemoteTransform3D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (!has_node(remote_node) || !Object::cast_to<Node3D>(get_node(remote_node))) { warnings.push_back(RTR("The \"Remote Path\" property must point to a valid Node3D or Node3D-derived node to work.")); diff --git a/scene/3d/remote_transform_3d.h b/scene/3d/remote_transform_3d.h index 3841821dae..40b59a5a5e 100644 --- a/scene/3d/remote_transform_3d.h +++ b/scene/3d/remote_transform_3d.h @@ -70,7 +70,7 @@ public: void force_update_cache(); - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; RemoteTransform3D(); }; diff --git a/scene/3d/shape_cast_3d.cpp b/scene/3d/shape_cast_3d.cpp index 4d9eeada06..ba4ce834da 100644 --- a/scene/3d/shape_cast_3d.cpp +++ b/scene/3d/shape_cast_3d.cpp @@ -170,8 +170,8 @@ void ShapeCast3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_shape_custom_color"), "set_debug_shape_custom_color", "get_debug_shape_custom_color"); } -PackedStringArray ShapeCast3D::get_configuration_warnings() const { - PackedStringArray warnings = Node3D::get_configuration_warnings(); +Array ShapeCast3D::get_configuration_warnings() const { + Array warnings = Node3D::get_configuration_warnings(); if (shape.is_null()) { warnings.push_back(RTR("This node cannot interact with other objects unless a Shape3D is assigned.")); diff --git a/scene/3d/shape_cast_3d.h b/scene/3d/shape_cast_3d.h index 043e35090f..d690a17a78 100644 --- a/scene/3d/shape_cast_3d.h +++ b/scene/3d/shape_cast_3d.h @@ -140,7 +140,7 @@ public: void remove_exception(const CollisionObject3D *p_node); void clear_exceptions(); - virtual PackedStringArray get_configuration_warnings() const override; + virtual Array get_configuration_warnings() const override; }; #endif // SHAPE_CAST_3D_H diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index 0aaa806ee6..fb6a45846c 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -373,8 +373,8 @@ void SoftBody3D::_bind_methods() { BIND_ENUM_CONSTANT(DISABLE_MODE_KEEP_ACTIVE); } -PackedStringArray SoftBody3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array SoftBody3D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (mesh.is_null()) { warnings.push_back(RTR("This body will be ignored until you set a mesh.")); diff --git a/scene/3d/soft_body_3d.h b/scene/3d/soft_body_3d.h index ab30f7e654..b5d31d8440 100644 --- a/scene/3d/soft_body_3d.h +++ b/scene/3d/soft_body_3d.h @@ -126,7 +126,7 @@ protected: void _notification(int p_what); static void _bind_methods(); - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; public: RID get_physics_rid() const { return physics_rid; } diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 8e76e75d0e..234baac61a 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -1430,8 +1430,8 @@ StringName AnimatedSprite3D::get_animation() const { return animation; } -PackedStringArray AnimatedSprite3D::get_configuration_warnings() const { - PackedStringArray warnings = SpriteBase3D::get_configuration_warnings(); +Array AnimatedSprite3D::get_configuration_warnings() const { + Array warnings = SpriteBase3D::get_configuration_warnings(); if (frames.is_null()) { warnings.push_back(RTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames.")); } diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index cbc7192ddf..c83ed88b2d 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -285,7 +285,7 @@ public: virtual Rect2 get_item_rect() const override; - virtual PackedStringArray get_configuration_warnings() const override; + virtual Array get_configuration_warnings() const override; virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; AnimatedSprite3D(); diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp index c23032d3b9..b309205697 100644 --- a/scene/3d/vehicle_body_3d.cpp +++ b/scene/3d/vehicle_body_3d.cpp @@ -105,8 +105,8 @@ void VehicleWheel3D::_notification(int p_what) { } } -PackedStringArray VehicleWheel3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array VehicleWheel3D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (!Object::cast_to<VehicleBody3D>(get_parent())) { warnings.push_back(RTR("VehicleWheel3D serves to provide a wheel system to a VehicleBody3D. Please use it as a child of a VehicleBody3D.")); diff --git a/scene/3d/vehicle_body_3d.h b/scene/3d/vehicle_body_3d.h index ce913f152d..8127340acc 100644 --- a/scene/3d/vehicle_body_3d.h +++ b/scene/3d/vehicle_body_3d.h @@ -147,7 +147,7 @@ public: void set_steering(real_t p_steering); real_t get_steering() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; VehicleWheel3D(); }; diff --git a/scene/3d/visible_on_screen_notifier_3d.cpp b/scene/3d/visible_on_screen_notifier_3d.cpp index d1ad713343..23f866bd3b 100644 --- a/scene/3d/visible_on_screen_notifier_3d.cpp +++ b/scene/3d/visible_on_screen_notifier_3d.cpp @@ -79,8 +79,8 @@ void VisibleOnScreenNotifier3D::_notification(int p_what) { } } -PackedStringArray VisibleOnScreenNotifier3D::get_configuration_warnings() const { - PackedStringArray warnings = VisualInstance3D::get_configuration_warnings(); +Array VisibleOnScreenNotifier3D::get_configuration_warnings() const { + Array warnings = VisualInstance3D::get_configuration_warnings(); if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") { warnings.push_back(RTR("VisibleOnScreenNotifier3D nodes are not supported when using the GL Compatibility backend yet. Support will be added in a future release.")); diff --git a/scene/3d/visible_on_screen_notifier_3d.h b/scene/3d/visible_on_screen_notifier_3d.h index 85156c256e..10e41ceec6 100644 --- a/scene/3d/visible_on_screen_notifier_3d.h +++ b/scene/3d/visible_on_screen_notifier_3d.h @@ -57,7 +57,7 @@ public: virtual AABB get_aabb() const override; bool is_on_screen() const; - virtual PackedStringArray get_configuration_warnings() const override; + virtual Array get_configuration_warnings() const override; VisibleOnScreenNotifier3D(); ~VisibleOnScreenNotifier3D(); diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index 503c39ae3e..47a9a581b8 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -425,8 +425,8 @@ bool GeometryInstance3D::is_ignoring_occlusion_culling() { return ignore_occlusion_culling; } -PackedStringArray GeometryInstance3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array GeometryInstance3D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (!Math::is_zero_approx(visibility_range_end) && visibility_range_end <= visibility_range_begin) { warnings.push_back(RTR("The GeometryInstance3D visibility range's End distance is set to a non-zero value, but is lower than the Begin distance.\nThis means the GeometryInstance3D will never be visible.\nTo resolve this, set the End distance to 0 or to a value greater than the Begin distance.")); diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 59ede26ac1..c088e189ce 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -194,7 +194,7 @@ public: void set_ignore_occlusion_culling(bool p_enabled); bool is_ignoring_occlusion_culling(); - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; GeometryInstance3D(); virtual ~GeometryInstance3D(); }; diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index 8250083a9f..7c617e8bd2 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -518,8 +518,8 @@ AABB VoxelGI::get_aabb() const { return AABB(-size / 2, size); } -PackedStringArray VoxelGI::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array VoxelGI::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") { warnings.push_back(RTR("VoxelGI nodes are not supported when using the GL Compatibility backend yet. Support will be added in a future release.")); diff --git a/scene/3d/voxel_gi.h b/scene/3d/voxel_gi.h index 7d7787f721..316cb26b77 100644 --- a/scene/3d/voxel_gi.h +++ b/scene/3d/voxel_gi.h @@ -163,7 +163,7 @@ public: virtual AABB get_aabb() const override; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; VoxelGI(); ~VoxelGI(); diff --git a/scene/3d/world_environment.cpp b/scene/3d/world_environment.cpp index 4687c84734..8db310ce30 100644 --- a/scene/3d/world_environment.cpp +++ b/scene/3d/world_environment.cpp @@ -135,8 +135,8 @@ Ref<CameraAttributes> WorldEnvironment::get_camera_attributes() const { return camera_attributes; } -PackedStringArray WorldEnvironment::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array WorldEnvironment::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (!environment.is_valid() && !camera_attributes.is_valid()) { warnings.push_back(RTR("To have any visible effect, WorldEnvironment requires its \"Environment\" property to contain an Environment, its \"Camera Attributes\" property to contain a CameraAttributes resource, or both.")); diff --git a/scene/3d/world_environment.h b/scene/3d/world_environment.h index 2809d2550a..57d95f1f0e 100644 --- a/scene/3d/world_environment.h +++ b/scene/3d/world_environment.h @@ -55,7 +55,7 @@ public: void set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes); Ref<CameraAttributes> get_camera_attributes() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; WorldEnvironment(); }; diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index 70e32c1a31..79db9d5560 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -76,8 +76,8 @@ void XRCamera3D::_pose_changed(const Ref<XRPose> &p_pose) { } } -PackedStringArray XRCamera3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array XRCamera3D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (is_visible() && is_inside_tree()) { // must be child node of XROrigin3D! @@ -424,8 +424,8 @@ XRNode3D::~XRNode3D() { xr_server->disconnect("tracker_removed", callable_mp(this, &XRNode3D::_removed_tracker)); } -PackedStringArray XRNode3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array XRNode3D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (is_visible() && is_inside_tree()) { // must be child node of XROrigin! @@ -603,8 +603,8 @@ Plane XRAnchor3D::get_plane() const { Vector<XROrigin3D *> XROrigin3D::origin_nodes; -PackedStringArray XROrigin3D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array XROrigin3D::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (is_visible() && is_inside_tree()) { bool has_camera = false; diff --git a/scene/3d/xr_nodes.h b/scene/3d/xr_nodes.h index ad52cf113d..7f75e69fbc 100644 --- a/scene/3d/xr_nodes.h +++ b/scene/3d/xr_nodes.h @@ -55,7 +55,7 @@ protected: void _pose_changed(const Ref<XRPose> &p_pose); public: - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const override; virtual Point2 unproject_position(const Vector3 &p_pos) const override; @@ -109,7 +109,7 @@ public: Ref<XRPose> get_pose(); - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; XRNode3D(); ~XRNode3D(); @@ -193,7 +193,7 @@ protected: static void _bind_methods(); public: - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; real_t get_world_scale() const; void set_world_scale(real_t p_world_scale); diff --git a/scene/animation/animation_mixer.compat.inc b/scene/animation/animation_mixer.compat.inc index 09c7d7a977..f46e34bf98 100644 --- a/scene/animation/animation_mixer.compat.inc +++ b/scene/animation/animation_mixer.compat.inc @@ -38,7 +38,7 @@ Variant AnimationMixer::_post_process_key_value_bind_compat_86687(const Ref<Anim } void AnimationMixer::_bind_compatibility_methods() { - ClassDB::bind_compatibility_method(D_METHOD("_post_process_key_value_bind_compat", "animation", "track", "value", "object", "object_idx"), &AnimationMixer::_post_process_key_value_bind_compat_86687); + ClassDB::bind_compatibility_method(D_METHOD("_post_process_key_value", "animation", "track", "value", "object", "object_idx"), &AnimationMixer::_post_process_key_value_bind_compat_86687); } #endif // DISABLE_DEPRECATED diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 17fbc68973..b208d5af5b 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -606,8 +606,8 @@ uint64_t AnimationTree::get_last_process_pass() const { return process_pass; } -PackedStringArray AnimationTree::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array AnimationTree::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (!root_animation_node.is_valid()) { warnings.push_back(RTR("No root AnimationNode for the graph is set.")); } diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 87928e4d20..7d153b2736 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -261,7 +261,7 @@ public: void set_advance_expression_base_node(const NodePath &p_path); NodePath get_advance_expression_base_node() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; bool is_state_invalid() const; String get_invalid_state_reason() const; diff --git a/scene/audio/audio_stream_player.compat.inc b/scene/audio/audio_stream_player.compat.inc new file mode 100644 index 0000000000..b0f091ec08 --- /dev/null +++ b/scene/audio/audio_stream_player.compat.inc @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* audio_stream_player.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +bool AudioStreamPlayer::_is_autoplay_enabled_bind_compat_86907() { + return is_autoplay_enabled(); +} + +void AudioStreamPlayer::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("is_autoplay_enabled"), &AudioStreamPlayer::_is_autoplay_enabled_bind_compat_86907); +} + +#endif // DISABLE_DEPRECATED diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp index d7582526a3..dadcfab69f 100644 --- a/scene/audio/audio_stream_player.cpp +++ b/scene/audio/audio_stream_player.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "audio_stream_player.h" +#include "audio_stream_player.compat.inc" #include "scene/audio/audio_stream_player_internal.h" #include "servers/audio/audio_stream.h" @@ -126,7 +127,7 @@ void AudioStreamPlayer::set_autoplay(bool p_enable) { internal->autoplay = p_enable; } -bool AudioStreamPlayer::is_autoplay_enabled() { +bool AudioStreamPlayer::is_autoplay_enabled() const { return internal->autoplay; } diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h index 754e670553..ab3266f247 100644 --- a/scene/audio/audio_stream_player.h +++ b/scene/audio/audio_stream_player.h @@ -67,6 +67,11 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; +#ifndef DISABLE_DEPRECATED + bool _is_autoplay_enabled_bind_compat_86907(); + static void _bind_compatibility_methods(); +#endif // DISABLE_DEPRECATED + public: void set_stream(Ref<AudioStream> p_stream); Ref<AudioStream> get_stream() const; @@ -90,7 +95,7 @@ public: StringName get_bus() const; void set_autoplay(bool p_enable); - bool is_autoplay_enabled(); + bool is_autoplay_enabled() const; void set_mix_target(MixTarget p_target); MixTarget get_mix_target() const; diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 66b14dc967..49616f87cc 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -430,8 +430,8 @@ bool BaseButton::_was_pressed_by_mouse() const { return was_mouse_pressed; } -PackedStringArray BaseButton::get_configuration_warnings() const { - PackedStringArray warnings = Control::get_configuration_warnings(); +Array BaseButton::get_configuration_warnings() const { + Array warnings = Control::get_configuration_warnings(); if (get_button_group().is_valid() && !is_toggle_mode()) { warnings.push_back(RTR("ButtonGroup is intended to be used only with buttons that have toggle_mode set to true.")); diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h index a8d5cee44c..b5fbf11c8d 100644 --- a/scene/gui/base_button.h +++ b/scene/gui/base_button.h @@ -139,7 +139,7 @@ public: void set_button_group(const Ref<ButtonGroup> &p_group); Ref<ButtonGroup> get_button_group() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; BaseButton(); ~BaseButton(); diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp index c6e66c95c6..594ae3eee8 100644 --- a/scene/gui/container.cpp +++ b/scene/gui/container.cpp @@ -189,8 +189,8 @@ void Container::_notification(int p_what) { } } -PackedStringArray Container::get_configuration_warnings() const { - PackedStringArray warnings = Control::get_configuration_warnings(); +Array Container::get_configuration_warnings() const { + Array warnings = Control::get_configuration_warnings(); if (get_class() == "Container" && get_script().is_null()) { warnings.push_back(RTR("Container by itself serves no purpose unless a script configures its children placement behavior.\nIf you don't intend to add a script, use a plain Control node instead.")); diff --git a/scene/gui/container.h b/scene/gui/container.h index 94c3c540d7..d04a71fe23 100644 --- a/scene/gui/container.h +++ b/scene/gui/container.h @@ -63,7 +63,7 @@ public: virtual Vector<int> get_allowed_size_flags_horizontal() const; virtual Vector<int> get_allowed_size_flags_vertical() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; Container(); }; diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index fcdbdb4edb..7bbc0a5ec5 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -228,9 +228,9 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List } } -PackedStringArray Control::get_configuration_warnings() const { - ERR_READ_THREAD_GUARD_V(PackedStringArray()); - PackedStringArray warnings = Node::get_configuration_warnings(); +Array Control::get_configuration_warnings() const { + ERR_READ_THREAD_GUARD_V(Array()); + Array warnings = Node::get_configuration_warnings(); if (data.mouse_filter == MOUSE_FILTER_IGNORE && !data.tooltip.is_empty()) { warnings.push_back(RTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\".")); diff --git a/scene/gui/control.h b/scene/gui/control.h index aec61dc2fe..e0bd624edf 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -408,7 +408,7 @@ public: static void set_root_layout_direction(int p_root_dir); virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; virtual bool is_text_field() const; diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index c23d21775f..7ea3aa344a 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -251,8 +251,8 @@ Control::CursorShape GraphEdit::get_cursor_shape(const Point2 &p_pos) const { return Control::get_cursor_shape(p_pos); } -PackedStringArray GraphEdit::get_configuration_warnings() const { - PackedStringArray warnings = Control::get_configuration_warnings(); +Array GraphEdit::get_configuration_warnings() const { + Array warnings = Control::get_configuration_warnings(); warnings.push_back(RTR("Please be aware that GraphEdit and GraphNode will undergo extensive refactoring in a future 4.x version involving compatibility-breaking API changes.")); diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index e24f039e84..dd99651d60 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -375,7 +375,7 @@ public: virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; Error connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); bool is_node_connected(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 6518472757..44f2ab5880 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -35,6 +35,8 @@ #include "core/string/translation.h" #include "scene/theme/theme_db.h" +PropertyListHelper ItemList::base_property_helper; + void ItemList::_shape_text(int p_idx) { Item &item = items.write[p_idx]; @@ -1678,23 +1680,10 @@ TextServer::OverrunBehavior ItemList::get_text_overrun_behavior() const { } bool ItemList::_set(const StringName &p_name, const Variant &p_value) { - Vector<String> components = String(p_name).split("/", true, 2); - if (components.size() >= 2 && components[0].begins_with("item_") && components[0].trim_prefix("item_").is_valid_int()) { - int item_index = components[0].trim_prefix("item_").to_int(); - if (components[1] == "text") { - set_item_text(item_index, p_value); - return true; - } else if (components[1] == "icon") { - set_item_icon(item_index, p_value); - return true; - } else if (components[1] == "disabled") { - set_item_disabled(item_index, p_value); - return true; - } else if (components[1] == "selectable") { - set_item_selectable(item_index, p_value); - return true; - } + if (property_helper.property_set_value(p_name, p_value)) { + return true; } + #ifndef DISABLE_DEPRECATED // Compatibility. if (p_name == "items") { @@ -1717,42 +1706,19 @@ bool ItemList::_set(const StringName &p_name, const Variant &p_value) { } bool ItemList::_get(const StringName &p_name, Variant &r_ret) const { - Vector<String> components = String(p_name).split("/", true, 2); - if (components.size() >= 2 && components[0].begins_with("item_") && components[0].trim_prefix("item_").is_valid_int()) { - int item_index = components[0].trim_prefix("item_").to_int(); - if (components[1] == "text") { - r_ret = get_item_text(item_index); - return true; - } else if (components[1] == "icon") { - r_ret = get_item_icon(item_index); - return true; - } else if (components[1] == "disabled") { - r_ret = is_item_disabled(item_index); - return true; - } else if (components[1] == "selectable") { - r_ret = is_item_selectable(item_index); - return true; - } - } - return false; + return property_helper.property_get_value(p_name, r_ret); } void ItemList::_get_property_list(List<PropertyInfo> *p_list) const { - for (int i = 0; i < items.size(); i++) { - p_list->push_back(PropertyInfo(Variant::STRING, vformat("item_%d/text", i))); - - PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("item_%d/icon", i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"); - pi.usage &= ~(get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0); - p_list->push_back(pi); + property_helper.get_property_list(p_list, items.size()); +} - pi = PropertyInfo(Variant::BOOL, vformat("item_%d/selectable", i)); - pi.usage &= ~(is_item_selectable(i) ? PROPERTY_USAGE_STORAGE : 0); - p_list->push_back(pi); +bool ItemList::_property_can_revert(const StringName &p_name) const { + return property_helper.property_can_revert(p_name); +} - pi = PropertyInfo(Variant::BOOL, vformat("item_%d/disabled", i)); - pi.usage &= ~(!is_item_disabled(i) ? PROPERTY_USAGE_STORAGE : 0); - p_list->push_back(pi); - } +bool ItemList::_property_get_revert(const StringName &p_name, Variant &r_property) const { + return property_helper.property_get_revert(p_name, r_property); } void ItemList::_bind_methods() { @@ -1919,6 +1885,14 @@ void ItemList::_bind_methods() { BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ItemList, cursor_style, "cursor_unfocused"); BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ItemList, cursor_focus_style, "cursor"); BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, ItemList, guide_color); + + Item defaults(true); + + base_property_helper.set_prefix("item_"); + base_property_helper.register_property(PropertyInfo(Variant::STRING, "text"), defaults.text, "set_item_text", "get_item_text"); + base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), defaults.icon, "set_item_icon", "get_item_icon"); + base_property_helper.register_property(PropertyInfo(Variant::BOOL, "selectable"), defaults.selectable, "set_item_selectable", "is_item_selectable"); + base_property_helper.register_property(PropertyInfo(Variant::BOOL, "disabled"), defaults.disabled, "set_item_disabled", "is_item_disabled"); } ItemList::ItemList() { @@ -1930,6 +1904,8 @@ ItemList::ItemList() { set_focus_mode(FOCUS_ALL); set_clip_contents(true); + + property_helper.setup_for_instance(base_property_helper, this); } ItemList::~ItemList() { diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h index 28f9012058..571f5b77e6 100644 --- a/scene/gui/item_list.h +++ b/scene/gui/item_list.h @@ -33,6 +33,7 @@ #include "scene/gui/control.h" #include "scene/gui/scroll_bar.h" +#include "scene/property_list_helper.h" #include "scene/resources/text_paragraph.h" class ItemList : public Control { @@ -82,8 +83,13 @@ private: Item() { text_buf.instantiate(); } + + Item(bool p_dummy) {} }; + static PropertyListHelper base_property_helper; + PropertyListHelper property_helper; + int current = -1; int hovered = -1; @@ -157,6 +163,8 @@ protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; + bool _property_can_revert(const StringName &p_name) const; + bool _property_get_revert(const StringName &p_name, Variant &r_property) const; static void _bind_methods(); public: diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 56da1332e7..f847971015 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -330,8 +330,8 @@ inline void draw_glyph_outline(const Glyph &p_gl, const RID &p_canvas, const Col } } -PackedStringArray Label::get_configuration_warnings() const { - PackedStringArray warnings = Control::get_configuration_warnings(); +Array Label::get_configuration_warnings() const { + Array warnings = Control::get_configuration_warnings(); // FIXME: This is not ideal and the sizing model should be fixed, // but for now we have to warn about this impossible to resolve combination. diff --git a/scene/gui/label.h b/scene/gui/label.h index 4bd0e53605..fe90bd06bb 100644 --- a/scene/gui/label.h +++ b/scene/gui/label.h @@ -97,7 +97,7 @@ protected: public: virtual Size2 get_minimum_size() const override; - virtual PackedStringArray get_configuration_warnings() const override; + virtual Array get_configuration_warnings() const override; void set_horizontal_alignment(HorizontalAlignment p_alignment); HorizontalAlignment get_horizontal_alignment() const; diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index a6109a3925..beafe7ec7a 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -2278,8 +2278,8 @@ void LineEdit::_emit_text_change() { emit_signal(SNAME("text_changed"), text); text_changed_dirty = false; } -PackedStringArray LineEdit::get_configuration_warnings() const { - PackedStringArray warnings = Control::get_configuration_warnings(); +Array LineEdit::get_configuration_warnings() const { + Array warnings = Control::get_configuration_warnings(); if (secret_character.length() > 1) { warnings.push_back("Secret Character property supports only one character. Extra characters will be ignored."); } diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 993bc727e4..b2582e0eb5 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -385,7 +385,7 @@ public: virtual bool is_text_field() const override; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; void show_virtual_keyboard(); diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp index 236dfcc864..da40c7f3e2 100644 --- a/scene/gui/range.cpp +++ b/scene/gui/range.cpp @@ -30,8 +30,8 @@ #include "range.h" -PackedStringArray Range::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array Range::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (shared->exp_ratio && shared->min <= 0) { warnings.push_back(RTR("If \"Exp Edit\" is enabled, \"Min Value\" must be greater than 0.")); diff --git a/scene/gui/range.h b/scene/gui/range.h index b1c2446ded..e08a77f0f7 100644 --- a/scene/gui/range.h +++ b/scene/gui/range.h @@ -103,7 +103,7 @@ public: void share(Range *p_range); void unshare(); - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; Range(); ~Range(); diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 89d308de3f..fd3a1f4a45 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -537,8 +537,8 @@ void ScrollContainer::set_follow_focus(bool p_follow) { follow_focus = p_follow; } -PackedStringArray ScrollContainer::get_configuration_warnings() const { - PackedStringArray warnings = Container::get_configuration_warnings(); +Array ScrollContainer::get_configuration_warnings() const { + Array warnings = Container::get_configuration_warnings(); int found = 0; diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h index 02146618cd..69ff7ccc14 100644 --- a/scene/gui/scroll_container.h +++ b/scene/gui/scroll_container.h @@ -119,7 +119,7 @@ public: VScrollBar *get_v_scroll_bar(); void ensure_control_visible(Control *p_control); - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; ScrollContainer(); }; diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp index 0d33774e20..4ed136005a 100644 --- a/scene/gui/subviewport_container.cpp +++ b/scene/gui/subviewport_container.cpp @@ -259,8 +259,8 @@ void SubViewportContainer::remove_child_notify(Node *p_child) { } } -PackedStringArray SubViewportContainer::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array SubViewportContainer::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); bool has_viewport = false; for (int i = 0; i < get_child_count(); i++) { diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h index 06420de730..e172f2f040 100644 --- a/scene/gui/subviewport_container.h +++ b/scene/gui/subviewport_container.h @@ -68,7 +68,7 @@ public: virtual Vector<int> get_allowed_size_flags_horizontal() const override; virtual Vector<int> get_allowed_size_flags_vertical() const override; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; SubViewportContainer(); }; diff --git a/scene/main/missing_node.cpp b/scene/main/missing_node.cpp index ccb6ac3571..49e565a39c 100644 --- a/scene/main/missing_node.cpp +++ b/scene/main/missing_node.cpp @@ -74,9 +74,9 @@ bool MissingNode::is_recording_properties() const { return recording_properties; } -PackedStringArray MissingNode::get_configuration_warnings() const { +Array MissingNode::get_configuration_warnings() const { // The mere existence of this node is warning. - PackedStringArray ret; + Array ret; ret.push_back(vformat(RTR("This node was saved as class type '%s', which was no longer available when this scene was loaded."), original_class)); ret.push_back(RTR("Data from the original node is kept as a placeholder until this type of node is available again. It can hence be safely re-saved without risk of data loss.")); return ret; diff --git a/scene/main/missing_node.h b/scene/main/missing_node.h index 7fa2c99c96..a56469b8ac 100644 --- a/scene/main/missing_node.h +++ b/scene/main/missing_node.h @@ -55,7 +55,7 @@ public: void set_recording_properties(bool p_enable); bool is_recording_properties() const; - virtual PackedStringArray get_configuration_warnings() const override; + virtual Array get_configuration_warnings() const override; MissingNode(); }; diff --git a/scene/main/node.compat.inc b/scene/main/node.compat.inc new file mode 100644 index 0000000000..7e957e5a14 --- /dev/null +++ b/scene/main/node.compat.inc @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* node.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +PackedStringArray Node::get_configuration_warnings_bind_compat_68420() const { + return PackedStringArray(get_configuration_warnings()); +} + +void Node::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("get_configuration_warnings"), &Node::get_configuration_warnings_bind_compat_68420); +} + +#endif // DISABLE_DEPRECATED diff --git a/scene/main/node.cpp b/scene/main/node.cpp index b78dfb2f2c..dfbc940660 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2549,6 +2549,11 @@ Node *Node::_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap) c for (List<const Node *>::Element *N = node_tree.front(); N; N = N->next()) { for (int i = 0; i < N->get()->get_child_count(); ++i) { Node *descendant = N->get()->get_child(i); + + if (!descendant->get_owner()) { + continue; // Internal nodes or nodes added by scripts. + } + // Skip nodes not really belonging to the instantiated hierarchy; they'll be processed normally later // but remember non-instantiated nodes that are hidden below instantiated ones if (!instance_roots.has(descendant->get_owner())) { @@ -3130,18 +3135,93 @@ void Node::clear_internal_tree_resource_paths() { } } -PackedStringArray Node::get_configuration_warnings() const { - ERR_THREAD_GUARD_V(PackedStringArray()); - PackedStringArray ret; +Array Node::get_configuration_warnings() const { + ERR_THREAD_GUARD_V(Array()); + Array warnings; + GDVIRTUAL_CALL(_get_configuration_warnings, warnings); + return warnings; +} - Vector<String> warnings; - if (GDVIRTUAL_CALL(_get_configuration_warnings, warnings)) { - ret.append_array(warnings); +Dictionary Node::configuration_warning_to_dict(const Variant &p_warning) const { + switch (p_warning.get_type()) { + case Variant::Type::DICTIONARY: + return p_warning; + case Variant::Type::STRING: { + // Convert string to dictionary. + Dictionary warning; + warning["message"] = p_warning; + return warning; + } + default: { + ERR_FAIL_V_MSG(Dictionary(), "Node::get_configuration_warnings returned a value which is neither a string nor a dictionary, but a " + Variant::get_type_name(p_warning.get_type())); + } } +} +Vector<Dictionary> Node::get_configuration_warnings_as_dicts() const { + Vector<Dictionary> ret; + Array mixed = get_configuration_warnings(); + for (int i = 0; i < mixed.size(); i++) { + ret.append(configuration_warning_to_dict(mixed[i])); + } return ret; } +Vector<Dictionary> Node::get_configuration_warnings_of_property(const String &p_property) const { + Vector<Dictionary> ret; + Vector<Dictionary> warnings = get_configuration_warnings_as_dicts(); + if (p_property.is_empty()) { + ret.append_array(warnings); + } else { + // Filter by property path. + for (int i = 0; i < warnings.size(); i++) { + Dictionary warning = warnings[i]; + String warning_property = warning.get("property", String()); + if (p_property == warning_property) { + ret.append(warning); + } + } + } + return ret; +} + +PackedStringArray Node::get_configuration_warnings_as_strings(bool p_wrap_lines, const String &p_property) const { + Vector<Dictionary> warnings = get_configuration_warnings_of_property(p_property); + + const String bullet_point = U"• "; + PackedStringArray all_warnings; + for (const Dictionary &warning : warnings) { + if (!warning.has("message")) { + continue; + } + + // Prefix with property name if we are showing all warnings. + String text; + if (warning.has("property") && p_property.is_empty()) { + text = bullet_point + vformat("[%s] %s", warning["property"], warning["message"]); + } else { + text = bullet_point + static_cast<String>(warning["message"]); + } + + if (p_wrap_lines) { + // Limit the line width while keeping some padding. + // It is not efficient, but it does not have to be. + const PackedInt32Array boundaries = TS->string_get_word_breaks(text, "", 80); + PackedStringArray lines; + for (int i = 0; i < boundaries.size(); i += 2) { + const int start = boundaries[i]; + const int end = boundaries[i + 1]; + String line = text.substr(start, end - start); + lines.append(line); + } + text = String("\n").join(lines); + } + text = text.replace("\n", "\n "); + all_warnings.append(text); + } + return all_warnings; +} + void Node::update_configuration_warnings() { ERR_THREAD_GUARD #ifdef TOOLS_ENABLED @@ -3611,6 +3691,16 @@ String Node::_get_name_num_separator() { return " "; } +StringName Node::get_configuration_warning_icon(int p_count) { + if (p_count == 1) { + return SNAME("NodeWarning"); + } else if (p_count <= 3) { + return vformat("NodeWarnings%d", p_count); + } else { + return SNAME("NodeWarnings4Plus"); + } +} + Node::Node() { orphan_node_count++; } diff --git a/scene/main/node.h b/scene/main/node.h index 48eae3bbfa..5d246cc18e 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -294,6 +294,7 @@ protected: static void _bind_methods(); static String _get_name_num_separator(); + static StringName get_configuration_warning_icon(int p_count); friend class SceneState; @@ -320,7 +321,7 @@ protected: GDVIRTUAL0(_enter_tree) GDVIRTUAL0(_exit_tree) GDVIRTUAL0(_ready) - GDVIRTUAL0RC(Vector<String>, _get_configuration_warnings) + GDVIRTUAL0RC(Array, _get_configuration_warnings) GDVIRTUAL1(_input, Ref<InputEvent>) GDVIRTUAL1(_shortcut_input, Ref<InputEvent>) @@ -636,7 +637,11 @@ public: _FORCE_INLINE_ Viewport *get_viewport() const { return data.viewport; } - virtual PackedStringArray get_configuration_warnings() const; + virtual Array get_configuration_warnings() const; + Dictionary configuration_warning_to_dict(const Variant &p_warning) const; + Vector<Dictionary> get_configuration_warnings_as_dicts() const; + Vector<Dictionary> get_configuration_warnings_of_property(const String &p_property = String()) const; + PackedStringArray get_configuration_warnings_as_strings(bool p_wrap_lines, const String &p_property = String()) const; void update_configuration_warnings(); diff --git a/scene/main/shader_globals_override.cpp b/scene/main/shader_globals_override.cpp index e3c26c30e2..b3d3659c5b 100644 --- a/scene/main/shader_globals_override.cpp +++ b/scene/main/shader_globals_override.cpp @@ -271,8 +271,8 @@ void ShaderGlobalsOverride::_notification(int p_what) { } } -PackedStringArray ShaderGlobalsOverride::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array ShaderGlobalsOverride::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (!active) { warnings.push_back(RTR("ShaderGlobalsOverride is not active because another node of the same type is in the scene.")); diff --git a/scene/main/shader_globals_override.h b/scene/main/shader_globals_override.h index d8557ecf6a..72226c0cb3 100644 --- a/scene/main/shader_globals_override.h +++ b/scene/main/shader_globals_override.h @@ -58,7 +58,7 @@ protected: static void _bind_methods(); public: - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; ShaderGlobalsOverride(); }; diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp index 0f4f18b495..acb788f76f 100644 --- a/scene/main/timer.cpp +++ b/scene/main/timer.cpp @@ -180,8 +180,8 @@ void Timer::_set_process(bool p_process, bool p_force) { processing = p_process; } -PackedStringArray Timer::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); +Array Timer::get_configuration_warnings() const { + Array warnings = Node::get_configuration_warnings(); if (wait_time < 0.05 - CMP_EPSILON) { warnings.push_back(RTR("Very low timer wait times (< 0.05 seconds) may behave in significantly different ways depending on the rendered or physics frame rate.\nConsider using a script's process loop instead of relying on a Timer for very low wait times.")); diff --git a/scene/main/timer.h b/scene/main/timer.h index d16e49793d..add61ef5f4 100644 --- a/scene/main/timer.h +++ b/scene/main/timer.h @@ -73,7 +73,7 @@ public: double get_time_left() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; void set_timer_process_callback(TimerProcessCallback p_callback); TimerProcessCallback get_timer_process_callback() const; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 22418641c0..a2c889dbf7 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -3500,9 +3500,9 @@ Variant Viewport::gui_get_drag_data() const { return gui.drag_data; } -PackedStringArray Viewport::get_configuration_warnings() const { - ERR_MAIN_THREAD_GUARD_V(PackedStringArray()); - PackedStringArray warnings = Node::get_configuration_warnings(); +Array Viewport::get_configuration_warnings() const { + ERR_MAIN_THREAD_GUARD_V(Array()); + Array warnings = Node::get_configuration_warnings(); if (size.x <= 1 || size.y <= 1) { warnings.push_back(RTR("The Viewport size must be greater than or equal to 2 pixels on both dimensions to render anything.")); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 4ac9f18416..6c0ddc0406 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -611,7 +611,7 @@ public: Control *gui_get_focus_owner() const; Control *gui_get_hovered_control() const; - PackedStringArray get_configuration_warnings() const override; + Array get_configuration_warnings() const override; void set_debug_draw(DebugDraw p_debug_draw); DebugDraw get_debug_draw() const; diff --git a/scene/property_list_helper.cpp b/scene/property_list_helper.cpp new file mode 100644 index 0000000000..596a6a050a --- /dev/null +++ b/scene/property_list_helper.cpp @@ -0,0 +1,138 @@ +/**************************************************************************/ +/* property_list_helper.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 "property_list_helper.h" + +const PropertyListHelper::Property *PropertyListHelper::_get_property(const String &p_property, int *r_index) const { + const Vector<String> components = p_property.split("/", true, 2); + if (components.size() < 2 || !components[0].begins_with(prefix)) { + return nullptr; + } + + { + const String index_string = components[0].trim_prefix(prefix); + if (!index_string.is_valid_int()) { + return nullptr; + } + *r_index = index_string.to_int(); + } + + return property_list.getptr(components[1]); +} + +void PropertyListHelper::_bind_property(const Property &p_property, const Object *p_object) { + Property property = p_property; + property.info = p_property.info; + property.default_value = p_property.default_value; + property.setter = Callable(p_object, p_property.setter_name); + property.getter = Callable(p_object, p_property.getter_name); + + property_list[property.info.name] = property; +} + +void PropertyListHelper::set_prefix(const String &p_prefix) { + prefix = p_prefix; +} + +void PropertyListHelper::register_property(const PropertyInfo &p_info, const Variant &p_default, const StringName &p_setter, const StringName &p_getter) { + Property property; + property.info = p_info; + property.default_value = p_default; + property.setter_name = p_setter; + property.getter_name = p_getter; + + property_list[p_info.name] = property; +} + +void PropertyListHelper::setup_for_instance(const PropertyListHelper &p_base, const Object *p_object) { + prefix = p_base.prefix; + for (const KeyValue<String, Property> &E : p_base.property_list) { + _bind_property(E.value, p_object); + } +} + +void PropertyListHelper::get_property_list(List<PropertyInfo> *p_list, int p_count) const { + for (int i = 0; i < p_count; i++) { + for (const KeyValue<String, Property> &E : property_list) { + const Property &property = E.value; + + PropertyInfo info = property.info; + if (property.getter.call(i) == property.default_value) { + info.usage &= (~PROPERTY_USAGE_STORAGE); + } + + info.name = vformat("%s%d/%s", prefix, i, info.name); + p_list->push_back(info); + } + } +} + +bool PropertyListHelper::property_get_value(const String &p_property, Variant &r_ret) const { + int index; + const Property *property = _get_property(p_property, &index); + + if (property) { + r_ret = property->getter.call(index); + return true; + } + return false; +} + +bool PropertyListHelper::property_set_value(const String &p_property, const Variant &p_value) const { + int index; + const Property *property = _get_property(p_property, &index); + + if (property) { + property->setter.call(index, p_value); + return true; + } + return false; +} + +bool PropertyListHelper::property_can_revert(const String &p_property) const { + int index; + const Property *property = _get_property(p_property, &index); + + if (property) { + return property->getter.call(index) != property->default_value; + } + return false; +} + +bool PropertyListHelper::property_get_revert(const String &p_property, Variant &r_value) const { + int index; + const Property *property = _get_property(p_property, &index); + + if (property) { + r_value = property->default_value; + return true; + } + return false; +} diff --git a/scene/property_list_helper.h b/scene/property_list_helper.h new file mode 100644 index 0000000000..f3cf0239a1 --- /dev/null +++ b/scene/property_list_helper.h @@ -0,0 +1,64 @@ +/**************************************************************************/ +/* property_list_helper.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 PROPERTY_LIST_HELPER_H +#define PROPERTY_LIST_HELPER_H + +#include "core/object/object.h" + +class PropertyListHelper { + struct Property { + PropertyInfo info; + Variant default_value; + StringName setter_name; + StringName getter_name; + Callable setter; + Callable getter; + }; + + String prefix; + HashMap<String, Property> property_list; + + const Property *_get_property(const String &p_property, int *r_index) const; + void _bind_property(const Property &p_property, const Object *p_object); + +public: + void set_prefix(const String &p_prefix); + void register_property(const PropertyInfo &p_info, const Variant &p_default, const StringName &p_setter, const StringName &p_getter); + void setup_for_instance(const PropertyListHelper &p_base, const Object *p_object); + + void get_property_list(List<PropertyInfo> *p_list, int p_count) const; + bool property_get_value(const String &p_property, Variant &r_ret) const; + bool property_set_value(const String &p_property, const Variant &p_value) const; + bool property_can_revert(const String &p_property) const; + bool property_get_revert(const String &p_property, Variant &r_value) const; +}; + +#endif // PROPERTY_LIST_HELPER_H diff --git a/servers/rendering/shader_compiler.h b/servers/rendering/shader_compiler.h index 2d58be7261..e0ec42a4d1 100644 --- a/servers/rendering/shader_compiler.h +++ b/servers/rendering/shader_compiler.h @@ -52,38 +52,38 @@ public: HashMap<StringName, bool *> usage_flag_pointers; HashMap<StringName, bool *> write_flag_pointers; - HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> *uniforms; + HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> *uniforms = nullptr; }; struct GeneratedCode { Vector<String> defines; struct Texture { StringName name; - ShaderLanguage::DataType type; - ShaderLanguage::ShaderNode::Uniform::Hint hint; + ShaderLanguage::DataType type = ShaderLanguage::DataType::TYPE_VOID; + ShaderLanguage::ShaderNode::Uniform::Hint hint = ShaderLanguage::ShaderNode::Uniform::Hint::HINT_NONE; bool use_color = false; - ShaderLanguage::TextureFilter filter; - ShaderLanguage::TextureRepeat repeat; - bool global; - int array_size; + ShaderLanguage::TextureFilter filter = ShaderLanguage::TextureFilter::FILTER_DEFAULT; + ShaderLanguage::TextureRepeat repeat = ShaderLanguage::TextureRepeat::REPEAT_DEFAULT; + bool global = false; + int array_size = 0; }; Vector<Texture> texture_uniforms; Vector<uint32_t> uniform_offsets; - uint32_t uniform_total_size; + uint32_t uniform_total_size = 0; String uniforms; String stage_globals[STAGE_MAX]; HashMap<String, String> code; - bool uses_global_textures; - bool uses_fragment_time; - bool uses_vertex_time; - bool uses_screen_texture_mipmaps; - bool uses_screen_texture; - bool uses_depth_texture; - bool uses_normal_roughness_texture; + bool uses_global_textures = false; + bool uses_fragment_time = false; + bool uses_vertex_time = false; + bool uses_screen_texture_mipmaps = false; + bool uses_screen_texture = false; + bool uses_depth_texture = false; + bool uses_normal_roughness_texture = false; }; struct DefaultIdentifierActions { @@ -91,8 +91,8 @@ public: HashMap<StringName, String> render_mode_defines; HashMap<StringName, String> usage_defines; HashMap<StringName, String> custom_samplers; - ShaderLanguage::TextureFilter default_filter; - ShaderLanguage::TextureRepeat default_repeat; + ShaderLanguage::TextureFilter default_filter = ShaderLanguage::TextureFilter::FILTER_DEFAULT; + ShaderLanguage::TextureRepeat default_repeat = ShaderLanguage::TextureRepeat::REPEAT_DEFAULT; int base_texture_binding_index = 0; int texture_layout_set = 0; String base_uniform_string; |