2
0
Эх сурвалжийг харах

Merge pull request #36751 from Faless/debugger/threads_and_profilers

ScriptDebugger refactor, threading, profilers.
Rémi Verschelde 5 жил өмнө
parent
commit
478337c412
65 өөрчлөгдсөн 3161 нэмэгдсэн , 2400 устгасан
  1. 1 0
      core/SCsub
  2. 5 0
      core/debugger/SCsub
  3. 329 0
      core/debugger/debugger_marshalls.cpp
  4. 175 0
      core/debugger/debugger_marshalls.h
  5. 186 0
      core/debugger/engine_debugger.cpp
  6. 130 0
      core/debugger/engine_debugger.h
  7. 142 150
      core/debugger/local_debugger.cpp
  8. 16 23
      core/debugger/local_debugger.h
  9. 935 0
      core/debugger/remote_debugger.cpp
  10. 114 0
      core/debugger/remote_debugger.h
  11. 242 0
      core/debugger/remote_debugger_peer.cpp
  12. 94 0
      core/debugger/remote_debugger_peer.h
  13. 123 0
      core/debugger/script_debugger.cpp
  14. 80 0
      core/debugger/script_debugger.h
  15. 29 122
      core/io/multiplayer_api.cpp
  16. 0 34
      core/io/multiplayer_api.h
  17. 5 0
      core/io/stream_peer_tcp.cpp
  18. 3 0
      core/io/stream_peer_tcp.h
  19. 0 1143
      core/script_debugger_remote.cpp
  20. 0 316
      core/script_debugger_remote.h
  21. 4 85
      core/script_language.cpp
  22. 5 58
      core/script_language.h
  23. 2 1
      core/variant.cpp
  24. 2 2
      core/variant_call.cpp
  25. 10 10
      core/variant_op.cpp
  26. 7 7
      doc/classes/ProjectSettings.xml
  27. 6 4
      drivers/unix/os_unix.cpp
  28. 2 2
      editor/debugger/editor_debugger_inspector.cpp
  29. 36 9
      editor/debugger/editor_debugger_node.cpp
  30. 26 13
      editor/debugger/editor_debugger_node.h
  31. 90 0
      editor/debugger/editor_debugger_server.cpp
  32. 49 0
      editor/debugger/editor_debugger_server.h
  33. 4 4
      editor/debugger/editor_network_profiler.cpp
  34. 3 2
      editor/debugger/editor_network_profiler.h
  35. 2 2
      editor/debugger/editor_profiler.cpp
  36. 0 0
      editor/debugger/editor_profiler.h
  37. 2 2
      editor/debugger/editor_visual_profiler.cpp
  38. 0 0
      editor/debugger/editor_visual_profiler.h
  39. 138 182
      editor/debugger/script_editor_debugger.cpp
  40. 18 27
      editor/debugger/script_editor_debugger.h
  41. 3 3
      editor/plugins/canvas_item_editor_plugin.cpp
  42. 0 1
      editor/plugins/script_editor_plugin.cpp
  43. 5 5
      editor/plugins/spatial_editor_plugin.cpp
  44. 24 75
      main/main.cpp
  45. 1 1
      modules/gdnative/pluginscript/pluginscript_script.cpp
  46. 6 6
      modules/gdscript/gdscript.cpp
  47. 8 6
      modules/gdscript/gdscript.h
  48. 2 2
      modules/gdscript/gdscript_compiler.cpp
  49. 4 4
      modules/gdscript/gdscript_editor.cpp
  50. 11 11
      modules/gdscript/gdscript_function.cpp
  51. 7 5
      modules/mono/csharp_script.cpp
  52. 3 2
      modules/mono/mono_gd/gd_mono.cpp
  53. 5 2
      modules/mono/mono_gd/gd_mono_internals.cpp
  54. 5 3
      modules/mono/mono_gd/gd_mono_utils.cpp
  55. 15 15
      modules/visual_script/visual_script.cpp
  56. 8 6
      modules/visual_script/visual_script.h
  57. 5 4
      platform/windows/os_windows.cpp
  58. 13 8
      scene/debugger/scene_debugger.cpp
  59. 3 2
      scene/debugger/scene_debugger.h
  60. 3 7
      scene/main/scene_tree.cpp
  61. 3 2
      scene/main/viewport.cpp
  62. 4 2
      servers/audio_server.cpp
  63. 4 3
      servers/physics/physics_server_sw.cpp
  64. 4 3
      servers/physics_2d/physics_2d_server_sw.cpp
  65. 0 24
      servers/register_server_types.cpp

+ 1 - 0
core/SCsub

@@ -165,6 +165,7 @@ SConscript('os/SCsub')
 SConscript('math/SCsub')
 SConscript('crypto/SCsub')
 SConscript('io/SCsub')
+SConscript('debugger/SCsub')
 SConscript('bind/SCsub')
 
 

+ 5 - 0
core/debugger/SCsub

@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+Import('env')
+
+env.add_source_files(env.core_sources, "*.cpp")

+ 329 - 0
core/debugger/debugger_marshalls.cpp

@@ -0,0 +1,329 @@
+/*************************************************************************/
+/*  debugger_marshalls.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_marshalls.h"
+
+#include "core/io/marshalls.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 long. Exptected size: " + itos(expected) + ", actual size: " + itos(arr.size()))
+
+Array DebuggerMarshalls::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 DebuggerMarshalls::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 DebuggerMarshalls::ScriptFunctionSignature::serialize() {
+	Array arr;
+	arr.push_back(name);
+	arr.push_back(id);
+	return arr;
+}
+
+bool DebuggerMarshalls::ScriptFunctionSignature::deserialize(const Array &p_arr) {
+	CHECK_SIZE(p_arr, 2, "ScriptFunctionSignature");
+	name = p_arr[0];
+	id = p_arr[1];
+	CHECK_END(p_arr, 2, "ScriptFunctionSignature");
+	return true;
+}
+
+Array DebuggerMarshalls::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 DebuggerMarshalls::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;
+}
+
+Array DebuggerMarshalls::ServersProfilerFrame::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(script_time);
+
+	arr.push_back(servers.size());
+	for (int i = 0; i < servers.size(); i++) {
+		ServerInfo &s = servers[i];
+		arr.push_back(s.name);
+		arr.push_back(s.functions.size() * 2);
+		for (int j = 0; j < s.functions.size(); j++) {
+			ServerFunctionInfo &f = s.functions[j];
+			arr.push_back(f.name);
+			arr.push_back(f.time);
+		}
+	}
+
+	arr.push_back(script_functions.size() * 4);
+	for (int i = 0; i < script_functions.size(); i++) {
+		arr.push_back(script_functions[i].sig_id);
+		arr.push_back(script_functions[i].call_count);
+		arr.push_back(script_functions[i].self_time);
+		arr.push_back(script_functions[i].total_time);
+	}
+	return arr;
+}
+
+bool DebuggerMarshalls::ServersProfilerFrame::deserialize(const Array &p_arr) {
+	CHECK_SIZE(p_arr, 7, "ServersProfilerFrame");
+	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];
+	int servers_size = p_arr[6];
+	int idx = 7;
+	while (servers_size) {
+		CHECK_SIZE(p_arr, idx + 2, "ServersProfilerFrame");
+		servers_size--;
+		ServerInfo si;
+		si.name = p_arr[idx];
+		int sub_data_size = p_arr[idx + 1];
+		idx += 2;
+		CHECK_SIZE(p_arr, idx + sub_data_size, "ServersProfilerFrame");
+		for (int j = 0; j < sub_data_size / 2; j++) {
+			ServerFunctionInfo sf;
+			sf.name = p_arr[idx];
+			sf.time = p_arr[idx + 1];
+			idx += 2;
+			si.functions.push_back(sf);
+		}
+		servers.push_back(si);
+	}
+	CHECK_SIZE(p_arr, idx + 3, "ServersProfilerFrame");
+	int func_size = p_arr[idx];
+	idx += 1;
+	CHECK_SIZE(p_arr, idx + func_size, "ServersProfilerFrame");
+	for (int i = 0; i < func_size / 4; i++) {
+		ScriptFunctionInfo fi;
+		fi.sig_id = p_arr[idx];
+		fi.call_count = p_arr[idx + 1];
+		fi.self_time = p_arr[idx + 2];
+		fi.total_time = p_arr[idx + 3];
+		script_functions.push_back(fi);
+		idx += 4;
+	}
+	CHECK_END(p_arr, idx, "ServersProfilerFrame");
+	return true;
+}
+
+Array DebuggerMarshalls::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 DebuggerMarshalls::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 DebuggerMarshalls::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 DebuggerMarshalls::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 DebuggerMarshalls::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 DebuggerMarshalls::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 DebuggerMarshalls::VisualProfilerFrame::serialize() {
+	Array arr;
+	arr.push_back(frame_number);
+	arr.push_back(areas.size() * 3);
+	for (int i = 0; i < areas.size(); i++) {
+		arr.push_back(areas[i].name);
+		arr.push_back(areas[i].cpu_msec);
+		arr.push_back(areas[i].gpu_msec);
+	}
+	return arr;
+}
+
+bool DebuggerMarshalls::VisualProfilerFrame::deserialize(const Array &p_arr) {
+	CHECK_SIZE(p_arr, 2, "VisualProfilerFrame");
+	frame_number = p_arr[0];
+	int size = p_arr[1];
+	CHECK_SIZE(p_arr, size, "VisualProfilerFrame");
+	int idx = 2;
+	areas.resize(size / 3);
+	VS::FrameProfileArea *w = areas.ptrw();
+	for (int i = 0; i < size / 3; i++) {
+		w[i].name = p_arr[idx];
+		w[i].cpu_msec = p_arr[idx + 1];
+		w[i].gpu_msec = p_arr[idx + 2];
+		idx += 3;
+	}
+	CHECK_END(p_arr, idx, "VisualProfilerFrame");
+	return true;
+}

+ 175 - 0
core/debugger/debugger_marshalls.h

@@ -0,0 +1,175 @@
+/*************************************************************************/
+/*  debugger_marshalls.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_MARSHARLLS_H
+#define DEBUGGER_MARSHARLLS_H
+
+#include "core/script_language.h"
+#include "servers/visual_server.h"
+
+struct DebuggerMarshalls {
+
+	// Memory usage
+	struct ResourceInfo {
+		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;
+		}
+	};
+
+	struct ResourceUsage {
+		List<ResourceInfo> infos;
+
+		Array serialize();
+		bool deserialize(const Array &p_arr);
+	};
+
+	// Network profiler
+	struct MultiplayerNodeInfo {
+		ObjectID node;
+		String node_path;
+		int incoming_rpc = 0;
+		int incoming_rset = 0;
+		int outgoing_rpc = 0;
+		int outgoing_rset = 0;
+	};
+
+	struct NetworkProfilerFrame {
+		Vector<MultiplayerNodeInfo> infos;
+
+		Array serialize();
+		bool deserialize(const Array &p_arr);
+	};
+
+	// Script Profiler
+	class ScriptFunctionSignature {
+	public:
+		StringName name;
+		int id = -1;
+
+		Array serialize();
+		bool deserialize(const Array &p_arr);
+	};
+
+	struct ScriptFunctionInfo {
+		StringName name;
+		int sig_id = -1;
+		int call_count = 0;
+		float self_time = 0;
+		float total_time = 0;
+	};
+
+	// Servers profiler
+	struct ServerFunctionInfo {
+		StringName name;
+		float time = 0;
+	};
+
+	struct ServerInfo {
+		StringName name;
+		List<ServerFunctionInfo> functions;
+	};
+
+	struct ServersProfilerFrame {
+		int frame_number = 0;
+		float frame_time = 0;
+		float idle_time = 0;
+		float physics_time = 0;
+		float physics_frame_time = 0;
+		float script_time = 0;
+		List<ServerInfo> servers;
+		Vector<ScriptFunctionInfo> script_functions;
+
+		Array serialize();
+		bool deserialize(const Array &p_arr);
+	};
+
+	struct ScriptStackVariable {
+		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);
+	};
+
+	struct ScriptStackDump {
+		List<ScriptLanguage::StackInfo> frames;
+		ScriptStackDump() {}
+
+		Array serialize();
+		bool deserialize(const Array &p_arr);
+	};
+
+	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;
+		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);
+	};
+
+	// Visual Profiler
+	struct VisualProfilerFrame {
+		uint64_t frame_number;
+		Vector<VS::FrameProfileArea> areas;
+
+		Array serialize();
+		bool deserialize(const Array &p_arr);
+	};
+};
+
+#endif // DEBUGGER_MARSHARLLS_H

+ 186 - 0
core/debugger/engine_debugger.cpp

@@ -0,0 +1,186 @@
+/*************************************************************************/
+/*  engine_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 "engine_debugger.h"
+
+#include "core/debugger/local_debugger.h"
+#include "core/debugger/remote_debugger.h"
+#include "core/debugger/script_debugger.h"
+#include "core/os/os.h"
+
+EngineDebugger *EngineDebugger::singleton = NULL;
+ScriptDebugger *EngineDebugger::script_debugger = NULL;
+
+Map<StringName, EngineDebugger::Profiler> EngineDebugger::profilers;
+Map<StringName, EngineDebugger::Capture> EngineDebugger::captures;
+
+void EngineDebugger::register_profiler(const StringName &p_name, const Profiler &p_func) {
+	ERR_FAIL_COND_MSG(profilers.has(p_name), "Profiler already registered: " + p_name);
+	profilers.insert(p_name, p_func);
+}
+
+void EngineDebugger::unregister_profiler(const StringName &p_name) {
+	ERR_FAIL_COND_MSG(!profilers.has(p_name), "Profiler not registered: " + p_name);
+	Profiler &p = profilers[p_name];
+	if (p.active && p.toggle) {
+		p.toggle(p.data, false, Array());
+		p.active = false;
+	}
+	profilers.erase(p_name);
+}
+
+void EngineDebugger::register_message_capture(const StringName &p_name, Capture p_func) {
+	ERR_FAIL_COND_MSG(captures.has(p_name), "Capture already registered: " + p_name);
+	captures.insert(p_name, p_func);
+}
+
+void EngineDebugger::unregister_message_capture(const StringName &p_name) {
+	ERR_FAIL_COND_MSG(!captures.has(p_name), "Capture not registered: " + p_name);
+	captures.erase(p_name);
+}
+
+void EngineDebugger::profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts) {
+	ERR_FAIL_COND_MSG(!profilers.has(p_name), "Can't change profiler state, no profiler: " + p_name);
+	Profiler &p = profilers[p_name];
+	if (p.toggle) {
+		p.toggle(p.data, p_enabled, p_opts);
+	}
+	p.active = p_enabled;
+}
+
+void EngineDebugger::profiler_add_frame_data(const StringName &p_name, const Array &p_data) {
+	ERR_FAIL_COND_MSG(!profilers.has(p_name), "Can't add frame data, no profiler: " + p_name);
+	Profiler &p = profilers[p_name];
+	if (p.add) {
+		p.add(p.data, p_data);
+	}
+}
+
+bool EngineDebugger::is_profiling(const StringName &p_name) {
+	return profilers.has(p_name) && profilers[p_name].active;
+}
+
+bool EngineDebugger::has_profiler(const StringName &p_name) {
+	return profilers.has(p_name);
+}
+
+bool EngineDebugger::has_capture(const StringName &p_name) {
+	return captures.has(p_name);
+}
+
+Error EngineDebugger::capture_parse(const StringName &p_name, const String &p_msg, const Array &p_args, bool &r_captured) {
+	r_captured = false;
+	ERR_FAIL_COND_V_MSG(!captures.has(p_name), ERR_UNCONFIGURED, "Capture not registered: " + p_name);
+	const Capture &cap = captures[p_name];
+	return cap.capture(cap.data, p_msg, p_args, r_captured);
+}
+
+void EngineDebugger::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(false);
+	poll_every++;
+}
+
+void EngineDebugger::iteration(uint64_t p_frame_ticks, uint64_t p_idle_ticks, uint64_t p_physics_ticks, float p_physics_frame_time) {
+	frame_time = USEC_TO_SEC(p_frame_ticks);
+	idle_time = USEC_TO_SEC(p_idle_ticks);
+	physics_time = USEC_TO_SEC(p_physics_ticks);
+	physics_frame_time = p_physics_frame_time;
+	// Notify tick to running profilers
+	for (Map<StringName, Profiler>::Element *E = profilers.front(); E; E = E->next()) {
+		Profiler &p = E->get();
+		if (!p.active || !p.tick)
+			continue;
+		p.tick(p.data, frame_time, idle_time, physics_time, physics_frame_time);
+	}
+	singleton->poll_events(true);
+}
+
+void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, Vector<String> p_breakpoints) {
+	if (p_uri.empty())
+		return;
+	if (p_uri == "local://") {
+		singleton = memnew(LocalDebugger);
+		script_debugger = memnew(ScriptDebugger);
+		// Tell the OS that we want to handle termination signals.
+		OS::get_singleton()->initialize_debugging();
+	} else {
+		singleton = RemoteDebugger::create_for_uri(p_uri);
+		if (!singleton)
+			return;
+		script_debugger = memnew(ScriptDebugger);
+		// Notify editor of our pid (to allow focus stealing).
+		Array msg;
+		msg.push_back(OS::get_singleton()->get_process_id());
+		singleton->send_message("set_pid", msg);
+	}
+	if (!singleton)
+		return;
+
+	// There is a debugger, parse breakpoints.
+	ScriptDebugger *script_debugger = singleton->get_script_debugger();
+	script_debugger->set_skip_breakpoints(p_skip_breakpoints);
+
+	for (int i = 0; i < p_breakpoints.size(); i++) {
+
+		String bp = p_breakpoints[i];
+		int sp = bp.find_last(":");
+		ERR_CONTINUE_MSG(sp == -1, "Invalid breakpoint: '" + bp + "', expected file:line format.");
+
+		script_debugger->insert_breakpoint(bp.substr(sp + 1, bp.length()).to_int(), bp.substr(0, sp));
+	}
+}
+
+void EngineDebugger::deinitialize() {
+	if (!singleton)
+		return;
+
+	// Stop all profilers
+	for (Map<StringName, Profiler>::Element *E = profilers.front(); E; E = E->next()) {
+		if (E->get().active)
+			singleton->profiler_enable(E->key(), false);
+	}
+
+	// Flush any remaining message
+	singleton->poll_events(false);
+
+	memdelete(singleton);
+	singleton = NULL;
+	profilers.clear();
+	captures.clear();
+}
+
+EngineDebugger::~EngineDebugger() {
+	if (script_debugger)
+		memdelete(script_debugger);
+	script_debugger = NULL;
+	singleton = NULL;
+}

+ 130 - 0
core/debugger/engine_debugger.h

@@ -0,0 +1,130 @@
+/*************************************************************************/
+/*  engine_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 ENGINE_DEBUGGER_H
+#define ENGINE_DEBUGGER_H
+
+#include "core/array.h"
+#include "core/map.h"
+#include "core/string_name.h"
+#include "core/ustring.h"
+#include "core/variant.h"
+#include "core/vector.h"
+
+class ScriptDebugger;
+
+class EngineDebugger {
+public:
+	typedef void (*ProfilingToggle)(void *p_user, bool p_enable, const Array &p_opts);
+	typedef void (*ProfilingTick)(void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time);
+	typedef void (*ProfilingAdd)(void *p_user, const Array &p_arr);
+	typedef Error (*CaptureFunc)(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured);
+
+	class Profiler {
+		friend class EngineDebugger;
+
+		ProfilingToggle toggle = NULL;
+		ProfilingAdd add = NULL;
+		ProfilingTick tick = NULL;
+		void *data = NULL;
+		bool active = false;
+
+	public:
+		Profiler() {}
+		Profiler(void *p_data, ProfilingToggle p_toggle, ProfilingAdd p_add, ProfilingTick p_tick) {
+			data = p_data;
+			toggle = p_toggle;
+			add = p_add;
+			tick = p_tick;
+		}
+	};
+
+	class Capture {
+		friend class EngineDebugger;
+
+		CaptureFunc capture = NULL;
+		void *data = NULL;
+
+	public:
+		Capture() {}
+		Capture(void *p_data, CaptureFunc p_capture) {
+			data = p_data;
+			capture = p_capture;
+		}
+	};
+
+private:
+	float frame_time = 0.0;
+	float idle_time = 0.0;
+	float physics_time = 0.0;
+	float physics_frame_time = 0.0;
+
+	uint32_t poll_every = 0;
+
+protected:
+	static EngineDebugger *singleton;
+	static ScriptDebugger *script_debugger;
+
+	static Map<StringName, Profiler> profilers;
+	static Map<StringName, Capture> captures;
+
+public:
+	_FORCE_INLINE_ static EngineDebugger *get_singleton() { return singleton; }
+	_FORCE_INLINE_ static bool is_active() { return singleton != NULL && script_debugger != NULL; }
+
+	_FORCE_INLINE_ static ScriptDebugger *get_script_debugger() { return script_debugger; };
+
+	static void initialize(const String &p_uri, bool p_skip_breakpoints, Vector<String> p_breakpoints);
+	static void deinitialize();
+	static void register_profiler(const StringName &p_name, const Profiler &p_profiler);
+	static void unregister_profiler(const StringName &p_name);
+	static bool is_profiling(const StringName &p_name);
+	static bool has_profiler(const StringName &p_name);
+	static void profiler_add_frame_data(const StringName &p_name, const Array &p_data);
+
+	static void register_message_capture(const StringName &p_name, Capture p_func);
+	static void unregister_message_capture(const StringName &p_name);
+	static bool has_capture(const StringName &p_name);
+
+	void iteration(uint64_t p_frame_ticks, uint64_t p_idle_ticks, uint64_t p_physics_ticks, float p_physics_frame_time);
+	void profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts = Array());
+	Error capture_parse(const StringName &p_name, const String &p_msg, const Array &p_args, bool &r_captured);
+
+	void line_poll();
+
+	virtual void poll_events(bool p_is_idle) {}
+	virtual void send_message(const String &p_msg, const Array &p_data) = 0;
+	virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type) = 0;
+	virtual void debug(bool p_can_continue = true, bool p_is_error_breakpoint = false) = 0;
+
+	virtual ~EngineDebugger();
+};
+
+#endif // ENGINE_DEBUGGER_H

+ 142 - 150
core/script_debugger_local.cpp → core/debugger/local_debugger.cpp

@@ -1,5 +1,5 @@
 /*************************************************************************/
-/*  script_debugger_local.cpp                                            */
+/*  local_debugger.cpp                                                   */
 /*************************************************************************/
 /*                       This file is part of:                           */
 /*                           GODOT ENGINE                                */
@@ -28,28 +28,112 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 
-#include "script_debugger_local.h"
+#include "local_debugger.h"
 
+#include "core/debugger/script_debugger.h"
 #include "core/os/os.h"
 #include "scene/main/scene_tree.h"
 
-void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, bool p_is_error_breakpoint) {
+struct LocalDebugger::ScriptsProfiler {
+	struct ProfileInfoSort {
+
+		bool operator()(const ScriptLanguage::ProfilingInfo &A, const ScriptLanguage::ProfilingInfo &B) const {
+			return A.total_time > B.total_time;
+		}
+	};
+
+	float frame_time = 0;
+	uint64_t idle_accum = 0;
+	Vector<ScriptLanguage::ProfilingInfo> pinfo;
+
+	void toggle(bool p_enable, const Array &p_opts) {
+		if (p_enable) {
+			for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+				ScriptServer::get_language(i)->profiling_start();
+			}
+
+			print_line("BEGIN PROFILING");
+			pinfo.resize(32768);
+		} else {
+			_print_frame_data(true);
+			for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+				ScriptServer::get_language(i)->profiling_stop();
+			}
+		}
+	}
+
+	void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+		frame_time = p_frame_time;
+		_print_frame_data(false);
+	}
+
+	void _print_frame_data(bool p_accumulated) {
+		uint64_t diff = OS::get_singleton()->get_ticks_usec() - idle_accum;
+
+		if (!p_accumulated && diff < 1000000) //show every one second
+			return;
+
+		idle_accum = OS::get_singleton()->get_ticks_usec();
+
+		int ofs = 0;
+		for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+			if (p_accumulated)
+				ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&pinfo.write[ofs], pinfo.size() - ofs);
+			else
+				ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&pinfo.write[ofs], pinfo.size() - ofs);
+		}
+
+		SortArray<ScriptLanguage::ProfilingInfo, ProfileInfoSort> sort;
+		sort.sort(pinfo.ptrw(), ofs);
+
+		// compute total script frame time
+		uint64_t script_time_us = 0;
+		for (int i = 0; i < ofs; i++) {
+
+			script_time_us += pinfo[i].self_time;
+		}
+		float script_time = USEC_TO_SEC(script_time_us);
+		float total_time = p_accumulated ? script_time : frame_time;
+
+		if (!p_accumulated) {
+			print_line("FRAME: total: " + rtos(total_time) + " script: " + rtos(script_time) + "/" + itos(script_time * 100 / total_time) + " %");
+		} else {
+			print_line("ACCUMULATED: total: " + rtos(total_time));
+		}
+
+		for (int i = 0; i < ofs; i++) {
+
+			print_line(itos(i) + ":" + pinfo[i].signature);
+			float tt = USEC_TO_SEC(pinfo[i].total_time);
+			float st = USEC_TO_SEC(pinfo[i].self_time);
+			print_line("\ttotal: " + rtos(tt) + "/" + itos(tt * 100 / total_time) + " % \tself: " + rtos(st) + "/" + itos(st * 100 / total_time) + " % tcalls: " + itos(pinfo[i].call_count));
+		}
+	}
+
+	ScriptsProfiler() {
+		idle_accum = OS::get_singleton()->get_ticks_usec();
+	}
+};
+
+void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
+
+	ScriptLanguage *script_lang = script_debugger->get_break_language();
 
 	if (!target_function.empty()) {
-		String current_function = p_script->debug_get_stack_level_function(0);
+		String current_function = script_lang->debug_get_stack_level_function(0);
 		if (current_function != target_function) {
-			set_depth(0);
-			set_lines_left(1);
+			script_debugger->set_depth(0);
+			script_debugger->set_lines_left(1);
 			return;
 		}
 		target_function = "";
 	}
 
