diff options
Diffstat (limited to 'modules')
135 files changed, 1745 insertions, 704 deletions
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index a5b3db89d9..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_"); @@ -1052,7 +1052,7 @@ void CSGSphere3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGSphere3D::set_material); ClassDB::bind_method(D_METHOD("get_material"), &CSGSphere3D::get_material); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,suffix:m"), "set_radius", "get_radius"); ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1"), "set_radial_segments", "get_radial_segments"); ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1"), "set_rings", "get_rings"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces"); @@ -1225,7 +1225,7 @@ void CSGBox3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGBox3D::set_material); ClassDB::bind_method(D_METHOD("get_material"), &CSGBox3D::get_material); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material"); } @@ -1405,8 +1405,8 @@ void CSGCylinder3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGCylinder3D::set_smooth_faces); ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGCylinder3D::get_smooth_faces); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_radius", "get_radius"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp,suffix:m"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp,suffix:m"), "set_height", "get_height"); ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cone"), "set_cone", "is_cone"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces"); @@ -1630,8 +1630,8 @@ void CSGTorus3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGTorus3D::set_smooth_faces); ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGTorus3D::get_smooth_faces); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_inner_radius", "get_inner_radius"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_outer_radius", "get_outer_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp,suffix:m"), "set_inner_radius", "get_inner_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp,suffix:m"), "set_outer_radius", "get_outer_radius"); ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides"); ADD_PROPERTY(PropertyInfo(Variant::INT, "ring_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_ring_sides", "get_ring_sides"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces"); @@ -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/csg/editor/csg_gizmos.cpp b/modules/csg/editor/csg_gizmos.cpp index 4d972e46c6..6442ff71fc 100644 --- a/modules/csg/editor/csg_gizmos.cpp +++ b/modules/csg/editor/csg_gizmos.cpp @@ -349,9 +349,11 @@ void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { if (cs->is_root_shape()) { Array csg_meshes = cs->get_meshes(); - Ref<Mesh> csg_mesh = csg_meshes[1]; - if (csg_mesh.is_valid()) { - p_gizmo->add_collision_triangles(csg_mesh->generate_triangle_mesh()); + if (csg_meshes.size() == 2) { + Ref<Mesh> csg_mesh = csg_meshes[1]; + if (csg_mesh.is_valid()) { + p_gizmo->add_collision_triangles(csg_mesh->generate_triangle_mesh()); + } } } diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub index d23c4b637c..4b2ea6faa5 100644 --- a/modules/freetype/SCsub +++ b/modules/freetype/SCsub @@ -49,6 +49,7 @@ if env["builtin_freetype"]: "src/psnames/psnames.c", "src/raster/raster.c", "src/sdf/sdf.c", + "src/svg/svg.c", "src/smooth/smooth.c", "src/truetype/truetype.c", "src/type1/type1.c", @@ -87,7 +88,7 @@ if env["builtin_freetype"]: # Also needed in main env for scene/ env.Prepend(CPPPATH=[thirdparty_dir + "/include"]) - env_freetype.Append(CPPDEFINES=["FT2_BUILD_LIBRARY", "FT_CONFIG_OPTION_USE_PNG"]) + env_freetype.Append(CPPDEFINES=["FT2_BUILD_LIBRARY", "FT_CONFIG_OPTION_USE_PNG", "FT_CONFIG_OPTION_SYSTEM_ZLIB"]) if env["target"] == "debug": env_freetype.Append(CPPDEFINES=["ZLIB_DEBUG"]) diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index 191568661d..b86e9b386d 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -387,9 +387,9 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l in_member_variable = false; } - if (!in_node_path && in_region == -1 && str[j] == '$') { + if (!in_node_path && in_region == -1 && (str[j] == '$' || str[j] == '%')) { in_node_path = true; - } else if (in_region != -1 || (is_a_symbol && str[j] != '/')) { + } else if (in_region != -1 || (is_a_symbol && str[j] != '/' && str[j] != '%')) { in_node_path = false; } diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.h b/modules/gdscript/editor/gdscript_translation_parser_plugin.h index 4633a431d8..969a50f48c 100644 --- a/modules/gdscript/editor/gdscript_translation_parser_plugin.h +++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.h @@ -31,7 +31,7 @@ #ifndef GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H #define GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H -#include "core/templates/rb_set.h" +#include "core/templates/hash_set.h" #include "editor/editor_translation_parser.h" #include "modules/gdscript/gdscript_parser.h" @@ -44,9 +44,9 @@ class GDScriptEditorTranslationParserPlugin : public EditorTranslationParserPlug // List of patterns used for extracting translation strings. StringName tr_func = "tr"; StringName trn_func = "tr_n"; - RBSet<StringName> assignment_patterns; - RBSet<StringName> first_arg_patterns; - RBSet<StringName> second_arg_patterns; + HashSet<StringName> assignment_patterns; + HashSet<StringName> first_arg_patterns; + HashSet<StringName> second_arg_patterns; // FileDialog patterns. StringName fd_add_filter = "add_filter"; StringName fd_set_filter = "set_filters"; 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 e400d0bf94..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); } @@ -764,8 +765,8 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc _update_exports_values(values, propnames); if (changed) { - for (RBSet<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { - E->get()->update(propnames, values); + for (PlaceHolderScriptInstance *E : placeholders) { + E->update(propnames, values); } } else { p_instance_to_update->update(propnames, values); @@ -788,10 +789,10 @@ void GDScript::update_exports() { return; } - RBSet<ObjectID> copy = inheriters_cache; //might get modified + HashSet<ObjectID> copy = inheriters_cache; //might get modified - for (RBSet<ObjectID>::Element *E = copy.front(); E; E = E->next()) { - Object *id = ObjectDB::get_instance(E->get()); + for (const ObjectID &E : copy) { + Object *id = ObjectDB::get_instance(E); GDScript *s = Object::cast_to<GDScript>(id); if (!s) { continue; @@ -937,10 +938,10 @@ void GDScript::get_constants(HashMap<StringName, Variant> *p_constants) { } } -void GDScript::get_members(RBSet<StringName> *p_members) { +void GDScript::get_members(HashSet<StringName> *p_members) { if (p_members) { - for (RBSet<StringName>::Element *E = members.front(); E; E = E->next()) { - p_members->insert(E->get()); + for (const StringName &E : members) { + p_members->insert(E); } } } @@ -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); } @@ -1916,7 +1920,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so #ifdef TOOLS_ENABLED while (script->placeholders.size()) { - Object *obj = script->placeholders.front()->get()->get_owner(); + Object *obj = (*script->placeholders.begin())->get_owner(); //save instance info if (obj->get_script_instance()) { @@ -1926,7 +1930,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so obj->set_script(Variant()); } else { // no instance found. Let's remove it so we don't loop forever - script->placeholders.erase(script->placeholders.front()->get()); + script->placeholders.erase(*script->placeholders.begin()); } } @@ -2232,9 +2236,13 @@ GDScriptLanguage::GDScriptLanguage() { GLOBAL_DEF("debug/gdscript/warnings/treat_warnings_as_errors", false); GLOBAL_DEF("debug/gdscript/warnings/exclude_addons", true); for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) { - String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower(); - bool default_enabled = !warning.begins_with("unsafe_"); - GLOBAL_DEF("debug/gdscript/warnings/" + warning, default_enabled); + GDScriptWarning::Code code = (GDScriptWarning::Code)i; + Variant default_enabled = GDScriptWarning::get_default_value(code); + String path = GDScriptWarning::get_settings_path_from_code(code); + GLOBAL_DEF(path, default_enabled); + + PropertyInfo property_info = GDScriptWarning::get_property_info(code); + ProjectSettings::get_singleton()->set_custom_property_info(path, property_info); } #endif // DEBUG_ENABLED } diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 5199d3215d..80f187a375 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -37,6 +37,7 @@ #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/object/script_language.h" +#include "core/templates/rb_set.h" #include "gdscript_function.h" class GDScriptNativeClass : public RefCounted { @@ -80,7 +81,7 @@ class GDScript : public Script { GDScript *_base = nullptr; //fast pointer access GDScript *_owner = nullptr; //for subclasses - RBSet<StringName> members; //members are just indices to the instantiated script. + HashSet<StringName> members; //members are just indices to the instantiated script. HashMap<StringName, Variant> constants; HashMap<StringName, GDScriptFunction *> member_functions; HashMap<StringName, MemberInfo> member_indices; //members are just indices to the instantiated script. @@ -95,7 +96,7 @@ class GDScript : public Script { List<PropertyInfo> members_cache; HashMap<StringName, Variant> member_default_values_cache; Ref<GDScript> base_cache; - RBSet<ObjectID> inheriters_cache; + HashSet<ObjectID> inheriters_cache; bool source_changed_cache = false; bool placeholder_fallback_enabled = false; void _update_exports_values(HashMap<StringName, Variant> &values, List<PropertyInfo> &propnames); @@ -139,7 +140,7 @@ class GDScript : public Script { String _get_debug_path() const; #ifdef TOOLS_ENABLED - RBSet<PlaceHolderScriptInstance *> placeholders; + HashSet<PlaceHolderScriptInstance *> placeholders; //void _update_placeholder(PlaceHolderScriptInstance *p_placeholder); virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override; #endif @@ -178,7 +179,7 @@ public: const HashMap<StringName, Ref<GDScript>> &get_subclasses() const { return subclasses; } const HashMap<StringName, Variant> &get_constants() const { return constants; } - const RBSet<StringName> &get_members() const { return members; } + const HashSet<StringName> &get_members() const { return members; } const GDScriptDataType &get_member_type(const StringName &p_member) const { CRASH_COND(!member_indices.has(p_member)); return member_indices[p_member].data_type; @@ -246,7 +247,7 @@ public: } virtual void get_constants(HashMap<StringName, Variant> *p_constants) override; - virtual void get_members(RBSet<StringName> *p_members) override; + virtual void get_members(HashSet<StringName> *p_members) override; virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override; @@ -449,7 +450,7 @@ public: virtual bool is_using_templates() override; virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const override; virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) override; - virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, RBSet<int> *r_safe_lines = nullptr) const override; + virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override; virtual Script *create_script() const override; virtual bool has_named_classes() const override; virtual bool supports_builtin_mode() const override; diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 32fa3b8c87..a070d319f3 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -898,7 +898,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) { } #ifdef DEBUG_ENABLED - RBSet<uint32_t> previously_ignored = parser->ignored_warning_codes; + HashSet<uint32_t> previously_ignored = parser->ignored_warning_codes; for (uint32_t ignored_warning : member.function->ignored_warnings) { parser->ignored_warning_codes.insert(ignored_warning); } @@ -947,7 +947,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) { GDScriptParser::ClassNode::Member member = p_class->members[i]; if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) { #ifdef DEBUG_ENABLED - RBSet<uint32_t> previously_ignored = parser->ignored_warning_codes; + HashSet<uint32_t> previously_ignored = parser->ignored_warning_codes; for (uint32_t ignored_warning : member.function->ignored_warnings) { parser->ignored_warning_codes.insert(ignored_warning); } @@ -1160,8 +1160,16 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * } } } else { - GDScriptParser::DataType return_type = resolve_datatype(p_function->return_type); - p_function->set_datatype(return_type); + if (p_function->return_type != nullptr) { + p_function->set_datatype(resolve_datatype(p_function->return_type)); + } else { + // In case the function is not typed, we can safely assume it's a Variant, so it's okay to mark as "inferred" here. + // It's not "undetected" to not mix up with unknown functions. + GDScriptParser::DataType return_type; + return_type.type_source = GDScriptParser::DataType::INFERRED; + return_type.kind = GDScriptParser::DataType::VARIANT; + p_function->set_datatype(return_type); + } #ifdef TOOLS_ENABLED // Check if the function signature matches the parent. If not it's an error since it breaks polymorphism. @@ -1231,7 +1239,7 @@ void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_fun GDScriptParser::DataType return_type = p_function->body->get_datatype(); - if (p_function->get_datatype().has_no_type() && return_type.is_set()) { + if (!p_function->get_datatype().is_hard_type() && return_type.is_set()) { // Use the suite inferred type if return isn't explicitly set. return_type.type_source = GDScriptParser::DataType::INFERRED; p_function->set_datatype(p_function->body->get_datatype()); @@ -1279,7 +1287,7 @@ void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) { } #ifdef DEBUG_ENABLED - RBSet<uint32_t> previously_ignored = parser->ignored_warning_codes; + HashSet<uint32_t> previously_ignored = parser->ignored_warning_codes; for (uint32_t ignored_warning : stmt->ignored_warnings) { parser->ignored_warning_codes.insert(ignored_warning); } @@ -1514,10 +1522,22 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant) { GDScriptParser::DataType type; + GDScriptParser::DataType explicit_type; + if (p_constant->datatype_specifier != nullptr) { + explicit_type = resolve_datatype(p_constant->datatype_specifier); + explicit_type.is_meta_type = false; + } + if (p_constant->initializer != nullptr) { reduce_expression(p_constant->initializer); if (p_constant->initializer->type == GDScriptParser::Node::ARRAY) { - const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer)); + GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer); + const_fold_array(array); + + // Can only infer typed array if it has elements. + if (array->elements.size() > 0 || (p_constant->datatype_specifier != nullptr && explicit_type.has_container_element_type())) { + update_array_literal_element_type(explicit_type, array); + } } else if (p_constant->initializer->type == GDScriptParser::Node::DICTIONARY) { const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_constant->initializer)); } @@ -1536,8 +1556,6 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant } if (p_constant->datatype_specifier != nullptr) { - GDScriptParser::DataType explicit_type = resolve_datatype(p_constant->datatype_specifier); - explicit_type.is_meta_type = false; if (!is_type_compatible(explicit_type, type)) { push_error(vformat(R"(Assigned value for constant "%s" has type %s which is not compatible with defined type %s.)", p_constant->identifier->name, type.to_string(), explicit_type.to_string()), p_constant->initializer); #ifdef DEBUG_ENABLED @@ -1639,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; @@ -2057,7 +2075,8 @@ void GDScriptAnalyzer::reduce_await(GDScriptParser::AwaitNode *p_await) { p_await->set_datatype(awaiting_type); #ifdef DEBUG_ENABLED - if (!awaiting_type.is_coroutine && awaiting_type.builtin_type != Variant::SIGNAL) { + awaiting_type = p_await->to_await->get_datatype(); + if (!(awaiting_type.has_no_type() || awaiting_type.is_coroutine || awaiting_type.builtin_type == Variant::SIGNAL)) { parser->push_warning(p_await, GDScriptWarning::REDUNDANT_AWAIT); } #endif diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 5b03f6dbb4..3966b81b6e 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -33,7 +33,7 @@ #include "core/object/object.h" #include "core/object/ref_counted.h" -#include "core/templates/rb_set.h" +#include "core/templates/hash_set.h" #include "gdscript_cache.h" #include "gdscript_parser.h" diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index bd98d66fcc..4c15fca91e 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -223,13 +223,13 @@ Error GDScriptCache::finish_compiling(const String &p_owner) { singleton->full_gdscript_cache[p_owner] = script.ptr(); singleton->shallow_gdscript_cache.erase(p_owner); - RBSet<String> depends = singleton->dependencies[p_owner]; + HashSet<String> depends = singleton->dependencies[p_owner]; Error err = OK; - for (const RBSet<String>::Element *E = depends.front(); E != nullptr; E = E->next()) { + for (const String &E : depends) { Error this_err = OK; // No need to save the script. We assume it's already referenced in the owner. - get_full_script(E->get(), this_err); + get_full_script(E, this_err); if (this_err != OK) { err = this_err; diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h index 8abae7d4ad..b971bdd984 100644 --- a/modules/gdscript/gdscript_cache.h +++ b/modules/gdscript/gdscript_cache.h @@ -34,7 +34,7 @@ #include "core/object/ref_counted.h" #include "core/os/mutex.h" #include "core/templates/hash_map.h" -#include "core/templates/rb_set.h" +#include "core/templates/hash_set.h" #include "gdscript.h" class GDScriptAnalyzer; @@ -74,7 +74,7 @@ class GDScriptCache { HashMap<String, GDScriptParserRef *> parser_map; HashMap<String, GDScript *> shallow_gdscript_cache; HashMap<String, GDScript *> full_gdscript_cache; - HashMap<String, RBSet<String>> dependencies; + HashMap<String, HashSet<String>> dependencies; friend class GDScript; friend class GDScriptParserRef; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index cb389fd86a..b2cce9d8ee 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -108,11 +108,15 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D result.native_type = result.script_type->get_instance_base_type(); } break; case GDScriptParser::DataType::CLASS: { - // Locate class by constructing the path to it and following that path + // Locate class by constructing the path to it and following that path. GDScriptParser::ClassNode *class_type = p_datatype.class_type; if (class_type) { - const bool is_inner_by_path = (!main_script->path.is_empty()) && (class_type->fqcn.split("::")[0] == main_script->path); - const bool is_inner_by_name = (!main_script->name.is_empty()) && (class_type->fqcn.split("::")[0] == main_script->name); + result.kind = GDScriptDataType::GDSCRIPT; + result.builtin_type = p_datatype.builtin_type; + + String class_name = class_type->fqcn.split("::")[0]; + const bool is_inner_by_path = (!main_script->path.is_empty()) && (class_name == main_script->path); + const bool is_inner_by_name = (!main_script->name.is_empty()) && (class_name == main_script->name); if (is_inner_by_path || is_inner_by_name) { // Local class. List<StringName> names; @@ -131,16 +135,41 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D script = script->subclasses[names.back()->get()]; names.pop_back(); } - result.kind = GDScriptDataType::GDSCRIPT; result.script_type = script.ptr(); result.native_type = script->get_instance_base_type(); - result.builtin_type = p_datatype.builtin_type; } else { - result.kind = GDScriptDataType::GDSCRIPT; - result.script_type_ref = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path); + // Inner class. + PackedStringArray classes = class_type->fqcn.split("::"); + if (!classes.is_empty()) { + for (GDScript *script : parsed_classes) { + // Checking of inheritance structure of inner class to find a correct script link. + if (script->name == classes[classes.size() - 1]) { + PackedStringArray classes2 = script->fully_qualified_name.split("::"); + bool valid = true; + if (classes.size() != classes2.size()) { + valid = false; + } else { + for (int i = 0; i < classes.size(); i++) { + if (classes[i] != classes2[i]) { + valid = false; + break; + } + } + } + if (!valid) { + continue; + } + result.script_type_ref = Ref<GDScript>(script); + break; + } + } + } + if (result.script_type_ref.is_null()) { + result.script_type_ref = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path); + } + result.script_type = result.script_type_ref.ptr(); result.native_type = p_datatype.native_type; - result.builtin_type = p_datatype.builtin_type; } } } break; @@ -638,20 +667,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code case GDScriptParser::Node::GET_NODE: { const GDScriptParser::GetNodeNode *get_node = static_cast<const GDScriptParser::GetNodeNode *>(p_expression); - String node_name; - if (get_node->string != nullptr) { - node_name += String(get_node->string->value); - } else { - for (int i = 0; i < get_node->chain.size(); i++) { - if (i > 0) { - node_name += "/"; - } - node_name += get_node->chain[i]->name; - } - } - Vector<GDScriptCodeGenerator::Address> args; - args.push_back(codegen.add_constant(NodePath(node_name))); + args.push_back(codegen.add_constant(NodePath(get_node->full_path))); GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(get_node->get_datatype())); @@ -1372,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++) { @@ -1400,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); @@ -1414,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) { @@ -1471,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++) { @@ -1502,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); @@ -1513,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); @@ -1528,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) { @@ -1548,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/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h index c9ffb04fb8..4841884e2d 100644 --- a/modules/gdscript/gdscript_compiler.h +++ b/modules/gdscript/gdscript_compiler.h @@ -31,7 +31,7 @@ #ifndef GDSCRIPT_COMPILER_H #define GDSCRIPT_COMPILER_H -#include "core/templates/rb_set.h" +#include "core/templates/hash_set.h" #include "gdscript.h" #include "gdscript_codegen.h" #include "gdscript_function.h" @@ -39,8 +39,8 @@ class GDScriptCompiler { const GDScriptParser *parser = nullptr; - RBSet<GDScript *> parsed_classes; - RBSet<GDScript *> parsing_classes; + HashSet<GDScript *> parsed_classes; + HashSet<GDScript *> parsing_classes; GDScript *main_script = nullptr; struct CodeGen { diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 72f54626e3..202d1dcdf4 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -110,7 +110,7 @@ static void get_function_names_recursively(const GDScriptParser::ClassNode *p_cl } } -bool GDScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, RBSet<int> *r_safe_lines) const { +bool GDScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, HashSet<int> *r_safe_lines) const { GDScriptParser parser; GDScriptAnalyzer analyzer(&parser); @@ -159,7 +159,7 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li #ifdef DEBUG_ENABLED if (r_safe_lines) { - const RBSet<int> &unsafe_lines = parser.get_unsafe_lines(); + const HashSet<int> &unsafe_lines = parser.get_unsafe_lines(); for (int i = 1; i <= parser.get_last_line_number(); i++) { if (!unsafe_lines.has(i)) { r_safe_lines->insert(i); @@ -3155,7 +3155,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co } ::Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) { - // Before parsing, try the usual stuff + // Before parsing, try the usual stuff. if (ClassDB::class_exists(p_symbol)) { r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS; r_result.class_name = p_symbol; @@ -3171,7 +3171,9 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co } } - if (GDScriptUtilityFunctions::function_exists(p_symbol)) { + // Need special checks for assert and preload as they are technically + // keywords, so are not registered in GDScriptUtilityFunctions. + if (GDScriptUtilityFunctions::function_exists(p_symbol) || "assert" == p_symbol || "preload" == p_symbol) { r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD; r_result.class_name = "@GDScript"; r_result.class_member = p_symbol; @@ -3227,6 +3229,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co is_function = true; [[fallthrough]]; } + case GDScriptParser::COMPLETION_ASSIGN: case GDScriptParser::COMPLETION_CALL_ARGUMENTS: case GDScriptParser::COMPLETION_IDENTIFIER: { GDScriptParser::DataType base_type; diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index deef593f34..cd3b7d69c5 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -270,6 +270,8 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) { if (EngineDebugger::is_active()) { GDScriptLanguage::get_singleton()->exit_function(); } + + _clear_stack(); #endif } diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 8563f2b432..bc225850c9 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -203,7 +203,8 @@ void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_ if (ignored_warnings.has(warn_name)) { return; } - if (!GLOBAL_GET("debug/gdscript/warnings/" + warn_name)) { + int warn_level = (int)GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(p_code)); + if (!warn_level) { return; } @@ -215,6 +216,11 @@ void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_ warning.leftmost_column = p_source->leftmost_column; warning.rightmost_column = p_source->rightmost_column; + if (warn_level == GDScriptWarning::WarnLevel::ERROR) { + push_error(warning.get_message(), p_source); + return; + } + List<GDScriptWarning>::Element *before = nullptr; for (List<GDScriptWarning>::Element *E = warnings.front(); E; E = E->next()) { if (E->get().start_line > warning.start_line) { @@ -1624,6 +1630,10 @@ GDScriptParser::Node *GDScriptParser::parse_statement() { case Node::AWAIT: // Fine. break; + case Node::LAMBDA: + // Standalone lambdas can't be used, so make this an error. + push_error("Standalone lambdas cannot be accessed. Consider assigning it to a variable.", expression); + break; default: push_warning(expression, GDScriptWarning::STANDALONE_EXPRESSION); } @@ -1808,7 +1818,6 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() { #ifdef DEBUG_ENABLED bool all_have_return = true; bool have_wildcard = false; - bool wildcard_has_return = false; bool have_wildcard_without_continue = false; #endif @@ -1826,9 +1835,6 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() { if (branch->has_wildcard) { have_wildcard = true; - if (branch->block->has_return) { - wildcard_has_return = true; - } if (!branch->block->has_continue) { have_wildcard_without_continue = true; } @@ -1843,7 +1849,7 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() { consume(GDScriptTokenizer::Token::DEDENT, R"(Expected an indented block after "match" statement.)"); #ifdef DEBUG_ENABLED - if (wildcard_has_return || (all_have_return && have_wildcard)) { + if (all_have_return && have_wildcard) { current_suite->has_return = true; } #endif @@ -2103,7 +2109,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr ExpressionNode *previous_operand = (this->*prefix_rule)(nullptr, p_can_assign); while (p_precedence <= get_rule(current.type)->precedence) { - if (previous_operand == nullptr || (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL)) { + if (previous_operand == nullptr || (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL) || (previous_operand->type == Node::LAMBDA && lambda_ended)) { return previous_operand; } // Also switch multiline mode on here for infix operators. @@ -2824,51 +2830,97 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre } GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p_previous_operand, bool p_can_assign) { - if (match(GDScriptTokenizer::Token::LITERAL)) { - if (previous.literal.get_type() != Variant::STRING) { - push_error(R"(Expect node path as string or identifier after "$".)"); + if (!current.is_node_name() && !check(GDScriptTokenizer::Token::LITERAL) && !check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) { + push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous.get_name())); + return nullptr; + } + + if (check(GDScriptTokenizer::Token::LITERAL)) { + if (current.literal.get_type() != Variant::STRING) { + push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous.get_name())); return nullptr; } - GetNodeNode *get_node = alloc_node<GetNodeNode>(); - make_completion_context(COMPLETION_GET_NODE, get_node); - get_node->string = parse_literal(); - return get_node; - } else if (current.is_node_name()) { - GetNodeNode *get_node = alloc_node<GetNodeNode>(); - int chain_position = 0; - do { - make_completion_context(COMPLETION_GET_NODE, get_node, chain_position++); - if (!current.is_node_name()) { - push_error(R"(Expect node path after "/".)"); + } + + GetNodeNode *get_node = alloc_node<GetNodeNode>(); + + // Store the last item in the path so the parser knows what to expect. + // Allow allows more specific error messages. + enum PathState { + PATH_STATE_START, + PATH_STATE_SLASH, + PATH_STATE_PERCENT, + PATH_STATE_NODE_NAME, + } path_state = PATH_STATE_START; + + if (previous.type == GDScriptTokenizer::Token::DOLLAR) { + // Detect initial slash, which will be handled in the loop if it matches. + match(GDScriptTokenizer::Token::SLASH); +#ifdef DEBUG_ENABLED + } else { + get_node->use_dollar = false; +#endif + } + + int context_argument = 0; + + do { + if (previous.type == GDScriptTokenizer::Token::PERCENT) { + if (path_state != PATH_STATE_START && path_state != PATH_STATE_SLASH) { + push_error(R"("%" is only valid in the beginning of a node name (either after "$" or after "/"))"); return nullptr; } - advance(); - IdentifierNode *identifier = alloc_node<IdentifierNode>(); - identifier->name = previous.get_identifier(); - get_node->chain.push_back(identifier); - } while (match(GDScriptTokenizer::Token::SLASH)); - return get_node; - } else if (match(GDScriptTokenizer::Token::SLASH)) { - GetNodeNode *get_node = alloc_node<GetNodeNode>(); - IdentifierNode *identifier_root = alloc_node<IdentifierNode>(); - get_node->chain.push_back(identifier_root); - int chain_position = 0; - do { - make_completion_context(COMPLETION_GET_NODE, get_node, chain_position++); - if (!current.is_node_name()) { - push_error(R"(Expect node path after "/".)"); + get_node->full_path += "%"; + + path_state = PATH_STATE_PERCENT; + } else if (previous.type == GDScriptTokenizer::Token::SLASH) { + if (path_state != PATH_STATE_START && path_state != PATH_STATE_NODE_NAME) { + push_error(R"("/" is only valid at the beginning of the path or after a node name.)"); + return nullptr; + } + + get_node->full_path += "/"; + + path_state = PATH_STATE_SLASH; + } + + make_completion_context(COMPLETION_GET_NODE, get_node, context_argument++); + + if (match(GDScriptTokenizer::Token::LITERAL)) { + if (previous.literal.get_type() != Variant::STRING) { + String previous_token; + switch (path_state) { + case PATH_STATE_START: + previous_token = "$"; + break; + case PATH_STATE_PERCENT: + previous_token = "%"; + break; + case PATH_STATE_SLASH: + previous_token = "/"; + break; + default: + break; + } + push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous_token)); return nullptr; } + + get_node->full_path += previous.literal.operator String(); + + path_state = PATH_STATE_NODE_NAME; + } else if (current.is_node_name()) { advance(); - IdentifierNode *identifier = alloc_node<IdentifierNode>(); - identifier->name = previous.get_identifier(); - get_node->chain.push_back(identifier); - } while (match(GDScriptTokenizer::Token::SLASH)); - return get_node; - } else { - push_error(R"(Expect node path as string or identifier after "$".)"); - return nullptr; - } + get_node->full_path += previous.get_identifier(); + + path_state = PATH_STATE_NODE_NAME; + } else if (!check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) { + push_error(vformat(R"(Unexpected "%s" in node path.)", current.get_name())); + return nullptr; + } + } while (match(GDScriptTokenizer::Token::SLASH) || match(GDScriptTokenizer::Token::PERCENT)); + + return get_node; } GDScriptParser::ExpressionNode *GDScriptParser::parse_preload(ExpressionNode *p_previous_operand, bool p_can_assign) { @@ -2926,6 +2978,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p current_function = function; SuiteNode *body = alloc_node<SuiteNode>(); + body->parent_function = current_function; + body->parent_block = current_suite; + SuiteNode *previous_suite = current_suite; current_suite = body; @@ -3138,24 +3193,23 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String & String title, link; // For tutorials. String doc_line = comments[line++].comment.trim_prefix("##"); - String striped_line = doc_line.strip_edges(); + String stripped_line = doc_line.strip_edges(); // Set the read mode. - if (striped_line.begins_with("@desc:") && p_desc.is_empty()) { + if (stripped_line.is_empty() && mode == BRIEF && !p_brief.is_empty()) { mode = DESC; - striped_line = striped_line.trim_prefix("@desc:"); - in_codeblock = _in_codeblock(doc_line, in_codeblock); + continue; - } else if (striped_line.begins_with("@tutorial")) { + } else if (stripped_line.begins_with("@tutorial")) { int begin_scan = String("@tutorial").length(); - if (begin_scan >= striped_line.length()) { + if (begin_scan >= stripped_line.length()) { continue; // invalid syntax. } - if (striped_line[begin_scan] == ':') { // No title. + if (stripped_line[begin_scan] == ':') { // No title. // Syntax: ## @tutorial: https://godotengine.org/ // The title argument is optional. title = ""; - link = striped_line.trim_prefix("@tutorial:").strip_edges(); + link = stripped_line.trim_prefix("@tutorial:").strip_edges(); } else { /* Syntax: @@ -3163,35 +3217,35 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String & * ^ open ^ close ^ colon ^ url */ int open_bracket_pos = begin_scan, close_bracket_pos = 0; - while (open_bracket_pos < striped_line.length() && (striped_line[open_bracket_pos] == ' ' || striped_line[open_bracket_pos] == '\t')) { + while (open_bracket_pos < stripped_line.length() && (stripped_line[open_bracket_pos] == ' ' || stripped_line[open_bracket_pos] == '\t')) { open_bracket_pos++; } - if (open_bracket_pos == striped_line.length() || striped_line[open_bracket_pos++] != '(') { + if (open_bracket_pos == stripped_line.length() || stripped_line[open_bracket_pos++] != '(') { continue; // invalid syntax. } close_bracket_pos = open_bracket_pos; - while (close_bracket_pos < striped_line.length() && striped_line[close_bracket_pos] != ')') { + while (close_bracket_pos < stripped_line.length() && stripped_line[close_bracket_pos] != ')') { close_bracket_pos++; } - if (close_bracket_pos == striped_line.length()) { + if (close_bracket_pos == stripped_line.length()) { continue; // invalid syntax. } int colon_pos = close_bracket_pos + 1; - while (colon_pos < striped_line.length() && (striped_line[colon_pos] == ' ' || striped_line[colon_pos] == '\t')) { + while (colon_pos < stripped_line.length() && (stripped_line[colon_pos] == ' ' || stripped_line[colon_pos] == '\t')) { colon_pos++; } - if (colon_pos == striped_line.length() || striped_line[colon_pos++] != ':') { + if (colon_pos == stripped_line.length() || stripped_line[colon_pos++] != ':') { continue; // invalid syntax. } - title = striped_line.substr(open_bracket_pos, close_bracket_pos - open_bracket_pos).strip_edges(); - link = striped_line.substr(colon_pos).strip_edges(); + title = stripped_line.substr(open_bracket_pos, close_bracket_pos - open_bracket_pos).strip_edges(); + link = stripped_line.substr(colon_pos).strip_edges(); } mode = TUTORIALS; in_codeblock = false; - } else if (striped_line.is_empty()) { + } else if (stripped_line.is_empty()) { continue; } else { // Tutorial docs are single line, we need a @tag after it. @@ -3211,7 +3265,7 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String & } doc_line = doc_line.substr(i); } else { - doc_line = striped_line; + doc_line = stripped_line; } String line_join = (in_codeblock) ? "\n" : " "; @@ -3270,7 +3324,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty { nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // STAR, { nullptr, &GDScriptParser::parse_binary_operator, PREC_POWER }, // STAR_STAR, { nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // SLASH, - { nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // PERCENT, + { &GDScriptParser::parse_get_node, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // PERCENT, // Assignment { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // EQUAL, { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // PLUS_EQUAL, @@ -4250,17 +4304,10 @@ void GDScriptParser::TreePrinter::print_function(FunctionNode *p_function, const } void GDScriptParser::TreePrinter::print_get_node(GetNodeNode *p_get_node) { - push_text("$"); - if (p_get_node->string != nullptr) { - print_literal(p_get_node->string); - } else { - for (int i = 0; i < p_get_node->chain.size(); i++) { - if (i > 0) { - push_text("/"); - } - print_identifier(p_get_node->chain[i]); - } + if (p_get_node->use_dollar) { + push_text("$"); } + push_text(p_get_node->full_path); } void GDScriptParser::TreePrinter::print_identifier(IdentifierNode *p_identifier) { diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 17f87edeeb..e3f8d4b8ba 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -749,8 +749,10 @@ public: }; struct GetNodeNode : public ExpressionNode { - LiteralNode *string = nullptr; - Vector<IdentifierNode *> chain; + String full_path; +#ifdef DEBUG_ENABLED + bool use_dollar = true; +#endif GetNodeNode() { type = GET_NODE; @@ -1205,9 +1207,9 @@ private: List<ParserError> errors; #ifdef DEBUG_ENABLED List<GDScriptWarning> warnings; - RBSet<String> ignored_warnings; - RBSet<uint32_t> ignored_warning_codes; - RBSet<int> unsafe_lines; + HashSet<String> ignored_warnings; + HashSet<uint32_t> ignored_warning_codes; + HashSet<int> unsafe_lines; #endif GDScriptTokenizer tokenizer; @@ -1419,7 +1421,7 @@ public: } #ifdef DEBUG_ENABLED const List<GDScriptWarning> &get_warnings() const { return warnings; } - const RBSet<int> &get_unsafe_lines() const { return unsafe_lines; } + const HashSet<int> &get_unsafe_lines() const { return unsafe_lines; } int get_last_line_number() const { return current.end_line; } #endif diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index ad818cf812..7fb715f2c8 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -32,8 +32,8 @@ #define GDSCRIPT_TOKENIZER_H #include "core/templates/hash_map.h" +#include "core/templates/hash_set.h" #include "core/templates/list.h" -#include "core/templates/rb_set.h" #include "core/templates/vector.h" #include "core/variant/variant.h" diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 8f85d8159b..55f4ebb1c5 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -3450,23 +3450,26 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time; } - // Check if this function has been interrupted by `await`. - // If that is the case we want to keep it in the debugger until it actually exits. + // Check if this is not the last time it was interrupted by `await` or if it's the first time executing. + // If that is the case then we exit the function as normal. Otherwise we postpone it until the last `await` is completed. // This ensures the call stack can be properly shown when using `await`, showing what resumed the function. - if (!awaited) { + if (!p_state || awaited) { if (EngineDebugger::is_active()) { GDScriptLanguage::get_singleton()->exit_function(); } - } #endif - // Clear the stack even if there was an `await`. - // The stack saved in the state is a copy, so this needs to be destructed to avoid leaks. - if (_stack_size) { - // Free stack. - for (int i = 0; i < _stack_size; i++) { + // Free stack, except reserved addresses. + for (int i = 3; i < _stack_size; i++) { stack[i].~Variant(); } +#ifdef DEBUG_ENABLED + } +#endif + + // Always free reserved addresses, since they are never copied. + for (int i = 0; i < 3; i++) { + stack[i].~Variant(); } return retvalue; diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp index ad96e36640..1cae7bdfac 100644 --- a/modules/gdscript/gdscript_warning.cpp +++ b/modules/gdscript/gdscript_warning.cpp @@ -163,6 +163,18 @@ String GDScriptWarning::get_message() const { #undef CHECK_SYMBOLS } +int GDScriptWarning::get_default_value(Code p_code) { + if (get_name_from_code(p_code).to_lower().begins_with("unsafe_")) { + return WarnLevel::IGNORE; + } + return WarnLevel::WARN; +} + +PropertyInfo GDScriptWarning::get_property_info(Code p_code) { + // Making this a separate function in case a warning needs different PropertyInfo in the future. + return PropertyInfo(Variant::INT, get_settings_path_from_code(p_code), PROPERTY_HINT_ENUM, "Ignore,Warn,Error"); +} + String GDScriptWarning::get_name() const { return get_name_from_code(code); } @@ -210,6 +222,10 @@ String GDScriptWarning::get_name_from_code(Code p_code) { return names[(int)p_code]; } +String GDScriptWarning::get_settings_path_from_code(Code p_code) { + return "debug/gdscript/warnings/" + get_name_from_code(p_code).to_lower(); +} + GDScriptWarning::Code GDScriptWarning::get_code_from_name(const String &p_name) { for (int i = 0; i < WARNING_MAX; i++) { if (get_name_from_code((Code)i) == p_name) { diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h index 82efe3568f..f47f31aedf 100644 --- a/modules/gdscript/gdscript_warning.h +++ b/modules/gdscript/gdscript_warning.h @@ -33,11 +33,18 @@ #ifdef DEBUG_ENABLED +#include "core/object/object.h" #include "core/string/ustring.h" #include "core/templates/vector.h" class GDScriptWarning { public: + enum WarnLevel { + IGNORE, + WARN, + ERROR + }; + enum Code { UNASSIGNED_VARIABLE, // Variable used but never assigned. UNASSIGNED_VARIABLE_OP_ASSIGN, // Variable never assigned but used in an assignment operation (+=, *=, etc). @@ -81,7 +88,10 @@ public: String get_name() const; String get_message() const; + static int get_default_value(Code p_code); + static PropertyInfo get_property_info(Code p_code); static String get_name_from_code(Code p_code); + static String get_settings_path_from_code(Code p_code); static Code get_code_from_name(const String &p_name); }; 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/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index d9de112bb0..8d484a43b3 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -784,7 +784,7 @@ GDScriptWorkspace::GDScriptWorkspace() { } GDScriptWorkspace::~GDScriptWorkspace() { - RBSet<String> cached_parsers; + HashSet<String> cached_parsers; for (const KeyValue<String, ExtendGDScriptParser *> &E : parse_results) { cached_parsers.insert(E.key); @@ -794,7 +794,7 @@ GDScriptWorkspace::~GDScriptWorkspace() { cached_parsers.insert(E.key); } - for (RBSet<String>::Element *E = cached_parsers.front(); E; E = E->next()) { - remove_cache_parser(E->get()); + for (const String &E : cached_parsers) { + remove_cache_parser(E); } } 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/register_types.cpp b/modules/gdscript/register_types.cpp index 7cedbda804..b230c6ba36 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -70,7 +70,7 @@ class EditorExportGDScript : public EditorExportPlugin { GDCLASS(EditorExportGDScript, EditorExportPlugin); public: - virtual void _export_file(const String &p_path, const String &p_type, const RBSet<String> &p_features) override { + virtual void _export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) override { int script_mode = EditorExportPreset::MODE_SCRIPT_COMPILED; String script_key; diff --git a/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.gd b/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.gd new file mode 100644 index 0000000000..9a7c6a8250 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.gd @@ -0,0 +1,12 @@ +# https://github.com/godotengine/godot/issues/54589 +# https://github.com/godotengine/godot/issues/56265 + +extends Resource + +func test(): + print("okay") + await self.changed + await unknown(self) + +func unknown(arg): + await arg.changed diff --git a/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.out b/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.out new file mode 100644 index 0000000000..2dc04a363e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.out @@ -0,0 +1,2 @@ +GDTEST_OK +okay diff --git a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd new file mode 100644 index 0000000000..9f86d0531c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd @@ -0,0 +1,2 @@ +func test(): + const arr: Array[int] = ["Hello", "World"] diff --git a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out new file mode 100644 index 0000000000..26b6e13d4f --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Assigned value for constant "arr" has type Array[String] which is not compatible with defined type Array[int]. diff --git a/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out b/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out index b3dc181a22..9fafcb5a64 100644 --- a/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out +++ b/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out @@ -1,2 +1,2 @@ GDTEST_PARSER_ERROR -Expect node path as string or identifier after "$". +Expected node path as string or identifier after "$". diff --git a/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.gd b/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.gd new file mode 100644 index 0000000000..fa0a43094e --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.gd @@ -0,0 +1,3 @@ +func test(): + func standalone(): + print("can't be accessed") diff --git a/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.out b/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.out new file mode 100644 index 0000000000..c6830c8258 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Standalone lambdas cannot be accessed. Consider assigning it to a variable. diff --git a/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out index b3dc181a22..9fafcb5a64 100644 --- a/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out +++ b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out @@ -1,2 +1,2 @@ GDTEST_PARSER_ERROR -Expect node path as string or identifier after "$". +Expected node path as string or identifier after "$". diff --git a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out index b3dc181a22..9fafcb5a64 100644 --- a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out +++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out @@ -1,2 +1,2 @@ GDTEST_PARSER_ERROR -Expect node path as string or identifier after "$". +Expected node path as string or identifier after "$". diff --git a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out index dcb4ccecb0..3062f0be70 100644 --- a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out +++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out @@ -1,2 +1,2 @@ GDTEST_PARSER_ERROR -Expect node path after "/". +Expected node path as string or identifier after "/". diff --git a/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd new file mode 100644 index 0000000000..f04f4de08d --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd @@ -0,0 +1,49 @@ +extends Node + +func test(): + var child = Node.new() + child.name = "Child" + add_child(child) + child.owner = self + + var hey = Node.new() + hey.name = "Hey" + child.add_child(hey) + hey.owner = self + hey.unique_name_in_owner = true + + var fake_hey = Node.new() + fake_hey.name = "Hey" + add_child(fake_hey) + fake_hey.owner = self + + var sub_child = Node.new() + sub_child.name = "SubChild" + hey.add_child(sub_child) + sub_child.owner = self + + var howdy = Node.new() + howdy.name = "Howdy" + sub_child.add_child(howdy) + howdy.owner = self + howdy.unique_name_in_owner = true + + print(hey == $Child/Hey) + print(howdy == $Child/Hey/SubChild/Howdy) + + print(%Hey == hey) + print($%Hey == hey) + print(%"Hey" == hey) + print($"%Hey" == hey) + print($%"Hey" == hey) + print(%Hey/%Howdy == howdy) + print($%Hey/%Howdy == howdy) + print($"%Hey/%Howdy" == howdy) + print($"%Hey"/"%Howdy" == howdy) + print(%"Hey"/"%Howdy" == howdy) + print($%"Hey"/"%Howdy" == howdy) + print($"%Hey"/%"Howdy" == howdy) + print(%"Hey"/%"Howdy" == howdy) + print($%"Hey"/%"Howdy" == howdy) + print(%"Hey/%Howdy" == howdy) + print($%"Hey/%Howdy" == howdy) diff --git a/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.out b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.out new file mode 100644 index 0000000000..041c4439b0 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.out @@ -0,0 +1,19 @@ +GDTEST_OK +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true diff --git a/modules/gdscript/tests/scripts/parser/features/if_after_lambda.gd b/modules/gdscript/tests/scripts/parser/features/if_after_lambda.gd new file mode 100644 index 0000000000..f5e26ab1ab --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/if_after_lambda.gd @@ -0,0 +1,7 @@ +# https://github.com/godotengine/godot/issues/61231 + +func test(): + var my_lambda = func(): + print("hello") + if 0 == 0: + my_lambda.call() diff --git a/modules/gdscript/tests/scripts/parser/features/if_after_lambda.out b/modules/gdscript/tests/scripts/parser/features/if_after_lambda.out new file mode 100644 index 0000000000..58774d2d3f --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/if_after_lambda.out @@ -0,0 +1,2 @@ +GDTEST_OK +hello diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.gd b/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.gd new file mode 100644 index 0000000000..2140b6923e --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.gd @@ -0,0 +1,7 @@ +# https://github.com/godotengine/godot/issues/56751 + +func test(): + var x = "local" + var lambda = func(param = x): + print(param) + lambda.call() diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.out b/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.out new file mode 100644 index 0000000000..ce3241b94d --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.out @@ -0,0 +1,2 @@ +GDTEST_OK +local 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/scripts/parser/warnings/unreachable_code_after_return_bug_55154.gd b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.gd new file mode 100644 index 0000000000..d00d483a73 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.gd @@ -0,0 +1,16 @@ +func test(): + var foo := "bar" + match foo: + "baz": + return + _: + pass + match foo: + "baz": + return + match foo: + "bar": + pass + _: + return + print("reached") diff --git a/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.out b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.out new file mode 100644 index 0000000000..47db6b631b --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.out @@ -0,0 +1,2 @@ +GDTEST_OK +reached 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/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml index 03b4380ff4..3c28546ad7 100644 --- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml +++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml @@ -7,65 +7,52 @@ <tutorials> </tutorials> <methods> - <method name="export_post"> - <return type="int" enum="Error" /> - <argument index="0" name="document" type="GLTFDocument" /> + <method name="_export_node" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="state" type="GLTFState" /> + <argument index="1" name="gltf_node" type="GLTFNode" /> + <argument index="2" name="json" type="Dictionary" /> + <argument index="3" name="node" type="Node" /> <description> </description> </method> - <method name="export_preflight"> - <return type="int" enum="Error" /> - <argument index="0" name="document" type="GLTFDocument" /> - <argument index="1" name="node" type="Node" /> + <method name="_export_post" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="state" type="GLTFState" /> <description> </description> </method> - <method name="get_export_setting" qualifiers="const"> - <return type="Variant" /> - <argument index="0" name="key" type="StringName" /> + <method name="_export_preflight" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="root" type="Node" /> <description> </description> </method> - <method name="get_export_setting_keys" qualifiers="const"> - <return type="Array" /> + <method name="_import_node" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="state" type="GLTFState" /> + <argument index="1" name="gltf_node" type="GLTFNode" /> + <argument index="2" name="json" type="Dictionary" /> + <argument index="3" name="node" type="Node" /> <description> </description> </method> - <method name="get_import_setting" qualifiers="const"> - <return type="Variant" /> - <argument index="0" name="key" type="StringName" /> + <method name="_import_post" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="state" type="GLTFState" /> + <argument index="1" name="root" type="Node" /> <description> </description> </method> - <method name="get_import_setting_keys" qualifiers="const"> - <return type="Array" /> + <method name="_import_post_parse" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="state" type="GLTFState" /> <description> </description> </method> - <method name="import_post"> - <return type="int" enum="Error" /> - <argument index="0" name="document" type="GLTFDocument" /> - <argument index="1" name="node" type="Node" /> - <description> - </description> - </method> - <method name="import_preflight"> - <return type="int" enum="Error" /> - <argument index="0" name="document" type="GLTFDocument" /> - <description> - </description> - </method> - <method name="set_export_setting"> - <return type="void" /> - <argument index="0" name="key" type="StringName" /> - <argument index="1" name="value" type="Variant" /> - <description> - </description> - </method> - <method name="set_import_setting"> - <return type="void" /> - <argument index="0" name="key" type="StringName" /> - <argument index="1" name="value" type="Variant" /> + <method name="_import_preflight" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="state" type="GLTFState" /> <description> </description> </method> diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml index a59fa900bc..44a1723563 100644 --- a/modules/gltf/doc_classes/GLTFState.xml +++ b/modules/gltf/doc_classes/GLTFState.xml @@ -192,6 +192,8 @@ </method> </methods> <members> + <member name="base_path" type="String" setter="set_base_path" getter="get_base_path" default=""""> + </member> <member name="buffers" type="Array" setter="set_buffers" getter="get_buffers" default="[]"> </member> <member name="glb_data" type="PackedByteArray" setter="set_glb_data" getter="get_glb_data" default="PackedByteArray()"> diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp index 63f446561a..676862ea5a 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.cpp +++ b/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -310,7 +310,7 @@ static bool _test_blender_path(const String &p_path, String *r_err = nullptr) { Error err = OS::get_singleton()->execute(path, args, &pipe); if (err != OK) { if (r_err) { - *r_err = TTR("Can't excecute Blender binary."); + *r_err = TTR("Can't execute Blender binary."); } return false; } diff --git a/modules/gltf/editor/editor_scene_importer_gltf.cpp b/modules/gltf/editor/editor_scene_importer_gltf.cpp index d5b247fc6e..3fadec5167 100644 --- a/modules/gltf/editor/editor_scene_importer_gltf.cpp +++ b/modules/gltf/editor/editor_scene_importer_gltf.cpp @@ -35,7 +35,6 @@ #include "../gltf_document.h" #include "../gltf_state.h" -#include "scene/main/node.h" #include "scene/resources/animation.h" uint32_t EditorSceneFormatImporterGLTF::get_import_flags() const { diff --git a/modules/gltf/editor/editor_scene_importer_gltf.h b/modules/gltf/editor/editor_scene_importer_gltf.h index edca038532..b17a1e4eaa 100644 --- a/modules/gltf/editor/editor_scene_importer_gltf.h +++ b/modules/gltf/editor/editor_scene_importer_gltf.h @@ -33,6 +33,9 @@ #ifdef TOOLS_ENABLED +#include "../gltf_document_extension.h" +#include "../gltf_state.h" + #include "editor/import/resource_importer_scene.h" class Animation; diff --git a/modules/gltf/gltf_accessor.h b/modules/gltf/gltf_accessor.h index 6bf1bf543a..f412dc2c7f 100644 --- a/modules/gltf/gltf_accessor.h +++ b/modules/gltf/gltf_accessor.h @@ -34,7 +34,6 @@ #include "core/io/resource.h" #include "gltf_document.h" -#include "gltf_document_extension.h" struct GLTFAccessor : public Resource { GDCLASS(GLTFAccessor, Resource); diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index f440a29f28..2017355717 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -128,10 +128,10 @@ Error GLTFDocument::_serialize(Ref<GLTFState> state, const String &p_path) { state->buffers.push_back(Vector<uint8_t>()); } - /* STEP 1 CONVERT MESH INSTANCES */ + /* STEP CONVERT MESH INSTANCES */ _convert_mesh_instances(state); - /* STEP 2 SERIALIZE CAMERAS */ + /* STEP SERIALIZE CAMERAS */ Error err = _serialize_cameras(state); if (err != OK) { return Error::FAILED; @@ -143,37 +143,37 @@ Error GLTFDocument::_serialize(Ref<GLTFState> state, const String &p_path) { return Error::FAILED; } - /* STEP 5 SERIALIZE MESHES (we have enough info now) */ + /* STEP SERIALIZE MESHES (we have enough info now) */ err = _serialize_meshes(state); if (err != OK) { return Error::FAILED; } - /* STEP 6 SERIALIZE TEXTURES */ + /* STEP SERIALIZE TEXTURES */ err = _serialize_materials(state); if (err != OK) { return Error::FAILED; } - /* STEP 7 SERIALIZE ANIMATIONS */ + /* STEP SERIALIZE ANIMATIONS */ err = _serialize_animations(state); if (err != OK) { return Error::FAILED; } - /* STEP 8 SERIALIZE ACCESSORS */ + /* STEP SERIALIZE ACCESSORS */ err = _encode_accessors(state); if (err != OK) { return Error::FAILED; } - /* STEP 9 SERIALIZE IMAGES */ + /* STEP SERIALIZE IMAGES */ err = _serialize_images(state, p_path); if (err != OK) { return Error::FAILED; } - /* STEP 10 SERIALIZE TEXTURES */ + /* STEP SERIALIZE TEXTURES */ err = _serialize_textures(state); if (err != OK) { return Error::FAILED; @@ -183,42 +183,49 @@ Error GLTFDocument::_serialize(Ref<GLTFState> state, const String &p_path) { state->buffer_views.write[i]->buffer = 0; } - /* STEP 11 SERIALIZE BUFFER VIEWS */ + /* STEP SERIALIZE BUFFER VIEWS */ err = _encode_buffer_views(state); if (err != OK) { return Error::FAILED; } - /* STEP 12 SERIALIZE NODES */ + /* STEP SERIALIZE NODES */ err = _serialize_nodes(state); if (err != OK) { return Error::FAILED; } - /* STEP 13 SERIALIZE SCENE */ + /* STEP SERIALIZE SCENE */ err = _serialize_scenes(state); if (err != OK) { return Error::FAILED; } - /* STEP 14 SERIALIZE SCENE */ + /* STEP SERIALIZE SCENE */ err = _serialize_lights(state); if (err != OK) { return Error::FAILED; } - /* STEP 15 SERIALIZE EXTENSIONS */ + /* STEP SERIALIZE EXTENSIONS */ err = _serialize_extensions(state); if (err != OK) { return Error::FAILED; } - /* STEP 16 SERIALIZE VERSION */ + /* STEP SERIALIZE VERSION */ err = _serialize_version(state); if (err != OK) { return Error::FAILED; } + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + err = ext->export_post(state); + ERR_FAIL_COND_V(err != OK, err); + } + return OK; } @@ -442,6 +449,15 @@ Error GLTFDocument::_serialize_nodes(Ref<GLTFState> state) { } node["children"] = children; } + + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + ERR_CONTINUE(!state->scene_nodes.find(i)); + Error err = ext->export_node(state, n, state->json, state->scene_nodes[i]); + ERR_CONTINUE(err != OK); + } + nodes.push_back(node); } state->json["nodes"] = nodes; @@ -5658,7 +5674,7 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> state, Node *scene_parent if (!current_node) { current_node = _generate_spatial(state, node_index); } - scene_parent->add_child(current_node); + scene_parent->add_child(current_node, true); if (current_node != scene_root) { current_node->set_owner(scene_root); } @@ -6642,8 +6658,8 @@ Error GLTFDocument::_parse(Ref<GLTFState> state, String p_path, Ref<FileAccess> for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; ERR_CONTINUE(ext.is_null()); - err = ext->import_preflight(this); - ERR_FAIL_COND_V(err != OK, FAILED); + err = ext->import_preflight(state); + ERR_FAIL_COND_V(err != OK, err); } err = _parse_gltf_state(state, p_path, p_bake_fps); ERR_FAIL_COND_V(err != OK, err); @@ -6870,12 +6886,10 @@ PackedByteArray GLTFDocument::generate_buffer(Ref<GLTFState> state) { Error GLTFDocument::write_to_filesystem(Ref<GLTFState> state, const String &p_path) { ERR_FAIL_NULL_V(state, ERR_INVALID_PARAMETER); - Error err = _serialize(state, p_path); if (err != OK) { return err; } - err = _serialize_file(state, p_path); if (err != OK) { return Error::FAILED; @@ -6886,6 +6900,7 @@ Error GLTFDocument::write_to_filesystem(Ref<GLTFState> state, const String &p_pa Node *GLTFDocument::generate_scene(Ref<GLTFState> state, int32_t p_bake_fps) { ERR_FAIL_NULL_V(state, nullptr); ERR_FAIL_INDEX_V(0, state->root_nodes.size(), nullptr); + Error err = OK; GLTFNodeIndex gltf_root = state->root_nodes.write[0]; Node *gltf_root_node = state->get_scene_node(gltf_root); Node *root = gltf_root_node->get_parent(); @@ -6899,12 +6914,26 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> state, int32_t p_bake_fps) { _import_animation(state, ap, i, p_bake_fps); } } - + for (KeyValue<GLTFNodeIndex, Node *> E : state->scene_nodes) { + ERR_CONTINUE(!E.value); + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + ERR_CONTINUE(!state->json.has("nodes")); + Array nodes = state->json["nodes"]; + ERR_CONTINUE(E.key >= nodes.size()); + ERR_CONTINUE(E.key < 0); + Dictionary node_json = nodes[E.key]; + Ref<GLTFNode> gltf_node = state->nodes[E.key]; + err = ext->import_node(state, gltf_node, node_json, E.value); + ERR_CONTINUE(err != OK); + } + } for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; ERR_CONTINUE(ext.is_null()); - Error err = ext->import_post(this, root); - ERR_FAIL_COND_V(err != OK, nullptr); + err = ext->import_post(state, root); + ERR_CONTINUE(err != OK); } ERR_FAIL_NULL_V(root, nullptr); return root; @@ -6919,13 +6948,23 @@ Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> state, uint32 if (!state->buffers.size()) { state->buffers.push_back(Vector<uint8_t>()); } + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + } - /* STEP 1 CONVERT MESH INSTANCES */ - _convert_mesh_instances(state); + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + Error err = ext->export_preflight(p_node); + ERR_FAIL_COND_V(err != OK, FAILED); + } + _convert_scene_node(state, p_node, -1, -1); + if (!state->buffers.size()) { + state->buffers.push_back(Vector<uint8_t>()); + } - /* STEP 2 CREATE SKINS */ - Error err = _serialize_skins(state); - return err; + return OK; } Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> state, uint32_t p_flags, int32_t p_bake_fps) { @@ -6938,8 +6977,15 @@ Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_pa Ref<FileAccessMemory> file_access; file_access.instantiate(); file_access->open_custom(p_bytes.ptr(), p_bytes.size()); - err = _parse(state, p_base_path.get_base_dir(), file_access, p_bake_fps); - ERR_FAIL_COND_V(err != OK, FAILED); + state->base_path = p_base_path.get_base_dir(); + err = _parse(state, state->base_path, file_access, p_bake_fps); + ERR_FAIL_COND_V(err != OK, err); + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + err = ext->import_post_parse(state); + ERR_FAIL_COND_V(err != OK, err); + } return OK; } @@ -7030,6 +7076,7 @@ Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> state, const String &p_sear for (int32_t root_i = 0; root_i < state->root_nodes.size(); root_i++) { _generate_scene_node(state, root, root, state->root_nodes[root_i]); } + return OK; } @@ -7049,9 +7096,16 @@ Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> r_state, uint if (base_path.is_empty()) { base_path = p_path.get_base_dir(); } + r_state->base_path = base_path; err = _parse(r_state, base_path, f, p_bake_fps); - ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); - return err; + ERR_FAIL_COND_V(err != OK, err); + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + err = ext->import_post_parse(r_state); + ERR_FAIL_COND_V(err != OK, err); + } + return OK; } Error GLTFDocument::_parse_gltf_extensions(Ref<GLTFState> state) { diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index 9db824a0d4..2f61210ff9 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -33,19 +33,12 @@ #include "gltf_animation.h" -#include "core/error/error_list.h" -#include "core/variant/dictionary.h" -#include "core/variant/variant.h" -#include "gltf_document_extension_convert_importer_mesh.h" #include "scene/3d/bone_attachment_3d.h" #include "scene/3d/importer_mesh_instance_3d.h" #include "scene/3d/light_3d.h" #include "scene/3d/mesh_instance_3d.h" -#include "scene/3d/node_3d.h" -#include "scene/3d/skeleton_3d.h" #include "scene/animation/animation_player.h" #include "scene/resources/material.h" -#include "scene/resources/texture.h" #include "modules/modules_enabled.gen.h" // For csg, gridmap. @@ -135,12 +128,12 @@ private: } template <class T> - static Array to_array(const RBSet<T> &p_inp) { + static Array to_array(const HashSet<T> &p_inp) { Array ret; - typename RBSet<T>::Element *elem = p_inp.front(); + typename HashSet<T>::Iterator elem = p_inp.begin(); while (elem) { - ret.push_back(elem->get()); - elem = elem->next(); + ret.push_back(*elem); + ++elem; } return ret; } @@ -154,7 +147,7 @@ private: } template <class T> - static void set_from_array(RBSet<T> &r_out, const Array &p_inp) { + static void set_from_array(HashSet<T> &r_out, const Array &p_inp) { r_out.clear(); for (int i = 0; i < p_inp.size(); i++) { r_out.insert(p_inp[i]); diff --git a/modules/gltf/gltf_document_extension.cpp b/modules/gltf/gltf_document_extension.cpp index 192a1d347c..d0bd7651e0 100644 --- a/modules/gltf/gltf_document_extension.cpp +++ b/modules/gltf/gltf_document_extension.cpp @@ -30,59 +30,79 @@ #include "gltf_document_extension.h" -#include "gltf_document.h" - void GLTFDocumentExtension::_bind_methods() { - // Import - ClassDB::bind_method(D_METHOD("get_import_setting_keys"), - &GLTFDocumentExtension::get_import_setting_keys); - ClassDB::bind_method(D_METHOD("import_preflight", "document"), - &GLTFDocumentExtension::import_preflight); - ClassDB::bind_method(D_METHOD("get_import_setting", "key"), - &GLTFDocumentExtension::get_import_setting); - ClassDB::bind_method(D_METHOD("set_import_setting", "key", "value"), - &GLTFDocumentExtension::set_import_setting); - ClassDB::bind_method(D_METHOD("import_post", "document", "node"), - &GLTFDocumentExtension::import_post); - // Export - ClassDB::bind_method(D_METHOD("get_export_setting_keys"), - &GLTFDocumentExtension::get_export_setting_keys); - ClassDB::bind_method(D_METHOD("get_export_setting", "key"), - &GLTFDocumentExtension::get_export_setting); - ClassDB::bind_method(D_METHOD("set_export_setting", "key", "value"), - &GLTFDocumentExtension::set_export_setting); - ClassDB::bind_method(D_METHOD("export_preflight", "document", "node"), - &GLTFDocumentExtension::export_preflight); - ClassDB::bind_method(D_METHOD("export_post", "document"), - &GLTFDocumentExtension::export_post); + GDVIRTUAL_BIND(_import_preflight, "state"); + GDVIRTUAL_BIND(_import_post_parse, "state"); + GDVIRTUAL_BIND(_import_node, "state", "gltf_node", "json", "node"); + GDVIRTUAL_BIND(_import_post, "state", "root"); + GDVIRTUAL_BIND(_export_preflight, "root"); + GDVIRTUAL_BIND(_export_node, "state", "gltf_node", "json", "node"); + GDVIRTUAL_BIND(_export_post, "state"); } -Array GLTFDocumentExtension::get_import_setting_keys() const { - return import_settings.keys(); +Error GLTFDocumentExtension::import_post(Ref<GLTFState> p_state, Node *p_root) { + ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER); + ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + int err = OK; + if (GDVIRTUAL_CALL(_import_post, p_state, p_root, err)) { + return Error(err); + } + return OK; } -Variant GLTFDocumentExtension::get_import_setting(const StringName &p_key) const { - if (!import_settings.has(p_key)) { - return Variant(); +Error GLTFDocumentExtension::import_preflight(Ref<GLTFState> p_state) { + ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + int err = OK; + if (GDVIRTUAL_CALL(_import_preflight, p_state, err)) { + return Error(err); } - return import_settings[p_key]; + return OK; } -void GLTFDocumentExtension::set_import_setting(const StringName &p_key, Variant p_var) { - import_settings[p_key] = p_var; +Error GLTFDocumentExtension::import_post_parse(Ref<GLTFState> p_state) { + ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + int err = OK; + if (GDVIRTUAL_CALL(_import_post_parse, p_state, err)) { + return Error(err); + } + return OK; } -Array GLTFDocumentExtension::get_export_setting_keys() const { - return import_settings.keys(); +Error GLTFDocumentExtension::export_post(Ref<GLTFState> p_state) { + ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + int err = OK; + if (GDVIRTUAL_CALL(_export_post, p_state, err)) { + return Error(err); + } + return OK; +} +Error GLTFDocumentExtension::export_preflight(Node *p_root) { + ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER); + int err = OK; + if (GDVIRTUAL_CALL(_export_preflight, p_root, err)) { + return Error(err); + } + return OK; } -Variant GLTFDocumentExtension::get_export_setting(const StringName &p_key) const { - if (!import_settings.has(p_key)) { - return Variant(); +Error GLTFDocumentExtension::import_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) { + ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); + ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER); + int err = OK; + if (GDVIRTUAL_CALL(_import_node, p_state, p_gltf_node, r_dict, p_node, err)) { + return Error(err); } - return import_settings[p_key]; + return OK; } -void GLTFDocumentExtension::set_export_setting(const StringName &p_key, Variant p_var) { - import_settings[p_key] = p_var; +Error GLTFDocumentExtension::export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) { + ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); + ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER); + int err = OK; + if (GDVIRTUAL_CALL(_export_node, p_state, p_gltf_node, r_dict, p_node, err)) { + return Error(err); + } + return OK; } diff --git a/modules/gltf/gltf_document_extension.h b/modules/gltf/gltf_document_extension.h index f7a3531282..556f79e887 100644 --- a/modules/gltf/gltf_document_extension.h +++ b/modules/gltf/gltf_document_extension.h @@ -31,33 +31,31 @@ #ifndef GLTF_DOCUMENT_EXTENSION_H #define GLTF_DOCUMENT_EXTENSION_H -#include "core/io/resource.h" -#include "core/variant/dictionary.h" -#include "core/variant/typed_array.h" -#include "core/variant/variant.h" -class GLTFDocument; +#include "gltf_accessor.h" +#include "gltf_node.h" +#include "gltf_state.h" + class GLTFDocumentExtension : public Resource { GDCLASS(GLTFDocumentExtension, Resource); - Dictionary import_settings; - Dictionary export_settings; - protected: static void _bind_methods(); public: - virtual Array get_import_setting_keys() const; - virtual Variant get_import_setting(const StringName &p_key) const; - virtual void set_import_setting(const StringName &p_key, Variant p_var); - virtual Error import_preflight(Ref<GLTFDocument> p_document) { return OK; } - virtual Error import_post(Ref<GLTFDocument> p_document, Node *p_node) { return OK; } - -public: - virtual Array get_export_setting_keys() const; - virtual Variant get_export_setting(const StringName &p_key) const; - virtual void set_export_setting(const StringName &p_key, Variant p_var); - virtual Error export_preflight(Ref<GLTFDocument> p_document, Node *p_node) { return OK; } - virtual Error export_post(Ref<GLTFDocument> p_document) { return OK; } + virtual Error import_preflight(Ref<GLTFState> p_state); + virtual Error import_post_parse(Ref<GLTFState> p_state); + virtual Error export_post(Ref<GLTFState> p_state); + virtual Error import_post(Ref<GLTFState> p_state, Node *p_node); + virtual Error export_preflight(Node *p_state); + virtual Error import_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_json, Node *p_node); + virtual Error export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_json, Node *p_node); + GDVIRTUAL1R(int, _import_preflight, Ref<GLTFState>); + GDVIRTUAL1R(int, _import_post_parse, Ref<GLTFState>); + GDVIRTUAL4R(int, _import_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *); + GDVIRTUAL2R(int, _import_post, Ref<GLTFState>, Node *); + GDVIRTUAL1R(int, _export_preflight, Node *); + GDVIRTUAL4R(int, _export_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *); + GDVIRTUAL1R(int, _export_post, Ref<GLTFState>); }; #endif // GLTF_DOCUMENT_EXTENSION_H diff --git a/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp b/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp index 47a3e5598f..1620900a04 100644 --- a/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp +++ b/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp @@ -29,41 +29,40 @@ /*************************************************************************/ #include "gltf_document_extension_convert_importer_mesh.h" + +#include "gltf_state.h" + #include "core/error/error_macros.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/resources/importer_mesh.h" -#include <cstddef> - void GLTFDocumentExtensionConvertImporterMesh::_bind_methods() { } -Error GLTFDocumentExtensionConvertImporterMesh::import_post(Ref<GLTFDocument> p_document, Node *p_node) { - ERR_FAIL_NULL_V(p_document, ERR_INVALID_PARAMETER); - ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER); +Error GLTFDocumentExtensionConvertImporterMesh::import_post(Ref<GLTFState> p_state, Node *p_root) { + ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER); + ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); List<Node *> queue; - queue.push_back(p_node); + queue.push_back(p_root); List<Node *> delete_queue; while (!queue.is_empty()) { List<Node *>::Element *E = queue.front(); Node *node = E->get(); - { - ImporterMeshInstance3D *mesh_3d = cast_to<ImporterMeshInstance3D>(node); - if (mesh_3d) { - MeshInstance3D *mesh_instance_node_3d = memnew(MeshInstance3D); - Ref<ImporterMesh> mesh = mesh_3d->get_mesh(); - if (mesh.is_valid()) { - Ref<ArrayMesh> array_mesh = mesh->get_mesh(); - mesh_instance_node_3d->set_name(node->get_name()); - mesh_instance_node_3d->set_transform(mesh_3d->get_transform()); - mesh_instance_node_3d->set_mesh(array_mesh); - mesh_instance_node_3d->set_skin(mesh_3d->get_skin()); - mesh_instance_node_3d->set_skeleton_path(mesh_3d->get_skeleton_path()); - node->replace_by(mesh_instance_node_3d); - delete_queue.push_back(node); - } else { - memdelete(mesh_instance_node_3d); - } + ImporterMeshInstance3D *mesh_3d = cast_to<ImporterMeshInstance3D>(node); + if (mesh_3d) { + MeshInstance3D *mesh_instance_node_3d = memnew(MeshInstance3D); + Ref<ImporterMesh> mesh = mesh_3d->get_mesh(); + if (mesh.is_valid()) { + Ref<ArrayMesh> array_mesh = mesh->get_mesh(); + mesh_instance_node_3d->set_name(node->get_name()); + mesh_instance_node_3d->set_transform(mesh_3d->get_transform()); + mesh_instance_node_3d->set_mesh(array_mesh); + mesh_instance_node_3d->set_skin(mesh_3d->get_skin()); + mesh_instance_node_3d->set_skeleton_path(mesh_3d->get_skeleton_path()); + node->replace_by(mesh_instance_node_3d); + delete_queue.push_back(node); + } else { + memdelete(mesh_instance_node_3d); } } int child_count = node->get_child_count(); diff --git a/modules/gltf/gltf_document_extension_convert_importer_mesh.h b/modules/gltf/gltf_document_extension_convert_importer_mesh.h index 2d51143140..4c9e42a00f 100644 --- a/modules/gltf/gltf_document_extension_convert_importer_mesh.h +++ b/modules/gltf/gltf_document_extension_convert_importer_mesh.h @@ -31,14 +31,10 @@ #ifndef GLTF_EXTENSION_EDITOR_H #define GLTF_EXTENSION_EDITOR_H -#include "core/io/resource.h" -#include "core/variant/dictionary.h" - -#include "gltf_document.h" #include "gltf_document_extension.h" + #include "scene/3d/importer_mesh_instance_3d.h" #include "scene/3d/mesh_instance_3d.h" -#include "scene/main/node.h" #include "scene/resources/importer_mesh.h" class GLTFDocumentExtension; @@ -50,6 +46,6 @@ protected: static void _bind_methods(); public: - Error import_post(Ref<GLTFDocument> p_document, Node *p_node) override; + Error import_post(Ref<GLTFState> p_state, Node *p_root) override; }; #endif // GLTF_EXTENSION_EDITOR_H diff --git a/modules/gltf/gltf_skeleton.h b/modules/gltf/gltf_skeleton.h index 4dcee41465..92ee6e6234 100644 --- a/modules/gltf/gltf_skeleton.h +++ b/modules/gltf/gltf_skeleton.h @@ -50,7 +50,7 @@ private: Skeleton3D *godot_skeleton = nullptr; // Set of unique bone names for the skeleton - RBSet<String> unique_names; + HashSet<String> unique_names; HashMap<int32_t, GLTFNodeIndex> godot_bone_node; diff --git a/modules/gltf/gltf_state.cpp b/modules/gltf/gltf_state.cpp index e959a640b4..989fa476c2 100644 --- a/modules/gltf/gltf_state.cpp +++ b/modules/gltf/gltf_state.cpp @@ -57,6 +57,8 @@ void GLTFState::_bind_methods() { ClassDB::bind_method(D_METHOD("set_materials", "materials"), &GLTFState::set_materials); ClassDB::bind_method(D_METHOD("get_scene_name"), &GLTFState::get_scene_name); ClassDB::bind_method(D_METHOD("set_scene_name", "scene_name"), &GLTFState::set_scene_name); + ClassDB::bind_method(D_METHOD("get_base_path"), &GLTFState::get_base_path); + ClassDB::bind_method(D_METHOD("set_base_path", "base_path"), &GLTFState::set_base_path); ClassDB::bind_method(D_METHOD("get_root_nodes"), &GLTFState::get_root_nodes); ClassDB::bind_method(D_METHOD("set_root_nodes", "root_nodes"), &GLTFState::set_root_nodes); ClassDB::bind_method(D_METHOD("get_textures"), &GLTFState::get_textures); @@ -93,6 +95,7 @@ void GLTFState::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "meshes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_meshes", "get_meshes"); // Vector<Ref<GLTFMesh>> ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_materials", "get_materials"); // Vector<Ref<Material> ADD_PROPERTY(PropertyInfo(Variant::STRING, "scene_name"), "set_scene_name", "get_scene_name"); // String + ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_path"), "set_base_path", "get_base_path"); // String ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "root_nodes"), "set_root_nodes", "get_root_nodes"); // Vector<int> ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_textures", "get_textures"); // Vector<Ref<GLTFTexture>> ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "images", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_images", "get_images"); // Vector<Ref<Texture> @@ -313,3 +316,11 @@ void GLTFState::set_discard_meshes_and_materials(bool p_discard_meshes_and_mater bool GLTFState::get_discard_meshes_and_materials() { return discard_meshes_and_materials; } + +String GLTFState::get_base_path() { + return base_path; +} + +void GLTFState::set_base_path(String p_base_path) { + base_path = p_base_path; +} diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h index f695b20b49..2fdef19038 100644 --- a/modules/gltf/gltf_state.h +++ b/modules/gltf/gltf_state.h @@ -44,10 +44,7 @@ #include "gltf_skin.h" #include "gltf_texture.h" -#include "core/io/resource.h" -#include "core/templates/pair.h" #include "core/templates/rb_map.h" -#include "core/templates/vector.h" #include "scene/animation/animation_player.h" #include "scene/resources/texture.h" @@ -56,6 +53,7 @@ class GLTFState : public Resource { friend class GLTFDocument; String filename; + String base_path; Dictionary json; int major_version = 0; int minor_version = 0; @@ -83,8 +81,8 @@ class GLTFState : public Resource { Vector<Ref<GLTFSkin>> skins; Vector<Ref<GLTFCamera>> cameras; Vector<Ref<GLTFLight>> lights; - RBSet<String> unique_names; - RBSet<String> unique_animation_names; + HashSet<String> unique_names; + HashSet<String> unique_animation_names; Vector<Ref<GLTFSkeleton>> skeletons; HashMap<GLTFSkeletonIndex, GLTFNodeIndex> skeleton_to_node; @@ -137,6 +135,9 @@ public: String get_scene_name(); void set_scene_name(String p_scene_name); + String get_base_path(); + void set_base_path(String p_base_path); + Array get_root_nodes(); void set_root_nodes(Array p_root_nodes); diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp index 4902adb827..cfff5c61de 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -1221,7 +1221,7 @@ GridMapEditor::GridMapEditor() { search_box = memnew(LineEdit); search_box->set_h_size_flags(SIZE_EXPAND_FILL); - search_box->set_placeholder(TTR("Filter meshes")); + search_box->set_placeholder(TTR("Filter Meshes")); hb->add_child(search_box); search_box->connect("text_changed", callable_mp(this, &GridMapEditor::_text_changed)); search_box->connect("gui_input", callable_mp(this, &GridMapEditor::_sbox_input)); diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index 9da137f9d5..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(); @@ -483,15 +498,15 @@ bool GridMap::_octant_update(const OctantKey &p_key) { HashMap<int, List<Pair<Transform3D, IndexKey>>> multimesh_items; - for (RBSet<IndexKey>::Element *E = g.cells.front(); E; E = E->next()) { - ERR_CONTINUE(!cell_map.has(E->get())); - const Cell &c = cell_map[E->get()]; + for (const IndexKey &E : g.cells) { + ERR_CONTINUE(!cell_map.has(E)); + const Cell &c = cell_map[E]; if (!mesh_library.is_valid() || !mesh_library->has_item(c.item)) { continue; } - Vector3 cellpos = Vector3(E->get().x, E->get().y, E->get().z); + Vector3 cellpos = Vector3(E.x, E.y, E.z); Vector3 ofs = _get_offset(); Transform3D xform; @@ -507,7 +522,7 @@ bool GridMap::_octant_update(const OctantKey &p_key) { Pair<Transform3D, IndexKey> p; p.first = xform * mesh_library->get_item_mesh_transform(c.item); - p.second = E->get(); + p.second = E; multimesh_items[c.item].push_back(p); } } @@ -533,14 +548,29 @@ 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->get()] = nm; + 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"); @@ -970,7 +1009,7 @@ Array GridMap::get_meshes() const { xform.set_origin(cellpos * cell_size + ofs); xform.basis.scale(Vector3(cell_scale, cell_scale, cell_scale)); - meshes.push_back(xform); + meshes.push_back(xform * mesh_library->get_item_mesh_transform(id)); meshes.push_back(mesh); } diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h index 88e16bce82..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 { @@ -113,7 +114,7 @@ class GridMap : public Node3D { }; Vector<MultimeshInstance> multimesh_instances; - RBSet<IndexKey> cells; + HashSet<IndexKey> cells; RID collision_debug; RID collision_debug_instance; @@ -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 d0140f117c..622838b308 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -875,7 +875,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { // Script::instances are deleted during managed object disposal, which happens on domain finalize. // Only placeholders are kept. Therefore we need to keep a copy before that happens. - for (Object *&obj : script->instances) { + for (Object *obj : script->instances) { script->pending_reload_instances.insert(obj->get_instance_id()); RefCounted *rc = Object::cast_to<RefCounted>(obj); @@ -885,7 +885,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { } #ifdef TOOLS_ENABLED - for (PlaceHolderScriptInstance *&script_instance : script->placeholders) { + for (PlaceHolderScriptInstance *script_instance : script->placeholders) { Object *obj = script_instance->get_owner(); script->pending_reload_instances.insert(obj->get_instance_id()); @@ -899,7 +899,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { // Save state and remove script from instances RBMap<ObjectID, CSharpScript::StateBackup> &owners_map = script->pending_reload_state; - for (Object *&obj : script->instances) { + for (Object *obj : script->instances) { ERR_CONTINUE(!obj->get_script_instance()); CSharpInstance *csi = static_cast<CSharpInstance *>(obj->get_script_instance()); @@ -922,8 +922,8 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { // After the state of all instances is saved, clear scripts and script instances for (Ref<CSharpScript> &script : scripts) { - while (script->instances.front()) { - Object *obj = script->instances.front()->get(); + while (script->instances.begin()) { + Object *obj = *script->instances.begin(); obj->set_script(Ref<RefCounted>()); // Remove script and existing script instances (placeholder are not removed before domain reload) } @@ -2315,9 +2315,9 @@ CSharpInstance::~CSharpInstance() { #ifdef DEBUG_ENABLED // CSharpInstance must not be created unless it's going to be added to the list for sure - RBSet<Object *>::Element *match = script->instances.find(owner); + HashSet<Object *>::Iterator match = script->instances.find(owner); CRASH_COND(!match); - script->instances.erase(match); + script->instances.remove(match); #else script->instances.erase(owner); #endif @@ -2572,7 +2572,7 @@ bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_upda _update_exports_values(values, propnames); if (changed) { - for (PlaceHolderScriptInstance *&script_instance : placeholders) { + for (PlaceHolderScriptInstance *script_instance : placeholders) { script_instance->update(propnames, values); } } else { @@ -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; } @@ -3574,7 +3576,7 @@ CSharpScript::~CSharpScript() { #endif } -void CSharpScript::get_members(RBSet<StringName> *p_members) { +void CSharpScript::get_members(HashSet<StringName> *p_members) { #if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED) if (p_members) { for (const StringName &member_name : exported_members_names) { diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 6e600bb47a..69bd8703aa 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -110,7 +110,7 @@ private: Ref<CSharpScript> base_cache; // TODO what's this for? - RBSet<Object *> instances; + HashSet<Object *> instances; #ifdef GD_MONO_HOT_RELOAD struct StateBackup { @@ -121,7 +121,7 @@ private: List<Pair<StringName, Array>> event_signals; }; - RBSet<ObjectID> pending_reload_instances; + HashSet<ObjectID> pending_reload_instances; RBMap<ObjectID, StateBackup> pending_reload_state; StringName tied_class_name_for_reload; StringName tied_class_namespace_for_reload; @@ -141,7 +141,7 @@ private: #ifdef TOOLS_ENABLED List<PropertyInfo> exported_members_cache; // members_cache HashMap<StringName, Variant> exported_members_defval_cache; // member_default_values_cache - RBSet<PlaceHolderScriptInstance *> placeholders; + HashSet<PlaceHolderScriptInstance *> placeholders; bool source_changed_cache = false; bool placeholder_fallback_enabled = false; bool exports_invalidated = true; @@ -151,7 +151,7 @@ private: #endif #if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED) - RBSet<StringName> exported_members_names; + HashSet<StringName> exported_members_names; #endif HashMap<StringName, PropertyInfo> member_info; @@ -218,7 +218,7 @@ public: void get_script_property_list(List<PropertyInfo> *r_list) const override; void update_exports() override; - void get_members(RBSet<StringName> *p_members) override; + void get_members(HashSet<StringName> *p_members) override; bool is_tool() const override { return tool; } bool is_valid() const override { return valid; } @@ -467,7 +467,7 @@ public: virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const override; virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) override; /* TODO */ bool validate(const String &p_script, const String &p_path, List<String> *r_functions, - List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, RBSet<int> *r_safe_lines = nullptr) const override { + List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override { return true; } String validate_path(const String &p_path) const override; 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("<", "<").replacen(">", ">"); - 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 ¶m_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 42aad77979..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 { @@ -256,13 +256,19 @@ Array GodotNavigationServer::map_get_agents(RID p_map) const { RID GodotNavigationServer::region_get_map(RID p_region) const { NavRegion *region = region_owner.get_or_null(p_region); ERR_FAIL_COND_V(region == nullptr, RID()); - return region->get_map()->get_self(); + if (region->get_map()) { + return region->get_map()->get_self(); + } + return RID(); } RID GodotNavigationServer::agent_get_map(RID p_agent) const { RvoAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_COND_V(agent == nullptr, RID()); - return agent->get_map()->get_self(); + if (agent->get_map()) { + return agent->get_map()->get_self(); + } + return RID(); } RID GodotNavigationServer::region_create() const { @@ -303,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_enter_cost(p_enter_cost); +} + +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_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_layers(p_layers); + region->set_navigation_layers(p_navigation_layers); } -uint32_t GodotNavigationServer::region_get_layers(RID p_region) const { +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_layers(); + 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 0ebdea30e1..2036dbecd7 100644 --- a/modules/navigation/nav_map.h +++ b/modules/navigation/nav_map.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef RVO_SPACE_H -#define RVO_SPACE_H +#ifndef NAV_MAP_H +#define NAV_MAP_H #include "nav_rid.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; @@ -141,4 +141,4 @@ private: void clip_path(const std::vector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly) const; }; -#endif // RVO_SPACE_H +#endif // NAV_MAP_H 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 5cbaded6c1..e430f5fd59 100644 --- a/modules/navigation/navigation_mesh_generator.cpp +++ b/modules/navigation/navigation_mesh_generator.cpp @@ -172,14 +172,16 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans if (Object::cast_to<MultiMeshInstance3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) { MultiMeshInstance3D *multimesh_instance = Object::cast_to<MultiMeshInstance3D>(p_node); Ref<MultiMesh> multimesh = multimesh_instance->get_multimesh(); - Ref<Mesh> mesh = multimesh->get_mesh(); - if (mesh.is_valid()) { - int n = multimesh->get_visible_instance_count(); - if (n == -1) { - n = multimesh->get_instance_count(); - } - for (int i = 0; i < n; i++) { - _add_mesh(mesh, p_navmesh_transform * multimesh_instance->get_global_transform() * multimesh->get_instance_transform(i), p_vertices, p_indices); + if (multimesh.is_valid()) { + Ref<Mesh> mesh = multimesh->get_mesh(); + if (mesh.is_valid()) { + int n = multimesh->get_visible_instance_count(); + if (n == -1) { + n = multimesh->get_instance_count(); + } + for (int i = 0; i < n; i++) { + _add_mesh(mesh, p_navmesh_transform * multimesh_instance->get_global_transform() * multimesh->get_instance_transform(i), p_vertices, p_indices); + } } } } @@ -448,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/fastnoise_lite.cpp b/modules/noise/fastnoise_lite.cpp index 914cdec24c..b21e3247d7 100644 --- a/modules/noise/fastnoise_lite.cpp +++ b/modules/noise/fastnoise_lite.cpp @@ -300,6 +300,12 @@ real_t FastNoiseLite::get_domain_warp_fractal_gain() const { // Noise interface functions. real_t FastNoiseLite::get_noise_1d(real_t p_x) const { + p_x += offset.x; + if (domain_warp_enabled) { + // Needed since DomainWarp expects a reference. + real_t y_dummy = 0; + _domain_warp_noise.DomainWarp(p_x, y_dummy); + } return get_noise_2d(p_x, 0.0); } @@ -308,10 +314,12 @@ real_t FastNoiseLite::get_noise_2dv(Vector2 p_v) const { } real_t FastNoiseLite::get_noise_2d(real_t p_x, real_t p_y) const { + p_x += offset.x; + p_y += offset.y; if (domain_warp_enabled) { _domain_warp_noise.DomainWarp(p_x, p_y); } - return _noise.GetNoise(p_x + offset.x, p_y + offset.y); + return _noise.GetNoise(p_x, p_y); } real_t FastNoiseLite::get_noise_3dv(Vector3 p_v) const { @@ -319,10 +327,13 @@ real_t FastNoiseLite::get_noise_3dv(Vector3 p_v) const { } real_t FastNoiseLite::get_noise_3d(real_t p_x, real_t p_y, real_t p_z) const { + p_x += offset.x; + p_y += offset.y; + p_z += offset.z; if (domain_warp_enabled) { _domain_warp_noise.DomainWarp(p_x, p_y, p_z); } - return _noise.GetNoise(p_x + offset.x, p_y + offset.y, p_z + offset.z); + return _noise.GetNoise(p_x, p_y, p_z); } void FastNoiseLite::_changed() { 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/editor/openxr_action_editor.cpp b/modules/openxr/editor/openxr_action_editor.cpp index e2a4f67f16..41c6465f43 100644 --- a/modules/openxr/editor/openxr_action_editor.cpp +++ b/modules/openxr/editor/openxr_action_editor.cpp @@ -63,8 +63,7 @@ void OpenXRActionEditor::_on_action_localized_name_changed(const String p_new_te } void OpenXRActionEditor::_on_item_selected(int p_idx) { - ERR_FAIL_COND(p_idx < 0); - ERR_FAIL_COND(p_idx >= OpenXRAction::OPENXR_ACTION_MAX); + ERR_FAIL_INDEX(p_idx, OpenXRAction::OPENXR_ACTION_MAX); action->set_action_type(OpenXRAction::ActionType(p_idx)); } diff --git a/modules/openxr/editor/openxr_action_map_editor.cpp b/modules/openxr/editor/openxr_action_map_editor.cpp index 6e9a2e1b61..87b7f50224 100644 --- a/modules/openxr/editor/openxr_action_map_editor.cpp +++ b/modules/openxr/editor/openxr_action_map_editor.cpp @@ -344,6 +344,7 @@ OpenXRActionMapEditor::OpenXRActionMapEditor() { tabs = memnew(TabContainer); tabs->set_h_size_flags(SIZE_EXPAND_FILL); tabs->set_v_size_flags(SIZE_EXPAND_FILL); + tabs->set_theme_type_variation("TabContainerOdd"); tabs->connect("tab_changed", callable_mp(this, &OpenXRActionMapEditor::_on_tabs_tab_changed)); tabs->connect("tab_button_pressed", callable_mp(this, &OpenXRActionMapEditor::_on_tab_button_pressed)); add_child(tabs); diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 6e94e3b444..d8d3bacb19 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -172,7 +172,7 @@ bool OpenXRAPI::load_supported_extensions() { bool OpenXRAPI::is_extension_supported(const String &p_extension) const { for (uint32_t i = 0; i < num_supported_extensions; i++) { - if ((supported_extensions[i].extensionName == p_extension) == 0) { + if (supported_extensions[i].extensionName == p_extension) { #ifdef DEBUG print_line("OpenXR: requested extension", p_extension, "is supported"); #endif @@ -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/raycast/lightmap_raycaster.cpp b/modules/raycast/lightmap_raycaster.cpp index 7e7c3f9067..9b35b5616e 100644 --- a/modules/raycast/lightmap_raycaster.cpp +++ b/modules/raycast/lightmap_raycaster.cpp @@ -152,17 +152,17 @@ void LightmapRaycasterEmbree::commit() { rtcCommitScene(embree_scene); } -void LightmapRaycasterEmbree::set_mesh_filter(const RBSet<int> &p_mesh_ids) { - for (RBSet<int>::Element *E = p_mesh_ids.front(); E; E = E->next()) { - rtcDisableGeometry(rtcGetGeometry(embree_scene, E->get())); +void LightmapRaycasterEmbree::set_mesh_filter(const HashSet<int> &p_mesh_ids) { + for (const int &E : p_mesh_ids) { + rtcDisableGeometry(rtcGetGeometry(embree_scene, E)); } rtcCommitScene(embree_scene); filter_meshes = p_mesh_ids; } void LightmapRaycasterEmbree::clear_mesh_filter() { - for (RBSet<int>::Element *E = filter_meshes.front(); E; E = E->next()) { - rtcEnableGeometry(rtcGetGeometry(embree_scene, E->get())); + for (const int &E : filter_meshes) { + rtcEnableGeometry(rtcGetGeometry(embree_scene, E)); } rtcCommitScene(embree_scene); filter_meshes.clear(); diff --git a/modules/raycast/lightmap_raycaster.h b/modules/raycast/lightmap_raycaster.h index 4c52758fd8..4266b46ea8 100644 --- a/modules/raycast/lightmap_raycaster.h +++ b/modules/raycast/lightmap_raycaster.h @@ -53,7 +53,7 @@ private: static void filter_function(const struct RTCFilterFunctionNArguments *p_args); HashMap<unsigned int, AlphaTextureData> alpha_textures; - RBSet<int> filter_meshes; + HashSet<int> filter_meshes; public: virtual bool intersect(Ray &p_ray) override; @@ -64,7 +64,7 @@ public: virtual void set_mesh_alpha_texture(Ref<Image> p_alpha_texture, unsigned int p_id) override; virtual void commit() override; - virtual void set_mesh_filter(const RBSet<int> &p_mesh_ids) override; + virtual void set_mesh_filter(const HashSet<int> &p_mesh_ids) override; virtual void clear_mesh_filter() override; static LightmapRaycaster *create_embree_raycaster(); diff --git a/modules/raycast/raycast_occlusion_cull.cpp b/modules/raycast/raycast_occlusion_cull.cpp index c165354c7b..89e75f774e 100644 --- a/modules/raycast/raycast_occlusion_cull.cpp +++ b/modules/raycast/raycast_occlusion_cull.cpp @@ -223,9 +223,9 @@ void RaycastOcclusionCull::occluder_set_mesh(RID p_occluder, const PackedVector3 occluder->vertices = p_vertices; occluder->indices = p_indices; - for (RBSet<InstanceID>::Element *E = occluder->users.front(); E; E = E->next()) { - RID scenario_rid = E->get().scenario; - RID instance_rid = E->get().instance; + for (const InstanceID &E : occluder->users) { + RID scenario_rid = E.scenario; + RID instance_rid = E.instance; ERR_CONTINUE(!scenarios.has(scenario_rid)); Scenario &scenario = scenarios[scenario_rid]; ERR_CONTINUE(!scenario.instances.has(instance_rid)); diff --git a/modules/raycast/raycast_occlusion_cull.h b/modules/raycast/raycast_occlusion_cull.h index 779f6fb3c4..4474031991 100644 --- a/modules/raycast/raycast_occlusion_cull.h +++ b/modules/raycast/raycast_occlusion_cull.h @@ -86,11 +86,13 @@ private: RID scenario; RID instance; - bool operator<(const InstanceID &rhs) const { - if (instance == rhs.instance) { - return rhs.scenario < scenario; - } - return instance < rhs.instance; + static uint32_t hash(const InstanceID &p_ins) { + uint32_t h = hash_djb2_one_64(p_ins.scenario.get_id()); + return hash_djb2_one_64(p_ins.instance.get_id(), h); + } + bool operator==(const InstanceID &rhs) const { + return instance == rhs.instance && rhs.scenario == scenario; + ; } InstanceID() {} @@ -101,7 +103,7 @@ private: struct Occluder { PackedVector3Array vertices; PackedInt32Array indices; - RBSet<InstanceID> users; + HashSet<InstanceID, InstanceID> users; }; struct OccluderInstance { @@ -136,7 +138,7 @@ private: int current_scene_idx = 0; HashMap<RID, OccluderInstance> instances; - RBSet<RID> dirty_instances; // To avoid duplicates + HashSet<RID> dirty_instances; // To avoid duplicates LocalVector<RID> dirty_instances_array; // To iterate and split into threads LocalVector<RID> removed_instances; diff --git a/modules/raycast/static_raycaster.cpp b/modules/raycast/static_raycaster.cpp index f2e944a82d..7659eea27f 100644 --- a/modules/raycast/static_raycaster.cpp +++ b/modules/raycast/static_raycaster.cpp @@ -94,17 +94,17 @@ void StaticRaycasterEmbree::commit() { rtcCommitScene(embree_scene); } -void StaticRaycasterEmbree::set_mesh_filter(const RBSet<int> &p_mesh_ids) { - for (RBSet<int>::Element *E = p_mesh_ids.front(); E; E = E->next()) { - rtcDisableGeometry(rtcGetGeometry(embree_scene, E->get())); +void StaticRaycasterEmbree::set_mesh_filter(const HashSet<int> &p_mesh_ids) { + for (const int &E : p_mesh_ids) { + rtcDisableGeometry(rtcGetGeometry(embree_scene, E)); } rtcCommitScene(embree_scene); filter_meshes = p_mesh_ids; } void StaticRaycasterEmbree::clear_mesh_filter() { - for (RBSet<int>::Element *E = filter_meshes.front(); E; E = E->next()) { - rtcEnableGeometry(rtcGetGeometry(embree_scene, E->get())); + for (const int &E : filter_meshes) { + rtcEnableGeometry(rtcGetGeometry(embree_scene, E)); } rtcCommitScene(embree_scene); filter_meshes.clear(); diff --git a/modules/raycast/static_raycaster.h b/modules/raycast/static_raycaster.h index b52b5bccfc..e2909f9b56 100644 --- a/modules/raycast/static_raycaster.h +++ b/modules/raycast/static_raycaster.h @@ -41,7 +41,7 @@ private: static RTCDevice embree_device; RTCScene embree_scene; - RBSet<int> filter_meshes; + HashSet<int> filter_meshes; public: virtual bool intersect(Ray &p_ray) override; @@ -50,7 +50,7 @@ public: virtual void add_mesh(const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices, unsigned int p_id) override; virtual void commit() override; - virtual void set_mesh_filter(const RBSet<int> &p_mesh_ids) override; + virtual void set_mesh_filter(const HashSet<int> &p_mesh_ids) override; virtual void clear_mesh_filter() override; static StaticRaycaster *create_embree_raycaster(); diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub index 525d4d3efb..a46f17311a 100644 --- a/modules/text_server_adv/SCsub +++ b/modules/text_server_adv/SCsub @@ -442,7 +442,7 @@ if env["builtin_icu"]: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - icu_data_name = "icudt70l.dat" + icu_data_name = "icudt71l.dat" if env_icu["tools"]: env_icu.Depends("#thirdparty/icu4c/icudata.gen.h", "#thirdparty/icu4c/" + icu_data_name) diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct index 1c38398c88..0e36ef6805 100644 --- a/modules/text_server_adv/gdextension_build/SConstruct +++ b/modules/text_server_adv/gdextension_build/SConstruct @@ -116,6 +116,7 @@ if env["freetype_enabled"]: "src/psnames/psnames.c", "src/raster/raster.c", "src/sdf/sdf.c", + "src/svg/svg.c", "src/smooth/smooth.c", "src/truetype/truetype.c", "src/type1/type1.c", @@ -164,7 +165,14 @@ if env["freetype_enabled"]: env_freetype.Append(CPPPATH=[thirdparty_freetype_dir + "/include", thirdparty_zlib_dir, thirdparty_png_dir]) env.Append(CPPPATH=[thirdparty_freetype_dir + "/include"]) - env_freetype.Append(CPPDEFINES=["FT2_BUILD_LIBRARY", "FT_CONFIG_OPTION_USE_PNG", ("PNG_ARM_NEON_OPT", 0)]) + env_freetype.Append( + CPPDEFINES=[ + "FT2_BUILD_LIBRARY", + "FT_CONFIG_OPTION_USE_PNG", + ("PNG_ARM_NEON_OPT", 0), + "FT_CONFIG_OPTION_SYSTEM_ZLIB", + ] + ) if env["target"] == "debug": env_freetype.Append(CPPDEFINES=["ZLIB_DEBUG"]) @@ -564,7 +572,7 @@ thirdparty_icu_sources = [ ] thirdparty_icu_sources = [thirdparty_icu_dir + file for file in thirdparty_icu_sources] -icu_data_name = "icudt70l.dat" +icu_data_name = "icudt71l.dat" if env["static_icu_data"]: env_icu.Depends("../../../thirdparty/icu4c/icudata.gen.h", "../../../thirdparty/icu4c/" + icu_data_name) diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index bd14b15561..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. @@ -1691,6 +1700,8 @@ hb_font_t *TextServerAdvanced::_font_get_hb_handle(const RID &p_font_rid, int64_ } RID TextServerAdvanced::create_font() { + _THREAD_SAFE_METHOD_ + FontDataAdvanced *fd = memnew(FontDataAdvanced); return font_owner.make_rid(fd); @@ -1718,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); @@ -2778,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) { @@ -3270,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--; @@ -3359,6 +3433,7 @@ void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) { RID TextServerAdvanced::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) { _THREAD_SAFE_METHOD_ + ShapedTextDataAdvanced *sd = memnew(ShapedTextDataAdvanced); sd->hb_buffer = hb_buffer_create(); sd->direction = p_direction; @@ -3748,6 +3823,8 @@ void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const { } RID TextServerAdvanced::shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const { + _THREAD_SAFE_METHOD_ + const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, RID()); @@ -4775,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); @@ -5584,9 +5664,9 @@ PackedInt32Array TextServerAdvanced::string_get_word_breaks(const String &p_stri // Convert to UTF-16. Char16String utf16 = p_string.utf16(); - RBSet<int> breaks; + 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++) { @@ -5635,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 897c27b6fa..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> @@ -126,7 +126,7 @@ class TextServerAdvanced : public TextServerExtension { _THREAD_SAFE_CLASS_ struct NumSystemData { - RBSet<StringName> lang; + HashSet<StringName> lang; String digits; String percent_sign; String exp; @@ -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 @@ -235,7 +235,7 @@ class TextServerAdvanced : public TextServerExtension { HashMap<Vector2i, FontDataForSizeAdvanced *, VariantHasher, VariantComparator> cache; bool face_init = false; - RBSet<uint32_t> supported_scripts; + HashSet<uint32_t> supported_scripts; Dictionary supported_features; Dictionary supported_varaitions; Dictionary feature_overrides; @@ -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/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct index 1753bc8b86..6c9e10db18 100644 --- a/modules/text_server_fb/gdextension_build/SConstruct +++ b/modules/text_server_fb/gdextension_build/SConstruct @@ -111,6 +111,7 @@ if env["freetype_enabled"]: "src/psnames/psnames.c", "src/raster/raster.c", "src/sdf/sdf.c", + "src/svg/svg.c", "src/smooth/smooth.c", "src/truetype/truetype.c", "src/type1/type1.c", @@ -159,7 +160,14 @@ if env["freetype_enabled"]: env_freetype.Append(CPPPATH=[thirdparty_freetype_dir + "/include", thirdparty_zlib_dir, thirdparty_png_dir]) env.Append(CPPPATH=[thirdparty_freetype_dir + "/include"]) - env_freetype.Append(CPPDEFINES=["FT2_BUILD_LIBRARY", "FT_CONFIG_OPTION_USE_PNG", ("PNG_ARM_NEON_OPT", 0)]) + env_freetype.Append( + CPPDEFINES=[ + "FT2_BUILD_LIBRARY", + "FT_CONFIG_OPTION_USE_PNG", + ("PNG_ARM_NEON_OPT", 0), + "FT_CONFIG_OPTION_SYSTEM_ZLIB", + ] + ) if env["target"] == "debug": env_freetype.Append(CPPDEFINES=["ZLIB_DEBUG"]) diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index c53ca7766a..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; @@ -853,6 +862,8 @@ _FORCE_INLINE_ void TextServerFallback::_font_clear_cache(FontDataFallback *p_fo } RID TextServerFallback::create_font() { + _THREAD_SAFE_METHOD_ + FontDataFallback *fd = memnew(FontDataFallback); return font_owner.make_rid(fd); @@ -890,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); @@ -1926,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) { @@ -2429,6 +2503,7 @@ void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) { RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) { _THREAD_SAFE_METHOD_ + ShapedTextDataFallback *sd = memnew(ShapedTextDataFallback); sd->direction = p_direction; sd->orientation = p_orientation; @@ -2807,6 +2882,8 @@ void TextServerFallback::_realign(ShapedTextDataFallback *p_sd) const { } RID TextServerFallback::shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const { + _THREAD_SAFE_METHOD_ + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, RID()); 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 4847b24f61..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); } @@ -980,6 +1022,9 @@ void VisualScriptEditor::_update_graph(int p_only_id) { float graph_minimap_opacity = EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity"); graph->set_minimap_opacity(graph_minimap_opacity); + float graph_lines_curvature = EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature"); + graph->set_connection_lines_curvature(graph_lines_curvature); + // Use default_func instead of default_func for now I think that should be good stop gap solution to ensure not breaking anything. graph->call_deferred(SNAME("set_scroll_ofs"), script->get_scroll() * EDSCALE); updating_graph = false; @@ -1440,7 +1485,11 @@ void VisualScriptEditor::_deselect_input_names() { } } -void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_button) { +void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_button, MouseButton p_mouse_button) { + if (p_mouse_button != MouseButton::LEFT) { + return; + } + TreeItem *ti = Object::cast_to<TreeItem>(p_item); TreeItem *root = members->get_root(); @@ -1625,8 +1674,8 @@ void VisualScriptEditor::_remove_output_port(int p_id, int p_port) { undo_redo->add_do_method(this, "_update_graph", p_id); for (const KeyValue<int, RBSet<int>> &E : conn_map) { - for (const RBSet<int>::Element *F = E.value.front(); F; F = F->next()) { - undo_redo->add_undo_method(script.ptr(), "data_connect", p_id, p_port, E.key, F->get()); + for (const int &F : E.value) { + undo_redo->add_undo_method(script.ptr(), "data_connect", p_id, p_port, E.key, F); } } @@ -1813,14 +1862,14 @@ void VisualScriptEditor::_on_nodes_paste() { undo_redo->add_undo_method(script.ptr(), "remove_node", new_id); } - for (RBSet<VisualScript::SequenceConnection>::Element *E = clipboard->sequence_connections.front(); E; E = E->next()) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]); - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]); + for (const VisualScript::SequenceConnection &E : clipboard->sequence_connections) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", remap[E.from_node], E.from_output, remap[E.to_node]); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", remap[E.from_node], E.from_output, remap[E.to_node]); } - for (RBSet<VisualScript::DataConnection>::Element *E = clipboard->data_connections.front(); E; E = E->next()) { - undo_redo->add_do_method(script.ptr(), "data_connect", remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port); - undo_redo->add_undo_method(script.ptr(), "data_disconnect", remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port); + for (const VisualScript::DataConnection &E : clipboard->data_connections) { + undo_redo->add_do_method(script.ptr(), "data_connect", remap[E.from_node], E.from_port, remap[E.to_node], E.to_port); + undo_redo->add_undo_method(script.ptr(), "data_disconnect", remap[E.from_node], E.from_port, remap[E.to_node], E.to_port); } undo_redo->add_do_method(this, "_update_graph"); @@ -1910,17 +1959,17 @@ void VisualScriptEditor::_on_nodes_duplicate() { RBSet<int> to_select; HashMap<int, int> remap; - for (RBSet<int>::Element *F = to_duplicate.front(); F; F = F->next()) { + for (const int &F : to_duplicate) { // Duplicate from the specific function but place it into the default func as it would lack the connections. - Ref<VisualScriptNode> node = script->get_node(F->get()); + Ref<VisualScriptNode> node = script->get_node(F); Ref<VisualScriptNode> dupe = node->duplicate(true); int new_id = idc++; - remap.insert(F->get(), new_id); + remap.insert(F, new_id); to_select.insert(new_id); - undo_redo->add_do_method(script.ptr(), "add_node", new_id, dupe, script->get_node_position(F->get()) + Vector2(20, 20)); + undo_redo->add_do_method(script.ptr(), "add_node", new_id, dupe, script->get_node_position(F) + Vector2(20, 20)); undo_redo->add_undo_method(script.ptr(), "remove_node", new_id); } @@ -1966,15 +2015,6 @@ void VisualScriptEditor::_generic_search(Vector2 pos, bool node_centered) { } new_connect_node_select->select_from_visual_script(script, false); // do not reset text - - // Ensure that the dialog fits inside the graph. - Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size(); - pos.x = pos.x > bounds.x ? bounds.x : pos.x; - pos.y = pos.y > bounds.y ? bounds.y : pos.y; - - if (pos != Vector2()) { - new_connect_node_select->set_position(pos); - } } void VisualScriptEditor::input(const Ref<InputEvent> &p_event) { @@ -3935,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: { @@ -4201,9 +4244,9 @@ void VisualScriptEditor::_menu_option(int p_what) { // If we still don't have a start node then, // run through the nodes and select the first tree node, // i.e. node without any input sequence but output sequence. - for (RBSet<int>::Element *E = nodes_from.front(); E; E = E->next()) { - if (!nodes_to.has(E->get())) { - start_node = E->get(); + for (const int &E : nodes_from) { + if (!nodes_to.has(E)) { + start_node = E; } } } @@ -4272,13 +4315,13 @@ void VisualScriptEditor::_menu_option(int p_what) { // Move the nodes. // Handles reconnection of sequence connections on undo, start here in case of issues. - for (RBSet<VisualScript::SequenceConnection>::Element *E = seqext.front(); E; E = E->next()) { - undo_redo->add_do_method(script.ptr(), "sequence_disconnect", E->get().from_node, E->get().from_output, E->get().to_node); - undo_redo->add_undo_method(script.ptr(), "sequence_connect", E->get().from_node, E->get().from_output, E->get().to_node); + for (const VisualScript::SequenceConnection &E : seqext) { + undo_redo->add_do_method(script.ptr(), "sequence_disconnect", E.from_node, E.from_output, E.to_node); + undo_redo->add_undo_method(script.ptr(), "sequence_connect", E.from_node, E.from_output, E.to_node); } - for (RBSet<VisualScript::DataConnection>::Element *E = dataext.front(); E; E = E->next()) { - undo_redo->add_do_method(script.ptr(), "data_disconnect", E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); - undo_redo->add_undo_method(script.ptr(), "data_connect", E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + for (const VisualScript::DataConnection &E : dataext) { + undo_redo->add_do_method(script.ptr(), "data_disconnect", E.from_node, E.from_port, E.to_node, E.to_port); + undo_redo->add_undo_method(script.ptr(), "data_connect", E.from_node, E.from_port, E.to_node, E.to_port); } // I don't really think we need support for non sequenced functions at this moment. @@ -4286,24 +4329,24 @@ void VisualScriptEditor::_menu_option(int p_what) { // Could fail with the new changes, start here when searching for bugs in create function shortcut. int m = 1; - for (RBSet<int>::Element *G = end_nodes.front(); G; G = G->next()) { + for (const int &G : end_nodes) { Ref<VisualScriptReturn> ret_node; ret_node.instantiate(); int ret_id = fn_id + (m++); selections.insert(ret_id); - Vector2 posi = _get_available_pos(false, script->get_node_position(G->get()) + Vector2(80, -100)); + Vector2 posi = _get_available_pos(false, script->get_node_position(G) + Vector2(80, -100)); undo_redo->add_do_method(script.ptr(), "add_node", ret_id, ret_node, posi); undo_redo->add_undo_method(script.ptr(), "remove_node", ret_id); - undo_redo->add_do_method(script.ptr(), "sequence_connect", G->get(), 0, ret_id); + undo_redo->add_do_method(script.ptr(), "sequence_connect", G, 0, ret_id); // Add data outputs from each of the end_nodes. - Ref<VisualScriptNode> vsn = script->get_node(G->get()); + Ref<VisualScriptNode> vsn = script->get_node(G); if (vsn.is_valid() && vsn->get_output_value_port_count() > 0) { ret_node->set_enable_return_value(true); // Use the zeroth data port cause that's the likely one that is planned to be used. ret_node->set_return_type(vsn->get_output_value_port_info(0).type); - undo_redo->add_do_method(script.ptr(), "data_connect", G->get(), 0, ret_id, 0); + undo_redo->add_do_method(script.ptr(), "data_connect", G, 0, ret_id, 0); } } @@ -4352,7 +4395,11 @@ void VisualScriptEditor::_get_ends(int p_node, const List<VisualScript::Sequence } } -void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos) { +void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos, MouseButton p_button) { + if (p_button != MouseButton::RIGHT) { + return; + } + TreeItem *ti = members->get_selected(); ERR_FAIL_COND(!ti); @@ -4553,11 +4600,11 @@ VisualScriptEditor::VisualScriptEditor() { members_section->add_margin_child(TTR("Members:"), members, true); members->set_custom_minimum_size(Size2(0, 50 * EDSCALE)); members->set_hide_root(true); - members->connect("button_pressed", callable_mp(this, &VisualScriptEditor::_member_button)); + members->connect("button_clicked", callable_mp(this, &VisualScriptEditor::_member_button)); members->connect("item_edited", callable_mp(this, &VisualScriptEditor::_member_edited)); members->connect("cell_selected", callable_mp(this, &VisualScriptEditor::_member_selected), varray(), CONNECT_DEFERRED); members->connect("gui_input", callable_mp(this, &VisualScriptEditor::_members_gui_input)); - members->connect("item_rmb_selected", callable_mp(this, &VisualScriptEditor::_member_rmb_selected)); + members->connect("item_mouse_selected", callable_mp(this, &VisualScriptEditor::_member_rmb_selected)); members->set_allow_rmb_select(true); members->set_allow_reselect(true); members->set_hide_folding(true); @@ -4594,6 +4641,8 @@ VisualScriptEditor::VisualScriptEditor() { graph->set_drag_forwarding(this); float graph_minimap_opacity = EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity"); graph->set_minimap_opacity(graph_minimap_opacity); + float graph_lines_curvature = EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature"); + graph->set_connection_lines_curvature(graph_lines_curvature); graph->hide(); graph->connect("scroll_offset_changed", callable_mp(this, &VisualScriptEditor::_graph_ofs_changed)); diff --git a/modules/visual_script/editor/visual_script_editor.h b/modules/visual_script/editor/visual_script_editor.h index c7fd8c4a78..fd59d22cbe 100644 --- a/modules/visual_script/editor/visual_script_editor.h +++ b/modules/visual_script/editor/visual_script_editor.h @@ -226,7 +226,7 @@ class VisualScriptEditor : public ScriptEditorBase { void _update_available_nodes(); - void _member_button(Object *p_item, int p_column, int p_button); + void _member_button(Object *p_item, int p_column, int p_button, MouseButton p_mouse_button); void _expression_text_changed(const String &p_text, int p_id); void _add_input_port(int p_id); @@ -289,7 +289,7 @@ class VisualScriptEditor : public ScriptEditorBase { VisualScriptNode::TypeGuess _guess_output_type(int p_port_action_node, int p_port_action_output, RBSet<int> &p_visited_nodes); - void _member_rmb_selected(const Vector2 &p_pos); + void _member_rmb_selected(const Vector2 &p_pos, MouseButton p_button); void _member_option(int p_option); void _toggle_scripts_pressed(); diff --git a/modules/visual_script/editor/visual_script_property_selector.cpp b/modules/visual_script/editor/visual_script_property_selector.cpp index 7557d12526..ae30655301 100644 --- a/modules/visual_script/editor/visual_script_property_selector.cpp +++ b/modules/visual_script/editor/visual_script_property_selector.cpp @@ -521,8 +521,17 @@ VisualScriptPropertySelector::VisualScriptPropertySelector() { results_tree->connect("item_selected", callable_mp(this, &VisualScriptPropertySelector::_item_selected)); vbox->add_child(results_tree); + ScrollContainer *scroller = memnew(ScrollContainer); + scroller->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED); + scroller->set_v_size_flags(Control::SIZE_EXPAND_FILL); + scroller->set_custom_minimum_size(Size2(600, 400) * EDSCALE); + vbox->add_child(scroller); + help_bit = memnew(EditorHelpBit); - vbox->add_child(help_bit); + help_bit->set_h_size_flags(Control::SIZE_EXPAND_FILL); + help_bit->set_v_size_flags(Control::SIZE_EXPAND_FILL); + scroller->add_child(help_bit); + help_bit->connect("request_hide", callable_mp(this, &VisualScriptPropertySelector::_hide_requested)); get_ok_button()->set_text(TTR("Open")); get_ok_button()->set_disabled(true); diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index a287b8e69f..30b64d0a7b 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -230,12 +230,12 @@ void VisualScript::_node_ports_changed(int p_id) { { List<SequenceConnection> to_remove; - for (RBSet<SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) { - if (E->get().from_node == p_id && E->get().from_output >= vsn->get_output_sequence_port_count()) { - to_remove.push_back(E->get()); + for (const SequenceConnection &E : sequence_connections) { + if (E.from_node == p_id && E.from_output >= vsn->get_output_sequence_port_count()) { + to_remove.push_back(E); } - if (E->get().to_node == p_id && !vsn->has_input_sequence_port()) { - to_remove.push_back(E->get()); + if (E.to_node == p_id && !vsn->has_input_sequence_port()) { + to_remove.push_back(E); } } @@ -248,12 +248,12 @@ void VisualScript::_node_ports_changed(int p_id) { { List<DataConnection> to_remove; - for (RBSet<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { - if (E->get().from_node == p_id && E->get().from_port >= vsn->get_output_value_port_count()) { - to_remove.push_back(E->get()); + for (const DataConnection &E : data_connections) { + if (E.from_node == p_id && E.from_port >= vsn->get_output_value_port_count()) { + to_remove.push_back(E); } - if (E->get().to_node == p_id && E->get().to_port >= vsn->get_input_value_port_count()) { - to_remove.push_back(E->get()); + if (E.to_node == p_id && E.to_port >= vsn->get_input_value_port_count()) { + to_remove.push_back(E); } } @@ -292,9 +292,9 @@ void VisualScript::remove_node(int p_id) { { List<SequenceConnection> to_remove; - for (RBSet<SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) { - if (E->get().from_node == p_id || E->get().to_node == p_id) { - to_remove.push_back(E->get()); + for (const SequenceConnection &E : sequence_connections) { + if (E.from_node == p_id || E.to_node == p_id) { + to_remove.push_back(E); } } @@ -307,9 +307,9 @@ void VisualScript::remove_node(int p_id) { { List<DataConnection> to_remove; - for (RBSet<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { - if (E->get().from_node == p_id || E->get().to_node == p_id) { - to_remove.push_back(E->get()); + for (const DataConnection &E : data_connections) { + if (E.from_node == p_id || E.to_node == p_id) { + to_remove.push_back(E); } } @@ -384,8 +384,8 @@ bool VisualScript::has_sequence_connection(int p_from_node, int p_from_output, i } void VisualScript::get_sequence_connection_list(List<SequenceConnection> *r_connection) const { - for (const RBSet<SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) { - r_connection->push_back(E->get()); + for (const SequenceConnection &E : sequence_connections) { + r_connection->push_back(E); } } @@ -426,8 +426,8 @@ bool VisualScript::has_data_connection(int p_from_node, int p_from_port, int p_t } bool VisualScript::is_input_value_port_connected(int p_node, int p_port) const { - for (const RBSet<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { - if (E->get().to_node == p_node && E->get().to_port == p_port) { + for (const DataConnection &E : data_connections) { + if (E.to_node == p_node && E.to_port == p_port) { return true; } } @@ -435,10 +435,10 @@ bool VisualScript::is_input_value_port_connected(int p_node, int p_port) const { } bool VisualScript::get_input_value_port_connection_source(int p_node, int p_port, int *r_node, int *r_port) const { - for (const RBSet<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { - if (E->get().to_node == p_node && E->get().to_port == p_port) { - *r_node = E->get().from_node; - *r_port = E->get().from_port; + for (const DataConnection &E : data_connections) { + if (E.to_node == p_node && E.to_port == p_port) { + *r_node = E.from_node; + *r_port = E.from_port; return true; } } @@ -446,8 +446,8 @@ bool VisualScript::get_input_value_port_connection_source(int p_node, int p_port } void VisualScript::get_data_connection_list(List<DataConnection> *r_connection) const { - for (const RBSet<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { - r_connection->push_back(E->get()); + for (const DataConnection &E : data_connections) { + r_connection->push_back(E); } } @@ -764,8 +764,8 @@ void VisualScript::_update_placeholders() { values[p.name] = variables[E.key].default_value; } - for (RBSet<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { - E->get()->update(pinfo, values); + for (PlaceHolderScriptInstance *E : placeholders) { + E->update(pinfo, values); } } @@ -1078,19 +1078,19 @@ Dictionary VisualScript::_get_data() const { d["nodes"] = nds; Array seqconns; - for (const RBSet<SequenceConnection>::Element *F = sequence_connections.front(); F; F = F->next()) { - seqconns.push_back(F->get().from_node); - seqconns.push_back(F->get().from_output); - seqconns.push_back(F->get().to_node); + for (const SequenceConnection &F : sequence_connections) { + seqconns.push_back(F.from_node); + seqconns.push_back(F.from_output); + seqconns.push_back(F.to_node); } d["sequence_connections"] = seqconns; Array dataconns; - for (const RBSet<DataConnection>::Element *F = data_connections.front(); F; F = F->next()) { - dataconns.push_back(F->get().from_node); - dataconns.push_back(F->get().from_port); - dataconns.push_back(F->get().to_node); - dataconns.push_back(F->get().to_port); + for (const DataConnection &F : data_connections) { + dataconns.push_back(F.from_node); + dataconns.push_back(F.from_port); + dataconns.push_back(F.to_node); + dataconns.push_back(F.to_port); } d["data_connections"] = dataconns; @@ -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; } @@ -1869,23 +1869,23 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o List<int> nd_queue; nd_queue.push_back(function.node); while (!nd_queue.is_empty()) { - for (const RBSet<VisualScript::SequenceConnection>::Element *F = script->sequence_connections.front(); F; F = F->next()) { - if (nd_queue.front()->get() == F->get().from_node && !node_ids.has(F->get().to_node)) { - nd_queue.push_back(F->get().to_node); - node_ids.insert(F->get().to_node); + for (const VisualScript::SequenceConnection &F : script->sequence_connections) { + if (nd_queue.front()->get() == F.from_node && !node_ids.has(F.to_node)) { + nd_queue.push_back(F.to_node); + node_ids.insert(F.to_node); } - if (nd_queue.front()->get() == F->get().from_node && !seqconns.has(F->get())) { - seqconns.insert(F->get()); + if (nd_queue.front()->get() == F.from_node && !seqconns.has(F)) { + seqconns.insert(F); } } nd_queue.pop_front(); } HashMap<int, HashMap<int, Pair<int, int>>> dc_lut; // :: to -> to_port -> (from, from_port) - for (const RBSet<VisualScript::DataConnection>::Element *F = script->data_connections.front(); F; F = F->next()) { - dc_lut[F->get().to_node][F->get().to_port] = Pair<int, int>(F->get().from_node, F->get().from_port); + for (const VisualScript::DataConnection &F : script->data_connections) { + dc_lut[F.to_node][F.to_port] = Pair<int, int>(F.from_node, F.from_port); } - for (const RBSet<int>::Element *F = node_ids.front(); F; F = F->next()) { - nd_queue.push_back(F->get()); + for (const int &F : node_ids) { + nd_queue.push_back(F); } List<int> dc_keys; while (!nd_queue.is_empty()) { @@ -1907,15 +1907,15 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o //Multiple passes are required to set up this complex thing.. //First create the nodes. - for (const RBSet<int>::Element *F = node_ids.front(); F; F = F->next()) { - Ref<VisualScriptNode> node = script->nodes[F->get()].node; + for (const int &F : node_ids) { + Ref<VisualScriptNode> node = script->nodes[F].node; VisualScriptNodeInstance *instance = node->instantiate(this); // Create instance. ERR_FAIL_COND(!instance); instance->base = node.ptr(); - instance->id = F->get(); + instance->id = F; instance->input_port_count = node->get_input_value_port_count(); instance->input_ports = nullptr; instance->output_port_count = node->get_output_value_port_count(); @@ -1975,14 +1975,14 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o max_input_args = MAX(max_input_args, instance->input_port_count); max_output_args = MAX(max_output_args, instance->output_port_count); - instances[F->get()] = instance; + instances[F] = instance; } function.trash_pos = function.max_stack++; // create pos for trash // Second pass, do data connections. - for (const RBSet<VisualScript::DataConnection>::Element *F = dataconns.front(); F; F = F->next()) { - VisualScript::DataConnection dc = F->get(); + for (const VisualScript::DataConnection &F : dataconns) { + VisualScript::DataConnection dc = F; ERR_CONTINUE(!instances.has(dc.from_node)); VisualScriptNodeInstance *from = instances[dc.from_node]; ERR_CONTINUE(!instances.has(dc.to_node)); @@ -2008,8 +2008,8 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o } // Third pass, do sequence connections. - for (const RBSet<VisualScript::SequenceConnection>::Element *F = seqconns.front(); F; F = F->next()) { - VisualScript::SequenceConnection sc = F->get(); + for (const VisualScript::SequenceConnection &F : seqconns) { + VisualScript::SequenceConnection sc = F; ERR_CONTINUE(!instances.has(sc.from_node)); VisualScriptNodeInstance *from = instances[sc.from_node]; ERR_CONTINUE(!instances.has(sc.to_node)); @@ -2022,11 +2022,11 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o //fourth pass: // 1) unassigned input ports to default values // 2) connect unassigned output ports to trash - for (const RBSet<int>::Element *F = node_ids.front(); F; F = F->next()) { - ERR_CONTINUE(!instances.has(F->get())); + for (const int &F : node_ids) { + ERR_CONTINUE(!instances.has(F)); - Ref<VisualScriptNode> node = script->nodes[F->get()].node; - VisualScriptNodeInstance *instance = instances[F->get()]; + Ref<VisualScriptNode> node = script->nodes[F].node; + VisualScriptNodeInstance *instance = instances[F]; // Connect to default values. for (int i = 0; i < instance->input_port_count; i++) { @@ -2223,7 +2223,7 @@ Ref<Script> VisualScriptLanguage::make_template(const String &p_template, const return script; } -bool VisualScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, RBSet<int> *r_safe_lines) const { +bool VisualScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, HashSet<int> *r_safe_lines) const { return false; } diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h index c28ec2268b..0f3b8de3fc 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -36,6 +36,7 @@ #include "core/doc_data.h" #include "core/object/script_language.h" #include "core/os/thread.h" +#include "core/templates/rb_set.h" class VisualScriptInstance; class VisualScriptNodeInstance; @@ -570,7 +571,7 @@ public: virtual void get_string_delimiters(List<String> *p_delimiters) const override; virtual bool is_using_templates() override; virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const override; - virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, RBSet<int> *r_safe_lines = nullptr) const override; + virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override; virtual Script *create_script() const override; virtual bool has_named_classes() const override; virtual bool supports_builtin_mode() const override; 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_data_channel.h b/modules/webrtc/webrtc_data_channel.h index eac8f85a84..75e29283ec 100644 --- a/modules/webrtc/webrtc_data_channel.h +++ b/modules/webrtc/webrtc_data_channel.h @@ -33,7 +33,7 @@ #include "core/io/packet_peer.h" -#define WRTC_IN_BUF "network/limits/webrtc/max_channel_in_buffer_kb" +#define WRTC_IN_BUF PNAME("network/limits/webrtc/max_channel_in_buffer_kb") class WebRTCDataChannel : public PacketPeer { GDCLASS(WebRTCDataChannel, PacketPeer); 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; |