diff options
34 files changed, 281 insertions, 73 deletions
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index 672b030806..04e1561a0c 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -3601,15 +3601,6 @@ bool Variant::is_type_shared(Variant::Type p_type) { case OBJECT: case ARRAY: case DICTIONARY: - case PACKED_BYTE_ARRAY: - case PACKED_INT32_ARRAY: - case PACKED_INT64_ARRAY: - case PACKED_FLOAT32_ARRAY: - case PACKED_FLOAT64_ARRAY: - case PACKED_STRING_ARRAY: - case PACKED_VECTOR2_ARRAY: - case PACKED_VECTOR3_ARRAY: - case PACKED_COLOR_ARRAY: return true; default: { } diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 143e1f23e9..792cd38741 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -529,7 +529,7 @@ <return type="bool" /> <param index="0" name="expr" type="String" /> <description> - Does a simple expression match, where [code]*[/code] matches zero or more arbitrary characters and [code]?[/code] matches any single character except a period ([code].[/code]). An empty string or empty expression always evaluates to [code]false[/code]. + Does a simple expression match (also called "glob" or "globbing"), where [code]*[/code] matches zero or more arbitrary characters and [code]?[/code] matches any single character except a period ([code].[/code]). An empty string or empty expression always evaluates to [code]false[/code]. </description> </method> <method name="matchn" qualifiers="const"> diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index c116939bd8..5dedea50d0 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -279,8 +279,8 @@ <description> Sets the tile indentifiers for the cell on layer [param layer] at coordinates [param coords]. Each tile of the [TileSet] is identified using three parts: - The source identifier [param source_id] identifies a [TileSetSource] identifier. See [method TileSet.set_source_id], - - The atlas coordinates identifier [param atlas_coords] identifies a tile coordinates in the atlas (if the source is a [TileSetAtlasSource]. For [TileSetScenesCollectionSource] it should always be [code]Vector2i(0, 0)[/code]), - - The alternative tile identifier [param alternative_tile] identifies a tile alternative the source is a [TileSetAtlasSource], and the scene for a [TileSetScenesCollectionSource]. + - The atlas coordinates identifier [param atlas_coords] identifies a tile coordinates in the atlas (if the source is a [TileSetAtlasSource]). For [TileSetScenesCollectionSource] it should always be [code]Vector2i(0, 0)[/code]), + - The alternative tile identifier [param alternative_tile] identifies a tile alternative in the atlas (if the source is a [TileSetAtlasSource]), and the scene for a [TileSetScenesCollectionSource]. If [param source_id] is set to [code]-1[/code], [param atlas_coords] to [code]Vector2i(-1, -1)[/code] or [param alternative_tile] to [code]-1[/code], the cell will be erased. An erased cell gets [b]all[/b] its identifiers automatically set to their respective invalid values, namely [code]-1[/code], [code]Vector2i(-1, -1)[/code] and [code]-1[/code]. </description> </method> diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index cf3504fcc1..e1924c7994 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -2636,7 +2636,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<Str new_menu->connect("id_pressed", callable_mp(this, &FileSystemDock::_tree_rmb_option)); p_popup->add_child(new_menu); - p_popup->add_submenu_item(TTR("New"), "New"); + p_popup->add_submenu_item(TTR("New"), "New", FILE_NEW); new_menu->add_icon_item(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")), TTR("Folder..."), FILE_NEW_FOLDER); new_menu->add_icon_item(get_theme_icon(SNAME("PackedScene"), SNAME("EditorIcons")), TTR("Scene..."), FILE_NEW_SCENE); diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index ede6869eea..9060f5c0a4 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -90,17 +90,18 @@ private: FILE_DUPLICATE, FILE_REIMPORT, FILE_INFO, - FILE_NEW_FOLDER, - FILE_NEW_SCRIPT, - FILE_NEW_SCENE, + FILE_NEW, FILE_SHOW_IN_EXPLORER, FILE_OPEN_EXTERNAL, FILE_COPY_PATH, FILE_COPY_UID, - FILE_NEW_RESOURCE, - FILE_NEW_TEXTFILE, FOLDER_EXPAND_ALL, FOLDER_COLLAPSE_ALL, + FILE_NEW_RESOURCE, + FILE_NEW_TEXTFILE, + FILE_NEW_FOLDER, + FILE_NEW_SCRIPT, + FILE_NEW_SCENE, }; FileSortOption file_sort = FILE_SORT_NAME; diff --git a/editor/icons/GizmoBakedLightmap.svg b/editor/icons/GizmoLightmapGI.svg index a7828615fd..a7828615fd 100644 --- a/editor/icons/GizmoBakedLightmap.svg +++ b/editor/icons/GizmoLightmapGI.svg diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index fd664e9d9f..77785b15ca 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -1109,7 +1109,7 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { add_options.push_back(AddOption("Add3", "AnimationNodeAdd3", 3)); add_options.push_back(AddOption("Blend2", "AnimationNodeBlend2", 2)); add_options.push_back(AddOption("Blend3", "AnimationNodeBlend3", 3)); - add_options.push_back(AddOption("Seek", "AnimationNodeTimeSeek", 1)); + add_options.push_back(AddOption("TimeSeek", "AnimationNodeTimeSeek", 1)); add_options.push_back(AddOption("TimeScale", "AnimationNodeTimeScale", 1)); add_options.push_back(AddOption("Transition", "AnimationNodeTransition")); add_options.push_back(AddOption("BlendTree", "AnimationNodeBlendTree")); diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index d3e16211f7..26f872421e 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -2404,7 +2404,7 @@ Vector<String> ProjectConverter3To4::check_for_files() { directories_to_check.append(current_dir.path_join(file_name) + "/"); } else { bool proper_extension = false; - if (file_name.ends_with(".gd") || file_name.ends_with(".shader") || file_name.ends_with(".tscn") || file_name.ends_with(".tres") || file_name.ends_with(".godot") || file_name.ends_with(".cs") || file_name.ends_with(".csproj")) + if (file_name.ends_with(".gd") || file_name.ends_with(".shader") || file_name.ends_with(".gdshader") || file_name.ends_with(".tscn") || file_name.ends_with(".tres") || file_name.ends_with(".godot") || file_name.ends_with(".cs") || file_name.ends_with(".csproj")) proper_extension = true; if (proper_extension) { diff --git a/main/main.cpp b/main/main.cpp index c5a9f94417..3aa9a44a21 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2366,8 +2366,10 @@ bool Main::start() { String _export_preset; bool export_debug = false; bool export_pack_only = false; +#ifndef DISABLE_DEPRECATED bool converting_project = false; bool validating_converting_project = false; +#endif // DISABLE_DEPRECATED #endif main_timer_sync.init(OS::get_singleton()->get_ticks_usec()); @@ -2383,10 +2385,12 @@ bool Main::start() { #ifdef TOOLS_ENABLED } else if (args[i] == "--no-docbase") { doc_base = false; +#ifndef DISABLE_DEPRECATED } else if (args[i] == "--convert-3to4") { converting_project = true; } else if (args[i] == "--validate-conversion-3to4") { validating_converting_project = true; +#endif // DISABLE_DEPRECATED } else if (args[i] == "-e" || args[i] == "--editor") { editor = true; } else if (args[i] == "-p" || args[i] == "--project-manager") { @@ -2547,6 +2551,7 @@ bool Main::start() { return false; } +#ifndef DISABLE_DEPRECATED if (converting_project) { int exit_code = ProjectConverter3To4(converter_max_kb_file, converter_max_line_length).convert(); OS::get_singleton()->set_exit_code(exit_code); @@ -2557,6 +2562,7 @@ bool Main::start() { OS::get_singleton()->set_exit_code(exit_code); return false; } +#endif // DISABLE_DEPRECATED #endif diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index cdb4f29854..fd04d3c660 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -2240,6 +2240,28 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig if (assignee_type.is_constant || (p_assignment->assignee->type == GDScriptParser::Node::SUBSCRIPT && static_cast<GDScriptParser::SubscriptNode *>(p_assignment->assignee)->base->is_constant)) { push_error("Cannot assign a new value to a constant.", p_assignment->assignee); + return; + } else if (assignee_type.is_read_only) { + push_error("Cannot assign a new value to a read-only property.", p_assignment->assignee); + return; + } else if (p_assignment->assignee->type == GDScriptParser::Node::SUBSCRIPT) { + GDScriptParser::SubscriptNode *sub = static_cast<GDScriptParser::SubscriptNode *>(p_assignment->assignee); + while (sub) { + const GDScriptParser::DataType &base_type = sub->base->datatype; + if (base_type.is_hard_type() && base_type.is_read_only) { + if (base_type.kind == GDScriptParser::DataType::BUILTIN && !Variant::is_type_shared(base_type.builtin_type)) { + push_error("Cannot assign a new value to a read-only property.", p_assignment->assignee); + return; + } + } else { + break; + } + if (sub->base->type == GDScriptParser::Node::SUBSCRIPT) { + sub = static_cast<GDScriptParser::SubscriptNode *>(sub->base); + } else { + sub = nullptr; + } + } } // Check if assigned value is an array literal, so we can make it a typed array too if appropriate. @@ -3329,7 +3351,8 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod StringName getter_name = ClassDB::get_property_getter(native, name); MethodBind *getter = ClassDB::get_method(native, getter_name); if (getter != nullptr) { - p_identifier->set_datatype(type_from_property(getter->get_return_info())); + bool has_setter = ClassDB::get_property_setter(native, name) != StringName(); + p_identifier->set_datatype(type_from_property(getter->get_return_info(), false, !has_setter)); p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE; } return; @@ -4268,8 +4291,9 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_metatype(const GDScriptPars return result; } -GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo &p_property, bool p_is_arg) const { +GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo &p_property, bool p_is_arg, bool p_is_readonly) const { GDScriptParser::DataType result; + result.is_read_only = p_is_readonly; result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; if (p_property.type == Variant::NIL && (p_is_arg || (p_property.usage & PROPERTY_USAGE_NIL_IS_VARIANT))) { // Variant diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index b51564fb0a..75d52509a4 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -115,7 +115,7 @@ class GDScriptAnalyzer { Array make_array_from_element_datatype(const GDScriptParser::DataType &p_element_datatype, const GDScriptParser::Node *p_source_node = nullptr); GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source); static GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type); - GDScriptParser::DataType type_from_property(const PropertyInfo &p_property, bool p_is_arg = false) const; + GDScriptParser::DataType type_from_property(const PropertyInfo &p_property, bool p_is_arg = false, bool p_is_readonly = false) const; GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source); bool get_function_signature(GDScriptParser::Node *p_source, bool p_is_constructor, GDScriptParser::DataType base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg); bool function_signature_from_info(const MethodInfo &p_info, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index f88ac581ca..12c10642ec 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1953,17 +1953,19 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, case GDScriptParser::DataType::CLASS: if (base_type.class_type->has_function(p_context.current_function->identifier->name)) { GDScriptParser::FunctionNode *parent_function = base_type.class_type->get_member(p_context.current_function->identifier->name).function; - const GDScriptParser::ParameterNode *parameter = parent_function->parameters[parent_function->parameters_indices[p_identifier]]; - if ((!id_type.is_set() || id_type.is_variant()) && parameter->get_datatype().is_hard_type()) { - id_type = parameter->get_datatype(); - } - if (parameter->initializer) { - GDScriptParser::CompletionContext c = p_context; - c.current_function = parent_function; - c.current_class = base_type.class_type; - c.base = nullptr; - if (_guess_expression_type(c, parameter->initializer, r_type)) { - return true; + if (parent_function->parameters_indices.has(p_identifier)) { + const GDScriptParser::ParameterNode *parameter = parent_function->parameters[parent_function->parameters_indices[p_identifier]]; + if ((!id_type.is_set() || id_type.is_variant()) && parameter->get_datatype().is_hard_type()) { + id_type = parameter->get_datatype(); + } + if (parameter->initializer) { + GDScriptParser::CompletionContext c = p_context; + c.current_function = parent_function; + c.current_class = base_type.class_type; + c.base = nullptr; + if (_guess_expression_type(c, parameter->initializer, r_type)) { + return true; + } } } } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 07dac25ec5..bc0fe58fa7 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -122,6 +122,7 @@ public: TypeSource type_source = UNDETECTED; bool is_constant = false; + bool is_read_only = false; bool is_meta_type = false; bool is_coroutine = false; // For function calls. @@ -206,6 +207,7 @@ public: void operator=(const DataType &p_other) { kind = p_other.kind; type_source = p_other.type_source; + is_read_only = p_other.is_read_only; is_constant = p_other.is_constant; is_meta_type = p_other.is_meta_type; is_coroutine = p_other.is_coroutine; diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index e18a4a6190..b99f5d2685 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -811,13 +811,22 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED if (!valid) { + Object *obj = dst->get_validated_object(); String v = index->operator String(); - if (!v.is_empty()) { - v = "'" + v + "'"; + bool read_only_property = false; + if (obj) { + read_only_property = ClassDB::has_property(obj->get_class_name(), v) && (ClassDB::get_property_setter(obj->get_class_name(), v) == StringName()); + } + if (read_only_property) { + err_text = vformat(R"(Cannot set value into property "%s" (on base "%s") because it is read-only.)", v, _get_var_type(dst)); } else { - v = "of type '" + _get_var_type(index) + "'"; + if (!v.is_empty()) { + v = "'" + v + "'"; + } else { + v = "of type '" + _get_var_type(index) + "'"; + } + err_text = "Invalid set index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'"; } - err_text = "Invalid set index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'"; OPCODE_BREAK; } #endif @@ -1003,8 +1012,16 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED if (!valid) { - String err_type; - err_text = "Invalid set index '" + String(*index) + "' (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'."; + Object *obj = dst->get_validated_object(); + bool read_only_property = false; + if (obj) { + read_only_property = ClassDB::has_property(obj->get_class_name(), *index) && (ClassDB::get_property_setter(obj->get_class_name(), *index) == StringName()); + } + if (read_only_property) { + err_text = vformat(R"(Cannot set value into property "%s" (on base "%s") because it is read-only.)", String(*index), _get_var_type(dst)); + } else { + err_text = "Invalid set index '" + String(*index) + "' (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'."; + } OPCODE_BREAK; } #endif diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property.gd b/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property.gd new file mode 100644 index 0000000000..2b1c4c9594 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property.gd @@ -0,0 +1,4 @@ +func test(): + var tree := SceneTree.new() + tree.root = Window.new() + tree.free() diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property.out b/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property.out new file mode 100644 index 0000000000..b236d70ec8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot assign a new value to a read-only property. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property_indirectly.gd b/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property_indirectly.gd new file mode 100644 index 0000000000..c97ee0ea69 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property_indirectly.gd @@ -0,0 +1,4 @@ +func test(): + var state := PhysicsDirectBodyState3DExtension.new() + state.center_of_mass.x += 1.0 + state.free() diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property_indirectly.out b/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property_indirectly.out new file mode 100644 index 0000000000..b236d70ec8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property_indirectly.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot assign a new value to a read-only property. diff --git a/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property.gd b/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property.gd new file mode 100644 index 0000000000..19c4186622 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property.gd @@ -0,0 +1,7 @@ +func test(): + var state = PhysicsDirectBodyState3DExtension.new() + assign(state) + state.free() + +func assign(state): + state.center_of_mass.x -= 1.0 diff --git a/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property.out b/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property.out new file mode 100644 index 0000000000..c181c5dd02 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: assign() +>> runtime/assign_to_read_only_property.gd +>> 7 +>> Cannot set value into property "center_of_mass" (on base "PhysicsDirectBodyState3DExtension") because it is read-only. diff --git a/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property_with_variable_index.gd b/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property_with_variable_index.gd new file mode 100644 index 0000000000..f15f580272 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property_with_variable_index.gd @@ -0,0 +1,8 @@ +func test(): + var state = PhysicsDirectBodyState3DExtension.new() + var prop = &"center_of_mass" + assign(state, prop) + state.free() + +func assign(state, prop): + state[prop].x = 1.0 diff --git a/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property_with_variable_index.out b/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property_with_variable_index.out new file mode 100644 index 0000000000..2cdc81aacc --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property_with_variable_index.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: assign() +>> runtime/assign_to_read_only_property_with_variable_index.gd +>> 8 +>> Cannot set value into property "center_of_mass" (on base "PhysicsDirectBodyState3DExtension") because it is read-only. diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index 019504ad66..70b48b0e3a 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -185,7 +185,9 @@ namespace GodotTools.Export foreach (string file in Directory.GetFiles(publishOutputTempDir, "*", SearchOption.AllDirectories)) { - AddSharedObject(file, tags: null, projectDataDirName); + AddSharedObject(file, tags: null, + Path.Join(projectDataDirName, + Path.GetRelativePath(publishOutputTempDir, Path.GetDirectoryName(file)))); } } } diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub index 3b39967ba4..0dd41675b6 100644 --- a/modules/openxr/SCsub +++ b/modules/openxr/SCsub @@ -103,6 +103,7 @@ env_openxr.add_source_files(module_obj, "extensions/openxr_fb_passthrough_extens env_openxr.add_source_files(module_obj, "extensions/openxr_fb_display_refresh_rate_extension.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_pico_controller_extension.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_wmr_controller_extension.cpp") +env_openxr.add_source_files(module_obj, "extensions/openxr_ml2_controller_extension.cpp") env.modules_sources += module_obj diff --git a/modules/openxr/extensions/openxr_ml2_controller_extension.cpp b/modules/openxr/extensions/openxr_ml2_controller_extension.cpp new file mode 100644 index 0000000000..ae372f69b3 --- /dev/null +++ b/modules/openxr/extensions/openxr_ml2_controller_extension.cpp @@ -0,0 +1,71 @@ +/**************************************************************************/ +/* openxr_ml2_controller_extension.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "openxr_ml2_controller_extension.h" +#include "../action_map/openxr_interaction_profile_meta_data.h" + +HashMap<String, bool *> OpenXRML2ControllerExtension::get_requested_extensions() { + HashMap<String, bool *> request_extensions; + + request_extensions[XR_ML_ML2_CONTROLLER_INTERACTION_EXTENSION_NAME] = &available; + + return request_extensions; +} + +bool OpenXRML2ControllerExtension::is_available() { + return available; +} + +void OpenXRML2ControllerExtension::on_register_metadata() { + OpenXRInteractionProfileMetaData *metadata = OpenXRInteractionProfileMetaData::get_singleton(); + ERR_FAIL_NULL(metadata); + + // Magic Leap 2 Controller + const String profile_path = "/interaction_profiles/ml/ml2_controller"; + metadata->register_interaction_profile("Magic Leap 2 controller", "/interaction_profiles/ml/ml2_controller", XR_ML_ML2_CONTROLLER_INTERACTION_EXTENSION_NAME); + for (const String user_path : { "/user/hand/left", "/user/hand/right" }) { + metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE); + metadata->register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE); + + metadata->register_io_path(profile_path, "Menu click", user_path, user_path + "/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT); + metadata->register_io_path(profile_path, "Trigger click", user_path, user_path + "/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + + metadata->register_io_path(profile_path, "Shoulder click", user_path, user_path + "/input/shoulder/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + + metadata->register_io_path(profile_path, "Trackpad click", user_path, user_path + "/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path(profile_path, "Trackpad force", user_path, user_path + "/input/trackpad/force", "", OpenXRAction::OPENXR_ACTION_FLOAT); + metadata->register_io_path(profile_path, "Trackpad X", user_path, user_path + "/input/trackpad/x", "", OpenXRAction::OPENXR_ACTION_FLOAT); + metadata->register_io_path(profile_path, "Trackpad Y", user_path, user_path + "/input/trackpad/y", "", OpenXRAction::OPENXR_ACTION_FLOAT); + metadata->register_io_path(profile_path, "Trackpad touch", user_path, user_path + "/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_VECTOR2); + + metadata->register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC); + } +} diff --git a/modules/openxr/extensions/openxr_ml2_controller_extension.h b/modules/openxr/extensions/openxr_ml2_controller_extension.h new file mode 100644 index 0000000000..216cd55a2f --- /dev/null +++ b/modules/openxr/extensions/openxr_ml2_controller_extension.h @@ -0,0 +1,48 @@ +/**************************************************************************/ +/* openxr_ml2_controller_extension.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef OPENXR_ML2_CONTROLLER_EXTENSION_H +#define OPENXR_ML2_CONTROLLER_EXTENSION_H + +#include "openxr_extension_wrapper.h" + +class OpenXRML2ControllerExtension : public OpenXRExtensionWrapper { +public: + virtual HashMap<String, bool *> get_requested_extensions() override; + + bool is_available(); + + virtual void on_register_metadata() override; + +private: + bool available = false; +}; + +#endif // OPENXR_ML2_CONTROLLER_EXTENSION_H diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp index 306a2f1bbd..c39e49387a 100644 --- a/modules/openxr/register_types.cpp +++ b/modules/openxr/register_types.cpp @@ -52,6 +52,7 @@ #include "extensions/openxr_htc_controller_extension.h" #include "extensions/openxr_htc_vive_tracker_extension.h" #include "extensions/openxr_huawei_controller_extension.h" +#include "extensions/openxr_ml2_controller_extension.h" #include "extensions/openxr_palm_pose_extension.h" #include "extensions/openxr_pico_controller_extension.h" #include "extensions/openxr_wmr_controller_extension.h" @@ -102,6 +103,7 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) { OpenXRAPI::register_extension_wrapper(memnew(OpenXRFbPassthroughExtensionWrapper)); OpenXRAPI::register_extension_wrapper(memnew(OpenXRDisplayRefreshRateExtension)); OpenXRAPI::register_extension_wrapper(memnew(OpenXRWMRControllerExtension)); + OpenXRAPI::register_extension_wrapper(memnew(OpenXRML2ControllerExtension)); } if (OpenXRAPI::openxr_is_enabled()) { diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp index 75c23655f2..41d1f1d050 100644 --- a/platform/linuxbsd/os_linuxbsd.cpp +++ b/platform/linuxbsd/os_linuxbsd.cpp @@ -81,7 +81,7 @@ void OS_LinuxBSD::alert(const String &p_alert, const String &p_title) { List<String> args; if (program.ends_with("zenity")) { - args.push_back("--error"); + args.push_back("--warning"); args.push_back("--width"); args.push_back("500"); args.push_back("--title"); @@ -91,7 +91,9 @@ void OS_LinuxBSD::alert(const String &p_alert, const String &p_title) { } if (program.ends_with("kdialog")) { - args.push_back("--error"); + // `--sorry` uses the same icon as `--warning` in Zenity. + // As of KDialog 22.12.1, its `--warning` options are only available for yes/no questions. + args.push_back("--sorry"); args.push_back(p_alert); args.push_back("--title"); args.push_back(p_title); diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 797999625b..12a96c8679 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -518,7 +518,7 @@ void AnimationNodeBlend2::get_parameter_list(List<PropertyInfo> *r_list) const { } Variant AnimationNodeBlend2::get_parameter_default_value(const StringName &p_parameter) const { - return 0; //for blend amount + return 0; // For blend amount. } String AnimationNodeBlend2::get_caption() const { @@ -531,7 +531,7 @@ double AnimationNodeBlend2::process(double p_time, bool p_seek, bool p_is_extern double rem0 = blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0 - amount, FILTER_BLEND, sync); double rem1 = blend_input(1, p_time, p_seek, p_is_external_seeking, amount, FILTER_PASS, sync); - return amount > 0.5 ? rem1 : rem0; //hacky but good enough + return amount > 0.5 ? rem1 : rem0; // Hacky but good enough. } bool AnimationNodeBlend2::has_filter() const { @@ -553,7 +553,7 @@ void AnimationNodeBlend3::get_parameter_list(List<PropertyInfo> *r_list) const { } Variant AnimationNodeBlend3::get_parameter_default_value(const StringName &p_parameter) const { - return 0; //for blend amount + return 0; // For blend amount. } String AnimationNodeBlend3::get_caption() const { @@ -566,7 +566,7 @@ double AnimationNodeBlend3::process(double p_time, bool p_seek, bool p_is_extern double rem1 = blend_input(1, p_time, p_seek, p_is_external_seeking, 1.0 - ABS(amount), FILTER_IGNORE, sync); double rem2 = blend_input(2, p_time, p_seek, p_is_external_seeking, MAX(0, amount), FILTER_IGNORE, sync); - return amount > 0.5 ? rem2 : (amount < -0.5 ? rem0 : rem1); //hacky but good enough + return amount > 0.5 ? rem2 : (amount < -0.5 ? rem0 : rem1); // Hacky but good enough. } void AnimationNodeBlend3::_bind_methods() { @@ -585,7 +585,7 @@ void AnimationNodeTimeScale::get_parameter_list(List<PropertyInfo> *r_list) cons } Variant AnimationNodeTimeScale::get_parameter_default_value(const StringName &p_parameter) const { - return 1.0; //initial timescale + return 1.0; // Initial timescale. } String AnimationNodeTimeScale::get_caption() const { @@ -611,24 +611,24 @@ AnimationNodeTimeScale::AnimationNodeTimeScale() { //////////////////////////////////// void AnimationNodeTimeSeek::get_parameter_list(List<PropertyInfo> *r_list) const { - r_list->push_back(PropertyInfo(Variant::FLOAT, seek_pos, PROPERTY_HINT_RANGE, "-1,3600,0.01,or_greater")); + r_list->push_back(PropertyInfo(Variant::FLOAT, seek_pos_request, PROPERTY_HINT_RANGE, "-1,3600,0.01,or_greater")); // It will be reset to -1 after seeking the position immediately. } Variant AnimationNodeTimeSeek::get_parameter_default_value(const StringName &p_parameter) const { - return 1.0; //initial timescale + return -1.0; // Initial seek request. } String AnimationNodeTimeSeek::get_caption() const { - return "Seek"; + return "TimeSeek"; } double AnimationNodeTimeSeek::process(double p_time, bool p_seek, bool p_is_external_seeking) { - double cur_seek_pos = get_parameter(seek_pos); + double cur_seek_pos = get_parameter(seek_pos_request); if (p_seek) { return blend_input(0, p_time, true, p_is_external_seeking, 1.0, FILTER_IGNORE, true); } else if (cur_seek_pos >= 0) { double ret = blend_input(0, cur_seek_pos, true, true, 1.0, FILTER_IGNORE, true); - set_parameter(seek_pos, -1.0); //reset + set_parameter(seek_pos_request, -1.0); // Reset. return ret; } else { return blend_input(0, p_time, false, p_is_external_seeking, 1.0, FILTER_IGNORE, true); @@ -871,7 +871,7 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex } } - if (cur_prev_index < 0) { // process current animation, check for transition + if (cur_prev_index < 0) { // Process current animation, check for transition. rem = blend_input(cur_current_index, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true); @@ -885,7 +885,7 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex set_parameter(transition_request, get_input_name((cur_current_index + 1) % get_input_count())); } - } else { // cross-fading from prev to current + } else { // Cross-fading from prev to current. real_t blend = xfade_time == 0 ? 0 : (cur_prev_xfading / xfade_time); if (xfade_curve.is_valid()) { @@ -894,7 +894,7 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex // Blend values must be more than CMP_EPSILON to process discrete keys in edge. real_t blend_inv = 1.0 - blend; - if (input_data[cur_current_index].reset && !p_seek && switched) { //just switched, seek to start of current + if (input_data[cur_current_index].reset && !p_seek && switched) { // Just switched, seek to start of current. rem = blend_input(cur_current_index, 0, true, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true); } else { rem = blend_input(cur_current_index, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true); @@ -1052,7 +1052,7 @@ void AnimationNodeBlendTree::remove_node(const StringName &p_name) { nodes.erase(p_name); - //erase connections to name + // Erase connections to name. for (KeyValue<StringName, Node> &E : nodes) { for (int i = 0; i < E.value.connections.size(); i++) { if (E.value.connections[i] == p_name) { @@ -1076,7 +1076,7 @@ void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringN nodes[p_new_name] = nodes[p_name]; nodes.erase(p_name); - //rename connections + // Rename connections. for (KeyValue<StringName, Node> &E : nodes) { for (int i = 0; i < E.value.connections.size(); i++) { if (E.value.connections[i] == p_name) { @@ -1084,7 +1084,7 @@ void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringN } } } - //connection must be done with new name + // Connection must be done with new name. nodes[p_new_name].node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_new_name), CONNECT_REFERENCE_COUNTED); emit_signal(SNAME("tree_changed")); diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index 20f8e9b190..1e90952564 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -257,7 +257,7 @@ public: class AnimationNodeTimeSeek : public AnimationNode { GDCLASS(AnimationNodeTimeSeek, AnimationNode); - StringName seek_pos = PNAME("seek_position"); + StringName seek_pos_request = PNAME("seek_request"); protected: static void _bind_methods(); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 6f5e2cf058..f09e4962a9 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -692,7 +692,7 @@ Transform2D Control::get_transform() const { return xform; } -void Control::_toplevel_changed_on_parent() { +void Control::_top_level_changed_on_parent() { // Update root control status. _notification(NOTIFICATION_EXIT_CANVAS); _notification(NOTIFICATION_ENTER_CANVAS); diff --git a/scene/gui/control.h b/scene/gui/control.h index 5977f4dbea..2fb5d559b6 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -292,8 +292,8 @@ private: void _update_minimum_size(); void _size_changed(); - void _toplevel_changed() override{}; // Controls don't need to do anything, only other CanvasItems. - void _toplevel_changed_on_parent() override; + void _top_level_changed() override {} // Controls don't need to do anything, only other CanvasItems. + void _top_level_changed_on_parent() override; void _clear_size_warning(); diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 541c7a0587..0ea8f6c5f1 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -185,7 +185,7 @@ void CanvasItem::_top_level_raise_self() { } void CanvasItem::_enter_canvas() { - // Resolves to nullptr if the node is toplevel. + // Resolves to nullptr if the node is top_level. CanvasItem *parent_item = get_parent_item(); if (parent_item) { @@ -400,26 +400,26 @@ void CanvasItem::set_as_top_level(bool p_top_level) { _exit_canvas(); top_level = p_top_level; - _toplevel_changed(); + _top_level_changed(); _enter_canvas(); _notify_transform(); } -void CanvasItem::_toplevel_changed() { - // Inform children that toplevel status has changed on a parent. +void CanvasItem::_top_level_changed() { + // Inform children that top_level status has changed on a parent. int children = get_child_count(); for (int i = 0; i < children; i++) { CanvasItem *child = Object::cast_to<CanvasItem>(get_child(i)); if (child) { - child->_toplevel_changed_on_parent(); + child->_top_level_changed_on_parent(); } } } -void CanvasItem::_toplevel_changed_on_parent() { - // Inform children that toplevel status has changed on a parent. - _toplevel_changed(); +void CanvasItem::_top_level_changed_on_parent() { + // Inform children that top_level status has changed on a parent. + _top_level_changed(); } bool CanvasItem::is_set_as_top_level() const { diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index 1ddfaa288c..2fa1d56667 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -125,8 +125,8 @@ private: void _propagate_visibility_changed(bool p_parent_visible_in_tree); void _handle_visibility_change(bool p_visible); - virtual void _toplevel_changed(); - virtual void _toplevel_changed_on_parent(); + virtual void _top_level_changed(); + virtual void _top_level_changed_on_parent(); void _redraw_callback(); |