summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabio Alessandrelli <fabio.alessandrelli@gmail.com>2020-02-07 02:52:05 +0100
committerFabio Alessandrelli <fabio.alessandrelli@gmail.com>2020-02-21 11:12:03 +0100
commitcbc450c0e53dbfc31a30f5ebf37c51cee77cde5c (patch)
treefa1e7a1e80ec7cbe0546a752732bf34b66299100
parent8b058d4b9a63799e29d3e9ef551bbdf8144f8f8b (diff)
Huge Debugger/EditorDebugger refactor.
-rw-r--r--core/script_debugger_remote.cpp1145
-rw-r--r--core/script_debugger_remote.h (renamed from scene/debugger/script_debugger_remote.h)207
-rw-r--r--editor/SCsub1
-rw-r--r--editor/debugger/SCsub5
-rw-r--r--editor/debugger/editor_debugger_inspector.cpp277
-rw-r--r--editor/debugger/editor_debugger_inspector.h98
-rw-r--r--editor/debugger/editor_debugger_node.cpp564
-rw-r--r--editor/debugger/editor_debugger_node.h176
-rw-r--r--editor/debugger/editor_debugger_tree.cpp274
-rw-r--r--editor/debugger/editor_debugger_tree.h74
-rw-r--r--editor/debugger/script_editor_debugger.cpp (renamed from editor/script_editor_debugger.cpp)1592
-rw-r--r--editor/debugger/script_editor_debugger.h (renamed from editor/script_editor_debugger.h)112
-rw-r--r--editor/editor_node.cpp50
-rw-r--r--editor/editor_node.h8
-rw-r--r--editor/editor_path.cpp2
-rw-r--r--editor/editor_run.cpp32
-rw-r--r--editor/editor_run.h8
-rw-r--r--editor/inspector_dock.cpp2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp18
-rw-r--r--editor/plugins/debugger_editor_plugin.cpp51
-rw-r--r--editor/plugins/debugger_editor_plugin.h50
-rw-r--r--editor/plugins/script_editor_plugin.cpp129
-rw-r--r--editor/plugins/script_editor_plugin.h14
-rw-r--r--editor/plugins/script_text_editor.cpp8
-rw-r--r--editor/plugins/spatial_editor_plugin.cpp14
-rw-r--r--editor/scene_tree_dock.cpp32
-rw-r--r--editor/settings_config_dialog.cpp7
-rw-r--r--main/main.cpp8
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs4
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp14
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp2
-rw-r--r--platform/windows/os_windows.cpp1
-rw-r--r--scene/debugger/scene_debugger.cpp867
-rw-r--r--scene/debugger/scene_debugger.h151
-rw-r--r--scene/debugger/script_debugger_remote.cpp1313
-rw-r--r--scene/main/node.cpp28
-rw-r--r--scene/main/scene_tree.cpp388
-rw-r--r--scene/main/scene_tree.h36
-rw-r--r--scene/register_scene_types.cpp3
-rw-r--r--servers/register_server_types.cpp8
41 files changed, 4485 insertions, 3290 deletions
diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp
new file mode 100644
index 0000000000..fdb6135edd
--- /dev/null
+++ b/core/script_debugger_remote.cpp
@@ -0,0 +1,1145 @@
+/*************************************************************************/
+/* script_debugger_remote.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 "script_debugger_remote.h"
+
+#include "core/engine.h"
+#include "core/io/ip.h"
+#include "core/io/marshalls.h"
+#include "core/os/input.h"
+#include "core/os/os.h"
+#include "core/project_settings.h"
+#include "servers/visual_server.h"
+
+#define CHECK_SIZE(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() < (uint32_t)(expected), false, String("Malformed ") + what + " message from script debugger, message too short. Exptected size: " + itos(expected) + ", actual size: " + itos(arr.size()))
+#define CHECK_END(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() > (uint32_t)expected, false, String("Malformed ") + what + " message from script debugger, message too short. Exptected size: " + itos(expected) + ", actual size: " + itos(arr.size()))
+
+Array ScriptDebuggerRemote::ScriptStackDump::serialize() {
+ Array arr;
+ arr.push_back(frames.size() * 3);
+ for (int i = 0; i < frames.size(); i++) {
+ arr.push_back(frames[i].file);
+ arr.push_back(frames[i].line);
+ arr.push_back(frames[i].func);
+ }
+ return arr;
+}
+
+bool ScriptDebuggerRemote::ScriptStackDump::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 1, "ScriptStackDump");
+ uint32_t size = p_arr[0];
+ CHECK_SIZE(p_arr, size, "ScriptStackDump");
+ int idx = 1;
+ for (uint32_t i = 0; i < size / 3; i++) {
+ ScriptLanguage::StackInfo sf;
+ sf.file = p_arr[idx];
+ sf.line = p_arr[idx + 1];
+ sf.func = p_arr[idx + 2];
+ frames.push_back(sf);
+ idx += 3;
+ }
+ CHECK_END(p_arr, idx, "ScriptStackDump");
+ return true;
+}
+
+Array ScriptDebuggerRemote::ScriptStackVariable::serialize(int max_size) {
+ Array arr;
+ arr.push_back(name);
+ arr.push_back(type);
+
+ Variant var = value;
+ if (value.get_type() == Variant::OBJECT && value.get_validated_object() == nullptr) {
+ var = Variant();
+ }
+
+ int len = 0;
+ Error err = encode_variant(var, NULL, len, true);
+ if (err != OK)
+ ERR_PRINT("Failed to encode variant.");
+
+ if (len > max_size) {
+ arr.push_back(Variant());
+ } else {
+ arr.push_back(var);
+ }
+ return arr;
+}
+
+bool ScriptDebuggerRemote::ScriptStackVariable::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 3, "ScriptStackVariable");
+ name = p_arr[0];
+ type = p_arr[1];
+ value = p_arr[2];
+ CHECK_END(p_arr, 3, "ScriptStackVariable");
+ return true;
+}
+
+Array ScriptDebuggerRemote::OutputError::serialize() {
+ Array arr;
+ arr.push_back(hr);
+ arr.push_back(min);
+ arr.push_back(sec);
+ arr.push_back(msec);
+ arr.push_back(source_file);
+ arr.push_back(source_func);
+ arr.push_back(source_line);
+ arr.push_back(error);
+ arr.push_back(error_descr);
+ arr.push_back(warning);
+ unsigned int size = callstack.size();
+ const ScriptLanguage::StackInfo *r = callstack.ptr();
+ arr.push_back(size * 3);
+ for (int i = 0; i < callstack.size(); i++) {
+ arr.push_back(r[i].file);
+ arr.push_back(r[i].func);
+ arr.push_back(r[i].line);
+ }
+ return arr;
+}
+
+bool ScriptDebuggerRemote::OutputError::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 11, "OutputError");
+ hr = p_arr[0];
+ min = p_arr[1];
+ sec = p_arr[2];
+ msec = p_arr[3];
+ source_file = p_arr[4];
+ source_func = p_arr[5];
+ source_line = p_arr[6];
+ error = p_arr[7];
+ error_descr = p_arr[8];
+ warning = p_arr[9];
+ unsigned int stack_size = p_arr[10];
+ CHECK_SIZE(p_arr, stack_size, "OutputError");
+ int idx = 11;
+ callstack.resize(stack_size / 3);
+ ScriptLanguage::StackInfo *w = callstack.ptrw();
+ for (unsigned int i = 0; i < stack_size / 3; i++) {
+ w[i].file = p_arr[idx];
+ w[i].func = p_arr[idx + 1];
+ w[i].line = p_arr[idx + 2];
+ idx += 3;
+ }
+ CHECK_END(p_arr, idx, "OutputError");
+ return true;
+}
+
+Array ScriptDebuggerRemote::ResourceUsage::serialize() {
+ infos.sort();
+
+ Array arr;
+ arr.push_back(infos.size() * 4);
+ for (List<ResourceInfo>::Element *E = infos.front(); E; E = E->next()) {
+ arr.push_back(E->get().path);
+ arr.push_back(E->get().format);
+ arr.push_back(E->get().type);
+ arr.push_back(E->get().vram);
+ }
+ return arr;
+}
+
+bool ScriptDebuggerRemote::ResourceUsage::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 1, "ResourceUsage");
+ uint32_t size = p_arr[0];
+ CHECK_SIZE(p_arr, size, "ResourceUsage");
+ int idx = 1;
+ for (uint32_t i = 0; i < size / 4; i++) {
+ ResourceInfo info;
+ info.path = p_arr[idx];
+ info.format = p_arr[idx + 1];
+ info.type = p_arr[idx + 2];
+ info.vram = p_arr[idx + 3];
+ infos.push_back(info);
+ }
+ CHECK_END(p_arr, idx, "ResourceUsage");
+ return true;
+}
+
+Array ScriptDebuggerRemote::ProfilerSignature::serialize() {
+ Array arr;
+ arr.push_back(name);
+ arr.push_back(id);
+ return arr;
+}
+
+bool ScriptDebuggerRemote::ProfilerSignature::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 2, "ProfilerSignature");
+ name = p_arr[0];
+ id = p_arr[1];
+ CHECK_END(p_arr, 2, "ProfilerSignature");
+ return true;
+}
+
+Array ScriptDebuggerRemote::ProfilerFrame::serialize() {
+ Array arr;
+ arr.push_back(frame_number);
+ arr.push_back(frame_time);
+ arr.push_back(idle_time);
+ arr.push_back(physics_time);
+ arr.push_back(physics_frame_time);
+ arr.push_back(USEC_TO_SEC(script_time));
+
+ arr.push_back(frames_data.size());
+ arr.push_back(frame_functions.size() * 4);
+
+ // Servers profiling info.
+ for (int i = 0; i < frames_data.size(); i++) {
+ arr.push_back(frames_data[i].name); // Type (physics/process/audio/...)
+ arr.push_back(frames_data[i].data.size());
+ for (int j = 0; j < frames_data[i].data.size() / 2; j++) {
+ arr.push_back(frames_data[i].data[2 * j]); // NAME
+ arr.push_back(frames_data[i].data[2 * j + 1]); // TIME
+ }
+ }
+ for (int i = 0; i < frame_functions.size(); i++) {
+ arr.push_back(frame_functions[i].sig_id);
+ arr.push_back(frame_functions[i].call_count);
+ arr.push_back(frame_functions[i].self_time);
+ arr.push_back(frame_functions[i].total_time);
+ }
+ return arr;
+}
+
+bool ScriptDebuggerRemote::ProfilerFrame::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 8, "ProfilerFrame");
+ frame_number = p_arr[0];
+ frame_time = p_arr[1];
+ idle_time = p_arr[2];
+ physics_time = p_arr[3];
+ physics_frame_time = p_arr[4];
+ script_time = p_arr[5];
+ uint32_t frame_data_size = p_arr[6];
+ int frame_func_size = p_arr[7];
+ int idx = 8;
+ while (frame_data_size) {
+ CHECK_SIZE(p_arr, idx + 2, "ProfilerFrame");
+ frame_data_size--;
+ FrameData fd;
+ fd.name = p_arr[idx];
+ int sub_data_size = p_arr[idx + 1];
+ idx += 2;
+ CHECK_SIZE(p_arr, idx + sub_data_size, "ProfilerFrame");
+ for (int j = 0; j < sub_data_size / 2; j++) {
+ fd.data.push_back(p_arr[idx]); // NAME
+ fd.data.push_back(p_arr[idx + 1]); // TIME
+ idx += 2;
+ }
+ frames_data.push_back(fd);
+ }
+ CHECK_SIZE(p_arr, idx + frame_func_size, "ProfilerFrame");
+ for (int i = 0; i < frame_func_size / 4; i++) {
+ FrameFunction ff;
+ ff.sig_id = p_arr[idx];
+ ff.call_count = p_arr[idx + 1];
+ ff.self_time = p_arr[idx + 2];
+ ff.total_time = p_arr[idx + 3];
+ frame_functions.push_back(ff);
+ idx += 4;
+ }
+ CHECK_END(p_arr, idx, "ProfilerFrame");
+ return true;
+}
+
+Array ScriptDebuggerRemote::NetworkProfilerFrame::serialize() {
+ Array arr;
+ arr.push_back(infos.size() * 6);
+ for (int i = 0; i < infos.size(); ++i) {
+ arr.push_back(uint64_t(infos[i].node));
+ arr.push_back(infos[i].node_path);
+ arr.push_back(infos[i].incoming_rpc);
+ arr.push_back(infos[i].incoming_rset);
+ arr.push_back(infos[i].outgoing_rpc);
+ arr.push_back(infos[i].outgoing_rset);
+ }
+ return arr;
+}
+
+bool ScriptDebuggerRemote::NetworkProfilerFrame::deserialize(const Array &p_arr) {
+ CHECK_SIZE(p_arr, 1, "NetworkProfilerFrame");
+ uint32_t size = p_arr[0];
+ CHECK_SIZE(p_arr, size, "NetworkProfilerFrame");
+ infos.resize(size);
+ int idx = 1;
+ for (uint32_t i = 0; i < size / 6; ++i) {
+ infos.write[i].node = uint64_t(p_arr[idx]);
+ infos.write[i].node_path = p_arr[idx + 1];
+ infos.write[i].incoming_rpc = p_arr[idx + 2];
+ infos.write[i].incoming_rset = p_arr[idx + 3];
+ infos.write[i].outgoing_rpc = p_arr[idx + 4];
+ infos.write[i].outgoing_rset = p_arr[idx + 5];
+ }
+ CHECK_END(p_arr, idx, "NetworkProfilerFrame");
+ return true;
+}
+
+void ScriptDebuggerRemote::_put_msg(String p_message, Array p_data) {
+ Array msg;
+ msg.push_back(p_message);
+ msg.push_back(p_data);
+ packet_peer_stream->put_var(msg);
+}
+
+bool ScriptDebuggerRemote::is_peer_connected() {
+ return tcp_client->is_connected_to_host() && tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED;
+}
+
+void ScriptDebuggerRemote::_send_video_memory() {
+
+ ResourceUsage usage;
+ if (resource_usage_func)
+ resource_usage_func(&usage);
+
+ _put_msg("message:video_mem", usage.serialize());
+}
+
+Error ScriptDebuggerRemote::connect_to_host(const String &p_host, uint16_t p_port) {
+
+ IP_Address ip;
+ if (p_host.is_valid_ip_address())
+ ip = p_host;
+ else
+ ip = IP::get_singleton()->resolve_hostname(p_host);
+
+ int port = p_port;
+
+ const int tries = 6;
+ int waits[tries] = { 1, 10, 100, 1000, 1000, 1000 };
+
+ tcp_client->connect_to_host(ip, port);
+
+ for (int i = 0; i < tries; i++) {
+
+ if (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED) {
+ print_verbose("Remote Debugger: Connected!");
+ break;
+ } else {
+
+ const int ms = waits[i];
+ OS::get_singleton()->delay_usec(ms * 1000);
+ print_verbose("Remote Debugger: Connection failed with status: '" + String::num(tcp_client->get_status()) + "', retrying in " + String::num(ms) + " msec.");
+ };
+ };
+
+ if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
+
+ ERR_PRINT("Remote Debugger: Unable to connect. Status: " + String::num(tcp_client->get_status()) + ".");
+ return FAILED;
+ };
+
+ packet_peer_stream->set_stream_peer(tcp_client);
+ Array msg;
+ msg.push_back(OS::get_singleton()->get_process_id());
+ send_message("set_pid", msg);
+
+ return OK;
+}
+
+void ScriptDebuggerRemote::_parse_message(const String p_command, const Array &p_data, ScriptLanguage *p_script) {
+
+ if (p_command == "request_video_mem") {
+ _send_video_memory();
+
+ } else if (p_command == "start_profiling") {
+ ERR_FAIL_COND(p_data.size() < 1);
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->profiling_start();
+ }
+
+ max_frame_functions = p_data[0];
+ profiler_function_signature_map.clear();
+ profiling = true;
+ frame_time = 0;
+ idle_time = 0;
+ physics_time = 0;
+ physics_frame_time = 0;
+ print_line("PROFILING ALRIGHT!");
+
+ } else if (p_command == "stop_profiling") {
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->profiling_stop();
+ }
+ profiling = false;
+ _send_profiling_data(false);
+ print_line("PROFILING END!");
+
+ } else if (p_command == "start_visual_profiling") {
+
+ visual_profiling = true;
+ VS::get_singleton()->set_frame_profiling_enabled(true);
+ } else if (p_command == "stop_visual_profiling") {
+
+ visual_profiling = false;
+ VS::get_singleton()->set_frame_profiling_enabled(false);
+
+ } else if (p_command == "start_network_profiling") {
+
+ network_profiling = true;
+ multiplayer->profiling_start();
+
+ } else if (p_command == "stop_network_profiling") {
+
+ network_profiling = false;
+ multiplayer->profiling_end();
+
+ } else if (p_command == "reload_scripts") {
+ reload_all_scripts = true;
+
+ } else if (p_command == "breakpoint") {
+ ERR_FAIL_COND(p_data.size() < 3);
+ bool set = p_data[2];
+ if (set)
+ insert_breakpoint(p_data[1], p_data[0]);
+ else
+ remove_breakpoint(p_data[1], p_data[0]);
+
+ } else if (p_command == "set_skip_breakpoints") {
+ ERR_FAIL_COND(p_data.size() < 1);
+ skip_breakpoints = p_data[0];
+
+ } else if (p_command == "get_stack_dump") {
+ ERR_FAIL_COND(!p_script);
+ ScriptStackDump dump;
+ int slc = p_script->debug_get_stack_level_count();
+ for (int i = 0; i < slc; i++) {
+ ScriptLanguage::StackInfo frame;
+ frame.file = p_script->debug_get_stack_level_source(i);
+ frame.line = p_script->debug_get_stack_level_line(i);
+ frame.func = p_script->debug_get_stack_level_function(i);
+ dump.frames.push_back(frame);
+ }
+ _put_msg("stack_dump", dump.serialize());
+
+ } else if (p_command == "get_stack_frame_vars") {
+ ERR_FAIL_COND(p_data.size() != 1);
+ ERR_FAIL_COND(!p_script);
+ int lv = p_data[0];
+
+ List<String> members;
+ List<Variant> member_vals;
+ if (ScriptInstance *inst = p_script->debug_get_stack_level_instance(lv)) {
+ members.push_back("self");
+ member_vals.push_back(inst->get_owner());
+ }
+ p_script->debug_get_stack_level_members(lv, &members, &member_vals);
+ ERR_FAIL_COND(members.size() != member_vals.size());
+
+ List<String> locals;
+ List<Variant> local_vals;
+ p_script->debug_get_stack_level_locals(lv, &locals, &local_vals);
+ ERR_FAIL_COND(locals.size() != local_vals.size());
+
+ List<String> globals;
+ List<Variant> globals_vals;
+ p_script->debug_get_globals(&globals, &globals_vals);
+ ERR_FAIL_COND(globals.size() != globals_vals.size());
+
+ _put_msg("stack_frame_vars", Array());
+
+ ScriptStackVariable stvar;
+ { //locals
+ List<String>::Element *E = locals.front();
+ List<Variant>::Element *F = local_vals.front();
+ while (E) {
+ stvar.name = E->get();
+ stvar.value = F->get();
+ stvar.type = 0;
+ _put_msg("stack_frame_var", stvar.serialize());
+
+ E = E->next();
+ F = F->next();
+ }
+ }
+
+ { //members
+ List<String>::Element *E = members.front();
+ List<Variant>::Element *F = member_vals.front();
+ while (E) {
+ stvar.name = E->get();
+ stvar.value = F->get();
+ stvar.type = 1;
+ _put_msg("stack_frame_var", stvar.serialize());
+
+ E = E->next();
+ F = F->next();
+ }
+ }
+
+ { //globals
+ List<String>::Element *E = globals.front();
+ List<Variant>::Element *F = globals_vals.front();
+ while (E) {
+ stvar.name = E->get();
+ stvar.value = F->get();
+ stvar.type = 2;
+ _put_msg("stack_frame_var", stvar.serialize());
+
+ E = E->next();
+ F = F->next();
+ }
+ }
+
+ } else {
+ if (scene_tree_parse_func) {
+ scene_tree_parse_func(p_command, p_data);
+ }
+ // Unknown message...
+ }
+}
+
+void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue, bool p_is_error_breakpoint) {
+
+ //this function is called when there is a debugger break (bug on script)
+ //or when execution is paused from editor
+
+ if (skip_breakpoints && !p_is_error_breakpoint)
+ return;
+
+ ERR_FAIL_COND_MSG(!is_peer_connected(), "Script Debugger failed to connect, but being used anyway.");
+
+ Array msg;
+ msg.push_back(p_can_continue);
+ msg.push_back(p_script->debug_get_error());
+ _put_msg("debug_enter", msg);
+
+ skip_profile_frame = true; // to avoid super long frame time for the frame
+
+ Input::MouseMode mouse_mode = Input::get_singleton()->get_mouse_mode();
+ if (mouse_mode != Input::MOUSE_MODE_VISIBLE)
+ Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
+
+ uint64_t loop_begin_usec = 0;
+ uint64_t loop_time_sec = 0;
+ while (true) {
+ loop_begin_usec = OS::get_singleton()->get_ticks_usec();
+
+ _get_output();
+
+ if (packet_peer_stream->get_available_packet_count() > 0) {
+
+ Variant var;
+ Error err = packet_peer_stream->get_var(var);
+
+ ERR_CONTINUE(err != OK);
+ ERR_CONTINUE(var.get_type() != Variant::ARRAY);
+
+ Array cmd = var;
+
+ ERR_CONTINUE(cmd.size() != 2);
+ ERR_CONTINUE(cmd[0].get_type() != Variant::STRING);
+ ERR_CONTINUE(cmd[1].get_type() != Variant::ARRAY);
+
+ String command = cmd[0];
+ Array data = cmd[1];
+ if (command == "step") {
+
+ set_depth(-1);
+ set_lines_left(1);
+ break;
+ } else if (command == "next") {
+
+ set_depth(0);
+ set_lines_left(1);
+ break;
+
+ } else if (command == "continue") {
+ set_depth(-1);
+ set_lines_left(-1);
+ OS::get_singleton()->move_window_to_foreground();
+ break;
+ } else if (command == "break") {
+ ERR_PRINT("Got break when already broke!");
+ break;
+ }
+
+ _parse_message(command, data, p_script);
+ } else {
+ OS::get_singleton()->delay_usec(10000);
+ OS::get_singleton()->process_and_drop_events();
+ }
+
+ // This is for the camera override to stay live even when the game is paused from the editor
+ loop_time_sec = (OS::get_singleton()->get_ticks_usec() - loop_begin_usec) / 1000000.0f;
+ VisualServer::get_singleton()->sync();
+ if (VisualServer::get_singleton()->has_changed()) {
+ VisualServer::get_singleton()->draw(true, loop_time_sec * Engine::get_singleton()->get_time_scale());
+ }
+ }
+
+ _put_msg("debug_exit", Array());
+
+ if (mouse_mode != Input::MOUSE_MODE_VISIBLE)
+ Input::get_singleton()->set_mouse_mode(mouse_mode);
+}
+
+void ScriptDebuggerRemote::_get_output() {
+
+ mutex->lock();
+ if (output_strings.size()) {
+
+ locking = true;
+
+ while (output_strings.size()) {
+
+ Array arr;
+ arr.push_back(output_strings.front()->get());
+ _put_msg("output", arr);
+ output_strings.pop_front();
+ }
+ locking = false;
+ }
+
+ if (n_messages_dropped > 0) {
+ Message msg;
+ msg.message = "Too many messages! " + String::num_int64(n_messages_dropped) + " messages were dropped.";
+ messages.push_back(msg);
+ n_messages_dropped = 0;
+ }
+
+ while (messages.size()) {
+ locking = true;
+ Message msg = messages.front()->get();
+ _put_msg("message:" + msg.message, msg.data);
+ messages.pop_front();
+ locking = false;
+ }
+
+ if (n_errors_dropped == 1) {
+ // Only print one message about dropping per second
+ OutputError oe;
+ oe.error = "TOO_MANY_ERRORS";
+ oe.error_descr = "Too many errors! Ignoring errors for up to 1 second.";
+ oe.warning = false;
+ uint64_t time = OS::get_singleton()->get_ticks_msec();
+ oe.hr = time / 3600000;
+ oe.min = (time / 60000) % 60;
+ oe.sec = (time / 1000) % 60;
+ oe.msec = time % 1000;
+ errors.push_back(oe);
+ }
+
+ if (n_warnings_dropped == 1) {
+ // Only print one message about dropping per second
+ OutputError oe;
+ oe.error = "TOO_MANY_WARNINGS";
+ oe.error_descr = "Too many warnings! Ignoring warnings for up to 1 second.";
+ oe.warning = true;
+ uint64_t time = OS::get_singleton()->get_ticks_msec();
+ oe.hr = time / 3600000;
+ oe.min = (time / 60000) % 60;
+ oe.sec = (time / 1000) % 60;
+ oe.msec = time % 1000;
+ errors.push_back(oe);
+ }
+
+ while (errors.size()) {
+ locking = true;
+ OutputError oe = errors.front()->get();
+ _put_msg("error", oe.serialize());
+ errors.pop_front();
+ locking = false;
+ }
+ mutex->unlock();
+}
+
+void ScriptDebuggerRemote::line_poll() {
+
+ //the purpose of this is just processing events every now and then when the script might get too busy
+ //otherwise bugs like infinite loops can't be caught
+ if (poll_every % 2048 == 0)
+ _poll_events();
+ poll_every++;
+}
+
+void ScriptDebuggerRemote::_err_handler(void *ud, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, ErrorHandlerType p_type) {
+
+ if (p_type == ERR_HANDLER_SCRIPT)
+ return; //ignore script errors, those go through debugger
+
+ Vector<ScriptLanguage::StackInfo> si;
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ si = ScriptServer::get_language(i)->debug_get_current_stack_info();
+ if (si.size())
+ break;
+ }
+
+ ScriptDebuggerRemote *sdr = (ScriptDebuggerRemote *)ud;
+ sdr->send_error(p_func, p_file, p_line, p_err, p_descr, p_type, si);
+}
+
+void ScriptDebuggerRemote::_poll_events() {
+
+ //this si called from ::idle_poll, happens only when running the game,
+ //does not get called while on debug break
+
+ while (packet_peer_stream->get_available_packet_count() > 0) {
+
+ _get_output();
+
+ //send over output_strings
+
+ Variant var;
+ Error err = packet_peer_stream->get_var(var);
+
+ ERR_CONTINUE(err != OK);
+ ERR_CONTINUE(var.get_type() != Variant::ARRAY);
+
+ Array cmd = var;
+
+ ERR_CONTINUE(cmd.size() < 2);
+ ERR_CONTINUE(cmd[0].get_type() != Variant::STRING);
+ ERR_CONTINUE(cmd[1].get_type() != Variant::ARRAY);
+
+ String command = cmd[0];
+ Array data = cmd[1];
+
+ if (command == "break") {
+
+ if (get_break_language())
+ debug(get_break_language());
+ } else {
+ _parse_message(command, data);
+ }
+ }
+}
+
+void ScriptDebuggerRemote::_send_profiling_data(bool p_for_frame) {
+
+ int ofs = 0;
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ if (p_for_frame)
+ ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&profile_info.write[ofs], profile_info.size() - ofs);
+ else
+ ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&profile_info.write[ofs], profile_info.size() - ofs);
+ }
+
+ for (int i = 0; i < ofs; i++) {
+ profile_info_ptrs.write[i] = &profile_info.write[i];
+ }
+
+ SortArray<ScriptLanguage::ProfilingInfo *, ProfileInfoSort> sa;
+ sa.sort(profile_info_ptrs.ptrw(), ofs);
+
+ int to_send = MIN(ofs, max_frame_functions);
+
+ //check signatures first
+ uint64_t total_script_time = 0;
+
+ for (int i = 0; i < to_send; i++) {
+
+ if (!profiler_function_signature_map.has(profile_info_ptrs[i]->signature)) {
+
+ int idx = profiler_function_signature_map.size();
+ ProfilerSignature sig;
+ sig.name = profile_info_ptrs[i]->signature;
+ sig.id = idx;
+ _put_msg("profile_sig", sig.serialize());
+ profiler_function_signature_map[profile_info_ptrs[i]->signature] = idx;
+ }
+
+ total_script_time += profile_info_ptrs[i]->self_time;
+ }
+
+ //send frames then
+ ProfilerFrame metric;
+ metric.frame_number = Engine::get_singleton()->get_frames_drawn();
+ metric.frame_time = frame_time;
+ metric.idle_time = idle_time;
+ metric.physics_time = physics_time;
+ metric.physics_frame_time = physics_frame_time;
+ metric.script_time = total_script_time;
+
+ // Add script functions information.
+ metric.frame_functions.resize(to_send);
+ FrameFunction *w = metric.frame_functions.ptrw();
+ for (int i = 0; i < to_send; i++) {
+
+ if (profiler_function_signature_map.has(profile_info_ptrs[i]->signature)) {
+ w[i].sig_id = profiler_function_signature_map[profile_info_ptrs[i]->signature];
+ }
+
+ w[i].call_count = profile_info_ptrs[i]->call_count;
+ w[i].total_time = profile_info_ptrs[i]->total_time / 1000000.0;
+ w[i].self_time = profile_info_ptrs[i]->self_time / 1000000.0;
+ }
+ if (p_for_frame) {
+ // Add profile frame data information.
+ metric.frames_data.append_array(profile_frame_data);
+ _put_msg("profile_frame", metric.serialize());
+ profile_frame_data.clear();
+ } else {
+ _put_msg("profile_total", metric.serialize());
+ }
+}
+
+void ScriptDebuggerRemote::idle_poll() {
+
+ // this function is called every frame, except when there is a debugger break (::debug() in this class)
+ // execution stops and remains in the ::debug function
+
+ _get_output();
+
+ if (requested_quit) {
+
+ _put_msg("kill_me", Array());
+ requested_quit = false;
+ }
+
+ if (performance) {
+
+ uint64_t pt = OS::get_singleton()->get_ticks_msec();
+ if (pt - last_perf_time > 1000) {
+
+ last_perf_time = pt;
+ int max = performance->get("MONITOR_MAX");
+ Array arr;
+ arr.resize(max);
+ for (int i = 0; i < max; i++) {
+ arr[i] = performance->call("get_monitor", i);
+ }
+ _put_msg("performance", arr);
+ }
+ }
+
+ if (visual_profiling) {
+ Vector<VS::FrameProfileArea> profile_areas = VS::get_singleton()->get_frame_profile();
+ if (profile_areas.size()) {
+ Vector<String> area_names;
+ Vector<real_t> area_times;
+ area_names.resize(profile_areas.size());
+ area_times.resize(profile_areas.size() * 2);
+ {
+ String *area_namesw = area_names.ptrw();
+ real_t *area_timesw = area_times.ptrw();
+
+ for (int i = 0; i < profile_areas.size(); i++) {
+ area_namesw[i] = profile_areas[i].name;
+ area_timesw[i * 2 + 0] = profile_areas[i].cpu_msec;
+ area_timesw[i * 2 + 1] = profile_areas[i].gpu_msec;
+ }
+ }
+ Array msg;
+ msg.push_back(VS::get_singleton()->get_frame_profile_frame());
+ msg.push_back(area_names);
+ msg.push_back(area_times);
+ _put_msg("visual_profile", msg);
+ }
+ }
+
+ if (profiling) {
+
+ if (skip_profile_frame) {
+ skip_profile_frame = false;
+ } else {
+ //send profiling info normally
+ _send_profiling_data(true);
+ }
+ }
+
+ if (network_profiling) {
+ uint64_t pt = OS::get_singleton()->get_ticks_msec();
+ if (pt - last_net_bandwidth_time > 200) {
+ last_net_bandwidth_time = pt;
+ _send_network_bandwidth_usage();
+ }
+ if (pt - last_net_prof_time > 100) {
+ last_net_prof_time = pt;
+ _send_network_profiling_data();
+ }
+ }
+
+ if (reload_all_scripts) {
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->reload_all_scripts();
+ }
+ reload_all_scripts = false;
+ }
+
+ _poll_events();
+}
+
+void ScriptDebuggerRemote::_send_network_profiling_data() {
+ ERR_FAIL_COND(multiplayer.is_null());
+
+ int n_nodes = multiplayer->get_profiling_frame(&network_profile_info.write[0]);
+
+ NetworkProfilerFrame frame;
+ for (int i = 0; i < n_nodes; i++) {
+ frame.infos.push_back(network_profile_info[i]);
+ }
+ _put_msg("network_profile", frame.serialize());
+}
+
+void ScriptDebuggerRemote::_send_network_bandwidth_usage() {
+ ERR_FAIL_COND(multiplayer.is_null());
+
+ int incoming_bandwidth = multiplayer->get_incoming_bandwidth_usage();
+ int outgoing_bandwidth = multiplayer->get_outgoing_bandwidth_usage();
+
+ Array arr;
+ arr.push_back(incoming_bandwidth);
+ arr.push_back(outgoing_bandwidth);
+ _put_msg("network_bandwidth", arr);
+}
+
+void ScriptDebuggerRemote::send_message(const String &p_message, const Array &p_args) {
+
+ mutex->lock();
+ if (!locking && is_peer_connected()) {
+
+ if (messages.size() >= max_messages_per_frame) {
+ n_messages_dropped++;
+ } else {
+ Message msg;
+ msg.message = p_message;
+ msg.data = p_args;
+ messages.push_back(msg);
+ }
+ }
+ mutex->unlock();
+}
+
+void ScriptDebuggerRemote::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info) {
+
+ OutputError oe;
+ oe.error = p_err;
+ oe.error_descr = p_descr;
+ oe.source_file = p_file;
+ oe.source_line = p_line;
+ oe.source_func = p_func;
+ oe.warning = p_type == ERR_HANDLER_WARNING;
+ uint64_t time = OS::get_singleton()->get_ticks_msec();
+ oe.hr = time / 3600000;
+ oe.min = (time / 60000) % 60;
+ oe.sec = (time / 1000) % 60;
+ oe.msec = time % 1000;
+ Array cstack;
+
+ uint64_t ticks = OS::get_singleton()->get_ticks_usec() / 1000;
+ msec_count += ticks - last_msec;
+ last_msec = ticks;
+
+ if (msec_count > 1000) {
+ msec_count = 0;
+
+ err_count = 0;
+ n_errors_dropped = 0;
+ warn_count = 0;
+ n_warnings_dropped = 0;
+ }
+
+ cstack.resize(p_stack_info.size() * 3);
+ for (int i = 0; i < p_stack_info.size(); i++) {
+ cstack[i * 3 + 0] = p_stack_info[i].file;
+ cstack[i * 3 + 1] = p_stack_info[i].func;
+ cstack[i * 3 + 2] = p_stack_info[i].line;
+ }
+
+ //oe.callstack = cstack;
+ if (oe.warning) {
+ warn_count++;
+ } else {
+ err_count++;
+ }
+
+ mutex->lock();
+
+ if (!locking && is_peer_connected()) {
+
+ if (oe.warning) {
+ if (warn_count > max_warnings_per_second) {
+ n_warnings_dropped++;
+ } else {
+ errors.push_back(oe);
+ }
+ } else {
+ if (err_count > max_errors_per_second) {
+ n_errors_dropped++;
+ } else {
+ errors.push_back(oe);
+ }
+ }
+ }
+
+ mutex->unlock();
+}
+
+void ScriptDebuggerRemote::_print_handler(void *p_this, const String &p_string, bool p_error) {
+
+ ScriptDebuggerRemote *sdr = (ScriptDebuggerRemote *)p_this;
+
+ uint64_t ticks = OS::get_singleton()->get_ticks_usec() / 1000;
+ sdr->msec_count += ticks - sdr->last_msec;
+ sdr->last_msec = ticks;
+
+ if (sdr->msec_count > 1000) {
+ sdr->char_count = 0;
+ sdr->msec_count = 0;
+ }
+
+ String s = p_string;
+ int allowed_chars = MIN(MAX(sdr->max_cps - sdr->char_count, 0), s.length());
+
+ if (allowed_chars == 0)
+ return;
+
+ if (allowed_chars < s.length()) {
+ s = s.substr(0, allowed_chars);
+ }
+
+ sdr->char_count += allowed_chars;
+ bool overflowed = sdr->char_count >= sdr->max_cps;
+
+ sdr->mutex->lock();
+ if (!sdr->locking && sdr->is_peer_connected()) {
+
+ if (overflowed)
+ s += "[...]";
+
+ sdr->output_strings.push_back(s);
+
+ if (overflowed) {
+ sdr->output_strings.push_back("[output overflow, print less text!]");
+ }
+ }
+ sdr->mutex->unlock();
+}
+
+void ScriptDebuggerRemote::request_quit() {
+
+ requested_quit = true;
+}
+
+void ScriptDebuggerRemote::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
+ multiplayer = p_multiplayer;
+}
+
+bool ScriptDebuggerRemote::is_profiling() const {
+
+ return profiling;
+}
+void ScriptDebuggerRemote::add_profiling_frame_data(const StringName &p_name, const Array &p_data) {
+
+ int idx = -1;
+ for (int i = 0; i < profile_frame_data.size(); i++) {
+ if (profile_frame_data[i].name == p_name) {
+ idx = i;
+ break;
+ }
+ }
+
+ FrameData fd;
+ fd.name = p_name;
+ fd.data = p_data;
+
+ if (idx == -1) {
+ profile_frame_data.push_back(fd);
+ } else {
+ profile_frame_data.write[idx] = fd;
+ }
+}
+
+void ScriptDebuggerRemote::profiling_start() {
+ //ignores this, uses it via connection
+}
+
+void ScriptDebuggerRemote::profiling_end() {
+ //ignores this, uses it via connection
+}
+
+void ScriptDebuggerRemote::profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+
+ frame_time = p_frame_time;
+ idle_time = p_idle_time;
+ physics_time = p_physics_time;
+ physics_frame_time = p_physics_frame_time;
+}
+
+void ScriptDebuggerRemote::set_skip_breakpoints(bool p_skip_breakpoints) {
+ skip_breakpoints = p_skip_breakpoints;
+}
+
+ScriptDebuggerRemote::ResourceUsageFunc ScriptDebuggerRemote::resource_usage_func = NULL;
+ScriptDebuggerRemote::ParseMessageFunc ScriptDebuggerRemote::scene_tree_parse_func = NULL;
+
+ScriptDebuggerRemote::ScriptDebuggerRemote() :
+ profiling(false),
+ visual_profiling(false),
+ network_profiling(false),
+ max_frame_functions(16),
+ skip_profile_frame(false),
+ reload_all_scripts(false),
+ tcp_client(Ref<StreamPeerTCP>(memnew(StreamPeerTCP))),
+ packet_peer_stream(Ref<PacketPeerStream>(memnew(PacketPeerStream))),
+ last_perf_time(0),
+ last_net_prof_time(0),
+ last_net_bandwidth_time(0),
+ performance(Engine::get_singleton()->get_singleton_object("Performance")),
+ requested_quit(false),
+ mutex(Mutex::create()),
+ max_messages_per_frame(GLOBAL_GET("network/limits/debugger_stdout/max_messages_per_frame")),
+ n_messages_dropped(0),
+ max_errors_per_second(GLOBAL_GET("network/limits/debugger_stdout/max_errors_per_second")),
+ max_warnings_per_second(GLOBAL_GET("network/limits/debugger_stdout/max_warnings_per_second")),
+ n_errors_dropped(0),
+ max_cps(GLOBAL_GET("network/limits/debugger_stdout/max_chars_per_second")),
+ char_count(0),
+ err_count(0),
+ warn_count(0),
+ last_msec(0),
+ msec_count(0),
+ locking(false),
+ poll_every(0) {
+
+ packet_peer_stream->set_stream_peer(tcp_client);
+ packet_peer_stream->set_output_buffer_max_size((1024 * 1024 * 8) - 4); // 8 MiB should be way more than enough, minus 4 bytes for separator.
+
+ phl.printfunc = _print_handler;
+ phl.userdata = this;
+ add_print_handler(&phl);
+
+ eh.errfunc = _err_handler;
+ eh.userdata = this;
+ add_error_handler(&eh);
+
+ profile_info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
+ network_profile_info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
+ profile_info_ptrs.resize(profile_info.size());
+}
+
+ScriptDebuggerRemote::~ScriptDebuggerRemote() {
+
+ remove_print_handler(&phl);
+ remove_error_handler(&eh);
+ memdelete(mutex);
+}
diff --git a/scene/debugger/script_debugger_remote.h b/core/script_debugger_remote.h
index ae44bf9ca2..682da1d276 100644
--- a/scene/debugger/script_debugger_remote.h
+++ b/core/script_debugger_remote.h
@@ -37,16 +37,170 @@
#include "core/os/os.h"
#include "core/script_language.h"
-class SceneTree;
-
class ScriptDebuggerRemote : public ScriptDebugger {
- struct Message {
+public:
+ class ResourceInfo {
+ public:
+ String path;
+ String format;
+ String type;
+ RID id;
+ int vram;
+ bool operator<(const ResourceInfo &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; }
+ ResourceInfo() {
+ vram = 0;
+ }
+ };
+
+ class ResourceUsage {
+ public:
+ List<ResourceInfo> infos;
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+ };
+
+ class FrameInfo {
+ public:
+ StringName name;
+ float self_time;
+ float total_time;
+
+ FrameInfo() {
+ self_time = 0;
+ total_time = 0;
+ }
+ };
+
+ class FrameFunction {
+ public:
+ int sig_id;
+ int call_count;
+ StringName name;
+ float self_time;
+ float total_time;
+
+ FrameFunction() {
+ sig_id = -1;
+ call_count = 0;
+ self_time = 0;
+ total_time = 0;
+ }
+ };
+ class ScriptStackVariable {
+ public:
+ String name;
+ Variant value;
+ int type;
+ ScriptStackVariable() {
+ type = -1;
+ }
+
+ Array serialize(int max_size = 1 << 20); // 1 MiB default.
+ bool deserialize(const Array &p_arr);
+ };
+
+ class ScriptStackDump {
+ public:
+ List<ScriptLanguage::StackInfo> frames;
+ ScriptStackDump() {}
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+ };
+
+ class Message {
+
+ public:
String message;
Array data;
+
+ Message() {}
+ };
+
+ class OutputError {
+ public:
+ int hr;
+ int min;
+ int sec;
+ int msec;
+ String source_file;
+ String source_func;
+ int source_line;
+ String error;
+ String error_descr;
+ bool warning;
+ Vector<ScriptLanguage::StackInfo> callstack;
+
+ OutputError() {
+ hr = -1;
+ min = -1;
+ sec = -1;
+ msec = -1;
+ source_line = -1;
+ warning = false;
+ }
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+ };
+
+ struct FrameData {
+
+ StringName name;
+ Array data;
+ };
+
+ class ProfilerSignature {
+ public:
+ StringName name;
+ int id;
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+
+ ProfilerSignature() {
+ id = -1;
+ };
+ };
+
+ class ProfilerFrame {
+ public:
+ int frame_number;
+ float frame_time;
+ float idle_time;
+ float physics_time;
+ float physics_frame_time;
+ float script_time;
+
+ Vector<FrameData> frames_data;
+ Vector<FrameFunction> frame_functions;
+
+ ProfilerFrame() {
+ frame_number = 0;
+ frame_time = 0;
+ idle_time = 0;
+ physics_time = 0;
+ physics_frame_time = 0;
+ }
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
};
+ class NetworkProfilerFrame {
+ public:
+ Vector<MultiplayerAPI::ProfilingInfo> infos;
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+
+ NetworkProfilerFrame(){};
+ };
+
+protected:
struct ProfileInfoSort {
bool operator()(ScriptLanguage::ProfilingInfo *A, ScriptLanguage::ProfilingInfo *B) const {
@@ -78,21 +232,6 @@ class ScriptDebuggerRemote : public ScriptDebugger {
bool requested_quit;
Mutex *mutex;
- struct OutputError {
-
- int hr;
- int min;
- int sec;
- int msec;
- String source_file;
- String source_func;
- int source_line;
- String error;
- String error_descr;
- bool warning;
- Array callstack;
- };
-
List<String> output_strings;
List<Message> messages;
int max_messages_per_frame;
@@ -119,9 +258,7 @@ class ScriptDebuggerRemote : public ScriptDebugger {
void _poll_events();
uint32_t poll_every;
- SceneTree *scene_tree;
-
- bool _parse_live_edit(const Array &p_command);
+ void _parse_message(const String p_command, const Array &p_data, ScriptLanguage *p_script = NULL);
void _set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value);
@@ -133,40 +270,24 @@ class ScriptDebuggerRemote : public ScriptDebugger {
ErrorHandlerList eh;
static void _err_handler(void *, const char *, const char *, int p_line, const char *, const char *, ErrorHandlerType p_type);
+ void _put_msg(String p_message, Array p_data);
void _send_profiling_data(bool p_for_frame);
void _send_network_profiling_data();
void _send_network_bandwidth_usage();
- struct FrameData {
-
- StringName name;
- Array data;
- };
-
Vector<FrameData> profile_frame_data;
- void _put_variable(const String &p_name, const Variant &p_variable);
-
- void _save_node(ObjectID id, const String &p_path);
-
bool skip_breakpoints;
public:
- struct ResourceUsage {
-
- String path;
- String format;
- String type;
- RID id;
- int vram;
- bool operator<(const ResourceUsage &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; }
- };
-
- typedef void (*ResourceUsageFunc)(List<ResourceUsage> *);
+ typedef void (*ResourceUsageFunc)(ResourceUsage *);
+ typedef Error (*ParseMessageFunc)(const String &p_name, const Array &p_msg); // Returns true if something was found (stopping propagation).
static ResourceUsageFunc resource_usage_func;
+ static ParseMessageFunc scene_tree_parse_func; // Could be made into list, extensible...
Error connect_to_host(const String &p_host, uint16_t p_port);
+ bool is_peer_connected();
virtual void debug(ScriptLanguage *p_script, bool p_can_continue = true, bool p_is_error_breakpoint = false);
virtual void idle_poll();
virtual void line_poll();
@@ -188,8 +309,6 @@ public:
virtual void set_skip_breakpoints(bool p_skip_breakpoints);
- void set_scene_tree(SceneTree *p_scene_tree) { scene_tree = p_scene_tree; };
-
ScriptDebuggerRemote();
~ScriptDebuggerRemote();
};
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: {
diff --git a/main/main.cpp b/main/main.cpp
index 9f1d9ce5fe..efcbf04585 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -44,6 +44,7 @@
#include "core/project_settings.h"
#include "core/register_core_types.h"
#include "core/script_debugger_local.h"
+#include "core/script_debugger_remote.h"
#include "core/script_language.h"
#include "core/translation.h"
#include "core/version.h"
@@ -58,7 +59,6 @@
#include "main/tests/test_main.h"
#include "modules/register_module_types.h"
#include "platform/register_platform_apis.h"
-#include "scene/debugger/script_debugger_remote.h"
#include "scene/main/scene_tree.h"
#include "scene/main/viewport.h"
#include "scene/register_scene_types.h"
@@ -1657,12 +1657,6 @@ bool Main::start() {
if (!project_manager && !editor) { // game
if (game_path != "" || script != "") {
- if (script_debugger && script_debugger->is_remote()) {
- ScriptDebuggerRemote *remote_debugger = static_cast<ScriptDebuggerRemote *>(script_debugger);
-
- remote_debugger->set_scene_tree(sml);
- }
-
//autoload
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
diff --git a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs b/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
index 4c76d2abf1..bd7eb59913 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
@@ -172,7 +172,7 @@ namespace GodotTools
return;
// Notify running game for hot-reload
- Internal.ScriptEditorDebuggerReloadScripts();
+ Internal.EditorDebuggerNodeReloadScripts();
// Hot-reload in the editor
GodotSharpEditor.Instance.GetNode<HotReloadAssemblyWatcher>("HotReloadAssemblyWatcher").RestartTimer();
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
index de361ba844..2e121ba879 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
@@ -34,7 +34,7 @@ namespace GodotTools.Internals
public static void ReloadAssemblies(bool softReload) => internal_ReloadAssemblies(softReload);
- public static void ScriptEditorDebuggerReloadScripts() => internal_ScriptEditorDebuggerReloadScripts();
+ public static void EditorDebuggerNodeReloadScripts() => internal_EditorDebuggerNodeReloadScripts();
public static bool ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus = true) =>
internal_ScriptEditorEdit(resource, line, col, grabFocus);
@@ -88,7 +88,7 @@ namespace GodotTools.Internals
private static extern void internal_ReloadAssemblies(bool softReload);
[MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_ScriptEditorDebuggerReloadScripts();
+ private static extern void internal_EditorDebuggerNodeReloadScripts();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool internal_ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus);
diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index c8d20e80be..31996a03d0 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -36,10 +36,10 @@
#include "core/os/os.h"
#include "core/version.h"
+#include "editor/debugger/editor_debugger_node.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/plugins/script_editor_plugin.h"
-#include "editor/script_editor_debugger.h"
#include "main/main.h"
#include "../csharp_script.h"
@@ -305,8 +305,8 @@ void godot_icall_Internal_ReloadAssemblies(MonoBoolean p_soft_reload) {
#endif
}
-void godot_icall_Internal_ScriptEditorDebuggerReloadScripts() {
- ScriptEditor::get_singleton()->get_debugger()->reload_scripts();
+void godot_icall_Internal_EditorDebuggerNodeReloadScripts() {
+ EditorDebuggerNode::get_singleton()->reload_scripts();
}
MonoBoolean godot_icall_Internal_ScriptEditorEdit(MonoObject *p_resource, int32_t p_line, int32_t p_col, MonoBoolean p_grab_focus) {
@@ -348,9 +348,9 @@ void godot_icall_Internal_EditorRunStop() {
}
void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() {
- ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
- if (sed) {
- sed->reload_scripts();
+ EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
+ if (ed) {
+ ed->reload_scripts();
}
}
@@ -446,7 +446,7 @@ void register_editor_internal_calls() {
mono_add_internal_call("GodotTools.Internals.Internal::internal_GetEditorApiHash", (void *)godot_icall_Internal_GetEditorApiHash);
mono_add_internal_call("GodotTools.Internals.Internal::internal_IsAssembliesReloadingNeeded", (void *)godot_icall_Internal_IsAssembliesReloadingNeeded);
mono_add_internal_call("GodotTools.Internals.Internal::internal_ReloadAssemblies", (void *)godot_icall_Internal_ReloadAssemblies);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorDebuggerReloadScripts", (void *)godot_icall_Internal_ScriptEditorDebuggerReloadScripts);
+ mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorDebuggerNodeReloadScripts", (void *)godot_icall_Internal_EditorDebuggerNodeReloadScripts);
mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorEdit", (void *)godot_icall_Internal_ScriptEditorEdit);
mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorNodeShowScriptScreen", (void *)godot_icall_Internal_EditorNodeShowScriptScreen);
mono_add_internal_call("GodotTools.Internals.Internal::internal_GetScriptsMetadataOrNothing", (void *)godot_icall_Internal_GetScriptsMetadataOrNothing);
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index 05077a00c4..ae6625a6c6 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -38,7 +38,7 @@
#include "core/reference.h"
#ifdef TOOLS_ENABLED
-#include "editor/script_editor_debugger.h"
+#include "editor/debugger/script_editor_debugger.h"
#endif
#include "../csharp_script.h"
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index b9cd277657..41720360c3 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -34,6 +34,7 @@
#include "os_windows.h"
#include "core/io/marshalls.h"
+#include "core/script_language.h"
#include "core/version_generated.gen.h"
#if defined(OPENGL_ENABLED)
diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp
new file mode 100644
index 0000000000..9b9f0455a1
--- /dev/null
+++ b/scene/debugger/scene_debugger.cpp
@@ -0,0 +1,867 @@
+/*************************************************************************/
+/* scene_debugger.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 "scene_debugger.h"
+
+#include "core/io/marshalls.h"
+#include "core/script_debugger_remote.h"
+#include "scene/main/scene_tree.h"
+#include "scene/main/viewport.h"
+#include "scene/resources/packed_scene.h"
+
+void SceneDebugger::initialize() {
+#ifdef DEBUG_ENABLED
+ LiveEditor::singleton = memnew(LiveEditor);
+ ScriptDebuggerRemote::scene_tree_parse_func = SceneDebugger::parse_message;
+#endif
+}
+
+void SceneDebugger::deinitialize() {
+#ifdef DEBUG_ENABLED
+ if (LiveEditor::singleton) {
+ memdelete(LiveEditor::singleton);
+ LiveEditor::singleton = NULL;
+ }
+#endif
+}
+
+#ifdef DEBUG_ENABLED
+Error SceneDebugger::parse_message(const String &p_msg, const Array &p_args) {
+ SceneTree *scene_tree = SceneTree::get_singleton();
+ if (!scene_tree)
+ return ERR_UNCONFIGURED;
+ LiveEditor *live_editor = LiveEditor::get_singleton();
+ if (!live_editor)
+ return ERR_UNCONFIGURED;
+ if (p_msg == "request_scene_tree") { // Scene tree
+ live_editor->_send_tree();
+
+ } else if (p_msg == "save_node") { // Save node.
+ ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
+ _save_node(p_args[0], p_args[1]);
+
+ } else if (p_msg == "inspect_object") { // Object Inspect
+ ERR_FAIL_COND_V(p_args.size() < 1, ERR_INVALID_DATA);
+ ObjectID id = p_args[0];
+ _send_object_id(id);
+
+ } else if (p_msg == "override_camera_2D:set") { // Camera
+ ERR_FAIL_COND_V(p_args.size() < 1, ERR_INVALID_DATA);
+ bool enforce = p_args[0];
+ scene_tree->get_root()->enable_canvas_transform_override(enforce);
+
+ } else if (p_msg == "override_camera_2D:transform") {
+ ERR_FAIL_COND_V(p_args.size() < 1, ERR_INVALID_DATA);
+ Transform2D transform = p_args[1];
+ scene_tree->get_root()->set_canvas_transform_override(transform);
+
+ } else if (p_msg == "override_camera_3D:set") {
+ ERR_FAIL_COND_V(p_args.size() < 1, ERR_INVALID_DATA);
+ bool enable = p_args[0];
+ scene_tree->get_root()->enable_camera_override(enable);
+
+ } else if (p_msg == "override_camera_3D:transform") {
+ ERR_FAIL_COND_V(p_args.size() < 5, ERR_INVALID_DATA);
+ Transform transform = p_args[0];
+ bool is_perspective = p_args[1];
+ float size_or_fov = p_args[2];
+ float near = p_args[3];
+ float far = p_args[4];
+ if (is_perspective) {
+ scene_tree->get_root()->set_camera_override_perspective(size_or_fov, near, far);
+ } else {
+ scene_tree->get_root()->set_camera_override_orthogonal(size_or_fov, near, far);
+ }
+ scene_tree->get_root()->set_camera_override_transform(transform);
+
+ } else if (p_msg == "set_object_property") {
+ ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
+ _set_object_property(p_args[0], p_args[1], p_args[2]);
+
+ } else if (!p_msg.begins_with("live_")) { // Live edits below.
+ return ERR_SKIP;
+ } else if (p_msg == "live_set_root") {
+ ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
+ live_editor->_root_func(p_args[0], p_args[1]);
+
+ } else if (p_msg == "live_node_path") {
+ ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
+ live_editor->_node_path_func(p_args[0], p_args[1]);
+
+ } else if (p_msg == "live_res_path") {
+ ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
+ live_editor->_res_path_func(p_args[0], p_args[1]);
+
+ } else if (p_msg == "live_node_prop_res") {
+ ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
+ live_editor->_node_set_res_func(p_args[0], p_args[1], p_args[2]);
+
+ } else if (p_msg == "live_node_prop") {
+ ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
+ live_editor->_node_set_func(p_args[0], p_args[1], p_args[2]);
+
+ } else if (p_msg == "live_res_prop_res") {
+ ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
+ live_editor->_res_set_res_func(p_args[0], p_args[1], p_args[2]);
+
+ } else if (p_msg == "live_res_prop") {
+ ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
+ live_editor->_res_set_func(p_args[0], p_args[1], p_args[2]);
+
+ } else if (p_msg == "live_node_call") {
+ ERR_FAIL_COND_V(p_args.size() < 7, ERR_INVALID_DATA);
+ live_editor->_node_call_func(p_args[0], p_args[1], p_args[2], p_args[3], p_args[4], p_args[5], p_args[6]);
+
+ } else if (p_msg == "live_res_call") {
+ ERR_FAIL_COND_V(p_args.size() < 7, ERR_INVALID_DATA);
+ live_editor->_res_call_func(p_args[0], p_args[1], p_args[2], p_args[3], p_args[4], p_args[5], p_args[6]);
+
+ } else if (p_msg == "live_create_node") {
+ ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
+ live_editor->_create_node_func(p_args[0], p_args[1], p_args[2]);
+
+ } else if (p_msg == "live_instance_node") {
+ ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
+ live_editor->_instance_node_func(p_args[0], p_args[1], p_args[2]);
+
+ } else if (p_msg == "live_remove_node") {
+ ERR_FAIL_COND_V(p_args.size() < 1, ERR_INVALID_DATA);
+ live_editor->_remove_node_func(p_args[0]);
+
+ } else if (p_msg == "live_remove_and_keep_node") {
+ ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
+ live_editor->_remove_and_keep_node_func(p_args[0], p_args[1]);
+
+ } else if (p_msg == "live_restore_node") {
+ ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
+ live_editor->_restore_node_func(p_args[0], p_args[1], p_args[2]);
+
+ } else if (p_msg == "live_duplicate_node") {
+ ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
+ live_editor->_duplicate_node_func(p_args[0], p_args[1]);
+
+ } else if (p_msg == "live_reparent_node") {
+ ERR_FAIL_COND_V(p_args.size() < 4, ERR_INVALID_DATA);
+ live_editor->_reparent_node_func(p_args[0], p_args[1], p_args[2], p_args[3]);
+ } else {
+ return ERR_SKIP;
+ }
+ return OK;
+}
+
+void SceneDebugger::_save_node(ObjectID id, const String &p_path) {
+ Node *node = Object::cast_to<Node>(ObjectDB::get_instance(id));
+ ERR_FAIL_COND(!node);
+
+ WARN_PRINT("SAVING " + itos(id) + " TO " + p_path);
+ Ref<PackedScene> ps = memnew(PackedScene);
+ ps->pack(node);
+ ResourceSaver::save(p_path, ps);
+}
+
+void SceneDebugger::_send_object_id(ObjectID p_id, int p_max_size) {
+ SceneDebuggerObject obj(p_id);
+ if (obj.id.is_null())
+ return;
+
+ Array arr;
+ obj.serialize(arr);
+ ScriptDebugger::get_singleton()->send_message("inspect_object", arr);
+}
+
+void SceneDebugger::_set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value) {
+
+ Object *obj = ObjectDB::get_instance(p_id);
+ if (!obj)
+ return;
+
+ String prop_name = p_property;
+ if (p_property.begins_with("Members/")) {
+ Vector<String> ss = p_property.split("/");
+ prop_name = ss[ss.size() - 1];
+ }
+
+ obj->set(prop_name, p_value);
+}
+
+void SceneDebugger::add_to_cache(const String &p_filename, Node *p_node) {
+ LiveEditor *debugger = LiveEditor::get_singleton();
+ if (!debugger)
+ return;
+
+ if (ScriptDebugger::get_singleton() && p_filename != String()) {
+ debugger->live_scene_edit_cache[p_filename].insert(p_node);
+ }
+}
+void SceneDebugger::remove_from_cache(const String &p_filename, Node *p_node) {
+ LiveEditor *debugger = LiveEditor::get_singleton();
+ if (!debugger)
+ return;
+
+ Map<String, Set<Node *> > &edit_cache = debugger->live_scene_edit_cache;
+ Map<String, Set<Node *> >::Element *E = edit_cache.find(p_filename);
+ if (E) {
+ E->get().erase(p_node);
+ if (E->get().size() == 0) {
+ edit_cache.erase(E);
+ }
+ }
+
+ Map<Node *, Map<ObjectID, Node *> > &remove_list = debugger->live_edit_remove_list;
+ Map<Node *, Map<ObjectID, Node *> >::Element *F = remove_list.find(p_node);
+ if (F) {
+ for (Map<ObjectID, Node *>::Element *G = F->get().front(); G; G = G->next()) {
+
+ memdelete(G->get());
+ }
+ remove_list.erase(F);
+ }
+}
+
+/// SceneDebuggerObject
+SceneDebuggerObject::SceneDebuggerObject(ObjectID p_id) {
+ id = ObjectID();
+ Object *obj = ObjectDB::get_instance(p_id);
+ if (!obj)
+ return;
+
+ id = p_id;
+ class_name = obj->get_class();
+
+ if (ScriptInstance *si = obj->get_script_instance()) {
+ // Read script instance constants and variables
+ if (!si->get_script().is_null()) {
+ Script *s = si->get_script().ptr();
+ _parse_script_properties(s, si);
+ }
+ }
+
+ if (Node *node = Object::cast_to<Node>(obj)) {
+ // Add specialized NodePath info (if inside tree).
+ if (node->is_inside_tree()) {
+ PropertyInfo pi(Variant::NODE_PATH, String("Node/path"));
+ properties.push_back(SceneDebuggerProperty(pi, node->get_path()));
+ } else { // Can't ask for path if a node is not in tree.
+ PropertyInfo pi(Variant::STRING, String("Node/path"));
+ properties.push_back(SceneDebuggerProperty(pi, "[Orphan]"));
+ }
+ } else if (Script *s = Object::cast_to<Script>(obj)) {
+ // Add script constants (no instance).
+ _parse_script_properties(s, NULL);
+ }
+
+ // Add base object properties.
+ List<PropertyInfo> pinfo;
+ obj->get_property_list(&pinfo, true);
+ for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+ if (E->get().usage & (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CATEGORY)) {
+ properties.push_back(SceneDebuggerProperty(E->get(), obj->get(E->get().name)));
+ }
+ }
+}
+
+void SceneDebuggerObject::_parse_script_properties(Script *p_script, ScriptInstance *p_instance) {
+ typedef Map<const Script *, Set<StringName> > ScriptMemberMap;
+ typedef Map<const Script *, Map<StringName, Variant> > ScriptConstantsMap;
+
+ ScriptMemberMap members;
+ if (p_instance) {
+ members[p_script] = Set<StringName>();
+ p_script->get_members(&(members[p_script]));
+ }
+
+ ScriptConstantsMap constants;
+ constants[p_script] = Map<StringName, Variant>();
+ p_script->get_constants(&(constants[p_script]));
+
+ Ref<Script> base = p_script->get_base_script();
+ while (base.is_valid()) {
+ if (p_instance) {
+ members[base.ptr()] = Set<StringName>();
+ base->get_members(&(members[base.ptr()]));
+ }
+
+ constants[base.ptr()] = Map<StringName, Variant>();
+ base->get_constants(&(constants[base.ptr()]));
+
+ base = base->get_base_script();
+ }
+
+ // Members
+ for (ScriptMemberMap::Element *sm = members.front(); sm; sm = sm->next()) {
+ for (Set<StringName>::Element *E = sm->get().front(); E; E = E->next()) {
+ Variant m;
+ if (p_instance->get(E->get(), m)) {
+ String script_path = sm->key() == p_script ? "" : sm->key()->get_path().get_file() + "/";
+ PropertyInfo pi(m.get_type(), "Members/" + script_path + E->get());
+ properties.push_back(SceneDebuggerProperty(pi, m));
+ }
+ }
+ }
+ // Constants
+ for (ScriptConstantsMap::Element *sc = constants.front(); sc; sc = sc->next()) {
+ for (Map<StringName, Variant>::Element *E = sc->get().front(); E; E = E->next()) {
+ String script_path = sc->key() == p_script ? "" : sc->key()->get_path().get_file() + "/";
+ if (E->value().get_type() == Variant::OBJECT) {
+ Variant id = ((Object *)E->value())->get_instance_id();
+ PropertyInfo pi(id.get_type(), "Constants/" + E->key(), PROPERTY_HINT_OBJECT_ID, "Object");
+ properties.push_back(SceneDebuggerProperty(pi, id));
+ } else {
+ PropertyInfo pi(E->value().get_type(), "Constants/" + script_path + E->key());
+ properties.push_back(SceneDebuggerProperty(pi, E->value()));
+ }
+ }
+ }
+}
+
+void SceneDebuggerObject::serialize(Array &r_arr, int p_max_size) {
+ Array send_props;
+ for (int i = 0; i < properties.size(); i++) {
+ const PropertyInfo &pi = properties[i].first;
+ Variant &var = properties[i].second;
+
+ WeakRef *ref = Object::cast_to<WeakRef>(var);
+ if (ref) {
+ var = ref->get_ref();
+ }
+
+ RES res = var;
+
+ Array prop;
+ prop.push_back(pi.name);
+ prop.push_back(pi.type);
+
+ PropertyHint hint = pi.hint;
+ String hint_string = pi.hint_string;
+ if (!res.is_null()) {
+ var = res->get_path();
+ } else { //only send information that can be sent..
+ int len = 0; //test how big is this to encode
+ encode_variant(var, NULL, len);
+ if (len > p_max_size) { //limit to max size
+ hint = PROPERTY_HINT_OBJECT_TOO_BIG;
+ hint_string = "";
+ var = Variant();
+ }
+ }
+ prop.push_back(hint);
+ prop.push_back(hint_string);
+ prop.push_back(pi.usage);
+ prop.push_back(var);
+ send_props.push_back(prop);
+ }
+ r_arr.push_back(uint64_t(id));
+ r_arr.push_back(class_name);
+ r_arr.push_back(send_props);
+}
+
+void SceneDebuggerObject::deserialize(const Array &p_arr) {
+#define CHECK_TYPE(p_what, p_type) ERR_FAIL_COND(p_what.get_type() != Variant::p_type);
+ ERR_FAIL_COND(p_arr.size() < 3);
+ CHECK_TYPE(p_arr[0], INT);
+ CHECK_TYPE(p_arr[1], STRING);
+ CHECK_TYPE(p_arr[2], ARRAY);
+
+ id = uint64_t(p_arr[0]);
+ class_name = p_arr[1];
+ Array props = p_arr[2];
+
+ for (int i = 0; i < props.size(); i++) {
+
+ CHECK_TYPE(props[i], ARRAY);
+ Array prop = props[i];
+
+ ERR_FAIL_COND(prop.size() != 6);
+ CHECK_TYPE(prop[0], STRING);
+ CHECK_TYPE(prop[1], INT);
+ CHECK_TYPE(prop[2], INT);
+ CHECK_TYPE(prop[3], STRING);
+ CHECK_TYPE(prop[4], INT);
+
+ 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::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";
+ }
+ }
+ }
+ properties.push_back(SceneDebuggerProperty(pinfo, var));
+ }
+}
+
+/// SceneDebuggerTree
+SceneDebuggerTree::SceneDebuggerTree(Node *p_root) {
+ // Flatten tree into list, depth first, use stack to avoid recursion.
+ List<Node *> stack;
+ stack.push_back(p_root);
+ while (stack.size()) {
+ Node *n = stack[0];
+ stack.pop_front();
+ int count = n->get_child_count();
+ nodes.push_back(RemoteNode(count, n->get_name(), n->get_class(), n->get_instance_id()));
+ for (int i = 0; i < count; i++) {
+ stack.push_front(n->get_child(count - i - 1));
+ }
+ }
+}
+
+void SceneDebuggerTree::serialize(Array &p_arr) {
+ for (List<RemoteNode>::Element *E = nodes.front(); E; E = E->next()) {
+ RemoteNode &n = E->get();
+ p_arr.push_back(n.child_count);
+ p_arr.push_back(n.name);
+ p_arr.push_back(n.type_name);
+ p_arr.push_back(n.id);
+ }
+}
+
+void SceneDebuggerTree::deserialize(const Array &p_arr) {
+ int idx = 0;
+ while (p_arr.size() > idx) {
+ ERR_FAIL_COND(p_arr.size() < 4);
+ CHECK_TYPE(p_arr[idx], INT);
+ CHECK_TYPE(p_arr[idx + 1], STRING);
+ CHECK_TYPE(p_arr[idx + 2], STRING);
+ CHECK_TYPE(p_arr[idx + 3], INT);
+ nodes.push_back(RemoteNode(p_arr[idx], p_arr[idx + 1], p_arr[idx + 2], p_arr[idx + 3]));
+ idx += 4;
+ }
+}
+
+/// LiveEditor
+LiveEditor *LiveEditor::singleton = NULL;
+LiveEditor *LiveEditor::get_singleton() {
+ return singleton;
+}
+
+void LiveEditor::_send_tree() {
+ SceneTree *scene_tree = SceneTree::get_singleton();
+ if (!scene_tree)
+ return;
+
+ Array arr;
+ // Encoded as a flat list depth fist.
+ SceneDebuggerTree tree(scene_tree->root);
+ tree.serialize(arr);
+ ScriptDebugger::get_singleton()->send_message("scene_tree", arr);
+}
+
+void LiveEditor::_node_path_func(const NodePath &p_path, int p_id) {
+
+ live_edit_node_path_cache[p_id] = p_path;
+}
+
+void LiveEditor::_res_path_func(const String &p_path, int p_id) {
+
+ live_edit_resource_cache[p_id] = p_path;
+}
+
+void LiveEditor::_node_set_func(int p_id, const StringName &p_prop, const Variant &p_value) {
+
+ SceneTree *scene_tree = SceneTree::get_singleton();
+ if (!scene_tree)
+ return;
+
+ if (!live_edit_node_path_cache.has(p_id))
+ return;
+
+ NodePath np = live_edit_node_path_cache[p_id];
+ Node *base = NULL;
+ if (scene_tree->root->has_node(live_edit_root))
+ base = scene_tree->root->get_node(live_edit_root);
+
+ Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
+ if (!E)
+ return; //scene not editable
+
+ for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
+
+ Node *n = F->get();
+
+ if (base && !base->is_a_parent_of(n))
+ continue;
+
+ if (!n->has_node(np))
+ continue;
+ Node *n2 = n->get_node(np);
+
+ n2->set(p_prop, p_value);
+ }
+}
+
+void LiveEditor::_node_set_res_func(int p_id, const StringName &p_prop, const String &p_value) {
+
+ RES r = ResourceLoader::load(p_value);
+ if (!r.is_valid())
+ return;
+ _node_set_func(p_id, p_prop, r);
+}
+void LiveEditor::_node_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE) {
+ SceneTree *scene_tree = SceneTree::get_singleton();
+ if (!scene_tree)
+ return;
+ if (!live_edit_node_path_cache.has(p_id))
+ return;
+
+ NodePath np = live_edit_node_path_cache[p_id];
+ Node *base = NULL;
+ if (scene_tree->root->has_node(live_edit_root))
+ base = scene_tree->root->get_node(live_edit_root);
+
+ Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
+ if (!E)
+ return; //scene not editable
+
+ for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
+
+ Node *n = F->get();
+
+ if (base && !base->is_a_parent_of(n))
+ continue;
+
+ if (!n->has_node(np))
+ continue;
+ Node *n2 = n->get_node(np);
+
+ n2->call(p_method, VARIANT_ARG_PASS);
+ }
+}
+void LiveEditor::_res_set_func(int p_id, const StringName &p_prop, const Variant &p_value) {
+
+ if (!live_edit_resource_cache.has(p_id))
+ return;
+
+ String resp = live_edit_resource_cache[p_id];
+
+ if (!ResourceCache::has(resp))
+ return;
+
+ RES r = ResourceCache::get(resp);
+ if (!r.is_valid())
+ return;
+
+ r->set(p_prop, p_value);
+}
+void LiveEditor::_res_set_res_func(int p_id, const StringName &p_prop, const String &p_value) {
+
+ RES r = ResourceLoader::load(p_value);
+ if (!r.is_valid())
+ return;
+ _res_set_func(p_id, p_prop, r);
+}
+void LiveEditor::_res_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE) {
+
+ if (!live_edit_resource_cache.has(p_id))
+ return;
+
+ String resp = live_edit_resource_cache[p_id];
+
+ if (!ResourceCache::has(resp))
+ return;
+
+ RES r = ResourceCache::get(resp);
+ if (!r.is_valid())
+ return;
+
+ r->call(p_method, VARIANT_ARG_PASS);
+}
+
+void LiveEditor::_root_func(const NodePath &p_scene_path, const String &p_scene_from) {
+
+ live_edit_root = p_scene_path;
+ live_edit_scene = p_scene_from;
+}
+
+void LiveEditor::_create_node_func(const NodePath &p_parent, const String &p_type, const String &p_name) {
+ SceneTree *scene_tree = SceneTree::get_singleton();
+ if (!scene_tree)
+ return;
+
+ Node *base = NULL;
+ if (scene_tree->root->has_node(live_edit_root))
+ base = scene_tree->root->get_node(live_edit_root);
+
+ Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
+ if (!E)
+ return; //scene not editable
+
+ for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
+
+ Node *n = F->get();
+
+ if (base && !base->is_a_parent_of(n))
+ continue;
+
+ if (!n->has_node(p_parent))
+ continue;
+ Node *n2 = n->get_node(p_parent);
+
+ Node *no = Object::cast_to<Node>(ClassDB::instance(p_type));
+ if (!no) {
+ continue;
+ }
+
+ no->set_name(p_name);
+ n2->add_child(no);
+ }
+}
+void LiveEditor::_instance_node_func(const NodePath &p_parent, const String &p_path, const String &p_name) {
+ SceneTree *scene_tree = SceneTree::get_singleton();
+ if (!scene_tree)
+ return;
+
+ Ref<PackedScene> ps = ResourceLoader::load(p_path);
+
+ if (!ps.is_valid())
+ return;
+
+ Node *base = NULL;
+ if (scene_tree->root->has_node(live_edit_root))
+ base = scene_tree->root->get_node(live_edit_root);
+
+ Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
+ if (!E)
+ return; //scene not editable
+
+ for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
+
+ Node *n = F->get();
+
+ if (base && !base->is_a_parent_of(n))
+ continue;
+
+ if (!n->has_node(p_parent))
+ continue;
+ Node *n2 = n->get_node(p_parent);
+
+ Node *no = ps->instance();
+ if (!no) {
+ continue;
+ }
+
+ no->set_name(p_name);
+ n2->add_child(no);
+ }
+}
+void LiveEditor::_remove_node_func(const NodePath &p_at) {
+ SceneTree *scene_tree = SceneTree::get_singleton();
+ if (!scene_tree)
+ return;
+
+ Node *base = NULL;
+ if (scene_tree->root->has_node(live_edit_root))
+ base = scene_tree->root->get_node(live_edit_root);
+
+ Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
+ if (!E)
+ return; //scene not editable
+
+ for (Set<Node *>::Element *F = E->get().front(); F;) {
+
+ Set<Node *>::Element *N = F->next();
+
+ Node *n = F->get();
+
+ if (base && !base->is_a_parent_of(n))
+ continue;
+
+ if (!n->has_node(p_at))
+ continue;
+ Node *n2 = n->get_node(p_at);
+
+ memdelete(n2);
+
+ F = N;
+ }
+}
+void LiveEditor::_remove_and_keep_node_func(const NodePath &p_at, ObjectID p_keep_id) {
+ SceneTree *scene_tree = SceneTree::get_singleton();
+ if (!scene_tree)
+ return;
+
+ Node *base = NULL;
+ if (scene_tree->root->has_node(live_edit_root))
+ base = scene_tree->root->get_node(live_edit_root);
+
+ Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
+ if (!E)
+ return; //scene not editable
+
+ for (Set<Node *>::Element *F = E->get().front(); F;) {
+
+ Set<Node *>::Element *N = F->next();
+
+ Node *n = F->get();
+
+ if (base && !base->is_a_parent_of(n))
+ continue;
+
+ if (!n->has_node(p_at))
+ continue;
+
+ Node *n2 = n->get_node(p_at);
+
+ n2->get_parent()->remove_child(n2);
+
+ live_edit_remove_list[n][p_keep_id] = n2;
+
+ F = N;
+ }
+}
+void LiveEditor::_restore_node_func(ObjectID p_id, const NodePath &p_at, int p_at_pos) {
+ SceneTree *scene_tree = SceneTree::get_singleton();
+ if (!scene_tree)
+ return;
+
+ Node *base = NULL;
+ if (scene_tree->root->has_node(live_edit_root))
+ base = scene_tree->root->get_node(live_edit_root);
+
+ Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
+ if (!E)
+ return; //scene not editable
+
+ for (Set<Node *>::Element *F = E->get().front(); F;) {
+
+ Set<Node *>::Element *N = F->next();
+
+ Node *n = F->get();
+
+ if (base && !base->is_a_parent_of(n))
+ continue;
+
+ if (!n->has_node(p_at))
+ continue;
+ Node *n2 = n->get_node(p_at);
+
+ Map<Node *, Map<ObjectID, Node *> >::Element *EN = live_edit_remove_list.find(n);
+
+ if (!EN)
+ continue;
+
+ Map<ObjectID, Node *>::Element *FN = EN->get().find(p_id);
+
+ if (!FN)
+ continue;
+ n2->add_child(FN->get());
+
+ EN->get().erase(FN);
+
+ if (EN->get().size() == 0) {
+ live_edit_remove_list.erase(EN);
+ }
+
+ F = N;
+ }
+}
+void LiveEditor::_duplicate_node_func(const NodePath &p_at, const String &p_new_name) {
+ SceneTree *scene_tree = SceneTree::get_singleton();
+ if (!scene_tree)
+ return;
+
+ Node *base = NULL;
+ if (scene_tree->root->has_node(live_edit_root))
+ base = scene_tree->root->get_node(live_edit_root);
+
+ Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
+ if (!E)
+ return; //scene not editable
+
+ for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
+
+ Node *n = F->get();
+
+ if (base && !base->is_a_parent_of(n))
+ continue;
+
+ if (!n->has_node(p_at))
+ continue;
+ Node *n2 = n->get_node(p_at);
+
+ Node *dup = n2->duplicate(Node::DUPLICATE_SIGNALS | Node::DUPLICATE_GROUPS | Node::DUPLICATE_SCRIPTS);
+
+ if (!dup)
+ continue;
+
+ dup->set_name(p_new_name);
+ n2->get_parent()->add_child(dup);
+ }
+}
+void LiveEditor::_reparent_node_func(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos) {
+ SceneTree *scene_tree = SceneTree::get_singleton();
+ if (!scene_tree)
+ return;
+
+ Node *base = NULL;
+ if (scene_tree->root->has_node(live_edit_root))
+ base = scene_tree->root->get_node(live_edit_root);
+
+ Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
+ if (!E)
+ return; //scene not editable
+
+ for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
+
+ Node *n = F->get();
+
+ if (base && !base->is_a_parent_of(n))
+ continue;
+
+ if (!n->has_node(p_at))
+ continue;
+ Node *nfrom = n->get_node(p_at);
+
+ if (!n->has_node(p_new_place))
+ continue;
+ Node *nto = n->get_node(p_new_place);
+
+ nfrom->get_parent()->remove_child(nfrom);
+ nfrom->set_name(p_new_name);
+
+ nto->add_child(nfrom);
+ if (p_at_pos >= 0)
+ nto->move_child(nfrom, p_at_pos);
+ }
+}
+
+#endif
diff --git a/scene/debugger/scene_debugger.h b/scene/debugger/scene_debugger.h
new file mode 100644
index 0000000000..d22f8e8e18
--- /dev/null
+++ b/scene/debugger/scene_debugger.h
@@ -0,0 +1,151 @@
+/*************************************************************************/
+/* scene_debugger.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 SCENE_DEBUGGER_H
+#define SCENE_DEBUGGER_H
+
+#include "core/array.h"
+#include "core/object.h"
+#include "core/pair.h"
+#include "core/script_language.h"
+#include "core/ustring.h"
+
+class SceneDebugger {
+
+public:
+ static void initialize();
+ static void deinitialize();
+
+#ifdef DEBUG_ENABLED
+private:
+ static void _save_node(ObjectID id, const String &p_path);
+ static void _set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value);
+ static void _send_object_id(ObjectID p_id, int p_max_size = 1 << 20);
+
+public:
+ static Error parse_message(const String &p_msg, const Array &p_args);
+ static void add_to_cache(const String &p_filename, Node *p_node);
+ static void remove_from_cache(const String &p_filename, Node *p_node);
+#endif
+};
+
+#ifdef DEBUG_ENABLED
+class SceneDebuggerObject {
+
+private:
+ void _parse_script_properties(Script *p_script, ScriptInstance *p_instance);
+
+public:
+ typedef Pair<PropertyInfo, Variant> SceneDebuggerProperty;
+ ObjectID id;
+ String class_name;
+ List<SceneDebuggerProperty> properties;
+
+ SceneDebuggerObject(ObjectID p_id);
+ SceneDebuggerObject() {}
+
+ void serialize(Array &r_arr, int p_max_size = 1 << 20);
+ void deserialize(const Array &p_arr);
+};
+
+class SceneDebuggerTree {
+
+public:
+ struct RemoteNode {
+ int child_count;
+ String name;
+ String type_name;
+ ObjectID id;
+
+ RemoteNode(int p_child, const String &p_name, const String &p_type, ObjectID p_id) {
+ child_count = p_child;
+ name = p_name;
+ type_name = p_type;
+ id = p_id;
+ }
+
+ RemoteNode() {}
+ };
+
+ List<RemoteNode> nodes;
+
+ void serialize(Array &r_arr);
+ void deserialize(const Array &p_arr);
+ SceneDebuggerTree(Node *p_root);
+ SceneDebuggerTree(){};
+};
+
+class LiveEditor {
+
+private:
+ friend class SceneDebugger;
+ Map<int, NodePath> live_edit_node_path_cache;
+ Map<int, String> live_edit_resource_cache;
+
+ NodePath live_edit_root;
+ String live_edit_scene;
+
+ Map<String, Set<Node *> > live_scene_edit_cache;
+ Map<Node *, Map<ObjectID, Node *> > live_edit_remove_list;
+
+ void _send_tree();
+
+ void _node_path_func(const NodePath &p_path, int p_id);
+ void _res_path_func(const String &p_path, int p_id);
+
+ void _node_set_func(int p_id, const StringName &p_prop, const Variant &p_value);
+ void _node_set_res_func(int p_id, const StringName &p_prop, const String &p_value);
+ void _node_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE);
+ void _res_set_func(int p_id, const StringName &p_prop, const Variant &p_value);
+ void _res_set_res_func(int p_id, const StringName &p_prop, const String &p_value);
+ void _res_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE);
+ void _root_func(const NodePath &p_scene_path, const String &p_scene_from);
+
+ void _create_node_func(const NodePath &p_parent, const String &p_type, const String &p_name);
+ void _instance_node_func(const NodePath &p_parent, const String &p_path, const String &p_name);
+ void _remove_node_func(const NodePath &p_at);
+ void _remove_and_keep_node_func(const NodePath &p_at, ObjectID p_keep_id);
+ void _restore_node_func(ObjectID p_id, const NodePath &p_at, int p_at_pos);
+ void _duplicate_node_func(const NodePath &p_at, const String &p_new_name);
+ void _reparent_node_func(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos);
+
+ LiveEditor() {
+ singleton = this;
+ live_edit_root = NodePath("/root");
+ };
+
+ static LiveEditor *singleton;
+
+public:
+ static LiveEditor *get_singleton();
+};
+#endif
+
+#endif
diff --git a/scene/debugger/script_debugger_remote.cpp b/scene/debugger/script_debugger_remote.cpp
deleted file mode 100644
index 0e61a5746b..0000000000
--- a/scene/debugger/script_debugger_remote.cpp
+++ /dev/null
@@ -1,1313 +0,0 @@
-/*************************************************************************/
-/* script_debugger_remote.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 "script_debugger_remote.h"
-
-#include "core/engine.h"
-#include "core/io/ip.h"
-#include "core/io/marshalls.h"
-#include "core/os/input.h"
-#include "core/os/os.h"
-#include "core/project_settings.h"
-#include "scene/main/node.h"
-#include "scene/main/scene_tree.h"
-#include "scene/main/viewport.h"
-#include "scene/resources/packed_scene.h"
-#include "servers/visual_server.h"
-
-void ScriptDebuggerRemote::_send_video_memory() {
-
- List<ResourceUsage> usage;
- if (resource_usage_func)
- resource_usage_func(&usage);
-
- usage.sort();
-
- packet_peer_stream->put_var("message:video_mem");
- packet_peer_stream->put_var(usage.size() * 4);
-
- for (List<ResourceUsage>::Element *E = usage.front(); E; E = E->next()) {
-
- packet_peer_stream->put_var(E->get().path);
- packet_peer_stream->put_var(E->get().type);
- packet_peer_stream->put_var(E->get().format);
- packet_peer_stream->put_var(E->get().vram);
- }
-}
-
-Error ScriptDebuggerRemote::connect_to_host(const String &p_host, uint16_t p_port) {
-
- IP_Address ip;
- if (p_host.is_valid_ip_address())
- ip = p_host;
- else
- ip = IP::get_singleton()->resolve_hostname(p_host);
-
- int port = p_port;
-
- const int tries = 6;
- int waits[tries] = { 1, 10, 100, 1000, 1000, 1000 };
-
- tcp_client->connect_to_host(ip, port);
-
- for (int i = 0; i < tries; i++) {
-
- if (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED) {
- print_verbose("Remote Debugger: Connected!");
- break;
- } else {
-
- const int ms = waits[i];
- OS::get_singleton()->delay_usec(ms * 1000);
- print_verbose("Remote Debugger: Connection failed with status: '" + String::num(tcp_client->get_status()) + "', retrying in " + String::num(ms) + " msec.");
- };
- };
-
- if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
-
- ERR_PRINT("Remote Debugger: Unable to connect. Status: " + String::num(tcp_client->get_status()) + ".");
- return FAILED;
- };
-
- packet_peer_stream->set_stream_peer(tcp_client);
-
- return OK;
-}
-
-void ScriptDebuggerRemote::_put_variable(const String &p_name, const Variant &p_variable) {
-
- packet_peer_stream->put_var(p_name);
-
- Variant var = p_variable;
- if (p_variable.get_type() == Variant::OBJECT && p_variable.get_validated_object() == nullptr) {
- var = Variant();
- }
-
- int len = 0;
- Error err = encode_variant(var, NULL, len, true);
- if (err != OK)
- ERR_PRINT("Failed to encode variant.");
-
- if (len > packet_peer_stream->get_output_buffer_max_size()) { //limit to max size
- packet_peer_stream->put_var(Variant());
- } else {
- packet_peer_stream->put_var(var);
- }
-}
-
-void ScriptDebuggerRemote::_save_node(ObjectID id, const String &p_path) {
-
- Node *node = Object::cast_to<Node>(ObjectDB::get_instance(id));
- ERR_FAIL_COND(!node);
-
- Ref<PackedScene> ps = memnew(PackedScene);
- ps->pack(node);
- ResourceSaver::save(p_path, ps);
-}
-
-void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue, bool p_is_error_breakpoint) {
-
- //this function is called when there is a debugger break (bug on script)
- //or when execution is paused from editor
-
- if (skip_breakpoints && !p_is_error_breakpoint)
- return;
-
- ERR_FAIL_COND_MSG(!tcp_client->is_connected_to_host(), "Script Debugger failed to connect, but being used anyway.");
-
- packet_peer_stream->put_var("debug_enter");
- packet_peer_stream->put_var(2);
- packet_peer_stream->put_var(p_can_continue);
- packet_peer_stream->put_var(p_script->debug_get_error());
-
- skip_profile_frame = true; // to avoid super long frame time for the frame
-
- Input::MouseMode mouse_mode = Input::get_singleton()->get_mouse_mode();
- if (mouse_mode != Input::MOUSE_MODE_VISIBLE)
- Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
-
- uint64_t loop_begin_usec = 0;
- uint64_t loop_time_sec = 0;
- while (true) {
- loop_begin_usec = OS::get_singleton()->get_ticks_usec();
-
- _get_output();
-
- if (packet_peer_stream->get_available_packet_count() > 0) {
-
- Variant var;
- Error err = packet_peer_stream->get_var(var);
-
- ERR_CONTINUE(err != OK);
- ERR_CONTINUE(var.get_type() != Variant::ARRAY);
-
- Array cmd = var;
-
- ERR_CONTINUE(cmd.size() == 0);
- ERR_CONTINUE(cmd[0].get_type() != Variant::STRING);
-
- String command = cmd[0];
-
- if (command == "get_stack_dump") {
-
- packet_peer_stream->put_var("stack_dump");
- int slc = p_script->debug_get_stack_level_count();
- packet_peer_stream->put_var(slc);
-
- for (int i = 0; i < slc; i++) {
-
- Dictionary d;
- d["file"] = p_script->debug_get_stack_level_source(i);
- d["line"] = p_script->debug_get_stack_level_line(i);
- d["function"] = p_script->debug_get_stack_level_function(i);
- //d["id"]=p_script->debug_get_stack_level_
- d["id"] = 0;
-
- packet_peer_stream->put_var(d);
- }
-
- } else if (command == "get_stack_frame_vars") {
-
- cmd.remove(0);
- ERR_CONTINUE(cmd.size() != 1);
- int lv = cmd[0];
-
- List<String> members;
- List<Variant> member_vals;
- if (ScriptInstance *inst = p_script->debug_get_stack_level_instance(lv)) {
- members.push_back("self");
- member_vals.push_back(inst->get_owner());
- }
- p_script->debug_get_stack_level_members(lv, &members, &member_vals);
- ERR_CONTINUE(members.size() != member_vals.size());
-
- List<String> locals;
- List<Variant> local_vals;
- p_script->debug_get_stack_level_locals(lv, &locals, &local_vals);
- ERR_CONTINUE(locals.size() != local_vals.size());
-
- List<String> globals;
- List<Variant> globals_vals;
- p_script->debug_get_globals(&globals, &globals_vals);
- ERR_CONTINUE(globals.size() != globals_vals.size());
-
- packet_peer_stream->put_var("stack_frame_vars");
- packet_peer_stream->put_var(3 + (locals.size() + members.size() + globals.size()) * 2);
-
- { //locals
- packet_peer_stream->put_var(locals.size());
-
- List<String>::Element *E = locals.front();
- List<Variant>::Element *F = local_vals.front();
-
- while (E) {
- _put_variable(E->get(), F->get());
-
- E = E->next();
- F = F->next();
- }
- }
-
- { //members
- packet_peer_stream->put_var(members.size());
-
- List<String>::Element *E = members.front();
- List<Variant>::Element *F = member_vals.front();
-
- while (E) {
-
- _put_variable(E->get(), F->get());
-
- E = E->next();
- F = F->next();
- }
- }
-
- { //globals
- packet_peer_stream->put_var(globals.size());
-
- List<String>::Element *E = globals.front();
- List<Variant>::Element *F = globals_vals.front();
-
- while (E) {
- _put_variable(E->get(), F->get());
-
- E = E->next();
- F = F->next();
- }
- }
-
- } else if (command == "step") {
-
- set_depth(-1);
- set_lines_left(1);
- break;
- } else if (command == "next") {
-
- set_depth(0);
- set_lines_left(1);
- break;
-
- } else if (command == "continue") {
- set_depth(-1);
- set_lines_left(-1);
- OS::get_singleton()->move_window_to_foreground();
- break;
- } else if (command == "break") {
- ERR_PRINT("Got break when already broke!");
- break;
- } else if (command == "request_scene_tree") {
-
-#ifdef DEBUG_ENABLED
- if (scene_tree)
- scene_tree->_debugger_request_tree();
-#endif
- } else if (command == "request_video_mem") {
-
- _send_video_memory();
- } else if (command == "inspect_object") {
-
- ObjectID id = cmd[1];
- _send_object_id(id);
- } else if (command == "set_object_property") {
-
- _set_object_property(cmd[1], cmd[2], cmd[3]);
-
- } else if (command == "override_camera_2D:set") {
- bool enforce = cmd[1];
-
- if (scene_tree) {
- scene_tree->get_root()->enable_canvas_transform_override(enforce);
- }
- } else if (command == "override_camera_2D:transform") {
- Transform2D transform = cmd[1];
-
- if (scene_tree) {
- scene_tree->get_root()->set_canvas_transform_override(transform);
- }
- } else if (command == "override_camera_3D:set") {
- bool enable = cmd[1];
-
- if (scene_tree) {
- scene_tree->get_root()->enable_camera_override(enable);
- }
- } else if (command == "override_camera_3D:transform") {
- Transform transform = cmd[1];
- bool is_perspective = cmd[2];
- float size_or_fov = cmd[3];
- float near = cmd[4];
- float far = cmd[5];
-
- if (scene_tree) {
- if (is_perspective) {
- scene_tree->get_root()->set_camera_override_perspective(size_or_fov, near, far);
- } else {
- scene_tree->get_root()->set_camera_override_orthogonal(size_or_fov, near, far);
- }
- scene_tree->get_root()->set_camera_override_transform(transform);
- }
-
- } else if (command == "reload_scripts") {
- reload_all_scripts = true;
- } else if (command == "breakpoint") {
-
- bool set = cmd[3];
- if (set)
- insert_breakpoint(cmd[2], cmd[1]);
- else
- remove_breakpoint(cmd[2], cmd[1]);
-
- } else if (command == "save_node") {
- _save_node(cmd[1], cmd[2]);
- } else if (command == "set_skip_breakpoints") {
- skip_breakpoints = cmd[1];
- } else {
- _parse_live_edit(cmd);
- }
-
- } else {
- OS::get_singleton()->delay_usec(10000);
- OS::get_singleton()->process_and_drop_events();
- }
-
- // This is for the camera override to stay live even when the game is paused from the editor
- loop_time_sec = (OS::get_singleton()->get_ticks_usec() - loop_begin_usec) / 1000000.0f;
- VisualServer::get_singleton()->sync();
- if (VisualServer::get_singleton()->has_changed()) {
- VisualServer::get_singleton()->draw(true, loop_time_sec * Engine::get_singleton()->get_time_scale());
- }
- }
-
- packet_peer_stream->put_var("debug_exit");
- packet_peer_stream->put_var(0);
-
- if (mouse_mode != Input::MOUSE_MODE_VISIBLE)
- Input::get_singleton()->set_mouse_mode(mouse_mode);
-}
-
-void ScriptDebuggerRemote::_get_output() {
-
- mutex->lock();
- if (output_strings.size()) {
-
- locking = true;
- packet_peer_stream->put_var("output");
- packet_peer_stream->put_var(output_strings.size());
-
- while (output_strings.size()) {
-
- packet_peer_stream->put_var(output_strings.front()->get());
- output_strings.pop_front();
- }
- locking = false;
- }
-
- if (n_messages_dropped > 0) {
- Message msg;
- msg.message = "Too many messages! " + String::num_int64(n_messages_dropped) + " messages were dropped.";
- messages.push_back(msg);
- n_messages_dropped = 0;
- }
-
- while (messages.size()) {
- locking = true;
- packet_peer_stream->put_var("message:" + messages.front()->get().message);
- packet_peer_stream->put_var(messages.front()->get().data.size());
- for (int i = 0; i < messages.front()->get().data.size(); i++) {
- packet_peer_stream->put_var(messages.front()->get().data[i]);
- }
- messages.pop_front();
- locking = false;
- }
-
- if (n_errors_dropped == 1) {
- // Only print one message about dropping per second
- OutputError oe;
- oe.error = "TOO_MANY_ERRORS";
- oe.error_descr = "Too many errors! Ignoring errors for up to 1 second.";
- oe.warning = false;
- uint64_t time = OS::get_singleton()->get_ticks_msec();
- oe.hr = time / 3600000;
- oe.min = (time / 60000) % 60;
- oe.sec = (time / 1000) % 60;
- oe.msec = time % 1000;
- errors.push_back(oe);
- }
-
- if (n_warnings_dropped == 1) {
- // Only print one message about dropping per second
- OutputError oe;
- oe.error = "TOO_MANY_WARNINGS";
- oe.error_descr = "Too many warnings! Ignoring warnings for up to 1 second.";
- oe.warning = true;
- uint64_t time = OS::get_singleton()->get_ticks_msec();
- oe.hr = time / 3600000;
- oe.min = (time / 60000) % 60;
- oe.sec = (time / 1000) % 60;
- oe.msec = time % 1000;
- errors.push_back(oe);
- }
-
- while (errors.size()) {
- locking = true;
- packet_peer_stream->put_var("error");
- OutputError oe = errors.front()->get();
-
- packet_peer_stream->put_var(oe.callstack.size() + 2);
-
- Array error_data;
-
- error_data.push_back(oe.hr);
- error_data.push_back(oe.min);
- error_data.push_back(oe.sec);
- error_data.push_back(oe.msec);
- error_data.push_back(oe.source_func);
- error_data.push_back(oe.source_file);
- error_data.push_back(oe.source_line);
- error_data.push_back(oe.error);
- error_data.push_back(oe.error_descr);
- error_data.push_back(oe.warning);
- packet_peer_stream->put_var(error_data);
- packet_peer_stream->put_var(oe.callstack.size());
- for (int i = 0; i < oe.callstack.size(); i++) {
- packet_peer_stream->put_var(oe.callstack[i]);
- }
-
- errors.pop_front();
- locking = false;
- }
- mutex->unlock();
-}
-
-void ScriptDebuggerRemote::line_poll() {
-
- //the purpose of this is just processing events every now and then when the script might get too busy
- //otherwise bugs like infinite loops can't be caught
- if (poll_every % 2048 == 0)
- _poll_events();
- poll_every++;
-}
-
-void ScriptDebuggerRemote::_err_handler(void *ud, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, ErrorHandlerType p_type) {
-
- if (p_type == ERR_HANDLER_SCRIPT)
- return; //ignore script errors, those go through debugger
-
- Vector<ScriptLanguage::StackInfo> si;
-
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- si = ScriptServer::get_language(i)->debug_get_current_stack_info();
- if (si.size())
- break;
- }
-
- ScriptDebuggerRemote *sdr = (ScriptDebuggerRemote *)ud;
- sdr->send_error(p_func, p_file, p_line, p_err, p_descr, p_type, si);
-}
-
-bool ScriptDebuggerRemote::_parse_live_edit(const Array &p_command) {
-
-#ifdef DEBUG_ENABLED
-
- String cmdstr = p_command[0];
- if (!scene_tree || !cmdstr.begins_with("live_"))
- return false;
-
- if (cmdstr == "live_set_root") {
-
- scene_tree->_live_edit_root_func(p_command[1], p_command[2]);
-
- } else if (cmdstr == "live_node_path") {
-
- scene_tree->_live_edit_node_path_func(p_command[1], p_command[2]);
-
- } else if (cmdstr == "live_res_path") {
-
- scene_tree->_live_edit_res_path_func(p_command[1], p_command[2]);
-
- } else if (cmdstr == "live_node_prop_res") {
-
- scene_tree->_live_edit_node_set_res_func(p_command[1], p_command[2], p_command[3]);
-
- } else if (cmdstr == "live_node_prop") {
-
- scene_tree->_live_edit_node_set_func(p_command[1], p_command[2], p_command[3]);
-
- } else if (cmdstr == "live_res_prop_res") {
-
- scene_tree->_live_edit_res_set_res_func(p_command[1], p_command[2], p_command[3]);
-
- } else if (cmdstr == "live_res_prop") {
-
- scene_tree->_live_edit_res_set_func(p_command[1], p_command[2], p_command[3]);
-
- } else if (cmdstr == "live_node_call") {
-
- scene_tree->_live_edit_node_call_func(p_command[1], p_command[2], p_command[3], p_command[4], p_command[5], p_command[6], p_command[7]);
-
- } else if (cmdstr == "live_res_call") {
-
- scene_tree->_live_edit_res_call_func(p_command[1], p_command[2], p_command[3], p_command[4], p_command[5], p_command[6], p_command[7]);
-
- } else if (cmdstr == "live_create_node") {
-
- scene_tree->_live_edit_create_node_func(p_command[1], p_command[2], p_command[3]);
-
- } else if (cmdstr == "live_instance_node") {
-
- scene_tree->_live_edit_instance_node_func(p_command[1], p_command[2], p_command[3]);
-
- } else if (cmdstr == "live_remove_node") {
-
- scene_tree->_live_edit_remove_node_func(p_command[1]);
-
- } else if (cmdstr == "live_remove_and_keep_node") {
-
- scene_tree->_live_edit_remove_and_keep_node_func(p_command[1], p_command[2]);
-
- } else if (cmdstr == "live_restore_node") {
-
- scene_tree->_live_edit_restore_node_func(p_command[1], p_command[2], p_command[3]);
-
- } else if (cmdstr == "live_duplicate_node") {
-
- scene_tree->_live_edit_duplicate_node_func(p_command[1], p_command[2]);
-
- } else if (cmdstr == "live_reparent_node") {
-
- scene_tree->_live_edit_reparent_node_func(p_command[1], p_command[2], p_command[3], p_command[4]);
-
- } else {
-
- return false;
- }
-
- return true;
-#else
-
- return false;
-#endif
-}
-
-void ScriptDebuggerRemote::_send_object_id(ObjectID p_id) {
-
- Object *obj = ObjectDB::get_instance(p_id);
- if (!obj)
- return;
-
- typedef Pair<PropertyInfo, Variant> PropertyDesc;
- List<PropertyDesc> properties;
-
- if (ScriptInstance *si = obj->get_script_instance()) {
- if (!si->get_script().is_null()) {
-
- typedef Map<const Script *, Set<StringName> > ScriptMemberMap;
- typedef Map<const Script *, Map<StringName, Variant> > ScriptConstantsMap;
-
- ScriptMemberMap members;
- members[si->get_script().ptr()] = Set<StringName>();
- si->get_script()->get_members(&(members[si->get_script().ptr()]));
-
- ScriptConstantsMap constants;
- constants[si->get_script().ptr()] = Map<StringName, Variant>();
- si->get_script()->get_constants(&(constants[si->get_script().ptr()]));
-
- Ref<Script> base = si->get_script()->get_base_script();
- while (base.is_valid()) {
-
- members[base.ptr()] = Set<StringName>();
- base->get_members(&(members[base.ptr()]));
-
- constants[base.ptr()] = Map<StringName, Variant>();
- base->get_constants(&(constants[base.ptr()]));
-
- base = base->get_base_script();
- }
-
- for (ScriptMemberMap::Element *sm = members.front(); sm; sm = sm->next()) {
- for (Set<StringName>::Element *E = sm->get().front(); E; E = E->next()) {
- Variant m;
- if (si->get(E->get(), m)) {
- String script_path = sm->key() == si->get_script().ptr() ? "" : sm->key()->get_path().get_file() + "/";
- PropertyInfo pi(m.get_type(), "Members/" + script_path + E->get());
- properties.push_back(PropertyDesc(pi, m));
- }
- }
- }
-
- for (ScriptConstantsMap::Element *sc = constants.front(); sc; sc = sc->next()) {
- for (Map<StringName, Variant>::Element *E = sc->get().front(); E; E = E->next()) {
- String script_path = sc->key() == si->get_script().ptr() ? "" : sc->key()->get_path().get_file() + "/";
- if (E->value().get_type() == Variant::OBJECT) {
- Variant id = ((Object *)E->value())->get_instance_id();
- PropertyInfo pi(id.get_type(), "Constants/" + E->key(), PROPERTY_HINT_OBJECT_ID, "Object");
- properties.push_back(PropertyDesc(pi, id));
- } else {
- PropertyInfo pi(E->value().get_type(), "Constants/" + script_path + E->key());
- properties.push_back(PropertyDesc(pi, E->value()));
- }
- }
- }
- }
- }
-
- if (Node *node = Object::cast_to<Node>(obj)) {
- // in some cases node will not be in tree here
- // for instance where it created as variable and not yet added to tree
- // in such cases we can't ask for it's path
- if (node->is_inside_tree()) {
- PropertyInfo pi(Variant::NODE_PATH, String("Node/path"));
- properties.push_front(PropertyDesc(pi, node->get_path()));
- } else {
- PropertyInfo pi(Variant::STRING, String("Node/path"));
- properties.push_front(PropertyDesc(pi, "[Orphan]"));
- }
-
- } else if (Resource *res = Object::cast_to<Resource>(obj)) {
- if (Script *s = Object::cast_to<Script>(res)) {
- Map<StringName, Variant> constants;
- s->get_constants(&constants);
- for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
- if (E->value().get_type() == Variant::OBJECT) {
- Variant id = ((Object *)E->value())->get_instance_id();
- PropertyInfo pi(id.get_type(), "Constants/" + E->key(), PROPERTY_HINT_OBJECT_ID, "Object");
- properties.push_front(PropertyDesc(pi, E->value()));
- } else {
- PropertyInfo pi(E->value().get_type(), String("Constants/") + E->key());
- properties.push_front(PropertyDesc(pi, E->value()));
- }
- }
- }
- }
-
- List<PropertyInfo> pinfo;
- obj->get_property_list(&pinfo, true);
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
- if (E->get().usage & (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CATEGORY)) {
- properties.push_back(PropertyDesc(E->get(), obj->get(E->get().name)));
- }
- }
-
- Array send_props;
- for (int i = 0; i < properties.size(); i++) {
- const PropertyInfo &pi = properties[i].first;
- Variant &var = properties[i].second;
-
- WeakRef *ref = Object::cast_to<WeakRef>(var);
- if (ref) {
- var = ref->get_ref();
- }
-
- RES res = var;
-
- Array prop;
- prop.push_back(pi.name);
- prop.push_back(pi.type);
-
- //only send information that can be sent..
- int len = 0; //test how big is this to encode
- encode_variant(var, NULL, len);
- if (len > packet_peer_stream->get_output_buffer_max_size()) { //limit to max size
- prop.push_back(PROPERTY_HINT_OBJECT_TOO_BIG);
- prop.push_back("");
- prop.push_back(pi.usage);
- prop.push_back(Variant());
- } else {
- prop.push_back(pi.hint);
- prop.push_back(pi.hint_string);
- prop.push_back(pi.usage);
-
- if (!res.is_null()) {
- var = res->get_path();
- }
-
- prop.push_back(var);
- }
- send_props.push_back(prop);
- }
-
- packet_peer_stream->put_var("message:inspect_object");
- packet_peer_stream->put_var(3);
- packet_peer_stream->put_var(p_id);
- packet_peer_stream->put_var(obj->get_class());
- packet_peer_stream->put_var(send_props);
-}
-
-void ScriptDebuggerRemote::_set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value) {
-
- Object *obj = ObjectDB::get_instance(p_id);
- if (!obj)
- return;
-
- String prop_name = p_property;
- if (p_property.begins_with("Members/")) {
- Vector<String> ss = p_property.split("/");
- prop_name = ss[ss.size() - 1];
- }
-
- obj->set(prop_name, p_value);
-}
-
-void ScriptDebuggerRemote::_poll_events() {
-
- //this si called from ::idle_poll, happens only when running the game,
- //does not get called while on debug break
-
- while (packet_peer_stream->get_available_packet_count() > 0) {
-
- _get_output();
-
- //send over output_strings
-
- Variant var;
- Error err = packet_peer_stream->get_var(var);
-
- ERR_CONTINUE(err != OK);
- ERR_CONTINUE(var.get_type() != Variant::ARRAY);
-
- Array cmd = var;
-
- ERR_CONTINUE(cmd.size() == 0);
- ERR_CONTINUE(cmd[0].get_type() != Variant::STRING);
-
- String command = cmd[0];
- //cmd.remove(0);
-
- if (command == "break") {
-
- if (get_break_language())
- debug(get_break_language());
- } else if (command == "request_scene_tree") {
-
-#ifdef DEBUG_ENABLED
- if (scene_tree)
- scene_tree->_debugger_request_tree();
-#endif
- } else if (command == "request_video_mem") {
-
- _send_video_memory();
- } else if (command == "inspect_object") {
-
- ObjectID id = cmd[1];
- _send_object_id(id);
- } else if (command == "set_object_property") {
-
- _set_object_property(cmd[1], cmd[2], cmd[3]);
-
- } else if (command == "start_profiling") {
-
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- ScriptServer::get_language(i)->profiling_start();
- }
-
- max_frame_functions = cmd[1];
- profiler_function_signature_map.clear();
- profiling = true;
- frame_time = 0;
- idle_time = 0;
- physics_time = 0;
- physics_frame_time = 0;
-
- print_line("PROFILING ALRIGHT!");
-
- } else if (command == "stop_profiling") {
-
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- ScriptServer::get_language(i)->profiling_stop();
- }
- profiling = false;
- _send_profiling_data(false);
- print_line("PROFILING END!");
- } else if (command == "start_visual_profiling") {
-
- visual_profiling = true;
- VS::get_singleton()->set_frame_profiling_enabled(true);
- } else if (command == "stop_visual_profiling") {
-
- visual_profiling = false;
- VS::get_singleton()->set_frame_profiling_enabled(false);
- } else if (command == "start_network_profiling") {
-
- network_profiling = true;
- multiplayer->profiling_start();
- } else if (command == "stop_network_profiling") {
-
- network_profiling = false;
- multiplayer->profiling_end();
- } else if (command == "override_camera_2D:set") {
- bool enforce = cmd[1];
-
- if (scene_tree) {
- scene_tree->get_root()->enable_canvas_transform_override(enforce);
- }
- } else if (command == "override_camera_2D:transform") {
- Transform2D transform = cmd[1];
-
- if (scene_tree) {
- scene_tree->get_root()->set_canvas_transform_override(transform);
- }
- } else if (command == "override_camera_3D:set") {
- bool enable = cmd[1];
-
- if (scene_tree) {
- scene_tree->get_root()->enable_camera_override(enable);
- }
- } else if (command == "override_camera_3D:transform") {
- Transform transform = cmd[1];
- bool is_perspective = cmd[2];
- float size_or_fov = cmd[3];
- float near = cmd[4];
- float far = cmd[5];
-
- if (scene_tree) {
- if (is_perspective) {
- scene_tree->get_root()->set_camera_override_perspective(size_or_fov, near, far);
- } else {
- scene_tree->get_root()->set_camera_override_orthogonal(size_or_fov, near, far);
- }
- scene_tree->get_root()->set_camera_override_transform(transform);
- }
-
- } else if (command == "reload_scripts") {
- reload_all_scripts = true;
- } else if (command == "breakpoint") {
-
- bool set = cmd[3];
- if (set)
- insert_breakpoint(cmd[2], cmd[1]);
- else
- remove_breakpoint(cmd[2], cmd[1]);
- } else if (command == "set_skip_breakpoints") {
- skip_breakpoints = cmd[1];
- } else {
- _parse_live_edit(cmd);
- }
- }
-}
-
-void ScriptDebuggerRemote::_send_profiling_data(bool p_for_frame) {
-
- int ofs = 0;
-
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- if (p_for_frame)
- ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&profile_info.write[ofs], profile_info.size() - ofs);
- else
- ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&profile_info.write[ofs], profile_info.size() - ofs);
- }
-
- for (int i = 0; i < ofs; i++) {
- profile_info_ptrs.write[i] = &profile_info.write[i];
- }
-
- SortArray<ScriptLanguage::ProfilingInfo *, ProfileInfoSort> sa;
- sa.sort(profile_info_ptrs.ptrw(), ofs);
-
- int to_send = MIN(ofs, max_frame_functions);
-
- //check signatures first
- uint64_t total_script_time = 0;
-
- for (int i = 0; i < to_send; i++) {
-
- if (!profiler_function_signature_map.has(profile_info_ptrs[i]->signature)) {
-
- int idx = profiler_function_signature_map.size();
- packet_peer_stream->put_var("profile_sig");
- packet_peer_stream->put_var(2);
- packet_peer_stream->put_var(profile_info_ptrs[i]->signature);
- packet_peer_stream->put_var(idx);
-
- profiler_function_signature_map[profile_info_ptrs[i]->signature] = idx;
- }
-
- total_script_time += profile_info_ptrs[i]->self_time;
- }
-
- //send frames then
-
- if (p_for_frame) {
- packet_peer_stream->put_var("profile_frame");
- packet_peer_stream->put_var(8 + profile_frame_data.size() * 2 + to_send * 4);
- } else {
- packet_peer_stream->put_var("profile_total");
- packet_peer_stream->put_var(8 + to_send * 4);
- }
-
- packet_peer_stream->put_var(Engine::get_singleton()->get_frames_drawn()); //total frame time
- packet_peer_stream->put_var(frame_time); //total frame time
- packet_peer_stream->put_var(idle_time); //idle frame time
- packet_peer_stream->put_var(physics_time); //fixed frame time
- packet_peer_stream->put_var(physics_frame_time); //fixed frame time
-
- packet_peer_stream->put_var(USEC_TO_SEC(total_script_time)); //total script execution time
-
- if (p_for_frame) {
-
- packet_peer_stream->put_var(profile_frame_data.size()); //how many profile framedatas to send
- packet_peer_stream->put_var(to_send); //how many script functions to send
- for (int i = 0; i < profile_frame_data.size(); i++) {
-
- packet_peer_stream->put_var(profile_frame_data[i].name);
- packet_peer_stream->put_var(profile_frame_data[i].data);
- }
- } else {
- packet_peer_stream->put_var(0); //how many script functions to send
- packet_peer_stream->put_var(to_send); //how many script functions to send
- }
-
- for (int i = 0; i < to_send; i++) {
-
- int sig_id = -1;
-
- if (profiler_function_signature_map.has(profile_info_ptrs[i]->signature)) {
- sig_id = profiler_function_signature_map[profile_info_ptrs[i]->signature];
- }
-
- packet_peer_stream->put_var(sig_id);
- packet_peer_stream->put_var(profile_info_ptrs[i]->call_count);
- packet_peer_stream->put_var(profile_info_ptrs[i]->total_time / 1000000.0);
- packet_peer_stream->put_var(profile_info_ptrs[i]->self_time / 1000000.0);
- }
-
- if (p_for_frame) {
- profile_frame_data.clear();
- }
-}
-
-void ScriptDebuggerRemote::idle_poll() {
-
- // this function is called every frame, except when there is a debugger break (::debug() in this class)
- // execution stops and remains in the ::debug function
-
- _get_output();
-
- if (requested_quit) {
-
- packet_peer_stream->put_var("kill_me");
- packet_peer_stream->put_var(0);
- requested_quit = false;
- }
-
- if (performance) {
-
- uint64_t pt = OS::get_singleton()->get_ticks_msec();
- if (pt - last_perf_time > 1000) {
-
- last_perf_time = pt;
- int max = performance->get("MONITOR_MAX");
- Array arr;
- arr.resize(max);
- for (int i = 0; i < max; i++) {
- arr[i] = performance->call("get_monitor", i);
- }
- packet_peer_stream->put_var("performance");
- packet_peer_stream->put_var(1);
- packet_peer_stream->put_var(arr);
- }
- }
- if (visual_profiling) {
- Vector<VS::FrameProfileArea> profile_areas = VS::get_singleton()->get_frame_profile();
- if (profile_areas.size()) {
- Vector<String> area_names;
- Vector<real_t> area_times;
- area_names.resize(profile_areas.size());
- area_times.resize(profile_areas.size() * 2);
- {
- String *area_namesw = area_names.ptrw();
- real_t *area_timesw = area_times.ptrw();
-
- for (int i = 0; i < profile_areas.size(); i++) {
- area_namesw[i] = profile_areas[i].name;
- area_timesw[i * 2 + 0] = profile_areas[i].cpu_msec;
- area_timesw[i * 2 + 1] = profile_areas[i].gpu_msec;
- }
- }
- packet_peer_stream->put_var("visual_profile");
- packet_peer_stream->put_var(3);
- packet_peer_stream->put_var(VS::get_singleton()->get_frame_profile_frame());
- packet_peer_stream->put_var(area_names);
- packet_peer_stream->put_var(area_times);
- }
- }
-
- if (profiling) {
-
- if (skip_profile_frame) {
- skip_profile_frame = false;
- } else {
- //send profiling info normally
- _send_profiling_data(true);
- }
- }
-
- if (network_profiling) {
- uint64_t pt = OS::get_singleton()->get_ticks_msec();
- if (pt - last_net_bandwidth_time > 200) {
- last_net_bandwidth_time = pt;
- _send_network_bandwidth_usage();
- }
- if (pt - last_net_prof_time > 100) {
- last_net_prof_time = pt;
- _send_network_profiling_data();
- }
- }
-
- if (reload_all_scripts) {
-
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- ScriptServer::get_language(i)->reload_all_scripts();
- }
- reload_all_scripts = false;
- }
-
- _poll_events();
-}
-
-void ScriptDebuggerRemote::_send_network_profiling_data() {
- ERR_FAIL_COND(multiplayer.is_null());
-
- int n_nodes = multiplayer->get_profiling_frame(&network_profile_info.write[0]);
-
- packet_peer_stream->put_var("network_profile");
- packet_peer_stream->put_var(n_nodes * 6);
- for (int i = 0; i < n_nodes; ++i) {
- packet_peer_stream->put_var(network_profile_info[i].node);
- packet_peer_stream->put_var(network_profile_info[i].node_path);
- packet_peer_stream->put_var(network_profile_info[i].incoming_rpc);
- packet_peer_stream->put_var(network_profile_info[i].incoming_rset);
- packet_peer_stream->put_var(network_profile_info[i].outgoing_rpc);
- packet_peer_stream->put_var(network_profile_info[i].outgoing_rset);
- }
-}
-
-void ScriptDebuggerRemote::_send_network_bandwidth_usage() {
- ERR_FAIL_COND(multiplayer.is_null());
-
- int incoming_bandwidth = multiplayer->get_incoming_bandwidth_usage();
- int outgoing_bandwidth = multiplayer->get_outgoing_bandwidth_usage();
-
- packet_peer_stream->put_var("network_bandwidth");
- packet_peer_stream->put_var(2);
- packet_peer_stream->put_var(incoming_bandwidth);
- packet_peer_stream->put_var(outgoing_bandwidth);
-}
-
-void ScriptDebuggerRemote::send_message(const String &p_message, const Array &p_args) {
-
- mutex->lock();
- if (!locking && tcp_client->is_connected_to_host()) {
-
- if (messages.size() >= max_messages_per_frame) {
- n_messages_dropped++;
- } else {
- Message msg;
- msg.message = p_message;
- msg.data = p_args;
- messages.push_back(msg);
- }
- }
- mutex->unlock();
-}
-
-void ScriptDebuggerRemote::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info) {
-
- OutputError oe;
- oe.error = p_err;
- oe.error_descr = p_descr;
- oe.source_file = p_file;
- oe.source_line = p_line;
- oe.source_func = p_func;
- oe.warning = p_type == ERR_HANDLER_WARNING;
- uint64_t time = OS::get_singleton()->get_ticks_msec();
- oe.hr = time / 3600000;
- oe.min = (time / 60000) % 60;
- oe.sec = (time / 1000) % 60;
- oe.msec = time % 1000;
- Array cstack;
-
- uint64_t ticks = OS::get_singleton()->get_ticks_usec() / 1000;
- msec_count += ticks - last_msec;
- last_msec = ticks;
-
- if (msec_count > 1000) {
- msec_count = 0;
-
- err_count = 0;
- n_errors_dropped = 0;
- warn_count = 0;
- n_warnings_dropped = 0;
- }
-
- cstack.resize(p_stack_info.size() * 3);
- for (int i = 0; i < p_stack_info.size(); i++) {
- cstack[i * 3 + 0] = p_stack_info[i].file;
- cstack[i * 3 + 1] = p_stack_info[i].func;
- cstack[i * 3 + 2] = p_stack_info[i].line;
- }
-
- oe.callstack = cstack;
- if (oe.warning) {
- warn_count++;
- } else {
- err_count++;
- }
-
- mutex->lock();
-
- if (!locking && tcp_client->is_connected_to_host()) {
-
- if (oe.warning) {
- if (warn_count > max_warnings_per_second) {
- n_warnings_dropped++;
- } else {
- errors.push_back(oe);
- }
- } else {
- if (err_count > max_errors_per_second) {
- n_errors_dropped++;
- } else {
- errors.push_back(oe);
- }
- }
- }
-
- mutex->unlock();
-}
-
-void ScriptDebuggerRemote::_print_handler(void *p_this, const String &p_string, bool p_error) {
-
- ScriptDebuggerRemote *sdr = (ScriptDebuggerRemote *)p_this;
-
- uint64_t ticks = OS::get_singleton()->get_ticks_usec() / 1000;
- sdr->msec_count += ticks - sdr->last_msec;
- sdr->last_msec = ticks;
-
- if (sdr->msec_count > 1000) {
- sdr->char_count = 0;
- sdr->msec_count = 0;
- }
-
- String s = p_string;
- int allowed_chars = MIN(MAX(sdr->max_cps - sdr->char_count, 0), s.length());
-
- if (allowed_chars == 0)
- return;
-
- if (allowed_chars < s.length()) {
- s = s.substr(0, allowed_chars);
- }
-
- sdr->char_count += allowed_chars;
- bool overflowed = sdr->char_count >= sdr->max_cps;
-
- sdr->mutex->lock();
- if (!sdr->locking && sdr->tcp_client->is_connected_to_host()) {
-
- if (overflowed)
- s += "[...]";
-
- sdr->output_strings.push_back(s);
-
- if (overflowed) {
- sdr->output_strings.push_back("[output overflow, print less text!]");
- }
- }
- sdr->mutex->unlock();
-}
-
-void ScriptDebuggerRemote::request_quit() {
-
- requested_quit = true;
-}
-
-void ScriptDebuggerRemote::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
- multiplayer = p_multiplayer;
-}
-
-bool ScriptDebuggerRemote::is_profiling() const {
-
- return profiling;
-}
-void ScriptDebuggerRemote::add_profiling_frame_data(const StringName &p_name, const Array &p_data) {
-
- int idx = -1;
- for (int i = 0; i < profile_frame_data.size(); i++) {
- if (profile_frame_data[i].name == p_name) {
- idx = i;
- break;
- }
- }
-
- FrameData fd;
- fd.name = p_name;
- fd.data = p_data;
-
- if (idx == -1) {
- profile_frame_data.push_back(fd);
- } else {
- profile_frame_data.write[idx] = fd;
- }
-}
-
-void ScriptDebuggerRemote::profiling_start() {
- //ignores this, uses it via connection
-}
-
-void ScriptDebuggerRemote::profiling_end() {
- //ignores this, uses it via connection
-}
-
-void ScriptDebuggerRemote::profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
-
- frame_time = p_frame_time;
- idle_time = p_idle_time;
- physics_time = p_physics_time;
- physics_frame_time = p_physics_frame_time;
-}
-
-void ScriptDebuggerRemote::set_skip_breakpoints(bool p_skip_breakpoints) {
- skip_breakpoints = p_skip_breakpoints;
-}
-
-ScriptDebuggerRemote::ResourceUsageFunc ScriptDebuggerRemote::resource_usage_func = NULL;
-
-ScriptDebuggerRemote::ScriptDebuggerRemote() :
- profiling(false),
- visual_profiling(false),
- network_profiling(false),
- max_frame_functions(16),
- skip_profile_frame(false),
- reload_all_scripts(false),
- tcp_client(Ref<StreamPeerTCP>(memnew(StreamPeerTCP))),
- packet_peer_stream(Ref<PacketPeerStream>(memnew(PacketPeerStream))),
- last_perf_time(0),
- last_net_prof_time(0),
- last_net_bandwidth_time(0),
- performance(Engine::get_singleton()->get_singleton_object("Performance")),
- requested_quit(false),
- mutex(Mutex::create()),
- max_messages_per_frame(GLOBAL_GET("network/limits/debugger_stdout/max_messages_per_frame")),
- n_messages_dropped(0),
- max_errors_per_second(GLOBAL_GET("network/limits/debugger_stdout/max_errors_per_second")),
- max_warnings_per_second(GLOBAL_GET("network/limits/debugger_stdout/max_warnings_per_second")),
- n_errors_dropped(0),
- max_cps(GLOBAL_GET("network/limits/debugger_stdout/max_chars_per_second")),
- char_count(0),
- err_count(0),
- warn_count(0),
- last_msec(0),
- msec_count(0),
- locking(false),
- poll_every(0),
- scene_tree(NULL) {
-
- packet_peer_stream->set_stream_peer(tcp_client);
- packet_peer_stream->set_output_buffer_max_size((1024 * 1024 * 8) - 4); // 8 MiB should be way more than enough, minus 4 bytes for separator.
-
- phl.printfunc = _print_handler;
- phl.userdata = this;
- add_print_handler(&phl);
-
- eh.errfunc = _err_handler;
- eh.userdata = this;
- add_error_handler(&eh);
-
- profile_info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
- network_profile_info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
- profile_info_ptrs.resize(profile_info.size());
-}
-
-ScriptDebuggerRemote::~ScriptDebuggerRemote() {
-
- remove_print_handler(&phl);
- remove_error_handler(&eh);
- memdelete(mutex);
-}
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 2c15ac6aae..6556dd6d6c 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -37,6 +37,7 @@
#include "core/message_queue.h"
#include "core/print_string.h"
#include "instance_placeholder.h"
+#include "scene/debugger/scene_debugger.h"
#include "scene/resources/packed_scene.h"
#include "scene/scene_string_names.h"
#include "viewport.h"
@@ -244,11 +245,7 @@ void Node::_propagate_enter_tree() {
data.blocked--;
#ifdef DEBUG_ENABLED
-
- if (ScriptDebugger::get_singleton() && data.filename != String()) {
- //used for live edit
- data.tree->live_scene_edit_cache[data.filename].insert(this);
- }
+ SceneDebugger::add_to_cache(data.filename, this);
#endif
// enter groups
}
@@ -268,26 +265,7 @@ void Node::_propagate_exit_tree() {
//block while removing children
#ifdef DEBUG_ENABLED
-
- if (ScriptDebugger::get_singleton() && data.filename != String()) {
- //used for live edit
- Map<String, Set<Node *> >::Element *E = data.tree->live_scene_edit_cache.find(data.filename);
- if (E) {
- E->get().erase(this);
- if (E->get().size() == 0) {
- data.tree->live_scene_edit_cache.erase(E);
- }
- }
-
- Map<Node *, Map<ObjectID, Node *> >::Element *F = data.tree->live_edit_remove_list.find(this);
- if (F) {
- for (Map<ObjectID, Node *>::Element *G = F->get().front(); G; G = G->next()) {
-
- memdelete(G->get());
- }
- data.tree->live_edit_remove_list.erase(F);
- }
- }
+ SceneDebugger::remove_from_cache(data.filename, this);
#endif
data.blocked++;
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index f558670693..9f9dace9f9 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -38,9 +38,10 @@
#include "core/os/os.h"
#include "core/print_string.h"
#include "core/project_settings.h"
+#include "core/script_debugger_remote.h"
#include "main/input_default.h"
#include "node.h"
-#include "scene/debugger/script_debugger_remote.h"
+#include "scene/debugger/scene_debugger.h"
#include "scene/resources/dynamic_font.h"
#include "scene/resources/material.h"
#include "scene/resources/mesh.h"
@@ -1329,380 +1330,6 @@ void SceneTree::add_current_scene(Node *p_current) {
root->add_child(p_current);
}
-#ifdef DEBUG_ENABLED
-
-static void _fill_array(Node *p_node, Array &array, int p_level) {
-
- array.push_back(p_node->get_child_count());
- array.push_back(p_node->get_name());
- array.push_back(p_node->get_class());
- array.push_back(p_node->get_instance_id());
- for (int i = 0; i < p_node->get_child_count(); i++) {
-
- _fill_array(p_node->get_child(i), array, p_level + 1);
- }
-}
-
-void SceneTree::_debugger_request_tree() {
-
- Array arr;
- _fill_array(root, arr, 0);
- ScriptDebugger::get_singleton()->send_message("scene_tree", arr);
-}
-
-void SceneTree::_live_edit_node_path_func(const NodePath &p_path, int p_id) {
-
- live_edit_node_path_cache[p_id] = p_path;
-}
-
-void SceneTree::_live_edit_res_path_func(const String &p_path, int p_id) {
-
- live_edit_resource_cache[p_id] = p_path;
-}
-
-void SceneTree::_live_edit_node_set_func(int p_id, const StringName &p_prop, const Variant &p_value) {
-
- if (!live_edit_node_path_cache.has(p_id))
- return;
-
- NodePath np = live_edit_node_path_cache[p_id];
- Node *base = NULL;
- if (root->has_node(live_edit_root))
- base = root->get_node(live_edit_root);
-
- Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
- if (!E)
- return; //scene not editable
-
- for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
-
- Node *n = F->get();
-
- if (base && !base->is_a_parent_of(n))
- continue;
-
- if (!n->has_node(np))
- continue;
- Node *n2 = n->get_node(np);
-
- n2->set(p_prop, p_value);
- }
-}
-
-void SceneTree::_live_edit_node_set_res_func(int p_id, const StringName &p_prop, const String &p_value) {
-
- RES r = ResourceLoader::load(p_value);
- if (!r.is_valid())
- return;
- _live_edit_node_set_func(p_id, p_prop, r);
-}
-void SceneTree::_live_edit_node_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE) {
-
- if (!live_edit_node_path_cache.has(p_id))
- return;
-
- NodePath np = live_edit_node_path_cache[p_id];
- Node *base = NULL;
- if (root->has_node(live_edit_root))
- base = root->get_node(live_edit_root);
-
- Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
- if (!E)
- return; //scene not editable
-
- for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
-
- Node *n = F->get();
-
- if (base && !base->is_a_parent_of(n))
- continue;
-
- if (!n->has_node(np))
- continue;
- Node *n2 = n->get_node(np);
-
- n2->call(p_method, VARIANT_ARG_PASS);
- }
-}
-void SceneTree::_live_edit_res_set_func(int p_id, const StringName &p_prop, const Variant &p_value) {
-
- if (!live_edit_resource_cache.has(p_id))
- return;
-
- String resp = live_edit_resource_cache[p_id];
-
- if (!ResourceCache::has(resp))
- return;
-
- RES r = ResourceCache::get(resp);
- if (!r.is_valid())
- return;
-
- r->set(p_prop, p_value);
-}
-void SceneTree::_live_edit_res_set_res_func(int p_id, const StringName &p_prop, const String &p_value) {
-
- RES r = ResourceLoader::load(p_value);
- if (!r.is_valid())
- return;
- _live_edit_res_set_func(p_id, p_prop, r);
-}
-void SceneTree::_live_edit_res_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE) {
-
- if (!live_edit_resource_cache.has(p_id))
- return;
-
- String resp = live_edit_resource_cache[p_id];
-
- if (!ResourceCache::has(resp))
- return;
-
- RES r = ResourceCache::get(resp);
- if (!r.is_valid())
- return;
-
- r->call(p_method, VARIANT_ARG_PASS);
-}
-
-void SceneTree::_live_edit_root_func(const NodePath &p_scene_path, const String &p_scene_from) {
-
- live_edit_root = p_scene_path;
- live_edit_scene = p_scene_from;
-}
-
-void SceneTree::_live_edit_create_node_func(const NodePath &p_parent, const String &p_type, const String &p_name) {
-
- Node *base = NULL;
- if (root->has_node(live_edit_root))
- base = root->get_node(live_edit_root);
-
- Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
- if (!E)
- return; //scene not editable
-
- for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
-
- Node *n = F->get();
-
- if (base && !base->is_a_parent_of(n))
- continue;
-
- if (!n->has_node(p_parent))
- continue;
- Node *n2 = n->get_node(p_parent);
-
- Node *no = Object::cast_to<Node>(ClassDB::instance(p_type));
- if (!no) {
- continue;
- }
-
- no->set_name(p_name);
- n2->add_child(no);
- }
-}
-void SceneTree::_live_edit_instance_node_func(const NodePath &p_parent, const String &p_path, const String &p_name) {
-
- Ref<PackedScene> ps = ResourceLoader::load(p_path);
-
- if (!ps.is_valid())
- return;
-
- Node *base = NULL;
- if (root->has_node(live_edit_root))
- base = root->get_node(live_edit_root);
-
- Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
- if (!E)
- return; //scene not editable
-
- for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
-
- Node *n = F->get();
-
- if (base && !base->is_a_parent_of(n))
- continue;
-
- if (!n->has_node(p_parent))
- continue;
- Node *n2 = n->get_node(p_parent);
-
- Node *no = ps->instance();
- if (!no) {
- continue;
- }
-
- no->set_name(p_name);
- n2->add_child(no);
- }
-}
-void SceneTree::_live_edit_remove_node_func(const NodePath &p_at) {
-
- Node *base = NULL;
- if (root->has_node(live_edit_root))
- base = root->get_node(live_edit_root);
-
- Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
- if (!E)
- return; //scene not editable
-
- for (Set<Node *>::Element *F = E->get().front(); F;) {
-
- Set<Node *>::Element *N = F->next();
-
- Node *n = F->get();
-
- if (base && !base->is_a_parent_of(n))
- continue;
-
- if (!n->has_node(p_at))
- continue;
- Node *n2 = n->get_node(p_at);
-
- memdelete(n2);
-
- F = N;
- }
-}
-void SceneTree::_live_edit_remove_and_keep_node_func(const NodePath &p_at, ObjectID p_keep_id) {
-
- Node *base = NULL;
- if (root->has_node(live_edit_root))
- base = root->get_node(live_edit_root);
-
- Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
- if (!E)
- return; //scene not editable
-
- for (Set<Node *>::Element *F = E->get().front(); F;) {
-
- Set<Node *>::Element *N = F->next();
-
- Node *n = F->get();
-
- if (base && !base->is_a_parent_of(n))
- continue;
-
- if (!n->has_node(p_at))
- continue;
-
- Node *n2 = n->get_node(p_at);
-
- n2->get_parent()->remove_child(n2);
-
- live_edit_remove_list[n][p_keep_id] = n2;
-
- F = N;
- }
-}
-void SceneTree::_live_edit_restore_node_func(ObjectID p_id, const NodePath &p_at, int p_at_pos) {
-
- Node *base = NULL;
- if (root->has_node(live_edit_root))
- base = root->get_node(live_edit_root);
-
- Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
- if (!E)
- return; //scene not editable
-
- for (Set<Node *>::Element *F = E->get().front(); F;) {
-
- Set<Node *>::Element *N = F->next();
-
- Node *n = F->get();
-
- if (base && !base->is_a_parent_of(n))
- continue;
-
- if (!n->has_node(p_at))
- continue;
- Node *n2 = n->get_node(p_at);
-
- Map<Node *, Map<ObjectID, Node *> >::Element *EN = live_edit_remove_list.find(n);
-
- if (!EN)
- continue;
-
- Map<ObjectID, Node *>::Element *FN = EN->get().find(p_id);
-
- if (!FN)
- continue;
- n2->add_child(FN->get());
-
- EN->get().erase(FN);
-
- if (EN->get().size() == 0) {
- live_edit_remove_list.erase(EN);
- }
-
- F = N;
- }
-}
-void SceneTree::_live_edit_duplicate_node_func(const NodePath &p_at, const String &p_new_name) {
-
- Node *base = NULL;
- if (root->has_node(live_edit_root))
- base = root->get_node(live_edit_root);
-
- Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
- if (!E)
- return; //scene not editable
-
- for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
-
- Node *n = F->get();
-
- if (base && !base->is_a_parent_of(n))
- continue;
-
- if (!n->has_node(p_at))
- continue;
- Node *n2 = n->get_node(p_at);
-
- Node *dup = n2->duplicate(Node::DUPLICATE_SIGNALS | Node::DUPLICATE_GROUPS | Node::DUPLICATE_SCRIPTS);
-
- if (!dup)
- continue;
-
- dup->set_name(p_new_name);
- n2->get_parent()->add_child(dup);
- }
-}
-void SceneTree::_live_edit_reparent_node_func(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos) {
-
- Node *base = NULL;
- if (root->has_node(live_edit_root))
- base = root->get_node(live_edit_root);
-
- Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
- if (!E)
- return; //scene not editable
-
- for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
-
- Node *n = F->get();
-
- if (base && !base->is_a_parent_of(n))
- continue;
-
- if (!n->has_node(p_at))
- continue;
- Node *nfrom = n->get_node(p_at);
-
- if (!n->has_node(p_new_place))
- continue;
- Node *nto = n->get_node(p_new_place);
-
- nfrom->get_parent()->remove_child(nfrom);
- nfrom->set_name(p_new_name);
-
- nto->add_child(nfrom);
- if (p_at_pos >= 0)
- nto->move_child(nfrom, p_at_pos);
- }
-}
-
-#endif
-
void SceneTree::drop_files(const Vector<String> &p_files, int p_from_screen) {
emit_signal("files_dropped", p_files, p_from_screen);
@@ -2116,11 +1743,6 @@ SceneTree::SceneTree() {
_update_root_rect();
if (ScriptDebugger::get_singleton()) {
- if (ScriptDebugger::get_singleton()->is_remote()) {
- ScriptDebuggerRemote *remote_debugger = static_cast<ScriptDebuggerRemote *>(ScriptDebugger::get_singleton());
-
- remote_debugger->set_scene_tree(this);
- }
ScriptDebugger::get_singleton()->set_multiplayer(multiplayer);
}
@@ -2129,12 +1751,6 @@ SceneTree::SceneTree() {
#ifdef TOOLS_ENABLED
edited_scene_root = NULL;
#endif
-
-#ifdef DEBUG_ENABLED
-
- live_edit_root = NodePath("/root");
-
-#endif
}
SceneTree::~SceneTree() {
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index 80f0da66e2..1bef0d3131 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -44,6 +44,7 @@ class Node;
class Viewport;
class Material;
class Mesh;
+class SceneDebugger;
class SceneTreeTimer : public Reference {
GDCLASS(SceneTreeTimer, Reference);
@@ -219,39 +220,8 @@ private:
SelfList<Node>::List xform_change_list;
- friend class ScriptDebuggerRemote;
-#ifdef DEBUG_ENABLED
-
- Map<int, NodePath> live_edit_node_path_cache;
- Map<int, String> live_edit_resource_cache;
-
- NodePath live_edit_root;
- String live_edit_scene;
-
- Map<String, Set<Node *> > live_scene_edit_cache;
- Map<Node *, Map<ObjectID, Node *> > live_edit_remove_list;
-
- void _debugger_request_tree();
-
- void _live_edit_node_path_func(const NodePath &p_path, int p_id);
- void _live_edit_res_path_func(const String &p_path, int p_id);
-
- void _live_edit_node_set_func(int p_id, const StringName &p_prop, const Variant &p_value);
- void _live_edit_node_set_res_func(int p_id, const StringName &p_prop, const String &p_value);
- void _live_edit_node_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE);
- void _live_edit_res_set_func(int p_id, const StringName &p_prop, const Variant &p_value);
- void _live_edit_res_set_res_func(int p_id, const StringName &p_prop, const String &p_value);
- void _live_edit_res_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE);
- void _live_edit_root_func(const NodePath &p_scene_path, const String &p_scene_from);
-
- void _live_edit_create_node_func(const NodePath &p_parent, const String &p_type, const String &p_name);
- void _live_edit_instance_node_func(const NodePath &p_parent, const String &p_path, const String &p_name);
- void _live_edit_remove_node_func(const NodePath &p_at);
- void _live_edit_remove_and_keep_node_func(const NodePath &p_at, ObjectID p_keep_id);
- void _live_edit_restore_node_func(ObjectID p_id, const NodePath &p_at, int p_at_pos);
- void _live_edit_duplicate_node_func(const NodePath &p_at, const String &p_new_name);
- void _live_edit_reparent_node_func(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos);
-
+#ifdef DEBUG_ENABLED // No live editor in release build.
+ friend class LiveEditor;
#endif
enum {
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 40f24ece87..7ffead9b86 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -76,6 +76,7 @@
#include "scene/animation/root_motion_view.h"
#include "scene/animation/tween.h"
#include "scene/audio/audio_stream_player.h"
+#include "scene/debugger/scene_debugger.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/center_container.h"
@@ -777,10 +778,12 @@ void register_scene_types() {
ERR_PRINT("Error loading custom theme '" + theme_path + "'");
}
}
+ SceneDebugger::initialize();
}
void unregister_scene_types() {
+ SceneDebugger::deinitialize();
clear_default_theme();
ResourceLoader::remove_resource_format_loader(resource_loader_dynamic_font);
diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp
index 25d122604a..c9f5277a4d 100644
--- a/servers/register_server_types.cpp
+++ b/servers/register_server_types.cpp
@@ -56,6 +56,7 @@
#include "audio_server.h"
#include "camera/camera_feed.h"
#include "camera_server.h"
+#include "core/script_debugger_remote.h"
#include "navigation_2d_server.h"
#include "navigation_server.h"
#include "physics/physics_server_sw.h"
@@ -63,18 +64,17 @@
#include "physics_2d/physics_2d_server_wrap_mt.h"
#include "physics_2d_server.h"
#include "physics_server.h"
-#include "scene/debugger/script_debugger_remote.h"
#include "visual/shader_types.h"
#include "visual_server.h"
-static void _debugger_get_resource_usage(List<ScriptDebuggerRemote::ResourceUsage> *r_usage) {
+static void _debugger_get_resource_usage(ScriptDebuggerRemote::ResourceUsage *r_usage) {
List<VS::TextureInfo> tinfo;
VS::get_singleton()->texture_debug_usage(&tinfo);
for (List<VS::TextureInfo>::Element *E = tinfo.front(); E; E = E->next()) {
- ScriptDebuggerRemote::ResourceUsage usage;
+ ScriptDebuggerRemote::ResourceInfo usage;
usage.path = E->get().path;
usage.vram = E->get().bytes;
usage.id = E->get().texture;
@@ -84,7 +84,7 @@ static void _debugger_get_resource_usage(List<ScriptDebuggerRemote::ResourceUsag
} else {
usage.format = itos(E->get().width) + "x" + itos(E->get().height) + "x" + itos(E->get().depth) + " " + Image::get_format_name(E->get().format);
}
- r_usage->push_back(usage);
+ r_usage->infos.push_back(usage);
}
}