summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.yml3
-rw-r--r--core/object/script_language.cpp12
-rw-r--r--core/object/script_language.h1
-rw-r--r--doc/classes/EditorProperty.xml3
-rw-r--r--doc/classes/Node.xml6
-rw-r--r--doc/classes/StringName.xml2
-rw-r--r--editor/editor_help.cpp47
-rw-r--r--editor/editor_inspector.cpp96
-rw-r--r--editor/editor_inspector.h10
-rw-r--r--editor/editor_resource_preview.cpp10
-rw-r--r--editor/export/editor_export.cpp2
-rw-r--r--editor/export/editor_export_preset.cpp9
-rw-r--r--editor/export/editor_export_preset.h10
-rw-r--r--editor/export/export_template_manager.cpp8
-rw-r--r--editor/export/project_export.cpp32
-rw-r--r--editor/export/project_export.h4
-rw-r--r--editor/gui/scene_tree_editor.cpp50
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp12
-rw-r--r--editor/plugins/editor_preview_plugins.cpp37
-rw-r--r--editor/plugins/editor_preview_plugins.h5
-rw-r--r--misc/extension_api_validation/4.2-stable.expected22
-rw-r--r--modules/gdscript/gdscript.cpp24
-rw-r--r--modules/gdscript/gdscript.h5
-rw-r--r--modules/gdscript/gdscript_cache.cpp55
-rw-r--r--modules/gdscript/gdscript_cache.h1
-rw-r--r--modules/gdscript/gdscript_editor.cpp2
-rw-r--r--modules/gdscript/gdscript_parser.cpp107
-rw-r--r--modules/gdscript/gdscript_parser.h3
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp69
-rw-r--r--modules/gdscript/gdscript_tokenizer.h58
-rw-r--r--modules/gdscript/gdscript_tokenizer_buffer.cpp493
-rw-r--r--modules/gdscript/gdscript_tokenizer_buffer.h93
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp2
-rw-r--r--modules/gdscript/register_types.cpp27
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp96
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.h13
-rw-r--r--modules/gdscript/tests/gdscript_test_runner_suite.h6
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.textonly.gd (renamed from modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.gd)0
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.textonly.out (renamed from modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.out)0
-rw-r--r--modules/gdscript/tests/scripts/parser/features/multiline_if.gd1
-rw-r--r--modules/gdscript/tests/test_gdscript.cpp61
-rw-r--r--modules/gdscript/tests/test_gdscript.h1
-rw-r--r--modules/gltf/structures/gltf_buffer_view.compat.inc61
-rw-r--r--modules/gltf/structures/gltf_buffer_view.cpp11
-rw-r--r--modules/gltf/structures/gltf_buffer_view.h19
-rw-r--r--modules/multiplayer/multiplayer_spawner.cpp4
-rw-r--r--modules/multiplayer/multiplayer_spawner.h2
-rw-r--r--modules/multiplayer/multiplayer_synchronizer.cpp4
-rw-r--r--modules/multiplayer/multiplayer_synchronizer.h2
-rw-r--r--platform/macos/os_macos.mm5
-rw-r--r--scene/2d/animated_sprite_2d.cpp4
-rw-r--r--scene/2d/animated_sprite_2d.h2
-rw-r--r--scene/2d/audio_stream_player_2d.compat.inc41
-rw-r--r--scene/2d/audio_stream_player_2d.cpp3
-rw-r--r--scene/2d/audio_stream_player_2d.h7
-rw-r--r--scene/2d/canvas_modulate.cpp4
-rw-r--r--scene/2d/canvas_modulate.h2
-rw-r--r--scene/2d/collision_object_2d.cpp4
-rw-r--r--scene/2d/collision_object_2d.h2
-rw-r--r--scene/2d/collision_polygon_2d.cpp4
-rw-r--r--scene/2d/collision_polygon_2d.h2
-rw-r--r--scene/2d/collision_shape_2d.cpp4
-rw-r--r--scene/2d/collision_shape_2d.h2
-rw-r--r--scene/2d/cpu_particles_2d.cpp4
-rw-r--r--scene/2d/cpu_particles_2d.h2
-rw-r--r--scene/2d/gpu_particles_2d.cpp4
-rw-r--r--scene/2d/gpu_particles_2d.h2
-rw-r--r--scene/2d/joint_2d.cpp4
-rw-r--r--scene/2d/joint_2d.h2
-rw-r--r--scene/2d/light_2d.cpp9
-rw-r--r--scene/2d/light_2d.h2
-rw-r--r--scene/2d/light_occluder_2d.cpp4
-rw-r--r--scene/2d/light_occluder_2d.h2
-rw-r--r--scene/2d/navigation_agent_2d.cpp4
-rw-r--r--scene/2d/navigation_agent_2d.h2
-rw-r--r--scene/2d/navigation_link_2d.cpp4
-rw-r--r--scene/2d/navigation_link_2d.h2
-rw-r--r--scene/2d/navigation_region_2d.cpp4
-rw-r--r--scene/2d/navigation_region_2d.h2
-rw-r--r--scene/2d/parallax_layer.cpp4
-rw-r--r--scene/2d/parallax_layer.h2
-rw-r--r--scene/2d/path_2d.cpp4
-rw-r--r--scene/2d/path_2d.h2
-rw-r--r--scene/2d/physical_bone_2d.cpp4
-rw-r--r--scene/2d/physical_bone_2d.h2
-rw-r--r--scene/2d/physics_body_2d.cpp4
-rw-r--r--scene/2d/physics_body_2d.h2
-rw-r--r--scene/2d/remote_transform_2d.cpp4
-rw-r--r--scene/2d/remote_transform_2d.h2
-rw-r--r--scene/2d/shape_cast_2d.cpp4
-rw-r--r--scene/2d/shape_cast_2d.h2
-rw-r--r--scene/2d/skeleton_2d.cpp4
-rw-r--r--scene/2d/skeleton_2d.h2
-rw-r--r--scene/2d/tile_map.cpp4
-rw-r--r--scene/2d/tile_map.h2
-rw-r--r--scene/3d/audio_stream_player_3d.compat.inc41
-rw-r--r--scene/3d/audio_stream_player_3d.cpp3
-rw-r--r--scene/3d/audio_stream_player_3d.h7
-rw-r--r--scene/3d/bone_attachment_3d.cpp4
-rw-r--r--scene/3d/bone_attachment_3d.h3
-rw-r--r--scene/3d/collision_object_3d.cpp4
-rw-r--r--scene/3d/collision_object_3d.h2
-rw-r--r--scene/3d/collision_polygon_3d.cpp4
-rw-r--r--scene/3d/collision_polygon_3d.h2
-rw-r--r--scene/3d/collision_shape_3d.cpp4
-rw-r--r--scene/3d/collision_shape_3d.h2
-rw-r--r--scene/3d/cpu_particles_3d.cpp4
-rw-r--r--scene/3d/cpu_particles_3d.h2
-rw-r--r--scene/3d/decal.cpp4
-rw-r--r--scene/3d/decal.h2
-rw-r--r--scene/3d/fog_volume.cpp4
-rw-r--r--scene/3d/fog_volume.h2
-rw-r--r--scene/3d/gpu_particles_3d.cpp4
-rw-r--r--scene/3d/gpu_particles_3d.h2
-rw-r--r--scene/3d/gpu_particles_collision_3d.cpp4
-rw-r--r--scene/3d/gpu_particles_collision_3d.h2
-rw-r--r--scene/3d/joint_3d.cpp4
-rw-r--r--scene/3d/joint_3d.h2
-rw-r--r--scene/3d/light_3d.cpp12
-rw-r--r--scene/3d/light_3d.h6
-rw-r--r--scene/3d/lightmap_gi.cpp4
-rw-r--r--scene/3d/lightmap_gi.h2
-rw-r--r--scene/3d/navigation_agent_3d.cpp4
-rw-r--r--scene/3d/navigation_agent_3d.h2
-rw-r--r--scene/3d/navigation_link_3d.cpp4
-rw-r--r--scene/3d/navigation_link_3d.h2
-rw-r--r--scene/3d/navigation_region_3d.cpp4
-rw-r--r--scene/3d/navigation_region_3d.h2
-rw-r--r--scene/3d/occluder_instance_3d.cpp4
-rw-r--r--scene/3d/occluder_instance_3d.h2
-rw-r--r--scene/3d/path_3d.cpp4
-rw-r--r--scene/3d/path_3d.h2
-rw-r--r--scene/3d/physics_body_3d.cpp4
-rw-r--r--scene/3d/physics_body_3d.h2
-rw-r--r--scene/3d/reflection_probe.cpp4
-rw-r--r--scene/3d/reflection_probe.h2
-rw-r--r--scene/3d/remote_transform_3d.cpp4
-rw-r--r--scene/3d/remote_transform_3d.h2
-rw-r--r--scene/3d/shape_cast_3d.cpp4
-rw-r--r--scene/3d/shape_cast_3d.h2
-rw-r--r--scene/3d/soft_body_3d.cpp4
-rw-r--r--scene/3d/soft_body_3d.h2
-rw-r--r--scene/3d/sprite_3d.cpp4
-rw-r--r--scene/3d/sprite_3d.h2
-rw-r--r--scene/3d/vehicle_body_3d.cpp4
-rw-r--r--scene/3d/vehicle_body_3d.h2
-rw-r--r--scene/3d/visible_on_screen_notifier_3d.cpp4
-rw-r--r--scene/3d/visible_on_screen_notifier_3d.h2
-rw-r--r--scene/3d/visual_instance_3d.cpp4
-rw-r--r--scene/3d/visual_instance_3d.h2
-rw-r--r--scene/3d/voxel_gi.cpp4
-rw-r--r--scene/3d/voxel_gi.h2
-rw-r--r--scene/3d/world_environment.cpp4
-rw-r--r--scene/3d/world_environment.h2
-rw-r--r--scene/3d/xr_nodes.cpp12
-rw-r--r--scene/3d/xr_nodes.h6
-rw-r--r--scene/animation/animation_mixer.compat.inc2
-rw-r--r--scene/animation/animation_tree.cpp4
-rw-r--r--scene/animation/animation_tree.h2
-rw-r--r--scene/audio/audio_stream_player.compat.inc41
-rw-r--r--scene/audio/audio_stream_player.cpp3
-rw-r--r--scene/audio/audio_stream_player.h7
-rw-r--r--scene/gui/base_button.cpp4
-rw-r--r--scene/gui/base_button.h2
-rw-r--r--scene/gui/container.cpp4
-rw-r--r--scene/gui/container.h2
-rw-r--r--scene/gui/control.cpp6
-rw-r--r--scene/gui/control.h2
-rw-r--r--scene/gui/graph_edit.cpp4
-rw-r--r--scene/gui/graph_edit.h2
-rw-r--r--scene/gui/item_list.cpp70
-rw-r--r--scene/gui/item_list.h8
-rw-r--r--scene/gui/label.cpp4
-rw-r--r--scene/gui/label.h2
-rw-r--r--scene/gui/line_edit.cpp4
-rw-r--r--scene/gui/line_edit.h2
-rw-r--r--scene/gui/range.cpp4
-rw-r--r--scene/gui/range.h2
-rw-r--r--scene/gui/scroll_container.cpp4
-rw-r--r--scene/gui/scroll_container.h2
-rw-r--r--scene/gui/subviewport_container.cpp4
-rw-r--r--scene/gui/subviewport_container.h2
-rw-r--r--scene/main/missing_node.cpp4
-rw-r--r--scene/main/missing_node.h2
-rw-r--r--scene/main/node.compat.inc41
-rw-r--r--scene/main/node.cpp102
-rw-r--r--scene/main/node.h9
-rw-r--r--scene/main/shader_globals_override.cpp4
-rw-r--r--scene/main/shader_globals_override.h2
-rw-r--r--scene/main/timer.cpp4
-rw-r--r--scene/main/timer.h2
-rw-r--r--scene/main/viewport.cpp6
-rw-r--r--scene/main/viewport.h2
-rw-r--r--scene/property_list_helper.cpp138
-rw-r--r--scene/property_list_helper.h64
-rw-r--r--servers/rendering/shader_compiler.h34
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="&quot;&quot;">
+ 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]&amp;"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]&amp;"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;