-	print_line("\nDebugger Break, Reason: '" + p_script->debug_get_error() + "'");
-	print_line("*Frame " + itos(0) + " - " + p_script->debug_get_stack_level_source(0) + ":" + itos(p_script->debug_get_stack_level_line(0)) + " in function '" + p_script->debug_get_stack_level_function(0) + "'");
+	print_line("\nDebugger Break, Reason: '" + script_lang->debug_get_error() + "'");
+	print_line("*Frame " + itos(0) + " - " + script_lang->debug_get_stack_level_source(0) + ":" + itos(script_lang->debug_get_stack_level_line(0)) + " in function '" + script_lang->debug_get_stack_level_function(0) + "'");
 	print_line("Enter \"help\" for assistance.");
 	int current_frame = 0;
-	int total_frames = p_script->debug_get_stack_level_count();
+	int total_frames = script_lang->debug_get_stack_level_count();
 	while (true) {
 
 		OS::get_singleton()->print("debug> ");
@@ -59,8 +143,8 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
 		String variable_prefix = options["variable_prefix"];
 
 		if (line == "") {
-			print_line("\nDebugger Break, Reason: '" + p_script->debug_get_error() + "'");
-			print_line("*Frame " + itos(current_frame) + " - " + p_script->debug_get_stack_level_source(current_frame) + ":" + itos(p_script->debug_get_stack_level_line(current_frame)) + " in function '" + p_script->debug_get_stack_level_function(current_frame) + "'");
+			print_line("\nDebugger Break, Reason: '" + script_lang->debug_get_error() + "'");
+			print_line("*Frame " + itos(current_frame) + " - " + script_lang->debug_get_stack_level_source(current_frame) + ":" + itos(script_lang->debug_get_stack_level_line(current_frame)) + " in function '" + script_lang->debug_get_stack_level_function(current_frame) + "'");
 			print_line("Enter \"help\" for assistance.");
 		} else if (line == "c" || line == "continue")
 			break;
@@ -69,20 +153,20 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
 			for (int i = 0; i < total_frames; i++) {
 
 				String cfi = (current_frame == i) ? "*" : " "; //current frame indicator
-				print_line(cfi + "Frame " + itos(i) + " - " + p_script->debug_get_stack_level_source(i) + ":" + itos(p_script->debug_get_stack_level_line(i)) + " in function '" + p_script->debug_get_stack_level_function(i) + "'");
+				print_line(cfi + "Frame " + itos(i) + " - " + script_lang->debug_get_stack_level_source(i) + ":" + itos(script_lang->debug_get_stack_level_line(i)) + " in function '" + script_lang->debug_get_stack_level_function(i) + "'");
 			}
 
 		} else if (line.begins_with("fr") || line.begins_with("frame")) {
 
 			if (line.get_slice_count(" ") == 1) {
-				print_line("*Frame " + itos(current_frame) + " - " + p_script->debug_get_stack_level_source(current_frame) + ":" + itos(p_script->debug_get_stack_level_line(current_frame)) + " in function '" + p_script->debug_get_stack_level_function(current_frame) + "'");
+				print_line("*Frame " + itos(current_frame) + " - " + script_lang->debug_get_stack_level_source(current_frame) + ":" + itos(script_lang->debug_get_stack_level_line(current_frame)) + " in function '" + script_lang->debug_get_stack_level_function(current_frame) + "'");
 			} else {
 				int frame = line.get_slicec(' ', 1).to_int();
 				if (frame < 0 || frame >= total_frames) {
 					print_line("Error: Invalid frame.");
 				} else {
 					current_frame = frame;
-					print_line("*Frame " + itos(frame) + " - " + p_script->debug_get_stack_level_source(frame) + ":" + itos(p_script->debug_get_stack_level_line(frame)) + " in function '" + p_script->debug_get_stack_level_function(frame) + "'");
+					print_line("*Frame " + itos(frame) + " - " + script_lang->debug_get_stack_level_source(frame) + ":" + itos(script_lang->debug_get_stack_level_line(frame)) + " in function '" + script_lang->debug_get_stack_level_function(frame) + "'");
 				}
 			}
 
@@ -120,21 +204,21 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
 
 			List<String> locals;
 			List<Variant> values;
-			p_script->debug_get_stack_level_locals(current_frame, &locals, &values);
+			script_lang->debug_get_stack_level_locals(current_frame, &locals, &values);
 			print_variables(locals, values, variable_prefix);
 
 		} else if (line == "gv" || line == "globals") {
 
 			List<String> globals;
 			List<Variant> values;
-			p_script->debug_get_globals(&globals, &values);
+			script_lang->debug_get_globals(&globals, &values);
 			print_variables(globals, values, variable_prefix);
 
 		} else if (line == "mv" || line == "members") {
 
 			List<String> members;
 			List<Variant> values;
-			p_script->debug_get_stack_level_members(current_frame, &members, &values);
+			script_lang->debug_get_stack_level_members(current_frame, &members, &values);
 			print_variables(members, values, variable_prefix);
 
 		} else if (line.begins_with("p") || line.begins_with("print")) {
@@ -144,29 +228,29 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
 			} else {
 
 				String expr = line.get_slicec(' ', 2);
-				String res = p_script->debug_parse_stack_level_expression(current_frame, expr);
+				String res = script_lang->debug_parse_stack_level_expression(current_frame, expr);
 				print_line(res);
 			}
 
 		} else if (line == "s" || line == "step") {
 
-			set_depth(-1);
-			set_lines_left(1);
+			script_debugger->set_depth(-1);
+			script_debugger->set_lines_left(1);
 			break;
 		} else if (line == "n" || line == "next") {
 
-			set_depth(0);
-			set_lines_left(1);
+			script_debugger->set_depth(0);
+			script_debugger->set_lines_left(1);
 			break;
 		} else if (line == "fin" || line == "finish") {
 
-			String current_function = p_script->debug_get_stack_level_function(0);
+			String current_function = script_lang->debug_get_stack_level_function(0);
 
 			for (int i = 0; i < total_frames; i++) {
-				target_function = p_script->debug_get_stack_level_function(i);
+				target_function = script_lang->debug_get_stack_level_function(i);
 				if (target_function != current_function) {
-					set_depth(0);
-					set_lines_left(1);
+					script_debugger->set_depth(0);
+					script_debugger->set_lines_left(1);
 					return;
 				}
 			}
@@ -178,7 +262,7 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
 
 			if (line.get_slice_count(" ") <= 1) {
 
-				const Map<int, Set<StringName> > &breakpoints = get_breakpoints();
+				const Map<int, Set<StringName> > &breakpoints = script_debugger->get_breakpoints();
 				if (breakpoints.size() == 0) {
 					print_line("No Breakpoints.");
 					continue;
@@ -199,7 +283,7 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
 				if (source.empty())
 					continue;
 
-				insert_breakpoint(linenr, source);
+				script_debugger->insert_breakpoint(linenr, source);
 
 				print_line("Added breakpoint at " + source + ":" + itos(linenr));
 			}
@@ -207,16 +291,16 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
 		} else if (line == "q" || line == "quit") {
 
 			// Do not stop again on quit
-			clear_breakpoints();
-			ScriptDebugger::get_singleton()->set_depth(-1);
-			ScriptDebugger::get_singleton()->set_lines_left(-1);
+			script_debugger->clear_breakpoints();
+			script_debugger->set_depth(-1);
+			script_debugger->set_lines_left(-1);
 
 			SceneTree::get_singleton()->quit();
 			break;
 		} else if (line.begins_with("delete")) {
 
 			if (line.get_slice_count(" ") <= 1) {
-				clear_breakpoints();
+				script_debugger->clear_breakpoints();
 			} else {
 
 				Pair<String, int> breakpoint = to_breakpoint(line);
@@ -227,7 +311,7 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
 				if (source.empty())
 					continue;
 
-				remove_breakpoint(linenr, source);
+				script_debugger->remove_breakpoint(linenr, source);
 
 				print_line("Removed breakpoint at " + source + ":" + itos(linenr));
 			}
@@ -255,7 +339,7 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
 	}
 }
 
-void ScriptDebuggerLocal::print_variables(const List<String> &names, const List<Variant> &values, const String &variable_prefix) {
+void LocalDebugger::print_variables(const List<String> &names, const List<Variant> &values, const String &variable_prefix) {
 
 	String value;
 	Vector<String> value_lines;
@@ -279,7 +363,7 @@ void ScriptDebuggerLocal::print_variables(const List<String> &names, const List<
 	}
 }
 
-Pair<String, int> ScriptDebuggerLocal::to_breakpoint(const String &p_line) {
+Pair<String, int> LocalDebugger::to_breakpoint(const String &p_line) {
 
 	String breakpoint_part = p_line.get_slicec(' ', 1);
 	Pair<String, int> breakpoint;
@@ -290,135 +374,43 @@ Pair<String, int> ScriptDebuggerLocal::to_breakpoint(const String &p_line) {
 		return breakpoint;
 	}
 
-	breakpoint.first = breakpoint_find_source(breakpoint_part.left(last_colon).strip_edges());
+	breakpoint.first = script_debugger->breakpoint_find_source(breakpoint_part.left(last_colon).strip_edges());
 	breakpoint.second = breakpoint_part.right(last_colon).strip_edges().to_int();
 
 	return breakpoint;
 }
 
-struct _ScriptDebuggerLocalProfileInfoSort {
-
-	bool operator()(const ScriptLanguage::ProfilingInfo &A, const ScriptLanguage::ProfilingInfo &B) const {
-		return A.total_time > B.total_time;
-	}
-};
-
-void ScriptDebuggerLocal::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 ScriptDebuggerLocal::idle_poll() {
-
-	if (!profiling)
-		return;
-
-	uint64_t diff = OS::get_singleton()->get_ticks_usec() - idle_accum;
-
-	if (diff < 1000000) //show every one second
-		return;
-
-	idle_accum = OS::get_singleton()->get_ticks_usec();
-
-	int ofs = 0;
-	for (int i = 0; i < ScriptServer::get_language_count(); i++) {
-		ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&pinfo.write[ofs], pinfo.size() - ofs);
-	}
-
-	SortArray<ScriptLanguage::ProfilingInfo, _ScriptDebuggerLocalProfileInfoSort> sort;
-	sort.sort(pinfo.ptrw(), ofs);
-
-	//falta el frame time
-
-	uint64_t script_time_us = 0;
-
-	for (int i = 0; i < ofs; i++) {
-
-		script_time_us += pinfo[i].self_time;
-	}
-
-	float script_time = USEC_TO_SEC(script_time_us);
-
-	float total_time = frame_time;
-
-	//print script total
-
-	print_line("FRAME: total: " + rtos(frame_time) + " script: " + rtos(script_time) + "/" + itos(script_time * 100 / total_time) + " %");
-
-	for (int i = 0; i < ofs; i++) {
-
-		print_line(itos(i) + ":" + pinfo[i].signature);
-		float tt = USEC_TO_SEC(pinfo[i].total_time);
-		float st = USEC_TO_SEC(pinfo[i].self_time);
-		print_line("\ttotal: " + rtos(tt) + "/" + itos(tt * 100 / total_time) + " % \tself: " + rtos(st) + "/" + itos(st * 100 / total_time) + " % tcalls: " + itos(pinfo[i].call_count));
-	}
-}
-
-void ScriptDebuggerLocal::profiling_start() {
-
-	for (int i = 0; i < ScriptServer::get_language_count(); i++) {
-		ScriptServer::get_language(i)->profiling_start();
-	}
-
-	print_line("BEGIN PROFILING");
-	profiling = true;
-	pinfo.resize(32768);
-	frame_time = 0;
-	physics_time = 0;
-	idle_time = 0;
-	physics_frame_time = 0;
-}
-
-void ScriptDebuggerLocal::profiling_end() {
-
-	int ofs = 0;
-
-	for (int i = 0; i < ScriptServer::get_language_count(); i++) {
-		ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&pinfo.write[ofs], pinfo.size() - ofs);
-	}
-
-	SortArray<ScriptLanguage::ProfilingInfo, _ScriptDebuggerLocalProfileInfoSort> sort;
-	sort.sort(pinfo.ptrw(), ofs);
-
-	uint64_t total_us = 0;
-	for (int i = 0; i < ofs; i++) {
-		total_us += pinfo[i].self_time;
-	}
-
-	float total_time = total_us / 1000000.0;
-
-	for (int i = 0; i < ofs; i++) {
-
-		print_line(itos(i) + ":" + pinfo[i].signature);
-		float tt = USEC_TO_SEC(pinfo[i].total_time);
-		float st = USEC_TO_SEC(pinfo[i].self_time);
-		print_line("\ttotal_ms: " + rtos(tt) + "\tself_ms: " + rtos(st) + "total%: " + itos(tt * 100 / total_time) + "\tself%: " + itos(st * 100 / total_time) + "\tcalls: " + itos(pinfo[i].call_count));
-	}
-
-	for (int i = 0; i < ScriptServer::get_language_count(); i++) {
-		ScriptServer::get_language(i)->profiling_stop();
-	}
-
-	profiling = false;
-}
-
-void ScriptDebuggerLocal::send_message(const String &p_message, const Array &p_args) {
+void LocalDebugger::send_message(const String &p_message, const Array &p_args) {
 
 	// This needs to be cleaned up entirely.
 	// print_line("MESSAGE: '" + p_message + "' - " + String(Variant(p_args)));
 }
 
-void ScriptDebuggerLocal::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) {
+void LocalDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type) {
 
 	print_line("ERROR: '" + (p_descr.empty() ? p_err : p_descr) + "'");
 }
 
-ScriptDebuggerLocal::ScriptDebuggerLocal() {
+LocalDebugger::LocalDebugger() {
 
-	profiling = false;
-	idle_accum = OS::get_singleton()->get_ticks_usec();
 	options["variable_prefix"] = "";
+
+	// Bind scripts profiler.
+	scripts_profiler = memnew(ScriptsProfiler);
+	Profiler scr_prof(
+			scripts_profiler,
+			[](void *p_user, bool p_enable, const Array &p_opts) {
+				((ScriptsProfiler *)p_user)->toggle(p_enable, p_opts);
+			},
+			NULL,
+			[](void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+				((ScriptsProfiler *)p_user)->tick(p_frame_time, p_idle_time, p_physics_time, p_physics_frame_time);
+			});
+	register_profiler("scripts", scr_prof);
+}
+
+LocalDebugger::~LocalDebugger() {
+	unregister_profiler("scripts");
+	if (scripts_profiler)
+		memdelete(scripts_profiler);
 }

+ 16 - 23
core/script_debugger_local.h → core/debugger/local_debugger.h

@@ -1,5 +1,5 @@
 /*************************************************************************/
-/*  script_debugger_local.h                                              */
+/*  local_debugger.h                                                     */
 /*************************************************************************/
 /*                       This file is part of:                           */
 /*                           GODOT ENGINE                                */
@@ -28,40 +28,33 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 
-#ifndef SCRIPT_DEBUGGER_LOCAL_H
-#define SCRIPT_DEBUGGER_LOCAL_H
+#ifndef LOCAL_DEBUGGER_H
+#define LOCAL_DEBUGGER_H
 
+#include "core/debugger/engine_debugger.h"
 #include "core/list.h"
 #include "core/script_language.h"
 
-class ScriptDebuggerLocal : public ScriptDebugger {
+class LocalDebugger : public EngineDebugger {
+
+private:
+	struct ScriptsProfiler;
+
+	ScriptsProfiler *scripts_profiler = NULL;
 
-	bool profiling;
-	float frame_time, idle_time, physics_time, physics_frame_time;
-	uint64_t idle_accum;
 	String target_function;
 	Map<String, String> options;
 
-	Vector<ScriptLanguage::ProfilingInfo> pinfo;
-
 	Pair<String, int> to_breakpoint(const String &p_line);
 	void print_variables(const List<String> &names, const List<Variant> &values, const String &variable_prefix);
 
 public:
-	void debug(ScriptLanguage *p_script, bool p_can_continue, bool p_is_error_breakpoint);
-	virtual void send_message(const String &p_message, const Array &p_args);
-	virtual void 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);
-
-	virtual bool is_profiling() const { return profiling; }
-	virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data) {}
-
-	virtual void idle_poll();
-
-	virtual void profiling_start();
-	virtual void profiling_end();
-	virtual void profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time);
+	void debug(bool p_can_continue, bool p_is_error_breakpoint);
+	void send_message(const String &p_message, const Array &p_args);
+	void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type);
 
-	ScriptDebuggerLocal();
+	LocalDebugger();
+	~LocalDebugger();
 };
 
-#endif // SCRIPT_DEBUGGER_LOCAL_H
+#endif // LOCAL_DEBUGGER_H

+ 935 - 0
core/debugger/remote_debugger.cpp

