summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml2
-rw-r--r--modules/gdscript/gdscript.cpp100
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp79
-rw-r--r--modules/gdscript/gdscript_analyzer.h2
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp67
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h25
-rw-r--r--modules/gdscript/gdscript_codegen.h6
-rw-r--r--modules/gdscript/gdscript_compiler.cpp17
-rw-r--r--modules/gdscript/gdscript_disassembler.cpp21
-rw-r--r--modules/gdscript/gdscript_editor.cpp3
-rw-r--r--modules/gdscript/gdscript_function.h10
-rw-r--r--modules/gdscript/gdscript_parser.cpp121
-rw-r--r--modules/gdscript/gdscript_parser.h9
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp62
-rw-r--r--modules/gdscript/gdscript_tokenizer.h7
-rw-r--r--modules/gdscript/gdscript_warning.cpp5
-rw-r--r--modules/gdscript/gdscript_warning.h1
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.gd10
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.out1
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out1
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/identifier_similar_to_keyword.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/identifier_similar_to_keyword.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/export_variable.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match.out1
-rw-r--r--modules/gdscript/tests/scripts/parser/features/nested_match.gd7
-rw-r--r--modules/gdscript/tests/scripts/parser/features/nested_match.out1
-rw-r--r--modules/gdscript/tests/scripts/parser/features/unicode_identifiers.gd35
-rw-r--r--modules/gdscript/tests/scripts/parser/features/unicode_identifiers.out14
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/await_on_void.gd2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/gdscript.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd10
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd2
-rw-r--r--modules/gltf/gltf_document.cpp1
-rw-r--r--modules/minimp3/resource_importer_mp3.cpp2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs19
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs24
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs33
-rw-r--r--modules/mono/editor/bindings_generator.cpp153
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs33
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs38
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs115
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs42
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs450
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs11
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs1128
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs112
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs6
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs8
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs44
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs8
-rw-r--r--modules/mono/glue/runtime_interop.cpp26
-rw-r--r--modules/multiplayer/editor/replication_editor.cpp2
-rw-r--r--modules/multiplayer/scene_replication_interface.cpp2
-rw-r--r--modules/navigation/editor/navigation_mesh_editor_plugin.cpp30
-rw-r--r--modules/navigation/godot_navigation_server.cpp16
-rw-r--r--modules/navigation/godot_navigation_server.h8
-rw-r--r--modules/navigation/nav_link.cpp8
-rw-r--r--modules/navigation/nav_link.h16
-rw-r--r--modules/navigation/nav_map.cpp8
-rw-r--r--modules/navigation/nav_utils.h2
-rw-r--r--modules/openxr/doc_classes/OpenXRAction.xml4
-rw-r--r--modules/openxr/openxr_api.cpp2
-rw-r--r--modules/openxr/openxr_api.h2
-rw-r--r--modules/openxr/register_types.cpp4
-rw-r--r--modules/text_server_adv/text_server_adv.cpp61
-rw-r--r--modules/vorbis/resource_importer_ogg_vorbis.cpp2
-rw-r--r--modules/webxr/doc_classes/WebXRInterface.xml2
-rw-r--r--modules/webxr/webxr_interface_js.cpp12
98 files changed, 2307 insertions, 871 deletions
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 3fe741a582..5bed1b9da3 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -485,7 +485,7 @@
Export a [NodePath] property with a filter for allowed node types.
See also [constant PROPERTY_HINT_NODE_PATH_VALID_TYPES].
[codeblock]
- @export_node_path(Button, TouchScreenButton) var some_button
+ @export_node_path("Button", "TouchScreenButton") var some_button
[/codeblock]
</description>
</annotation>
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 6b325d6451..4fc3929bbd 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -2448,7 +2448,6 @@ bool GDScriptLanguage::handles_global_class_type(const String &p_type) const {
}
String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const {
- Vector<uint8_t> sourcef;
Error err;
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
if (err) {
@@ -2459,88 +2458,31 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
GDScriptParser parser;
err = parser.parse(source, p_path, false);
+ if (err) {
+ return String();
+ }
- // TODO: Simplify this code by using the analyzer to get full inheritance.
- if (err == OK) {
- const GDScriptParser::ClassNode *c = parser.get_tree();
- if (r_icon_path) {
- if (c->icon_path.is_empty() || c->icon_path.is_absolute_path()) {
- *r_icon_path = c->icon_path;
- } else if (c->icon_path.is_relative_path()) {
- *r_icon_path = p_path.get_base_dir().path_join(c->icon_path).simplify_path();
- }
- }
- if (r_base_type) {
- const GDScriptParser::ClassNode *subclass = c;
- String path = p_path;
- GDScriptParser subparser;
- while (subclass) {
- if (subclass->extends_used) {
- if (!subclass->extends_path.is_empty()) {
- if (subclass->extends.size() == 0) {
- get_global_class_name(subclass->extends_path, r_base_type);
- subclass = nullptr;
- break;
- } else {
- Vector<StringName> extend_classes = subclass->extends;
-
- Ref<FileAccess> subfile = FileAccess::open(subclass->extends_path, FileAccess::READ);
- if (subfile.is_null()) {
- break;
- }
- String subsource = subfile->get_as_utf8_string();
-
- if (subsource.is_empty()) {
- break;
- }
- String subpath = subclass->extends_path;
- if (subpath.is_relative_path()) {
- subpath = path.get_base_dir().path_join(subpath).simplify_path();
- }
-
- if (OK != subparser.parse(subsource, subpath, false)) {
- break;
- }
- path = subpath;
- subclass = subparser.get_tree();
-
- while (extend_classes.size() > 0) {
- bool found = false;
- for (int i = 0; i < subclass->members.size(); i++) {
- if (subclass->members[i].type != GDScriptParser::ClassNode::Member::CLASS) {
- continue;
- }
-
- const GDScriptParser::ClassNode *inner_class = subclass->members[i].m_class;
- if (inner_class->identifier->name == extend_classes[0]) {
- extend_classes.remove_at(0);
- found = true;
- subclass = inner_class;
- break;
- }
- }
- if (!found) {
- subclass = nullptr;
- break;
- }
- }
- }
- } else if (subclass->extends.size() == 1) {
- *r_base_type = subclass->extends[0];
- subclass = nullptr;
- } else {
- break;
- }
- } else {
- *r_base_type = "RefCounted";
- subclass = nullptr;
- }
- }
+ GDScriptAnalyzer analyzer(&parser);
+ err = analyzer.resolve_inheritance();
+ if (err) {
+ return String();
+ }
+
+ const GDScriptParser::ClassNode *c = parser.get_tree();
+
+ if (r_base_type) {
+ *r_base_type = c->get_datatype().native_type;
+ }
+
+ if (r_icon_path) {
+ if (c->icon_path.is_empty() || c->icon_path.is_absolute_path()) {
+ *r_icon_path = c->icon_path.simplify_path();
+ } else if (c->icon_path.is_relative_path()) {
+ *r_icon_path = p_path.get_base_dir().path_join(c->icon_path).simplify_path();
}
- return c->identifier != nullptr ? String(c->identifier->name) : String();
}
- return String();
+ return c->identifier != nullptr ? String(c->identifier->name) : String();
}
GDScriptLanguage::GDScriptLanguage() {
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index e04a962dcb..d7f6126207 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -802,6 +802,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : member.variable->annotations) {
+ resolve_annotation(E);
E->apply(parser, member.variable);
}
} break;
@@ -812,6 +813,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : member.constant->annotations) {
+ resolve_annotation(E);
E->apply(parser, member.constant);
}
} break;
@@ -835,6 +837,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : member.signal->annotations) {
+ resolve_annotation(E);
E->apply(parser, member.signal);
}
} break;
@@ -882,6 +885,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : member.m_enum->annotations) {
+ resolve_annotation(E);
E->apply(parser, member.m_enum);
}
} break;
@@ -1064,6 +1068,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co
if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) {
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : member.function->annotations) {
+ resolve_annotation(E);
E->apply(parser, member.function);
}
@@ -1290,7 +1295,55 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root
}
void GDScriptAnalyzer::resolve_annotation(GDScriptParser::AnnotationNode *p_annotation) {
- // TODO: Add second validation function for annotations, so they can use checked types.
+ ERR_FAIL_COND_MSG(!parser->valid_annotations.has(p_annotation->name), vformat(R"(Annotation "%s" not found to validate.)", p_annotation->name));
+
+ const MethodInfo &annotation_info = parser->valid_annotations[p_annotation->name].info;
+
+ const List<PropertyInfo>::Element *E = annotation_info.arguments.front();
+ for (int i = 0; i < p_annotation->arguments.size(); i++) {
+ GDScriptParser::ExpressionNode *argument = p_annotation->arguments[i];
+ const PropertyInfo &argument_info = E->get();
+
+ if (E->next() != nullptr) {
+ E = E->next();
+ }
+
+ reduce_expression(argument);
+
+ if (!argument->is_constant) {
+ push_error(vformat(R"(Argument %d of annotation "%s" isn't a constant expression.)", i + 1, p_annotation->name), argument);
+ return;
+ }
+
+ Variant value = argument->reduced_value;
+
+ if (value.get_type() != argument_info.type) {
+#ifdef DEBUG_ENABLED
+ if (argument_info.type == Variant::INT && value.get_type() == Variant::FLOAT) {
+ parser->push_warning(argument, GDScriptWarning::NARROWING_CONVERSION);
+ }
+#endif
+
+ if (!Variant::can_convert_strict(value.get_type(), argument_info.type)) {
+ push_error(vformat(R"(Invalid argument for annotation "%s": argument %d should be "%s" but is "%s".)", p_annotation->name, i + 1, Variant::get_type_name(argument_info.type), argument->get_datatype().to_string()), argument);
+ return;
+ }
+
+ Variant converted_to;
+ const Variant *converted_from = &value;
+ Callable::CallError call_error;
+ Variant::construct(argument_info.type, converted_to, &converted_from, 1, call_error);
+
+ if (call_error.error != Callable::CallError::CALL_OK) {
+ push_error(vformat(R"(Cannot convert argument %d of annotation "%s" from "%s" to "%s".)", i + 1, p_annotation->name, Variant::get_type_name(value.get_type()), Variant::get_type_name(argument_info.type)), argument);
+ return;
+ }
+
+ value = converted_to;
+ }
+
+ p_annotation->resolved_arguments.push_back(value);
+ }
}
void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *p_function, const GDScriptParser::Node *p_source, bool p_is_lambda) {
@@ -1486,8 +1539,10 @@ void GDScriptAnalyzer::decide_suite_type(GDScriptParser::Node *p_suite, GDScript
void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) {
for (int i = 0; i < p_suite->statements.size(); i++) {
GDScriptParser::Node *stmt = p_suite->statements[i];
- for (GDScriptParser::AnnotationNode *&annotation : stmt->annotations) {
- annotation->apply(parser, stmt);
+ // Apply annotations.
+ for (GDScriptParser::AnnotationNode *&E : stmt->annotations) {
+ resolve_annotation(E);
+ E->apply(parser, stmt);
}
#ifdef DEBUG_ENABLED
@@ -1544,10 +1599,8 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
GDScriptParser::DataType initializer_type = p_assignable->initializer->get_datatype();
if (p_assignable->infer_datatype) {
- if (!initializer_type.is_set() || initializer_type.has_no_type()) {
+ if (!initializer_type.is_set() || initializer_type.has_no_type() || !initializer_type.is_hard_type()) {
push_error(vformat(R"(Cannot infer the type of "%s" %s because the value doesn't have a set type.)", p_assignable->identifier->name, p_kind), p_assignable->initializer);
- } else if (initializer_type.is_variant() && !initializer_type.is_hard_type()) {
- push_error(vformat(R"(Cannot infer the type of "%s" %s because the value is Variant. Use explicit "Variant" type if this is intended.)", p_assignable->identifier->name, p_kind), p_assignable->initializer);
} else if (initializer_type.kind == GDScriptParser::DataType::BUILTIN && initializer_type.builtin_type == Variant::NIL && !is_constant) {
push_error(vformat(R"(Cannot infer the type of "%s" %s because the value is "null".)", p_assignable->identifier->name, p_kind), p_assignable->initializer);
}
@@ -2014,7 +2067,7 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre
reduce_subscript(static_cast<GDScriptParser::SubscriptNode *>(p_expression));
break;
case GDScriptParser::Node::TERNARY_OPERATOR:
- reduce_ternary_op(static_cast<GDScriptParser::TernaryOpNode *>(p_expression));
+ reduce_ternary_op(static_cast<GDScriptParser::TernaryOpNode *>(p_expression), p_is_root);
break;
case GDScriptParser::Node::UNARY_OPERATOR:
reduce_unary_op(static_cast<GDScriptParser::UnaryOpNode *>(p_expression));
@@ -3534,6 +3587,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
}
#endif
result_type.kind = GDScriptParser::DataType::VARIANT;
+ mark_node_unsafe(p_subscript);
}
}
if (!valid) {
@@ -3735,10 +3789,10 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
p_subscript->set_datatype(result_type);
}
-void GDScriptAnalyzer::reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op) {
+void GDScriptAnalyzer::reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op, bool p_is_root) {
reduce_expression(p_ternary_op->condition);
- reduce_expression(p_ternary_op->true_expr);
- reduce_expression(p_ternary_op->false_expr);
+ reduce_expression(p_ternary_op->true_expr, p_is_root);
+ reduce_expression(p_ternary_op->false_expr, p_is_root);
GDScriptParser::DataType result;
@@ -4552,6 +4606,11 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::get_parser_for(const String &p_path) {
}
Error GDScriptAnalyzer::resolve_inheritance() {
+ // Apply annotations.
+ for (GDScriptParser::AnnotationNode *&E : parser->head->annotations) {
+ resolve_annotation(E);
+ E->apply(parser, parser->head);
+ }
return resolve_class_inheritance(parser->head, true);
}
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index b22d47982f..5397be33f0 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -99,7 +99,7 @@ class GDScriptAnalyzer {
void reduce_preload(GDScriptParser::PreloadNode *p_preload);
void reduce_self(GDScriptParser::SelfNode *p_self);
void reduce_subscript(GDScriptParser::SubscriptNode *p_subscript);
- void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op);
+ void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op, bool p_is_root = false);
void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op);
void const_fold_array(GDScriptParser::ArrayNode *p_array, bool p_is_const);
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index 6c80fb7665..e19dda090e 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -401,6 +401,16 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->_instruction_args_size = instr_args_max;
function->_ptrcall_args_size = ptrcall_max;
+#ifdef DEBUG_ENABLED
+ function->operator_names = operator_names;
+ function->setter_names = setter_names;
+ function->getter_names = getter_names;
+ function->builtin_methods_names = builtin_methods_names;
+ function->constructors_names = constructors_names;
+ function->utilities_names = utilities_names;
+ function->gds_utilities_names = gds_utilities_names;
+#endif
+
ended = true;
return function;
}
@@ -551,6 +561,9 @@ void GDScriptByteCodeGenerator::write_unary_operator(const Address &p_target, Va
append(Address());
append(p_target);
append(op_func);
+#ifdef DEBUG_ENABLED
+ add_debug_name(operator_names, get_operation_pos(op_func), Variant::get_operator_name(p_operator));
+#endif
return;
}
@@ -580,6 +593,9 @@ void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, V
append(p_right_operand);
append(p_target);
append(op_func);
+#ifdef DEBUG_ENABLED
+ add_debug_name(operator_names, get_operation_pos(op_func), Variant::get_operator_name(p_operator));
+#endif
return;
}
@@ -765,6 +781,9 @@ void GDScriptByteCodeGenerator::write_set_named(const Address &p_target, const S
append(p_target);
append(p_source);
append(setter);
+#ifdef DEBUG_ENABLED
+ add_debug_name(setter_names, get_setter_pos(setter), p_name);
+#endif
return;
}
append_opcode(GDScriptFunction::OPCODE_SET_NAMED);
@@ -780,6 +799,9 @@ void GDScriptByteCodeGenerator::write_get_named(const Address &p_target, const S
append(p_source);
append(p_target);
append(getter);
+#ifdef DEBUG_ENABLED
+ add_debug_name(getter_names, get_getter_pos(getter), p_name);
+#endif
return;
}
append_opcode(GDScriptFunction::OPCODE_GET_NAMED);
@@ -972,14 +994,18 @@ void GDScriptByteCodeGenerator::write_call_async(const Address &p_target, const
append(p_function_name);
}
-void GDScriptByteCodeGenerator::write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) {
+void GDScriptByteCodeGenerator::write_call_gdscript_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) {
append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_GDSCRIPT_UTILITY, 1 + p_arguments.size());
+ GDScriptUtilityFunctions::FunctionPtr gds_function = GDScriptUtilityFunctions::get_function(p_function);
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(get_call_target(p_target));
append(p_arguments.size());
- append(p_function);
+ append(gds_function);
+#ifdef DEBUG_ENABLED
+ add_debug_name(gds_utilities_names, get_gds_utility_pos(gds_function), p_function);
+#endif
}
void GDScriptByteCodeGenerator::write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) {
@@ -1012,6 +1038,9 @@ void GDScriptByteCodeGenerator::write_call_utility(const Address &p_target, cons
append(target);
append(p_arguments.size());
append(Variant::get_validated_utility_function(p_function));
+#ifdef DEBUG_ENABLED
+ add_debug_name(utilities_names, get_utility_pos(Variant::get_validated_utility_function(p_function)), p_function);
+#endif
} else {
append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_UTILITY, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
@@ -1074,6 +1103,9 @@ void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target,
append(target);
append(p_arguments.size());
append(Variant::get_validated_builtin_method(p_type, p_method));
+#ifdef DEBUG_ENABLED
+ add_debug_name(builtin_methods_names, get_builtin_method_pos(Variant::get_validated_builtin_method(p_type, p_method)), p_method);
+#endif
}
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) {
@@ -1263,6 +1295,9 @@ void GDScriptByteCodeGenerator::write_construct(const Address &p_target, Variant
append(get_call_target(p_target));
append(p_arguments.size());
append(Variant::get_validated_constructor(p_type, valid_constructor));
+#ifdef DEBUG_ENABLED
+ add_debug_name(constructors_names, get_constructor_pos(Variant::get_validated_constructor(p_type, valid_constructor)), Variant::get_type_name(p_type));
+#endif
return;
}
}
@@ -1543,28 +1578,6 @@ void GDScriptByteCodeGenerator::write_endwhile() {
current_breaks_to_patch.pop_back();
}
-void GDScriptByteCodeGenerator::start_match() {
- match_continues_to_patch.push_back(List<int>());
-}
-
-void GDScriptByteCodeGenerator::start_match_branch() {
- // Patch continue statements.
- for (const int &E : match_continues_to_patch.back()->get()) {
- patch_jump(E);
- }
- match_continues_to_patch.pop_back();
- // Start a new list for next branch.
- match_continues_to_patch.push_back(List<int>());
-}
-
-void GDScriptByteCodeGenerator::end_match() {
- // Patch continue statements.
- for (const int &E : match_continues_to_patch.back()->get()) {
- patch_jump(E);
- }
- match_continues_to_patch.pop_back();
-}
-
void GDScriptByteCodeGenerator::write_break() {
append_opcode(GDScriptFunction::OPCODE_JUMP);
current_breaks_to_patch.back()->get().push_back(opcodes.size());
@@ -1576,12 +1589,6 @@ void GDScriptByteCodeGenerator::write_continue() {
append(continue_addrs.back()->get());
}
-void GDScriptByteCodeGenerator::write_continue_match() {
- append_opcode(GDScriptFunction::OPCODE_JUMP);
- match_continues_to_patch.back()->get().push_back(opcodes.size());
- append(0);
-}
-
void GDScriptByteCodeGenerator::write_breakpoint() {
append_opcode(GDScriptFunction::OPCODE_BREAKPOINT);
}
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
index 171c505116..8aa02b86c4 100644
--- a/modules/gdscript/gdscript_byte_codegen.h
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -95,6 +95,24 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
RBMap<MethodBind *, int> method_bind_map;
RBMap<GDScriptFunction *, int> lambdas_map;
+#if DEBUG_ENABLED
+ // Keep method and property names for pointer and validated operations.
+ // Used when disassembling the bytecode.
+ Vector<String> operator_names;
+ Vector<String> setter_names;
+ Vector<String> getter_names;
+ Vector<String> builtin_methods_names;
+ Vector<String> constructors_names;
+ Vector<String> utilities_names;
+ Vector<String> gds_utilities_names;
+ void add_debug_name(Vector<String> &vector, int index, const String &name) {
+ if (index >= vector.size()) {
+ vector.resize(index + 1);
+ }
+ vector.write[index] = name;
+ }
+#endif
+
// Lists since these can be nested.
List<int> if_jmp_addrs;
List<int> for_jmp_addrs;
@@ -113,7 +131,6 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
List<int> ternary_jump_skip_pos;
List<List<int>> current_breaks_to_patch;
- List<List<int>> match_continues_to_patch;
void add_stack_identifier(const StringName &p_id, int p_stackpos) {
if (locals.size() > max_locals) {
@@ -468,8 +485,8 @@ public:
virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
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_gdscript_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) override;
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;
@@ -496,12 +513,8 @@ public:
virtual void start_while_condition() override;
virtual void write_while(const Address &p_condition) override;
virtual void write_endwhile() override;
- virtual void start_match() override;
- virtual void start_match_branch() override;
- virtual void end_match() override;
virtual void write_break() override;
virtual void write_continue() override;
- virtual void write_continue_match() override;
virtual void write_breakpoint() override;
virtual void write_newline(int p_line) override;
virtual void write_return(const Address &p_return_value) override;
diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h
index e885938eba..6d42d152b9 100644
--- a/modules/gdscript/gdscript_codegen.h
+++ b/modules/gdscript/gdscript_codegen.h
@@ -121,7 +121,7 @@ public:
virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) = 0;
- virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_gdscript_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) = 0;
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) = 0;
virtual void write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) = 0;
virtual void write_call_native_static(const Address &p_target, const StringName &p_class, const StringName &p_method, const Vector<Address> &p_arguments) = 0;
@@ -148,12 +148,8 @@ public:
virtual void start_while_condition() = 0; // Used to allow a jump to the expression evaluation.
virtual void write_while(const Address &p_condition) = 0;
virtual void write_endwhile() = 0;
- virtual void start_match() = 0;
- virtual void start_match_branch() = 0;
- virtual void end_match() = 0;
virtual void write_break() = 0;
virtual void write_continue() = 0;
- virtual void write_continue_match() = 0;
virtual void write_breakpoint() = 0;
virtual void write_newline(int p_line) = 0;
virtual void write_return(const Address &p_return_value) = 0;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index d63a1b4536..c78dd1528f 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -546,7 +546,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
gen->write_call_utility(result, call->function_name, arguments);
} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptUtilityFunctions::function_exists(call->function_name)) {
// GDScript utility function.
- gen->write_call_gdscript_utility(result, GDScriptUtilityFunctions::get_function(call->function_name), arguments);
+ gen->write_call_gdscript_utility(result, call->function_name, arguments);
} else {
// Regular function.
const GDScriptParser::ExpressionNode *callee = call->callee;
@@ -1410,7 +1410,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type);
Vector<GDScriptCodeGenerator::Address> len_args;
len_args.push_back(p_value_addr);
- codegen.generator->write_call_gdscript_utility(value_length_addr, GDScriptUtilityFunctions::get_function("len"), len_args);
+ codegen.generator->write_call_gdscript_utility(value_length_addr, "len", len_args);
// Test length compatibility.
temp_type.builtin_type = Variant::BOOL;
@@ -1508,7 +1508,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type);
Vector<GDScriptCodeGenerator::Address> func_args;
func_args.push_back(p_value_addr);
- codegen.generator->write_call_gdscript_utility(value_length_addr, GDScriptUtilityFunctions::get_function("len"), func_args);
+ codegen.generator->write_call_gdscript_utility(value_length_addr, "len", func_args);
// Test length compatibility.
temp_type.builtin_type = Variant::BOOL;
@@ -1679,7 +1679,6 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
case GDScriptParser::Node::MATCH: {
const GDScriptParser::MatchNode *match = static_cast<const GDScriptParser::MatchNode *>(s);
- gen->start_match();
codegen.start_block();
// Evaluate the match expression.
@@ -1718,7 +1717,6 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
const GDScriptParser::MatchBranchNode *branch = match->branches[j];
- gen->start_match_branch(); // Need so lower level code can patch 'continue' jumps.
codegen.start_block(); // Create an extra block around for binds.
// Add locals in block before patterns, so temporaries don't use the stack address for binds.
@@ -1756,8 +1754,6 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
for (int j = 0; j < match->branches.size(); j++) {
gen->write_endif();
}
-
- gen->end_match();
} break;
case GDScriptParser::Node::IF: {
const GDScriptParser::IfNode *if_n = static_cast<const GDScriptParser::IfNode *>(s);
@@ -1845,12 +1841,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
gen->write_break();
} break;
case GDScriptParser::Node::CONTINUE: {
- const GDScriptParser::ContinueNode *cont = static_cast<const GDScriptParser::ContinueNode *>(s);
- if (cont->is_for_match) {
- gen->write_continue_match();
- } else {
- gen->write_continue();
- }
+ gen->write_continue();
} break;
case GDScriptParser::Node::RETURN: {
const GDScriptParser::ReturnNode *return_n = static_cast<const GDScriptParser::ReturnNode *>(s);
diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp
index 4edabdcb40..b5c8a6f478 100644
--- a/modules/gdscript/gdscript_disassembler.cpp
+++ b/modules/gdscript/gdscript_disassembler.cpp
@@ -128,7 +128,9 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
text += DADDR(3);
text += " = ";
text += DADDR(1);
- text += " <operator function> ";
+ text += " ";
+ text += operator_names[_code_ptr[ip + 4]];
+ text += " ";
text += DADDR(2);
incr += 5;
@@ -230,7 +232,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
text += "set_named validated ";
text += DADDR(1);
text += "[\"";
- text += "<unknown name>";
+ text += setter_names[_code_ptr[ip + 3]];
text += "\"] = ";
text += DADDR(2);
@@ -253,7 +255,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
text += " = ";
text += DADDR(1);
text += "[\"";
- text += "<unknown name>";
+ text += getter_names[_code_ptr[ip + 3]];
text += "\"]";
incr += 4;
@@ -398,7 +400,8 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
text += DADDR(1 + argc);
text += " = ";
- text += "<unknown type>(";
+ text += constructors_names[_code_ptr[ip + 3 + argc]];
+ text += "(";
for (int i = 0; i < argc; i++) {
if (i > 0) {
text += ", ";
@@ -687,7 +690,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
text += DADDR(2 + argc) + " = ";
text += DADDR(1) + ".";
- text += "<unknown method>";
+ text += builtin_methods_names[_code_ptr[ip + 4 + argc]];
text += "(";
@@ -725,12 +728,12 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
case OPCODE_CALL_UTILITY_VALIDATED: {
int instr_var_args = _code_ptr[++ip];
- text += "call-utility ";
+ text += "call-utility validated ";
int argc = _code_ptr[ip + 1 + instr_var_args];
text += DADDR(1 + argc) + " = ";
- text += "<unknown function>";
+ text += utilities_names[_code_ptr[ip + 3 + argc]];
text += "(";
for (int i = 0; i < argc; i++) {
@@ -746,12 +749,12 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
case OPCODE_CALL_GDSCRIPT_UTILITY: {
int instr_var_args = _code_ptr[++ip];
- text += "call-gscript-utility ";
+ text += "call-gdscript-utility ";
int argc = _code_ptr[ip + 1 + instr_var_args];
text += DADDR(1 + argc) + " = ";
- text += "<unknown function>";
+ text += gds_utilities_names[_code_ptr[ip + 3 + argc]];
text += "(";
for (int i = 0; i < argc; i++) {
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 3fc0924b4c..f88ac581ca 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -782,6 +782,7 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
}
} else if (p_annotation->name == SNAME("@export_node_path")) {
ScriptLanguage::CodeCompletionOption node("Node", ScriptLanguage::CODE_COMPLETION_KIND_CLASS);
+ node.insert_text = node.display.quote(p_quote_style);
r_result.insert(node.display, node);
List<StringName> node_types;
ClassDB::get_inheriters_from_class("Node", &node_types);
@@ -790,11 +791,13 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
continue;
}
ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS);
+ option.insert_text = option.display.quote(p_quote_style);
r_result.insert(option.display, option);
}
} else if (p_annotation->name == SNAME("@warning_ignore")) {
for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) {
ScriptLanguage::CodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
+ warning.insert_text = warning.display.quote(p_quote_style);
r_result.insert(warning.display, warning);
}
}
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index 76214f3482..37416a734d 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -505,6 +505,16 @@ private:
Vector<Variant> default_arg_values;
#endif
+#ifdef DEBUG_ENABLED
+ Vector<String> operator_names;
+ Vector<String> setter_names;
+ Vector<String> getter_names;
+ Vector<String> builtin_methods_names;
+ Vector<String> constructors_names;
+ Vector<String> utilities_names;
+ Vector<String> gds_utilities_names;
+#endif
+
List<StackDebug> stack_debug;
Variant _get_default_variant_for_data_type(const GDScriptDataType &p_data_type);
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index f5d3306376..1a744d59ba 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -41,6 +41,7 @@
#include "core/os/os.h"
#include "core/string/string_builder.h"
#include "gdscript_warning.h"
+#include "servers/text_server.h"
#endif // DEBUG_ENABLED
#ifdef TOOLS_ENABLED
@@ -186,24 +187,6 @@ void GDScriptParser::push_error(const String &p_message, const Node *p_origin) {
}
#ifdef DEBUG_ENABLED
-void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_code, const String &p_symbol1, const String &p_symbol2, const String &p_symbol3, const String &p_symbol4) {
- ERR_FAIL_COND(p_source == nullptr);
- Vector<String> symbols;
- if (!p_symbol1.is_empty()) {
- symbols.push_back(p_symbol1);
- }
- if (!p_symbol2.is_empty()) {
- symbols.push_back(p_symbol2);
- }
- if (!p_symbol3.is_empty()) {
- symbols.push_back(p_symbol3);
- }
- if (!p_symbol4.is_empty()) {
- symbols.push_back(p_symbol4);
- }
- push_warning(p_source, p_code, symbols);
-}
-
void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_code, const Vector<String> &p_symbols) {
ERR_FAIL_COND(p_source == nullptr);
if (is_ignoring_warnings) {
@@ -546,7 +529,7 @@ void GDScriptParser::parse_program() {
AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
if (annotation != nullptr) {
if (annotation->applies_to(AnnotationInfo::SCRIPT)) {
- annotation->apply(this, head);
+ head->annotations.push_back(annotation);
} else {
annotation_stack.push_back(annotation);
// This annotation must appear after script-level annotations
@@ -788,7 +771,6 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)()
return;
}
- // Apply annotations.
for (AnnotationNode *&annotation : annotations) {
member->annotations.push_back(annotation);
}
@@ -865,7 +847,7 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) {
if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
push_error(R"(Expected newline after a standalone annotation.)");
}
- annotation->apply(this, head);
+ head->annotations.push_back(annotation);
} else {
annotation_stack.push_back(annotation);
}
@@ -1451,7 +1433,11 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali
annotation->info = &valid_annotations[annotation->name];
if (!annotation->applies_to(p_valid_targets)) {
- push_error(vformat(R"(Annotation "%s" is not allowed in this level.)", annotation->name));
+ if (annotation->applies_to(AnnotationInfo::SCRIPT)) {
+ push_error(vformat(R"(Annotation "%s" must be at the top of the script, before "extends" and "class_name".)", annotation->name));
+ } else {
+ push_error(vformat(R"(Annotation "%s" is not allowed in this level.)", annotation->name));
+ }
valid = false;
}
@@ -1483,7 +1469,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali
match(GDScriptTokenizer::Token::NEWLINE); // Newline after annotation is optional.
if (valid) {
- valid = validate_annotation_arguments(annotation);
+ valid = validate_annotation_argument_count(annotation);
}
return valid ? annotation : nullptr;
@@ -1714,6 +1700,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
case Node::CALL:
case Node::ASSIGNMENT:
case Node::AWAIT:
+ case Node::TERNARY_OPERATOR:
// Fine.
break;
case Node::LAMBDA:
@@ -1729,7 +1716,6 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
}
}
- // Apply annotations to statement.
while (!is_annotation && result != nullptr && !annotation_stack.is_empty()) {
AnnotationNode *last_annotation = annotation_stack.back()->get();
if (last_annotation->applies_to(AnnotationInfo::STATEMENT)) {
@@ -1806,11 +1792,10 @@ GDScriptParser::BreakNode *GDScriptParser::parse_break() {
GDScriptParser::ContinueNode *GDScriptParser::parse_continue() {
if (!can_continue) {
- push_error(R"(Cannot use "continue" outside of a loop or pattern matching block.)");
+ push_error(R"(Cannot use "continue" outside of a loop.)");
}
current_suite->has_continue = true;
ContinueNode *cont = alloc_node<ContinueNode>();
- cont->is_for_match = is_continue_match;
complete_extents(cont);
end_statement(R"("continue")");
return cont;
@@ -1836,12 +1821,10 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() {
// Save break/continue state.
bool could_break = can_break;
bool could_continue = can_continue;
- bool was_continue_match = is_continue_match;
// Allow break/continue.
can_break = true;
can_continue = true;
- is_continue_match = false;
SuiteNode *suite = alloc_node<SuiteNode>();
if (n_for->variable) {
@@ -1859,7 +1842,6 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() {
// Reset break/continue state.
can_break = could_break;
can_continue = could_continue;
- is_continue_match = was_continue_match;
return n_for;
}
@@ -1996,13 +1978,6 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
return nullptr;
}
- // Save continue state.
- bool could_continue = can_continue;
- bool was_continue_match = is_continue_match;
- // Allow continue for match.
- can_continue = true;
- is_continue_match = true;
-
SuiteNode *suite = alloc_node<SuiteNode>();
if (branch->patterns.size() > 0) {
for (const KeyValue<StringName, IdentifierNode *> &E : branch->patterns[0]->binds) {
@@ -2015,10 +1990,6 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
branch->block = parse_suite("match pattern block", suite);
complete_extents(branch);
- // Restore continue state.
- can_continue = could_continue;
- is_continue_match = was_continue_match;
-
return branch;
}
@@ -2177,12 +2148,10 @@ GDScriptParser::WhileNode *GDScriptParser::parse_while() {
// Save break/continue state.
bool could_break = can_break;
bool could_continue = can_continue;
- bool was_continue_match = is_continue_match;
// Allow break/continue.
can_break = true;
can_continue = true;
- is_continue_match = false;
n_while->loop = parse_suite(R"("while" block)");
n_while->loop->is_loop = true;
@@ -2191,7 +2160,6 @@ GDScriptParser::WhileNode *GDScriptParser::parse_while() {
// Reset break/continue state.
can_break = could_break;
can_continue = could_continue;
- is_continue_match = was_continue_match;
return n_while;
}
@@ -2251,7 +2219,14 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_expression(bool p_can_assi
}
GDScriptParser::IdentifierNode *GDScriptParser::parse_identifier() {
- return static_cast<IdentifierNode *>(parse_identifier(nullptr, false));
+ IdentifierNode *identifier = static_cast<IdentifierNode *>(parse_identifier(nullptr, false));
+#ifdef DEBUG_ENABLED
+ // Check for spoofing here (if available in TextServer) since this isn't called inside expressions. This is only relevant for declarations.
+ if (identifier && TS->has_feature(TextServer::FEATURE_UNICODE_SECURITY) && TS->spoof_check(identifier->name.operator String())) {
+ push_warning(identifier, GDScriptWarning::CONFUSABLE_IDENTIFIER, identifier->name.operator String());
+ }
+#endif
+ return identifier;
}
GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode *p_previous_operand, bool p_can_assign) {
@@ -3621,7 +3596,7 @@ bool GDScriptParser::AnnotationNode::applies_to(uint32_t p_target_kinds) const {
return (info->target_kind & p_target_kinds) > 0;
}
-bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation) {
+bool GDScriptParser::validate_annotation_argument_count(AnnotationNode *p_annotation) {
ERR_FAIL_COND_V_MSG(!valid_annotations.has(p_annotation->name), false, vformat(R"(Annotation "%s" not found to validate.)", p_annotation->name));
const MethodInfo &info = valid_annotations[p_annotation->name].info;
@@ -3636,62 +3611,6 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation)
return false;
}
- const List<PropertyInfo>::Element *E = info.arguments.front();
- for (int i = 0; i < p_annotation->arguments.size(); i++) {
- ExpressionNode *argument = p_annotation->arguments[i];
- const PropertyInfo &parameter = E->get();
-
- if (E->next() != nullptr) {
- E = E->next();
- }
-
- switch (parameter.type) {
- case Variant::STRING:
- case Variant::STRING_NAME:
- case Variant::NODE_PATH:
- // Allow "quote-less strings", as long as they are recognized as identifiers.
- if (argument->type == Node::IDENTIFIER) {
- IdentifierNode *string = static_cast<IdentifierNode *>(argument);
- Callable::CallError error;
- Vector<Variant> args = varray(string->name);
- const Variant *name = args.ptr();
- Variant r;
- Variant::construct(parameter.type, r, &(name), 1, error);
- p_annotation->resolved_arguments.push_back(r);
- if (error.error != Callable::CallError::CALL_OK) {
- push_error(vformat(R"(Expected %s as argument %d of annotation "%s".)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
- p_annotation->resolved_arguments.remove_at(p_annotation->resolved_arguments.size() - 1);
- return false;
- }
- break;
- }
- [[fallthrough]];
- default: {
- if (argument->type != Node::LITERAL) {
- push_error(vformat(R"(Expected %s as argument %d of annotation "%s".)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
- return false;
- }
-
- Variant value = static_cast<LiteralNode *>(argument)->value;
- if (!Variant::can_convert_strict(value.get_type(), parameter.type)) {
- push_error(vformat(R"(Expected %s as argument %d of annotation "%s".)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
- return false;
- }
- Callable::CallError error;
- const Variant *args = &value;
- Variant r;
- Variant::construct(parameter.type, r, &(args), 1, error);
- p_annotation->resolved_arguments.push_back(r);
- if (error.error != Callable::CallError::CALL_OK) {
- push_error(vformat(R"(Expected %s as argument %d of annotation "%s".)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
- p_annotation->resolved_arguments.remove_at(p_annotation->resolved_arguments.size() - 1);
- return false;
- }
- break;
- }
- }
- }
-
return true;
}
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 0903f62061..74e12d0b5e 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -758,7 +758,6 @@ public:
};
struct ContinueNode : public Node {
- bool is_for_match = false;
ContinueNode() {
type = CONTINUE;
}
@@ -1254,7 +1253,6 @@ private:
bool panic_mode = false;
bool can_break = false;
bool can_continue = false;
- bool is_continue_match = false; // Whether a `continue` will act on a `match`.
List<bool> multiline_stack;
ClassNode *head = nullptr;
@@ -1361,8 +1359,11 @@ private:
void clear();
void push_error(const String &p_message, const Node *p_origin = nullptr);
#ifdef DEBUG_ENABLED
- void push_warning(const Node *p_source, GDScriptWarning::Code p_code, const String &p_symbol1 = String(), const String &p_symbol2 = String(), const String &p_symbol3 = String(), const String &p_symbol4 = String());
void push_warning(const Node *p_source, GDScriptWarning::Code p_code, const Vector<String> &p_symbols);
+ template <typename... Symbols>
+ void push_warning(const Node *p_source, GDScriptWarning::Code p_code, const Symbols &...p_symbols) {
+ push_warning(p_source, p_code, Vector<String>{ p_symbols... });
+ }
#endif
void make_completion_context(CompletionType p_type, Node *p_node, int p_argument = -1, bool p_force = false);
@@ -1400,7 +1401,7 @@ private:
// Annotations
AnnotationNode *parse_annotation(uint32_t p_valid_targets);
bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments = Vector<Variant>(), bool p_is_vararg = false);
- bool validate_annotation_arguments(AnnotationNode *p_annotation);
+ bool validate_annotation_argument_count(AnnotationNode *p_annotation);
void clear_unused_annotations();
bool tool_annotation(const AnnotationNode *p_annotation, Node *p_target);
bool icon_annotation(const AnnotationNode *p_annotation, Node *p_target);
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index e17a804003..d7f1114fd3 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -31,10 +31,14 @@
#include "gdscript_tokenizer.h"
#include "core/error/error_macros.h"
+#include "core/string/char_utils.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
#endif
+#ifdef DEBUG_ENABLED
+#include "servers/text_server.h"
+#endif
static const char *token_names[] = {
"Empty", // EMPTY,
@@ -435,10 +439,12 @@ GDScriptTokenizer::Token GDScriptTokenizer::check_vcs_marker(char32_t p_test, To
}
GDScriptTokenizer::Token GDScriptTokenizer::annotation() {
- if (!is_ascii_identifier_char(_peek())) {
+ if (is_unicode_identifier_start(_peek())) {
+ _advance(); // Consume start character.
+ } else {
push_error("Expected annotation identifier after \"@\".");
}
- while (is_ascii_identifier_char(_peek())) {
+ while (is_unicode_identifier_continue(_peek())) {
// Consume all identifier characters.
_advance();
}
@@ -447,7 +453,6 @@ GDScriptTokenizer::Token GDScriptTokenizer::annotation() {
return annotation;
}
-GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() {
#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \
KEYWORD_GROUP('a') \
KEYWORD("as", Token::AS) \
@@ -512,8 +517,21 @@ GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() {
#define MIN_KEYWORD_LENGTH 2
#define MAX_KEYWORD_LENGTH 10
- // Consume all alphanumeric characters.
- while (is_ascii_identifier_char(_peek())) {
+#ifdef DEBUG_ENABLED
+void GDScriptTokenizer::make_keyword_list() {
+#define KEYWORD_LINE(keyword, token_type) keyword,
+#define KEYWORD_GROUP_IGNORE(group)
+ keyword_list = {
+ KEYWORDS(KEYWORD_GROUP_IGNORE, KEYWORD_LINE)
+ };
+#undef KEYWORD_LINE
+#undef KEYWORD_GROUP_IGNORE
+}
+#endif // DEBUG_ENABLED
+
+GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() {
+ // Consume all identifier characters.
+ while (is_unicode_identifier_continue(_peek())) {
_advance();
}
@@ -565,15 +583,28 @@ GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() {
}
// Not a keyword, so must be an identifier.
- return make_identifier(name);
+ Token id = make_identifier(name);
+
+#ifdef DEBUG_ENABLED
+ // Additional checks for identifiers but only in debug and if it's available in TextServer.
+ if (TS->has_feature(TextServer::FEATURE_UNICODE_SECURITY)) {
+ int64_t confusable = TS->is_confusable(name, keyword_list);
+ if (confusable >= 0) {
+ push_error(vformat(R"(Identifier "%s" is visually similar to the GDScript keyword "%s" and thus not allowed.)", name, keyword_list[confusable]));
+ }
+ }
+#endif // DEBUG_ENABLED
+
+ return id;
-#undef KEYWORDS
-#undef MIN_KEYWORD_LENGTH
-#undef MAX_KEYWORD_LENGTH
#undef KEYWORD_GROUP_CASE
#undef KEYWORD
}
+#undef MAX_KEYWORD_LENGTH
+#undef MIN_KEYWORD_LENGTH
+#undef KEYWORDS
+
void GDScriptTokenizer::newline(bool p_make_token) {
// Don't overwrite previous newline, nor create if we want a line continuation.
if (p_make_token && !pending_newline && !line_continuation) {
@@ -720,7 +751,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
error.rightmost_column = column + 1;
push_error(error);
has_error = true;
- } else if (is_ascii_identifier_char(_peek())) {
+ } else if (is_unicode_identifier_start(_peek()) || is_unicode_identifier_continue(_peek())) {
// Letter at the end of the number.
push_error("Invalid numeric notation.");
}
@@ -1311,7 +1342,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() {
if (is_digit(c)) {
return number();
- } else if (is_ascii_identifier_char(c)) {
+ } else if (is_unicode_identifier_start(c)) {
return potential_identifier();
}
@@ -1504,7 +1535,11 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() {
}
default:
- return make_error(vformat(R"(Unknown character "%s".)", String(&c, 1)));
+ if (is_whitespace(c)) {
+ return make_error(vformat(R"(Invalid white space character "\\u%X".)", static_cast<int32_t>(c)));
+ } else {
+ return make_error(vformat(R"(Unknown character "%s".)", String(&c, 1)));
+ }
}
}
@@ -1514,4 +1549,7 @@ GDScriptTokenizer::GDScriptTokenizer() {
tab_size = EditorSettings::get_singleton()->get_setting("text_editor/behavior/indent/size");
}
#endif // TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
+ make_keyword_list();
+#endif // DEBUG_ENABLED
}
diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h
index 9588922122..608840d3f1 100644
--- a/modules/gdscript/gdscript_tokenizer.h
+++ b/modules/gdscript/gdscript_tokenizer.h
@@ -224,6 +224,9 @@ private:
char32_t indent_char = '\0';
int position = 0;
int length = 0;
+#ifdef DEBUG_ENABLED
+ Vector<String> keyword_list;
+#endif // DEBUG_ENABLED
#ifdef TOOLS_ENABLED
HashMap<int, CommentData> comments;
@@ -239,6 +242,10 @@ private:
void _skip_whitespace();
void check_indent();
+#ifdef DEBUG_ENABLED
+ void make_keyword_list();
+#endif // DEBUG_ENABLED
+
Token make_error(const String &p_message);
void push_error(const String &p_message);
void push_error(const Token &p_error);
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 184cecb316..a6cbb7f6ae 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -155,6 +155,10 @@ String GDScriptWarning::get_message() const {
CHECK_SYMBOLS(2);
return vformat(R"(The function '%s()' is a static function but was called from an instance. Instead, it should be directly called from the type: '%s.%s()'.)", symbols[0], symbols[1], symbols[0]);
}
+ case CONFUSABLE_IDENTIFIER: {
+ CHECK_SYMBOLS(1);
+ return vformat(R"(The identifier "%s" has misleading characters and might be confused with something else.)", symbols[0]);
+ }
case WARNING_MAX:
break; // Can't happen, but silences warning
}
@@ -219,6 +223,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"SHADOWED_GLOBAL_IDENTIFIER",
"INT_ASSIGNED_TO_ENUM",
"STATIC_CALLED_ON_INSTANCE",
+ "CONFUSABLE_IDENTIFIER",
};
static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index e3aee45f33..b485f02b9c 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -78,6 +78,7 @@ public:
SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable.
INT_ASSIGNED_TO_ENUM, // An integer value was assigned to an enum-typed variable without casting.
STATIC_CALLED_ON_INSTANCE, // A static method was called on an instance of a class instead of on the class itself.
+ CONFUSABLE_IDENTIFIER, // The identifier contains misleading characters that can be confused. E.g. "usеr" (has Cyrillic "е" instead of Latin "e").
WARNING_MAX,
};
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.gd b/modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.gd
new file mode 100644
index 0000000000..75524c32ae
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.gd
@@ -0,0 +1,6 @@
+var num := 1
+
+@export_range(num, 10) var a
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.out b/modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.out
new file mode 100644
index 0000000000..b4f0e79237
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Argument 1 of annotation "@export_range" isn't a constant expression.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.gd b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.gd
new file mode 100644
index 0000000000..6014ee831c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.gd
@@ -0,0 +1,3 @@
+func test():
+ var untyped = 1
+ var inferred := untyped
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.out b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.out
new file mode 100644
index 0000000000..b6dc6d0b01
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot infer the type of "inferred" variable because the value doesn't have a set type.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.gd b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.gd
new file mode 100644
index 0000000000..040aa2e82a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.gd
@@ -0,0 +1,5 @@
+var untyped = 1
+var inferred := untyped
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.out b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.out
new file mode 100644
index 0000000000..b6dc6d0b01
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot infer the type of "inferred" variable because the value doesn't have a set type.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.gd b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.gd
new file mode 100644
index 0000000000..80c676488e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.gd
@@ -0,0 +1,5 @@
+func check(untyped = 1, inferred := untyped):
+ pass
+
+func test():
+ check()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.out b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.out
new file mode 100644
index 0000000000..8c9f0c13ae
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot infer the type of "inferred" parameter because the value doesn't have a set type.
diff --git a/modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.gd b/modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.gd
new file mode 100644
index 0000000000..272dce8bbe
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.gd
@@ -0,0 +1,10 @@
+const BEFORE = 1
+
+@export_range(-10, 10) var a = 0
+@export_range(1 + 2, absi(-10) + 1) var b = 5
+@export_range(BEFORE + 1, BEFORE + AFTER + 1) var c = 5
+
+const AFTER = 10
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.out b/modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.out
new file mode 100644
index 0000000000..d73c5eb7cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.out
@@ -0,0 +1 @@
+GDTEST_OK
diff --git a/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd
index 2d2c2bef19..595563541f 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd
@@ -1,9 +1,4 @@
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)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out
index 7536c38490..0ddfa4b75f 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out
+++ b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out
@@ -1,5 +1,4 @@
GDTEST_OK
-1
2
3
4
diff --git a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.gd b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.gd
index 877a4ea221..4c02fd4b0d 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.gd
@@ -1,12 +1,12 @@
-@warning_ignore(unused_private_class_variable)
+@warning_ignore("unused_private_class_variable")
var _unused = 2
-@warning_ignore(unused_variable)
+@warning_ignore("unused_variable")
func test():
print("test")
var unused = 3
- @warning_ignore(redundant_await)
+ @warning_ignore("redundant_await")
print(await regular_func())
print("done")
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.out b/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.out
index b018091c18..32e230fc80 100644
--- a/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.out
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.out
@@ -2,4 +2,4 @@ GDTEST_OK
>> WARNING
>> Line: 2
>> UNUSED_PARAMETER
->>
+>> The parameter 'unused' is never used in the function ''. If this is intended, prefix it with an underscore: '_unused'
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 a598ff8424..5fcf1cbcad 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
-Annotation "@icon" is not allowed in this level.
+Annotation "@icon" must be at the top of the script, before "extends" and "class_name".
diff --git a/modules/gdscript/tests/scripts/parser/errors/identifier_similar_to_keyword.gd b/modules/gdscript/tests/scripts/parser/errors/identifier_similar_to_keyword.gd
new file mode 100644
index 0000000000..4b1f284070
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/identifier_similar_to_keyword.gd
@@ -0,0 +1,3 @@
+func test():
+ var аs # Using Cyrillic "а".
+ print(аs)
diff --git a/modules/gdscript/tests/scripts/parser/errors/identifier_similar_to_keyword.out b/modules/gdscript/tests/scripts/parser/errors/identifier_similar_to_keyword.out
new file mode 100644
index 0000000000..337dec2f4d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/identifier_similar_to_keyword.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Identifier "аs" is visually similar to the GDScript keyword "as" and thus not allowed.
diff --git a/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd b/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd
index cc78309ae4..a34cc26e67 100644
--- a/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd
+++ b/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd
@@ -1,6 +1,6 @@
# https://github.com/godotengine/godot/issues/50285
-@warning_ignore(unused_local_constant)
+@warning_ignore("unused_local_constant")
func test():
const CONST_INNER_DICTIONARY = { "key": true }
const CONST_NESTED_DICTIONARY_OLD_WORKAROUND = {
diff --git a/modules/gdscript/tests/scripts/parser/features/export_variable.gd b/modules/gdscript/tests/scripts/parser/features/export_variable.gd
index 1e072728fc..acf9ff2e21 100644
--- a/modules/gdscript/tests/scripts/parser/features/export_variable.gd
+++ b/modules/gdscript/tests/scripts/parser/features/export_variable.gd
@@ -5,7 +5,7 @@
@export var color: Color
@export_color_no_alpha var color_no_alpha: Color
-@export_node_path(Sprite2D, Sprite3D, Control, Node) var nodepath := ^"hello"
+@export_node_path("Sprite2D", "Sprite3D", "Control", "Node") var nodepath := ^"hello"
func test():
diff --git a/modules/gdscript/tests/scripts/parser/features/match.gd b/modules/gdscript/tests/scripts/parser/features/match.gd
index 4d05490aa5..59b5ba2426 100644
--- a/modules/gdscript/tests/scripts/parser/features/match.gd
+++ b/modules/gdscript/tests/scripts/parser/features/match.gd
@@ -3,8 +3,6 @@ func test():
match i:
"Hello":
print("hello")
- # This will fall through to the default case below.
- continue
"Good bye":
print("bye")
_:
diff --git a/modules/gdscript/tests/scripts/parser/features/match.out b/modules/gdscript/tests/scripts/parser/features/match.out
index 732885c7a2..a2cb94399c 100644
--- a/modules/gdscript/tests/scripts/parser/features/match.out
+++ b/modules/gdscript/tests/scripts/parser/features/match.out
@@ -1,4 +1,3 @@
GDTEST_OK
hello
-default
This will match
diff --git a/modules/gdscript/tests/scripts/parser/features/nested_match.gd b/modules/gdscript/tests/scripts/parser/features/nested_match.gd
index aaddcc7e83..491d917a8e 100644
--- a/modules/gdscript/tests/scripts/parser/features/nested_match.gd
+++ b/modules/gdscript/tests/scripts/parser/features/nested_match.gd
@@ -8,11 +8,10 @@ func test():
1234:
print("2")
match number:
- 1234:
- print("3")
- continue
+ 4321:
+ print("Should not be printed")
_:
- print("Should also be printed")
+ print("3")
match number:
1234:
print("4")
diff --git a/modules/gdscript/tests/scripts/parser/features/nested_match.out b/modules/gdscript/tests/scripts/parser/features/nested_match.out
index 651d76cc59..c2d2e29a06 100644
--- a/modules/gdscript/tests/scripts/parser/features/nested_match.out
+++ b/modules/gdscript/tests/scripts/parser/features/nested_match.out
@@ -2,7 +2,6 @@ GDTEST_OK
1
2
3
-Should also be printed
4
5
6
diff --git a/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.gd b/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.gd
new file mode 100644
index 0000000000..523959a016
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.gd
@@ -0,0 +1,35 @@
+const π = PI
+var ㄥ = π
+
+func test():
+ var փորձարկում = "test"
+ prints("փորձարկում", փորձարկում)
+ var امتحان = "test"
+ prints("امتحان", امتحان)
+ var পরীক্ষা = "test"
+ prints("পরীক্ষা", পরীক্ষা)
+ var тест = "test"
+ prints("тест", тест)
+ var जाँच = "test"
+ prints("जाँच", जाँच)
+ var 기준 = "test"
+ prints("기준", 기준)
+ var 测试 = "test"
+ prints("测试", 测试)
+ var テスト = "test"
+ prints("テスト", テスト)
+ var 試験 = "test"
+ prints("試験", 試験)
+ var പരീക്ഷ = "test"
+ prints("പരീക്ഷ", പരീക്ഷ)
+ var ทดสอบ = "test"
+ prints("ทดสอบ", ทดสอบ)
+ var δοκιμή = "test"
+ prints("δοκιμή", δοκιμή)
+
+ const d = 1.1
+ _process(d)
+ print(is_equal_approx(ㄥ, PI + (d * PI)))
+
+func _process(Δ: float) -> void:
+ ㄥ += Δ * π
diff --git a/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.out b/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.out
new file mode 100644
index 0000000000..c071380a8f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.out
@@ -0,0 +1,14 @@
+GDTEST_OK
+փորձարկում test
+امتحان test
+পরীক্ষা test
+тест test
+जाँच test
+기준 test
+测试 test
+テスト test
+試験 test
+പരീക്ഷ test
+ทดสอบ test
+δοκιμή test
+true
diff --git a/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.gd b/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.gd
new file mode 100644
index 0000000000..e2caac8ffd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.gd
@@ -0,0 +1,5 @@
+func test():
+ var port = 0 # Only latin characters.
+ var pοrt = 1 # The "ο" is Greek omicron.
+
+ prints(port, pοrt)
diff --git a/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.out b/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.out
new file mode 100644
index 0000000000..c483396443
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.out
@@ -0,0 +1,6 @@
+GDTEST_OK
+>> WARNING
+>> Line: 3
+>> CONFUSABLE_IDENTIFIER
+>> The identifier "pοrt" has misleading characters and might be confused with something else.
+0 1
diff --git a/modules/gdscript/tests/scripts/runtime/features/await_on_void.gd b/modules/gdscript/tests/scripts/runtime/features/await_on_void.gd
index 46b9fbc951..1490a164c9 100644
--- a/modules/gdscript/tests/scripts/runtime/features/await_on_void.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/await_on_void.gd
@@ -2,6 +2,6 @@ func wait() -> void:
pass
func test():
- @warning_ignore(redundant_await)
+ @warning_ignore("redundant_await")
await wait()
print("end")
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
index 1d4b400d81..48af734317 100644
--- 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
@@ -7,11 +7,11 @@ func test():
func builtin_method():
var pba := PackedByteArray()
- @warning_ignore(return_value_discarded)
+ @warning_ignore("return_value_discarded")
pba.resize(1) # Built-in validated.
func builtin_method_static():
var _pba := PackedByteArray()
- @warning_ignore(return_value_discarded)
+ @warning_ignore("return_value_discarded")
Vector2.from_angle(PI) # Static built-in validated.
diff --git a/modules/gdscript/tests/scripts/runtime/features/gdscript.gd b/modules/gdscript/tests/scripts/runtime/features/gdscript.gd
index f2368643de..e686cffc48 100644
--- a/modules/gdscript/tests/scripts/runtime/features/gdscript.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/gdscript.gd
@@ -11,10 +11,10 @@ class InnerClass:
func _init() -> void:
prints("Inner")
'''
- @warning_ignore(return_value_discarded)
+ @warning_ignore("return_value_discarded")
gdscr.reload()
var inst = gdscr.new()
- @warning_ignore(unsafe_method_access)
+ @warning_ignore("unsafe_method_access")
inst.test()
diff --git a/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd b/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd
index cc34e71b01..2f55059334 100644
--- a/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd
@@ -20,26 +20,26 @@ func test_utility(v, f):
assert(not f) # Test unary operator reading from `nil`.
func test_builtin_call(v, f):
- @warning_ignore(unsafe_method_access)
+ @warning_ignore("unsafe_method_access")
v.angle() # Built-in method call.
assert(not f) # Test unary operator reading from `nil`.
func test_builtin_call_validated(v: Vector2, f):
- @warning_ignore(return_value_discarded)
+ @warning_ignore("return_value_discarded")
v.abs() # Built-in method call validated.
assert(not f) # Test unary operator reading from `nil`.
func test_object_call(v, f):
- @warning_ignore(unsafe_method_access)
+ @warning_ignore("unsafe_method_access")
v.get_reference_count() # Native type method call.
assert(not f) # Test unary operator reading from `nil`.
func test_object_call_method_bind(v: Resource, f):
- @warning_ignore(return_value_discarded)
+ @warning_ignore("return_value_discarded")
v.duplicate() # Native type method call with MethodBind.
assert(not f) # Test unary operator reading from `nil`.
func test_object_call_ptrcall(v: RefCounted, f):
- @warning_ignore(return_value_discarded)
+ @warning_ignore("return_value_discarded")
v.get_reference_count() # Native type method call with ptrcall.
assert(not f) # Test unary operator reading from `nil`.
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
index af3f3cb941..efa8270526 100644
--- 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
@@ -1,7 +1,7 @@
# https://github.com/godotengine/godot/issues/71172
func test():
- @warning_ignore(narrowing_conversion)
+ @warning_ignore("narrowing_conversion")
var foo: int = 0.0
print(typeof(foo) == TYPE_INT)
var dict : Dictionary = {"a":0.0}
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 5634c51c61..b243ba933d 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -6072,6 +6072,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_
const int track_idx = animation->get_track_count();
animation->add_track(Animation::TYPE_BLEND_SHAPE);
animation->track_set_path(track_idx, blend_path);
+ animation->track_set_imported(track_idx, true); //helps merging later
// Only LINEAR and STEP (NEAREST) can be supported out of the box by Godot's Animation,
// the other modes have to be baked.
diff --git a/modules/minimp3/resource_importer_mp3.cpp b/modules/minimp3/resource_importer_mp3.cpp
index 6a04e40c73..4e56120ec6 100644
--- a/modules/minimp3/resource_importer_mp3.cpp
+++ b/modules/minimp3/resource_importer_mp3.cpp
@@ -71,7 +71,7 @@ String ResourceImporterMP3::get_preset_name(int p_idx) const {
}
void ResourceImporterMP3::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "loop_offset"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,or_greater"), 0));
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
index 5fb29b86da..22a21a1754 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
@@ -111,15 +111,16 @@ namespace Godot.SourceGenerators
ClassIsEnum = 65536,
NilIsVariant = 131072,
Array = 262144,
- DoNotShareOnDuplicate = 524288,
- HighEndGfx = 1048576,
- NodePathFromSceneRoot = 2097152,
- ResourceNotPersistent = 4194304,
- KeyingIncrements = 8388608,
- DeferredSetResource = 16777216,
- EditorInstantiateObject = 33554432,
- EditorBasicSetting = 67108864,
- ReadOnly = 134217728,
+ AlwaysDuplicate = 524288,
+ NeverDuplicate = 1048576,
+ HighEndGfx = 2097152,
+ NodePathFromSceneRoot = 4194304,
+ ResourceNotPersistent = 8388608,
+ KeyingIncrements = 16777216,
+ DeferredSetResource = 33554432,
+ EditorInstantiateObject = 67108864,
+ EditorBasicSetting = 134217728,
+ ReadOnly = 268435456,
Default = 6,
NoEditor = 2
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs
index 98ca534c66..2a9758516c 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs
@@ -26,6 +26,10 @@ namespace Godot.SourceGenerators
private void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
+ // Ignore syntax inside comments
+ if (IsInsideDocumentation(context.Node))
+ return;
+
var typeArgListSyntax = (TypeArgumentListSyntax)context.Node;
// Method invocation or variable declaration that contained the type arguments
@@ -74,6 +78,26 @@ namespace Godot.SourceGenerators
}
/// <summary>
+ /// Check if the syntax node is inside a documentation syntax.
+ /// </summary>
+ /// <param name="syntax">Syntax node to check.</param>
+ /// <returns><see langword="true"/> if the syntax node is inside a documentation syntax.</returns>
+ private bool IsInsideDocumentation(SyntaxNode? syntax)
+ {
+ while (syntax != null)
+ {
+ if (syntax is DocumentationCommentTriviaSyntax)
+ {
+ return true;
+ }
+
+ syntax = syntax.Parent;
+ }
+
+ return false;
+ }
+
+ /// <summary>
/// Check if the given type argument is being used in a type parameter that contains
/// the <c>MustBeVariantAttribute</c>; otherwise, we ignore the attribute.
/// </summary>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
index ba6c10aa31..d67cb5349d 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
@@ -272,6 +272,25 @@ namespace Godot.SourceGenerators
source.Append(" }\n");
}
+ // Generate HasGodotClassSignal
+
+ if (godotSignalDelegates.Count > 0)
+ {
+ source.Append(
+ " protected override bool HasGodotClassSignal(in godot_string_name signal)\n {\n");
+
+ bool isFirstEntry = true;
+ foreach (var signal in godotSignalDelegates)
+ {
+ GenerateHasSignalEntry(signal.Name, source, isFirstEntry);
+ isFirstEntry = false;
+ }
+
+ source.Append(" return base.HasGodotClassSignal(signal);\n");
+
+ source.Append(" }\n");
+ }
+
source.Append("}\n"); // partial class
if (isInnerClass)
@@ -397,6 +416,20 @@ namespace Godot.SourceGenerators
PropertyHint.None, string.Empty, propUsage, exported: false);
}
+ private static void GenerateHasSignalEntry(
+ string signalName,
+ StringBuilder source,
+ bool isFirstEntry
+ )
+ {
+ source.Append(" ");
+ if (!isFirstEntry)
+ source.Append("else ");
+ source.Append("if (signal == SignalName.");
+ source.Append(signalName);
+ source.Append(") {\n return true;\n }\n");
+ }
+
private static void GenerateSignalEventInvoker(
GodotSignalDelegateData signal,
StringBuilder source
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 03cbfda1bd..2e8655f9b5 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -84,10 +84,12 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) {
#define CS_PROPERTY_SINGLETON "Singleton"
#define CS_METHOD_INVOKE_GODOT_CLASS_METHOD "InvokeGodotClassMethod"
#define CS_METHOD_HAS_GODOT_CLASS_METHOD "HasGodotClassMethod"
+#define CS_METHOD_HAS_GODOT_CLASS_SIGNAL "HasGodotClassSignal"
#define CS_STATIC_FIELD_NATIVE_CTOR "NativeCtor"
#define CS_STATIC_FIELD_METHOD_BIND_PREFIX "MethodBind"
#define CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX "MethodProxyName_"
+#define CS_STATIC_FIELD_SIGNAL_PROXY_NAME_PREFIX "SignalProxyName_"
#define ICALL_PREFIX "godot_icall_"
#define ICALL_CLASSDB_GET_METHOD "ClassDB_get_method"
@@ -229,6 +231,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
List<String> tag_stack;
bool code_tag = false;
+ bool line_del = false;
int pos = 0;
while (pos < bbcode.length()) {
@@ -239,20 +242,22 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
}
if (brk_pos > pos) {
- String text = bbcode.substr(pos, brk_pos - pos);
- if (code_tag || tag_stack.size() > 0) {
- xml_output.append(text.xml_escape());
- } else {
- Vector<String> lines = text.split("\n");
- for (int i = 0; i < lines.size(); i++) {
- if (i != 0) {
- xml_output.append("<para>");
- }
+ if (!line_del) {
+ String text = bbcode.substr(pos, brk_pos - pos);
+ if (code_tag || tag_stack.size() > 0) {
+ xml_output.append(text.xml_escape());
+ } else {
+ Vector<String> lines = text.split("\n");
+ for (int i = 0; i < lines.size(); i++) {
+ if (i != 0) {
+ xml_output.append("<para>");
+ }
- xml_output.append(lines[i].xml_escape());
+ xml_output.append(lines[i].xml_escape());
- if (i != lines.size() - 1) {
- xml_output.append("</para>\n");
+ if (i != lines.size() - 1) {
+ xml_output.append("</para>\n");
+ }
}
}
}
@@ -265,20 +270,22 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
int brk_end = bbcode.find("]", brk_pos + 1);
if (brk_end == -1) {
- String text = bbcode.substr(brk_pos, bbcode.length() - brk_pos);
- if (code_tag || tag_stack.size() > 0) {
- xml_output.append(text.xml_escape());
- } else {
- Vector<String> lines = text.split("\n");
- for (int i = 0; i < lines.size(); i++) {
- if (i != 0) {
- xml_output.append("<para>");
- }
+ if (!line_del) {
+ String text = bbcode.substr(brk_pos, bbcode.length() - brk_pos);
+ if (code_tag || tag_stack.size() > 0) {
+ xml_output.append(text.xml_escape());
+ } else {
+ Vector<String> lines = text.split("\n");
+ for (int i = 0; i < lines.size(); i++) {
+ if (i != 0) {
+ xml_output.append("<para>");
+ }
- xml_output.append(lines[i].xml_escape());
+ xml_output.append(lines[i].xml_escape());
- if (i != lines.size() - 1) {
- xml_output.append("</para>\n");
+ if (i != lines.size() - 1) {
+ xml_output.append("</para>\n");
+ }
}
}
}
@@ -292,7 +299,9 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
bool tag_ok = tag_stack.size() && tag_stack.front()->get() == tag.substr(1, tag.length());
if (!tag_ok) {
- xml_output.append("[");
+ if (!line_del) {
+ xml_output.append("[");
+ }
pos = brk_pos + 1;
continue;
}
@@ -307,11 +316,20 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
xml_output.append("</c>");
} else if (tag == "/codeblock") {
xml_output.append("</code>");
+ } else if (tag == "/b") {
+ xml_output.append("</b>");
+ } else if (tag == "/i") {
+ xml_output.append("</i>");
+ } else if (tag == "/csharp") {
+ xml_output.append("</code>");
+ line_del = true;
+ } else if (tag == "/codeblocks") {
+ line_del = false;
}
} else if (code_tag) {
xml_output.append("[");
pos = brk_pos + 1;
- } else if (tag.begins_with("method ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ") || tag.begins_with("theme_item ")) {
+ } else if (tag.begins_with("method ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ") || tag.begins_with("theme_item ") || tag.begins_with("param ")) {
const int tag_end = tag.find(" ");
const String link_tag = tag.substr(0, tag_end);
const String link_target = tag.substr(tag_end + 1, tag.length()).lstrip(" ");
@@ -356,6 +374,8 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
} else if (link_tag == "theme_item") {
// We do not declare theme_items in any way in C#, so there is nothing to reference
_append_xml_undeclared(xml_output, link_target);
+ } else if (link_tag == "param") {
+ _append_xml_undeclared(xml_output, snake_to_camel_case(link_target, false));
}
pos = brk_end + 1;
@@ -377,8 +397,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
#endif
"\"/>");
} else if (tag == "Variant") {
- // We use System.Object for Variant, so there is no Variant type in C#
- xml_output.append("<c>Variant</c>");
+ xml_output.append("<see cref=\"Godot.Variant\"/>");
} else if (tag == "String") {
xml_output.append("<see cref=\"string\"/>");
} else if (tag == "Nil") {
@@ -428,11 +447,13 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
pos = brk_end + 1;
} else if (tag == "b") {
- // bold is not supported in xml comments
+ xml_output.append("<b>");
+
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "i") {
- // italics is not supported in xml comments
+ xml_output.append("<i>");
+
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "code") {
@@ -447,6 +468,17 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
code_tag = true;
pos = brk_end + 1;
tag_stack.push_front(tag);
+ } else if (tag == "codeblocks") {
+ line_del = true;
+ pos = brk_end + 1;
+ tag_stack.push_front(tag);
+ } else if (tag == "csharp") {
+ xml_output.append("<code>");
+
+ line_del = false;
+ code_tag = true;
+ pos = brk_end + 1;
+ tag_stack.push_front(tag);
} else if (tag == "kbd") {
// keyboard combinations are not supported in xml comments
pos = brk_end + 1;
@@ -459,7 +491,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
xml_output.append("\n"); // FIXME: Should use <para> instead. Luckily this tag isn't used for now.
pos = brk_end + 1;
} else if (tag == "u") {
- // underline is not supported in xml comments
+ // underline is not supported in Rider xml comments
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "s") {
@@ -510,7 +542,9 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
pos = brk_end + 1;
tag_stack.push_front("font");
} else {
- xml_output.append("["); // ignore
+ if (!line_del) {
+ xml_output.append("["); // ignore
+ }
pos = brk_pos + 1;
}
}
@@ -1608,6 +1642,16 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
<< " = \"" << imethod.proxy_name << "\";\n";
}
+ // Generate signal names cache fields
+
+ for (const SignalInterface &isignal : itype.signals_) {
+ output << MEMBER_BEGIN "// ReSharper disable once InconsistentNaming\n"
+ << INDENT1 "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
+ << INDENT1 "private static readonly StringName "
+ << CS_STATIC_FIELD_SIGNAL_PROXY_NAME_PREFIX << isignal.name
+ << " = \"" << isignal.proxy_name << "\";\n";
+ }
+
// TODO: Only generate HasGodotClassMethod and InvokeGodotClassMethod if there's any method
// Generate InvokeGodotClassMethod
@@ -1721,6 +1765,34 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
}
output << INDENT1 "}\n";
+
+ // Generate HasGodotClassSignal
+
+ output << MEMBER_BEGIN "protected internal " << (is_derived_type ? "override" : "virtual")
+ << " bool " CS_METHOD_HAS_GODOT_CLASS_SIGNAL "(in godot_string_name signal)\n"
+ << INDENT1 "{\n";
+
+ for (const SignalInterface &isignal : itype.signals_) {
+ // We check for native names (snake_case). If we detect one, we call HasGodotClassSignal
+ // again, but this time with the respective proxy name (PascalCase). It's the job of
+ // user derived classes to override the method and check for those. Our C# source
+ // generators take care of generating those override methods.
+ output << INDENT2 "if (signal == SignalName." << isignal.proxy_name
+ << ")\n" INDENT2 "{\n"
+ << INDENT3 "if (" CS_METHOD_HAS_GODOT_CLASS_SIGNAL "("
+ << CS_STATIC_FIELD_SIGNAL_PROXY_NAME_PREFIX << isignal.name
+ << ".NativeValue.DangerousSelfRef))\n" INDENT3 "{\n"
+ << INDENT4 "return true;\n"
+ << INDENT3 "}\n" INDENT2 "}\n";
+ }
+
+ if (is_derived_type) {
+ output << INDENT2 "return base." CS_METHOD_HAS_GODOT_CLASS_SIGNAL "(signal);\n";
+ } else {
+ output << INDENT2 "return false;\n";
+ }
+
+ output << INDENT1 "}\n";
}
//Generate StringName for all class members
@@ -2775,6 +2847,18 @@ bool BindingsGenerator::_arg_default_value_is_assignable_to_type(const Variant &
return false;
}
+bool method_has_ptr_parameter(MethodInfo p_method_info) {
+ if (p_method_info.return_val.type == Variant::INT && p_method_info.return_val.hint == PROPERTY_HINT_INT_IS_POINTER) {
+ return true;
+ }
+ for (PropertyInfo arg : p_method_info.arguments) {
+ if (arg.type == Variant::INT && arg.hint == PROPERTY_HINT_INT_IS_POINTER) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool BindingsGenerator::_populate_object_type_interfaces() {
obj_types.clear();
@@ -2918,6 +3002,11 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
continue;
}
+ if (method_has_ptr_parameter(method_info)) {
+ // Pointers are not supported.
+ continue;
+ }
+
MethodInterface imethod;
imethod.name = method_info.name;
imethod.cname = cname;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
index acdae83d2e..81659f74de 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
@@ -10,7 +10,7 @@ namespace Godot
/// collection of types that implement scripts; otherwise, retrieving the types requires lookup.
/// </summary>
[AttributeUsage(AttributeTargets.Assembly)]
- public class AssemblyHasScriptsAttribute : Attribute
+ public sealed class AssemblyHasScriptsAttribute : Attribute
{
/// <summary>
/// If the Godot scripts contained in the assembly require lookup
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs
index 23088378d1..0070223c95 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs
@@ -7,5 +7,5 @@ namespace Godot
/// that can be marshaled from/to a <see cref="Variant"/>.
/// </summary>
[AttributeUsage(AttributeTargets.GenericParameter)]
- public class MustBeVariantAttribute : Attribute { }
+ public sealed class MustBeVariantAttribute : Attribute { }
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs
index afee926464..02a38d310e 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs
@@ -9,7 +9,7 @@ namespace Godot
/// By default, methods are not exposed to networking (and RPCs).
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
- public class RPCAttribute : Attribute
+ public sealed class RPCAttribute : Attribute
{
/// <summary>
/// RPC mode for the annotated method.
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
index f05bcdac38..d363e14c5d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
@@ -6,7 +6,7 @@ namespace Godot
/// An attribute that contains the path to the object's script.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
- public class ScriptPathAttribute : Attribute
+ public sealed class ScriptPathAttribute : Attribute
{
/// <summary>
/// File path to the script.
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs
index 38e68a89d5..0a08bb5df8 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs
@@ -3,5 +3,5 @@ using System;
namespace Godot
{
[AttributeUsage(AttributeTargets.Delegate)]
- public class SignalAttribute : Attribute { }
+ public sealed class SignalAttribute : Attribute { }
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs
index d2344389f4..4c56201727 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs
@@ -3,5 +3,5 @@ using System;
namespace Godot
{
[AttributeUsage(AttributeTargets.Class)]
- public class ToolAttribute : Attribute { }
+ public sealed class ToolAttribute : Attribute { }
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
index b57317e1d0..2b139a78bd 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
@@ -120,6 +120,24 @@ namespace Godot
}
/// <summary>
+ /// Assuming that the matrix is the combination of a rotation and scaling,
+ /// return the absolute value of scaling factors along each axis.
+ /// </summary>
+ public readonly Vector3 Scale
+ {
+ get
+ {
+ real_t detSign = Mathf.Sign(Determinant());
+ return detSign * new Vector3
+ (
+ Column0.Length(),
+ Column1.Length(),
+ Column2.Length()
+ );
+ }
+ }
+
+ /// <summary>
/// Access whole columns in the form of <see cref="Vector3"/>.
/// </summary>
/// <param name="column">Which column vector.</param>
@@ -542,21 +560,6 @@ namespace Godot
}
/// <summary>
- /// Assuming that the matrix is the combination of a rotation and scaling,
- /// return the absolute value of scaling factors along each axis.
- /// </summary>
- public readonly Vector3 GetScale()
- {
- real_t detSign = Mathf.Sign(Determinant());
- return detSign * new Vector3
- (
- Column0.Length(),
- Column1.Length(),
- Column2.Length()
- );
- }
-
- /// <summary>
/// Returns the inverse of the matrix.
/// </summary>
/// <returns>The inverse matrix.</returns>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs
index 354212da1b..ccb21502c0 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs
@@ -84,12 +84,29 @@ namespace Godot.Bridge
if (godotObject == null)
throw new InvalidOperationException();
+ // Properties
if (godotObject.GetGodotClassPropertyValue(CustomUnsafe.AsRef(name), out godot_variant outRetValue))
{
*outRet = outRetValue;
return godot_bool.True;
}
+ // Signals
+ if (godotObject.HasGodotClassSignal(CustomUnsafe.AsRef(name)))
+ {
+ godot_signal signal = new godot_signal(*name, godotObject.GetInstanceId());
+ *outRet = VariantUtils.CreateFromSignalTakingOwnershipOfDisposableValue(signal);
+ return godot_bool.True;
+ }
+
+ // Methods
+ if (godotObject.HasGodotClassMethod(CustomUnsafe.AsRef(name)))
+ {
+ godot_callable method = new godot_callable(*name, godotObject.GetInstanceId());
+ *outRet = VariantUtils.CreateFromCallableTakingOwnershipOfDisposableValue(method);
+ return godot_bool.True;
+ }
+
var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name)));
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs
index ff385da1c9..3005582bea 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs
@@ -45,7 +45,7 @@ public readonly partial struct Callable
}
/// <inheritdoc cref="From(Action)"/>
- public static unsafe Callable From<T0>(
+ public static unsafe Callable From<[MustBeVariant] T0>(
Action<T0> action
)
{
@@ -64,7 +64,7 @@ public readonly partial struct Callable
}
/// <inheritdoc cref="From(Action)"/>
- public static unsafe Callable From<T0, T1>(
+ public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1>(
Action<T0, T1> action
)
{
@@ -84,7 +84,7 @@ public readonly partial struct Callable
}
/// <inheritdoc cref="From(Action)"/>
- public static unsafe Callable From<T0, T1, T2>(
+ public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2>(
Action<T0, T1, T2> action
)
{
@@ -105,7 +105,7 @@ public readonly partial struct Callable
}
/// <inheritdoc cref="From(Action)"/>
- public static unsafe Callable From<T0, T1, T2, T3>(
+ public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3>(
Action<T0, T1, T2, T3> action
)
{
@@ -127,7 +127,7 @@ public readonly partial struct Callable
}
/// <inheritdoc cref="From(Action)"/>
- public static unsafe Callable From<T0, T1, T2, T3, T4>(
+ public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4>(
Action<T0, T1, T2, T3, T4> action
)
{
@@ -150,7 +150,7 @@ public readonly partial struct Callable
}
/// <inheritdoc cref="From(Action)"/>
- public static unsafe Callable From<T0, T1, T2, T3, T4, T5>(
+ public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5>(
Action<T0, T1, T2, T3, T4, T5> action
)
{
@@ -174,7 +174,7 @@ public readonly partial struct Callable
}
/// <inheritdoc cref="From(Action)"/>
- public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6>(
+ public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6>(
Action<T0, T1, T2, T3, T4, T5, T6> action
)
{
@@ -199,7 +199,7 @@ public readonly partial struct Callable
}
/// <inheritdoc cref="From(Action)"/>
- public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7>(
+ public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7>(
Action<T0, T1, T2, T3, T4, T5, T6, T7> action
)
{
@@ -225,7 +225,7 @@ public readonly partial struct Callable
}
/// <inheritdoc cref="From(Action)"/>
- public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7, T8>(
+ public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7, [MustBeVariant] T8>(
Action<T0, T1, T2, T3, T4, T5, T6, T7, T8> action
)
{
@@ -255,7 +255,7 @@ public readonly partial struct Callable
/// Constructs a new <see cref="Callable"/> for the given <paramref name="func"/>.
/// </summary>
/// <param name="func">Action method that will be called.</param>
- public static unsafe Callable From<TResult>(
+ public static unsafe Callable From<[MustBeVariant] TResult>(
Func<TResult> func
)
{
@@ -272,7 +272,7 @@ public readonly partial struct Callable
}
/// <inheritdoc cref="From{TResult}(Func{TResult})"/>
- public static unsafe Callable From<T0, TResult>(
+ public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] TResult>(
Func<T0, TResult> func
)
{
@@ -291,7 +291,7 @@ public readonly partial struct Callable
}
/// <inheritdoc cref="From{TResult}(Func{TResult})"/>
- public static unsafe Callable From<T0, T1, TResult>(
+ public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] TResult>(
Func<T0, T1, TResult> func
)
{
@@ -311,7 +311,7 @@ public readonly partial struct Callable
}
/// <inheritdoc cref="From{TResult}(Func{TResult})"/>
- public static unsafe Callable From<T0, T1, T2, TResult>(
+ public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] TResult>(
Func<T0, T1, T2, TResult> func
)
{
@@ -332,7 +332,7 @@ public readonly partial struct Callable
}
/// <inheritdoc cref="From{TResult}(Func{TResult})"/>
- public static unsafe Callable From<T0, T1, T2, T3, TResult>(
+ public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] TResult>(
Func<T0, T1, T2, T3, TResult> func
)
{
@@ -354,7 +354,7 @@ public readonly partial struct Callable
}
/// <inheritdoc cref="From{TResult}(Func{TResult})"/>
- public static unsafe Callable From<T0, T1, T2, T3, T4, TResult>(
+ public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] TResult>(
Func<T0, T1, T2, T3, T4, TResult> func
)
{
@@ -377,7 +377,7 @@ public readonly partial struct Callable
}
/// <inheritdoc cref="From{TResult}(Func{TResult})"/>
- public static unsafe Callable From<T0, T1, T2, T3, T4, T5, TResult>(
+ public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] TResult>(
Func<T0, T1, T2, T3, T4, T5, TResult> func
)
{
@@ -401,7 +401,7 @@ public readonly partial struct Callable
}
/// <inheritdoc cref="From{TResult}(Func{TResult})"/>
- public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, TResult>(
+ public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] TResult>(
Func<T0, T1, T2, T3, T4, T5, T6, TResult> func
)
{
@@ -426,7 +426,7 @@ public readonly partial struct Callable
}
/// <inheritdoc cref="From{TResult}(Func{TResult})"/>
- public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7, TResult>(
+ public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7, [MustBeVariant] TResult>(
Func<T0, T1, T2, T3, T4, T5, T6, T7, TResult> func
)
{
@@ -452,7 +452,7 @@ public readonly partial struct Callable
}
/// <inheritdoc cref="From{TResult}(Func{TResult})"/>
- public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7, T8, TResult>(
+ public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7, [MustBeVariant] T8, [MustBeVariant] TResult>(
Func<T0, T1, T2, T3, T4, T5, T6, T7, T8, TResult> func
)
{
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
index 2effdecf40..6a7863112a 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
@@ -797,7 +797,7 @@ namespace Godot
{
if (saturation == 0)
{
- // Achromatic (grey)
+ // Achromatic (gray)
return new Color(value, value, value, alpha);
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
index d94fbff331..0f97149130 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
@@ -399,7 +399,7 @@ namespace Godot
{
ulong objectId = reader.ReadUInt64();
// ReSharper disable once RedundantNameQualifier
- Godot.Object godotObject = GD.InstanceFromId(objectId);
+ Godot.Object godotObject = Godot.Object.InstanceFromId(objectId);
if (godotObject == null)
return false;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
index b5a8742d3d..505763e6a2 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
@@ -68,7 +68,15 @@ namespace Godot.Collections
}
/// <summary>
- /// Duplicates this <see cref="Dictionary"/>.
+ /// Returns a copy of the <see cref="Dictionary"/>.
+ /// If <paramref name="deep"/> is <see langword="true"/>, a deep copy is performed:
+ /// all nested arrays and dictionaries are duplicated and will not be shared with
+ /// the original dictionary. If <see langword="false"/>, a shallow copy is made and
+ /// references to the original nested arrays and dictionaries are kept, so that
+ /// modifying a sub-array or dictionary in the copy will also impact those
+ /// referenced in the source dictionary. Note that any <see cref="Object"/> derived
+ /// elements will be shallow copied regardless of the <paramref name="deep"/>
+ /// setting.
/// </summary>
/// <param name="deep">If <see langword="true"/>, performs a deep copy.</param>
/// <returns>A new Godot Dictionary.</returns>
@@ -80,6 +88,39 @@ namespace Godot.Collections
return CreateTakingOwnershipOfDisposableValue(newDictionary);
}
+ /// <summary>
+ /// Adds entries from <paramref name="dictionary"/> to this dictionary.
+ /// By default, duplicate keys are not copied over, unless <paramref name="overwrite"/>
+ /// is <see langword="true"/>.
+ /// </summary>
+ /// <param name="dictionary">Dictionary to copy entries from.</param>
+ /// <param name="overwrite">If duplicate keys should be copied over as well.</param>
+ public void Merge(Dictionary dictionary, bool overwrite = false)
+ {
+ var self = (godot_dictionary)NativeValue;
+ var other = (godot_dictionary)dictionary.NativeValue;
+ NativeFuncs.godotsharp_dictionary_merge(ref self, in other, overwrite.ToGodotBool());
+ }
+
+ /// <summary>
+ /// Compares this <see cref="Dictionary"/> against the <paramref name="other"/>
+ /// <see cref="Dictionary"/> recursively. Returns <see langword="true"/> if the
+ /// two dictionaries contain the same keys and values. The order of the entries
+ /// does not matter.
+ /// otherwise.
+ /// </summary>
+ /// <param name="other">The other dictionary to compare against.</param>
+ /// <returns>
+ /// <see langword="true"/> if the dictionaries contain the same keys and values,
+ /// <see langword="false"/> otherwise.
+ /// </returns>
+ public bool RecursiveEqual(Dictionary other)
+ {
+ var self = (godot_dictionary)NativeValue;
+ var otherVariant = (godot_dictionary)other.NativeValue;
+ return NativeFuncs.godotsharp_dictionary_recursive_equal(ref self, otherVariant).ToBool();
+ }
+
// IDictionary
/// <summary>
@@ -134,6 +175,9 @@ namespace Godot.Collections
/// <summary>
/// Returns the value at the given <paramref name="key"/>.
/// </summary>
+ /// <exception cref="KeyNotFoundException">
+ /// An entry for <paramref name="key"/> does not exist in the dictionary.
+ /// </exception>
/// <value>The value at the given <paramref name="key"/>.</value>
public Variant this[Variant key]
{
@@ -163,6 +207,9 @@ namespace Godot.Collections
/// Adds an value <paramref name="value"/> at key <paramref name="key"/>
/// to this <see cref="Dictionary"/>.
/// </summary>
+ /// <exception cref="ArgumentException">
+ /// An entry for <paramref name="key"/> already exists in the dictionary.
+ /// </exception>
/// <param name="key">The key at which to add the value.</param>
/// <param name="value">The value to add.</param>
public void Add(Variant key, Variant value)
@@ -181,7 +228,7 @@ namespace Godot.Collections
=> Add(item.Key, item.Value);
/// <summary>
- /// Erases all items from this <see cref="Dictionary"/>.
+ /// Clears the dictionary, removing all entries from it.
/// </summary>
public void Clear()
{
@@ -200,7 +247,7 @@ namespace Godot.Collections
return NativeFuncs.godotsharp_dictionary_contains_key(ref self, (godot_variant)key.NativeVar).ToBool();
}
- public bool Contains(KeyValuePair<Variant, Variant> item)
+ bool ICollection<KeyValuePair<Variant, Variant>>.Contains(KeyValuePair<Variant, Variant> item)
{
godot_variant variantKey = (godot_variant)item.Key.NativeVar;
var self = (godot_dictionary)NativeValue;
@@ -227,7 +274,7 @@ namespace Godot.Collections
return NativeFuncs.godotsharp_dictionary_remove_key(ref self, (godot_variant)key.NativeVar).ToBool();
}
- public bool Remove(KeyValuePair<Variant, Variant> item)
+ bool ICollection<KeyValuePair<Variant, Variant>>.Remove(KeyValuePair<Variant, Variant> item)
{
godot_variant variantKey = (godot_variant)item.Key.NativeVar;
var self = (godot_dictionary)NativeValue;
@@ -266,6 +313,14 @@ namespace Godot.Collections
bool ICollection<KeyValuePair<Variant, Variant>>.IsReadOnly => false;
+ /// <summary>
+ /// Gets the value for the given <paramref name="key"/> in the dictionary.
+ /// Returns <see langword="true"/> if an entry for the given key exists in
+ /// the dictionary; otherwise, returns <see langword="false"/>.
+ /// </summary>
+ /// <param name="key">The key of the element to get.</param>
+ /// <param name="value">The value at the given <paramref name="key"/>.</param>
+ /// <returns>If an entry was found for the given <paramref name="key"/>.</returns>
public bool TryGetValue(Variant key, out Variant value)
{
var self = (godot_dictionary)NativeValue;
@@ -283,7 +338,7 @@ namespace Godot.Collections
/// </summary>
/// <param name="array">The array to copy to.</param>
/// <param name="arrayIndex">The index to start at.</param>
- public void CopyTo(KeyValuePair<Variant, Variant>[] array, int arrayIndex)
+ void ICollection<KeyValuePair<Variant, Variant>>.CopyTo(KeyValuePair<Variant, Variant>[] array, int arrayIndex)
{
if (array == null)
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
@@ -433,15 +488,49 @@ namespace Godot.Collections
}
/// <summary>
- /// Duplicates this <see cref="Dictionary{TKey, TValue}"/>.
+ /// Returns a copy of the <see cref="Dictionary{TKey, TValue}"/>.
+ /// If <paramref name="deep"/> is <see langword="true"/>, a deep copy is performed:
+ /// all nested arrays and dictionaries are duplicated and will not be shared with
+ /// the original dictionary. If <see langword="false"/>, a shallow copy is made and
+ /// references to the original nested arrays and dictionaries are kept, so that
+ /// modifying a sub-array or dictionary in the copy will also impact those
+ /// referenced in the source dictionary. Note that any <see cref="Object"/> derived
+ /// elements will be shallow copied regardless of the <paramref name="deep"/>
+ /// setting.
/// </summary>
- /// <param name="deep">If <see langword="true"/>, performs a deep copy.</param>
- /// <returns>A new Godot Dictionary.</returns>
public Dictionary<TKey, TValue> Duplicate(bool deep = false)
{
return new Dictionary<TKey, TValue>(_underlyingDict.Duplicate(deep));
}
+ /// <summary>
+ /// Adds entries from <paramref name="dictionary"/> to this dictionary.
+ /// By default, duplicate keys are not copied over, unless <paramref name="overwrite"/>
+ /// is <see langword="true"/>.
+ /// </summary>
+ /// <param name="dictionary">Dictionary to copy entries from.</param>
+ /// <param name="overwrite">If duplicate keys should be copied over as well.</param>
+ public void Merge(Dictionary<TKey, TValue> dictionary, bool overwrite = false)
+ {
+ _underlyingDict.Merge(dictionary._underlyingDict, overwrite);
+ }
+
+ /// <summary>
+ /// Compares this <see cref="Dictionary{TKey, TValue}"/> against the <paramref name="other"/>
+ /// <see cref="Dictionary{TKey, TValue}"/> recursively. Returns <see langword="true"/> if the
+ /// two dictionaries contain the same keys and values. The order of the entries does not matter.
+ /// otherwise.
+ /// </summary>
+ /// <param name="other">The other dictionary to compare against.</param>
+ /// <returns>
+ /// <see langword="true"/> if the dictionaries contain the same keys and values,
+ /// <see langword="false"/> otherwise.
+ /// </returns>
+ public bool RecursiveEqual(Dictionary<TKey, TValue> other)
+ {
+ return _underlyingDict.RecursiveEqual(other._underlyingDict);
+ }
+
// IDictionary<TKey, TValue>
/// <summary>
@@ -565,11 +654,13 @@ namespace Godot.Collections
}
/// <summary>
- /// Gets the object at the given <paramref name="key"/>.
+ /// Gets the value for the given <paramref name="key"/> in the dictionary.
+ /// Returns <see langword="true"/> if an entry for the given key exists in
+ /// the dictionary; otherwise, returns <see langword="false"/>.
/// </summary>
/// <param name="key">The key of the element to get.</param>
/// <param name="value">The value at the given <paramref name="key"/>.</param>
- /// <returns>If an object was found for the given <paramref name="key"/>.</returns>
+ /// <returns>If an entry was found for the given <paramref name="key"/>.</returns>
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
{
using var variantKey = VariantUtils.CreateFrom(key);
@@ -598,7 +689,7 @@ namespace Godot.Collections
=> Add(item.Key, item.Value);
/// <summary>
- /// Erases all the items from this <see cref="Dictionary{TKey, TValue}"/>.
+ /// Clears the dictionary, removing all entries from it.
/// </summary>
public void Clear() => _underlyingDict.Clear();
@@ -625,7 +716,7 @@ namespace Godot.Collections
/// </summary>
/// <param name="array">The array to copy to.</param>
/// <param name="arrayIndex">The index to start at.</param>
- public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
+ void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
if (array == null)
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
index e6cb4171a7..e7370b2b05 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
@@ -9,7 +9,10 @@ namespace Godot
internal static GodotTaskScheduler DefaultGodotTaskScheduler;
internal static void InitializeDefaultGodotTaskScheduler()
- => DefaultGodotTaskScheduler = new GodotTaskScheduler();
+ {
+ DefaultGodotTaskScheduler?.Dispose();
+ DefaultGodotTaskScheduler = new GodotTaskScheduler();
+ }
public static GodotSynchronizationContext SynchronizationContext => DefaultGodotTaskScheduler.Context;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs
index 4094ceeb22..a9f3f02856 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs
@@ -6,8 +6,46 @@ namespace Godot
public partial class Object
{
/// <summary>
- /// Returns whether <paramref name="instance"/> is a valid object
- /// (e.g. has not been deleted from memory).
+ /// Returns the <see cref="Object"/> that corresponds to <paramref name="instanceId"/>.
+ /// All Objects have a unique instance ID. See also <see cref="GetInstanceId"/>.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// public partial class MyNode : Node
+ /// {
+ /// public string Foo { get; set; } = "bar";
+ ///
+ /// public override void _Ready()
+ /// {
+ /// ulong id = GetInstanceId();
+ /// var inst = (MyNode)InstanceFromId(Id);
+ /// GD.Print(inst.Foo); // Prints bar
+ /// }
+ /// }
+ /// </code>
+ /// </example>
+ /// <param name="instanceId">Instance ID of the Object to retrieve.</param>
+ /// <returns>The <see cref="Object"/> instance.</returns>
+ public static Object InstanceFromId(ulong instanceId)
+ {
+ return InteropUtils.UnmanagedGetManaged(NativeFuncs.godotsharp_instance_from_id(instanceId));
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if the <see cref="Object"/> that corresponds
+ /// to <paramref name="id"/> is a valid object (e.g. has not been deleted from
+ /// memory). All Objects have a unique instance ID.
+ /// </summary>
+ /// <param name="id">The Object ID to check.</param>
+ /// <returns>If the instance with the given ID is a valid object.</returns>
+ public static bool IsInstanceIdValid(ulong id)
+ {
+ return IsInstanceValid(InstanceFromId(id));
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if <paramref name="instance"/> is a
+ /// valid <see cref="Object"/> (e.g. has not been deleted from memory).
/// </summary>
/// <param name="instance">The instance to check.</param>
/// <returns>If the instance is a valid object.</returns>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
index e4b79e7ec4..9425b7424c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Text;
using Godot.NativeInterop;
namespace Godot
@@ -10,36 +11,45 @@ namespace Godot
public static partial class GD
{
/// <summary>
- /// Decodes a byte array back to a <c>Variant</c> value.
- /// If <paramref name="allowObjects"/> is <see langword="true"/> decoding objects is allowed.
- ///
- /// WARNING: Deserialized object can contain code which gets executed.
- /// Do not set <paramref name="allowObjects"/> to <see langword="true"/>
- /// if the serialized object comes from untrusted sources to avoid
- /// potential security threats (remote code execution).
+ /// Decodes a byte array back to a <see cref="Variant"/> value, without decoding objects.
+ /// Note: If you need object deserialization, see <see cref="BytesToVarWithObjects"/>.
/// </summary>
- /// <param name="bytes">Byte array that will be decoded to a <c>Variant</c>.</param>
- /// <param name="allowObjects">If objects should be decoded.</param>
- /// <returns>The decoded <c>Variant</c>.</returns>
- public static Variant BytesToVar(Span<byte> bytes, bool allowObjects = false)
+ /// <param name="bytes">Byte array that will be decoded to a <see cref="Variant"/>.</param>
+ /// <returns>The decoded <see cref="Variant"/>.</returns>
+ public static Variant BytesToVar(Span<byte> bytes)
{
using var varBytes = Marshaling.ConvertSystemArrayToNativePackedByteArray(bytes);
- NativeFuncs.godotsharp_bytes_to_var(varBytes, allowObjects.ToGodotBool(), out godot_variant ret);
+ NativeFuncs.godotsharp_bytes_to_var(varBytes, godot_bool.False, out godot_variant ret);
return Variant.CreateTakingOwnershipOfDisposableValue(ret);
}
/// <summary>
- /// Converts from a <c>Variant</c> type to another in the best way possible.
+ /// Decodes a byte array back to a <see cref="Variant"/> value. Decoding objects is allowed.
+ /// Warning: Deserialized object can contain code which gets executed. Do not use this
+ /// option if the serialized object comes from untrusted sources to avoid potential security
+ /// threats (remote code execution).
+ /// </summary>
+ /// <param name="bytes">Byte array that will be decoded to a <see cref="Variant"/>.</param>
+ /// <returns>The decoded <see cref="Variant"/>.</returns>
+ public static Variant BytesToVarWithObjects(Span<byte> bytes)
+ {
+ using var varBytes = Marshaling.ConvertSystemArrayToNativePackedByteArray(bytes);
+ NativeFuncs.godotsharp_bytes_to_var(varBytes, godot_bool.True, out godot_variant ret);
+ return Variant.CreateTakingOwnershipOfDisposableValue(ret);
+ }
+
+ /// <summary>
+ /// Converts <paramref name="what"/> to <paramref name="type"/> in the best way possible.
/// The <paramref name="type"/> parameter uses the <see cref="Variant.Type"/> values.
/// </summary>
/// <example>
/// <code>
- /// var a = new Vector2(1, 0);
- /// // Prints 1
- /// GD.Print(a.Length());
- /// var b = GD.Convert(a, Variant.Type.String)
- /// // Prints 6 as "(1, 0)" is 6 characters
- /// GD.Print(b.Length);
+ /// Variant a = new Godot.Collections.Array { 4, 2.5, 1.2 };
+ /// GD.Print(a.VariantType == Variant.Type.Array); // Prints true
+ ///
+ /// var b = GD.Convert(a, Variant.Type.PackedByteArray);
+ /// GD.Print(b); // Prints [4, 2, 1]
+ /// GD.Print(b.VariantType == Variant.Type.Array); // Prints false
/// </code>
/// </example>
/// <returns>The <c>Variant</c> converted to the given <paramref name="type"/>.</returns>
@@ -50,28 +60,7 @@ namespace Godot
}
/// <summary>
- /// Converts from decibels to linear energy (audio).
- /// </summary>
- /// <seealso cref="LinearToDb(real_t)"/>
- /// <param name="db">Decibels to convert.</param>
- /// <returns>Audio volume as linear energy.</returns>
- public static real_t DbToLinear(real_t db)
- {
- return (real_t)Math.Exp(db * 0.11512925464970228420089957273422);
- }
-
- private static string[] GetPrintParams(object[] parameters)
- {
- if (parameters == null)
- {
- return new[] { "null" };
- }
-
- return Array.ConvertAll(parameters, x => x?.ToString() ?? "null");
- }
-
- /// <summary>
- /// Returns the integer hash of the variable passed.
+ /// Returns the integer hash of the passed <paramref name="var"/>.
/// </summary>
/// <example>
/// <code>
@@ -86,52 +75,6 @@ namespace Godot
}
/// <summary>
- /// Returns the <see cref="Object"/> that corresponds to <paramref name="instanceId"/>.
- /// All Objects have a unique instance ID.
- /// </summary>
- /// <example>
- /// <code>
- /// public class MyNode : Node
- /// {
- /// public string foo = "bar";
- ///
- /// public override void _Ready()
- /// {
- /// ulong id = GetInstanceId();
- /// var inst = (MyNode)GD.InstanceFromId(Id);
- /// GD.Print(inst.foo); // Prints bar
- /// }
- /// }
- /// </code>
- /// </example>
- /// <param name="instanceId">Instance ID of the Object to retrieve.</param>
- /// <returns>The <see cref="Object"/> instance.</returns>
- public static Object InstanceFromId(ulong instanceId)
- {
- return InteropUtils.UnmanagedGetManaged(NativeFuncs.godotsharp_instance_from_id(instanceId));
- }
-
- /// <summary>
- /// Converts from linear energy to decibels (audio).
- /// This can be used to implement volume sliders that behave as expected (since volume isn't linear).
- /// </summary>
- /// <seealso cref="DbToLinear(real_t)"/>
- /// <example>
- /// <code>
- /// // "slider" refers to a node that inherits Range such as HSlider or VSlider.
- /// // Its range must be configured to go from 0 to 1.
- /// // Change the bus name if you'd like to change the volume of a specific bus only.
- /// AudioServer.SetBusVolumeDb(AudioServer.GetBusIndex("Master"), GD.LinearToDb(slider.value));
- /// </code>
- /// </example>
- /// <param name="linear">The linear energy to convert.</param>
- /// <returns>Audio as decibels.</returns>
- public static real_t LinearToDb(real_t linear)
- {
- return (real_t)(Math.Log(linear) * 8.6858896380650365530225783783321);
- }
-
- /// <summary>
/// Loads a resource from the filesystem located at <paramref name="path"/>.
/// The resource is loaded on the method call (unless it's referenced already
/// elsewhere, e.g. in another script or in the scene), which might cause slight delay,
@@ -185,57 +128,96 @@ namespace Godot
return ResourceLoader.Load<T>(path);
}
- /// <summary>
- /// Pushes an error message to Godot's built-in debugger and to the OS terminal.
- ///
- /// Note: Errors printed this way will not pause project execution.
- /// </summary>
- /// <example>
- /// <code>
- /// GD.PushError("test_error"); // Prints "test error" to debugger and terminal as error call
- /// </code>
- /// </example>
- /// <param name="message">Error message.</param>
- public static void PushError(string message)
+ private static string AppendPrintParams(object[] parameters)
{
- using var godotStr = Marshaling.ConvertStringToNative(message);
- NativeFuncs.godotsharp_pusherror(godotStr);
+ if (parameters == null)
+ {
+ return "null";
+ }
+
+ var sb = new StringBuilder();
+ for (int i = 0; i < parameters.Length; i++)
+ {
+ sb.Append(parameters[i]?.ToString() ?? "null");
+ }
+ return sb.ToString();
+ }
+
+ private static string AppendPrintParams(char separator, object[] parameters)
+ {
+ if (parameters == null)
+ {
+ return "null";
+ }
+
+ var sb = new StringBuilder();
+ for (int i = 0; i < parameters.Length; i++)
+ {
+ if (i != 0)
+ sb.Append(separator);
+ sb.Append(parameters[i]?.ToString() ?? "null");
+ }
+ return sb.ToString();
}
/// <summary>
- /// Pushes a warning message to Godot's built-in debugger and to the OS terminal.
+ /// Prints a message to the console.
+ ///
+ /// Note: Consider using <see cref="PushError(string)"/> and <see cref="PushWarning(string)"/>
+ /// to print error and warning messages instead of <see cref="Print(string)"/>.
+ /// This distinguishes them from print messages used for debugging purposes,
+ /// while also displaying a stack trace when an error or warning is printed.
/// </summary>
- /// <example>
- /// GD.PushWarning("test warning"); // Prints "test warning" to debugger and terminal as warning call
- /// </example>
- /// <param name="message">Warning message.</param>
- public static void PushWarning(string message)
+ /// <param name="what">Message that will be printed.</param>
+ public static void Print(string what)
{
- using var godotStr = Marshaling.ConvertStringToNative(message);
- NativeFuncs.godotsharp_pushwarning(godotStr);
+ using var godotStr = Marshaling.ConvertStringToNative(what);
+ NativeFuncs.godotsharp_print(godotStr);
}
/// <summary>
/// Converts one or more arguments of any type to string in the best way possible
/// and prints them to the console.
///
- /// Note: Consider using <see cref="PushError(string)"/> and <see cref="PushWarning(string)"/>
+ /// Note: Consider using <see cref="PushError(object[])"/> and <see cref="PushWarning(object[])"/>
/// to print error and warning messages instead of <see cref="Print(object[])"/>.
/// This distinguishes them from print messages used for debugging purposes,
/// while also displaying a stack trace when an error or warning is printed.
/// </summary>
/// <example>
/// <code>
- /// var a = new int[] { 1, 2, 3 };
+ /// var a = new Godot.Collections.Array { 1, 2, 3 };
/// GD.Print("a", "b", a); // Prints ab[1, 2, 3]
/// </code>
/// </example>
/// <param name="what">Arguments that will be printed.</param>
public static void Print(params object[] what)
{
- string str = string.Concat(GetPrintParams(what));
- using var godotStr = Marshaling.ConvertStringToNative(str);
- NativeFuncs.godotsharp_print(godotStr);
+ Print(AppendPrintParams(what));
+ }
+
+ /// <summary>
+ /// Prints a message to the console.
+ /// The following BBCode tags are supported: b, i, u, s, indent, code, url, center,
+ /// right, color, bgcolor, fgcolor.
+ /// Color tags only support named colors such as <c>red</c>, not hexadecimal color codes.
+ /// Unsupported tags will be left as-is in standard output.
+ /// When printing to standard output, the supported subset of BBCode is converted to
+ /// ANSI escape codes for the terminal emulator to display. Displaying ANSI escape codes
+ /// is currently only supported on Linux and macOS. Support for ANSI escape codes may vary
+ /// across terminal emulators, especially for italic and strikethrough.
+ ///
+ /// Note: Consider using <see cref="PushError(string)"/> and <see cref="PushWarning(string)"/>
+ /// to print error and warning messages instead of <see cref="Print(string)"/> or
+ /// <see cref="PrintRich(string)"/>.
+ /// This distinguishes them from print messages used for debugging purposes,
+ /// while also displaying a stack trace when an error or warning is printed.
+ /// </summary>
+ /// <param name="what">Message that will be printed.</param>
+ public static void PrintRich(string what)
+ {
+ using var godotStr = Marshaling.ConvertStringToNative(what);
+ NativeFuncs.godotsharp_print_rich(godotStr);
}
/// <summary>
@@ -250,7 +232,7 @@ namespace Godot
/// is currently only supported on Linux and macOS. Support for ANSI escape codes may vary
/// across terminal emulators, especially for italic and strikethrough.
///
- /// Note: Consider using <see cref="PushError(string)"/> and <see cref="PushWarning(string)"/>
+ /// Note: Consider using <see cref="PushError(object[])"/> and <see cref="PushWarning(object[])"/>
/// to print error and warning messages instead of <see cref="Print(object[])"/> or
/// <see cref="PrintRich(object[])"/>.
/// This distinguishes them from print messages used for debugging purposes,
@@ -258,23 +240,23 @@ namespace Godot
/// </summary>
/// <example>
/// <code>
- /// GD.PrintRich("[b]Hello world![/b]"); // Prints out "Hello world!" in bold.
+ /// GD.PrintRich("[code][b]Hello world![/b][/code]"); // Prints out: [b]Hello world![/b]
/// </code>
/// </example>
/// <param name="what">Arguments that will be printed.</param>
public static void PrintRich(params object[] what)
{
- string str = string.Concat(GetPrintParams(what));
- using var godotStr = Marshaling.ConvertStringToNative(str);
- NativeFuncs.godotsharp_print_rich(godotStr);
+ PrintRich(AppendPrintParams(what));
}
/// <summary>
- /// Prints the current stack trace information to the console.
+ /// Prints a message to standard error line.
/// </summary>
- public static void PrintStack()
+ /// <param name="what">Message that will be printed.</param>
+ public static void PrintErr(string what)
{
- Print(System.Environment.StackTrace);
+ using var godotStr = Marshaling.ConvertStringToNative(what);
+ NativeFuncs.godotsharp_printerr(godotStr);
}
/// <summary>
@@ -288,31 +270,36 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void PrintErr(params object[] what)
{
- string str = string.Concat(GetPrintParams(what));
- using var godotStr = Marshaling.ConvertStringToNative(str);
- NativeFuncs.godotsharp_printerr(godotStr);
+ PrintErr(AppendPrintParams(what));
}
/// <summary>
- /// Prints one or more arguments to strings in the best way possible to console.
- /// No newline is added at the end.
- ///
- /// Note: Due to limitations with Godot's built-in console, this only prints to the terminal.
- /// If you need to print in the editor, use another method, such as <see cref="Print(object[])"/>.
+ /// Prints a message to the OS terminal.
+ /// Unlike <see cref="Print(string)"/>, no newline is added at the end.
+ /// </summary>
+ /// <param name="what">Message that will be printed.</param>
+ public static void PrintRaw(string what)
+ {
+ using var godotStr = Marshaling.ConvertStringToNative(what);
+ NativeFuncs.godotsharp_printraw(godotStr);
+ }
+
+ /// <summary>
+ /// Prints one or more arguments to strings in the best way possible to the OS terminal.
+ /// Unlike <see cref="Print(object[])"/>, no newline is added at the end.
/// </summary>
/// <example>
/// <code>
/// GD.PrintRaw("A");
/// GD.PrintRaw("B");
- /// // Prints AB
+ /// GD.PrintRaw("C");
+ /// // Prints ABC to terminal
/// </code>
/// </example>
/// <param name="what">Arguments that will be printed.</param>
public static void PrintRaw(params object[] what)
{
- string str = string.Concat(GetPrintParams(what));
- using var godotStr = Marshaling.ConvertStringToNative(str);
- NativeFuncs.godotsharp_printraw(godotStr);
+ PrintRaw(AppendPrintParams(what));
}
/// <summary>
@@ -326,8 +313,8 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void PrintS(params object[] what)
{
- string str = string.Join(' ', GetPrintParams(what));
- using var godotStr = Marshaling.ConvertStringToNative(str);
+ string message = AppendPrintParams(' ', what);
+ using var godotStr = Marshaling.ConvertStringToNative(message);
NativeFuncs.godotsharp_prints(godotStr);
}
@@ -342,12 +329,74 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void PrintT(params object[] what)
{
- string str = string.Join('\t', GetPrintParams(what));
- using var godotStr = Marshaling.ConvertStringToNative(str);
+ string message = AppendPrintParams('\t', what);
+ using var godotStr = Marshaling.ConvertStringToNative(message);
NativeFuncs.godotsharp_printt(godotStr);
}
/// <summary>
+ /// Pushes an error message to Godot's built-in debugger and to the OS terminal.
+ ///
+ /// Note: Errors printed this way will not pause project execution.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.PushError("test error"); // Prints "test error" to debugger and terminal as error call
+ /// </code>
+ /// </example>
+ /// <param name="message">Error message.</param>
+ public static void PushError(string message)
+ {
+ using var godotStr = Marshaling.ConvertStringToNative(message);
+ NativeFuncs.godotsharp_pusherror(godotStr);
+ }
+
+ /// <summary>
+ /// Pushes an error message to Godot's built-in debugger and to the OS terminal.
+ ///
+ /// Note: Errors printed this way will not pause project execution.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.PushError("test_error"); // Prints "test error" to debugger and terminal as error call
+ /// </code>
+ /// </example>
+ /// <param name="what">Arguments that form the error message.</param>
+ public static void PushError(params object[] what)
+ {
+ PushError(AppendPrintParams(what));
+ }
+
+ /// <summary>
+ /// Pushes a warning message to Godot's built-in debugger and to the OS terminal.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.PushWarning("test warning"); // Prints "test warning" to debugger and terminal as warning call
+ /// </code>
+ /// </example>
+ /// <param name="message">Warning message.</param>
+ public static void PushWarning(string message)
+ {
+ using var godotStr = Marshaling.ConvertStringToNative(message);
+ NativeFuncs.godotsharp_pushwarning(godotStr);
+ }
+
+ /// <summary>
+ /// Pushes a warning message to Godot's built-in debugger and to the OS terminal.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.PushWarning("test warning"); // Prints "test warning" to debugger and terminal as warning call
+ /// </code>
+ /// </example>
+ /// <param name="what">Arguments that form the warning message.</param>
+ public static void PushWarning(params object[] what)
+ {
+ PushWarning(AppendPrintParams(what));
+ }
+
+ /// <summary>
/// Returns a random floating point value between <c>0.0</c> and <c>1.0</c> (inclusive).
/// </summary>
/// <example>
@@ -362,7 +411,9 @@ namespace Godot
}
/// <summary>
- /// Returns a normally-distributed pseudo-random number, using Box-Muller transform with the specified <c>mean</c> and a standard <c>deviation</c>.
+ /// Returns a normally-distributed pseudo-random floating point value
+ /// using Box-Muller transform with the specified <pararmref name="mean"/>
+ /// and a standard <paramref name="deviation"/>.
/// This is also called Gaussian distribution.
/// </summary>
/// <returns>A random normally-distributed <see langword="float"/> number.</returns>
@@ -373,7 +424,8 @@ namespace Godot
/// <summary>
/// Returns a random unsigned 32-bit integer.
- /// Use remainder to obtain a random value in the interval <c>[0, N - 1]</c> (where N is smaller than 2^32).
+ /// Use remainder to obtain a random value in the interval <c>[0, N - 1]</c>
+ /// (where N is smaller than 2^32).
/// </summary>
/// <example>
/// <code>
@@ -391,11 +443,11 @@ namespace Godot
/// <summary>
/// Randomizes the seed (or the internal state) of the random number generator.
- /// Current implementation reseeds using a number based on time.
+ /// The current implementation uses a number based on the device's time.
///
/// Note: This method is called automatically when the project is run.
- /// If you need to fix the seed to have reproducible results, use <see cref="Seed(ulong)"/>
- /// to initialize the random number generator.
+ /// If you need to fix the seed to have consistent, reproducible results,
+ /// use <see cref="Seed(ulong)"/> to initialize the random number generator.
/// </summary>
public static void Randomize()
{
@@ -403,12 +455,13 @@ namespace Godot
}
/// <summary>
- /// Returns a random floating point value on the interval between <paramref name="from"/>
+ /// Returns a random floating point value between <paramref name="from"/>
/// and <paramref name="to"/> (inclusive).
/// </summary>
/// <example>
/// <code>
- /// GD.PrintS(GD.RandRange(-10.0, 10.0), GD.RandRange(-10.0, 10.0)); // Prints e.g. -3.844535 7.45315
+ /// GD.RandRange(0.0, 20.5); // Returns e.g. 7.45315
+ /// GD.RandRange(-10.0, 10.0); // Returns e.g. -3.844535
/// </code>
/// </example>
/// <returns>A random <see langword="double"/> number inside the given range.</returns>
@@ -424,8 +477,8 @@ namespace Godot
/// </summary>
/// <example>
/// <code>
- /// GD.Print(GD.RandRange(0, 1)); // Prints 0 or 1
- /// GD.Print(GD.RandRange(-10, 1000)); // Prints any number from -10 to 1000
+ /// GD.RandRange(0, 1); // Returns either 0 or 1
+ /// GD.RandRange(-10, 1000); // Returns random integer between -10 and 1000
/// </code>
/// </example>
/// <returns>A random <see langword="int"/> number inside the given range.</returns>
@@ -435,8 +488,18 @@ namespace Godot
}
/// <summary>
- /// Returns a random unsigned 32-bit integer, using the given <paramref name="seed"/>.
+ /// Given a <paramref name="seed"/>, returns a randomized <see langword="uint"/>
+ /// value. The <paramref name="seed"/> may be modified.
+ /// Passing the same <paramref name="seed"/> consistently returns the same value.
+ ///
+ /// Note: "Seed" here refers to the internal state of the pseudo random number
+ /// generator, currently implemented as a 64 bit integer.
/// </summary>
+ /// <example>
+ /// <code>
+ /// var a = GD.RandFromSeed(4);
+ /// </code>
+ /// </example>
/// <param name="seed">
/// Seed to use to generate the random number.
/// If a different seed is used, its value will be modified.
@@ -449,7 +512,8 @@ namespace Godot
/// <summary>
/// Returns a <see cref="IEnumerable{T}"/> that iterates from
- /// <c>0</c> to <paramref name="end"/> in steps of <c>1</c>.
+ /// <c>0</c> (inclusive) to <paramref name="end"/> (exclusive)
+ /// in steps of <c>1</c>.
/// </summary>
/// <param name="end">The last index.</param>
public static IEnumerable<int> Range(int end)
@@ -459,7 +523,8 @@ namespace Godot
/// <summary>
/// Returns a <see cref="IEnumerable{T}"/> that iterates from
- /// <paramref name="start"/> to <paramref name="end"/> in steps of <c>1</c>.
+ /// <paramref name="start"/> (inclusive) to <paramref name="end"/> (exclusive)
+ /// in steps of <c>1</c>.
/// </summary>
/// <param name="start">The first index.</param>
/// <param name="end">The last index.</param>
@@ -470,13 +535,21 @@ namespace Godot
/// <summary>
/// Returns a <see cref="IEnumerable{T}"/> that iterates from
- /// <paramref name="start"/> to <paramref name="end"/> in steps of <paramref name="step"/>.
+ /// <paramref name="start"/> (inclusive) to <paramref name="end"/> (exclusive)
+ /// in steps of <paramref name="step"/>.
+ /// The argument <paramref name="step"/> can be negative, but not <c>0</c>.
/// </summary>
+ /// <exception cref="ArgumentException">
+ /// <paramref name="step"/> is 0.
+ /// </exception>
/// <param name="start">The first index.</param>
/// <param name="end">The last index.</param>
/// <param name="step">The amount by which to increment the index on each iteration.</param>
public static IEnumerable<int> Range(int start, int end, int step)
{
+ if (step == 0)
+ throw new ArgumentException("step cannot be 0.", nameof(step));
+
if (end < start && step > 0)
yield break;
@@ -496,8 +569,20 @@ namespace Godot
}
/// <summary>
- /// Sets seed for the random number generator.
+ /// Sets seed for the random number generator to <paramref name="seed"/>.
+ /// Setting the seed manually can ensure consistent, repeatable results for
+ /// most random functions.
/// </summary>
+ /// <example>
+ /// <code>
+ /// ulong mySeed = (ulong)GD.Hash("Godot Rocks");
+ /// GD.Seed(mySeed);
+ /// var a = GD.Randf() + GD.Randi();
+ /// GD.Seed(mySeed);
+ /// var b = GD.Randf() + GD.Randi();
+ /// // a and b are now identical
+ /// </code>
+ /// </example>
/// <param name="seed">Seed that will be used.</param>
public static void Seed(ulong seed)
{
@@ -505,26 +590,14 @@ namespace Godot
}
/// <summary>
- /// Converts one or more arguments of any type to string in the best way possible.
- /// </summary>
- /// <param name="what">Arguments that will converted to string.</param>
- /// <returns>The string formed by the given arguments.</returns>
- public static string Str(params Variant[] what)
- {
- using var whatGodot = new Godot.Collections.Array(what);
- NativeFuncs.godotsharp_str((godot_array)whatGodot.NativeValue, out godot_string ret);
- using (ret)
- return Marshaling.ConvertStringToManaged(ret);
- }
-
- /// <summary>
- /// Converts a formatted string that was returned by <see cref="VarToStr(Variant)"/> to the original value.
+ /// Converts a formatted string that was returned by <see cref="VarToStr(Variant)"/>
+ /// to the original value.
/// </summary>
/// <example>
/// <code>
- /// string a = "{\"a\": 1, \"b\": 2 }";
- /// var b = (Godot.Collections.Dictionary)GD.StrToVar(a);
- /// GD.Print(b["a"]); // Prints 1
+ /// string a = "{ \"a\": 1, \"b\": 2 }"; // a is a string
+ /// var b = GD.StrToVar(a).AsGodotDictionary(); // b is a Dictionary
+ /// GD.Print(b["a"]); // Prints 1
/// </code>
/// </example>
/// <param name="str">String that will be converted to Variant.</param>
@@ -537,38 +610,49 @@ namespace Godot
}
/// <summary>
- /// Encodes a <c>Variant</c> value to a byte array.
- /// If <paramref name="fullObjects"/> is <see langword="true"/> encoding objects is allowed
- /// (and can potentially include code).
- /// Deserialization can be done with <see cref="BytesToVar(Span{byte}, bool)"/>.
+ /// Encodes a <see cref="Variant"/> value to a byte array, without encoding objects.
+ /// Deserialization can be done with <see cref="BytesToVar"/>.
+ /// Note: If you need object serialization, see <see cref="VarToBytesWithObjects"/>.
+ /// </summary>
+ /// <param name="var"><see cref="Variant"/> that will be encoded.</param>
+ /// <returns>The <see cref="Variant"/> encoded as an array of bytes.</returns>
+ public static byte[] VarToBytes(Variant var)
+ {
+ NativeFuncs.godotsharp_var_to_bytes((godot_variant)var.NativeVar, godot_bool.False, out var varBytes);
+ using (varBytes)
+ return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes);
+ }
+
+ /// <summary>
+ /// Encodes a <see cref="Variant"/>. Encoding objects is allowed (and can potentially
+ /// include executable code). Deserialization can be done with <see cref="BytesToVarWithObjects"/>.
/// </summary>
- /// <param name="var">Variant that will be encoded.</param>
- /// <param name="fullObjects">If objects should be serialized.</param>
- /// <returns>The <c>Variant</c> encoded as an array of bytes.</returns>
- public static byte[] VarToBytes(Variant var, bool fullObjects = false)
+ /// <param name="var"><see cref="Variant"/> that will be encoded.</param>
+ /// <returns>The <see cref="Variant"/> encoded as an array of bytes.</returns>
+ public static byte[] VarToBytesWithObjects(Variant var)
{
- NativeFuncs.godotsharp_var_to_bytes((godot_variant)var.NativeVar, fullObjects.ToGodotBool(), out var varBytes);
+ NativeFuncs.godotsharp_var_to_bytes((godot_variant)var.NativeVar, godot_bool.True, out var varBytes);
using (varBytes)
return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes);
}
/// <summary>
- /// Converts a <c>Variant</c> <paramref name="var"/> to a formatted string that
+ /// Converts a <see cref="Variant"/> <paramref name="var"/> to a formatted string that
/// can later be parsed using <see cref="StrToVar(string)"/>.
/// </summary>
/// <example>
/// <code>
/// var a = new Godot.Collections.Dictionary { ["a"] = 1, ["b"] = 2 };
/// GD.Print(GD.VarToStr(a));
- /// // Prints
+ /// // Prints:
/// // {
- /// // "a": 1,
- /// // "b": 2
+ /// // "a": 1,
+ /// // "b": 2
/// // }
/// </code>
/// </example>
/// <param name="var">Variant that will be converted to string.</param>
- /// <returns>The <c>Variant</c> encoded as a string.</returns>
+ /// <returns>The <see cref="Variant"/> encoded as a string.</returns>
public static string VarToStr(Variant var)
{
NativeFuncs.godotsharp_var_to_str((godot_variant)var.NativeVar, out godot_string ret);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs
index 1b599beab5..027eab30fc 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs
@@ -1,13 +1,13 @@
+using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
namespace Godot
{
- public class GodotSynchronizationContext : SynchronizationContext
+ public sealed class GodotSynchronizationContext : SynchronizationContext, IDisposable
{
- private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> _queue =
- new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>();
+ private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> _queue = new();
public override void Post(SendOrPostCallback d, object state)
{
@@ -24,5 +24,10 @@ namespace Godot
workItem.Key(workItem.Value);
}
}
+
+ public void Dispose()
+ {
+ _queue.Dispose();
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs
index 408bed71b2..f6c36455b2 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs
@@ -10,7 +10,7 @@ namespace Godot
/// GodotTaskScheduler contains a linked list of tasks to perform as a queue. Methods
/// within the class are used to control the queue and perform the contained tasks.
/// </summary>
- public class GodotTaskScheduler : TaskScheduler
+ public sealed class GodotTaskScheduler : TaskScheduler, IDisposable
{
/// <summary>
/// The current synchronization context.
@@ -108,5 +108,10 @@ namespace Godot
}
}
}
+
+ public void Dispose()
+ {
+ Context.Dispose();
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
index b2cb0f5e6b..ca0032df73 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
@@ -1,4 +1,5 @@
using System;
+using System.Runtime.CompilerServices;
namespace Godot
{
@@ -35,15 +36,18 @@ namespace Godot
public const real_t NaN = real_t.NaN;
// 0.0174532924f and 0.0174532925199433
- private const real_t _degToRadConst = (real_t)0.0174532925199432957692369077M;
+ private const float _degToRadConstF = (float)0.0174532925199432957692369077M;
+ private const double _degToRadConstD = (double)0.0174532925199432957692369077M;
// 57.29578f and 57.2957795130823
- private const real_t _radToDegConst = (real_t)57.295779513082320876798154814M;
+ private const float _radToDegConstF = (float)57.295779513082320876798154814M;
+ private const double _radToDegConstD = (double)57.295779513082320876798154814M;
/// <summary>
/// Returns the absolute value of <paramref name="s"/> (i.e. positive value).
/// </summary>
/// <param name="s">The input number.</param>
/// <returns>The absolute value of <paramref name="s"/>.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Abs(int s)
{
return Math.Abs(s);
@@ -54,12 +58,38 @@ namespace Godot
/// </summary>
/// <param name="s">The input number.</param>
/// <returns>The absolute value of <paramref name="s"/>.</returns>
- public static real_t Abs(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Abs(float s)
{
return Math.Abs(s);
}
/// <summary>
+ /// Returns the absolute value of <paramref name="s"/> (i.e. positive value).
+ /// </summary>
+ /// <param name="s">The input number.</param>
+ /// <returns>The absolute value of <paramref name="s"/>.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Abs(double s)
+ {
+ return Math.Abs(s);
+ }
+
+ /// <summary>
+ /// Returns the arc cosine of <paramref name="s"/> in radians.
+ /// Use to get the angle of cosine <paramref name="s"/>.
+ /// </summary>
+ /// <param name="s">The input cosine value. Must be on the range of -1.0 to 1.0.</param>
+ /// <returns>
+ /// An angle that would result in the given cosine value. On the range <c>0</c> to <c>Tau/2</c>.
+ /// </returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Acos(float s)
+ {
+ return MathF.Acos(s);
+ }
+
+ /// <summary>
/// Returns the arc cosine of <paramref name="s"/> in radians.
/// Use to get the angle of cosine <paramref name="s"/>.
/// </summary>
@@ -67,9 +97,24 @@ namespace Godot
/// <returns>
/// An angle that would result in the given cosine value. On the range <c>0</c> to <c>Tau/2</c>.
/// </returns>
- public static real_t Acos(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Acos(double s)
+ {
+ return Math.Acos(s);
+ }
+
+ /// <summary>
+ /// Returns the arc sine of <paramref name="s"/> in radians.
+ /// Use to get the angle of sine <paramref name="s"/>.
+ /// </summary>
+ /// <param name="s">The input sine value. Must be on the range of -1.0 to 1.0.</param>
+ /// <returns>
+ /// An angle that would result in the given sine value. On the range <c>-Tau/4</c> to <c>Tau/4</c>.
+ /// </returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Asin(float s)
{
- return (real_t)Math.Acos(s);
+ return MathF.Asin(s);
}
/// <summary>
@@ -80,9 +125,27 @@ namespace Godot
/// <returns>
/// An angle that would result in the given sine value. On the range <c>-Tau/4</c> to <c>Tau/4</c>.
/// </returns>
- public static real_t Asin(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Asin(double s)
+ {
+ return Math.Asin(s);
+ }
+
+ /// <summary>
+ /// Returns the arc tangent of <paramref name="s"/> in radians.
+ /// Use to get the angle of tangent <paramref name="s"/>.
+ ///
+ /// The method cannot know in which quadrant the angle should fall.
+ /// See <see cref="Atan2(float, float)"/> if you have both <c>y</c> and <c>x</c>.
+ /// </summary>
+ /// <param name="s">The input tangent value.</param>
+ /// <returns>
+ /// An angle that would result in the given tangent value. On the range <c>-Tau/4</c> to <c>Tau/4</c>.
+ /// </returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Atan(float s)
{
- return (real_t)Math.Asin(s);
+ return MathF.Atan(s);
}
/// <summary>
@@ -90,15 +153,34 @@ namespace Godot
/// Use to get the angle of tangent <paramref name="s"/>.
///
/// The method cannot know in which quadrant the angle should fall.
- /// See <see cref="Atan2(real_t, real_t)"/> if you have both <c>y</c> and <c>x</c>.
+ /// See <see cref="Atan2(double, double)"/> if you have both <c>y</c> and <c>x</c>.
/// </summary>
/// <param name="s">The input tangent value.</param>
/// <returns>
/// An angle that would result in the given tangent value. On the range <c>-Tau/4</c> to <c>Tau/4</c>.
/// </returns>
- public static real_t Atan(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Atan(double s)
+ {
+ return Math.Atan(s);
+ }
+
+ /// <summary>
+ /// Returns the arc tangent of <paramref name="y"/> and <paramref name="x"/> in radians.
+ /// Use to get the angle of the tangent of <c>y/x</c>. To compute the value, the method takes into
+ /// account the sign of both arguments in order to determine the quadrant.
+ ///
+ /// Important note: The Y coordinate comes first, by convention.
+ /// </summary>
+ /// <param name="y">The Y coordinate of the point to find the angle to.</param>
+ /// <param name="x">The X coordinate of the point to find the angle to.</param>
+ /// <returns>
+ /// An angle that would result in the given tangent value. On the range <c>-Tau/2</c> to <c>Tau/2</c>.
+ /// </returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Atan2(float y, float x)
{
- return (real_t)Math.Atan(s);
+ return MathF.Atan2(y, x);
}
/// <summary>
@@ -113,9 +195,21 @@ namespace Godot
/// <returns>
/// An angle that would result in the given tangent value. On the range <c>-Tau/2</c> to <c>Tau/2</c>.
/// </returns>
- public static real_t Atan2(real_t y, real_t x)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Atan2(double y, double x)
+ {
+ return Math.Atan2(y, x);
+ }
+
+ /// <summary>
+ /// Rounds <paramref name="s"/> upward (towards positive infinity).
+ /// </summary>
+ /// <param name="s">The number to ceil.</param>
+ /// <returns>The smallest whole number that is not less than <paramref name="s"/>.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Ceil(float s)
{
- return (real_t)Math.Atan2(y, x);
+ return MathF.Ceiling(s);
}
/// <summary>
@@ -123,9 +217,10 @@ namespace Godot
/// </summary>
/// <param name="s">The number to ceil.</param>
/// <returns>The smallest whole number that is not less than <paramref name="s"/>.</returns>
- public static real_t Ceil(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Ceil(double s)
{
- return (real_t)Math.Ceiling(s);
+ return Math.Ceiling(s);
}
/// <summary>
@@ -136,9 +231,24 @@ namespace Godot
/// <param name="min">The minimum allowed value.</param>
/// <param name="max">The maximum allowed value.</param>
/// <returns>The clamped value.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Clamp(int value, int min, int max)
{
- return value < min ? min : value > max ? max : value;
+ return Math.Clamp(value, min, max);
+ }
+
+ /// <summary>
+ /// Clamps a <paramref name="value"/> so that it is not less than <paramref name="min"/>
+ /// and not more than <paramref name="max"/>.
+ /// </summary>
+ /// <param name="value">The value to clamp.</param>
+ /// <param name="min">The minimum allowed value.</param>
+ /// <param name="max">The maximum allowed value.</param>
+ /// <returns>The clamped value.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Clamp(float value, float min, float max)
+ {
+ return Math.Clamp(value, min, max);
}
/// <summary>
@@ -149,9 +259,21 @@ namespace Godot
/// <param name="min">The minimum allowed value.</param>
/// <param name="max">The maximum allowed value.</param>
/// <returns>The clamped value.</returns>
- public static real_t Clamp(real_t value, real_t min, real_t max)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Clamp(double value, double min, double max)
+ {
+ return Math.Clamp(value, min, max);
+ }
+
+ /// <summary>
+ /// Returns the cosine of angle <paramref name="s"/> in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The cosine of that angle.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Cos(float s)
{
- return value < min ? min : value > max ? max : value;
+ return MathF.Cos(s);
}
/// <summary>
@@ -159,9 +281,21 @@ namespace Godot
/// </summary>
/// <param name="s">The angle in radians.</param>
/// <returns>The cosine of that angle.</returns>
- public static real_t Cos(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Cos(double s)
+ {
+ return Math.Cos(s);
+ }
+
+ /// <summary>
+ /// Returns the hyperbolic cosine of angle <paramref name="s"/> in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The hyperbolic cosine of that angle.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Cosh(float s)
{
- return (real_t)Math.Cos(s);
+ return MathF.Cosh(s);
}
/// <summary>
@@ -169,9 +303,10 @@ namespace Godot
/// </summary>
/// <param name="s">The angle in radians.</param>
/// <returns>The hyperbolic cosine of that angle.</returns>
- public static real_t Cosh(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Cosh(double s)
{
- return (real_t)Math.Cosh(s);
+ return Math.Cosh(s);
}
/// <summary>
@@ -184,7 +319,7 @@ namespace Godot
/// <param name="post">The value which after "to" value for interpolation.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The resulting value of the interpolation.</returns>
- public static real_t CubicInterpolate(real_t from, real_t to, real_t pre, real_t post, real_t weight)
+ public static float CubicInterpolate(float from, float to, float pre, float post, float weight)
{
return 0.5f *
((from * 2.0f) +
@@ -194,9 +329,28 @@ namespace Godot
}
/// <summary>
+ /// Cubic interpolates between two values by the factor defined in <paramref name="weight"/>
+ /// with pre and post values.
+ /// </summary>
+ /// <param name="from">The start value for interpolation.</param>
+ /// <param name="to">The destination value for interpolation.</param>
+ /// <param name="pre">The value which before "from" value for interpolation.</param>
+ /// <param name="post">The value which after "to" value for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting value of the interpolation.</returns>
+ public static double CubicInterpolate(double from, double to, double pre, double post, double weight)
+ {
+ return 0.5 *
+ ((from * 2.0) +
+ (-pre + to) * weight +
+ (2.0 * pre - 5.0 * from + 4.0 * to - post) * (weight * weight) +
+ (-pre + 3.0 * from - 3.0 * to + post) * (weight * weight * weight));
+ }
+
+ /// <summary>
/// Cubic interpolates between two rotation values with shortest path
/// by the factor defined in <paramref name="weight"/> with pre and post values.
- /// See also <see cref="LerpAngle"/>.
+ /// See also <see cref="LerpAngle(float, float, float)"/>.
/// </summary>
/// <param name="from">The start value for interpolation.</param>
/// <param name="to">The destination value for interpolation.</param>
@@ -204,18 +358,45 @@ namespace Godot
/// <param name="post">The value which after "to" value for interpolation.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The resulting value of the interpolation.</returns>
- public static real_t CubicInterpolateAngle(real_t from, real_t to, real_t pre, real_t post, real_t weight)
+ public static float CubicInterpolateAngle(float from, float to, float pre, float post, float weight)
{
- real_t fromRot = from % Mathf.Tau;
+ float fromRot = from % MathF.Tau;
- real_t preDiff = (pre - fromRot) % Mathf.Tau;
- real_t preRot = fromRot + (2.0f * preDiff) % Mathf.Tau - preDiff;
+ float preDiff = (pre - fromRot) % MathF.Tau;
+ float preRot = fromRot + (2.0f * preDiff) % MathF.Tau - preDiff;
- real_t toDiff = (to - fromRot) % Mathf.Tau;
- real_t toRot = fromRot + (2.0f * toDiff) % Mathf.Tau - toDiff;
+ float toDiff = (to - fromRot) % MathF.Tau;
+ float toRot = fromRot + (2.0f * toDiff) % MathF.Tau - toDiff;
- real_t postDiff = (post - toRot) % Mathf.Tau;
- real_t postRot = toRot + (2.0f * postDiff) % Mathf.Tau - postDiff;
+ float postDiff = (post - toRot) % MathF.Tau;
+ float postRot = toRot + (2.0f * postDiff) % MathF.Tau - postDiff;
+
+ return CubicInterpolate(fromRot, toRot, preRot, postRot, weight);
+ }
+
+ /// <summary>
+ /// Cubic interpolates between two rotation values with shortest path
+ /// by the factor defined in <paramref name="weight"/> with pre and post values.
+ /// See also <see cref="LerpAngle(double, double, double)"/>.
+ /// </summary>
+ /// <param name="from">The start value for interpolation.</param>
+ /// <param name="to">The destination value for interpolation.</param>
+ /// <param name="pre">The value which before "from" value for interpolation.</param>
+ /// <param name="post">The value which after "to" value for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting value of the interpolation.</returns>
+ public static double CubicInterpolateAngle(double from, double to, double pre, double post, double weight)
+ {
+ double fromRot = from % Math.Tau;
+
+ double preDiff = (pre - fromRot) % Math.Tau;
+ double preRot = fromRot + (2.0 * preDiff) % Math.Tau - preDiff;
+
+ double toDiff = (to - fromRot) % Math.Tau;
+ double toRot = fromRot + (2.0 * toDiff) % Math.Tau - toDiff;
+
+ double postDiff = (post - toRot) % Math.Tau;
+ double postRot = toRot + (2.0 * postDiff) % Math.Tau - postDiff;
return CubicInterpolate(fromRot, toRot, preRot, postRot, weight);
}
@@ -223,7 +404,8 @@ namespace Godot
/// <summary>
/// Cubic interpolates between two values by the factor defined in <paramref name="weight"/>
/// with pre and post values.
- /// It can perform smoother interpolation than <see cref="CubicInterpolate"/>
+ /// It can perform smoother interpolation than
+ /// <see cref="CubicInterpolate(float, float, float, float, float)"/>
/// by the time values.
/// </summary>
/// <param name="from">The start value for interpolation.</param>
@@ -235,23 +417,52 @@ namespace Godot
/// <param name="preT"></param>
/// <param name="postT"></param>
/// <returns>The resulting value of the interpolation.</returns>
- public static real_t CubicInterpolateInTime(real_t from, real_t to, real_t pre, real_t post, real_t weight, real_t toT, real_t preT, real_t postT)
+ public static float CubicInterpolateInTime(float from, float to, float pre, float post, float weight, float toT, float preT, float postT)
{
/* Barry-Goldman method */
- real_t t = Lerp(0.0f, toT, weight);
- real_t a1 = Lerp(pre, from, preT == 0 ? 0.0f : (t - preT) / -preT);
- real_t a2 = Lerp(from, to, toT == 0 ? 0.5f : t / toT);
- real_t a3 = Lerp(to, post, postT - toT == 0 ? 1.0f : (t - toT) / (postT - toT));
- real_t b1 = Lerp(a1, a2, toT - preT == 0 ? 0.0f : (t - preT) / (toT - preT));
- real_t b2 = Lerp(a2, a3, postT == 0 ? 1.0f : t / postT);
+ float t = Lerp(0.0f, toT, weight);
+ float a1 = Lerp(pre, from, preT == 0 ? 0.0f : (t - preT) / -preT);
+ float a2 = Lerp(from, to, toT == 0 ? 0.5f : t / toT);
+ float a3 = Lerp(to, post, postT - toT == 0 ? 1.0f : (t - toT) / (postT - toT));
+ float b1 = Lerp(a1, a2, toT - preT == 0 ? 0.0f : (t - preT) / (toT - preT));
+ float b2 = Lerp(a2, a3, postT == 0 ? 1.0f : t / postT);
return Lerp(b1, b2, toT == 0 ? 0.5f : t / toT);
}
/// <summary>
+ /// Cubic interpolates between two values by the factor defined in <paramref name="weight"/>
+ /// with pre and post values.
+ /// It can perform smoother interpolation than
+ /// <see cref="CubicInterpolate(double, double, double, double, double)"/>
+ /// by the time values.
+ /// </summary>
+ /// <param name="from">The start value for interpolation.</param>
+ /// <param name="to">The destination value for interpolation.</param>
+ /// <param name="pre">The value which before "from" value for interpolation.</param>
+ /// <param name="post">The value which after "to" value for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <param name="toT"></param>
+ /// <param name="preT"></param>
+ /// <param name="postT"></param>
+ /// <returns>The resulting value of the interpolation.</returns>
+ public static double CubicInterpolateInTime(double from, double to, double pre, double post, double weight, double toT, double preT, double postT)
+ {
+ /* Barry-Goldman method */
+ double t = Lerp(0.0, toT, weight);
+ double a1 = Lerp(pre, from, preT == 0 ? 0.0 : (t - preT) / -preT);
+ double a2 = Lerp(from, to, toT == 0 ? 0.5 : t / toT);
+ double a3 = Lerp(to, post, postT - toT == 0 ? 1.0 : (t - toT) / (postT - toT));
+ double b1 = Lerp(a1, a2, toT - preT == 0 ? 0.0 : (t - preT) / (toT - preT));
+ double b2 = Lerp(a2, a3, postT == 0 ? 1.0 : t / postT);
+ return Lerp(b1, b2, toT == 0 ? 0.5 : t / toT);
+ }
+
+ /// <summary>
/// Cubic interpolates between two rotation values with shortest path
/// by the factor defined in <paramref name="weight"/> with pre and post values.
- /// See also <see cref="LerpAngle"/>.
- /// It can perform smoother interpolation than <see cref="CubicInterpolateAngle"/>
+ /// See also <see cref="LerpAngle(float, float, float)"/>.
+ /// It can perform smoother interpolation than
+ /// <see cref="CubicInterpolateAngle(float, float, float, float, float)"/>
/// by the time values.
/// </summary>
/// <param name="from">The start value for interpolation.</param>
@@ -263,24 +474,78 @@ namespace Godot
/// <param name="preT"></param>
/// <param name="postT"></param>
/// <returns>The resulting value of the interpolation.</returns>
- public static real_t CubicInterpolateAngleInTime(real_t from, real_t to, real_t pre, real_t post, real_t weight,
- real_t toT, real_t preT, real_t postT)
+ public static float CubicInterpolateAngleInTime(float from, float to, float pre, float post, float weight, float toT, float preT, float postT)
{
- real_t fromRot = from % Mathf.Tau;
+ float fromRot = from % MathF.Tau;
- real_t preDiff = (pre - fromRot) % Mathf.Tau;
- real_t preRot = fromRot + (2.0f * preDiff) % Mathf.Tau - preDiff;
+ float preDiff = (pre - fromRot) % MathF.Tau;
+ float preRot = fromRot + (2.0f * preDiff) % MathF.Tau - preDiff;
- real_t toDiff = (to - fromRot) % Mathf.Tau;
- real_t toRot = fromRot + (2.0f * toDiff) % Mathf.Tau - toDiff;
+ float toDiff = (to - fromRot) % MathF.Tau;
+ float toRot = fromRot + (2.0f * toDiff) % MathF.Tau - toDiff;
- real_t postDiff = (post - toRot) % Mathf.Tau;
- real_t postRot = toRot + (2.0f * postDiff) % Mathf.Tau - postDiff;
+ float postDiff = (post - toRot) % MathF.Tau;
+ float postRot = toRot + (2.0f * postDiff) % MathF.Tau - postDiff;
return CubicInterpolateInTime(fromRot, toRot, preRot, postRot, weight, toT, preT, postT);
}
/// <summary>
+ /// Cubic interpolates between two rotation values with shortest path
+ /// by the factor defined in <paramref name="weight"/> with pre and post values.
+ /// See also <see cref="LerpAngle(double, double, double)"/>.
+ /// It can perform smoother interpolation than
+ /// <see cref="CubicInterpolateAngle(double, double, double, double, double)"/>
+ /// by the time values.
+ /// </summary>
+ /// <param name="from">The start value for interpolation.</param>
+ /// <param name="to">The destination value for interpolation.</param>
+ /// <param name="pre">The value which before "from" value for interpolation.</param>
+ /// <param name="post">The value which after "to" value for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <param name="toT"></param>
+ /// <param name="preT"></param>
+ /// <param name="postT"></param>
+ /// <returns>The resulting value of the interpolation.</returns>
+ public static double CubicInterpolateAngleInTime(double from, double to, double pre, double post, double weight, double toT, double preT, double postT)
+ {
+ double fromRot = from % Math.Tau;
+
+ double preDiff = (pre - fromRot) % Math.Tau;
+ double preRot = fromRot + (2.0 * preDiff) % Math.Tau - preDiff;
+
+ double toDiff = (to - fromRot) % Math.Tau;
+ double toRot = fromRot + (2.0 * toDiff) % Math.Tau - toDiff;
+
+ double postDiff = (post - toRot) % Math.Tau;
+ double postRot = toRot + (2.0 * postDiff) % Math.Tau - postDiff;
+
+ return CubicInterpolateInTime(fromRot, toRot, preRot, postRot, weight, toT, preT, postT);
+ }
+
+ /// <summary>
+ /// Returns the point at the given <paramref name="t"/> on a one-dimensional Bezier curve defined by
+ /// the given <paramref name="control1"/>, <paramref name="control2"/>, and <paramref name="end"/> points.
+ /// </summary>
+ /// <param name="start">The start value for the interpolation.</param>
+ /// <param name="control1">Control point that defines the bezier curve.</param>
+ /// <param name="control2">Control point that defines the bezier curve.</param>
+ /// <param name="end">The destination value for the interpolation.</param>
+ /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting value of the interpolation.</returns>
+ public static float BezierInterpolate(float start, float control1, float control2, float end, float t)
+ {
+ // Formula from Wikipedia article on Bezier curves
+ float omt = 1.0f - t;
+ float omt2 = omt * omt;
+ float omt3 = omt2 * omt;
+ float t2 = t * t;
+ float t3 = t2 * t;
+
+ return start * omt3 + control1 * omt2 * t * 3.0f + control2 * omt * t2 * 3.0f + end * t3;
+ }
+
+ /// <summary>
/// Returns the point at the given <paramref name="t"/> on a one-dimensional Bezier curve defined by
/// the given <paramref name="control1"/>, <paramref name="control2"/>, and <paramref name="end"/> points.
/// </summary>
@@ -290,16 +555,37 @@ namespace Godot
/// <param name="end">The destination value for the interpolation.</param>
/// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The resulting value of the interpolation.</returns>
- public static real_t BezierInterpolate(real_t start, real_t control1, real_t control2, real_t end, real_t t)
+ public static double BezierInterpolate(double start, double control1, double control2, double end, double t)
+ {
+ // Formula from Wikipedia article on Bezier curves
+ double omt = 1.0 - t;
+ double omt2 = omt * omt;
+ double omt3 = omt2 * omt;
+ double t2 = t * t;
+ double t3 = t2 * t;
+
+ return start * omt3 + control1 * omt2 * t * 3.0 + control2 * omt * t2 * 3.0 + end * t3;
+ }
+
+ /// <summary>
+ /// Returns the derivative at the given <paramref name="t"/> on a one dimensional Bezier curve defined by
+ /// the given <paramref name="control1"/>, <paramref name="control2"/>, and <paramref name="end"/> points.
+ /// </summary>
+ /// <param name="start">The start value for the interpolation.</param>
+ /// <param name="control1">Control point that defines the bezier curve.</param>
+ /// <param name="control2">Control point that defines the bezier curve.</param>
+ /// <param name="end">The destination value for the interpolation.</param>
+ /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting value of the interpolation.</returns>
+ public static float BezierDerivative(float start, float control1, float control2, float end, float t)
{
// Formula from Wikipedia article on Bezier curves
- real_t omt = 1 - t;
- real_t omt2 = omt * omt;
- real_t omt3 = omt2 * omt;
- real_t t2 = t * t;
- real_t t3 = t2 * t;
+ float omt = 1.0f - t;
+ float omt2 = omt * omt;
+ float t2 = t * t;
- return start * omt3 + control1 * omt2 * t * 3 + control2 * omt * t2 * 3 + end * t3;
+ float d = (control1 - start) * 3.0f * omt2 + (control2 - control1) * 6.0f * omt * t + (end - control2) * 3.0f * t2;
+ return d;
}
/// <summary>
@@ -312,25 +598,57 @@ namespace Godot
/// <param name="end">The destination value for the interpolation.</param>
/// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The resulting value of the interpolation.</returns>
- public static real_t BezierDerivative(real_t start, real_t control1, real_t control2, real_t end, real_t t)
+ public static double BezierDerivative(double start, double control1, double control2, double end, double t)
{
// Formula from Wikipedia article on Bezier curves
- real_t omt = 1 - t;
- real_t omt2 = omt * omt;
- real_t t2 = t * t;
+ double omt = 1.0 - t;
+ double omt2 = omt * omt;
+ double t2 = t * t;
- real_t d = (control1 - start) * 3 * omt2 + (control2 - control1) * 6 * omt * t + (end - control2) * 3 * t2;
+ double d = (control1 - start) * 3.0 * omt2 + (control2 - control1) * 6.0 * omt * t + (end - control2) * 3.0 * t2;
return d;
}
/// <summary>
+ /// Converts from decibels to linear energy (audio).
+ /// </summary>
+ /// <seealso cref="LinearToDb(float)"/>
+ /// <param name="db">Decibels to convert.</param>
+ /// <returns>Audio volume as linear energy.</returns>
+ public static float DbToLinear(float db)
+ {
+ return MathF.Exp(db * 0.11512925464970228420089957273422f);
+ }
+
+ /// <summary>
+ /// Converts from decibels to linear energy (audio).
+ /// </summary>
+ /// <seealso cref="LinearToDb(double)"/>
+ /// <param name="db">Decibels to convert.</param>
+ /// <returns>Audio volume as linear energy.</returns>
+ public static double DbToLinear(double db)
+ {
+ return Math.Exp(db * 0.11512925464970228420089957273422);
+ }
+
+ /// <summary>
/// Converts an angle expressed in degrees to radians.
/// </summary>
/// <param name="deg">An angle expressed in degrees.</param>
/// <returns>The same angle expressed in radians.</returns>
- public static real_t DegToRad(real_t deg)
+ public static float DegToRad(float deg)
{
- return deg * _degToRadConst;
+ return deg * _degToRadConstF;
+ }
+
+ /// <summary>
+ /// Converts an angle expressed in degrees to radians.
+ /// </summary>
+ /// <param name="deg">An angle expressed in degrees.</param>
+ /// <returns>The same angle expressed in radians.</returns>
+ public static double DegToRad(double deg)
+ {
+ return deg * _degToRadConstD;
}
/// <summary>
@@ -343,38 +661,94 @@ namespace Godot
/// <c>0</c> is constant, <c>1</c> is linear, <c>0</c> to <c>1</c> is ease-in, <c>1</c> or more is ease-out.
/// </param>
/// <returns>The eased value.</returns>
- public static real_t Ease(real_t s, real_t curve)
+ public static float Ease(float s, float curve)
{
- if (s < 0f)
+ if (s < 0.0f)
{
- s = 0f;
+ s = 0.0f;
}
else if (s > 1.0f)
{
s = 1.0f;
}
- if (curve > 0f)
+ if (curve > 0.0f)
{
if (curve < 1.0f)
{
- return 1.0f - Pow(1.0f - s, 1.0f / curve);
+ return 1.0f - MathF.Pow(1.0f - s, 1.0f / curve);
}
- return Pow(s, curve);
+ return MathF.Pow(s, curve);
}
- if (curve < 0f)
+ if (curve < 0.0f)
{
if (s < 0.5f)
{
- return Pow(s * 2.0f, -curve) * 0.5f;
+ return MathF.Pow(s * 2.0f, -curve) * 0.5f;
+ }
+
+ return ((1.0f - MathF.Pow(1.0f - ((s - 0.5f) * 2.0f), -curve)) * 0.5f) + 0.5f;
+ }
+
+ return 0.0f;
+ }
+
+ /// <summary>
+ /// Easing function, based on exponent. The <paramref name="curve"/> values are:
+ /// <c>0</c> is constant, <c>1</c> is linear, <c>0</c> to <c>1</c> is ease-in, <c>1</c> or more is ease-out.
+ /// Negative values are in-out/out-in.
+ /// </summary>
+ /// <param name="s">The value to ease.</param>
+ /// <param name="curve">
+ /// <c>0</c> is constant, <c>1</c> is linear, <c>0</c> to <c>1</c> is ease-in, <c>1</c> or more is ease-out.
+ /// </param>
+ /// <returns>The eased value.</returns>
+ public static double Ease(double s, double curve)
+ {
+ if (s < 0.0)
+ {
+ s = 0.0;
+ }
+ else if (s > 1.0)
+ {
+ s = 1.0;
+ }
+
+ if (curve > 0)
+ {
+ if (curve < 1.0)
+ {
+ return 1.0 - Math.Pow(1.0 - s, 1.0 / curve);
+ }
+
+ return Math.Pow(s, curve);
+ }
+
+ if (curve < 0.0)
+ {
+ if (s < 0.5)
+ {
+ return Math.Pow(s * 2.0, -curve) * 0.5;
}
- return ((1.0f - Pow(1.0f - ((s - 0.5f) * 2.0f), -curve)) * 0.5f) + 0.5f;
+ return ((1.0 - Math.Pow(1.0 - ((s - 0.5) * 2.0), -curve)) * 0.5) + 0.5;
}
- return 0f;
+ return 0.0;
+ }
+
+ /// <summary>
+ /// The natural exponential function. It raises the mathematical
+ /// constant <c>e</c> to the power of <paramref name="s"/> and returns it.
+ /// </summary>
+ /// <param name="s">The exponent to raise <c>e</c> to.</param>
+ /// <returns><c>e</c> raised to the power of <paramref name="s"/>.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Exp(float s)
+ {
+ return MathF.Exp(s);
}
/// <summary>
@@ -383,9 +757,21 @@ namespace Godot
/// </summary>
/// <param name="s">The exponent to raise <c>e</c> to.</param>
/// <returns><c>e</c> raised to the power of <paramref name="s"/>.</returns>
- public static real_t Exp(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Exp(double s)
+ {
+ return Math.Exp(s);
+ }
+
+ /// <summary>
+ /// Rounds <paramref name="s"/> downward (towards negative infinity).
+ /// </summary>
+ /// <param name="s">The number to floor.</param>
+ /// <returns>The largest whole number that is not more than <paramref name="s"/>.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Floor(float s)
{
- return (real_t)Math.Exp(s);
+ return MathF.Floor(s);
}
/// <summary>
@@ -393,14 +779,32 @@ namespace Godot
/// </summary>
/// <param name="s">The number to floor.</param>
/// <returns>The largest whole number that is not more than <paramref name="s"/>.</returns>
- public static real_t Floor(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Floor(double s)
+ {
+ return Math.Floor(s);
+ }
+
+ /// <summary>
+ /// Returns a normalized value considering the given range.
+ /// This is the opposite of <see cref="Lerp(float, float, float)"/>.
+ /// </summary>
+ /// <param name="from">The start value for interpolation.</param>
+ /// <param name="to">The destination value for interpolation.</param>
+ /// <param name="weight">The interpolated value.</param>
+ /// <returns>
+ /// The resulting value of the inverse interpolation.
+ /// The returned value will be between 0.0 and 1.0 if <paramref name="weight"/> is
+ /// between <paramref name="from"/> and <paramref name="to"/> (inclusive).
+ /// </returns>
+ public static float InverseLerp(float from, float to, float weight)
{
- return (real_t)Math.Floor(s);
+ return (weight - from) / (to - from);
}
/// <summary>
/// Returns a normalized value considering the given range.
- /// This is the opposite of <see cref="Lerp(real_t, real_t, real_t)"/>.
+ /// This is the opposite of <see cref="Lerp(double, double, double)"/>.
/// </summary>
/// <param name="from">The start value for interpolation.</param>
/// <param name="to">The destination value for interpolation.</param>
@@ -410,7 +814,7 @@ namespace Godot
/// The returned value will be between 0.0 and 1.0 if <paramref name="weight"/> is
/// between <paramref name="from"/> and <paramref name="to"/> (inclusive).
/// </returns>
- public static real_t InverseLerp(real_t from, real_t to, real_t weight)
+ public static double InverseLerp(double from, double to, double weight)
{
return (weight - from) / (to - from);
}
@@ -423,7 +827,31 @@ namespace Godot
/// <param name="a">One of the values.</param>
/// <param name="b">The other value.</param>
/// <returns>A <see langword="bool"/> for whether or not the two values are approximately equal.</returns>
- public static bool IsEqualApprox(real_t a, real_t b)
+ public static bool IsEqualApprox(float a, float b)
+ {
+ // Check for exact equality first, required to handle "infinity" values.
+ if (a == b)
+ {
+ return true;
+ }
+ // Then check for approximate equality.
+ float tolerance = _epsilonF * Math.Abs(a);
+ if (tolerance < _epsilonF)
+ {
+ tolerance = _epsilonF;
+ }
+ return Math.Abs(a - b) < tolerance;
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if <paramref name="a"/> and <paramref name="b"/> are approximately equal
+ /// to each other.
+ /// The comparison is done using a tolerance calculation with <see cref="Epsilon"/>.
+ /// </summary>
+ /// <param name="a">One of the values.</param>
+ /// <param name="b">The other value.</param>
+ /// <returns>A <see langword="bool"/> for whether or not the two values are approximately equal.</returns>
+ public static bool IsEqualApprox(double a, double b)
{
// Check for exact equality first, required to handle "infinity" values.
if (a == b)
@@ -431,12 +859,24 @@ namespace Godot
return true;
}
// Then check for approximate equality.
- real_t tolerance = Epsilon * Abs(a);
- if (tolerance < Epsilon)
+ double tolerance = _epsilonD * Math.Abs(a);
+ if (tolerance < _epsilonD)
{
- tolerance = Epsilon;
+ tolerance = _epsilonD;
}
- return Abs(a - b) < tolerance;
+ return Math.Abs(a - b) < tolerance;
+ }
+
+ /// <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>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsFinite(float s)
+ {
+ return float.IsFinite(s);
}
/// <summary>
@@ -445,9 +885,21 @@ namespace Godot
/// </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)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsFinite(double s)
+ {
+ return double.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>
+ /// <returns>A <see langword="bool"/> for whether or not the value is an infinity value.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsInf(float s)
{
- return real_t.IsFinite(s);
+ return float.IsInfinity(s);
}
/// <summary>
@@ -455,9 +907,21 @@ namespace Godot
/// </summary>
/// <param name="s">The value to check.</param>
/// <returns>A <see langword="bool"/> for whether or not the value is an infinity value.</returns>
- public static bool IsInf(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsInf(double s)
+ {
+ return double.IsInfinity(s);
+ }
+
+ /// <summary>
+ /// Returns whether <paramref name="s"/> is a <c>NaN</c> ("Not a Number" or invalid) value.
+ /// </summary>
+ /// <param name="s">The value to check.</param>
+ /// <returns>A <see langword="bool"/> for whether or not the value is a <c>NaN</c> value.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsNaN(float s)
{
- return real_t.IsInfinity(s);
+ return float.IsNaN(s);
}
/// <summary>
@@ -465,34 +929,64 @@ namespace Godot
/// </summary>
/// <param name="s">The value to check.</param>
/// <returns>A <see langword="bool"/> for whether or not the value is a <c>NaN</c> value.</returns>
- public static bool IsNaN(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsNaN(double s)
+ {
+ return double.IsNaN(s);
+ }
+
+ /// <summary>
+ /// 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(float, float)"/> 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>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsZeroApprox(float s)
{
- return real_t.IsNaN(s);
+ return Math.Abs(s) < _epsilonF;
}
/// <summary>
/// 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
+ /// This method is faster than using <see cref="IsEqualApprox(double, double)"/> 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>
- public static bool IsZeroApprox(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsZeroApprox(double s)
+ {
+ return Math.Abs(s) < _epsilonD;
+ }
+
+ /// <summary>
+ /// Linearly interpolates between two values by a normalized value.
+ /// This is the opposite <see cref="InverseLerp(float, float, float)"/>.
+ /// </summary>
+ /// <param name="from">The start value for interpolation.</param>
+ /// <param name="to">The destination value for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting value of the interpolation.</returns>
+ public static float Lerp(float from, float to, float weight)
{
- return Abs(s) < Epsilon;
+ return from + ((to - from) * weight);
}
/// <summary>
/// Linearly interpolates between two values by a normalized value.
- /// This is the opposite <see cref="InverseLerp(real_t, real_t, real_t)"/>.
+ /// This is the opposite <see cref="InverseLerp(double, double, double)"/>.
/// </summary>
/// <param name="from">The start value for interpolation.</param>
/// <param name="to">The destination value for interpolation.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The resulting value of the interpolation.</returns>
- public static real_t Lerp(real_t from, real_t to, real_t weight)
+ public static double Lerp(double from, double to, double weight)
{
return from + ((to - from) * weight);
}
@@ -500,30 +994,101 @@ namespace Godot
/// <summary>
/// Linearly interpolates between two angles (in radians) by a normalized value.
///
- /// Similar to <see cref="Lerp(real_t, real_t, real_t)"/>,
+ /// Similar to <see cref="Lerp(float, float, float)"/>,
+ /// but interpolates correctly when the angles wrap around <see cref="Tau"/>.
+ /// </summary>
+ /// <param name="from">The start angle for interpolation.</param>
+ /// <param name="to">The destination angle for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting angle of the interpolation.</returns>
+ public static float LerpAngle(float from, float to, float weight)
+ {
+ float difference = (to - from) % MathF.Tau;
+ float distance = ((2 * difference) % MathF.Tau) - difference;
+ return from + (distance * weight);
+ }
+
+ /// <summary>
+ /// Linearly interpolates between two angles (in radians) by a normalized value.
+ ///
+ /// Similar to <see cref="Lerp(double, double, double)"/>,
/// but interpolates correctly when the angles wrap around <see cref="Tau"/>.
/// </summary>
/// <param name="from">The start angle for interpolation.</param>
/// <param name="to">The destination angle for interpolation.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The resulting angle of the interpolation.</returns>
- public static real_t LerpAngle(real_t from, real_t to, real_t weight)
+ public static double LerpAngle(double from, double to, double weight)
{
- real_t difference = (to - from) % Mathf.Tau;
- real_t distance = ((2 * difference) % Mathf.Tau) - difference;
+ double difference = (to - from) % Math.Tau;
+ double distance = ((2 * difference) % Math.Tau) - difference;
return from + (distance * weight);
}
/// <summary>
+ /// Converts from linear energy to decibels (audio).
+ /// This can be used to implement volume sliders that behave as expected (since volume isn't linear).
+ /// </summary>
+ /// <seealso cref="DbToLinear(float)"/>
+ /// <example>
+ /// <code>
+ /// // "slider" refers to a node that inherits Range such as HSlider or VSlider.
+ /// // Its range must be configured to go from 0 to 1.
+ /// // Change the bus name if you'd like to change the volume of a specific bus only.
+ /// AudioServer.SetBusVolumeDb(AudioServer.GetBusIndex("Master"), GD.LinearToDb(slider.value));
+ /// </code>
+ /// </example>
+ /// <param name="linear">The linear energy to convert.</param>
+ /// <returns>Audio as decibels.</returns>
+ public static float LinearToDb(float linear)
+ {
+ return MathF.Log(linear) * 8.6858896380650365530225783783321f;
+ }
+
+ /// <summary>
+ /// Converts from linear energy to decibels (audio).
+ /// This can be used to implement volume sliders that behave as expected (since volume isn't linear).
+ /// </summary>
+ /// <seealso cref="DbToLinear(double)"/>
+ /// <example>
+ /// <code>
+ /// // "slider" refers to a node that inherits Range such as HSlider or VSlider.
+ /// // Its range must be configured to go from 0 to 1.
+ /// // Change the bus name if you'd like to change the volume of a specific bus only.
+ /// AudioServer.SetBusVolumeDb(AudioServer.GetBusIndex("Master"), GD.LinearToDb(slider.value));
+ /// </code>
+ /// </example>
+ /// <param name="linear">The linear energy to convert.</param>
+ /// <returns>Audio as decibels.</returns>
+ public static double LinearToDb(double linear)
+ {
+ return Math.Log(linear) * 8.6858896380650365530225783783321;
+ }
+
+ /// <summary>
+ /// Natural logarithm. The amount of time needed to reach a certain level of continuous growth.
+ ///
+ /// Note: This is not the same as the "log" function on most calculators, which uses a base 10 logarithm.
+ /// </summary>
+ /// <param name="s">The input value.</param>
+ /// <returns>The natural log of <paramref name="s"/>.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Log(float s)
+ {
+ return MathF.Log(s);
+ }
+
+ /// <summary>
/// Natural logarithm. The amount of time needed to reach a certain level of continuous growth.
///
/// Note: This is not the same as the "log" function on most calculators, which uses a base 10 logarithm.
/// </summary>
/// <param name="s">The input value.</param>
/// <returns>The natural log of <paramref name="s"/>.</returns>
- public static real_t Log(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Log(double s)
{
- return (real_t)Math.Log(s);
+ return Math.Log(s);
}
/// <summary>
@@ -532,9 +1097,22 @@ namespace Godot
/// <param name="a">One of the values.</param>
/// <param name="b">The other value.</param>
/// <returns>Whichever of the two values is higher.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Max(int a, int b)
{
- return a > b ? a : b;
+ return Math.Max(a, b);
+ }
+
+ /// <summary>
+ /// Returns the maximum of two values.
+ /// </summary>
+ /// <param name="a">One of the values.</param>
+ /// <param name="b">The other value.</param>
+ /// <returns>Whichever of the two values is higher.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Max(float a, float b)
+ {
+ return Math.Max(a, b);
}
/// <summary>
@@ -543,9 +1121,10 @@ namespace Godot
/// <param name="a">One of the values.</param>
/// <param name="b">The other value.</param>
/// <returns>Whichever of the two values is higher.</returns>
- public static real_t Max(real_t a, real_t b)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Max(double a, double b)
{
- return a > b ? a : b;
+ return Math.Max(a, b);
}
/// <summary>
@@ -554,9 +1133,22 @@ namespace Godot
/// <param name="a">One of the values.</param>
/// <param name="b">The other value.</param>
/// <returns>Whichever of the two values is lower.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Min(int a, int b)
{
- return a < b ? a : b;
+ return Math.Min(a, b);
+ }
+
+ /// <summary>
+ /// Returns the minimum of two values.
+ /// </summary>
+ /// <param name="a">One of the values.</param>
+ /// <param name="b">The other value.</param>
+ /// <returns>Whichever of the two values is lower.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Min(float a, float b)
+ {
+ return Math.Min(a, b);
}
/// <summary>
@@ -565,9 +1157,27 @@ namespace Godot
/// <param name="a">One of the values.</param>
/// <param name="b">The other value.</param>
/// <returns>Whichever of the two values is lower.</returns>
- public static real_t Min(real_t a, real_t b)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Min(double a, double b)
+ {
+ return Math.Min(a, b);
+ }
+
+ /// <summary>
+ /// Moves <paramref name="from"/> toward <paramref name="to"/> by the <paramref name="delta"/> value.
+ ///
+ /// Use a negative <paramref name="delta"/> value to move away.
+ /// </summary>
+ /// <param name="from">The start value.</param>
+ /// <param name="to">The value to move towards.</param>
+ /// <param name="delta">The amount to move by.</param>
+ /// <returns>The value after moving.</returns>
+ public static float MoveToward(float from, float to, float delta)
{
- return a < b ? a : b;
+ if (Math.Abs(to - from) <= delta)
+ return to;
+
+ return from + (Math.Sign(to - from) * delta);
}
/// <summary>
@@ -579,12 +1189,12 @@ namespace Godot
/// <param name="to">The value to move towards.</param>
/// <param name="delta">The amount to move by.</param>
/// <returns>The value after moving.</returns>
- public static real_t MoveToward(real_t from, real_t to, real_t delta)
+ public static double MoveToward(double from, double to, double delta)
{
- if (Abs(to - from) <= delta)
+ if (Math.Abs(to - from) <= delta)
return to;
- return from + (Sign(to - from) * delta);
+ return from + (Math.Sign(to - from) * delta);
}
/// <summary>
@@ -626,9 +1236,25 @@ namespace Godot
/// <param name="a">The dividend, the primary input.</param>
/// <param name="b">The divisor. The output is on the range [0, <paramref name="b"/>).</param>
/// <returns>The resulting output.</returns>
- public static real_t PosMod(real_t a, real_t b)
+ public static float PosMod(float a, float b)
+ {
+ float c = a % b;
+ if ((c < 0 && b > 0) || (c > 0 && b < 0))
+ {
+ c += b;
+ }
+ return c;
+ }
+
+ /// <summary>
+ /// Performs a canonical Modulus operation, where the output is on the range [0, <paramref name="b"/>).
+ /// </summary>
+ /// <param name="a">The dividend, the primary input.</param>
+ /// <param name="b">The divisor. The output is on the range [0, <paramref name="b"/>).</param>
+ /// <returns>The resulting output.</returns>
+ public static double PosMod(double a, double b)
{
- real_t c = a % b;
+ double c = a % b;
if ((c < 0 && b > 0) || (c > 0 && b < 0))
{
c += b;
@@ -642,9 +1268,33 @@ namespace Godot
/// <param name="x">The base.</param>
/// <param name="y">The exponent.</param>
/// <returns><paramref name="x"/> raised to the power of <paramref name="y"/>.</returns>
- public static real_t Pow(real_t x, real_t y)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Pow(float x, float y)
+ {
+ return MathF.Pow(x, y);
+ }
+
+ /// <summary>
+ /// Returns the result of <paramref name="x"/> raised to the power of <paramref name="y"/>.
+ /// </summary>
+ /// <param name="x">The base.</param>
+ /// <param name="y">The exponent.</param>
+ /// <returns><paramref name="x"/> raised to the power of <paramref name="y"/>.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Pow(double x, double y)
+ {
+ return Math.Pow(x, y);
+ }
+
+ /// <summary>
+ /// Converts an angle expressed in radians to degrees.
+ /// </summary>
+ /// <param name="rad">An angle expressed in radians.</param>
+ /// <returns>The same angle expressed in degrees.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float RadToDeg(float rad)
{
- return (real_t)Math.Pow(x, y);
+ return rad * _radToDegConstF;
}
/// <summary>
@@ -652,9 +1302,25 @@ namespace Godot
/// </summary>
/// <param name="rad">An angle expressed in radians.</param>
/// <returns>The same angle expressed in degrees.</returns>
- public static real_t RadToDeg(real_t rad)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double RadToDeg(double rad)
+ {
+ return rad * _radToDegConstD;
+ }
+
+ /// <summary>
+ /// Maps a <paramref name="value"/> from [<paramref name="inFrom"/>, <paramref name="inTo"/>]
+ /// to [<paramref name="outFrom"/>, <paramref name="outTo"/>].
+ /// </summary>
+ /// <param name="value">The value to map.</param>
+ /// <param name="inFrom">The start value for the input interpolation.</param>
+ /// <param name="inTo">The destination value for the input interpolation.</param>
+ /// <param name="outFrom">The start value for the output interpolation.</param>
+ /// <param name="outTo">The destination value for the output interpolation.</param>
+ /// <returns>The resulting mapped value mapped.</returns>
+ public static float Remap(float value, float inFrom, float inTo, float outFrom, float outTo)
{
- return rad * _radToDegConst;
+ return Lerp(outFrom, outTo, InverseLerp(inFrom, inTo, value));
}
/// <summary>
@@ -667,7 +1333,7 @@ namespace Godot
/// <param name="outFrom">The start value for the output interpolation.</param>
/// <param name="outTo">The destination value for the output interpolation.</param>
/// <returns>The resulting mapped value mapped.</returns>
- public static real_t Remap(real_t value, real_t inFrom, real_t inTo, real_t outFrom, real_t outTo)
+ public static double Remap(double value, double inFrom, double inTo, double outFrom, double outTo)
{
return Lerp(outFrom, outTo, InverseLerp(inFrom, inTo, value));
}
@@ -678,9 +1344,22 @@ namespace Godot
/// </summary>
/// <param name="s">The number to round.</param>
/// <returns>The rounded number.</returns>
- public static real_t Round(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Round(float s)
+ {
+ return MathF.Round(s);
+ }
+
+ /// <summary>
+ /// Rounds <paramref name="s"/> to the nearest whole number,
+ /// with halfway cases rounded towards the nearest multiple of two.
+ /// </summary>
+ /// <param name="s">The number to round.</param>
+ /// <returns>The rounded number.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Round(double s)
{
- return (real_t)Math.Round(s);
+ return Math.Round(s);
}
/// <summary>
@@ -689,11 +1368,22 @@ namespace Godot
/// </summary>
/// <param name="s">The input number.</param>
/// <returns>One of three possible values: <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Sign(int s)
{
- if (s == 0)
- return 0;
- return s < 0 ? -1 : 1;
+ return Math.Sign(s);
+ }
+
+ /// <summary>
+ /// Returns the sign of <paramref name="s"/>: <c>-1</c> or <c>1</c>.
+ /// Returns <c>0</c> if <paramref name="s"/> is <c>0</c>.
+ /// </summary>
+ /// <param name="s">The input number.</param>
+ /// <returns>One of three possible values: <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int Sign(float s)
+ {
+ return Math.Sign(s);
}
/// <summary>
@@ -702,11 +1392,21 @@ namespace Godot
/// </summary>
/// <param name="s">The input number.</param>
/// <returns>One of three possible values: <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
- public static int Sign(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int Sign(double s)
+ {
+ return Math.Sign(s);
+ }
+
+ /// <summary>
+ /// Returns the sine of angle <paramref name="s"/> in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The sine of that angle.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Sin(float s)
{
- if (s == 0)
- return 0;
- return s < 0 ? -1 : 1;
+ return MathF.Sin(s);
}
/// <summary>
@@ -714,9 +1414,21 @@ namespace Godot
/// </summary>
/// <param name="s">The angle in radians.</param>
/// <returns>The sine of that angle.</returns>
- public static real_t Sin(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Sin(double s)
+ {
+ return Math.Sin(s);
+ }
+
+ /// <summary>
+ /// Returns the hyperbolic sine of angle <paramref name="s"/> in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The hyperbolic sine of that angle.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Sinh(float s)
{
- return (real_t)Math.Sin(s);
+ return MathF.Sinh(s);
}
/// <summary>
@@ -724,27 +1436,47 @@ namespace Godot
/// </summary>
/// <param name="s">The angle in radians.</param>
/// <returns>The hyperbolic sine of that angle.</returns>
- public static real_t Sinh(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Sinh(double s)
+ {
+ return Math.Sinh(s);
+ }
+
+ /// <summary>
+ /// Returns a number smoothly interpolated between <paramref name="from"/> and <paramref name="to"/>,
+ /// based on the <paramref name="weight"/>. Similar to <see cref="Lerp(float, float, float)"/>,
+ /// but interpolates faster at the beginning and slower at the end.
+ /// </summary>
+ /// <param name="from">The start value for interpolation.</param>
+ /// <param name="to">The destination value for interpolation.</param>
+ /// <param name="weight">A value representing the amount of interpolation.</param>
+ /// <returns>The resulting value of the interpolation.</returns>
+ public static float SmoothStep(float from, float to, float weight)
{
- return (real_t)Math.Sinh(s);
+ if (IsEqualApprox(from, to))
+ {
+ return from;
+ }
+ float x = Math.Clamp((weight - from) / (to - from), 0.0f, 1.0f);
+ return x * x * (3 - (2 * x));
}
/// <summary>
/// Returns a number smoothly interpolated between <paramref name="from"/> and <paramref name="to"/>,
- /// based on the <paramref name="weight"/>. Similar to <see cref="Lerp(real_t, real_t, real_t)"/>,
+ /// based on the <paramref name="weight"/>. Similar to <see cref="Lerp(double, double, double)"/>,
/// but interpolates faster at the beginning and slower at the end.
/// </summary>
/// <param name="from">The start value for interpolation.</param>
/// <param name="to">The destination value for interpolation.</param>
/// <param name="weight">A value representing the amount of interpolation.</param>
/// <returns>The resulting value of the interpolation.</returns>
- public static real_t SmoothStep(real_t from, real_t to, real_t weight)
+ public static double SmoothStep(double from, double to, double weight)
{
if (IsEqualApprox(from, to))
{
return from;
}
- real_t x = Clamp((weight - from) / (to - from), (real_t)0.0, (real_t)1.0);
+ double x = Math.Clamp((weight - from) / (to - from), 0.0, 1.0);
return x * x * (3 - (2 * x));
}
@@ -755,9 +1487,23 @@ namespace Godot
/// </summary>
/// <param name="s">The input number. Must not be negative.</param>
/// <returns>The square root of <paramref name="s"/>.</returns>
- public static real_t Sqrt(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Sqrt(float s)
+ {
+ return MathF.Sqrt(s);
+ }
+
+ /// <summary>
+ /// Returns the square root of <paramref name="s"/>, where <paramref name="s"/> is a non-negative number.
+ ///
+ /// If you need negative inputs, use <see cref="System.Numerics.Complex"/>.
+ /// </summary>
+ /// <param name="s">The input number. Must not be negative.</param>
+ /// <returns>The square root of <paramref name="s"/>.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Sqrt(double s)
{
- return (real_t)Math.Sqrt(s);
+ return Math.Sqrt(s);
}
/// <summary>
@@ -767,7 +1513,7 @@ namespace Godot
/// </summary>
/// <param name="step">The input value.</param>
/// <returns>The position of the first non-zero digit.</returns>
- public static int StepDecimals(real_t step)
+ public static int StepDecimals(double step)
{
double[] sd = new double[]
{
@@ -781,7 +1527,7 @@ namespace Godot
0.00000009999,
0.000000009999,
};
- double abs = Abs(step);
+ double abs = Math.Abs(step);
double decs = abs - (int)abs; // Strip away integer part
for (int i = 0; i < sd.Length; i++)
{
@@ -800,11 +1546,28 @@ namespace Godot
/// <param name="s">The value to snap.</param>
/// <param name="step">The step size to snap to.</param>
/// <returns>The snapped value.</returns>
- public static real_t Snapped(real_t s, real_t step)
+ public static float Snapped(float s, float step)
+ {
+ if (step != 0f)
+ {
+ return MathF.Floor((s / step) + 0.5f) * step;
+ }
+
+ return s;
+ }
+
+ /// <summary>
+ /// Snaps float value <paramref name="s"/> to a given <paramref name="step"/>.
+ /// This can also be used to round a floating point number to an arbitrary number of decimals.
+ /// </summary>
+ /// <param name="s">The value to snap.</param>
+ /// <param name="step">The step size to snap to.</param>
+ /// <returns>The snapped value.</returns>
+ public static double Snapped(double s, double step)
{
if (step != 0f)
{
- return Floor((s / step) + 0.5f) * step;
+ return Math.Floor((s / step) + 0.5f) * step;
}
return s;
@@ -815,9 +1578,32 @@ namespace Godot
/// </summary>
/// <param name="s">The angle in radians.</param>
/// <returns>The tangent of that angle.</returns>
- public static real_t Tan(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Tan(float s)
+ {
+ return MathF.Tan(s);
+ }
+
+ /// <summary>
+ /// Returns the tangent of angle <paramref name="s"/> in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The tangent of that angle.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Tan(double s)
+ {
+ return Math.Tan(s);
+ }
+
+ /// <summary>
+ /// Returns the hyperbolic tangent of angle <paramref name="s"/> in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The hyperbolic tangent of that angle.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Tanh(float s)
{
- return (real_t)Math.Tan(s);
+ return MathF.Tanh(s);
}
/// <summary>
@@ -825,9 +1611,10 @@ namespace Godot
/// </summary>
/// <param name="s">The angle in radians.</param>
/// <returns>The hyperbolic tangent of that angle.</returns>
- public static real_t Tanh(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Tanh(double s)
{
- return (real_t)Math.Tanh(s);
+ return Math.Tanh(s);
}
/// <summary>
@@ -853,15 +1640,35 @@ namespace Godot
/// Wraps <paramref name="value"/> between <paramref name="min"/> and <paramref name="max"/>.
/// Usable for creating loop-alike behavior or infinite surfaces.
/// If <paramref name="min"/> is <c>0</c>, this is equivalent
- /// to <see cref="PosMod(real_t, real_t)"/>, so prefer using that instead.
+ /// to <see cref="PosMod(float, float)"/>, so prefer using that instead.
+ /// </summary>
+ /// <param name="value">The value to wrap.</param>
+ /// <param name="min">The minimum allowed value and lower bound of the range.</param>
+ /// <param name="max">The maximum allowed value and upper bound of the range.</param>
+ /// <returns>The wrapped value.</returns>
+ public static float Wrap(float value, float min, float max)
+ {
+ float range = max - min;
+ if (IsZeroApprox(range))
+ {
+ return min;
+ }
+ return min + ((((value - min) % range) + range) % range);
+ }
+
+ /// <summary>
+ /// Wraps <paramref name="value"/> between <paramref name="min"/> and <paramref name="max"/>.
+ /// Usable for creating loop-alike behavior or infinite surfaces.
+ /// If <paramref name="min"/> is <c>0</c>, this is equivalent
+ /// to <see cref="PosMod(double, double)"/>, so prefer using that instead.
/// </summary>
/// <param name="value">The value to wrap.</param>
/// <param name="min">The minimum allowed value and lower bound of the range.</param>
/// <param name="max">The maximum allowed value and upper bound of the range.</param>
/// <returns>The wrapped value.</returns>
- public static real_t Wrap(real_t value, real_t min, real_t max)
+ public static double Wrap(double value, double min, double max)
{
- real_t range = max - min;
+ double range = max - min;
if (IsZeroApprox(range))
{
return min;
@@ -869,9 +1676,23 @@ namespace Godot
return min + ((((value - min) % range) + range) % range);
}
- private static real_t Fract(real_t value)
+ /// <summary>
+ /// Returns the <paramref name="value"/> wrapped between <c>0</c> and the <paramref name="length"/>.
+ /// If the limit is reached, the next value the function returned is decreased to the <c>0</c> side
+ /// or increased to the <paramref name="length"/> side (like a triangle wave).
+ /// If <paramref name="length"/> is less than zero, it becomes positive.
+ /// </summary>
+ /// <param name="value">The value to pingpong.</param>
+ /// <param name="length">The maximum value of the function.</param>
+ /// <returns>The ping-ponged value.</returns>
+ public static float PingPong(float value, float length)
{
- return value - (real_t)Math.Floor(value);
+ return (length != 0.0f) ? Math.Abs(Fract((value - length) / (length * 2.0f)) * length * 2.0f - length) : 0.0f;
+
+ static float Fract(float value)
+ {
+ return value - MathF.Floor(value);
+ }
}
/// <summary>
@@ -883,9 +1704,14 @@ namespace Godot
/// <param name="value">The value to pingpong.</param>
/// <param name="length">The maximum value of the function.</param>
/// <returns>The ping-ponged value.</returns>
- public static real_t PingPong(real_t value, real_t length)
+ public static double PingPong(double value, double length)
{
- return (length != (real_t)0.0) ? Abs(Fract((value - length) / (length * (real_t)2.0)) * length * (real_t)2.0 - length) : (real_t)0.0;
+ return (length != 0.0) ? Math.Abs(Fract((value - length) / (length * 2.0)) * length * 2.0 - length) : 0.0;
+
+ static double Fract(double value)
+ {
+ return value - Math.Floor(value);
+ }
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
index 72a1868964..cc2d61f58d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
@@ -1,4 +1,8 @@
using System;
+using System.Runtime.CompilerServices;
+
+// This file contains extra members for the Mathf class that aren't part of Godot's Core API.
+// Math API that is also part of Core should go into Mathf.cs.
namespace Godot
{
@@ -16,14 +20,18 @@ namespace Godot
/// </summary>
public const real_t Sqrt2 = (real_t)1.4142135623730950488016887242M; // 1.4142136f and 1.414213562373095
+ // Epsilon size should depend on the precision used.
+ private const float _epsilonF = 1e-06f;
+ private const double _epsilonD = 1e-14;
+
/// <summary>
/// A very small number used for float comparison with error tolerance.
/// 1e-06 with single-precision floats, but 1e-14 if <c>REAL_T_IS_DOUBLE</c>.
/// </summary>
#if REAL_T_IS_DOUBLE
- public const real_t Epsilon = 1e-14; // Epsilon size should depend on the precision used.
+ public const real_t Epsilon = _epsilonD;
#else
- public const real_t Epsilon = 1e-06f;
+ public const real_t Epsilon = _epsilonF;
#endif
/// <summary>
@@ -31,7 +39,8 @@ namespace Godot
/// </summary>
/// <param name="s">The input value.</param>
/// <returns>The amount of digits.</returns>
- public static int DecimalCount(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int DecimalCount(double s)
{
return DecimalCount((decimal)s);
}
@@ -41,6 +50,7 @@ namespace Godot
/// </summary>
/// <param name="s">The input <see langword="decimal"/> value.</param>
/// <returns>The amount of digits.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int DecimalCount(decimal s)
{
return BitConverter.GetBytes(decimal.GetBits(s)[3])[2];
@@ -49,11 +59,25 @@ namespace Godot
/// <summary>
/// Rounds <paramref name="s"/> upward (towards positive infinity).
///
- /// This is the same as <see cref="Ceil(real_t)"/>, but returns an <see langword="int"/>.
+ /// This is the same as <see cref="Ceil(float)"/>, but returns an <see langword="int"/>.
+ /// </summary>
+ /// <param name="s">The number to ceil.</param>
+ /// <returns>The smallest whole number that is not less than <paramref name="s"/>.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int CeilToInt(float s)
+ {
+ return (int)MathF.Ceiling(s);
+ }
+
+ /// <summary>
+ /// Rounds <paramref name="s"/> upward (towards positive infinity).
+ ///
+ /// This is the same as <see cref="Ceil(double)"/>, but returns an <see langword="int"/>.
/// </summary>
/// <param name="s">The number to ceil.</param>
/// <returns>The smallest whole number that is not less than <paramref name="s"/>.</returns>
- public static int CeilToInt(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int CeilToInt(double s)
{
return (int)Math.Ceiling(s);
}
@@ -61,11 +85,25 @@ namespace Godot
/// <summary>
/// Rounds <paramref name="s"/> downward (towards negative infinity).
///
- /// This is the same as <see cref="Floor(real_t)"/>, but returns an <see langword="int"/>.
+ /// This is the same as <see cref="Floor(float)"/>, but returns an <see langword="int"/>.
+ /// </summary>
+ /// <param name="s">The number to floor.</param>
+ /// <returns>The largest whole number that is not more than <paramref name="s"/>.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int FloorToInt(float s)
+ {
+ return (int)MathF.Floor(s);
+ }
+
+ /// <summary>
+ /// Rounds <paramref name="s"/> downward (towards negative infinity).
+ ///
+ /// This is the same as <see cref="Floor(double)"/>, but returns an <see langword="int"/>.
/// </summary>
/// <param name="s">The number to floor.</param>
/// <returns>The largest whole number that is not more than <paramref name="s"/>.</returns>
- public static int FloorToInt(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int FloorToInt(double s)
{
return (int)Math.Floor(s);
}
@@ -73,11 +111,25 @@ namespace Godot
/// <summary>
/// Rounds <paramref name="s"/> to the nearest whole number.
///
- /// This is the same as <see cref="Round(real_t)"/>, but returns an <see langword="int"/>.
+ /// This is the same as <see cref="Round(float)"/>, but returns an <see langword="int"/>.
/// </summary>
/// <param name="s">The number to round.</param>
/// <returns>The rounded number.</returns>
- public static int RoundToInt(real_t s)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int RoundToInt(float s)
+ {
+ return (int)MathF.Round(s);
+ }
+
+ /// <summary>
+ /// Rounds <paramref name="s"/> to the nearest whole number.
+ ///
+ /// This is the same as <see cref="Round(double)"/>, but returns an <see langword="int"/>.
+ /// </summary>
+ /// <param name="s">The number to round.</param>
+ /// <returns>The rounded number.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int RoundToInt(double s)
{
return (int)Math.Round(s);
}
@@ -87,23 +139,53 @@ namespace Godot
/// </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)
+ public static (float Sin, float Cos) SinCos(float s)
+ {
+ return MathF.SinCos(s);
+ }
+
+ /// <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 (double Sin, double Cos) SinCos(double s)
{
- (double sin, double cos) = Math.SinCos(s);
- return ((real_t)sin, (real_t)cos);
+ return Math.SinCos(s);
+ }
+
+ /// <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.
+ /// If you want the tolerance to be calculated for you, use <see cref="IsEqualApprox(float, float)"/>.
+ /// </summary>
+ /// <param name="a">One of the values.</param>
+ /// <param name="b">The other value.</param>
+ /// <param name="tolerance">The pre-calculated tolerance value.</param>
+ /// <returns>A <see langword="bool"/> for whether or not the two values are equal.</returns>
+ public static bool IsEqualApprox(float a, float b, float tolerance)
+ {
+ // Check for exact equality first, required to handle "infinity" values.
+ if (a == b)
+ {
+ return true;
+ }
+ // Then check for approximate equality.
+ return Math.Abs(a - b) < tolerance;
}
/// <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.
- /// If you want the tolerance to be calculated for you, use <see cref="IsEqualApprox(real_t, real_t)"/>.
+ /// If you want the tolerance to be calculated for you, use <see cref="IsEqualApprox(double, double)"/>.
/// </summary>
/// <param name="a">One of the values.</param>
/// <param name="b">The other value.</param>
/// <param name="tolerance">The pre-calculated tolerance value.</param>
/// <returns>A <see langword="bool"/> for whether or not the two values are equal.</returns>
- public static bool IsEqualApprox(real_t a, real_t b, real_t tolerance)
+ public static bool IsEqualApprox(double a, double b, double tolerance)
{
// Check for exact equality first, required to handle "infinity" values.
if (a == b)
@@ -111,7 +193,7 @@ namespace Godot
return true;
}
// Then check for approximate equality.
- return Abs(a - b) < tolerance;
+ return Math.Abs(a - b) < tolerance;
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
index 0d9a698af0..11e600ca0b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
@@ -310,7 +310,7 @@ namespace Godot.NativeInterop
public static Signal ConvertSignalToManaged(in godot_signal p_signal)
{
- var owner = GD.InstanceFromId(p_signal.ObjectId);
+ var owner = Godot.Object.InstanceFromId(p_signal.ObjectId);
var name = StringName.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_string_name_new_copy(p_signal.Name));
return new Signal(owner, name);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
index 57488bd586..66c94166ae 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
@@ -409,6 +409,10 @@ namespace Godot.NativeInterop
public static partial void godotsharp_dictionary_duplicate(ref godot_dictionary p_self, godot_bool p_deep,
out godot_dictionary r_dest);
+ public static partial void godotsharp_dictionary_merge(ref godot_dictionary p_self, in godot_dictionary p_dictionary, godot_bool p_overwrite);
+
+ public static partial godot_bool godotsharp_dictionary_recursive_equal(ref godot_dictionary p_self, in godot_dictionary p_other);
+
public static partial godot_bool godotsharp_dictionary_remove_key(ref godot_dictionary p_self,
in godot_variant p_key);
@@ -494,8 +498,6 @@ namespace Godot.NativeInterop
internal static partial void godotsharp_weakref(IntPtr p_obj, out godot_ref r_weak_ref);
- internal static partial void godotsharp_str(in godot_array p_what, out godot_string r_ret);
-
internal static partial void godotsharp_str_to_var(in godot_string p_str, out godot_variant r_ret);
internal static partial void godotsharp_var_to_bytes(in godot_variant p_what, godot_bool p_full_objects,
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
index 9c9258dd9e..bb306ddaea 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
@@ -260,7 +260,7 @@ namespace Godot.NativeInterop
=> from != null ? CreateFromArray((godot_array)from.NativeValue) : default;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static godot_variant CreateFromArray<T>(Array<T>? from)
+ public static godot_variant CreateFromArray<[MustBeVariant] T>(Array<T>? from)
=> from != null ? CreateFromArray((godot_array)((Collections.Array)from).NativeValue) : default;
public static godot_variant CreateFromDictionary(godot_dictionary from)
@@ -274,7 +274,7 @@ namespace Godot.NativeInterop
=> from != null ? CreateFromDictionary((godot_dictionary)from.NativeValue) : default;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static godot_variant CreateFromDictionary<TKey, TValue>(Dictionary<TKey, TValue>? from)
+ public static godot_variant CreateFromDictionary<[MustBeVariant] TKey, [MustBeVariant] TValue>(Dictionary<TKey, TValue>? from)
=> from != null ? CreateFromDictionary((godot_dictionary)((Dictionary)from).NativeValue) : default;
public static godot_variant CreateFromStringName(godot_string_name from)
@@ -526,7 +526,7 @@ namespace Godot.NativeInterop
=> Collections.Array.CreateTakingOwnershipOfDisposableValue(ConvertToNativeArray(p_var));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Array<T> ConvertToArray<T>(in godot_variant p_var)
+ public static Array<T> ConvertToArray<[MustBeVariant] T>(in godot_variant p_var)
=> Array<T>.CreateTakingOwnershipOfDisposableValue(ConvertToNativeArray(p_var));
public static godot_dictionary ConvertToNativeDictionary(in godot_variant p_var)
@@ -539,7 +539,7 @@ namespace Godot.NativeInterop
=> Dictionary.CreateTakingOwnershipOfDisposableValue(ConvertToNativeDictionary(p_var));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Dictionary<TKey, TValue> ConvertToDictionary<TKey, TValue>(in godot_variant p_var)
+ public static Dictionary<TKey, TValue> ConvertToDictionary<[MustBeVariant] TKey, [MustBeVariant] TValue>(in godot_variant p_var)
=> Dictionary<TKey, TValue>.CreateTakingOwnershipOfDisposableValue(ConvertToNativeDictionary(p_var));
public static byte[] ConvertAsPackedByteArrayToSystemArray(in godot_variant p_var)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
index fa060e3a53..1dfa83ffc6 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
@@ -1,4 +1,5 @@
using System;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Godot
@@ -32,6 +33,23 @@ namespace Godot
public Vector2 origin;
/// <summary>
+ /// Returns the transform's rotation (in radians).
+ /// </summary>
+ public readonly real_t Rotation => Mathf.Atan2(x.y, x.x);
+
+ /// <summary>
+ /// Returns the scale.
+ /// </summary>
+ public readonly Vector2 Scale
+ {
+ get
+ {
+ real_t detSign = Mathf.Sign(BasisDeterminant());
+ return new Vector2(x.Length(), detSign * y.Length());
+ }
+ }
+
+ /// <summary>
/// Access whole columns in the form of <see cref="Vector2"/>.
/// The third column is the <see cref="origin"/> vector.
/// </summary>
@@ -131,6 +149,7 @@ namespace Godot
/// and is usually considered invalid.
/// </summary>
/// <returns>The determinant of the basis matrix.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private readonly real_t BasisDeterminant()
{
return (x.x * y.y) - (x.y * y.x);
@@ -164,23 +183,6 @@ 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>
@@ -188,11 +190,11 @@ namespace Godot
/// <returns>The interpolated transform.</returns>
public readonly Transform2D InterpolateWith(Transform2D transform, real_t weight)
{
- real_t r1 = GetRotation();
- real_t r2 = transform.GetRotation();
+ real_t r1 = Rotation;
+ real_t r2 = transform.Rotation;
- Vector2 s1 = GetScale();
- Vector2 s2 = transform.GetScale();
+ Vector2 s1 = Scale;
+ Vector2 s2 = transform.Scale;
// Slerp rotation
(real_t sin1, real_t cos1) = Mathf.SinCos(r1);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
index 6b2475fc59..ce5f98a6fc 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
@@ -124,11 +124,11 @@ namespace Godot
/// <returns>The interpolated transform.</returns>
public readonly Transform3D InterpolateWith(Transform3D transform, real_t weight)
{
- Vector3 sourceScale = basis.GetScale();
+ Vector3 sourceScale = basis.Scale;
Quaternion sourceRotation = basis.GetRotationQuaternion();
Vector3 sourceLocation = origin;
- Vector3 destinationScale = transform.basis.GetScale();
+ Vector3 destinationScale = transform.basis.Scale;
Quaternion destinationRotation = transform.basis.GetRotationQuaternion();
Vector3 destinationLocation = transform.origin;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs
index da12309217..0ac2e2969c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs
@@ -328,11 +328,11 @@ public partial struct Variant : IDisposable
VariantUtils.ConvertToSystemArrayOfGodotObject<T>((godot_variant)NativeVar);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Collections.Dictionary<TKey, TValue> AsGodotDictionary<TKey, TValue>() =>
+ public Collections.Dictionary<TKey, TValue> AsGodotDictionary<[MustBeVariant] TKey, [MustBeVariant] TValue>() =>
VariantUtils.ConvertToDictionary<TKey, TValue>((godot_variant)NativeVar);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Collections.Array<T> AsGodotArray<T>() =>
+ public Collections.Array<T> AsGodotArray<[MustBeVariant] T>() =>
VariantUtils.ConvertToArray<T>((godot_variant)NativeVar);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -647,11 +647,11 @@ public partial struct Variant : IDisposable
public static Variant CreateFrom(Godot.Object[] from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Variant CreateFrom<TKey, TValue>(Collections.Dictionary<TKey, TValue> from) =>
+ public static Variant CreateFrom<[MustBeVariant] TKey, [MustBeVariant] TValue>(Collections.Dictionary<TKey, TValue> from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromDictionary(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Variant CreateFrom<T>(Collections.Array<T> from) =>
+ public static Variant CreateFrom<[MustBeVariant] T>(Collections.Array<T> from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromArray(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp
index f0ea0313ea..55e09f0501 100644
--- a/modules/mono/glue/runtime_interop.cpp
+++ b/modules/mono/glue/runtime_interop.cpp
@@ -1066,6 +1066,14 @@ void godotsharp_dictionary_duplicate(const Dictionary *p_self, bool p_deep, Dict
memnew_placement(r_dest, Dictionary(p_self->duplicate(p_deep)));
}
+void godotsharp_dictionary_merge(Dictionary *p_self, const Dictionary *p_dictionary, bool p_overwrite) {
+ p_self->merge(*p_dictionary, p_overwrite);
+}
+
+bool godotsharp_dictionary_recursive_equal(const Dictionary *p_self, const Dictionary *p_other) {
+ return p_self->recursive_equal(*p_other, 0);
+}
+
bool godotsharp_dictionary_remove_key(Dictionary *p_self, const Variant *p_key) {
return p_self->erase(*p_key);
}
@@ -1180,21 +1188,6 @@ void godotsharp_weakref(Object *p_ptr, Ref<RefCounted> *r_weak_ref) {
memnew_placement(r_weak_ref, Ref<RefCounted>(wref));
}
-void godotsharp_str(const godot_array *p_what, godot_string *r_ret) {
- String &str = *memnew_placement(r_ret, String);
- const Array &what = *reinterpret_cast<const Array *>(p_what);
-
- for (int i = 0; i < what.size(); i++) {
- String os = what[i].operator String();
-
- if (i == 0) {
- str = os;
- } else {
- str += os;
- }
- }
-}
-
void godotsharp_print(const godot_string *p_what) {
print_line(*reinterpret_cast<const String *>(p_what));
}
@@ -1455,6 +1448,8 @@ static const void *unmanaged_callbacks[]{
(void *)godotsharp_dictionary_clear,
(void *)godotsharp_dictionary_contains_key,
(void *)godotsharp_dictionary_duplicate,
+ (void *)godotsharp_dictionary_merge,
+ (void *)godotsharp_dictionary_recursive_equal,
(void *)godotsharp_dictionary_remove_key,
(void *)godotsharp_dictionary_to_string,
(void *)godotsharp_string_simplify_path,
@@ -1488,7 +1483,6 @@ static const void *unmanaged_callbacks[]{
(void *)godotsharp_rand_from_seed,
(void *)godotsharp_seed,
(void *)godotsharp_weakref,
- (void *)godotsharp_str,
(void *)godotsharp_str_to_var,
(void *)godotsharp_var_to_bytes,
(void *)godotsharp_var_to_str,
diff --git a/modules/multiplayer/editor/replication_editor.cpp b/modules/multiplayer/editor/replication_editor.cpp
index 66e12a338a..9e542828ee 100644
--- a/modules/multiplayer/editor/replication_editor.cpp
+++ b/modules/multiplayer/editor/replication_editor.cpp
@@ -172,7 +172,7 @@ ReplicationEditor::ReplicationEditor() {
set_custom_minimum_size(Size2(0, 200) * EDSCALE);
delete_dialog = memnew(ConfirmationDialog);
- delete_dialog->connect("cancelled", callable_mp(this, &ReplicationEditor::_dialog_closed).bind(false));
+ delete_dialog->connect("canceled", callable_mp(this, &ReplicationEditor::_dialog_closed).bind(false));
delete_dialog->connect("confirmed", callable_mp(this, &ReplicationEditor::_dialog_closed).bind(true));
add_child(delete_dialog);
diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp
index 233ff76c7d..3466cb10df 100644
--- a/modules/multiplayer/scene_replication_interface.cpp
+++ b/modules/multiplayer/scene_replication_interface.cpp
@@ -125,7 +125,7 @@ void SceneReplicationInterface::on_reset() {
}
void SceneReplicationInterface::on_network_process() {
- // Prevent endless stalling in case of unforseen spawn errors.
+ // Prevent endless stalling in case of unforeseen 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) {
diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
index 54f7abda8d..557d45b386 100644
--- a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
+++ b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
@@ -60,12 +60,40 @@ void NavigationMeshEditor::_bake_pressed() {
button_bake->set_pressed(false);
ERR_FAIL_COND(!node);
- if (!node->get_navigation_mesh().is_valid()) {
+ Ref<NavigationMesh> navmesh = node->get_navigation_mesh();
+ if (!navmesh.is_valid()) {
err_dialog->set_text(TTR("A NavigationMesh resource must be set or created for this node to work."));
err_dialog->popup_centered();
return;
}
+ String path = navmesh->get_path();
+ if (!path.is_resource_file()) {
+ int srpos = path.find("::");
+ if (srpos != -1) {
+ String base = path.substr(0, srpos);
+ if (ResourceLoader::get_resource_type(base) == "PackedScene") {
+ if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
+ err_dialog->set_text(TTR("Cannot generate navigation mesh because it does not belong to the edited scene. Make it unique first."));
+ err_dialog->popup_centered();
+ return;
+ }
+ } else {
+ if (FileAccess::exists(base + ".import")) {
+ err_dialog->set_text(TTR("Cannot generate navigation mesh because it belongs to a resource which was imported."));
+ err_dialog->popup_centered();
+ return;
+ }
+ }
+ }
+ } else {
+ if (FileAccess::exists(path + ".import")) {
+ err_dialog->set_text(TTR("Cannot generate navigation mesh because the resource was imported from another type."));
+ err_dialog->popup_centered();
+ return;
+ }
+ }
+
NavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh());
NavigationMeshGenerator::get_singleton()->bake(node->get_navigation_mesh(), node);
diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp
index 2b5db6462c..5baf6db2e8 100644
--- a/modules/navigation/godot_navigation_server.cpp
+++ b/modules/navigation/godot_navigation_server.cpp
@@ -535,32 +535,32 @@ uint32_t GodotNavigationServer::link_get_navigation_layers(const RID p_link) con
return link->get_navigation_layers();
}
-COMMAND_2(link_set_start_location, RID, p_link, Vector3, p_location) {
+COMMAND_2(link_set_start_position, RID, p_link, Vector3, p_position) {
NavLink *link = link_owner.get_or_null(p_link);
ERR_FAIL_COND(link == nullptr);
- link->set_start_location(p_location);
+ link->set_start_position(p_position);
}
-Vector3 GodotNavigationServer::link_get_start_location(RID p_link) const {
+Vector3 GodotNavigationServer::link_get_start_position(RID p_link) const {
const NavLink *link = link_owner.get_or_null(p_link);
ERR_FAIL_COND_V(link == nullptr, Vector3());
- return link->get_start_location();
+ return link->get_start_position();
}
-COMMAND_2(link_set_end_location, RID, p_link, Vector3, p_location) {
+COMMAND_2(link_set_end_position, RID, p_link, Vector3, p_position) {
NavLink *link = link_owner.get_or_null(p_link);
ERR_FAIL_COND(link == nullptr);
- link->set_end_location(p_location);
+ link->set_end_position(p_position);
}
-Vector3 GodotNavigationServer::link_get_end_location(RID p_link) const {
+Vector3 GodotNavigationServer::link_get_end_position(RID p_link) const {
const NavLink *link = link_owner.get_or_null(p_link);
ERR_FAIL_COND_V(link == nullptr, Vector3());
- return link->get_end_location();
+ return link->get_end_position();
}
COMMAND_2(link_set_enter_cost, RID, p_link, real_t, p_enter_cost) {
diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h
index a87a88d3bc..efefa60de8 100644
--- a/modules/navigation/godot_navigation_server.h
+++ b/modules/navigation/godot_navigation_server.h
@@ -158,10 +158,10 @@ public:
virtual bool link_is_bidirectional(RID p_link) const override;
COMMAND_2(link_set_navigation_layers, RID, p_link, uint32_t, p_navigation_layers);
virtual uint32_t link_get_navigation_layers(RID p_link) const override;
- COMMAND_2(link_set_start_location, RID, p_link, Vector3, p_location);
- virtual Vector3 link_get_start_location(RID p_link) const override;
- COMMAND_2(link_set_end_location, RID, p_link, Vector3, p_location);
- virtual Vector3 link_get_end_location(RID p_link) const override;
+ COMMAND_2(link_set_start_position, RID, p_link, Vector3, p_position);
+ virtual Vector3 link_get_start_position(RID p_link) const override;
+ COMMAND_2(link_set_end_position, RID, p_link, Vector3, p_position);
+ virtual Vector3 link_get_end_position(RID p_link) const override;
COMMAND_2(link_set_enter_cost, RID, p_link, real_t, p_enter_cost);
virtual real_t link_get_enter_cost(RID p_link) const override;
COMMAND_2(link_set_travel_cost, RID, p_link, real_t, p_travel_cost);
diff --git a/modules/navigation/nav_link.cpp b/modules/navigation/nav_link.cpp
index 05d2b21487..ad87cc0b05 100644
--- a/modules/navigation/nav_link.cpp
+++ b/modules/navigation/nav_link.cpp
@@ -42,13 +42,13 @@ void NavLink::set_bidirectional(bool p_bidirectional) {
link_dirty = true;
}
-void NavLink::set_start_location(const Vector3 p_location) {
- start_location = p_location;
+void NavLink::set_start_position(const Vector3 p_position) {
+ start_position = p_position;
link_dirty = true;
}
-void NavLink::set_end_location(const Vector3 p_location) {
- end_location = p_location;
+void NavLink::set_end_position(const Vector3 p_position) {
+ end_position = p_position;
link_dirty = true;
}
diff --git a/modules/navigation/nav_link.h b/modules/navigation/nav_link.h
index 47c1211db8..0b8ad4db69 100644
--- a/modules/navigation/nav_link.h
+++ b/modules/navigation/nav_link.h
@@ -37,8 +37,8 @@
class NavLink : public NavBase {
NavMap *map = nullptr;
bool bidirectional = true;
- Vector3 start_location;
- Vector3 end_location;
+ Vector3 start_position;
+ Vector3 end_position;
bool link_dirty = true;
@@ -57,14 +57,14 @@ public:
return bidirectional;
}
- void set_start_location(Vector3 p_location);
- Vector3 get_start_location() const {
- return start_location;
+ void set_start_position(Vector3 p_position);
+ Vector3 get_start_position() const {
+ return start_position;
}
- void set_end_location(Vector3 p_location);
- Vector3 get_end_location() const {
- return end_location;
+ void set_end_position(Vector3 p_position);
+ Vector3 get_end_position() const {
+ return end_position;
}
bool check_dirty();
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index 7090588c6e..d763b1d3bc 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -225,7 +225,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
avp.entry = new_entry;
}
} else {
- // Add the neighbour polygon to the reachable ones.
+ // Add the neighbor polygon to the reachable ones.
gd::NavigationPoly new_navigation_poly = gd::NavigationPoly(connection.polygon);
new_navigation_poly.self_id = navigation_polys.size();
new_navigation_poly.back_navigation_poly_id = least_cost_id;
@@ -236,7 +236,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
new_navigation_poly.entry = new_entry;
navigation_polys.push_back(new_navigation_poly);
- // Add the neighbour polygon to the polygons to visit.
+ // Add the neighbor polygon to the polygons to visit.
to_visit.push_back(navigation_polys.size() - 1);
}
}
@@ -780,8 +780,8 @@ void NavMap::sync() {
// Search for polygons within range of a nav link.
for (const NavLink *link : links) {
- const Vector3 start = link->get_start_location();
- const Vector3 end = link->get_end_location();
+ const Vector3 start = link->get_start_position();
+ const Vector3 end = link->get_end_position();
gd::Polygon *closest_start_polygon = nullptr;
real_t closest_start_distance = link_connection_radius;
diff --git a/modules/navigation/nav_utils.h b/modules/navigation/nav_utils.h
index 50437469aa..06a1a1f403 100644
--- a/modules/navigation/nav_utils.h
+++ b/modules/navigation/nav_utils.h
@@ -125,7 +125,7 @@ struct NavigationPoly {
Vector3 back_navigation_edge_pathway_start;
Vector3 back_navigation_edge_pathway_end;
- /// The entry location of this poly.
+ /// The entry position of this poly.
Vector3 entry;
/// The distance to the destination.
float traveled_distance = 0.0;
diff --git a/modules/openxr/doc_classes/OpenXRAction.xml b/modules/openxr/doc_classes/OpenXRAction.xml
index a3a45ebb4c..d53648723a 100644
--- a/modules/openxr/doc_classes/OpenXRAction.xml
+++ b/modules/openxr/doc_classes/OpenXRAction.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
This resource defines an OpenXR action. Actions can be used both for inputs (buttons/joystick/trigger/etc) and outputs (haptics).
- OpenXR performs automatic conversion between action type and input type whenever possible. An analogue trigger bound to a boolean action will thus return [code]false[/code] if the trigger is depressed and [code]true[/code] if pressed fully.
+ OpenXR performs automatic conversion between action type and input type whenever possible. An analog trigger bound to a boolean action will thus return [code]false[/code] if the trigger is depressed and [code]true[/code] if pressed fully.
Actions are not directly bound to specific devices, instead OpenXR recognizes a limited number of top level paths that identify devices by usage. We can restrict which devices an action can be bound to by these top level paths. For instance an action that should only be used for hand held controllers can have the top level paths "/user/hand/left" and "/user/hand/right" associated with them. See the [url=https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#semantic-path-reserved]reserved path section in the OpenXR specification[/url] for more info on the top level paths.
Note that the name of the resource is used to register the action with.
</description>
@@ -27,7 +27,7 @@
This action provides a boolean value.
</constant>
<constant name="OPENXR_ACTION_FLOAT" value="1" enum="ActionType">
- This action provides a float value between [code]0.0[/code] and [code]1.0[/code] for any analogue input such as triggers.
+ This action provides a float value between [code]0.0[/code] and [code]1.0[/code] for any analog input such as triggers.
</constant>
<constant name="OPENXR_ACTION_VECTOR2" value="2" enum="ActionType">
This action provides a vector2 value and can be bound to embedded trackpads and joysticks
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index 6b8f140923..0a25cd68b7 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -751,7 +751,7 @@ bool OpenXRAPI::create_swapchains() {
Also Godot only creates a swapchain for the main output.
OpenXR will require us to create swapchains as the render target for additional viewports if we want to use the layer system
- to optimise text rendering and background rendering as OpenXR may choose to re-use the results for reprojection while we're
+ to optimize text rendering and background rendering as OpenXR may choose to re-use the results for reprojection while we're
already rendering the next frame.
Finally an area we need to expand upon is that Foveated rendering is only enabled for the swap chain we create,
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index 52a1af5a09..e1787c6da0 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -53,7 +53,7 @@
#include "util.h"
-// Note, OpenXR code that we wrote for our plugin makes use of C++20 notation for initialising structs which ensures zeroing out unspecified members.
+// Note, OpenXR code that we wrote for our plugin makes use of C++20 notation for initializing structs which ensures zeroing out unspecified members.
// Godot is currently restricted to C++17 which doesn't allow this notation. Make sure critical fields are set.
// forward declarations, we don't want to include these fully
diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp
index 1c4d53c43a..306a2f1bbd 100644
--- a/modules/openxr/register_types.cpp
+++ b/modules/openxr/register_types.cpp
@@ -70,7 +70,7 @@ static void _editor_init() {
// Only add our OpenXR action map editor if OpenXR is enabled for our project
if (openxr_interaction_profile_meta_data == nullptr) {
- // If we didn't initialize our actionmap meta data at startup, we initialise it now.
+ // If we didn't initialize our actionmap meta data at startup, we initialize it now.
openxr_interaction_profile_meta_data = memnew(OpenXRInteractionProfileMetaData);
ERR_FAIL_NULL(openxr_interaction_profile_meta_data);
}
@@ -85,7 +85,7 @@ static void _editor_init() {
void initialize_openxr_module(ModuleInitializationLevel p_level) {
if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
if (OpenXRAPI::openxr_is_enabled(false)) {
- // Always register our extension wrappers even if we don't initialise OpenXR.
+ // Always register our extension wrappers even if we don't initialize OpenXR.
// Some of these wrappers will add functionality to our editor.
#ifdef ANDROID_ENABLED
OpenXRAPI::register_extension_wrapper(memnew(OpenXRAndroidExtension));
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 79ca4a7024..e53aef965a 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -443,19 +443,11 @@ bool TextServerAdvanced::_load_support_data(const String &p_filename) {
}
String TextServerAdvanced::_get_support_data_filename() const {
-#ifdef ICU_STATIC_DATA
return _MKSTR(ICU_DATA_NAME);
-#else
- return String();
-#endif
}
String TextServerAdvanced::_get_support_data_info() const {
-#ifdef ICU_STATIC_DATA
return String("ICU break iteration data (") + _MKSTR(ICU_DATA_NAME) + String(").");
-#else
- return String();
-#endif
}
bool TextServerAdvanced::_save_support_data(const String &p_filename) const {
@@ -4136,8 +4128,9 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
if (p_sd->bidi_override[ov].x >= p_start + p_length || p_sd->bidi_override[ov].y <= p_start) {
continue;
}
- int start = _convert_pos_inv(p_sd, MAX(0, p_start - p_sd->bidi_override[ov].x));
- int end = _convert_pos_inv(p_sd, MIN(p_start + p_length, p_sd->bidi_override[ov].y) - p_sd->bidi_override[ov].x);
+ int ov_start = _convert_pos_inv(p_sd, p_sd->bidi_override[ov].x);
+ int start = MAX(0, _convert_pos_inv(p_sd, p_start) - ov_start);
+ int end = MIN(_convert_pos_inv(p_sd, p_start + p_length), _convert_pos_inv(p_sd, p_sd->bidi_override[ov].y)) - ov_start;
ERR_FAIL_COND_V_MSG((start < 0 || end - start > p_new_sd->utf16.length()), false, "Invalid BiDi override range.");
@@ -4159,8 +4152,8 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
int32_t _bidi_run_length = 0;
ubidi_getVisualRun(bidi_iter, i, &_bidi_run_start, &_bidi_run_length);
- int32_t bidi_run_start = _convert_pos(p_sd, p_sd->bidi_override[ov].x + start + _bidi_run_start);
- int32_t bidi_run_end = _convert_pos(p_sd, p_sd->bidi_override[ov].x + start + _bidi_run_start + _bidi_run_length);
+ int32_t bidi_run_start = _convert_pos(p_sd, ov_start + start + _bidi_run_start);
+ int32_t bidi_run_end = _convert_pos(p_sd, ov_start + start + _bidi_run_start + _bidi_run_length);
for (int j = 0; j < sd_size; j++) {
if ((sd_glyphs[j].start >= bidi_run_start) && (sd_glyphs[j].end <= bidi_run_end)) {
@@ -5587,7 +5580,7 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
}
UErrorCode err = U_ZERO_ERROR;
- UBiDi *bidi_iter = ubidi_openSized(end, 0, &err);
+ UBiDi *bidi_iter = ubidi_openSized(end - start, 0, &err);
ERR_FAIL_COND_V_MSG(U_FAILURE(err), false, u_errorName(err));
switch (static_cast<TextServer::Direction>(sd->bidi_override[ov].z)) {
@@ -5605,7 +5598,7 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
if (direction != UBIDI_NEUTRAL) {
ubidi_setPara(bidi_iter, data + start, end - start, direction, nullptr, &err);
} else {
- ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_DEFAULT_LTR, nullptr, &err);
+ ubidi_setPara(bidi_iter, data + start, end - start, base_para_direction, nullptr, &err);
}
} break;
}
@@ -5637,8 +5630,8 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
}
}
- int32_t bidi_run_start = _convert_pos(sd, sd->bidi_override[ov].x - sd->start + _bidi_run_start);
- int32_t bidi_run_end = _convert_pos(sd, sd->bidi_override[ov].x - sd->start + _bidi_run_start + _bidi_run_length);
+ int32_t bidi_run_start = _convert_pos(sd, start + _bidi_run_start);
+ int32_t bidi_run_end = _convert_pos(sd, start + _bidi_run_start + _bidi_run_length);
// Shape runs.
@@ -6113,6 +6106,11 @@ String TextServerAdvanced::_percent_sign(const String &p_language) const {
}
int64_t TextServerAdvanced::_is_confusable(const String &p_string, const PackedStringArray &p_dict) const {
+#ifndef ICU_STATIC_DATA
+ if (!icu_data_loaded) {
+ return -1;
+ }
+#endif
UErrorCode status = U_ZERO_ERROR;
int64_t match_index = -1;
@@ -6153,6 +6151,11 @@ int64_t TextServerAdvanced::_is_confusable(const String &p_string, const PackedS
}
bool TextServerAdvanced::_spoof_check(const String &p_string) const {
+#ifndef ICU_STATIC_DATA
+ if (!icu_data_loaded) {
+ return false;
+ }
+#endif
UErrorCode status = U_ZERO_ERROR;
Char16String utf16 = p_string.utf16();
@@ -6175,6 +6178,11 @@ bool TextServerAdvanced::_spoof_check(const String &p_string) const {
}
String TextServerAdvanced::_strip_diacritics(const String &p_string) const {
+#ifndef ICU_STATIC_DATA
+ if (!icu_data_loaded) {
+ return TextServer::strip_diacritics(p_string);
+ }
+#endif
UErrorCode err = U_ZERO_ERROR;
// Get NFKD normalizer singleton.
@@ -6212,6 +6220,12 @@ String TextServerAdvanced::_strip_diacritics(const String &p_string) const {
}
String TextServerAdvanced::_string_to_upper(const String &p_string, const String &p_language) const {
+#ifndef ICU_STATIC_DATA
+ if (!icu_data_loaded) {
+ return p_string.to_upper();
+ }
+#endif
+
if (p_string.is_empty()) {
return p_string;
}
@@ -6234,6 +6248,12 @@ String TextServerAdvanced::_string_to_upper(const String &p_string, const String
}
String TextServerAdvanced::_string_to_lower(const String &p_string, const String &p_language) const {
+#ifndef ICU_STATIC_DATA
+ if (!icu_data_loaded) {
+ return p_string.to_lower();
+ }
+#endif
+
if (p_string.is_empty()) {
return p_string;
}
@@ -6269,8 +6289,8 @@ PackedInt32Array TextServerAdvanced::_string_get_word_breaks(const String &p_str
breaks.insert(pos);
}
}
+ ubrk_close(bi);
}
- ubrk_close(bi);
PackedInt32Array ret;
@@ -6351,6 +6371,13 @@ PackedInt32Array TextServerAdvanced::_string_get_word_breaks(const String &p_str
}
bool TextServerAdvanced::_is_valid_identifier(const String &p_string) const {
+#ifndef ICU_STATIC_DATA
+ if (!icu_data_loaded) {
+ WARN_PRINT_ONCE("ICU data is not loaded, Unicode security and spoofing detection disabled.");
+ return TextServer::is_valid_identifier(p_string);
+ }
+#endif
+
enum UAX31SequenceStatus {
SEQ_NOT_STARTED,
SEQ_STARTED,
diff --git a/modules/vorbis/resource_importer_ogg_vorbis.cpp b/modules/vorbis/resource_importer_ogg_vorbis.cpp
index 64d254f221..b712d63030 100644
--- a/modules/vorbis/resource_importer_ogg_vorbis.cpp
+++ b/modules/vorbis/resource_importer_ogg_vorbis.cpp
@@ -73,7 +73,7 @@ String ResourceImporterOggVorbis::get_preset_name(int p_idx) const {
}
void ResourceImporterOggVorbis::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "loop_offset"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,or_greater"), 0));
diff --git a/modules/webxr/doc_classes/WebXRInterface.xml b/modules/webxr/doc_classes/WebXRInterface.xml
index ba1750386f..0c18acbcb1 100644
--- a/modules/webxr/doc_classes/WebXRInterface.xml
+++ b/modules/webxr/doc_classes/WebXRInterface.xml
@@ -90,7 +90,7 @@
You can use both methods to allow your game or app to support a wider or narrower set of devices and input methods, or to allow more advanced interactions with more advanced devices.
</description>
<tutorials>
- <link title="How to make a VR game for WebXR with Godot">https://www.snopekgames.com/blog/2020/how-make-vr-game-webxr-godot</link>
+ <link title="How to make a VR game for WebXR with Godot 4">https://www.snopekgames.com/tutorial/2023/how-make-vr-game-webxr-godot-4</link>
</tutorials>
<methods>
<method name="get_input_source_target_ray_mode" qualifiers="const">
diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp
index 8485c62218..97b2eea4d7 100644
--- a/modules/webxr/webxr_interface_js.cpp
+++ b/modules/webxr/webxr_interface_js.cpp
@@ -347,9 +347,8 @@ Transform3D WebXRInterfaceJS::get_camera_transform() {
ERR_FAIL_NULL_V(xr_server, camera_transform);
if (initialized) {
- float world_scale = xr_server->get_world_scale();
+ double world_scale = xr_server->get_world_scale();
- // just scale our origin point of our transform
Transform3D _head_transform = head_transform;
_head_transform.origin *= world_scale;
@@ -372,13 +371,8 @@ Transform3D WebXRInterfaceJS::get_transform_for_view(uint32_t p_view, const Tran
Transform3D transform_for_view = _js_matrix_to_transform(js_matrix);
- float world_scale = xr_server->get_world_scale();
- // Scale only the center point of our eye transform, so we don't scale the
- // distance between the eyes.
- Transform3D _head_transform = head_transform;
- transform_for_view.origin -= _head_transform.origin;
- _head_transform.origin *= world_scale;
- transform_for_view.origin += _head_transform.origin;
+ double world_scale = xr_server->get_world_scale();
+ transform_for_view.origin *= world_scale;
return p_cam_transform * xr_server->get_reference_frame() * transform_for_view;
};