diff options
Diffstat (limited to 'modules')
| -rw-r--r-- | modules/bmp/image_loader_bmp.cpp | 31 | ||||
| -rw-r--r-- | modules/csg/csg.cpp | 24 | ||||
| -rw-r--r-- | modules/csg/csg_shape.cpp | 8 | ||||
| -rw-r--r-- | modules/gdscript/editor/gdscript_highlighter.cpp | 4 | ||||
| -rw-r--r-- | modules/gdscript/gdscript.cpp | 15 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_analyzer.cpp | 225 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_analyzer.h | 4 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_cache.cpp | 15 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_editor.cpp | 12 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_parser.cpp | 82 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_parser.h | 14 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_tokenizer.cpp | 12 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_tokenizer.h | 5 | ||||
| -rw-r--r-- | modules/gdscript/language_server/gdscript_extend_parser.cpp | 2 | ||||
| -rw-r--r-- | modules/gdscript/register_types.cpp | 4 | ||||
| -rw-r--r-- | modules/mobile_vr/register_types.cpp | 8 | ||||
| -rw-r--r-- | modules/mono/editor/GodotTools/GodotTools/BuildManager.cs | 19 | ||||
| -rw-r--r-- | modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs | 2 | ||||
| -rw-r--r-- | modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs | 23 |
19 files changed, 351 insertions, 158 deletions
diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp index ac4e534983..757afeb9e3 100644 --- a/modules/bmp/image_loader_bmp.cpp +++ b/modules/bmp/image_loader_bmp.cpp @@ -51,16 +51,20 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, if (bits_per_pixel == 1) { // Requires bit unpacking... - ERR_FAIL_COND_V(width % 8 != 0, ERR_UNAVAILABLE); - ERR_FAIL_COND_V(height % 8 != 0, ERR_UNAVAILABLE); + ERR_FAIL_COND_V_MSG(width % 8 != 0, ERR_UNAVAILABLE, + vformat("1-bpp BMP images must have a width that is a multiple of 8, but the imported BMP is %d pixels wide.", int(width))); + ERR_FAIL_COND_V_MSG(height % 8 != 0, ERR_UNAVAILABLE, + vformat("1-bpp BMP images must have a height that is a multiple of 8, but the imported BMP is %d pixels tall.", int(height))); } else if (bits_per_pixel == 4) { // Requires bit unpacking... - ERR_FAIL_COND_V(width % 2 != 0, ERR_UNAVAILABLE); - ERR_FAIL_COND_V(height % 2 != 0, ERR_UNAVAILABLE); + ERR_FAIL_COND_V_MSG(width % 2 != 0, ERR_UNAVAILABLE, + vformat("4-bpp BMP images must have a width that is a multiple of 2, but the imported BMP is %d pixels wide.", int(width))); + ERR_FAIL_COND_V_MSG(height % 2 != 0, ERR_UNAVAILABLE, + vformat("4-bpp BMP images must have a height that is a multiple of 2, but the imported BMP is %d pixels tall.", int(height))); } else if (bits_per_pixel == 16) { - ERR_FAIL_V(ERR_UNAVAILABLE); + ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "16-bpp BMP images are not supported."); } // Image data (might be indexed) @@ -72,7 +76,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, } else { // color data_len = width * height * 4; } - ERR_FAIL_COND_V(data_len == 0, ERR_BUG); + ERR_FAIL_COND_V_MSG(data_len == 0, ERR_BUG, "Couldn't parse the BMP image data."); err = data.resize(data_len); uint8_t *data_w = data.ptrw(); @@ -215,13 +219,15 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f, // Info Header bmp_header.bmp_info_header.bmp_header_size = f->get_32(); - ERR_FAIL_COND_V(bmp_header.bmp_info_header.bmp_header_size < BITMAP_INFO_HEADER_MIN_SIZE, ERR_FILE_CORRUPT); + ERR_FAIL_COND_V_MSG(bmp_header.bmp_info_header.bmp_header_size < BITMAP_INFO_HEADER_MIN_SIZE, ERR_FILE_CORRUPT, + vformat("Couldn't parse the BMP info header. The file is likely corrupt: %s", f->get_path())); bmp_header.bmp_info_header.bmp_width = f->get_32(); bmp_header.bmp_info_header.bmp_height = f->get_32(); bmp_header.bmp_info_header.bmp_planes = f->get_16(); - ERR_FAIL_COND_V(bmp_header.bmp_info_header.bmp_planes != 1, ERR_FILE_CORRUPT); + ERR_FAIL_COND_V_MSG(bmp_header.bmp_info_header.bmp_planes != 1, ERR_FILE_CORRUPT, + vformat("Couldn't parse the BMP planes. The file is likely corrupt: %s", f->get_path())); bmp_header.bmp_info_header.bmp_bit_count = f->get_16(); bmp_header.bmp_info_header.bmp_compression = f->get_32(); @@ -236,10 +242,10 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f, case BI_RLE4: case BI_CMYKRLE8: case BI_CMYKRLE4: { - // Stop parsing - String bmp_path = f->get_path(); + // Stop parsing. f->close(); - ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Compressed BMP files are not supported: " + bmp_path + "."); + ERR_FAIL_V_MSG(ERR_UNAVAILABLE, + vformat("Compressed BMP files are not supported: %s", f->get_path())); } break; } // Don't rely on sizeof(bmp_file_header) as structure padding @@ -255,7 +261,8 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f, if (bmp_header.bmp_info_header.bmp_bit_count <= 8) { // Support 256 colors max color_table_size = 1 << bmp_header.bmp_info_header.bmp_bit_count; - ERR_FAIL_COND_V(color_table_size == 0, ERR_BUG); + ERR_FAIL_COND_V_MSG(color_table_size == 0, ERR_BUG, + vformat("Couldn't parse the BMP color table: %s", f->get_path())); } Vector<uint8_t> bmp_color_table; diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp index 6c0a3a4ca3..47982d519a 100644 --- a/modules/csg/csg.cpp +++ b/modules/csg/csg.cpp @@ -154,6 +154,14 @@ inline bool is_point_in_triangle(const Vector3 &p_point, const Vector3 p_vertice return true; } +inline static bool is_triangle_degenerate(const Vector2 p_vertices[3], real_t p_vertex_snap2) { + real_t det = p_vertices[0].x * p_vertices[1].y - p_vertices[0].x * p_vertices[2].y + + p_vertices[0].y * p_vertices[2].x - p_vertices[0].y * p_vertices[1].x + + p_vertices[1].x * p_vertices[2].y - p_vertices[1].y * p_vertices[2].x; + + return det < p_vertex_snap2; +} + inline static bool are_segements_parallel(const Vector2 p_segment1_points[2], const Vector2 p_segment2_points[2], float p_vertex_snap2) { Vector2 segment1 = p_segment1_points[1] - p_segment1_points[0]; Vector2 segment2 = p_segment2_points[1] - p_segment2_points[0]; @@ -583,8 +591,8 @@ bool CSGBrushOperation::MeshMerge::_bvh_inside(FaceBVH *facebvhptr, int p_max_de // Check if faces are co-planar. if ((current_normal - face_normal).length_squared() < CMP_EPSILON2 && is_point_in_triangle(face_center, current_points)) { - // Only add an intersection if checking a B face. - if (face.from_b) { + // Only add an intersection if not a B face. + if (!face.from_b) { _add_distance(intersectionsA, intersectionsB, current_face.from_b, 0); } } else if (ray_intersects_triangle(face_center, face_normal, current_points, CMP_EPSILON, intersection_point)) { @@ -1117,6 +1125,11 @@ int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) { face_vertices[2].uv }; + // Skip degenerate triangles. + if (is_triangle_degenerate(points, vertex_snap2)) { + continue; + } + // Check if point is existing face vertex. for (int i = 0; i < 3; ++i) { if ((p_point - face_vertices[i].point).length_squared() < vertex_snap2) { @@ -1198,11 +1211,8 @@ int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) { // The new vertex is the last vertex. for (int i = 0; i < 3; ++i) { // Don't create degenerate triangles. - Vector2 edge[2] = { points[i], points[(i + 1) % 3] }; - Vector2 new_edge1[2] = { vertices[new_vertex_idx].point, points[i] }; - Vector2 new_edge2[2] = { vertices[new_vertex_idx].point, points[(i + 1) % 3] }; - if (are_segements_parallel(edge, new_edge1, vertex_snap2) && - are_segements_parallel(edge, new_edge2, vertex_snap2)) { + Vector2 new_points[3] = { points[i], points[(i + 1) % 3], vertices[new_vertex_idx].point }; + if (is_triangle_degenerate(new_points, vertex_snap2)) { continue; } diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 82a47f594b..8f2ebc7232 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -506,6 +506,12 @@ void CSGShape3D::_notification(int p_what) { _make_dirty(); } + if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { + if (use_collision && is_root_shape() && root_collision_instance.is_valid()) { + PhysicsServer3D::get_singleton()->body_set_state(root_collision_instance, PhysicsServer3D::BODY_STATE_TRANSFORM, get_global_transform()); + } + } + if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { if (parent) { parent->_make_dirty(); @@ -641,7 +647,7 @@ CSGShape3D::~CSGShape3D() { ////////////////////////////////// CSGBrush *CSGCombiner3D::_build_brush() { - return nullptr; //does not build anything + return memnew(CSGBrush); //does not build anything } CSGCombiner3D::CSGCombiner3D() { diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index aba3e07134..ae1f2893f1 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -82,6 +82,10 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) const String &str = text_edit->get_line(p_line); const int line_length = str.length(); Color prev_color; + + if (in_region != -1 && str.length() == 0) { + color_region_cache[p_line] = in_region; + } for (int j = 0; j < str.length(); j++) { Dictionary highlighter_info; diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 9170255c02..541d46a2af 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -596,6 +596,21 @@ Error GDScript::reload(bool p_keep_state) { return OK; } + { + String source_path = path; + if (source_path.empty()) { + source_path = get_path(); + } + if (!source_path.empty()) { + MutexLock lock(GDScriptCache::singleton->lock); + Ref<GDScript> self(this); + if (!GDScriptCache::singleton->shallow_gdscript_cache.has(source_path)) { + GDScriptCache::singleton->shallow_gdscript_cache[source_path] = self; + self->unreference(); + } + } + } + valid = false; GDScriptParser parser; Error err = parser.parse(source, path, false); diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 531666bec5..2082339ba7 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -71,6 +71,10 @@ static StringName get_real_class_name(const StringName &p_source) { return p_source; } +void GDScriptAnalyzer::cleanup() { + underscore_map.clear(); +} + static GDScriptParser::DataType make_callable_type(const MethodInfo &p_info) { GDScriptParser::DataType type; type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; @@ -494,7 +498,13 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas if (member.variable->initializer != nullptr) { if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true)) { - push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer); + // Try reverse test since it can be a masked subtype. + if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true)) { + push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer); + } else { + // TODO: Add warning. + mark_node_unsafe(member.variable->initializer); + } } else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) { #ifdef DEBUG_ENABLED parser->push_warning(member.variable->initializer, GDScriptWarning::NARROWING_CONVERSION); @@ -592,17 +602,67 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas enum_type.is_meta_type = true; enum_type.is_constant = true; + // Enums can't be nested, so we can safely override this. + current_enum = member.m_enum; + for (int j = 0; j < member.m_enum->values.size(); j++) { - enum_type.enum_values[member.m_enum->values[j].identifier->name] = member.m_enum->values[j].value; + GDScriptParser::EnumNode::Value &element = member.m_enum->values.write[j]; + + if (element.custom_value) { + reduce_expression(element.custom_value); + if (!element.custom_value->is_constant) { + push_error(R"(Enum values must be constant.)", element.custom_value); + } else if (element.custom_value->reduced_value.get_type() != Variant::INT) { + push_error(R"(Enum values must be integers.)", element.custom_value); + } else { + element.value = element.custom_value->reduced_value; + element.resolved = true; + } + } else { + if (element.index > 0) { + element.value = element.parent_enum->values[element.index - 1].value + 1; + } else { + element.value = 0; + } + element.resolved = true; + } + + enum_type.enum_values[element.identifier->name] = element.value; } + current_enum = nullptr; + member.m_enum->set_datatype(enum_type); } break; case GDScriptParser::ClassNode::Member::FUNCTION: resolve_function_signature(member.function); break; - case GDScriptParser::ClassNode::Member::ENUM_VALUE: - break; // Nothing to do, type and value set in parser. + case GDScriptParser::ClassNode::Member::ENUM_VALUE: { + if (member.enum_value.custom_value) { + current_enum = member.enum_value.parent_enum; + reduce_expression(member.enum_value.custom_value); + current_enum = nullptr; + + if (!member.enum_value.custom_value->is_constant) { + push_error(R"(Enum values must be constant.)", member.enum_value.custom_value); + } else if (member.enum_value.custom_value->reduced_value.get_type() != Variant::INT) { + push_error(R"(Enum values must be integers.)", member.enum_value.custom_value); + } else { + member.enum_value.value = member.enum_value.custom_value->reduced_value; + member.enum_value.resolved = true; + } + } else { + if (member.enum_value.index > 0) { + member.enum_value.value = member.enum_value.parent_enum->values[member.enum_value.index - 1].value + 1; + } else { + member.enum_value.value = 0; + } + member.enum_value.resolved = true; + } + // Also update the original references. + member.enum_value.parent_enum->values.write[member.enum_value.index] = member.enum_value; + p_class->members.write[i].enum_value = member.enum_value; + } break; case GDScriptParser::ClassNode::Member::CLASS: break; // Done later. case GDScriptParser::ClassNode::Member::UNDEFINED: @@ -872,7 +932,8 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { // Use int, Vector2, Vector3 instead, which also can be used as range iterators. if (p_for->list && p_for->list->type == GDScriptParser::Node::CALL) { GDScriptParser::CallNode *call = static_cast<GDScriptParser::CallNode *>(p_for->list); - if (call->callee->type == GDScriptParser::Node::IDENTIFIER) { + GDScriptParser::Node::Type callee_type = call->get_callee_type(); + if (callee_type == GDScriptParser::Node::IDENTIFIER) { GDScriptParser::IdentifierNode *callee = static_cast<GDScriptParser::IdentifierNode *>(call->callee); if (callee->name == "range") { list_resolved = true; @@ -989,7 +1050,13 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable if (p_variable->initializer != nullptr) { if (!is_type_compatible(type, p_variable->initializer->get_datatype(), true)) { - push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", p_variable->initializer->get_datatype().to_string(), type.to_string()), p_variable->initializer); + // Try reverse test since it can be a masked subtype. + if (!is_type_compatible(p_variable->initializer->get_datatype(), type, true)) { + push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", p_variable->initializer->get_datatype().to_string(), type.to_string()), p_variable->initializer); + } else { + // TODO: Add warning. + mark_node_unsafe(p_variable->initializer); + } #ifdef DEBUG_ENABLED } else if (type.builtin_type == Variant::INT && p_variable->initializer->get_datatype().builtin_type == Variant::FLOAT) { parser->push_warning(p_variable->initializer, GDScriptWarning::NARROWING_CONVERSION); @@ -1369,60 +1436,32 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig push_error("Cannot assign a new value to a constant.", p_assignment->assignee); } - Variant::Operator vop = Variant::Operator::OP_EQUAL; - switch (p_assignment->operation) { - case GDScriptParser::AssignmentNode::OP_NONE: - vop = Variant::Operator::OP_EQUAL; - break; - case GDScriptParser::AssignmentNode::OP_ADDITION: - vop = Variant::Operator::OP_ADD; - break; - case GDScriptParser::AssignmentNode::OP_SUBTRACTION: - vop = Variant::Operator::OP_SUBTRACT; - break; - case GDScriptParser::AssignmentNode::OP_MULTIPLICATION: - vop = Variant::Operator::OP_MULTIPLY; - break; - case GDScriptParser::AssignmentNode::OP_DIVISION: - vop = Variant::Operator::OP_DIVIDE; - break; - case GDScriptParser::AssignmentNode::OP_MODULO: - vop = Variant::Operator::OP_MODULE; - break; - case GDScriptParser::AssignmentNode::OP_BIT_SHIFT_LEFT: - vop = Variant::Operator::OP_SHIFT_LEFT; - break; - case GDScriptParser::AssignmentNode::OP_BIT_SHIFT_RIGHT: - vop = Variant::Operator::OP_SHIFT_RIGHT; - break; - case GDScriptParser::AssignmentNode::OP_BIT_AND: - vop = Variant::Operator::OP_BIT_AND; - break; - case GDScriptParser::AssignmentNode::OP_BIT_OR: - vop = Variant::Operator::OP_BIT_OR; - break; - case GDScriptParser::AssignmentNode::OP_BIT_XOR: - vop = Variant::Operator::OP_BIT_XOR; - break; - } - - bool compatible = true; - GDScriptParser::DataType op_type = p_assignment->assigned_value->get_datatype(); - if (vop != Variant::OP_EQUAL) { - op_type = get_operation_type(vop, p_assignment->assignee->get_datatype(), p_assignment->assigned_value->get_datatype(), compatible); - } + if (!p_assignment->assignee->get_datatype().is_variant() && !p_assignment->assigned_value->get_datatype().is_variant()) { + bool compatible = true; + GDScriptParser::DataType op_type = p_assignment->assigned_value->get_datatype(); + if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) { + op_type = get_operation_type(p_assignment->variant_op, p_assignment->assignee->get_datatype(), p_assignment->assigned_value->get_datatype(), compatible); + } - if (compatible) { - compatible = is_type_compatible(p_assignment->assignee->get_datatype(), op_type, true); - if (!compatible) { - if (p_assignment->assignee->get_datatype().is_hard_type()) { - push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", p_assignment->assigned_value->get_datatype().to_string(), p_assignment->assignee->get_datatype().to_string()), p_assignment->assigned_value); - } else { - // TODO: Warning in this case. + if (compatible) { + compatible = is_type_compatible(p_assignment->assignee->get_datatype(), op_type, true); + if (!compatible) { + if (p_assignment->assignee->get_datatype().is_hard_type()) { + // Try reverse test since it can be a masked subtype. + if (!is_type_compatible(op_type, p_assignment->assignee->get_datatype(), true)) { + push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", p_assignment->assigned_value->get_datatype().to_string(), p_assignment->assignee->get_datatype().to_string()), p_assignment->assigned_value); + } else { + // TODO: Add warning. + mark_node_unsafe(p_assignment); + } + } else { + // TODO: Warning in this case. + mark_node_unsafe(p_assignment); + } } + } else { + push_error(vformat(R"(Invalid operands "%s" and "%s" for assignment operator.)", p_assignment->assignee->get_datatype().to_string(), p_assignment->assigned_value->get_datatype().to_string()), p_assignment); } - } else { - push_error(vformat(R"(Invalid operands "%s" and "%s" for assignment operator.)", p_assignment->assignee->get_datatype().to_string(), p_assignment->assigned_value->get_datatype().to_string()), p_assignment); } if (p_assignment->assignee->get_datatype().has_no_type() || p_assignment->assigned_value->get_datatype().is_variant()) { @@ -1536,7 +1575,19 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o if (p_binary_op->left_operand->is_constant && p_binary_op->right_operand->is_constant) { p_binary_op->is_constant = true; if (p_binary_op->variant_op < Variant::OP_MAX) { - p_binary_op->reduced_value = Variant::evaluate(p_binary_op->variant_op, p_binary_op->left_operand->reduced_value, p_binary_op->right_operand->reduced_value); + bool valid = false; + Variant::evaluate(p_binary_op->variant_op, p_binary_op->left_operand->reduced_value, p_binary_op->right_operand->reduced_value, p_binary_op->reduced_value, valid); + if (!valid) { + if (p_binary_op->reduced_value.get_type() == Variant::STRING) { + push_error(vformat(R"(%s in operator %s.)", p_binary_op->reduced_value, Variant::get_operator_name(p_binary_op->variant_op)), p_binary_op); + } else { + push_error(vformat(R"(Invalid operands to operator %s, %s and %s.".)", + Variant::get_operator_name(p_binary_op->variant_op), + Variant::get_type_name(p_binary_op->left_operand->reduced_value.get_type()), + Variant::get_type_name(p_binary_op->right_operand->reduced_value.get_type())), + p_binary_op); + } + } } else { if (p_binary_op->operation == GDScriptParser::BinaryOpNode::OP_TYPE_TEST) { GDScriptParser::DataType test_type = right_type; @@ -1608,9 +1659,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa all_is_constant = all_is_constant && p_call->arguments[i]->is_constant; } + GDScriptParser::Node::Type callee_type = p_call->get_callee_type(); GDScriptParser::DataType call_type; - if (!p_call->is_super && p_call->callee->type == GDScriptParser::Node::IDENTIFIER) { + if (!p_call->is_super && callee_type == GDScriptParser::Node::IDENTIFIER) { // Call to name directly. StringName function_name = p_call->function_name; Variant::Type builtin_type = GDScriptParser::get_builtin_type(function_name); @@ -1651,6 +1703,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa } signature += p_call->arguments[i]->get_datatype().to_string(); } + signature += ")"; push_error(vformat(R"(No constructor of "%s" matches the signature "%s".)", Variant::get_type_name(builtin_type), signature), p_call->callee); } break; case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: @@ -1701,7 +1754,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa for (int i = 0; i < p_call->arguments.size(); i++) { GDScriptParser::DataType par_type = type_from_property(info.arguments[i]); - if (!is_type_compatible(par_type, p_call->arguments[i]->get_datatype())) { + if (!is_type_compatible(par_type, p_call->arguments[i]->get_datatype(), true)) { types_match = false; break; #ifdef DEBUG_ENABLED @@ -1728,6 +1781,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa } signature += p_call->arguments[i]->get_datatype().to_string(); } + signature += ")"; push_error(vformat(R"(No constructor of "%s" matches the signature "%s".)", Variant::get_type_name(builtin_type), signature), p_call); } } @@ -1785,10 +1839,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa if (p_call->is_super) { base_type = parser->current_class->base_type; is_self = true; - } else if (p_call->callee->type == GDScriptParser::Node::IDENTIFIER) { + } else if (callee_type == GDScriptParser::Node::IDENTIFIER) { base_type = parser->current_class->get_datatype(); is_self = true; - } else if (p_call->callee->type == GDScriptParser::Node::SUBSCRIPT) { + } else if (callee_type == GDScriptParser::Node::SUBSCRIPT) { GDScriptParser::SubscriptNode *subscript = static_cast<GDScriptParser::SubscriptNode *>(p_call->callee); if (!subscript->is_attribute) { // Invalid call. Error already sent in parser. @@ -1825,9 +1879,9 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa } else { // Check if the name exists as something else. bool found = false; - if (!p_call->is_super) { + if (!p_call->is_super && callee_type != GDScriptParser::Node::NONE) { GDScriptParser::IdentifierNode *callee_id; - if (p_call->callee->type == GDScriptParser::Node::IDENTIFIER) { + if (callee_type == GDScriptParser::Node::IDENTIFIER) { callee_id = static_cast<GDScriptParser::IdentifierNode *>(p_call->callee); } else { // Can only be attribute. @@ -1835,13 +1889,13 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa } if (callee_id) { reduce_identifier_from_base(callee_id, &base_type); - GDScriptParser::DataType callee_type = callee_id->get_datatype(); - if (callee_type.is_set() && !callee_type.is_variant()) { + GDScriptParser::DataType callee_datatype = callee_id->get_datatype(); + if (callee_datatype.is_set() && !callee_datatype.is_variant()) { found = true; - if (callee_type.builtin_type == Variant::CALLABLE) { + if (callee_datatype.builtin_type == Variant::CALLABLE) { push_error(vformat(R"*(Name "%s" is a Callable. You can call it with "%s.call()" instead.)*", p_call->function_name, p_call->function_name), p_call->callee); } else { - push_error(vformat(R"*(Name "%s" called as a function but is a "%s".)*", p_call->function_name, callee_type.to_string()), p_call->callee); + push_error(vformat(R"*(Name "%s" called as a function but is a "%s".)*", p_call->function_name, callee_datatype.to_string()), p_call->callee); } #ifdef DEBUG_ENABLED } else if (!is_self) { @@ -2121,6 +2175,33 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_identifier, bool can_be_builtin) { // TODO: This is opportunity to further infer types. + + // Check if we are inside and enum. This allows enum values to access other elements of the same enum. + if (current_enum) { + for (int i = 0; i < current_enum->values.size(); i++) { + const GDScriptParser::EnumNode::Value &element = current_enum->values[i]; + if (element.identifier->name == p_identifier->name) { + GDScriptParser::DataType type; + type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; + type.kind = element.parent_enum->identifier ? GDScriptParser::DataType::ENUM_VALUE : GDScriptParser::DataType::BUILTIN; + type.builtin_type = Variant::INT; + type.is_constant = true; + if (element.parent_enum->identifier) { + type.enum_type = element.parent_enum->identifier->name; + } + p_identifier->set_datatype(type); + + if (element.resolved) { + p_identifier->is_constant = true; + p_identifier->reduced_value = element.value; + } else { + push_error(R"(Cannot use another enum element before it was declared.)", p_identifier); + } + return; // Found anyway. + } + } + } + // Check if identifier is local. // If that's the case, the declaration already was solved before. switch (p_identifier->source) { @@ -2526,6 +2607,12 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op) GDScriptParser::DataType result; + if (p_unary_op->operand == nullptr) { + result.kind = GDScriptParser::DataType::VARIANT; + p_unary_op->set_datatype(result); + return; + } + if (p_unary_op->operand->is_constant) { p_unary_op->is_constant = true; p_unary_op->reduced_value = Variant::evaluate(p_unary_op->variant_op, p_unary_op->operand->reduced_value, Variant()); @@ -2900,7 +2987,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator } // Avoid error in formatting operator (%) where it doesn't find a placeholder. - if (a_type == Variant::STRING) { + if (a_type == Variant::STRING && b_type != Variant::ARRAY) { a = String("%s"); } diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 85183d3272..da767522ad 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -41,6 +41,8 @@ class GDScriptAnalyzer { GDScriptParser *parser = nullptr; HashMap<String, Ref<GDScriptParserRef>> depended_parsers; + const GDScriptParser::EnumNode *current_enum = nullptr; + Error resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive = true); GDScriptParser::DataType resolve_datatype(GDScriptParser::TypeNode *p_type); @@ -113,6 +115,8 @@ public: Error analyze(); GDScriptAnalyzer(GDScriptParser *p_parser); + + static void cleanup(); }; #endif // GDSCRIPT_ANALYZER_H diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index 583283ff46..cdb14d6281 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -85,6 +85,17 @@ Error GDScriptParserRef::raise_status(Status p_new_status) { return result; } } + if (result != OK) { + if (parser != nullptr) { + memdelete(parser); + parser = nullptr; + } + if (analyzer != nullptr) { + memdelete(analyzer); + analyzer = nullptr; + } + return result; + } } return result; @@ -118,6 +129,10 @@ Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptP if (singleton->parser_map.has(p_path)) { ref = singleton->parser_map[p_path]; } else { + if (!FileAccess::exists(p_path)) { + r_error = ERR_FILE_NOT_FOUND; + return ref; + } GDScriptParser *parser = memnew(GDScriptParser); ref.instance(); ref->parser = parser; diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 239015060e..88bf8cde0f 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1259,8 +1259,10 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, GDScriptParser::CompletionContext c = p_context; c.current_line = call->start_line; + GDScriptParser::Node::Type callee_type = call->get_callee_type(); + GDScriptCompletionIdentifier base; - if (call->callee->type == GDScriptParser::Node::IDENTIFIER || call->is_super) { + if (callee_type == GDScriptParser::Node::IDENTIFIER || call->is_super) { // Simple call, so base is 'self'. if (p_context.current_class) { base.type.kind = GDScriptParser::DataType::CLASS; @@ -1271,7 +1273,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, } else { break; } - } else if (call->callee->type == GDScriptParser::Node::SUBSCRIPT && static_cast<const GDScriptParser::SubscriptNode *>(call->callee)->is_attribute) { + } else if (callee_type == GDScriptParser::Node::SUBSCRIPT && static_cast<const GDScriptParser::SubscriptNode *>(call->callee)->is_attribute) { if (!_guess_expression_type(c, static_cast<const GDScriptParser::SubscriptNode *>(call->callee)->base, base)) { found = false; break; @@ -2290,6 +2292,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c GDScriptParser::DataType base_type; bool _static = false; const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_call); + GDScriptParser::Node::Type callee_type = GDScriptParser::Node::NONE; GDScriptCompletionIdentifier connect_base; @@ -2319,14 +2322,14 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c i++; } return; - } else if (call->is_super || call->callee->type == GDScriptParser::Node::IDENTIFIER) { + } else if (call->is_super || callee_type == GDScriptParser::Node::IDENTIFIER) { base = p_context.base; if (p_context.current_class) { base_type = p_context.current_class->get_datatype(); _static = !p_context.current_function || p_context.current_function->is_static; } - } else if (call->callee->type == GDScriptParser::Node::SUBSCRIPT) { + } else if (callee_type == GDScriptParser::Node::SUBSCRIPT) { const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee); if (subscript->is_attribute) { @@ -2762,6 +2765,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co if (base_type.class_type->has_member(p_symbol)) { r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION; r_result.location = base_type.class_type->get_member(p_symbol).get_line(); + return OK; } base_type = base_type.class_type->base_type; } diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index af07457750..e53a1090fe 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -94,6 +94,10 @@ Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) { return Variant::VARIANT_MAX; } +void GDScriptParser::cleanup() { + builtin_types.clear(); +} + GDScriptFunctions::Function GDScriptParser::get_builtin_function(const StringName &p_name) { for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) { if (p_name == GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))) { @@ -462,44 +466,40 @@ void GDScriptParser::pop_multiline() { } bool GDScriptParser::is_statement_end() { - return check(GDScriptTokenizer::Token::NEWLINE) || check(GDScriptTokenizer::Token::SEMICOLON); + return check(GDScriptTokenizer::Token::NEWLINE) || check(GDScriptTokenizer::Token::SEMICOLON) || check(GDScriptTokenizer::Token::TK_EOF); } void GDScriptParser::end_statement(const String &p_context) { bool found = false; - while (is_statement_end()) { + while (is_statement_end() && !is_at_end()) { // Remove sequential newlines/semicolons. found = true; advance(); } - if (!found) { + if (!found && !is_at_end()) { push_error(vformat(R"(Expected end of statement after %s, found "%s" instead.)", p_context, current.get_name())); } } void GDScriptParser::parse_program() { - if (current.type == GDScriptTokenizer::Token::TK_EOF) { - // Empty file. - push_error("Source file is empty."); - return; - } - head = alloc_node<ClassNode>(); current_class = head; if (match(GDScriptTokenizer::Token::ANNOTATION)) { // Check for @tool annotation. AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::CLASS_LEVEL); - if (annotation->name == "@tool") { - // TODO: don't allow @tool anywhere else. (Should all script annotations be the first thing?). - _is_tool = true; - if (previous.type != GDScriptTokenizer::Token::NEWLINE) { - push_error(R"(Expected newline after "@tool" annotation.)"); - } - // @tool annotation has no specific target. - annotation->apply(this, nullptr); - } else { - annotation_stack.push_back(annotation); + if (annotation != nullptr) { + if (annotation->name == "@tool") { + // TODO: don't allow @tool anywhere else. (Should all script annotations be the first thing?). + _is_tool = true; + if (previous.type != GDScriptTokenizer::Token::NEWLINE) { + push_error(R"(Expected newline after "@tool" annotation.)"); + } + // @tool annotation has no specific target. + annotation->apply(this, nullptr); + } else { + annotation_stack.push_back(annotation); + } } } @@ -1022,8 +1022,6 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() { push_multiline(true); consume(GDScriptTokenizer::Token::BRACE_OPEN, vformat(R"(Expected "{" after %s.)", named ? "enum name" : R"("enum")")); - int current_value = 0; - do { if (check(GDScriptTokenizer::Token::BRACE_CLOSE)) { break; // Allow trailing comma. @@ -1031,6 +1029,9 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() { if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifer for enum key.)")) { EnumNode::Value item; item.identifier = parse_identifier(); + item.parent_enum = enum_node; + item.line = previous.start_line; + item.leftmost_column = previous.leftmost_column; if (!named) { // TODO: Abstract this recursive member check. @@ -1045,18 +1046,15 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() { } if (match(GDScriptTokenizer::Token::EQUAL)) { - if (consume(GDScriptTokenizer::Token::LITERAL, R"(Expected integer value after "=".)")) { - item.custom_value = parse_literal(); - - if (item.custom_value->value.get_type() != Variant::INT) { - push_error(R"(Expected integer value after "=".)"); - item.custom_value = nullptr; - } else { - current_value = item.custom_value->value; - } + ExpressionNode *value = parse_expression(false); + if (value == nullptr) { + push_error(R"(Expected expression value after "=".)"); } + item.custom_value = value; } - item.value = current_value++; + item.rightmost_column = previous.rightmost_column; + + item.index = enum_node->values.size(); enum_node->values.push_back(item); if (!named) { // Add as member of current class. @@ -2005,7 +2003,6 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(Expression push_error(vformat(R"(Expected expression after "%s" operator.")", op.get_name())); } - // TODO: Store the Variant operator here too (in the node). // TODO: Also for unary, ternary, and assignment. switch (op.type) { case GDScriptTokenizer::Token::PLUS: @@ -2167,39 +2164,50 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode switch (previous.type) { case GDScriptTokenizer::Token::EQUAL: assignment->operation = AssignmentNode::OP_NONE; + assignment->variant_op = Variant::OP_MAX; #ifdef DEBUG_ENABLED has_operator = false; #endif break; case GDScriptTokenizer::Token::PLUS_EQUAL: assignment->operation = AssignmentNode::OP_ADDITION; + assignment->variant_op = Variant::OP_ADD; break; case GDScriptTokenizer::Token::MINUS_EQUAL: assignment->operation = AssignmentNode::OP_SUBTRACTION; + assignment->variant_op = Variant::OP_SUBTRACT; break; case GDScriptTokenizer::Token::STAR_EQUAL: assignment->operation = AssignmentNode::OP_MULTIPLICATION; + assignment->variant_op = Variant::OP_MULTIPLY; break; case GDScriptTokenizer::Token::SLASH_EQUAL: assignment->operation = AssignmentNode::OP_DIVISION; + assignment->variant_op = Variant::OP_DIVIDE; break; case GDScriptTokenizer::Token::PERCENT_EQUAL: assignment->operation = AssignmentNode::OP_MODULO; + assignment->variant_op = Variant::OP_MODULE; break; case GDScriptTokenizer::Token::LESS_LESS_EQUAL: assignment->operation = AssignmentNode::OP_BIT_SHIFT_LEFT; + assignment->variant_op = Variant::OP_SHIFT_LEFT; break; case GDScriptTokenizer::Token::GREATER_GREATER_EQUAL: assignment->operation = AssignmentNode::OP_BIT_SHIFT_RIGHT; + assignment->variant_op = Variant::OP_SHIFT_RIGHT; break; case GDScriptTokenizer::Token::AMPERSAND_EQUAL: assignment->operation = AssignmentNode::OP_BIT_AND; + assignment->variant_op = Variant::OP_BIT_AND; break; case GDScriptTokenizer::Token::PIPE_EQUAL: assignment->operation = AssignmentNode::OP_BIT_OR; + assignment->variant_op = Variant::OP_BIT_OR; break; case GDScriptTokenizer::Token::CARET_EQUAL: assignment->operation = AssignmentNode::OP_BIT_XOR; + assignment->variant_op = Variant::OP_BIT_XOR; break; default: break; // Unreachable. @@ -2328,7 +2336,11 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_dictionary(ExpressionNode GDScriptParser::ExpressionNode *GDScriptParser::parse_grouping(ExpressionNode *p_previous_operand, bool p_can_assign) { ExpressionNode *grouped = parse_expression(false); pop_multiline(); - consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after grouping expression.)*"); + if (grouped == nullptr) { + push_error(R"(Expected grouping expression.)"); + } else { + consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after grouping expression.)*"); + } return grouped; } @@ -2419,7 +2431,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre } else { call->callee = p_previous_operand; - if (call->callee->type == Node::IDENTIFIER) { + if (call->callee == nullptr) { + push_error(R"*(Cannot call on an expression. Use ".call()" if it's a Callable.)*"); + } else if (call->callee->type == Node::IDENTIFIER) { call->function_name = static_cast<IdentifierNode *>(call->callee)->name; make_completion_context(COMPLETION_METHOD, call->callee); } else if (call->callee->type == Node::SUBSCRIPT) { diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index a741ae0cc7..7d8ae7fc55 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -383,6 +383,14 @@ public: CallNode() { type = CALL; } + + Type get_callee_type() const { + if (callee == nullptr) { + return Type::NONE; + } else { + return callee->type; + } + } }; struct CastNode : public ExpressionNode { @@ -397,7 +405,10 @@ public: struct EnumNode : public Node { struct Value { IdentifierNode *identifier = nullptr; - LiteralNode *custom_value = nullptr; + ExpressionNode *custom_value = nullptr; + EnumNode *parent_enum = nullptr; + int index = -1; + bool resolved = false; int value = 0; int line = 0; int leftmost_column = 0; @@ -1335,6 +1346,7 @@ public: void print_tree(const GDScriptParser &p_parser); }; #endif // DEBUG_ENABLED + static void cleanup(); }; #endif // GDSCRIPT_PARSER_H diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index 7a4bdd88ba..8ced06e2fd 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -156,6 +156,18 @@ const char *GDScriptTokenizer::Token::get_name() const { return token_names[type]; } +bool GDScriptTokenizer::Token::is_identifier() const { + // Note: Most keywords should not be recognized as identifiers. + // These are only exceptions for stuff that already is on the engine's API. + switch (type) { + case IDENTIFIER: + case MATCH: // Used in String.match(). + return true; + default: + return false; + } +} + String GDScriptTokenizer::get_token_name(Token::Type p_token_type) { ERR_FAIL_INDEX_V_MSG(p_token_type, Token::TK_MAX, "<error>", "Using token type out of the enum."); return token_names[p_token_type]; diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index 059a226924..956530341c 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -168,9 +168,8 @@ public: String source; const char *get_name() const; - // TODO: Allow some keywords as identifiers? - bool is_identifier() const { return type == IDENTIFIER; } - StringName get_identifier() const { return literal; } + bool is_identifier() const; + StringName get_identifier() const { return source; } Token(Type p_type) { type = p_type; diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index ae7898fdf2..4d79d9d395 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -237,7 +237,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p case ClassNode::Member::ENUM_VALUE: { lsp::DocumentSymbol symbol; - symbol.name = m.constant->identifier->name; + symbol.name = m.enum_value.identifier->name; symbol.kind = lsp::SymbolKind::EnumMember; symbol.deprecated = false; symbol.range.start.line = LINE_NUMBER_TO_INDEX(m.enum_value.line); diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index 23c7f97b5a..c554cbac05 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -35,6 +35,7 @@ #include "core/os/dir_access.h" #include "core/os/file_access.h" #include "gdscript.h" +#include "gdscript_analyzer.h" #include "gdscript_cache.h" #include "gdscript_tokenizer.h" @@ -148,4 +149,7 @@ void unregister_gdscript_types() { EditorTranslationParser::get_singleton()->remove_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD); gdscript_translation_parser_plugin.unref(); #endif // TOOLS_ENABLED + + GDScriptParser::cleanup(); + GDScriptAnalyzer::cleanup(); } diff --git a/modules/mobile_vr/register_types.cpp b/modules/mobile_vr/register_types.cpp index 75638d47c4..0bb555e780 100644 --- a/modules/mobile_vr/register_types.cpp +++ b/modules/mobile_vr/register_types.cpp @@ -35,9 +35,11 @@ void register_mobile_vr_types() { ClassDB::register_class<MobileVRInterface>(); - Ref<MobileVRInterface> mobile_vr; - mobile_vr.instance(); - XRServer::get_singleton()->add_interface(mobile_vr); + if (XRServer::get_singleton()) { + Ref<MobileVRInterface> mobile_vr; + mobile_vr.instance(); + XRServer::get_singleton()->add_interface(mobile_vr); + } } void unregister_mobile_vr_types() { diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs index 6399991b84..ff7ce97c47 100644 --- a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs @@ -205,23 +205,8 @@ namespace GodotTools if (File.Exists(editorScriptsMetadataPath)) File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath); - var currentPlayRequest = GodotSharpEditor.Instance.CurrentPlaySettings; - - if (currentPlayRequest != null) - { - if (currentPlayRequest.Value.HasDebugger) - { - // Set the environment variable that will tell the player to connect to the IDE debugger - // TODO: We should probably add a better way to do this - Environment.SetEnvironmentVariable("GODOT_MONO_DEBUGGER_AGENT", - "--debugger-agent=transport=dt_socket" + - $",address={currentPlayRequest.Value.DebuggerHost}:{currentPlayRequest.Value.DebuggerPort}" + - ",server=n"); - } - - if (!currentPlayRequest.Value.BuildBeforePlaying) - return true; // Requested play from an external editor/IDE which already built the project - } + if (GodotSharpEditor.Instance.SkipBuildBeforePlaying) + return true; // Requested play from an external editor/IDE which already built the project return BuildProjectBlocking("Debug"); } diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index a363ecc920..e2f7e87388 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -38,7 +38,7 @@ namespace GodotTools public BottomPanel BottomPanel { get; private set; } - public PlaySettings? CurrentPlaySettings { get; set; } + public bool SkipBuildBeforePlaying { get; set; } = false; public static string ProjectAssemblyName { diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs index 17f3339560..eb34a2d0f7 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs @@ -330,9 +330,10 @@ namespace GodotTools.Ides { DispatchToMainThread(() => { - GodotSharpEditor.Instance.CurrentPlaySettings = new PlaySettings(); + // TODO: Add BuildBeforePlaying flag to PlayRequest + + // Run the game Internal.EditorRunPlay(); - GodotSharpEditor.Instance.CurrentPlaySettings = null; }); return Task.FromResult<Response>(new PlayResponse()); } @@ -341,10 +342,22 @@ namespace GodotTools.Ides { DispatchToMainThread(() => { - GodotSharpEditor.Instance.CurrentPlaySettings = - new PlaySettings(request.DebuggerHost, request.DebuggerPort, request.BuildBeforePlaying ?? true); + // Tell the build callback whether the editor already built the solution or not + GodotSharpEditor.Instance.SkipBuildBeforePlaying = !(request.BuildBeforePlaying ?? true); + + // Pass the debugger agent settings to the player via an environment variables + // TODO: It would be better if this was an argument in EditorRunPlay instead + Environment.SetEnvironmentVariable("GODOT_MONO_DEBUGGER_AGENT", + "--debugger-agent=transport=dt_socket" + + $",address={request.DebuggerHost}:{request.DebuggerPort}" + + ",server=n"); + + // Run the game Internal.EditorRunPlay(); - GodotSharpEditor.Instance.CurrentPlaySettings = null; + + // Restore normal settings + Environment.SetEnvironmentVariable("GODOT_MONO_DEBUGGER_AGENT", ""); + GodotSharpEditor.Instance.SkipBuildBeforePlaying = false; }); return Task.FromResult<Response>(new DebugPlayResponse()); } |