@@ -0,0 +1,935 @@
+/*************************************************************************/
+/*  remote_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 "remote_debugger.h"
+
+#include "core/debugger/debugger_marshalls.h"
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/script_debugger.h"
+#include "core/os/input.h"
+#include "core/os/os.h"
+#include "core/project_settings.h"
+#include "core/script_language.h"
+#include "scene/main/node.h"
+
+template <typename T>
+void RemoteDebugger::_bind_profiler(const String &p_name, T *p_prof) {
+	EngineDebugger::Profiler prof(
+			p_prof,
+			[](void *p_user, bool p_enable, const Array &p_opts) {
+				((T *)p_user)->toggle(p_enable, p_opts);
+			},
+			[](void *p_user, const Array &p_data) {
+				((T *)p_user)->add(p_data);
+			},
+			[](void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+				((T *)p_user)->tick(p_frame_time, p_idle_time, p_physics_time, p_physics_frame_time);
+			});
+	EngineDebugger::register_profiler(p_name, prof);
+}
+
+struct RemoteDebugger::NetworkProfiler {
+
+public:
+	typedef DebuggerMarshalls::MultiplayerNodeInfo NodeInfo;
+	struct BandwidthFrame {
+		uint32_t timestamp;
+		int packet_size;
+	};
+
+	int bandwidth_in_ptr = 0;
+	Vector<BandwidthFrame> bandwidth_in;
+	int bandwidth_out_ptr = 0;
+	Vector<BandwidthFrame> bandwidth_out;
+	uint64_t last_bandwidth_time = 0;
+
+	Map<ObjectID, NodeInfo> multiplayer_node_data;
+	uint64_t last_profile_time = 0;
+
+	NetworkProfiler() {}
+
+	int bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer) {
+		int total_bandwidth = 0;
+
+		uint32_t timestamp = OS::get_singleton()->get_ticks_msec();
+		uint32_t final_timestamp = timestamp - 1000;
+
+		int i = (p_pointer + p_buffer.size() - 1) % p_buffer.size();
+
+		while (i != p_pointer && p_buffer[i].packet_size > 0) {
+			if (p_buffer[i].timestamp < final_timestamp) {
+				return total_bandwidth;
+			}
+			total_bandwidth += p_buffer[i].packet_size;
+			i = (i + p_buffer.size() - 1) % p_buffer.size();
+		}
+
+		ERR_FAIL_COND_V_MSG(i == p_pointer, total_bandwidth, "Reached the end of the bandwidth profiler buffer, values might be inaccurate.");
+		return total_bandwidth;
+	}
+
+	void init_node(const ObjectID p_node) {
+		if (multiplayer_node_data.has(p_node))
+			return;
+		multiplayer_node_data.insert(p_node, DebuggerMarshalls::MultiplayerNodeInfo());
+		multiplayer_node_data[p_node].node = p_node;
+		multiplayer_node_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path();
+		multiplayer_node_data[p_node].incoming_rpc = 0;
+		multiplayer_node_data[p_node].incoming_rset = 0;
+		multiplayer_node_data[p_node].outgoing_rpc = 0;
+		multiplayer_node_data[p_node].outgoing_rset = 0;
+	}
+
+	void toggle(bool p_enable, const Array &p_opts) {
+		multiplayer_node_data.clear();
+
+		if (!p_enable) {
+			bandwidth_in.clear();
+			bandwidth_out.clear();
+		} else {
+			bandwidth_in_ptr = 0;
+			bandwidth_in.resize(16384); // ~128kB
+			for (int i = 0; i < bandwidth_in.size(); ++i) {
+				bandwidth_in.write[i].packet_size = -1;
+			}
+			bandwidth_out_ptr = 0;
+			bandwidth_out.resize(16384); // ~128kB
+			for (int i = 0; i < bandwidth_out.size(); ++i) {
+				bandwidth_out.write[i].packet_size = -1;
+			}
+		}
+	}
+
+	void add(const Array &p_data) {
+		ERR_FAIL_COND(p_data.size() < 1);
+		const String type = p_data[0];
+		if (type == "node") {
+			ERR_FAIL_COND(p_data.size() < 3);
+			const ObjectID id = p_data[1];
+			const String what = p_data[2];
+			init_node(id);
+			NodeInfo &info = multiplayer_node_data[id];
+			if (what == "rpc_in") {
+				info.incoming_rpc++;
+			} else if (what == "rpc_out") {
+				info.outgoing_rpc++;
+			} else if (what == "rset_in") {
+				info.incoming_rset = 0;
+			} else if (what == "rset_out") {
+				info.outgoing_rset++;
+			}
+		} else if (type == "bandwidth") {
+			ERR_FAIL_COND(p_data.size() < 4);
+			const String inout = p_data[1];
+			int time = p_data[2];
+			int size = p_data[3];
+			if (inout == "in") {
+				bandwidth_in.write[bandwidth_in_ptr].timestamp = time;
+				bandwidth_in.write[bandwidth_in_ptr].packet_size = size;
+				bandwidth_in_ptr = (bandwidth_in_ptr + 1) % bandwidth_in.size();
+			} else if (inout == "out") {
+				bandwidth_out.write[bandwidth_out_ptr].timestamp = time;
+				bandwidth_out.write[bandwidth_out_ptr].packet_size = size;
+				bandwidth_out_ptr = (bandwidth_out_ptr + 1) % bandwidth_out.size();
+			}
+		}
+	}
+
+	void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+		uint64_t pt = OS::get_singleton()->get_ticks_msec();
+		if (pt - last_bandwidth_time > 200) {
+			last_bandwidth_time = pt;
+			int incoming_bandwidth = bandwidth_usage(bandwidth_in, bandwidth_in_ptr);
+			int outgoing_bandwidth = bandwidth_usage(bandwidth_out, bandwidth_out_ptr);
+
+			Array arr;
+			arr.push_back(incoming_bandwidth);
+			arr.push_back(outgoing_bandwidth);
+			EngineDebugger::get_singleton()->send_message("network:bandwidth", arr);
+		}
+		if (pt - last_profile_time > 100) {
+			last_profile_time = pt;
+			DebuggerMarshalls::NetworkProfilerFrame frame;
+			for (Map<ObjectID, NodeInfo>::Element *E = multiplayer_node_data.front(); E; E = E->next()) {
+				frame.infos.push_back(E->get());
+			}
+			multiplayer_node_data.clear();
+			EngineDebugger::get_singleton()->send_message("network:profile_frame", frame.serialize());
+		}
+	}
+};
+
+struct RemoteDebugger::ScriptsProfiler {
+	typedef DebuggerMarshalls::ScriptFunctionSignature FunctionSignature;
+	typedef DebuggerMarshalls::ScriptFunctionInfo FunctionInfo;
+	struct ProfileInfoSort {
+
+		bool operator()(ScriptLanguage::ProfilingInfo *A, ScriptLanguage::ProfilingInfo *B) const {
+			return A->total_time < B->total_time;
+		}
+	};
+	Vector<ScriptLanguage::ProfilingInfo> info;
+	Vector<ScriptLanguage::ProfilingInfo *> ptrs;
+	Map<StringName, int> sig_map;
+	int max_frame_functions = 16;
+
+	void toggle(bool p_enable, const Array &p_opts) {
+		if (p_enable) {
+			sig_map.clear();
+			for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+				ScriptServer::get_language(i)->profiling_start();
+			}
+			if (p_opts.size() == 1 && p_opts[0].get_type() == Variant::INT) {
+				max_frame_functions = MAX(0, int(p_opts[0]));
+			}
+		} else {
+			for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+				ScriptServer::get_language(i)->profiling_stop();
+			}
+		}
+	}
+
+	void write_frame_data(Vector<FunctionInfo> &r_funcs, uint64_t &r_total, bool p_accumulated) {
+		int ofs = 0;
+		for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+			if (p_accumulated)
+				ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&info.write[ofs], info.size() - ofs);
+			else
+				ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&info.write[ofs], info.size() - ofs);
+		}
+
+		for (int i = 0; i < ofs; i++) {
+			ptrs.write[i] = &info.write[i];
+		}
+
+		SortArray<ScriptLanguage::ProfilingInfo *, ProfileInfoSort> sa;
+		sa.sort(ptrs.ptrw(), ofs);
+
+		int to_send = MIN(ofs, max_frame_functions);
+
+		// Check signatures first, and compute total time.
+		r_total = 0;
+		for (int i = 0; i < to_send; i++) {
+			if (!sig_map.has(ptrs[i]->signature)) {
+				int idx = sig_map.size();
+				FunctionSignature sig;
+				sig.name = ptrs[i]->signature;
+				sig.id = idx;
+				EngineDebugger::get_singleton()->send_message("servers:function_signature", sig.serialize());
+				sig_map[ptrs[i]->signature] = idx;
+			}
+			r_total += ptrs[i]->self_time;
+		}
+
+		// Send frame, script time, functions information then
+		r_funcs.resize(to_send);
+
+		FunctionInfo *w = r_funcs.ptrw();
+		for (int i = 0; i < to_send; i++) {
+			if (sig_map.has(ptrs[i]->signature)) {
+				w[i].sig_id = sig_map[ptrs[i]->signature];
+			}
+			w[i].call_count = ptrs[i]->call_count;
+			w[i].total_time = ptrs[i]->total_time / 1000000.0;
+			w[i].self_time = ptrs[i]->self_time / 1000000.0;
+		}
+	}
+
+	ScriptsProfiler() {
+		info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
+		ptrs.resize(info.size());
+	}
+};
+
+struct RemoteDebugger::ServersProfiler {
+
+	bool skip_profile_frame = false;
+	typedef DebuggerMarshalls::ServerInfo ServerInfo;
+	typedef DebuggerMarshalls::ServerFunctionInfo ServerFunctionInfo;
+
+	Map<StringName, ServerInfo> server_data;
+	ScriptsProfiler scripts_profiler;
+
+	float frame_time = 0;
+	float idle_time = 0;
+	float physics_time = 0;
+	float physics_frame_time = 0;
+
+	void toggle(bool p_enable, const Array &p_opts) {
+		skip_profile_frame = false;
+		if (p_enable) {
+			server_data.clear(); // Clear old profiling data.
+		} else {
+			_send_frame_data(true); // Send final frame.
+		}
+		scripts_profiler.toggle(p_enable, p_opts);
+	}
+
+	void add(const Array &p_data) {
+		String name = p_data[0];
+		if (!server_data.has(name)) {
+			ServerInfo info;
+			info.name = name;
+			server_data[name] = info;
+		}
+		ServerInfo &srv = server_data[name];
+
+		ServerFunctionInfo fi;
+		fi.name = p_data[1];
+		fi.time = p_data[2];
+		srv.functions.push_back(fi);
+	}
+
+	void tick(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;
+		_send_frame_data(false);
+	}
+
+	void _send_frame_data(bool p_final) {
+		DebuggerMarshalls::ServersProfilerFrame frame;
+		frame.frame_number = Engine::get_singleton()->get_frames_drawn();
+		frame.frame_time = frame_time;
+		frame.idle_time = idle_time;
+		frame.physics_time = physics_time;
+		frame.physics_frame_time = physics_frame_time;
+		Map<StringName, ServerInfo>::Element *E = server_data.front();
+		while (E) {
+			if (!p_final) {
+				frame.servers.push_back(E->get());
+			}
+			E->get().functions.clear();
+			E = E->next();
+		}
+		uint64_t time = 0;
+		scripts_profiler.write_frame_data(frame.script_functions, time, p_final);
+		frame.script_time = USEC_TO_SEC(time);
+		if (skip_profile_frame) {
+			skip_profile_frame = false;
+			return;
+		}
+		if (p_final) {
+			EngineDebugger::get_singleton()->send_message("servers:profile_total", frame.serialize());
+		} else {
+			EngineDebugger::get_singleton()->send_message("servers:profile_frame", frame.serialize());
+		}
+	}
+};
+
+struct RemoteDebugger::VisualProfiler {
+
+	typedef DebuggerMarshalls::ServerInfo ServerInfo;
+	typedef DebuggerMarshalls::ServerFunctionInfo ServerFunctionInfo;
+
+	Map<StringName, ServerInfo> server_data;
+
+	void toggle(bool p_enable, const Array &p_opts) {
+		VS::get_singleton()->set_frame_profiling_enabled(p_enable);
+	}
+
+	void add(const Array &p_data) {}
+
+	void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+		Vector<VS::FrameProfileArea> profile_areas = VS::get_singleton()->get_frame_profile();
+		DebuggerMarshalls::VisualProfilerFrame frame;
+		if (!profile_areas.size())
+			return;
+
+		frame.frame_number = VS::get_singleton()->get_frame_profile_frame();
+		frame.areas.append_array(profile_areas);
+		EngineDebugger::get_singleton()->send_message("visual:profile_frame", frame.serialize());
+	}
+};
+
+struct RemoteDebugger::PerformanceProfiler {
+
+	Object *performance = NULL;
+	int last_perf_time = 0;
+
+	void toggle(bool p_enable, const Array &p_opts) {}
+	void add(const Array &p_data) {}
+	void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+		if (!performance)
+			return;
+
+		uint64_t pt = OS::get_singleton()->get_ticks_msec();
+		if (pt - last_perf_time < 1000)
+			return;
+		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);
+		}
+		EngineDebugger::get_singleton()->send_message("performance:profile_frame", arr);
+	}
+
+	PerformanceProfiler(Object *p_performance) {
+		performance = p_performance;
+	}
+};
+
+void RemoteDebugger::_send_resource_usage() {
+
+	DebuggerMarshalls::ResourceUsage usage;
+
+	List<VS::TextureInfo> tinfo;
+	VS::get_singleton()->texture_debug_usage(&tinfo);
+
+	for (List<VS::TextureInfo>::Element *E = tinfo.front(); E; E = E->next()) {
+
+		DebuggerMarshalls::ResourceInfo info;
+		info.path = E->get().path;
+		info.vram = E->get().bytes;
+		info.id = E->get().texture;
+		info.type = "Texture";
+		if (E->get().depth == 0) {
+			info.format = itos(E->get().width) + "x" + itos(E->get().height) + " " + Image::get_format_name(E->get().format);
+		} else {
+			info.format = itos(E->get().width) + "x" + itos(E->get().height) + "x" + itos(E->get().depth) + " " + Image::get_format_name(E->get().format);
+		}
+		usage.infos.push_back(info);
+	}
+
+	EngineDebugger::get_singleton()->send_message("memory:usage", usage.serialize());
+}
+
+Error RemoteDebugger::_put_msg(String p_message, Array p_data) {
+	Array msg;
+	msg.push_back(p_message);
+	msg.push_back(p_data);
+	Error err = peer->put_message(msg);
+	if (err != OK)
+		n_messages_dropped++;
+	return err;
+}
+
+void RemoteDebugger::_err_handler(void *p_this, 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
+
+	RemoteDebugger *rd = (RemoteDebugger *)p_this;
+	if (rd->flushing && Thread::get_caller_id() == rd->flush_thread) // Can't handle recursive errors during flush.
+		return;
+
+	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;
+	}
+
+	// send_error will lock internally.
+	rd->script_debugger->send_error(p_func, p_file, p_line, p_err, p_descr, p_type, si);
+}
+
+void RemoteDebugger::_print_handler(void *p_this, const String &p_string, bool p_error) {
+
+	RemoteDebugger *rd = (RemoteDebugger *)p_this;
+
+	if (rd->flushing && Thread::get_caller_id() == rd->flush_thread) // Can't handle recursive prints during flush.
+		return;
+
+	String s = p_string;
+	int allowed_chars = MIN(MAX(rd->max_chars_per_second - rd->char_count, 0), s.length());
+
+	if (allowed_chars == 0)
+		return;
+
+	if (allowed_chars < s.length()) {
+		s = s.substr(0, allowed_chars);
+	}
+
+	MutexLock lock(rd->mutex);
+
+	rd->char_count += allowed_chars;
+	bool overflowed = rd->char_count >= rd->max_chars_per_second;
+	if (rd->is_peer_connected()) {
+		if (overflowed)
+			s += "[...]";
+		rd->output_strings.push_back(s);
+
+		if (overflowed) {
+			rd->output_strings.push_back("[output overflow, print less text!]");
+		}
+	}
+}
+
+RemoteDebugger::ErrorMessage RemoteDebugger::_create_overflow_error(const String &p_what, const String &p_descr) {
+	ErrorMessage oe;
+	oe.error = p_what;
+	oe.error_descr = p_descr;
+	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;
+	return oe;
+}
+
+void RemoteDebugger::flush_output() {
+	flush_thread = Thread::get_caller_id();
+	flushing = true;
+	MutexLock lock(mutex);
+	if (!is_peer_connected())
+		return;
+
+	if (n_messages_dropped > 0) {
+		ErrorMessage err_msg = _create_overflow_error("TOO_MANY_MESSAGES", "Too many messages! " + String::num_int64(n_messages_dropped) + " messages were dropped. Profiling might misbheave, try raising 'network/limits/debugger/max_queued_messages' in project setting.");
+		if (_put_msg("error", err_msg.serialize()) == OK)
+			n_messages_dropped = 0;
+	}
+
+	if (output_strings.size()) {
+
+		// Join output strings so we generate less messages.
+		Vector<String> strings;
+		strings.resize(output_strings.size());
+		String *w = strings.ptrw();
+		for (int i = 0; i < output_strings.size(); i++) {
+			w[i] = output_strings[i];
+		}
+
+		Array arr;
+		arr.push_back(strings);
+		_put_msg("output", arr);
+		output_strings.clear();
+	}
+
+	while (errors.size()) {
+		ErrorMessage oe = errors.front()->get();
+		_put_msg("error", oe.serialize());
+		errors.pop_front();
+	}
+
+	// Update limits
+	uint64_t ticks = OS::get_singleton()->get_ticks_usec() / 1000;
+
+	if (ticks - last_reset > 1000) {
+		last_reset = ticks;
+		char_count = 0;
+		err_count = 0;
+		n_errors_dropped = 0;
+		warn_count = 0;
+		n_warnings_dropped = 0;
+	}
+	flushing = false;
+}
+
+void RemoteDebugger::send_message(const String &p_message, const Array &p_args) {
+
+	MutexLock lock(mutex);
+	if (is_peer_connected()) {
+		_put_msg(p_message, p_args);
+	}
+}
+
+void RemoteDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type) {
+
+	ErrorMessage 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;
+	oe.callstack.append_array(script_debugger->get_error_stack_info());
+
+	if (flushing && Thread::get_caller_id() == flush_thread) // Can't handle recursive errors during flush.
+		return;
+
+	MutexLock lock(mutex);
+
+	if (oe.warning) {
+		warn_count++;
+	} else {
+		err_count++;
+	}
+
+	if (is_peer_connected()) {
+
+		if (oe.warning) {
+			if (warn_count > max_warnings_per_second) {
+				n_warnings_dropped++;
+				if (n_warnings_dropped == 1) {
+					// Only print one message about dropping per second
+					ErrorMessage overflow = _create_overflow_error("TOO_MANY_WARNINGS", "Too many warnings! Ignoring warnings for up to 1 second.");
+					errors.push_back(overflow);
+				}
+			} else {
+				errors.push_back(oe);
+			}
+		} else {
+			if (err_count > max_errors_per_second) {
+				n_errors_dropped++;
+				if (n_errors_dropped == 1) {
+					// Only print one message about dropping per second
+					ErrorMessage overflow = _create_overflow_error("TOO_MANY_ERRORS", "Too many errors! Ignoring errors for up to 1 second.");
+					errors.push_back(overflow);
+				}
+			} else {
+				errors.push_back(oe);
+			}
+		}
+	}
+}
+
+void RemoteDebugger::_send_stack_vars(List<String> &p_names, List<Variant> &p_vals, int p_type) {
+	DebuggerMarshalls::ScriptStackVariable stvar;
+	List<String>::Element *E = p_names.front();
+	List<Variant>::Element *F = p_vals.front();
+	while (E) {
+		stvar.name = E->get();
+		stvar.value = F->get();
+		stvar.type = p_type;
+		send_message("stack_frame_var", stvar.serialize());
+		E = E->next();
+		F = F->next();
+	}
+}
+
+Error RemoteDebugger::_try_capture(const String &p_msg, const Array &p_data, bool &r_captured) {
+	const int idx = p_msg.find(":");
+	r_captured = false;
+	if (idx < 0) { // No prefix, unknown message.
+		return OK;
+	}
+	const String cap = p_msg.substr(0, idx);
+	if (!has_capture(cap))
+		return ERR_UNAVAILABLE; // Unknown message...
+	const String msg = p_msg.substr(idx + 1);
+	return capture_parse(cap, msg, p_data, r_captured);
+}
+
+void RemoteDebugger::debug(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 (script_debugger->is_skipping_breakpoints() && !p_is_error_breakpoint)
+		return;
+
+	ERR_FAIL_COND_MSG(!is_peer_connected(), "Script Debugger failed to connect, but being used anyway.");
+
+	ScriptLanguage *script_lang = script_debugger->get_break_language();
+	const String error_str = script_lang ? script_lang->debug_get_error() : "";
+	Array msg;
+	msg.push_back(p_can_continue);
+	msg.push_back(error_str);
+	send_message("debug_enter", msg);
+
+	servers_profiler->skip_profile_frame = true; // Avoid frame time spike in debug.
+
+	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 (is_peer_connected()) {
+		loop_begin_usec = OS::get_singleton()->get_ticks_usec();
+
+		flush_output();
+		peer->poll();
+
+		if (peer->has_message()) {
+
+			Array cmd = peer->get_message();
+
+			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") {
+				script_debugger->set_depth(-1);
+				script_debugger->set_lines_left(1);
+				break;
+
+			} else if (command == "next") {
+				script_debugger->set_depth(0);
+				script_debugger->set_lines_left(1);
+				break;
+
+			} else if (command == "continue") {
+				script_debugger->set_depth(-1);
+				script_debugger->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 == "get_stack_dump") {
+				ERR_FAIL_COND(!script_lang);
+				DebuggerMarshalls::ScriptStackDump dump;
+				int slc = script_lang->debug_get_stack_level_count();
+				for (int i = 0; i < slc; i++) {
+					ScriptLanguage::StackInfo frame;
+					frame.file = script_lang->debug_get_stack_level_source(i);
+					frame.line = script_lang->debug_get_stack_level_line(i);
+					frame.func = script_lang->debug_get_stack_level_function(i);
+					dump.frames.push_back(frame);
+				}
+				send_message("stack_dump", dump.serialize());
+
+			} else if (command == "get_stack_frame_vars") {
+				ERR_FAIL_COND(data.size() != 1);
+				ERR_FAIL_COND(!script_lang);
+				int lv = data[0];
+
+				List<String> members;
+				List<Variant> member_vals;
+				if (ScriptInstance *inst = script_lang->debug_get_stack_level_instance(lv)) {
+					members.push_back("self");
+					member_vals.push_back(inst->get_owner());
+				}
+				script_lang->debug_get_stack_level_members(lv, &members, &member_vals);
+				ERR_FAIL_COND(members.size() != member_vals.size());
+
+				List<String> locals;
+				List<Variant> local_vals;
+				script_lang->debug_get_stack_level_locals(lv, &locals, &local_vals);
+				ERR_FAIL_COND(locals.size() != local_vals.size());
+
+				List<String> globals;
+				List<Variant> globals_vals;
+				script_lang->debug_get_globals(&globals, &globals_vals);
+				ERR_FAIL_COND(globals.size() != globals_vals.size());
+
+				send_message("stack_frame_vars", Array());
+				_send_stack_vars(locals, local_vals, 0);
+				_send_stack_vars(members, member_vals, 1);
+				_send_stack_vars(globals, globals_vals, 2);
+
+			} else if (command == "reload_scripts") {
+				reload_all_scripts = true;
+
+			} else if (command == "breakpoint") {
+				ERR_FAIL_COND(data.size() < 3);
+				bool set = data[2];
+				if (set)
+					script_debugger->insert_breakpoint(data[1], data[0]);
+				else
+					script_debugger->remove_breakpoint(data[1], data[0]);
+
+			} else if (command == "set_skip_breakpoints") {
+				ERR_FAIL_COND(data.size() < 1);
+				script_debugger->set_skip_breakpoints(data[0]);
+			} else {
+				bool captured = false;
+				ERR_CONTINUE(_try_capture(command, data, captured) != OK);
+				if (!captured)
+					WARN_PRINT("Unknown message received from debugger: " + command);
+			}
+		} 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());
+		}
+	}
+
+	send_message("debug_exit", Array());
+
+	if (mouse_mode != Input::MOUSE_MODE_VISIBLE)
+		Input::get_singleton()->set_mouse_mode(mouse_mode);
+}
+
+void RemoteDebugger::poll_events(bool p_is_idle) {
+	if (peer.is_null())
+		return;
+
+	flush_output();
+	peer->poll();
+	while (peer->has_message()) {
+
+		Array arr = peer->get_message();
+
+		ERR_CONTINUE(arr.size() != 2);
+		ERR_CONTINUE(arr[0].get_type() != Variant::STRING);
+		ERR_CONTINUE(arr[1].get_type() != Variant::ARRAY);
+
+		const String cmd = arr[0];
+		const int idx = cmd.find(":");
+		bool parsed = false;
+		if (idx < 0) { // Not prefix, use scripts capture.
+			capture_parse("core", cmd, arr[1], parsed);
+			continue;
+		}
+
+		const String cap = cmd.substr(0, idx);
+		if (!has_capture(cap))
+			continue; // Unknown message...
+
+		const String msg = cmd.substr(idx + 1);
+		capture_parse(cap, msg, arr[1], parsed);
+	}
+
+	// Reload scripts during idle poll only.
+	if (p_is_idle && reload_all_scripts) {
+		for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+			ScriptServer::get_language(i)->reload_all_scripts();
+		}
+		reload_all_scripts = false;
+	}
+}
+
+Error RemoteDebugger::_core_capture(const String &p_cmd, const Array &p_data, bool &r_captured) {
+	r_captured = true;
+	if (p_cmd == "reload_scripts") {
+		reload_all_scripts = true;
+
+	} else if (p_cmd == "breakpoint") {
+		ERR_FAIL_COND_V(p_data.size() < 3, ERR_INVALID_DATA);
+		bool set = p_data[2];
+		if (set)
+			script_debugger->insert_breakpoint(p_data[1], p_data[0]);
+		else
+			script_debugger->remove_breakpoint(p_data[1], p_data[0]);
+
+	} else if (p_cmd == "set_skip_breakpoints") {
+		ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA);
+		script_debugger->set_skip_breakpoints(p_data[0]);
+	} else if (p_cmd == "memory") {
+		_send_resource_usage();
+	} else if (p_cmd == "break") {
+		script_debugger->debug(script_debugger->get_break_language());
+	} else {
+		r_captured = false;
+	}
+	return OK;
+}
+
+Error RemoteDebugger::_profiler_capture(const String &p_cmd, const Array &p_data, bool &r_captured) {
+	r_captured = false;
+	ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA);
+	ERR_FAIL_COND_V(p_data[0].get_type() != Variant::BOOL, ERR_INVALID_DATA);
+	ERR_FAIL_COND_V(!has_profiler(p_cmd), ERR_UNAVAILABLE);
+	Array opts;
+	if (p_data.size() > 1) { // Optional profiler parameters.
+		ERR_FAIL_COND_V(p_data[1].get_type() != Variant::ARRAY, ERR_INVALID_DATA);
+		opts = p_data[1];
+	}
+	r_captured = true;
+	profiler_enable(p_cmd, p_data[0], opts);
+	return OK;
+}
+
+RemoteDebugger *RemoteDebugger::create_for_uri(const String &p_uri) {
+	Ref<RemoteDebuggerPeer> peer = RemoteDebuggerPeer::create_from_uri(p_uri);
+	if (peer.is_valid())
+		return memnew(RemoteDebugger(peer));
+	return NULL;
+}
+
+RemoteDebugger::RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer) {
+	peer = p_peer;
+	max_chars_per_second = GLOBAL_GET("network/limits/debugger/max_chars_per_second");
+	max_errors_per_second = GLOBAL_GET("network/limits/debugger/max_errors_per_second");
+	max_warnings_per_second = GLOBAL_GET("network/limits/debugger/max_warnings_per_second");
+
+	// Network Profiler
+	network_profiler = memnew(NetworkProfiler);
+	_bind_profiler("network", network_profiler);
+
+	// Servers Profiler (audio/physics/...)
+	servers_profiler = memnew(ServersProfiler);
+	_bind_profiler("servers", servers_profiler);
+
+	// Visual Profiler (cpu/gpu times)
+	visual_profiler = memnew(VisualProfiler);
+	_bind_profiler("visual", visual_profiler);
+
+	// Perfromance Profiler
+	Object *perf = Engine::get_singleton()->get_singleton_object("Performance");
+	if (perf) {
+		performance_profiler = memnew(PerformanceProfiler(perf));
+		_bind_profiler("performance", performance_profiler);
+		profiler_enable("performance", true);
+	}
+
+	// Core and profiler captures.
+	Capture core_cap(this,
+			[](void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) {
+				return ((RemoteDebugger *)p_user)->_core_capture(p_cmd, p_data, r_captured);
+			});
+	register_message_capture("core", core_cap);
+	Capture profiler_cap(this,
+			[](void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) {
+				return ((RemoteDebugger *)p_user)->_profiler_capture(p_cmd, p_data, r_captured);
+			});
+	register_message_capture("profiler", profiler_cap);
+
+	// Error handlers
+	phl.printfunc = _print_handler;
+	phl.userdata = this;
+	add_print_handler(&phl);
+
+	eh.errfunc = _err_handler;
+	eh.userdata = this;
+	add_error_handler(&eh);
+}
+
+RemoteDebugger::~RemoteDebugger() {
+	remove_print_handler(&phl);
+	remove_error_handler(&eh);
+
+	EngineDebugger::get_singleton()->unregister_profiler("servers");
+	EngineDebugger::get_singleton()->unregister_profiler("network");
+	EngineDebugger::get_singleton()->unregister_profiler("visual");
+	if (EngineDebugger::has_profiler("performance")) {
+		EngineDebugger::get_singleton()->unregister_profiler("performance");
+	}
+	memdelete(servers_profiler);
+	memdelete(network_profiler);
+	memdelete(visual_profiler);
+	if (performance_profiler)
+		memdelete(performance_profiler);
+}

+ 114 - 0
core/debugger/remote_debugger.h

@@ -0,0 +1,114 @@
+/*************************************************************************/
+/*  remote_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 REMOTE_DEBUGGER_H
+#define REMOTE_DEBUGGER_H
+
+#include "core/array.h"
+#include "core/debugger/debugger_marshalls.h"
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/remote_debugger_peer.h"
+#include "core/object.h"
+#include "core/string_name.h"
+#include "core/ustring.h"
+
+class RemoteDebugger : public EngineDebugger {
+
+private:
+	typedef DebuggerMarshalls::OutputError ErrorMessage;
+
+	struct NetworkProfiler;
+	struct ServersProfiler;
+	struct ScriptsProfiler;
+	struct VisualProfiler;
+	struct PerformanceProfiler;
+
+	NetworkProfiler *network_profiler = NULL;
+	ServersProfiler *servers_profiler = NULL;
+	VisualProfiler *visual_profiler = NULL;
+	PerformanceProfiler *performance_profiler = NULL;
+
+	Ref<RemoteDebuggerPeer> peer;
+
+	List<String> output_strings;
+	List<ErrorMessage> errors;
+
+	int n_messages_dropped = 0;
+	int max_errors_per_second = 0;
+	int max_chars_per_second = 0;
+	int max_warnings_per_second = 0;
+	int n_errors_dropped = 0;
+	int n_warnings_dropped = 0;
+	int char_count = 0;
+	int err_count = 0;
+	int warn_count = 0;
+	int last_reset = 0;
+	bool reload_all_scripts = false;
+
+	// Make handlers and send_message thread safe.
+	Mutex mutex;
+	bool flushing = false;
+	Thread::ID flush_thread = 0;
+
+	PrintHandlerList phl;
+	static void _print_handler(void *p_this, const String &p_string, bool p_error);
+	ErrorHandlerList eh;
+	static void _err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, ErrorHandlerType p_type);
+
+	ErrorMessage _create_overflow_error(const String &p_what, const String &p_descr);
+	Error _put_msg(String p_message, Array p_data);
+
+	bool is_peer_connected() { return peer->is_peer_connected(); }
+	void flush_output();
+
+	void _send_resource_usage();
+	void _send_stack_vars(List<String> &p_names, List<Variant> &p_vals, int p_type);
+
+	Error _profiler_capture(const String &p_cmd, const Array &p_data, bool &r_captured);
+	Error _core_capture(const String &p_cmd, const Array &p_data, bool &r_captured);
+
+	template <typename T>
+	void _bind_profiler(const String &p_name, T *p_prof);
+	Error _try_capture(const String &p_name, const Array &p_data, bool &r_captured);
+
+public:
+	static RemoteDebugger *create_for_uri(const String &p_uri);
+
+	// Overrides
+	void poll_events(bool p_is_idle);
+	void send_message(const String &p_message, const Array &p_args);
+	void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type);
+	void debug(bool p_can_continue = true, bool p_is_error_breakpoint = false);
+
+	RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer);
+	~RemoteDebugger();
+};
+
+#endif // REMOTE_DEBUGGER_H

+ 242 - 0
core/debugger/remote_debugger_peer.cpp

@@ -0,0 +1,242 @@
+/*************************************************************************/
+/*  remote_debugger_peer.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 "remote_debugger_peer.h"
+
+#include "core/io/marshalls.h"
+#include "core/os/os.h"
+#include "core/project_settings.h"
+
+bool RemoteDebuggerPeerTCP::is_peer_connected() {
+	return connected;
+}
+
+bool RemoteDebuggerPeerTCP::has_message() {
+	return in_queue.size() > 0;
+}
+
+Array RemoteDebuggerPeerTCP::get_message() {
+	MutexLock lock(mutex);
+	ERR_FAIL_COND_V(!has_message(), Array());
+	Array out = in_queue[0];
+	in_queue.pop_front();
+	return out;
+}
+
+Error RemoteDebuggerPeerTCP::put_message(const Array &p_arr) {
+	MutexLock lock(mutex);
+	if (out_queue.size() >= max_queued_messages)
+		return ERR_OUT_OF_MEMORY;
+
+	out_queue.push_back(p_arr);
+	return OK;
+}
+
+int RemoteDebuggerPeerTCP::get_max_message_size() const {
+	return 8 << 20; // 8 MiB
+}
+
+void RemoteDebuggerPeerTCP::close() {
+	if (thread) {
+		running = false;
+		Thread::wait_to_finish(thread);
+		memdelete(thread);
+		thread = NULL;
+	}
+	tcp_client->disconnect_from_host();
+	out_buf.resize(0);
+	in_buf.resize(0);
+}
+
+RemoteDebuggerPeerTCP::RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_tcp) {
+	// This means remote debugger takes 16 MiB just because it exists...
+	in_buf.resize((8 << 20) + 4); // 8 MiB should be way more than enough (need 4 extra bytes for encoding packet size).
+	out_buf.resize(8 << 20); // 8 MiB should be way more than enough
+	tcp_client = p_tcp;
+	if (tcp_client.is_valid()) { // Attaching to an already connected stream.
+		connected = true;
+#ifndef NO_THREADS
+		running = true;
+		thread = Thread::create(_thread_func, this);
+#endif
+	} else {
+		tcp_client.instance();
+	}
+}
+
+RemoteDebuggerPeerTCP::~RemoteDebuggerPeerTCP() {
+	close();
+}
+
+void RemoteDebuggerPeerTCP::_write_out() {
+	while (tcp_client->poll(NetSocket::POLL_TYPE_OUT) == OK) {
+		uint8_t *buf = out_buf.ptrw();
+		if (out_left <= 0) {
+			if (out_queue.size() == 0)
+				break; // Nothing left to send
+			mutex.lock();
+			Variant var = out_queue[0];
+			out_queue.pop_front();
+			mutex.unlock();
+			int size = 0;
+			Error err = encode_variant(var, NULL, size);
+			ERR_CONTINUE(err != OK || size > out_buf.size() - 4); // 4 bytes separator.
+			encode_uint32(size, buf);
+			encode_variant(var, buf + 4, size);
+			out_left = size + 4;
+			out_pos = 0;
+		}
+		int sent = 0;
+		tcp_client->put_partial_data(buf + out_pos, out_left, sent);
+		out_left -= sent;
+		out_pos += sent;
+	}
+}
+
+void RemoteDebuggerPeerTCP::_read_in() {
+	while (tcp_client->poll(NetSocket::POLL_TYPE_IN) == OK) {
+		uint8_t *buf = in_buf.ptrw();
+		if (in_left <= 0) {
+			if (in_queue.size() > max_queued_messages) {
+				break; // Too many messages already in queue.
+			}
+			if (tcp_client->get_available_bytes() < 4) {
+				break; // Need 4 more bytes.
+			}
+			uint32_t size = 0;
+			int read = 0;
+			Error err = tcp_client->get_partial_data((uint8_t *)&size, 4, read);
+			ERR_CONTINUE(read != 4 || err != OK || size > (uint32_t)in_buf.size());
+			in_left = size;
+			in_pos = 0;
+		}
+		int read = 0;
+		tcp_client->get_partial_data(buf + in_pos, in_left, read);
+		in_left -= read;
+		in_pos += read;
+		if (in_left == 0) {
+			Variant var;
+			Error err = decode_variant(var, buf, in_pos, &read);
+			ERR_CONTINUE(read != in_pos || err != OK);
+			ERR_CONTINUE_MSG(var.get_type() != Variant::ARRAY, "Malformed packet received, not an Array.");
+			mutex.lock();
+			in_queue.push_back(var);
+			mutex.unlock();
+		}
+	}
+}
+
+Error RemoteDebuggerPeerTCP::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;
+	};
+	connected = true;
+#ifndef NO_THREADS
+	running = true;
+	thread = Thread::create(_thread_func, this);
+#endif
+	return OK;
+}
+
+void RemoteDebuggerPeerTCP::_thread_func(void *p_ud) {
+	RemoteDebuggerPeerTCP *peer = (RemoteDebuggerPeerTCP *)p_ud;
+	while (peer->running && peer->is_peer_connected()) {
+		peer->_poll();
+		if (!peer->is_peer_connected())
+			break;
+		peer->tcp_client->poll(NetSocket::POLL_TYPE_IN_OUT, 1);
+	}
+}
+
+void RemoteDebuggerPeerTCP::poll() {
+#ifdef NO_THREADS
+	_poll();
+#endif
+}
+
+void RemoteDebuggerPeerTCP::_poll() {
+	if (connected) {
+		_write_out();
+		_read_in();
+		connected = tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED;
+	}
+}
+
+Ref<RemoteDebuggerPeer> RemoteDebuggerPeer::create_from_uri(const String p_uri) {
+	if (!p_uri.begins_with("tcp://"))
+		return Ref<RemoteDebuggerPeer>(); // Only TCP supported for now, more to come.
+
+	String debug_host = p_uri.replace("tcp://", "");
+	uint16_t debug_port = 6007;
+
+	if (debug_host.find(":") != -1) {
+		int sep_pos = debug_host.find_last(":");
+		debug_port = debug_host.substr(sep_pos + 1).to_int();
+		debug_host = debug_host.substr(0, sep_pos);
+	}
+	Ref<RemoteDebuggerPeerTCP> peer = Ref<RemoteDebuggerPeer>(memnew(RemoteDebuggerPeerTCP));
+	Error err = peer->connect_to_host(debug_host, debug_port);
+	if (err != OK)
+		return Ref<RemoteDebuggerPeer>();
+	return peer;
+}
+
+RemoteDebuggerPeer::RemoteDebuggerPeer() {
+	max_queued_messages = (int)GLOBAL_GET("network/limits/debugger/max_queued_messages");
+}

+ 94 - 0
core/debugger/remote_debugger_peer.h

@@ -0,0 +1,94 @@
+/*************************************************************************/
+/*  remote_debugger_peer.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 REMOTE_DEBUGGER_PEER_H
+#define REMOTE_DEBUGGER_PEER_H
+
+#include "core/io/stream_peer_tcp.h"
+#include "core/os/mutex.h"
+#include "core/os/thread.h"
+#include "core/reference.h"
+#include "core/ustring.h"
+
+class RemoteDebuggerPeer : public Reference {
+protected:
+	int max_queued_messages = 4096;
+
+public:
+	static Ref<RemoteDebuggerPeer> create_from_uri(const String p_uri);
+	virtual bool is_peer_connected() = 0;
+	virtual bool has_message() = 0;
+	virtual Error put_message(const Array &p_arr) = 0;
+	virtual Array get_message() = 0;
+	virtual void close() = 0;
+	virtual void poll() = 0;
+	virtual int get_max_message_size() const = 0;
+
+	RemoteDebuggerPeer();
+};
+
+class RemoteDebuggerPeerTCP : public RemoteDebuggerPeer {
+private:
+	Ref<StreamPeerTCP> tcp_client;
+	Mutex mutex;
+	Thread *thread = NULL;
+	List<Array> in_queue;
+	List<Array> out_queue;
+	int out_left = 0;
+	int out_pos = 0;
+	Vector<uint8_t> out_buf;
+	int in_left = 0;
+	int in_pos = 0;
+	Vector<uint8_t> in_buf;
+	bool connected = false;
+	bool running = false;
+
+	static void _thread_func(void *p_ud);
+
+	void _poll();
+	void _write_out();
+	void _read_in();
+
+public:
+	Error connect_to_host(const String &p_host, uint16_t p_port);
+
+	void poll();
+	bool is_peer_connected();
+	bool has_message();
+	Array get_message();
+	Error put_message(const Array &p_arr);
+	int get_max_message_size() const;
+	void close();
+
+	RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_stream = Ref<StreamPeerTCP>());
+	~RemoteDebuggerPeerTCP();
+};
+
+#endif // REMOTE_DEBUGGER_PEER_H

+ 123 - 0
core/debugger/script_debugger.cpp

@@ -0,0 +1,123 @@
+/*************************************************************************/
+/*  script_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 "script_debugger.h"
+
+#include "core/debugger/engine_debugger.h"
+
+void ScriptDebugger::set_lines_left(int p_left) {
+
+	lines_left = p_left;
+}
+
+int ScriptDebugger::get_lines_left() const {
+
+	return lines_left;
+}
+
+void ScriptDebugger::set_depth(int p_depth) {
+
+	depth = p_depth;
+}
+
+int ScriptDebugger::get_depth() const {
+
+	return depth;
+}
+
+void ScriptDebugger::insert_breakpoint(int p_line, const StringName &p_source) {
+
+	if (!breakpoints.has(p_line))
+		breakpoints[p_line] = Set<StringName>();
+	breakpoints[p_line].insert(p_source);
+}
+
+void ScriptDebugger::remove_breakpoint(int p_line, const StringName &p_source) {
+
+	if (!breakpoints.has(p_line))
+		return;
+
+	breakpoints[p_line].erase(p_source);
+	if (breakpoints[p_line].size() == 0)
+		breakpoints.erase(p_line);
+}
+bool ScriptDebugger::is_breakpoint(int p_line, const StringName &p_source) const {
+
+	if (!breakpoints.has(p_line))
+		return false;
+	return breakpoints[p_line].has(p_source);
+}
+bool ScriptDebugger::is_breakpoint_line(int p_line) const {
+
+	return breakpoints.has(p_line);
+}
+
+String ScriptDebugger::breakpoint_find_source(const String &p_source) const {
+
+	return p_source;
+}
+
+void ScriptDebugger::clear_breakpoints() {
+
+	breakpoints.clear();
+}
+
+void ScriptDebugger::set_skip_breakpoints(bool p_skip_breakpoints) {
+
+	skip_breakpoints = p_skip_breakpoints;
+}
+
+bool ScriptDebugger::is_skipping_breakpoints() {
+
+	return skip_breakpoints;
+}
+
+void ScriptDebugger::debug(ScriptLanguage *p_lang, bool p_can_continue, bool p_is_error_breakpoint) {
+	ScriptLanguage *prev = break_lang;
+	break_lang = p_lang;
+	EngineDebugger::get_singleton()->debug(p_can_continue, p_is_error_breakpoint);
+	break_lang = prev;
+}
+
+void ScriptDebugger::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<StackInfo> &p_stack_info) {
+	// Store stack info, this is ugly, but allows us to separate EngineDebugger and ScriptDebugger. There might be a better way.
+	error_stack_info.append_array(p_stack_info);
+	EngineDebugger::get_singleton()->send_error(p_func, p_file, p_line, p_err, p_descr, p_type);
+	error_stack_info.resize(0);
+}
+
+Vector<ScriptLanguage::StackInfo> ScriptDebugger::get_error_stack_info() const {
+	return error_stack_info;
+}
+
+ScriptLanguage *ScriptDebugger::get_break_language() const {
+
+	return break_lang;
+}

+ 80 - 0
core/debugger/script_debugger.h

@@ -0,0 +1,80 @@
+/*************************************************************************/
+/*  script_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 SCRIPT_DEBUGGER_H
+#define SCRIPT_DEBUGGER_H
+
+#include "core/map.h"
+#include "core/script_language.h"
+#include "core/set.h"
+#include "core/string_name.h"
+#include "core/vector.h"
+
+class ScriptDebugger {
+
+	typedef ScriptLanguage::StackInfo StackInfo;
+
+	int lines_left = -1;
+	int depth = -1;
+	bool skip_breakpoints = false;
+
+	Map<int, Set<StringName> > breakpoints;
+
+	ScriptLanguage *break_lang = NULL;
+	Vector<StackInfo> error_stack_info;
+
+public:
+	void set_lines_left(int p_left);
+	int get_lines_left() const;
+
+	void set_depth(int p_depth);
+	int get_depth() const;
+
+	String breakpoint_find_source(const String &p_source) const;
+	void set_break_language(ScriptLanguage *p_lang) { break_lang = p_lang; }
+	ScriptLanguage *get_break_language() { return break_lang; }
+	void set_skip_breakpoints(bool p_skip_breakpoints);
+	bool is_skipping_breakpoints();
+	void insert_breakpoint(int p_line, const StringName &p_source);
+	void remove_breakpoint(int p_line, const StringName &p_source);
+	bool is_breakpoint(int p_line, const StringName &p_source) const;
+	bool is_breakpoint_line(int p_line) const;
+	void clear_breakpoints();
+	const Map<int, Set<StringName> > &get_breakpoints() const { return breakpoints; }
+
+	void debug(ScriptLanguage *p_lang, bool p_can_continue = true, bool p_is_error_breakpoint = false);
+	ScriptLanguage *get_break_language() const;
+
+	void 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<StackInfo> &p_stack_info);
+	Vector<StackInfo> get_error_stack_info() const;
+	ScriptDebugger() {}
+};
+
+#endif

+ 29 - 122
core/io/multiplayer_api.cpp

@@ -30,6 +30,7 @@
 
 #include "multiplayer_api.h"
 
+#include "core/debugger/engine_debugger.h"
 #include "core/io/marshalls.h"
 #include "scene/main/node.h"
 #include <stdint.h>
@@ -172,6 +173,28 @@ Ref<NetworkedMultiplayerPeer> MultiplayerAPI::get_network_peer() const {
 	return network_peer;
 }
 
+#ifdef DEBUG_ENABLED
+void _profile_node_data(const String &p_what, ObjectID p_id) {
+	if (EngineDebugger::is_profiling("multiplayer")) {
+		Array values;
+		values.push_back("node");
+		values.push_back(p_id);
+		values.push_back(p_what);
+		EngineDebugger::profiler_add_frame_data("multiplayer", values);
+	}
+}
+void _profile_bandwidth_data(const String &p_inout, int p_size) {
+	if (EngineDebugger::is_profiling("multiplayer")) {
+		Array values;
+		values.push_back("bandwidth");
+		values.push_back(p_inout);
+		values.push_back(OS::get_singleton()->get_ticks_msec());
+		values.push_back(p_size);
+		EngineDebugger::profiler_add_frame_data("multiplayer", values);
+	}
+}
+#endif
+
 // Returns the packet size stripping the node path added when the node is not yet cached.
 int get_packet_len(uint32_t p_node_target, int p_packet_len) {
 	if (p_node_target & 0x80000000) {
@@ -188,11 +211,7 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
 	ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small.");
 
 #ifdef DEBUG_ENABLED
-	if (profiling) {
-		bandwidth_incoming_data.write[bandwidth_incoming_pointer].timestamp = OS::get_singleton()->get_ticks_msec();
-		bandwidth_incoming_data.write[bandwidth_incoming_pointer].packet_size = p_packet_len;
-		bandwidth_incoming_pointer = (bandwidth_incoming_pointer + 1) % bandwidth_incoming_data.size();
-	}
+	_profile_bandwidth_data("in", p_packet_len);
 #endif
 
 	// Extract the `packet_type` from the LSB three bits:
@@ -381,11 +400,7 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id,
 	argp.resize(argc);
 
 #ifdef DEBUG_ENABLED
-	if (profiling) {
-		ObjectID id = p_node->get_instance_id();
-		_init_node_profile(id);
-		profiler_frame_data[id].incoming_rpc += 1;
-	}
+	_profile_node_data("in_rpc", p_node->get_instance_id());
 #endif
 
 	if (byte_only) {
@@ -437,11 +452,7 @@ void MultiplayerAPI::_process_rset(Node *p_node, const uint16_t p_rpc_property_i
 	ERR_FAIL_COND_MSG(!can_call, "RSET '" + String(name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
 
 #ifdef DEBUG_ENABLED
-	if (profiling) {
-		ObjectID id = p_node->get_instance_id();
-		_init_node_profile(id);
-		profiler_frame_data[id].incoming_rset += 1;
-	}
+	_profile_node_data("in_rset", p_node->get_instance_id());
 #endif
 
 	Variant value;
@@ -912,11 +923,7 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
 	packet_cache.write[0] = command_type + (node_id_compression << NODE_ID_COMPRESSION_SHIFT) + (name_id_compression << NAME_ID_COMPRESSION_SHIFT) + ((byte_only_or_no_args ? 1 : 0) << BYTE_ONLY_OR_NO_ARGS_SHIFT);
 
 #ifdef DEBUG_ENABLED
-	if (profiling) {
-		bandwidth_outgoing_data.write[bandwidth_outgoing_pointer].timestamp = OS::get_singleton()->get_ticks_msec();
-		bandwidth_outgoing_data.write[bandwidth_outgoing_pointer].packet_size = ofs;
-		bandwidth_outgoing_pointer = (bandwidth_outgoing_pointer + 1) % bandwidth_outgoing_data.size();
-	}
+	_profile_bandwidth_data("out", ofs);
 #endif
 
 	// Take chance and set transfer mode, since all send methods will use it.
@@ -1031,11 +1038,7 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const
 	if (!skip_rpc) {
 
 #ifdef DEBUG_ENABLED
-		if (profiling) {
-			ObjectID id = p_node->get_instance_id();
-			_init_node_profile(id);
-			profiler_frame_data[id].outgoing_rpc += 1;
-		}
+		_profile_node_data("out_rpc", p_node->get_instance_id());
 #endif
 
 		_send_rpc(p_node, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount);
@@ -1130,11 +1133,7 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const
 	}
 
 #ifdef DEBUG_ENABLED
-	if (profiling) {
-		ObjectID id = p_node->get_instance_id();
-		_init_node_profile(id);
-		profiler_frame_data[id].outgoing_rset += 1;
-	}
+	_profile_node_data("out_rset", p_node->get_instance_id());
 #endif
 
 	const Variant *vptr = &p_value;
@@ -1220,95 +1219,6 @@ bool MultiplayerAPI::is_object_decoding_allowed() const {
 	return allow_object_decoding;
 }
 
-void MultiplayerAPI::profiling_start() {
-#ifdef DEBUG_ENABLED
-	profiling = true;
-	profiler_frame_data.clear();
-
-	bandwidth_incoming_pointer = 0;
-	bandwidth_incoming_data.resize(16384); // ~128kB
-	for (int i = 0; i < bandwidth_incoming_data.size(); ++i) {
-		bandwidth_incoming_data.write[i].packet_size = -1;
-	}
-
-	bandwidth_outgoing_pointer = 0;
-	bandwidth_outgoing_data.resize(16384); // ~128kB
-	for (int i = 0; i < bandwidth_outgoing_data.size(); ++i) {
-		bandwidth_outgoing_data.write[i].packet_size = -1;
-	}
-#endif
-}
-
-void MultiplayerAPI::profiling_end() {
-#ifdef DEBUG_ENABLED
-	profiling = false;
-	bandwidth_incoming_data.clear();
-	bandwidth_outgoing_data.clear();
-#endif
-}
-
-int MultiplayerAPI::get_profiling_frame(ProfilingInfo *r_info) {
-	int i = 0;
-#ifdef DEBUG_ENABLED
-	for (Map<ObjectID, ProfilingInfo>::Element *E = profiler_frame_data.front(); E; E = E->next()) {
-		r_info[i] = E->get();
-		++i;
-	}
-	profiler_frame_data.clear();
-#endif
-	return i;
-}
-
-int MultiplayerAPI::get_incoming_bandwidth_usage() {
-#ifdef DEBUG_ENABLED
-	return _get_bandwidth_usage(bandwidth_incoming_data, bandwidth_incoming_pointer);
-#else
-	return 0;
-#endif
-}
-
-int MultiplayerAPI::get_outgoing_bandwidth_usage() {
-#ifdef DEBUG_ENABLED
-	return _get_bandwidth_usage(bandwidth_outgoing_data, bandwidth_outgoing_pointer);
-#else
-	return 0;
-#endif
-}
-
-#ifdef DEBUG_ENABLED
-int MultiplayerAPI::_get_bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer) {
-	int total_bandwidth = 0;
-
-	uint32_t timestamp = OS::get_singleton()->get_ticks_msec();
-	uint32_t final_timestamp = timestamp - 1000;
-
-	int i = (p_pointer + p_buffer.size() - 1) % p_buffer.size();
-
-	while (i != p_pointer && p_buffer[i].packet_size > 0) {
-		if (p_buffer[i].timestamp < final_timestamp) {
-			return total_bandwidth;
-		}
-		total_bandwidth += p_buffer[i].packet_size;
-		i = (i + p_buffer.size() - 1) % p_buffer.size();
-	}
-
-	ERR_FAIL_COND_V_MSG(i == p_pointer, total_bandwidth, "Reached the end of the bandwidth profiler buffer, values might be inaccurate.");
-	return total_bandwidth;
-}
-
-void MultiplayerAPI::_init_node_profile(ObjectID p_node) {
-	if (profiler_frame_data.has(p_node))
-		return;
-	profiler_frame_data.insert(p_node, ProfilingInfo());
-	profiler_frame_data[p_node].node = p_node;
-	profiler_frame_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path();
-	profiler_frame_data[p_node].incoming_rpc = 0;
-	profiler_frame_data[p_node].incoming_rset = 0;
-	profiler_frame_data[p_node].outgoing_rpc = 0;
-	profiler_frame_data[p_node].outgoing_rset = 0;
-}
-#endif
-
 void MultiplayerAPI::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node);
 	ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE));
@@ -1352,9 +1262,6 @@ MultiplayerAPI::MultiplayerAPI() :
 		allow_object_decoding(false) {
 	rpc_sender_id = 0;
 	root_node = NULL;
-#ifdef DEBUG_ENABLED
-	profiling = false;
-#endif
 	clear();
 }
 

+ 0 - 34
core/io/multiplayer_api.h

@@ -38,16 +38,6 @@ class MultiplayerAPI : public Reference {
 
 	GDCLASS(MultiplayerAPI, Reference);
 
-public:
-	struct ProfilingInfo {
-		ObjectID node;
-		String node_path;
-		int incoming_rpc;
-		int incoming_rset;
-		int outgoing_rpc;
-		int outgoing_rset;
-	};
-
 private:
 	//path sent caches
 	struct PathSentCache {
@@ -65,23 +55,6 @@ private:
 		Map<int, NodeInfo> nodes;
 	};
 
-#ifdef DEBUG_ENABLED
-	struct BandwidthFrame {
-		uint32_t timestamp;
-		int packet_size;
-	};
-
-	int bandwidth_incoming_pointer;
-	Vector<BandwidthFrame> bandwidth_incoming_data;
-	int bandwidth_outgoing_pointer;
-	Vector<BandwidthFrame> bandwidth_outgoing_data;
-	Map<ObjectID, ProfilingInfo> profiler_frame_data;
-	bool profiling;
-
-	void _init_node_profile(ObjectID p_node);
-	int _get_bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer);
-#endif
-
 	Ref<NetworkedMultiplayerPeer> network_peer;
 	int rpc_sender_id;
 	Set<int> connected_peers;
@@ -169,13 +142,6 @@ public:
 	void set_allow_object_decoding(bool p_enable);
 	bool is_object_decoding_allowed() const;
 
-	void profiling_start();
-	void profiling_end();
-
-	int get_profiling_frame(ProfilingInfo *r_info);
-	int get_incoming_bandwidth_usage();
-	int get_outgoing_bandwidth_usage();
-
 	MultiplayerAPI();
 	~MultiplayerAPI();
 };

+ 5 - 0
core/io/stream_peer_tcp.cpp

@@ -288,6 +288,11 @@ void StreamPeerTCP::disconnect_from_host() {
 	peer_port = 0;
 }
 
+Error StreamPeerTCP::poll(NetSocket::PollType p_type, int timeout) {
+	ERR_FAIL_COND_V(_sock.is_null() || !_sock->is_open(), ERR_UNAVAILABLE);
+	return _sock->poll(p_type, timeout);
+}
+
 Error StreamPeerTCP::put_data(const uint8_t *p_data, int p_bytes) {
 
 	int total;

+ 3 - 0
core/io/stream_peer_tcp.h

@@ -78,6 +78,9 @@ public:
 
 	void set_no_delay(bool p_enabled);
 
+	// Poll functions (wait or check for writable, readable)
+	Error poll(NetSocket::PollType p_type, int timeout = 0);
+
 	// Read/Write from StreamPeer
 	Error put_data(const uint8_t *p_data, int p_bytes);
 	Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent);

+ 0 - 1143
core/script_debugger_remote.cpp

@@ -1,1143 +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 "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() {
-
-	MutexLock lock(mutex);
-
-	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;
-	}
-}
-
-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) {
-
-	MutexLock lock(mutex);
-
-	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);
-		}
-	}
-}
-
-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++;
-	}
-
-	MutexLock lock(mutex);
-
-	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);
-			}
-		}
-	}
-}
-
-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;
-
-	{
-		MutexLock lock(sdr->mutex);
-
-		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!]");
-			}
-		}
-	}
-}
-
-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),
-		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);
-}

+ 0 - 316
core/script_debugger_remote.h

@@ -1,316 +0,0 @@
-/*************************************************************************/
-/*  script_debugger_remote.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 SCRIPT_DEBUGGER_REMOTE_H
-#define SCRIPT_DEBUGGER_REMOTE_H
-
-#include "core/io/packet_peer.h"
-#include "core/io/stream_peer_tcp.h"
-#include "core/list.h"
-#include "core/os/os.h"
-#include "core/script_language.h"
-
-class ScriptDebuggerRemote : public ScriptDebugger {
-
-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 {
-			return A->total_time < B->total_time;
-		}
-	};
-
-	Vector<ScriptLanguage::ProfilingInfo> profile_info;
-	Vector<ScriptLanguage::ProfilingInfo *> profile_info_ptrs;
-	Vector<MultiplayerAPI::ProfilingInfo> network_profile_info;
-
-	Map<StringName, int> profiler_function_signature_map;
-	float frame_time, idle_time, physics_time, physics_frame_time;
-
-	bool profiling;
-	bool visual_profiling;
-	bool network_profiling;
-	int max_frame_functions;
-	bool skip_profile_frame;
-	bool reload_all_scripts;
-
-	Ref<StreamPeerTCP> tcp_client;
-	Ref<PacketPeerStream> packet_peer_stream;
-
-	uint64_t last_perf_time;
-	uint64_t last_net_prof_time;
-	uint64_t last_net_bandwidth_time;
-	Object *performance;
-	bool requested_quit;
-	Mutex mutex;
-
-	List<String> output_strings;
-	List<Message> messages;
-	int max_messages_per_frame;
-	int n_messages_dropped;
-	List<OutputError> errors;
-	int max_errors_per_second;
-	int max_warnings_per_second;
-	int n_errors_dropped;
-	int n_warnings_dropped;
-
-	int max_cps;
-	int char_count;
-	int err_count;
-	int warn_count;
-	uint64_t last_msec;
-	uint64_t msec_count;
-
-	bool locking; //hack to avoid a deadloop
-	static void _print_handler(void *p_this, const String &p_string, bool p_error);
-
-	PrintHandlerList phl;
-
-	void _get_output();
-	void _poll_events();
-	uint32_t poll_every;
-
-	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);
-
-	void _send_object_id(ObjectID p_id);
-	void _send_video_memory();
-
-	Ref<MultiplayerAPI> multiplayer;
-
-	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();
-
-	Vector<FrameData> profile_frame_data;
-
-	bool skip_breakpoints;
-
-public:
-	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();
-
-	virtual bool is_remote() const { return true; }
-	virtual void request_quit();
-
-	virtual void send_message(const String &p_message, const Array &p_args);
-	virtual void 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);
-
-	virtual void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer);
-
-	virtual bool is_profiling() const;
-	virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data);
-
-	virtual void profiling_start();
-	virtual void profiling_end();
-	virtual void profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time);
-
-	virtual void set_skip_breakpoints(bool p_skip_breakpoints);
-
-	ScriptDebuggerRemote();
-	~ScriptDebuggerRemote();
-};
-
-#endif // SCRIPT_DEBUGGER_REMOTE_H

+ 4 - 85
core/script_language.cpp

@@ -31,6 +31,8 @@
 #include "script_language.h"
 
 #include "core/core_string_names.h"
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/script_debugger.h"
 #include "core/project_settings.h"
 #include <stdint.h>
 
@@ -46,8 +48,8 @@ void Script::_notification(int p_what) {
 
 	if (p_what == NOTIFICATION_POSTINITIALIZE) {
 
-		if (ScriptDebugger::get_singleton())
-			ScriptDebugger::get_singleton()->set_break_language(get_language());
+		if (EngineDebugger::is_active())
+			EngineDebugger::get_script_debugger()->set_break_language(get_language());
 	}
 }
 
@@ -356,89 +358,6 @@ ScriptCodeCompletionCache::ScriptCodeCompletionCache() {
 void ScriptLanguage::frame() {
 }
 
-ScriptDebugger *ScriptDebugger::singleton = NULL;
-
-void ScriptDebugger::set_lines_left(int p_left) {
-
-	lines_left = p_left;
-}
-
-int ScriptDebugger::get_lines_left() const {
-
-	return lines_left;
-}
-
-void ScriptDebugger::set_depth(int p_depth) {
-
-	depth = p_depth;
-}
-
-int ScriptDebugger::get_depth() const {
-
-	return depth;
-}
-
-void ScriptDebugger::insert_breakpoint(int p_line, const StringName &p_source) {
-
-	if (!breakpoints.has(p_line))
-		breakpoints[p_line] = Set<StringName>();
-	breakpoints[p_line].insert(p_source);
-}
-
-void ScriptDebugger::remove_breakpoint(int p_line, const StringName &p_source) {
-
-	if (!breakpoints.has(p_line))
-		return;
-
-	breakpoints[p_line].erase(p_source);
-	if (breakpoints[p_line].size() == 0)
-		breakpoints.erase(p_line);
-}
-bool ScriptDebugger::is_breakpoint(int p_line, const StringName &p_source) const {
-
-	if (!breakpoints.has(p_line))
-		return false;
-	return breakpoints[p_line].has(p_source);
-}
-bool ScriptDebugger::is_breakpoint_line(int p_line) const {
-
-	return breakpoints.has(p_line);
-}
-
-String ScriptDebugger::breakpoint_find_source(const String &p_source) const {
-
-	return p_source;
-}
-
-void ScriptDebugger::clear_breakpoints() {
-
-	breakpoints.clear();
-}
-
-void ScriptDebugger::idle_poll() {
-}
-
-void ScriptDebugger::line_poll() {
-}
-
-void ScriptDebugger::set_break_language(ScriptLanguage *p_lang) {
-
-	break_lang = p_lang;
-}
-
-ScriptLanguage *ScriptDebugger::get_break_language() const {
-
-	return break_lang;
-}
-
-ScriptDebugger::ScriptDebugger() {
-
-	singleton = this;
-	lines_left = -1;
-	depth = -1;
-	break_lang = NULL;
-}
-
 bool PlaceHolderScriptInstance::set(const StringName &p_name, const Variant &p_value) {
 
 	if (script->is_placeholder_fallback_enabled())

+ 5 - 58
core/script_language.h

@@ -350,6 +350,11 @@ public:
 	virtual void thread_exit() {}
 
 	/* DEBUGGER FUNCTIONS */
