summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/csg/csg_shape.cpp6
-rw-r--r--modules/gdscript/editor/script_templates/EditorScenePostImport/basic_import_script.gd11
-rw-r--r--modules/gdscript/editor/script_templates/EditorScenePostImport/no_comments.gd7
-rw-r--r--modules/gdscript/gdscript.cpp4
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp4
-rw-r--r--modules/gdscript/gdscript_compiler.cpp93
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.cpp8
-rw-r--r--modules/gdscript/language_server/lsp.hpp2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_bind_unused.gd13
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_bind_unused.out6
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_dictionary.gd43
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_dictionary.out15
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd26
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.out9
-rw-r--r--modules/gdscript/tests/test_gdscript.cpp46
-rw-r--r--modules/gridmap/grid_map.cpp53
-rw-r--r--modules/gridmap/grid_map.h3
-rw-r--r--modules/mbedtls/stream_peer_mbedtls.cpp4
-rw-r--r--modules/mbedtls/stream_peer_mbedtls.h1
-rw-r--r--modules/mono/csharp_script.cpp6
-rw-r--r--modules/mono/editor/bindings_generator.cpp73
-rw-r--r--modules/mono/editor/bindings_generator.h6
-rw-r--r--modules/mono/editor/script_templates/EditorScenePostImport/basic_import_script.cs18
-rw-r--r--modules/mono/editor/script_templates/EditorScenePostImport/no_comments.cs15
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs10
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs8
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs6
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs21
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaitable.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaiter.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs5
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.cpp2
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.h1
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp8
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h1
-rw-r--r--modules/navigation/godot_navigation_server.cpp42
-rw-r--r--modules/navigation/godot_navigation_server.h12
-rw-r--r--modules/navigation/nav_map.cpp22
-rw-r--r--modules/navigation/nav_map.h6
-rw-r--r--modules/navigation/nav_region.cpp8
-rw-r--r--modules/navigation/nav_region.h14
-rw-r--r--modules/navigation/navigation_mesh_generator.cpp25
-rw-r--r--modules/navigation/rvo_agent.cpp3
-rw-r--r--modules/noise/noise_texture.cpp4
-rw-r--r--modules/openxr/openxr_api.cpp6
-rw-r--r--modules/openxr/openxr_api.h6
-rw-r--r--modules/text_server_adv/text_server_adv.cpp87
-rw-r--r--modules/text_server_adv/text_server_adv.h10
-rw-r--r--modules/text_server_fb/text_server_fb.cpp76
-rw-r--r--modules/text_server_fb/text_server_fb.h10
-rw-r--r--modules/visual_script/editor/visual_script_editor.cpp47
-rw-r--r--modules/visual_script/visual_script.cpp4
-rw-r--r--modules/visual_script/visual_script_flow_control.cpp4
-rw-r--r--modules/visual_script/visual_script_nodes.cpp8
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnection.xml7
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml5
-rw-r--r--modules/webrtc/webrtc_peer_connection.cpp7
-rw-r--r--modules/webrtc/webrtc_peer_connection_extension.cpp7
-rw-r--r--modules/webrtc/webrtc_peer_connection_extension.h2
65 files changed, 832 insertions, 168 deletions
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index 25772bebbe..0f09eb2020 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -638,7 +638,7 @@ void CSGShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_meshes"), &CSGShape3D::get_meshes);
ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001"), "set_snap", "get_snap");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001,suffix:m"), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "calculate_tangents"), "set_calculate_tangents", "is_calculating_tangents");
ADD_GROUP("Collision", "collision_");
@@ -2135,7 +2135,7 @@ void CSGPolygon3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Depth,Spin,Path"), "set_mode", "get_mode");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.01,100.0,0.01,or_greater,exp"), "set_depth", "get_depth");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.01,100.0,0.01,or_greater,exp,suffix:m"), "set_depth", "get_depth");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "spin_degrees", PROPERTY_HINT_RANGE, "1,360,0.1"), "set_spin_degrees", "get_spin_degrees");
ADD_PROPERTY(PropertyInfo(Variant::INT, "spin_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_spin_sides", "get_spin_sides");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "path_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Path3D"), "set_path_node", "get_path_node");
@@ -2145,7 +2145,7 @@ void CSGPolygon3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_rotation", PROPERTY_HINT_ENUM, "Polygon,Path,PathFollow"), "set_path_rotation", "get_path_rotation");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_local"), "set_path_local", "is_path_local");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_continuous_u"), "set_path_continuous_u", "is_path_continuous_u");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_u_distance", PROPERTY_HINT_RANGE, "0.0,10.0,0.01,or_greater"), "set_path_u_distance", "get_path_u_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_u_distance", PROPERTY_HINT_RANGE, "0.0,10.0,0.01,or_greater,suffix:m"), "set_path_u_distance", "get_path_u_distance");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_joined"), "set_path_joined", "is_path_joined");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
diff --git a/modules/gdscript/editor/script_templates/EditorScenePostImport/basic_import_script.gd b/modules/gdscript/editor/script_templates/EditorScenePostImport/basic_import_script.gd
new file mode 100644
index 0000000000..b4b2305b8c
--- /dev/null
+++ b/modules/gdscript/editor/script_templates/EditorScenePostImport/basic_import_script.gd
@@ -0,0 +1,11 @@
+# meta-description: Basic import script template
+@tool
+extends EditorScenePostImport
+
+
+# Called by the editor when a scene has this script set as the import script in the import tab.
+func _post_import(scene: Node) -> Object:
+ # Modify the contents of the scene upon import. For example, setting up LODs:
+# (scene.get_node(^"HighPolyMesh") as MeshInstance3D).draw_distance_end = 5.0
+# (scene.get_node(^"LowPolyMesh") as MeshInstance3D).draw_distance_begin = 5.0
+ return scene # Return the modified root node when you're done.
diff --git a/modules/gdscript/editor/script_templates/EditorScenePostImport/no_comments.gd b/modules/gdscript/editor/script_templates/EditorScenePostImport/no_comments.gd
new file mode 100644
index 0000000000..875afb4fc0
--- /dev/null
+++ b/modules/gdscript/editor/script_templates/EditorScenePostImport/no_comments.gd
@@ -0,0 +1,7 @@
+# meta-description: Basic import script template (no comments)
+@tool
+extends EditorScenePostImport
+
+
+func _post_import(scene: Node) -> Object:
+ return scene
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 55a7e39dec..64258ee8d3 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -128,6 +128,7 @@ void GDScript::_super_implicit_constructor(GDScript *p_script, GDScriptInstance
return;
}
}
+ ERR_FAIL_NULL(p_script->implicit_initializer);
p_script->implicit_initializer->call(p_instance, nullptr, 0, r_error);
}
@@ -1475,6 +1476,9 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
if (d.has("usage")) {
pinfo.usage = d["usage"];
}
+ if (d.has("class_name")) {
+ pinfo.class_name = d["class_name"];
+ }
props.push_back(pinfo);
}
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 9fa518ca0b..a070d319f3 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -1657,8 +1657,8 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc
p_match_pattern->bind->set_datatype(result);
#ifdef DEBUG_ENABLED
is_shadowing(p_match_pattern->bind, "pattern bind");
- if (p_match_pattern->bind->usages == 0) {
- parser->push_warning(p_match_pattern->bind, GDScriptWarning::UNASSIGNED_VARIABLE, p_match_pattern->bind->name);
+ if (p_match_pattern->bind->usages == 0 && !String(p_match_pattern->bind->name).begins_with("_")) {
+ parser->push_warning(p_match_pattern->bind, GDScriptWarning::UNUSED_VARIABLE, p_match_pattern->bind->name);
}
#endif
break;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 910f94a936..b2cce9d8ee 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -1389,25 +1389,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
codegen.generator->pop_temporary();
codegen.generator->pop_temporary();
- // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
- if (p_is_nested) {
- // Use the previous value as target, since we only need one temporary variable.
- codegen.generator->write_and_right_operand(result_addr);
- codegen.generator->write_end_and(p_previous_test);
- } else if (!p_is_first) {
- // Use the previous value as target, since we only need one temporary variable.
- codegen.generator->write_or_right_operand(result_addr);
- codegen.generator->write_end_or(p_previous_test);
- } else {
- // Just assign this value to the accumulator temporary.
- codegen.generator->write_assign(p_previous_test, result_addr);
- }
- codegen.generator->pop_temporary(); // Remove temp result addr.
-
// Create temporaries outside the loop so they can be reused.
GDScriptCodeGenerator::Address element_addr = codegen.add_temporary();
GDScriptCodeGenerator::Address element_type_addr = codegen.add_temporary();
- GDScriptCodeGenerator::Address test_addr = p_previous_test;
// Evaluate element by element.
for (int i = 0; i < p_pattern->array.size(); i++) {
@@ -1417,7 +1401,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
}
// Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get).
- codegen.generator->write_and_left_operand(test_addr);
+ codegen.generator->write_and_left_operand(result_addr);
// Add index to constant map.
GDScriptCodeGenerator::Address index_addr = codegen.add_constant(i);
@@ -1431,19 +1415,34 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
codegen.generator->write_call_utility(element_type_addr, "typeof", typeof_args);
// Try the pattern inside the element.
- test_addr = _parse_match_pattern(codegen, r_error, p_pattern->array[i], element_addr, element_type_addr, p_previous_test, false, true);
+ result_addr = _parse_match_pattern(codegen, r_error, p_pattern->array[i], element_addr, element_type_addr, result_addr, false, true);
if (r_error != OK) {
return GDScriptCodeGenerator::Address();
}
- codegen.generator->write_and_right_operand(test_addr);
- codegen.generator->write_end_and(test_addr);
+ codegen.generator->write_and_right_operand(result_addr);
+ codegen.generator->write_end_and(result_addr);
}
// Remove element temporaries.
codegen.generator->pop_temporary();
codegen.generator->pop_temporary();
- return test_addr;
+ // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
+ if (p_is_nested) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_and_right_operand(result_addr);
+ codegen.generator->write_end_and(p_previous_test);
+ } else if (!p_is_first) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_or_right_operand(result_addr);
+ codegen.generator->write_end_or(p_previous_test);
+ } else {
+ // Just assign this value to the accumulator temporary.
+ codegen.generator->write_assign(p_previous_test, result_addr);
+ }
+ codegen.generator->pop_temporary(); // Remove temp result addr.
+
+ return p_previous_test;
} break;
case GDScriptParser::PatternNode::PT_DICTIONARY: {
if (p_is_nested) {
@@ -1488,27 +1487,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
codegen.generator->pop_temporary();
codegen.generator->pop_temporary();
- // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
- if (p_is_nested) {
- // Use the previous value as target, since we only need one temporary variable.
- codegen.generator->write_and_right_operand(result_addr);
- codegen.generator->write_end_and(p_previous_test);
- } else if (!p_is_first) {
- // Use the previous value as target, since we only need one temporary variable.
- codegen.generator->write_or_right_operand(result_addr);
- codegen.generator->write_end_or(p_previous_test);
- } else {
- // Just assign this value to the accumulator temporary.
- codegen.generator->write_assign(p_previous_test, result_addr);
- }
- codegen.generator->pop_temporary(); // Remove temp result addr.
-
// Create temporaries outside the loop so they can be reused.
- temp_type.builtin_type = Variant::BOOL;
- GDScriptCodeGenerator::Address test_result = codegen.add_temporary(temp_type);
GDScriptCodeGenerator::Address element_addr = codegen.add_temporary();
GDScriptCodeGenerator::Address element_type_addr = codegen.add_temporary();
- GDScriptCodeGenerator::Address test_addr = p_previous_test;
// Evaluate element by element.
for (int i = 0; i < p_pattern->dictionary.size(); i++) {
@@ -1519,7 +1500,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
}
// Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get).
- codegen.generator->write_and_left_operand(test_addr);
+ codegen.generator->write_and_left_operand(result_addr);
// Get the pattern key.
GDScriptCodeGenerator::Address pattern_key_addr = _parse_expression(codegen, r_error, element.key);
@@ -1530,11 +1511,11 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Check if pattern key exists in user's dictionary. This will be AND-ed with next result.
func_args.clear();
func_args.push_back(pattern_key_addr);
- codegen.generator->write_call(test_result, p_value_addr, "has", func_args);
+ codegen.generator->write_call(result_addr, p_value_addr, "has", func_args);
if (element.value_pattern != nullptr) {
// Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get).
- codegen.generator->write_and_left_operand(test_result);
+ codegen.generator->write_and_left_operand(result_addr);
// Get actual value from user dictionary.
codegen.generator->write_get(element_addr, pattern_key_addr, p_value_addr);
@@ -1545,16 +1526,16 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
codegen.generator->write_call_utility(element_type_addr, "typeof", func_args);
// Try the pattern inside the value.
- test_addr = _parse_match_pattern(codegen, r_error, element.value_pattern, element_addr, element_type_addr, test_addr, false, true);
+ result_addr = _parse_match_pattern(codegen, r_error, element.value_pattern, element_addr, element_type_addr, result_addr, false, true);
if (r_error != OK) {
return GDScriptCodeGenerator::Address();
}
- codegen.generator->write_and_right_operand(test_addr);
- codegen.generator->write_end_and(test_addr);
+ codegen.generator->write_and_right_operand(result_addr);
+ codegen.generator->write_end_and(result_addr);
}
- codegen.generator->write_and_right_operand(test_addr);
- codegen.generator->write_end_and(test_addr);
+ codegen.generator->write_and_right_operand(result_addr);
+ codegen.generator->write_end_and(result_addr);
// Remove pattern key temporary.
if (pattern_key_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
@@ -1565,9 +1546,23 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Remove element temporaries.
codegen.generator->pop_temporary();
codegen.generator->pop_temporary();
- codegen.generator->pop_temporary();
- return test_addr;
+ // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
+ if (p_is_nested) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_and_right_operand(result_addr);
+ codegen.generator->write_end_and(p_previous_test);
+ } else if (!p_is_first) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_or_right_operand(result_addr);
+ codegen.generator->write_end_or(p_previous_test);
+ } else {
+ // Just assign this value to the accumulator temporary.
+ codegen.generator->write_assign(p_previous_test, result_addr);
+ }
+ codegen.generator->pop_temporary(); // Remove temp result addr.
+
+ return p_previous_test;
} break;
case GDScriptParser::PatternNode::PT_REST:
// Do nothing.
diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp
index d763701911..5ad9680ea0 100644
--- a/modules/gdscript/language_server/gdscript_text_document.cpp
+++ b/modules/gdscript/language_server/gdscript_text_document.cpp
@@ -169,6 +169,7 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
lsp::CompletionItem item;
item.label = option.display;
item.data = request_data;
+ item.insertText = option.insert_text;
switch (option.kind) {
case ScriptLanguage::CODE_COMPLETION_KIND_ENUM:
@@ -278,12 +279,7 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
item.documentation = symbol->render();
}
- if ((item.kind == lsp::CompletionItemKind::Method || item.kind == lsp::CompletionItemKind::Function) && !item.label.ends_with("):")) {
- item.insertText = item.label + "(";
- if (symbol && symbol->children.is_empty()) {
- item.insertText += ")";
- }
- } else if (item.kind == lsp::CompletionItemKind::Event) {
+ if (item.kind == lsp::CompletionItemKind::Event) {
if (params.context.triggerKind == lsp::CompletionTriggerKind::TriggerCharacter && (params.context.triggerCharacter == "(")) {
const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";
item.insertText = item.label.quote(quote_style);
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index d4aa207972..1c9349097f 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -1004,8 +1004,8 @@ struct CompletionItem {
dict["label"] = label;
dict["kind"] = kind;
dict["data"] = data;
+ dict["insertText"] = insertText;
if (resolved) {
- dict["insertText"] = insertText;
dict["detail"] = detail;
dict["documentation"] = documentation.to_json();
dict["deprecated"] = deprecated;
diff --git a/modules/gdscript/tests/scripts/parser/features/match_bind_unused.gd b/modules/gdscript/tests/scripts/parser/features/match_bind_unused.gd
new file mode 100644
index 0000000000..707e4532cc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_bind_unused.gd
@@ -0,0 +1,13 @@
+# https://github.com/godotengine/godot/pull/61666
+
+func test():
+ var dict := {"key": "value"}
+ match dict:
+ {"key": var value}:
+ print(value) # used, no warning
+ match dict:
+ {"key": var value}:
+ pass # unused, warning
+ match dict:
+ {"key": var _value}:
+ pass # unused, suppressed warning from underscore
diff --git a/modules/gdscript/tests/scripts/parser/features/match_bind_unused.out b/modules/gdscript/tests/scripts/parser/features/match_bind_unused.out
new file mode 100644
index 0000000000..057c1b11e5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_bind_unused.out
@@ -0,0 +1,6 @@
+GDTEST_OK
+>> WARNING
+>> Line: 9
+>> UNUSED_VARIABLE
+>> The local variable 'value' is declared but never used in the block. If this is intended, prefix it with an underscore: '_value'
+value
diff --git a/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd b/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd
new file mode 100644
index 0000000000..377dd25e9e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd
@@ -0,0 +1,43 @@
+func foo(x):
+ match x:
+ {"key1": "value1", "key2": "value2"}:
+ print('{"key1": "value1", "key2": "value2"}')
+ {"key1": "value1", "key2"}:
+ print('{"key1": "value1", "key2"}')
+ {"key1", "key2": "value2"}:
+ print('{"key1", "key2": "value2"}')
+ {"key1", "key2"}:
+ print('{"key1", "key2"}')
+ {"key1": "value1"}:
+ print('{"key1": "value1"}')
+ {"key1"}:
+ print('{"key1"}')
+ _:
+ print("wildcard")
+
+func bar(x):
+ match x:
+ {0}:
+ print("0")
+ {1}:
+ print("1")
+ {2}:
+ print("2")
+ _:
+ print("wildcard")
+
+func test():
+ foo({"key1": "value1", "key2": "value2"})
+ foo({"key1": "value1", "key2": ""})
+ foo({"key1": "", "key2": "value2"})
+ foo({"key1": "", "key2": ""})
+ foo({"key1": "value1"})
+ foo({"key1": ""})
+ foo({"key1": "value1", "key2": "value2", "key3": "value3"})
+ foo({"key1": "value1", "key3": ""})
+ foo({"key2": "value2"})
+ foo({"key3": ""})
+ bar({0: "0"})
+ bar({1: "1"})
+ bar({2: "2"})
+ bar({3: "3"})
diff --git a/modules/gdscript/tests/scripts/parser/features/match_dictionary.out b/modules/gdscript/tests/scripts/parser/features/match_dictionary.out
new file mode 100644
index 0000000000..4dee886927
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_dictionary.out
@@ -0,0 +1,15 @@
+GDTEST_OK
+{"key1": "value1", "key2": "value2"}
+{"key1": "value1", "key2"}
+{"key1", "key2": "value2"}
+{"key1", "key2"}
+{"key1": "value1"}
+{"key1"}
+wildcard
+wildcard
+wildcard
+wildcard
+0
+1
+2
+wildcard
diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd
new file mode 100644
index 0000000000..dbe223f5f5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd
@@ -0,0 +1,26 @@
+func foo(x):
+ match x:
+ 1, [2]:
+ print('1, [2]')
+ _:
+ print('wildcard')
+
+func bar(x):
+ match x:
+ [1], [2], [3]:
+ print('[1], [2], [3]')
+ [4]:
+ print('[4]')
+ _:
+ print('wildcard')
+
+func test():
+ foo(1)
+ foo([2])
+ foo(2)
+ bar([1])
+ bar([2])
+ bar([3])
+ bar([4])
+ bar([5])
+
diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.out b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.out
new file mode 100644
index 0000000000..a12b934d67
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.out
@@ -0,0 +1,9 @@
+GDTEST_OK
+1, [2]
+1, [2]
+wildcard
+[1], [2], [3]
+[1], [2], [3]
+[1], [2], [3]
+[4]
+wildcard
diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp
index d8f60d5e9b..cbcd7b2955 100644
--- a/modules/gdscript/tests/test_gdscript.cpp
+++ b/modules/gdscript/tests/test_gdscript.cpp
@@ -134,6 +134,34 @@ static void test_parser(const String &p_code, const String &p_script_path, const
#endif
}
+static void recursively_disassemble_functions(const Ref<GDScript> script, const Vector<String> &p_lines) {
+ for (const KeyValue<StringName, GDScriptFunction *> &E : script->get_member_functions()) {
+ const GDScriptFunction *func = E.value;
+
+ String signature = "Disassembling " + func->get_name().operator String() + "(";
+ for (int i = 0; i < func->get_argument_count(); i++) {
+ if (i > 0) {
+ signature += ", ";
+ }
+ signature += func->get_argument_name(i);
+ }
+ print_line(signature + ")");
+#ifdef TOOLS_ENABLED
+ func->disassemble(p_lines);
+#endif
+ print_line("");
+ print_line("");
+ }
+
+ for (const KeyValue<StringName, Ref<GDScript>> &F : script->get_subclasses()) {
+ const Ref<GDScript> inner_script = F.value;
+ print_line("");
+ print_line(vformat("Inner Class: %s", inner_script->get_script_class_name()));
+ print_line("");
+ recursively_disassemble_functions(inner_script, p_lines);
+ }
+}
+
static void test_compiler(const String &p_code, const String &p_script_path, const Vector<String> &p_lines) {
GDScriptParser parser;
Error err = parser.parse(p_code, p_script_path, false);
@@ -172,23 +200,7 @@ static void test_compiler(const String &p_code, const String &p_script_path, con
return;
}
- for (const KeyValue<StringName, GDScriptFunction *> &E : script->get_member_functions()) {
- const GDScriptFunction *func = E.value;
-
- String signature = "Disassembling " + func->get_name().operator String() + "(";
- for (int i = 0; i < func->get_argument_count(); i++) {
- if (i > 0) {
- signature += ", ";
- }
- signature += func->get_argument_name(i);
- }
- print_line(signature + ")");
-#ifdef TOOLS_ENABLED
- func->disassemble(p_lines);
-#endif
- print_line("");
- print_line("");
- }
+ recursively_disassemble_functions(script, p_lines);
}
void test(TestType p_type) {
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index dcdb520d11..0e34b5907e 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -226,8 +226,8 @@ bool GridMap::is_baking_navigation() {
return bake_navigation;
}
-void GridMap::set_navigation_layers(uint32_t p_layers) {
- navigation_layers = p_layers;
+void GridMap::set_navigation_layers(uint32_t p_navigation_layers) {
+ navigation_layers = p_navigation_layers;
_recreate_octant_data();
}
@@ -433,6 +433,18 @@ void GridMap::_octant_transform(const OctantKey &p_key) {
RS::get_singleton()->instance_set_transform(g.collision_debug_instance, get_global_transform());
}
+ // update transform for NavigationServer regions and navigation debugmesh instances
+ for (const KeyValue<IndexKey, Octant::NavMesh> &E : g.navmesh_ids) {
+ if (bake_navigation) {
+ if (E.value.region.is_valid()) {
+ NavigationServer3D::get_singleton()->region_set_transform(E.value.region, get_global_transform() * E.value.xform);
+ }
+ if (E.value.navmesh_debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_transform(E.value.navmesh_debug_instance, get_global_transform() * E.value.xform);
+ }
+ }
+ }
+
for (int i = 0; i < g.multimesh_instances.size(); i++) {
RS::get_singleton()->instance_set_transform(g.multimesh_instances[i].instance, get_global_transform());
}
@@ -456,6 +468,9 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
//erase navigation
for (const KeyValue<IndexKey, Octant::NavMesh> &E : g.navmesh_ids) {
NavigationServer3D::get_singleton()->free(E.value.region);
+ if (E.value.navmesh_debug_instance.is_valid()) {
+ RS::get_singleton()->free(E.value.navmesh_debug_instance);
+ }
}
g.navmesh_ids.clear();
@@ -533,11 +548,26 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
if (bake_navigation) {
RID region = NavigationServer3D::get_singleton()->region_create();
- NavigationServer3D::get_singleton()->region_set_layers(region, navigation_layers);
+ NavigationServer3D::get_singleton()->region_set_navigation_layers(region, navigation_layers);
NavigationServer3D::get_singleton()->region_set_navmesh(region, navmesh);
- NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform() * mesh_library->get_item_navmesh_transform(c.item));
+ NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform() * nm.xform);
NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map());
nm.region = region;
+
+ // add navigation debugmesh visual instances if debug is enabled
+ SceneTree *st = SceneTree::get_singleton();
+ if (st && st->is_debugging_navigation_hint()) {
+ if (!nm.navmesh_debug_instance.is_valid()) {
+ RID navmesh_debug_rid = navmesh->get_debug_mesh()->get_rid();
+ nm.navmesh_debug_instance = RS::get_singleton()->instance_create();
+ RS::get_singleton()->instance_set_base(nm.navmesh_debug_instance, navmesh_debug_rid);
+ RS::get_singleton()->mesh_surface_set_material(navmesh_debug_rid, 0, st->get_debug_navigation_material()->get_rid());
+ }
+ if (is_inside_tree()) {
+ RS::get_singleton()->instance_set_scenario(nm.navmesh_debug_instance, get_world_3d()->get_scenario());
+ RS::get_singleton()->instance_set_transform(nm.navmesh_debug_instance, get_global_transform() * nm.xform);
+ }
+ }
}
g.navmesh_ids[E] = nm;
@@ -629,7 +659,7 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) {
Ref<NavigationMesh> nm = mesh_library->get_item_navmesh(cell_map[F.key].item);
if (nm.is_valid()) {
RID region = NavigationServer3D::get_singleton()->region_create();
- NavigationServer3D::get_singleton()->region_set_layers(region, navigation_layers);
+ NavigationServer3D::get_singleton()->region_set_navigation_layers(region, navigation_layers);
NavigationServer3D::get_singleton()->region_set_navmesh(region, nm);
NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform() * F.value.xform);
NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map());
@@ -660,6 +690,10 @@ void GridMap::_octant_exit_world(const OctantKey &p_key) {
NavigationServer3D::get_singleton()->free(F.value.region);
F.value.region = RID();
}
+ if (F.value.navmesh_debug_instance.is_valid()) {
+ RS::get_singleton()->free(F.value.navmesh_debug_instance);
+ F.value.navmesh_debug_instance = RID();
+ }
}
}
@@ -678,7 +712,12 @@ void GridMap::_octant_clean_up(const OctantKey &p_key) {
// Erase navigation
for (const KeyValue<IndexKey, Octant::NavMesh> &E : g.navmesh_ids) {
- NavigationServer3D::get_singleton()->free(E.value.region);
+ if (E.value.region.is_valid()) {
+ NavigationServer3D::get_singleton()->free(E.value.region);
+ }
+ if (E.value.navmesh_debug_instance.is_valid()) {
+ RS::get_singleton()->free(E.value.navmesh_debug_instance);
+ }
}
g.navmesh_ids.clear();
@@ -890,7 +929,7 @@ void GridMap::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh_library", PROPERTY_HINT_RESOURCE_TYPE, "MeshLibrary"), "set_mesh_library", "get_mesh_library");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material", "get_physics_material");
ADD_GROUP("Cell", "cell_");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "cell_size"), "set_cell_size", "get_cell_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "cell_size", PROPERTY_HINT_NONE, "suffix:m"), "set_cell_size", "get_cell_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_octant_size", PROPERTY_HINT_RANGE, "1,1024,1"), "set_octant_size", "get_octant_size");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cell_center_x"), "set_center_x", "get_center_x");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cell_center_y"), "set_center_y", "get_center_y");
diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h
index d0872a839b..08ed4d3d12 100644
--- a/modules/gridmap/grid_map.h
+++ b/modules/gridmap/grid_map.h
@@ -98,6 +98,7 @@ class GridMap : public Node3D {
struct NavMesh {
RID region;
Transform3D xform;
+ RID navmesh_debug_instance;
};
struct MultimeshInstance {
@@ -237,7 +238,7 @@ public:
void set_bake_navigation(bool p_bake_navigation);
bool is_baking_navigation();
- void set_navigation_layers(uint32_t p_layers);
+ void set_navigation_layers(uint32_t p_navigation_layers);
uint32_t get_navigation_layers();
void set_mesh_library(const Ref<MeshLibrary> &p_mesh_library);
diff --git a/modules/mbedtls/stream_peer_mbedtls.cpp b/modules/mbedtls/stream_peer_mbedtls.cpp
index 1818048877..92590fbcf6 100644
--- a/modules/mbedtls/stream_peer_mbedtls.cpp
+++ b/modules/mbedtls/stream_peer_mbedtls.cpp
@@ -298,6 +298,10 @@ StreamPeerMbedTLS::Status StreamPeerMbedTLS::get_status() const {
return status;
}
+Ref<StreamPeer> StreamPeerMbedTLS::get_stream() const {
+ return base;
+}
+
StreamPeerSSL *StreamPeerMbedTLS::_create_func() {
return memnew(StreamPeerMbedTLS);
}
diff --git a/modules/mbedtls/stream_peer_mbedtls.h b/modules/mbedtls/stream_peer_mbedtls.h
index 98b91e65ab..7660410e04 100644
--- a/modules/mbedtls/stream_peer_mbedtls.h
+++ b/modules/mbedtls/stream_peer_mbedtls.h
@@ -57,6 +57,7 @@ public:
virtual Error accept_stream(Ref<StreamPeer> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain = Ref<X509Certificate>());
virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String(), Ref<X509Certificate> p_valid_cert = Ref<X509Certificate>());
virtual Status get_status() const;
+ virtual Ref<StreamPeer> get_stream() const;
virtual void disconnect_from_stream();
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index bee4e0e1fb..622838b308 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -2807,7 +2807,8 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
GD_MONO_ASSERT_THREAD_ATTACHED;
if (p_variant_type == Variant::INT && p_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(p_type.type_class->get_mono_ptr())) {
- r_hint = PROPERTY_HINT_ENUM;
+ MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
+ r_hint = GDMonoUtils::Marshal::type_has_flags_attribute(reftype) ? PROPERTY_HINT_FLAGS : PROPERTY_HINT_ENUM;
Vector<MonoClassField *> fields = p_type.type_class->get_enum_fields();
@@ -2844,7 +2845,8 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
uint64_t val = GDMonoUtils::unbox_enum_value(val_obj, enum_basetype, r_error);
ERR_FAIL_COND_V_MSG(r_error, -1, "Failed to unbox '" + enum_field_name + "' constant enum value.");
- if (val != (unsigned int)i) {
+ unsigned int expected_val = r_hint == PROPERTY_HINT_FLAGS ? 1 << i : i;
+ if (val != expected_val) {
uses_default_values = false;
}
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index e602396ede..960d2fe27c 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -1652,7 +1652,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
p_output.append("static ");
}
- p_output.append(prop_itype->cs_type);
+ String prop_cs_type = prop_itype->cs_type + _get_generic_type_parameters(*prop_itype, proptype_name.generic_type_parameters);
+
+ p_output.append(prop_cs_type);
p_output.append(" ");
p_output.append(p_iprop.proxy_name);
p_output.append("\n" INDENT2 OPEN_BLOCK);
@@ -1762,6 +1764,8 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
"Invalid default value for parameter '" + iarg.name + "' of method '" + p_itype.name + "." + p_imethod.name + "'.");
}
+ String arg_cs_type = arg_type->cs_type + _get_generic_type_parameters(*arg_type, iarg.type.generic_type_parameters);
+
// Add the current arguments to the signature
// If the argument has a default value which is not a constant, we will make it Nullable
{
@@ -1773,7 +1777,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
arguments_sig += "Nullable<";
}
- arguments_sig += arg_type->cs_type;
+ arguments_sig += arg_cs_type;
if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) {
arguments_sig += "> ";
@@ -1800,7 +1804,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
String arg_in = iarg.name;
arg_in += "_in";
- cs_in_statements += arg_type->cs_type;
+ cs_in_statements += arg_cs_type;
cs_in_statements += " ";
cs_in_statements += arg_in;
cs_in_statements += " = ";
@@ -1820,7 +1824,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
cs_in_statements += " : ";
}
- String cs_type = arg_type->cs_type;
+ String cs_type = arg_cs_type;
if (cs_type.ends_with("[]")) {
cs_type = cs_type.substr(0, cs_type.length() - 2);
}
@@ -1837,7 +1841,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
// Escape < and > in the attribute default value
String param_def_arg = def_arg.replacen("<", "&lt;").replacen(">", "&gt;");
- default_args_doc.append(MEMBER_BEGIN "/// <param name=\"" + param_tag_name + "\">If the parameter is null, then the default value is " + param_def_arg + "</param>");
+ default_args_doc.append(MEMBER_BEGIN "/// <param name=\"" + param_tag_name + "\">If the parameter is null, then the default value is <c>" + param_def_arg + "</c>.</param>");
} else {
icall_params += arg_type->cs_in.is_empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name);
}
@@ -1903,7 +1907,9 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
p_output.append("virtual ");
}
- p_output.append(return_type->cs_type + " ");
+ String return_cs_type = return_type->cs_type + _get_generic_type_parameters(*return_type, p_imethod.return_type.generic_type_parameters);
+
+ p_output.append(return_cs_type + " ");
p_output.append(p_imethod.proxy_name + "(");
p_output.append(arguments_sig + ")\n" OPEN_BLOCK_L2);
@@ -1914,7 +1920,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
p_output.append("return;\n" CLOSE_BLOCK_L2);
} else {
p_output.append("return default(");
- p_output.append(return_type->cs_type);
+ p_output.append(return_cs_type);
p_output.append(");\n" CLOSE_BLOCK_L2);
}
@@ -1956,7 +1962,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
} else if (return_type->cs_out.is_empty()) {
p_output.append("return " + im_call + "(" + icall_params + ");\n");
} else {
- p_output.append(sformat(return_type->cs_out, im_call, icall_params, return_type->cs_type, return_type->im_type_out));
+ p_output.append(sformat(return_type->cs_out, im_call, icall_params, return_cs_type, return_type->im_type_out));
p_output.append("\n");
}
@@ -2517,6 +2523,42 @@ const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placehol
return &placeholder_types.insert(placeholder.cname, placeholder)->value;
}
+const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters) {
+ if (p_generic_type_parameters.is_empty()) {
+ return "";
+ }
+
+ ERR_FAIL_COND_V_MSG(p_itype.type_parameter_count != p_generic_type_parameters.size(), "",
+ "Generic type parameter count mismatch for type '" + p_itype.name + "'." +
+ " Found " + itos(p_generic_type_parameters.size()) + ", but requires " +
+ itos(p_itype.type_parameter_count) + ".");
+
+ int i = 0;
+ String params = "<";
+ for (const TypeReference &param_type : p_generic_type_parameters) {
+ const TypeInterface *param_itype = _get_type_or_placeholder(param_type);
+
+ ERR_FAIL_COND_V_MSG(param_itype->is_singleton, "",
+ "Generic type parameter is a singleton: '" + param_itype->name + "'.");
+
+ if (p_itype.api_type == ClassDB::API_CORE) {
+ ERR_FAIL_COND_V_MSG(param_itype->api_type == ClassDB::API_EDITOR, "",
+ "Generic type parameter '" + param_itype->name + "' has type from the editor API." +
+ " Core API cannot have dependencies on the editor API.");
+ }
+
+ params += param_itype->cs_type;
+ if (i < p_generic_type_parameters.size() - 1) {
+ params += ", ";
+ }
+
+ i++;
+ }
+ params += ">";
+
+ return params;
+}
+
StringName BindingsGenerator::_get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta) {
switch (p_meta) {
case GodotTypeInfo::METADATA_INT_IS_INT8:
@@ -2832,6 +2874,9 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
ERR_FAIL_COND_V_MSG(bad_reference_hint, false,
String() + "Return type is reference but hint is not '" _STR(PROPERTY_HINT_RESOURCE_TYPE) "'." +
" Are you returning a reference type by pointer? Method: '" + itype.name + "." + imethod.name + "'.");
+ } else if (return_info.type == Variant::ARRAY && return_info.hint == PROPERTY_HINT_ARRAY_TYPE) {
+ imethod.return_type.cname = Variant::get_type_name(return_info.type);
+ imethod.return_type.generic_type_parameters.push_back(TypeReference(return_info.hint_string));
} else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
imethod.return_type.cname = return_info.hint_string;
} else if (return_info.type == Variant::NIL && return_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
@@ -2861,6 +2906,9 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
iarg.type.is_enum = true;
} else if (arginfo.class_name != StringName()) {
iarg.type.cname = arginfo.class_name;
+ } else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
+ iarg.type.cname = Variant::get_type_name(arginfo.type);
+ iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string));
} else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
iarg.type.cname = arginfo.hint_string;
} else if (arginfo.type == Variant::NIL) {
@@ -2966,6 +3014,9 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
iarg.type.is_enum = true;
} else if (arginfo.class_name != StringName()) {
iarg.type.cname = arginfo.class_name;
+ } else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
+ iarg.type.cname = Variant::get_type_name(arginfo.type);
+ iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string));
} else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
iarg.type.cname = arginfo.hint_string;
} else if (arginfo.type == Variant::NIL) {
@@ -3549,13 +3600,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "Array";
itype.cname = itype.name;
itype.proxy_name = itype.name;
+ itype.type_parameter_count = 1;
itype.c_out = "\treturn memnew(Array(%1));\n";
itype.c_type = itype.name;
itype.c_type_in = itype.c_type + "*";
itype.c_type_out = itype.c_type + "*";
itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name;
itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
- itype.cs_out = "return new " + itype.cs_type + "(%0(%1));";
+ itype.cs_out = "return new %2(%0(%1));";
itype.im_type_in = "IntPtr";
itype.im_type_out = "IntPtr";
builtin_types.insert(itype.cname, itype);
@@ -3565,13 +3617,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "Dictionary";
itype.cname = itype.name;
itype.proxy_name = itype.name;
+ itype.type_parameter_count = 2;
itype.c_out = "\treturn memnew(Dictionary(%1));\n";
itype.c_type = itype.name;
itype.c_type_in = itype.c_type + "*";
itype.c_type_out = itype.c_type + "*";
itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name;
itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
- itype.cs_out = "return new " + itype.cs_type + "(%0(%1));";
+ itype.cs_out = "return new %2(%0(%1));";
itype.im_type_in = "IntPtr";
itype.im_type_out = "IntPtr";
builtin_types.insert(itype.cname, itype);
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index fb7e0e5a81..f0ba2b18e4 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -87,6 +87,8 @@ class BindingsGenerator {
StringName cname;
bool is_enum = false;
+ List<TypeReference> generic_type_parameters;
+
TypeReference() {}
TypeReference(const StringName &p_cname) :
@@ -206,6 +208,8 @@ class BindingsGenerator {
String name;
StringName cname;
+ int type_parameter_count;
+
/**
* Identifier name of the base class.
*/
@@ -679,6 +683,8 @@ class BindingsGenerator {
const TypeInterface *_get_type_or_null(const TypeReference &p_typeref);
const TypeInterface *_get_type_or_placeholder(const TypeReference &p_typeref);
+ const String _get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters);
+
StringName _get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta);
StringName _get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta);
diff --git a/modules/mono/editor/script_templates/EditorScenePostImport/basic_import_script.cs b/modules/mono/editor/script_templates/EditorScenePostImport/basic_import_script.cs
new file mode 100644
index 0000000000..0690205d01
--- /dev/null
+++ b/modules/mono/editor/script_templates/EditorScenePostImport/basic_import_script.cs
@@ -0,0 +1,18 @@
+// meta-description: Basic import script template
+
+#if TOOLS
+using _BINDINGS_NAMESPACE_;
+using System;
+
+[Tool]
+public partial class _CLASS_ : _BASE_
+{
+ public override Object _PostImport(Node scene)
+ {
+ // Modify the contents of the scene upon import. For example, setting up LODs:
+// scene.GetNode<MeshInstance3D>("HighPolyMesh").DrawDistanceEnd = 5.0
+// scene.GetNode<MeshInstance3D>("LowPolyMesh").DrawDistanceBegin = 5.0
+ return scene // Return the modified root node when you're done.
+ }
+}
+#endif
diff --git a/modules/mono/editor/script_templates/EditorScenePostImport/no_comments.cs b/modules/mono/editor/script_templates/EditorScenePostImport/no_comments.cs
new file mode 100644
index 0000000000..4e2d9b7088
--- /dev/null
+++ b/modules/mono/editor/script_templates/EditorScenePostImport/no_comments.cs
@@ -0,0 +1,15 @@
+// meta-description: Basic import script template (no comments)
+
+#if TOOLS
+using _BINDINGS_NAMESPACE_;
+using System;
+
+[Tool]
+public partial class _CLASS_ : _BASE_
+{
+ public override Object _PostImport(Node scene)
+ {
+ return scene
+ }
+}
+#endif
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
index ef135da51a..2febf37f05 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
@@ -2,17 +2,27 @@ using System;
namespace Godot
{
+ /// <summary>
+ /// An attribute that determines if an assembly has scripts. If so, what types of scripts the assembly has.
+ /// </summary>
[AttributeUsage(AttributeTargets.Assembly)]
public class AssemblyHasScriptsAttribute : Attribute
{
private readonly bool requiresLookup;
private readonly System.Type[] scriptTypes;
+ /// <summary>
+ /// Constructs a new AssemblyHasScriptsAttribute instance.
+ /// </summary>
public AssemblyHasScriptsAttribute()
{
requiresLookup = true;
}
+ /// <summary>
+ /// Constructs a new AssemblyHasScriptsAttribute instance.
+ /// </summary>
+ /// <param name="scriptTypes">The specified type(s) of scripts.</param>
public AssemblyHasScriptsAttribute(System.Type[] scriptTypes)
{
requiresLookup = false;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
index e93bc89811..0b00878e8c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
@@ -2,6 +2,9 @@ using System;
namespace Godot
{
+ /// <summary>
+ /// An attribute that disables Godot Generators.
+ /// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class DisableGodotGeneratorsAttribute : Attribute { }
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs
index 6adf044886..46eb128d37 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs
@@ -2,12 +2,20 @@ using System;
namespace Godot
{
+ /// <summary>
+ /// An attribute used to export objects.
+ /// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class ExportAttribute : Attribute
{
private PropertyHint hint;
private string hintString;
+ /// <summary>
+ /// Constructs a new ExportAttribute Instance.
+ /// </summary>
+ /// <param name="hint">A hint to the exported object.</param>
+ /// <param name="hintString">A string representing the exported object.</param>
public ExportAttribute(PropertyHint hint = PropertyHint.None, string hintString = "")
{
this.hint = hint;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs
index 55848769d5..8d4ff0fdb7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs
@@ -2,6 +2,9 @@ using System;
namespace Godot
{
+ /// <summary>
+ /// An attribute for a method.
+ /// </summary>
[AttributeUsage(AttributeTargets.Method)]
internal class GodotMethodAttribute : Attribute
{
@@ -9,6 +12,10 @@ namespace Godot
public string MethodName { get { return methodName; } }
+ /// <summary>
+ /// Constructs a new GodotMethodAttribute instance.
+ /// </summary>
+ /// <param name="methodName">The name of the method.</param>
public GodotMethodAttribute(string methodName)
{
this.methodName = methodName;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs
index b8b9bc660c..f0d37c344d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs
@@ -2,9 +2,15 @@ using System;
namespace Godot
{
+ /// <summary>
+ /// Constructs a new AnyPeerAttribute instance. Members with the AnyPeerAttribute are given authority over their own player.
+ /// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class AnyPeerAttribute : Attribute { }
+ /// <summary>
+ /// Constructs a new AuthorityAttribute instance. Members with the AuthorityAttribute are given authority over the game.
+ /// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class AuthorityAttribute : Attribute { }
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
index 12eb1035c3..3ebb6612de 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
@@ -2,11 +2,18 @@ using System;
namespace Godot
{
+ /// <summary>
+ /// An attribute that contains the path to the object's script.
+ /// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class ScriptPathAttribute : Attribute
{
private string path;
+ /// <summary>
+ /// Constructs a new ScriptPathAttribute instance.
+ /// </summary>
+ /// <param name="path">The file path to the script</param>
public ScriptPathAttribute(string path)
{
this.path = path;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
index 072e0f20ff..6475237002 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
@@ -4,9 +4,16 @@ namespace Godot
{
public static class Dispatcher
{
+ /// <summary>
+ /// Implements an external instance of GodotTaskScheduler.
+ /// </summary>
+ /// <returns>A GodotTaskScheduler instance.</returns>
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern GodotTaskScheduler godot_icall_DefaultGodotTaskScheduler();
+ /// <summary>
+ /// Initializes the synchronization context as the context of the GodotTaskScheduler.
+ /// </summary>
public static GodotSynchronizationContext SynchronizationContext =>
godot_icall_DefaultGodotTaskScheduler().Context;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs
index c01c926e82..1b599beab5 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs
@@ -14,6 +14,9 @@ namespace Godot
_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
}
+ /// <summary>
+ /// Calls the Key method on each workItem object in the _queue to activate their callbacks.
+ /// </summary>
public void ExecutePendingContinuations()
{
while (_queue.TryTake(out var workItem))
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs
index 8eaeea50dc..408bed71b2 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs
@@ -6,11 +6,25 @@ using System.Threading.Tasks;
namespace Godot
{
+ /// <summary>
+ /// 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
{
+ /// <summary>
+ /// The current synchronization context.
+ /// </summary>
internal GodotSynchronizationContext Context { get; }
+
+ /// <summary>
+ /// The queue of tasks for the task scheduler.
+ /// </summary>
private readonly LinkedList<Task> _tasks = new LinkedList<Task>();
+ /// <summary>
+ /// Constructs a new GodotTaskScheduler instance.
+ /// </summary>
public GodotTaskScheduler()
{
Context = new GodotSynchronizationContext();
@@ -53,12 +67,19 @@ namespace Godot
}
}
+ /// <summary>
+ /// Executes all queued tasks and pending tasks from the current context.
+ /// </summary>
public void Activate()
{
ExecuteQueuedTasks();
Context.ExecutePendingContinuations();
}
+ /// <summary>
+ /// Loops through and attempts to execute each task in _tasks.
+ /// </summary>
+ /// <exception cref="InvalidOperationException"></exception>
private void ExecuteQueuedTasks()
{
while (true)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaitable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaitable.cs
index 0397957d00..e747e03c1e 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaitable.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaitable.cs
@@ -1,10 +1,17 @@
namespace Godot
{
+ /// <summary>
+ /// An interface that requires a GetAwaiter() method to get a reference to the Awaiter.
+ /// </summary>
public interface IAwaitable
{
IAwaiter GetAwaiter();
}
+ /// <summary>
+ /// A templated interface that requires a GetAwaiter() method to get a reference to the Awaiter.
+ /// </summary>
+ /// <typeparam name="TResult">A reference to the result to be passed out.</typeparam>
public interface IAwaitable<out TResult>
{
IAwaiter<TResult> GetAwaiter();
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaiter.cs
index d3be9d781c..dec225eb29 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaiter.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaiter.cs
@@ -2,6 +2,9 @@ using System.Runtime.CompilerServices;
namespace Godot
{
+ /// <summary>
+ /// An interface that requires a boolean for completion status and a method that gets the result of completion.
+ /// </summary>
public interface IAwaiter : INotifyCompletion
{
bool IsCompleted { get; }
@@ -9,6 +12,10 @@ namespace Godot
void GetResult();
}
+ /// <summary>
+ /// A templated interface that requires a boolean for completion status and a method that gets the result of completion and returns it.
+ /// </summary>
+ /// <typeparam name="TResult">A reference to the result to be passed out.</typeparam>
public interface IAwaiter<out TResult> : INotifyCompletion
{
bool IsCompleted { get; }
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs
index c3fa2f3e82..90b4d1b8d3 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs
@@ -1,5 +1,8 @@
namespace Godot
{
+ /// <summary>
+ /// An interface that requires methods for before and after serialization.
+ /// </summary>
public interface ISerializationListener
{
void OnBeforeSerialize();
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
index ee4d0eed08..50ae2eb112 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
@@ -80,6 +80,11 @@ namespace Godot
private static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>);
/// <summary>
+ /// Returns <see langword="true"/> if the <see cref="FlagsAttribute"/> is applied to the given type.
+ /// </summary>
+ private static bool TypeHasFlagsAttribute(Type type) => type.IsDefined(typeof(FlagsAttribute), false);
+
+ /// <summary>
/// Returns the generic type definition of <paramref name="type"/>.
/// </summary>
/// <exception cref="InvalidOperationException">
diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp
index d8fd244067..44a8e26b8f 100644
--- a/modules/mono/mono_gd/gd_mono_cache.cpp
+++ b/modules/mono/mono_gd/gd_mono_cache.cpp
@@ -176,6 +176,7 @@ void CachedData::clear_godot_api_cache() {
methodthunk_MarshalUtils_TypeIsGenericIEnumerable.nullify();
methodthunk_MarshalUtils_TypeIsGenericICollection.nullify();
methodthunk_MarshalUtils_TypeIsGenericIDictionary.nullify();
+ methodthunk_MarshalUtils_TypeHasFlagsAttribute.nullify();
methodthunk_MarshalUtils_GetGenericTypeDefinition.nullify();
@@ -300,6 +301,7 @@ void update_godot_api_cache() {
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIEnumerable, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIEnumerable", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericICollection, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericICollection", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIDictionary", 1));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeHasFlagsAttribute, GODOT_API_CLASS(MarshalUtils)->get_method("TypeHasFlagsAttribute", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GetGenericTypeDefinition, GODOT_API_CLASS(MarshalUtils)->get_method("GetGenericTypeDefinition", 2));
diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h
index 4000342c94..92136e1f41 100644
--- a/modules/mono/mono_gd/gd_mono_cache.h
+++ b/modules/mono/mono_gd/gd_mono_cache.h
@@ -147,6 +147,7 @@ struct CachedData {
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericIEnumerable;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericICollection;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericIDictionary;
+ GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeHasFlagsAttribute;
GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_GetGenericTypeDefinition;
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index 25678be624..1983d6ebe2 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -614,6 +614,14 @@ bool type_is_generic_idictionary(MonoReflectionType *p_reftype) {
return (bool)res;
}
+bool type_has_flags_attribute(MonoReflectionType *p_reftype) {
+ NO_GLUE_RET(false);
+ MonoException *exc = nullptr;
+ MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeHasFlagsAttribute).invoke(p_reftype, &exc);
+ UNHANDLED_EXCEPTION(exc);
+ return (bool)res;
+}
+
void get_generic_type_definition(MonoReflectionType *p_reftype, MonoReflectionType **r_generic_reftype) {
MonoException *exc = nullptr;
CACHED_METHOD_THUNK(MarshalUtils, GetGenericTypeDefinition).invoke(p_reftype, r_generic_reftype, &exc);
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index 4c2c2c93c2..246a1cd31e 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -61,6 +61,7 @@ bool type_is_system_generic_dictionary(MonoReflectionType *p_reftype);
bool type_is_generic_ienumerable(MonoReflectionType *p_reftype);
bool type_is_generic_icollection(MonoReflectionType *p_reftype);
bool type_is_generic_idictionary(MonoReflectionType *p_reftype);
+bool type_has_flags_attribute(MonoReflectionType *p_reftype);
void get_generic_type_definition(MonoReflectionType *p_reftype, MonoReflectionType **r_generic_reftype);
diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp
index cc9d05da47..2f8cb6c230 100644
--- a/modules/navigation/godot_navigation_server.cpp
+++ b/modules/navigation/godot_navigation_server.cpp
@@ -198,11 +198,11 @@ real_t GodotNavigationServer::map_get_edge_connection_margin(RID p_map) const {
return map->get_edge_connection_margin();
}
-Vector<Vector3> GodotNavigationServer::map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_layers) const {
+Vector<Vector3> GodotNavigationServer::map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers) const {
const NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND_V(map == nullptr, Vector<Vector3>());
- return map->get_path(p_origin, p_destination, p_optimize, p_layers);
+ return map->get_path(p_origin, p_destination, p_optimize, p_navigation_layers);
}
Vector3 GodotNavigationServer::map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const {
@@ -309,18 +309,48 @@ COMMAND_2(region_set_transform, RID, p_region, Transform3D, p_transform) {
region->set_transform(p_transform);
}
-COMMAND_2(region_set_layers, RID, p_region, uint32_t, p_layers) {
+COMMAND_2(region_set_enter_cost, RID, p_region, real_t, p_enter_cost) {
NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_COND(region == nullptr);
+ ERR_FAIL_COND(p_enter_cost < 0.0);
- region->set_layers(p_layers);
+ region->set_enter_cost(p_enter_cost);
}
-uint32_t GodotNavigationServer::region_get_layers(RID p_region) const {
+real_t GodotNavigationServer::region_get_enter_cost(RID p_region) const {
NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_COND_V(region == nullptr, 0);
- return region->get_layers();
+ return region->get_enter_cost();
+}
+
+COMMAND_2(region_set_travel_cost, RID, p_region, real_t, p_travel_cost) {
+ NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND(region == nullptr);
+ ERR_FAIL_COND(p_travel_cost < 0.0);
+
+ region->set_travel_cost(p_travel_cost);
+}
+
+real_t GodotNavigationServer::region_get_travel_cost(RID p_region) const {
+ NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND_V(region == nullptr, 0);
+
+ return region->get_travel_cost();
+}
+
+COMMAND_2(region_set_navigation_layers, RID, p_region, uint32_t, p_navigation_layers) {
+ NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND(region == nullptr);
+
+ region->set_navigation_layers(p_navigation_layers);
+}
+
+uint32_t GodotNavigationServer::region_get_navigation_layers(RID p_region) const {
+ NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND_V(region == nullptr, 0);
+
+ return region->get_navigation_layers();
}
COMMAND_2(region_set_navmesh, RID, p_region, Ref<NavigationMesh>, p_nav_mesh) {
diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h
index 89e7311e51..d931dbaee0 100644
--- a/modules/navigation/godot_navigation_server.h
+++ b/modules/navigation/godot_navigation_server.h
@@ -98,7 +98,7 @@ public:
COMMAND_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin);
virtual real_t map_get_edge_connection_margin(RID p_map) const override;
- virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_layers = 1) const override;
+ virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const override;
virtual Vector3 map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision = false) const override;
virtual Vector3 map_get_closest_point(RID p_map, const Vector3 &p_point) const override;
@@ -109,10 +109,16 @@ public:
virtual Array map_get_agents(RID p_map) const override;
virtual RID region_create() const override;
+
+ COMMAND_2(region_set_enter_cost, RID, p_region, real_t, p_enter_cost);
+ virtual real_t region_get_enter_cost(RID p_region) const override;
+ COMMAND_2(region_set_travel_cost, RID, p_region, real_t, p_travel_cost);
+ virtual real_t region_get_travel_cost(RID p_region) const override;
+
COMMAND_2(region_set_map, RID, p_region, RID, p_map);
virtual RID region_get_map(RID p_region) const override;
- COMMAND_2(region_set_layers, RID, p_region, uint32_t, p_layers);
- virtual uint32_t region_get_layers(RID p_region) const override;
+ COMMAND_2(region_set_navigation_layers, RID, p_region, uint32_t, p_navigation_layers);
+ virtual uint32_t region_get_navigation_layers(RID p_region) const override;
COMMAND_2(region_set_transform, RID, p_region, Transform3D, p_transform);
COMMAND_2(region_set_navmesh, RID, p_region, Ref<NavigationMesh>, p_nav_mesh);
virtual void region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const override;
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index 344475fb37..49c12813b3 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -65,7 +65,7 @@ gd::PointKey NavMap::get_point_key(const Vector3 &p_pos) const {
return p;
}
-Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_layers) const {
+Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers) const {
// Find the start poly and the end poly on this map.
const gd::Polygon *begin_poly = nullptr;
const gd::Polygon *end_poly = nullptr;
@@ -78,7 +78,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
const gd::Polygon &p = polygons[i];
// Only consider the polygon if it in a region with compatible layers.
- if ((p_layers & p.owner->get_layers()) == 0) {
+ if ((p_navigation_layers & p.owner->get_navigation_layers()) == 0) {
continue;
}
@@ -140,6 +140,8 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
float reachable_d = 1e30;
bool is_reachable = true;
+ gd::NavigationPoly *prev_least_cost_poly = nullptr;
+
while (true) {
// Takes the current least_cost_poly neighbors (iterating over its edges) and compute the traveled_distance.
for (size_t i = 0; i < navigation_polys[least_cost_id].poly->edges.size(); i++) {
@@ -152,13 +154,21 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
const gd::Edge::Connection &connection = edge.connections[connection_index];
// Only consider the connection to another polygon if this polygon is in a region with compatible layers.
- if ((p_layers & connection.polygon->owner->get_layers()) == 0) {
+ if ((p_navigation_layers & connection.polygon->owner->get_navigation_layers()) == 0) {
continue;
}
+ float region_enter_cost = 0.0;
+ float region_travel_cost = least_cost_poly->poly->owner->get_travel_cost();
+
+ if (prev_least_cost_poly != nullptr && !(prev_least_cost_poly->poly->owner->get_self() == least_cost_poly->poly->owner->get_self())) {
+ region_enter_cost = least_cost_poly->poly->owner->get_enter_cost();
+ }
+ prev_least_cost_poly = least_cost_poly;
+
Vector3 pathway[2] = { connection.pathway_start, connection.pathway_end };
const Vector3 new_entry = Geometry3D::get_closest_point_to_segment(least_cost_poly->entry, pathway);
- const float new_distance = least_cost_poly->entry.distance_to(new_entry) + least_cost_poly->traveled_distance;
+ const float new_distance = (least_cost_poly->entry.distance_to(new_entry) * region_travel_cost) + region_enter_cost + least_cost_poly->traveled_distance;
const std::vector<gd::NavigationPoly>::iterator it = std::find(
navigation_polys.begin(),
@@ -238,7 +248,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
for (List<uint32_t>::Element *element = to_visit.front(); element != nullptr; element = element->next()) {
gd::NavigationPoly *np = &navigation_polys[element->get()];
float cost = np->traveled_distance;
- cost += np->entry.distance_to(end_point);
+ cost += (np->entry.distance_to(end_point) * np->poly->owner->get_travel_cost());
if (cost < least_cost) {
least_cost_id = np->self_id;
least_cost = cost;
@@ -249,7 +259,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
// Stores the further reachable end polygon, in case our goal is not reachable.
if (is_reachable) {
- float d = navigation_polys[least_cost_id].entry.distance_to(p_destination);
+ float d = navigation_polys[least_cost_id].entry.distance_to(p_destination) * navigation_polys[least_cost_id].poly->owner->get_travel_cost();
if (reachable_d > d) {
reachable_d = d;
reachable_end = navigation_polys[least_cost_id].poly;
diff --git a/modules/navigation/nav_map.h b/modules/navigation/nav_map.h
index f58a78d4ca..2036dbecd7 100644
--- a/modules/navigation/nav_map.h
+++ b/modules/navigation/nav_map.h
@@ -50,10 +50,10 @@ class NavMap : public NavRid {
/// To find the polygons edges the vertices are displaced in a grid where
/// each cell has the following cell_size.
- real_t cell_size = 0.3;
+ real_t cell_size = 0.25;
/// This value is used to detect the near edges to connect.
- real_t edge_connection_margin = 5.0;
+ real_t edge_connection_margin = 0.25;
bool regenerate_polygons = true;
bool regenerate_links = true;
@@ -105,7 +105,7 @@ public:
gd::PointKey get_point_key(const Vector3 &p_pos) const;
- Vector<Vector3> get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_layers = 1) const;
+ Vector<Vector3> get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const;
Vector3 get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const;
Vector3 get_closest_point(const Vector3 &p_point) const;
Vector3 get_closest_point_normal(const Vector3 &p_point) const;
diff --git a/modules/navigation/nav_region.cpp b/modules/navigation/nav_region.cpp
index fea0ad519a..88740807eb 100644
--- a/modules/navigation/nav_region.cpp
+++ b/modules/navigation/nav_region.cpp
@@ -40,12 +40,12 @@ void NavRegion::set_map(NavMap *p_map) {
}
}
-void NavRegion::set_layers(uint32_t p_layers) {
- layers = p_layers;
+void NavRegion::set_navigation_layers(uint32_t p_navigation_layers) {
+ navigation_layers = p_navigation_layers;
}
-uint32_t NavRegion::get_layers() const {
- return layers;
+uint32_t NavRegion::get_navigation_layers() const {
+ return navigation_layers;
}
void NavRegion::set_transform(Transform3D p_transform) {
diff --git a/modules/navigation/nav_region.h b/modules/navigation/nav_region.h
index 7a6da281c0..484856ae36 100644
--- a/modules/navigation/nav_region.h
+++ b/modules/navigation/nav_region.h
@@ -45,7 +45,9 @@ class NavRegion : public NavRid {
NavMap *map = nullptr;
Transform3D transform;
Ref<NavigationMesh> mesh;
- uint32_t layers = 1;
+ uint32_t navigation_layers = 1;
+ float enter_cost = 0.0;
+ float travel_cost = 1.0;
Vector<gd::Edge::Connection> connections;
bool polygons_dirty = true;
@@ -65,8 +67,14 @@ public:
return map;
}
- void set_layers(uint32_t p_layers);
- uint32_t get_layers() const;
+ void set_enter_cost(float p_enter_cost) { enter_cost = MAX(p_enter_cost, 0.0); }
+ float get_enter_cost() const { return enter_cost; }
+
+ void set_travel_cost(float p_travel_cost) { travel_cost = MAX(p_travel_cost, 0.0); }
+ float get_travel_cost() const { return travel_cost; }
+
+ void set_navigation_layers(uint32_t p_navigation_layers);
+ uint32_t get_navigation_layers() const;
void set_transform(Transform3D transform);
const Transform3D &get_transform() const {
diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp
index 1bc7cd7cdc..e430f5fd59 100644
--- a/modules/navigation/navigation_mesh_generator.cpp
+++ b/modules/navigation/navigation_mesh_generator.cpp
@@ -450,6 +450,31 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh(
cfg.detailSampleDist = MAX(p_nav_mesh->get_cell_size() * p_nav_mesh->get_detail_sample_distance(), 0.1f);
cfg.detailSampleMaxError = p_nav_mesh->get_cell_height() * p_nav_mesh->get_detail_sample_max_error();
+ if (!Math::is_equal_approx((float)cfg.walkableHeight * cfg.ch, p_nav_mesh->get_agent_height())) {
+ WARN_PRINT("Property agent_height is ceiled to cell_height voxel units and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.walkableClimb * cfg.ch, p_nav_mesh->get_agent_max_climb())) {
+ WARN_PRINT("Property agent_max_climb is floored to cell_height voxel units and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.walkableRadius * cfg.cs, p_nav_mesh->get_agent_radius())) {
+ WARN_PRINT("Property agent_radius is ceiled to cell_size voxel units and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.maxEdgeLen * cfg.cs, p_nav_mesh->get_edge_max_length())) {
+ WARN_PRINT("Property edge_max_length is rounded to cell_size voxel units and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.minRegionArea, p_nav_mesh->get_region_min_size() * p_nav_mesh->get_region_min_size())) {
+ WARN_PRINT("Property region_min_size is converted to int and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.mergeRegionArea, p_nav_mesh->get_region_merge_size() * p_nav_mesh->get_region_merge_size())) {
+ WARN_PRINT("Property region_merge_size is converted to int and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.maxVertsPerPoly, p_nav_mesh->get_verts_per_poly())) {
+ WARN_PRINT("Property verts_per_poly is converted to int and loses precision.");
+ }
+ if (p_nav_mesh->get_cell_size() * p_nav_mesh->get_detail_sample_distance() < 0.1f) {
+ WARN_PRINT("Property detail_sample_distance is clamped to 0.1 world units as the resulting value from multiplying with cell_size is too low.");
+ }
+
cfg.bmin[0] = bmin[0];
cfg.bmin[1] = bmin[1];
cfg.bmin[2] = bmin[2];
diff --git a/modules/navigation/rvo_agent.cpp b/modules/navigation/rvo_agent.cpp
index a6a5660c0c..4ec72ad43f 100644
--- a/modules/navigation/rvo_agent.cpp
+++ b/modules/navigation/rvo_agent.cpp
@@ -65,8 +65,9 @@ void RvoAgent::dispatch_callback() {
return;
}
Object *obj = ObjectDB::get_instance(callback.id);
- if (obj == nullptr) {
+ if (!obj) {
callback.id = ObjectID();
+ return;
}
Callable::CallError responseCallError;
diff --git a/modules/noise/noise_texture.cpp b/modules/noise/noise_texture.cpp
index e9d16e548c..ca55d3b96d 100644
--- a/modules/noise/noise_texture.cpp
+++ b/modules/noise/noise_texture.cpp
@@ -81,8 +81,8 @@ void NoiseTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_noise", "noise"), &NoiseTexture::set_noise);
ClassDB::bind_method(D_METHOD("get_noise"), &NoiseTexture::get_noise);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_width", "get_width");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater,suffix:px"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater,suffix:px"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert"), "set_invert", "get_invert");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "in_3d_space"), "set_in_3d_space", "is_in_3d_space");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps"), "set_generate_mipmaps", "is_generating_mipmaps");
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index f42af0492f..d8d3bacb19 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -1102,7 +1102,7 @@ Size2 OpenXRAPI::get_recommended_target_size() {
return target_size;
}
-XRPose::TrackingConfidence OpenXRAPI::get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, const Vector3 &r_angular_velocity) {
+XRPose::TrackingConfidence OpenXRAPI::get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity) {
XrResult result;
ERR_FAIL_COND_V(!running, XRPose::XR_TRACKING_CONFIDENCE_NONE);
@@ -1730,7 +1730,7 @@ XRPose::TrackingConfidence OpenXRAPI::transform_from_location(const XrHandJointL
return _transform_from_location(p_location, r_transform);
}
-void OpenXRAPI::parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_linear_velocity, Vector3 r_angular_velocity) {
+void OpenXRAPI::parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity) {
if (p_velocity.velocityFlags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) {
XrVector3f linear_velocity = p_velocity.linearVelocity;
r_linear_velocity = Vector3(linear_velocity.x, linear_velocity.y, linear_velocity.z);
@@ -2303,7 +2303,7 @@ Vector2 OpenXRAPI::get_action_vector2(RID p_action, RID p_tracker) {
return result_state.isActive ? Vector2(result_state.currentState.x, result_state.currentState.y) : Vector2();
}
-XRPose::TrackingConfidence OpenXRAPI::get_action_pose(RID p_action, RID p_tracker, Transform3D &r_transform, Vector3 &r_linear_velocity, const Vector3 &r_angular_velocity) {
+XRPose::TrackingConfidence OpenXRAPI::get_action_pose(RID p_action, RID p_tracker, Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity) {
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, XRPose::XR_TRACKING_CONFIDENCE_NONE);
Action *action = action_owner.get_or_null(p_action);
ERR_FAIL_NULL_V(action, XRPose::XR_TRACKING_CONFIDENCE_NONE);
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index bd71f0e1c2..fe9e2937b2 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -225,7 +225,7 @@ protected:
// helper method to get a valid Transform3D from an openxr space location
XRPose::TrackingConfidence transform_from_location(const XrSpaceLocation &p_location, Transform3D &r_transform);
XRPose::TrackingConfidence transform_from_location(const XrHandJointLocationEXT &p_location, Transform3D &r_transform);
- void parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_linear_velocity, Vector3 r_angular_velocity);
+ void parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity);
public:
static bool openxr_is_enabled(bool p_check_run_in_editor = true);
@@ -247,7 +247,7 @@ public:
bool can_render() { return instance != XR_NULL_HANDLE && session != XR_NULL_HANDLE && running && view_pose_valid && frame_state.shouldRender; };
Size2 get_recommended_target_size();
- XRPose::TrackingConfidence get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, const Vector3 &r_angular_velocity);
+ XRPose::TrackingConfidence get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity);
bool get_view_transform(uint32_t p_view, Transform3D &r_transform);
bool get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, CameraMatrix &p_camera_matrix);
bool process();
@@ -285,7 +285,7 @@ public:
bool get_action_bool(RID p_action, RID p_tracker);
float get_action_float(RID p_action, RID p_tracker);
Vector2 get_action_vector2(RID p_action, RID p_tracker);
- XRPose::TrackingConfidence get_action_pose(RID p_action, RID p_tracker, Transform3D &r_transform, Vector3 &r_linear_velocity, const Vector3 &r_angular_velocity);
+ XRPose::TrackingConfidence get_action_pose(RID p_action, RID p_tracker, Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity);
bool trigger_haptic_pulse(RID p_action, RID p_tracker, float p_frequency, float p_amplitude, XrDuration p_duration_ns);
OpenXRAPI();
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 0f45855a76..86687357d6 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -1288,7 +1288,16 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced
fargs.memory_size = p_font_data->data_size;
fargs.flags = FT_OPEN_MEMORY;
fargs.stream = &fd->stream;
- error = FT_Open_Face(ft_library, &fargs, 0, &fd->face);
+
+ int max_index = 0;
+ FT_Face tmp_face;
+ error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
+ if (error == 0) {
+ max_index = tmp_face->num_faces - 1;
+ }
+ FT_Done_Face(tmp_face);
+
+ error = FT_Open_Face(ft_library, &fargs, CLAMP(p_font_data->face_index, 0, max_index), &fd->face);
if (error) {
FT_Done_Face(fd->face);
fd->face = nullptr;
@@ -1329,7 +1338,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced
fd->underline_position = (-FT_MulFix(fd->face->underline_position, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale;
fd->underline_thickness = (FT_MulFix(fd->face->underline_thickness, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale;
- hb_font_set_synthetic_slant(fd->hb_handle, p_font_data->transform.columns[0][1]);
+ hb_font_set_synthetic_slant(fd->hb_handle, p_font_data->transform[0][1]);
if (!p_font_data->face_init) {
// Get style flags and name.
@@ -1720,6 +1729,69 @@ void TextServerAdvanced::font_set_data_ptr(const RID &p_font_rid, const uint8_t
fd->data_size = p_data_size;
}
+void TextServerAdvanced::font_set_face_index(const RID &p_font_rid, int64_t p_face_index) {
+ ERR_FAIL_COND(p_face_index < 0);
+ ERR_FAIL_COND(p_face_index >= 0x7FFF);
+
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->face_index != p_face_index) {
+ fd->face_index = p_face_index;
+ _font_clear_cache(fd);
+ }
+}
+
+int64_t TextServerAdvanced::font_get_face_index(const RID &p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ return fd->face_index;
+}
+
+int64_t TextServerAdvanced::font_get_face_count(const RID &p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ int face_count = 0;
+
+ if (fd->data_ptr && (fd->data_size > 0)) {
+ // Init dynamic font.
+#ifdef MODULE_FREETYPE_ENABLED
+ int error = 0;
+ if (!ft_library) {
+ error = FT_Init_FreeType(&ft_library);
+ ERR_FAIL_COND_V_MSG(error != 0, false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
+ }
+
+ FT_StreamRec stream;
+ memset(&stream, 0, sizeof(FT_StreamRec));
+ stream.base = (unsigned char *)fd->data_ptr;
+ stream.size = fd->data_size;
+ stream.pos = 0;
+
+ FT_Open_Args fargs;
+ memset(&fargs, 0, sizeof(FT_Open_Args));
+ fargs.memory_base = (unsigned char *)fd->data_ptr;
+ fargs.memory_size = fd->data_size;
+ fargs.flags = FT_OPEN_MEMORY;
+ fargs.stream = &stream;
+
+ FT_Face tmp_face;
+ error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
+ if (error == 0) {
+ face_count = tmp_face->num_faces;
+ }
+ FT_Done_Face(tmp_face);
+#endif
+ }
+
+ return face_count;
+}
+
void TextServerAdvanced::font_set_style(const RID &p_font_rid, int64_t /*FontStyle*/ p_style) {
FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2780,7 +2852,7 @@ Vector2 TextServerAdvanced::font_get_kerning(const RID &p_font_rid, int64_t p_si
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
- const HashMap<Vector2i, Vector2, VariantHasher, VariantComparator> &kern = fd->cache[size]->kerning_map;
+ const HashMap<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map;
if (kern.has(p_glyph_pair)) {
if (fd->msdf) {
@@ -3272,7 +3344,7 @@ void TextServerAdvanced::font_set_global_oversampling(double p_oversampling) {
int64_t TextServerAdvanced::_convert_pos(const String &p_utf32, const Char16String &p_utf16, int64_t p_pos) const {
int64_t limit = p_pos;
if (p_utf32.length() != p_utf16.length()) {
- const UChar *data = p_utf16.ptr();
+ const UChar *data = p_utf16.get_data();
for (int i = 0; i < p_pos; i++) {
if (U16_IS_LEAD(data[i])) {
limit--;
@@ -4780,6 +4852,9 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
RID f = p_fonts[p_fb_index];
FontDataAdvanced *fd = font_owner.get_or_null(f);
+ ERR_FAIL_COND(!fd);
+ MutexLock lock(fd->mutex);
+
Vector2i fss = _get_size(fd, fs);
hb_font_t *hb_font = _font_get_hb_handle(f, fs);
double scale = font_get_scale(f, fs);
@@ -5591,7 +5666,7 @@ PackedInt32Array TextServerAdvanced::string_get_word_breaks(const String &p_stri
HashSet<int> breaks;
UErrorCode err = U_ZERO_ERROR;
- UBreakIterator *bi = ubrk_open(UBRK_LINE, p_language.ascii().get_data(), (const UChar *)utf16.ptr(), utf16.length(), &err);
+ UBreakIterator *bi = ubrk_open(UBRK_LINE, p_language.ascii().get_data(), (const UChar *)utf16.get_data(), utf16.length(), &err);
if (U_FAILURE(err)) {
// No data loaded - use fallback.
for (int i = 0; i < p_string.length(); i++) {
@@ -5640,8 +5715,10 @@ TextServerAdvanced::TextServerAdvanced() {
TextServerAdvanced::~TextServerAdvanced() {
_bmp_free_font_funcs();
+#ifdef MODULE_FREETYPE_ENABLED
if (ft_library != nullptr) {
FT_Done_FreeType(ft_library);
}
+#endif
u_cleanup();
}
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index fe1d4bdcbf..e7690cb70d 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -67,8 +67,8 @@
#include <godot_cpp/classes/ref.hpp>
#include <godot_cpp/templates/hash_map.hpp>
+#include <godot_cpp/templates/hash_set.hpp>
#include <godot_cpp/templates/rid_owner.hpp>
-#include <godot_cpp/templates/set.hpp>
#include <godot_cpp/templates/thread_work_pool.hpp>
#include <godot_cpp/templates/vector.hpp>
@@ -191,7 +191,7 @@ class TextServerAdvanced : public TextServerExtension {
Vector<FontTexture> textures;
HashMap<int32_t, FontGlyph> glyph_map;
- HashMap<Vector2i, Vector2, VariantHasher, VariantComparator> kerning_map;
+ HashMap<Vector2i, Vector2> kerning_map;
hb_font_t *hb_handle = nullptr;
#ifdef MODULE_FREETYPE_ENABLED
@@ -247,6 +247,7 @@ class TextServerAdvanced : public TextServerExtension {
PackedByteArray data;
const uint8_t *data_ptr;
size_t data_size;
+ int face_index = 0;
mutable ThreadWorkPool work_pool;
~FontDataAdvanced() {
@@ -473,6 +474,11 @@ public:
virtual void font_set_data(const RID &p_font_rid, const PackedByteArray &p_data) override;
virtual void font_set_data_ptr(const RID &p_font_rid, const uint8_t *p_data_ptr, int64_t p_data_size) override;
+ virtual void font_set_face_index(const RID &p_font_rid, int64_t p_index) override;
+ virtual int64_t font_get_face_index(const RID &p_font_rid) const override;
+
+ virtual int64_t font_get_face_count(const RID &p_font_rid) const override;
+
virtual void font_set_style(const RID &p_font_rid, int64_t /*FontStyle*/ p_style) override;
virtual int64_t /*FontStyle*/ font_get_style(const RID &p_font_rid) const override;
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 498b58175e..0bece4e7a7 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -733,7 +733,16 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback
fargs.memory_size = p_font_data->data_size;
fargs.flags = FT_OPEN_MEMORY;
fargs.stream = &fd->stream;
- error = FT_Open_Face(ft_library, &fargs, 0, &fd->face);
+
+ int max_index = 0;
+ FT_Face tmp_face;
+ error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
+ if (error == 0) {
+ max_index = tmp_face->num_faces - 1;
+ }
+ FT_Done_Face(tmp_face);
+
+ error = FT_Open_Face(ft_library, &fargs, CLAMP(p_font_data->face_index, 0, max_index), &fd->face);
if (error) {
FT_Done_Face(fd->face);
fd->face = nullptr;
@@ -892,6 +901,69 @@ void TextServerFallback::font_set_style(const RID &p_font_rid, int64_t /*FontSty
fd->style_flags = p_style;
}
+void TextServerFallback::font_set_face_index(const RID &p_font_rid, int64_t p_face_index) {
+ ERR_FAIL_COND(p_face_index < 0);
+ ERR_FAIL_COND(p_face_index >= 0x7FFF);
+
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->face_index != p_face_index) {
+ fd->face_index = p_face_index;
+ _font_clear_cache(fd);
+ }
+}
+
+int64_t TextServerFallback::font_get_face_index(const RID &p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ return fd->face_index;
+}
+
+int64_t TextServerFallback::font_get_face_count(const RID &p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ int face_count = 0;
+
+ if (fd->data_ptr && (fd->data_size > 0)) {
+ // Init dynamic font.
+#ifdef MODULE_FREETYPE_ENABLED
+ int error = 0;
+ if (!ft_library) {
+ error = FT_Init_FreeType(&ft_library);
+ ERR_FAIL_COND_V_MSG(error != 0, false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
+ }
+
+ FT_StreamRec stream;
+ memset(&stream, 0, sizeof(FT_StreamRec));
+ stream.base = (unsigned char *)fd->data_ptr;
+ stream.size = fd->data_size;
+ stream.pos = 0;
+
+ FT_Open_Args fargs;
+ memset(&fargs, 0, sizeof(FT_Open_Args));
+ fargs.memory_base = (unsigned char *)fd->data_ptr;
+ fargs.memory_size = fd->data_size;
+ fargs.flags = FT_OPEN_MEMORY;
+ fargs.stream = &stream;
+
+ FT_Face tmp_face;
+ error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
+ if (error == 0) {
+ face_count = tmp_face->num_faces;
+ }
+ FT_Done_Face(tmp_face);
+#endif
+ }
+
+ return face_count;
+}
+
int64_t /*FontStyle*/ TextServerFallback::font_get_style(const RID &p_font_rid) const {
FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0);
@@ -1928,7 +2000,7 @@ Vector2 TextServerFallback::font_get_kerning(const RID &p_font_rid, int64_t p_si
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
- const HashMap<Vector2i, Vector2, VariantHasher, VariantComparator> &kern = fd->cache[size]->kerning_map;
+ const HashMap<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map;
if (kern.has(p_glyph_pair)) {
if (fd->msdf) {
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index 0d2fc2628d..e4c81aed5b 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -67,8 +67,8 @@
#include <godot_cpp/classes/ref.hpp>
#include <godot_cpp/templates/hash_map.hpp>
+#include <godot_cpp/templates/hash_set.hpp>
#include <godot_cpp/templates/rid_owner.hpp>
-#include <godot_cpp/templates/set.hpp>
#include <godot_cpp/templates/thread_work_pool.hpp>
#include <godot_cpp/templates/vector.hpp>
@@ -159,7 +159,7 @@ class TextServerFallback : public TextServerExtension {
Vector<FontTexture> textures;
HashMap<int32_t, FontGlyph> glyph_map;
- HashMap<Vector2i, Vector2, VariantHasher, VariantComparator> kerning_map;
+ HashMap<Vector2i, Vector2> kerning_map;
#ifdef MODULE_FREETYPE_ENABLED
FT_Face face = nullptr;
@@ -209,6 +209,7 @@ class TextServerFallback : public TextServerExtension {
PackedByteArray data;
const uint8_t *data_ptr;
size_t data_size;
+ int face_index = 0;
mutable ThreadWorkPool work_pool;
@@ -364,6 +365,11 @@ public:
virtual void font_set_data(const RID &p_font_rid, const PackedByteArray &p_data) override;
virtual void font_set_data_ptr(const RID &p_font_rid, const uint8_t *p_data_ptr, int64_t p_data_size) override;
+ virtual void font_set_face_index(const RID &p_font_rid, int64_t p_index) override;
+ virtual int64_t font_get_face_index(const RID &p_font_rid) const override;
+
+ virtual int64_t font_get_face_count(const RID &p_font_rid) const override;
+
virtual void font_set_style(const RID &p_font_rid, int64_t /*FontStyle*/ p_style) override;
virtual int64_t /*FontStyle*/ font_get_style(const RID &p_font_rid) const override;
diff --git a/modules/visual_script/editor/visual_script_editor.cpp b/modules/visual_script/editor/visual_script_editor.cpp
index 684faf7c8e..ceea0eaf1d 100644
--- a/modules/visual_script/editor/visual_script_editor.cpp
+++ b/modules/visual_script/editor/visual_script_editor.cpp
@@ -889,7 +889,49 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_button_resource_previewed", arr);
} else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_ENUM) {
- button->set_text(pi.hint_string.get_slice(",", value));
+ bool found = false;
+ const Vector<String> options = pi.hint_string.split(",");
+ int64_t current_val = 0;
+ for (const String &option : options) {
+ Vector<String> text_split = option.split(":");
+ if (text_split.size() != 1) {
+ current_val = text_split[1].to_int();
+ }
+ if (value.operator int() == current_val) {
+ button->set_text(text_split[0]);
+ found = true;
+ break;
+ }
+ current_val += 1;
+ }
+ if (!found) {
+ button->set_text(value);
+ }
+ } else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_FLAGS) {
+ Vector<String> value_texts;
+ const Vector<String> options = pi.hint_string.split(",");
+ uint32_t v = value;
+ for (const String &option : options) {
+ uint32_t current_val;
+ Vector<String> text_split = option.split(":");
+ if (text_split.size() != -1) {
+ current_val = text_split[1].to_int();
+ } else {
+ current_val = 1 << i;
+ }
+ if ((v & current_val) == current_val) {
+ value_texts.push_back(text_split[0]);
+ }
+ }
+ if (value_texts.size() != 0) {
+ String value_text = value_texts[0];
+ for (const String &text : value_texts) {
+ value_text += " | " + text;
+ }
+ button->set_text(value_text);
+ } else {
+ button->set_text(value);
+ }
} else {
button->set_text(value);
}
@@ -3933,6 +3975,9 @@ void VisualScriptEditor::_notification(int p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
graph->get_panner()->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
graph->set_warped_panning(bool(EditorSettings::get_singleton()->get("editors/panning/warped_mouse_panning")));
+ graph->set_minimap_opacity(EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity"));
+ graph->set_connection_lines_curvature(EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature"));
+ _update_graph();
} break;
case NOTIFICATION_READY: {
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index 24bb22960e..30b64d0a7b 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -1528,7 +1528,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
// If no exit bit was set, and has sequence outputs, guess next node.
if (output >= node->sequence_output_count) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- error_str = RTR("Node returned an invalid sequence output: ") + itos(output);
+ error_str = RTR("Node returned an invalid sequence output:") + " " + itos(output);
error = true;
break;
}
@@ -1594,7 +1594,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
// Check for stack overflow.
if (flow_stack_pos + 1 >= flow_max) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- error_str = RTR("Stack overflow with stack depth: ") + itos(output);
+ error_str = RTR("Stack overflow with stack depth:") + " " + itos(output);
error = true;
break;
}
diff --git a/modules/visual_script/visual_script_flow_control.cpp b/modules/visual_script/visual_script_flow_control.cpp
index 0e63753720..bbbb995635 100644
--- a/modules/visual_script/visual_script_flow_control.cpp
+++ b/modules/visual_script/visual_script_flow_control.cpp
@@ -391,7 +391,7 @@ public:
if (!valid) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = RTR("Input type not iterable: ") + Variant::get_type_name(p_inputs[0]->get_type());
+ r_error_str = RTR("Input type not iterable:") + " " + Variant::get_type_name(p_inputs[0]->get_type());
return 0;
}
@@ -414,7 +414,7 @@ public:
if (!valid) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = RTR("Iterator became invalid: ") + Variant::get_type_name(p_inputs[0]->get_type());
+ r_error_str = RTR("Iterator became invalid:") + " " + Variant::get_type_name(p_inputs[0]->get_type());
return 0;
}
diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp
index ac33526ddc..2dfc6da181 100644
--- a/modules/visual_script/visual_script_nodes.cpp
+++ b/modules/visual_script/visual_script_nodes.cpp
@@ -1116,9 +1116,9 @@ public:
r_error_str = *p_outputs[0];
} else {
if (unary) {
- r_error_str = String(Variant::get_operator_name(op)) + RTR(": Invalid argument of type: ") + Variant::get_type_name(p_inputs[0]->get_type());
+ r_error_str = String(Variant::get_operator_name(op)) + ": " + RTR("Invalid argument of type:") + " " + Variant::get_type_name(p_inputs[0]->get_type());
} else {
- r_error_str = String(Variant::get_operator_name(op)) + RTR(": Invalid arguments: ") + "A: " + Variant::get_type_name(p_inputs[0]->get_type()) + " B: " + Variant::get_type_name(p_inputs[1]->get_type());
+ r_error_str = String(Variant::get_operator_name(op)) + ": " + RTR("Invalid arguments:") + " A: " + Variant::get_type_name(p_inputs[0]->get_type()) + ", B: " + Variant::get_type_name(p_inputs[1]->get_type());
}
}
}
@@ -1335,7 +1335,7 @@ public:
virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
if (!instance->get_variable(variable, p_outputs[0])) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = RTR("VariableGet not found in script: ") + "'" + String(variable) + "'";
+ r_error_str = RTR("VariableGet not found in script:") + " '" + String(variable) + "'";
return 0;
}
return 0;
@@ -1447,7 +1447,7 @@ public:
virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
if (!instance->set_variable(variable, *p_inputs[0])) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = RTR("VariableSet not found in script: ") + "'" + String(variable) + "'";
+ r_error_str = RTR("VariableSet not found in script:") + " '" + String(variable) + "'";
}
return 0;
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
index b4d97077e3..fed67397d1 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
@@ -95,6 +95,13 @@
Call this method frequently (e.g. in [method Node._process] or [method Node._physics_process]) to properly receive signals.
</description>
</method>
+ <method name="set_default_extension" qualifiers="static">
+ <return type="void" />
+ <argument index="0" name="extension_class" type="StringName" />
+ <description>
+ Sets the [code]extension_class[/code] as the default [WebRTCPeerConnectionExtension] returned when creating a new [WebRTCPeerConnection].
+ </description>
+ </method>
<method name="set_local_description">
<return type="int" enum="Error" />
<argument index="0" name="type" type="String" />
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml b/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml
index e88acdc845..163d939ac1 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml
@@ -62,10 +62,5 @@
<description>
</description>
</method>
- <method name="make_default">
- <return type="void" />
- <description>
- </description>
- </method>
</methods>
</class>
diff --git a/modules/webrtc/webrtc_peer_connection.cpp b/modules/webrtc/webrtc_peer_connection.cpp
index 7fdf26d3cd..75716017d7 100644
--- a/modules/webrtc/webrtc_peer_connection.cpp
+++ b/modules/webrtc/webrtc_peer_connection.cpp
@@ -32,13 +32,14 @@
#ifdef JAVASCRIPT_ENABLED
#include "webrtc_peer_connection_js.h"
-#else
-#include "webrtc_peer_connection_extension.h"
#endif
+#include "webrtc_peer_connection_extension.h"
+
StringName WebRTCPeerConnection::default_extension;
void WebRTCPeerConnection::set_default_extension(const StringName &p_extension) {
+ ERR_FAIL_COND_MSG(!ClassDB::is_parent_class(p_extension, WebRTCPeerConnectionExtension::get_class_static()), vformat("Can't make %s the default WebRTC extension since it does not extend WebRTCPeerConnectionExtension.", p_extension));
default_extension = p_extension;
}
@@ -56,6 +57,8 @@ WebRTCPeerConnection *WebRTCPeerConnection::create() {
}
void WebRTCPeerConnection::_bind_methods() {
+ ClassDB::bind_static_method(get_class_static(), D_METHOD("set_default_extension", "extension_class"), &WebRTCPeerConnectionExtension::set_default_extension);
+
ClassDB::bind_method(D_METHOD("initialize", "configuration"), &WebRTCPeerConnection::initialize, DEFVAL(Dictionary()));
ClassDB::bind_method(D_METHOD("create_data_channel", "label", "options"), &WebRTCPeerConnection::create_data_channel, DEFVAL(Dictionary()));
ClassDB::bind_method(D_METHOD("create_offer"), &WebRTCPeerConnection::create_offer);
diff --git a/modules/webrtc/webrtc_peer_connection_extension.cpp b/modules/webrtc/webrtc_peer_connection_extension.cpp
index 3bc7de217e..85c04b3b19 100644
--- a/modules/webrtc/webrtc_peer_connection_extension.cpp
+++ b/modules/webrtc/webrtc_peer_connection_extension.cpp
@@ -31,8 +31,6 @@
#include "webrtc_peer_connection_extension.h"
void WebRTCPeerConnectionExtension::_bind_methods() {
- ClassDB::bind_method(D_METHOD("make_default"), &WebRTCPeerConnectionExtension::make_default);
-
GDVIRTUAL_BIND(_get_connection_state);
GDVIRTUAL_BIND(_initialize, "p_config");
GDVIRTUAL_BIND(_create_data_channel, "p_label", "p_config");
@@ -44,11 +42,6 @@ void WebRTCPeerConnectionExtension::_bind_methods() {
GDVIRTUAL_BIND(_close);
}
-void WebRTCPeerConnectionExtension::make_default() {
- ERR_FAIL_COND_MSG(!_get_extension(), vformat("Can't make %s the default without extending it.", get_class()));
- WebRTCPeerConnection::set_default_extension(get_class());
-}
-
WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionExtension::get_connection_state() const {
int state;
if (GDVIRTUAL_CALL(_get_connection_state, state)) {
diff --git a/modules/webrtc/webrtc_peer_connection_extension.h b/modules/webrtc/webrtc_peer_connection_extension.h
index 82e32b5602..bde19c173b 100644
--- a/modules/webrtc/webrtc_peer_connection_extension.h
+++ b/modules/webrtc/webrtc_peer_connection_extension.h
@@ -44,8 +44,6 @@ protected:
static void _bind_methods();
public:
- void make_default();
-
virtual ConnectionState get_connection_state() const override;
virtual Error initialize(Dictionary p_config = Dictionary()) override;