diff options
37 files changed, 964 insertions, 225 deletions
diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp index 54abc1b7f2..b53dc05a00 100644 --- a/core/math/vector2.cpp +++ b/core/math/vector2.cpp @@ -34,6 +34,10 @@ real_t Vector2::angle() const { return Math::atan2(y, x); } +Vector2 Vector2::from_angle(const real_t p_angle) { + return Vector2(Math::cos(p_angle), Math::sin(p_angle)); +} + real_t Vector2::length() const { return Math::sqrt(x * x + y * y); } diff --git a/core/math/vector2.h b/core/math/vector2.h index 330b4741b1..332c0475fa 100644 --- a/core/math/vector2.h +++ b/core/math/vector2.h @@ -147,6 +147,7 @@ struct Vector2 { bool operator>=(const Vector2 &p_vec2) const { return x == p_vec2.x ? (y >= p_vec2.y) : (x > p_vec2.x); } real_t angle() const; + static Vector2 from_angle(const real_t p_angle); _FORCE_INLINE_ Vector2 abs() const { return Vector2(Math::abs(x), Math::abs(y)); diff --git a/core/templates/vector.h b/core/templates/vector.h index 08cbef6ba4..033345d04c 100644 --- a/core/templates/vector.h +++ b/core/templates/vector.h @@ -229,7 +229,7 @@ public: _FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return elem_ptr == b.elem_ptr; } _FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return elem_ptr != b.elem_ptr; } - ConstIterator(T *p_ptr) { elem_ptr = p_ptr; } + ConstIterator(const T *p_ptr) { elem_ptr = p_ptr; } ConstIterator() {} ConstIterator(const ConstIterator &p_it) { elem_ptr = p_it.elem_ptr; } diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index c9c7eca40d..39207df9e7 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1502,6 +1502,8 @@ static void _register_variant_builtin_methods() { bind_method(Vector2, clamp, sarray("min", "max"), varray()); bind_method(Vector2, snapped, sarray("step"), varray()); + bind_static_method(Vector2, from_angle, sarray("angle"), varray()); + /* Vector2i */ bind_method(Vector2i, aspect, sarray(), varray()); diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index f5cb2d40d6..232054d0ca 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -267,14 +267,6 @@ struct VariantUtilityFunctions { return Math::db2linear(db); } - static inline Vector2 polar2cartesian(double r, double th) { - return Vector2(r * Math::cos(th), r * Math::sin(th)); - } - - static inline Vector2 cartesian2polar(double x, double y) { - return Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x)); - } - static inline int64_t wrapi(int64_t value, int64_t min, int64_t max) { return Math::wrapi(value, min, max); } @@ -1214,9 +1206,6 @@ void Variant::_register_variant_utility_functions() { FUNCBINDR(linear2db, sarray("lin"), Variant::UTILITY_FUNC_TYPE_MATH); FUNCBINDR(db2linear, sarray("db"), Variant::UTILITY_FUNC_TYPE_MATH); - FUNCBINDR(polar2cartesian, sarray("r", "th"), Variant::UTILITY_FUNC_TYPE_MATH); - FUNCBINDR(cartesian2polar, sarray("x", "y"), Variant::UTILITY_FUNC_TYPE_MATH); - FUNCBINDR(wrapi, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH); FUNCBINDR(wrapf, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH); diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 585b94a522..616d88c7f3 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -99,14 +99,6 @@ [b]Warning:[/b] Deserialized object can contain code which gets executed. Do not use this option if the serialized object comes from untrusted sources to avoid potential security threats (remote code execution). </description> </method> - <method name="cartesian2polar"> - <return type="Vector2" /> - <argument index="0" name="x" type="float" /> - <argument index="1" name="y" type="float" /> - <description> - Converts a 2D point expressed in the cartesian coordinate system (X and Y axis) to the polar coordinate system (a distance from the origin and an angle). - </description> - </method> <method name="ceil"> <return type="float" /> <argument index="0" name="x" type="float" /> @@ -521,14 +513,6 @@ [b]Warning:[/b] Due to the way it is implemented, this function returns [code]0[/code] rather than [code]1[/code] for non-positive values of [code]value[/code] (in reality, 1 is the smallest integer power of 2). </description> </method> - <method name="polar2cartesian"> - <return type="Vector2" /> - <argument index="0" name="r" type="float" /> - <argument index="1" name="th" type="float" /> - <description> - Converts a 2D point expressed in the polar coordinate system (a distance from the origin [code]r[/code] and an angle [code]th[/code]) to the cartesian coordinate system (X and Y axis). - </description> - </method> <method name="posmod"> <return type="int" /> <argument index="0" name="x" type="int" /> @@ -2474,6 +2458,7 @@ <constant name="METHOD_FLAG_STATIC" value="256" enum="MethodFlags"> </constant> <constant name="METHOD_FLAG_OBJECT_CORE" value="512" enum="MethodFlags"> + Used internally. Allows to not dump core virtuals such as [code]_notification[/code] to the JSON API. </constant> <constant name="METHOD_FLAGS_DEFAULT" value="1" enum="MethodFlags"> Default method flags. diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index e564e8045c..b8436be76a 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -56,11 +56,11 @@ Called by the engine when the 3D editor's viewport is updated. Use the [code]overlay[/code] [Control] for drawing. You can update the viewport manually by calling [method update_overlays]. [codeblocks] [gdscript] - func _forward_spatial_3d_over_viewport(overlay): + func _forward_3d_draw_over_viewport(overlay): # Draw a circle at cursor position. overlay.draw_circle(overlay.get_local_mouse_position(), 64) - func _forward_spatial_gui_input(camera, event): + func _forward_3d_gui_input(camera, event): if event is InputEventMouseMotion: # Redraw viewport when cursor is moved. update_overlays() @@ -68,13 +68,13 @@ return false [/gdscript] [csharp] - public override void ForwardSpatialDrawOverViewport(Godot.Control overlay) + public override void _Forward3dDrawOverViewport(Godot.Control overlay) { // Draw a circle at cursor position. overlay.DrawCircle(overlay.GetLocalMousePosition(), 64, Colors.White); } - public override bool ForwardSpatialGuiInput(Godot.Camera3D camera, InputEvent @event) + public override bool _Forward3dGuiInput(Godot.Camera3D camera, InputEvent @event) { if (@event is InputEventMouseMotion) { @@ -104,12 +104,12 @@ [codeblocks] [gdscript] # Prevents the InputEvent to reach other Editor classes. - func _forward_spatial_gui_input(camera, event): + func _forward_3d_gui_input(camera, event): return true [/gdscript] [csharp] // Prevents the InputEvent to reach other Editor classes. - public override bool ForwardSpatialGuiInput(Camera3D camera, InputEvent @event) + public override bool _Forward3dGuiInput(Camera3D camera, InputEvent @event) { return true; } @@ -119,12 +119,12 @@ [codeblocks] [gdscript] # Consumes InputEventMouseMotion and forwards other InputEvent types. - func _forward_spatial_gui_input(camera, event): + func _forward_3d_gui_input(camera, event): return event is InputEventMouseMotion [/gdscript] [csharp] // Consumes InputEventMouseMotion and forwards other InputEvent types. - public override bool ForwardSpatialGuiInput(Camera3D camera, InputEvent @event) + public override bool _Forward3dGuiInput(Camera3D camera, InputEvent @event) { return @event is InputEventMouseMotion; } diff --git a/doc/classes/TextureProgressBar.xml b/doc/classes/TextureProgressBar.xml index 9da89ebe43..ee47557b39 100644 --- a/doc/classes/TextureProgressBar.xml +++ b/doc/classes/TextureProgressBar.xml @@ -60,6 +60,9 @@ [Texture2D] that clips based on the node's [code]value[/code] and [member fill_mode]. As [code]value[/code] increased, the texture fills up. It shows entirely when [code]value[/code] reaches [code]max_value[/code]. It doesn't show at all if [code]value[/code] is equal to [code]min_value[/code]. The [code]value[/code] property comes from [Range]. See [member Range.value], [member Range.min_value], [member Range.max_value]. </member> + <member name="texture_progress_offset" type="Vector2" setter="set_texture_progress_offset" getter="get_texture_progress_offset" default="Vector2(0, 0)"> + The offset of [member texture_progress]. Useful for [member texture_over] and [member texture_under] with fancy borders, to avoid transparent margins in your progress texture. + </member> <member name="texture_under" type="Texture2D" setter="set_under_texture" getter="get_under_texture"> [Texture2D] that draws under the progress bar. The bar's background. </member> diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml index cb5662419e..ab4d0e181a 100644 --- a/doc/classes/Vector2.xml +++ b/doc/classes/Vector2.xml @@ -155,6 +155,18 @@ Returns the vector with all components rounded down (towards negative infinity). </description> </method> + <method name="from_angle" qualifiers="static"> + <return type="Vector2" /> + <argument index="0" name="angle" type="float" /> + <description> + Creates a unit [Vector2] rotated to the given [code]angle[/code] in radians. This is equivalent to doing [code]Vector2(cos(angle), sin(angle))[/code] or [code]Vector2.RIGHT.rotated(angle)[/code]. + [codeblock] + print(Vector2.from_angle(0)) # Prints (1, 0). + print(Vector2(1, 0).angle()) # Prints 0, which is the angle used above. + print(Vector2.from_angle(PI / 2)) # Prints (0, 1). + [/codeblock] + </description> + </method> <method name="is_equal_approx" qualifiers="const"> <return type="bool" /> <argument index="0" name="to" type="Vector2" /> diff --git a/editor/debugger/debug_adapter/debug_adapter_parser.cpp b/editor/debugger/debug_adapter/debug_adapter_parser.cpp index 945291b163..fbd3aaa409 100644 --- a/editor/debugger/debug_adapter/debug_adapter_parser.cpp +++ b/editor/debugger/debug_adapter/debug_adapter_parser.cpp @@ -33,12 +33,15 @@ #include "editor/debugger/editor_debugger_node.h" #include "editor/debugger/script_editor_debugger.h" #include "editor/editor_node.h" +#include "editor/editor_run_native.h" void DebugAdapterParser::_bind_methods() { // Requests ClassDB::bind_method(D_METHOD("req_initialize", "params"), &DebugAdapterParser::req_initialize); - ClassDB::bind_method(D_METHOD("req_disconnect", "params"), &DebugAdapterParser::prepare_success_response); + ClassDB::bind_method(D_METHOD("req_disconnect", "params"), &DebugAdapterParser::req_disconnect); ClassDB::bind_method(D_METHOD("req_launch", "params"), &DebugAdapterParser::req_launch); + ClassDB::bind_method(D_METHOD("req_attach", "params"), &DebugAdapterParser::req_attach); + ClassDB::bind_method(D_METHOD("req_restart", "params"), &DebugAdapterParser::req_restart); ClassDB::bind_method(D_METHOD("req_terminate", "params"), &DebugAdapterParser::req_terminate); ClassDB::bind_method(D_METHOD("req_configurationDone", "params"), &DebugAdapterParser::prepare_success_response); ClassDB::bind_method(D_METHOD("req_pause", "params"), &DebugAdapterParser::req_pause); @@ -46,10 +49,13 @@ void DebugAdapterParser::_bind_methods() { ClassDB::bind_method(D_METHOD("req_threads", "params"), &DebugAdapterParser::req_threads); ClassDB::bind_method(D_METHOD("req_stackTrace", "params"), &DebugAdapterParser::req_stackTrace); ClassDB::bind_method(D_METHOD("req_setBreakpoints", "params"), &DebugAdapterParser::req_setBreakpoints); + ClassDB::bind_method(D_METHOD("req_breakpointLocations", "params"), &DebugAdapterParser::req_breakpointLocations); ClassDB::bind_method(D_METHOD("req_scopes", "params"), &DebugAdapterParser::req_scopes); ClassDB::bind_method(D_METHOD("req_variables", "params"), &DebugAdapterParser::req_variables); ClassDB::bind_method(D_METHOD("req_next", "params"), &DebugAdapterParser::req_next); ClassDB::bind_method(D_METHOD("req_stepIn", "params"), &DebugAdapterParser::req_stepIn); + ClassDB::bind_method(D_METHOD("req_evaluate", "params"), &DebugAdapterParser::req_evaluate); + ClassDB::bind_method(D_METHOD("req_godot/put_msg", "params"), &DebugAdapterParser::req_godot_put_msg); } Dictionary DebugAdapterParser::prepare_base_event() const { @@ -80,13 +86,31 @@ Dictionary DebugAdapterParser::prepare_error_response(const Dictionary &p_params DAP::Message message; String error, error_desc; switch (err_type) { + case DAP::ErrorType::WRONG_PATH: + error = "wrong_path"; + error_desc = "The editor and client are working on different paths; the client is on \"{clientPath}\", but the editor is on \"{editorPath}\""; + break; + case DAP::ErrorType::NOT_RUNNING: + error = "not_running"; + error_desc = "Can't attach to a running session since there isn't one."; + break; + case DAP::ErrorType::TIMEOUT: + error = "timeout"; + error_desc = "Timeout reached while processing a request."; + break; + case DAP::ErrorType::UNKNOWN_PLATFORM: + error = "unknown_platform"; + error_desc = "The specified platform is unknown."; + break; + case DAP::ErrorType::MISSING_DEVICE: + error = "missing_device"; + error_desc = "There's no connected device with specified id."; + break; case DAP::ErrorType::UNKNOWN: + default: error = "unknown"; error_desc = "An unknown error has ocurred when processing the request."; break; - case DAP::ErrorType::WRONG_PATH: - error = "wrong_path"; - error_desc = "The editor and client are working on different paths; the client is on \"{clientPath}\", but the editor is on \"{editorPath}\""; } message.id = err_type; @@ -114,10 +138,35 @@ Dictionary DebugAdapterParser::req_initialize(const Dictionary &p_params) const DebugAdapterProtocol::get_singleton()->notify_initialized(); + if (DebugAdapterProtocol::get_singleton()->_sync_breakpoints) { + // Send all current breakpoints + List<String> breakpoints; + ScriptEditor::get_singleton()->get_breakpoints(&breakpoints); + for (List<String>::Element *E = breakpoints.front(); E; E = E->next()) { + String breakpoint = E->get(); + + String path = breakpoint.left(breakpoint.find(":", 6)); // Skip initial part of path, aka "res://" + int line = breakpoint.substr(path.size()).to_int(); + + DebugAdapterProtocol::get_singleton()->on_debug_breakpoint_toggled(path, line, true); + } + } else { + // Remove all current breakpoints + EditorDebuggerNode::get_singleton()->get_default_debugger()->_clear_breakpoints(); + } + return response; } -Dictionary DebugAdapterParser::req_launch(const Dictionary &p_params) { +Dictionary DebugAdapterParser::req_disconnect(const Dictionary &p_params) const { + if (!DebugAdapterProtocol::get_singleton()->get_current_peer()->attached) { + EditorNode::get_singleton()->run_stop(); + } + + return prepare_success_response(p_params); +} + +Dictionary DebugAdapterParser::req_launch(const Dictionary &p_params) const { Dictionary args = p_params["arguments"]; if (args.has("project") && !is_valid_path(args["project"])) { Dictionary variables; @@ -126,17 +175,85 @@ Dictionary DebugAdapterParser::req_launch(const Dictionary &p_params) { return prepare_error_response(p_params, DAP::ErrorType::WRONG_PATH, variables); } + if (args.has("godot/custom_data")) { + DebugAdapterProtocol::get_singleton()->get_current_peer()->supportsCustomData = args["godot/custom_data"]; + } + ScriptEditorDebugger *dbg = EditorDebuggerNode::get_singleton()->get_default_debugger(); if ((bool)args["noDebug"] != dbg->is_skip_breakpoints()) { dbg->debug_skip_breakpoints(); } - EditorNode::get_singleton()->run_play(); + String platform_string = args.get("platform", "host"); + if (platform_string == "host") { + EditorNode::get_singleton()->run_play(); + } else { + int device = args.get("device", -1); + int idx = -1; + if (platform_string == "android") { + for (int i = 0; i < EditorExport::get_singleton()->get_export_platform_count(); i++) { + if (EditorExport::get_singleton()->get_export_platform(i)->get_name() == "Android") { + idx = i; + break; + } + } + } else if (platform_string == "web") { + for (int i = 0; i < EditorExport::get_singleton()->get_export_platform_count(); i++) { + if (EditorExport::get_singleton()->get_export_platform(i)->get_name() == "HTML5") { + idx = i; + break; + } + } + } + + if (idx == -1) { + return prepare_error_response(p_params, DAP::ErrorType::UNKNOWN_PLATFORM); + } + + EditorNode *editor = EditorNode::get_singleton(); + Error err = platform_string == "android" ? editor->run_play_native(device, idx) : editor->run_play_native(-1, idx); + if (err) { + if (err == ERR_INVALID_PARAMETER && platform_string == "android") { + return prepare_error_response(p_params, DAP::ErrorType::MISSING_DEVICE); + } else { + return prepare_error_response(p_params, DAP::ErrorType::UNKNOWN); + } + } + } + + DebugAdapterProtocol::get_singleton()->get_current_peer()->attached = false; DebugAdapterProtocol::get_singleton()->notify_process(); return prepare_success_response(p_params); } +Dictionary DebugAdapterParser::req_attach(const Dictionary &p_params) const { + ScriptEditorDebugger *dbg = EditorDebuggerNode::get_singleton()->get_default_debugger(); + if (!dbg->is_session_active()) { + return prepare_error_response(p_params, DAP::ErrorType::NOT_RUNNING); + } + + DebugAdapterProtocol::get_singleton()->get_current_peer()->attached = true; + DebugAdapterProtocol::get_singleton()->notify_process(); + return prepare_success_response(p_params); +} + +Dictionary DebugAdapterParser::req_restart(const Dictionary &p_params) const { + // Extract embedded "arguments" so it can be given to req_launch/req_attach + Dictionary params = p_params, args; + args = params["arguments"]; + args = args["arguments"]; + params["arguments"] = args; + + Dictionary response = DebugAdapterProtocol::get_singleton()->get_current_peer()->attached ? req_attach(params) : req_launch(params); + if (!response["success"]) { + response["command"] = p_params["command"]; + return response; + } + + return prepare_success_response(p_params); +} + Dictionary DebugAdapterParser::req_terminate(const Dictionary &p_params) const { EditorNode::get_singleton()->run_stop(); @@ -205,7 +322,7 @@ Dictionary DebugAdapterParser::req_stackTrace(const Dictionary &p_params) const return response; } -Dictionary DebugAdapterParser::req_setBreakpoints(const Dictionary &p_params) { +Dictionary DebugAdapterParser::req_setBreakpoints(const Dictionary &p_params) const { Dictionary response = prepare_success_response(p_params), body; response["body"] = body; @@ -230,14 +347,30 @@ Dictionary DebugAdapterParser::req_setBreakpoints(const Dictionary &p_params) { lines.push_back(breakpoint.line + !lines_at_one); } - EditorDebuggerNode::get_singleton()->set_breakpoints(ProjectSettings::get_singleton()->localize_path(source.path), lines); Array updated_breakpoints = DebugAdapterProtocol::get_singleton()->update_breakpoints(source.path, lines); body["breakpoints"] = updated_breakpoints; return response; } -Dictionary DebugAdapterParser::req_scopes(const Dictionary &p_params) { +Dictionary DebugAdapterParser::req_breakpointLocations(const Dictionary &p_params) const { + Dictionary response = prepare_success_response(p_params), body; + response["body"] = body; + Dictionary args = p_params["arguments"]; + + Array locations; + DAP::BreakpointLocation location; + location.line = args["line"]; + if (args.has("endLine")) { + location.endLine = args["endLine"]; + } + locations.push_back(location.to_json()); + + body["breakpoints"] = locations; + return response; +} + +Dictionary DebugAdapterParser::req_scopes(const Dictionary &p_params) const { Dictionary response = prepare_success_response(p_params), body; response["body"] = body; @@ -291,7 +424,14 @@ Dictionary DebugAdapterParser::req_variables(const Dictionary &p_params) const { int variable_id = args["variablesReference"]; Map<int, Array>::Element *E = DebugAdapterProtocol::get_singleton()->variable_list.find(variable_id); + if (E) { + if (!DebugAdapterProtocol::get_singleton()->get_current_peer()->supportsVariableType) { + for (int i = 0; i < E->value().size(); i++) { + Dictionary variable = E->value()[i]; + variable.erase("type"); + } + } body["variables"] = E ? E->value() : Array(); return response; } else { @@ -313,6 +453,29 @@ Dictionary DebugAdapterParser::req_stepIn(const Dictionary &p_params) const { return prepare_success_response(p_params); } +Dictionary DebugAdapterParser::req_evaluate(const Dictionary &p_params) const { + Dictionary response = prepare_success_response(p_params), body; + response["body"] = body; + + Dictionary args = p_params["arguments"]; + + String value = EditorDebuggerNode::get_singleton()->get_var_value(args["expression"]); + body["result"] = value; + + return response; +} + +Dictionary DebugAdapterParser::req_godot_put_msg(const Dictionary &p_params) const { + Dictionary args = p_params["arguments"]; + + String msg = args["message"]; + Array data = args["data"]; + + EditorDebuggerNode::get_singleton()->get_default_debugger()->_put_msg(msg, data); + + return prepare_success_response(p_params); +} + Dictionary DebugAdapterParser::ev_initialized() const { Dictionary event = prepare_base_event(); event["event"] = "initialized"; @@ -423,3 +586,25 @@ Dictionary DebugAdapterParser::ev_output(const String &p_message) const { return event; } + +Dictionary DebugAdapterParser::ev_breakpoint(const DAP::Breakpoint &p_breakpoint, const bool &p_enabled) const { + Dictionary event = prepare_base_event(), body; + event["event"] = "breakpoint"; + event["body"] = body; + + body["reason"] = p_enabled ? "new" : "removed"; + body["breakpoint"] = p_breakpoint.to_json(); + + return event; +} + +Dictionary DebugAdapterParser::ev_custom_data(const String &p_msg, const Array &p_data) const { + Dictionary event = prepare_base_event(), body; + event["event"] = "godot/custom_data"; + event["body"] = body; + + body["message"] = p_msg; + body["data"] = p_data; + + return event; +} diff --git a/editor/debugger/debug_adapter/debug_adapter_parser.h b/editor/debugger/debug_adapter/debug_adapter_parser.h index b86b37d067..4c93464e39 100644 --- a/editor/debugger/debug_adapter/debug_adapter_parser.h +++ b/editor/debugger/debug_adapter/debug_adapter_parser.h @@ -44,7 +44,7 @@ class DebugAdapterParser : public Object { private: friend DebugAdapterProtocol; - _FORCE_INLINE_ bool is_valid_path(const String &p_path) { + _FORCE_INLINE_ bool is_valid_path(const String &p_path) const { return p_path.begins_with(ProjectSettings::get_singleton()->get_resource_path()); } @@ -60,17 +60,23 @@ protected: public: // Requests Dictionary req_initialize(const Dictionary &p_params) const; - Dictionary req_launch(const Dictionary &p_params); + Dictionary req_launch(const Dictionary &p_params) const; + Dictionary req_disconnect(const Dictionary &p_params) const; + Dictionary req_attach(const Dictionary &p_params) const; + Dictionary req_restart(const Dictionary &p_params) const; Dictionary req_terminate(const Dictionary &p_params) const; Dictionary req_pause(const Dictionary &p_params) const; Dictionary req_continue(const Dictionary &p_params) const; Dictionary req_threads(const Dictionary &p_params) const; Dictionary req_stackTrace(const Dictionary &p_params) const; - Dictionary req_setBreakpoints(const Dictionary &p_params); - Dictionary req_scopes(const Dictionary &p_params); + Dictionary req_setBreakpoints(const Dictionary &p_params) const; + Dictionary req_breakpointLocations(const Dictionary &p_params) const; + Dictionary req_scopes(const Dictionary &p_params) const; Dictionary req_variables(const Dictionary &p_params) const; Dictionary req_next(const Dictionary &p_params) const; Dictionary req_stepIn(const Dictionary &p_params) const; + Dictionary req_evaluate(const Dictionary &p_params) const; + Dictionary req_godot_put_msg(const Dictionary &p_params) const; // Events Dictionary ev_initialized() const; @@ -83,6 +89,8 @@ public: Dictionary ev_stopped_step() const; Dictionary ev_continued() const; Dictionary ev_output(const String &p_message) const; + Dictionary ev_custom_data(const String &p_msg, const Array &p_data) const; + Dictionary ev_breakpoint(const DAP::Breakpoint &p_breakpoint, const bool &p_enabled) const; }; #endif diff --git a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp b/editor/debugger/debug_adapter/debug_adapter_protocol.cpp index 0482271432..2e0e6cb7c8 100644 --- a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp +++ b/editor/debugger/debug_adapter/debug_adapter_protocol.cpp @@ -94,11 +94,17 @@ Error DAPeer::handle_data() { String msg; msg.parse_utf8((const char *)req_buf, req_pos); + // Apply a timestamp if it there's none yet + if (!timestamp) { + timestamp = OS::get_singleton()->get_ticks_msec(); + } + // Response if (DebugAdapterProtocol::get_singleton()->process_message(msg)) { // Reset to read again req_pos = 0; has_header = false; + timestamp = 0; } } return OK; @@ -180,12 +186,460 @@ void DebugAdapterProtocol::reset_stack_info() { variable_list.clear(); } +int DebugAdapterProtocol::parse_variant(const Variant &p_var) { + switch (p_var.get_type()) { + case Variant::VECTOR2: + case Variant::VECTOR2I: { + int id = variable_id++; + Vector2 vec = p_var; + DAP::Variable x, y; + x.name = "x"; + y.name = "y"; + x.type = y.type = Variant::get_type_name(p_var.get_type() == Variant::VECTOR2 ? Variant::FLOAT : Variant::INT); + x.value = rtos(vec.x); + y.value = rtos(vec.y); + + Array arr; + arr.push_back(x.to_json()); + arr.push_back(y.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::RECT2: + case Variant::RECT2I: { + int id = variable_id++; + Rect2 rect = p_var; + DAP::Variable x, y, w, h; + x.name = "x"; + y.name = "y"; + w.name = "w"; + h.name = "h"; + x.type = y.type = w.type = h.type = Variant::get_type_name(p_var.get_type() == Variant::RECT2 ? Variant::FLOAT : Variant::INT); + x.value = rtos(rect.position.x); + y.value = rtos(rect.position.y); + w.value = rtos(rect.size.x); + h.value = rtos(rect.size.y); + + Array arr; + arr.push_back(x.to_json()); + arr.push_back(y.to_json()); + arr.push_back(w.to_json()); + arr.push_back(h.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::VECTOR3: + case Variant::VECTOR3I: { + int id = variable_id++; + Vector3 vec = p_var; + DAP::Variable x, y, z; + x.name = "x"; + y.name = "y"; + z.name = "z"; + x.type = y.type = z.type = Variant::get_type_name(p_var.get_type() == Variant::VECTOR3 ? Variant::FLOAT : Variant::INT); + x.value = rtos(vec.x); + y.value = rtos(vec.y); + z.value = rtos(vec.z); + + Array arr; + arr.push_back(x.to_json()); + arr.push_back(y.to_json()); + arr.push_back(z.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::TRANSFORM2D: { + int id = variable_id++; + Transform2D transform = p_var; + DAP::Variable x, y, origin; + x.name = "x"; + y.name = "y"; + origin.name = "origin"; + x.type = y.type = origin.type = Variant::get_type_name(Variant::VECTOR2); + x.value = transform.elements[0]; + y.value = transform.elements[1]; + origin.value = transform.elements[2]; + x.variablesReference = parse_variant(transform.elements[0]); + y.variablesReference = parse_variant(transform.elements[1]); + origin.variablesReference = parse_variant(transform.elements[2]); + + Array arr; + arr.push_back(x.to_json()); + arr.push_back(y.to_json()); + arr.push_back(origin.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::PLANE: { + int id = variable_id++; + Plane plane = p_var; + DAP::Variable d, normal; + d.name = "d"; + normal.name = "normal"; + d.type = Variant::get_type_name(Variant::FLOAT); + normal.type = Variant::get_type_name(Variant::VECTOR3); + d.value = rtos(plane.d); + normal.value = plane.normal; + normal.variablesReference = parse_variant(plane.normal); + + Array arr; + arr.push_back(d.to_json()); + arr.push_back(normal.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::QUATERNION: { + int id = variable_id++; + Quaternion quat = p_var; + DAP::Variable x, y, z, w; + x.name = "x"; + y.name = "y"; + z.name = "z"; + w.name = "w"; + x.type = y.type = z.type = w.type = Variant::get_type_name(Variant::FLOAT); + x.value = rtos(quat.x); + y.value = rtos(quat.y); + z.value = rtos(quat.z); + w.value = rtos(quat.w); + + Array arr; + arr.push_back(x.to_json()); + arr.push_back(y.to_json()); + arr.push_back(z.to_json()); + arr.push_back(w.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::AABB: { + int id = variable_id++; + AABB aabb = p_var; + DAP::Variable position, size; + position.name = "position"; + size.name = "size"; + position.type = size.type = Variant::get_type_name(Variant::VECTOR3); + position.value = aabb.position; + size.value = aabb.size; + position.variablesReference = parse_variant(aabb.position); + size.variablesReference = parse_variant(aabb.size); + + Array arr; + arr.push_back(position.to_json()); + arr.push_back(size.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::BASIS: { + int id = variable_id++; + Basis basis = p_var; + DAP::Variable x, y, z; + x.name = "x"; + y.name = "y"; + z.name = "z"; + x.type = y.type = z.type = Variant::get_type_name(Variant::VECTOR2); + x.value = basis.elements[0]; + y.value = basis.elements[1]; + z.value = basis.elements[2]; + x.variablesReference = parse_variant(basis.elements[0]); + y.variablesReference = parse_variant(basis.elements[1]); + z.variablesReference = parse_variant(basis.elements[2]); + + Array arr; + arr.push_back(x.to_json()); + arr.push_back(y.to_json()); + arr.push_back(z.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::TRANSFORM3D: { + int id = variable_id++; + Transform3D transform = p_var; + DAP::Variable basis, origin; + basis.name = "basis"; + origin.name = "origin"; + basis.type = Variant::get_type_name(Variant::BASIS); + origin.type = Variant::get_type_name(Variant::VECTOR3); + basis.value = transform.basis; + origin.value = transform.origin; + basis.variablesReference = parse_variant(transform.basis); + origin.variablesReference = parse_variant(transform.origin); + + Array arr; + arr.push_back(basis.to_json()); + arr.push_back(origin.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::COLOR: { + int id = variable_id++; + Color color = p_var; + DAP::Variable r, g, b, a; + r.name = "r"; + g.name = "g"; + b.name = "b"; + a.name = "a"; + r.type = g.type = b.type = a.type = Variant::get_type_name(Variant::FLOAT); + r.value = rtos(color.r); + g.value = rtos(color.g); + b.value = rtos(color.b); + a.value = rtos(color.a); + + Array arr; + arr.push_back(r.to_json()); + arr.push_back(g.to_json()); + arr.push_back(b.to_json()); + arr.push_back(a.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::ARRAY: { + int id = variable_id++; + Array array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = Variant::get_type_name(array[i].get_type()); + var.value = array[i]; + var.variablesReference = parse_variant(array[i]); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::DICTIONARY: { + int id = variable_id++; + Dictionary dictionary = p_var; + Array arr; + + for (int i = 0; i < dictionary.size(); i++) { + DAP::Variable var; + var.name = dictionary.get_key_at_index(i); + Variant value = dictionary.get_value_at_index(i); + var.type = Variant::get_type_name(value.get_type()); + var.value = value; + var.variablesReference = parse_variant(value); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::PACKED_BYTE_ARRAY: { + int id = variable_id++; + PackedByteArray array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = "byte"; + var.value = itos(array[i]); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::PACKED_INT32_ARRAY: { + int id = variable_id++; + PackedInt32Array array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = "int"; + var.value = itos(array[i]); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::PACKED_INT64_ARRAY: { + int id = variable_id++; + PackedInt64Array array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = "long"; + var.value = itos(array[i]); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::PACKED_FLOAT32_ARRAY: { + int id = variable_id++; + PackedFloat32Array array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = "float"; + var.value = rtos(array[i]); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::PACKED_FLOAT64_ARRAY: { + int id = variable_id++; + PackedFloat64Array array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = "double"; + var.value = rtos(array[i]); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::PACKED_STRING_ARRAY: { + int id = variable_id++; + PackedStringArray array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = Variant::get_type_name(Variant::STRING); + var.value = array[i]; + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::PACKED_VECTOR2_ARRAY: { + int id = variable_id++; + PackedVector2Array array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = Variant::get_type_name(Variant::VECTOR2); + var.value = array[i]; + var.variablesReference = parse_variant(array[i]); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::PACKED_VECTOR3_ARRAY: { + int id = variable_id++; + PackedVector2Array array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = Variant::get_type_name(Variant::VECTOR3); + var.value = array[i]; + var.variablesReference = parse_variant(array[i]); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::PACKED_COLOR_ARRAY: { + int id = variable_id++; + PackedColorArray array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = Variant::get_type_name(Variant::COLOR); + var.value = array[i]; + var.variablesReference = parse_variant(array[i]); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + default: + // Simple atomic stuff, or too complex to be manipulated + return 0; + } +} + bool DebugAdapterProtocol::process_message(const String &p_text) { JSON json; ERR_FAIL_COND_V_MSG(json.parse(p_text) != OK, true, "Mal-formed message!"); Dictionary params = json.get_data(); bool completed = true; + if (OS::get_singleton()->get_ticks_msec() - _current_peer->timestamp > _request_timeout) { + Dictionary response = parser->prepare_error_response(params, DAP::ErrorType::TIMEOUT); + _current_peer->res_queue.push_front(response); + return true; + } + // Append "req_" to any command received; prevents name clash with existing functions, and possibly exploiting String command = "req_" + (String)params["command"]; if (parser->has_method(command)) { @@ -211,7 +665,7 @@ void DebugAdapterProtocol::notify_initialized() { } void DebugAdapterProtocol::notify_process() { - String launch_mode = _current_request.is_empty() ? "launch" : _current_request; + String launch_mode = _current_peer->attached ? "attach" : "launch"; Dictionary event = parser->ev_process(launch_mode); for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { @@ -222,7 +676,7 @@ void DebugAdapterProtocol::notify_process() { void DebugAdapterProtocol::notify_terminated() { Dictionary event = parser->ev_terminated(); for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { - if (_current_request == "launch" && _current_peer == E->get()) { + if ((_current_request == "launch" || _current_request == "restart") && _current_peer == E->get()) { continue; } E->get()->res_queue.push_back(event); @@ -232,7 +686,7 @@ void DebugAdapterProtocol::notify_terminated() { void DebugAdapterProtocol::notify_exited(const int &p_exitcode) { Dictionary event = parser->ev_exited(p_exitcode); for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { - if (_current_request == "launch" && _current_peer == E->get()) { + if ((_current_request == "launch" || _current_request == "restart") && _current_peer == E->get()) { continue; } E->get()->res_queue.push_back(event); @@ -286,25 +740,46 @@ void DebugAdapterProtocol::notify_output(const String &p_message) { } } +void DebugAdapterProtocol::notify_custom_data(const String &p_msg, const Array &p_data) { + Dictionary event = parser->ev_custom_data(p_msg, p_data); + for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { + Ref<DAPeer> peer = E->get(); + if (peer->supportsCustomData) { + peer->res_queue.push_back(event); + } + } +} + +void DebugAdapterProtocol::notify_breakpoint(const DAP::Breakpoint &p_breakpoint, const bool &p_enabled) { + Dictionary event = parser->ev_breakpoint(p_breakpoint, p_enabled); + for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { + if (_current_request == "setBreakpoints" && E->get() == _current_peer) { + continue; + } + E->get()->res_queue.push_back(event); + } +} + Array DebugAdapterProtocol::update_breakpoints(const String &p_path, const Array &p_lines) { Array updated_breakpoints; + // Add breakpoints for (int i = 0; i < p_lines.size(); i++) { + EditorDebuggerNode::get_singleton()->get_default_debugger()->_set_breakpoint(p_path, p_lines[i], true); DAP::Breakpoint breakpoint; - breakpoint.verified = true; - breakpoint.source.path = p_path; - breakpoint.source.compute_checksums(); breakpoint.line = p_lines[i]; + breakpoint.source.path = p_path; - List<DAP::Breakpoint>::Element *E = breakpoint_list.find(breakpoint); - if (E) { - breakpoint.id = E->get().id; - } else { - breakpoint.id = breakpoint_id++; - breakpoint_list.push_back(breakpoint); - } + ERR_FAIL_COND_V(!breakpoint_list.find(breakpoint), Array()); + updated_breakpoints.push_back(breakpoint_list.find(breakpoint)->get().to_json()); + } - updated_breakpoints.push_back(breakpoint.to_json()); + // Remove breakpoints + for (List<DAP::Breakpoint>::Element *E = breakpoint_list.front(); E; E = E->next()) { + DAP::Breakpoint b = E->get(); + if (b.source.path == p_path && !p_lines.has(b.line)) { + EditorDebuggerNode::get_singleton()->get_default_debugger()->_set_breakpoint(p_path, b.line, false); + } } return updated_breakpoints; @@ -347,6 +822,29 @@ void DebugAdapterProtocol::on_debug_breaked(const bool &p_reallydid, const bool _processing_stackdump = p_has_stackdump; } +void DebugAdapterProtocol::on_debug_breakpoint_toggled(const String &p_path, const int &p_line, const bool &p_enabled) { + DAP::Breakpoint breakpoint; + breakpoint.verified = true; + breakpoint.source.path = ProjectSettings::get_singleton()->globalize_path(p_path); + breakpoint.source.compute_checksums(); + breakpoint.line = p_line; + + if (p_enabled) { + // Add the breakpoint + breakpoint.id = breakpoint_id++; + breakpoint_list.push_back(breakpoint); + } else { + // Remove the breakpoint + List<DAP::Breakpoint>::Element *E = breakpoint_list.find(breakpoint); + if (E) { + breakpoint.id = E->get().id; + breakpoint_list.erase(E); + } + } + + notify_breakpoint(breakpoint, p_enabled); +} + void DebugAdapterProtocol::on_debug_stack_dump(const Array &p_stack_dump) { if (_processing_breakpoint && !p_stack_dump.is_empty()) { // Find existing breakpoint @@ -424,11 +922,21 @@ void DebugAdapterProtocol::on_debug_stack_frame_var(const Array &p_data) { variable.name = stack_var.name; variable.value = stack_var.value; variable.type = Variant::get_type_name(stack_var.value.get_type()); + variable.variablesReference = parse_variant(stack_var.value); variable_list.find(variable_id)->value().push_back(variable.to_json()); _remaining_vars--; } +void DebugAdapterProtocol::on_debug_data(const String &p_msg, const Array &p_data) { + // Ignore data that is already handled by DAP + if (p_msg == "debug_enter" || p_msg == "debug_exit" || p_msg == "stack_dump" || p_msg == "stack_frame_vars" || p_msg == "stack_frame_var" || p_msg == "output" || p_msg == "request_quit") { + return; + } + + notify_custom_data(p_msg, p_data); +} + void DebugAdapterProtocol::poll() { if (server->is_connection_available()) { on_client_connected(); @@ -459,6 +967,8 @@ void DebugAdapterProtocol::poll() { } Error DebugAdapterProtocol::start(int p_port, const IPAddress &p_bind_ip) { + _request_timeout = (uint64_t)_EDITOR_GET("network/debug_adapter/request_timeout"); + _sync_breakpoints = (bool)_EDITOR_GET("network/debug_adapter/sync_breakpoints"); _initialized = true; return server->listen(p_port, p_bind_ip); } @@ -484,12 +994,15 @@ DebugAdapterProtocol::DebugAdapterProtocol() { node->get_pause_button()->connect("pressed", callable_mp(this, &DebugAdapterProtocol::on_debug_paused)); EditorDebuggerNode *debugger_node = EditorDebuggerNode::get_singleton(); + debugger_node->connect("breakpoint_toggled", callable_mp(this, &DebugAdapterProtocol::on_debug_breakpoint_toggled)); + debugger_node->get_default_debugger()->connect("stopped", callable_mp(this, &DebugAdapterProtocol::on_debug_stopped)); debugger_node->get_default_debugger()->connect("output", callable_mp(this, &DebugAdapterProtocol::on_debug_output)); debugger_node->get_default_debugger()->connect("breaked", callable_mp(this, &DebugAdapterProtocol::on_debug_breaked)); debugger_node->get_default_debugger()->connect("stack_dump", callable_mp(this, &DebugAdapterProtocol::on_debug_stack_dump)); debugger_node->get_default_debugger()->connect("stack_frame_vars", callable_mp(this, &DebugAdapterProtocol::on_debug_stack_frame_vars)); debugger_node->get_default_debugger()->connect("stack_frame_var", callable_mp(this, &DebugAdapterProtocol::on_debug_stack_frame_var)); + debugger_node->get_default_debugger()->connect("debug_data", callable_mp(this, &DebugAdapterProtocol::on_debug_data)); } DebugAdapterProtocol::~DebugAdapterProtocol() { diff --git a/editor/debugger/debug_adapter/debug_adapter_protocol.h b/editor/debugger/debug_adapter/debug_adapter_protocol.h index 6b542033ed..d4291992bf 100644 --- a/editor/debugger/debug_adapter/debug_adapter_protocol.h +++ b/editor/debugger/debug_adapter/debug_adapter_protocol.h @@ -52,12 +52,17 @@ struct DAPeer : RefCounted { int content_length = 0; List<Dictionary> res_queue; int seq = 0; + uint64_t timestamp = 0; // Client specific info bool linesStartAt1 = false; bool columnsStartAt1 = false; bool supportsVariableType = false; bool supportsInvalidatedEvent = false; + bool supportsCustomData = false; + + // Internal client info + bool attached = false; Error handle_data(); Error send_data(); @@ -82,20 +87,26 @@ private: void on_debug_stopped(); void on_debug_output(const String &p_message); void on_debug_breaked(const bool &p_reallydid, const bool &p_can_debug, const String &p_reason, const bool &p_has_stackdump); + void on_debug_breakpoint_toggled(const String &p_path, const int &p_line, const bool &p_enabled); void on_debug_stack_dump(const Array &p_stack_dump); void on_debug_stack_frame_vars(const int &p_size); void on_debug_stack_frame_var(const Array &p_data); + void on_debug_data(const String &p_msg, const Array &p_data); void reset_current_info(); void reset_ids(); void reset_stack_info(); + int parse_variant(const Variant &p_var); + bool _initialized = false; bool _processing_breakpoint = false; bool _stepping = false; bool _processing_stackdump = false; int _remaining_vars = 0; int _current_frame = 0; + uint64_t _request_timeout = 1000; + bool _sync_breakpoints = false; String _current_request; Ref<DAPeer> _current_peer; @@ -108,6 +119,8 @@ private: Map<int, Array> variable_list; public: + friend class DebugAdapterServer; + _FORCE_INLINE_ static DebugAdapterProtocol *get_singleton() { return singleton; } _FORCE_INLINE_ bool is_active() const { return _initialized && clients.size() > 0; } @@ -126,8 +139,10 @@ public: void notify_stopped_step(); void notify_continued(); void notify_output(const String &p_message); + void notify_custom_data(const String &p_msg, const Array &p_data); + void notify_breakpoint(const DAP::Breakpoint &p_breakpoint, const bool &p_enabled); - Array update_breakpoints(const String &p_path, const Array &p_breakpoints); + Array update_breakpoints(const String &p_path, const Array &p_lines); void poll(); Error start(int p_port, const IPAddress &p_bind_ip); diff --git a/editor/debugger/debug_adapter/debug_adapter_server.cpp b/editor/debugger/debug_adapter/debug_adapter_server.cpp index f9092a1791..4775e2c8b0 100644 --- a/editor/debugger/debug_adapter/debug_adapter_server.cpp +++ b/editor/debugger/debug_adapter/debug_adapter_server.cpp @@ -36,7 +36,8 @@ DebugAdapterServer::DebugAdapterServer() { _EDITOR_DEF("network/debug_adapter/remote_port", remote_port); - _EDITOR_DEF("network/debug_adapter/use_thread", use_thread); + _EDITOR_DEF("network/debug_adapter/request_timeout", protocol._request_timeout); + _EDITOR_DEF("network/debug_adapter/sync_breakpoints", protocol._sync_breakpoints); } void DebugAdapterServer::_notification(int p_what) { @@ -50,16 +51,17 @@ void DebugAdapterServer::_notification(int p_what) { case NOTIFICATION_INTERNAL_PROCESS: { // The main loop can be run again during request processing, which modifies internal state of the protocol. // Thus, "polling" is needed to prevent it from parsing other requests while the current one isn't finished. - if (started && !use_thread && !polling) { + if (started && !polling) { polling = true; protocol.poll(); polling = false; } } break; case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + protocol._request_timeout = EditorSettings::get_singleton()->get("network/debug_adapter/request_timeout"); + protocol._sync_breakpoints = EditorSettings::get_singleton()->get("network/debug_adapter/sync_breakpoints"); int remote_port = (int)_EDITOR_GET("network/debug_adapter/remote_port"); - bool use_thread = (bool)_EDITOR_GET("network/debug_adapter/use_thread"); - if (remote_port != this->remote_port || use_thread != this->use_thread) { + if (remote_port != this->remote_port) { this->stop(); this->start(); } @@ -67,35 +69,16 @@ void DebugAdapterServer::_notification(int p_what) { } } -void DebugAdapterServer::thread_func(void *p_userdata) { - DebugAdapterServer *self = static_cast<DebugAdapterServer *>(p_userdata); - while (self->thread_running) { - // Poll 20 times per second - self->protocol.poll(); - OS::get_singleton()->delay_usec(50000); - } -} - void DebugAdapterServer::start() { remote_port = (int)_EDITOR_GET("network/debug_adapter/remote_port"); - use_thread = (bool)_EDITOR_GET("network/debug_adapter/use_thread"); if (protocol.start(remote_port, IPAddress("127.0.0.1")) == OK) { EditorNode::get_log()->add_message("--- Debug adapter server started ---", EditorLog::MSG_TYPE_EDITOR); - if (use_thread) { - thread_running = true; - thread.start(DebugAdapterServer::thread_func, this); - } - set_process_internal(!use_thread); + set_process_internal(true); started = true; } } void DebugAdapterServer::stop() { - if (use_thread) { - ERR_FAIL_COND(!thread.is_started()); - thread_running = false; - thread.wait_to_finish(); - } protocol.stop(); started = false; EditorNode::get_log()->add_message("--- Debug adapter server stopped ---", EditorLog::MSG_TYPE_EDITOR); diff --git a/editor/debugger/debug_adapter/debug_adapter_server.h b/editor/debugger/debug_adapter/debug_adapter_server.h index f8a38965cc..c449403cc2 100644 --- a/editor/debugger/debug_adapter/debug_adapter_server.h +++ b/editor/debugger/debug_adapter/debug_adapter_server.h @@ -39,11 +39,9 @@ class DebugAdapterServer : public EditorPlugin { DebugAdapterProtocol protocol; - Thread thread; int remote_port = 6006; bool thread_running = false; bool started = false; - bool use_thread = false; bool polling = false; static void thread_func(void *p_userdata); diff --git a/editor/debugger/debug_adapter/debug_adapter_types.h b/editor/debugger/debug_adapter/debug_adapter_types.h index aa9ab1adcd..5156c91d14 100644 --- a/editor/debugger/debug_adapter/debug_adapter_types.h +++ b/editor/debugger/debug_adapter/debug_adapter_types.h @@ -38,7 +38,11 @@ namespace DAP { enum ErrorType { UNKNOWN, - WRONG_PATH + WRONG_PATH, + NOT_RUNNING, + TIMEOUT, + UNKNOWN_PLATFORM, + MISSING_DEVICE }; struct Checksum { @@ -118,10 +122,14 @@ struct Breakpoint { struct BreakpointLocation { int line; + int endLine = -1; _FORCE_INLINE_ Dictionary to_json() const { Dictionary dict; dict["line"] = line; + if (endLine >= 0) { + dict["endLine"] = endLine; + } return dict; } diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp index a9cb1a0131..07c02eb022 100644 --- a/editor/debugger/editor_debugger_node.cpp +++ b/editor/debugger/editor_debugger_node.cpp @@ -164,6 +164,7 @@ void EditorDebuggerNode::_bind_methods() { ADD_SIGNAL(MethodInfo("set_execution", PropertyInfo("script"), PropertyInfo(Variant::INT, "line"))); ADD_SIGNAL(MethodInfo("clear_execution", PropertyInfo("script"))); ADD_SIGNAL(MethodInfo("breaked", PropertyInfo(Variant::BOOL, "reallydid"), PropertyInfo(Variant::BOOL, "can_debug"))); + ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::BOOL, "enabled"))); } EditorDebuggerRemoteObject *EditorDebuggerNode::get_inspected_remote_object() { @@ -487,6 +488,8 @@ void EditorDebuggerNode::set_breakpoint(const String &p_path, int p_line, bool p _for_all(tabs, [&](ScriptEditorDebugger *dbg) { dbg->set_breakpoint(p_path, p_line, p_enabled); }); + + emit_signal("breakpoint_toggled", p_path, p_line, p_enabled); } void EditorDebuggerNode::set_breakpoints(const String &p_path, Array p_lines) { diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 2a5013893f..2f5bde64a9 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -296,6 +296,7 @@ Size2 ScriptEditorDebugger::get_minimum_size() const { } void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_data) { + emit_signal(SNAME("debug_data"), p_msg, p_data); if (p_msg == "debug_enter") { _put_msg("get_stack_dump", Array()); @@ -872,6 +873,16 @@ void ScriptEditorDebugger::_clear_execution() { inspector->clear_stack_variables(); } +void ScriptEditorDebugger::_set_breakpoint(const String &p_file, const int &p_line, const bool &p_enabled) { + Ref<Script> script = ResourceLoader::load(p_file); + emit_signal("set_breakpoint", script, p_line - 1, p_enabled); + script.unref(); +} + +void ScriptEditorDebugger::_clear_breakpoints() { + emit_signal("clear_breakpoints"); +} + void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) { error_count = 0; warning_count = 0; @@ -1503,6 +1514,9 @@ void ScriptEditorDebugger::_bind_methods() { ADD_SIGNAL(MethodInfo("stack_dump", PropertyInfo(Variant::ARRAY, "stack_dump"))); ADD_SIGNAL(MethodInfo("stack_frame_vars", PropertyInfo(Variant::INT, "num_vars"))); ADD_SIGNAL(MethodInfo("stack_frame_var", PropertyInfo(Variant::ARRAY, "data"))); + ADD_SIGNAL(MethodInfo("debug_data", PropertyInfo(Variant::STRING, "msg"), PropertyInfo(Variant::ARRAY, "data"))); + ADD_SIGNAL(MethodInfo("set_breakpoint", PropertyInfo("script"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::BOOL, "enabled"))); + ADD_SIGNAL(MethodInfo("clear_breakpoints")); } void ScriptEditorDebugger::add_debugger_plugin(const Ref<Script> &p_script) { diff --git a/editor/debugger/script_editor_debugger.h b/editor/debugger/script_editor_debugger.h index 499dda86da..1c1c0fd3e5 100644 --- a/editor/debugger/script_editor_debugger.h +++ b/editor/debugger/script_editor_debugger.h @@ -205,6 +205,9 @@ private: void _clear_execution(); void _stop_and_notify(); + void _set_breakpoint(const String &p_path, const int &p_line, const bool &p_enabled); + void _clear_breakpoints(); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index b1c546a8c0..9c7bddf037 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -4770,6 +4770,10 @@ bool EditorNode::ensure_main_scene(bool p_from_native) { return true; } +Error EditorNode::run_play_native(int p_idx, int p_platform) { + return run_native->run_native(p_idx, p_platform); +} + void EditorNode::run_play() { _menu_option_confirm(RUN_STOP, true); _run(false); diff --git a/editor/editor_node.h b/editor/editor_node.h index 51d01d07ef..03c18a8972 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -898,6 +898,7 @@ public: bool ensure_main_scene(bool p_from_native); + Error run_play_native(int p_idx, int p_platform); void run_play(); void run_play_current(); void run_play_custom(const String &p_custom); diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp index e115cd77e1..5828549bdc 100644 --- a/editor/editor_run_native.cpp +++ b/editor/editor_run_native.cpp @@ -52,8 +52,8 @@ void EditorRunNative::_notification(int p_what) { small_icon.instantiate(); small_icon->create_from_image(im); MenuButton *mb = memnew(MenuButton); - mb->get_popup()->connect("id_pressed", callable_mp(this, &EditorRunNative::_run_native), varray(i)); - mb->connect("pressed", callable_mp(this, &EditorRunNative::_run_native), varray(-1, i)); + mb->get_popup()->connect("id_pressed", callable_mp(this, &EditorRunNative::run_native), varray(i)); + mb->connect("pressed", callable_mp(this, &EditorRunNative::run_native), varray(-1, i)); mb->set_icon(small_icon); add_child(mb); menus[i] = mb; @@ -93,22 +93,22 @@ void EditorRunNative::_notification(int p_what) { } } -void EditorRunNative::_run_native(int p_idx, int p_platform) { +Error EditorRunNative::run_native(int p_idx, int p_platform) { if (!EditorNode::get_singleton()->ensure_main_scene(true)) { resume_idx = p_idx; resume_platform = p_platform; - return; + return OK; } Ref<EditorExportPlatform> eep = EditorExport::get_singleton()->get_export_platform(p_platform); - ERR_FAIL_COND(eep.is_null()); + ERR_FAIL_COND_V(eep.is_null(), ERR_UNAVAILABLE); if (p_idx == -1) { if (eep->get_options_count() == 1) { menus[p_platform]->get_popup()->hide(); p_idx = 0; } else { - return; + return ERR_INVALID_PARAMETER; } } @@ -124,7 +124,7 @@ void EditorRunNative::_run_native(int p_idx, int p_platform) { if (preset.is_null()) { EditorNode::get_singleton()->show_warning(TTR("No runnable export preset found for this platform.\nPlease add a runnable preset in the Export menu or define an existing preset as runnable.")); - return; + return ERR_UNAVAILABLE; } emit_signal(SNAME("native_run"), preset); @@ -149,11 +149,11 @@ void EditorRunNative::_run_native(int p_idx, int p_platform) { flags |= EditorExportPlatform::DEBUG_FLAG_VIEW_NAVIGATION; } - eep->run(preset, p_idx, flags); + return eep->run(preset, p_idx, flags); } void EditorRunNative::resume_run_native() { - _run_native(resume_idx, resume_platform); + run_native(resume_idx, resume_platform); } void EditorRunNative::_bind_methods() { diff --git a/editor/editor_run_native.h b/editor/editor_run_native.h index 3516f668c6..97f6fc005a 100644 --- a/editor/editor_run_native.h +++ b/editor/editor_run_native.h @@ -43,13 +43,12 @@ class EditorRunNative : public HBoxContainer { int resume_idx; int resume_platform; - void _run_native(int p_idx, int p_platform); - protected: static void _bind_methods(); void _notification(int p_what); public: + Error run_native(int p_idx, int p_platform); bool is_deploy_debug_remote_enabled() const; void resume_run_native(); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index a24249b95b..7ef5993ec5 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -37,6 +37,7 @@ #include "core/os/keyboard.h" #include "core/os/os.h" #include "editor/debugger/editor_debugger_node.h" +#include "editor/debugger/script_editor_debugger.h" #include "editor/editor_node.h" #include "editor/editor_run_script.h" #include "editor/editor_scale.h" @@ -479,6 +480,29 @@ void ScriptEditor::_clear_execution(REF p_script) { } } +void ScriptEditor::_set_breakpoint(REF p_script, int p_line, bool p_enabled) { + Ref<Script> script = Object::cast_to<Script>(*p_script); + if (script.is_valid() && (script->has_source_code() || script->get_path().is_resource_file())) { + if (edit(p_script, p_line, 0, false)) { + editor->push_item(p_script.ptr()); + + ScriptEditorBase *se = _get_current_editor(); + if (se) { + se->set_breakpoint(p_line, p_enabled); + } + } + } +} + +void ScriptEditor::_clear_breakpoints() { + for (int i = 0; i < tab_container->get_child_count(); i++) { + ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i)); + if (se) { + se->clear_breakpoints(); + } + } +} + ScriptEditorBase *ScriptEditor::_get_current_editor() const { int selected = tab_container->get_current_tab(); if (selected < 0 || selected >= tab_container->get_child_count()) { @@ -3484,6 +3508,8 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { debugger->connect("set_execution", callable_mp(this, &ScriptEditor::_set_execution)); debugger->connect("clear_execution", callable_mp(this, &ScriptEditor::_clear_execution)); debugger->connect("breaked", callable_mp(this, &ScriptEditor::_breaked)); + debugger->get_default_debugger()->connect("set_breakpoint", callable_mp(this, &ScriptEditor::_set_breakpoint)); + debugger->get_default_debugger()->connect("clear_breakpoints", callable_mp(this, &ScriptEditor::_clear_breakpoints)); menu_hb->add_spacer(); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index a57aeea5c0..e2420b4623 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -155,6 +155,8 @@ public: virtual void tag_saved_version() = 0; virtual void reload(bool p_soft) {} virtual Array get_breakpoints() = 0; + virtual void set_breakpoint(int p_line, bool p_enabled) = 0; + virtual void clear_breakpoints() = 0; virtual void add_callback(const String &p_function, PackedStringArray p_args) = 0; virtual void update_settings() = 0; virtual void set_debugger_active(bool p_active) = 0; @@ -374,6 +376,8 @@ class ScriptEditor : public PanelContainer { void _breaked(bool p_breaked, bool p_can_debug); void _update_window_menu(); void _script_created(Ref<Script> p_script); + void _set_breakpoint(REF p_scrpt, int p_line, bool p_enabled); + void _clear_breakpoints(); ScriptEditorBase *_get_current_editor() const; Array _get_open_script_editors() const; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index b7d5403919..c44760807f 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -1370,6 +1370,14 @@ Array ScriptTextEditor::get_breakpoints() { return code_editor->get_text_editor()->get_breakpointed_lines(); } +void ScriptTextEditor::set_breakpoint(int p_line, bool p_enabled) { + code_editor->get_text_editor()->set_line_as_breakpoint(p_line, p_enabled); +} + +void ScriptTextEditor::clear_breakpoints() { + code_editor->get_text_editor()->clear_breakpointed_lines(); +} + void ScriptTextEditor::set_tooltip_request_func(String p_method, Object *p_obj) { code_editor->get_text_editor()->set_tooltip_request_func(p_obj, p_method, this); } diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index 1ca6f56ea1..4208d67f17 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -225,6 +225,8 @@ public: virtual void reload(bool p_soft) override; virtual Array get_breakpoints() override; + virtual void set_breakpoint(int p_line, bool p_enabled) override; + virtual void clear_breakpoints() override; virtual void add_callback(const String &p_function, PackedStringArray p_args) override; virtual void update_settings() override; diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h index 839e1c5f7a..6bf0042393 100644 --- a/editor/plugins/text_editor.h +++ b/editor/plugins/text_editor.h @@ -121,6 +121,8 @@ public: virtual void set_edit_state(const Variant &p_state) override; virtual Vector<String> get_functions() override; virtual Array get_breakpoints() override; + virtual void set_breakpoint(int p_line, bool p_enabled) override{}; + virtual void clear_breakpoints() override{}; virtual void goto_line(int p_line, bool p_with_error = false) override; void goto_line_selection(int p_line, int p_begin, int p_end); virtual void set_executing_line(int p_line) override; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs index 213f84ad73..c8dd0ac9ce 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs @@ -110,19 +110,6 @@ namespace Godot } /// <summary> - /// Converts a 2D point expressed in the cartesian coordinate - /// system (X and Y axis) to the polar coordinate system - /// (a distance from the origin and an angle). - /// </summary> - /// <param name="x">The input X coordinate.</param> - /// <param name="y">The input Y coordinate.</param> - /// <returns>A <see cref="Vector2"/> with X representing the distance and Y representing the angle.</returns> - public static Vector2 Cartesian2Polar(real_t x, real_t y) - { - return new Vector2(Sqrt(x * x + y * y), Atan2(y, x)); - } - - /// <summary> /// Rounds `s` upward (towards positive infinity). /// </summary> /// <param name="s">The number to ceil.</param> @@ -436,19 +423,6 @@ namespace Godot } /// <summary> - /// Converts a 2D point expressed in the polar coordinate - /// system (a distance from the origin `r` and an angle `th`) - /// to the cartesian coordinate system (X and Y axis). - /// </summary> - /// <param name="r">The distance from the origin.</param> - /// <param name="th">The angle of the point.</param> - /// <returns>A <see cref="Vector2"/> representing the cartesian coordinate.</returns> - public static Vector2 Polar2Cartesian(real_t r, real_t th) - { - return new Vector2(r * Cos(th), r * Sin(th)); - } - - /// <summary> /// Performs a canonical Modulus operation, where the output is on the range `[0, b)`. /// </summary> /// <param name="a">The dividend, the primary input.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs index 8bb5e90a68..b4c4ddabb9 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs @@ -603,6 +603,17 @@ namespace Godot y = v.y; } + /// <summary> + /// Creates a unit Vector2 rotated to the given angle. This is equivalent to doing + /// `Vector2(Mathf.Cos(angle), Mathf.Sin(angle))` or `Vector2.Right.Rotated(angle)`. + /// </summary> + /// <param name="angle">Angle of the vector, in radians.</param> + /// <returns>The resulting vector.</returns> + public static Vector2 FromAngle(real_t angle) + { + return new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)); + } + public static Vector2 operator +(Vector2 left, Vector2 right) { left.x += right.x; diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml index 195d766c1d..55d0b392fa 100644 --- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml +++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml @@ -138,81 +138,75 @@ <constant name="MATH_DB2LINEAR" value="40" enum="BuiltinFunc"> Convert the input from decibel volume to linear volume. </constant> - <constant name="MATH_POLAR2CARTESIAN" value="41" enum="BuiltinFunc"> - Converts a 2D point expressed in the polar coordinate system (a distance from the origin [code]r[/code] and an angle [code]th[/code]) to the cartesian coordinate system (X and Y axis). + <constant name="MATH_WRAP" value="41" enum="BuiltinFunc"> </constant> - <constant name="MATH_CARTESIAN2POLAR" value="42" enum="BuiltinFunc"> - Converts a 2D point expressed in the cartesian coordinate system (X and Y axis) to the polar coordinate system (a distance from the origin and an angle). + <constant name="MATH_WRAPF" value="42" enum="BuiltinFunc"> </constant> - <constant name="MATH_WRAP" value="43" enum="BuiltinFunc"> - </constant> - <constant name="MATH_WRAPF" value="44" enum="BuiltinFunc"> - </constant> - <constant name="LOGIC_MAX" value="45" enum="BuiltinFunc"> + <constant name="LOGIC_MAX" value="43" enum="BuiltinFunc"> Return the greater of the two numbers, also known as their maximum. </constant> - <constant name="LOGIC_MIN" value="46" enum="BuiltinFunc"> + <constant name="LOGIC_MIN" value="44" enum="BuiltinFunc"> Return the lesser of the two numbers, also known as their minimum. </constant> - <constant name="LOGIC_CLAMP" value="47" enum="BuiltinFunc"> + <constant name="LOGIC_CLAMP" value="45" enum="BuiltinFunc"> Return the input clamped inside the given range, ensuring the result is never outside it. Equivalent to [code]min(max(input, range_low), range_high)[/code]. </constant> - <constant name="LOGIC_NEAREST_PO2" value="48" enum="BuiltinFunc"> + <constant name="LOGIC_NEAREST_PO2" value="46" enum="BuiltinFunc"> Return the nearest power of 2 to the input. </constant> - <constant name="OBJ_WEAKREF" value="49" enum="BuiltinFunc"> + <constant name="OBJ_WEAKREF" value="47" enum="BuiltinFunc"> Create a [WeakRef] from the input. </constant> - <constant name="TYPE_CONVERT" value="50" enum="BuiltinFunc"> + <constant name="TYPE_CONVERT" value="48" enum="BuiltinFunc"> Convert between types. </constant> - <constant name="TYPE_OF" value="51" enum="BuiltinFunc"> + <constant name="TYPE_OF" value="49" enum="BuiltinFunc"> Return the type of the input as an integer. Check [enum Variant.Type] for the integers that might be returned. </constant> - <constant name="TYPE_EXISTS" value="52" enum="BuiltinFunc"> + <constant name="TYPE_EXISTS" value="50" enum="BuiltinFunc"> Checks if a type is registered in the [ClassDB]. </constant> - <constant name="TEXT_CHAR" value="53" enum="BuiltinFunc"> + <constant name="TEXT_CHAR" value="51" enum="BuiltinFunc"> Return a character with the given ascii value. </constant> - <constant name="TEXT_STR" value="54" enum="BuiltinFunc"> + <constant name="TEXT_STR" value="52" enum="BuiltinFunc"> Convert the input to a string. </constant> - <constant name="TEXT_PRINT" value="55" enum="BuiltinFunc"> + <constant name="TEXT_PRINT" value="53" enum="BuiltinFunc"> Print the given string to the output window. </constant> - <constant name="TEXT_PRINTERR" value="56" enum="BuiltinFunc"> + <constant name="TEXT_PRINTERR" value="54" enum="BuiltinFunc"> Print the given string to the standard error output. </constant> - <constant name="TEXT_PRINTRAW" value="57" enum="BuiltinFunc"> + <constant name="TEXT_PRINTRAW" value="55" enum="BuiltinFunc"> Print the given string to the standard output, without adding a newline. </constant> - <constant name="VAR_TO_STR" value="58" enum="BuiltinFunc"> + <constant name="VAR_TO_STR" value="56" enum="BuiltinFunc"> Serialize a [Variant] to a string. </constant> - <constant name="STR_TO_VAR" value="59" enum="BuiltinFunc"> + <constant name="STR_TO_VAR" value="57" enum="BuiltinFunc"> Deserialize a [Variant] from a string serialized using [constant VAR_TO_STR]. </constant> - <constant name="VAR_TO_BYTES" value="60" enum="BuiltinFunc"> + <constant name="VAR_TO_BYTES" value="58" enum="BuiltinFunc"> Serialize a [Variant] to a [PackedByteArray]. </constant> - <constant name="BYTES_TO_VAR" value="61" enum="BuiltinFunc"> + <constant name="BYTES_TO_VAR" value="59" enum="BuiltinFunc"> Deserialize a [Variant] from a [PackedByteArray] serialized using [constant VAR_TO_BYTES]. </constant> - <constant name="MATH_SMOOTHSTEP" value="62" enum="BuiltinFunc"> + <constant name="MATH_SMOOTHSTEP" value="60" enum="BuiltinFunc"> Return a number smoothly interpolated between the first two inputs, based on the third input. Similar to [constant MATH_LERP], but interpolates faster at the beginning and slower at the end. Using Hermite interpolation formula: [codeblock] var t = clamp((weight - from) / (to - from), 0.0, 1.0) return t * t * (3.0 - 2.0 * t) [/codeblock] </constant> - <constant name="MATH_POSMOD" value="63" enum="BuiltinFunc"> + <constant name="MATH_POSMOD" value="61" enum="BuiltinFunc"> </constant> - <constant name="MATH_LERP_ANGLE" value="64" enum="BuiltinFunc"> + <constant name="MATH_LERP_ANGLE" value="62" enum="BuiltinFunc"> </constant> - <constant name="TEXT_ORD" value="65" enum="BuiltinFunc"> + <constant name="TEXT_ORD" value="63" enum="BuiltinFunc"> </constant> - <constant name="FUNC_MAX" value="66" enum="BuiltinFunc"> + <constant name="FUNC_MAX" value="64" enum="BuiltinFunc"> Represents the size of the [enum BuiltinFunc] enum. </constant> </constants> diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp index c61c3ae272..2bd7220d15 100644 --- a/modules/visual_script/visual_script_builtin_funcs.cpp +++ b/modules/visual_script/visual_script_builtin_funcs.cpp @@ -79,8 +79,6 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX "rad2deg", "linear2db", "db2linear", - "polar2cartesian", - "cartesian2polar", "wrapi", "wrapf", "max", @@ -194,8 +192,6 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) { case MATH_SNAPPED: case MATH_RANDF_RANGE: case MATH_RANDI_RANGE: - case MATH_POLAR2CARTESIAN: - case MATH_CARTESIAN2POLAR: case LOGIC_MAX: case LOGIC_MIN: case TYPE_CONVERT: @@ -381,20 +377,6 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const case MATH_DB2LINEAR: { return PropertyInfo(Variant::FLOAT, "db"); } break; - case MATH_POLAR2CARTESIAN: { - if (p_idx == 0) { - return PropertyInfo(Variant::FLOAT, "r"); - } else { - return PropertyInfo(Variant::FLOAT, "th"); - } - } break; - case MATH_CARTESIAN2POLAR: { - if (p_idx == 0) { - return PropertyInfo(Variant::FLOAT, "x"); - } else { - return PropertyInfo(Variant::FLOAT, "y"); - } - } break; case MATH_WRAP: { if (p_idx == 0) { return PropertyInfo(Variant::INT, "value"); @@ -553,10 +535,6 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons case MATH_DB2LINEAR: { t = Variant::FLOAT; } break; - case MATH_POLAR2CARTESIAN: - case MATH_CARTESIAN2POLAR: { - t = Variant::VECTOR2; - } break; case MATH_WRAP: { t = Variant::INT; } break; @@ -874,20 +852,6 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in VALIDATE_ARG_NUM(0); *r_return = Math::db2linear((double)*p_inputs[0]); } break; - case VisualScriptBuiltinFunc::MATH_POLAR2CARTESIAN: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - double r = *p_inputs[0]; - double th = *p_inputs[1]; - *r_return = Vector2(r * Math::cos(th), r * Math::sin(th)); - } break; - case VisualScriptBuiltinFunc::MATH_CARTESIAN2POLAR: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - double x = *p_inputs[0]; - double y = *p_inputs[1]; - *r_return = Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x)); - } break; case VisualScriptBuiltinFunc::MATH_WRAP: { VALIDATE_ARG_NUM(0); VALIDATE_ARG_NUM(1); @@ -1229,8 +1193,6 @@ void VisualScriptBuiltinFunc::_bind_methods() { BIND_ENUM_CONSTANT(MATH_RAD2DEG); BIND_ENUM_CONSTANT(MATH_LINEAR2DB); BIND_ENUM_CONSTANT(MATH_DB2LINEAR); - BIND_ENUM_CONSTANT(MATH_POLAR2CARTESIAN); - BIND_ENUM_CONSTANT(MATH_CARTESIAN2POLAR); BIND_ENUM_CONSTANT(MATH_WRAP); BIND_ENUM_CONSTANT(MATH_WRAPF); BIND_ENUM_CONSTANT(LOGIC_MAX); @@ -1320,8 +1282,6 @@ void register_visual_script_builtin_func_node() { VisualScriptLanguage::singleton->add_register_func("functions/built_in/rad2deg", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAD2DEG>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/linear2db", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LINEAR2DB>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/db2linear", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DB2LINEAR>); - VisualScriptLanguage::singleton->add_register_func("functions/built_in/polar2cartesian", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_POLAR2CARTESIAN>); - VisualScriptLanguage::singleton->add_register_func("functions/built_in/cartesian2polar", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_CARTESIAN2POLAR>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/wrapi", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_WRAP>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/wrapf", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_WRAPF>); diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h index f59a7a0f0c..26abc1e479 100644 --- a/modules/visual_script/visual_script_builtin_funcs.h +++ b/modules/visual_script/visual_script_builtin_funcs.h @@ -79,8 +79,6 @@ public: MATH_RAD2DEG, MATH_LINEAR2DB, MATH_DB2LINEAR, - MATH_POLAR2CARTESIAN, - MATH_CARTESIAN2POLAR, MATH_WRAP, MATH_WRAPF, LOGIC_MAX, diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 7dfb4fa270..ab32aae7aa 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -311,6 +311,8 @@ public: virtual void tag_saved_version() override; virtual void reload(bool p_soft) override; virtual Array get_breakpoints() override; + virtual void set_breakpoint(int p_line, bool p_enable) override{}; + virtual void clear_breakpoints() override{}; virtual void add_callback(const String &p_function, PackedStringArray p_args) override; virtual void update_settings() override; virtual bool show_members_overview() override; diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 481e211eb3..845a2ec6c7 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -434,14 +434,14 @@ void TabContainer::_notification(int p_what) { } int tab_width = tab_widths[i]; - if (get_tab_disabled(index)) { + if (index == current) { + x_current = x; + } else if (get_tab_disabled(index)) { if (rtl) { _draw_tab(tab_disabled, font_disabled_color, index, size.width - (tabs_ofs_cache + x) - tab_width); } else { _draw_tab(tab_disabled, font_disabled_color, index, tabs_ofs_cache + x); } - } else if (index == current) { - x_current = x; } else { if (rtl) { _draw_tab(tab_unselected, font_unselected_color, index, size.width - (tabs_ofs_cache + x) - tab_width); @@ -459,12 +459,13 @@ void TabContainer::_notification(int p_what) { panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height)); } - // Draw selected tab in front. only draw selected tab when it's in visible range. + // Draw selected tab in front. Only draw selected tab when it's in visible range. if (tabs.size() > 0 && current - first_tab_cache < tab_widths.size() && current >= first_tab_cache) { + Ref<StyleBox> current_style_box = get_tab_disabled(current) ? tab_disabled : tab_selected; if (rtl) { - _draw_tab(tab_selected, font_selected_color, current, size.width - (tabs_ofs_cache + x_current) - tab_widths[current]); + _draw_tab(current_style_box, font_selected_color, current, size.width - (tabs_ofs_cache + x_current) - tab_widths[current]); } else { - _draw_tab(tab_selected, font_selected_color, current, tabs_ofs_cache + x_current); + _draw_tab(current_style_box, font_selected_color, current, tabs_ofs_cache + x_current); } } @@ -640,8 +641,8 @@ void TabContainer::_on_mouse_exited() { int TabContainer::_get_tab_width(int p_index) const { ERR_FAIL_INDEX_V(p_index, get_tab_count(), 0); - Control *control = Object::cast_to<Control>(_get_tabs()[p_index]); - if (!control || control->is_set_as_top_level() || get_tab_hidden(p_index)) { + Control *control = get_tab_control(p_index); + if (!control || get_tab_hidden(p_index)) { return 0; } diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp index 5e5dec3579..286f01ee33 100644 --- a/scene/gui/texture_progress_bar.cpp +++ b/scene/gui/texture_progress_bar.cpp @@ -100,6 +100,15 @@ Ref<Texture2D> TextureProgressBar::get_progress_texture() const { return progress; } +void TextureProgressBar::set_progress_offset(Point2 p_offset) { + progress_offset = p_offset; + update(); +} + +Point2 TextureProgressBar::get_progress_offset() const { + return progress_offset; +} + void TextureProgressBar::set_tint_under(const Color &p_tint) { tint_under = p_tint; update(); @@ -360,6 +369,9 @@ void TextureProgressBar::draw_nine_patch_stretched(const Ref<Texture2D> &p_textu } } + if (p_texture == progress) { + dst_rect.position += progress_offset; + } p_texture->get_rect_region(dst_rect, src_rect, dst_rect, src_rect); RID ci = get_canvas_item(); @@ -403,20 +415,24 @@ void TextureProgressBar::_notification(int p_what) { Size2 s = progress->get_size(); switch (mode) { case FILL_LEFT_TO_RIGHT: { - Rect2 region = Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)); - draw_texture_rect_region(progress, region, region, tint_progress); + Rect2 region = Rect2(progress_offset, Size2(s.x * get_as_ratio(), s.y)); + Rect2 source = Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)); + draw_texture_rect_region(progress, region, source, tint_progress); } break; case FILL_RIGHT_TO_LEFT: { - Rect2 region = Rect2(Point2(s.x - s.x * get_as_ratio(), 0), Size2(s.x * get_as_ratio(), s.y)); - draw_texture_rect_region(progress, region, region, tint_progress); + Rect2 region = Rect2(progress_offset + Point2(s.x - s.x * get_as_ratio(), 0), Size2(s.x * get_as_ratio(), s.y)); + Rect2 source = Rect2(Point2(s.x - s.x * get_as_ratio(), 0), Size2(s.x * get_as_ratio(), s.y)); + draw_texture_rect_region(progress, region, source, tint_progress); } break; case FILL_TOP_TO_BOTTOM: { - Rect2 region = Rect2(Point2(), Size2(s.x, s.y * get_as_ratio())); - draw_texture_rect_region(progress, region, region, tint_progress); + Rect2 region = Rect2(progress_offset + Point2(), Size2(s.x, s.y * get_as_ratio())); + Rect2 source = Rect2(Point2(), Size2(s.x, s.y * get_as_ratio())); + draw_texture_rect_region(progress, region, source, tint_progress); } break; case FILL_BOTTOM_TO_TOP: { - Rect2 region = Rect2(Point2(0, s.y - s.y * get_as_ratio()), Size2(s.x, s.y * get_as_ratio())); - draw_texture_rect_region(progress, region, region, tint_progress); + Rect2 region = Rect2(progress_offset + Point2(0, s.y - s.y * get_as_ratio()), Size2(s.x, s.y * get_as_ratio())); + Rect2 source = Rect2(Point2(0, s.y - s.y * get_as_ratio()), Size2(s.x, s.y * get_as_ratio())); + draw_texture_rect_region(progress, region, source, tint_progress); } break; case FILL_CLOCKWISE: case FILL_COUNTER_CLOCKWISE: @@ -427,8 +443,9 @@ void TextureProgressBar::_notification(int p_what) { float val = get_as_ratio() * rad_max_degrees / 360; if (val == 1) { - Rect2 region = Rect2(Point2(), s); - draw_texture_rect(progress, region, false, tint_progress); + Rect2 region = Rect2(progress_offset, s); + Rect2 source = Rect2(Point2(), s); + draw_texture_rect_region(progress, region, source, tint_progress); } else if (val != 0) { Array pts; float direction = mode == FILL_COUNTER_CLOCKWISE ? -1 : 1; @@ -454,14 +471,14 @@ void TextureProgressBar::_notification(int p_what) { Vector<Point2> uvs; Vector<Point2> points; uvs.push_back(get_relative_center()); - points.push_back(Point2(s.x * get_relative_center().x, s.y * get_relative_center().y)); + points.push_back(progress_offset + Point2(s.x * get_relative_center().x, s.y * get_relative_center().y)); for (int i = 0; i < pts.size(); i++) { Point2 uv = unit_val_to_uv(pts[i]); if (uvs.find(uv) >= 0) { continue; } uvs.push_back(uv); - points.push_back(Point2(uv.x * s.x, uv.y * s.y)); + points.push_back(progress_offset + Point2(uv.x * s.x, uv.y * s.y)); } Vector<Color> colors; colors.push_back(tint_progress); @@ -484,17 +501,19 @@ void TextureProgressBar::_notification(int p_what) { } } break; case FILL_BILINEAR_LEFT_AND_RIGHT: { - Rect2 region = Rect2(Point2(s.x / 2 - s.x * get_as_ratio() / 2, 0), Size2(s.x * get_as_ratio(), s.y)); - draw_texture_rect_region(progress, region, region, tint_progress); + Rect2 region = Rect2(progress_offset + Point2(s.x / 2 - s.x * get_as_ratio() / 2, 0), Size2(s.x * get_as_ratio(), s.y)); + Rect2 source = Rect2(Point2(s.x / 2 - s.x * get_as_ratio() / 2, 0), Size2(s.x * get_as_ratio(), s.y)); + draw_texture_rect_region(progress, region, source, tint_progress); } break; case FILL_BILINEAR_TOP_AND_BOTTOM: { - Rect2 region = Rect2(Point2(0, s.y / 2 - s.y * get_as_ratio() / 2), Size2(s.x, s.y * get_as_ratio())); - draw_texture_rect_region(progress, region, region, tint_progress); + Rect2 region = Rect2(progress_offset + Point2(0, s.y / 2 - s.y * get_as_ratio() / 2), Size2(s.x, s.y * get_as_ratio())); + Rect2 source = Rect2(Point2(0, s.y / 2 - s.y * get_as_ratio() / 2), Size2(s.x, s.y * get_as_ratio())); + draw_texture_rect_region(progress, region, source, tint_progress); } break; case FILL_MODE_MAX: break; default: - draw_texture_rect_region(progress, Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress); + draw_texture_rect_region(progress, Rect2(progress_offset, Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress); } } if (over.is_valid()) { @@ -585,6 +604,9 @@ void TextureProgressBar::_bind_methods() { ClassDB::bind_method(D_METHOD("set_tint_over", "tint"), &TextureProgressBar::set_tint_over); ClassDB::bind_method(D_METHOD("get_tint_over"), &TextureProgressBar::get_tint_over); + ClassDB::bind_method(D_METHOD("set_texture_progress_offset", "offset"), &TextureProgressBar::set_progress_offset); + ClassDB::bind_method(D_METHOD("get_texture_progress_offset"), &TextureProgressBar::get_progress_offset); + ClassDB::bind_method(D_METHOD("set_radial_initial_angle", "mode"), &TextureProgressBar::set_radial_initial_angle); ClassDB::bind_method(D_METHOD("get_radial_initial_angle"), &TextureProgressBar::get_radial_initial_angle); @@ -604,6 +626,7 @@ void TextureProgressBar::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_under", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_under_texture", "get_under_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_over", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_over_texture", "get_over_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_progress", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_progress_texture", "get_progress_texture"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "texture_progress_offset"), "set_texture_progress_offset", "get_texture_progress_offset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise,Bilinear (Left and Right),Bilinear (Top and Bottom),Clockwise and Counter Clockwise"), "set_fill_mode", "get_fill_mode"); ADD_GROUP("Tint", "tint_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_under"), "set_tint_under", "get_tint_under"); diff --git a/scene/gui/texture_progress_bar.h b/scene/gui/texture_progress_bar.h index d147c43a26..c508f41387 100644 --- a/scene/gui/texture_progress_bar.h +++ b/scene/gui/texture_progress_bar.h @@ -61,6 +61,9 @@ public: void set_fill_mode(int p_fill); int get_fill_mode(); + void set_progress_offset(Point2 p_offset); + Point2 get_progress_offset() const; + void set_radial_initial_angle(float p_angle); float get_radial_initial_angle(); @@ -100,6 +103,7 @@ public: private: FillMode mode = FILL_LEFT_TO_RIGHT; + Point2 progress_offset; float rad_init_angle = 0.0; float rad_max_degrees = 360.0; Point2 rad_center_off; |