Added debugger plugin support

Changes:
* EngineDebugger is exposed to gdscript. Game side of communication can be implemented through it.
* EditorDebuggerPlugin is added which handles the editor side of communication.
This commit is contained in:
simpu 2020-05-18 20:25:49 +05:30
parent a609b30ddb
commit ea2eaf8c27
16 changed files with 780 additions and 1 deletions

View file

@ -34,6 +34,7 @@
#include "editor/debugger/script_editor_debugger.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
#include "editor/plugins/editor_debugger_plugin.h"
#include "editor/plugins/script_editor_plugin.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/tab_container.h"
@ -114,6 +115,12 @@ ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() {
tabs->add_theme_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox("DebuggerPanel", "EditorStyles"));
}
if (!debugger_plugins.empty()) {
for (Set<Ref<Script>>::Element *i = debugger_plugins.front(); i; i = i->next()) {
node->add_debugger_plugin(i->get());
}
}
return node;
}
@ -618,3 +625,23 @@ void EditorDebuggerNode::live_debug_reparent_node(const NodePath &p_at, const No
dbg->live_debug_reparent_node(p_at, p_new_place, p_new_name, p_at_pos);
});
}
void EditorDebuggerNode::add_debugger_plugin(const Ref<Script> &p_script) {
ERR_FAIL_COND_MSG(debugger_plugins.has(p_script), "Debugger plugin already exists.");
ERR_FAIL_COND_MSG(p_script.is_null(), "Debugger plugin script is null");
ERR_FAIL_COND_MSG(String(p_script->get_instance_base_type()) == "", "Debugger plugin script has error.");
ERR_FAIL_COND_MSG(String(p_script->get_instance_base_type()) != "EditorDebuggerPlugin", "Base type of debugger plugin is not 'EditorDebuggerPlugin'.");
ERR_FAIL_COND_MSG(!p_script->is_tool(), "Debugger plugin script is not in tool mode.");
debugger_plugins.insert(p_script);
for (int i = 0; get_debugger(i); i++) {
get_debugger(i)->add_debugger_plugin(p_script);
}
}
void EditorDebuggerNode::remove_debugger_plugin(const Ref<Script> &p_script) {
ERR_FAIL_COND_MSG(!debugger_plugins.has(p_script), "Debugger plugin doesn't exists.");
debugger_plugins.erase(p_script);
for (int i = 0; get_debugger(i); i++) {
get_debugger(i)->remove_debugger_plugin(p_script);
}
}

View file

@ -103,6 +103,8 @@ private:
CameraOverride camera_override = OVERRIDE_NONE;
Map<Breakpoint, bool> breakpoints;
Set<Ref<Script>> debugger_plugins;
ScriptEditorDebugger *_add_debugger();
EditorDebuggerRemoteObject *get_inspected_remote_object();
@ -186,5 +188,8 @@ public:
Error start(const String &p_protocol = "tcp://");
void stop();
void add_debugger_plugin(const Ref<Script> &p_script);
void remove_debugger_plugin(const Ref<Script> &p_script);
};
#endif // EDITOR_DEBUGGER_NODE_H

View file