+	struct StackInfo {
+		String file;
+		String func;
+		int line;
+	};
 
 	virtual String debug_get_error() const = 0;
 	virtual int debug_get_stack_level_count() const = 0;
@@ -362,12 +367,6 @@ public:
 	virtual void debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) = 0;
 	virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1) = 0;
 
-	struct StackInfo {
-		String file;
-		String func;
-		int line;
-	};
-
 	virtual Vector<StackInfo> debug_get_current_stack_info() { return Vector<StackInfo>(); }
 
 	virtual void reload_all_scripts() = 0;
@@ -460,56 +459,4 @@ public:
 	PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner);
 	~PlaceHolderScriptInstance();
 };
-
-class ScriptDebugger {
-
-	int lines_left;
-	int depth;
-
-	static ScriptDebugger *singleton;
-	Map<int, Set<StringName> > breakpoints;
-
-	ScriptLanguage *break_lang;
-
-public:
-	_FORCE_INLINE_ static ScriptDebugger *get_singleton() { return singleton; }
-	void set_lines_left(int p_left);
-	int get_lines_left() const;
-
-	void set_depth(int p_depth);
-	int get_depth() const;
-
-	String breakpoint_find_source(const String &p_source) const;
-	void insert_breakpoint(int p_line, const StringName &p_source);
-	void remove_breakpoint(int p_line, const StringName &p_source);
-	bool is_breakpoint(int p_line, const StringName &p_source) const;
-	bool is_breakpoint_line(int p_line) const;
-	void clear_breakpoints();
-	const Map<int, Set<StringName> > &get_breakpoints() const { return breakpoints; }
-
-	virtual void debug(ScriptLanguage *p_script, bool p_can_continue = true, bool p_is_error_breakpoint = false) = 0;
-	virtual void idle_poll();
-	virtual void line_poll();
-
-	void set_break_language(ScriptLanguage *p_lang);
-	ScriptLanguage *get_break_language() const;
-
-	virtual void send_message(const String &p_message, const Array &p_args) = 0;
-	virtual void 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) = 0;
-
-	virtual bool is_remote() const { return false; }
-	virtual void request_quit() {}
-
-	virtual void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {}
-
-	virtual bool is_profiling() const = 0;
-	virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data) = 0;
-	virtual void profiling_start() = 0;
-	virtual void profiling_end() = 0;
-	virtual void profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) = 0;
-
-	ScriptDebugger();
-	virtual ~ScriptDebugger() { singleton = NULL; }
-};
-
 #endif

