diff options
Diffstat (limited to 'editor')
25 files changed, 2131 insertions, 1467 deletions
diff --git a/editor/SCsub b/editor/SCsub index 2b560f68e8..4431166ee6 100644 --- a/editor/SCsub +++ b/editor/SCsub @@ -82,6 +82,7 @@ if env['tools']: SConscript('collada/SCsub') SConscript('doc/SCsub') + SConscript('debugger/SCsub') SConscript('fileserver/SCsub') SConscript('icons/SCsub') SConscript('import/SCsub') diff --git a/editor/debugger/SCsub b/editor/debugger/SCsub new file mode 100644 index 0000000000..2b1e889fb0 --- /dev/null +++ b/editor/debugger/SCsub @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +Import('env') + +env.add_source_files(env.editor_sources, "*.cpp") diff --git a/editor/debugger/editor_debugger_inspector.cpp b/editor/debugger/editor_debugger_inspector.cpp new file mode 100644 index 0000000000..35d7dda3f4 --- /dev/null +++ b/editor/debugger/editor_debugger_inspector.cpp @@ -0,0 +1,277 @@ +/*************************************************************************/ +/* editor_debugger_inspector.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "editor_debugger_inspector.h" + +#include "core/io/marshalls.h" +#include "core/script_debugger_remote.h" +#include "editor/editor_node.h" +#include "scene/debugger/scene_debugger.h" + +bool EditorDebuggerRemoteObject::_set(const StringName &p_name, const Variant &p_value) { + + if (!editable || !prop_values.has(p_name) || String(p_name).begins_with("Constants/")) + return false; + + prop_values[p_name] = p_value; + emit_signal("value_edited", remote_object_id, p_name, p_value); + return true; +} + +bool EditorDebuggerRemoteObject::_get(const StringName &p_name, Variant &r_ret) const { + + if (!prop_values.has(p_name)) + return false; + + r_ret = prop_values[p_name]; + return true; +} + +void EditorDebuggerRemoteObject::_get_property_list(List<PropertyInfo> *p_list) const { + + p_list->clear(); //sorry, no want category + for (const List<PropertyInfo>::Element *E = prop_list.front(); E; E = E->next()) { + p_list->push_back(E->get()); + } +} + +String EditorDebuggerRemoteObject::get_title() { + if (remote_object_id.is_valid()) + return TTR("Remote ") + String(type_name) + ": " + itos(remote_object_id); + else + return "<null>"; +} + +Variant EditorDebuggerRemoteObject::get_variant(const StringName &p_name) { + Variant var; + _get(p_name, var); + return var; +} + +void EditorDebuggerRemoteObject::_bind_methods() { + + ClassDB::bind_method(D_METHOD("get_title"), &EditorDebuggerRemoteObject::get_title); + ClassDB::bind_method(D_METHOD("get_variant"), &EditorDebuggerRemoteObject::get_variant); + ClassDB::bind_method(D_METHOD("clear"), &EditorDebuggerRemoteObject::clear); + ClassDB::bind_method(D_METHOD("get_remote_object_id"), &EditorDebuggerRemoteObject::get_remote_object_id); + + ADD_SIGNAL(MethodInfo("value_edited", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "property"), PropertyInfo("value"))); +} + +EditorDebuggerInspector::EditorDebuggerInspector() { + variables = memnew(EditorDebuggerRemoteObject); + variables->editable = false; +} + +EditorDebuggerInspector::~EditorDebuggerInspector() { + memdelete(variables); +} + +void EditorDebuggerInspector::_bind_methods() { + ClassDB::bind_method(D_METHOD("_object_edited", "name", "value"), &EditorDebuggerInspector::_object_edited); + ClassDB::bind_method(D_METHOD("_object_selected", "id"), &EditorDebuggerInspector::_object_selected); + ADD_SIGNAL(MethodInfo("object_selected", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("object_edited", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property"), PropertyInfo("value"))); + ADD_SIGNAL(MethodInfo("object_property_updated", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property"))); +} + +void EditorDebuggerInspector::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_POSTINITIALIZE: + connect_compat("object_id_selected", this, "_object_selected"); + break; + case NOTIFICATION_ENTER_TREE: + edit(variables); + break; + default: + break; + } +} + +void EditorDebuggerInspector::_object_edited(ObjectID p_id, const String &p_prop, const Variant &p_value) { + + emit_signal("object_edited", p_id, p_prop, p_value); +} + +void EditorDebuggerInspector::_object_selected(ObjectID p_object) { + + emit_signal("object_selected", p_object); +} + +ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) { + EditorDebuggerRemoteObject *debugObj = NULL; + + SceneDebuggerObject obj; + obj.deserialize(p_arr); + ERR_FAIL_COND_V(obj.id.is_null(), ObjectID()); + + if (remote_objects.has(obj.id)) { + debugObj = remote_objects[obj.id]; + } else { + debugObj = memnew(EditorDebuggerRemoteObject); + debugObj->remote_object_id = obj.id; + debugObj->type_name = obj.class_name; + remote_objects[obj.id] = debugObj; + debugObj->connect_compat("value_edited", this, "_object_edited"); + } + + int old_prop_size = debugObj->prop_list.size(); + + debugObj->prop_list.clear(); + int new_props_added = 0; + Set<String> changed; + for (int i = 0; i < obj.properties.size(); i++) { + + PropertyInfo &pinfo = obj.properties[i].first; + Variant &var = obj.properties[i].second; + + if (pinfo.type == Variant::OBJECT) { + if (var.get_type() == Variant::STRING) { + String path = var; + if (path.find("::") != -1) { + // built-in resource + String base_path = path.get_slice("::", 0); + if (ResourceLoader::get_resource_type(base_path) == "PackedScene") { + if (!EditorNode::get_singleton()->is_scene_open(base_path)) { + EditorNode::get_singleton()->load_scene(base_path); + } + } else { + EditorNode::get_singleton()->load_resource(base_path); + } + } + var = ResourceLoader::load(path); + + if (pinfo.hint_string == "Script") { + if (debugObj->get_script() != var) { + debugObj->set_script(REF()); + Ref<Script> script(var); + if (!script.is_null()) { + ScriptInstance *script_instance = script->placeholder_instance_create(debugObj); + debugObj->set_script_and_instance(var, script_instance); + } + } + } + } + } + + //always add the property, since props may have been added or removed + debugObj->prop_list.push_back(pinfo); + + if (!debugObj->prop_values.has(pinfo.name)) { + new_props_added++; + debugObj->prop_values[pinfo.name] = var; + } else { + + if (bool(Variant::evaluate(Variant::OP_NOT_EQUAL, debugObj->prop_values[pinfo.name], var))) { + debugObj->prop_values[pinfo.name] = var; + changed.insert(pinfo.name); + } + } + } + + if (old_prop_size == debugObj->prop_list.size() && new_props_added == 0) { + //only some may have changed, if so, then update those, if exist + for (Set<String>::Element *E = changed.front(); E; E = E->next()) { + emit_signal("object_property_updated", debugObj->remote_object_id, E->get()); + } + } else { + //full update, because props were added or removed + debugObj->update(); + } + return obj.id; +} + +void EditorDebuggerInspector::clear_cache() { + for (Map<ObjectID, EditorDebuggerRemoteObject *>::Element *E = remote_objects.front(); E; E = E->next()) { + EditorNode *editor = EditorNode::get_singleton(); + if (editor->get_editor_history()->get_current() == E->value()->get_instance_id()) { + editor->push_item(NULL); + } + memdelete(E->value()); + } + remote_objects.clear(); +} + +Object *EditorDebuggerInspector::get_object(ObjectID p_id) { + if (remote_objects.has(p_id)) + return remote_objects[p_id]; + return NULL; +} + +void EditorDebuggerInspector::add_stack_variable(const Array &p_array) { + + ScriptDebuggerRemote::ScriptStackVariable var; + var.deserialize(p_array); + String n = var.name; + Variant v = var.value; + + PropertyHint h = PROPERTY_HINT_NONE; + String hs = String(); + + if (v.get_type() == Variant::OBJECT) { + v = Object::cast_to<EncodedObjectAsID>(v)->get_object_id(); + h = PROPERTY_HINT_OBJECT_ID; + hs = "Object"; + } + String type; + switch (var.type) { + case 0: + type = "Locals/"; + break; + case 1: + type = "Members/"; + break; + case 2: + type = "Globals/"; + break; + default: + type = "Unknown/"; + } + + PropertyInfo pinfo; + pinfo.name = type + n; + pinfo.type = v.get_type(); + pinfo.hint = h; + pinfo.hint_string = hs; + + variables->prop_list.push_back(pinfo); + variables->prop_values[type + n] = v; + variables->update(); + edit(variables); +} + +void EditorDebuggerInspector::clear_stack_variables() { + variables->clear(); + variables->update(); +} + +String EditorDebuggerInspector::get_stack_variable(const String &p_var) { + return variables->get_variant(p_var); +} diff --git a/editor/debugger/editor_debugger_inspector.h b/editor/debugger/editor_debugger_inspector.h new file mode 100644 index 0000000000..e1dfbefcf3 --- /dev/null +++ b/editor/debugger/editor_debugger_inspector.h @@ -0,0 +1,98 @@ +/*************************************************************************/ +/* editor_debugger_inspector.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef EDITOR_DEBUGGER_INSPECTOR_H +#define EDITOR_DEBUGGER_INSPECTOR_H +#include "editor/editor_inspector.h" + +class EditorDebuggerRemoteObject : public Object { + + GDCLASS(EditorDebuggerRemoteObject, Object); + +protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + static void _bind_methods(); + +public: + bool editable = false; + ObjectID remote_object_id; + String type_name; + List<PropertyInfo> prop_list; + Map<StringName, Variant> prop_values; + + ObjectID get_remote_object_id() { return remote_object_id; }; + String get_title(); + + Variant get_variant(const StringName &p_name); + + void clear() { + prop_list.clear(); + prop_values.clear(); + } + + void update() { _change_notify(); } + + EditorDebuggerRemoteObject(){}; +}; + +class EditorDebuggerInspector : public EditorInspector { + + GDCLASS(EditorDebuggerInspector, EditorInspector); + +private: + ObjectID inspected_object_id; + Map<ObjectID, EditorDebuggerRemoteObject *> remote_objects; + EditorDebuggerRemoteObject *variables; + + void _object_selected(ObjectID p_object); + void _object_edited(ObjectID p_id, const String &p_prop, const Variant &p_value); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + EditorDebuggerInspector(); + ~EditorDebuggerInspector(); + + // Remote Object cache + ObjectID add_object(const Array &p_arr); + Object *get_object(ObjectID p_id); + void clear_cache(); + + // Stack Dump variables + String get_stack_variable(const String &p_var); + void add_stack_variable(const Array &p_arr); + void clear_stack_variables(); +}; + +#endif // EDITOR_DEBUGGER_INSPECTOR_H diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp new file mode 100644 index 0000000000..cf600312fe --- /dev/null +++ b/editor/debugger/editor_debugger_node.cpp @@ -0,0 +1,564 @@ +/*************************************************************************/ +/* editor_debugger_node.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "editor_debugger_node.h" + +#include "editor/debugger/editor_debugger_tree.h" +#include "editor/editor_log.h" +#include "editor/editor_node.h" +#include "editor/plugins/script_editor_plugin.h" + +template <typename Func> +void _for_all(EditorDebuggerNode *p_node, const Func &p_func) { + for (int i = 0; i < p_node->get_tab_count(); i++) { + ScriptEditorDebugger *dbg = Object::cast_to<ScriptEditorDebugger>(p_node->get_tab_control(i)); + ERR_FAIL_COND(!dbg); + p_func(dbg); + } +} + +EditorDebuggerNode *EditorDebuggerNode::singleton = NULL; + +EditorDebuggerNode::EditorDebuggerNode() { + if (!singleton) + singleton = this; + server.instance(); + EditorNode *editor = EditorNode::get_singleton(); + set_tab_align(TabAlign::ALIGN_LEFT); + add_style_override("panel", editor->get_gui_base()->get_stylebox("DebuggerPanel", "EditorStyles")); + add_style_override("tab_fg", editor->get_gui_base()->get_stylebox("DebuggerTabFG", "EditorStyles")); + add_style_override("tab_bg", editor->get_gui_base()->get_stylebox("DebuggerTabBG", "EditorStyles")); + + auto_switch_remote_scene_tree = EDITOR_DEF("debugger/auto_switch_to_remote_scene_tree", false); + _add_debugger("Debugger"); + + // Remote scene tree + remote_scene_tree = memnew(EditorDebuggerTree); + remote_scene_tree->connect_compat("object_selected", this, "_remote_object_requested"); + remote_scene_tree->connect_compat("save_node", this, "_save_node_requested"); + EditorNode::get_singleton()->get_scene_tree_dock()->add_remote_tree_editor(remote_scene_tree); + EditorNode::get_singleton()->get_scene_tree_dock()->connect_compat("remote_tree_selected", this, "request_remote_tree"); + + remote_scene_tree_timeout = EDITOR_DEF("debugger/remote_scene_tree_refresh_interval", 1.0); + inspect_edited_object_timeout = EDITOR_DEF("debugger/remote_inspect_refresh_interval", 0.2); + + editor->get_undo_redo()->set_method_notify_callback(_method_changeds, this); + editor->get_undo_redo()->set_property_notify_callback(_property_changeds, this); + editor->get_pause_button()->connect_compat("pressed", this, "_paused"); +} + +ScriptEditorDebugger *EditorDebuggerNode::_add_debugger(String p_name) { + ScriptEditorDebugger *node = memnew(ScriptEditorDebugger(EditorNode::get_singleton())); + node->set_name(p_name); + int id = get_tab_count(); + node->connect_compat("stop_requested", this, "_debugger_wants_stop", varray(id)); + node->connect_compat("stopped", this, "_debugger_stopped", varray(id)); + node->connect_compat("stack_frame_selected", this, "_stack_frame_selected", varray(id)); + node->connect_compat("error_selected", this, "_error_selected", varray(id)); + node->connect_compat("clear_execution", this, "_clear_execution"); + node->connect_compat("breaked", this, "_breaked", varray(id)); + node->connect_compat("remote_tree_updated", this, "_remote_tree_updated", varray(id)); + node->connect_compat("remote_object_updated", this, "_remote_object_updated", varray(id)); + node->connect_compat("remote_object_property_updated", this, "_remote_object_property_updated", varray(id)); + node->connect_compat("remote_object_requested", this, "_remote_object_requested", varray(id)); + add_child(node); + return node; +} + +void EditorDebuggerNode::_stack_frame_selected(int p_debugger) { + const ScriptEditorDebugger *dbg = get_debugger(p_debugger); + ERR_FAIL_COND(!dbg); + if (dbg != get_current_debugger()) + return; + _text_editor_stack_goto(dbg); +} + +void EditorDebuggerNode::_error_selected(const String &p_file, int p_line, int p_debugger) { + Ref<Script> s = ResourceLoader::load(p_file); + emit_signal("goto_script_line", s, p_line - 1); +} + +void EditorDebuggerNode::_text_editor_stack_goto(const ScriptEditorDebugger *p_debugger) { + const String file = p_debugger->get_stack_script_file(); + if (file.empty()) + return; + stack_script = ResourceLoader::load(file); + const int line = p_debugger->get_stack_script_line() - 1; + emit_signal("goto_script_line", stack_script, line); + emit_signal("set_execution", stack_script, line); + stack_script.unref(); // Why?!? +} + +void EditorDebuggerNode::_bind_methods() { + ClassDB::bind_method("_menu_option", &EditorDebuggerNode::_menu_option); + ClassDB::bind_method("_debugger_stopped", &EditorDebuggerNode::_debugger_stopped); + ClassDB::bind_method("_debugger_wants_stop", &EditorDebuggerNode::_debugger_wants_stop); + ClassDB::bind_method("_debugger_changed", &EditorDebuggerNode::_debugger_changed); + ClassDB::bind_method("_stack_frame_selected", &EditorDebuggerNode::_stack_frame_selected); + ClassDB::bind_method("_error_selected", &EditorDebuggerNode::_error_selected); + ClassDB::bind_method("_clear_execution", &EditorDebuggerNode::_clear_execution); + ClassDB::bind_method("_breaked", &EditorDebuggerNode::_breaked); + ClassDB::bind_method("start", &EditorDebuggerNode::start); + ClassDB::bind_method("stop", &EditorDebuggerNode::stop); + ClassDB::bind_method("_paused", &EditorDebuggerNode::_paused); + ClassDB::bind_method("request_remote_tree", &EditorDebuggerNode::request_remote_tree); + ClassDB::bind_method("_remote_tree_updated", &EditorDebuggerNode::_remote_tree_updated); + ClassDB::bind_method("_remote_object_updated", &EditorDebuggerNode::_remote_object_updated); + ClassDB::bind_method("_remote_object_property_updated", &EditorDebuggerNode::_remote_object_property_updated); + ClassDB::bind_method("_remote_object_requested", &EditorDebuggerNode::_remote_object_requested); + ClassDB::bind_method("_save_node_requested", &EditorDebuggerNode::_save_node_requested); + + // LiveDebug. + ClassDB::bind_method("live_debug_create_node", &EditorDebuggerNode::live_debug_create_node); + ClassDB::bind_method("live_debug_instance_node", &EditorDebuggerNode::live_debug_instance_node); + ClassDB::bind_method("live_debug_remove_node", &EditorDebuggerNode::live_debug_remove_node); + ClassDB::bind_method("live_debug_remove_and_keep_node", &EditorDebuggerNode::live_debug_remove_and_keep_node); + ClassDB::bind_method("live_debug_restore_node", &EditorDebuggerNode::live_debug_restore_node); + ClassDB::bind_method("live_debug_duplicate_node", &EditorDebuggerNode::live_debug_duplicate_node); + ClassDB::bind_method("live_debug_reparent_node", &EditorDebuggerNode::live_debug_reparent_node); + + ADD_SIGNAL(MethodInfo("goto_script_line")); + 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"))); +} + +EditorDebuggerRemoteObject *EditorDebuggerNode::get_inspected_remote_object() { + return Object::cast_to<EditorDebuggerRemoteObject>(ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_history()->get_current())); +} + +ScriptEditorDebugger *EditorDebuggerNode::get_debugger(int p_id) const { + return Object::cast_to<ScriptEditorDebugger>(get_tab_control(p_id)); +} + +ScriptEditorDebugger *EditorDebuggerNode::get_current_debugger() const { + return Object::cast_to<ScriptEditorDebugger>(get_tab_control(get_current_tab())); +} + +ScriptEditorDebugger *EditorDebuggerNode::get_default_debugger() const { + return Object::cast_to<ScriptEditorDebugger>(get_tab_control(0)); +} + +Error EditorDebuggerNode::start() { + stop(); + if (EDITOR_GET("run/output/always_open_output_on_play")) { + EditorNode::get_singleton()->make_bottom_panel_item_visible(EditorNode::get_log()); + } else { + EditorNode::get_singleton()->make_bottom_panel_item_visible(this); + } + + int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port"); + const Error err = server->listen(remote_port); + if (err != OK) { + EditorNode::get_log()->add_message(String("Error listening on port ") + itos(remote_port), EditorLog::MSG_TYPE_ERROR); + return err; + } + set_process(true); + EditorNode::get_log()->add_message("--- Debugging process started ---", EditorLog::MSG_TYPE_EDITOR); + return OK; +} + +void EditorDebuggerNode::stop() { + if (server->is_listening()) { + server->stop(); + EditorNode::get_log()->add_message("--- Debugging process stopped ---", EditorLog::MSG_TYPE_EDITOR); + } + // Also close all debugging sessions. + _for_all(this, [&](ScriptEditorDebugger *dbg) { + if (dbg->is_session_active()) + dbg->stop(); + }); + _break_state_changed(); + if (hide_on_stop) { + if (is_visible_in_tree()) + EditorNode::get_singleton()->hide_bottom_panel(); + } + breakpoints.clear(); + set_process(false); +} + +void EditorDebuggerNode::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_POSTINITIALIZE: { + connect_compat("tab_changed", this, "_debugger_changed"); + } break; + case NOTIFICATION_ENTER_TREE: { + EditorNode::get_singleton()->connect_compat("play_pressed", this, "start"); + EditorNode::get_singleton()->connect_compat("stop_pressed", this, "stop"); + } break; + case NOTIFICATION_EXIT_TREE: { + EditorNode::get_singleton()->disconnect_compat("play_pressed", this, "start"); + EditorNode::get_singleton()->disconnect_compat("stop_pressed", this, "stop"); + } break; + default: + break; + } + + if (p_what != NOTIFICATION_PROCESS || !server->is_listening()) + return; + + // Errors and warnings + int error_count = 0; + int warning_count = 0; + _for_all(this, [&](ScriptEditorDebugger *dbg) { + error_count += dbg->get_error_count(); + warning_count += dbg->get_warning_count(); + }); + + if (error_count != last_error_count || warning_count != last_warning_count) { + + _for_all(this, [&](ScriptEditorDebugger *dbg) { + dbg->update_tabs(); + }); + + if (error_count == 0 && warning_count == 0) { + debugger_button->set_text(TTR("Debugger")); + debugger_button->set_icon(Ref<Texture2D>()); + } else { + debugger_button->set_text(TTR("Debugger") + " (" + itos(error_count + warning_count) + ")"); + if (error_count == 0) { + debugger_button->set_icon(get_icon("Warning", "EditorIcons")); + } else { + debugger_button->set_icon(get_icon("Error", "EditorIcons")); + } + } + last_error_count = error_count; + last_warning_count = warning_count; + } + + // Remote scene tree update + remote_scene_tree_timeout -= get_process_delta_time(); + if (remote_scene_tree_timeout < 0) { + remote_scene_tree_timeout = EditorSettings::get_singleton()->get("debugger/remote_scene_tree_refresh_interval"); + if (remote_scene_tree->is_visible_in_tree()) { + get_current_debugger()->request_remote_tree(); + } + } + + // Remote inspector update + inspect_edited_object_timeout -= get_process_delta_time(); + if (inspect_edited_object_timeout < 0) { + inspect_edited_object_timeout = EditorSettings::get_singleton()->get("debugger/remote_inspect_refresh_interval"); + if (EditorDebuggerRemoteObject *obj = get_inspected_remote_object()) { + get_current_debugger()->request_remote_object(obj->remote_object_id); + } + } + + // Take connections. + if (server->is_connection_available()) { + ScriptEditorDebugger *debugger = NULL; + _for_all(this, [&](ScriptEditorDebugger *dbg) { + if (debugger || dbg->is_session_active()) + return; + debugger = dbg; + }); + if (debugger == NULL) { + if (get_tab_count() <= 4) { // Max 4 debugging sessions active. + debugger = _add_debugger("Session " + itos(get_tab_count())); + } else { + // We already have too many sessions, disconnecting new clients to prevent it from hanging. + // (Not keeping a reference to the connection will disconnect it) + server->take_connection(); + return; // Can't add, stop here. + } + } + + EditorNode::get_singleton()->get_pause_button()->set_disabled(false); + // Switch to remote tree view if so desired. + auto_switch_remote_scene_tree = (bool)EditorSettings::get_singleton()->get("debugger/auto_switch_to_remote_scene_tree"); + if (auto_switch_remote_scene_tree) { + EditorNode::get_singleton()->get_scene_tree_dock()->show_remote_tree(); + } + // Good to go. + EditorNode::get_singleton()->get_scene_tree_dock()->show_tab_buttons(); + debugger->set_editor_remote_tree(remote_scene_tree); + debugger->start(server->take_connection()); + // Send breakpoints. + for (Map<Breakpoint, bool>::Element *E = breakpoints.front(); E; E = E->next()) { + const Breakpoint &bp = E->key(); + debugger->set_breakpoint(bp.source, bp.line, E->get()); + } // Will arrive too late, how does the regular run work? + + debugger->update_live_edit_root(); + } +} + +void EditorDebuggerNode::_debugger_stopped(int p_id) { + ScriptEditorDebugger *dbg = get_debugger(p_id); + ERR_FAIL_COND(!dbg); + + bool found = false; + _for_all(this, [&](ScriptEditorDebugger *p_debugger) { + if (p_debugger->is_session_active()) + found = true; + }); + if (!found) { + EditorNode::get_singleton()->get_pause_button()->set_pressed(false); + EditorNode::get_singleton()->get_pause_button()->set_disabled(true); + EditorNode::get_singleton()->get_scene_tree_dock()->hide_remote_tree(); + EditorNode::get_singleton()->get_scene_tree_dock()->hide_tab_buttons(); + EditorNode::get_singleton()->notify_all_debug_sessions_exited(); + } +} + +void EditorDebuggerNode::_debugger_wants_stop(int p_id) { + // Ask editor to kill PID. + int pid = get_debugger(p_id)->get_remote_pid(); + if (pid) + EditorNode::get_singleton()->call_deferred("stop_child_process", pid); +} + +void EditorDebuggerNode::_debugger_changed(int p_tab) { + if (get_inspected_remote_object()) { + // Clear inspected object, you can only inspect objects in selected debugger. + // Hopefully, in the future, we will have one inspector per debugger. + EditorNode::get_singleton()->push_item(NULL); + } + if (remote_scene_tree->is_visible_in_tree()) { + get_current_debugger()->request_remote_tree(); + } + if (get_current_debugger()->is_breaked()) { + _text_editor_stack_goto(get_current_debugger()); + } +} + +void EditorDebuggerNode::set_script_debug_button(MenuButton *p_button) { + script_menu = p_button; + script_menu->set_text(TTR("Debug")); + script_menu->set_switch_on_hover(true); + PopupMenu *p = script_menu->get_popup(); + p->set_hide_on_window_lose_focus(true); + p->add_shortcut(ED_GET_SHORTCUT("debugger/step_into"), DEBUG_STEP); + p->add_shortcut(ED_GET_SHORTCUT("debugger/step_over"), DEBUG_NEXT); + p->add_separator(); + p->add_shortcut(ED_GET_SHORTCUT("debugger/break"), DEBUG_BREAK); + p->add_shortcut(ED_GET_SHORTCUT("debugger/continue"), DEBUG_CONTINUE); + p->add_separator(); + p->add_check_shortcut(ED_GET_SHORTCUT("debugger/keep_debugger_open"), DEBUG_SHOW_KEEP_OPEN); + p->add_check_shortcut(ED_GET_SHORTCUT("debugger/debug_with_external_editor"), DEBUG_WITH_EXTERNAL_EDITOR); + p->connect_compat("id_pressed", this, "_menu_option"); + + _break_state_changed(); + script_menu->show(); +} + +void EditorDebuggerNode::_break_state_changed() { + const bool breaked = get_current_debugger()->is_breaked(); + const bool can_debug = get_current_debugger()->is_debuggable(); + if (breaked) // Show debugger. + EditorNode::get_singleton()->make_bottom_panel_item_visible(this); + + // Update script menu. + if (!script_menu) + return; + PopupMenu *p = script_menu->get_popup(); + p->set_item_disabled(p->get_item_index(DEBUG_NEXT), !(breaked && can_debug)); + p->set_item_disabled(p->get_item_index(DEBUG_STEP), !(breaked && can_debug)); + p->set_item_disabled(p->get_item_index(DEBUG_BREAK), breaked); + p->set_item_disabled(p->get_item_index(DEBUG_CONTINUE), !breaked); +} + +void EditorDebuggerNode::_menu_option(int p_id) { + switch (p_id) { + case DEBUG_NEXT: { + debug_next(); + } break; + case DEBUG_STEP: { + debug_step(); + } break; + case DEBUG_BREAK: { + debug_break(); + } break; + case DEBUG_CONTINUE: { + debug_continue(); + } break; + + case DEBUG_SHOW_KEEP_OPEN: { + bool visible = script_menu->get_popup()->is_item_checked(script_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN)); + hide_on_stop = visible; + script_menu->get_popup()->set_item_checked(script_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN), !visible); + } break; + case DEBUG_WITH_EXTERNAL_EDITOR: { + bool checked = !script_menu->get_popup()->is_item_checked(script_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR)); + debug_with_external_editor = checked; + script_menu->get_popup()->set_item_checked(script_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR), checked); + } break; + } +} + +void EditorDebuggerNode::_paused() { + const bool paused = EditorNode::get_singleton()->get_pause_button()->is_pressed(); + _for_all(this, [&](ScriptEditorDebugger *dbg) { + if (paused && !dbg->is_breaked()) { + dbg->debug_break(); + } else if (!paused && dbg->is_breaked()) { + dbg->debug_continue(); + } + }); +} + +void EditorDebuggerNode::_breaked(bool p_breaked, bool p_can_debug, int p_debugger) { + if (get_current_debugger() != get_debugger(p_debugger)) { + if (!p_breaked) + return; + set_current_tab(p_debugger); + } + _break_state_changed(); + EditorNode::get_singleton()->get_pause_button()->set_pressed(p_breaked); + emit_signal("breaked", p_breaked, p_can_debug); +} + +bool EditorDebuggerNode::is_skip_breakpoints() const { + return get_default_debugger()->is_skip_breakpoints(); +} + +void EditorDebuggerNode::set_breakpoint(const String &p_path, int p_line, bool p_enabled) { + breakpoints[Breakpoint(p_path, p_line)] = p_enabled; + _for_all(this, [&](ScriptEditorDebugger *dbg) { + dbg->set_breakpoint(p_path, p_line, p_enabled); + }); +} + +void EditorDebuggerNode::reload_scripts() { + _for_all(this, [&](ScriptEditorDebugger *dbg) { + dbg->reload_scripts(); + }); +} + +// LiveEdit/Inspector +void EditorDebuggerNode::request_remote_tree() { + get_current_debugger()->request_remote_tree(); +} + +void EditorDebuggerNode::_remote_tree_updated(int p_debugger) { + if (p_debugger != get_current_tab()) + return; + remote_scene_tree->clear(); + remote_scene_tree->update_scene_tree(get_current_debugger()->get_remote_tree(), p_debugger); +} + +void EditorDebuggerNode::_remote_object_updated(ObjectID p_id, int p_debugger) { + if (p_debugger != get_current_tab()) + return; + if (EditorDebuggerRemoteObject *obj = get_inspected_remote_object()) { + if (obj->remote_object_id == p_id) + return; // Already being edited + } + + EditorNode::get_singleton()->push_item(get_current_debugger()->get_remote_object(p_id)); +} + +void EditorDebuggerNode::_remote_object_property_updated(ObjectID p_id, const String &p_property, int p_debugger) { + if (p_debugger != get_current_tab()) + return; + if (EditorDebuggerRemoteObject *obj = get_inspected_remote_object()) { + if (obj->remote_object_id != p_id) + return; + EditorNode::get_singleton()->get_inspector()->update_property(p_property); + } +} + +void EditorDebuggerNode::_remote_object_requested(ObjectID p_id, int p_debugger) { + if (p_debugger != get_current_tab()) + return; + inspect_edited_object_timeout = 0.7; // Temporarily disable timeout to avoid multiple requests. + get_current_debugger()->request_remote_object(p_id); +} + +void EditorDebuggerNode::_save_node_requested(ObjectID p_id, const String &p_file, int p_debugger) { + if (p_debugger != get_current_tab()) + return; + get_current_debugger()->save_node(p_id, p_file); +} + +// Remote inspector/edit. +void EditorDebuggerNode::_method_changeds(void *p_ud, Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE) { + if (!singleton) + return; + _for_all(singleton, [&](ScriptEditorDebugger *dbg) { + dbg->_method_changed(p_base, p_name, VARIANT_ARG_PASS); + }); +} + +void EditorDebuggerNode::_property_changeds(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value) { + if (!singleton) + return; + _for_all(singleton, [&](ScriptEditorDebugger *dbg) { + dbg->_property_changed(p_base, p_property, p_value); + }); +} + +// LiveDebug +void EditorDebuggerNode::set_live_debugging(bool p_enabled) { + + _for_all(this, [&](ScriptEditorDebugger *dbg) { + dbg->set_live_debugging(p_enabled); + }); +} +void EditorDebuggerNode::update_live_edit_root() { + _for_all(this, [&](ScriptEditorDebugger *dbg) { + dbg->update_live_edit_root(); + }); +} +void EditorDebuggerNode::live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name) { + _for_all(this, [&](ScriptEditorDebugger *dbg) { + dbg->live_debug_create_node(p_parent, p_type, p_name); + }); +} +void EditorDebuggerNode::live_debug_instance_node(const NodePath &p_parent, const String &p_path, const String &p_name) { + _for_all(this, [&](ScriptEditorDebugger *dbg) { + dbg->live_debug_instance_node(p_parent, p_path, p_name); + }); +} +void EditorDebuggerNode::live_debug_remove_node(const NodePath &p_at) { + _for_all(this, [&](ScriptEditorDebugger *dbg) { + dbg->live_debug_remove_node(p_at); + }); +} +void EditorDebuggerNode::live_debug_remove_and_keep_node(const NodePath &p_at, ObjectID p_keep_id) { + _for_all(this, [&](ScriptEditorDebugger *dbg) { + dbg->live_debug_remove_and_keep_node(p_at, p_keep_id); + }); +} +void EditorDebuggerNode::live_debug_restore_node(ObjectID p_id, const NodePath &p_at, int p_at_pos) { + _for_all(this, [&](ScriptEditorDebugger *dbg) { + dbg->live_debug_restore_node(p_id, p_at, p_at_pos); + }); +} +void EditorDebuggerNode::live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name) { + _for_all(this, [&](ScriptEditorDebugger *dbg) { + dbg->live_debug_duplicate_node(p_at, p_new_name); + }); +} +void EditorDebuggerNode::live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos) { + _for_all(this, [&](ScriptEditorDebugger *dbg) { + dbg->live_debug_reparent_node(p_at, p_new_place, p_new_name, p_at_pos); + }); +} diff --git a/editor/debugger/editor_debugger_node.h b/editor/debugger/editor_debugger_node.h new file mode 100644 index 0000000000..df833d4f60 --- /dev/null +++ b/editor/debugger/editor_debugger_node.h @@ -0,0 +1,176 @@ +/*************************************************************************/ +/* editor_debugger_node.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef EDITOR_DEBUGGER_NODE_H +#define EDITOR_DEBUGGER_NODE_H + +#include "core/io/tcp_server.h" +#include "editor/debugger/script_editor_debugger.h" +#include "scene/gui/button.h" +#include "scene/gui/tab_container.h" + +class EditorDebuggerTree; + +class EditorDebuggerNode : public TabContainer { + + GDCLASS(EditorDebuggerNode, TabContainer); + +private: + enum Options { + DEBUG_NEXT, + DEBUG_STEP, + DEBUG_BREAK, + DEBUG_CONTINUE, + DEBUG_SHOW_KEEP_OPEN, + DEBUG_WITH_EXTERNAL_EDITOR, + }; + + class Breakpoint { + public: + String source; + int line = 0; + + bool operator<(const Breakpoint &p_b) const { + if (line == p_b.line) + return line < p_b.line; + return line < p_b.line; + } + + Breakpoint(){}; + + Breakpoint(const String &p_source, int p_line) { + line = p_line; + source = p_source; + } + }; + + Ref<TCP_Server> server = NULL; + Button *debugger_button = NULL; + MenuButton *script_menu = NULL; + + Ref<Script> stack_script; // Why?!? + + int last_error_count = 0; + int last_warning_count = 0; + + float inspect_edited_object_timeout = 0; + EditorDebuggerTree *remote_scene_tree = NULL; + float remote_scene_tree_timeout = 0.0; + bool auto_switch_remote_scene_tree = false; + bool debug_with_external_editor = false; + bool hide_on_stop = true; + ScriptEditorDebugger::CameraOverride camera_override = ScriptEditorDebugger::OVERRIDE_NONE; + Map<Breakpoint, bool> breakpoints; + + ScriptEditorDebugger *_add_debugger(String p_name); + EditorDebuggerRemoteObject *get_inspected_remote_object(); + + friend class DebuggerEditorPlugin; + static EditorDebuggerNode *singleton; + EditorDebuggerNode(); + +protected: + void _debugger_stopped(int p_id); + void _debugger_wants_stop(int p_id); + void _debugger_changed(int p_tab); + void _remote_tree_updated(int p_debugger); + void _remote_object_updated(ObjectID p_id, int p_debugger); + void _remote_object_property_updated(ObjectID p_id, const String &p_property, int p_debugger); + void _remote_object_requested(ObjectID p_id, int p_debugger); + void _save_node_requested(ObjectID p_id, const String &p_file, int p_debugger); + + void _clear_execution(REF p_script) { + emit_signal("clear_execution", p_script); + } + + void _text_editor_stack_goto(const ScriptEditorDebugger *p_debugger); + void _stack_frame_selected(int p_debugger); + void _error_selected(const String &p_file, int p_line, int p_debugger); + void _breaked(bool p_breaked, bool p_can_debug, int p_debugger); + void _paused(); + void _break_state_changed(); + void _menu_option(int p_id); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + static EditorDebuggerNode *get_singleton() { return singleton; } + + ScriptEditorDebugger *get_current_debugger() const; + ScriptEditorDebugger *get_default_debugger() const; + ScriptEditorDebugger *get_debugger(int p_debugger) const; + + void debug_next() { get_default_debugger()->debug_next(); } + void debug_step() { get_default_debugger()->debug_step(); } + void debug_break() { get_default_debugger()->debug_break(); } + void debug_continue() { get_default_debugger()->debug_continue(); } + + void set_script_debug_button(MenuButton *p_button); + + void set_tool_button(Button *p_button) { + debugger_button = p_button; + } + + String get_var_value(const String &p_var) const { return get_default_debugger()->get_var_value(p_var); } + Ref<Script> get_dump_stack_script() const { return stack_script; } // Why do we need this? + + bool get_debug_with_external_editor() { return debug_with_external_editor; } + + bool is_skip_breakpoints() const; + void set_breakpoint(const String &p_path, int p_line, bool p_enabled); + void reload_scripts(); + + // Remote inspector/edit. + void request_remote_tree(); + static void _method_changeds(void *p_ud, Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE); + static void _property_changeds(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value); + + // LiveDebug + void set_live_debugging(bool p_enabled); + void update_live_edit_root(); + void live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name); + void live_debug_instance_node(const NodePath &p_parent, const String &p_path, const String &p_name); + void live_debug_remove_node(const NodePath &p_at); + void live_debug_remove_and_keep_node(const NodePath &p_at, ObjectID p_keep_id); + void live_debug_restore_node(ObjectID p_id, const NodePath &p_at, int p_at_pos); + void live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name); + void live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos); + + // Camera + void set_camera_override(ScriptEditorDebugger::CameraOverride p_override) { camera_override = p_override; } + ScriptEditorDebugger::CameraOverride get_camera_override() { return camera_override; } + + Error start(); + + void stop(); +}; +#endif // EDITOR_DEBUGGER_NODE_H diff --git a/editor/debugger/editor_debugger_tree.cpp b/editor/debugger/editor_debugger_tree.cpp new file mode 100644 index 0000000000..9ba5d0cbe1 --- /dev/null +++ b/editor/debugger/editor_debugger_tree.cpp @@ -0,0 +1,274 @@ +/*************************************************************************/ +/* editor_debugger_tree.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "editor_debugger_tree.h" + +#include "editor/editor_node.h" +#include "scene/debugger/scene_debugger.h" +#include "scene/resources/packed_scene.h" + +EditorDebuggerTree::EditorDebuggerTree() { + set_v_size_flags(SIZE_EXPAND_FILL); + set_allow_rmb_select(true); + + // Popup + item_menu = memnew(PopupMenu); + item_menu->connect_compat("id_pressed", this, "_item_menu_id_pressed"); + add_child(item_menu); + + // File Dialog + file_dialog = memnew(EditorFileDialog); + file_dialog->connect_compat("file_selected", this, "_file_selected"); + add_child(file_dialog); +} + +void EditorDebuggerTree::_notification(int p_what) { + if (p_what == NOTIFICATION_POSTINITIALIZE) { + connect_compat("cell_selected", this, "_scene_tree_selected"); + connect_compat("item_collapsed", this, "_scene_tree_folded"); + connect_compat("item_rmb_selected", this, "_scene_tree_rmb_selected"); + } +} + +void EditorDebuggerTree::_bind_methods() { + ClassDB::bind_method(D_METHOD("_scene_tree_selected"), &EditorDebuggerTree::_scene_tree_selected); + ClassDB::bind_method(D_METHOD("_scene_tree_folded"), &EditorDebuggerTree::_scene_tree_folded); + ClassDB::bind_method(D_METHOD("_scene_tree_rmb_selected"), &EditorDebuggerTree::_scene_tree_rmb_selected); + ClassDB::bind_method(D_METHOD("_item_menu_id_pressed"), &EditorDebuggerTree::_item_menu_id_pressed); + ClassDB::bind_method(D_METHOD("_file_selected"), &EditorDebuggerTree::_file_selected); + ADD_SIGNAL(MethodInfo("object_selected", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::INT, "debugger"))); + ADD_SIGNAL(MethodInfo("save_node", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "filename"), PropertyInfo(Variant::INT, "debugger"))); +} + +void EditorDebuggerTree::_scene_tree_selected() { + + if (updating_scene_tree) { + return; + } + + TreeItem *item = get_selected(); + if (!item) { + return; + } + + inspected_object_id = uint64_t(item->get_metadata(0)); + + emit_signal("object_selected", inspected_object_id, debugger_id); +} + +void EditorDebuggerTree::_scene_tree_folded(Object *p_obj) { + + if (updating_scene_tree) { + + return; + } + TreeItem *item = Object::cast_to<TreeItem>(p_obj); + + if (!item) + return; + + ObjectID id = ObjectID(uint64_t(item->get_metadata(0))); + if (unfold_cache.has(id)) { + unfold_cache.erase(id); + } else { + unfold_cache.insert(id); + } +} + +void EditorDebuggerTree::_scene_tree_rmb_selected(const Vector2 &p_position) { + + TreeItem *item = get_item_at_position(p_position); + if (!item) + return; + + item->select(0); + + item_menu->clear(); + item_menu->add_icon_item(get_icon("CreateNewSceneFrom", "EditorIcons"), TTR("Save Branch as Scene"), ITEM_MENU_SAVE_REMOTE_NODE); + item_menu->add_icon_item(get_icon("CopyNodePath", "EditorIcons"), TTR("Copy Node Path"), ITEM_MENU_COPY_NODE_PATH); + item_menu->set_global_position(get_global_mouse_position()); + item_menu->popup(); +} + +/// Populates inspect_scene_tree given data in nodes as a flat list, encoded depth first. +/// +/// Given a nodes array like [R,A,B,C,D,E] the following Tree will be generated, assuming +/// filter is an empty String, R and A child count are 2, B is 1 and C, D and E are 0. +/// +/// R +/// |-A +/// | |-B +/// | | |-C +/// | | +/// | |-D +/// | +/// |-E +/// +void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int p_debugger) { + updating_scene_tree = true; + const String last_path = get_selected_path(); + const String filter = EditorNode::get_singleton()->get_scene_tree_dock()->get_filter(); + + // Nodes are in a flatten list, depth first. Use a stack of parents, avoid recursion. + List<Pair<TreeItem *, int> > parents; + for (int i = 0; i < p_tree->nodes.size(); i++) { + TreeItem *parent = NULL; + if (parents.size()) { // Find last parent. + Pair<TreeItem *, int> &p = parents[0]; + parent = p.first; + if (!(--p.second)) { // If no child left, remove it. + parents.pop_front(); + } + } + // Add this node. + const SceneDebuggerTree::RemoteNode &node = p_tree->nodes[i]; + TreeItem *item = create_item(parent); + item->set_text(0, node.name); + item->set_tooltip(0, TTR("Type:") + " " + node.type_name); + Ref<Texture2D> icon = EditorNode::get_singleton()->get_class_icon(node.type_name, ""); + if (icon.is_valid()) { + item->set_icon(0, icon); + } + item->set_metadata(0, node.id); + + // Set current item as collapsed if necessary (root is never collapsed) + if (parent) { + if (!unfold_cache.has(node.id)) { + item->set_collapsed(true); + } + } + // Select previously selected node. + if (debugger_id == p_debugger) { // Can use remote id. + if (node.id == inspected_object_id) { + item->select(0); + } + } else { // Must use path + if (last_path == _get_path(item)) { + updating_scene_tree = false; // Force emission of new selection + item->select(0); + updating_scene_tree = true; + } + } + + // Add in front of the parents stack if children are expected. + if (node.child_count) { + parents.push_front(Pair<TreeItem *, int>(item, node.child_count)); + } else { + // Apply filters. + while (parent) { + const bool had_siblings = item->get_prev() || item->get_next(); + if (filter.is_subsequence_ofi(item->get_text(0))) + break; // Filter matches, must survive. + parent->remove_child(item); + memdelete(item); + if (had_siblings) + break; // Parent must survive. + item = parent; + parent = item->get_parent(); + // Check if parent expects more children. + for (int j = 0; j < parents.size(); j++) { + if (parents[j].first == item) { + parent = NULL; + break; // Might have more children. + } + } + } + } + } + debugger_id = p_debugger; // Needed by hook, could be avoided if every debugger had its own tree + updating_scene_tree = false; +} + +String EditorDebuggerTree::get_selected_path() { + if (!get_selected()) + return ""; + return _get_path(get_selected()); +} + +String EditorDebuggerTree::_get_path(TreeItem *p_item) { + ERR_FAIL_COND_V(!p_item, ""); + + if (p_item->get_parent() == NULL) { + return "/root"; + } + String text = p_item->get_text(0); + TreeItem *cur = p_item->get_parent(); + while (cur) { + text = cur->get_text(0) + "/" + text; + cur = cur->get_parent(); + } + return "/" + text; +} + +void EditorDebuggerTree::_item_menu_id_pressed(int p_option) { + + switch (p_option) { + + case ITEM_MENU_SAVE_REMOTE_NODE: { + + file_dialog->set_access(EditorFileDialog::ACCESS_RESOURCES); + file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE); + + List<String> extensions; + Ref<PackedScene> sd = memnew(PackedScene); + ResourceSaver::get_recognized_extensions(sd, &extensions); + file_dialog->clear_filters(); + for (int i = 0; i < extensions.size(); i++) { + file_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper()); + } + + file_dialog->popup_centered_ratio(); + } break; + case ITEM_MENU_COPY_NODE_PATH: { + + String text = get_selected_path(); + if (text.empty()) { + return; + } else if (text == "/root") { + text = "."; + } else { + text = text.replace("/root/", ""); + int slash = text.find("/"); + if (slash < 0) { + text = "."; + } else { + text = text.substr(slash + 1); + } + } + OS::get_singleton()->set_clipboard(text); + } break; + } +} + +void EditorDebuggerTree::_file_selected(const String &p_file) { + if (inspected_object_id.is_null()) + return; + emit_signal("save_node", inspected_object_id, p_file, debugger_id); +} diff --git a/editor/debugger/editor_debugger_tree.h b/editor/debugger/editor_debugger_tree.h new file mode 100644 index 0000000000..d9084bc596 --- /dev/null +++ b/editor/debugger/editor_debugger_tree.h @@ -0,0 +1,74 @@ +/*************************************************************************/ +/* editor_debugger_tree.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "scene/gui/tree.h" + +#ifndef EDITOR_DEBUGGER_TREE_H +#define EDITOR_DEBUGGER_TREE_H + +class SceneDebuggerTree; +class EditorFileDialog; + +class EditorDebuggerTree : public Tree { + + GDCLASS(EditorDebuggerTree, Tree); + +private: + enum ItemMenu { + ITEM_MENU_SAVE_REMOTE_NODE, + ITEM_MENU_COPY_NODE_PATH, + }; + + ObjectID inspected_object_id; + int debugger_id = 0; + bool updating_scene_tree = false; + Set<ObjectID> unfold_cache; + PopupMenu *item_menu = NULL; + EditorFileDialog *file_dialog = NULL; + + String _get_path(TreeItem *p_item); + void _scene_tree_folded(Object *p_obj); + void _scene_tree_selected(); + void _scene_tree_rmb_selected(const Vector2 &p_position); + void _item_menu_id_pressed(int p_option); + void _file_selected(const String &p_file); + +protected: + static void _bind_methods(); + void _notification(int p_what); + +public: + String get_selected_path(); + ObjectID get_selected_object(); + int get_current_debugger(); // Would love to have one tree for every debugger. + void update_scene_tree(const SceneDebuggerTree *p_tree, int p_debugger); + EditorDebuggerTree(); +}; +#endif // EDITOR_DEBUGGER_TREE_H diff --git a/editor/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index cc8fc0a3b9..d113fe8718 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -32,18 +32,20 @@ #include "core/io/marshalls.h" #include "core/project_settings.h" +#include "core/script_debugger_remote.h" #include "core/ustring.h" #include "editor/editor_log.h" #include "editor/editor_network_profiler.h" +#include "editor/editor_node.h" +#include "editor/editor_profiler.h" +#include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "editor/editor_visual_profiler.h" #include "editor/plugins/canvas_item_editor_plugin.h" #include "editor/plugins/spatial_editor_plugin.h" -#include "editor_node.h" -#include "editor_profiler.h" -#include "editor_scale.h" -#include "editor_settings.h" +#include "editor/property_editor.h" #include "main/performance.h" -#include "property_editor.h" +#include "scene/debugger/scene_debugger.h" #include "scene/gui/dialogs.h" #include "scene/gui/label.h" #include "scene/gui/line_edit.h" @@ -56,149 +58,14 @@ #include "scene/gui/tree.h" #include "scene/resources/packed_scene.h" -class ScriptEditorDebuggerVariables : public Object { - - GDCLASS(ScriptEditorDebuggerVariables, Object); - - List<PropertyInfo> props; - Map<StringName, Variant> values; - -protected: - bool _set(const StringName &p_name, const Variant &p_value) { - - return false; - } - - bool _get(const StringName &p_name, Variant &r_ret) const { - - if (!values.has(p_name)) - return false; - r_ret = values[p_name]; - return true; - } - void _get_property_list(List<PropertyInfo> *p_list) const { - - for (const List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) - p_list->push_back(E->get()); - } - -public: - void clear() { - - props.clear(); - values.clear(); - } - - String get_var_value(const String &p_var) const { - - for (Map<StringName, Variant>::Element *E = values.front(); E; E = E->next()) { - String v = E->key().operator String().get_slice("/", 1); - if (v == p_var) - return E->get(); - } - - return ""; - } - - void add_property(const String &p_name, const Variant &p_value, const PropertyHint &p_hint, const String p_hint_string) { - - PropertyInfo pinfo; - pinfo.name = p_name; - pinfo.type = p_value.get_type(); - pinfo.hint = p_hint; - pinfo.hint_string = p_hint_string; - props.push_back(pinfo); - values[p_name] = p_value; - } - - void update() { - _change_notify(); - } - - ScriptEditorDebuggerVariables() { - } -}; - -class ScriptEditorDebuggerInspectedObject : public Object { - - GDCLASS(ScriptEditorDebuggerInspectedObject, Object); - -protected: - bool _set(const StringName &p_name, const Variant &p_value) { - - if (!prop_values.has(p_name) || String(p_name).begins_with("Constants/")) - return false; - - prop_values[p_name] = p_value; - emit_signal("value_edited", p_name, p_value); - return true; - } - - bool _get(const StringName &p_name, Variant &r_ret) const { - - if (!prop_values.has(p_name)) - return false; - - r_ret = prop_values[p_name]; - return true; - } - - void _get_property_list(List<PropertyInfo> *p_list) const { - - p_list->clear(); //sorry, no want category - for (const List<PropertyInfo>::Element *E = prop_list.front(); E; E = E->next()) { - p_list->push_back(E->get()); - } - } - - static void _bind_methods() { - - ClassDB::bind_method(D_METHOD("get_title"), &ScriptEditorDebuggerInspectedObject::get_title); - ClassDB::bind_method(D_METHOD("get_variant"), &ScriptEditorDebuggerInspectedObject::get_variant); - ClassDB::bind_method(D_METHOD("clear"), &ScriptEditorDebuggerInspectedObject::clear); - ClassDB::bind_method(D_METHOD("get_remote_object_id"), &ScriptEditorDebuggerInspectedObject::get_remote_object_id); - - ADD_SIGNAL(MethodInfo("value_edited")); - } - -public: - String type_name; - ObjectID remote_object_id; - List<PropertyInfo> prop_list; - Map<StringName, Variant> prop_values; - - ObjectID get_remote_object_id() { - return remote_object_id; - } - - String get_title() { - if (remote_object_id.is_valid()) - return TTR("Remote ") + String(type_name) + ": " + itos(remote_object_id); - else - return "<null>"; - } - Variant get_variant(const StringName &p_name) { - - Variant var; - _get(p_name, var); - return var; - } - - void clear() { - - prop_list.clear(); - prop_values.clear(); - } - void update() { - _change_notify(); - } - void update_single(const char *p_prop) { - _change_notify(p_prop); - } - - ScriptEditorDebuggerInspectedObject() { +void ScriptEditorDebugger::_put_msg(String p_message, Array p_data) { + if (is_session_active()) { + Array msg; + msg.push_back(p_message); + msg.push_back(p_data); + ppeer->put_var(msg); } -}; +} void ScriptEditorDebugger::debug_copy() { String msg = reason->get_text(); @@ -213,285 +80,149 @@ void ScriptEditorDebugger::debug_skip_breakpoints() { else skip_breakpoints->set_icon(get_icon("DebugSkipBreakpointsOff", "EditorIcons")); - if (connection.is_valid()) { - Array msg; - msg.push_back("set_skip_breakpoints"); - msg.push_back(skip_breakpoints_value); - ppeer->put_var(msg); - } + Array msg; + msg.push_back(skip_breakpoints_value); + _put_msg("set_skip_breakpoints", msg); } void ScriptEditorDebugger::debug_next() { ERR_FAIL_COND(!breaked); - ERR_FAIL_COND(connection.is_null()); - ERR_FAIL_COND(!connection->is_connected_to_host()); - Array msg; - msg.push_back("next"); - ppeer->put_var(msg); + + _put_msg("next", Array()); _clear_execution(); - stack_dump->clear(); } void ScriptEditorDebugger::debug_step() { ERR_FAIL_COND(!breaked); - ERR_FAIL_COND(connection.is_null()); - ERR_FAIL_COND(!connection->is_connected_to_host()); - Array msg; - msg.push_back("step"); - ppeer->put_var(msg); + _put_msg("step", Array()); _clear_execution(); - stack_dump->clear(); } void ScriptEditorDebugger::debug_break() { ERR_FAIL_COND(breaked); - ERR_FAIL_COND(connection.is_null()); - ERR_FAIL_COND(!connection->is_connected_to_host()); - Array msg; - msg.push_back("break"); - ppeer->put_var(msg); + _put_msg("break", Array()); } void ScriptEditorDebugger::debug_continue() { ERR_FAIL_COND(!breaked); - ERR_FAIL_COND(connection.is_null()); - ERR_FAIL_COND(!connection->is_connected_to_host()); - OS::get_singleton()->enable_for_stealing_focus(EditorNode::get_singleton()->get_child_process_id()); + // Allow focus stealing only if we actually run this client for security. + if (remote_pid && EditorNode::get_singleton()->has_child_process(remote_pid)) + OS::get_singleton()->enable_for_stealing_focus(remote_pid); - Array msg; _clear_execution(); - msg.push_back("continue"); - ppeer->put_var(msg); + _put_msg("continue", Array()); } -void ScriptEditorDebugger::_scene_tree_folded(Object *obj) { - - if (updating_scene_tree) { - - return; - } - TreeItem *item = Object::cast_to<TreeItem>(obj); - - if (!item) - return; - - ObjectID id = item->get_metadata(0); - if (unfold_cache.has(id)) { - unfold_cache.erase(id); +void ScriptEditorDebugger::update_tabs() { + if (error_count == 0 && warning_count == 0) { + errors_tab->set_name(TTR("Errors")); + tabs->set_tab_icon(errors_tab->get_index(), Ref<Texture2D>()); } else { - unfold_cache.insert(id); + errors_tab->set_name(TTR("Errors") + " (" + itos(error_count + warning_count) + ")"); + if (error_count == 0) { + tabs->set_tab_icon(errors_tab->get_index(), get_icon("Warning", "EditorIcons")); + } else { + tabs->set_tab_icon(errors_tab->get_index(), get_icon("Error", "EditorIcons")); + } } } -void ScriptEditorDebugger::_scene_tree_selected() { +void ScriptEditorDebugger::save_node(ObjectID p_id, const String &p_file) { + Array msg; + msg.push_back(p_id); + msg.push_back(p_file); + _put_msg("save_node", msg); +} - if (updating_scene_tree) { +void ScriptEditorDebugger::_file_selected(const String &p_file) { + Error err; + FileAccessRef file = FileAccess::open(p_file, FileAccess::WRITE, &err); + if (err != OK) { + ERR_PRINT("Failed to open " + p_file); return; } - TreeItem *item = inspect_scene_tree->get_selected(); - if (!item) { + Vector<String> line; + line.resize(Performance::MONITOR_MAX); - return; + // signatures + for (int i = 0; i < Performance::MONITOR_MAX; i++) { + line.write[i] = Performance::get_singleton()->get_monitor_name(Performance::Monitor(i)); } + file->store_csv_line(line); - inspected_object_id = item->get_metadata(0).operator ObjectID(); - - Array msg; - msg.push_back("inspect_object"); - msg.push_back(inspected_object_id); - ppeer->put_var(msg); -} + // values + List<Vector<float> >::Element *E = perf_history.back(); + while (E) { -void ScriptEditorDebugger::_scene_tree_rmb_selected(const Vector2 &p_position) { + Vector<float> &perf_data = E->get(); + for (int i = 0; i < perf_data.size(); i++) { - TreeItem *item = inspect_scene_tree->get_item_at_position(p_position); - if (!item) - return; - - item->select(0); + line.write[i] = String::num_real(perf_data[i]); + } + file->store_csv_line(line); + E = E->prev(); + } + file->store_string("\n"); - item_menu->clear(); - item_menu->add_icon_item(get_icon("CreateNewSceneFrom", "EditorIcons"), TTR("Save Branch as Scene"), ITEM_MENU_SAVE_REMOTE_NODE); - item_menu->add_icon_item(get_icon("CopyNodePath", "EditorIcons"), TTR("Copy Node Path"), ITEM_MENU_COPY_NODE_PATH); - item_menu->set_global_position(get_global_mouse_position()); - item_menu->popup(); + Vector<Vector<String> > profiler_data = profiler->get_data_as_csv(); + for (int i = 0; i < profiler_data.size(); i++) { + file->store_csv_line(profiler_data[i]); + } } -void ScriptEditorDebugger::_file_selected(const String &p_file) { - switch (file_dialog_mode) { - case SAVE_NODE: { - Array msg; - msg.push_back("save_node"); - msg.push_back(inspected_object_id); - msg.push_back(p_file); - ppeer->put_var(msg); - } break; - case SAVE_CSV: { - Error err; - FileAccessRef file = FileAccess::open(p_file, FileAccess::WRITE, &err); +void ScriptEditorDebugger::request_remote_tree() { - if (err != OK) { - ERR_PRINT("Failed to open " + p_file); - return; - } - Vector<String> line; - line.resize(Performance::MONITOR_MAX); - - // signatures - for (int i = 0; i < Performance::MONITOR_MAX; i++) { - line.write[i] = Performance::get_singleton()->get_monitor_name(Performance::Monitor(i)); - } - file->store_csv_line(line); - - // values - List<Vector<float> >::Element *E = perf_history.back(); - while (E) { - - Vector<float> &perf_data = E->get(); - for (int i = 0; i < perf_data.size(); i++) { - - line.write[i] = String::num_real(perf_data[i]); - } - file->store_csv_line(line); - E = E->prev(); - } - file->store_string("\n"); - - Vector<Vector<String> > profiler_data = profiler->get_data_as_csv(); - for (int i = 0; i < profiler_data.size(); i++) { - file->store_csv_line(profiler_data[i]); - } + _put_msg("request_scene_tree", Array()); +} - } break; - } +const SceneDebuggerTree *ScriptEditorDebugger::get_remote_tree() { + return scene_tree; } -void ScriptEditorDebugger::_scene_tree_property_value_edited(const String &p_prop, const Variant &p_value) { +void ScriptEditorDebugger::update_remote_object(ObjectID p_obj_id, const String &p_prop, const Variant &p_value) { Array msg; - msg.push_back("set_object_property"); - msg.push_back(inspected_object_id); + msg.push_back(p_obj_id); msg.push_back(p_prop); msg.push_back(p_value); - ppeer->put_var(msg); - inspect_edited_object_timeout = 0.7; //avoid annoyance, don't request soon after editing + _put_msg("set_object_property", msg); } -void ScriptEditorDebugger::_scene_tree_property_select_object(ObjectID p_object) { +void ScriptEditorDebugger::request_remote_object(ObjectID p_obj_id) { - inspected_object_id = p_object; + ERR_FAIL_COND(p_obj_id.is_null()); Array msg; - msg.push_back("inspect_object"); - msg.push_back(inspected_object_id); - ppeer->put_var(msg); + msg.push_back(p_obj_id); + _put_msg("inspect_object", msg); } -void ScriptEditorDebugger::_scene_tree_request() { - - ERR_FAIL_COND(connection.is_null()); - ERR_FAIL_COND(!connection->is_connected_to_host()); - - Array msg; - msg.push_back("request_scene_tree"); - ppeer->put_var(msg); -} - -/// Populates inspect_scene_tree recursively given data in nodes. -/// Nodes is an array containing 4 elements for each node, it follows this pattern: -/// nodes[i] == number of direct children of this node -/// nodes[i + 1] == node name -/// nodes[i + 2] == node class -/// nodes[i + 3] == node instance id -/// -/// Returns the number of items parsed in nodes from current_index. -/// -/// Given a nodes array like [R,A,B,C,D,E] the following Tree will be generated, assuming -/// filter is an empty String, R and A child count are 2, B is 1 and C, D and E are 0. -/// -/// R -/// |-A -/// | |-B -/// | | |-C -/// | | -/// | |-D -/// | -/// |-E -/// -int ScriptEditorDebugger::_update_scene_tree(TreeItem *parent, const Array &nodes, int current_index) { - String filter = EditorNode::get_singleton()->get_scene_tree_dock()->get_filter(); - String item_text = nodes[current_index + 1]; - String item_type = nodes[current_index + 2]; - bool keep = filter.is_subsequence_ofi(item_text); - - TreeItem *item = inspect_scene_tree->create_item(parent); - item->set_text(0, item_text); - item->set_tooltip(0, TTR("Type:") + " " + item_type); - ObjectID id = nodes[current_index + 3].operator ObjectID(); - Ref<Texture2D> icon = EditorNode::get_singleton()->get_class_icon(nodes[current_index + 2], ""); - if (icon.is_valid()) { - item->set_icon(0, icon); - } - item->set_metadata(0, id); - - if (id == inspected_object_id) { - TreeItem *cti = item->get_parent(); - while (cti) { - cti->set_collapsed(false); - cti = cti->get_parent(); - } - item->select(0); - } - - // Set current item as collapsed if necessary - if (parent) { - if (!unfold_cache.has(id)) { - item->set_collapsed(true); - } - } +Object *ScriptEditorDebugger::get_remote_object(ObjectID p_id) { + return inspector->get_object(p_id); +} - int children_count = nodes[current_index]; - // Tracks the total number of items parsed in nodes, this is used to skips nodes that - // are not direct children of the current node since we can't know in advance the total - // number of children, direct and not, of a node without traversing the nodes array previously. - // Keeping track of this allows us to build our remote scene tree by traversing the node - // array just once. - int items_count = 1; - for (int i = 0; i < children_count; i++) { - // Called for each direct child of item. - // Direct children of current item might not be adjacent so items_count must - // be incremented by the number of items parsed until now, otherwise we would not - // be able to access the next child of the current item. - // items_count is multiplied by 4 since that's the number of elements in the nodes - // array needed to represent a single node. - items_count += _update_scene_tree(item, nodes, current_index + items_count * 4); - } +void ScriptEditorDebugger::_remote_object_selected(ObjectID p_id) { + emit_signal("remote_object_requested", p_id); +} - // If item has not children and should not be kept delete it - if (!keep && !item->get_children() && parent) { - parent->remove_child(item); - memdelete(item); - } +void ScriptEditorDebugger::_remote_object_edited(ObjectID p_id, const String &p_prop, const Variant &p_value) { + update_remote_object(p_id, p_prop, p_value); + request_remote_object(p_id); +} - return items_count; +void ScriptEditorDebugger::_remote_object_property_updated(ObjectID p_id, const String &p_property) { + emit_signal("remote_object_property_updated", p_id, p_property); } void ScriptEditorDebugger::_video_mem_request() { - if (connection.is_null() || !connection->is_connected_to_host()) { - // Video RAM usage is only available while a project is being debugged. - return; - } - - Array msg; - msg.push_back("request_video_mem"); - ppeer->put_var(msg); + _put_msg("request_video_mem", Array()); } Size2 ScriptEditorDebugger::get_minimum_size() const { @@ -504,184 +235,72 @@ Size2 ScriptEditorDebugger::get_minimum_size() const { void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_data) { if (p_msg == "debug_enter") { - Array msg; - msg.push_back("get_stack_dump"); - ppeer->put_var(msg); + + _put_msg("get_stack_dump", Array()); + ERR_FAIL_COND(p_data.size() != 2); bool can_continue = p_data[0]; String error = p_data[1]; - step->set_disabled(!can_continue); - next->set_disabled(!can_continue); - _set_reason_text(error, MESSAGE_ERROR); - copy->set_disabled(false); breaked = true; - dobreak->set_disabled(true); - docontinue->set_disabled(false); + can_debug = can_continue; + _update_buttons_state(); + _set_reason_text(error, MESSAGE_ERROR); emit_signal("breaked", true, can_continue); OS::get_singleton()->move_window_to_foreground(); if (error != "") { tabs->set_current_tab(0); } profiler->set_enabled(false); - EditorNode::get_singleton()->get_pause_button()->set_pressed(true); - EditorNode::get_singleton()->make_bottom_panel_item_visible(this); - _clear_remote_objects(); + inspector->clear_cache(); // Take a chance to force remote objects update. } else if (p_msg == "debug_exit") { breaked = false; + can_debug = false; _clear_execution(); - copy->set_disabled(true); - step->set_disabled(true); - next->set_disabled(true); - reason->set_text(""); - reason->set_tooltip(""); - back->set_disabled(true); - forward->set_disabled(true); - dobreak->set_disabled(false); - docontinue->set_disabled(true); - emit_signal("breaked", false, false, Variant()); + _update_buttons_state(); + _set_reason_text(TTR("Execution resumed."), MESSAGE_SUCCESS); + emit_signal("breaked", false, false); profiler->set_enabled(true); profiler->disable_seeking(); - EditorNode::get_singleton()->get_pause_button()->set_pressed(false); + } else if (p_msg == "message:set_pid") { + + ERR_FAIL_COND(p_data.size() < 1); + remote_pid = p_data[0]; } else if (p_msg == "message:click_ctrl") { + ERR_FAIL_COND(p_data.size() < 2); clicked_ctrl->set_text(p_data[0]); clicked_ctrl_type->set_text(p_data[1]); - } else if (p_msg == "message:scene_tree") { - inspect_scene_tree->clear(); - Map<int, TreeItem *> lv; - - updating_scene_tree = true; - - _update_scene_tree(NULL, p_data, 0); - - updating_scene_tree = false; - - le_clear->set_disabled(false); - le_set->set_disabled(false); + scene_tree->nodes.clear(); + scene_tree->deserialize(p_data); + emit_signal("remote_tree_updated"); + _update_buttons_state(); } else if (p_msg == "message:inspect_object") { - ScriptEditorDebuggerInspectedObject *debugObj = NULL; - - ObjectID id = p_data[0]; - String type = p_data[1]; - Array properties = p_data[2]; - - if (remote_objects.has(id)) { - debugObj = remote_objects[id]; - } else { - debugObj = memnew(ScriptEditorDebuggerInspectedObject); - debugObj->remote_object_id = id; - debugObj->type_name = type; - remote_objects[id] = debugObj; - debugObj->connect_compat("value_edited", this, "_scene_tree_property_value_edited"); - } - - int old_prop_size = debugObj->prop_list.size(); - - debugObj->prop_list.clear(); - int new_props_added = 0; - Set<String> changed; - for (int i = 0; i < properties.size(); i++) { - - Array prop = properties[i]; - if (prop.size() != 6) - continue; - - PropertyInfo pinfo; - pinfo.name = prop[0]; - pinfo.type = Variant::Type(int(prop[1])); - pinfo.hint = PropertyHint(int(prop[2])); - pinfo.hint_string = prop[3]; - pinfo.usage = PropertyUsageFlags(int(prop[4])); - Variant var = prop[5]; - - if (pinfo.type == Variant::OBJECT) { - if (var.is_zero()) { - var = RES(); - } else if (var.get_type() == Variant::STRING) { - String path = var; - if (path.find("::") != -1) { - // built-in resource - String base_path = path.get_slice("::", 0); - if (ResourceLoader::get_resource_type(base_path) == "PackedScene") { - if (!EditorNode::get_singleton()->is_scene_open(base_path)) { - EditorNode::get_singleton()->load_scene(base_path); - } - } else { - EditorNode::get_singleton()->load_resource(base_path); - } - } - var = ResourceLoader::load(path); - - if (pinfo.hint_string == "Script") { - if (debugObj->get_script() != var) { - debugObj->set_script(REF()); - Ref<Script> script(var); - if (!script.is_null()) { - ScriptInstance *script_instance = script->placeholder_instance_create(debugObj); - debugObj->set_script_and_instance(var, script_instance); - } - } - } - } else if (var.get_type() == Variant::OBJECT) { - if (((Object *)var)->is_class("EncodedObjectAsID")) { - var = Object::cast_to<EncodedObjectAsID>(var)->get_object_id(); - pinfo.type = var.get_type(); - pinfo.hint = PROPERTY_HINT_OBJECT_ID; - pinfo.hint_string = "Object"; - } - } - } - - //always add the property, since props may have been added or removed - debugObj->prop_list.push_back(pinfo); - - if (!debugObj->prop_values.has(pinfo.name)) { - new_props_added++; - debugObj->prop_values[pinfo.name] = var; - } else { - - if (bool(Variant::evaluate(Variant::OP_NOT_EQUAL, debugObj->prop_values[pinfo.name], var))) { - debugObj->prop_values[pinfo.name] = var; - changed.insert(pinfo.name); - } - } - } - - if (editor->get_editor_history()->get_current() != debugObj->get_instance_id()) { - editor->push_item(debugObj, ""); - } else { - - if (old_prop_size == debugObj->prop_list.size() && new_props_added == 0) { - //only some may have changed, if so, then update those, if exist - for (Set<String>::Element *E = changed.front(); E; E = E->next()) { - EditorNode::get_singleton()->get_inspector()->update_property(E->get()); - } - } else { - //full update, because props were added or removed - debugObj->update(); - } - } + ObjectID id = inspector->add_object(p_data); + if (id.is_valid()) + emit_signal("remote_object_updated", id); } else if (p_msg == "message:video_mem") { vmem_tree->clear(); TreeItem *root = vmem_tree->create_item(); + ScriptDebuggerRemote::ResourceUsage usage; + usage.deserialize(p_data); int total = 0; - for (int i = 0; i < p_data.size(); i += 4) { + for (List<ScriptDebuggerRemote::ResourceInfo>::Element *E = usage.infos.front(); E; E = E->next()) { TreeItem *it = vmem_tree->create_item(root); - String type = p_data[i + 1]; - int bytes = p_data[i + 3].operator int(); - it->set_text(0, p_data[i + 0]); //path - it->set_text(1, type); //type - it->set_text(2, p_data[i + 2]); //type - it->set_text(3, String::humanize_size(bytes)); //type + String type = E->get().type; + int bytes = E->get().vram; + it->set_text(0, E->get().path); + it->set_text(1, type); + it->set_text(2, E->get().format); + it->set_text(3, String::humanize_size(bytes)); total += bytes; if (has_icon(type, "EditorIcons")) @@ -693,18 +312,21 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da } else if (p_msg == "stack_dump") { + ScriptDebuggerRemote::ScriptStackDump stack; + stack.deserialize(p_data); + stack_dump->clear(); + inspector->clear_stack_variables(); TreeItem *r = stack_dump->create_item(); - for (int i = 0; i < p_data.size(); i++) { + for (int i = 0; i < stack.frames.size(); i++) { - Dictionary d = p_data[i]; - ERR_CONTINUE(!d.has("function")); - ERR_CONTINUE(!d.has("file")); - ERR_CONTINUE(!d.has("line")); - ERR_CONTINUE(!d.has("id")); TreeItem *s = stack_dump->create_item(r); + Dictionary d; d["frame"] = i; + d["file"] = stack.frames[i].file; + d["function"] = stack.frames[i].func; + d["line"] = stack.frames[i].line; s->set_metadata(0, d); String line = itos(i) + " - " + String(d["file"]) + ":" + itos(d["line"]) + " - at function: " + d["function"]; @@ -715,97 +337,22 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da } } else if (p_msg == "stack_frame_vars") { - variables->clear(); - - int ofs = 0; - int mcount = p_data[ofs]; - ofs++; - for (int i = 0; i < mcount; i++) { + inspector->clear_stack_variables(); - String n = p_data[ofs + i * 2 + 0]; - Variant v = p_data[ofs + i * 2 + 1]; + } else if (p_msg == "stack_frame_var") { - PropertyHint h = PROPERTY_HINT_NONE; - String hs = String(); - - if (v.get_type() == Variant::OBJECT) { - v = Object::cast_to<EncodedObjectAsID>(v)->get_object_id(); - h = PROPERTY_HINT_OBJECT_ID; - hs = "Object"; - } - - variables->add_property("Locals/" + n, v, h, hs); - } - - ofs += mcount * 2; - mcount = p_data[ofs]; - ofs++; - for (int i = 0; i < mcount; i++) { - - String n = p_data[ofs + i * 2 + 0]; - Variant v = p_data[ofs + i * 2 + 1]; - PropertyHint h = PROPERTY_HINT_NONE; - String hs = String(); - - if (v.get_type() == Variant::OBJECT) { - v = Object::cast_to<EncodedObjectAsID>(v)->get_object_id(); - h = PROPERTY_HINT_OBJECT_ID; - hs = "Object"; - } - - variables->add_property("Members/" + n, v, h, hs); - - if (n == "self") { - _scene_tree_property_select_object(v); - } - } - - ofs += mcount * 2; - mcount = p_data[ofs]; - ofs++; - for (int i = 0; i < mcount; i++) { - - String n = p_data[ofs + i * 2 + 0]; - Variant v = p_data[ofs + i * 2 + 1]; - PropertyHint h = PROPERTY_HINT_NONE; - String hs = String(); - - if (v.get_type() == Variant::OBJECT) { - v = Object::cast_to<EncodedObjectAsID>(v)->get_object_id(); - h = PROPERTY_HINT_OBJECT_ID; - hs = "Object"; - } - - variables->add_property("Globals/" + n, v, h, hs); - } - - variables->update(); - inspector->edit(variables); + inspector->add_stack_variable(p_data); } else if (p_msg == "output") { - - //OUT - for (int i = 0; i < p_data.size(); i++) { - - String t = p_data[i]; - //LOG - - if (!EditorNode::get_log()->is_visible()) { - if (EditorNode::get_singleton()->are_bottom_panels_hidden()) { - if (EDITOR_GET("run/output/always_open_output_on_play")) { - EditorNode::get_singleton()->make_bottom_panel_item_visible(EditorNode::get_log()); - } - } - } - EditorNode::get_log()->add_message(t); - } + ERR_FAIL_COND(p_data.size() < 1); + String t = p_data[0]; + EditorNode::get_log()->add_message(t); } else if (p_msg == "performance") { - Array arr = p_data[0]; Vector<float> p; - p.resize(arr.size()); - for (int i = 0; i < arr.size(); i++) { - p.write[i] = arr[i]; + p.resize(p_data.size()); + for (int i = 0; i < p_data.size(); i++) { + p.write[i] = p_data[i]; if (i < perf_items.size()) { const float value = p[i]; @@ -833,7 +380,9 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da } perf_history.push_front(p); perf_draw->update(); + } else if (p_msg == "visual_profile") { + // TODO check me. uint64_t frame = p_data[0]; Vector<String> names = p_data[1]; Vector<real_t> values = p_data[2]; @@ -857,42 +406,29 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da areas_ptr[i].gpu_time = rr[i * 2 + 1]; } } - visual_profiler->add_frame_metric(metric); } else if (p_msg == "error") { - // Should have at least two elements, error array and stack items count. - ERR_FAIL_COND_MSG(p_data.size() < 2, "Malformed error message from script debugger."); - - // Error or warning data. - Array err = p_data[0]; - ERR_FAIL_COND_MSG(err.size() < 10, "Malformed error message from script debugger."); + ScriptDebuggerRemote::OutputError oe; + ERR_FAIL_COND_MSG(oe.deserialize(p_data) == false, "Failed to deserialize error message"); // Format time. Array time_vals; - time_vals.push_back(err[0]); - time_vals.push_back(err[1]); - time_vals.push_back(err[2]); - time_vals.push_back(err[3]); + time_vals.push_back(oe.hr); + time_vals.push_back(oe.min); + time_vals.push_back(oe.sec); + time_vals.push_back(oe.msec); bool e; - String time = String("%d:%02d:%02d.%03d").sprintf(time_vals, &e); + String time = String("%d:%02d:%02d:%04d").sprintf(time_vals, &e); // Rest of the error data. - String method = err[4]; - String source_file = err[5]; - String source_line = err[6]; - String error_cond = err[7]; - String error_msg = err[8]; - bool is_warning = err[9]; - bool has_method = !method.empty(); - bool has_error_msg = !error_msg.empty(); - bool source_is_project_file = source_file.begins_with("res://"); + bool source_is_project_file = oe.source_file.begins_with("res://"); // Metadata to highlight error line in scripts. Array source_meta; - source_meta.push_back(source_file); - source_meta.push_back(source_line); + source_meta.push_back(oe.source_file); + source_meta.push_back(oe.source_line); // Create error tree to display above error or warning details. TreeItem *r = error_tree->get_root(); @@ -902,40 +438,42 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da // Also provide the relevant details as tooltip to quickly check without // uncollapsing the tree. - String tooltip = is_warning ? TTR("Warning:") : TTR("Error:"); + String tooltip = oe.warning ? TTR("Warning:") : TTR("Error:"); TreeItem *error = error_tree->create_item(r); error->set_collapsed(true); - error->set_icon(0, get_icon(is_warning ? "Warning" : "Error", "EditorIcons")); + error->set_icon(0, get_icon(oe.warning ? "Warning" : "Error", "EditorIcons")); error->set_text(0, time); error->set_text_align(0, TreeItem::ALIGN_LEFT); String error_title; // Include method name, when given, in error title. - if (has_method) - error_title += method + ": "; + if (!oe.source_func.empty()) + error_title += oe.source_func + ": "; // If we have a (custom) error message, use it as title, and add a C++ Error // item with the original error condition. - error_title += error_msg.empty() ? error_cond : error_msg; + error_title += oe.error_descr.empty() ? oe.error : oe.error_descr; error->set_text(1, error_title); tooltip += " " + error_title + "\n"; - if (has_error_msg) { + if (!oe.error_descr.empty()) { // Add item for C++ error condition. TreeItem *cpp_cond = error_tree->create_item(error); cpp_cond->set_text(0, "<" + TTR("C++ Error") + ">"); - cpp_cond->set_text(1, error_cond); + cpp_cond->set_text(1, oe.error); cpp_cond->set_text_align(0, TreeItem::ALIGN_LEFT); - tooltip += TTR("C++ Error:") + " " + error_cond + "\n"; + tooltip += TTR("C++ Error:") + " " + oe.error + "\n"; if (source_is_project_file) cpp_cond->set_metadata(0, source_meta); } + Vector<uint8_t> v; + v.resize(100); // Source of the error. - String source_txt = (source_is_project_file ? source_file.get_file() : source_file) + ":" + source_line; - if (has_method) - source_txt += " @ " + method + "()"; + String source_txt = (source_is_project_file ? oe.source_file.get_file() : oe.source_file) + ":" + itos(oe.source_line); + if (!oe.source_func.empty()) + source_txt += " @ " + oe.source_func + "()"; TreeItem *cpp_source = error_tree->create_item(error); cpp_source->set_text(0, "<" + (source_is_project_file ? TTR("Source") : TTR("C++ Source")) + ">"); @@ -955,17 +493,14 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da // Format stack trace. // stack_items_count is the number of elements to parse, with 3 items per frame // of the stack trace (script, method, line). - int stack_items_count = p_data[1]; + const ScriptLanguage::StackInfo *infos = oe.callstack.ptr(); + for (unsigned int i = 0; i < (unsigned int)oe.callstack.size(); i++) { - for (int i = 0; i < stack_items_count; i += 3) { - String script = p_data[2 + i]; - String method2 = p_data[3 + i]; - int line = p_data[4 + i]; TreeItem *stack_trace = error_tree->create_item(error); Array meta; - meta.push_back(script); - meta.push_back(line); + meta.push_back(infos[i].file); + meta.push_back(infos[i].line); stack_trace->set_metadata(0, meta); if (i == 0) { @@ -973,29 +508,32 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da stack_trace->set_text_align(0, TreeItem::ALIGN_LEFT); error->set_metadata(0, meta); } - stack_trace->set_text(1, script.get_file() + ":" + itos(line) + " @ " + method2 + "()"); + stack_trace->set_text(1, infos[i].file.get_file() + ":" + itos(infos[i].line) + " @ " + infos[i].func + "()"); } - if (is_warning) + if (oe.warning) warning_count++; else error_count++; } else if (p_msg == "profile_sig") { - //cache a signature - profiler_signature[p_data[1]] = p_data[0]; + // Cache a profiler signature. + ScriptDebuggerRemote::ProfilerSignature sig; + sig.deserialize(p_data); + profiler_signature[sig.id] = sig.name; } else if (p_msg == "profile_frame" || p_msg == "profile_total") { - EditorProfiler::Metric metric; + ScriptDebuggerRemote::ProfilerFrame frame; + frame.deserialize(p_data); metric.valid = true; - metric.frame_number = p_data[0]; - metric.frame_time = p_data[1]; - metric.idle_time = p_data[2]; - metric.physics_time = p_data[3]; - metric.physics_frame_time = p_data[4]; - int frame_data_amount = p_data[6]; - int frame_function_amount = p_data[7]; + metric.frame_number = frame.frame_number; + metric.frame_time = frame.frame_time; + metric.idle_time = frame.idle_time; + metric.physics_time = frame.physics_time; + metric.physics_frame_time = frame.physics_frame_time; + int frame_data_amount = frame.frames_data.size(); + int frame_function_amount = frame.frame_functions.size(); if (frame_data_amount) { EditorProfiler::Metric::Category frame_time; @@ -1031,12 +569,11 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da metric.categories.push_back(frame_time); } - int idx = 8; for (int i = 0; i < frame_data_amount; i++) { EditorProfiler::Metric::Category c; - String name = p_data[idx++]; - Array values = p_data[idx++]; + String name = frame.frames_data[i].name; + Array values = frame.frames_data[i].data; c.name = name.capitalize(); c.items.resize(values.size() / 2); c.total_time = 0; @@ -1058,16 +595,16 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da } EditorProfiler::Metric::Category funcs; - funcs.total_time = p_data[5]; //script time + funcs.total_time = frame.script_time; funcs.items.resize(frame_function_amount); funcs.name = "Script Functions"; funcs.signature = "script_functions"; for (int i = 0; i < frame_function_amount; i++) { - int signature = p_data[idx++]; - int calls = p_data[idx++]; - float total = p_data[idx++]; - float self = p_data[idx++]; + int signature = frame.frame_functions[i].sig_id; + int calls = frame.frame_functions[i].call_count; + float total = frame.frame_functions[i].total_time; + float self = frame.frame_functions[i].self_time; EditorProfiler::Metric::Category::Item item; if (profiler_signature.has(signature)) { @@ -1102,23 +639,20 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da profiler->add_frame_metric(metric, false); else profiler->add_frame_metric(metric, true); + } else if (p_msg == "network_profile") { - int frame_size = 6; - for (int i = 0; i < p_data.size(); i += frame_size) { - MultiplayerAPI::ProfilingInfo pi; - pi.node = p_data[i + 0].operator ObjectID(); - pi.node_path = p_data[i + 1]; - pi.incoming_rpc = p_data[i + 2]; - pi.incoming_rset = p_data[i + 3]; - pi.outgoing_rpc = p_data[i + 4]; - pi.outgoing_rset = p_data[i + 5]; - network_profiler->add_node_frame_data(pi); + ScriptDebuggerRemote::NetworkProfilerFrame frame; + frame.deserialize(p_data); + for (int i = 0; i < frame.infos.size(); i++) { + network_profiler->add_node_frame_data(frame.infos[i]); } } else if (p_msg == "network_bandwidth") { + ERR_FAIL_COND(p_data.size() < 2); network_profiler->set_bandwidth(p_data[0], p_data[1]); } else if (p_msg == "kill_me") { - editor->call_deferred("stop_child_process"); + emit_signal("stop_requested"); + _stop_and_notify(); } } @@ -1218,14 +752,11 @@ void ScriptEditorDebugger::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { - inspector->edit(variables); skip_breakpoints->set_icon(get_icon("DebugSkipBreakpointsOff", "EditorIcons")); copy->set_icon(get_icon("ActionCopy", "EditorIcons")); step->set_icon(get_icon("DebugStep", "EditorIcons")); next->set_icon(get_icon("DebugNext", "EditorIcons")); - back->set_icon(get_icon("Back", "EditorIcons")); - forward->set_icon(get_icon("Forward", "EditorIcons")); dobreak->set_icon(get_icon("Pause", "EditorIcons")); docontinue->set_icon(get_icon("DebugContinue", "EditorIcons")); le_set->connect_compat("pressed", this, "_live_edit_set"); @@ -1239,31 +770,7 @@ void ScriptEditorDebugger::_notification(int p_what) { } break; case NOTIFICATION_PROCESS: { - if (connection.is_valid()) { - - inspect_scene_tree_timeout -= get_process_delta_time(); - if (inspect_scene_tree_timeout < 0) { - inspect_scene_tree_timeout = EditorSettings::get_singleton()->get("debugger/remote_scene_tree_refresh_interval"); - if (inspect_scene_tree->is_visible_in_tree()) { - _scene_tree_request(); - } - } - - inspect_edited_object_timeout -= get_process_delta_time(); - if (inspect_edited_object_timeout < 0) { - inspect_edited_object_timeout = EditorSettings::get_singleton()->get("debugger/remote_inspect_refresh_interval"); - if (inspected_object_id.is_valid()) { - if (ScriptEditorDebuggerInspectedObject *obj = Object::cast_to<ScriptEditorDebuggerInspectedObject>(ObjectDB::get_instance(editor->get_editor_history()->get_current()))) { - if (obj->remote_object_id == inspected_object_id) { - //take the chance and re-inspect selected object - Array msg; - msg.push_back("inspect_object"); - msg.push_back(inspected_object_id); - ppeer->put_var(msg); - } - } - } - } + if (is_session_active()) { if (camera_override == OVERRIDE_2D) { CanvasItemEditor *editor = CanvasItemEditor::get_singleton(); @@ -1277,9 +784,8 @@ void ScriptEditorDebugger::_notification(int p_what) { transform.elements[2] = -offset * zoom; Array msg; - msg.push_back("override_camera_2D:transform"); msg.push_back(transform); - ppeer->put_var(msg); + _put_msg("override_camera_2D:transform", msg); } else if (camera_override >= OVERRIDE_3D_1) { int viewport_idx = camera_override - OVERRIDE_3D_1; @@ -1287,7 +793,6 @@ void ScriptEditorDebugger::_notification(int p_what) { Camera *const cam = viewport->get_camera(); Array msg; - msg.push_back("override_camera_3D:transform"); msg.push_back(cam->get_camera_transform()); if (cam->get_projection() == Camera::PROJECTION_ORTHOGONAL) { msg.push_back(false); @@ -1298,86 +803,12 @@ void ScriptEditorDebugger::_notification(int p_what) { } msg.push_back(cam->get_znear()); msg.push_back(cam->get_zfar()); - ppeer->put_var(msg); - } - } - - if (error_count != last_error_count || warning_count != last_warning_count) { - - if (error_count == 0 && warning_count == 0) { - errors_tab->set_name(TTR("Errors")); - debugger_button->set_text(TTR("Debugger")); - debugger_button->set_icon(Ref<Texture2D>()); - tabs->set_tab_icon(errors_tab->get_index(), Ref<Texture2D>()); - } else { - errors_tab->set_name(TTR("Errors") + " (" + itos(error_count + warning_count) + ")"); - debugger_button->set_text(TTR("Debugger") + " (" + itos(error_count + warning_count) + ")"); - if (error_count == 0) { - debugger_button->set_icon(get_icon("Warning", "EditorIcons")); - tabs->set_tab_icon(errors_tab->get_index(), get_icon("Warning", "EditorIcons")); - } else { - debugger_button->set_icon(get_icon("Error", "EditorIcons")); - tabs->set_tab_icon(errors_tab->get_index(), get_icon("Error", "EditorIcons")); - } - } - last_error_count = error_count; - last_warning_count = warning_count; - } - - if (server->is_connection_available()) { - if (connection.is_valid()) { - // We already have a valid connection. Disconnecting any new connecting client to prevent it from hanging. - // (If we don't keep a reference to the connection it will be destroyed and disconnect_from_host will be called internally) - server->take_connection(); - } else { - // We just got the first connection. - connection = server->take_connection(); - if (connection.is_null()) - break; - - EditorNode::get_log()->add_message("--- Debugging process started ---", EditorLog::MSG_TYPE_EDITOR); - - ppeer->set_stream_peer(connection); - - //EditorNode::get_singleton()->make_bottom_panel_item_visible(this); - //emit_signal("show_debugger",true); - - dobreak->set_disabled(false); - tabs->set_current_tab(0); - - _set_reason_text(TTR("Child process connected."), MESSAGE_SUCCESS); - profiler->clear(); - - inspect_scene_tree->clear(); - le_set->set_disabled(true); - le_clear->set_disabled(false); - vmem_refresh->set_disabled(false); - error_tree->clear(); - error_count = 0; - warning_count = 0; - profiler_signature.clear(); - //live_edit_root->set_text("/root"); - - EditorNode::get_singleton()->get_pause_button()->set_pressed(false); - EditorNode::get_singleton()->get_pause_button()->set_disabled(false); - - update_live_edit_root(); - if (profiler->is_profiling()) { - _profiler_activate(true); - } - - if (network_profiler->is_profiling()) { - _network_profiler_activate(true); - } + _put_msg("override_camera_3D:transform", msg); } } - if (connection.is_null()) - break; - - if (!connection->is_connected_to_host()) { - stop(); - editor->notify_child_process_exited(); //somehow, exited + if (!is_session_active()) { + _stop_and_notify(); break; }; @@ -1389,67 +820,22 @@ void ScriptEditorDebugger::_notification(int p_what) { while (ppeer->get_available_packet_count() > 0) { - if (pending_in_queue) { - - int todo = MIN(ppeer->get_available_packet_count(), pending_in_queue); - - for (int i = 0; i < todo; i++) { - - Variant cmd; - Error ret = ppeer->get_var(cmd); - if (ret != OK) { - stop(); - ERR_FAIL_COND(ret != OK); - } - - message.push_back(cmd); - pending_in_queue--; - } - - if (pending_in_queue == 0) { - _parse_message(message_type, message); - message.clear(); - } - - } else { - - if (ppeer->get_available_packet_count() >= 2) { - - Variant cmd; - Error ret = ppeer->get_var(cmd); - if (ret != OK) { - stop(); - ERR_FAIL_COND(ret != OK); - } - if (cmd.get_type() != Variant::STRING) { - stop(); - ERR_FAIL_COND(cmd.get_type() != Variant::STRING); - } - - message_type = cmd; - - ret = ppeer->get_var(cmd); - if (ret != OK) { - stop(); - ERR_FAIL_COND(ret != OK); - } - if (cmd.get_type() != Variant::INT) { - stop(); - ERR_FAIL_COND(cmd.get_type() != Variant::INT); - } - - pending_in_queue = cmd; - - if (pending_in_queue == 0) { - _parse_message(message_type, Array()); - message.clear(); - } - - } else { - - break; - } + Variant cmd; + Error ret = ppeer->get_var(cmd); + if (ret != OK) { + _stop_and_notify(); + ERR_FAIL_MSG("Error decoding variant from peer"); + } + if (cmd.get_type() != Variant::ARRAY) { + _stop_and_notify(); + ERR_FAIL_MSG("Invalid message format received from peer"); } + Array arr = cmd; + if (arr.size() != 2 || arr[0].get_type() != Variant::STRING || arr[1].get_type() != Variant::ARRAY) { + _stop_and_notify(); + ERR_FAIL_MSG("Invalid message format received from peer"); + } + _parse_message(arr[0], arr[1]); if (OS::get_singleton()->get_ticks_msec() > until) break; @@ -1460,15 +846,13 @@ void ScriptEditorDebugger::_notification(int p_what) { add_constant_override("margin_left", -EditorNode::get_singleton()->get_gui_base()->get_stylebox("BottomPanelDebuggerOverride", "EditorStyles")->get_margin(MARGIN_LEFT)); add_constant_override("margin_right", -EditorNode::get_singleton()->get_gui_base()->get_stylebox("BottomPanelDebuggerOverride", "EditorStyles")->get_margin(MARGIN_RIGHT)); - tabs->add_style_override("panel", editor->get_gui_base()->get_stylebox("DebuggerPanel", "EditorStyles")); - tabs->add_style_override("tab_fg", editor->get_gui_base()->get_stylebox("DebuggerTabFG", "EditorStyles")); - tabs->add_style_override("tab_bg", editor->get_gui_base()->get_stylebox("DebuggerTabBG", "EditorStyles")); + tabs->add_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_stylebox("DebuggerPanel", "EditorStyles")); + tabs->add_style_override("tab_fg", EditorNode::get_singleton()->get_gui_base()->get_stylebox("DebuggerTabFG", "EditorStyles")); + tabs->add_style_override("tab_bg", EditorNode::get_singleton()->get_gui_base()->get_stylebox("DebuggerTabBG", "EditorStyles")); copy->set_icon(get_icon("ActionCopy", "EditorIcons")); step->set_icon(get_icon("DebugStep", "EditorIcons")); next->set_icon(get_icon("DebugNext", "EditorIcons")); - back->set_icon(get_icon("Back", "EditorIcons")); - forward->set_icon(get_icon("Forward", "EditorIcons")); dobreak->set_icon(get_icon("Pause", "EditorIcons")); docontinue->set_icon(get_icon("DebugContinue", "EditorIcons")); vmem_refresh->set_icon(get_icon("Reload", "EditorIcons")); @@ -1486,15 +870,18 @@ void ScriptEditorDebugger::_clear_execution() { stack_script = ResourceLoader::load(d["file"]); emit_signal("clear_execution", stack_script); stack_script.unref(); + stack_dump->clear(); + inspector->clear_stack_variables(); } -void ScriptEditorDebugger::start() { +void ScriptEditorDebugger::start(Ref<StreamPeerTCP> p_connection) { + error_count = 0; + warning_count = 0; stop(); - if (is_visible_in_tree()) { - EditorNode::get_singleton()->make_bottom_panel_item_visible(this); - } + connection = p_connection; + ppeer->set_stream_peer(connection); perf_history.clear(); for (int i = 0; i < Performance::MONITOR_MAX; i++) { @@ -1502,91 +889,81 @@ void ScriptEditorDebugger::start() { perf_max.write[i] = 0; } - int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port"); - if (server->listen(remote_port) != OK) { - EditorNode::get_log()->add_message(String("Error listening on port ") + itos(remote_port), EditorLog::MSG_TYPE_ERROR); - return; - } - - EditorNode::get_singleton()->get_scene_tree_dock()->show_tab_buttons(); - auto_switch_remote_scene_tree = (bool)EditorSettings::get_singleton()->get("debugger/auto_switch_to_remote_scene_tree"); - if (auto_switch_remote_scene_tree) { - EditorNode::get_singleton()->get_scene_tree_dock()->show_remote_tree(); - } - set_process(true); breaked = false; + can_debug = true; camera_override = OVERRIDE_NONE; + + tabs->set_current_tab(0); + _set_reason_text(TTR("Debug session started."), MESSAGE_SUCCESS); + _update_buttons_state(); + + if (profiler->is_profiling()) { + _profiler_activate(true); + } + + if (network_profiler->is_profiling()) { + _network_profiler_activate(true); + } } -void ScriptEditorDebugger::pause() { +void ScriptEditorDebugger::_update_buttons_state() { + const bool active = is_session_active(); + const bool has_editor_tree = active && editor_remote_tree && editor_remote_tree->get_selected(); + vmem_refresh->set_disabled(!active); + step->set_disabled(!active || !breaked || !can_debug); + next->set_disabled(!active || !breaked || !can_debug); + copy->set_disabled(!active || !breaked); + docontinue->set_disabled(!active || !breaked); + dobreak->set_disabled(!active || breaked); + le_clear->set_disabled(!active); + le_set->set_disabled(!has_editor_tree); } -void ScriptEditorDebugger::unpause() { +void ScriptEditorDebugger::_stop_and_notify() { + stop(); + emit_signal("stopped"); + _set_reason_text(TTR("Debug session closed."), MESSAGE_WARNING); } void ScriptEditorDebugger::stop() { set_process(false); breaked = false; + can_debug = false; + remote_pid = 0; _clear_execution(); - server->stop(); - _clear_remote_objects(); + inspector->clear_cache(); ppeer->set_stream_peer(Ref<StreamPeer>()); if (connection.is_valid()) { - EditorNode::get_log()->add_message("--- Debugging process stopped ---", EditorLog::MSG_TYPE_EDITOR); connection.unref(); - reason->set_text(""); reason->set_tooltip(""); } - pending_in_queue = 0; - message.clear(); - node_path_cache.clear(); res_path_cache.clear(); profiler_signature.clear(); - le_clear->set_disabled(false); - le_set->set_disabled(true); - profiler->set_enabled(true); - vmem_refresh->set_disabled(true); - inspect_scene_tree->clear(); inspector->edit(NULL); - EditorNode::get_singleton()->get_pause_button()->set_pressed(false); - EditorNode::get_singleton()->get_pause_button()->set_disabled(true); - EditorNode::get_singleton()->get_scene_tree_dock()->hide_remote_tree(); - EditorNode::get_singleton()->get_scene_tree_dock()->hide_tab_buttons(); - - if (hide_on_stop) { - if (is_visible_in_tree()) - EditorNode::get_singleton()->hide_bottom_panel(); - emit_signal("show_debugger", false); - } + _update_buttons_state(); } void ScriptEditorDebugger::_profiler_activate(bool p_enable) { - if (!connection.is_valid()) - return; - if (p_enable) { profiler_signature.clear(); Array msg; - msg.push_back("start_profiling"); int max_funcs = EditorSettings::get_singleton()->get("debugger/profiler_frame_max_functions"); max_funcs = CLAMP(max_funcs, 16, 512); msg.push_back(max_funcs); - ppeer->put_var(msg); + _put_msg("start_profiling", msg); print_verbose("Starting profiling."); } else { - Array msg; - msg.push_back("stop_profiling"); - ppeer->put_var(msg); + _put_msg("stop_profiling", Array()); print_verbose("Ending profiling."); } } @@ -1598,44 +975,30 @@ void ScriptEditorDebugger::_visual_profiler_activate(bool p_enable) { if (p_enable) { profiler_signature.clear(); - Array msg; - msg.push_back("start_visual_profiling"); - ppeer->put_var(msg); + _put_msg("start_visual_profiling", Array()); print_verbose("Starting visual profiling."); } else { - Array msg; - msg.push_back("stop_visual_profiling"); - ppeer->put_var(msg); + _put_msg("stop_visual_profiling", Array()); print_verbose("Ending visual profiling."); } } void ScriptEditorDebugger::_network_profiler_activate(bool p_enable) { - if (!connection.is_valid()) - return; - if (p_enable) { profiler_signature.clear(); - Array msg; - msg.push_back("start_network_profiling"); - ppeer->put_var(msg); + _put_msg("start_network_profiling", Array()); print_verbose("Starting network profiling."); } else { - Array msg; - msg.push_back("stop_network_profiling"); - ppeer->put_var(msg); + _put_msg("stop_network_profiling", Array()); print_verbose("Ending network profiling."); } } void ScriptEditorDebugger::_profiler_seeked() { - if (!connection.is_valid() || !connection->is_connected_to_host()) - return; - if (breaked) return; debug_break(); @@ -1643,45 +1006,30 @@ void ScriptEditorDebugger::_profiler_seeked() { void ScriptEditorDebugger::_stack_dump_frame_selected() { - TreeItem *ti = stack_dump->get_selected(); - if (!ti) - return; + emit_signal("stack_frame_selected"); - Dictionary d = ti->get_metadata(0); - - stack_script = ResourceLoader::load(d["file"]); - emit_signal("goto_script_line", stack_script, int(d["line"]) - 1); - emit_signal("set_execution", stack_script, int(d["line"]) - 1); - stack_script.unref(); + int frame = get_stack_script_frame(); - if (connection.is_valid() && connection->is_connected_to_host()) { + if (is_session_active() && frame >= 0) { Array msg; - msg.push_back("get_stack_frame_vars"); - msg.push_back(d["frame"]); - ppeer->put_var(msg); + msg.push_back(frame); + _put_msg("get_stack_frame_vars", msg); } else { inspector->edit(NULL); } } -void ScriptEditorDebugger::_output_clear() { - - //output->clear(); - //output->push_color(Color(0,0,0)); -} - void ScriptEditorDebugger::_export_csv() { file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE); file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); - file_dialog_mode = SAVE_CSV; file_dialog->popup_centered_ratio(); } String ScriptEditorDebugger::get_var_value(const String &p_var) const { if (!breaked) return String(); - return variables->get_var_value(p_var); + return inspector->get_stack_variable(p_var); } int ScriptEditorDebugger::_get_node_path_cache(const NodePath &p_path) { @@ -1694,10 +1042,9 @@ int ScriptEditorDebugger::_get_node_path_cache(const NodePath &p_path) { node_path_cache[p_path] = last_path_id; Array msg; - msg.push_back("live_node_path"); msg.push_back(p_path); msg.push_back(last_path_id); - ppeer->put_var(msg); + _put_msg("live_node_path", msg); return last_path_id; } @@ -1713,17 +1060,16 @@ int ScriptEditorDebugger::_get_res_path_cache(const String &p_path) { res_path_cache[p_path] = last_path_id; Array msg; - msg.push_back("live_res_path"); msg.push_back(p_path); msg.push_back(last_path_id); - ppeer->put_var(msg); + _put_msg("live_res_path", msg); return last_path_id; } void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE) { - if (!p_base || !live_debug || !connection.is_valid() || !editor->get_edited_scene()) + if (!p_base || !live_debug || !is_session_active() || !editor->get_edited_scene()) return; Node *node = Object::cast_to<Node>(p_base); @@ -1742,14 +1088,13 @@ void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_n int pathid = _get_node_path_cache(path); Array msg; - msg.push_back("live_node_call"); msg.push_back(pathid); msg.push_back(p_name); for (int i = 0; i < VARIANT_ARG_MAX; i++) { //no pointers, sorry msg.push_back(*argptr[i]); } - ppeer->put_var(msg); + _put_msg("live_node_call", msg); return; } @@ -1762,14 +1107,13 @@ void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_n int pathid = _get_res_path_cache(respath); Array msg; - msg.push_back("live_res_call"); msg.push_back(pathid); msg.push_back(p_name); for (int i = 0; i < VARIANT_ARG_MAX; i++) { //no pointers, sorry msg.push_back(*argptr[i]); } - ppeer->put_var(msg); + _put_msg("live_res_call", msg); return; } @@ -1777,7 +1121,7 @@ void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_n void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p_property, const Variant &p_value) { - if (!p_base || !live_debug || !connection.is_valid() || !editor->get_edited_scene()) + if (!p_base || !live_debug || !editor->get_edited_scene()) return; Node *node = Object::cast_to<Node>(p_base); @@ -1792,20 +1136,18 @@ void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p if (res.is_valid() && res->get_path() != String()) { Array msg; - msg.push_back("live_node_prop_res"); msg.push_back(pathid); msg.push_back(p_property); msg.push_back(res->get_path()); - ppeer->put_var(msg); + _put_msg("live_node_prop_res", msg); } } else { Array msg; - msg.push_back("live_node_prop"); msg.push_back(pathid); msg.push_back(p_property); msg.push_back(p_value); - ppeer->put_var(msg); + _put_msg("live_node_prop", msg); } return; @@ -1823,36 +1165,46 @@ void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p if (res2.is_valid() && res2->get_path() != String()) { Array msg; - msg.push_back("live_res_prop_res"); msg.push_back(pathid); msg.push_back(p_property); msg.push_back(res2->get_path()); - ppeer->put_var(msg); + _put_msg("live_res_prop_res", msg); } } else { Array msg; - msg.push_back("live_res_prop"); msg.push_back(pathid); msg.push_back(p_property); msg.push_back(p_value); - ppeer->put_var(msg); + _put_msg("live_res_prop", msg); } return; } } -void ScriptEditorDebugger::_method_changeds(void *p_ud, Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE) { - - ScriptEditorDebugger *sed = (ScriptEditorDebugger *)p_ud; - sed->_method_changed(p_base, p_name, VARIANT_ARG_PASS); +String ScriptEditorDebugger::get_stack_script_file() const { + TreeItem *ti = stack_dump->get_selected(); + if (!ti) + return ""; + Dictionary d = ti->get_metadata(0); + return d["file"]; } -void ScriptEditorDebugger::_property_changeds(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value) { +int ScriptEditorDebugger::get_stack_script_line() const { + TreeItem *ti = stack_dump->get_selected(); + if (!ti) + return -1; + Dictionary d = ti->get_metadata(0); + return d["line"]; +} - ScriptEditorDebugger *sed = (ScriptEditorDebugger *)p_ud; - sed->_property_changed(p_base, p_property, p_value); +int ScriptEditorDebugger::get_stack_script_frame() const { + TreeItem *ti = stack_dump->get_selected(); + if (!ti) + return -1; + Dictionary d = ti->get_metadata(0); + return d["frame"]; } void ScriptEditorDebugger::set_live_debugging(bool p_enable) { @@ -1862,12 +1214,13 @@ void ScriptEditorDebugger::set_live_debugging(bool p_enable) { void ScriptEditorDebugger::_live_edit_set() { - if (!connection.is_valid()) + if (!is_session_active() || !editor_remote_tree) return; - TreeItem *ti = inspect_scene_tree->get_selected(); + TreeItem *ti = editor_remote_tree->get_selected(); if (!ti) return; + String path; while (ti) { @@ -1895,92 +1248,82 @@ void ScriptEditorDebugger::update_live_edit_root() { NodePath np = editor->get_editor_data().get_edited_scene_live_edit_root(); - if (connection.is_valid()) { - Array msg; - msg.push_back("live_set_root"); - msg.push_back(np); - if (editor->get_edited_scene()) - msg.push_back(editor->get_edited_scene()->get_filename()); - else - msg.push_back(""); - ppeer->put_var(msg); - } + Array msg; + msg.push_back(np); + if (editor->get_edited_scene()) + msg.push_back(editor->get_edited_scene()->get_filename()); + else + msg.push_back(""); + _put_msg("live_set_root", msg); live_edit_root->set_text(np); } void ScriptEditorDebugger::live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name) { - if (live_debug && connection.is_valid()) { + if (live_debug) { Array msg; - msg.push_back("live_create_node"); msg.push_back(p_parent); msg.push_back(p_type); msg.push_back(p_name); - ppeer->put_var(msg); + _put_msg("live_create_node", msg); } } void ScriptEditorDebugger::live_debug_instance_node(const NodePath &p_parent, const String &p_path, const String &p_name) { - if (live_debug && connection.is_valid()) { + if (live_debug) { Array msg; - msg.push_back("live_instance_node"); msg.push_back(p_parent); msg.push_back(p_path); msg.push_back(p_name); - ppeer->put_var(msg); + _put_msg("live_instance_node", msg); } } void ScriptEditorDebugger::live_debug_remove_node(const NodePath &p_at) { - if (live_debug && connection.is_valid()) { + if (live_debug) { Array msg; - msg.push_back("live_remove_node"); msg.push_back(p_at); - ppeer->put_var(msg); + _put_msg("live_remove_node", msg); } } void ScriptEditorDebugger::live_debug_remove_and_keep_node(const NodePath &p_at, ObjectID p_keep_id) { - if (live_debug && connection.is_valid()) { + if (live_debug) { Array msg; - msg.push_back("live_remove_and_keep_node"); msg.push_back(p_at); msg.push_back(p_keep_id); - ppeer->put_var(msg); + _put_msg("live_remove_and_keep_node", msg); } } void ScriptEditorDebugger::live_debug_restore_node(ObjectID p_id, const NodePath &p_at, int p_at_pos) { - if (live_debug && connection.is_valid()) { + if (live_debug) { Array msg; - msg.push_back("live_restore_node"); msg.push_back(p_id); msg.push_back(p_at); msg.push_back(p_at_pos); - ppeer->put_var(msg); + _put_msg("live_restore_node", msg); } } void ScriptEditorDebugger::live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name) { - if (live_debug && connection.is_valid()) { + if (live_debug) { Array msg; - msg.push_back("live_duplicate_node"); msg.push_back(p_at); msg.push_back(p_new_name); - ppeer->put_var(msg); + _put_msg("live_duplicate_node", msg); } } void ScriptEditorDebugger::live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos) { - if (live_debug && connection.is_valid()) { + if (live_debug) { Array msg; - msg.push_back("live_reparent_node"); msg.push_back(p_at); msg.push_back(p_new_place); msg.push_back(p_new_name); msg.push_back(p_at_pos); - ppeer->put_var(msg); + _put_msg("live_reparent_node", msg); } } @@ -1991,33 +1334,21 @@ ScriptEditorDebugger::CameraOverride ScriptEditorDebugger::get_camera_override() void ScriptEditorDebugger::set_camera_override(CameraOverride p_override) { if (p_override == OVERRIDE_2D && camera_override != OVERRIDE_2D) { - if (connection.is_valid()) { - Array msg; - msg.push_back("override_camera_2D:set"); - msg.push_back(true); - ppeer->put_var(msg); - } + Array msg; + msg.push_back(true); + _put_msg("override_camera_2D:set", msg); } else if (p_override != OVERRIDE_2D && camera_override == OVERRIDE_2D) { - if (connection.is_valid()) { - Array msg; - msg.push_back("override_camera_2D:set"); - msg.push_back(false); - ppeer->put_var(msg); - } + Array msg; + msg.push_back(false); + _put_msg("override_camera_2D:set", msg); } else if (p_override >= OVERRIDE_3D_1 && camera_override < OVERRIDE_3D_1) { - if (connection.is_valid()) { - Array msg; - msg.push_back("override_camera_3D:set"); - msg.push_back(true); - ppeer->put_var(msg); - } + Array msg; + msg.push_back(true); + _put_msg("override_camera_3D:set", msg); } else if (p_override < OVERRIDE_3D_1 && camera_override >= OVERRIDE_3D_1) { - if (connection.is_valid()) { - Array msg; - msg.push_back("override_camera_3D:set"); - msg.push_back(false); - ppeer->put_var(msg); - } + Array msg; + msg.push_back(false); + _put_msg("override_camera_3D:set", msg); } camera_override = p_override; @@ -2025,23 +1356,16 @@ void ScriptEditorDebugger::set_camera_override(CameraOverride p_override) { void ScriptEditorDebugger::set_breakpoint(const String &p_path, int p_line, bool p_enabled) { - if (connection.is_valid()) { - Array msg; - msg.push_back("breakpoint"); - msg.push_back(p_path); - msg.push_back(p_line); - msg.push_back(p_enabled); - ppeer->put_var(msg); - } + Array msg; + msg.push_back(p_path); + msg.push_back(p_line); + msg.push_back(p_enabled); + _put_msg("breakpoint", msg); } void ScriptEditorDebugger::reload_scripts() { - if (connection.is_valid()) { - Array msg; - msg.push_back("reload_scripts"); - ppeer->put_var(msg); - } + _put_msg("reload_scripts", Array()); } bool ScriptEditorDebugger::is_skip_breakpoints() { @@ -2059,15 +1383,12 @@ void ScriptEditorDebugger::_error_activated() { void ScriptEditorDebugger::_error_selected() { TreeItem *selected = error_tree->get_selected(); - Array meta = selected->get_metadata(0); - if (meta.size() == 0) { return; } - Ref<Script> s = ResourceLoader::load(meta[0]); - emit_signal("goto_script_line", s, int(meta[1]) - 1); + emit_signal("error_selected", String(meta[0]), int(meta[1])); } void ScriptEditorDebugger::_expand_errors_list() { @@ -2096,58 +1417,6 @@ void ScriptEditorDebugger::_collapse_errors_list() { } } -void ScriptEditorDebugger::set_hide_on_stop(bool p_hide) { - - hide_on_stop = p_hide; -} - -bool ScriptEditorDebugger::get_debug_with_external_editor() const { - - return enable_external_editor; -} - -void ScriptEditorDebugger::set_debug_with_external_editor(bool p_enabled) { - - enable_external_editor = p_enabled; -} - -Ref<Script> ScriptEditorDebugger::get_dump_stack_script() const { - - return stack_script; -} - -void ScriptEditorDebugger::_paused() { - - ERR_FAIL_COND(connection.is_null()); - ERR_FAIL_COND(!connection->is_connected_to_host()); - - if (!breaked && EditorNode::get_singleton()->get_pause_button()->is_pressed()) { - debug_break(); - } - - if (breaked && !EditorNode::get_singleton()->get_pause_button()->is_pressed()) { - debug_continue(); - } -} - -void ScriptEditorDebugger::_set_remote_object(ObjectID p_id, ScriptEditorDebuggerInspectedObject *p_obj) { - - if (remote_objects.has(p_id)) - memdelete(remote_objects[p_id]); - remote_objects[p_id] = p_obj; -} - -void ScriptEditorDebugger::_clear_remote_objects() { - - for (Map<ObjectID, ScriptEditorDebuggerInspectedObject *>::Element *E = remote_objects.front(); E; E = E->next()) { - if (editor->get_editor_history()->get_current() == E->value()->get_instance_id()) { - editor->push_item(NULL); - } - memdelete(E->value()); - } - remote_objects.clear(); -} - void ScriptEditorDebugger::_clear_errors_list() { error_tree->clear(); @@ -2163,7 +1432,7 @@ void ScriptEditorDebugger::_error_tree_item_rmb_selected(const Vector2 &p_pos) { item_menu->set_size(Size2(1, 1)); if (error_tree->is_anything_selected()) { - item_menu->add_icon_item(get_icon("ActionCopy", "EditorIcons"), TTR("Copy Error"), ITEM_MENU_COPY_ERROR); + item_menu->add_icon_item(get_icon("ActionCopy", "EditorIcons"), TTR("Copy Error"), 0); } if (item_menu->get_item_count() > 0) { @@ -2173,70 +1442,29 @@ void ScriptEditorDebugger::_error_tree_item_rmb_selected(const Vector2 &p_pos) { } void ScriptEditorDebugger::_item_menu_id_pressed(int p_option) { + TreeItem *ti = error_tree->get_selected(); + while (ti->get_parent() != error_tree->get_root()) + ti = ti->get_parent(); - switch (p_option) { - - case ITEM_MENU_COPY_ERROR: { - TreeItem *ti = error_tree->get_selected(); - while (ti->get_parent() != error_tree->get_root()) - ti = ti->get_parent(); - - String type; - - if (ti->get_icon(0) == get_icon("Warning", "EditorIcons")) { - type = "W "; - } else if (ti->get_icon(0) == get_icon("Error", "EditorIcons")) { - type = "E "; - } - - String text = ti->get_text(0) + " "; - int rpad_len = text.length(); - - text = type + text + ti->get_text(1) + "\n"; - TreeItem *ci = ti->get_children(); - while (ci) { - text += " " + ci->get_text(0).rpad(rpad_len) + ci->get_text(1) + "\n"; - ci = ci->get_next(); - } - - OS::get_singleton()->set_clipboard(text); - - } break; - case ITEM_MENU_SAVE_REMOTE_NODE: { - - file_dialog->set_access(EditorFileDialog::ACCESS_RESOURCES); - file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE); - file_dialog_mode = SAVE_NODE; - - List<String> extensions; - Ref<PackedScene> sd = memnew(PackedScene); - ResourceSaver::get_recognized_extensions(sd, &extensions); - file_dialog->clear_filters(); - for (int i = 0; i < extensions.size(); i++) { - file_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper()); - } + String type; - file_dialog->popup_centered_ratio(); - } break; - case ITEM_MENU_COPY_NODE_PATH: { + if (ti->get_icon(0) == get_icon("Warning", "EditorIcons")) { + type = "W "; + } else if (ti->get_icon(0) == get_icon("Error", "EditorIcons")) { + type = "E "; + } - TreeItem *ti = inspect_scene_tree->get_selected(); - String text = ti->get_text(0); + String text = ti->get_text(0) + " "; + int rpad_len = text.length(); - if (ti->get_parent() == NULL) { - text = "."; - } else if (ti->get_parent()->get_parent() == NULL) { - text = "."; - } else { - while (ti->get_parent()->get_parent() != inspect_scene_tree->get_root()) { - ti = ti->get_parent(); - text = ti->get_text(0) + "/" + text; - } - } - - OS::get_singleton()->set_clipboard(text); - } break; + text = type + text + ti->get_text(1) + "\n"; + TreeItem *ci = ti->get_children(); + while (ci) { + text += " " + ci->get_text(0).rpad(rpad_len) + ci->get_text(1) + "\n"; + ci = ci->get_next(); } + + OS::get_singleton()->set_clipboard(text); } void ScriptEditorDebugger::_tab_changed(int p_tab) { @@ -2257,11 +1485,9 @@ void ScriptEditorDebugger::_bind_methods() { ClassDB::bind_method(D_METHOD("debug_step"), &ScriptEditorDebugger::debug_step); ClassDB::bind_method(D_METHOD("debug_break"), &ScriptEditorDebugger::debug_break); ClassDB::bind_method(D_METHOD("debug_continue"), &ScriptEditorDebugger::debug_continue); - ClassDB::bind_method(D_METHOD("_output_clear"), &ScriptEditorDebugger::_output_clear); ClassDB::bind_method(D_METHOD("_export_csv"), &ScriptEditorDebugger::_export_csv); ClassDB::bind_method(D_METHOD("_performance_draw"), &ScriptEditorDebugger::_performance_draw); ClassDB::bind_method(D_METHOD("_performance_select"), &ScriptEditorDebugger::_performance_select); - ClassDB::bind_method(D_METHOD("_scene_tree_request"), &ScriptEditorDebugger::_scene_tree_request); ClassDB::bind_method(D_METHOD("_video_mem_request"), &ScriptEditorDebugger::_video_mem_request); ClassDB::bind_method(D_METHOD("_live_edit_set"), &ScriptEditorDebugger::_live_edit_set); ClassDB::bind_method(D_METHOD("_live_edit_clear"), &ScriptEditorDebugger::_live_edit_clear); @@ -2279,13 +1505,10 @@ void ScriptEditorDebugger::_bind_methods() { ClassDB::bind_method(D_METHOD("_error_tree_item_rmb_selected"), &ScriptEditorDebugger::_error_tree_item_rmb_selected); ClassDB::bind_method(D_METHOD("_item_menu_id_pressed"), &ScriptEditorDebugger::_item_menu_id_pressed); ClassDB::bind_method(D_METHOD("_tab_changed"), &ScriptEditorDebugger::_tab_changed); - - ClassDB::bind_method(D_METHOD("_paused"), &ScriptEditorDebugger::_paused); - - ClassDB::bind_method(D_METHOD("_scene_tree_selected"), &ScriptEditorDebugger::_scene_tree_selected); - ClassDB::bind_method(D_METHOD("_scene_tree_folded"), &ScriptEditorDebugger::_scene_tree_folded); - ClassDB::bind_method(D_METHOD("_scene_tree_rmb_selected"), &ScriptEditorDebugger::_scene_tree_rmb_selected); ClassDB::bind_method(D_METHOD("_file_selected"), &ScriptEditorDebugger::_file_selected); + ClassDB::bind_method(D_METHOD("_remote_object_selected", "id"), &ScriptEditorDebugger::_remote_object_selected); + ClassDB::bind_method(D_METHOD("_remote_object_edited", "id", "property", "value"), &ScriptEditorDebugger::_remote_object_edited); + ClassDB::bind_method(D_METHOD("_remote_object_property_updated", "id", "property"), &ScriptEditorDebugger::_remote_object_property_updated); ClassDB::bind_method(D_METHOD("live_debug_create_node"), &ScriptEditorDebugger::live_debug_create_node); ClassDB::bind_method(D_METHOD("live_debug_instance_node"), &ScriptEditorDebugger::live_debug_instance_node); @@ -2294,14 +1517,20 @@ void ScriptEditorDebugger::_bind_methods() { ClassDB::bind_method(D_METHOD("live_debug_restore_node"), &ScriptEditorDebugger::live_debug_restore_node); ClassDB::bind_method(D_METHOD("live_debug_duplicate_node"), &ScriptEditorDebugger::live_debug_duplicate_node); ClassDB::bind_method(D_METHOD("live_debug_reparent_node"), &ScriptEditorDebugger::live_debug_reparent_node); - ClassDB::bind_method(D_METHOD("_scene_tree_property_select_object"), &ScriptEditorDebugger::_scene_tree_property_select_object); - ClassDB::bind_method(D_METHOD("_scene_tree_property_value_edited"), &ScriptEditorDebugger::_scene_tree_property_value_edited); + ClassDB::bind_method(D_METHOD("request_remote_object", "id"), &ScriptEditorDebugger::request_remote_object); + ClassDB::bind_method(D_METHOD("update_remote_object", "id", "property", "value"), &ScriptEditorDebugger::update_remote_object); - ADD_SIGNAL(MethodInfo("goto_script_line")); + ADD_SIGNAL(MethodInfo("stopped")); + ADD_SIGNAL(MethodInfo("stop_requested")); + ADD_SIGNAL(MethodInfo("stack_frame_selected", PropertyInfo(Variant::INT, "frame"))); + ADD_SIGNAL(MethodInfo("error_selected", PropertyInfo(Variant::INT, "error"))); 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("show_debugger", PropertyInfo(Variant::BOOL, "reallydid"))); + ADD_SIGNAL(MethodInfo("remote_object_requested", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("remote_object_updated", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("remote_object_property_updated", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property"))); + ADD_SIGNAL(MethodInfo("remote_tree_updated")); } ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { @@ -2312,7 +1541,6 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { ppeer = Ref<PacketPeerStream>(memnew(PacketPeerStream)); ppeer->set_input_buffer_max_size((1024 * 1024 * 8) - 4); // 8 MiB should be enough, minus 4 bytes for separator. editor = p_editor; - editor->get_inspector()->connect_compat("object_id_selected", this, "_scene_tree_property_select_object"); tabs = memnew(TabContainer); tabs->set_tab_align(TabContainer::ALIGN_LEFT); @@ -2381,16 +1609,6 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { docontinue->set_shortcut(ED_GET_SHORTCUT("debugger/continue")); docontinue->connect_compat("pressed", this, "debug_continue"); - back = memnew(Button); - hbc->add_child(back); - back->set_tooltip(TTR("Inspect Previous Instance")); - back->hide(); - - forward = memnew(Button); - hbc->add_child(forward); - forward->set_tooltip(TTR("Inspect Next Instance")); - forward->hide(); - HSplitContainer *sc = memnew(HSplitContainer); vbc->add_child(sc); sc->set_v_size_flags(SIZE_EXPAND_FILL); @@ -2405,21 +1623,14 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { stack_dump->connect_compat("cell_selected", this, "_stack_dump_frame_selected"); sc->add_child(stack_dump); - inspector = memnew(EditorInspector); + inspector = memnew(EditorDebuggerInspector); inspector->set_h_size_flags(SIZE_EXPAND_FILL); inspector->set_enable_capitalize_paths(false); inspector->set_read_only(true); - inspector->connect_compat("object_id_selected", this, "_scene_tree_property_select_object"); + inspector->connect_compat("object_selected", this, "_remote_object_selected"); + inspector->connect_compat("object_edited", this, "_remote_object_edited"); + inspector->connect_compat("object_property_updated", this, "_remote_object_property_updated"); sc->add_child(inspector); - - server.instance(); - - pending_in_queue = 0; - - variables = memnew(ScriptEditorDebuggerVariables); - - breaked = false; - tabs->add_child(dbg); } @@ -2472,23 +1683,6 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { tabs->add_child(errors_tab); } - { // remote scene tree - - inspect_scene_tree = memnew(Tree); - EditorNode::get_singleton()->get_scene_tree_dock()->add_remote_tree_editor(inspect_scene_tree); - EditorNode::get_singleton()->get_scene_tree_dock()->connect_compat("remote_tree_selected", this, "_scene_tree_selected"); - inspect_scene_tree->set_v_size_flags(SIZE_EXPAND_FILL); - inspect_scene_tree->connect_compat("cell_selected", this, "_scene_tree_selected"); - inspect_scene_tree->connect_compat("item_collapsed", this, "_scene_tree_folded"); - inspect_scene_tree->set_allow_rmb_select(true); - inspect_scene_tree->connect_compat("item_rmb_selected", this, "_scene_tree_rmb_selected"); - auto_switch_remote_scene_tree = EDITOR_DEF("debugger/auto_switch_to_remote_scene_tree", false); - inspect_scene_tree_timeout = EDITOR_DEF("debugger/remote_scene_tree_refresh_interval", 1.0); - inspect_edited_object_timeout = EDITOR_DEF("debugger/remote_inspect_refresh_interval", 0.2); - inspected_object_id = ObjectID(); - updating_scene_tree = false; - } - { // File dialog file_dialog = memnew(EditorFileDialog); file_dialog->connect_compat("file_selected", this, "_file_selected"); @@ -2508,6 +1702,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { visual_profiler->set_name(TTR("Visual Profiler")); tabs->add_child(visual_profiler); visual_profiler->connect_compat("enable_profiling", this, "_visual_profiler_activate"); + visual_profiler->connect_compat("break_request", this, "_profiler_seeked"); } { //network profiler @@ -2590,7 +1785,6 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { vmem_total->set_custom_minimum_size(Size2(100, 0) * EDSCALE); vmem_hb->add_child(vmem_total); vmem_refresh = memnew(ToolButton); - vmem_refresh->set_disabled(true); vmem_hb->add_child(vmem_refresh); vmem_vb->add_child(vmem_hb); vmem_refresh->connect_compat("pressed", this, "_video_mem_request"); @@ -2638,6 +1832,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { info_left->add_child(memnew(Label(TTR("Clicked Control Type:")))); info_left->add_child(clicked_ctrl_type); + scene_tree = memnew(SceneDebuggerTree); live_edit_root = memnew(LineEdit); live_edit_root->set_h_size_flags(SIZE_EXPAND_FILL); @@ -2651,8 +1846,6 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { le_clear = memnew(Button(TTR("Clear"))); lehb->add_child(le_clear); info_left->add_child(lehb); - le_set->set_disabled(true); - le_clear->set_disabled(true); } misc->add_child(memnew(VSeparator)); @@ -2669,27 +1862,18 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { msgdialog = memnew(AcceptDialog); add_child(msgdialog); - p_editor->get_undo_redo()->set_method_notify_callback(_method_changeds, this); - p_editor->get_undo_redo()->set_property_notify_callback(_property_changeds, this); live_debug = true; camera_override = OVERRIDE_NONE; last_path_id = false; error_count = 0; warning_count = 0; - hide_on_stop = true; - enable_external_editor = false; - last_error_count = 0; - last_warning_count = 0; - - EditorNode::get_singleton()->get_pause_button()->connect_compat("pressed", this, "_paused"); + _update_buttons_state(); } ScriptEditorDebugger::~ScriptEditorDebugger() { - memdelete(variables); - ppeer->set_stream_peer(Ref<StreamPeer>()); - server->stop(); - _clear_remote_objects(); + inspector->clear_cache(); + memdelete(scene_tree); } diff --git a/editor/script_editor_debugger.h b/editor/debugger/script_editor_debugger.h index 5a821a4a88..ce205e95b1 100644 --- a/editor/script_editor_debugger.h +++ b/editor/debugger/script_editor_debugger.h @@ -32,7 +32,8 @@ #define SCRIPT_EDITOR_DEBUGGER_H #include "core/io/packet_peer.h" -#include "core/io/tcp_server.h" +#include "core/io/stream_peer_tcp.h" +#include "editor/debugger/editor_debugger_inspector.h" #include "editor/editor_inspector.h" #include "editor/property_editor.h" #include "scene/3d/camera.h" @@ -41,7 +42,6 @@ class Tree; class EditorNode; -class ScriptEditorDebuggerVariables; class LineEdit; class TabContainer; class RichTextLabel; @@ -53,13 +53,14 @@ class ItemList; class EditorProfiler; class EditorVisualProfiler; class EditorNetworkProfiler; - -class ScriptEditorDebuggerInspectedObject; +class SceneDebuggerTree; class ScriptEditorDebugger : public MarginContainer { GDCLASS(ScriptEditorDebugger, MarginContainer); + friend class EditorDebuggerNode; + public: enum CameraOverride { OVERRIDE_NONE, @@ -77,16 +78,8 @@ private: MESSAGE_SUCCESS, }; - enum ItemMenu { - ITEM_MENU_COPY_ERROR, - ITEM_MENU_SAVE_REMOTE_NODE, - ITEM_MENU_COPY_NODE_PATH, - }; - AcceptDialog *msgdialog; - Button *debugger_button; - LineEdit *clicked_ctrl; LineEdit *clicked_ctrl_type; LineEdit *live_edit_root; @@ -94,35 +87,15 @@ private: Button *le_clear; Button *export_csv; - bool updating_scene_tree; - float inspect_scene_tree_timeout; - float inspect_edited_object_timeout; - bool auto_switch_remote_scene_tree; - ObjectID inspected_object_id; - ScriptEditorDebuggerVariables *variables; - Map<ObjectID, ScriptEditorDebuggerInspectedObject *> remote_objects; - Set<ObjectID> unfold_cache; - VBoxContainer *errors_tab; Tree *error_tree; - Tree *inspect_scene_tree; Button *clearbutton; PopupMenu *item_menu; EditorFileDialog *file_dialog; - enum FileDialogMode { - SAVE_CSV, - SAVE_NODE, - }; - FileDialogMode file_dialog_mode; int error_count; int warning_count; - int last_error_count; - int last_warning_count; - - bool hide_on_stop; - bool enable_external_editor; bool skip_breakpoints_value = false; Ref<Script> stack_script; @@ -135,10 +108,11 @@ private: Button *copy; Button *step; Button *next; - Button *back; - Button *forward; Button *dobreak; Button *docontinue; + // Reference to "Remote" tab in scene tree. Needed by _live_edit_set and buttons state. + // Each debugger should have it's tree in the future I guess. + const Tree *editor_remote_tree = NULL; List<Vector<float> > perf_history; Vector<float> perf_max; @@ -155,16 +129,12 @@ private: LineEdit *vmem_total; Tree *stack_dump; - EditorInspector *inspector; + EditorDebuggerInspector *inspector; + SceneDebuggerTree *scene_tree; - Ref<TCP_Server> server; Ref<StreamPeerTCP> connection; Ref<PacketPeerStream> ppeer; - String message_type; - Array message; - int pending_in_queue; - HashMap<NodePath, int> node_path_cache; int last_path_id; Map<String, int> res_path_cache; @@ -175,7 +145,9 @@ private: EditorNode *editor; - bool breaked; + OS::ProcessID remote_pid = 0; + bool breaked = false; + bool can_debug = false; bool live_debug; @@ -184,18 +156,14 @@ private: void _performance_draw(); void _performance_select(); void _stack_dump_frame_selected(); - void _output_clear(); - void _scene_tree_folded(Object *obj); - void _scene_tree_selected(); - void _scene_tree_rmb_selected(const Vector2 &p_position); void _file_selected(const String &p_file); - void _scene_tree_request(); void _parse_message(const String &p_msg, const Array &p_data); void _set_reason_text(const String &p_reason, MessageType p_type); - void _scene_tree_property_select_object(ObjectID p_object); - void _scene_tree_property_value_edited(const String &p_prop, const Variant &p_value); - int _update_scene_tree(TreeItem *parent, const Array &nodes, int current_index); + void _update_buttons_state(); + void _remote_object_selected(ObjectID p_object); + void _remote_object_edited(ObjectID, const String &p_prop, const Variant &p_value); + void _remote_object_property_updated(ObjectID p_id, const String &p_property); void _video_mem_request(); @@ -221,28 +189,34 @@ private: void _network_profiler_activate(bool p_enable); - void _paused(); - - void _set_remote_object(ObjectID p_id, ScriptEditorDebuggerInspectedObject *p_obj); - void _clear_remote_objects(); void _clear_errors_list(); void _error_tree_item_rmb_selected(const Vector2 &p_pos); void _item_menu_id_pressed(int p_option); void _tab_changed(int p_tab); + void _put_msg(String p_message, Array p_data); void _export_csv(); void _clear_execution(); + void _stop_and_notify(); protected: void _notification(int p_what); static void _bind_methods(); public: - void start(); - void pause(); - void unpause(); + void request_remote_object(ObjectID p_obj_id); + void update_remote_object(ObjectID p_obj_id, const String &p_prop, const Variant &p_value); + Object *get_remote_object(ObjectID p_id); + + // Needed by _live_edit_set, buttons state. + void set_editor_remote_tree(const Tree *p_tree) { editor_remote_tree = p_tree; } + + void request_remote_tree(); + const SceneDebuggerTree *get_remote_tree(); + + void start(Ref<StreamPeerTCP> p_connection); void stop(); void debug_skip_breakpoints(); @@ -252,14 +226,23 @@ public: void debug_step(); void debug_break(); void debug_continue(); - + bool is_breaked() const { return breaked; } + bool is_debuggable() const { return can_debug; } + bool is_session_active() { return connection.is_valid() && connection->is_connected_to_host(); }; + int get_remote_pid() const { return remote_pid; } + + int get_error_count() const { return error_count; } + int get_warning_count() const { return warning_count; } + String get_stack_script_file() const; + int get_stack_script_line() const; + int get_stack_script_frame() const; + + void update_tabs(); String get_var_value(const String &p_var) const; + void save_node(ObjectID p_id, const String &p_file); void set_live_debugging(bool p_enable); - static void _method_changeds(void *p_ud, Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE); - static void _property_changeds(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value); - void live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name); void live_debug_instance_node(const NodePath &p_parent, const String &p_path, const String &p_name); void live_debug_remove_node(const NodePath &p_at); @@ -275,15 +258,6 @@ public: void update_live_edit_root(); - void set_hide_on_stop(bool p_hide); - - bool get_debug_with_external_editor() const; - void set_debug_with_external_editor(bool p_enabled); - - Ref<Script> get_dump_stack_script() const; - - void set_tool_button(Button *p_tb) { debugger_button = p_tb; } - void reload_scripts(); bool is_skip_breakpoints(); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 59fa40846f..413959bb99 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -123,6 +123,7 @@ #include "editor/plugins/cpu_particles_2d_editor_plugin.h" #include "editor/plugins/cpu_particles_editor_plugin.h" #include "editor/plugins/curve_editor_plugin.h" +#include "editor/plugins/debugger_editor_plugin.h" #include "editor/plugins/editor_preview_plugins.h" #include "editor/plugins/gi_probe_editor_plugin.h" #include "editor/plugins/gradient_editor_plugin.h" @@ -168,7 +169,6 @@ #include "editor/quick_open.h" #include "editor/register_exporters.h" #include "editor/run_settings_dialog.h" -#include "editor/script_editor_debugger.h" #include "editor/settings_config_dialog.h" #include <stdio.h> @@ -472,7 +472,7 @@ void EditorNode::_notification(int p_what) { recent_scenes->set_as_minsize(); // debugger area - if (ScriptEditor::get_singleton()->get_debugger()->is_visible()) + if (EditorDebuggerNode::get_singleton()->is_visible()) bottom_panel->add_style_override("panel", gui_base->get_stylebox("BottomPanelDebuggerOverride", "EditorStyles")); // update_icons @@ -1846,7 +1846,7 @@ void EditorNode::_edit_current() { Node *selected_node = NULL; - if (current_obj->is_class("ScriptEditorDebuggerInspectedObject")) { + if (current_obj->is_class("EditorDebuggerRemoteObject")) { editable_warning = TTR("This is a remote object, so changes to it won't be kept.\nPlease read the documentation relevant to debugging to better understand this workflow."); capitalize = false; disable_folding = true; @@ -2048,9 +2048,13 @@ void EditorNode::_run(bool p_current, const String &p_custom) { editor_data.get_editor_breakpoints(&breakpoints); args = ProjectSettings::get_singleton()->get("editor/main_run_args"); - skip_breakpoints = ScriptEditor::get_singleton()->get_debugger()->is_skip_breakpoints(); + skip_breakpoints = EditorDebuggerNode::get_singleton()->is_skip_breakpoints(); - Error error = editor_run.run(run_filename, args, breakpoints, skip_breakpoints); + int instances = 1; + if (debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_TWO))) + instances = 2; + + Error error = editor_run.run(run_filename, args, breakpoints, skip_breakpoints, instances); if (error != OK) { @@ -2481,6 +2485,16 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { run_settings_dialog->popup_run_settings(); } break; + case RUN_DEBUG_ONE: { + debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_ONE), true); + debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_TWO), false); + + } break; + case RUN_DEBUG_TWO: { + debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_TWO), true); + debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_ONE), false); + + } break; case RUN_SETTINGS: { project_settings->popup_project_settings(); @@ -2571,7 +2585,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_LIVE_DEBUG)); debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_LIVE_DEBUG), !ischecked); - ScriptEditor::get_singleton()->get_debugger()->set_live_debugging(!ischecked); + EditorDebuggerNode::get_singleton()->set_live_debugging(!ischecked); EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_live_debug", !ischecked); } break; @@ -3242,7 +3256,7 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) { //this should only happen at the very end - ScriptEditor::get_singleton()->get_debugger()->update_live_edit_root(); + EditorDebuggerNode::get_singleton()->update_live_edit_root(); ScriptEditor::get_singleton()->set_scene_root_script(editor_data.get_scene_root_script(editor_data.get_edited_scene())); editor_data.notify_edited_scene_changed(); } @@ -3477,7 +3491,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b opening_prev = false; scene_tree_dock->set_selected(new_scene); - ScriptEditor::get_singleton()->get_debugger()->update_live_edit_root(); + EditorDebuggerNode::get_singleton()->update_live_edit_root(); push_item(new_scene); @@ -3619,7 +3633,7 @@ void EditorNode::_quick_run() { _run(false, quick_run->get_selected()); } -void EditorNode::notify_child_process_exited() { +void EditorNode::notify_all_debug_sessions_exited() { _menu_option_confirm(RUN_STOP, false); stop_button->set_pressed(false); @@ -3703,9 +3717,13 @@ void EditorNode::unregister_editor_types() { _init_callbacks.clear(); } -void EditorNode::stop_child_process() { +void EditorNode::stop_child_process(OS::ProcessID p_pid) { - _menu_option_confirm(RUN_STOP, false); + if (has_child_process(p_pid)) { + editor_run.stop_child_process(p_pid); + if (!editor_run.get_child_process_count()) // All children stopped. Closing. + _menu_option_confirm(RUN_STOP, false); + } } Ref<Script> EditorNode::get_object_custom_type_base(const Object *p_object) const { @@ -4888,7 +4906,7 @@ void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) { bottom_panel_items[i].button->set_pressed(i == p_idx); bottom_panel_items[i].control->set_visible(i == p_idx); } - if (ScriptEditor::get_singleton()->get_debugger() == bottom_panel_items[p_idx].control) { // this is the debug panel which uses tabs, so the top section should be smaller + if (EditorDebuggerNode::get_singleton() == bottom_panel_items[p_idx].control) { // this is the debug panel which uses tabs, so the top section should be smaller bottom_panel->add_style_override("panel", gui_base->get_stylebox("BottomPanelDebuggerOverride", "EditorStyles")); } else { bottom_panel->add_style_override("panel", gui_base->get_stylebox("panel", "TabContainer")); @@ -6254,6 +6272,13 @@ EditorNode::EditorNode() { p->add_check_shortcut(ED_SHORTCUT("editor/sync_script_changes", TTR("Sync Script Changes")), RUN_RELOAD_SCRIPTS); p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is turned on, any script that is saved will be reloaded on the running game.\nWhen used remotely on a device, this is more efficient with network filesystem.")); p->set_item_checked(p->get_item_count() - 1, true); + + // Multi-instance, start/stop + p->add_separator(); + p->add_radio_check_item(TTR("Debug 1 instance"), RUN_DEBUG_ONE); + p->add_radio_check_item(TTR("Debug 2 instances"), RUN_DEBUG_TWO); + p->set_item_checked(p->get_item_index(RUN_DEBUG_ONE), true); + p->connect_compat("id_pressed", this, "_menu_option"); menu_hb->add_spacer(); @@ -6639,6 +6664,7 @@ EditorNode::EditorNode() { file_server = memnew(EditorFileServer); + add_editor_plugin(memnew(DebuggerEditorPlugin(this))); add_editor_plugin(memnew(AnimationPlayerEditorPlugin(this))); add_editor_plugin(memnew(CanvasItemEditorPlugin(this))); add_editor_plugin(memnew(SpatialEditorPlugin(this))); diff --git a/editor/editor_node.h b/editor/editor_node.h index ef9be0677d..a982b9d85f 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -162,6 +162,8 @@ private: RUN_PLAY_NATIVE, RUN_PLAY_CUSTOM_SCENE, RUN_SCENE_SETTINGS, + RUN_DEBUG_ONE, + RUN_DEBUG_TWO, RUN_SETTINGS, RUN_PROJECT_DATA_FOLDER, RUN_PROJECT_MANAGER, @@ -764,10 +766,10 @@ public: void set_convert_old_scene(bool p_old) { convert_old = p_old; } - void notify_child_process_exited(); + void notify_all_debug_sessions_exited(); - OS::ProcessID get_child_process_id() const { return editor_run.get_pid(); } - void stop_child_process(); + OS::ProcessID has_child_process(OS::ProcessID p_pid) const { return editor_run.has_child_process(p_pid); } + void stop_child_process(OS::ProcessID p_pid); Ref<Theme> get_editor_theme() const { return theme; } Ref<Script> get_object_custom_type_base(const Object *p_object) const; diff --git a/editor/editor_path.cpp b/editor/editor_path.cpp index cfb70a087a..696474d4b1 100644 --- a/editor/editor_path.cpp +++ b/editor/editor_path.cpp @@ -106,7 +106,7 @@ void EditorPath::update_path() { if (name == "") name = r->get_class(); - } else if (obj->is_class("ScriptEditorDebuggerInspectedObject")) + } else if (obj->is_class("EditorDebuggerRemoteObject")) name = obj->call("get_title"); else if (Object::cast_to<Node>(obj)) name = Object::cast_to<Node>(obj)->get_name(); diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp index ff7420e19b..3200a0ac8b 100644 --- a/editor/editor_run.cpp +++ b/editor/editor_run.cpp @@ -38,7 +38,7 @@ EditorRun::Status EditorRun::get_status() const { return status; } -Error EditorRun::run(const String &p_scene, const String &p_custom_args, const List<String> &p_breakpoints, const bool &p_skip_breakpoints) { +Error EditorRun::run(const String &p_scene, const String &p_custom_args, const List<String> &p_breakpoints, const bool &p_skip_breakpoints, const int &p_instances) { List<String> args; @@ -187,20 +187,40 @@ Error EditorRun::run(const String &p_scene, const String &p_custom_args, const L }; printf("\n"); - pid = 0; - Error err = OS::get_singleton()->execute(exec, args, false, &pid); - ERR_FAIL_COND_V(err, err); + for (int i = 0; i < p_instances; i++) { + OS::ProcessID pid = 0; + Error err = OS::get_singleton()->execute(exec, args, false, &pid); + ERR_FAIL_COND_V(err, err); + pids.push_back(pid); + } status = STATUS_PLAY; return OK; } +bool EditorRun::has_child_process(OS::ProcessID p_pid) const { + for (const List<OS::ProcessID>::Element *E = pids.front(); E; E = E->next()) { + if (E->get() == p_pid) + return true; + } + return false; +} + +void EditorRun::stop_child_process(OS::ProcessID p_pid) { + if (has_child_process(p_pid)) { + OS::get_singleton()->kill(p_pid); + pids.erase(p_pid); + } +} + void EditorRun::stop() { - if (status != STATUS_STOP && pid != 0) { + if (status != STATUS_STOP && pids.size() > 0) { - OS::get_singleton()->kill(pid); + for (List<OS::ProcessID>::Element *E = pids.front(); E; E = E->next()) { + OS::get_singleton()->kill(E->get()); + } } status = STATUS_STOP; diff --git a/editor/editor_run.h b/editor/editor_run.h index b50a2c2f0e..389a1e6b20 100644 --- a/editor/editor_run.h +++ b/editor/editor_run.h @@ -42,7 +42,7 @@ public: STATUS_STOP }; - OS::ProcessID pid; + List<OS::ProcessID> pids; private: bool debug_collisions; @@ -51,11 +51,13 @@ private: public: Status get_status() const; - Error run(const String &p_scene, const String &p_custom_args, const List<String> &p_breakpoints, const bool &p_skip_breakpoints = false); + Error run(const String &p_scene, const String &p_custom_args, const List<String> &p_breakpoints, const bool &p_skip_breakpoints = false, const int &p_instances = 1); void run_native_notify() { status = STATUS_PLAY; } void stop(); - OS::ProcessID get_pid() const { return pid; } + void stop_child_process(OS::ProcessID p_pid); + bool has_child_process(OS::ProcessID p_pid) const; + int get_child_process_count() const { return pids.size(); } void set_debug_collisions(bool p_debug); bool get_debug_collisions() const; diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index 6eaf6eb04a..4effdc63d1 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -247,7 +247,7 @@ void InspectorDock::_prepare_history() { } } else if (Object::cast_to<Node>(obj)) { text = Object::cast_to<Node>(obj)->get_name(); - } else if (obj->is_class("ScriptEditorDebuggerInspectedObject")) { + } else if (obj->is_class("EditorDebuggerRemoteObject")) { text = obj->call("get_title"); } else { text = obj->get_class(); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index b91b346089..dfcaf5cdd2 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -34,12 +34,12 @@ #include "core/os/keyboard.h" #include "core/print_string.h" #include "core/project_settings.h" +#include "editor/debugger/editor_debugger_node.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/script_editor_plugin.h" -#include "editor/script_editor_debugger.h" #include "scene/2d/light_2d.h" #include "scene/2d/particles_2d.h" #include "scene/2d/polygon_2d.h" @@ -3990,7 +3990,7 @@ void CanvasItemEditor::_notification(int p_what) { if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { if (!is_visible() && override_camera_button->is_pressed()) { - ScriptEditorDebugger *debugger = ScriptEditor::get_singleton()->get_debugger(); + EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton(); debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_NONE); override_camera_button->set_pressed(false); @@ -4345,7 +4345,7 @@ void CanvasItemEditor::_button_toggle_grid_snap(bool p_status) { viewport->update(); } void CanvasItemEditor::_button_override_camera(bool p_pressed) { - ScriptEditorDebugger *debugger = ScriptEditor::get_singleton()->get_debugger(); + EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton(); if (p_pressed) { debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_2D); @@ -5960,9 +5960,9 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String & if (parent) { String new_name = parent->validate_child_name(child); - ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); - editor_data->get_undo_redo().add_do_method(sed, "live_debug_create_node", editor->get_edited_scene()->get_path_to(parent), child->get_class(), new_name); - editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); + EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); + editor_data->get_undo_redo().add_do_method(ed, "live_debug_create_node", editor->get_edited_scene()->get_path_to(parent), child->get_class(), new_name); + editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); } // handle with different property for texture @@ -6030,9 +6030,9 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons editor_data->get_undo_redo().add_undo_method(parent, "remove_child", instanced_scene); String new_name = parent->validate_child_name(instanced_scene); - ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); - editor_data->get_undo_redo().add_do_method(sed, "live_debug_instance_node", editor->get_edited_scene()->get_path_to(parent), path, new_name); - editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); + EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); + editor_data->get_undo_redo().add_do_method(ed, "live_debug_instance_node", editor->get_edited_scene()->get_path_to(parent), path, new_name); + editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); CanvasItem *parent_ci = Object::cast_to<CanvasItem>(parent); if (parent_ci) { diff --git a/editor/plugins/debugger_editor_plugin.cpp b/editor/plugins/debugger_editor_plugin.cpp new file mode 100644 index 0000000000..2534a2cc17 --- /dev/null +++ b/editor/plugins/debugger_editor_plugin.cpp @@ -0,0 +1,51 @@ +/*************************************************************************/ +/* debugger_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "debugger_editor_plugin.h" + +#include "core/os/keyboard.h" +#include "editor/debugger/editor_debugger_node.h" + +DebuggerEditorPlugin::DebuggerEditorPlugin(EditorNode *p_editor) { + ED_SHORTCUT("debugger/step_into", TTR("Step Into"), KEY_F11); + ED_SHORTCUT("debugger/step_over", TTR("Step Over"), KEY_F10); + ED_SHORTCUT("debugger/break", TTR("Break")); + ED_SHORTCUT("debugger/continue", TTR("Continue"), KEY_F12); + ED_SHORTCUT("debugger/keep_debugger_open", TTR("Keep Debugger Open")); + ED_SHORTCUT("debugger/debug_with_external_editor", TTR("Debug with External Editor")); + + EditorDebuggerNode *debugger = memnew(EditorDebuggerNode); + Button *db = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Debugger"), debugger); + debugger->set_tool_button(db); +} + +DebuggerEditorPlugin::~DebuggerEditorPlugin() { + // Should delete debugger? +} diff --git a/editor/plugins/debugger_editor_plugin.h b/editor/plugins/debugger_editor_plugin.h new file mode 100644 index 0000000000..05d6ece72d --- /dev/null +++ b/editor/plugins/debugger_editor_plugin.h @@ -0,0 +1,50 @@ +/*************************************************************************/ +/* debugger_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef DEBUGGER_EDITOR_PLUGIN_H +#define DEBUGGER_EDITOR_PLUGIN_H + +#include "editor/debugger/editor_debugger_node.h" +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" + +class DebuggerEditorPlugin : public EditorPlugin { + + GDCLASS(DebuggerEditorPlugin, EditorPlugin); + +public: + virtual String get_name() const { return "Debugger"; } + bool has_main_screen() const { return false; } + + DebuggerEditorPlugin(EditorNode *p_node); + ~DebuggerEditorPlugin(); +}; + +#endif // DEBUGGER_EDITOR_PLUGIN_H diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index cea18b63f1..c0928ba79b 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -36,6 +36,8 @@ #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/project_settings.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" @@ -44,7 +46,6 @@ #include "editor/find_in_files.h" #include "editor/node_dock.h" #include "editor/plugins/shader_editor_plugin.h" -#include "editor/script_editor_debugger.h" #include "scene/main/viewport.h" #include "scene/scene_string_names.h" #include "script_text_editor.h" @@ -261,7 +262,7 @@ ScriptEditor *ScriptEditor::script_editor = NULL; String ScriptEditor::_get_debug_tooltip(const String &p_text, Node *_se) { - String val = debugger->get_var_value(p_text); + String val = EditorDebuggerNode::get_singleton()->get_var_value(p_text); if (val != String()) { return p_text + ": " + val; } else { @@ -276,11 +277,6 @@ void ScriptEditor::_breaked(bool p_breaked, bool p_can_debug) { return; } - debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), !(p_breaked && p_can_debug)); - debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), !(p_breaked && p_can_debug)); - debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), p_breaked); - debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), !p_breaked); - for (int i = 0; i < tab_container->get_child_count(); i++) { ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i)); @@ -292,11 +288,6 @@ void ScriptEditor::_breaked(bool p_breaked, bool p_can_debug) { } } -void ScriptEditor::_show_debugger(bool p_show) { - - //debug_menu->get_popup()->set_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW), p_show); -} - void ScriptEditor::_script_created(Ref<Script> p_script) { editor->push_item(p_script.operator->()); } @@ -843,7 +834,7 @@ void ScriptEditor::_res_saved_callback(const Ref<Resource> &p_res) { void ScriptEditor::_live_auto_reload_running_scripts() { pending_auto_reload = false; - debugger->reload_scripts(); + EditorDebuggerNode::get_singleton()->reload_scripts(); } bool ScriptEditor::_test_script_times_on_disk(RES p_for_script) { @@ -1123,27 +1114,6 @@ void ScriptEditor::_menu_option(int p_option) { _sort_list_on_update = true; _update_script_names(); } break; - case DEBUG_SHOW: { - if (debugger) { - bool visible = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW)); - debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW), !visible); - if (visible) - debugger->hide(); - else - debugger->show(); - } - } break; - case DEBUG_SHOW_KEEP_OPEN: { - bool visible = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN)); - if (debugger) - debugger->set_hide_on_stop(visible); - debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN), !visible); - } break; - case DEBUG_WITH_EXTERNAL_EDITOR: { - bool debug_with_external_editor = !debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR)); - debugger->set_debug_with_external_editor(debug_with_external_editor); - debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR), debug_with_external_editor); - } break; case TOGGLE_SCRIPTS_PANEL: { if (current) { ScriptTextEditor *editor = Object::cast_to<ScriptTextEditor>(current); @@ -1294,29 +1264,6 @@ void ScriptEditor::_menu_option(int p_option) { case CLOSE_ALL: { _close_all_tabs(); } break; - case DEBUG_NEXT: { - - if (debugger) - debugger->debug_next(); - } break; - case DEBUG_STEP: { - - if (debugger) - debugger->debug_step(); - - } break; - case DEBUG_BREAK: { - - if (debugger) - debugger->debug_break(); - - } break; - case DEBUG_CONTINUE: { - - if (debugger) - debugger->debug_continue(); - - } break; case WINDOW_MOVE_UP: { if (tab_container->get_current_tab() > 0) { @@ -1439,8 +1386,6 @@ void ScriptEditor::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { - editor->connect_compat("play_pressed", this, "_editor_play"); - editor->connect_compat("pause_pressed", this, "_editor_pause"); editor->connect_compat("stop_pressed", this, "_editor_stop"); editor->connect_compat("script_add_function_request", this, "_add_callback"); editor->connect_compat("resource_saved", this, "_res_saved_callback"); @@ -1481,8 +1426,6 @@ void ScriptEditor::_notification(int p_what) { case NOTIFICATION_EXIT_TREE: { - editor->disconnect_compat("play_pressed", this, "_editor_play"); - editor->disconnect_compat("pause_pressed", this, "_editor_pause"); editor->disconnect_compat("stop_pressed", this, "_editor_stop"); } break; @@ -2062,7 +2005,7 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra return false; } - if ((debugger->get_dump_stack_script() != p_resource || debugger->get_debug_with_external_editor()) && + if ((EditorDebuggerNode::get_singleton()->get_dump_stack_script() != p_resource || EditorDebuggerNode::get_singleton()->get_debug_with_external_editor()) && p_resource->get_path().is_resource_file() && p_resource->get_class_name() != StringName("VisualScript") && bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor"))) { @@ -2277,26 +2220,7 @@ void ScriptEditor::open_script_create_dialog(const String &p_base_name, const St script_create_dialog->config(p_base_name, p_base_path); } -void ScriptEditor::_editor_play() { - - debugger->start(); - debug_menu->get_popup()->grab_focus(); - debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true); - debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), true); - debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), false); - debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true); -} - -void ScriptEditor::_editor_pause() { -} void ScriptEditor::_editor_stop() { - - debugger->stop(); - debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true); - debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), true); - debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), true); - debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true); - for (int i = 0; i < tab_container->get_child_count(); i++) { ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i)); @@ -3125,8 +3049,6 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method("_close_other_tabs", &ScriptEditor::_close_other_tabs); ClassDB::bind_method("_open_recent_script", &ScriptEditor::_open_recent_script); ClassDB::bind_method("_theme_option", &ScriptEditor::_theme_option); - ClassDB::bind_method("_editor_play", &ScriptEditor::_editor_play); - ClassDB::bind_method("_editor_pause", &ScriptEditor::_editor_pause); ClassDB::bind_method("_editor_stop", &ScriptEditor::_editor_stop); ClassDB::bind_method("_add_callback", &ScriptEditor::_add_callback); ClassDB::bind_method("_reload_scripts", &ScriptEditor::_reload_scripts); @@ -3141,7 +3063,6 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method("_copy_script_path", &ScriptEditor::_copy_script_path); ClassDB::bind_method("_breaked", &ScriptEditor::_breaked); - ClassDB::bind_method("_show_debugger", &ScriptEditor::_show_debugger); ClassDB::bind_method("_get_debug_tooltip", &ScriptEditor::_get_debug_tooltip); ClassDB::bind_method("_autosave_scripts", &ScriptEditor::_autosave_scripts); ClassDB::bind_method("_update_autosave_timer", &ScriptEditor::_update_autosave_timer); @@ -3358,26 +3279,16 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { script_search_menu->get_popup()->set_hide_on_window_lose_focus(true); script_search_menu->get_popup()->connect_compat("id_pressed", this, "_menu_option"); - debug_menu = memnew(MenuButton); + MenuButton *debug_menu = memnew(MenuButton); menu_hb->add_child(debug_menu); - debug_menu->set_text(TTR("Debug")); - debug_menu->set_switch_on_hover(true); - debug_menu->get_popup()->set_hide_on_window_lose_focus(true); - debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_into", TTR("Step Into"), KEY_F11), DEBUG_STEP); - debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_over", TTR("Step Over"), KEY_F10), DEBUG_NEXT); - debug_menu->get_popup()->add_separator(); - debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/break", TTR("Break")), DEBUG_BREAK); - debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/continue", TTR("Continue"), KEY_F12), DEBUG_CONTINUE); - debug_menu->get_popup()->add_separator(); - //debug_menu->get_popup()->add_check_item("Show Debugger",DEBUG_SHOW); - debug_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("debugger/keep_debugger_open", TTR("Keep Debugger Open")), DEBUG_SHOW_KEEP_OPEN); - debug_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("debugger/debug_with_external_editor", TTR("Debug with External Editor")), DEBUG_WITH_EXTERNAL_EDITOR); - debug_menu->get_popup()->connect_compat("id_pressed", this, "_menu_option"); - - debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true); - debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), true); - debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), true); - debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true); + debug_menu->hide(); // Handled by EditorDebuggerNode below. + + EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton(); + debugger->set_script_debug_button(debug_menu); + debugger->connect_compat("goto_script_line", this, "_goto_script_line"); + debugger->connect_compat("set_execution", this, "_set_execution"); + debugger->connect_compat("clear_execution", this, "_clear_execution"); + debugger->connect_compat("breaked", this, "_breaked"); menu_hb->add_spacer(); @@ -3445,12 +3356,6 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { error_dialog = memnew(AcceptDialog); add_child(error_dialog); - debugger = memnew(ScriptEditorDebugger(editor)); - debugger->connect_compat("goto_script_line", this, "_goto_script_line"); - debugger->connect_compat("set_execution", this, "_set_execution"); - debugger->connect_compat("clear_execution", this, "_clear_execution"); - debugger->connect_compat("show_debugger", this, "_show_debugger"); - disk_changed = memnew(ConfirmationDialog); { VBoxContainer *vbc = memnew(VBoxContainer); @@ -3475,11 +3380,6 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { script_editor = this; - Button *db = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Debugger"), debugger); - debugger->set_tool_button(db); - - debugger->connect_compat("breaked", this, "_breaked"); - autosave_timer = memnew(Timer); autosave_timer->set_one_shot(false); autosave_timer->connect_compat(SceneStringNames::get_singleton()->tree_entered, this, "_update_autosave_timer"); @@ -3505,7 +3405,6 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { find_in_files_button->hide(); history_pos = -1; - //debugger_gui->hide(); edit_pass = 0; trim_trailing_whitespace_on_save = EditorSettings::get_singleton()->get("text_editor/files/trim_trailing_whitespace_on_save"); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index ec84bcb461..b4b4f33fc5 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -73,7 +73,7 @@ public: ScriptEditorQuickOpen(); }; -class ScriptEditorDebugger; +class EditorDebuggerNode; class ScriptEditorBase : public VBoxContainer { @@ -155,13 +155,6 @@ class ScriptEditor : public PanelContainer { FILE_COPY_PATH, FILE_TOOL_RELOAD, FILE_TOOL_RELOAD_SOFT, - DEBUG_NEXT, - DEBUG_STEP, - DEBUG_BREAK, - DEBUG_CONTINUE, - DEBUG_SHOW, - DEBUG_SHOW_KEEP_OPEN, - DEBUG_WITH_EXTERNAL_EDITOR, SEARCH_IN_FILES, REPLACE_IN_FILES, SEARCH_HELP, @@ -233,7 +226,6 @@ class ScriptEditor : public PanelContainer { AcceptDialog *error_dialog; ConfirmationDialog *erase_tab_confirm; ScriptCreateDialog *script_create_dialog; - ScriptEditorDebugger *debugger; ToolButton *scripts_visible; String current_theme; @@ -315,8 +307,6 @@ class ScriptEditor : public PanelContainer { EditorScriptCodeCompletionCache *completion_cache; - void _editor_play(); - void _editor_pause(); void _editor_stop(); int edit_pass; @@ -335,7 +325,6 @@ class ScriptEditor : public PanelContainer { void _set_execution(REF p_script, int p_line); void _clear_execution(REF p_script); void _breaked(bool p_breaked, bool p_can_debug); - void _show_debugger(bool p_show); void _update_window_menu(); void _script_created(Ref<Script> p_script); @@ -457,7 +446,6 @@ public: VSplitContainer *get_left_list_split() { return list_split; } - ScriptEditorDebugger *get_debugger() { return debugger; } void set_live_auto_reload_running_scripts(bool p_enabled); static void register_create_syntax_highlighter_function(CreateSyntaxHighlighterFunc p_func); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 2069c035fb..4986e60ff0 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -32,10 +32,10 @@ #include "core/math/expression.h" #include "core/os/keyboard.h" +#include "editor/debugger/editor_debugger_node.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" -#include "editor/script_editor_debugger.h" void ConnectionInfoDialog::ok_pressed() { } @@ -870,7 +870,7 @@ void ScriptTextEditor::_breakpoint_item_pressed(int p_idx) { void ScriptTextEditor::_breakpoint_toggled(int p_row) { - ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(script->get_path(), p_row + 1, code_editor->get_text_edit()->is_line_set_as_breakpoint(p_row)); + EditorDebuggerNode::get_singleton()->set_breakpoint(script->get_path(), p_row + 1, code_editor->get_text_edit()->is_line_set_as_breakpoint(p_row)); } void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_column) { @@ -1294,7 +1294,7 @@ void ScriptTextEditor::_edit_option(int p_op) { int line = tx->cursor_get_line(); bool dobreak = !tx->is_line_set_as_breakpoint(line); tx->set_line_as_breakpoint(line, dobreak); - ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(script->get_path(), line + 1, dobreak); + EditorDebuggerNode::get_singleton()->set_breakpoint(script->get_path(), line + 1, dobreak); } break; case DEBUG_REMOVE_ALL_BREAKPOINTS: { @@ -1305,7 +1305,7 @@ void ScriptTextEditor::_edit_option(int p_op) { int line = E->get(); bool dobreak = !tx->is_line_set_as_breakpoint(line); tx->set_line_as_breakpoint(line, dobreak); - ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(script->get_path(), line + 1, dobreak); + EditorDebuggerNode::get_singleton()->set_breakpoint(script->get_path(), line + 1, dobreak); } } break; case DEBUG_GOTO_NEXT_BREAKPOINT: { diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 45f2a5063c..6e8eeaec82 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -36,12 +36,12 @@ #include "core/print_string.h" #include "core/project_settings.h" #include "core/sort_array.h" +#include "editor/debugger/editor_debugger_node.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/script_editor_plugin.h" -#include "editor/script_editor_debugger.h" #include "editor/spatial_editor_gizmos.h" #include "scene/3d/camera.h" #include "scene/3d/collision_shape.h" @@ -3421,9 +3421,9 @@ bool SpatialEditorViewport::_create_instance(Node *parent, String &path, const P editor_data->get_undo_redo().add_undo_method(parent, "remove_child", instanced_scene); String new_name = parent->validate_child_name(instanced_scene); - ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); - editor_data->get_undo_redo().add_do_method(sed, "live_debug_instance_node", editor->get_edited_scene()->get_path_to(parent), path, new_name); - editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); + EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); + editor_data->get_undo_redo().add_do_method(ed, "live_debug_instance_node", editor->get_edited_scene()->get_path_to(parent), path, new_name); + editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); Transform global_transform; Spatial *parent_spatial = Object::cast_to<Spatial>(parent); @@ -4497,7 +4497,7 @@ void SpatialEditor::_menu_item_toggled(bool pressed, int p_option) { } break; case MENU_TOOL_OVERRIDE_CAMERA: { - ScriptEditorDebugger *const debugger = ScriptEditor::get_singleton()->get_debugger(); + EditorDebuggerNode *const debugger = EditorDebuggerNode::get_singleton(); if (pressed) { using Override = ScriptEditorDebugger::CameraOverride; @@ -4554,7 +4554,7 @@ void SpatialEditor::_update_camera_override_viewport(Object *p_viewport) { if (!current_viewport) return; - ScriptEditorDebugger *const debugger = ScriptEditor::get_singleton()->get_debugger(); + EditorDebuggerNode *const debugger = EditorDebuggerNode::get_singleton(); camera_override_viewport_id = current_viewport->index; if (debugger->get_camera_override() >= ScriptEditorDebugger::OVERRIDE_3D_1) { @@ -5513,7 +5513,7 @@ void SpatialEditor::_notification(int p_what) { _init_grid(); } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { if (!is_visible() && tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->is_pressed()) { - ScriptEditorDebugger *debugger = ScriptEditor::get_singleton()->get_debugger(); + EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton(); debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_NONE); tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_pressed(false); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index beba7f0ed6..ebc7d56b24 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -35,6 +35,7 @@ #include "core/os/keyboard.h" #include "core/project_settings.h" +#include "editor/debugger/editor_debugger_node.h" #include "editor/editor_feature_profile.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" @@ -44,7 +45,6 @@ #include "editor/plugins/canvas_item_editor_plugin.h" #include "editor/plugins/script_editor_plugin.h" #include "editor/plugins/spatial_editor_plugin.h" -#include "editor/script_editor_debugger.h" #include "scene/main/viewport.h" #include "scene/resources/packed_scene.h" @@ -229,9 +229,9 @@ void SceneTreeDock::_perform_instance_scenes(const Vector<String> &p_files, Node editor_data->get_undo_redo().add_undo_method(parent, "remove_child", instanced_scene); String new_name = parent->validate_child_name(instanced_scene); - ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); - editor_data->get_undo_redo().add_do_method(sed, "live_debug_instance_node", edited_scene->get_path_to(parent), p_files[i], new_name); - editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(new_name))); + EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); + editor_data->get_undo_redo().add_do_method(ed, "live_debug_instance_node", edited_scene->get_path_to(parent), p_files[i], new_name); + editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(new_name))); } editor_data->get_undo_redo().commit_action(); @@ -591,10 +591,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { editor_data->get_undo_redo().add_undo_method(parent, "remove_child", dup); editor_data->get_undo_redo().add_do_reference(dup); - ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); + EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); - editor_data->get_undo_redo().add_do_method(sed, "live_debug_duplicate_node", edited_scene->get_path_to(node), dup->get_name()); - editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(dup->get_name()))); + editor_data->get_undo_redo().add_do_method(ed, "live_debug_duplicate_node", edited_scene->get_path_to(node), dup->get_name()); + editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(dup->get_name()))); } editor_data->get_undo_redo().commit_action(); @@ -1584,7 +1584,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V if (p_position_in_parent >= 0) editor_data->get_undo_redo().add_do_method(new_parent, "move_child", node, p_position_in_parent + inc); - ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); + EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); String old_name = former_names[ni]; String new_name = new_parent->validate_child_name(node); @@ -1609,8 +1609,8 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V path_renames[ni].second = fixed_node_path; } - editor_data->get_undo_redo().add_do_method(sed, "live_debug_reparent_node", edited_scene->get_path_to(node), edited_scene->get_path_to(new_parent), new_name, p_position_in_parent + inc); - editor_data->get_undo_redo().add_undo_method(sed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)).plus_file(new_name)), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index()); + editor_data->get_undo_redo().add_do_method(ed, "live_debug_reparent_node", edited_scene->get_path_to(node), edited_scene->get_path_to(new_parent), new_name, p_position_in_parent + inc); + editor_data->get_undo_redo().add_undo_method(ed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)).plus_file(new_name)), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index()); if (p_keep_global_xform) { if (Object::cast_to<Node2D>(node)) @@ -1849,9 +1849,9 @@ void SceneTreeDock::_delete_confirm() { editor_data->get_undo_redo().add_undo_method(this, "_set_owners", edited_scene, owners); editor_data->get_undo_redo().add_undo_reference(n); - ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); - editor_data->get_undo_redo().add_do_method(sed, "live_debug_remove_and_keep_node", edited_scene->get_path_to(n), n->get_instance_id()); - editor_data->get_undo_redo().add_undo_method(sed, "live_debug_restore_node", n->get_instance_id(), edited_scene->get_path_to(n->get_parent()), n->get_index()); + EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); + editor_data->get_undo_redo().add_do_method(ed, "live_debug_remove_and_keep_node", edited_scene->get_path_to(n), n->get_instance_id()); + editor_data->get_undo_redo().add_undo_method(ed, "live_debug_restore_node", n->get_instance_id(), edited_scene->get_path_to(n->get_parent()), n->get_index()); } } editor_data->get_undo_redo().commit_action(); @@ -1950,9 +1950,9 @@ void SceneTreeDock::_do_create(Node *p_parent) { editor_data->get_undo_redo().add_undo_method(p_parent, "remove_child", child); String new_name = p_parent->validate_child_name(child); - ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); - editor_data->get_undo_redo().add_do_method(sed, "live_debug_create_node", edited_scene->get_path_to(p_parent), child->get_class(), new_name); - editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)).plus_file(new_name))); + EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); + editor_data->get_undo_redo().add_do_method(ed, "live_debug_create_node", edited_scene->get_path_to(p_parent), child->get_class(), new_name); + editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)).plus_file(new_name))); } else { diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp index c1a902bdcc..9f8a531762 100644 --- a/editor/settings_config_dialog.cpp +++ b/editor/settings_config_dialog.cpp @@ -32,13 +32,13 @@ #include "core/os/keyboard.h" #include "core/project_settings.h" +#include "editor/debugger/editor_debugger_node.h" #include "editor_file_system.h" #include "editor_log.h" #include "editor_node.h" #include "editor_scale.h" #include "editor_settings.h" #include "scene/gui/margin_container.h" -#include "script_editor_debugger.h" void EditorSettingsDialog::ok_pressed() { @@ -119,9 +119,8 @@ void EditorSettingsDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { - ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); - undo_redo->set_method_notify_callback(sed->_method_changeds, sed); - undo_redo->set_property_notify_callback(sed->_property_changeds, sed); + undo_redo->set_method_notify_callback(EditorDebuggerNode::_method_changeds, NULL); + undo_redo->set_property_notify_callback(EditorDebuggerNode::_property_changeds, NULL); undo_redo->set_commit_notify_callback(_undo_redo_callback, this); } break; case NOTIFICATION_ENTER_TREE: { |