summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/csg/editor/csg_gizmos.cpp8
-rw-r--r--modules/etcpak/image_compress_etcpak.cpp5
-rw-r--r--modules/gdscript/gdscript.cpp4
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp543
-rw-r--r--modules/gdscript/gdscript_analyzer.h4
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp108
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h5
-rw-r--r--modules/gdscript/gdscript_codegen.h2
-rw-r--r--modules/gdscript/gdscript_compiler.cpp4
-rw-r--r--modules/gdscript/gdscript_editor.cpp10
-rw-r--r--modules/gdscript/gdscript_parser.cpp84
-rw-r--r--modules/gdscript/gdscript_parser.h2
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp1
-rw-r--r--modules/gdscript/tests/README.md2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.gd1
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_outer_enum.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_outer_enum.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd14
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.out6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.out6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.out6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.out6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd14
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd19
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.gd1
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd20
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.gd12
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/lookup_class.gd50
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/lookup_class.out8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/lookup_class_external.notest.gd15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/lookup_signal.gd41
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/lookup_signal.out8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd62
-rw-r--r--modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd48
-rw-r--r--modules/gdscript/tests/scripts/parser/features/class_name.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/lambda_callable.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_dictionary.gd76
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd40
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd10
-rw-r--r--modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.gd17
-rw-r--r--modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.out5
-rw-r--r--modules/gdscript/tests/scripts/parser/features/vector_inf.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/features/vector_inf.out3
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.gd8
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.out8
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.gd2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.gd19
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.out8
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd17
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd51
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out14
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/gdscript.gd20
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/gdscript.out3
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd24
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd9
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.out3
-rw-r--r--modules/gltf/doc_classes/GLTFDocument.xml8
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.cpp10
-rw-r--r--modules/mono/csharp_script.cpp2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs59
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs217
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs9
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs16
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs11
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs58
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs6
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs72
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs10
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs37
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs110
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs107
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs36
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs22
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs22
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp4
-rw-r--r--modules/multiplayer/editor/replication_editor.cpp8
-rw-r--r--modules/multiplayer/multiplayer_spawner.cpp8
-rw-r--r--modules/multiplayer/multiplayer_spawner.h2
-rw-r--r--modules/multiplayer/scene_replication_interface.cpp48
-rw-r--r--modules/multiplayer/scene_replication_interface.h7
-rw-r--r--modules/navigation/godot_navigation_server.cpp61
-rw-r--r--modules/navigation/godot_navigation_server.h12
-rw-r--r--modules/navigation/nav_map.cpp33
-rw-r--r--modules/navigation/nav_map.h20
-rw-r--r--modules/navigation/navigation_mesh_generator.cpp6
-rw-r--r--modules/openxr/SCsub1
-rw-r--r--modules/openxr/action_map/openxr_action_map.cpp25
-rw-r--r--modules/openxr/editor/openxr_action_editor.cpp3
-rw-r--r--modules/openxr/editor/openxr_action_editor.h2
-rw-r--r--modules/openxr/editor/openxr_action_map_editor.cpp2
-rw-r--r--modules/openxr/editor/openxr_action_map_editor.h2
-rw-r--r--modules/openxr/editor/openxr_action_set_editor.cpp3
-rw-r--r--modules/openxr/editor/openxr_action_set_editor.h2
-rw-r--r--modules/openxr/editor/openxr_interaction_profile_editor.cpp3
-rw-r--r--modules/openxr/editor/openxr_interaction_profile_editor.h2
-rw-r--r--modules/openxr/extensions/openxr_android_extension.cpp12
-rw-r--r--modules/openxr/extensions/openxr_opengl_extension.cpp1
-rw-r--r--modules/openxr/extensions/openxr_pico_controller_extension.cpp98
-rw-r--r--modules/openxr/extensions/openxr_pico_controller_extension.h48
-rw-r--r--modules/openxr/openxr_api.cpp8
-rw-r--r--modules/openxr/openxr_api.h1
-rw-r--r--modules/openxr/register_types.cpp2
-rw-r--r--modules/openxr/util.h6
-rw-r--r--modules/raycast/SCsub2
-rw-r--r--modules/text_server_adv/text_server_adv.cpp2
-rw-r--r--modules/text_server_fb/text_server_fb.cpp2
-rw-r--r--modules/webxr/doc_classes/WebXRInterface.xml4
154 files changed, 1935 insertions, 994 deletions
diff --git a/modules/csg/editor/csg_gizmos.cpp b/modules/csg/editor/csg_gizmos.cpp
index 61787691a3..2c533cb36d 100644
--- a/modules/csg/editor/csg_gizmos.cpp
+++ b/modules/csg/editor/csg_gizmos.cpp
@@ -217,7 +217,7 @@ void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Sphere Shape Radius"));
ur->add_do_method(s, "set_radius", s->get_radius());
ur->add_undo_method(s, "set_radius", p_restore);
@@ -231,7 +231,7 @@ void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Box Shape Size"));
ur->add_do_method(s, "set_size", s->get_size());
ur->add_undo_method(s, "set_size", p_restore);
@@ -249,7 +249,7 @@ void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
if (p_id == 0) {
ur->create_action(TTR("Change Cylinder Radius"));
ur->add_do_method(s, "set_radius", s->get_radius());
@@ -274,7 +274,7 @@ void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
if (p_id == 0) {
ur->create_action(TTR("Change Torus Inner Radius"));
ur->add_do_method(s, "set_inner_radius", s->get_inner_radius());
diff --git a/modules/etcpak/image_compress_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp
index 1fd98a43d4..b5192bd664 100644
--- a/modules/etcpak/image_compress_etcpak.cpp
+++ b/modules/etcpak/image_compress_etcpak.cpp
@@ -118,6 +118,7 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_qua
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
target_format = Image::FORMAT_ETC2_RA_AS_RG;
r_img->convert_rg_to_ra_rgba8();
+ r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA) {
target_format = Image::FORMAT_ETC2_RGBA8;
r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
@@ -222,9 +223,9 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_qua
}
if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1) {
CompressEtc1RgbDither(src_mip_read, dest_mip_write, blocks, mip_w);
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2 || p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
+ } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2) {
CompressEtc2Rgb(src_mip_read, dest_mip_write, blocks, mip_w, true);
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA) {
+ } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA || p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
CompressEtc2Rgba(src_mip_read, dest_mip_write, blocks, mip_w, true);
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT1) {
CompressDxt1Dither(src_mip_read, dest_mip_write, blocks, mip_w);
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 7b79c9cf7e..28f478e9cd 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -1037,7 +1037,7 @@ String GDScript::get_script_path() const {
}
Error GDScript::load_source_code(const String &p_path) {
- if (p_path.is_empty() || ResourceLoader::get_resource_type(p_path.get_slice("::", 0)) == "PackedScene") {
+ if (p_path.is_empty() || p_path.begins_with("gdscript://") || ResourceLoader::get_resource_type(p_path.get_slice("::", 0)) == "PackedScene") {
return OK;
}
@@ -1363,6 +1363,8 @@ GDScript::GDScript() :
GDScriptLanguage::get_singleton()->script_list.add(&script_list);
}
+
+ path = vformat("gdscript://%d.gd", get_instance_id());
}
void GDScript::_save_orphaned_subclasses(GDScript::ClearData *p_clear_data) {
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index c2fd3b8a5d..edd94da824 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -68,9 +68,6 @@ static MethodInfo info_from_utility_func(const StringName &p_function) {
pi.name = "arg" + itos(i + 1);
#endif
pi.type = Variant::get_utility_function_argument_type(p_function, i);
- if (pi.type == Variant::NIL) {
- pi.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- }
info.arguments.push_back(pi);
}
}
@@ -103,8 +100,21 @@ static GDScriptParser::DataType make_native_meta_type(const StringName &p_class_
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
type.kind = GDScriptParser::DataType::NATIVE;
type.builtin_type = Variant::OBJECT;
- type.is_constant = true;
type.native_type = p_class_name;
+ type.is_constant = true;
+ type.is_meta_type = true;
+ return type;
+}
+
+static GDScriptParser::DataType make_script_meta_type(const Ref<Script> &p_script) {
+ GDScriptParser::DataType type;
+ type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ type.kind = GDScriptParser::DataType::SCRIPT;
+ type.builtin_type = Variant::OBJECT;
+ type.native_type = p_script->get_instance_base_type();
+ type.script_type = p_script;
+ type.script_path = p_script->get_path();
+ type.is_constant = true;
type.is_meta_type = true;
return type;
}
@@ -250,9 +260,13 @@ Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::C
}
void GDScriptAnalyzer::get_class_node_current_scope_classes(GDScriptParser::ClassNode *p_node, List<GDScriptParser::ClassNode *> *p_list) {
+ ERR_FAIL_NULL(p_node);
+ ERR_FAIL_NULL(p_list);
+
if (p_list->find(p_node) != nullptr) {
return;
}
+
p_list->push_back(p_node);
// TODO: Try to solve class inheritance if not yet resolving.
@@ -417,7 +431,7 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
return err;
}
base = info_parser->get_parser()->head->get_datatype();
- } else if (class_exists(name) && ClassDB::can_instantiate(name)) {
+ } else if (class_exists(name)) {
base.kind = GDScriptParser::DataType::NATIVE;
base.native_type = name;
} else {
@@ -577,11 +591,8 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
result.builtin_type = GDScriptParser::get_builtin_type(first);
if (result.builtin_type == Variant::ARRAY) {
- GDScriptParser::DataType container_type = resolve_datatype(p_type->container_type);
-
+ GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->container_type));
if (container_type.kind != GDScriptParser::DataType::VARIANT) {
- container_type.is_meta_type = false;
- container_type.is_constant = false;
result.set_container_element_type(container_type);
}
}
@@ -603,12 +614,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
}
result = ref->get_parser()->head->get_datatype();
} else {
- result.kind = GDScriptParser::DataType::SCRIPT;
- result.script_type = ResourceLoader::load(path, "Script");
- result.native_type = result.script_type->get_instance_base_type();
- result.script_path = path;
- result.is_constant = true;
- result.is_meta_type = false;
+ result = make_script_meta_type(ResourceLoader::load(path, "Script"));
}
}
} else if (ProjectSettings::get_singleton()->has_autoload(first) && ProjectSettings::get_singleton()->get_autoload(first).is_singleton) {
@@ -652,7 +658,6 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
case GDScriptParser::ClassNode::Member::CONSTANT:
if (member.get_datatype().is_meta_type) {
result = member.get_datatype();
- result.is_meta_type = false;
found = true;
break;
} else if (Ref<Script>(member.constant->initializer->reduced_value).is_valid()) {
@@ -664,15 +669,8 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
return bad_type;
}
result = ref->get_parser()->head->get_datatype();
- result.is_meta_type = false;
} else {
- Ref<Script> script = member.constant->initializer->reduced_value;
- result.kind = GDScriptParser::DataType::SCRIPT;
- result.builtin_type = Variant::OBJECT;
- result.script_type = script;
- result.script_path = script->get_path();
- result.native_type = script->get_instance_base_type();
- result.is_meta_type = false;
+ result = make_script_meta_type(member.constant->initializer->reduced_value);
}
found = true;
break;
@@ -828,8 +826,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
for (int j = 0; j < member.signal->parameters.size(); j++) {
GDScriptParser::ParameterNode *param = member.signal->parameters[j];
- GDScriptParser::DataType param_type = resolve_datatype(param->datatype_specifier);
- param_type.is_meta_type = false;
+ GDScriptParser::DataType param_type = type_from_metatype(resolve_datatype(param->datatype_specifier));
param->set_datatype(param_type);
mi.arguments.push_back(PropertyInfo(param_type.builtin_type, param->identifier->name));
// TODO: add signal parameter default values
@@ -892,11 +889,11 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
resolve_function_signature(member.function, p_source);
break;
case GDScriptParser::ClassNode::Member::ENUM_VALUE: {
+ member.enum_value.identifier->set_datatype(resolving_datatype);
+
if (member.enum_value.custom_value) {
check_class_member_name_conflict(p_class, member.enum_value.identifier->name, member.enum_value.custom_value);
- member.enum_value.identifier->set_datatype(resolving_datatype);
-
const GDScriptParser::EnumNode *prev_enum = current_enum;
current_enum = member.enum_value.parent_enum;
reduce_expression(member.enum_value.custom_value);
@@ -1514,14 +1511,12 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
GDScriptParser::DataType type;
type.kind = GDScriptParser::DataType::VARIANT;
- bool is_variable = p_assignable->type == GDScriptParser::Node::VARIABLE;
bool is_constant = p_assignable->type == GDScriptParser::Node::CONSTANT;
GDScriptParser::DataType specified_type;
bool has_specified_type = p_assignable->datatype_specifier != nullptr;
if (has_specified_type) {
- specified_type = resolve_datatype(p_assignable->datatype_specifier);
- specified_type.is_meta_type = false;
+ specified_type = type_from_metatype(resolve_datatype(p_assignable->datatype_specifier));
type = specified_type;
}
@@ -1577,13 +1572,14 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
} else if (!specified_type.is_variant()) {
if (initializer_type.is_variant() || !initializer_type.is_hard_type()) {
mark_node_unsafe(p_assignable->initializer);
- if (is_variable) {
- static_cast<GDScriptParser::VariableNode *>(p_assignable)->use_conversion_assign = true;
+ p_assignable->use_conversion_assign = true;
+ if (!initializer_type.is_variant() && !is_type_compatible(specified_type, initializer_type, true, p_assignable->initializer)) {
+ downgrade_node_type_source(p_assignable->initializer);
}
} else if (!is_type_compatible(specified_type, initializer_type, true, p_assignable->initializer)) {
- if (is_variable && is_type_compatible(initializer_type, specified_type, true, p_assignable->initializer)) {
+ if (!is_constant && is_type_compatible(initializer_type, specified_type, true, p_assignable->initializer)) {
mark_node_unsafe(p_assignable->initializer);
- static_cast<GDScriptParser::VariableNode *>(p_assignable)->use_conversion_assign = true;
+ p_assignable->use_conversion_assign = true;
} else {
push_error(vformat(R"(Cannot assign a value of type %s to %s "%s" with specified type %s.)", initializer_type.to_string(), p_kind, p_assignable->identifier->name, specified_type.to_string()), p_assignable->initializer);
}
@@ -1726,21 +1722,52 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
if (list_resolved) {
variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
variable_type.kind = GDScriptParser::DataType::BUILTIN;
- variable_type.builtin_type = Variant::INT; // Can this ever be a float or something else?
- p_for->variable->set_datatype(variable_type);
+ variable_type.builtin_type = Variant::INT;
} else if (p_for->list) {
resolve_node(p_for->list, false);
- if (p_for->list->datatype.has_container_element_type()) {
- variable_type = p_for->list->datatype.get_container_element_type();
- variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
- } else if (p_for->list->datatype.is_typed_container_type()) {
- variable_type = p_for->list->datatype.get_typed_container_type();
- variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
- } else {
- // Last resort
- // TODO: Must other cases be handled? Must we mark as unsafe?
- variable_type.type_source = GDScriptParser::DataType::UNDETECTED;
+ GDScriptParser::DataType list_type = p_for->list->get_datatype();
+ if (!list_type.is_hard_type()) {
+ mark_node_unsafe(p_for->list);
+ }
+ if (list_type.is_variant()) {
variable_type.kind = GDScriptParser::DataType::VARIANT;
+ mark_node_unsafe(p_for->list);
+ } else if (list_type.has_container_element_type()) {
+ variable_type = list_type.get_container_element_type();
+ variable_type.type_source = list_type.type_source;
+ } else if (list_type.is_typed_container_type()) {
+ variable_type = list_type.get_typed_container_type();
+ variable_type.type_source = list_type.type_source;
+ } else if (list_type.builtin_type == Variant::INT || list_type.builtin_type == Variant::FLOAT || list_type.builtin_type == Variant::STRING) {
+ variable_type.type_source = list_type.type_source;
+ variable_type.kind = GDScriptParser::DataType::BUILTIN;
+ variable_type.builtin_type = list_type.builtin_type;
+ } else if (list_type.builtin_type == Variant::VECTOR2I || list_type.builtin_type == Variant::VECTOR3I) {
+ variable_type.type_source = list_type.type_source;
+ variable_type.kind = GDScriptParser::DataType::BUILTIN;
+ variable_type.builtin_type = Variant::INT;
+ } else if (list_type.builtin_type == Variant::VECTOR2 || list_type.builtin_type == Variant::VECTOR3) {
+ variable_type.type_source = list_type.type_source;
+ variable_type.kind = GDScriptParser::DataType::BUILTIN;
+ variable_type.builtin_type = Variant::FLOAT;
+ } else if (list_type.builtin_type == Variant::OBJECT) {
+ GDScriptParser::DataType return_type;
+ List<GDScriptParser::DataType> par_types;
+ int default_arg_count = 0;
+ bool is_static = false;
+ bool is_vararg = false;
+ if (get_function_signature(p_for->list, false, list_type, CoreStringNames::get_singleton()->_iter_get, return_type, par_types, default_arg_count, is_static, is_vararg)) {
+ variable_type = return_type;
+ variable_type.type_source = list_type.type_source;
+ } else if (!list_type.is_hard_type()) {
+ variable_type.kind = GDScriptParser::DataType::VARIANT;
+ } else {
+ push_error(vformat(R"(Unable to iterate on object of type "%s".)", list_type.to_string()), p_for->list);
+ }
+ } else if (list_type.builtin_type == Variant::ARRAY || list_type.builtin_type == Variant::DICTIONARY || !list_type.is_hard_type()) {
+ variable_type.kind = GDScriptParser::DataType::VARIANT;
+ } else {
+ push_error(vformat(R"(Unable to iterate on value of type "%s".)", list_type.to_string()), p_for->list);
}
}
if (p_for->variable) {
@@ -2104,84 +2131,86 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
GDScriptParser::DataType assigned_value_type = p_assignment->assigned_value->get_datatype();
+ bool assignee_is_variant = assignee_type.is_variant();
+ bool assignee_is_hard = assignee_type.is_hard_type();
+ bool assigned_is_variant = assigned_value_type.is_variant();
+ bool assigned_is_hard = assigned_value_type.is_hard_type();
bool compatible = true;
+ bool downgrades_assignee = false;
+ bool downgrades_assigned = false;
GDScriptParser::DataType op_type = assigned_value_type;
- if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
+ if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE && !op_type.is_variant()) {
op_type = get_operation_type(p_assignment->variant_op, assignee_type, assigned_value_type, compatible, p_assignment->assigned_value);
+
+ if (assignee_is_variant) {
+ // variant assignee
+ mark_node_unsafe(p_assignment);
+ } else if (!compatible) {
+ // incompatible hard types and non-variant assignee
+ mark_node_unsafe(p_assignment);
+ if (assigned_is_variant) {
+ // incompatible hard non-variant assignee and hard variant assigned
+ p_assignment->use_conversion_assign = true;
+ } else {
+ // incompatible hard non-variant types
+ push_error(vformat(R"(Invalid operands "%s" and "%s" for assignment operator.)", assignee_type.to_string(), assigned_value_type.to_string()), p_assignment);
+ }
+ } else if (op_type.type_source == GDScriptParser::DataType::UNDETECTED && !assigned_is_variant) {
+ // incompatible non-variant types (at least one weak)
+ downgrades_assignee = !assignee_is_hard;
+ downgrades_assigned = !assigned_is_hard;
+ }
}
p_assignment->set_datatype(op_type);
- // If Assignee is a variant, then you can assign anything
- // When the assigned value has a known type, further checks are possible.
- if (assignee_type.is_hard_type() && !assignee_type.is_variant() && op_type.is_hard_type()) {
- if (compatible) {
- compatible = is_type_compatible(assignee_type, op_type, true, p_assignment->assigned_value);
- if (!compatible) {
- // Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(op_type, assignee_type, true)) {
- push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value);
- } else {
- // TODO: Add warning.
- mark_node_unsafe(p_assignment);
- p_assignment->use_conversion_assign = true;
- }
- }
- } else {
- push_error(vformat(R"(Invalid operands "%s" and "%s" for assignment operator.)", assignee_type.to_string(), assigned_value_type.to_string()), p_assignment);
+ if (assignee_is_variant) {
+ if (!assignee_is_hard) {
+ // weak variant assignee
+ mark_node_unsafe(p_assignment);
}
- } else if (assignee_type.is_hard_type() && !assignee_type.is_variant()) {
- mark_node_unsafe(p_assignment);
- p_assignment->use_conversion_assign = true;
} else {
- mark_node_unsafe(p_assignment);
- if (assignee_type.is_hard_type() && !assignee_type.is_variant()) {
+ if (assignee_is_hard && !assigned_is_hard) {
+ // hard non-variant assignee and weak assigned
+ mark_node_unsafe(p_assignment);
p_assignment->use_conversion_assign = true;
- }
- }
-
- if (p_assignment->assignee->type == GDScriptParser::Node::IDENTIFIER) {
- // Change source type so it's not wrongly detected later.
- GDScriptParser::IdentifierNode *identifier = static_cast<GDScriptParser::IdentifierNode *>(p_assignment->assignee);
-
- switch (identifier->source) {
- case GDScriptParser::IdentifierNode::MEMBER_VARIABLE: {
- GDScriptParser::DataType id_type = identifier->variable_source->get_datatype();
- if (!id_type.is_hard_type()) {
- id_type.kind = GDScriptParser::DataType::VARIANT;
- id_type.type_source = GDScriptParser::DataType::UNDETECTED;
- identifier->variable_source->set_datatype(id_type);
- }
- } break;
- case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER: {
- GDScriptParser::DataType id_type = identifier->parameter_source->get_datatype();
- if (!id_type.is_hard_type()) {
- id_type.kind = GDScriptParser::DataType::VARIANT;
- id_type.type_source = GDScriptParser::DataType::UNDETECTED;
- identifier->parameter_source->set_datatype(id_type);
- }
- } break;
- case GDScriptParser::IdentifierNode::LOCAL_VARIABLE: {
- GDScriptParser::DataType id_type = identifier->variable_source->get_datatype();
- if (!id_type.is_hard_type()) {
- id_type.kind = GDScriptParser::DataType::VARIANT;
- id_type.type_source = GDScriptParser::DataType::UNDETECTED;
- identifier->variable_source->set_datatype(id_type);
+ downgrades_assigned = downgrades_assigned || (!assigned_is_variant && !is_type_compatible(assignee_type, op_type, true, p_assignment->assigned_value));
+ } else if (compatible) {
+ if (op_type.is_variant()) {
+ // non-variant assignee and variant result
+ mark_node_unsafe(p_assignment);
+ if (assignee_is_hard) {
+ // hard non-variant assignee and variant result
+ p_assignment->use_conversion_assign = true;
+ } else {
+ // weak non-variant assignee and variant result
+ downgrades_assignee = true;
}
- } break;
- case GDScriptParser::IdentifierNode::LOCAL_ITERATOR: {
- GDScriptParser::DataType id_type = identifier->bind_source->get_datatype();
- if (!id_type.is_hard_type()) {
- id_type.kind = GDScriptParser::DataType::VARIANT;
- id_type.type_source = GDScriptParser::DataType::UNDETECTED;
- identifier->bind_source->set_datatype(id_type);
+ } else if (!is_type_compatible(assignee_type, op_type, assignee_is_hard, p_assignment->assigned_value)) {
+ // non-variant assignee and incompatible result
+ mark_node_unsafe(p_assignment);
+ if (assignee_is_hard) {
+ if (is_type_compatible(op_type, assignee_type, true, p_assignment->assigned_value)) {
+ // hard non-variant assignee and maybe compatible result
+ p_assignment->use_conversion_assign = true;
+ } else {
+ // hard non-variant assignee and incompatible result
+ push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value);
+ }
+ } else {
+ // weak non-variant assignee and incompatible result
+ downgrades_assignee = true;
}
- } break;
- default:
- // Nothing to do.
- break;
+ }
}
}
+ if (downgrades_assignee) {
+ downgrade_node_type_source(p_assignment->assignee);
+ }
+ if (downgrades_assigned) {
+ downgrade_node_type_source(p_assignment->assigned_value);
+ }
+
#ifdef DEBUG_ENABLED
if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_value_type.builtin_type == Variant::FLOAT) {
parser->push_warning(p_assignment->assigned_value, GDScriptWarning::NARROWING_CONVERSION);
@@ -2460,7 +2489,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
bool types_match = true;
for (int i = 0; i < p_call->arguments.size(); i++) {
- GDScriptParser::DataType par_type = type_from_property(info.arguments[i]);
+ GDScriptParser::DataType par_type = type_from_property(info.arguments[i], true);
if (!is_type_compatible(par_type, p_call->arguments[i]->get_datatype(), true)) {
types_match = false;
@@ -2517,7 +2546,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
PropertyInfo wrong_arg = function_info.arguments[err.argument];
push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*", function_name, err.argument + 1,
- type_from_property(wrong_arg).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
+ type_from_property(wrong_arg, true).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
p_call->arguments[err.argument]);
} break;
case Callable::CallError::CALL_ERROR_INVALID_METHOD:
@@ -2564,7 +2593,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
String expected_type_name;
if (err.argument < function_info.arguments.size()) {
- expected_type_name = type_from_property(function_info.arguments[err.argument]).to_string();
+ expected_type_name = type_from_property(function_info.arguments[err.argument], true).to_string();
} else {
expected_type_name = Variant::get_type_name((Variant::Type)err.expected);
}
@@ -2645,6 +2674,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
} else {
reduce_expression(subscript->base);
base_type = subscript->base->get_datatype();
+ is_self = subscript->base->type == GDScriptParser::Node::SELF;
}
} else {
// Invalid call. Error already sent in parser.
@@ -2762,14 +2792,13 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
reduce_expression(p_cast->operand);
- GDScriptParser::DataType cast_type = resolve_datatype(p_cast->cast_type);
+ GDScriptParser::DataType cast_type = type_from_metatype(resolve_datatype(p_cast->cast_type));
if (!cast_type.is_set()) {
mark_node_unsafe(p_cast);
return;
}
- cast_type = type_from_metatype(cast_type); // The casted value won't be a type name.
p_cast->set_datatype(cast_type);
if (!cast_type.is_variant()) {
@@ -2903,18 +2932,22 @@ GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const Str
return ref->get_parser()->head->get_datatype();
} else {
- type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- type.kind = GDScriptParser::DataType::SCRIPT;
- type.builtin_type = Variant::OBJECT;
- type.script_type = ResourceLoader::load(path, "Script");
- type.native_type = type.script_type->get_instance_base_type();
- type.script_path = path;
- type.is_constant = true;
- type.is_meta_type = true;
- return type;
+ return make_script_meta_type(ResourceLoader::load(path, "Script"));
}
}
+void GDScriptAnalyzer::reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype) {
+ ERR_FAIL_NULL(p_identifier);
+
+ p_identifier->set_datatype(p_identifier_datatype);
+ Error err = OK;
+ GDScript *scr = GDScriptCache::get_shallow_script(p_identifier_datatype.script_path, err).ptr();
+ ERR_FAIL_COND_MSG(err != OK, vformat(R"(Error while getting cache for script "%s".)", p_identifier_datatype.script_path));
+ scr = scr->find_class(p_identifier_datatype.class_type->fqcn);
+ p_identifier->reduced_value = scr;
+ p_identifier->is_constant = true;
+}
+
void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType *p_base) {
if (!p_identifier->get_datatype().has_no_type()) {
return;
@@ -2993,108 +3026,91 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
}
GDScriptParser::ClassNode *base_class = base.class_type;
+ List<GDScriptParser::ClassNode *> script_classes;
+ bool is_base = true;
- // TODO: Switch current class/function/suite here to avoid misrepresenting identifiers (in recursive reduce calls).
- while (base_class != nullptr) {
- if (base_class->identifier && base_class->identifier->name == name) {
- p_identifier->set_datatype(base_class->get_datatype());
+ if (base_class != nullptr) {
+ get_class_node_current_scope_classes(base_class, &script_classes);
+ }
+
+ for (GDScriptParser::ClassNode *script_class : script_classes) {
+ if (p_base == nullptr && script_class->identifier && script_class->identifier->name == name) {
+ reduce_identifier_from_base_set_class(p_identifier, script_class->get_datatype());
return;
}
- if (base_class->has_member(name)) {
- resolve_class_member(base_class, name, p_identifier);
+ if (script_class->has_member(name)) {
+ resolve_class_member(script_class, name, p_identifier);
- GDScriptParser::ClassNode::Member member = base_class->get_member(name);
- p_identifier->set_datatype(member.get_datatype());
+ GDScriptParser::ClassNode::Member member = script_class->get_member(name);
switch (member.type) {
- case GDScriptParser::ClassNode::Member::CONSTANT:
+ case GDScriptParser::ClassNode::Member::CONSTANT: {
+ p_identifier->set_datatype(member.get_datatype());
p_identifier->is_constant = true;
p_identifier->reduced_value = member.constant->initializer->reduced_value;
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
p_identifier->constant_source = member.constant;
- break;
- case GDScriptParser::ClassNode::Member::ENUM_VALUE:
+ return;
+ }
+
+ case GDScriptParser::ClassNode::Member::ENUM_VALUE: {
+ p_identifier->set_datatype(member.get_datatype());
p_identifier->is_constant = true;
p_identifier->reduced_value = member.enum_value.value;
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
- break;
- case GDScriptParser::ClassNode::Member::ENUM:
+ return;
+ }
+
+ case GDScriptParser::ClassNode::Member::ENUM: {
+ p_identifier->set_datatype(member.get_datatype());
p_identifier->is_constant = true;
p_identifier->reduced_value = member.m_enum->dictionary;
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
- break;
- case GDScriptParser::ClassNode::Member::VARIABLE:
- p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
- p_identifier->variable_source = member.variable;
- member.variable->usages += 1;
- break;
- case GDScriptParser::ClassNode::Member::SIGNAL:
- p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
- break;
- case GDScriptParser::ClassNode::Member::FUNCTION:
- p_identifier->set_datatype(make_callable_type(member.function->info));
- break;
- case GDScriptParser::ClassNode::Member::CLASS:
- if (p_base != nullptr && p_base->is_constant) {
- p_identifier->is_constant = true;
- p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
-
- Error err = OK;
- GDScript *scr = GDScriptCache::get_full_script(base.script_path, err).ptr();
- ERR_FAIL_COND_MSG(err != OK, "Error while getting subscript full script.");
- scr = scr->find_class(p_identifier->get_datatype().class_type->fqcn);
- p_identifier->reduced_value = scr;
- }
- break;
- default:
- break; // Type already set.
- }
- return;
- }
-
- // Check outer constants.
- // TODO: Allow outer static functions.
- if (base_class->outer != nullptr) {
- List<GDScriptParser::ClassNode *> script_classes;
- get_class_node_current_scope_classes(base_class->outer, &script_classes);
- for (GDScriptParser::ClassNode *script_class : script_classes) {
- if (script_class->identifier && script_class->identifier->name == name) {
- p_identifier->set_datatype(script_class->get_datatype());
return;
}
- if (script_class->has_member(name)) {
- resolve_class_member(script_class, name, p_identifier);
+ case GDScriptParser::ClassNode::Member::VARIABLE: {
+ if (is_base && !base.is_meta_type) {
+ p_identifier->set_datatype(member.get_datatype());
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
+ p_identifier->variable_source = member.variable;
+ member.variable->usages += 1;
+ return;
+ }
+ } break;
- GDScriptParser::ClassNode::Member member = script_class->get_member(name);
- switch (member.type) {
- case GDScriptParser::ClassNode::Member::CONSTANT:
- // TODO: Make sure loops won't cause problem. And make special error message for those.
- p_identifier->set_datatype(member.get_datatype());
- p_identifier->is_constant = true;
- p_identifier->reduced_value = member.constant->initializer->reduced_value;
- return;
- case GDScriptParser::ClassNode::Member::ENUM_VALUE:
- p_identifier->set_datatype(member.get_datatype());
- p_identifier->is_constant = true;
- p_identifier->reduced_value = member.enum_value.value;
- return;
- case GDScriptParser::ClassNode::Member::ENUM:
- p_identifier->set_datatype(member.get_datatype());
- p_identifier->is_constant = true;
- p_identifier->reduced_value = member.m_enum->dictionary;
- return;
- case GDScriptParser::ClassNode::Member::CLASS:
- p_identifier->set_datatype(member.get_datatype());
- return;
- default:
- break;
+ case GDScriptParser::ClassNode::Member::SIGNAL: {
+ if (is_base && !base.is_meta_type) {
+ p_identifier->set_datatype(member.get_datatype());
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
+ return;
}
+ } break;
+
+ case GDScriptParser::ClassNode::Member::FUNCTION: {
+ if (is_base && !base.is_meta_type) {
+ p_identifier->set_datatype(make_callable_type(member.function->info));
+ return;
+ }
+ } break;
+
+ case GDScriptParser::ClassNode::Member::CLASS: {
+ reduce_identifier_from_base_set_class(p_identifier, member.get_datatype());
+ return;
+ }
+
+ default: {
+ // Do nothing
}
}
}
- base_class = base_class->base_type.class_type;
+ if (is_base) {
+ is_base = script_class->base_type.class_type != nullptr;
+ if (!is_base && p_base != nullptr) {
+ break;
+ }
+ }
}
// Check native members. No need for native class recursion because Node exposes all Object's properties.
@@ -3225,18 +3241,20 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
}
if (found_source) {
- if ((p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) && parser->current_function && parser->current_function->is_static) {
+ bool source_is_variable = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
+ bool source_is_signal = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
+ if ((source_is_variable || source_is_signal) && parser->current_function && parser->current_function->is_static) {
// Get the parent function above any lambda.
GDScriptParser::FunctionNode *parent_function = parser->current_function;
while (parent_function->source_lambda) {
parent_function = parent_function->source_lambda->parent_function;
}
- push_error(vformat(R"*(Cannot access instance variable "%s" from the static function "%s()".)*", p_identifier->name, parent_function->identifier->name), p_identifier);
+ push_error(vformat(R"*(Cannot access %s "%s" from the static function "%s()".)*", source_is_signal ? "signal" : "instance variable", p_identifier->name, parent_function->identifier->name), p_identifier);
}
if (!lambda_stack.is_empty()) {
- // If the identifier is a member variable (including the native class properties), we consider the lambda to be using `self`, so we keep a reference to the current instance.
- if (p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) {
+ // If the identifier is a member variable (including the native class properties) or a signal, we consider the lambda to be using `self`, so we keep a reference to the current instance.
+ if (source_is_variable || source_is_signal) {
mark_lambda_use_self();
return; // No need to capture.
}
@@ -3534,12 +3552,12 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
Variant value = p_subscript->base->reduced_value.get(p_subscript->index->reduced_value, &valid);
if (!valid) {
push_error(vformat(R"(Cannot get index "%s" from "%s".)", p_subscript->index->reduced_value, p_subscript->base->reduced_value), p_subscript->index);
+ result_type.kind = GDScriptParser::DataType::VARIANT;
} else {
p_subscript->is_constant = true;
p_subscript->reduced_value = value;
result_type = type_from_variant(value, p_subscript);
}
- result_type.kind = GDScriptParser::DataType::VARIANT;
} else {
GDScriptParser::DataType base_type = p_subscript->base->get_datatype();
GDScriptParser::DataType index_type = p_subscript->index->get_datatype();
@@ -3800,8 +3818,6 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op)
}
void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array, bool p_is_const) {
- bool all_is_constant = true;
-
for (int i = 0; i < p_array->elements.size(); i++) {
GDScriptParser::ExpressionNode *element = p_array->elements[i];
@@ -3811,8 +3827,7 @@ void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array, bool
const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element), p_is_const);
}
- all_is_constant = all_is_constant && element->is_constant;
- if (!all_is_constant) {
+ if (!element->is_constant) {
return;
}
}
@@ -3830,8 +3845,6 @@ void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array, bool
}
void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary, bool p_is_const) {
- bool all_is_constant = true;
-
for (int i = 0; i < p_dictionary->elements.size(); i++) {
const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
@@ -3841,8 +3854,7 @@ void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_d
const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element.value), p_is_const);
}
- all_is_constant = all_is_constant && element.key->is_constant && element.value->is_constant;
- if (!all_is_constant) {
+ if (!element.key->is_constant || !element.value->is_constant) {
return;
}
}
@@ -3943,10 +3955,10 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_metatype(const GDScriptPars
return result;
}
-GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo &p_property) const {
+GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo &p_property, bool p_is_arg) const {
GDScriptParser::DataType result;
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- if (p_property.type == Variant::NIL && (p_property.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) {
+ if (p_property.type == Variant::NIL && (p_is_arg || (p_property.usage & PROPERTY_USAGE_NIL_IS_VARIANT))) {
// Variant
result.kind = GDScriptParser::DataType::VARIANT;
return result;
@@ -4035,6 +4047,25 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
return false;
}
+ StringName base_native = p_base_type.native_type;
+ if (base_native != StringName()) {
+ // Empty native class might happen in some Script implementations.
+ // Just ignore it.
+ if (!class_exists(base_native)) {
+ push_error(vformat("Native class %s used in script doesn't exist or isn't exposed.", base_native), p_source);
+ return false;
+ } else if (p_is_constructor && !ClassDB::can_instantiate(base_native)) {
+ if (p_base_type.kind == GDScriptParser::DataType::CLASS) {
+ push_error(vformat(R"(Class "%s" cannot be constructed as it is based on abstract native class "%s".)", p_base_type.class_type->fqcn.get_file(), base_native), p_source);
+ } else if (p_base_type.kind == GDScriptParser::DataType::SCRIPT) {
+ push_error(vformat(R"(Script "%s" cannot be constructed as it is based on abstract native class "%s".)", p_base_type.script_path.get_file(), base_native), p_source);
+ } else {
+ push_error(vformat(R"(Native class "%s" cannot be constructed as it is abstract.)", base_native), p_source);
+ }
+ return false;
+ }
+ }
+
if (p_is_constructor) {
function_name = "_init";
r_static = true;
@@ -4095,17 +4126,6 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
}
}
- StringName base_native = p_base_type.native_type;
-#ifdef DEBUG_ENABLED
- if (base_native != StringName()) {
- // Empty native class might happen in some Script implementations.
- // Just ignore it.
- if (!class_exists(base_native)) {
- ERR_FAIL_V_MSG(false, vformat("Native class %s used in script doesn't exist or isn't exposed.", base_native));
- }
- }
-#endif
-
if (p_is_constructor) {
// Native types always have a default constructor.
r_return_type = p_base_type;
@@ -4133,7 +4153,7 @@ bool GDScriptAnalyzer::function_signature_from_info(const MethodInfo &p_info, GD
r_static = (p_info.flags & METHOD_FLAG_STATIC) != 0;
for (const PropertyInfo &E : p_info.arguments) {
- r_par_types.push_back(type_from_property(E));
+ r_par_types.push_back(type_from_property(E, true));
}
return true;
}
@@ -4142,7 +4162,7 @@ bool GDScriptAnalyzer::validate_call_arg(const MethodInfo &p_method, const GDScr
List<GDScriptParser::DataType> arg_types;
for (const PropertyInfo &E : p_method.arguments) {
- arg_types.push_back(type_from_property(E));
+ arg_types.push_back(type_from_property(E, true));
}
return validate_call_arg(arg_types, p_method.default_arguments.size(), (p_method.flags & METHOD_FLAG_VARARG) != 0, p_call);
@@ -4260,9 +4280,6 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
}
GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source) {
- GDScriptParser::DataType result;
- result.kind = GDScriptParser::DataType::VARIANT;
-
Variant::Type a_type = p_a.builtin_type;
Variant::Type b_type = p_b.builtin_type;
@@ -4282,28 +4299,18 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
}
Variant::ValidatedOperatorEvaluator op_eval = Variant::get_validated_operator_evaluator(p_operation, a_type, b_type);
-
bool hard_operation = p_a.is_hard_type() && p_b.is_hard_type();
bool validated = op_eval != nullptr;
- if (hard_operation && !validated) {
- r_valid = false;
- return result;
- } else if (hard_operation && validated) {
+ GDScriptParser::DataType result;
+ if (validated) {
r_valid = true;
- result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
+ result.type_source = hard_operation ? GDScriptParser::DataType::ANNOTATED_INFERRED : GDScriptParser::DataType::INFERRED;
result.kind = GDScriptParser::DataType::BUILTIN;
result.builtin_type = Variant::get_operator_return_type(p_operation, a_type, b_type);
- } else if (!hard_operation && !validated) {
- r_valid = true;
- result.type_source = GDScriptParser::DataType::UNDETECTED;
+ } else {
+ r_valid = !hard_operation;
result.kind = GDScriptParser::DataType::VARIANT;
- result.builtin_type = Variant::NIL;
- } else if (!hard_operation && validated) {
- r_valid = true;
- result.type_source = GDScriptParser::DataType::INFERRED;
- result.kind = GDScriptParser::DataType::BUILTIN;
- result.builtin_type = Variant::get_operator_return_type(p_operation, a_type, b_type);
}
return result;
@@ -4479,6 +4486,46 @@ void GDScriptAnalyzer::mark_node_unsafe(const GDScriptParser::Node *p_node) {
#endif
}
+void GDScriptAnalyzer::downgrade_node_type_source(GDScriptParser::Node *p_node) {
+ GDScriptParser::IdentifierNode *identifier = nullptr;
+ if (p_node->type == GDScriptParser::Node::IDENTIFIER) {
+ identifier = static_cast<GDScriptParser::IdentifierNode *>(p_node);
+ } else if (p_node->type == GDScriptParser::Node::SUBSCRIPT) {
+ GDScriptParser::SubscriptNode *subscript = static_cast<GDScriptParser::SubscriptNode *>(p_node);
+ if (subscript->is_attribute) {
+ identifier = subscript->attribute;
+ }
+ }
+ if (identifier == nullptr) {
+ return;
+ }
+
+ GDScriptParser::Node *source = nullptr;
+ switch (identifier->source) {
+ case GDScriptParser::IdentifierNode::MEMBER_VARIABLE: {
+ source = identifier->variable_source;
+ } break;
+ case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER: {
+ source = identifier->parameter_source;
+ } break;
+ case GDScriptParser::IdentifierNode::LOCAL_VARIABLE: {
+ source = identifier->variable_source;
+ } break;
+ case GDScriptParser::IdentifierNode::LOCAL_ITERATOR: {
+ source = identifier->bind_source;
+ } break;
+ default:
+ break;
+ }
+ if (source == nullptr) {
+ return;
+ }
+
+ GDScriptParser::DataType datatype;
+ datatype.kind = GDScriptParser::DataType::VARIANT;
+ source->set_datatype(datatype);
+}
+
void GDScriptAnalyzer::mark_lambda_use_self() {
for (GDScriptParser::LambdaNode *lambda : lambda_stack) {
lambda->use_self = true;
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index a90a70dd9b..b22d47982f 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -108,7 +108,7 @@ class GDScriptAnalyzer {
// Helpers.
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) const;
+ GDScriptParser::DataType type_from_property(const PropertyInfo &p_property, bool p_is_arg = 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);
@@ -120,9 +120,11 @@ class GDScriptAnalyzer {
bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr);
void push_error(const String &p_message, const GDScriptParser::Node *p_origin = nullptr);
void mark_node_unsafe(const GDScriptParser::Node *p_node);
+ void downgrade_node_type_source(GDScriptParser::Node *p_node);
void mark_lambda_use_self();
bool class_exists(const StringName &p_class) const;
Ref<GDScriptParserRef> get_parser_for(const String &p_path);
+ static void reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype);
#ifdef DEBUG_ENABLED
bool is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context);
#endif
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index 15a17edb65..6c80fb7665 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -872,8 +872,12 @@ void GDScriptByteCodeGenerator::write_assign_false(const Address &p_target) {
append(p_target);
}
-void GDScriptByteCodeGenerator::write_assign_default_parameter(const Address &p_dst, const Address &p_src) {
- write_assign(p_dst, p_src);
+void GDScriptByteCodeGenerator::write_assign_default_parameter(const Address &p_dst, const Address &p_src, bool p_use_conversion) {
+ if (p_use_conversion) {
+ write_assign_with_conversion(p_dst, p_src);
+ } else {
+ write_assign(p_dst, p_src);
+ }
function->default_arguments.push_back(opcodes.size());
}
@@ -920,11 +924,17 @@ void GDScriptByteCodeGenerator::write_cast(const Address &p_target, const Addres
append(index);
}
-GDScriptCodeGenerator::Address GDScriptByteCodeGenerator::get_call_target(const GDScriptCodeGenerator::Address &p_target) {
+GDScriptCodeGenerator::Address GDScriptByteCodeGenerator::get_call_target(const GDScriptCodeGenerator::Address &p_target, Variant::Type p_type) {
if (p_target.mode == Address::NIL) {
- uint32_t addr = add_temporary(p_target.type);
+ GDScriptDataType type;
+ if (p_type != Variant::NIL) {
+ type.has_type = true;
+ type.kind = GDScriptDataType::BUILTIN;
+ type.builtin_type = p_type;
+ }
+ uint32_t addr = add_temporary(type);
pop_temporary();
- return Address(Address::TEMPORARY, addr, p_target.type);
+ return Address(Address::TEMPORARY, addr, type);
} else {
return p_target;
}
@@ -989,11 +999,17 @@ void GDScriptByteCodeGenerator::write_call_utility(const Address &p_target, cons
}
if (is_validated) {
+ Variant::Type result_type = Variant::has_utility_function_return_value(p_function) ? Variant::get_utility_function_return_type(p_function) : Variant::NIL;
+ Address target = get_call_target(p_target, result_type);
+ Variant::Type temp_type = temporaries[target.address].type;
+ if (result_type != temp_type) {
+ write_type_adjust(target, result_type);
+ }
append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_UTILITY_VALIDATED, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(get_call_target(p_target));
+ append(target);
append(p_arguments.size());
append(Variant::get_validated_utility_function(p_function));
} else {
@@ -1007,7 +1023,7 @@ void GDScriptByteCodeGenerator::write_call_utility(const Address &p_target, cons
}
}
-void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
+void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, bool p_is_static, const Vector<Address> &p_arguments) {
bool is_validated = false;
// Check if all types are correct.
@@ -1027,16 +1043,26 @@ void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target,
if (!is_validated) {
// Perform regular call.
- write_call(p_target, p_base, p_method, p_arguments);
+ if (p_is_static) {
+ append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_BUILTIN_STATIC, p_arguments.size() + 1);
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(get_call_target(p_target));
+ append(p_type);
+ append(p_method);
+ append(p_arguments.size());
+ } else {
+ write_call(p_target, p_base, p_method, p_arguments);
+ }
return;
}
- if (p_target.mode == Address::TEMPORARY) {
- Variant::Type result_type = Variant::get_builtin_method_return_type(p_type, p_method);
- Variant::Type temp_type = temporaries[p_target.address].type;
- if (result_type != temp_type) {
- write_type_adjust(p_target, result_type);
- }
+ Variant::Type result_type = Variant::get_builtin_method_return_type(p_type, p_method);
+ Address target = get_call_target(p_target, result_type);
+ Variant::Type temp_type = temporaries[target.address].type;
+ if (result_type != temp_type) {
+ write_type_adjust(target, result_type);
}
append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_BUILTIN_TYPE_VALIDATED, 2 + p_arguments.size());
@@ -1045,59 +1071,17 @@ void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target,
append(p_arguments[i]);
}
append(p_base);
- append(get_call_target(p_target));
+ append(target);
append(p_arguments.size());
append(Variant::get_validated_builtin_method(p_type, p_method));
}
-void GDScriptByteCodeGenerator::write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
- bool is_validated = false;
-
- // Check if all types are correct.
- if (Variant::is_builtin_method_vararg(p_type, p_method)) {
- is_validated = true; // Vararg works fine with any argument, since they can be any type.
- } else if (p_arguments.size() == Variant::get_builtin_method_argument_count(p_type, p_method)) {
- bool all_types_exact = true;
- for (int i = 0; i < p_arguments.size(); i++) {
- if (!IS_BUILTIN_TYPE(p_arguments[i], Variant::get_builtin_method_argument_type(p_type, p_method, i))) {
- all_types_exact = false;
- break;
- }
- }
-
- is_validated = all_types_exact;
- }
-
- if (!is_validated) {
- // Perform regular call.
- append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_BUILTIN_STATIC, p_arguments.size() + 1);
- for (int i = 0; i < p_arguments.size(); i++) {
- append(p_arguments[i]);
- }
- append(get_call_target(p_target));
- append(p_type);
- append(p_method);
- append(p_arguments.size());
- return;
- }
-
- if (p_target.mode == Address::TEMPORARY) {
- Variant::Type result_type = Variant::get_builtin_method_return_type(p_type, p_method);
- Variant::Type temp_type = temporaries[p_target.address].type;
- if (result_type != temp_type) {
- write_type_adjust(p_target, result_type);
- }
- }
-
- append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_BUILTIN_TYPE_VALIDATED, 2 + p_arguments.size());
+void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
+ write_call_builtin_type(p_target, p_base, p_type, p_method, false, p_arguments);
+}
- for (int i = 0; i < p_arguments.size(); i++) {
- append(p_arguments[i]);
- }
- append(Address()); // No base since it's static.
- append(get_call_target(p_target));
- append(p_arguments.size());
- append(Variant::get_validated_builtin_method(p_type, p_method));
+void GDScriptByteCodeGenerator::write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
+ write_call_builtin_type(p_target, Address(), p_type, p_method, true, p_arguments);
}
void GDScriptByteCodeGenerator::write_call_native_static(const Address &p_target, const StringName &p_class, const StringName &p_method, const Vector<Address> &p_arguments) {
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
index 258b9fb0c2..171c505116 100644
--- a/modules/gdscript/gdscript_byte_codegen.h
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -309,7 +309,7 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
}
}
- Address get_call_target(const Address &p_target);
+ Address get_call_target(const Address &p_target, Variant::Type p_type = Variant::NIL);
int address_of(const Address &p_address) {
switch (p_address.mode) {
@@ -460,7 +460,7 @@ public:
virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) override;
virtual void write_assign_true(const Address &p_target) override;
virtual void write_assign_false(const Address &p_target) override;
- virtual void write_assign_default_parameter(const Address &p_dst, const Address &p_src) override;
+ virtual void write_assign_default_parameter(const Address &p_dst, const Address &p_src, bool p_use_conversion) override;
virtual void write_store_global(const Address &p_dst, int p_global_index) override;
virtual void write_store_named_global(const Address &p_dst, const StringName &p_global) override;
virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) override;
@@ -469,6 +469,7 @@ public:
virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) override;
virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) override;
+ void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, bool p_is_static, const Vector<Address> &p_arguments);
virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override;
virtual void write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override;
virtual void write_call_native_static(const Address &p_target, const StringName &p_class, const StringName &p_method, const Vector<Address> &p_arguments) override;
diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h
index c7a1bcb9e9..e885938eba 100644
--- a/modules/gdscript/gdscript_codegen.h
+++ b/modules/gdscript/gdscript_codegen.h
@@ -113,7 +113,7 @@ public:
virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) = 0;
virtual void write_assign_true(const Address &p_target) = 0;
virtual void write_assign_false(const Address &p_target) = 0;
- virtual void write_assign_default_parameter(const Address &dst, const Address &src) = 0;
+ virtual void write_assign_default_parameter(const Address &dst, const Address &src, bool p_use_conversion) = 0;
virtual void write_store_global(const Address &p_dst, int p_global_index) = 0;
virtual void write_store_named_global(const Address &p_dst, const StringName &p_global) = 0;
virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) = 0;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index beed6e90d2..d63a1b4536 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -213,7 +213,7 @@ static bool _have_exact_arguments(const MethodBind *p_method, const Vector<GDScr
}
GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root, bool p_initializer, const GDScriptCodeGenerator::Address &p_index_addr) {
- if (p_expression->is_constant && !p_expression->get_datatype().is_meta_type) {
+ if (p_expression->is_constant && !(p_expression->get_datatype().is_meta_type && p_expression->get_datatype().kind == GDScriptParser::DataType::CLASS)) {
return codegen.add_constant(p_expression->reduced_value);
}
@@ -2116,7 +2116,7 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
}
}
- codegen.generator->write_assign_default_parameter(dst_addr, src_addr);
+ codegen.generator->write_assign_default_parameter(dst_addr, src_addr, parameter->use_conversion_assign);
if (src_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 0a1ae46927..3fc0924b4c 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -80,7 +80,7 @@ Ref<Script> GDScriptLanguage::make_template(const String &p_template, const Stri
}
processed_template = processed_template.replace("_BASE_", p_base_class_name)
- .replace("_CLASS_", p_class_name)
+ .replace("_CLASS_", p_class_name.to_pascal_case())
.replace("_TS_", _get_indentation());
scr->set_source_code(processed_template);
return scr;
@@ -1275,6 +1275,14 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context
}
r_result.insert(option.display, option);
}
+
+ // Global classes
+ List<StringName> global_classes;
+ ScriptServer::get_global_class_list(&global_classes);
+ for (const StringName &E : global_classes) {
+ ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE);
+ r_result.insert(option.display, option);
+ }
}
static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value) {
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index a6b8537074..f5d3306376 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -540,43 +540,28 @@ void GDScriptParser::parse_program() {
head = alloc_node<ClassNode>();
head->fqcn = script_path;
current_class = head;
+ bool can_have_class_or_extends = true;
- // If we happen to parse an annotation before extends or class_name keywords, track it.
- // @tool is allowed, but others should fail.
- AnnotationNode *premature_annotation = nullptr;
-
- if (match(GDScriptTokenizer::Token::ANNOTATION)) {
- // Check for @tool, script-level, or standalone annotation.
+ while (match(GDScriptTokenizer::Token::ANNOTATION)) {
AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
if (annotation != nullptr) {
- if (annotation->name == SNAME("@tool")) {
- // TODO: don't allow @tool anywhere else. (Should all script annotations be the first thing?).
- _is_tool = true;
- if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
- push_error(R"(Expected newline after "@tool" annotation.)");
- }
- // @tool annotation has no specific target.
- annotation->apply(this, nullptr);
- } else if (annotation->applies_to(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE)) {
- premature_annotation = annotation;
- if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
- push_error(R"(Expected newline after a standalone annotation.)");
- }
+ if (annotation->applies_to(AnnotationInfo::SCRIPT)) {
annotation->apply(this, head);
} else {
- premature_annotation = annotation;
annotation_stack.push_back(annotation);
+ // This annotation must appear after script-level annotations
+ // and class_name/extends (ex: could be @onready or @export),
+ // so we stop looking for script-level stuff.
+ can_have_class_or_extends = false;
+ break;
}
}
}
- for (bool should_break = false; !should_break;) {
+ while (can_have_class_or_extends) {
// Order here doesn't matter, but there should be only one of each at most.
switch (current.type) {
case GDScriptTokenizer::Token::CLASS_NAME:
- if (premature_annotation != nullptr) {
- push_error(R"("class_name" should be used before annotations (except @tool).)");
- }
advance();
if (head->identifier != nullptr) {
push_error(R"("class_name" can only be used once.)");
@@ -585,9 +570,6 @@ void GDScriptParser::parse_program() {
}
break;
case GDScriptTokenizer::Token::EXTENDS:
- if (premature_annotation != nullptr) {
- push_error(R"("extends" should be used before annotations (except @tool).)");
- }
advance();
if (head->extends_used) {
push_error(R"("extends" can only be used once.)");
@@ -597,7 +579,8 @@ void GDScriptParser::parse_program() {
}
break;
default:
- should_break = true;
+ // No tokens are allowed between script annotations and class/extends.
+ can_have_class_or_extends = false;
break;
}
@@ -606,21 +589,6 @@ void GDScriptParser::parse_program() {
}
}
- if (match(GDScriptTokenizer::Token::ANNOTATION)) {
- // Check for a script-level, or standalone annotation.
- AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
- if (annotation != nullptr) {
- if (annotation->applies_to(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE)) {
- if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
- push_error(R"(Expected newline after a standalone annotation.)");
- }
- annotation->apply(this, head);
- } else {
- annotation_stack.push_back(annotation);
- }
- }
- }
-
parse_class_body(true);
complete_extents(head);
@@ -1321,14 +1289,8 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
if (elements.has(item.identifier->name)) {
push_error(vformat(R"(Name "%s" was already in this enum (at line %d).)", item.identifier->name, elements[item.identifier->name]), item.identifier);
} else if (!named) {
- // TODO: Abstract this recursive member check.
- ClassNode *parent = current_class;
- while (parent != nullptr) {
- if (parent->members_indices.has(item.identifier->name)) {
- push_error(vformat(R"(Name "%s" is already used as a class %s.)", item.identifier->name, parent->get_member(item.identifier->name).get_type_name()));
- break;
- }
- parent = parent->outer;
+ if (current_class->members_indices.has(item.identifier->name)) {
+ push_error(vformat(R"(Name "%s" is already used as a class %s.)", item.identifier->name, current_class->get_member(item.identifier->name).get_type_name()));
}
}
@@ -3800,6 +3762,26 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.type = Variant::INT;
}
}
+ if (p_annotation->name == SNAME("@export_multiline")) {
+ if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type()) {
+ DataType inner_type = export_type.get_container_element_type();
+ if (inner_type.builtin_type != Variant::STRING) {
+ push_error(vformat(R"("%s" annotation on arrays requires a string type but type "%s" was given instead.)", p_annotation->name.operator String(), inner_type.to_string()), variable);
+ return false;
+ }
+
+ String hint_prefix = itos(inner_type.builtin_type) + "/" + itos(variable->export_info.hint);
+ variable->export_info.hint = PROPERTY_HINT_TYPE_STRING;
+ variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string;
+ variable->export_info.type = Variant::ARRAY;
+
+ return true;
+ } else if (export_type.builtin_type == Variant::DICTIONARY) {
+ variable->export_info.type = Variant::DICTIONARY;
+
+ return true;
+ }
+ }
if (p_annotation->name == SNAME("@export")) {
if (variable->datatype_specifier == nullptr && variable->initializer == nullptr) {
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 4bb02c4ea3..0903f62061 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -360,6 +360,7 @@ public:
ExpressionNode *initializer = nullptr;
TypeNode *datatype_specifier = nullptr;
bool infer_datatype = false;
+ bool use_conversion_assign = false;
int usages = 0;
virtual ~AssignableNode() {}
@@ -1182,7 +1183,6 @@ public:
bool onready = false;
PropertyInfo export_info;
int assignments = 0;
- bool use_conversion_assign = false;
#ifdef TOOLS_ENABLED
String doc_description;
#endif // TOOLS_ENABLED
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 04612c6793..e17a804003 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -164,6 +164,7 @@ bool GDScriptTokenizer::Token::is_identifier() const {
switch (type) {
case IDENTIFIER:
case MATCH: // Used in String.match().
+ case CONST_INF: // Used in Vector{2,3,4}.INF
return true;
default:
return false;
diff --git a/modules/gdscript/tests/README.md b/modules/gdscript/tests/README.md
index 6e54085962..361d586d32 100644
--- a/modules/gdscript/tests/README.md
+++ b/modules/gdscript/tests/README.md
@@ -4,5 +4,5 @@ The `scripts/` folder contains integration tests in the form of GDScript files
and output files.
See the
-[Integration tests for GDScript documentation](https://docs.godotengine.org/en/latest/development/cpp/unit_testing.html#integration-tests-for-gdscript)
+[Integration tests for GDScript documentation](https://docs.godotengine.org/en/latest/contributing/development/core_and_modules/unit_testing.html#integration-tests-for-gdscript)
for information about creating and running GDScript integration tests.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd
new file mode 100644
index 0000000000..38c2faa859
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd
@@ -0,0 +1,2 @@
+func test():
+ CanvasItem.new()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out
new file mode 100644
index 0000000000..9eff912b59
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Native class "CanvasItem" cannot be constructed as it is abstract.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd
new file mode 100644
index 0000000000..118e7e8a45
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd
@@ -0,0 +1,9 @@
+class A extends CanvasItem:
+ func _init():
+ print('no')
+
+class B extends A:
+ pass
+
+func test():
+ B.new()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out
new file mode 100644
index 0000000000..8b956f5974
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Class "abstract_script_instantiate.gd::B" cannot be constructed as it is based on abstract native class "CanvasItem".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.gd
new file mode 100644
index 0000000000..87fbe1229c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.gd
@@ -0,0 +1,5 @@
+const base := [0]
+
+func test():
+ var sub := base[0]
+ if sub is String: pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.out b/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.out
new file mode 100644
index 0000000000..54c190cf8a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "int" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.gd
index 98f1f3ec2d..81d5d59ae8 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.gd
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.gd
@@ -1,4 +1,5 @@
enum MyEnum { VALUE_A, VALUE_B, VALUE_C = 42 }
+
func test():
const P = preload("../features/enum_value_from_parent.gd")
var local_var: MyEnum
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_outer_enum.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_outer_enum.gd
deleted file mode 100644
index f3f3b5ffeb..0000000000
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_outer_enum.gd
+++ /dev/null
@@ -1,7 +0,0 @@
-enum { V }
-
-class InnerClass:
- enum { V }
-
-func test():
- pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_outer_enum.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_outer_enum.out
deleted file mode 100644
index c9706003e1..0000000000
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_outer_enum.out
+++ /dev/null
@@ -1,2 +0,0 @@
-GDTEST_PARSER_ERROR
-Name "V" is already used as a class enum value.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd
new file mode 100644
index 0000000000..cf56a0a933
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd
@@ -0,0 +1,6 @@
+const constant_float = 1.0
+
+func test():
+ for x in constant_float:
+ if x is String:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.out
new file mode 100644
index 0000000000..e309831b3e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "float" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd
new file mode 100644
index 0000000000..5ee8ac19e1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd
@@ -0,0 +1,6 @@
+const constant_int = 1
+
+func test():
+ for x in constant_int:
+ if x is String:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.out
new file mode 100644
index 0000000000..54c190cf8a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "int" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd
new file mode 100644
index 0000000000..b3db4f3b49
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd
@@ -0,0 +1,6 @@
+enum { enum_value = 1 }
+
+func test():
+ for x in enum_value:
+ if x is String:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.out
new file mode 100644
index 0000000000..54c190cf8a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "int" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd
new file mode 100644
index 0000000000..87c54f7402
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd
@@ -0,0 +1,6 @@
+func test():
+ var hard_float := 1.0
+
+ for x in hard_float:
+ if x is String:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.out
new file mode 100644
index 0000000000..e309831b3e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "float" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd
new file mode 100644
index 0000000000..2a43f5a930
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd
@@ -0,0 +1,6 @@
+func test():
+ var hard_int := 1
+
+ for x in hard_int:
+ if x is String:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.out
new file mode 100644
index 0000000000..54c190cf8a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "int" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd
new file mode 100644
index 0000000000..c3920d35b3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd
@@ -0,0 +1,14 @@
+class Iterator:
+ func _iter_init(_count):
+ return true
+ func _iter_next(_count):
+ return false
+ func _iter_get(_count) -> StringName:
+ return &'custom'
+
+func test():
+ var hard_iterator := Iterator.new()
+
+ for x in hard_iterator:
+ if x is int:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.out
new file mode 100644
index 0000000000..a48591a3b4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "StringName" so it can't be of type "int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd
new file mode 100644
index 0000000000..b36d87aabe
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd
@@ -0,0 +1,6 @@
+func test():
+ var hard_string := 'a'
+
+ for x in hard_string:
+ if x is int:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.out
new file mode 100644
index 0000000000..92c5ebc599
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "String" so it can't be of type "int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.gd
new file mode 100644
index 0000000000..060a8bedf9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.gd
@@ -0,0 +1,3 @@
+func test():
+ for x in true:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.out
new file mode 100644
index 0000000000..94cb038885
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Unable to iterate on value of type "bool".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd
new file mode 100644
index 0000000000..6cfc822482
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd
@@ -0,0 +1,4 @@
+func test():
+ for x in 1:
+ if x is String:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.out
new file mode 100644
index 0000000000..54c190cf8a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "int" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.gd b/modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.gd
new file mode 100644
index 0000000000..664e364493
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.gd
@@ -0,0 +1,3 @@
+func test():
+ var foo: bool = true
+ foo += 'bar'
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.out b/modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.out
new file mode 100644
index 0000000000..358b096a64
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Invalid operands "bool" and "String" for assignment operator.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.gd
new file mode 100644
index 0000000000..1cf3870a8e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.gd
@@ -0,0 +1,8 @@
+class Outer:
+ const OUTER_CONST: = 0
+ class Inner:
+ pass
+
+func test() -> void:
+ var type: = Outer.Inner
+ print(type.OUTER_CONST)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.out b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.out
new file mode 100644
index 0000000000..73a54d7820
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> analyzer/errors/outer_class_constants.gd
+>> 8
+>> Invalid get index 'OUTER_CONST' (on base: 'GDScript').
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.gd
new file mode 100644
index 0000000000..c1074df915
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.gd
@@ -0,0 +1,9 @@
+class Outer:
+ const OUTER_CONST: = 0
+ class Inner:
+ pass
+
+func test() -> void:
+ var type: = Outer.Inner
+ var type_v: Variant = type
+ print(type_v.OUTER_CONST)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.out b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.out
new file mode 100644
index 0000000000..92e7b9316e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> analyzer/errors/outer_class_constants_as_variant.gd
+>> 9
+>> Invalid get index 'OUTER_CONST' (on base: 'GDScript').
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.gd
new file mode 100644
index 0000000000..2631c3c500
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.gd
@@ -0,0 +1,8 @@
+class Outer:
+ const OUTER_CONST: = 0
+ class Inner:
+ pass
+
+func test() -> void:
+ var instance: = Outer.Inner.new()
+ print(instance.OUTER_CONST)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.out b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.out
new file mode 100644
index 0000000000..892f8e2c3f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> analyzer/errors/outer_class_instance_constants.gd
+>> 8
+>> Invalid get index 'OUTER_CONST' (on base: 'RefCounted (Inner)').
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.gd
new file mode 100644
index 0000000000..cba788381e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.gd
@@ -0,0 +1,9 @@
+class Outer:
+ const OUTER_CONST: = 0
+ class Inner:
+ pass
+
+func test() -> void:
+ var instance: = Outer.Inner.new()
+ var instance_v: Variant = instance
+ print(instance_v.OUTER_CONST)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.out b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.out
new file mode 100644
index 0000000000..8257e74f57
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> analyzer/errors/outer_class_instance_constants_as_variant.gd
+>> 9
+>> Invalid get index 'OUTER_CONST' (on base: 'RefCounted (Inner)').
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd
index 65c0d9dabc..200c352223 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd
@@ -1,12 +1,12 @@
class A:
- class B:
- func test():
- print(A.B.D)
+ class B:
+ func test():
+ print(A.B.D)
class C:
- class D:
- pass
+ class D:
+ pass
func test():
- var inst = A.B.new()
- inst.test()
+ var inst = A.B.new()
+ inst.test()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd
index 63587942f7..393b66c9f0 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd
+++ b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd
@@ -1,2 +1,2 @@
func test() -> void:
- return null
+ return null
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd
index 0ee4e7ea36..6be2730bab 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd
+++ b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd
@@ -1,4 +1,4 @@
func test() -> void:
- var a
- a = 1
- return a
+ var a
+ a = 1
+ return a
diff --git a/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd
new file mode 100644
index 0000000000..2d2c2bef19
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd
@@ -0,0 +1,19 @@
+func test():
+ var one_0 = 0
+ one_0 = 1
+ var one_1 := one_0
+ print(one_1)
+
+ var two: Variant = 0
+ two += 2
+ print(two)
+
+ var three_0: Variant = 1
+ var three_1: int = 2
+ three_0 += three_1
+ print(three_0)
+
+ var four_0: int = 3
+ var four_1: Variant = 1
+ four_0 += four_1
+ print(four_0)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out
new file mode 100644
index 0000000000..7536c38490
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+1
+2
+3
+4
diff --git a/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.gd b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.gd
index 7881a0feb6..a94487d989 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.gd
@@ -11,4 +11,3 @@ func test() -> void:
Extend.InnerClass.InnerInnerClass.test_a_b_c(A.new(), B.new(), C.new())
Extend.InnerClass.InnerInnerClass.test_enum(C.TestEnum.HELLO_WORLD)
Extend.InnerClass.InnerInnerClass.test_a_prime(A.APrime.new())
-
diff --git a/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd
index 30e7deb05a..7c846c59bd 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd
@@ -1,19 +1,19 @@
class A:
- var x = 3
+ var x = 3
class B:
- var x = 4
+ var x = 4
class C:
- var x = 5
+ var x = 5
class Test:
- var a = A.new()
- var b: B = B.new()
- var c := C.new()
+ var a = A.new()
+ var b: B = B.new()
+ var c := C.new()
func test():
- var test_instance := Test.new()
- prints(test_instance.a.x)
- prints(test_instance.b.x)
- prints(test_instance.c.x)
+ var test_instance := Test.new()
+ prints(test_instance.a.x)
+ prints(test_instance.b.x)
+ prints(test_instance.c.x)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.gd b/modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.gd
new file mode 100644
index 0000000000..95c3268130
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.gd
@@ -0,0 +1,12 @@
+class A extends CanvasItem:
+ func _init():
+ pass
+
+class B extends A:
+ pass
+
+class C extends CanvasItem:
+ pass
+
+func test():
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.out b/modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.out
new file mode 100644
index 0000000000..1b47ed10dc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+ok
diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd
index 757744b6f1..0c740935b9 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd
@@ -2,5 +2,5 @@ const External = preload("external_enum_as_constant_external.notest.gd")
const MyEnum = External.MyEnum
func test():
- print(MyEnum.WAITING == 0)
- print(MyEnum.GODOT == 1)
+ print(MyEnum.WAITING == 0)
+ print(MyEnum.GODOT == 1)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd
index 7c090844d0..24c1e41aab 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd
@@ -1,4 +1,4 @@
enum MyEnum {
- WAITING,
- GODOT
+ WAITING,
+ GODOT
}
diff --git a/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd
new file mode 100644
index 0000000000..7b74be6f2c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd
@@ -0,0 +1,15 @@
+func test():
+ var variant_int: Variant = 1
+ var weak_int = 1
+
+ for x in variant_int:
+ if x is String:
+ print('never')
+ print(x)
+
+ for x in weak_int:
+ if x is String:
+ print('never')
+ print(x)
+
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out
new file mode 100644
index 0000000000..7677671cfd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+0
+0
+ok
diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_class.gd b/modules/gdscript/tests/scripts/analyzer/features/lookup_class.gd
new file mode 100644
index 0000000000..541da78332
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_class.gd
@@ -0,0 +1,50 @@
+# Inner-outer class lookup
+class A:
+ const Q: = "right one"
+
+class X:
+ const Q: = "wrong one"
+
+class Y extends X:
+ class B extends A:
+ static func check() -> void:
+ print(Q)
+
+# External class lookup
+const External: = preload("lookup_class_external.notest.gd")
+
+class Internal extends External.A:
+ static func check() -> void:
+ print(TARGET)
+
+ class E extends External.E:
+ static func check() -> void:
+ print(TARGET)
+ print(WAITING)
+
+# Variable lookup
+class C:
+ var Q := 'right one'
+
+class D:
+ const Q := 'wrong one'
+
+class E extends D:
+ class F extends C:
+ func check() -> void:
+ print(Q)
+
+# Test
+func test() -> void:
+ # Inner-outer class lookup
+ Y.B.check()
+ print("---")
+
+ # External class lookup
+ Internal.check()
+ Internal.E.check()
+ print("---")
+
+ # Variable lookup
+ var f: = E.F.new()
+ f.check()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_class.out b/modules/gdscript/tests/scripts/analyzer/features/lookup_class.out
new file mode 100644
index 0000000000..a0983c1438
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_class.out
@@ -0,0 +1,8 @@
+GDTEST_OK
+right one
+---
+wrong
+right
+godot
+---
+right one
diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_class_external.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/lookup_class_external.notest.gd
new file mode 100644
index 0000000000..a2904e20a8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_class_external.notest.gd
@@ -0,0 +1,15 @@
+class A:
+ const TARGET: = "wrong"
+
+ class B:
+ const TARGET: = "wrong"
+ const WAITING: = "godot"
+
+ class D extends C:
+ pass
+
+class C:
+ const TARGET: = "right"
+
+class E extends A.B.D:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.gd b/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.gd
new file mode 100644
index 0000000000..26cf6c7322
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.gd
@@ -0,0 +1,41 @@
+signal hello
+
+func get_signal() -> Signal:
+ return hello
+
+class A:
+ signal hello
+
+ func get_signal() -> Signal:
+ return hello
+
+ class B:
+ signal hello
+
+ func get_signal() -> Signal:
+ return hello
+
+class C extends A.B:
+ func get_signal() -> Signal:
+ return hello
+
+func test():
+ var a: = A.new()
+ var b: = A.B.new()
+ var c: = C.new()
+
+ var hello_a_result: = hello == a.get_signal()
+ var hello_b_result: = hello == b.get_signal()
+ var hello_c_result: = hello == c.get_signal()
+ var a_b_result: = a.get_signal() == b.get_signal()
+ var a_c_result: = a.get_signal() == c.get_signal()
+ var b_c_result: = b.get_signal() == c.get_signal()
+ var c_c_result: = c.get_signal() == c.get_signal()
+
+ print("hello == A.hello? %s" % hello_a_result)
+ print("hello == A.B.hello? %s" % hello_b_result)
+ print("hello == C.hello? %s" % hello_c_result)
+ print("A.hello == A.B.hello? %s" % a_b_result)
+ print("A.hello == C.hello? %s" % a_c_result)
+ print("A.B.hello == C.hello? %s" % b_c_result)
+ print("C.hello == C.hello? %s" % c_c_result)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.out b/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.out
new file mode 100644
index 0000000000..6b0d32eaf8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.out
@@ -0,0 +1,8 @@
+GDTEST_OK
+hello == A.hello? false
+hello == A.B.hello? false
+hello == C.hello? false
+A.hello == A.B.hello? false
+A.hello == C.hello? false
+A.B.hello == C.hello? false
+C.hello == C.hello? true
diff --git a/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd
index c9caef7d7c..95f04421d1 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd
@@ -1,5 +1,5 @@
func variant() -> Variant:
- return 'variant'
+ return 'variant'
func test():
- print(variant())
+ print(variant())
diff --git a/modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.gd b/modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.gd
new file mode 100644
index 0000000000..da24c06b2e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.gd
@@ -0,0 +1,6 @@
+class Check extends Node:
+ func _set(_property: StringName, _value: Variant) -> bool:
+ return true
+
+func test() -> void:
+ print('OK')
diff --git a/modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.out b/modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.out
new file mode 100644
index 0000000000..1ccb591560
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+OK
diff --git a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd
index ada6030132..0085b3f367 100644
--- a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd
@@ -1,6 +1,6 @@
-# Error here. `class_name` should be used *before* annotations, not after (except @tool).
-@icon("res://path/to/optional/icon.svg")
+# Error here. Annotations should be used before `class_name`, not after.
class_name HelloWorld
+@icon("res://path/to/optional/icon.svg")
func test():
- pass
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out
index 02b33c8692..a598ff8424 100644
--- a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out
+++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out
@@ -1,2 +1,2 @@
GDTEST_PARSER_ERROR
-"class_name" should be used before annotations (except @tool).
+Annotation "@icon" is not allowed in this level.
diff --git a/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd b/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd
index 92dfb2366d..816783f239 100644
--- a/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd
@@ -1,2 +1,2 @@
func test():
- var dictionary = { hello = "world",, }
+ var dictionary = { hello = "world",, }
diff --git a/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd b/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd
index 4608c778aa..7a745bd995 100644
--- a/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd
@@ -1,4 +1,4 @@
func test():
- match 1:
- [[[var a]]], 2:
- pass
+ match 1:
+ [[[var a]]], 2:
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd
index 43b513045b..a7197bf68f 100644
--- a/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd
+++ b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd
@@ -1,34 +1,34 @@
func foo(x):
- match x:
- 1 + 1:
- print("1+1")
- [1,2,[1,{1:2,2:var z,..}]]:
- print("[1,2,[1,{1:2,2:var z,..}]]")
- print(z)
- 1 if true else 2:
- print("1 if true else 2")
- 1 < 2:
- print("1 < 2")
- 1 or 2 and 1:
- print("1 or 2 and 1")
- 6 | 1:
- print("1 | 1")
- 1 >> 1:
- print("1 >> 1")
- 1, 2 or 3, 4:
- print("1, 2 or 3, 4")
- _:
- print("wildcard")
+ match x:
+ 1 + 1:
+ print("1+1")
+ [1,2,[1,{1:2,2:var z,..}]]:
+ print("[1,2,[1,{1:2,2:var z,..}]]")
+ print(z)
+ 1 if true else 2:
+ print("1 if true else 2")
+ 1 < 2:
+ print("1 < 2")
+ 1 or 2 and 1:
+ print("1 or 2 and 1")
+ 6 | 1:
+ print("1 | 1")
+ 1 >> 1:
+ print("1 >> 1")
+ 1, 2 or 3, 4:
+ print("1, 2 or 3, 4")
+ _:
+ print("wildcard")
func test():
- foo(6 | 1)
- foo(1 >> 1)
- foo(2)
- foo(1)
- foo(1+1)
- foo(1 < 2)
- foo([2, 1])
- foo(4)
- foo([1, 2, [1, {1 : 2, 2:3}]])
- foo([1, 2, [1, {1 : 2, 2:[1,3,5, "123"], 4:2}]])
- foo([1, 2, [1, {1 : 2}]])
+ foo(6 | 1)
+ foo(1 >> 1)
+ foo(2)
+ foo(1)
+ foo(1+1)
+ foo(1 < 2)
+ foo([2, 1])
+ foo(4)
+ foo([1, 2, [1, {1 : 2, 2:3}]])
+ foo([1, 2, [1, {1 : 2, 2:[1,3,5, "123"], 4:2}]])
+ foo([1, 2, [1, {1 : 2}]])
diff --git a/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd b/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd
index 2b46f1e88a..c959c6c6af 100644
--- a/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd
+++ b/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd
@@ -1,27 +1,27 @@
func foo(x):
- match x:
- 1:
- print("1")
- 2:
- print("2")
- [1, 2]:
- print("[1, 2]")
- 3 or 4:
- print("3 or 4")
- 4:
- print("4")
- {1 : 2, 2 : 3}:
- print("{1 : 2, 2 : 3}")
- _:
- print("wildcard")
+ match x:
+ 1:
+ print("1")
+ 2:
+ print("2")
+ [1, 2]:
+ print("[1, 2]")
+ 3 or 4:
+ print("3 or 4")
+ 4:
+ print("4")
+ {1 : 2, 2 : 3}:
+ print("{1 : 2, 2 : 3}")
+ _:
+ print("wildcard")
func test():
- foo(0)
- foo(1)
- foo(2)
- foo([1, 2])
- foo(3)
- foo(4)
- foo([4,4])
- foo({1 : 2, 2 : 3})
- foo({1 : 2, 4 : 3})
+ foo(0)
+ foo(1)
+ foo(2)
+ foo([1, 2])
+ foo(3)
+ foo(4)
+ foo([4,4])
+ foo({1 : 2, 2 : 3})
+ foo({1 : 2, 4 : 3})
diff --git a/modules/gdscript/tests/scripts/parser/features/class_name.gd b/modules/gdscript/tests/scripts/parser/features/class_name.gd
index 8bd188e247..19009e433d 100644
--- a/modules/gdscript/tests/scripts/parser/features/class_name.gd
+++ b/modules/gdscript/tests/scripts/parser/features/class_name.gd
@@ -1,5 +1,5 @@
-class_name HelloWorld
@icon("res://path/to/optional/icon.svg")
+class_name HelloWorld
func test():
pass
diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_callable.gd b/modules/gdscript/tests/scripts/parser/features/lambda_callable.gd
index c3b2506156..17d00bce3c 100644
--- a/modules/gdscript/tests/scripts/parser/features/lambda_callable.gd
+++ b/modules/gdscript/tests/scripts/parser/features/lambda_callable.gd
@@ -1,4 +1,4 @@
func test():
- var my_lambda = func(x):
- print(x)
- my_lambda.call("hello")
+ var my_lambda = func(x):
+ print(x)
+ my_lambda.call("hello")
diff --git a/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd b/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd
index 377dd25e9e..75857fb8ff 100644
--- a/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd
+++ b/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd
@@ -1,43 +1,43 @@
func foo(x):
- match x:
- {"key1": "value1", "key2": "value2"}:
- print('{"key1": "value1", "key2": "value2"}')
- {"key1": "value1", "key2"}:
- print('{"key1": "value1", "key2"}')
- {"key1", "key2": "value2"}:
- print('{"key1", "key2": "value2"}')
- {"key1", "key2"}:
- print('{"key1", "key2"}')
- {"key1": "value1"}:
- print('{"key1": "value1"}')
- {"key1"}:
- print('{"key1"}')
- _:
- print("wildcard")
+ match x:
+ {"key1": "value1", "key2": "value2"}:
+ print('{"key1": "value1", "key2": "value2"}')
+ {"key1": "value1", "key2"}:
+ print('{"key1": "value1", "key2"}')
+ {"key1", "key2": "value2"}:
+ print('{"key1", "key2": "value2"}')
+ {"key1", "key2"}:
+ print('{"key1", "key2"}')
+ {"key1": "value1"}:
+ print('{"key1": "value1"}')
+ {"key1"}:
+ print('{"key1"}')
+ _:
+ print("wildcard")
func bar(x):
- match x:
- {0}:
- print("0")
- {1}:
- print("1")
- {2}:
- print("2")
- _:
- print("wildcard")
+ match x:
+ {0}:
+ print("0")
+ {1}:
+ print("1")
+ {2}:
+ print("2")
+ _:
+ print("wildcard")
func test():
- foo({"key1": "value1", "key2": "value2"})
- foo({"key1": "value1", "key2": ""})
- foo({"key1": "", "key2": "value2"})
- foo({"key1": "", "key2": ""})
- foo({"key1": "value1"})
- foo({"key1": ""})
- foo({"key1": "value1", "key2": "value2", "key3": "value3"})
- foo({"key1": "value1", "key3": ""})
- foo({"key2": "value2"})
- foo({"key3": ""})
- bar({0: "0"})
- bar({1: "1"})
- bar({2: "2"})
- bar({3: "3"})
+ foo({"key1": "value1", "key2": "value2"})
+ foo({"key1": "value1", "key2": ""})
+ foo({"key1": "", "key2": "value2"})
+ foo({"key1": "", "key2": ""})
+ foo({"key1": "value1"})
+ foo({"key1": ""})
+ foo({"key1": "value1", "key2": "value2", "key3": "value3"})
+ foo({"key1": "value1", "key3": ""})
+ foo({"key2": "value2"})
+ foo({"key3": ""})
+ bar({0: "0"})
+ bar({1: "1"})
+ bar({2: "2"})
+ bar({3: "3"})
diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd
index dbe223f5f5..a278ea1154 100644
--- a/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd
+++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd
@@ -1,26 +1,26 @@
func foo(x):
- match x:
- 1, [2]:
- print('1, [2]')
- _:
- print('wildcard')
+ match x:
+ 1, [2]:
+ print('1, [2]')
+ _:
+ print('wildcard')
func bar(x):
- match x:
- [1], [2], [3]:
- print('[1], [2], [3]')
- [4]:
- print('[4]')
- _:
- print('wildcard')
+ match x:
+ [1], [2], [3]:
+ print('[1], [2], [3]')
+ [4]:
+ print('[4]')
+ _:
+ print('wildcard')
func test():
- foo(1)
- foo([2])
- foo(2)
- bar([1])
- bar([2])
- bar([3])
- bar([4])
- bar([5])
+ foo(1)
+ foo([2])
+ foo(2)
+ bar([1])
+ bar([2])
+ bar([3])
+ bar([4])
+ bar([5])
diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd b/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd
index a0ae7fb17c..0a71f33c25 100644
--- a/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd
+++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd
@@ -1,6 +1,6 @@
func test():
- match [1, 2, 3]:
- [var a, var b, var c]:
- print(a == 1)
- print(b == 2)
- print(c == 3)
+ match [1, 2, 3]:
+ [var a, var b, var c]:
+ print(a == 1)
+ print(b == 2)
+ print(c == 3)
diff --git a/modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.gd b/modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.gd
new file mode 100644
index 0000000000..4cbb464f59
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.gd
@@ -0,0 +1,17 @@
+class A:
+ enum { X = 1 }
+
+ class B:
+ enum { X = 2 }
+
+class C:
+ const X = 3
+
+ class D:
+ enum { X = 4 }
+
+func test():
+ print(A.X)
+ print(A.B.X)
+ print(C.X)
+ print(C.D.X)
diff --git a/modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.out b/modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.out
new file mode 100644
index 0000000000..7536c38490
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+1
+2
+3
+4
diff --git a/modules/gdscript/tests/scripts/parser/features/vector_inf.gd b/modules/gdscript/tests/scripts/parser/features/vector_inf.gd
new file mode 100644
index 0000000000..039d51d9ed
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/vector_inf.gd
@@ -0,0 +1,6 @@
+func test():
+ var vec2: = Vector2.INF
+ var vec3: = Vector3.INF
+
+ print(vec2.x == INF)
+ print(vec3.z == INF)
diff --git a/modules/gdscript/tests/scripts/parser/features/vector_inf.out b/modules/gdscript/tests/scripts/parser/features/vector_inf.out
new file mode 100644
index 0000000000..9d111a8322
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/vector_inf.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+true
+true
diff --git a/modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.gd b/modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.gd
new file mode 100644
index 0000000000..a72ac9b5ee
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.gd
@@ -0,0 +1,8 @@
+var weakling = 'not float'
+func weak(x: float = weakling):
+ print(x)
+ print('typeof x is', typeof(x))
+
+func test():
+ print(typeof(weak()))
+ print('not ok')
diff --git a/modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.out b/modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.out
new file mode 100644
index 0000000000..8543cf976e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.out
@@ -0,0 +1,8 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: weak()
+>> runtime/errors/bad_conversion_for_default_parameter.gd
+>> 2
+>> Trying to assign value of type 'String' to a variable of type 'float'.
+0
+not ok
diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.gd b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.gd
index 935fb773dc..7b350e81ad 100644
--- a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.gd
+++ b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.gd
@@ -1,4 +1,4 @@
const dictionary := {}
func test():
- dictionary.erase(0)
+ dictionary.erase(0)
diff --git a/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd b/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd
index 9b64084fa6..bd38259cec 100644
--- a/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd
@@ -9,7 +9,7 @@ func test():
array_sname.push_back(&"godot")
print("String in Array: ", "godot" in array_sname)
- # Not equal because the values are different types.
+ # Not equal because the values are different types.
print("Arrays not equal: ", array_str != array_sname)
var string_array: Array[String] = []
diff --git a/modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.gd b/modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.gd
new file mode 100644
index 0000000000..9d0c6317b3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.gd
@@ -0,0 +1,19 @@
+func literal(x: float = 1):
+ print('x is ', x)
+ print('typeof x is ', typeof(x))
+
+var inferring := 2
+func inferred(x: float = inferring):
+ print('x is ', x)
+ print('typeof x is ', typeof(x))
+
+var weakling = 3
+func weak(x: float = weakling):
+ print('x is ', x)
+ print('typeof x is ', typeof(x))
+
+func test():
+ literal()
+ inferred()
+ weak()
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.out b/modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.out
new file mode 100644
index 0000000000..a9ef4919cf
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.out
@@ -0,0 +1,8 @@
+GDTEST_OK
+x is 1
+typeof x is 3
+x is 2
+typeof x is 3
+x is 3
+typeof x is 3
+ok
diff --git a/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd b/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd
index 1f15026f17..94bac1974f 100644
--- a/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd
@@ -13,5 +13,5 @@ func test():
print("String gets StringName: ", stringname_dict.get("abc"))
stringname_dict[&"abc"] = 42
- # They compare equal because StringName keys are converted to String.
+ # They compare equal because StringName keys are converted to String.
print("String Dictionary == StringName Dictionary: ", string_dict == stringname_dict)
diff --git a/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd b/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd
new file mode 100644
index 0000000000..1d4b400d81
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd
@@ -0,0 +1,17 @@
+# https://github.com/godotengine/godot/issues/71177
+
+func test():
+ builtin_method()
+ builtin_method_static()
+ print("done")
+
+func builtin_method():
+ var pba := PackedByteArray()
+ @warning_ignore(return_value_discarded)
+ pba.resize(1) # Built-in validated.
+
+
+func builtin_method_static():
+ var _pba := PackedByteArray()
+ @warning_ignore(return_value_discarded)
+ Vector2.from_angle(PI) # Static built-in validated.
diff --git a/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.out b/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.out
new file mode 100644
index 0000000000..8e68c97774
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+done
diff --git a/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd
new file mode 100644
index 0000000000..81355e0255
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd
@@ -0,0 +1,51 @@
+const constant_float = 1.0
+const constant_int = 1
+enum { enum_value = 1 }
+
+class Iterator:
+ func _iter_init(_count):
+ return true
+ func _iter_next(_count):
+ return false
+ func _iter_get(_count) -> StringName:
+ return &'custom'
+
+func test():
+ var hard_float := 1.0
+ var hard_int := 1
+ var hard_string := '0'
+ var hard_iterator := Iterator.new()
+
+ var variant_float: Variant = hard_float
+ var variant_int: Variant = hard_int
+ var variant_string: Variant = hard_string
+ var variant_iterator: Variant = hard_iterator
+
+ for i in 1.0:
+ print(typeof(i) == TYPE_FLOAT)
+ for i in 1:
+ print(typeof(i) == TYPE_INT)
+ for i in 'a':
+ print(typeof(i) == TYPE_STRING)
+ for i in Iterator.new():
+ print(typeof(i) == TYPE_STRING_NAME)
+
+ for i in hard_float:
+ print(typeof(i) == TYPE_FLOAT)
+ for i in hard_int:
+ print(typeof(i) == TYPE_INT)
+ for i in hard_string:
+ print(typeof(i) == TYPE_STRING)
+ for i in hard_iterator:
+ print(typeof(i) == TYPE_STRING_NAME)
+
+ for i in variant_float:
+ print(typeof(i) == TYPE_FLOAT)
+ for i in variant_int:
+ print(typeof(i) == TYPE_INT)
+ for i in variant_string:
+ print(typeof(i) == TYPE_STRING)
+ for i in variant_iterator:
+ print(typeof(i) == TYPE_STRING_NAME)
+
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out
new file mode 100644
index 0000000000..b3e82d52ef
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out
@@ -0,0 +1,14 @@
+GDTEST_OK
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+ok
diff --git a/modules/gdscript/tests/scripts/runtime/features/gdscript.gd b/modules/gdscript/tests/scripts/runtime/features/gdscript.gd
new file mode 100644
index 0000000000..f2368643de
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/gdscript.gd
@@ -0,0 +1,20 @@
+func test():
+ var gdscr: = GDScript.new()
+ gdscr.source_code = '''
+extends Resource
+
+func test() -> void:
+ prints("Outer")
+ var inner = InnerClass.new()
+
+class InnerClass:
+ func _init() -> void:
+ prints("Inner")
+'''
+ @warning_ignore(return_value_discarded)
+ gdscr.reload()
+
+ var inst = gdscr.new()
+
+ @warning_ignore(unsafe_method_access)
+ inst.test()
diff --git a/modules/gdscript/tests/scripts/runtime/features/gdscript.out b/modules/gdscript/tests/scripts/runtime/features/gdscript.out
new file mode 100644
index 0000000000..16114f57f7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/gdscript.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+Outer
+Inner
diff --git a/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd
index f33ba7dffd..252e100bda 100644
--- a/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd
@@ -3,23 +3,23 @@
var a: int = 1
func shadow_regular_assignment(a: Variant, b: Variant) -> void:
- print(a)
- print(self.a)
- a = b
- print(a)
- print(self.a)
+ print(a)
+ print(self.a)
+ a = b
+ print(a)
+ print(self.a)
var v := Vector2(0.0, 0.0)
func shadow_subscript_assignment(v: Vector2, x: float) -> void:
- print(v)
- print(self.v)
- v.x += x
- print(v)
- print(self.v)
+ print(v)
+ print(self.v)
+ v.x += x
+ print(v)
+ print(self.v)
func test():
- shadow_regular_assignment('a', 'b')
- shadow_subscript_assignment(Vector2(1.0, 1.0), 5.0)
+ shadow_regular_assignment('a', 'b')
+ shadow_subscript_assignment(Vector2(1.0, 1.0), 5.0)
diff --git a/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd
new file mode 100644
index 0000000000..af3f3cb941
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd
@@ -0,0 +1,9 @@
+# https://github.com/godotengine/godot/issues/71172
+
+func test():
+ @warning_ignore(narrowing_conversion)
+ var foo: int = 0.0
+ print(typeof(foo) == TYPE_INT)
+ var dict : Dictionary = {"a":0.0}
+ foo = dict.get("a")
+ print(typeof(foo) == TYPE_INT)
diff --git a/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.out b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.out
new file mode 100644
index 0000000000..9d111a8322
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+true
+true
diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml
index f313f4b28f..d7e8141eb1 100644
--- a/modules/gltf/doc_classes/GLTFDocument.xml
+++ b/modules/gltf/doc_classes/GLTFDocument.xml
@@ -15,7 +15,7 @@
<param index="2" name="state" type="GLTFState" />
<param index="3" name="flags" type="int" default="0" />
<description>
- Takes a [PackedByteArray] defining a gLTF and returns a [GLTFState] object through the [param state] parameter.
+ Takes a [PackedByteArray] defining a GLTF and imports the data to the given [GLTFState] object through the [param state] parameter.
[b]Note:[/b] The [param base_path] tells [method append_from_buffer] where to find dependencies and can be empty.
</description>
</method>
@@ -26,7 +26,7 @@
<param index="2" name="flags" type="int" default="0" />
<param index="3" name="base_path" type="String" default="&quot;&quot;" />
<description>
- Takes a path to a gLTF file and returns a [GLTFState] object through the [param state] parameter.
+ Takes a path to a GLTF file and imports the data at that file path to the given [GLTFState] object through the [param state] parameter.
[b]Note:[/b] The [param base_path] tells [method append_from_file] where to find dependencies and can be empty.
</description>
</method>
@@ -36,14 +36,14 @@
<param index="1" name="state" type="GLTFState" />
<param index="2" name="flags" type="int" default="0" />
<description>
- Takes a Godot Engine scene node and returns a [GLTFState] object through the [param state] parameter.
+ Takes a Godot Engine scene node and exports it and its descendants to the given [GLTFState] object through the [param state] parameter.
</description>
</method>
<method name="generate_buffer">
<return type="PackedByteArray" />
<param index="0" name="state" type="GLTFState" />
<description>
- Takes a [GLTFState] object through the [param state] parameter and returns a gLTF [PackedByteArray].
+ Takes a [GLTFState] object through the [param state] parameter and returns a GLTF [PackedByteArray].
</description>
</method>
<method name="generate_scene">
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp
index 67bb99de16..183190460e 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp
@@ -461,7 +461,7 @@ void GridMapEditor::_delete_selection() {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("GridMap Delete Selection"));
for (int i = selection.begin.x; i <= selection.end.x; i++) {
for (int j = selection.begin.y; j <= selection.end.y; j++) {
@@ -482,7 +482,7 @@ void GridMapEditor::_fill_selection() {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("GridMap Fill Selection"));
for (int i = selection.begin.x; i <= selection.end.x; i++) {
for (int j = selection.begin.y; j <= selection.end.y; j++) {
@@ -576,7 +576,7 @@ void GridMapEditor::_do_paste() {
rot = node->get_basis_with_orthogonal_index(paste_indicator.orientation);
Vector3 ofs = paste_indicator.current - paste_indicator.click;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("GridMap Paste Selection"));
for (const ClipboardItem &item : clipboard_items) {
@@ -664,7 +664,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
} else {
if ((mb->get_button_index() == MouseButton::RIGHT && input_action == INPUT_ERASE) || (mb->get_button_index() == MouseButton::LEFT && input_action == INPUT_PAINT)) {
if (set_items.size()) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("GridMap Paint"));
for (const SetItem &si : set_items) {
undo_redo->add_do_method(node, "set_cell_item", si.position, si.new_value, si.new_orientation);
@@ -686,7 +686,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
}
if (mb->get_button_index() == MouseButton::LEFT && input_action == INPUT_SELECT) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("GridMap Selection"));
undo_redo->add_do_method(this, "_set_selection", selection.active, selection.begin, selection.end);
undo_redo->add_undo_method(this, "_set_selection", last_selection.active, last_selection.begin, last_selection.end);
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index c45fdda75c..e39127e0a1 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -688,7 +688,7 @@ bool CSharpLanguage::is_assembly_reloading_needed() {
return false; // Already up to date
}
} else {
- String assembly_name = ProjectSettings::get_singleton()->get_setting("dotnet/project/assembly_name");
+ String assembly_name = GLOBAL_GET("dotnet/project/assembly_name");
if (assembly_name.is_empty()) {
assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
index a2916d4ae8..0e46b63b59 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
@@ -437,48 +437,25 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if the <see cref="AABB"/> overlaps with <paramref name="with"/>
/// (i.e. they have at least one point in common).
- ///
- /// If <paramref name="includeBorders"/> is <see langword="true"/>,
- /// they will also be considered overlapping if their borders touch,
- /// even without intersection.
/// </summary>
/// <param name="with">The other <see cref="AABB"/> to check for intersections with.</param>
- /// <param name="includeBorders">Whether or not to consider borders.</param>
/// <returns>
/// A <see langword="bool"/> for whether or not they are intersecting.
/// </returns>
- public readonly bool Intersects(AABB with, bool includeBorders = false)
+ public readonly bool Intersects(AABB with)
{
- if (includeBorders)
- {
- if (_position.x > with._position.x + with._size.x)
- return false;
- if (_position.x + _size.x < with._position.x)
- return false;
- if (_position.y > with._position.y + with._size.y)
- return false;
- if (_position.y + _size.y < with._position.y)
- return false;
- if (_position.z > with._position.z + with._size.z)
- return false;
- if (_position.z + _size.z < with._position.z)
- return false;
- }
- else
- {
- if (_position.x >= with._position.x + with._size.x)
- return false;
- if (_position.x + _size.x <= with._position.x)
- return false;
- if (_position.y >= with._position.y + with._size.y)
- return false;
- if (_position.y + _size.y <= with._position.y)
- return false;
- if (_position.z >= with._position.z + with._size.z)
- return false;
- if (_position.z + _size.z <= with._position.z)
- return false;
- }
+ if (_position.x >= with._position.x + with._size.x)
+ return false;
+ if (_position.x + _size.x <= with._position.x)
+ return false;
+ if (_position.y >= with._position.y + with._size.y)
+ return false;
+ if (_position.y + _size.y <= with._position.y)
+ return false;
+ if (_position.z >= with._position.z + with._size.z)
+ return false;
+ if (_position.z + _size.z <= with._position.z)
+ return false;
return true;
}
@@ -586,6 +563,16 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this <see cref="AABB"/> is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return _position.IsFinite() && _size.IsFinite();
+ }
+
+ /// <summary>
/// Returns a larger <see cref="AABB"/> that contains this <see cref="AABB"/> and <paramref name="with"/>.
/// </summary>
/// <param name="with">The other <see cref="AABB"/>.</param>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
index 5d390a298d..b57317e1d0 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
@@ -120,31 +120,6 @@ namespace Godot
}
/// <summary>
- /// The scale of this basis.
- /// </summary>
- /// <value>Equivalent to the lengths of each column vector, but negative if the determinant is negative.</value>
- public Vector3 Scale
- {
- readonly get
- {
- real_t detSign = Mathf.Sign(Determinant());
- return detSign * new Vector3
- (
- Column0.Length(),
- Column1.Length(),
- Column2.Length()
- );
- }
- set
- {
- value /= Scale; // Value becomes what's called "delta_scale" in core.
- Column0 *= value.x;
- Column1 *= value.y;
- Column2 *= value.z;
- }
- }
-
- /// <summary>
/// Access whole columns in the form of <see cref="Vector3"/>.
/// </summary>
/// <param name="column">Which column vector.</param>
@@ -493,12 +468,6 @@ namespace Godot
}
}
- /// <summary>
- /// Returns the basis's rotation in the form of a quaternion.
- /// See <see cref="GetEuler"/> if you need Euler angles, but keep in
- /// mind that quaternions should generally be preferred to Euler angles.
- /// </summary>
- /// <returns>A <see cref="Quaternion"/> representing the basis's rotation.</returns>
internal readonly Quaternion GetQuaternion()
{
real_t trace = Row0[0] + Row1[1] + Row2[2];
@@ -573,106 +542,18 @@ namespace Godot
}
/// <summary>
- /// Get rows by index. Rows are not very useful for user code,
- /// but are more efficient for some internal calculations.
- /// </summary>
- /// <param name="index">Which row.</param>
- /// <exception cref="ArgumentOutOfRangeException">
- /// <paramref name="index"/> is not 0, 1 or 2.
- /// </exception>
- /// <returns>One of <c>Row0</c>, <c>Row1</c>, or <c>Row2</c>.</returns>
- public readonly Vector3 GetRow(int index)
- {
- switch (index)
- {
- case 0:
- return Row0;
- case 1:
- return Row1;
- case 2:
- return Row2;
- default:
- throw new ArgumentOutOfRangeException(nameof(index));
- }
- }
-
- /// <summary>
- /// Sets rows by index. Rows are not very useful for user code,
- /// but are more efficient for some internal calculations.
+ /// Assuming that the matrix is the combination of a rotation and scaling,
+ /// return the absolute value of scaling factors along each axis.
/// </summary>
- /// <param name="index">Which row.</param>
- /// <param name="value">The vector to set the row to.</param>
- /// <exception cref="ArgumentOutOfRangeException">
- /// <paramref name="index"/> is not 0, 1 or 2.
- /// </exception>
- public void SetRow(int index, Vector3 value)
+ public readonly Vector3 GetScale()
{
- switch (index)
- {
- case 0:
- Row0 = value;
- return;
- case 1:
- Row1 = value;
- return;
- case 2:
- Row2 = value;
- return;
- default:
- throw new ArgumentOutOfRangeException(nameof(index));
- }
- }
-
- /// <summary>
- /// This function considers a discretization of rotations into
- /// 24 points on unit sphere, lying along the vectors (x, y, z) with
- /// each component being either -1, 0, or 1, and returns the index
- /// of the point best representing the orientation of the object.
- /// It is mainly used by the <see cref="GridMap"/> editor.
- ///
- /// For further details, refer to the Godot source code.
- /// </summary>
- /// <returns>The orthogonal index.</returns>
- public readonly int GetOrthogonalIndex()
- {
- var orth = this;
-
- for (int i = 0; i < 3; i++)
- {
- for (int j = 0; j < 3; j++)
- {
- var row = orth.GetRow(i);
-
- real_t v = row[j];
-
- if (v > 0.5f)
- {
- v = 1.0f;
- }
- else if (v < -0.5f)
- {
- v = -1.0f;
- }
- else
- {
- v = 0f;
- }
-
- row[j] = v;
-
- orth.SetRow(i, row);
- }
- }
-
- for (int i = 0; i < 24; i++)
- {
- if (orth == _orthoBases[i])
- {
- return i;
- }
- }
-
- return 0;
+ real_t detSign = Mathf.Sign(Determinant());
+ return detSign * new Vector3
+ (
+ Column0.Length(),
+ Column1.Length(),
+ Column2.Length()
+ );
}
/// <summary>
@@ -709,6 +590,16 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if this basis is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return Row0.IsFinite() && Row1.IsFinite() && Row2.IsFinite();
+ }
+
internal readonly Basis Lerp(Basis to, real_t weight)
{
Basis b = this;
@@ -896,7 +787,7 @@ namespace Godot
/// <param name="quaternion">The quaternion to create the basis from.</param>
public Basis(Quaternion quaternion)
{
- real_t s = 2.0f / quaternion.LengthSquared;
+ real_t s = 2.0f / quaternion.LengthSquared();
real_t xs = quaternion.x * s;
real_t ys = quaternion.y * s;
@@ -925,26 +816,26 @@ namespace Godot
public Basis(Vector3 axis, real_t angle)
{
Vector3 axisSq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z);
- real_t cosine = Mathf.Cos(angle);
- Row0.x = axisSq.x + cosine * (1.0f - axisSq.x);
- Row1.y = axisSq.y + cosine * (1.0f - axisSq.y);
- Row2.z = axisSq.z + cosine * (1.0f - axisSq.z);
+ (real_t sin, real_t cos) = Mathf.SinCos(angle);
+
+ Row0.x = axisSq.x + cos * (1.0f - axisSq.x);
+ Row1.y = axisSq.y + cos * (1.0f - axisSq.y);
+ Row2.z = axisSq.z + cos * (1.0f - axisSq.z);
- real_t sine = Mathf.Sin(angle);
- real_t t = 1.0f - cosine;
+ real_t t = 1.0f - cos;
real_t xyzt = axis.x * axis.y * t;
- real_t zyxs = axis.z * sine;
+ real_t zyxs = axis.z * sin;
Row0.y = xyzt - zyxs;
Row1.x = xyzt + zyxs;
xyzt = axis.x * axis.z * t;
- zyxs = axis.y * sine;
+ zyxs = axis.y * sin;
Row0.z = xyzt + zyxs;
Row2.x = xyzt - zyxs;
xyzt = axis.y * axis.z * t;
- zyxs = axis.x * sine;
+ zyxs = axis.x * sin;
Row1.z = xyzt - zyxs;
Row2.y = xyzt + zyxs;
}
@@ -967,8 +858,20 @@ namespace Godot
// We need to assign the struct fields here first so we can't do it that way...
}
- // Arguments are named such that xy is equal to calling x.y
- internal Basis(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz)
+ /// <summary>
+ /// Constructs a transformation matrix from the given components.
+ /// Arguments are named such that xy is equal to calling <c>x.y</c>.
+ /// </summary>
+ /// <param name="xx">The X component of the X column vector, accessed via <c>b.x.x</c> or <c>[0][0]</c>.</param>
+ /// <param name="yx">The X component of the Y column vector, accessed via <c>b.y.x</c> or <c>[1][0]</c>.</param>
+ /// <param name="zx">The X component of the Z column vector, accessed via <c>b.z.x</c> or <c>[2][0]</c>.</param>
+ /// <param name="xy">The Y component of the X column vector, accessed via <c>b.x.y</c> or <c>[0][1]</c>.</param>
+ /// <param name="yy">The Y component of the Y column vector, accessed via <c>b.y.y</c> or <c>[1][1]</c>.</param>
+ /// <param name="zy">The Y component of the Z column vector, accessed via <c>b.y.y</c> or <c>[2][1]</c>.</param>
+ /// <param name="xz">The Z component of the X column vector, accessed via <c>b.x.y</c> or <c>[0][2]</c>.</param>
+ /// <param name="yz">The Z component of the Y column vector, accessed via <c>b.y.y</c> or <c>[1][2]</c>.</param>
+ /// <param name="zz">The Z component of the Z column vector, accessed via <c>b.y.y</c> or <c>[2][2]</c>.</param>
+ public Basis(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz)
{
Row0 = new Vector3(xx, yx, zx);
Row1 = new Vector3(xy, yy, zy);
@@ -982,19 +885,29 @@ namespace Godot
/// <param name="order">The order to compose the Euler angles.</param>
public static Basis FromEuler(Vector3 euler, EulerOrder order = EulerOrder.Yxz)
{
- real_t c, s;
-
- c = Mathf.Cos(euler.x);
- s = Mathf.Sin(euler.x);
- Basis xmat = new Basis(new Vector3(1, 0, 0), new Vector3(0, c, s), new Vector3(0, -s, c));
+ (real_t sin, real_t cos) = Mathf.SinCos(euler.x);
+ Basis xmat = new Basis
+ (
+ new Vector3(1, 0, 0),
+ new Vector3(0, cos, sin),
+ new Vector3(0, -sin, cos)
+ );
- c = Mathf.Cos(euler.y);
- s = Mathf.Sin(euler.y);
- Basis ymat = new Basis(new Vector3(c, 0, -s), new Vector3(0, 1, 0), new Vector3(s, 0, c));
+ (sin, cos) = Mathf.SinCos(euler.y);
+ Basis ymat = new Basis
+ (
+ new Vector3(cos, 0, -sin),
+ new Vector3(0, 1, 0),
+ new Vector3(sin, 0, cos)
+ );
- c = Mathf.Cos(euler.z);
- s = Mathf.Sin(euler.z);
- Basis zmat = new Basis(new Vector3(c, s, 0), new Vector3(-s, c, 0), new Vector3(0, 0, 1));
+ (sin, cos) = Mathf.SinCos(euler.z);
+ Basis zmat = new Basis
+ (
+ new Vector3(cos, sin, 0),
+ new Vector3(-sin, cos, 0),
+ new Vector3(0, 0, 1)
+ );
switch (order)
{
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs
index 03996bafdd..8325af034c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs
@@ -48,13 +48,8 @@ namespace Godot
}
/// <summary>
- /// Fetches a node. The <see cref="NodePath"/> can be either a relative path (from
- /// the current node) or an absolute path (in the scene tree) to a node. If the path
- /// does not exist, a <see langword="null"/> instance is returned and an error
- /// is logged. Attempts to access methods on the return value will result in an
- /// "Attempt to call &lt;method&gt; on a null instance." error.
- /// Note: Fetching absolute paths only works when the node is inside the scene tree
- /// (see <see cref="IsInsideTree"/>).
+ /// Similar to <see cref="GetNode"/>, but does not log an error if <paramref name="path"/>
+ /// does not point to a valid <see cref="Node"/>.
/// </summary>
/// <example>
/// Example: Assume your current node is Character and the following tree:
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
index 3f9e986f62..b2cb0f5e6b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
@@ -440,6 +440,17 @@ namespace Godot
}
/// <summary>
+ /// Returns whether <paramref name="s"/> is a finite value, i.e. it is not
+ /// <see cref="NaN"/>, positive infinite, or negative infinity.
+ /// </summary>
+ /// <param name="s">The value to check.</param>
+ /// <returns>A <see langword="bool"/> for whether or not the value is a finite value.</returns>
+ public static bool IsFinite(real_t s)
+ {
+ return real_t.IsFinite(s);
+ }
+
+ /// <summary>
/// Returns whether <paramref name="s"/> is an infinity value (either positive infinity or negative infinity).
/// </summary>
/// <param name="s">The value to check.</param>
@@ -460,10 +471,11 @@ namespace Godot
}
/// <summary>
- /// Returns <see langword="true"/> if <paramref name="s"/> is approximately zero.
+ /// Returns <see langword="true"/> if <paramref name="s"/> is zero or almost zero.
/// The comparison is done using a tolerance calculation with <see cref="Epsilon"/>.
///
- /// This method is faster than using <see cref="IsEqualApprox(real_t, real_t)"/> with one value as zero.
+ /// This method is faster than using <see cref="IsEqualApprox(real_t, real_t)"/> with
+ /// one value as zero.
/// </summary>
/// <param name="s">The value to check.</param>
/// <returns>A <see langword="bool"/> for whether or not the value is nearly zero.</returns>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
index ea05c1547c..72a1868964 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
@@ -83,6 +83,17 @@ namespace Godot
}
/// <summary>
+ /// Returns the sine and cosine of angle <paramref name="s"/> in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The sine and cosine of that angle.</returns>
+ public static (real_t Sin, real_t Cos) SinCos(real_t s)
+ {
+ (double sin, double cos) = Math.SinCos(s);
+ return ((real_t)sin, (real_t)cos);
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if <paramref name="a"/> and <paramref name="b"/> are approximately
/// equal to each other.
/// The comparison is done using the provided tolerance value.
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
index 42c6b0a37e..8a125e3c73 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
@@ -15,7 +15,7 @@ namespace Godot
private Vector3 _normal;
/// <summary>
- /// The normal of the plane, which must be normalized.
+ /// The normal of the plane, which must be a unit vector.
/// In the scalar equation of the plane <c>ax + by + cz = d</c>, this is
/// the vector <c>(a, b, c)</c>, where <c>d</c> is the <see cref="D"/> property.
/// </summary>
@@ -85,23 +85,6 @@ namespace Godot
public real_t D { get; set; }
/// <summary>
- /// The center of the plane, the point where the normal line intersects the plane.
- /// </summary>
- /// <value>Equivalent to <see cref="Normal"/> multiplied by <see cref="D"/>.</value>
- public Vector3 Center
- {
- readonly get
- {
- return _normal * D;
- }
- set
- {
- _normal = value.Normalized();
- D = value.Length();
- }
- }
-
- /// <summary>
/// Returns the shortest distance from this plane to the position <paramref name="point"/>.
/// </summary>
/// <param name="point">The position to use for the calculation.</param>
@@ -112,6 +95,16 @@ namespace Godot
}
/// <summary>
+ /// Returns the center of the plane, the point on the plane closest to the origin.
+ /// The point where the normal line going through the origin intersects the plane.
+ /// </summary>
+ /// <value>Equivalent to <see cref="Normal"/> multiplied by <see cref="D"/>.</value>
+ public readonly Vector3 GetCenter()
+ {
+ return _normal * D;
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if point is inside the plane.
/// Comparison uses a custom minimum tolerance threshold.
/// </summary>
@@ -155,7 +148,7 @@ namespace Godot
/// <param name="from">The start of the ray.</param>
/// <param name="dir">The direction of the ray, normalized.</param>
/// <returns>The intersection, or <see langword="null"/> if none is found.</returns>
- public readonly Vector3? IntersectRay(Vector3 from, Vector3 dir)
+ public readonly Vector3? IntersectsRay(Vector3 from, Vector3 dir)
{
real_t den = _normal.Dot(dir);
@@ -183,7 +176,7 @@ namespace Godot
/// <param name="begin">The start of the line segment.</param>
/// <param name="end">The end of the line segment.</param>
/// <returns>The intersection, or <see langword="null"/> if none is found.</returns>
- public readonly Vector3? IntersectSegment(Vector3 begin, Vector3 end)
+ public readonly Vector3? IntersectsSegment(Vector3 begin, Vector3 end)
{
Vector3 segment = begin - end;
real_t den = _normal.Dot(segment);
@@ -205,6 +198,16 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this plane is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return _normal.IsFinite() && Mathf.IsFinite(D);
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if <paramref name="point"/> is located above the plane.
/// </summary>
/// <param name="point">The point to check.</param>
@@ -280,10 +283,21 @@ namespace Godot
}
/// <summary>
+ /// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector.
+ /// The plane will intersect the origin.
+ /// </summary>
+ /// <param name="normal">The normal of the plane, must be a unit vector.</param>
+ public Plane(Vector3 normal)
+ {
+ _normal = normal;
+ D = 0;
+ }
+
+ /// <summary>
/// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector and
/// the plane's distance to the origin <paramref name="d"/>.
/// </summary>
- /// <param name="normal">The normal of the plane, must be normalized.</param>
+ /// <param name="normal">The normal of the plane, must be a unit vector.</param>
/// <param name="d">The plane's distance from the origin. This value is typically non-negative.</param>
public Plane(Vector3 normal, real_t d)
{
@@ -295,7 +309,7 @@ namespace Godot
/// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector and
/// a <paramref name="point"/> on the plane.
/// </summary>
- /// <param name="normal">The normal of the plane, must be normalized.</param>
+ /// <param name="normal">The normal of the plane, must be a unit vector.</param>
/// <param name="point">The point on the plane.</param>
public Plane(Vector3 normal, Vector3 point)
{
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
index fd37f8d9e8..f11b3c553a 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
@@ -381,14 +381,14 @@ namespace Godot
}
real_t radians = Mathf.DegToRad(fovyDegrees / (real_t)2.0);
real_t deltaZ = zFar - zNear;
- real_t sine = Mathf.Sin(radians);
+ (real_t sin, real_t cos) = Mathf.SinCos(radians);
- if ((deltaZ == 0) || (sine == 0) || (aspect == 0))
+ if ((deltaZ == 0) || (sin == 0) || (aspect == 0))
{
return Zero;
}
- real_t cotangent = Mathf.Cos(radians) / sine;
+ real_t cotangent = cos / sin;
Projection proj = Projection.Identity;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
index bd0dea0c1c..8e4f9178f7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
@@ -97,27 +97,6 @@ namespace Godot
}
/// <summary>
- /// Returns the length (magnitude) of the quaternion.
- /// </summary>
- /// <seealso cref="LengthSquared"/>
- /// <value>Equivalent to <c>Mathf.Sqrt(LengthSquared)</c>.</value>
- public readonly real_t Length
- {
- get { return Mathf.Sqrt(LengthSquared); }
- }
-
- /// <summary>
- /// Returns the squared length (squared magnitude) of the quaternion.
- /// This method runs faster than <see cref="Length"/>, so prefer it if
- /// you need to compare quaternions or need the squared length for some formula.
- /// </summary>
- /// <value>Equivalent to <c>Dot(this)</c>.</value>
- public readonly real_t LengthSquared
- {
- get { return Dot(this); }
- }
-
- /// <summary>
/// Returns the angle between this quaternion and <paramref name="to"/>.
/// This is the magnitude of the angle you would need to rotate
/// by to get from one to the other.
@@ -340,12 +319,22 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this quaternion is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return Mathf.IsFinite(x) && Mathf.IsFinite(y) && Mathf.IsFinite(z) && Mathf.IsFinite(w);
+ }
+
+ /// <summary>
/// Returns whether the quaternion is normalized or not.
/// </summary>
/// <returns>A <see langword="bool"/> for whether the quaternion is normalized or not.</returns>
public readonly bool IsNormalized()
{
- return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon;
+ return Mathf.Abs(LengthSquared() - 1) <= Mathf.Epsilon;
}
public readonly Quaternion Log()
@@ -355,12 +344,33 @@ namespace Godot
}
/// <summary>
+ /// Returns the length (magnitude) of the quaternion.
+ /// </summary>
+ /// <seealso cref="LengthSquared"/>
+ /// <value>Equivalent to <c>Mathf.Sqrt(LengthSquared)</c>.</value>
+ public readonly real_t Length()
+ {
+ return Mathf.Sqrt(LengthSquared());
+ }
+
+ /// <summary>
+ /// Returns the squared length (squared magnitude) of the quaternion.
+ /// This method runs faster than <see cref="Length"/>, so prefer it if
+ /// you need to compare quaternions or need the squared length for some formula.
+ /// </summary>
+ /// <value>Equivalent to <c>Dot(this)</c>.</value>
+ public readonly real_t LengthSquared()
+ {
+ return Dot(this);
+ }
+
+ /// <summary>
/// Returns a copy of the quaternion, normalized to unit length.
/// </summary>
/// <returns>The normalized quaternion.</returns>
public readonly Quaternion Normalized()
{
- return this / Length;
+ return this / Length();
}
/// <summary>
@@ -532,14 +542,13 @@ namespace Godot
}
else
{
- real_t sinAngle = Mathf.Sin(angle * 0.5f);
- real_t cosAngle = Mathf.Cos(angle * 0.5f);
- real_t s = sinAngle / d;
+ (real_t sin, real_t cos) = Mathf.SinCos(angle * 0.5f);
+ real_t s = sin / d;
x = axis.x * s;
y = axis.y * s;
z = axis.z * s;
- w = cosAngle;
+ w = cos;
}
}
@@ -583,12 +592,9 @@ namespace Godot
// Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6)
// a3 is the angle of the first rotation, following the notation in this reference.
- real_t cosA1 = Mathf.Cos(halfA1);
- real_t sinA1 = Mathf.Sin(halfA1);
- real_t cosA2 = Mathf.Cos(halfA2);
- real_t sinA2 = Mathf.Sin(halfA2);
- real_t cosA3 = Mathf.Cos(halfA3);
- real_t sinA3 = Mathf.Sin(halfA3);
+ (real_t sinA1, real_t cosA1) = Mathf.SinCos(halfA1);
+ (real_t sinA2, real_t cosA2) = Mathf.SinCos(halfA2);
+ (real_t sinA3, real_t cosA3) = Mathf.SinCos(halfA3);
return new Quaternion(
(sinA1 * cosA2 * sinA3) + (cosA1 * sinA2 * cosA3),
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
index b0e0e75a34..1a8696d3bc 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
@@ -101,6 +101,16 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this <see cref="Rect2"/> is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public bool IsFinite()
+ {
+ return _position.IsFinite() && _size.IsFinite();
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if this <see cref="Rect2"/> completely encloses another one.
/// </summary>
/// <param name="b">The other <see cref="Rect2"/> that may be enclosed.</param>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
index faee81a98a..cf8939a859 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
@@ -275,38 +275,19 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if the <see cref="Rect2i"/> overlaps with <paramref name="b"/>
/// (i.e. they have at least one point in common).
- ///
- /// If <paramref name="includeBorders"/> is <see langword="true"/>,
- /// they will also be considered overlapping if their borders touch,
- /// even without intersection.
/// </summary>
/// <param name="b">The other <see cref="Rect2i"/> to check for intersections with.</param>
- /// <param name="includeBorders">Whether or not to consider borders.</param>
/// <returns>A <see langword="bool"/> for whether or not they are intersecting.</returns>
- public readonly bool Intersects(Rect2i b, bool includeBorders = false)
+ public readonly bool Intersects(Rect2i b)
{
- if (includeBorders)
- {
- if (_position.x > b._position.x + b._size.x)
- return false;
- if (_position.x + _size.x < b._position.x)
- return false;
- if (_position.y > b._position.y + b._size.y)
- return false;
- if (_position.y + _size.y < b._position.y)
- return false;
- }
- else
- {
- if (_position.x >= b._position.x + b._size.x)
- return false;
- if (_position.x + _size.x <= b._position.x)
- return false;
- if (_position.y >= b._position.y + b._size.y)
- return false;
- if (_position.y + _size.y <= b._position.y)
- return false;
- }
+ if (_position.x >= b._position.x + b._size.x)
+ return false;
+ if (_position.x + _size.x <= b._position.x)
+ return false;
+ if (_position.y >= b._position.y + b._size.y)
+ return false;
+ if (_position.y + _size.y <= b._position.y)
+ return false;
return true;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
index 756f71e5b2..fa060e3a53 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
@@ -32,45 +32,6 @@ namespace Godot
public Vector2 origin;
/// <summary>
- /// The rotation of this transformation matrix.
- /// </summary>
- /// <value>Getting is equivalent to calling <see cref="Mathf.Atan2(real_t, real_t)"/> with the values of <see cref="x"/>.</value>
- public real_t Rotation
- {
- readonly get
- {
- return Mathf.Atan2(x.y, x.x);
- }
- set
- {
- Vector2 scale = Scale;
- x.x = y.y = Mathf.Cos(value);
- x.y = y.x = Mathf.Sin(value);
- y.x *= -1;
- Scale = scale;
- }
- }
-
- /// <summary>
- /// The scale of this transformation matrix.
- /// </summary>
- /// <value>Equivalent to the lengths of each column vector, but Y is negative if the determinant is negative.</value>
- public Vector2 Scale
- {
- readonly get
- {
- real_t detSign = Mathf.Sign(BasisDeterminant());
- return new Vector2(x.Length(), detSign * y.Length());
- }
- set
- {
- value /= Scale; // Value becomes what's called "delta_scale" in core.
- x *= value.x;
- y *= value.y;
- }
- }
-
- /// <summary>
/// Access whole columns in the form of <see cref="Vector2"/>.
/// The third column is the <see cref="origin"/> vector.
/// </summary>
@@ -203,6 +164,23 @@ namespace Godot
}
/// <summary>
+ /// Returns the transform's rotation (in radians).
+ /// </summary>
+ public readonly real_t GetRotation()
+ {
+ return Mathf.Atan2(x.y, x.x);
+ }
+
+ /// <summary>
+ /// Returns the scale.
+ /// </summary>
+ public readonly Vector2 GetScale()
+ {
+ real_t detSign = Mathf.Sign(BasisDeterminant());
+ return new Vector2(x.Length(), detSign * y.Length());
+ }
+
+ /// <summary>
/// Interpolates this transform to the other <paramref name="transform"/> by <paramref name="weight"/>.
/// </summary>
/// <param name="transform">The other transform.</param>
@@ -210,15 +188,17 @@ namespace Godot
/// <returns>The interpolated transform.</returns>
public readonly Transform2D InterpolateWith(Transform2D transform, real_t weight)
{
- real_t r1 = Rotation;
- real_t r2 = transform.Rotation;
+ real_t r1 = GetRotation();
+ real_t r2 = transform.GetRotation();
- Vector2 s1 = Scale;
- Vector2 s2 = transform.Scale;
+ Vector2 s1 = GetScale();
+ Vector2 s2 = transform.GetScale();
// Slerp rotation
- var v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1));
- var v2 = new Vector2(Mathf.Cos(r2), Mathf.Sin(r2));
+ (real_t sin1, real_t cos1) = Mathf.SinCos(r1);
+ (real_t sin2, real_t cos2) = Mathf.SinCos(r2);
+ var v1 = new Vector2(cos1, sin1);
+ var v2 = new Vector2(cos2, sin2);
real_t dot = v1.Dot(v2);
@@ -235,7 +215,8 @@ namespace Godot
{
real_t angle = weight * Mathf.Acos(dot);
Vector2 v3 = (v2 - (v1 * dot)).Normalized();
- v = (v1 * Mathf.Cos(angle)) + (v3 * Mathf.Sin(angle));
+ (real_t sine, real_t cos) = Mathf.SinCos(angle);
+ v = (v1 * sine) + (v3 * cos);
}
// Extract parameters
@@ -271,6 +252,16 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this transform is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return x.IsFinite() && y.IsFinite() && origin.IsFinite();
+ }
+
+ /// <summary>
/// Returns the transform with the basis orthogonal (90 degrees),
/// and normalized axis vectors (scale of 1 or -1).
/// </summary>
@@ -423,7 +414,7 @@ namespace Godot
/// <summary>
/// Constructs a transformation matrix from the given components.
- /// Arguments are named such that xy is equal to calling x.y
+ /// Arguments are named such that xy is equal to calling <c>x.y</c>.
/// </summary>
/// <param name="xx">The X component of the X column vector, accessed via <c>t.x.x</c> or <c>[0][0]</c>.</param>
/// <param name="xy">The Y component of the X column vector, accessed via <c>t.x.y</c> or <c>[0][1]</c>.</param>
@@ -446,13 +437,34 @@ namespace Godot
/// <param name="origin">The origin vector, or column index 2.</param>
public Transform2D(real_t rotation, Vector2 origin)
{
- x.x = y.y = Mathf.Cos(rotation);
- x.y = y.x = Mathf.Sin(rotation);
+ (real_t sin, real_t cos) = Mathf.SinCos(rotation);
+ x.x = y.y = cos;
+ x.y = y.x = sin;
y.x *= -1;
this.origin = origin;
}
/// <summary>
+ /// Constructs a transformation matrix from a <paramref name="rotation"/> value,
+ /// <paramref name="scale"/> vector, <paramref name="skew"/> value, and
+ /// <paramref name="origin"/> vector.
+ /// </summary>
+ /// <param name="rotation">The rotation of the new transform, in radians.</param>
+ /// <param name="scale">The scale of the new transform.</param>
+ /// <param name="skew">The skew of the new transform, in radians.</param>
+ /// <param name="origin">The origin vector, or column index 2.</param>
+ public Transform2D(real_t rotation, Vector2 scale, real_t skew, Vector2 origin)
+ {
+ (real_t rotationSin, real_t rotationCos) = Mathf.SinCos(rotation);
+ (real_t rotationSkewSin, real_t rotationSkewCos) = Mathf.SinCos(rotation + skew);
+ x.x = rotationCos * scale.x;
+ y.y = rotationSkewCos * scale.y;
+ y.x = -rotationSkewSin * scale.y;
+ x.y = rotationSin * scale.x;
+ this.origin = origin;
+ }
+
+ /// <summary>
/// Composes these two transformation matrices by multiplying them
/// together. This has the effect of transforming the second transform
/// (the child) by the first transform (the parent).
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
index 39167bd116..6b2475fc59 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
@@ -115,16 +115,30 @@ namespace Godot
}
/// <summary>
- /// Interpolates this transform to the other <paramref name="transform"/> by <paramref name="weight"/>.
+ /// Returns a transform interpolated between this transform and another
+ /// <paramref name="transform"/> by a given <paramref name="weight"/>
+ /// (on the range of 0.0 to 1.0).
/// </summary>
/// <param name="transform">The other transform.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The interpolated transform.</returns>
public readonly Transform3D InterpolateWith(Transform3D transform, real_t weight)
{
- Basis retBasis = basis.Lerp(transform.basis, weight);
- Vector3 retOrigin = origin.Lerp(transform.origin, weight);
- return new Transform3D(retBasis, retOrigin);
+ Vector3 sourceScale = basis.GetScale();
+ Quaternion sourceRotation = basis.GetRotationQuaternion();
+ Vector3 sourceLocation = origin;
+
+ Vector3 destinationScale = transform.basis.GetScale();
+ Quaternion destinationRotation = transform.basis.GetRotationQuaternion();
+ Vector3 destinationLocation = transform.origin;
+
+ var interpolated = new Transform3D();
+ Quaternion quaternion = sourceRotation.Slerp(destinationRotation, weight).Normalized();
+ Vector3 scale = sourceScale.Lerp(destinationScale, weight);
+ interpolated.basis.SetQuaternionScale(quaternion, scale);
+ interpolated.origin = sourceLocation.Lerp(destinationLocation, weight);
+
+ return interpolated;
}
/// <summary>
@@ -140,6 +154,16 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this transform is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return basis.IsFinite() && origin.IsFinite();
+ }
+
+ /// <summary>
/// Returns a copy of the transform rotated such that its
/// -Z axis (forward) points towards the <paramref name="target"/> position.
///
@@ -223,34 +247,6 @@ namespace Godot
return new Transform3D(basis * tmpBasis, origin);
}
- /// <summary>
- /// Returns a transform spherically interpolated between this transform and
- /// another <paramref name="transform"/> by <paramref name="weight"/>.
- /// </summary>
- /// <param name="transform">The other transform.</param>
- /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
- /// <returns>The interpolated transform.</returns>
- public readonly Transform3D SphericalInterpolateWith(Transform3D transform, real_t weight)
- {
- /* not sure if very "efficient" but good enough? */
-
- Vector3 sourceScale = basis.Scale;
- Quaternion sourceRotation = basis.GetRotationQuaternion();
- Vector3 sourceLocation = origin;
-
- Vector3 destinationScale = transform.basis.Scale;
- Quaternion destinationRotation = transform.basis.GetRotationQuaternion();
- Vector3 destinationLocation = transform.origin;
-
- var interpolated = new Transform3D();
- Quaternion quaternion = sourceRotation.Slerp(destinationRotation, weight).Normalized();
- Vector3 scale = sourceScale.Lerp(destinationScale, weight);
- interpolated.basis.SetQuaternionScale(quaternion, scale);
- interpolated.origin = sourceLocation.Lerp(destinationLocation, weight);
-
- return interpolated;
- }
-
private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up)
{
// Make rotation matrix
@@ -346,15 +342,25 @@ namespace Godot
}
/// <summary>
- /// Constructs a transformation matrix from the given <paramref name="quaternion"/>
- /// and <paramref name="origin"/> vector.
+ /// Constructs a transformation matrix from the given components.
+ /// Arguments are named such that xy is equal to calling <c>basis.x.y</c>.
/// </summary>
- /// <param name="quaternion">The <see cref="Quaternion"/> to create the basis from.</param>
- /// <param name="origin">The origin vector, or column index 3.</param>
- public Transform3D(Quaternion quaternion, Vector3 origin)
+ /// <param name="xx">The X component of the X column vector, accessed via <c>t.basis.x.x</c> or <c>[0][0]</c>.</param>
+ /// <param name="yx">The X component of the Y column vector, accessed via <c>t.basis.y.x</c> or <c>[1][0]</c>.</param>
+ /// <param name="zx">The X component of the Z column vector, accessed via <c>t.basis.z.x</c> or <c>[2][0]</c>.</param>
+ /// <param name="xy">The Y component of the X column vector, accessed via <c>t.basis.x.y</c> or <c>[0][1]</c>.</param>
+ /// <param name="yy">The Y component of the Y column vector, accessed via <c>t.basis.y.y</c> or <c>[1][1]</c>.</param>
+ /// <param name="zy">The Y component of the Z column vector, accessed via <c>t.basis.y.y</c> or <c>[2][1]</c>.</param>
+ /// <param name="xz">The Z component of the X column vector, accessed via <c>t.basis.x.y</c> or <c>[0][2]</c>.</param>
+ /// <param name="yz">The Z component of the Y column vector, accessed via <c>t.basis.y.y</c> or <c>[1][2]</c>.</param>
+ /// <param name="zz">The Z component of the Z column vector, accessed via <c>t.basis.y.y</c> or <c>[2][2]</c>.</param>
+ /// <param name="ox">The X component of the origin vector, accessed via <c>t.origin.x</c> or <c>[2][0]</c>.</param>
+ /// <param name="oy">The Y component of the origin vector, accessed via <c>t.origin.y</c> or <c>[2][1]</c>.</param>
+ /// <param name="oz">The Z component of the origin vector, accessed via <c>t.origin.z</c> or <c>[2][2]</c>.</param>
+ public Transform3D(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz, real_t ox, real_t oy, real_t oz)
{
- basis = new Basis(quaternion);
- this.origin = origin;
+ basis = new Basis(xx, yx, zx, xy, yy, zy, xz, yz, zz);
+ origin = new Vector3(ox, oy, oz);
}
/// <summary>
@@ -370,6 +376,29 @@ namespace Godot
}
/// <summary>
+ /// Constructs a transformation matrix from the given <paramref name="projection"/>
+ /// by trimming the last row of the projection matrix (<c>projection.x.w</c>,
+ /// <c>projection.y.w</c>, <c>projection.z.w</c>, and <c>projection.w.w</c>
+ /// are not copied over).
+ /// </summary>
+ /// <param name="projection">The <see cref="Projection"/> to create the transform from.</param>
+ public Transform3D(Projection projection)
+ {
+ basis = new Basis
+ (
+ projection.x.x, projection.y.x, projection.z.x,
+ projection.x.y, projection.y.y, projection.z.y,
+ projection.x.z, projection.y.z, projection.z.z
+ );
+ origin = new Vector3
+ (
+ projection.w.x,
+ projection.w.y,
+ projection.w.z
+ );
+ }
+
+ /// <summary>
/// Composes these two transformation matrices by multiplying them
/// together. This has the effect of transforming the second transform
/// (the child) by the first transform (the parent).
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index 4c60080ee9..1e88e18b3d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -334,6 +334,16 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this vector is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return Mathf.IsFinite(x) && Mathf.IsFinite(y);
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise.
/// </summary>
/// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns>
@@ -529,11 +539,12 @@ namespace Godot
/// <returns>The rotated vector.</returns>
public readonly Vector2 Rotated(real_t angle)
{
- real_t sine = Mathf.Sin(angle);
- real_t cosi = Mathf.Cos(angle);
- return new Vector2(
- x * cosi - y * sine,
- x * sine + y * cosi);
+ (real_t sin, real_t cos) = Mathf.SinCos(angle);
+ return new Vector2
+ (
+ x * cos - y * sin,
+ x * sin + y * cos
+ );
}
/// <summary>
@@ -683,7 +694,8 @@ namespace Godot
/// <returns>The resulting vector.</returns>
public static Vector2 FromAngle(real_t angle)
{
- return new Vector2(Mathf.Cos(angle), Mathf.Sin(angle));
+ (real_t sin, real_t cos) = Mathf.SinCos(angle);
+ return new Vector2(cos, sin);
}
/// <summary>
@@ -989,6 +1001,18 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this vector's values are approximately zero,
+ /// by running <see cref="Mathf.IsZeroApprox(real_t)"/> on each component.
+ /// This method is faster than using <see cref="IsEqualApprox"/> with one value
+ /// as a zero vector.
+ /// </summary>
+ /// <returns>Whether or not the vector is approximately zero.</returns>
+ public readonly bool IsZeroApprox()
+ {
+ return Mathf.IsZeroApprox(x) && Mathf.IsZeroApprox(y);
+ }
+
+ /// <summary>
/// Serves as the hash function for <see cref="Vector2"/>.
/// </summary>
/// <returns>A hash code for this vector.</returns>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index fefdee33a5..031464dcc6 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -331,6 +331,16 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this vector is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return Mathf.IsFinite(x) && Mathf.IsFinite(y) && Mathf.IsFinite(z);
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise.
/// </summary>
/// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns>
@@ -1060,6 +1070,18 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this vector's values are approximately zero,
+ /// by running <see cref="Mathf.IsZeroApprox(real_t)"/> on each component.
+ /// This method is faster than using <see cref="IsEqualApprox"/> with one value
+ /// as a zero vector.
+ /// </summary>
+ /// <returns>Whether or not the vector is approximately zero.</returns>
+ public readonly bool IsZeroApprox()
+ {
+ return Mathf.IsZeroApprox(x) && Mathf.IsZeroApprox(y) && Mathf.IsZeroApprox(z);
+ }
+
+ /// <summary>
/// Serves as the hash function for <see cref="Vector3"/>.
/// </summary>
/// <returns>A hash code for this vector.</returns>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
index 3191e8adc0..0f4528bb40 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
@@ -280,6 +280,16 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this vector is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return Mathf.IsFinite(x) && Mathf.IsFinite(y) && Mathf.IsFinite(z) && Mathf.IsFinite(w);
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise.
/// </summary>
/// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns>
@@ -857,6 +867,18 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this vector's values are approximately zero,
+ /// by running <see cref="Mathf.IsZeroApprox(real_t)"/> on each component.
+ /// This method is faster than using <see cref="IsEqualApprox"/> with one value
+ /// as a zero vector.
+ /// </summary>
+ /// <returns>Whether or not the vector is approximately zero.</returns>
+ public readonly bool IsZeroApprox()
+ {
+ return Mathf.IsZeroApprox(x) && Mathf.IsZeroApprox(y) && Mathf.IsZeroApprox(z) && Mathf.IsZeroApprox(w);
+ }
+
+ /// <summary>
/// Serves as the hash function for <see cref="Vector4"/>.
/// </summary>
/// <returns>A hash code for this vector.</returns>
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 6664dfb2dd..86b0e04206 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -289,7 +289,7 @@ godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime
}
#else
static String get_assembly_name() {
- String assembly_name = ProjectSettings::get_singleton()->get_setting("dotnet/project/assembly_name");
+ String assembly_name = GLOBAL_GET("dotnet/project/assembly_name");
if (assembly_name.is_empty()) {
assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
@@ -466,7 +466,7 @@ void GDMono::_init_godot_api_hashes() {
#ifdef TOOLS_ENABLED
bool GDMono::_load_project_assembly() {
- String assembly_name = ProjectSettings::get_singleton()->get_setting("dotnet/project/assembly_name");
+ String assembly_name = GLOBAL_GET("dotnet/project/assembly_name");
if (assembly_name.is_empty()) {
assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
diff --git a/modules/multiplayer/editor/replication_editor.cpp b/modules/multiplayer/editor/replication_editor.cpp
index dbf1eecf0e..9b071ecc02 100644
--- a/modules/multiplayer/editor/replication_editor.cpp
+++ b/modules/multiplayer/editor/replication_editor.cpp
@@ -142,7 +142,7 @@ void ReplicationEditor::_add_sync_property(String p_path) {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add property to synchronizer"));
if (config.is_null()) {
@@ -250,7 +250,7 @@ ReplicationEditor::ReplicationEditor() {
tree->add_child(drop_label);
drop_label->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
- tree->set_drag_forwarding(this);
+ tree->set_drag_forwarding_compat(this);
}
void ReplicationEditor::_bind_methods() {
@@ -357,7 +357,7 @@ void ReplicationEditor::_tree_item_edited() {
int column = tree->get_edited_column();
ERR_FAIL_COND(column < 1 || column > 2);
const NodePath prop = ti->get_metadata(0);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
bool value = ti->is_checked(column);
String method;
if (column == 1) {
@@ -397,7 +397,7 @@ void ReplicationEditor::_dialog_closed(bool p_confirmed) {
int idx = config->property_get_index(prop);
bool spawn = config->property_get_spawn(prop);
bool sync = config->property_get_sync(prop);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove Property"));
undo_redo->add_do_method(config.ptr(), "remove_property", prop);
undo_redo->add_undo_method(config.ptr(), "add_property", prop, idx);
diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp
index 7ed69a84d0..0aa54b69f9 100644
--- a/modules/multiplayer/multiplayer_spawner.cpp
+++ b/modules/multiplayer/multiplayer_spawner.cpp
@@ -199,10 +199,6 @@ void MultiplayerSpawner::_notification(int p_what) {
Node *node = Object::cast_to<Node>(ObjectDB::get_instance(E.key));
ERR_CONTINUE(!node);
node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit));
- // This is unlikely, but might still crash the engine.
- if (node->is_connected(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready))) {
- node->disconnect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready));
- }
get_multiplayer()->object_configuration_remove(node, this);
}
tracked_nodes.clear();
@@ -244,11 +240,11 @@ void MultiplayerSpawner::_track(Node *p_node, const Variant &p_argument, int p_s
if (!tracked_nodes.has(oid)) {
tracked_nodes[oid] = SpawnInfo(p_argument.duplicate(true), p_scene_id);
p_node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit).bind(p_node->get_instance_id()), CONNECT_ONE_SHOT);
- p_node->connect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready).bind(p_node->get_instance_id()), CONNECT_ONE_SHOT);
+ _spawn_notify(p_node->get_instance_id());
}
}
-void MultiplayerSpawner::_node_ready(ObjectID p_id) {
+void MultiplayerSpawner::_spawn_notify(ObjectID p_id) {
get_multiplayer()->object_configuration_add(ObjectDB::get_instance(p_id), this);
}
diff --git a/modules/multiplayer/multiplayer_spawner.h b/modules/multiplayer/multiplayer_spawner.h
index 8d401a6818..8a54140e32 100644
--- a/modules/multiplayer/multiplayer_spawner.h
+++ b/modules/multiplayer/multiplayer_spawner.h
@@ -77,7 +77,7 @@ private:
void _track(Node *p_node, const Variant &p_argument, int p_scene_id = INVALID_ID);
void _node_added(Node *p_node);
void _node_exit(ObjectID p_id);
- void _node_ready(ObjectID p_id);
+ void _spawn_notify(ObjectID p_id);
Vector<String> _get_spawnable_scenes() const;
void _set_spawnable_scenes(const Vector<String> &p_scenes);
diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp
index e1b7b0c346..233ff76c7d 100644
--- a/modules/multiplayer/scene_replication_interface.cpp
+++ b/modules/multiplayer/scene_replication_interface.cpp
@@ -125,6 +125,20 @@ void SceneReplicationInterface::on_reset() {
}
void SceneReplicationInterface::on_network_process() {
+ // Prevent endless stalling in case of unforseen spawn errors.
+ if (spawn_queue.size()) {
+ ERR_PRINT("An error happened during last spawn, this usually means the 'ready' signal was not emitted by the spawned node.");
+ for (const ObjectID &oid : spawn_queue) {
+ Node *node = get_id_as<Node>(oid);
+ ERR_CONTINUE(!node);
+ if (node->is_connected(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready))) {
+ node->disconnect(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready));
+ }
+ }
+ spawn_queue.clear();
+ }
+
+ // Process timed syncs.
uint64_t msec = OS::get_singleton()->get_ticks_msec();
for (KeyValue<int, PeerInfo> &E : peers_info) {
const HashSet<ObjectID> to_sync = E.value.sync_nodes;
@@ -144,17 +158,39 @@ Error SceneReplicationInterface::on_spawn(Object *p_obj, Variant p_config) {
// Track node.
const ObjectID oid = node->get_instance_id();
TrackedNode &tobj = _track(oid);
+
+ // Spawn state needs to be callected after "ready", but the spawn order follows "enter_tree".
ERR_FAIL_COND_V(tobj.spawner != ObjectID(), ERR_ALREADY_IN_USE);
tobj.spawner = spawner->get_instance_id();
- spawned_nodes.insert(oid);
+ spawn_queue.insert(oid);
+ node->connect(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready).bind(oid), Node::CONNECT_ONE_SHOT);
+ return OK;
+}
+
+void SceneReplicationInterface::_node_ready(const ObjectID &p_oid) {
+ ERR_FAIL_COND(!spawn_queue.has(p_oid)); // Bug.
- if (multiplayer->has_multiplayer_peer() && spawner->is_multiplayer_authority()) {
- if (tobj.net_id == 0) {
- tobj.net_id = ++last_net_id;
+ // If we are a nested spawn, we need to wait until the parent is ready.
+ if (p_oid != *(spawn_queue.begin())) {
+ return;
+ }
+
+ for (const ObjectID &oid : spawn_queue) {
+ ERR_CONTINUE(!tracked_nodes.has(oid));
+
+ TrackedNode &tobj = tracked_nodes[oid];
+ MultiplayerSpawner *spawner = get_id_as<MultiplayerSpawner>(tobj.spawner);
+ ERR_CONTINUE(!spawner);
+
+ spawned_nodes.insert(oid);
+ if (multiplayer->has_multiplayer_peer() && spawner->is_multiplayer_authority()) {
+ if (tobj.net_id == 0) {
+ tobj.net_id = ++last_net_id;
+ }
+ _update_spawn_visibility(0, oid);
}
- _update_spawn_visibility(0, oid);
}
- return OK;
+ spawn_queue.clear();
}
Error SceneReplicationInterface::on_despawn(Object *p_obj, Variant p_config) {
diff --git a/modules/multiplayer/scene_replication_interface.h b/modules/multiplayer/scene_replication_interface.h
index a5e610cff6..cf45db2138 100644
--- a/modules/multiplayer/scene_replication_interface.h
+++ b/modules/multiplayer/scene_replication_interface.h
@@ -51,7 +51,6 @@ private:
bool operator==(const ObjectID &p_other) { return id == p_other; }
- _FORCE_INLINE_ MultiplayerSpawner *get_spawner() const { return spawner.is_valid() ? Object::cast_to<MultiplayerSpawner>(ObjectDB::get_instance(spawner)) : nullptr; }
TrackedNode() {}
TrackedNode(const ObjectID &p_id) { id = p_id; }
TrackedNode(const ObjectID &p_id, uint32_t p_net_id) {
@@ -75,7 +74,10 @@ private:
HashSet<ObjectID> spawned_nodes;
HashSet<ObjectID> sync_nodes;
- // Pending spawn information.
+ // Pending local spawn information (handles spawning nested nodes during ready).
+ HashSet<ObjectID> spawn_queue;
+
+ // Pending remote spawn information.
ObjectID pending_spawn;
int pending_spawn_remote = 0;
const uint8_t *pending_buffer = nullptr;
@@ -89,6 +91,7 @@ private:
TrackedNode &_track(const ObjectID &p_id);
void _untrack(const ObjectID &p_id);
+ void _node_ready(const ObjectID &p_oid);
void _send_sync(int p_peer, const HashSet<ObjectID> p_synchronizers, uint16_t p_sync_net_time, uint64_t p_msec);
Error _make_spawn_packet(Node *p_node, MultiplayerSpawner *p_spawner, int &r_len);
diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp
index b9b92b77c9..9b5d78d465 100644
--- a/modules/navigation/godot_navigation_server.cpp
+++ b/modules/navigation/godot_navigation_server.cpp
@@ -829,6 +829,15 @@ void GodotNavigationServer::process(real_t p_delta_time) {
return;
}
+ int _new_pm_region_count = 0;
+ int _new_pm_agent_count = 0;
+ int _new_pm_link_count = 0;
+ int _new_pm_polygon_count = 0;
+ int _new_pm_edge_count = 0;
+ int _new_pm_edge_merge_count = 0;
+ int _new_pm_edge_connection_count = 0;
+ int _new_pm_edge_free_count = 0;
+
// In c++ we can't be sure that this is performed in the main thread
// even with mutable functions.
MutexLock lock(operations_mutex);
@@ -837,6 +846,15 @@ void GodotNavigationServer::process(real_t p_delta_time) {
active_maps[i]->step(p_delta_time);
active_maps[i]->dispatch_callbacks();
+ _new_pm_region_count += active_maps[i]->get_pm_region_count();
+ _new_pm_agent_count += active_maps[i]->get_pm_agent_count();
+ _new_pm_link_count += active_maps[i]->get_pm_link_count();
+ _new_pm_polygon_count += active_maps[i]->get_pm_polygon_count();
+ _new_pm_edge_count += active_maps[i]->get_pm_edge_count();
+ _new_pm_edge_merge_count += active_maps[i]->get_pm_edge_merge_count();
+ _new_pm_edge_connection_count += active_maps[i]->get_pm_edge_connection_count();
+ _new_pm_edge_free_count += active_maps[i]->get_pm_edge_free_count();
+
// Emit a signal if a map changed.
const uint32_t new_map_update_id = active_maps[i]->get_map_update_id();
if (new_map_update_id != active_maps_update_id[i]) {
@@ -844,6 +862,15 @@ void GodotNavigationServer::process(real_t p_delta_time) {
active_maps_update_id[i] = new_map_update_id;
}
}
+
+ pm_region_count = _new_pm_region_count;
+ pm_agent_count = _new_pm_agent_count;
+ pm_link_count = _new_pm_link_count;
+ pm_polygon_count = _new_pm_polygon_count;
+ pm_edge_count = _new_pm_edge_count;
+ pm_edge_merge_count = _new_pm_edge_merge_count;
+ pm_edge_connection_count = _new_pm_edge_connection_count;
+ pm_edge_free_count = _new_pm_edge_free_count;
}
PathQueryResult GodotNavigationServer::_query_path(const PathQueryParameters &p_parameters) const {
@@ -886,6 +913,40 @@ PathQueryResult GodotNavigationServer::_query_path(const PathQueryParameters &p_
return r_query_result;
}
+int GodotNavigationServer::get_process_info(ProcessInfo p_info) const {
+ switch (p_info) {
+ case INFO_ACTIVE_MAPS: {
+ return active_maps.size();
+ } break;
+ case INFO_REGION_COUNT: {
+ return pm_region_count;
+ } break;
+ case INFO_AGENT_COUNT: {
+ return pm_agent_count;
+ } break;
+ case INFO_LINK_COUNT: {
+ return pm_link_count;
+ } break;
+ case INFO_POLYGON_COUNT: {
+ return pm_polygon_count;
+ } break;
+ case INFO_EDGE_COUNT: {
+ return pm_edge_count;
+ } break;
+ case INFO_EDGE_MERGE_COUNT: {
+ return pm_edge_merge_count;
+ } break;
+ case INFO_EDGE_CONNECTION_COUNT: {
+ return pm_edge_connection_count;
+ } break;
+ case INFO_EDGE_FREE_COUNT: {
+ return pm_edge_free_count;
+ } break;
+ }
+
+ return 0;
+}
+
#undef COMMAND_1
#undef COMMAND_2
#undef COMMAND_4
diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h
index 7a6e5bb208..a87a88d3bc 100644
--- a/modules/navigation/godot_navigation_server.h
+++ b/modules/navigation/godot_navigation_server.h
@@ -81,6 +81,16 @@ class GodotNavigationServer : public NavigationServer3D {
LocalVector<NavMap *> active_maps;
LocalVector<uint32_t> active_maps_update_id;
+ // Performance Monitor
+ int pm_region_count = 0;
+ int pm_agent_count = 0;
+ int pm_link_count = 0;
+ int pm_polygon_count = 0;
+ int pm_edge_count = 0;
+ int pm_edge_merge_count = 0;
+ int pm_edge_connection_count = 0;
+ int pm_edge_free_count = 0;
+
public:
GodotNavigationServer();
virtual ~GodotNavigationServer();
@@ -182,6 +192,8 @@ public:
virtual void process(real_t p_delta_time) override;
virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const override;
+
+ int get_process_info(ProcessInfo p_info) const override;
};
#undef COMMAND_1
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index 2a2f8aa1b7..fd735f8793 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -611,6 +611,16 @@ void NavMap::remove_agent_as_controlled(RvoAgent *agent) {
}
void NavMap::sync() {
+ // Performance Monitor
+ int _new_pm_region_count = regions.size();
+ int _new_pm_agent_count = agents.size();
+ int _new_pm_link_count = links.size();
+ int _new_pm_polygon_count = pm_polygon_count;
+ int _new_pm_edge_count = pm_edge_count;
+ int _new_pm_edge_merge_count = pm_edge_merge_count;
+ int _new_pm_edge_connection_count = pm_edge_connection_count;
+ int _new_pm_edge_free_count = pm_edge_free_count;
+
// Check if we need to update the links.
if (regenerate_polygons) {
for (uint32_t r = 0; r < regions.size(); r++) {
@@ -632,6 +642,12 @@ void NavMap::sync() {
}
if (regenerate_links) {
+ _new_pm_polygon_count = 0;
+ _new_pm_edge_count = 0;
+ _new_pm_edge_merge_count = 0;
+ _new_pm_edge_connection_count = 0;
+ _new_pm_edge_free_count = 0;
+
// Remove regions connections.
for (uint32_t r = 0; r < regions.size(); r++) {
regions[r]->get_connections().clear();
@@ -654,6 +670,8 @@ void NavMap::sync() {
count += regions[r]->get_polygons().size();
}
+ _new_pm_polygon_count = polygons.size();
+
// Group all edges per key.
HashMap<gd::EdgeKey, Vector<gd::Edge::Connection>, gd::EdgeKey> connections;
for (uint32_t poly_id = 0; poly_id < polygons.size(); poly_id++) {
@@ -666,6 +684,7 @@ void NavMap::sync() {
HashMap<gd::EdgeKey, Vector<gd::Edge::Connection>, gd::EdgeKey>::Iterator connection = connections.find(ek);
if (!connection) {
connections[ek] = Vector<gd::Edge::Connection>();
+ _new_pm_edge_count += 1;
}
if (connections[ek].size() <= 1) {
// Add the polygon/edge tuple to this key.
@@ -691,6 +710,7 @@ void NavMap::sync() {
c1.polygon->edges[c1.edge].connections.push_back(c2);
c2.polygon->edges[c2.edge].connections.push_back(c1);
// Note: The pathway_start/end are full for those connection and do not need to be modified.
+ _new_pm_edge_merge_count += 1;
} else {
CRASH_COND_MSG(E.value.size() != 1, vformat("Number of connection != 1. Found: %d", E.value.size()));
free_edges.push_back(E.value[0]);
@@ -704,6 +724,8 @@ void NavMap::sync() {
// to be connected, create new polygons to remove that small gap is
// not really useful and would result in wasteful computation during
// connection, integration and path finding.
+ _new_pm_edge_free_count = free_edges.size();
+
for (int i = 0; i < free_edges.size(); i++) {
const gd::Edge::Connection &free_edge = free_edges[i];
Vector3 edge_p1 = free_edge.polygon->points[free_edge.edge].pos;
@@ -757,6 +779,7 @@ void NavMap::sync() {
// Add the connection to the region_connection map.
((NavRegion *)free_edge.polygon->owner)->get_connections().push_back(new_connection);
+ _new_pm_edge_connection_count += 1;
}
}
@@ -892,6 +915,16 @@ void NavMap::sync() {
regenerate_polygons = false;
regenerate_links = false;
agents_dirty = false;
+
+ // Performance Monitor
+ pm_region_count = _new_pm_region_count;
+ pm_agent_count = _new_pm_agent_count;
+ pm_link_count = _new_pm_link_count;
+ pm_polygon_count = _new_pm_polygon_count;
+ pm_edge_count = _new_pm_edge_count;
+ pm_edge_merge_count = _new_pm_edge_merge_count;
+ pm_edge_connection_count = _new_pm_edge_connection_count;
+ pm_edge_free_count = _new_pm_edge_free_count;
}
void NavMap::compute_single_step(uint32_t index, RvoAgent **agent) {
diff --git a/modules/navigation/nav_map.h b/modules/navigation/nav_map.h
index 321d5560f0..fce7aff3ba 100644
--- a/modules/navigation/nav_map.h
+++ b/modules/navigation/nav_map.h
@@ -89,6 +89,16 @@ class NavMap : public NavRid {
/// Change the id each time the map is updated.
uint32_t map_update_id = 0;
+ // Performance Monitor
+ int pm_region_count = 0;
+ int pm_agent_count = 0;
+ int pm_link_count = 0;
+ int pm_polygon_count = 0;
+ int pm_edge_count = 0;
+ int pm_edge_merge_count = 0;
+ int pm_edge_connection_count = 0;
+ int pm_edge_free_count = 0;
+
public:
NavMap();
~NavMap();
@@ -152,6 +162,16 @@ public:
void step(real_t p_deltatime);
void dispatch_callbacks();
+ // Performance Monitor
+ int get_pm_region_count() const { return pm_region_count; }
+ int get_pm_agent_count() const { return pm_agent_count; }
+ int get_pm_link_count() const { return pm_link_count; }
+ int get_pm_polygon_count() const { return pm_polygon_count; }
+ int get_pm_edge_count() const { return pm_edge_count; }
+ int get_pm_edge_merge_count() const { return pm_edge_merge_count; }
+ int get_pm_edge_connection_count() const { return pm_edge_connection_count; }
+ int get_pm_edge_free_count() const { return pm_edge_free_count; }
+
private:
void compute_single_step(uint32_t index, RvoAgent **agent);
void clip_path(const LocalVector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly, Vector<int32_t> *r_path_types, TypedArray<RID> *r_path_rids, Vector<int64_t> *r_path_owners) const;
diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp
index 568e8b9b26..fff7a02fc4 100644
--- a/modules/navigation/navigation_mesh_generator.cpp
+++ b/modules/navigation/navigation_mesh_generator.cpp
@@ -207,11 +207,11 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans
List<uint32_t> shape_owners;
static_body->get_shape_owners(&shape_owners);
for (uint32_t shape_owner : shape_owners) {
+ if (static_body->is_shape_owner_disabled(shape_owner)) {
+ continue;
+ }
const int shape_count = static_body->shape_owner_get_shape_count(shape_owner);
for (int i = 0; i < shape_count; i++) {
- if (static_body->is_shape_owner_disabled(i)) {
- continue;
- }
Ref<Shape3D> s = static_body->shape_owner_get_shape(shape_owner, i);
if (s.is_null()) {
continue;
diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub
index fefee9bb24..3b39967ba4 100644
--- a/modules/openxr/SCsub
+++ b/modules/openxr/SCsub
@@ -101,6 +101,7 @@ env_openxr.add_source_files(module_obj, "extensions/openxr_huawei_controller_ext
env_openxr.add_source_files(module_obj, "extensions/openxr_hand_tracking_extension.cpp")
env_openxr.add_source_files(module_obj, "extensions/openxr_fb_passthrough_extension_wrapper.cpp")
env_openxr.add_source_files(module_obj, "extensions/openxr_fb_display_refresh_rate_extension.cpp")
+env_openxr.add_source_files(module_obj, "extensions/openxr_pico_controller_extension.cpp")
env_openxr.add_source_files(module_obj, "extensions/openxr_wmr_controller_extension.cpp")
env.modules_sources += module_obj
diff --git a/modules/openxr/action_map/openxr_action_map.cpp b/modules/openxr/action_map/openxr_action_map.cpp
index e3ff1b4382..669c395b3e 100644
--- a/modules/openxr/action_map/openxr_action_map.cpp
+++ b/modules/openxr/action_map/openxr_action_map.cpp
@@ -307,6 +307,31 @@ void OpenXRActionMap::create_default_action_sets() {
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
+ // Create our Pico 4 / Neo 3 controller profile
+ profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/pico/neo3_controller");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
+ profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
+ profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
+ profile->add_new_binding(select_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click"); // system click may not be available
+ profile->add_new_binding(menu_button, "/user/hand/left/input/back/click,/user/hand/right/input/back/click"); // right hand back click may not be available
+ profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand
+ profile->add_new_binding(ax_touch, "/user/hand/left/input/x/touch,/user/hand/right/input/a/touch");
+ profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand
+ profile->add_new_binding(by_touch, "/user/hand/left/input/y/touch,/user/hand/right/input/b/touch");
+ profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
+ profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); // should be converted to boolean
+ profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch");
+ profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // should be converted to boolean
+ profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
+ // primary on our pico controller is our thumbstick
+ profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
+ profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
+ profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
+ // pico controller has no secondary input
+ profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
+ add_interaction_profile(profile);
+
// Create our Valve index controller profile
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/valve/index_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
diff --git a/modules/openxr/editor/openxr_action_editor.cpp b/modules/openxr/editor/openxr_action_editor.cpp
index e3fe06c6f7..586b0b0697 100644
--- a/modules/openxr/editor/openxr_action_editor.cpp
+++ b/modules/openxr/editor/openxr_action_editor.cpp
@@ -29,7 +29,6 @@
/**************************************************************************/
#include "openxr_action_editor.h"
-#include "editor/editor_node.h"
void OpenXRActionEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_do_set_name", "name"), &OpenXRActionEditor::_do_set_name);
@@ -125,7 +124,7 @@ void OpenXRActionEditor::_on_remove_action() {
}
OpenXRActionEditor::OpenXRActionEditor(Ref<OpenXRAction> p_action) {
- undo_redo = EditorNode::get_undo_redo();
+ undo_redo = EditorUndoRedoManager::get_singleton();
action = p_action;
set_h_size_flags(Control::SIZE_EXPAND_FILL);
diff --git a/modules/openxr/editor/openxr_action_editor.h b/modules/openxr/editor/openxr_action_editor.h
index 8af5448aed..765b3ef378 100644
--- a/modules/openxr/editor/openxr_action_editor.h
+++ b/modules/openxr/editor/openxr_action_editor.h
@@ -43,7 +43,7 @@ class OpenXRActionEditor : public HBoxContainer {
GDCLASS(OpenXRActionEditor, HBoxContainer);
private:
- Ref<EditorUndoRedoManager> undo_redo;
+ EditorUndoRedoManager *undo_redo;
Ref<OpenXRAction> action;
LineEdit *action_name = nullptr;
diff --git a/modules/openxr/editor/openxr_action_map_editor.cpp b/modules/openxr/editor/openxr_action_map_editor.cpp
index 12c95f8978..ad5a515a01 100644
--- a/modules/openxr/editor/openxr_action_map_editor.cpp
+++ b/modules/openxr/editor/openxr_action_map_editor.cpp
@@ -387,7 +387,7 @@ void OpenXRActionMapEditor::_clear_action_map() {
}
OpenXRActionMapEditor::OpenXRActionMapEditor() {
- undo_redo = EditorNode::get_undo_redo();
+ undo_redo = EditorUndoRedoManager::get_singleton();
set_custom_minimum_size(Size2(0.0, 300.0));
top_hb = memnew(HBoxContainer);
diff --git a/modules/openxr/editor/openxr_action_map_editor.h b/modules/openxr/editor/openxr_action_map_editor.h
index 47bfda3425..a04bae4a6e 100644
--- a/modules/openxr/editor/openxr_action_map_editor.h
+++ b/modules/openxr/editor/openxr_action_map_editor.h
@@ -48,7 +48,7 @@ class OpenXRActionMapEditor : public VBoxContainer {
GDCLASS(OpenXRActionMapEditor, VBoxContainer);
private:
- Ref<EditorUndoRedoManager> undo_redo;
+ EditorUndoRedoManager *undo_redo;
String edited_path;
Ref<OpenXRActionMap> action_map;
diff --git a/modules/openxr/editor/openxr_action_set_editor.cpp b/modules/openxr/editor/openxr_action_set_editor.cpp
index 078d83f5f2..bcb0f5f8b1 100644
--- a/modules/openxr/editor/openxr_action_set_editor.cpp
+++ b/modules/openxr/editor/openxr_action_set_editor.cpp
@@ -29,7 +29,6 @@
/**************************************************************************/
#include "openxr_action_set_editor.h"
-#include "editor/editor_node.h"
#include "openxr_action_editor.h"
void OpenXRActionSetEditor::_bind_methods() {
@@ -212,7 +211,7 @@ void OpenXRActionSetEditor::set_focus_on_entry() {
}
OpenXRActionSetEditor::OpenXRActionSetEditor(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRActionSet> p_action_set) {
- undo_redo = EditorNode::get_undo_redo();
+ undo_redo = EditorUndoRedoManager::get_singleton();
action_map = p_action_map;
action_set = p_action_set;
diff --git a/modules/openxr/editor/openxr_action_set_editor.h b/modules/openxr/editor/openxr_action_set_editor.h
index 6040f7fb4e..129f800abe 100644
--- a/modules/openxr/editor/openxr_action_set_editor.h
+++ b/modules/openxr/editor/openxr_action_set_editor.h
@@ -44,7 +44,7 @@ class OpenXRActionSetEditor : public HBoxContainer {
GDCLASS(OpenXRActionSetEditor, HBoxContainer);
private:
- Ref<EditorUndoRedoManager> undo_redo;
+ EditorUndoRedoManager *undo_redo;
Ref<OpenXRActionMap> action_map;
Ref<OpenXRActionSet> action_set;
diff --git a/modules/openxr/editor/openxr_interaction_profile_editor.cpp b/modules/openxr/editor/openxr_interaction_profile_editor.cpp
index 76b0ae5a3c..6a848dd430 100644
--- a/modules/openxr/editor/openxr_interaction_profile_editor.cpp
+++ b/modules/openxr/editor/openxr_interaction_profile_editor.cpp
@@ -29,7 +29,6 @@
/**************************************************************************/
#include "openxr_interaction_profile_editor.h"
-#include "editor/editor_node.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/label.h"
@@ -141,7 +140,7 @@ void OpenXRInteractionProfileEditorBase::remove_all_bindings_for_action(Ref<Open
}
OpenXRInteractionProfileEditorBase::OpenXRInteractionProfileEditorBase(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile) {
- undo_redo = EditorNode::get_undo_redo();
+ undo_redo = EditorUndoRedoManager::get_singleton();
action_map = p_action_map;
interaction_profile = p_interaction_profile;
diff --git a/modules/openxr/editor/openxr_interaction_profile_editor.h b/modules/openxr/editor/openxr_interaction_profile_editor.h
index 73dba71cbd..fa25a000a9 100644
--- a/modules/openxr/editor/openxr_interaction_profile_editor.h
+++ b/modules/openxr/editor/openxr_interaction_profile_editor.h
@@ -43,7 +43,7 @@ class OpenXRInteractionProfileEditorBase : public ScrollContainer {
GDCLASS(OpenXRInteractionProfileEditorBase, ScrollContainer);
protected:
- Ref<EditorUndoRedoManager> undo_redo;
+ EditorUndoRedoManager *undo_redo;
Ref<OpenXRInteractionProfile> interaction_profile;
Ref<OpenXRActionMap> action_map;
diff --git a/modules/openxr/extensions/openxr_android_extension.cpp b/modules/openxr/extensions/openxr_android_extension.cpp
index aae284f6bd..4465daf22a 100644
--- a/modules/openxr/extensions/openxr_android_extension.cpp
+++ b/modules/openxr/extensions/openxr_android_extension.cpp
@@ -51,18 +51,17 @@ OpenXRAndroidExtension::OpenXRAndroidExtension() {
HashMap<String, bool *> OpenXRAndroidExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
- request_extensions[XR_KHR_LOADER_INIT_ANDROID_EXTENSION_NAME] = &loader_init_extension_available;
request_extensions[XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME] = &create_instance_extension_available;
return request_extensions;
}
void OpenXRAndroidExtension::on_before_instance_created() {
- if (!loader_init_extension_available) {
- print_line("OpenXR: XR_KHR_loader_init_android is not reported as available - trying to initialize anyway...");
+ if (XR_FAILED(EXT_TRY_INIT_XR_FUNC(xrInitializeLoaderKHR))) {
+ // XR_KHR_loader_init not supported on this platform
+ return;
}
-
- EXT_INIT_XR_FUNC(xrInitializeLoaderKHR);
+ loader_init_extension_available = true;
JNIEnv *env = get_jni_env();
JavaVM *vm;
@@ -85,6 +84,9 @@ static XrInstanceCreateInfoAndroidKHR instance_create_info;
void *OpenXRAndroidExtension::set_instance_create_info_and_get_next_pointer(void *p_next_pointer) {
if (!create_instance_extension_available) {
+ if (!loader_init_extension_available) {
+ WARN_PRINT("No Android extensions available, couldn't pass JVM and Activity to OpenXR");
+ }
return nullptr;
}
diff --git a/modules/openxr/extensions/openxr_opengl_extension.cpp b/modules/openxr/extensions/openxr_opengl_extension.cpp
index cd371b9ed9..6ce8f0805f 100644
--- a/modules/openxr/extensions/openxr_opengl_extension.cpp
+++ b/modules/openxr/extensions/openxr_opengl_extension.cpp
@@ -157,7 +157,6 @@ void *OpenXROpenGLExtension::set_session_create_and_get_next_pointer(void *p_nex
}
void OpenXROpenGLExtension::get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) {
- p_usable_swap_chains.push_back(GL_SRGB8_ALPHA8);
p_usable_swap_chains.push_back(GL_RGBA8);
}
diff --git a/modules/openxr/extensions/openxr_pico_controller_extension.cpp b/modules/openxr/extensions/openxr_pico_controller_extension.cpp
new file mode 100644
index 0000000000..f2fcf22ce2
--- /dev/null
+++ b/modules/openxr/extensions/openxr_pico_controller_extension.cpp
@@ -0,0 +1,98 @@
+/**************************************************************************/
+/* openxr_pico_controller_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "openxr_pico_controller_extension.h"
+#include "../action_map/openxr_interaction_profile_meta_data.h"
+
+// Pico controllers are not part of the OpenXR spec at the time of writing this
+// code. We'll hardcode the extension name that is used internally, verified by
+// tests on the Pico 4. Note that later versions of the Pico 4 and OpenXR
+// runtime on Pico might use a different, standardized extension name.
+#ifndef XR_PICO_CONTROLLER_INTERACTION_EXTENSION_NAME
+#define XR_PICO_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_PICO_controller_interaction"
+#endif
+
+HashMap<String, bool *> OpenXRPicoControllerExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
+
+ request_extensions[XR_PICO_CONTROLLER_INTERACTION_EXTENSION_NAME] = &available;
+
+ return request_extensions;
+}
+
+bool OpenXRPicoControllerExtension::is_available() {
+ return available;
+}
+
+void OpenXRPicoControllerExtension::on_register_metadata() {
+ OpenXRInteractionProfileMetaData *metadata = OpenXRInteractionProfileMetaData::get_singleton();
+ ERR_FAIL_NULL(metadata);
+
+ // Pico controller (Pico 4 and Pico Neo 3 controllers)
+ metadata->register_interaction_profile("Pico controller", "/interaction_profiles/pico/neo3_controller", XR_PICO_CONTROLLER_INTERACTION_EXTENSION_NAME);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Palm pose", "/user/hand/left", "/user/hand/left/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Palm pose", "/user/hand/right", "/user/hand/right/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Menu click", "/user/hand/left", "/user/hand/left/input/back/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Screenshot click", "/user/hand/right", "/user/hand/right/input/back/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "System click", "/user/hand/left", "/user/hand/left/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "System click", "/user/hand/right", "/user/hand/right/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "X click", "/user/hand/left", "/user/hand/left/input/x/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "X touch", "/user/hand/left", "/user/hand/left/input/x/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Y click", "/user/hand/left", "/user/hand/left/input/y/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Y touch", "/user/hand/left", "/user/hand/left/input/y/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "A touch", "/user/hand/right", "/user/hand/right/input/a/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "B touch", "/user/hand/right", "/user/hand/right/input/b/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Trigger", "/user/hand/left", "/user/hand/left/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Trigger touch", "/user/hand/left", "/user/hand/left/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Trigger", "/user/hand/right", "/user/hand/right/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Trigger touch", "/user/hand/right", "/user/hand/right/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Squeeze", "/user/hand/left", "/user/hand/left/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Squeeze", "/user/hand/right", "/user/hand/right/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick", "/user/hand/left", "/user/hand/left/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick click", "/user/hand/left", "/user/hand/left/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick touch", "/user/hand/left", "/user/hand/left/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick", "/user/hand/right", "/user/hand/right/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick click", "/user/hand/right", "/user/hand/right/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick touch", "/user/hand/right", "/user/hand/right/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Haptic output", "/user/hand/right", "/user/hand/right/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+}
diff --git a/modules/openxr/extensions/openxr_pico_controller_extension.h b/modules/openxr/extensions/openxr_pico_controller_extension.h
new file mode 100644
index 0000000000..a2a1e2f3d3
--- /dev/null
+++ b/modules/openxr/extensions/openxr_pico_controller_extension.h
@@ -0,0 +1,48 @@
+/**************************************************************************/
+/* openxr_pico_controller_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef OPENXR_PICO_CONTROLLER_EXTENSION_H
+#define OPENXR_PICO_CONTROLLER_EXTENSION_H
+
+#include "openxr_extension_wrapper.h"
+
+class OpenXRPicoControllerExtension : public OpenXRExtensionWrapper {
+public:
+ virtual HashMap<String, bool *> get_requested_extensions() override;
+
+ bool is_available();
+
+ virtual void on_register_metadata() override;
+
+private:
+ bool available = false;
+};
+
+#endif // OPENXR_PICO_CONTROLLER_EXTENSION_H
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index 550ed8052e..d556f475d2 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -217,7 +217,7 @@ bool OpenXRAPI::is_top_level_path_supported(const String &p_toplevel_path) {
String required_extension = OpenXRInteractionProfileMetaData::get_singleton()->get_top_level_extension(p_toplevel_path);
// If unsupported is returned we likely have a misspelled interaction profile path in our action map. Always output that as an error.
- ERR_FAIL_COND_V_MSG(required_extension == XR_PATH_UNSUPPORTED_NAME, false, "OpenXR: Unsupported interaction profile " + p_toplevel_path);
+ ERR_FAIL_COND_V_MSG(required_extension == XR_PATH_UNSUPPORTED_NAME, false, "OpenXR: Unsupported toplevel path " + p_toplevel_path);
if (required_extension == "") {
// no extension needed, core top level are always "supported", they just won't be used if not really supported
@@ -1224,8 +1224,12 @@ bool OpenXRAPI::resolve_instance_openxr_symbols() {
return true;
}
+XrResult OpenXRAPI::try_get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr) {
+ return xrGetInstanceProcAddr(instance, p_name, p_addr);
+}
+
XrResult OpenXRAPI::get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr) {
- XrResult result = xrGetInstanceProcAddr(instance, p_name, p_addr);
+ XrResult result = try_get_instance_proc_addr(p_name, p_addr);
if (result != XR_SUCCESS) {
String error_message = String("Symbol ") + p_name + " not found in OpenXR instance.";
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index a697a5f90a..5fb8de660e 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -305,6 +305,7 @@ public:
static bool openxr_is_enabled(bool p_check_run_in_editor = true);
_FORCE_INLINE_ static OpenXRAPI *get_singleton() { return singleton; }
+ XrResult try_get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr);
XrResult get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr);
String get_error_string(XrResult result);
String get_swapchain_format_name(int64_t p_swapchain_format) const;
diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp
index 56c31883e6..4e2fe3dab5 100644
--- a/modules/openxr/register_types.cpp
+++ b/modules/openxr/register_types.cpp
@@ -53,6 +53,7 @@
#include "extensions/openxr_htc_vive_tracker_extension.h"
#include "extensions/openxr_huawei_controller_extension.h"
#include "extensions/openxr_palm_pose_extension.h"
+#include "extensions/openxr_pico_controller_extension.h"
#include "extensions/openxr_wmr_controller_extension.h"
static OpenXRAPI *openxr_api = nullptr;
@@ -92,6 +93,7 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) {
// register our other extensions
OpenXRAPI::register_extension_wrapper(memnew(OpenXRPalmPoseExtension));
+ OpenXRAPI::register_extension_wrapper(memnew(OpenXRPicoControllerExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRCompositionLayerDepthExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRHTCControllerExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRHTCViveTrackerExtension));
diff --git a/modules/openxr/util.h b/modules/openxr/util.h
index f3fa187faa..6665d45007 100644
--- a/modules/openxr/util.h
+++ b/modules/openxr/util.h
@@ -53,6 +53,12 @@
#define EXT_INIT_XR_FUNC(name) INIT_XR_FUNC(OpenXRAPI::get_singleton(), name)
#define OPENXR_API_INIT_XR_FUNC(name) INIT_XR_FUNC(this, name)
+#define TRY_INIT_XR_FUNC(openxr_api, name) \
+ openxr_api->try_get_instance_proc_addr(#name, (PFN_xrVoidFunction *)&name##_ptr)
+
+#define EXT_TRY_INIT_XR_FUNC(name) TRY_INIT_XR_FUNC(OpenXRAPI::get_singleton(), name)
+#define OPENXR_TRY_API_INIT_XR_FUNC(name) TRY_INIT_XR_FUNC(this, name)
+
#define EXT_PROTO_XRRESULT_FUNC1(func_name, arg1_type, arg1) \
PFN_##func_name func_name##_ptr = nullptr; \
XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type p_##arg1) const { \
diff --git a/modules/raycast/SCsub b/modules/raycast/SCsub
index 37c8a95905..209ebab388 100644
--- a/modules/raycast/SCsub
+++ b/modules/raycast/SCsub
@@ -96,7 +96,7 @@ if env["builtin_embree"]:
if not env.msvc:
# Flags synced with upstream gnu.cmake.
- if env["arch"] == "arm64" and env["platform"] == "linuxbsd":
+ if env["arch"] == "arm64" and env["platform"] == "linuxbsd" and not env["use_llvm"]:
env_thirdparty.Append(CXXFLAGS=["-flax-vector-conversions"])
env_thirdparty.Append(
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 44a2c76727..e52b87741e 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -42,7 +42,7 @@
using namespace godot;
-#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var)
+#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get_setting_with_override(m_var)
#else
// Headers for building as built-in module.
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 809bfbe41a..034f88e387 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -42,7 +42,7 @@
using namespace godot;
-#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var)
+#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get_setting_with_override(m_var)
#else
// Headers for building as built-in module.
diff --git a/modules/webxr/doc_classes/WebXRInterface.xml b/modules/webxr/doc_classes/WebXRInterface.xml
index f5964eb4d1..ba1750386f 100644
--- a/modules/webxr/doc_classes/WebXRInterface.xml
+++ b/modules/webxr/doc_classes/WebXRInterface.xml
@@ -18,7 +18,7 @@
func _ready():
# We assume this node has a button as a child.
# This button is for the user to consent to entering immersive VR mode.
- $Button.pressed.connect(self._on_Button_pressed)
+ $Button.pressed.connect(self._on_button_pressed)
webxr_interface = XRServer.find_interface("WebXR")
if webxr_interface:
@@ -38,7 +38,7 @@
if session_mode == 'immersive-vr':
vr_supported = supported
- func _on_Button_pressed():
+ func _on_button_pressed():
if not vr_supported:
OS.alert("Your browser doesn't support VR")
return