+ 2 - 1
core/variant.cpp

@@ -31,6 +31,7 @@
 #include "variant.h"
 
 #include "core/core_string_names.h"
+#include "core/debugger/engine_debugger.h"
 #include "core/io/marshalls.h"
 #include "core/math/math_funcs.h"
 #include "core/print_string.h"
@@ -2221,7 +2222,7 @@ Variant::operator RID() const {
 		return RID();
 	} else if (type == OBJECT && _get_obj().obj) {
 #ifdef DEBUG_ENABLED
-		if (ScriptDebugger::get_singleton()) {
+		if (EngineDebugger::is_active()) {
 			ERR_FAIL_COND_V_MSG(ObjectDB::get_instance(_get_obj().id) == nullptr, RID(), "Invalid pointer (object was freed).");
 		};
 #endif

+ 2 - 2
core/variant_call.cpp

@@ -33,10 +33,10 @@
 #include "core/color_names.inc"
 #include "core/core_string_names.h"
 #include "core/crypto/crypto_core.h"
+#include "core/debugger/engine_debugger.h"
 #include "core/io/compression.h"
 #include "core/object.h"
 #include "core/os/os.h"
-#include "core/script_language.h"
 
 typedef void (*VariantFunc)(Variant &r_ret, Variant &p_self, const Variant **p_args);
 typedef void (*VariantConstructFunc)(Variant &r_ret, const Variant **p_args);
@@ -1213,7 +1213,7 @@ void Variant::call_ptr(const StringName &p_method, const Variant **p_args, int p
 			return;
 		}
 #ifdef DEBUG_ENABLED
-		if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+		if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
 			r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
 			return;
 		}

+ 10 - 10
core/variant_op.cpp

@@ -31,8 +31,8 @@
 #include "variant.h"
 
 #include "core/core_string_names.h"
+#include "core/debugger/engine_debugger.h"
 #include "core/object.h"
-#include "core/script_language.h"
 
 #define CASE_TYPE_ALL(PREFIX, OP) \
 	CASE_TYPE(PREFIX, OP, INT)    \
@@ -1739,7 +1739,7 @@ void Variant::set_named(const StringName &p_index, const Variant &p_value, bool
 #ifdef DEBUG_ENABLED
 			if (!_get_obj().obj) {
 				break;
-			} else if (ScriptDebugger::get_singleton() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+			} else if (EngineDebugger::is_active() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
 				break;
 			}
 
@@ -1941,7 +1941,7 @@ Variant Variant::get_named(const StringName &p_index, bool *r_valid) const {
 				return "Instance base is null.";
 			} else {
 
-				if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+				if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
 					if (r_valid)
 						*r_valid = false;
 					return "Attempted use of stray pointer object.";
@@ -2556,7 +2556,7 @@ void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid)
 
 			if (obj) {
 #ifdef DEBUG_ENABLED
-				if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+				if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
 
 					WARN_PRINT("Attempted use of previously freed pointer object.");
 					valid = false;
@@ -3011,7 +3011,7 @@ Variant Variant::get(const Variant &p_index, bool *r_valid) const {
 
 #ifdef DEBUG_ENABLED
 
-				if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+				if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
 					valid = false;
 					return "Attempted get on previously freed instance.";
 				}
@@ -3076,7 +3076,7 @@ bool Variant::in(const Variant &p_index, bool *r_valid) const {
 				bool valid = false;
 #ifdef DEBUG_ENABLED
 
-				if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+				if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
 					if (r_valid) {
 						*r_valid = false;
 					}
@@ -3405,7 +3405,7 @@ void Variant::get_property_list(List<PropertyInfo> *p_list) const {
 			if (obj) {
 #ifdef DEBUG_ENABLED
 
-				if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+				if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
 					WARN_PRINT("Attempted get_property list on previously freed instance.");
 					return;
 				}
@@ -3490,7 +3490,7 @@ bool Variant::iter_init(Variant &r_iter, bool &valid) const {
 
 #ifdef DEBUG_ENABLED
 
-			if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+			if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
 				valid = false;
 				return false;
 			}
@@ -3676,7 +3676,7 @@ bool Variant::iter_next(Variant &r_iter, bool &valid) const {
 
 #ifdef DEBUG_ENABLED
 
-			if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+			if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
 				valid = false;
 				return false;
 			}
@@ -3855,7 +3855,7 @@ Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const {
 				return Variant();
 			}
 #ifdef DEBUG_ENABLED
-			if (ScriptDebugger::get_singleton() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+			if (EngineDebugger::is_active() && !_get_obj().id.is_reference() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
 				r_valid = false;
 				return Variant();
 			}

+ 7 - 7
doc/classes/ProjectSettings.xml

@@ -824,17 +824,17 @@
 		</member>
 		<member name="mono/unhandled_exception_policy" type="int" setter="" getter="" default="0">
 		</member>
-		<member name="network/limits/debugger_stdout/max_chars_per_second" type="int" setter="" getter="" default="2048">
+		<member name="network/limits/debugger/max_chars_per_second" type="int" setter="" getter="" default="2048">
 			Maximum amount of characters allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
 		</member>
-		<member name="network/limits/debugger_stdout/max_errors_per_second" type="int" setter="" getter="" default="100">
-			Maximum number of errors allowed to be sent as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
+		<member name="network/limits/debugger/max_errors_per_second" type="int" setter="" getter="" default="100">
+			Maximum number of errors allowed to be sent from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
 		</member>
-		<member name="network/limits/debugger_stdout/max_messages_per_frame" type="int" setter="" getter="" default="10">
-			Maximum amount of messages allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
+		<member name="network/limits/debugger/max_queued_messages" type="int" setter="" getter="" default="10">
+			Maximum amount of messages in the debugger queue. Over this value, content is dropped. This helps to limit the debugger memory usage.
 		</member>
-		<member name="network/limits/debugger_stdout/max_warnings_per_second" type="int" setter="" getter="" default="100">
-			Maximum number of warnings allowed to be sent as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
+		<member name="network/limits/debugger/max_warnings_per_second" type="int" setter="" getter="" default="100">
+			Maximum number of warnings allowed to be sent from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
 		</member>
 		<member name="network/limits/packet_peer_stream/max_buffer_po2" type="int" setter="" getter="" default="16">
 			Default size of packet peer stream for deserializing Godot data. Over this size, data is dropped.

+ 6 - 4
drivers/unix/os_unix.cpp

@@ -32,6 +32,8 @@
 
 #ifdef UNIX_ENABLED
 
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/script_debugger.h"
 #include "core/os/thread_dummy.h"
 #include "core/project_settings.h"
 #include "drivers/unix/dir_access_unix.h"
@@ -94,16 +96,16 @@ void OS_Unix::debug_break() {
 };
 
 static void handle_interrupt(int sig) {
-	if (ScriptDebugger::get_singleton() == NULL)
+	if (!EngineDebugger::is_active())
 		return;
 
-	ScriptDebugger::get_singleton()->set_depth(-1);
-	ScriptDebugger::get_singleton()->set_lines_left(1);
+	EngineDebugger::get_script_debugger()->set_depth(-1);
+	EngineDebugger::get_script_debugger()->set_lines_left(1);
 }
 
 void OS_Unix::initialize_debugging() {
 
-	if (ScriptDebugger::get_singleton() != NULL) {
+	if (EngineDebugger::is_active()) {
 		struct sigaction action;
 		memset(&action, 0, sizeof(action));
 		action.sa_handler = handle_interrupt;

+ 2 - 2
editor/debugger/editor_debugger_inspector.cpp

@@ -30,8 +30,8 @@
 
 #include "editor_debugger_inspector.h"
 
+#include "core/debugger/debugger_marshalls.h"
 #include "core/io/marshalls.h"
-#include "core/script_debugger_remote.h"
 #include "editor/editor_node.h"
 #include "scene/debugger/scene_debugger.h"
 
@@ -226,7 +226,7 @@ Object *EditorDebuggerInspector::get_object(ObjectID p_id) {
 
 void EditorDebuggerInspector::add_stack_variable(const Array &p_array) {
 
-	ScriptDebuggerRemote::ScriptStackVariable var;
+	DebuggerMarshalls::ScriptStackVariable var;
 	var.deserialize(p_array);
 	String n = var.name;
 	Variant v = var.value;

+ 36 - 9
editor/debugger/editor_debugger_node.cpp

@@ -31,9 +31,12 @@
 #include "editor_debugger_node.h"
 
 #include "editor/debugger/editor_debugger_tree.h"
+#include "editor/debugger/script_editor_debugger.h"
 #include "editor/editor_log.h"
 #include "editor/editor_node.h"
 #include "editor/plugins/script_editor_plugin.h"
+#include "scene/gui/menu_button.h"
+#include "scene/gui/tab_container.h"
 
 template <typename Func>
 void _for_all(TabContainer *p_node, const Func &p_func) {
@@ -49,7 +52,6 @@ EditorDebuggerNode *EditorDebuggerNode::singleton = NULL;
 EditorDebuggerNode::EditorDebuggerNode() {
 	if (!singleton)
 		singleton = this;
-	server.instance();
 
 	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));
@@ -179,10 +181,9 @@ Error EditorDebuggerNode::start() {
 		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);
+	server = Ref<EditorDebuggerServer>(EditorDebuggerServer::create_default());
+	const Error err = server->start();
 	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);
@@ -191,9 +192,10 @@ Error EditorDebuggerNode::start() {
 }
 
 void EditorDebuggerNode::stop() {
-	if (server->is_listening()) {
+	if (server.is_valid()) {
 		server->stop();
 		EditorNode::get_log()->add_message("--- Debugging process stopped ---", EditorLog::MSG_TYPE_EDITOR);
+		server.unref();
 	}
 	// Also close all debugging sessions.
 	_for_all(tabs, [&](ScriptEditorDebugger *dbg) {
@@ -231,9 +233,15 @@ void EditorDebuggerNode::_notification(int p_what) {
 			break;
 	}
 
-	if (p_what != NOTIFICATION_PROCESS || !server->is_listening())
+	if (p_what != NOTIFICATION_PROCESS || !server.is_valid())
 		return;
 
+	if (!server.is_valid() || !server->is_active()) {
+		stop();
+		return;
+	}
+	server->poll();
+
 	// Errors and warnings
 	int error_count = 0;
 	int warning_count = 0;
@@ -293,9 +301,8 @@ void EditorDebuggerNode::_notification(int p_what) {
 			if (tabs->get_tab_count() <= 4) { // Max 4 debugging sessions active.
 				debugger = _add_debugger();
 			} 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();
+				// We already have too many sessions, disconnecting new clients to prevent them from hanging.
+				server->take_connection()->close();
 				return; // Can't add, stop here.
 			}
 		}
@@ -462,6 +469,26 @@ void EditorDebuggerNode::reload_scripts() {
 	});
 }
 
+void EditorDebuggerNode::debug_next() {
+	get_default_debugger()->debug_next();
+}
+
+void EditorDebuggerNode::debug_step() {
+	get_default_debugger()->debug_step();
+}
+
+void EditorDebuggerNode::debug_break() {
+	get_default_debugger()->debug_break();
+}
+
+void EditorDebuggerNode::debug_continue() {
+	get_default_debugger()->debug_continue();
+}
+
+String EditorDebuggerNode::get_var_value(const String &p_var) const {
+	return get_default_debugger()->get_var_value(p_var);
+}
+
 // LiveEdit/Inspector
 void EditorDebuggerNode::request_remote_tree() {
 	get_current_debugger()->request_remote_tree();

+ 26 - 13
editor/debugger/editor_debugger_node.h

@@ -31,17 +31,30 @@
 #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"
+#include "editor/debugger/editor_debugger_server.h"
+#include "scene/gui/margin_container.h"
 
+class Button;
 class EditorDebuggerTree;
+class EditorDebuggerRemoteObject;
+class MenuButton;
+class ScriptEditorDebugger;
+class TabContainer;
 
 class EditorDebuggerNode : public MarginContainer {
 
 	GDCLASS(EditorDebuggerNode, MarginContainer);
 
+public:
+	enum CameraOverride {
+		OVERRIDE_NONE,
+		OVERRIDE_2D,
+		OVERRIDE_3D_1, // 3D Viewport 1
+		OVERRIDE_3D_2, // 3D Viewport 2
+		OVERRIDE_3D_3, // 3D Viewport 3
+		OVERRIDE_3D_4 // 3D Viewport 4
+	};
+
 private:
 	enum Options {
 		DEBUG_NEXT,
@@ -71,7 +84,7 @@ private:
 		}
 	};
 
-	Ref<TCP_Server> server = NULL;
+	Ref<EditorDebuggerServer> server;
 	TabContainer *tabs = NULL;
 	Button *debugger_button = NULL;
 	MenuButton *script_menu = NULL;
@@ -87,7 +100,7 @@ private:
 	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;
+	CameraOverride camera_override = OVERRIDE_NONE;
 	Map<Breakpoint, bool> breakpoints;
 
 	ScriptEditorDebugger *_add_debugger();
@@ -130,10 +143,10 @@ public:
 	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 debug_next();
+	void debug_step();
+	void debug_break();
+	void debug_continue();
 
 	void set_script_debug_button(MenuButton *p_button);
 
@@ -141,7 +154,7 @@ public:
 		debugger_button = p_button;
 	}
 
-	String get_var_value(const String &p_var) const { return get_default_debugger()->get_var_value(p_var); }
+	String get_var_value(const String &p_var) const;
 	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; }
@@ -167,8 +180,8 @@ public:
 	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; }
+	void set_camera_override(CameraOverride p_override) { camera_override = p_override; }
+	CameraOverride get_camera_override() { return camera_override; }
 
 	Error start();
 

+ 90 - 0
editor/debugger/editor_debugger_server.cpp

@@ -0,0 +1,90 @@
+/*************************************************************************/
+/*  editor_debugger_server.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_server.h"
+
+#include "core/io/marshalls.h"
+#include "core/io/tcp_server.h"
+#include "core/os/mutex.h"
+#include "core/os/thread.h"
+#include "editor/editor_log.h"
+#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
+
+class EditorDebuggerServerTCP : public EditorDebuggerServer {
+
+private:
+	Ref<TCP_Server> server;
+
+public:
+	virtual void poll() {}
+	virtual Error start();
+	virtual void stop();
+	virtual bool is_active() const;
+	virtual bool is_connection_available() const;
+	virtual Ref<RemoteDebuggerPeer> take_connection();
+
+	EditorDebuggerServerTCP();
+};
+
+EditorDebuggerServerTCP::EditorDebuggerServerTCP() {
+	server.instance();
+}
+
+Error EditorDebuggerServerTCP::start() {
+	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;
+	}
+	return err;
+}
+
+void EditorDebuggerServerTCP::stop() {
+	server->stop();
+}
+
+bool EditorDebuggerServerTCP::is_active() const {
+	return server->is_listening();
+}
+
+bool EditorDebuggerServerTCP::is_connection_available() const {
+	return server->is_listening() && server->is_connection_available();
+}
+
+Ref<RemoteDebuggerPeer> EditorDebuggerServerTCP::take_connection() {
+	ERR_FAIL_COND_V(!is_connection_available(), Ref<RemoteDebuggerPeer>());
+	return memnew(RemoteDebuggerPeerTCP(server->take_connection()));
+}
+
+EditorDebuggerServer *EditorDebuggerServer::create_default() {
+	return memnew(EditorDebuggerServerTCP);
+}

+ 49 - 0
editor/debugger/editor_debugger_server.h

@@ -0,0 +1,49 @@
+/*************************************************************************/
+/*  editor_debugger_server.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_CONNECTION_H
+#define EDITOR_DEBUGGER_CONNECTION_H
+
+#include "core/debugger/remote_debugger_peer.h"
+#include "core/reference.h"
+
+class EditorDebuggerServer : public Reference {
+
+public:
+	static EditorDebuggerServer *create_default();
+	virtual void poll() = 0;
+	virtual Error start() = 0;
+	virtual void stop() = 0;
+	virtual bool is_active() const = 0;
+	virtual bool is_connection_available() const = 0;
+	virtual Ref<RemoteDebuggerPeer> take_connection() = 0;
+};
+
+#endif // EDITOR_DEBUGGER_CONNECTION_H

+ 4 - 4
editor/editor_network_profiler.cpp → editor/debugger/editor_network_profiler.cpp

@@ -31,8 +31,8 @@
 #include "editor_network_profiler.h"
 
 #include "core/os/os.h"
-#include "editor_scale.h"
-#include "editor_settings.h"
+#include "editor/editor_scale.h"
+#include "editor/editor_settings.h"
 
 void EditorNetworkProfiler::_bind_methods() {
 	ADD_SIGNAL(MethodInfo("enable_profiling", PropertyInfo(Variant::BOOL, "enable")));
@@ -58,7 +58,7 @@ void EditorNetworkProfiler::_update_frame() {
 
 	TreeItem *root = counters_display->create_item();
 
-	for (Map<ObjectID, MultiplayerAPI::ProfilingInfo>::Element *E = nodes_data.front(); E; E = E->next()) {
+	for (Map<ObjectID, DebuggerMarshalls::MultiplayerNodeInfo>::Element *E = nodes_data.front(); E; E = E->next()) {
 
 		TreeItem *node = counters_display->create_item(root);
 
@@ -95,7 +95,7 @@ void EditorNetworkProfiler::_clear_pressed() {
 	}
 }
 
-void EditorNetworkProfiler::add_node_frame_data(const MultiplayerAPI::ProfilingInfo p_frame) {
+void EditorNetworkProfiler::add_node_frame_data(const DebuggerMarshalls::MultiplayerNodeInfo p_frame) {
 
 	if (!nodes_data.has(p_frame.node)) {
 		nodes_data.insert(p_frame.node, p_frame);

+ 3 - 2
editor/editor_network_profiler.h → editor/debugger/editor_network_profiler.h

@@ -31,6 +31,7 @@
 #ifndef EDITORNETWORKPROFILER_H
 #define EDITORNETWORKPROFILER_H
 
+#include "core/debugger/debugger_marshalls.h"
 #include "scene/gui/box_container.h"
 #include "scene/gui/button.h"
 #include "scene/gui/label.h"
@@ -50,7 +51,7 @@ private:
 
 	Timer *frame_delay;
 
-	Map<ObjectID, MultiplayerAPI::ProfilingInfo> nodes_data;
+	Map<ObjectID, DebuggerMarshalls::MultiplayerNodeInfo> nodes_data;
 
 	void _update_frame();
 
@@ -62,7 +63,7 @@ protected:
 	static void _bind_methods();
 
 public:
-	void add_node_frame_data(const MultiplayerAPI::ProfilingInfo p_frame);
+	void add_node_frame_data(const DebuggerMarshalls::MultiplayerNodeInfo p_frame);
 	void set_bandwidth(int p_incoming, int p_outgoing);
 	bool is_profiling();
 

+ 2 - 2
editor/editor_profiler.cpp → editor/debugger/editor_profiler.cpp

@@ -31,8 +31,8 @@
 #include "editor_profiler.h"
 
 #include "core/os/os.h"
-#include "editor_scale.h"
-#include "editor_settings.h"
+#include "editor/editor_scale.h"
+#include "editor/editor_settings.h"
 
 void EditorProfiler::_make_metric_ptrs(Metric &m) {
 

+ 0 - 0
editor/editor_profiler.h → editor/debugger/editor_profiler.h


+ 2 - 2
editor/editor_visual_profiler.cpp → editor/debugger/editor_visual_profiler.cpp

@@ -31,8 +31,8 @@
 #include "editor_visual_profiler.h"
 
 #include "core/os/os.h"
-#include "editor_scale.h"
-#include "editor_settings.h"
+#include "editor/editor_scale.h"
+#include "editor/editor_settings.h"
 
 void EditorVisualProfiler::add_frame_metric(const Metric &p_metric) {
 

+ 0 - 0
editor/editor_visual_profiler.h → editor/debugger/editor_visual_profiler.h


+ 138 - 182
editor/debugger/script_editor_debugger.cpp

@@ -30,21 +30,22 @@
 
 #include "script_editor_debugger.h"
 
+#include "core/debugger/debugger_marshalls.h"
 #include "core/io/marshalls.h"
 #include "core/project_settings.h"
-#include "core/script_debugger_remote.h"
 #include "core/ustring.h"
+#include "editor/debugger/editor_network_profiler.h"
+#include "editor/debugger/editor_profiler.h"
+#include "editor/debugger/editor_visual_profiler.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/property_editor.h"
 #include "main/performance.h"
+#include "scene/3d/camera.h"
 #include "scene/debugger/scene_debugger.h"
 #include "scene/gui/dialogs.h"
 #include "scene/gui/label.h"
@@ -58,12 +59,14 @@
 #include "scene/gui/tree.h"
 #include "scene/resources/packed_scene.h"
 
+using CameraOverride = EditorDebuggerNode::CameraOverride;
+
 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);
+		peer->put_message(msg);
 	}
 }
 
@@ -141,7 +144,7 @@ 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);
+	_put_msg("scene:save_node", msg);
 }
 
 void ScriptEditorDebugger::_file_selected(const String &p_file) {
@@ -183,7 +186,7 @@ void ScriptEditorDebugger::_file_selected(const String &p_file) {
 
 void ScriptEditorDebugger::request_remote_tree() {
 
-	_put_msg("request_scene_tree", Array());
+	_put_msg("scene:request_scene_tree", Array());
 }
 
 const SceneDebuggerTree *ScriptEditorDebugger::get_remote_tree() {
@@ -196,7 +199,7 @@ void ScriptEditorDebugger::update_remote_object(ObjectID p_obj_id, const String
 	msg.push_back(p_obj_id);
 	msg.push_back(p_prop);
 	msg.push_back(p_value);
-	_put_msg("set_object_property", msg);
+	_put_msg("scene:set_object_property", msg);
 }
 
 void ScriptEditorDebugger::request_remote_object(ObjectID p_obj_id) {
@@ -204,7 +207,7 @@ void ScriptEditorDebugger::request_remote_object(ObjectID p_obj_id) {
 	ERR_FAIL_COND(p_obj_id.is_null());
 	Array msg;
 	msg.push_back(p_obj_id);
-	_put_msg("inspect_object", msg);
+	_put_msg("scene:inspect_object", msg);
 }
 
 Object *ScriptEditorDebugger::get_remote_object(ObjectID p_id) {
@@ -226,7 +229,7 @@ void ScriptEditorDebugger::_remote_object_property_updated(ObjectID p_id, const
 
 void ScriptEditorDebugger::_video_mem_request() {
 
-	_put_msg("request_video_mem", Array());
+	_put_msg("core:memory", Array());
 }
 
 Size2 ScriptEditorDebugger::get_minimum_size() const {
@@ -267,36 +270,36 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
 		emit_signal("breaked", false, false);
 		profiler->set_enabled(true);
 		profiler->disable_seeking();
-	} else if (p_msg == "message:set_pid") {
+	} else if (p_msg == "set_pid") {
 
 		ERR_FAIL_COND(p_data.size() < 1);
 		remote_pid = p_data[0];
-	} else if (p_msg == "message:click_ctrl") {
+	} else if (p_msg == "scene: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") {
+	} else if (p_msg == "scene:scene_tree") {
 
 		scene_tree->nodes.clear();
 		scene_tree->deserialize(p_data);
 		emit_signal("remote_tree_updated");
 		_update_buttons_state();
-	} else if (p_msg == "message:inspect_object") {
+	} else if (p_msg == "scene:inspect_object") {
 
 		ObjectID id = inspector->add_object(p_data);
 		if (id.is_valid())
 			emit_signal("remote_object_updated", id);
-	} else if (p_msg == "message:video_mem") {
+	} else if (p_msg == "memory:usage") {
 
 		vmem_tree->clear();
 		TreeItem *root = vmem_tree->create_item();
-		ScriptDebuggerRemote::ResourceUsage usage;
+		DebuggerMarshalls::ResourceUsage usage;
 		usage.deserialize(p_data);
 
 		int total = 0;
 
-		for (List<ScriptDebuggerRemote::ResourceInfo>::Element *E = usage.infos.front(); E; E = E->next()) {
+		for (List<DebuggerMarshalls::ResourceInfo>::Element *E = usage.infos.front(); E; E = E->next()) {
 
 			TreeItem *it = vmem_tree->create_item(root);
 			String type = E->get().type;
@@ -316,7 +319,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
 
 	} else if (p_msg == "stack_dump") {
 
-		ScriptDebuggerRemote::ScriptStackDump stack;
+		DebuggerMarshalls::ScriptStackDump stack;
 		stack.deserialize(p_data);
 
 		stack_dump->clear();
@@ -349,10 +352,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
 
 	} else if (p_msg == "output") {
 		ERR_FAIL_COND(p_data.size() < 1);
-		String t = p_data[0];
-		EditorNode::get_log()->add_message(t);
-
-	} else if (p_msg == "performance") {
+		ERR_FAIL_COND(p_data[0].get_type() != Variant::PACKED_STRING_ARRAY);
+		Vector<String> strings = p_data[0];
+		EditorNode::get_log()->add_message(String("\n").join(strings));
+	} else if (p_msg == "performance:profile_frame") {
 		Vector<float> p;
 		p.resize(p_data.size());
 		for (int i = 0; i < p_data.size(); i++) {
@@ -385,36 +388,28 @@ 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];
+	} else if (p_msg == "visual:profile_frame") {
+		DebuggerMarshalls::VisualProfilerFrame frame;
+		frame.deserialize(p_data);
 
 		EditorVisualProfiler::Metric metric;
-		metric.areas.resize(names.size());
-		metric.frame_number = frame;
+		metric.areas.resize(frame.areas.size());
+		metric.frame_number = frame.frame_number;
 		metric.valid = true;
 
 		{
 			EditorVisualProfiler::Metric::Area *areas_ptr = metric.areas.ptrw();
-			int metric_count = names.size();
-
-			const String *rs = names.ptr();
-			const real_t *rr = values.ptr();
-
-			for (int i = 0; i < metric_count; i++) {
-
-				areas_ptr[i].name = rs[i];
-				areas_ptr[i].cpu_time = rr[i * 2 + 0];
-				areas_ptr[i].gpu_time = rr[i * 2 + 1];
+			for (int i = 0; i < frame.areas.size(); i++) {
+				areas_ptr[i].name = frame.areas[i].name;
+				areas_ptr[i].cpu_time = frame.areas[i].cpu_msec;
+				areas_ptr[i].gpu_time = frame.areas[i].gpu_msec;
 			}
 		}
 		visual_profiler->add_frame_metric(metric);
 
 	} else if (p_msg == "error") {
 
-		ScriptDebuggerRemote::OutputError oe;
+		DebuggerMarshalls::OutputError oe;
 		ERR_FAIL_COND_MSG(oe.deserialize(p_data) == false, "Failed to deserialize error message");
 
 		// Format time.
@@ -520,15 +515,15 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
 		else
 			error_count++;
 
-	} else if (p_msg == "profile_sig") {
+	} else if (p_msg == "servers:function_signature") {
 		// Cache a profiler signature.
-		ScriptDebuggerRemote::ProfilerSignature sig;
+		DebuggerMarshalls::ScriptFunctionSignature sig;
 		sig.deserialize(p_data);
 		profiler_signature[sig.id] = sig.name;
 
-	} else if (p_msg == "profile_frame" || p_msg == "profile_total") {
+	} else if (p_msg == "servers:profile_frame" || p_msg == "servers:profile_total") {
 		EditorProfiler::Metric metric;
-		ScriptDebuggerRemote::ProfilerFrame frame;
+		DebuggerMarshalls::ServersProfilerFrame frame;
 		frame.deserialize(p_data);
 		metric.valid = true;
 		metric.frame_number = frame.frame_number;
@@ -536,10 +531,8 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
 		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) {
+		if (frame.servers.size()) {
 			EditorProfiler::Metric::Category frame_time;
 			frame_time.signature = "category_frame_time";
 			frame_time.name = "Frame Time";
@@ -573,42 +566,42 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
 			metric.categories.push_back(frame_time);
 		}
 
-		for (int i = 0; i < frame_data_amount; i++) {
+		for (int i = 0; i < frame.servers.size(); i++) {
 
+			const DebuggerMarshalls::ServerInfo &srv = frame.servers[i];
 			EditorProfiler::Metric::Category c;
-			String name = frame.frames_data[i].name;
-			Array values = frame.frames_data[i].data;
+			const String name = srv.name;
 			c.name = name.capitalize();
-			c.items.resize(values.size() / 2);
+			c.items.resize(srv.functions.size());
 			c.total_time = 0;
 			c.signature = "categ::" + name;
-			for (int j = 0; j < values.size(); j += 2) {
+			for (int j = 0; j < srv.functions.size(); j++) {
 
 				EditorProfiler::Metric::Category::Item item;
 				item.calls = 1;
 				item.line = 0;
-				item.name = values[j];
-				item.self = values[j + 1];
+				item.name = srv.functions[j].name;
+				item.self = srv.functions[j].time;
 				item.total = item.self;
 				item.signature = "categ::" + name + "::" + item.name;
 				item.name = item.name.capitalize();
 				c.total_time += item.total;
-				c.items.write[j / 2] = item;
+				c.items.write[j] = item;
 			}
 			metric.categories.push_back(c);
 		}
 
 		EditorProfiler::Metric::Category funcs;
 		funcs.total_time = frame.script_time;
-		funcs.items.resize(frame_function_amount);
+		funcs.items.resize(frame.script_functions.size());
 		funcs.name = "Script Functions";
 		funcs.signature = "script_functions";
-		for (int i = 0; i < frame_function_amount; i++) {
+		for (int i = 0; i < frame.script_functions.size(); i++) {
 
-			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;
+			int signature = frame.script_functions[i].sig_id;
+			int calls = frame.script_functions[i].call_count;
+			float total = frame.script_functions[i].total_time;
+			float self = frame.script_functions[i].self_time;
 
 			EditorProfiler::Metric::Category::Item item;
 			if (profiler_signature.has(signature)) {
@@ -639,24 +632,28 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
 
 		metric.categories.push_back(funcs);
 
-		if (p_msg == "profile_frame")
+		if (p_msg == "servers:profile_frame")
 			profiler->add_frame_metric(metric, false);
 		else
 			profiler->add_frame_metric(metric, true);
 
-	} else if (p_msg == "network_profile") {
-		ScriptDebuggerRemote::NetworkProfilerFrame frame;
+	} else if (p_msg == "network:profile_frame") {
+		DebuggerMarshalls::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") {
+
+	} 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") {
 
+	} else if (p_msg == "request_quit") {
 		emit_signal("stop_requested");
 		_stop_and_notify();
+
+	} else {
+		WARN_PRINT("unknown message " + p_msg);
 	}
 }
 
@@ -776,7 +773,7 @@ void ScriptEditorDebugger::_notification(int p_what) {
 
 			if (is_session_active()) {
 
-				if (camera_override == OVERRIDE_2D) {
+				if (camera_override == CameraOverride::OVERRIDE_2D) {
 					CanvasItemEditor *editor = CanvasItemEditor::get_singleton();
 
 					Dictionary state = editor->get_state();
@@ -789,10 +786,10 @@ void ScriptEditorDebugger::_notification(int p_what) {
 
 					Array msg;
 					msg.push_back(transform);
-					_put_msg("override_camera_2D:transform", msg);
+					_put_msg("scene:override_camera_2D:transform", msg);
 
-				} else if (camera_override >= OVERRIDE_3D_1) {
-					int viewport_idx = camera_override - OVERRIDE_3D_1;
+				} else if (camera_override >= CameraOverride::OVERRIDE_3D_1) {
+					int viewport_idx = camera_override - CameraOverride::OVERRIDE_3D_1;
 					SpatialEditorViewport *viewport = SpatialEditor::get_singleton()->get_editor_viewport(viewport_idx);
 					Camera *const cam = viewport->get_camera();
 
@@ -807,34 +804,15 @@ void ScriptEditorDebugger::_notification(int p_what) {
 					}
 					msg.push_back(cam->get_znear());
 					msg.push_back(cam->get_zfar());
-					_put_msg("override_camera_3D:transform", msg);
+					_put_msg("scene:override_camera_3D:transform", msg);
 				}
 			}
 
-			if (!is_session_active()) {
-				_stop_and_notify();
-				break;
-			};
-
-			if (ppeer->get_available_packet_count() <= 0) {
-				break;
-			};
-
 			const uint64_t until = OS::get_singleton()->get_ticks_msec() + 20;
 
-			while (ppeer->get_available_packet_count() > 0) {
+			while (peer->has_message()) {
 
-				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;
+				Array arr = peer->get_message();
 				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");
@@ -844,6 +822,10 @@ void ScriptEditorDebugger::_notification(int p_what) {
 				if (OS::get_singleton()->get_ticks_msec() > until)
 					break;
 			}
+			if (!is_session_active()) {
+				_stop_and_notify();
+				break;
+			};
 		} break;
 		case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
 
@@ -875,14 +857,14 @@ void ScriptEditorDebugger::_clear_execution() {
 	inspector->clear_stack_variables();
 }
 
-void ScriptEditorDebugger::start(Ref<StreamPeerTCP> p_connection) {
+void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) {
 
 	error_count = 0;
 	warning_count = 0;
 	stop();
 
-	connection = p_connection;
-	ppeer->set_stream_peer(connection);
+	peer = p_peer;
+	ERR_FAIL_COND(p_peer.is_null());
 
 	perf_history.clear();
 	for (int i = 0; i < Performance::MONITOR_MAX; i++) {
@@ -893,19 +875,11 @@ void ScriptEditorDebugger::start(Ref<StreamPeerTCP> p_connection) {
 	set_process(true);
 	breaked = false;
 	can_debug = true;
-	camera_override = OVERRIDE_NONE;
+	camera_override = CameraOverride::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::_update_buttons_state() {
@@ -936,10 +910,10 @@ void ScriptEditorDebugger::stop() {
 	_clear_execution();
 
 	inspector->clear_cache();
-	ppeer->set_stream_peer(Ref<StreamPeer>());
 
-	if (connection.is_valid()) {
-		connection.unref();
+	if (peer.is_valid()) {
+		peer->close();
+		peer.unref();
 		reason->set_text("");
 		reason->set_tooltip("");
 	}
@@ -952,49 +926,31 @@ void ScriptEditorDebugger::stop() {
 	_update_buttons_state();
 }
 
-void ScriptEditorDebugger::_profiler_activate(bool p_enable) {
-
-	if (p_enable) {
-		profiler_signature.clear();
-		Array msg;
-		int max_funcs = EditorSettings::get_singleton()->get("debugger/profiler_frame_max_functions");
-		max_funcs = CLAMP(max_funcs, 16, 512);
-		msg.push_back(max_funcs);
-		_put_msg("start_profiling", msg);
-		print_verbose("Starting profiling.");
-
-	} else {
-		_put_msg("stop_profiling", Array());
-		print_verbose("Ending profiling.");
-	}
-}
-
-void ScriptEditorDebugger::_visual_profiler_activate(bool p_enable) {
+void ScriptEditorDebugger::_profiler_activate(bool p_enable, int p_type) {
 
-	if (!connection.is_valid())
-		return;
-
-	if (p_enable) {
-		profiler_signature.clear();
-		_put_msg("start_visual_profiling", Array());
-		print_verbose("Starting visual profiling.");
-
-	} else {
-		_put_msg("stop_visual_profiling", Array());
-		print_verbose("Ending visual profiling.");
-	}
-}
-
-void ScriptEditorDebugger::_network_profiler_activate(bool p_enable) {
-
-	if (p_enable) {
-		profiler_signature.clear();
-		_put_msg("start_network_profiling", Array());
-		print_verbose("Starting network profiling.");
-
-	} else {
-		_put_msg("stop_network_profiling", Array());
-		print_verbose("Ending network profiling.");
+	Array data;
+	data.push_back(p_enable);
+	switch (p_type) {
+		case PROFILER_NETWORK:
+			_put_msg("profiler:network", data);
+			break;
+		case PROFILER_VISUAL:
+			_put_msg("profiler:visual", data);
+			break;
+		case PROFILER_SCRIPTS_SERVERS:
+			if (p_enable) {
+				// Clear old script signatures. (should we move all this into the profiler?)
+				profiler_signature.clear();
+				// Add max funcs options to request.
+				Array opts;
+				int max_funcs = EditorSettings::get_singleton()->get("debugger/profiler_frame_max_functions");
+				opts.push_back(CLAMP(max_funcs, 16, 512));
+				data.push_back(opts);
+			}
+			_put_msg("profiler:servers", data);
+			break;
+		default:
+			ERR_FAIL_MSG("Invalid profiler type");
 	}
 }
 
@@ -1045,7 +1001,7 @@ int ScriptEditorDebugger::_get_node_path_cache(const NodePath &p_path) {
 	Array msg;
 	msg.push_back(p_path);
 	msg.push_back(last_path_id);
-	_put_msg("live_node_path", msg);
+	_put_msg("scene:live_node_path", msg);
 
 	return last_path_id;
 }
@@ -1063,7 +1019,7 @@ int ScriptEditorDebugger::_get_res_path_cache(const String &p_path) {
 	Array msg;
 	msg.push_back(p_path);
 	msg.push_back(last_path_id);
-	_put_msg("live_res_path", msg);
+	_put_msg("scene:live_res_path", msg);
 
 	return last_path_id;
 }
@@ -1095,7 +1051,7 @@ void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_n
 			//no pointers, sorry
 			msg.push_back(*argptr[i]);
 		}
-		_put_msg("live_node_call", msg);
+		_put_msg("scene:live_node_call", msg);
 
 		return;
 	}
@@ -1114,7 +1070,7 @@ void ScriptEditorDebugger::_method_changed(Object *p_base, const StringName &p_n
 			//no pointers, sorry
 			msg.push_back(*argptr[i]);
 		}
-		_put_msg("live_res_call", msg);
+		_put_msg("scene:live_res_call", msg);
 
 		return;
 	}
@@ -1140,7 +1096,7 @@ void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p
 				msg.push_back(pathid);
 				msg.push_back(p_property);
 				msg.push_back(res->get_path());
-				_put_msg("live_node_prop_res", msg);
+				_put_msg("scene:live_node_prop_res", msg);
 			}
 		} else {
 
@@ -1148,7 +1104,7 @@ void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p
 			msg.push_back(pathid);
 			msg.push_back(p_property);
 			msg.push_back(p_value);
-			_put_msg("live_node_prop", msg);
+			_put_msg("scene:live_node_prop", msg);
 		}
 
 		return;
@@ -1169,7 +1125,7 @@ void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p
 				msg.push_back(pathid);
 				msg.push_back(p_property);
 				msg.push_back(res2->get_path());
-				_put_msg("live_res_prop_res", msg);
+				_put_msg("scene:live_res_prop_res", msg);
 			}
 		} else {
 
@@ -1177,7 +1133,7 @@ void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p
 			msg.push_back(pathid);
 			msg.push_back(p_property);
 			msg.push_back(p_value);
-			_put_msg("live_res_prop", msg);
+			_put_msg("scene:live_res_prop", msg);
 		}
 
 		return;
@@ -1255,7 +1211,7 @@ void ScriptEditorDebugger::update_live_edit_root() {
 		msg.push_back(editor->get_edited_scene()->get_filename());
 	else
 		msg.push_back("");
-	_put_msg("live_set_root", msg);
+	_put_msg("scene:live_set_root", msg);
 	live_edit_root->set_text(np);
 }
 
@@ -1266,7 +1222,7 @@ void ScriptEditorDebugger::live_debug_create_node(const NodePath &p_parent, cons
 		msg.push_back(p_parent);
 		msg.push_back(p_type);
 		msg.push_back(p_name);
-		_put_msg("live_create_node", msg);
+		_put_msg("scene:live_create_node", msg);
 	}
 }
 
@@ -1277,7 +1233,7 @@ void ScriptEditorDebugger::live_debug_instance_node(const NodePath &p_parent, co
 		msg.push_back(p_parent);
 		msg.push_back(p_path);
 		msg.push_back(p_name);
-		_put_msg("live_instance_node", msg);
+		_put_msg("scene:live_instance_node", msg);
 	}
 }
 void ScriptEditorDebugger::live_debug_remove_node(const NodePath &p_at) {
@@ -1285,7 +1241,7 @@ void ScriptEditorDebugger::live_debug_remove_node(const NodePath &p_at) {
 	if (live_debug) {
 		Array msg;
 		msg.push_back(p_at);
-		_put_msg("live_remove_node", msg);
+		_put_msg("scene:live_remove_node", msg);
 	}
 }
 void ScriptEditorDebugger::live_debug_remove_and_keep_node(const NodePath &p_at, ObjectID p_keep_id) {
@@ -1294,7 +1250,7 @@ void ScriptEditorDebugger::live_debug_remove_and_keep_node(const NodePath &p_at,
 		Array msg;
 		msg.push_back(p_at);
 		msg.push_back(p_keep_id);
-		_put_msg("live_remove_and_keep_node", msg);
+		_put_msg("scene:live_remove_and_keep_node", msg);
 	}
 }
 void ScriptEditorDebugger::live_debug_restore_node(ObjectID p_id, const NodePath &p_at, int p_at_pos) {
@@ -1304,7 +1260,7 @@ void ScriptEditorDebugger::live_debug_restore_node(ObjectID p_id, const NodePath
 		msg.push_back(p_id);
 		msg.push_back(p_at);
 		msg.push_back(p_at_pos);
-		_put_msg("live_restore_node", msg);
+		_put_msg("scene:live_restore_node", msg);
 	}
 }
 void ScriptEditorDebugger::live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name) {
@@ -1313,7 +1269,7 @@ void ScriptEditorDebugger::live_debug_duplicate_node(const NodePath &p_at, const
 		Array msg;
 		msg.push_back(p_at);
 		msg.push_back(p_new_name);
-		_put_msg("live_duplicate_node", msg);
+		_put_msg("scene: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) {
@@ -1324,32 +1280,32 @@ void ScriptEditorDebugger::live_debug_reparent_node(const NodePath &p_at, const
 		msg.push_back(p_new_place);
 		msg.push_back(p_new_name);
 		msg.push_back(p_at_pos);
-		_put_msg("live_reparent_node", msg);
+		_put_msg("scene:live_reparent_node", msg);
 	}
 }
 
-ScriptEditorDebugger::CameraOverride ScriptEditorDebugger::get_camera_override() const {
+CameraOverride ScriptEditorDebugger::get_camera_override() const {
 	return camera_override;
 }
 
 void ScriptEditorDebugger::set_camera_override(CameraOverride p_override) {
 
-	if (p_override == OVERRIDE_2D && camera_override != OVERRIDE_2D) {
+	if (p_override == CameraOverride::OVERRIDE_2D && camera_override != CameraOverride::OVERRIDE_2D) {
 		Array msg;
 		msg.push_back(true);
-		_put_msg("override_camera_2D:set", msg);
-	} else if (p_override != OVERRIDE_2D && camera_override == OVERRIDE_2D) {
+		_put_msg("scene:override_camera_2D:set", msg);
+	} else if (p_override != CameraOverride::OVERRIDE_2D && camera_override == CameraOverride::OVERRIDE_2D) {
 		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) {
+		_put_msg("scene:override_camera_2D:set", msg);
+	} else if (p_override >= CameraOverride::OVERRIDE_3D_1 && camera_override < CameraOverride::OVERRIDE_3D_1) {
 		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) {
+		_put_msg("scene:override_camera_3D:set", msg);
+	} else if (p_override < CameraOverride::OVERRIDE_3D_1 && camera_override >= CameraOverride::OVERRIDE_3D_1) {
 		Array msg;
 		msg.push_back(false);
-		_put_msg("override_camera_3D:set", msg);
+		_put_msg("scene:override_camera_3D:set", msg);
 	}
 
 	camera_override = p_override;
@@ -1423,7 +1379,6 @@ void ScriptEditorDebugger::_clear_errors_list() {
 	error_tree->clear();
 	error_count = 0;
 	warning_count = 0;
-	update_tabs();
 }
 
 // Right click on specific file(s) or folder(s).
@@ -1502,8 +1457,6 @@ void ScriptEditorDebugger::_bind_methods() {
 
 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;
 
 	tabs = memnew(TabContainer);
@@ -1655,7 +1608,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
 		profiler = memnew(EditorProfiler);
 		profiler->set_name(TTR("Profiler"));
 		tabs->add_child(profiler);
-		profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate));
+		profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate), varray(PROFILER_SCRIPTS_SERVERS));
 		profiler->connect("break_request", callable_mp(this, &ScriptEditorDebugger::_profiler_seeked));
 	}
 
@@ -1663,14 +1616,14 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
 		visual_profiler = memnew(EditorVisualProfiler);
 		visual_profiler->set_name(TTR("Visual Profiler"));
 		tabs->add_child(visual_profiler);
-		visual_profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_visual_profiler_activate));
+		visual_profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate), varray(PROFILER_VISUAL));
 	}
 
 	{ //network profiler
 		network_profiler = memnew(EditorNetworkProfiler);
 		network_profiler->set_name(TTR("Network Profiler"));
 		tabs->add_child(network_profiler);
-		network_profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_network_profiler_activate));
+		network_profiler->connect("enable_profiling", callable_mp(this, &ScriptEditorDebugger::_profiler_activate), varray(PROFILER_NETWORK));
 	}
 
 	{ //monitors
@@ -1824,7 +1777,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
 	add_child(msgdialog);
 
 	live_debug = true;
-	camera_override = OVERRIDE_NONE;
+	camera_override = CameraOverride::OVERRIDE_NONE;
 	last_path_id = false;
 	error_count = 0;
 	warning_count = 0;
@@ -1833,6 +1786,9 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
 
 ScriptEditorDebugger::~ScriptEditorDebugger() {
 
-	ppeer->set_stream_peer(Ref<StreamPeer>());
+	if (peer.is_valid()) {
+		peer->close();
+		peer.unref();
+	}
 	memdelete(scene_tree);
 }

+ 18 - 27
editor/debugger/script_editor_debugger.h

@@ -31,14 +31,13 @@
 #ifndef SCRIPT_EDITOR_DEBUGGER_H
 #define SCRIPT_EDITOR_DEBUGGER_H
 
-#include "core/io/packet_peer.h"
-#include "core/io/stream_peer_tcp.h"
+#include "core/os/os.h"
 #include "editor/debugger/editor_debugger_inspector.h"
-#include "editor/editor_inspector.h"
-#include "editor/property_editor.h"
-#include "scene/3d/camera.h"
-#include "scene/gui/box_container.h"
+#include "editor/debugger/editor_debugger_node.h"
+#include "editor/debugger/editor_debugger_server.h"
+#include "editor/editor_file_dialog.h"
 #include "scene/gui/button.h"
+#include "scene/gui/margin_container.h"
 
 class Tree;
 class EditorNode;
@@ -61,16 +60,6 @@ class ScriptEditorDebugger : public MarginContainer {
 
 	friend class EditorDebuggerNode;
 
-public:
-	enum CameraOverride {
-		OVERRIDE_NONE,
-		OVERRIDE_2D,
-		OVERRIDE_3D_1, // 3D Viewport 1
-		OVERRIDE_3D_2, // 3D Viewport 2
-		OVERRIDE_3D_3, // 3D Viewport 3
-		OVERRIDE_3D_4 // 3D Viewport 4
-	};
-
 private:
 	enum MessageType {
 		MESSAGE_ERROR,
@@ -78,6 +67,12 @@ private:
 		MESSAGE_SUCCESS,
 	};
 
+	enum ProfilerType {
+		PROFILER_NETWORK,
+		PROFILER_VISUAL,
+		PROFILER_SCRIPTS_SERVERS
+	};
+
 	AcceptDialog *msgdialog;
 
 	LineEdit *clicked_ctrl;
@@ -132,8 +127,7 @@ private:
 	EditorDebuggerInspector *inspector;
 	SceneDebuggerTree *scene_tree;
 
-	Ref<StreamPeerTCP> connection;
-	Ref<PacketPeerStream> ppeer;
+	Ref<RemoteDebuggerPeer> peer;
 
 	HashMap<NodePath, int> node_path_cache;
 	int last_path_id;
@@ -151,7 +145,7 @@ private:
 
 	bool live_debug;
 
-	CameraOverride camera_override;
+	EditorDebuggerNode::CameraOverride camera_override;
 
 	void _performance_draw();
 	void _performance_select();
@@ -183,12 +177,9 @@ private:
 	void _expand_errors_list();
 	void _collapse_errors_list();
 
-	void _visual_profiler_activate(bool p_enable);
-	void _profiler_activate(bool p_enable);
+	void _profiler_activate(bool p_enable, int p_profiler);
 	void _profiler_seeked();
 
-	void _network_profiler_activate(bool p_enable);
-
 	void _clear_errors_list();
 
 	void _error_tree_item_rmb_selected(const Vector2 &p_pos);
@@ -216,7 +207,7 @@ public:
 	void request_remote_tree();
 	const SceneDebuggerTree *get_remote_tree();
 
-	void start(Ref<StreamPeerTCP> p_connection);
+	void start(Ref<RemoteDebuggerPeer> p_peer);
 	void stop();
 
 	void debug_skip_breakpoints();
@@ -228,7 +219,7 @@ public:
 	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(); };
+	bool is_session_active() { return peer.is_valid() && peer->is_peer_connected(); };
 	int get_remote_pid() const { return remote_pid; }
 
 	int get_error_count() const { return error_count; }
@@ -252,8 +243,8 @@ public:
 	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);
 
-	CameraOverride get_camera_override() const;
-	void set_camera_override(CameraOverride p_override);
+	EditorDebuggerNode::CameraOverride get_camera_override() const;
+	void set_camera_override(EditorDebuggerNode::CameraOverride p_override);
 
 	void set_breakpoint(const String &p_path, int p_line, bool p_enabled);
 

+ 3 - 3
editor/plugins/canvas_item_editor_plugin.cpp

@@ -3992,7 +3992,7 @@ void CanvasItemEditor::_notification(int p_what) {
 		if (!is_visible() && override_camera_button->is_pressed()) {
 			EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton();
 
-			debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_NONE);
+			debugger->set_camera_override(EditorDebuggerNode::OVERRIDE_NONE);
 			override_camera_button->set_pressed(false);
 		}
 	}
@@ -4348,9 +4348,9 @@ void CanvasItemEditor::_button_override_camera(bool p_pressed) {
 	EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton();
 
 	if (p_pressed) {
-		debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_2D);
+		debugger->set_camera_override(EditorDebuggerNode::OVERRIDE_2D);
 	} else {
-		debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_NONE);
+		debugger->set_camera_override(EditorDebuggerNode::OVERRIDE_NONE);
 	}
 }
 

+ 0 - 1
editor/plugins/script_editor_plugin.cpp

@@ -37,7 +37,6 @@
 #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"

+ 5 - 5
editor/plugins/spatial_editor_plugin.cpp

@@ -4773,12 +4773,12 @@ void SpatialEditor::_menu_item_toggled(bool pressed, int p_option) {
 		case MENU_TOOL_OVERRIDE_CAMERA: {
 			EditorDebuggerNode *const debugger = EditorDebuggerNode::get_singleton();
 
+			using Override = EditorDebuggerNode::CameraOverride;
 			if (pressed) {
-				using Override = ScriptEditorDebugger::CameraOverride;
 
 				debugger->set_camera_override((Override)(Override::OVERRIDE_3D_1 + camera_override_viewport_id));
 			} else {
-				debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_NONE);
+				debugger->set_camera_override(Override::OVERRIDE_NONE);
 			}
 
 		} break;
@@ -4831,8 +4831,8 @@ void SpatialEditor::_update_camera_override_viewport(Object *p_viewport) {
 	EditorDebuggerNode *const debugger = EditorDebuggerNode::get_singleton();
 
 	camera_override_viewport_id = current_viewport->index;
-	if (debugger->get_camera_override() >= ScriptEditorDebugger::OVERRIDE_3D_1) {
-		using Override = ScriptEditorDebugger::CameraOverride;
+	if (debugger->get_camera_override() >= EditorDebuggerNode::OVERRIDE_3D_1) {
+		using Override = EditorDebuggerNode::CameraOverride;
 
 		debugger->set_camera_override((Override)(Override::OVERRIDE_3D_1 + camera_override_viewport_id));
 	}
@@ -5789,7 +5789,7 @@ void SpatialEditor::_notification(int p_what) {
 		if (!is_visible() && tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->is_pressed()) {
 			EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton();
 
-			debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_NONE);
+			debugger->set_camera_override(EditorDebuggerNode::OVERRIDE_NONE);
 			tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_pressed(false);
 		}
 	}

+ 24 - 75
main/main.cpp

@@ -31,6 +31,7 @@
 #include "main.h"
 
 #include "core/crypto/crypto.h"
+#include "core/debugger/engine_debugger.h"
 #include "core/input_map.h"
 #include "core/io/file_access_network.h"
 #include "core/io/file_access_pack.h"
@@ -43,9 +44,6 @@
 #include "core/os/os.h"
 #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"
 #include "core/version_hash.gen.h"
@@ -96,7 +94,6 @@ static PackedData *packed_data = NULL;
 static ZipArchive *zip_packed_data = NULL;
 #endif
 static FileAccessNetworkClient *file_access_network_client = NULL;
-static ScriptDebugger *script_debugger = NULL;
 static MessageQueue *message_queue = NULL;
 
 // Initialized in setup2()
@@ -410,8 +407,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 	String audio_driver = "";
 	String project_path = ".";
 	bool upwards = false;
-	String debug_mode;
-	String debug_host;
+	String debug_uri = "";
 	bool skip_breakpoints = false;
 	String main_pack;
 	bool quiet_stdout = false;
@@ -784,7 +780,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 			};
 
 		} else if (I->get() == "-d" || I->get() == "--debug") {
-			debug_mode = "local";
+			debug_uri = "local://";
 #if defined(DEBUG_ENABLED) && !defined(SERVER_ENABLED)
 		} else if (I->get() == "--debug-collisions") {
 			debug_collisions = true;
@@ -794,12 +790,12 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 		} else if (I->get() == "--remote-debug") {
 			if (I->next()) {
 
-				debug_mode = "remote";
-				debug_host = I->next()->get();
-				if (debug_host.find(":") == -1) { // wrong address
+				debug_uri = I->next()->get();
+				if (debug_uri.find(":") == -1) { // wrong address
 					OS::get_singleton()->print("Invalid debug host address, it should be of the form <host/IP>:<port>.\n");
 					goto error;
 				}
+				debug_uri = "tcp://" + debug_uri; // will support multiple protocols eventually.
 				N = I->next()->next();
 			} else {
 				OS::get_singleton()->print("Missing remote debug host address, aborting.\n");
@@ -886,50 +882,16 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 
 	GLOBAL_DEF("memory/limits/multithreaded_server/rid_pool_prealloc", 60);
 	ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/multithreaded_server/rid_pool_prealloc", PropertyInfo(Variant::INT, "memory/limits/multithreaded_server/rid_pool_prealloc", PROPERTY_HINT_RANGE, "0,500,1")); // No negative and limit to 500 due to crashes
-	GLOBAL_DEF("network/limits/debugger_stdout/max_chars_per_second", 2048);
-	ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger_stdout/max_chars_per_second", PropertyInfo(Variant::INT, "network/limits/debugger_stdout/max_chars_per_second", PROPERTY_HINT_RANGE, "0, 4096, 1, or_greater"));
-	GLOBAL_DEF("network/limits/debugger_stdout/max_messages_per_frame", 10);
-	ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger_stdout/max_messages_per_frame", PropertyInfo(Variant::INT, "network/limits/debugger_stdout/max_messages_per_frame", PROPERTY_HINT_RANGE, "0, 20, 1, or_greater"));
-	GLOBAL_DEF("network/limits/debugger_stdout/max_errors_per_second", 100);
-	ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger_stdout/max_errors_per_second", PropertyInfo(Variant::INT, "network/limits/debugger_stdout/max_errors_per_second", PROPERTY_HINT_RANGE, "0, 200, 1, or_greater"));
-	GLOBAL_DEF("network/limits/debugger_stdout/max_warnings_per_second", 100);
-	ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger_stdout/max_warnings_per_second", PropertyInfo(Variant::INT, "network/limits/debugger_stdout/max_warnings_per_second", PROPERTY_HINT_RANGE, "0, 200, 1, or_greater"));
-
-	if (debug_mode == "remote") {
-
-		ScriptDebuggerRemote *sdr = memnew(ScriptDebuggerRemote);
-		uint16_t debug_port = 6007;
-		if (debug_host.find(":") != -1) {
-			int sep_pos = debug_host.find_last(":");
-			debug_port = debug_host.substr(sep_pos + 1, debug_host.length()).to_int();
-			debug_host = debug_host.substr(0, sep_pos);
-		}
-		Error derr = sdr->connect_to_host(debug_host, debug_port);
-
-		sdr->set_skip_breakpoints(skip_breakpoints);
-
-		if (derr != OK) {
-			memdelete(sdr);
-		} else {
-			script_debugger = sdr;
-		}
-	} else if (debug_mode == "local") {
+	GLOBAL_DEF("network/limits/debugger/max_chars_per_second", 32768);
+	ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger/max_chars_per_second", PropertyInfo(Variant::INT, "network/limits/debugger/max_chars_per_second", PROPERTY_HINT_RANGE, "0, 4096, 1, or_greater"));
+	GLOBAL_DEF("network/limits/debugger/max_queued_messages", 2048);
+	ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger/max_queued_messages", PropertyInfo(Variant::INT, "network/limits/debugger/max_queued_messages", PROPERTY_HINT_RANGE, "0, 8192, 1, or_greater"));
+	GLOBAL_DEF("network/limits/debugger/max_errors_per_second", 400);
+	ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger/max_errors_per_second", PropertyInfo(Variant::INT, "network/limits/debugger/max_errors_per_second", PROPERTY_HINT_RANGE, "0, 200, 1, or_greater"));
+	GLOBAL_DEF("network/limits/debugger/max_warnings_per_second", 400);
+	ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger/max_warnings_per_second", PropertyInfo(Variant::INT, "network/limits/debugger/max_warnings_per_second", PROPERTY_HINT_RANGE, "0, 200, 1, or_greater"));
 
-		script_debugger = memnew(ScriptDebuggerLocal);
-		OS::get_singleton()->initialize_debugging();
-	}
-	if (script_debugger) {
-		//there is a debugger, parse breakpoints
-
-		for (int i = 0; i < breakpoints.size(); i++) {
-
-			String bp = breakpoints[i];
-			int sp = bp.find_last(":");
-			ERR_CONTINUE_MSG(sp == -1, "Invalid breakpoint: '" + bp + "', expected file:line format.");
-
-			script_debugger->insert_breakpoint(bp.substr(sp + 1, bp.length()).to_int(), bp.substr(0, sp));
-		}
-	}
+	EngineDebugger::initialize(debug_uri, skip_breakpoints, breakpoints);
 
 #ifdef TOOLS_ENABLED
 	if (editor) {
@@ -1179,6 +1141,8 @@ error:
 	if (show_help)
 		print_help(execpath);
 
+	EngineDebugger::deinitialize();
+
 	if (performance)
 		memdelete(performance);
 	if (input_map)
@@ -1189,8 +1153,6 @@ error:
 		memdelete(globals);
 	if (engine)
 		memdelete(engine);
-	if (script_debugger)
-		memdelete(script_debugger);
 	if (packed_data)
 		memdelete(packed_data);
 	if (file_access_network_client)
@@ -1401,8 +1363,10 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
 
 	audio_server->load_default_bus_layout();
 
-	if (use_debug_profiler && script_debugger) {
-		script_debugger->profiling_start();
+	if (use_debug_profiler && EngineDebugger::is_active()) {
+		// Start the "scripts" profiler, used in local debugging.
+		// We could add more, and make the CLI arg require a comma-separated list of profilers.
+		EngineDebugger::get_singleton()->profiler_enable("scripts", true);
 	}
 	_start_success = true;
 	locale = String();
@@ -2090,12 +2054,8 @@ bool Main::iteration() {
 
 	AudioServer::get_singleton()->update();
 
-	if (script_debugger) {
-		if (script_debugger->is_profiling()) {
-			script_debugger->profiling_set_frame_times(USEC_TO_SEC(frame_time), USEC_TO_SEC(idle_process_ticks), USEC_TO_SEC(physics_process_ticks), frame_slice);
-		}
-		script_debugger->idle_poll();
-	}
+	if (EngineDebugger::is_active())
+		EngineDebugger::get_singleton()->iteration(frame_time, idle_process_ticks, physics_process_ticks, frame_slice);
 
 	frames++;
 	Engine::get_singleton()->_idle_frames++;
@@ -2173,10 +2133,7 @@ void Main::cleanup() {
 
 	ERR_FAIL_COND(!_start_success);
 
-	if (script_debugger) {
-		// Flush any remaining messages
-		script_debugger->idle_poll();
-	}
+	EngineDebugger::deinitialize();
 
 	ResourceLoader::remove_custom_loaders();
 	ResourceSaver::remove_custom_savers();
@@ -2184,14 +2141,6 @@ void Main::cleanup() {
 	message_queue->flush();
 	memdelete(message_queue);
 
-	if (script_debugger) {
-		if (use_debug_profiler) {
-			script_debugger->profiling_end();
-		}
-
-		memdelete(script_debugger);
-	}
-
 	OS::get_singleton()->delete_main_loop();
 
 	OS::get_singleton()->_cmdline.clear();

+ 1 - 1
modules/gdnative/pluginscript/pluginscript_script.cpp

@@ -194,7 +194,7 @@ ScriptInstance *PluginScript::instance_create(Object *p_this) {
 		if (!ClassDB::is_parent_class(p_this->get_class_name(), base_type)) {
 			String msg = "Script inherits from native type '" + String(base_type) + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'";
 			// TODO: implement PluginscriptLanguage::debug_break_parse
-			// if (ScriptDebugger::get_singleton()) {
+			// if (EngineDebugger::is_active()) {
 			// 	_language->debug_break_parse(get_path(), 0, msg);
 			// }
 			ERR_FAIL_V_MSG(NULL, msg);

+ 6 - 6
modules/gdscript/gdscript.cpp

@@ -315,7 +315,7 @@ ScriptInstance *GDScript::instance_create(Object *p_this) {
 	if (top->native.is_valid()) {
 		if (!ClassDB::is_parent_class(p_this->get_class_name(), top->native->get_name())) {
 
-			if (ScriptDebugger::get_singleton()) {
+			if (EngineDebugger::is_active()) {
 				GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), 1, "Script inherits from native type '" + String(top->native->get_name()) + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'");
 			}
 			ERR_FAIL_V_MSG(NULL, "Script inherits from native type '" + String(top->native->get_name()) + "', so it can't be instanced in object of type '" + p_this->get_class() + "'" + ".");
@@ -556,7 +556,7 @@ Error GDScript::reload(bool p_keep_state) {
 	GDScriptParser parser;
 	Error err = parser.parse(source, basedir, false, path);
 	if (err) {
-		if (ScriptDebugger::get_singleton()) {
+		if (EngineDebugger::is_active()) {
 			GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), parser.get_error_line(), "Parser Error: " + parser.get_error());
 		}
 		_err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_error_line(), ("Parse Error: " + parser.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT);
@@ -571,7 +571,7 @@ Error GDScript::reload(bool p_keep_state) {
 	if (err) {
 
 		if (can_run) {
-			if (ScriptDebugger::get_singleton()) {
+			if (EngineDebugger::is_active()) {
 				GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error());
 			}
 			_err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT);
@@ -583,9 +583,9 @@ Error GDScript::reload(bool p_keep_state) {
 #ifdef DEBUG_ENABLED
 	for (const List<GDScriptWarning>::Element *E = parser.get_warnings().front(); E; E = E->next()) {
 		const GDScriptWarning &warning = E->get();
-		if (ScriptDebugger::get_singleton()) {
+		if (EngineDebugger::is_active()) {
 			Vector<ScriptLanguage::StackInfo> si;
-			ScriptDebugger::get_singleton()->send_error("", get_path(), warning.line, warning.get_name(), warning.get_message(), ERR_HANDLER_WARNING, si);
+			EngineDebugger::get_script_debugger()->send_error("", get_path(), warning.line, warning.get_name(), warning.get_message(), ERR_HANDLER_WARNING, si);
 		}
 	}
 #endif
@@ -2201,7 +2201,7 @@ GDScriptLanguage::GDScriptLanguage() {
 	int dmcs = GLOBAL_DEF("debug/settings/gdscript/max_call_stack", 1024);
 	ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/gdscript/max_call_stack", PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater")); //minimum is 1024
 
-	if (ScriptDebugger::get_singleton()) {
+	if (EngineDebugger::is_active()) {
 		//debugging enabled!
 
 		_debug_max_call_stack = dmcs;

+ 8 - 6
modules/gdscript/gdscript.h

@@ -31,6 +31,8 @@
 #ifndef GDSCRIPT_H
 #define GDSCRIPT_H
 
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/script_debugger.h"
 #include "core/io/resource_loader.h"
 #include "core/io/resource_saver.h"
 #include "core/script_language.h"
@@ -393,13 +395,13 @@ public:
 		if (Thread::get_main_id() != Thread::get_caller_id())
 			return; //no support for other threads than main for now
 
-		if (ScriptDebugger::get_singleton()->get_lines_left() > 0 && ScriptDebugger::get_singleton()->get_depth() >= 0)
-			ScriptDebugger::get_singleton()->set_depth(ScriptDebugger::get_singleton()->get_depth() + 1);
+		if (EngineDebugger::get_script_debugger()->get_lines_left() > 0 && EngineDebugger::get_script_debugger()->get_depth() >= 0)
+			EngineDebugger::get_script_debugger()->set_depth(EngineDebugger::get_script_debugger()->get_depth() + 1);
 
 		if (_debug_call_stack_pos >= _debug_max_call_stack) {
 			//stack overflow
 			_debug_error = "Stack Overflow (Stack Size: " + itos(_debug_max_call_stack) + ")";
-			ScriptDebugger::get_singleton()->debug(this);
+			EngineDebugger::get_script_debugger()->debug(this);
 			return;
 		}
 
@@ -416,13 +418,13 @@ public:
 		if (Thread::get_main_id() != Thread::get_caller_id())
 			return; //no support for other threads than main for now
 
-		if (ScriptDebugger::get_singleton()->get_lines_left() > 0 && ScriptDebugger::get_singleton()->get_depth() >= 0)
-			ScriptDebugger::get_singleton()->set_depth(ScriptDebugger::get_singleton()->get_depth() - 1);
+		if (EngineDebugger::get_script_debugger()->get_lines_left() > 0 && EngineDebugger::get_script_debugger()->get_depth() >= 0)
+			EngineDebugger::get_script_debugger()->set_depth(EngineDebugger::get_script_debugger()->get_depth() - 1);
 
 		if (_debug_call_stack_pos == 0) {
 
 			_debug_error = "Stack Underflow (Engine Bug)";
-			ScriptDebugger::get_singleton()->debug(this);
+			EngineDebugger::get_script_debugger()->debug(this);
 			return;
 		}
 

+ 2 - 2
modules/gdscript/gdscript_compiler.cpp

@@ -1579,7 +1579,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
 	codegen.stack_max = 0;
 	codegen.current_line = 0;
 	codegen.call_max = 0;
-	codegen.debug_stack = ScriptDebugger::get_singleton() != NULL;
+	codegen.debug_stack = EngineDebugger::is_active();
 	Vector<StringName> argnames;
 
 	int stack_level = 0;
@@ -1765,7 +1765,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
 	gdfunc->_call_size = codegen.call_max;
 	gdfunc->name = func_name;
 #ifdef DEBUG_ENABLED
-	if (ScriptDebugger::get_singleton()) {
+	if (EngineDebugger::is_active()) {
 		String signature;
 		//path
 		if (p_script->get_path() != String())

+ 4 - 4
modules/gdscript/gdscript_editor.cpp

@@ -221,12 +221,12 @@ Script *GDScriptLanguage::create_script() const {
 bool GDScriptLanguage::debug_break_parse(const String &p_file, int p_line, const String &p_error) {
 	//break because of parse error
 
-	if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) {
+	if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
 
 		_debug_parse_err_line = p_line;
 		_debug_parse_err_file = p_file;
 		_debug_error = p_error;
-		ScriptDebugger::get_singleton()->debug(this, false, true);
+		EngineDebugger::get_script_debugger()->debug(this, false, true);
 		return true;
 	} else {
 		return false;
@@ -235,13 +235,13 @@ bool GDScriptLanguage::debug_break_parse(const String &p_file, int p_line, const
 
 bool GDScriptLanguage::debug_break(const String &p_error, bool p_allow_continue) {
 
-	if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) {
+	if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
 
 		_debug_parse_err_line = -1;
 		_debug_parse_err_file = "";
 		_debug_error = p_error;
 		bool is_error_breakpoint = p_error != "Breakpoint";
-		ScriptDebugger::get_singleton()->debug(this, p_allow_continue, is_error_breakpoint);
+		EngineDebugger::get_script_debugger()->debug(this, p_allow_continue, is_error_breakpoint);
 		return true;
 	} else {
 		return false;

+ 11 - 11
modules/gdscript/gdscript_function.cpp

@@ -391,7 +391,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 
 #ifdef DEBUG_ENABLED
 
-	if (ScriptDebugger::get_singleton())
+	if (EngineDebugger::is_active())
 		GDScriptLanguage::get_singleton()->enter_function(p_instance, this, stack, &ip, &line);
 
 #define GD_ERR_BREAK(m_cond)                                                                                           \
@@ -1522,7 +1522,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 
 			OPCODE(OPCODE_BREAKPOINT) {
 #ifdef DEBUG_ENABLED
-				if (ScriptDebugger::get_singleton()) {
+				if (EngineDebugger::is_active()) {
 					GDScriptLanguage::get_singleton()->debug_break("Breakpoint Statement", true);
 				}
 #endif
@@ -1536,26 +1536,26 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 				line = _code_ptr[ip + 1];
 				ip += 2;
 
-				if (ScriptDebugger::get_singleton()) {
+				if (EngineDebugger::is_active()) {
 					// line
 					bool do_break = false;
 
-					if (ScriptDebugger::get_singleton()->get_lines_left() > 0) {
+					if (EngineDebugger::get_script_debugger()->get_lines_left() > 0) {
 
-						if (ScriptDebugger::get_singleton()->get_depth() <= 0)
-							ScriptDebugger::get_singleton()->set_lines_left(ScriptDebugger::get_singleton()->get_lines_left() - 1);
-						if (ScriptDebugger::get_singleton()->get_lines_left() <= 0)
+						if (EngineDebugger::get_script_debugger()->get_depth() <= 0)
+							EngineDebugger::get_script_debugger()->set_lines_left(EngineDebugger::get_script_debugger()->get_lines_left() - 1);
+						if (EngineDebugger::get_script_debugger()->get_lines_left() <= 0)
 							do_break = true;
 					}
 
-					if (ScriptDebugger::get_singleton()->is_breakpoint(line, source))
+					if (EngineDebugger::get_script_debugger()->is_breakpoint(line, source))
 						do_break = true;
 
 					if (do_break) {
 						GDScriptLanguage::get_singleton()->debug_break("Breakpoint", true);
 					}
 
-					ScriptDebugger::get_singleton()->line_poll();
+					EngineDebugger::get_singleton()->line_poll();
 				}
 			}
 			DISPATCH_OPCODE;
@@ -1622,7 +1622,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 	// When it's the last resume it will postpone the exit from stack,
 	// so the debugger knows which function triggered the resume of the next function (if any)
 	if (!p_state || yielded) {
-		if (ScriptDebugger::get_singleton())
+		if (EngineDebugger::is_active())
 			GDScriptLanguage::get_singleton()->exit_function();
 #endif
 
@@ -1884,7 +1884,7 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
 		}
 
 #ifdef DEBUG_ENABLED
-		if (ScriptDebugger::get_singleton())
+		if (EngineDebugger::is_active())
 			GDScriptLanguage::get_singleton()->exit_function();
 		if (state.stack_size) {
 			//free stack

+ 7 - 5
modules/mono/csharp_script.cpp

@@ -33,6 +33,8 @@
 #include <mono/metadata/threads.h>
 #include <stdint.h>
 
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/script_debugger.h"
 #include "core/io/json.h"
 #include "core/os/file_access.h"
 #include "core/os/mutex.h"
@@ -1134,11 +1136,11 @@ void CSharpLanguage::thread_exit() {
 bool CSharpLanguage::debug_break_parse(const String &p_file, int p_line, const String &p_error) {
 
 	// Not a parser error in our case, but it's still used for other type of errors
-	if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) {
+	if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
 		_debug_parse_err_line = p_line;
 		_debug_parse_err_file = p_file;
 		_debug_error = p_error;
-		ScriptDebugger::get_singleton()->debug(this, false, true);
+		EngineDebugger::get_script_debugger()->debug(this, false, true);
 		return true;
 	} else {
 		return false;
@@ -1147,11 +1149,11 @@ bool CSharpLanguage::debug_break_parse(const String &p_file, int p_line, const S
 
 bool CSharpLanguage::debug_break(const String &p_error, bool p_allow_continue) {
 
-	if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) {
+	if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
 		_debug_parse_err_line = -1;
 		_debug_parse_err_file = "";
 		_debug_error = p_error;
-		ScriptDebugger::get_singleton()->debug(this, p_allow_continue);
+		EngineDebugger::get_script_debugger()->debug(this, p_allow_continue);
 		return true;
 	} else {
 		return false;
@@ -2998,7 +3000,7 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
 	if (native) {
 		String native_name = NATIVE_GDMONOCLASS_NAME(native);
 		if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
-			if (ScriptDebugger::get_singleton()) {
+			if (EngineDebugger::is_active()) {
 				CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0, "Script inherits from native type '" + native_name + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'");
 			}
 			ERR_FAIL_V_MSG(NULL, "Script inherits from native type '" + native_name +

+ 3 - 2
modules/mono/mono_gd/gd_mono.cpp

@@ -37,6 +37,7 @@
 #include <mono/metadata/mono-gc.h>
 #include <mono/metadata/profiler.h>
 
+#include "core/debugger/engine_debugger.h"
 #include "core/os/dir_access.h"
 #include "core/os/file_access.h"
 #include "core/os/os.h"
@@ -1183,8 +1184,8 @@ void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) {
 
 #ifdef DEBUG_ENABLED
 	GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc);
-	if (ScriptDebugger::get_singleton())
-		ScriptDebugger::get_singleton()->idle_poll();
+	if (EngineDebugger::is_active())
+		EngineDebugger::get_singleton()->poll_events(false);
 #endif
 
 	exit(mono_environment_exitcode_get());

+ 5 - 2
modules/mono/mono_gd/gd_mono_internals.cpp

@@ -38,6 +38,9 @@
 #include "gd_mono_marshal.h"
 #include "gd_mono_utils.h"
 
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/script_debugger.h"
+
 #include <mono/metadata/exception.h>
 
 namespace GDMonoInternals {
@@ -120,8 +123,8 @@ void unhandled_exception(MonoException *p_exc) {
 	} else {
 #ifdef DEBUG_ENABLED
 		GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc);
-		if (ScriptDebugger::get_singleton())
-			ScriptDebugger::get_singleton()->idle_poll();
+		if (EngineDebugger::is_active())
+			EngineDebugger::get_singleton()->poll_events(false);
 #endif
 	}
 }

+ 5 - 3
modules/mono/mono_gd/gd_mono_utils.cpp

@@ -32,6 +32,8 @@
 
 #include <mono/metadata/exception.h>
 
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/script_debugger.h"
 #include "core/os/dir_access.h"
 #include "core/os/mutex.h"
 #include "core/os/os.h"
@@ -39,7 +41,7 @@
 #include "core/reference.h"
 
 #ifdef TOOLS_ENABLED
-#include "editor/debugger/script_editor_debugger.h"
+#include "editor/debugger/editor_debugger_node.h"
 #endif
 
 #include "../csharp_script.h"
@@ -351,7 +353,7 @@ void debug_print_unhandled_exception(MonoException *p_exc) {
 
 void debug_send_unhandled_exception_error(MonoException *p_exc) {
 #ifdef DEBUG_ENABLED
-	if (!ScriptDebugger::get_singleton()) {
+	if (!EngineDebugger::is_active()) {
 #ifdef TOOLS_ENABLED
 		if (Engine::get_singleton()->is_editor_hint()) {
 			ERR_PRINT(GDMonoUtils::get_exception_name_and_message(p_exc));
@@ -410,7 +412,7 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
 	int line = si.size() ? si[0].line : __LINE__;
 	String error_msg = "Unhandled exception";
 
-	ScriptDebugger::get_singleton()->send_error(func, file, line, error_msg, exc_msg, ERR_HANDLER_ERROR, si);
+	EngineDebugger::get_script_debugger()->send_error(func, file, line, error_msg, exc_msg, ERR_HANDLER_ERROR, si);
 #endif
 }
 

+ 15 - 15
modules/visual_script/visual_script.cpp

@@ -1629,7 +1629,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
 	int flow_stack_pos = p_flow_stack_pos;
 
 #ifdef DEBUG_ENABLED
-	if (ScriptDebugger::get_singleton()) {
+	if (EngineDebugger::is_active()) {
 		VisualScriptLanguage::singleton->enter_function(this, &p_method, variant_stack, &working_mem, &current_node_id);
 	}
 #endif
@@ -1766,7 +1766,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
 
 #ifdef DEBUG_ENABLED
 				//will re-enter later, so exiting
-				if (ScriptDebugger::get_singleton()) {
+				if (EngineDebugger::is_active()) {
 					VisualScriptLanguage::singleton->exit_function();
 				}
 #endif
@@ -1776,26 +1776,26 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
 		}
 
 #ifdef DEBUG_ENABLED
-		if (ScriptDebugger::get_singleton()) {
+		if (EngineDebugger::is_active()) {
 			// line
 			bool do_break = false;
 
-			if (ScriptDebugger::get_singleton()->get_lines_left() > 0) {
+			if (EngineDebugger::get_script_debugger()->get_lines_left() > 0) {
 
-				if (ScriptDebugger::get_singleton()->get_depth() <= 0)
-					ScriptDebugger::get_singleton()->set_lines_left(ScriptDebugger::get_singleton()->get_lines_left() - 1);
-				if (ScriptDebugger::get_singleton()->get_lines_left() <= 0)
+				if (EngineDebugger::get_script_debugger()->get_depth() <= 0)
+					EngineDebugger::get_script_debugger()->set_lines_left(EngineDebugger::get_script_debugger()->get_lines_left() - 1);
+				if (EngineDebugger::get_script_debugger()->get_lines_left() <= 0)
 					do_break = true;
 			}
 
-			if (ScriptDebugger::get_singleton()->is_breakpoint(current_node_id, source))
+			if (EngineDebugger::get_script_debugger()->is_breakpoint(current_node_id, source))
 				do_break = true;
 
 			if (do_break) {
 				VisualScriptLanguage::singleton->debug_break("Breakpoint", true);
 			}
 
-			ScriptDebugger::get_singleton()->line_poll();
+			EngineDebugger::get_singleton()->line_poll();
 		}
 #endif
 		int output = ret & VisualScriptNodeInstance::STEP_MASK;
@@ -1983,7 +1983,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
 	}
 
 #ifdef DEBUG_ENABLED
-	if (ScriptDebugger::get_singleton()) {
+	if (EngineDebugger::is_active()) {
 		VisualScriptLanguage::singleton->exit_function();
 	}
 #endif
@@ -2593,12 +2593,12 @@ void VisualScriptLanguage::add_global_constant(const StringName &p_variable, con
 bool VisualScriptLanguage::debug_break_parse(const String &p_file, int p_node, const String &p_error) {
 	//break because of parse error
 
-	if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) {
+	if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
 
 		_debug_parse_err_node = p_node;
 		_debug_parse_err_file = p_file;
 		_debug_error = p_error;
-		ScriptDebugger::get_singleton()->debug(this, false, true);
+		EngineDebugger::get_script_debugger()->debug(this, false, true);
 		return true;
 	} else {
 		return false;
@@ -2607,12 +2607,12 @@ bool VisualScriptLanguage::debug_break_parse(const String &p_file, int p_node, c
 
 bool VisualScriptLanguage::debug_break(const String &p_error, bool p_allow_continue) {
 
-	if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) {
+	if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
 
 		_debug_parse_err_node = -1;
 		_debug_parse_err_file = "";
 		_debug_error = p_error;
-		ScriptDebugger::get_singleton()->debug(this, p_allow_continue, true);
+		EngineDebugger::get_script_debugger()->debug(this, p_allow_continue, true);
 		return true;
 	} else {
 		return false;
@@ -2837,7 +2837,7 @@ VisualScriptLanguage::VisualScriptLanguage() {
 	int dmcs = GLOBAL_DEF("debug/settings/visual_script/max_call_stack", 1024);
 	ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/visual_script/max_call_stack", PropertyInfo(Variant::INT, "debug/settings/visual_script/max_call_stack", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater")); //minimum is 1024
 
-	if (ScriptDebugger::get_singleton()) {
+	if (EngineDebugger::is_active()) {
 		//debugging enabled!
 		_debug_max_call_stack = dmcs;
 		_call_stack = memnew_arr(CallLevel, _debug_max_call_stack + 1);

+ 8 - 6
modules/visual_script/visual_script.h

@@ -31,6 +31,8 @@
 #ifndef VISUAL_SCRIPT_H
 #define VISUAL_SCRIPT_H
 
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/script_debugger.h"
 #include "core/os/thread.h"
 #include "core/script_language.h"
 
@@ -540,13 +542,13 @@ public:
 		if (Thread::get_main_id() != Thread::get_caller_id())
 			return; //no support for other threads than main for now
 
-		if (ScriptDebugger::get_singleton()->get_lines_left() > 0 && ScriptDebugger::get_singleton()->get_depth() >= 0)
-			ScriptDebugger::get_singleton()->set_depth(ScriptDebugger::get_singleton()->get_depth() + 1);
+		if (EngineDebugger::get_script_debugger()->get_lines_left() > 0 && EngineDebugger::get_script_debugger()->get_depth() >= 0)
+			EngineDebugger::get_script_debugger()->set_depth(EngineDebugger::get_script_debugger()->get_depth() + 1);
 
 		if (_debug_call_stack_pos >= _debug_max_call_stack) {
 			//stack overflow
 			_debug_error = "Stack Overflow (Stack Size: " + itos(_debug_max_call_stack) + ")";
-			ScriptDebugger::get_singleton()->debug(this);
+			EngineDebugger::get_script_debugger()->debug(this);
 			return;
 		}
 
@@ -563,13 +565,13 @@ public:
 		if (Thread::get_main_id() != Thread::get_caller_id())
 			return; //no support for other threads than main for now
 
-		if (ScriptDebugger::get_singleton()->get_lines_left() > 0 && ScriptDebugger::get_singleton()->get_depth() >= 0)
-			ScriptDebugger::get_singleton()->set_depth(ScriptDebugger::get_singleton()->get_depth() - 1);
+		if (EngineDebugger::get_script_debugger()->get_lines_left() > 0 && EngineDebugger::get_script_debugger()->get_depth() >= 0)
+			EngineDebugger::get_script_debugger()->set_depth(EngineDebugger::get_script_debugger()->get_depth() - 1);
 
 		if (_debug_call_stack_pos == 0) {
 
 			_debug_error = "Stack Underflow (Engine Bug)";
-			ScriptDebugger::get_singleton()->debug(this);
+			EngineDebugger::get_script_debugger()->debug(this);
 			return;
 		}
 

+ 5 - 4
platform/windows/os_windows.cpp

@@ -33,8 +33,9 @@
 
 #include "os_windows.h"
 
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/script_debugger.h"
 #include "core/io/marshalls.h"
-#include "core/script_language.h"
 #include "core/version_generated.gen.h"
 
 #if defined(OPENGL_ENABLED)
@@ -194,13 +195,13 @@ void RedirectIOToConsole() {
 }
 
 BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) {
-	if (ScriptDebugger::get_singleton() == NULL)
+	if (!EngineDebugger::is_active())
 		return FALSE;
 
 	switch (dwCtrlType) {
 		case CTRL_C_EVENT:
-			ScriptDebugger::get_singleton()->set_depth(-1);
-			ScriptDebugger::get_singleton()->set_lines_left(1);
+			EngineDebugger::get_script_debugger()->set_depth(-1);
+			EngineDebugger::get_script_debugger()->set_lines_left(1);
 			return TRUE;
 		default:
 			return FALSE;

+ 13 - 8
scene/debugger/scene_debugger.cpp

@@ -30,8 +30,9 @@
 
 #include "scene_debugger.h"
 
+#include "core/debugger/engine_debugger.h"
 #include "core/io/marshalls.h"
-#include "core/script_debugger_remote.h"
+#include "core/script_language.h"
 #include "scene/main/scene_tree.h"
 #include "scene/main/viewport.h"
 #include "scene/resources/packed_scene.h"
@@ -39,13 +40,16 @@
 void SceneDebugger::initialize() {
 #ifdef DEBUG_ENABLED
 	LiveEditor::singleton = memnew(LiveEditor);
-	ScriptDebuggerRemote::scene_tree_parse_func = SceneDebugger::parse_message;
+	EngineDebugger::register_message_capture("scene", EngineDebugger::Capture(NULL, SceneDebugger::parse_message));
 #endif
 }
 
 void SceneDebugger::deinitialize() {
 #ifdef DEBUG_ENABLED
 	if (LiveEditor::singleton) {
+		// Should be removed automatically when deiniting debugger, but just in case
+		if (EngineDebugger::has_capture("scene"))
+			EngineDebugger::unregister_message_capture("scene");
 		memdelete(LiveEditor::singleton);
 		LiveEditor::singleton = NULL;
 	}
@@ -53,13 +57,15 @@ void SceneDebugger::deinitialize() {
 }
 
 #ifdef DEBUG_ENABLED
-Error SceneDebugger::parse_message(const String &p_msg, const Array &p_args) {
+Error SceneDebugger::parse_message(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured) {
 	SceneTree *scene_tree = SceneTree::get_singleton();
 	if (!scene_tree)
 		return ERR_UNCONFIGURED;
 	LiveEditor *live_editor = LiveEditor::get_singleton();
 	if (!live_editor)
 		return ERR_UNCONFIGURED;
+
+	r_captured = true;
 	if (p_msg == "request_scene_tree") { // Scene tree
 		live_editor->_send_tree();
 
@@ -171,7 +177,7 @@ Error SceneDebugger::parse_message(const String &p_msg, const Array &p_args) {
 		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;
+		r_captured = false;
 	}
 	return OK;
 }
@@ -180,7 +186,6 @@ 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);
@@ -193,7 +198,7 @@ void SceneDebugger::_send_object_id(ObjectID p_id, int p_max_size) {
 
 	Array arr;
 	obj.serialize(arr);
-	ScriptDebugger::get_singleton()->send_message("inspect_object", arr);
+	EngineDebugger::get_singleton()->send_message("scene:inspect_object", arr);
 }
 
 void SceneDebugger::_set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value) {
@@ -216,7 +221,7 @@ void SceneDebugger::add_to_cache(const String &p_filename, Node *p_node) {
 	if (!debugger)
 		return;
 
-	if (ScriptDebugger::get_singleton() && p_filename != String()) {
+	if (EngineDebugger::get_script_debugger() && p_filename != String()) {
 		debugger->live_scene_edit_cache[p_filename].insert(p_node);
 	}
 }
@@ -487,7 +492,7 @@ void LiveEditor::_send_tree() {
 	// Encoded as a flat list depth fist.
 	SceneDebuggerTree tree(scene_tree->root);
 	tree.serialize(arr);
-	ScriptDebugger::get_singleton()->send_message("scene_tree", arr);
+	EngineDebugger::get_singleton()->send_message("scene:scene_tree", arr);
 }
 
 void LiveEditor::_node_path_func(const NodePath &p_path, int p_id) {

+ 3 - 2
scene/debugger/scene_debugger.h

@@ -34,9 +34,10 @@
 #include "core/array.h"
 #include "core/object.h"
 #include "core/pair.h"
-#include "core/script_language.h"
 #include "core/ustring.h"
 
+class Script;
+
 class SceneDebugger {
 
 public:
@@ -50,7 +51,7 @@ private:
 	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 Error parse_message(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured);
 	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

+ 3 - 7
scene/main/scene_tree.cpp

@@ -30,6 +30,7 @@
 
 #include "scene_tree.h"
 
+#include "core/debugger/engine_debugger.h"
 #include "core/io/marshalls.h"
 #include "core/io/resource_loader.h"
 #include "core/message_queue.h"
@@ -38,7 +39,6 @@
 #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/scene_debugger.h"
@@ -432,11 +432,11 @@ void SceneTree::input_event(const Ref<InputEvent> &p_event) {
 
 	call_group_flags(GROUP_CALL_REALTIME, "_viewports", "_vp_input", ev); //special one for GUI, as controls use their own process check
 
-	if (ScriptDebugger::get_singleton() && ScriptDebugger::get_singleton()->is_remote()) {
+	if (EngineDebugger::is_active()) {
 		//quit from game window using F8
 		Ref<InputEventKey> k = ev;
 		if (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == KEY_F8) {
-			ScriptDebugger::get_singleton()->request_quit();
+			EngineDebugger::get_singleton()->send_message("request_quit", Array());
 		}
 	}
 
@@ -1737,10 +1737,6 @@ SceneTree::SceneTree() {
 	last_screen_size = Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height);
 	_update_root_rect();
 
-	if (ScriptDebugger::get_singleton()) {
-		ScriptDebugger::get_singleton()->set_multiplayer(multiplayer);
-	}
-
 	root->set_physics_object_picking(GLOBAL_DEF("physics/common/enable_object_picking", true));
 
 #ifdef TOOLS_ENABLED

+ 3 - 2
scene/main/viewport.cpp

@@ -31,6 +31,7 @@
 #include "viewport.h"
 
 #include "core/core_string_names.h"
+#include "core/debugger/engine_debugger.h"
 #include "core/os/input.h"
 #include "core/os/os.h"
 #include "core/project_settings.h"
@@ -1927,12 +1928,12 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
 			mb->set_position(pos);
 
 #ifdef DEBUG_ENABLED
-			if (ScriptDebugger::get_singleton() && gui.mouse_focus) {
+			if (EngineDebugger::get_singleton() && gui.mouse_focus) {
 
 				Array arr;
 				arr.push_back(gui.mouse_focus->get_path());
 				arr.push_back(gui.mouse_focus->get_class());
-				ScriptDebugger::get_singleton()->send_message("click_ctrl", arr);
+				EngineDebugger::get_singleton()->send_message("scene:click_ctrl", arr);
 			}
 #endif
 

+ 4 - 2
servers/audio_server.cpp

@@ -30,6 +30,7 @@
 
 #include "audio_server.h"
 
+#include "core/debugger/engine_debugger.h"
 #include "core/io/resource_loader.h"
 #include "core/os/file_access.h"
 #include "core/os/os.h"
@@ -992,7 +993,7 @@ void AudioServer::init() {
 
 void AudioServer::update() {
 #ifdef DEBUG_ENABLED
-	if (ScriptDebugger::get_singleton() && ScriptDebugger::get_singleton()->is_profiling()) {
+	if (EngineDebugger::is_profiling("servers")) {
 
 		// Driver time includes server time + effects times
 		// Server time includes effects times
@@ -1030,7 +1031,8 @@ void AudioServer::update() {
 		values.push_back("audio_driver");
 		values.push_back(USEC_TO_SEC(driver_time));
 
-		ScriptDebugger::get_singleton()->add_profiling_frame_data("audio_thread", values);
+		values.push_front("audio_thread");
+		EngineDebugger::profiler_add_frame_data("servers", values);
 	}
 
 	// Reset profiling times

+ 4 - 3
servers/physics/physics_server_sw.cpp

@@ -32,8 +32,8 @@
 
 #include "broad_phase_basic.h"
 #include "broad_phase_octree.h"
+#include "core/debugger/engine_debugger.h"
 #include "core/os/os.h"
-#include "core/script_language.h"
 #include "joints/cone_twist_joint_sw.h"
 #include "joints/generic_6dof_joint_sw.h"
 #include "joints/hinge_joint_sw.h"
@@ -1467,7 +1467,7 @@ void PhysicsServerSW::flush_queries() {
 
 	flushing_queries = false;
 
-	if (ScriptDebugger::get_singleton() && ScriptDebugger::get_singleton()->is_profiling()) {
+	if (EngineDebugger::is_profiling("servers")) {
 
 		uint64_t total_time[SpaceSW::ELAPSED_TIME_MAX];
 		static const char *time_name[SpaceSW::ELAPSED_TIME_MAX] = {
@@ -1498,7 +1498,8 @@ void PhysicsServerSW::flush_queries() {
 		values.push_back("flush_queries");
 		values.push_back(USEC_TO_SEC(OS::get_singleton()->get_ticks_usec() - time_beg));
 
-		ScriptDebugger::get_singleton()->add_profiling_frame_data("physics", values);
+		values.push_front("physics");
+		EngineDebugger::profiler_add_frame_data("server", values);
 	}
 #endif
 };

+ 4 - 3
servers/physics_2d/physics_2d_server_sw.cpp

@@ -32,9 +32,9 @@
 #include "broad_phase_2d_basic.h"
 #include "broad_phase_2d_hash_grid.h"
 #include "collision_solver_2d_sw.h"
+#include "core/debugger/engine_debugger.h"
 #include "core/os/os.h"
 #include "core/project_settings.h"
-#include "core/script_language.h"
 
 #define FLUSH_QUERY_CHECK(m_object) \
 	ERR_FAIL_COND_MSG(m_object->get_space() && flushing_queries, "Can't change this state while flushing queries. Use call_deferred() or set_deferred() to change monitoring state instead.");
@@ -1369,7 +1369,7 @@ void Physics2DServerSW::flush_queries() {
 
 	flushing_queries = false;
 
-	if (ScriptDebugger::get_singleton() && ScriptDebugger::get_singleton()->is_profiling()) {
+	if (EngineDebugger::is_profiling("servers")) {
 
 		uint64_t total_time[Space2DSW::ELAPSED_TIME_MAX];
 		static const char *time_name[Space2DSW::ELAPSED_TIME_MAX] = {
@@ -1400,7 +1400,8 @@ void Physics2DServerSW::flush_queries() {
 		values.push_back("flush_queries");
 		values.push_back(USEC_TO_SEC(OS::get_singleton()->get_ticks_usec() - time_beg));
 
-		ScriptDebugger::get_singleton()->add_profiling_frame_data("physics_2d", values);
+		values.push_front("physics_2d");
+		EngineDebugger::profiler_add_frame_data("servers", values);
 	}
 }
 

+ 0 - 24
servers/register_server_types.cpp

@@ -56,7 +56,6 @@
 #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"
@@ -67,27 +66,6 @@
 #include "visual/shader_types.h"
 #include "visual_server.h"
 
-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::ResourceInfo usage;
-		usage.path = E->get().path;
-		usage.vram = E->get().bytes;
-		usage.id = E->get().texture;
-		usage.type = "Texture";
-		if (E->get().depth == 0) {
-			usage.format = itos(E->get().width) + "x" + itos(E->get().height) + " " + Image::get_format_name(E->get().format);
-		} 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->infos.push_back(usage);
-	}
-}
-
 ShaderTypes *shader_types = NULL;
 
 PhysicsServer *_createGodotPhysicsCallback() {
@@ -189,8 +167,6 @@ void register_server_types() {
 	ClassDB::register_virtual_class<PhysicsDirectSpaceState>();
 	ClassDB::register_virtual_class<PhysicsShapeQueryResult>();
 
-	ScriptDebuggerRemote::resource_usage_func = _debugger_get_resource_usage;
-
 	// Physics 2D
 	GLOBAL_DEF(Physics2DServerManager::setting_property_name, "DEFAULT");
 	ProjectSettings::get_singleton()->set_custom_property_info(Physics2DServerManager::setting_property_name, PropertyInfo(Variant::STRING, Physics2DServerManager::setting_property_name, PROPERTY_HINT_ENUM, "DEFAULT"));