@ -44,6 +44,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "editor/plugins/editor_debugger_plugin.h"
#include "editor/plugins/node_3d_editor_plugin.h"
#include "editor/property_editor.h"
#include "main/performance.h"
@ -701,7 +702,28 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
performance_profiler->update_monitors(monitors);
} else {
WARN_PRINT("unknown message " + p_msg);
int colon_index = p_msg.find_char(':');
ERR_FAIL_COND_MSG(colon_index < 1, "Invalid message received");
bool parsed = false;
const String cap = p_msg.substr(0, colon_index);
Map<StringName, Callable>::Element *element = captures.find(cap);
if (element) {
Callable &c = element->value();
ERR_FAIL_COND_MSG(c.is_null(), "Invalid callable registered: " + cap);
Variant cmd = p_msg.substr(colon_index + 1), data = p_data;
const Variant *args[2] = { &cmd, &data };
Variant retval;
Callable::CallError err;
c.call(args, 2, retval, err);
ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'capture' to callable: " + Variant::get_callable_error_text(c, args, 2, err));
ERR_FAIL_COND_MSG(retval.get_type() != Variant::BOOL, "Error calling 'capture' to callable: " + String(c) + ". Return type is not bool.");
parsed = retval;
}
if (!parsed) {
WARN_PRINT("unknown message " + p_msg);
}
}
}
@ -847,6 +869,7 @@ void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) {
tabs->set_current_tab(0);
_set_reason_text(TTR("Debug session started."), MESSAGE_SUCCESS);
_update_buttons_state();
emit_signal("started");
}
void ScriptEditorDebugger::_update_buttons_state() {
@ -1395,6 +1418,7 @@ void ScriptEditorDebugger::_bind_methods() {
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("started"));
ADD_SIGNAL(MethodInfo("stopped"));
ADD_SIGNAL(MethodInfo("stop_requested"));
ADD_SIGNAL(MethodInfo("stack_frame_selected", PropertyInfo(Variant::INT, "frame")));
@ -1408,6 +1432,43 @@ void ScriptEditorDebugger::_bind_methods() {
ADD_SIGNAL(MethodInfo("remote_tree_updated"));
}
void ScriptEditorDebugger::add_debugger_plugin(const Ref<Script> &p_script) {
if (!debugger_plugins.has(p_script)) {
EditorDebuggerPlugin *plugin = memnew(EditorDebuggerPlugin());
plugin->attach_debugger(this);
plugin->set_script(p_script);
tabs->add_child(plugin);
debugger_plugins.insert(p_script, plugin);
}
}
void ScriptEditorDebugger::remove_debugger_plugin(const Ref<Script> &p_script) {
if (debugger_plugins.has(p_script)) {
tabs->remove_child(debugger_plugins[p_script]);
debugger_plugins[p_script]->detach_debugger(false);
memdelete(debugger_plugins[p_script]);
debugger_plugins.erase(p_script);
}
}
void ScriptEditorDebugger::send_message(const String &p_message, const Array &p_args) {
_put_msg(p_message, p_args);
}
void ScriptEditorDebugger::register_message_capture(const StringName &p_name, const Callable &p_callable) {
ERR_FAIL_COND_MSG(has_capture(p_name), "Capture already registered: " + p_name);
captures.insert(p_name, p_callable);
}
void ScriptEditorDebugger::unregister_message_capture(const StringName &p_name) {
ERR_FAIL_COND_MSG(!has_capture(p_name), "Capture not registered: " + p_name);
captures.erase(p_name);
}
bool ScriptEditorDebugger::has_capture(const StringName &p_name) {
return captures.has(p_name);
}
ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
editor = p_editor;

View file

@ -54,6 +54,7 @@ class EditorVisualProfiler;
class EditorNetworkProfiler;
class EditorPerformanceProfiler;
class SceneDebuggerTree;
class EditorDebuggerPlugin;
class ScriptEditorDebugger : public MarginContainer {
GDCLASS(ScriptEditorDebugger, MarginContainer);
@ -146,6 +147,10 @@ private:
EditorDebuggerNode::CameraOverride camera_override;
Map<Ref<Script>, EditorDebuggerPlugin *> debugger_plugins;
Map<StringName, Callable> captures;
void _stack_dump_frame_selected();
void _file_selected(const String &p_file);
@ -253,6 +258,16 @@ public:
bool is_skip_breakpoints();
virtual Size2 get_minimum_size() const override;
void add_debugger_plugin(const Ref<Script> &p_script);
void remove_debugger_plugin(const Ref<Script> &p_script);
void send_message(const String &p_message, const Array &p_args);
void register_message_capture(const StringName &p_name, const Callable &p_callable);
void unregister_message_capture(const StringName &p_name);
bool has_capture(const StringName &p_name);
ScriptEditorDebugger(EditorNode *p_editor = nullptr);
~ScriptEditorDebugger();
};