Huge Debugger/EditorDebugger refactor.

This commit is contained in:
Fabio Alessandrelli 2020-02-07 02:52:05 +01:00
parent 8b058d4b9a
commit cbc450c0e5
41 changed files with 4487 additions and 3292 deletions

File diff suppressed because it is too large Load diff

View file

@ -37,16 +37,170 @@
#include "core/os/os.h"
#include "core/script_language.h"
class SceneTree;
class ScriptDebuggerRemote : public ScriptDebugger {
struct Message {
public:
class ResourceInfo {
public:
String path;
String format;
String type;
RID id;
int vram;
bool operator<(const ResourceInfo &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; }
ResourceInfo() {
vram = 0;
}
};
class ResourceUsage {
public:
List<ResourceInfo> infos;
Array serialize();
bool deserialize(const Array &p_arr);
};
class FrameInfo {
public:
StringName name;
float self_time;
float total_time;
FrameInfo() {
self_time = 0;
total_time = 0;
}
};
class FrameFunction {
public:
int sig_id;
int call_count;
StringName name;
float self_time;
float total_time;
FrameFunction() {
sig_id = -1;
call_count = 0;
self_time = 0;
total_time = 0;
}
};
class ScriptStackVariable {
public:
String name;
Variant value;
int type;
ScriptStackVariable() {
type = -1;
}
Array serialize(int max_size = 1 << 20); // 1 MiB default.
bool deserialize(const Array &p_arr);
};
class ScriptStackDump {
public:
List<ScriptLanguage::StackInfo> frames;
ScriptStackDump() {}
Array serialize();
bool deserialize(const Array &p_arr);
};
class Message {
public:
String message;
Array data;
Message() {}
};
class OutputError {
public:
int hr;
int min;
int sec;
int msec;
String source_file;
String source_func;
int source_line;
String error;
String error_descr;
bool warning;
Vector<ScriptLanguage::StackInfo> callstack;
OutputError() {
hr = -1;
min = -1;
sec = -1;
msec = -1;
source_line = -1;
warning = false;
}
Array serialize();
bool deserialize(const Array &p_arr);
};
struct FrameData {
StringName name;
Array data;
};
class ProfilerSignature {
public:
StringName name;
int id;
Array serialize();
bool deserialize(const Array &p_arr);
ProfilerSignature() {
id = -1;
};
};
class ProfilerFrame {
public:
int frame_number;
float frame_time;
float idle_time;
float physics_time;
float physics_frame_time;
float script_time;
Vector<FrameData> frames_data;
Vector<FrameFunction> frame_functions;
ProfilerFrame() {
frame_number = 0;
frame_time = 0;
idle_time = 0;
physics_time = 0;
physics_frame_time = 0;
}
Array serialize();
bool deserialize(const Array &p_arr);
};
class NetworkProfilerFrame {
public:
Vector<MultiplayerAPI::ProfilingInfo> infos;
Array serialize();
bool deserialize(const Array &p_arr);
NetworkProfilerFrame(){};
};
protected:
struct ProfileInfoSort {
bool operator()(ScriptLanguage::ProfilingInfo *A, ScriptLanguage::ProfilingInfo *B) const {
@ -78,21 +232,6 @@ class ScriptDebuggerRemote : public ScriptDebugger {
bool requested_quit;
Mutex *mutex;
struct OutputError {
int hr;
int min;
int sec;
int msec;
String source_file;
String source_func;
int source_line;
String error;
String error_descr;
bool warning;
Array callstack;
};
List<String> output_strings;
List<Message> messages;
int max_messages_per_frame;
@ -119,9 +258,7 @@ class ScriptDebuggerRemote : public ScriptDebugger {
void _poll_events();
uint32_t poll_every;
SceneTree *scene_tree;
bool _parse_live_edit(const Array &p_command);
void _parse_message(const String p_command, const Array &p_data, ScriptLanguage *p_script = NULL);
void _set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value);
@ -133,40 +270,24 @@ class ScriptDebuggerRemote : public ScriptDebugger {
ErrorHandlerList eh;
static void _err_handler(void *, const char *, const char *, int p_line, const char *, const char *, ErrorHandlerType p_type);
void _put_msg(String p_message, Array p_data);
void _send_profiling_data(bool p_for_frame);
void _send_network_profiling_data();
void _send_network_bandwidth_usage();
struct FrameData {
StringName name;
Array data;
};
Vector<FrameData> profile_frame_data;
void _put_variable(const String &p_name, const Variant &p_variable);
void _save_node(ObjectID id, const String &p_path);
bool skip_breakpoints;
public:
struct ResourceUsage {
String path;
String format;
String type;
RID id;
int vram;
bool operator<(const ResourceUsage &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; }
};
typedef void (*ResourceUsageFunc)(List<ResourceUsage> *);
typedef void (*ResourceUsageFunc)(ResourceUsage *);
typedef Error (*ParseMessageFunc)(const String &p_name, const Array &p_msg); // Returns true if something was found (stopping propagation).
static ResourceUsageFunc resource_usage_func;
static ParseMessageFunc scene_tree_parse_func; // Could be made into list, extensible...
Error connect_to_host(const String &p_host, uint16_t p_port);
bool is_peer_connected();
virtual void debug(ScriptLanguage *p_script, bool p_can_continue = true, bool p_is_error_breakpoint = false);
virtual void idle_poll();
virtual void line_poll();
@ -188,8 +309,6 @@ public:
virtual void set_skip_breakpoints(bool p_skip_breakpoints);
void set_scene_tree(SceneTree *p_scene_tree) { scene_tree = p_scene_tree; };
ScriptDebuggerRemote();
~ScriptDebuggerRemote();
};

View file

@ -82,6 +82,7 @@ if env['tools']:
SConscript('collada/SCsub')
SConscript('doc/SCsub')
SConscript('debugger/SCsub')
SConscript('fileserver/SCsub')
SConscript('icons/SCsub')
SConscript('import/SCsub')

5
editor/debugger/SCsub Normal file
View file

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

View file

@ -0,0 +1,277 @@
/*************************************************************************/
/* editor_debugger_inspector.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "editor_debugger_inspector.h"
#include "core/io/marshalls.h"
#include "core/script_debugger_remote.h"
#include "editor/editor_node.h"
#include "scene/debugger/scene_debugger.h"
bool EditorDebuggerRemoteObject::_set(const StringName &p_name, const Variant &p_value) {
if (!editable || !prop_values.has(p_name) || String(p_name).begins_with("Constants/"))
return false;
prop_values[p_name] = p_value;
emit_signal("value_edited", remote_object_id, p_name, p_value);
return true;
}
bool EditorDebuggerRemoteObject::_get(const StringName &p_name, Variant &r_ret) const {
if (!prop_values.has(p_name))
return false;
r_ret = prop_values[p_name];
return true;
}
void EditorDebuggerRemoteObject::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->clear(); //sorry, no want category
for (const List<PropertyInfo>::Element *E = prop_list.front(); E; E = E->next()) {
p_list->push_back(E->get());
}
}
String EditorDebuggerRemoteObject::get_title() {
if (remote_object_id.is_valid())
return TTR("Remote ") + String(type_name) + ": " + itos(remote_object_id);
else
return "<null>";
}
Variant EditorDebuggerRemoteObject::get_variant(const StringName &p_name) {
Variant var;
_get(p_name, var);
return var;
}
void EditorDebuggerRemoteObject::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_title"), &EditorDebuggerRemoteObject::get_title);
ClassDB::bind_method(D_METHOD("get_variant"), &EditorDebuggerRemoteObject::get_variant);
ClassDB::bind_method(D_METHOD("clear"), &EditorDebuggerRemoteObject::clear);
ClassDB::bind_method(D_METHOD("get_remote_object_id"), &EditorDebuggerRemoteObject::get_remote_object_id);
ADD_SIGNAL(MethodInfo("value_edited", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "property"), PropertyInfo("value")));
}
EditorDebuggerInspector::EditorDebuggerInspector() {
variables = memnew(EditorDebuggerRemoteObject);
variables->editable = false;
}
EditorDebuggerInspector::~EditorDebuggerInspector() {
memdelete(variables);
}
void EditorDebuggerInspector::_bind_methods() {
ClassDB::bind_method(D_METHOD("_object_edited", "name", "value"), &EditorDebuggerInspector::_object_edited);
ClassDB::bind_method(D_METHOD("_object_selected", "id"), &EditorDebuggerInspector::_object_selected);
ADD_SIGNAL(MethodInfo("object_selected", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("object_edited", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property"), PropertyInfo("value")));
ADD_SIGNAL(MethodInfo("object_property_updated", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property")));
}
void EditorDebuggerInspector::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_POSTINITIALIZE:
connect_compat("object_id_selected", this, "_object_selected");
break;
case NOTIFICATION_ENTER_TREE:
edit(variables);
break;
default:
break;
}
}
void EditorDebuggerInspector::_object_edited(ObjectID p_id, const String &p_prop, const Variant &p_value) {
emit_signal("object_edited", p_id, p_prop, p_value);
}
void EditorDebuggerInspector::_object_selected(ObjectID p_object) {
emit_signal("object_selected", p_object);
}
ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) {
EditorDebuggerRemoteObject *debugObj = NULL;
SceneDebuggerObject obj;
obj.deserialize(p_arr);
ERR_FAIL_COND_V(obj.id.is_null(), ObjectID());
if (remote_objects.has(obj.id)) {
debugObj = remote_objects[obj.id];
} else {
debugObj = memnew(EditorDebuggerRemoteObject);
debugObj->remote_object_id = obj.id;
debugObj->type_name = obj.class_name;
remote_objects[obj.id] = debugObj;
debugObj->connect_compat("value_edited", this, "_object_edited");
}
int old_prop_size = debugObj->prop_list.size();
debugObj->prop_list.clear();
int new_props_added = 0;
Set<String> changed;
for (int i = 0; i < obj.properties.size(); i++) {
PropertyInfo &pinfo = obj.properties[i].first;
Variant &var = obj.properties[i].second;
if (pinfo.type == Variant::OBJECT) {
if (var.get_type() == Variant::STRING) {
String path = var;
if (path.find("::") != -1) {
// built-in resource
String base_path = path.get_slice("::", 0);
if (ResourceLoader::get_resource_type(base_path) == "PackedScene") {
if (!EditorNode::get_singleton()->is_scene_open(base_path)) {
EditorNode::get_singleton()->load_scene(base_path);
}
} else {
EditorNode::get_singleton()->load_resource(base_path);
}
}
var = ResourceLoader::load(path);
if (pinfo.hint_string == "Script") {
if (debugObj->get_script() != var) {
debugObj->set_script(REF());
Ref<Script> script(var);
if (!script.is_null()) {
ScriptInstance *script_instance = script->placeholder_instance_create(debugObj);
debugObj->set_script_and_instance(var, script_instance);
}
}
}
}
}
//always add the property, since props may have been added or removed
debugObj->prop_list.push_back(pinfo);
if (!debugObj->prop_values.has(pinfo.name)) {
new_props_added++;
debugObj->prop_values[pinfo.name] = var;
} else {
if (bool(Variant::evaluate(Variant::OP_NOT_EQUAL, debugObj->prop_values[pinfo.name], var))) {
debugObj->prop_values[pinfo.name] = var;
changed.insert(pinfo.name);
}
}
}
if (old_prop_size == debugObj->prop_list.size() && new_props_added == 0) {
//only some may have changed, if so, then update those, if exist
for (Set<String>::Element *E = changed.front(); E; E = E->next()) {
emit_signal("object_property_updated", debugObj->remote_object_id, E->get());
}
} else {
//full update, because props were added or removed
debugObj->update();
}
return obj.id;
}
void EditorDebuggerInspector::clear_cache() {
for (Map<ObjectID, EditorDebuggerRemoteObject *>::Element *E = remote_objects.front(); E; E = E->next()) {
EditorNode *editor = EditorNode::get_singleton();
if (editor->get_editor_history()->get_current() == E->value()->get_instance_id()) {
editor->push_item(NULL);
}
memdelete(E->value());
}
remote_objects.clear();
}
Object *EditorDebuggerInspector::get_object(ObjectID p_id) {
if (remote_objects.has(p_id))
return remote_objects[p_id];
return NULL;
}
void EditorDebuggerInspector::add_stack_variable(const Array &p_array) {
ScriptDebuggerRemote::ScriptStackVariable var;
var.deserialize(p_array);
String n = var.name;
Variant v = var.value;
PropertyHint h = PROPERTY_HINT_NONE;
String hs = String();
if (v.get_type() == Variant::OBJECT) {
v = Object::cast_to<EncodedObjectAsID>(v)->get_object_id();
h = PROPERTY_HINT_OBJECT_ID;
hs = "Object";
}
String type;
switch (var.type) {
case 0:
type = "Locals/";
break;
case 1:
type = "Members/";
break;
case 2:
type = "Globals/";
break;
default:
type = "Unknown/";
}
PropertyInfo pinfo;
pinfo.name = type + n;
pinfo.type = v.get_type();
pinfo.hint = h;
pinfo.hint_string = hs;
variables->prop_list.push_back(pinfo);
variables->prop_values[type + n] = v;
variables->update();
edit(variables);
}
void EditorDebuggerInspector::clear_stack_variables() {
variables->clear();
variables->update();
}
String EditorDebuggerInspector::get_stack_variable(const String &p_var) {
return variables->get_variant(p_var);
}

View file

@ -0,0 +1,98 @@
/*************************************************************************/
/* editor_debugger_inspector.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef EDITOR_DEBUGGER_INSPECTOR_H
#define EDITOR_DEBUGGER_INSPECTOR_H
#include "editor/editor_inspector.h"
class EditorDebuggerRemoteObject : public Object {
GDCLASS(EditorDebuggerRemoteObject, Object);
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
bool editable = false;
ObjectID remote_object_id;
String type_name;
List<PropertyInfo> prop_list;
Map<StringName, Variant> prop_values;
ObjectID get_remote_object_id() { return remote_object_id; };
String get_title();
Variant get_variant(const StringName &p_name);
void clear() {
prop_list.clear();
prop_values.clear();
}
void update() { _change_notify(); }
EditorDebuggerRemoteObject(){};
};
class EditorDebuggerInspector : public EditorInspector {
GDCLASS(EditorDebuggerInspector, EditorInspector);
private:
ObjectID inspected_object_id;
Map<ObjectID, EditorDebuggerRemoteObject *> remote_objects;
EditorDebuggerRemoteObject *variables;
void _object_selected(ObjectID p_object);
void _object_edited(ObjectID p_id, const String &p_prop, const Variant &p_value);
protected:
void _notification(int p_what);
static void _bind_methods();
public:
EditorDebuggerInspector();
~EditorDebuggerInspector();
// Remote Object cache
ObjectID add_object(const Array &p_arr);
Object *get_object(ObjectID p_id);
void clear_cache();
// Stack Dump variables
String get_stack_variable(const String &p_var);
void add_stack_variable(const Array &p_arr);
void clear_stack_variables();
};
#endif // EDITOR_DEBUGGER_INSPECTOR_H

View file

@ -0,0 +1,564 @@
/*************************************************************************/
/* editor_debugger_node.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "editor_debugger_node.h"
#include "editor/debugger/editor_debugger_tree.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
#include "editor/plugins/script_editor_plugin.h"
template <typename Func>
void _for_all(EditorDebuggerNode *p_node, const Func &p_func) {
for (int i = 0; i < p_node->get_tab_count(); i++) {
ScriptEditorDebugger *dbg = Object::cast_to<ScriptEditorDebugger>(p_node->get_tab_control(i));
ERR_FAIL_COND(!dbg);
p_func(dbg);
}
}
EditorDebuggerNode *EditorDebuggerNode::singleton = NULL;
EditorDebuggerNode::EditorDebuggerNode() {
if (!singleton)
singleton = this;
server.instance();
EditorNode *editor = EditorNode::get_singleton();
set_tab_align(TabAlign::ALIGN_LEFT);
add_style_override("panel", editor->get_gui_base()->get_stylebox("DebuggerPanel", "EditorStyles"));
add_style_override("tab_fg", editor->get_gui_base()->get_stylebox("DebuggerTabFG", "EditorStyles"));
add_style_override("tab_bg", editor->get_gui_base()->get_stylebox("DebuggerTabBG", "EditorStyles"));
auto_switch_remote_scene_tree = EDITOR_DEF("debugger/auto_switch_to_remote_scene_tree", false);
_add_debugger("Debugger");
// Remote scene tree
remote_scene_tree = memnew(EditorDebuggerTree);
remote_scene_tree->connect_compat("object_selected", this, "_remote_object_requested");
remote_scene_tree->connect_compat("save_node", this, "_save_node_requested");
EditorNode::get_singleton()->get_scene_tree_dock()->add_remote_tree_editor(remote_scene_tree);
EditorNode::get_singleton()->get_scene_tree_dock()->connect_compat("remote_tree_selected", this, "request_remote_tree");
remote_scene_tree_timeout = EDITOR_DEF("debugger/remote_scene_tree_refresh_interval", 1.0);
inspect_edited_object_timeout = EDITOR_DEF("debugger/remote_inspect_refresh_interval", 0.2);
editor->get_undo_redo()->set_method_notify_callback(_method_changeds, this);
editor->get_undo_redo()->set_property_notify_callback(_property_changeds, this);
editor->get_pause_button()->connect_compat("pressed", this, "_paused");
}
ScriptEditorDebugger *EditorDebuggerNode::_add_debugger(String p_name) {
ScriptEditorDebugger *node = memnew(ScriptEditorDebugger(EditorNode::get_singleton()));
node->set_name(p_name);
int id = get_tab_count();
node->connect_compat("stop_requested", this, "_debugger_wants_stop", varray(id));
node->connect_compat("stopped", this, "_debugger_stopped", varray(id));
node->connect_compat("stack_frame_selected", this, "_stack_frame_selected", varray(id));
node->connect_compat("error_selected", this, "_error_selected", varray(id));
node->connect_compat("clear_execution", this, "_clear_execution");
node->connect_compat("breaked", this, "_breaked", varray(id));
node->connect_compat("remote_tree_updated", this, "_remote_tree_updated", varray(id));
node->connect_compat("remote_object_updated", this, "_remote_object_updated", varray(id));
node->connect_compat("remote_object_property_updated", this, "_remote_object_property_updated", varray(id));
node->connect_compat("remote_object_requested", this, "_remote_object_requested", varray(id));
add_child(node);
return node;
}
void EditorDebuggerNode::_stack_frame_selected(int p_debugger) {
const ScriptEditorDebugger *dbg = get_debugger(p_debugger);
ERR_FAIL_COND(!dbg);
if (dbg != get_current_debugger())
return;
_text_editor_stack_goto(dbg);
}
void EditorDebuggerNode::_error_selected(const String &p_file, int p_line, int p_debugger) {
Ref<Script> s = ResourceLoader::load(p_file);
emit_signal("goto_script_line", s, p_line - 1);
}
void EditorDebuggerNode::_text_editor_stack_goto(const ScriptEditorDebugger *p_debugger) {
const String file = p_debugger->get_stack_script_file();
if (file.empty())
return;
stack_script = ResourceLoader::load(file);
const int line = p_debugger->get_stack_script_line() - 1;
emit_signal("goto_script_line", stack_script, line);
emit_signal("set_execution", stack_script, line);
stack_script.unref(); // Why?!?
}
void EditorDebuggerNode::_bind_methods() {
ClassDB::bind_method("_menu_option", &EditorDebuggerNode::_menu_option);
ClassDB::bind_method("_debugger_stopped", &EditorDebuggerNode::_debugger_stopped);
ClassDB::bind_method("_debugger_wants_stop", &EditorDebuggerNode::_debugger_wants_stop);
ClassDB::bind_method("_debugger_changed", &EditorDebuggerNode::_debugger_changed);
ClassDB::bind_method("_stack_frame_selected", &EditorDebuggerNode::_stack_frame_selected);
ClassDB::bind_method("_error_selected", &EditorDebuggerNode::_error_selected);
ClassDB::bind_method("_clear_execution", &EditorDebuggerNode::_clear_execution);
ClassDB::bind_method("_breaked", &EditorDebuggerNode::_breaked);
ClassDB::bind_method("start", &EditorDebuggerNode::start);
ClassDB::bind_method("stop", &EditorDebuggerNode::stop);
ClassDB::bind_method("_paused", &EditorDebuggerNode::_paused);
ClassDB::bind_method("request_remote_tree", &EditorDebuggerNode::request_remote_tree);
ClassDB::bind_method("_remote_tree_updated", &EditorDebuggerNode::_remote_tree_updated);
ClassDB::bind_method("_remote_object_updated", &EditorDebuggerNode::_remote_object_updated);
ClassDB::bind_method("_remote_object_property_updated", &EditorDebuggerNode::_remote_object_property_updated);
ClassDB::bind_method("_remote_object_requested", &EditorDebuggerNode::_remote_object_requested);
ClassDB::bind_method("_save_node_requested", &EditorDebuggerNode::_save_node_requested);
// LiveDebug.
ClassDB::bind_method("live_debug_create_node", &EditorDebuggerNode::live_debug_create_node);
ClassDB::bind_method("live_debug_instance_node", &EditorDebuggerNode::live_debug_instance_node);
ClassDB::bind_method("live_debug_remove_node", &EditorDebuggerNode::live_debug_remove_node);
ClassDB::bind_method("live_debug_remove_and_keep_node", &EditorDebuggerNode::live_debug_remove_and_keep_node);
ClassDB::bind_method("live_debug_restore_node", &EditorDebuggerNode::live_debug_restore_node);
ClassDB::bind_method("live_debug_duplicate_node", &EditorDebuggerNode::live_debug_duplicate_node);
ClassDB::bind_method("live_debug_reparent_node", &EditorDebuggerNode::live_debug_reparent_node);
ADD_SIGNAL(MethodInfo("goto_script_line"));
ADD_SIGNAL(MethodInfo("set_execution", PropertyInfo("script"), PropertyInfo(Variant::INT, "line")));
ADD_SIGNAL(MethodInfo("clear_execution", PropertyInfo("script")));
ADD_SIGNAL(MethodInfo("breaked", PropertyInfo(Variant::BOOL, "reallydid"), PropertyInfo(Variant::BOOL, "can_debug")));
}
EditorDebuggerRemoteObject *EditorDebuggerNode::get_inspected_remote_object() {
return Object::cast_to<EditorDebuggerRemoteObject>(ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_history()->get_current()));
}
ScriptEditorDebugger *EditorDebuggerNode::get_debugger(int p_id) const {
return Object::cast_to<ScriptEditorDebugger>(get_tab_control(p_id));
}
ScriptEditorDebugger *EditorDebuggerNode::get_current_debugger() const {
return Object::cast_to<ScriptEditorDebugger>(get_tab_control(get_current_tab()));
}
ScriptEditorDebugger *EditorDebuggerNode::get_default_debugger() const {
return Object::cast_to<ScriptEditorDebugger>(get_tab_control(0));
}
Error EditorDebuggerNode::start() {
stop();
if (EDITOR_GET("run/output/always_open_output_on_play")) {
EditorNode::get_singleton()->make_bottom_panel_item_visible(EditorNode::get_log());
} else {
EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
}
int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port");
const Error err = server->listen(remote_port);
if (err != OK) {
EditorNode::get_log()->add_message(String("Error listening on port ") + itos(remote_port), EditorLog::MSG_TYPE_ERROR);
return err;
}
set_process(true);
EditorNode::get_log()->add_message("--- Debugging process started ---", EditorLog::MSG_TYPE_EDITOR);
return OK;
}
void EditorDebuggerNode::stop() {
if (server->is_listening()) {
server->stop();
EditorNode::get_log()->add_message("--- Debugging process stopped ---", EditorLog::MSG_TYPE_EDITOR);
}
// Also close all debugging sessions.
_for_all(this, [&](ScriptEditorDebugger *dbg) {
if (dbg->is_session_active())
dbg->stop();
});
_break_state_changed();
if (hide_on_stop) {
if (is_visible_in_tree())
EditorNode::get_singleton()->hide_bottom_panel();
}
breakpoints.clear();
set_process(false);
}
void EditorDebuggerNode::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_POSTINITIALIZE: {
connect_compat("tab_changed", this, "_debugger_changed");
} break;
case NOTIFICATION_ENTER_TREE: {
EditorNode::get_singleton()->connect_compat("play_pressed", this, "start");
EditorNode::get_singleton()->connect_compat("stop_pressed", this, "stop");
} break;
case NOTIFICATION_EXIT_TREE: {
EditorNode::get_singleton()->disconnect_compat("play_pressed", this, "start");
EditorNode::get_singleton()->disconnect_compat("stop_pressed", this, "stop");
} break;
default:
break;
}
if (p_what != NOTIFICATION_PROCESS || !server->is_listening())
return;
// Errors and warnings
int error_count = 0;
int warning_count = 0;
_for_all(this, [&](ScriptEditorDebugger *dbg) {
error_count += dbg->get_error_count();
warning_count += dbg->get_warning_count();
});
if (error_count != last_error_count || warning_count != last_warning_count) {
_for_all(this, [&](ScriptEditorDebugger *dbg) {
dbg->update_tabs();
});
if (error_count == 0 && warning_count == 0) {
debugger_button->set_text(TTR("Debugger"));
debugger_button->set_icon(Ref<Texture2D>());
} else {
debugger_button->set_text(TTR("Debugger") + " (" + itos(error_count + warning_count) + ")");
if (error_count == 0) {
debugger_button->set_icon(get_icon("Warning", "EditorIcons"));
} else {
debugger_button->set_icon(get_icon("Error", "EditorIcons"));
}
}
last_error_count = error_count;
last_warning_count = warning_count;
}
// Remote scene tree update
remote_scene_tree_timeout -= get_process_delta_time();
if (remote_scene_tree_timeout < 0) {
remote_scene_tree_timeout = EditorSettings::get_singleton()->get("debugger/remote_scene_tree_refresh_interval");
if (remote_scene_tree->is_visible_in_tree()) {
get_current_debugger()->request_remote_tree();
}
}
// Remote inspector update
inspect_edited_object_timeout -= get_process_delta_time();
if (inspect_edited_object_timeout < 0) {
inspect_edited_object_timeout = EditorSettings::get_singleton()->get("debugger/remote_inspect_refresh_interval");
if (EditorDebuggerRemoteObject *obj = get_inspected_remote_object()) {
get_current_debugger()->request_remote_object(obj->remote_object_id);
}
}
// Take connections.
if (server->is_connection_available()) {
ScriptEditorDebugger *debugger = NULL;
_for_all(this, [&](ScriptEditorDebugger *dbg) {
if (debugger || dbg->is_session_active())
return;
debugger = dbg;
});
if (debugger == NULL) {
if (get_tab_count() <= 4) { // Max 4 debugging sessions active.
debugger = _add_debugger("Session " + itos(get_tab_count()));
} else {
// We already have too many sessions, disconnecting new clients to prevent it from hanging.
// (Not keeping a reference to the connection will disconnect it)
server->take_connection();
return; // Can't add, stop here.
}
}
EditorNode::get_singleton()->get_pause_button()->set_disabled(false);
// Switch to remote tree view if so desired.
auto_switch_remote_scene_tree = (bool)EditorSettings::get_singleton()->get("debugger/auto_switch_to_remote_scene_tree");
if (auto_switch_remote_scene_tree) {
EditorNode::get_singleton()->get_scene_tree_dock()->show_remote_tree();
}
// Good to go.
EditorNode::get_singleton()->get_scene_tree_dock()->show_tab_buttons();
debugger->set_editor_remote_tree(remote_scene_tree);
debugger->start(server->take_connection());
// Send breakpoints.
for (Map<Breakpoint, bool>::Element *E = breakpoints.front(); E; E = E->next()) {
const Breakpoint &bp = E->key();
debugger->set_breakpoint(bp.source, bp.line, E->get());
} // Will arrive too late, how does the regular run work?
debugger->update_live_edit_root();
}
}
void EditorDebuggerNode::_debugger_stopped(int p_id) {
ScriptEditorDebugger *dbg = get_debugger(p_id);
ERR_FAIL_COND(!dbg);
bool found = false;
_for_all(this, [&](ScriptEditorDebugger *p_debugger) {
if (p_debugger->is_session_active())
found = true;
});
if (!found) {
EditorNode::get_singleton()->get_pause_button()->set_pressed(false);
EditorNode::get_singleton()->get_pause_button()->set_disabled(true);
EditorNode::get_singleton()->get_scene_tree_dock()->hide_remote_tree();
EditorNode::get_singleton()->get_scene_tree_dock()->hide_tab_buttons();
EditorNode::get_singleton()->notify_all_debug_sessions_exited();
}
}
void EditorDebuggerNode::_debugger_wants_stop(int p_id) {
// Ask editor to kill PID.
int pid = get_debugger(p_id)->get_remote_pid();
if (pid)
EditorNode::get_singleton()->call_deferred("stop_child_process", pid);
}
void EditorDebuggerNode::_debugger_changed(int p_tab) {
if (get_inspected_remote_object()) {
// Clear inspected object, you can only inspect objects in selected debugger.
// Hopefully, in the future, we will have one inspector per debugger.
EditorNode::get_singleton()->push_item(NULL);
}
if (remote_scene_tree->is_visible_in_tree()) {
get_current_debugger()->request_remote_tree();
}
if (get_current_debugger()->is_breaked()) {
_text_editor_stack_goto(get_current_debugger());
}
}
void EditorDebuggerNode::set_script_debug_button(MenuButton *p_button) {
script_menu = p_button;
script_menu->set_text(TTR("Debug"));
script_menu->set_switch_on_hover(true);
PopupMenu *p = script_menu->get_popup();
p->set_hide_on_window_lose_focus(true);
p->add_shortcut(ED_GET_SHORTCUT("debugger/step_into"), DEBUG_STEP);
p->add_shortcut(ED_GET_SHORTCUT("debugger/step_over"), DEBUG_NEXT);
p->add_separator();
p->add_shortcut(ED_GET_SHORTCUT("debugger/break"), DEBUG_BREAK);
p->add_shortcut(ED_GET_SHORTCUT("debugger/continue"), DEBUG_CONTINUE);
p->add_separator();
p->add_check_shortcut(ED_GET_SHORTCUT("debugger/keep_debugger_open"), DEBUG_SHOW_KEEP_OPEN);
p->add_check_shortcut(ED_GET_SHORTCUT("debugger/debug_with_external_editor"), DEBUG_WITH_EXTERNAL_EDITOR);
p->connect_compat("id_pressed", this, "_menu_option");
_break_state_changed();
script_menu->show();
}
void EditorDebuggerNode::_break_state_changed() {
const bool breaked = get_current_debugger()->is_breaked();
const bool can_debug = get_current_debugger()->is_debuggable();
if (breaked) // Show debugger.
EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
// Update script menu.
if (!script_menu)
return;
PopupMenu *p = script_menu->get_popup();
p->set_item_disabled(p->get_item_index(DEBUG_NEXT), !(breaked && can_debug));
p->set_item_disabled(p->get_item_index(DEBUG_STEP), !(breaked && can_debug));
p->set_item_disabled(p->get_item_index(DEBUG_BREAK), breaked);
p->set_item_disabled(p->get_item_index(DEBUG_CONTINUE), !breaked);
}
void EditorDebuggerNode::_menu_option(int p_id) {
switch (p_id) {
case DEBUG_NEXT: {
debug_next();
} break;
case DEBUG_STEP: {
debug_step();
} break;
case DEBUG_BREAK: {
debug_break();
} break;
case DEBUG_CONTINUE: {
debug_continue();
} break;
case DEBUG_SHOW_KEEP_OPEN: {
bool visible = script_menu->get_popup()->is_item_checked(script_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN));
hide_on_stop = visible;
script_menu->get_popup()->set_item_checked(script_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN), !visible);
} break;
case DEBUG_WITH_EXTERNAL_EDITOR: {
bool checked = !script_menu->get_popup()->is_item_checked(script_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR));
debug_with_external_editor = checked;
script_menu->get_popup()->set_item_checked(script_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR), checked);
} break;
}
}
void EditorDebuggerNode::_paused() {
const bool paused = EditorNode::get_singleton()->get_pause_button()->is_pressed();
_for_all(this, [&](ScriptEditorDebugger *dbg) {
if (paused && !dbg->is_breaked()) {
dbg->debug_break();
} else if (!paused && dbg->is_breaked()) {
dbg->debug_continue();
}
});
}
void EditorDebuggerNode::_breaked(bool p_breaked, bool p_can_debug, int p_debugger) {
if (get_current_debugger() != get_debugger(p_debugger)) {
if (!p_breaked)
return;
set_current_tab(p_debugger);
}
_break_state_changed();
EditorNode::get_singleton()->get_pause_button()->set_pressed(p_breaked);
emit_signal("breaked", p_breaked, p_can_debug);
}
bool EditorDebuggerNode::is_skip_breakpoints() const {
return get_default_debugger()->is_skip_breakpoints();
}
void EditorDebuggerNode::set_breakpoint(const String &p_path, int p_line, bool p_enabled) {
breakpoints[Breakpoint(p_path, p_line)] = p_enabled;
_for_all(this, [&](ScriptEditorDebugger *dbg) {
dbg->set_breakpoint(p_path, p_line, p_enabled);
});
}
void EditorDebuggerNode::reload_scripts() {
_for_all(this, [&](ScriptEditorDebugger *dbg) {
dbg->reload_scripts();
});
}
// LiveEdit/Inspector
void EditorDebuggerNode::request_remote_tree() {
get_current_debugger()->request_remote_tree();
}
void EditorDebuggerNode::_remote_tree_updated(int p_debugger) {
if (p_debugger != get_current_tab())
return;
remote_scene_tree->clear();
remote_scene_tree->update_scene_tree(get_current_debugger()->get_remote_tree(), p_debugger);
}
void EditorDebuggerNode::_remote_object_updated(ObjectID p_id, int p_debugger) {
if (p_debugger != get_current_tab())
return;
if (EditorDebuggerRemoteObject *obj = get_inspected_remote_object()) {
if (obj->remote_object_id == p_id)
return; // Already being edited
}
EditorNode::get_singleton()->push_item(get_current_debugger()->get_remote_object(p_id));
}
void EditorDebuggerNode::_remote_object_property_updated(ObjectID p_id, const String &p_property, int p_debugger) {
if (p_debugger != get_current_tab())
return;
if (EditorDebuggerRemoteObject *obj = get_inspected_remote_object()) {
if (obj->remote_object_id != p_id)
return;
EditorNode::get_singleton()->get_inspector()->update_property(p_property);
}
}
void EditorDebuggerNode::_remote_object_requested(ObjectID p_id, int p_debugger) {
if (p_debugger != get_current_tab())
return;
inspect_edited_object_timeout = 0.7; // Temporarily disable timeout to avoid multiple requests.
get_current_debugger()->request_remote_object(p_id);
}
void EditorDebuggerNode::_save_node_requested(ObjectID p_id, const String &p_file, int p_debugger) {
if (p_debugger != get_current_tab())
return;
get_current_debugger()->save_node(p_id, p_file);
}
// Remote inspector/edit.
void EditorDebuggerNode::_method_changeds(void *p_ud, Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE) {
if (!singleton)
return;
_for_all(singleton, [&](ScriptEditorDebugger *dbg) {
dbg->_method_changed(p_base, p_name, VARIANT_ARG_PASS);
});
}
void EditorDebuggerNode::_property_changeds(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value) {
if (!singleton)
return;
_for_all(singleton, [&](ScriptEditorDebugger *dbg) {
dbg->_property_changed(p_base, p_property, p_value);
});
}
// LiveDebug
void EditorDebuggerNode::set_live_debugging(bool p_enabled) {
_for_all(this, [&](ScriptEditorDebugger *dbg) {
dbg->set_live_debugging(p_enabled);
});
}
void EditorDebuggerNode::update_live_edit_root() {
_for_all(this, [&](ScriptEditorDebugger *dbg) {
dbg->update_live_edit_root();
});
}
void EditorDebuggerNode::live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name) {
_for_all(this, [&](ScriptEditorDebugger *dbg) {
dbg->live_debug_create_node(p_parent, p_type, p_name);
});
}
void EditorDebuggerNode::live_debug_instance_node(const NodePath &p_parent, const String &p_path, const String &p_name) {
_for_all(this, [&](ScriptEditorDebugger *dbg) {
dbg->live_debug_instance_node(p_parent, p_path, p_name);
});
}
void EditorDebuggerNode::live_debug_remove_node(const NodePath &p_at) {
_for_all(this, [&](ScriptEditorDebugger *dbg) {
dbg->live_debug_remove_node(p_at);
});
}
void EditorDebuggerNode::live_debug_remove_and_keep_node(const NodePath &p_at, ObjectID p_keep_id) {
_for_all(this, [&](ScriptEditorDebugger *dbg) {
dbg->live_debug_remove_and_keep_node(p_at, p_keep_id);
});
}
void EditorDebuggerNode::live_debug_restore_node(ObjectID p_id, const NodePath &p_at, int p_at_pos) {
_for_all(this, [&](ScriptEditorDebugger *dbg) {
dbg->live_debug_restore_node(p_id, p_at, p_at_pos);
});
}
void EditorDebuggerNode::live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name) {
_for_all(this, [&](ScriptEditorDebugger *dbg) {
dbg->live_debug_duplicate_node(p_at, p_new_name);
});
}
void EditorDebuggerNode::live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos) {
_for_all(this, [&](ScriptEditorDebugger *dbg) {
dbg->live_debug_reparent_node(p_at, p_new_place, p_new_name, p_at_pos);
});
}

View file

@ -0,0 +1,176 @@
/*************************************************************************/
/* editor_debugger_node.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef EDITOR_DEBUGGER_NODE_H
#define EDITOR_DEBUGGER_NODE_H
#include "core/io/tcp_server.h"
#include "editor/debugger/script_editor_debugger.h"
#include "scene/gui/button.h"
#include "scene/gui/tab_container.h"
class EditorDebuggerTree;
class EditorDebuggerNode : public TabContainer {
GDCLASS(EditorDebuggerNode, TabContainer);
private:
enum Options {
DEBUG_NEXT,
DEBUG_STEP,
DEBUG_BREAK,
DEBUG_CONTINUE,
DEBUG_SHOW_KEEP_OPEN,
DEBUG_WITH_EXTERNAL_EDITOR,
};
class Breakpoint {
public:
String source;
int line = 0;
bool operator<(const Breakpoint &p_b) const {
if (line == p_b.line)
return line < p_b.line;
return line < p_b.line;
}
Breakpoint(){};
Breakpoint(const String &p_source, int p_line) {
line = p_line;
source = p_source;
}
};
Ref<TCP_Server> server = NULL;
Button *debugger_button = NULL;
MenuButton *script_menu = NULL;
Ref<Script> stack_script; // Why?!?
int last_error_count = 0;
int last_warning_count = 0;
float inspect_edited_object_timeout = 0;
EditorDebuggerTree *remote_scene_tree = NULL;
float remote_scene_tree_timeout = 0.0;
bool auto_switch_remote_scene_tree = false;
bool debug_with_external_editor = false;
bool hide_on_stop = true;
ScriptEditorDebugger::CameraOverride camera_override = ScriptEditorDebugger::OVERRIDE_NONE;
Map<Breakpoint, bool> breakpoints;
ScriptEditorDebugger *_add_debugger(String p_name);
EditorDebuggerRemoteObject *get_inspected_remote_object();
friend class DebuggerEditorPlugin;
static EditorDebuggerNode *singleton;
EditorDebuggerNode();
protected:
void _debugger_stopped(int p_id);
void _debugger_wants_stop(int p_id);
void _debugger_changed(int p_tab);
void _remote_tree_updated(int p_debugger);
void _remote_object_updated(ObjectID p_id, int p_debugger);
void _remote_object_property_updated(ObjectID p_id, const String &p_property, int p_debugger);
void _remote_object_requested(ObjectID p_id, int p_debugger);
void _save_node_requested(ObjectID p_id, const String &p_file, int p_debugger);
void _clear_execution(REF p_script) {
emit_signal("clear_execution", p_script);
}
void _text_editor_stack_goto(const ScriptEditorDebugger *p_debugger);
void _stack_frame_selected(int p_debugger);
void _error_selected(const String &p_file, int p_line, int p_debugger);
void _breaked(bool p_breaked, bool p_can_debug, int p_debugger);
void _paused();
void _break_state_changed();
void _menu_option(int p_id);
protected:
void _notification(int p_what);
static void _bind_methods();
public:
static EditorDebuggerNode *get_singleton() { return singleton; }
ScriptEditorDebugger *get_current_debugger() const;
ScriptEditorDebugger *get_default_debugger() const;
ScriptEditorDebugger *get_debugger(int p_debugger) const;
void debug_next() { get_default_debugger()->debug_next(); }
void debug_step() { get_default_debugger()->debug_step(); }
void debug_break() { get_default_debugger()->debug_break(); }
void debug_continue() { get_default_debugger()->debug_continue(); }
void set_script_debug_button(MenuButton *p_button);
void set_tool_button(Button *p_button) {
debugger_button = p_button;
}
String get_var_value(const String &p_var) const { return get_default_debugger()->get_var_value(p_var); }
Ref<Script> get_dump_stack_script() const { return stack_script; } // Why do we need this?
bool get_debug_with_external_editor() { return debug_with_external_editor; }
bool is_skip_breakpoints() const;
void set_breakpoint(const String &p_path, int p_line, bool p_enabled);
void reload_scripts();
// Remote inspector/edit.
void request_remote_tree();
static void _method_changeds(void *p_ud, Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE);
static void _property_changeds(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value);
// LiveDebug
void set_live_debugging(bool p_enabled);
void update_live_edit_root();
void live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name);
void live_debug_instance_node(const NodePath &p_parent, const String &p_path, const String &p_name);
void live_debug_remove_node(const NodePath &p_at);
void live_debug_remove_and_keep_node(const NodePath &p_at, ObjectID p_keep_id);
void live_debug_restore_node(ObjectID p_id, const NodePath &p_at, int p_at_pos);
void live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name);
void live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos);
// Camera
void set_camera_override(ScriptEditorDebugger::CameraOverride p_override) { camera_override = p_override; }
ScriptEditorDebugger::CameraOverride get_camera_override() { return camera_override; }
Error start();
void stop();
};
#endif // EDITOR_DEBUGGER_NODE_H

View file

@ -0,0 +1,274 @@
/*************************************************************************/
/* editor_debugger_tree.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "editor_debugger_tree.h"
#include "editor/editor_node.h"
#include "scene/debugger/scene_debugger.h"
#include "scene/resources/packed_scene.h"
EditorDebuggerTree::EditorDebuggerTree() {
set_v_size_flags(SIZE_EXPAND_FILL);
set_allow_rmb_select(true);
// Popup
item_menu = memnew(PopupMenu);
item_menu->connect_compat("id_pressed", this, "_item_menu_id_pressed");
add_child(item_menu);
// File Dialog
file_dialog = memnew(EditorFileDialog);
file_dialog->connect_compat("file_selected", this, "_file_selected");
add_child(file_dialog);
}
void EditorDebuggerTree::_notification(int p_what) {
if (p_what == NOTIFICATION_POSTINITIALIZE) {
connect_compat("cell_selected", this, "_scene_tree_selected");
connect_compat("item_collapsed", this, "_scene_tree_folded");
connect_compat("item_rmb_selected", this, "_scene_tree_rmb_selected");
}
}
void EditorDebuggerTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("_scene_tree_selected"), &EditorDebuggerTree::_scene_tree_selected);
ClassDB::bind_method(D_METHOD("_scene_tree_folded"), &EditorDebuggerTree::_scene_tree_folded);
ClassDB::bind_method(D_METHOD("_scene_tree_rmb_selected"), &EditorDebuggerTree::_scene_tree_rmb_selected);
ClassDB::bind_method(D_METHOD("_item_menu_id_pressed"), &EditorDebuggerTree::_item_menu_id_pressed);
ClassDB::bind_method(D_METHOD("_file_selected"), &EditorDebuggerTree::_file_selected);
ADD_SIGNAL(MethodInfo("object_selected", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::INT, "debugger")));
ADD_SIGNAL(MethodInfo("save_node", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "filename"), PropertyInfo(Variant::INT, "debugger")));
}
void EditorDebuggerTree::_scene_tree_selected() {
if (updating_scene_tree) {
return;
}
TreeItem *item = get_selected();
if (!item) {
return;
}
inspected_object_id = uint64_t(item->get_metadata(0));
emit_signal("object_selected", inspected_object_id, debugger_id);
}
void EditorDebuggerTree::_scene_tree_folded(Object *p_obj) {
if (updating_scene_tree) {
return;
}
TreeItem *item = Object::cast_to<TreeItem>(p_obj);
if (!item)
return;
ObjectID id = ObjectID(uint64_t(item->get_metadata(0)));
if (unfold_cache.has(id)) {
unfold_cache.erase(id);
} else {
unfold_cache.insert(id);
}
}
void EditorDebuggerTree::_scene_tree_rmb_selected(const Vector2 &p_position) {
TreeItem *item = get_item_at_position(p_position);
if (!item)
return;
item->select(0);
item_menu->clear();
item_menu->add_icon_item(get_icon("CreateNewSceneFrom", "EditorIcons"), TTR("Save Branch as Scene"), ITEM_MENU_SAVE_REMOTE_NODE);
item_menu->add_icon_item(get_icon("CopyNodePath", "EditorIcons"), TTR("Copy Node Path"), ITEM_MENU_COPY_NODE_PATH);
item_menu->set_global_position(get_global_mouse_position());
item_menu->popup();
}
/// Populates inspect_scene_tree given data in nodes as a flat list, encoded depth first.
///
/// Given a nodes array like [R,A,B,C,D,E] the following Tree will be generated, assuming
/// filter is an empty String, R and A child count are 2, B is 1 and C, D and E are 0.
///
/// R
/// |-A
/// | |-B
/// | | |-C
/// | |
/// | |-D
/// |
/// |-E
///
void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int p_debugger) {
updating_scene_tree = true;
const String last_path = get_selected_path();
const String filter = EditorNode::get_singleton()->get_scene_tree_dock()->get_filter();
// Nodes are in a flatten list, depth first. Use a stack of parents, avoid recursion.
List<Pair<TreeItem *, int> > parents;
for (int i = 0; i < p_tree->nodes.size(); i++) {
TreeItem *parent = NULL;
if (parents.size()) { // Find last parent.
Pair<TreeItem *, int> &p = parents[0];
parent = p.first;
if (!(--p.second)) { // If no child left, remove it.
parents.pop_front();
}
}
// Add this node.
const SceneDebuggerTree::RemoteNode &node = p_tree->nodes[i];
TreeItem *item = create_item(parent);
item->set_text(0, node.name);
item->set_tooltip(0, TTR("Type:") + " " + node.type_name);
Ref<Texture2D> icon = EditorNode::get_singleton()->get_class_icon(node.type_name, "");
if (icon.is_valid()) {
item->set_icon(0, icon);
}
item->set_metadata(0, node.id);
// Set current item as collapsed if necessary (root is never collapsed)
if (parent) {
if (!unfold_cache.has(node.id)) {
item->set_collapsed(true);
}
}
// Select previously selected node.
if (debugger_id == p_debugger) { // Can use remote id.
if (node.id == inspected_object_id) {
item->select(0);
}
} else { // Must use path
if (last_path == _get_path(item)) {
updating_scene_tree = false; // Force emission of new selection
item->select(0);
updating_scene_tree = true;
}
}
// Add in front of the parents stack if children are expected.
if (node.child_count) {
parents.push_front(Pair<TreeItem *, int>(item, node.child_count));
} else {
// Apply filters.
while (parent) {
const bool had_siblings = item->get_prev() || item->get_next();
if (filter.is_subsequence_ofi(item->get_text(0)))
break; // Filter matches, must survive.
parent->remove_child(item);
memdelete(item);
if (had_siblings)
break; // Parent must survive.
item = parent;
parent = item->get_parent();
// Check if parent expects more children.
for (int j = 0; j < parents.size(); j++) {
if (parents[j].first == item) {
parent = NULL;
break; // Might have more children.
}
}
}
}
}
debugger_id = p_debugger; // Needed by hook, could be avoided if every debugger had its own tree
updating_scene_tree = false;
}
String EditorDebuggerTree::get_selected_path() {
if (!get_selected())
return "";
return _get_path(get_selected());
}
String EditorDebuggerTree::_get_path(TreeItem *p_item) {
ERR_FAIL_COND_V(!p_item, "");
if (p_item->get_parent() == NULL) {
return "/root";
}
String text = p_item->get_text(0);
TreeItem *cur = p_item->get_parent();
while (cur) {
text = cur->get_text(0) + "/" + text;
cur = cur->get_parent();
}
return "/" + text;
}
void EditorDebuggerTree::_item_menu_id_pressed(int p_option) {
switch (p_option) {
case ITEM_MENU_SAVE_REMOTE_NODE: {
file_dialog->set_access(EditorFileDialog::ACCESS_RESOURCES);
file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
List<String> extensions;
Ref<PackedScene> sd = memnew(PackedScene);
ResourceSaver::get_recognized_extensions(sd, &extensions);
file_dialog->clear_filters();
for (int i = 0; i < extensions.size(); i++) {
file_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
}
file_dialog->popup_centered_ratio();
} break;
case ITEM_MENU_COPY_NODE_PATH: {
String text = get_selected_path();
if (text.empty()) {
return;
} else if (text == "/root") {
text = ".";
} else {
text = text.replace("/root/", "");
int slash = text.find("/");
if (slash < 0) {
text = ".";
} else {
text = text.substr(slash + 1);
}
}
OS::get_singleton()->set_clipboard(text);
} break;
}
}
void EditorDebuggerTree::_file_selected(const String &p_file) {
if (inspected_object_id.is_null())
return;
emit_signal("save_node", inspected_object_id, p_file, debugger_id);
}

View file

@ -0,0 +1,74 @@
/*************************************************************************/
/* editor_debugger_tree.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "scene/gui/tree.h"
#ifndef EDITOR_DEBUGGER_TREE_H
#define EDITOR_DEBUGGER_TREE_H
class SceneDebuggerTree;
class EditorFileDialog;
class EditorDebuggerTree : public Tree {
GDCLASS(EditorDebuggerTree, Tree);
private:
enum ItemMenu {
ITEM_MENU_SAVE_REMOTE_NODE,
ITEM_MENU_COPY_NODE_PATH,
};
ObjectID inspected_object_id;
int debugger_id = 0;
bool updating_scene_tree = false;
Set<ObjectID> unfold_cache;
PopupMenu *item_menu = NULL;
EditorFileDialog *file_dialog = NULL;
String _get_path(TreeItem *p_item);
void _scene_tree_folded(Object *p_obj);
void _scene_tree_selected();
void _scene_tree_rmb_selected(const Vector2 &p_position);
void _item_menu_id_pressed(int p_option);
void _file_selected(const String &p_file);
protected:
static void _bind_methods();
void _notification(int p_what);
public:
String get_selected_path();
ObjectID get_selected_object();
int get_current_debugger(); // Would love to have one tree for every debugger.
void update_scene_tree(const SceneDebuggerTree *p_tree, int p_debugger);
EditorDebuggerTree();
};
#endif // EDITOR_DEBUGGER_TREE_H

View file

@ -32,7 +32,8 @@
#define SCRIPT_EDITOR_DEBUGGER_H
#include "core/io/packet_peer.h"
#include "core/io/tcp_server.h"
#include "core/io/stream_peer_tcp.h"
#include "editor/debugger/editor_debugger_inspector.h"
#include "editor/editor_inspector.h"
#include "editor/property_editor.h"
#include "scene/3d/camera.h"
@ -41,7 +42,6 @@
class Tree;
class EditorNode;
class ScriptEditorDebuggerVariables;
class LineEdit;
class TabContainer;
class RichTextLabel;
@ -53,13 +53,14 @@ class ItemList;
class EditorProfiler;
class EditorVisualProfiler;
class EditorNetworkProfiler;
class ScriptEditorDebuggerInspectedObject;
class SceneDebuggerTree;
class ScriptEditorDebugger : public MarginContainer {
GDCLASS(ScriptEditorDebugger, MarginContainer);
friend class EditorDebuggerNode;
public:
enum CameraOverride {
OVERRIDE_NONE,
@ -77,16 +78,8 @@ private:
MESSAGE_SUCCESS,
};
enum ItemMenu {
ITEM_MENU_COPY_ERROR,
ITEM_MENU_SAVE_REMOTE_NODE,
ITEM_MENU_COPY_NODE_PATH,
};
AcceptDialog *msgdialog;
Button *debugger_button;
LineEdit *clicked_ctrl;
LineEdit *clicked_ctrl_type;
LineEdit *live_edit_root;
@ -94,35 +87,15 @@ private:
Button *le_clear;
Button *export_csv;
bool updating_scene_tree;
float inspect_scene_tree_timeout;
float inspect_edited_object_timeout;
bool auto_switch_remote_scene_tree;
ObjectID inspected_object_id;
ScriptEditorDebuggerVariables *variables;
Map<ObjectID, ScriptEditorDebuggerInspectedObject *> remote_objects;
Set<ObjectID> unfold_cache;
VBoxContainer *errors_tab;
Tree *error_tree;
Tree *inspect_scene_tree;
Button *clearbutton;
PopupMenu *item_menu;
EditorFileDialog *file_dialog;
enum FileDialogMode {
SAVE_CSV,
SAVE_NODE,
};
FileDialogMode file_dialog_mode;
int error_count;
int warning_count;
int last_error_count;
int last_warning_count;
bool hide_on_stop;
bool enable_external_editor;
bool skip_breakpoints_value = false;
Ref<Script> stack_script;
@ -135,10 +108,11 @@ private:
Button *copy;
Button *step;
Button *next;
Button *back;
Button *forward;
Button *dobreak;
Button *docontinue;
// Reference to "Remote" tab in scene tree. Needed by _live_edit_set and buttons state.
// Each debugger should have it's tree in the future I guess.
const Tree *editor_remote_tree = NULL;
List<Vector<float> > perf_history;
Vector<float> perf_max;
@ -155,16 +129,12 @@ private:
LineEdit *vmem_total;
Tree *stack_dump;
EditorInspector *inspector;
EditorDebuggerInspector *inspector;
SceneDebuggerTree *scene_tree;
Ref<TCP_Server> server;
Ref<StreamPeerTCP> connection;
Ref<PacketPeerStream> ppeer;
String message_type;
Array message;
int pending_in_queue;
HashMap<NodePath, int> node_path_cache;
int last_path_id;
Map<String, int> res_path_cache;
@ -175,7 +145,9 @@ private:
EditorNode *editor;
bool breaked;
OS::ProcessID remote_pid = 0;
bool breaked = false;
bool can_debug = false;
bool live_debug;
@ -184,18 +156,14 @@ private:
void _performance_draw();
void _performance_select();
void _stack_dump_frame_selected();
void _output_clear();
void _scene_tree_folded(Object *obj);
void _scene_tree_selected();
void _scene_tree_rmb_selected(const Vector2 &p_position);
void _file_selected(const String &p_file);
void _scene_tree_request();
void _parse_message(const String &p_msg, const Array &p_data);
void _set_reason_text(const String &p_reason, MessageType p_type);
void _scene_tree_property_select_object(ObjectID p_object);
void _scene_tree_property_value_edited(const String &p_prop, const Variant &p_value);
int _update_scene_tree(TreeItem *parent, const Array &nodes, int current_index);
void _update_buttons_state();
void _remote_object_selected(ObjectID p_object);
void _remote_object_edited(ObjectID, const String &p_prop, const Variant &p_value);
void _remote_object_property_updated(ObjectID p_id, const String &p_property);
void _video_mem_request();
@ -221,28 +189,34 @@ private:
void _network_profiler_activate(bool p_enable);
void _paused();
void _set_remote_object(ObjectID p_id, ScriptEditorDebuggerInspectedObject *p_obj);
void _clear_remote_objects();
void _clear_errors_list();
void _error_tree_item_rmb_selected(const Vector2 &p_pos);
void _item_menu_id_pressed(int p_option);
void _tab_changed(int p_tab);
void _put_msg(String p_message, Array p_data);
void _export_csv();
void _clear_execution();
void _stop_and_notify();
protected:
void _notification(int p_what);
static void _bind_methods();
public:
void start();
void pause();
void unpause();
void request_remote_object(ObjectID p_obj_id);
void update_remote_object(ObjectID p_obj_id, const String &p_prop, const Variant &p_value);
Object *get_remote_object(ObjectID p_id);
// Needed by _live_edit_set, buttons state.
void set_editor_remote_tree(const Tree *p_tree) { editor_remote_tree = p_tree; }
void request_remote_tree();
const SceneDebuggerTree *get_remote_tree();
void start(Ref<StreamPeerTCP> p_connection);
void stop();
void debug_skip_breakpoints();
@ -252,14 +226,23 @@ public:
void debug_step();
void debug_break();
void debug_continue();
bool is_breaked() const { return breaked; }
bool is_debuggable() const { return can_debug; }
bool is_session_active() { return connection.is_valid() && connection->is_connected_to_host(); };
int get_remote_pid() const { return remote_pid; }
int get_error_count() const { return error_count; }
int get_warning_count() const { return warning_count; }
String get_stack_script_file() const;
int get_stack_script_line() const;
int get_stack_script_frame() const;
void update_tabs();
String get_var_value(const String &p_var) const;
void save_node(ObjectID p_id, const String &p_file);
void set_live_debugging(bool p_enable);
static void _method_changeds(void *p_ud, Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE);
static void _property_changeds(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value);
void live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name);
void live_debug_instance_node(const NodePath &p_parent, const String &p_path, const String &p_name);
void live_debug_remove_node(const NodePath &p_at);
@ -275,15 +258,6 @@ public:
void update_live_edit_root();
void set_hide_on_stop(bool p_hide);
bool get_debug_with_external_editor() const;
void set_debug_with_external_editor(bool p_enabled);
Ref<Script> get_dump_stack_script() const;
void set_tool_button(Button *p_tb) { debugger_button = p_tb; }
void reload_scripts();
bool is_skip_breakpoints();

View file

@ -123,6 +123,7 @@
#include "editor/plugins/cpu_particles_2d_editor_plugin.h"
#include "editor/plugins/cpu_particles_editor_plugin.h"
#include "editor/plugins/curve_editor_plugin.h"
#include "editor/plugins/debugger_editor_plugin.h"
#include "editor/plugins/editor_preview_plugins.h"
#include "editor/plugins/gi_probe_editor_plugin.h"
#include "editor/plugins/gradient_editor_plugin.h"
@ -168,7 +169,6 @@
#include "editor/quick_open.h"
#include "editor/register_exporters.h"
#include "editor/run_settings_dialog.h"
#include "editor/script_editor_debugger.h"
#include "editor/settings_config_dialog.h"
#include <stdio.h>
@ -472,7 +472,7 @@ void EditorNode::_notification(int p_what) {
recent_scenes->set_as_minsize();
// debugger area
if (ScriptEditor::get_singleton()->get_debugger()->is_visible())
if (EditorDebuggerNode::get_singleton()->is_visible())
bottom_panel->add_style_override("panel", gui_base->get_stylebox("BottomPanelDebuggerOverride", "EditorStyles"));
// update_icons
@ -1846,7 +1846,7 @@ void EditorNode::_edit_current() {
Node *selected_node = NULL;
if (current_obj->is_class("ScriptEditorDebuggerInspectedObject")) {
if (current_obj->is_class("EditorDebuggerRemoteObject")) {
editable_warning = TTR("This is a remote object, so changes to it won't be kept.\nPlease read the documentation relevant to debugging to better understand this workflow.");
capitalize = false;
disable_folding = true;
@ -2048,9 +2048,13 @@ void EditorNode::_run(bool p_current, const String &p_custom) {
editor_data.get_editor_breakpoints(&breakpoints);
args = ProjectSettings::get_singleton()->get("editor/main_run_args");
skip_breakpoints = ScriptEditor::get_singleton()->get_debugger()->is_skip_breakpoints();
skip_breakpoints = EditorDebuggerNode::get_singleton()->is_skip_breakpoints();
Error error = editor_run.run(run_filename, args, breakpoints, skip_breakpoints);
int instances = 1;
if (debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_TWO)))
instances = 2;
Error error = editor_run.run(run_filename, args, breakpoints, skip_breakpoints, instances);
if (error != OK) {
@ -2481,6 +2485,16 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
run_settings_dialog->popup_run_settings();
} break;
case RUN_DEBUG_ONE: {
debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_ONE), true);
debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_TWO), false);
} break;
case RUN_DEBUG_TWO: {
debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_TWO), true);
debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_ONE), false);
} break;
case RUN_SETTINGS: {
project_settings->popup_project_settings();
@ -2571,7 +2585,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_LIVE_DEBUG));
debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_LIVE_DEBUG), !ischecked);
ScriptEditor::get_singleton()->get_debugger()->set_live_debugging(!ischecked);
EditorDebuggerNode::get_singleton()->set_live_debugging(!ischecked);
EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_live_debug", !ischecked);
} break;
@ -3242,7 +3256,7 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) {
//this should only happen at the very end
ScriptEditor::get_singleton()->get_debugger()->update_live_edit_root();
EditorDebuggerNode::get_singleton()->update_live_edit_root();
ScriptEditor::get_singleton()->set_scene_root_script(editor_data.get_scene_root_script(editor_data.get_edited_scene()));
editor_data.notify_edited_scene_changed();
}
@ -3477,7 +3491,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
opening_prev = false;
scene_tree_dock->set_selected(new_scene);
ScriptEditor::get_singleton()->get_debugger()->update_live_edit_root();
EditorDebuggerNode::get_singleton()->update_live_edit_root();
push_item(new_scene);
@ -3619,7 +3633,7 @@ void EditorNode::_quick_run() {
_run(false, quick_run->get_selected());
}
void EditorNode::notify_child_process_exited() {
void EditorNode::notify_all_debug_sessions_exited() {
_menu_option_confirm(RUN_STOP, false);
stop_button->set_pressed(false);
@ -3703,9 +3717,13 @@ void EditorNode::unregister_editor_types() {
_init_callbacks.clear();
}
void EditorNode::stop_child_process() {
void EditorNode::stop_child_process(OS::ProcessID p_pid) {
_menu_option_confirm(RUN_STOP, false);
if (has_child_process(p_pid)) {
editor_run.stop_child_process(p_pid);
if (!editor_run.get_child_process_count()) // All children stopped. Closing.
_menu_option_confirm(RUN_STOP, false);
}
}
Ref<Script> EditorNode::get_object_custom_type_base(const Object *p_object) const {
@ -4888,7 +4906,7 @@ void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) {
bottom_panel_items[i].button->set_pressed(i == p_idx);
bottom_panel_items[i].control->set_visible(i == p_idx);
}
if (ScriptEditor::get_singleton()->get_debugger() == bottom_panel_items[p_idx].control) { // this is the debug panel which uses tabs, so the top section should be smaller
if (EditorDebuggerNode::get_singleton() == bottom_panel_items[p_idx].control) { // this is the debug panel which uses tabs, so the top section should be smaller
bottom_panel->add_style_override("panel", gui_base->get_stylebox("BottomPanelDebuggerOverride", "EditorStyles"));
} else {
bottom_panel->add_style_override("panel", gui_base->get_stylebox("panel", "TabContainer"));
@ -6254,6 +6272,13 @@ EditorNode::EditorNode() {
p->add_check_shortcut(ED_SHORTCUT("editor/sync_script_changes", TTR("Sync Script Changes")), RUN_RELOAD_SCRIPTS);
p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is turned on, any script that is saved will be reloaded on the running game.\nWhen used remotely on a device, this is more efficient with network filesystem."));
p->set_item_checked(p->get_item_count() - 1, true);
// Multi-instance, start/stop
p->add_separator();
p->add_radio_check_item(TTR("Debug 1 instance"), RUN_DEBUG_ONE);
p->add_radio_check_item(TTR("Debug 2 instances"), RUN_DEBUG_TWO);
p->set_item_checked(p->get_item_index(RUN_DEBUG_ONE), true);
p->connect_compat("id_pressed", this, "_menu_option");
menu_hb->add_spacer();
@ -6639,6 +6664,7 @@ EditorNode::EditorNode() {
file_server = memnew(EditorFileServer);
add_editor_plugin(memnew(DebuggerEditorPlugin(this)));
add_editor_plugin(memnew(AnimationPlayerEditorPlugin(this)));
add_editor_plugin(memnew(CanvasItemEditorPlugin(this)));
add_editor_plugin(memnew(SpatialEditorPlugin(this)));

View file

@ -162,6 +162,8 @@ private:
RUN_PLAY_NATIVE,
RUN_PLAY_CUSTOM_SCENE,
RUN_SCENE_SETTINGS,
RUN_DEBUG_ONE,
RUN_DEBUG_TWO,
RUN_SETTINGS,
RUN_PROJECT_DATA_FOLDER,
RUN_PROJECT_MANAGER,
@ -764,10 +766,10 @@ public:
void set_convert_old_scene(bool p_old) { convert_old = p_old; }
void notify_child_process_exited();
void notify_all_debug_sessions_exited();
OS::ProcessID get_child_process_id() const { return editor_run.get_pid(); }
void stop_child_process();
OS::ProcessID has_child_process(OS::ProcessID p_pid) const { return editor_run.has_child_process(p_pid); }
void stop_child_process(OS::ProcessID p_pid);
Ref<Theme> get_editor_theme() const { return theme; }
Ref<Script> get_object_custom_type_base(const Object *p_object) const;

View file

@ -106,7 +106,7 @@ void EditorPath::update_path() {
if (name == "")
name = r->get_class();
} else if (obj->is_class("ScriptEditorDebuggerInspectedObject"))
} else if (obj->is_class("EditorDebuggerRemoteObject"))
name = obj->call("get_title");
else if (Object::cast_to<Node>(obj))
name = Object::cast_to<Node>(obj)->get_name();

View file

@ -38,7 +38,7 @@ EditorRun::Status EditorRun::get_status() const {
return status;
}
Error EditorRun::run(const String &p_scene, const String &p_custom_args, const List<String> &p_breakpoints, const bool &p_skip_breakpoints) {
Error EditorRun::run(const String &p_scene, const String &p_custom_args, const List<String> &p_breakpoints, const bool &p_skip_breakpoints, const int &p_instances) {
List<String> args;
@ -187,20 +187,40 @@ Error EditorRun::run(const String &p_scene, const String &p_custom_args, const L
};
printf("\n");
pid = 0;
Error err = OS::get_singleton()->execute(exec, args, false, &pid);
ERR_FAIL_COND_V(err, err);
for (int i = 0; i < p_instances; i++) {
OS::ProcessID pid = 0;
Error err = OS::get_singleton()->execute(exec, args, false, &pid);
ERR_FAIL_COND_V(err, err);
pids.push_back(pid);
}
status = STATUS_PLAY;
return OK;
}
bool EditorRun::has_child_process(OS::ProcessID p_pid) const {
for (const List<OS::ProcessID>::Element *E = pids.front(); E; E = E->next()) {
if (E->get() == p_pid)
return true;
}
return false;
}
void EditorRun::stop_child_process(OS::ProcessID p_pid) {
if (has_child_process(p_pid)) {
OS::get_singleton()->kill(p_pid);
pids.erase(p_pid);
}
}
void EditorRun::stop() {
if (status != STATUS_STOP && pid != 0) {
if (status != STATUS_STOP && pids.size() > 0) {
OS::get_singleton()->kill(pid);
for (List<OS::ProcessID>::Element *E = pids.front(); E; E = E->next()) {
OS::get_singleton()->kill(E->get());
}
}
status = STATUS_STOP;

View file

@ -42,7 +42,7 @@ public:
STATUS_STOP
};
OS::ProcessID pid;
List<OS::ProcessID> pids;
private:
bool debug_collisions;
@ -51,11 +51,13 @@ private:
public:
Status get_status() const;
Error run(const String &p_scene, const String &p_custom_args, const List<String> &p_breakpoints, const bool &p_skip_breakpoints = false);
Error run(const String &p_scene, const String &p_custom_args, const List<String> &p_breakpoints, const bool &p_skip_breakpoints = false, const int &p_instances = 1);
void run_native_notify() { status = STATUS_PLAY; }
void stop();
OS::ProcessID get_pid() const { return pid; }
void stop_child_process(OS::ProcessID p_pid);
bool has_child_process(OS::ProcessID p_pid) const;
int get_child_process_count() const { return pids.size(); }
void set_debug_collisions(bool p_debug);
bool get_debug_collisions() const;

View file

@ -247,7 +247,7 @@ void InspectorDock::_prepare_history() {
}
} else if (Object::cast_to<Node>(obj)) {
text = Object::cast_to<Node>(obj)->get_name();
} else if (obj->is_class("ScriptEditorDebuggerInspectedObject")) {
} else if (obj->is_class("EditorDebuggerRemoteObject")) {
text = obj->call("get_title");
} else {
text = obj->get_class();

View file

@ -34,12 +34,12 @@
#include "core/os/keyboard.h"
#include "core/print_string.h"
#include "core/project_settings.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/plugins/animation_player_editor_plugin.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/script_editor_debugger.h"
#include "scene/2d/light_2d.h"
#include "scene/2d/particles_2d.h"
#include "scene/2d/polygon_2d.h"
@ -3990,7 +3990,7 @@ void CanvasItemEditor::_notification(int p_what) {
if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
if (!is_visible() && override_camera_button->is_pressed()) {
ScriptEditorDebugger *debugger = ScriptEditor::get_singleton()->get_debugger();
EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton();
debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_NONE);
override_camera_button->set_pressed(false);
@ -4345,7 +4345,7 @@ void CanvasItemEditor::_button_toggle_grid_snap(bool p_status) {
viewport->update();
}
void CanvasItemEditor::_button_override_camera(bool p_pressed) {
ScriptEditorDebugger *debugger = ScriptEditor::get_singleton()->get_debugger();
EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton();
if (p_pressed) {
debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_2D);
@ -5960,9 +5960,9 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &
if (parent) {
String new_name = parent->validate_child_name(child);
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
editor_data->get_undo_redo().add_do_method(sed, "live_debug_create_node", editor->get_edited_scene()->get_path_to(parent), child->get_class(), new_name);
editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
editor_data->get_undo_redo().add_do_method(ed, "live_debug_create_node", editor->get_edited_scene()->get_path_to(parent), child->get_class(), new_name);
editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
}
// handle with different property for texture
@ -6030,9 +6030,9 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons
editor_data->get_undo_redo().add_undo_method(parent, "remove_child", instanced_scene);
String new_name = parent->validate_child_name(instanced_scene);
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
editor_data->get_undo_redo().add_do_method(sed, "live_debug_instance_node", editor->get_edited_scene()->get_path_to(parent), path, new_name);
editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
editor_data->get_undo_redo().add_do_method(ed, "live_debug_instance_node", editor->get_edited_scene()->get_path_to(parent), path, new_name);
editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
CanvasItem *parent_ci = Object::cast_to<CanvasItem>(parent);
if (parent_ci) {

View file

@ -0,0 +1,51 @@
/*************************************************************************/
/* debugger_editor_plugin.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "debugger_editor_plugin.h"
#include "core/os/keyboard.h"
#include "editor/debugger/editor_debugger_node.h"
DebuggerEditorPlugin::DebuggerEditorPlugin(EditorNode *p_editor) {
ED_SHORTCUT("debugger/step_into", TTR("Step Into"), KEY_F11);
ED_SHORTCUT("debugger/step_over", TTR("Step Over"), KEY_F10);
ED_SHORTCUT("debugger/break", TTR("Break"));
ED_SHORTCUT("debugger/continue", TTR("Continue"), KEY_F12);
ED_SHORTCUT("debugger/keep_debugger_open", TTR("Keep Debugger Open"));
ED_SHORTCUT("debugger/debug_with_external_editor", TTR("Debug with External Editor"));
EditorDebuggerNode *debugger = memnew(EditorDebuggerNode);
Button *db = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Debugger"), debugger);
debugger->set_tool_button(db);
}
DebuggerEditorPlugin::~DebuggerEditorPlugin() {
// Should delete debugger?
}

View file

@ -0,0 +1,50 @@
/*************************************************************************/
/* debugger_editor_plugin.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef DEBUGGER_EDITOR_PLUGIN_H
#define DEBUGGER_EDITOR_PLUGIN_H
#include "editor/debugger/editor_debugger_node.h"
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
class DebuggerEditorPlugin : public EditorPlugin {
GDCLASS(DebuggerEditorPlugin, EditorPlugin);
public:
virtual String get_name() const { return "Debugger"; }
bool has_main_screen() const { return false; }
DebuggerEditorPlugin(EditorNode *p_node);
~DebuggerEditorPlugin();
};
#endif // DEBUGGER_EDITOR_PLUGIN_H

View file

@ -36,6 +36,8 @@
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "core/project_settings.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/debugger/script_editor_debugger.h"
#include "editor/editor_node.h"
#include "editor/editor_run_script.h"
#include "editor/editor_scale.h"
@ -44,7 +46,6 @@
#include "editor/find_in_files.h"
#include "editor/node_dock.h"
#include "editor/plugins/shader_editor_plugin.h"
#include "editor/script_editor_debugger.h"
#include "scene/main/viewport.h"
#include "scene/scene_string_names.h"
#include "script_text_editor.h"
@ -261,7 +262,7 @@ ScriptEditor *ScriptEditor::script_editor = NULL;
String ScriptEditor::_get_debug_tooltip(const String &p_text, Node *_se) {
String val = debugger->get_var_value(p_text);
String val = EditorDebuggerNode::get_singleton()->get_var_value(p_text);
if (val != String()) {
return p_text + ": " + val;
} else {
@ -276,11 +277,6 @@ void ScriptEditor::_breaked(bool p_breaked, bool p_can_debug) {
return;
}
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), !(p_breaked && p_can_debug));
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), !(p_breaked && p_can_debug));
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), p_breaked);
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), !p_breaked);
for (int i = 0; i < tab_container->get_child_count(); i++) {
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
@ -292,11 +288,6 @@ void ScriptEditor::_breaked(bool p_breaked, bool p_can_debug) {
}
}
void ScriptEditor::_show_debugger(bool p_show) {
//debug_menu->get_popup()->set_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW), p_show);
}
void ScriptEditor::_script_created(Ref<Script> p_script) {
editor->push_item(p_script.operator->());
}
@ -843,7 +834,7 @@ void ScriptEditor::_res_saved_callback(const Ref<Resource> &p_res) {
void ScriptEditor::_live_auto_reload_running_scripts() {
pending_auto_reload = false;
debugger->reload_scripts();
EditorDebuggerNode::get_singleton()->reload_scripts();
}
bool ScriptEditor::_test_script_times_on_disk(RES p_for_script) {
@ -1123,27 +1114,6 @@ void ScriptEditor::_menu_option(int p_option) {
_sort_list_on_update = true;
_update_script_names();
} break;
case DEBUG_SHOW: {
if (debugger) {
bool visible = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW));
debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW), !visible);
if (visible)
debugger->hide();
else
debugger->show();
}
} break;
case DEBUG_SHOW_KEEP_OPEN: {
bool visible = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN));
if (debugger)
debugger->set_hide_on_stop(visible);
debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN), !visible);
} break;
case DEBUG_WITH_EXTERNAL_EDITOR: {
bool debug_with_external_editor = !debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR));
debugger->set_debug_with_external_editor(debug_with_external_editor);
debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR), debug_with_external_editor);
} break;
case TOGGLE_SCRIPTS_PANEL: {
if (current) {
ScriptTextEditor *editor = Object::cast_to<ScriptTextEditor>(current);
@ -1294,29 +1264,6 @@ void ScriptEditor::_menu_option(int p_option) {
case CLOSE_ALL: {
_close_all_tabs();
} break;
case DEBUG_NEXT: {
if (debugger)
debugger->debug_next();
} break;
case DEBUG_STEP: {
if (debugger)
debugger->debug_step();
} break;
case DEBUG_BREAK: {
if (debugger)
debugger->debug_break();
} break;
case DEBUG_CONTINUE: {
if (debugger)
debugger->debug_continue();
} break;
case WINDOW_MOVE_UP: {
if (tab_container->get_current_tab() > 0) {
@ -1439,8 +1386,6 @@ void ScriptEditor::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
editor->connect_compat("play_pressed", this, "_editor_play");
editor->connect_compat("pause_pressed", this, "_editor_pause");
editor->connect_compat("stop_pressed", this, "_editor_stop");
editor->connect_compat("script_add_function_request", this, "_add_callback");
editor->connect_compat("resource_saved", this, "_res_saved_callback");
@ -1481,8 +1426,6 @@ void ScriptEditor::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
editor->disconnect_compat("play_pressed", this, "_editor_play");
editor->disconnect_compat("pause_pressed", this, "_editor_pause");
editor->disconnect_compat("stop_pressed", this, "_editor_stop");
} break;
@ -2062,7 +2005,7 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
return false;
}
if ((debugger->get_dump_stack_script() != p_resource || debugger->get_debug_with_external_editor()) &&
if ((EditorDebuggerNode::get_singleton()->get_dump_stack_script() != p_resource || EditorDebuggerNode::get_singleton()->get_debug_with_external_editor()) &&
p_resource->get_path().is_resource_file() &&
p_resource->get_class_name() != StringName("VisualScript") &&
bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor"))) {
@ -2277,26 +2220,7 @@ void ScriptEditor::open_script_create_dialog(const String &p_base_name, const St
script_create_dialog->config(p_base_name, p_base_path);
}
void ScriptEditor::_editor_play() {
debugger->start();
debug_menu->get_popup()->grab_focus();
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true);
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), true);
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), false);
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true);
}
void ScriptEditor::_editor_pause() {
}
void ScriptEditor::_editor_stop() {
debugger->stop();
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true);
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), true);
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), true);
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true);
for (int i = 0; i < tab_container->get_child_count(); i++) {
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
@ -3125,8 +3049,6 @@ void ScriptEditor::_bind_methods() {
ClassDB::bind_method("_close_other_tabs", &ScriptEditor::_close_other_tabs);
ClassDB::bind_method("_open_recent_script", &ScriptEditor::_open_recent_script);
ClassDB::bind_method("_theme_option", &ScriptEditor::_theme_option);
ClassDB::bind_method("_editor_play", &ScriptEditor::_editor_play);
ClassDB::bind_method("_editor_pause", &ScriptEditor::_editor_pause);
ClassDB::bind_method("_editor_stop", &ScriptEditor::_editor_stop);
ClassDB::bind_method("_add_callback", &ScriptEditor::_add_callback);
ClassDB::bind_method("_reload_scripts", &ScriptEditor::_reload_scripts);
@ -3141,7 +3063,6 @@ void ScriptEditor::_bind_methods() {
ClassDB::bind_method("_copy_script_path", &ScriptEditor::_copy_script_path);
ClassDB::bind_method("_breaked", &ScriptEditor::_breaked);
ClassDB::bind_method("_show_debugger", &ScriptEditor::_show_debugger);
ClassDB::bind_method("_get_debug_tooltip", &ScriptEditor::_get_debug_tooltip);
ClassDB::bind_method("_autosave_scripts", &ScriptEditor::_autosave_scripts);
ClassDB::bind_method("_update_autosave_timer", &ScriptEditor::_update_autosave_timer);
@ -3358,26 +3279,16 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
script_search_menu->get_popup()->set_hide_on_window_lose_focus(true);
script_search_menu->get_popup()->connect_compat("id_pressed", this, "_menu_option");
debug_menu = memnew(MenuButton);
MenuButton *debug_menu = memnew(MenuButton);
menu_hb->add_child(debug_menu);
debug_menu->set_text(TTR("Debug"));
debug_menu->set_switch_on_hover(true);
debug_menu->get_popup()->set_hide_on_window_lose_focus(true);
debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_into", TTR("Step Into"), KEY_F11), DEBUG_STEP);
debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_over", TTR("Step Over"), KEY_F10), DEBUG_NEXT);
debug_menu->get_popup()->add_separator();
debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/break", TTR("Break")), DEBUG_BREAK);
debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/continue", TTR("Continue"), KEY_F12), DEBUG_CONTINUE);
debug_menu->get_popup()->add_separator();
//debug_menu->get_popup()->add_check_item("Show Debugger",DEBUG_SHOW);
debug_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("debugger/keep_debugger_open", TTR("Keep Debugger Open")), DEBUG_SHOW_KEEP_OPEN);
debug_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("debugger/debug_with_external_editor", TTR("Debug with External Editor")), DEBUG_WITH_EXTERNAL_EDITOR);
debug_menu->get_popup()->connect_compat("id_pressed", this, "_menu_option");
debug_menu->hide(); // Handled by EditorDebuggerNode below.
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true);
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), true);
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), true);
debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true);
EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton();
debugger->set_script_debug_button(debug_menu);
debugger->connect_compat("goto_script_line", this, "_goto_script_line");
debugger->connect_compat("set_execution", this, "_set_execution");
debugger->connect_compat("clear_execution", this, "_clear_execution");
debugger->connect_compat("breaked", this, "_breaked");
menu_hb->add_spacer();
@ -3445,12 +3356,6 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
error_dialog = memnew(AcceptDialog);
add_child(error_dialog);
debugger = memnew(ScriptEditorDebugger(editor));
debugger->connect_compat("goto_script_line", this, "_goto_script_line");
debugger->connect_compat("set_execution", this, "_set_execution");
debugger->connect_compat("clear_execution", this, "_clear_execution");
debugger->connect_compat("show_debugger", this, "_show_debugger");
disk_changed = memnew(ConfirmationDialog);
{
VBoxContainer *vbc = memnew(VBoxContainer);
@ -3475,11 +3380,6 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
script_editor = this;
Button *db = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Debugger"), debugger);
debugger->set_tool_button(db);
debugger->connect_compat("breaked", this, "_breaked");
autosave_timer = memnew(Timer);
autosave_timer->set_one_shot(false);
autosave_timer->connect_compat(SceneStringNames::get_singleton()->tree_entered, this, "_update_autosave_timer");
@ -3505,7 +3405,6 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
find_in_files_button->hide();
history_pos = -1;
//debugger_gui->hide();
edit_pass = 0;
trim_trailing_whitespace_on_save = EditorSettings::get_singleton()->get("text_editor/files/trim_trailing_whitespace_on_save");

View file

@ -73,7 +73,7 @@ public:
ScriptEditorQuickOpen();
};
class ScriptEditorDebugger;
class EditorDebuggerNode;
class ScriptEditorBase : public VBoxContainer {
@ -155,13 +155,6 @@ class ScriptEditor : public PanelContainer {
FILE_COPY_PATH,
FILE_TOOL_RELOAD,
FILE_TOOL_RELOAD_SOFT,
DEBUG_NEXT,
DEBUG_STEP,
DEBUG_BREAK,
DEBUG_CONTINUE,
DEBUG_SHOW,
DEBUG_SHOW_KEEP_OPEN,
DEBUG_WITH_EXTERNAL_EDITOR,
SEARCH_IN_FILES,
REPLACE_IN_FILES,
SEARCH_HELP,
@ -233,7 +226,6 @@ class ScriptEditor : public PanelContainer {
AcceptDialog *error_dialog;
ConfirmationDialog *erase_tab_confirm;
ScriptCreateDialog *script_create_dialog;
ScriptEditorDebugger *debugger;
ToolButton *scripts_visible;
String current_theme;
@ -315,8 +307,6 @@ class ScriptEditor : public PanelContainer {
EditorScriptCodeCompletionCache *completion_cache;
void _editor_play();
void _editor_pause();
void _editor_stop();
int edit_pass;
@ -335,7 +325,6 @@ class ScriptEditor : public PanelContainer {
void _set_execution(REF p_script, int p_line);
void _clear_execution(REF p_script);
void _breaked(bool p_breaked, bool p_can_debug);
void _show_debugger(bool p_show);
void _update_window_menu();
void _script_created(Ref<Script> p_script);
@ -457,7 +446,6 @@ public:
VSplitContainer *get_left_list_split() { return list_split; }
ScriptEditorDebugger *get_debugger() { return debugger; }
void set_live_auto_reload_running_scripts(bool p_enabled);
static void register_create_syntax_highlighter_function(CreateSyntaxHighlighterFunc p_func);

View file

@ -32,10 +32,10 @@
#include "core/math/expression.h"
#include "core/os/keyboard.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/script_editor_debugger.h"
void ConnectionInfoDialog::ok_pressed() {
}
@ -870,7 +870,7 @@ void ScriptTextEditor::_breakpoint_item_pressed(int p_idx) {
void ScriptTextEditor::_breakpoint_toggled(int p_row) {
ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(script->get_path(), p_row + 1, code_editor->get_text_edit()->is_line_set_as_breakpoint(p_row));
EditorDebuggerNode::get_singleton()->set_breakpoint(script->get_path(), p_row + 1, code_editor->get_text_edit()->is_line_set_as_breakpoint(p_row));
}
void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_column) {
@ -1294,7 +1294,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
int line = tx->cursor_get_line();
bool dobreak = !tx->is_line_set_as_breakpoint(line);
tx->set_line_as_breakpoint(line, dobreak);
ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(script->get_path(), line + 1, dobreak);
EditorDebuggerNode::get_singleton()->set_breakpoint(script->get_path(), line + 1, dobreak);
} break;
case DEBUG_REMOVE_ALL_BREAKPOINTS: {
@ -1305,7 +1305,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
int line = E->get();
bool dobreak = !tx->is_line_set_as_breakpoint(line);
tx->set_line_as_breakpoint(line, dobreak);
ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(script->get_path(), line + 1, dobreak);
EditorDebuggerNode::get_singleton()->set_breakpoint(script->get_path(), line + 1, dobreak);
}
} break;
case DEBUG_GOTO_NEXT_BREAKPOINT: {

View file

@ -36,12 +36,12 @@
#include "core/print_string.h"
#include "core/project_settings.h"
#include "core/sort_array.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/plugins/animation_player_editor_plugin.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/script_editor_debugger.h"
#include "editor/spatial_editor_gizmos.h"
#include "scene/3d/camera.h"
#include "scene/3d/collision_shape.h"
@ -3421,9 +3421,9 @@ bool SpatialEditorViewport::_create_instance(Node *parent, String &path, const P
editor_data->get_undo_redo().add_undo_method(parent, "remove_child", instanced_scene);
String new_name = parent->validate_child_name(instanced_scene);
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
editor_data->get_undo_redo().add_do_method(sed, "live_debug_instance_node", editor->get_edited_scene()->get_path_to(parent), path, new_name);
editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
editor_data->get_undo_redo().add_do_method(ed, "live_debug_instance_node", editor->get_edited_scene()->get_path_to(parent), path, new_name);
editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
Transform global_transform;
Spatial *parent_spatial = Object::cast_to<Spatial>(parent);
@ -4497,7 +4497,7 @@ void SpatialEditor::_menu_item_toggled(bool pressed, int p_option) {
} break;
case MENU_TOOL_OVERRIDE_CAMERA: {
ScriptEditorDebugger *const debugger = ScriptEditor::get_singleton()->get_debugger();
EditorDebuggerNode *const debugger = EditorDebuggerNode::get_singleton();
if (pressed) {
using Override = ScriptEditorDebugger::CameraOverride;
@ -4554,7 +4554,7 @@ void SpatialEditor::_update_camera_override_viewport(Object *p_viewport) {
if (!current_viewport)
return;
ScriptEditorDebugger *const debugger = ScriptEditor::get_singleton()->get_debugger();
EditorDebuggerNode *const debugger = EditorDebuggerNode::get_singleton();
camera_override_viewport_id = current_viewport->index;
if (debugger->get_camera_override() >= ScriptEditorDebugger::OVERRIDE_3D_1) {
@ -5513,7 +5513,7 @@ void SpatialEditor::_notification(int p_what) {
_init_grid();
} else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
if (!is_visible() && tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->is_pressed()) {
ScriptEditorDebugger *debugger = ScriptEditor::get_singleton()->get_debugger();
EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton();
debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_NONE);
tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_pressed(false);

View file

@ -35,6 +35,7 @@
#include "core/os/keyboard.h"
#include "core/project_settings.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/editor_feature_profile.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
@ -44,7 +45,6 @@
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/plugins/spatial_editor_plugin.h"
#include "editor/script_editor_debugger.h"
#include "scene/main/viewport.h"
#include "scene/resources/packed_scene.h"
@ -229,9 +229,9 @@ void SceneTreeDock::_perform_instance_scenes(const Vector<String> &p_files, Node
editor_data->get_undo_redo().add_undo_method(parent, "remove_child", instanced_scene);
String new_name = parent->validate_child_name(instanced_scene);
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
editor_data->get_undo_redo().add_do_method(sed, "live_debug_instance_node", edited_scene->get_path_to(parent), p_files[i], new_name);
editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(new_name)));
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
editor_data->get_undo_redo().add_do_method(ed, "live_debug_instance_node", edited_scene->get_path_to(parent), p_files[i], new_name);
editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(new_name)));
}
editor_data->get_undo_redo().commit_action();
@ -591,10 +591,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
editor_data->get_undo_redo().add_undo_method(parent, "remove_child", dup);
editor_data->get_undo_redo().add_do_reference(dup);
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
editor_data->get_undo_redo().add_do_method(sed, "live_debug_duplicate_node", edited_scene->get_path_to(node), dup->get_name());
editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(dup->get_name())));
editor_data->get_undo_redo().add_do_method(ed, "live_debug_duplicate_node", edited_scene->get_path_to(node), dup->get_name());
editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(dup->get_name())));
}
editor_data->get_undo_redo().commit_action();
@ -1584,7 +1584,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
if (p_position_in_parent >= 0)
editor_data->get_undo_redo().add_do_method(new_parent, "move_child", node, p_position_in_parent + inc);
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
String old_name = former_names[ni];
String new_name = new_parent->validate_child_name(node);
@ -1609,8 +1609,8 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
path_renames[ni].second = fixed_node_path;
}
editor_data->get_undo_redo().add_do_method(sed, "live_debug_reparent_node", edited_scene->get_path_to(node), edited_scene->get_path_to(new_parent), new_name, p_position_in_parent + inc);
editor_data->get_undo_redo().add_undo_method(sed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)).plus_file(new_name)), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index());
editor_data->get_undo_redo().add_do_method(ed, "live_debug_reparent_node", edited_scene->get_path_to(node), edited_scene->get_path_to(new_parent), new_name, p_position_in_parent + inc);
editor_data->get_undo_redo().add_undo_method(ed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)).plus_file(new_name)), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index());
if (p_keep_global_xform) {
if (Object::cast_to<Node2D>(node))
@ -1849,9 +1849,9 @@ void SceneTreeDock::_delete_confirm() {
editor_data->get_undo_redo().add_undo_method(this, "_set_owners", edited_scene, owners);
editor_data->get_undo_redo().add_undo_reference(n);
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
editor_data->get_undo_redo().add_do_method(sed, "live_debug_remove_and_keep_node", edited_scene->get_path_to(n), n->get_instance_id());
editor_data->get_undo_redo().add_undo_method(sed, "live_debug_restore_node", n->get_instance_id(), edited_scene->get_path_to(n->get_parent()), n->get_index());
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
editor_data->get_undo_redo().add_do_method(ed, "live_debug_remove_and_keep_node", edited_scene->get_path_to(n), n->get_instance_id());
editor_data->get_undo_redo().add_undo_method(ed, "live_debug_restore_node", n->get_instance_id(), edited_scene->get_path_to(n->get_parent()), n->get_index());
}
}
editor_data->get_undo_redo().commit_action();
@ -1950,9 +1950,9 @@ void SceneTreeDock::_do_create(Node *p_parent) {
editor_data->get_undo_redo().add_undo_method(p_parent, "remove_child", child);
String new_name = p_parent->validate_child_name(child);
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
editor_data->get_undo_redo().add_do_method(sed, "live_debug_create_node", edited_scene->get_path_to(p_parent), child->get_class(), new_name);
editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)).plus_file(new_name)));
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
editor_data->get_undo_redo().add_do_method(ed, "live_debug_create_node", edited_scene->get_path_to(p_parent), child->get_class(), new_name);
editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)).plus_file(new_name)));
} else {

View file

@ -32,13 +32,13 @@
#include "core/os/keyboard.h"
#include "core/project_settings.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor_file_system.h"
#include "editor_log.h"
#include "editor_node.h"
#include "editor_scale.h"
#include "editor_settings.h"
#include "scene/gui/margin_container.h"
#include "script_editor_debugger.h"
void EditorSettingsDialog::ok_pressed() {
@ -119,9 +119,8 @@ void EditorSettingsDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
undo_redo->set_method_notify_callback(sed->_method_changeds, sed);
undo_redo->set_property_notify_callback(sed->_property_changeds, sed);
undo_redo->set_method_notify_callback(EditorDebuggerNode::_method_changeds, NULL);
undo_redo->set_property_notify_callback(EditorDebuggerNode::_property_changeds, NULL);
undo_redo->set_commit_notify_callback(_undo_redo_callback, this);
} break;
case NOTIFICATION_ENTER_TREE: {

View file

@ -44,6 +44,7 @@
#include "core/project_settings.h"
#include "core/register_core_types.h"
#include "core/script_debugger_local.h"
#include "core/script_debugger_remote.h"
#include "core/script_language.h"
#include "core/translation.h"
#include "core/version.h"
@ -58,7 +59,6 @@
#include "main/tests/test_main.h"
#include "modules/register_module_types.h"
#include "platform/register_platform_apis.h"
#include "scene/debugger/script_debugger_remote.h"
#include "scene/main/scene_tree.h"
#include "scene/main/viewport.h"
#include "scene/register_scene_types.h"
@ -1657,12 +1657,6 @@ bool Main::start() {
if (!project_manager && !editor) { // game
if (game_path != "" || script != "") {
if (script_debugger && script_debugger->is_remote()) {
ScriptDebuggerRemote *remote_debugger = static_cast<ScriptDebuggerRemote *>(script_debugger);
remote_debugger->set_scene_tree(sml);
}
//autoload
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);

View file

@ -172,7 +172,7 @@ namespace GodotTools
return;
// Notify running game for hot-reload
Internal.ScriptEditorDebuggerReloadScripts();
Internal.EditorDebuggerNodeReloadScripts();
// Hot-reload in the editor
GodotSharpEditor.Instance.GetNode<HotReloadAssemblyWatcher>("HotReloadAssemblyWatcher").RestartTimer();

View file

@ -34,7 +34,7 @@ namespace GodotTools.Internals
public static void ReloadAssemblies(bool softReload) => internal_ReloadAssemblies(softReload);
public static void ScriptEditorDebuggerReloadScripts() => internal_ScriptEditorDebuggerReloadScripts();
public static void EditorDebuggerNodeReloadScripts() => internal_EditorDebuggerNodeReloadScripts();
public static bool ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus = true) =>
internal_ScriptEditorEdit(resource, line, col, grabFocus);
@ -88,7 +88,7 @@ namespace GodotTools.Internals
private static extern void internal_ReloadAssemblies(bool softReload);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_ScriptEditorDebuggerReloadScripts();
private static extern void internal_EditorDebuggerNodeReloadScripts();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool internal_ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus);

View file

@ -36,10 +36,10 @@
#include "core/os/os.h"
#include "core/version.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/script_editor_debugger.h"
#include "main/main.h"
#include "../csharp_script.h"
@ -305,8 +305,8 @@ void godot_icall_Internal_ReloadAssemblies(MonoBoolean p_soft_reload) {
#endif
}
void godot_icall_Internal_ScriptEditorDebuggerReloadScripts() {
ScriptEditor::get_singleton()->get_debugger()->reload_scripts();
void godot_icall_Internal_EditorDebuggerNodeReloadScripts() {
EditorDebuggerNode::get_singleton()->reload_scripts();
}
MonoBoolean godot_icall_Internal_ScriptEditorEdit(MonoObject *p_resource, int32_t p_line, int32_t p_col, MonoBoolean p_grab_focus) {
@ -348,9 +348,9 @@ void godot_icall_Internal_EditorRunStop() {
}
void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() {
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
if (sed) {
sed->reload_scripts();
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
if (ed) {
ed->reload_scripts();
}
}
@ -446,7 +446,7 @@ void register_editor_internal_calls() {
mono_add_internal_call("GodotTools.Internals.Internal::internal_GetEditorApiHash", (void *)godot_icall_Internal_GetEditorApiHash);
mono_add_internal_call("GodotTools.Internals.Internal::internal_IsAssembliesReloadingNeeded", (void *)godot_icall_Internal_IsAssembliesReloadingNeeded);
mono_add_internal_call("GodotTools.Internals.Internal::internal_ReloadAssemblies", (void *)godot_icall_Internal_ReloadAssemblies);
mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorDebuggerReloadScripts", (void *)godot_icall_Internal_ScriptEditorDebuggerReloadScripts);
mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorDebuggerNodeReloadScripts", (void *)godot_icall_Internal_EditorDebuggerNodeReloadScripts);
mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorEdit", (void *)godot_icall_Internal_ScriptEditorEdit);
mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorNodeShowScriptScreen", (void *)godot_icall_Internal_EditorNodeShowScriptScreen);
mono_add_internal_call("GodotTools.Internals.Internal::internal_GetScriptsMetadataOrNothing", (void *)godot_icall_Internal_GetScriptsMetadataOrNothing);

View file

@ -38,7 +38,7 @@
#include "core/reference.h"
#ifdef TOOLS_ENABLED
#include "editor/script_editor_debugger.h"
#include "editor/debugger/script_editor_debugger.h"
#endif
#include "../csharp_script.h"

View file

@ -34,6 +34,7 @@
#include "os_windows.h"
#include "core/io/marshalls.h"
#include "core/script_language.h"
#include "core/version_generated.gen.h"
#if defined(OPENGL_ENABLED)

View file

@ -0,0 +1,867 @@
/*************************************************************************/
/* scene_debugger.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "scene_debugger.h"
#include "core/io/marshalls.h"
#include "core/script_debugger_remote.h"
#include "scene/main/scene_tree.h"
#include "scene/main/viewport.h"
#include "scene/resources/packed_scene.h"
void SceneDebugger::initialize() {
#ifdef DEBUG_ENABLED
LiveEditor::singleton = memnew(LiveEditor);
ScriptDebuggerRemote::scene_tree_parse_func = SceneDebugger::parse_message;
#endif
}
void SceneDebugger::deinitialize() {
#ifdef DEBUG_ENABLED
if (LiveEditor::singleton) {
memdelete(LiveEditor::singleton);
LiveEditor::singleton = NULL;
}
#endif
}
#ifdef DEBUG_ENABLED
Error SceneDebugger::parse_message(const String &p_msg, const Array &p_args) {
SceneTree *scene_tree = SceneTree::get_singleton();
if (!scene_tree)
return ERR_UNCONFIGURED;
LiveEditor *live_editor = LiveEditor::get_singleton();
if (!live_editor)
return ERR_UNCONFIGURED;
if (p_msg == "request_scene_tree") { // Scene tree
live_editor->_send_tree();
} else if (p_msg == "save_node") { // Save node.
ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
_save_node(p_args[0], p_args[1]);
} else if (p_msg == "inspect_object") { // Object Inspect
ERR_FAIL_COND_V(p_args.size() < 1, ERR_INVALID_DATA);
ObjectID id = p_args[0];
_send_object_id(id);
} else if (p_msg == "override_camera_2D:set") { // Camera
ERR_FAIL_COND_V(p_args.size() < 1, ERR_INVALID_DATA);
bool enforce = p_args[0];
scene_tree->get_root()->enable_canvas_transform_override(enforce);
} else if (p_msg == "override_camera_2D:transform") {
ERR_FAIL_COND_V(p_args.size() < 1, ERR_INVALID_DATA);
Transform2D transform = p_args[1];
scene_tree->get_root()->set_canvas_transform_override(transform);
} else if (p_msg == "override_camera_3D:set") {
ERR_FAIL_COND_V(p_args.size() < 1, ERR_INVALID_DATA);
bool enable = p_args[0];
scene_tree->get_root()->enable_camera_override(enable);
} else if (p_msg == "override_camera_3D:transform") {
ERR_FAIL_COND_V(p_args.size() < 5, ERR_INVALID_DATA);
Transform transform = p_args[0];
bool is_perspective = p_args[1];
float size_or_fov = p_args[2];
float near = p_args[3];
float far = p_args[4];
if (is_perspective) {
scene_tree->get_root()->set_camera_override_perspective(size_or_fov, near, far);
} else {
scene_tree->get_root()->set_camera_override_orthogonal(size_or_fov, near, far);
}
scene_tree->get_root()->set_camera_override_transform(transform);
} else if (p_msg == "set_object_property") {
ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
_set_object_property(p_args[0], p_args[1], p_args[2]);
} else if (!p_msg.begins_with("live_")) { // Live edits below.
return ERR_SKIP;
} else if (p_msg == "live_set_root") {
ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
live_editor->_root_func(p_args[0], p_args[1]);
} else if (p_msg == "live_node_path") {
ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
live_editor->_node_path_func(p_args[0], p_args[1]);
} else if (p_msg == "live_res_path") {
ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
live_editor->_res_path_func(p_args[0], p_args[1]);
} else if (p_msg == "live_node_prop_res") {
ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
live_editor->_node_set_res_func(p_args[0], p_args[1], p_args[2]);
} else if (p_msg == "live_node_prop") {
ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
live_editor->_node_set_func(p_args[0], p_args[1], p_args[2]);
} else if (p_msg == "live_res_prop_res") {
ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
live_editor->_res_set_res_func(p_args[0], p_args[1], p_args[2]);
} else if (p_msg == "live_res_prop") {
ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
live_editor->_res_set_func(p_args[0], p_args[1], p_args[2]);
} else if (p_msg == "live_node_call") {
ERR_FAIL_COND_V(p_args.size() < 7, ERR_INVALID_DATA);
live_editor->_node_call_func(p_args[0], p_args[1], p_args[2], p_args[3], p_args[4], p_args[5], p_args[6]);
} else if (p_msg == "live_res_call") {
ERR_FAIL_COND_V(p_args.size() < 7, ERR_INVALID_DATA);
live_editor->_res_call_func(p_args[0], p_args[1], p_args[2], p_args[3], p_args[4], p_args[5], p_args[6]);
} else if (p_msg == "live_create_node") {
ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
live_editor->_create_node_func(p_args[0], p_args[1], p_args[2]);
} else if (p_msg == "live_instance_node") {
ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
live_editor->_instance_node_func(p_args[0], p_args[1], p_args[2]);
} else if (p_msg == "live_remove_node") {
ERR_FAIL_COND_V(p_args.size() < 1, ERR_INVALID_DATA);
live_editor->_remove_node_func(p_args[0]);
} else if (p_msg == "live_remove_and_keep_node") {
ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
live_editor->_remove_and_keep_node_func(p_args[0], p_args[1]);
} else if (p_msg == "live_restore_node") {
ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
live_editor->_restore_node_func(p_args[0], p_args[1], p_args[2]);
} else if (p_msg == "live_duplicate_node") {
ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
live_editor->_duplicate_node_func(p_args[0], p_args[1]);
} else if (p_msg == "live_reparent_node") {
ERR_FAIL_COND_V(p_args.size() < 4, ERR_INVALID_DATA);
live_editor->_reparent_node_func(p_args[0], p_args[1], p_args[2], p_args[3]);
} else {
return ERR_SKIP;
}
return OK;
}
void SceneDebugger::_save_node(ObjectID id, const String &p_path) {
Node *node = Object::cast_to<Node>(ObjectDB::get_instance(id));
ERR_FAIL_COND(!node);
WARN_PRINT("SAVING " + itos(id) + " TO " + p_path);
Ref<PackedScene> ps = memnew(PackedScene);
ps->pack(node);
ResourceSaver::save(p_path, ps);
}
void SceneDebugger::_send_object_id(ObjectID p_id, int p_max_size) {
SceneDebuggerObject obj(p_id);
if (obj.id.is_null())
return;
Array arr;
obj.serialize(arr);
ScriptDebugger::get_singleton()->send_message("inspect_object", arr);
}
void SceneDebugger::_set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value) {
Object *obj = ObjectDB::get_instance(p_id);
if (!obj)
return;
String prop_name = p_property;
if (p_property.begins_with("Members/")) {
Vector<String> ss = p_property.split("/");
prop_name = ss[ss.size() - 1];
}
obj->set(prop_name, p_value);
}
void SceneDebugger::add_to_cache(const String &p_filename, Node *p_node) {
LiveEditor *debugger = LiveEditor::get_singleton();
if (!debugger)
return;
if (ScriptDebugger::get_singleton() && p_filename != String()) {
debugger->live_scene_edit_cache[p_filename].insert(p_node);
}
}
void SceneDebugger::remove_from_cache(const String &p_filename, Node *p_node) {
LiveEditor *debugger = LiveEditor::get_singleton();
if (!debugger)
return;
Map<String, Set<Node *> > &edit_cache = debugger->live_scene_edit_cache;
Map<String, Set<Node *> >::Element *E = edit_cache.find(p_filename);
if (E) {
E->get().erase(p_node);
if (E->get().size() == 0) {
edit_cache.erase(E);
}
}
Map<Node *, Map<ObjectID, Node *> > &remove_list = debugger->live_edit_remove_list;
Map<Node *, Map<ObjectID, Node *> >::Element *F = remove_list.find(p_node);
if (F) {
for (Map<ObjectID, Node *>::Element *G = F->get().front(); G; G = G->next()) {
memdelete(G->get());
}
remove_list.erase(F);
}
}
/// SceneDebuggerObject
SceneDebuggerObject::SceneDebuggerObject(ObjectID p_id) {
id = ObjectID();
Object *obj = ObjectDB::get_instance(p_id);
if (!obj)
return;
id = p_id;
class_name = obj->get_class();
if (ScriptInstance *si = obj->get_script_instance()) {
// Read script instance constants and variables
if (!si->get_script().is_null()) {
Script *s = si->get_script().ptr();
_parse_script_properties(s, si);
}
}
if (Node *node = Object::cast_to<Node>(obj)) {
// Add specialized NodePath info (if inside tree).
if (node->is_inside_tree()) {
PropertyInfo pi(Variant::NODE_PATH, String("Node/path"));
properties.push_back(SceneDebuggerProperty(pi, node->get_path()));
} else { // Can't ask for path if a node is not in tree.
PropertyInfo pi(Variant::STRING, String("Node/path"));
properties.push_back(SceneDebuggerProperty(pi, "[Orphan]"));
}
} else if (Script *s = Object::cast_to<Script>(obj)) {
// Add script constants (no instance).
_parse_script_properties(s, NULL);
}
// Add base object properties.
List<PropertyInfo> pinfo;
obj->get_property_list(&pinfo, true);
for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
if (E->get().usage & (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CATEGORY)) {
properties.push_back(SceneDebuggerProperty(E->get(), obj->get(E->get().name)));
}
}
}
void SceneDebuggerObject::_parse_script_properties(Script *p_script, ScriptInstance *p_instance) {
typedef Map<const Script *, Set<StringName> > ScriptMemberMap;
typedef Map<const Script *, Map<StringName, Variant> > ScriptConstantsMap;
ScriptMemberMap members;
if (p_instance) {
members[p_script] = Set<StringName>();
p_script->get_members(&(members[p_script]));
}
ScriptConstantsMap constants;
constants[p_script] = Map<StringName, Variant>();
p_script->get_constants(&(constants[p_script]));
Ref<Script> base = p_script->get_base_script();
while (base.is_valid()) {
if (p_instance) {
members[base.ptr()] = Set<StringName>();
base->get_members(&(members[base.ptr()]));
}
constants[base.ptr()] = Map<StringName, Variant>();
base->get_constants(&(constants[base.ptr()]));
base = base->get_base_script();
}
// Members
for (ScriptMemberMap::Element *sm = members.front(); sm; sm = sm->next()) {
for (Set<StringName>::Element *E = sm->get().front(); E; E = E->next()) {
Variant m;
if (p_instance->get(E->get(), m)) {
String script_path = sm->key() == p_script ? "" : sm->key()->get_path().get_file() + "/";
PropertyInfo pi(m.get_type(), "Members/" + script_path + E->get());
properties.push_back(SceneDebuggerProperty(pi, m));
}
}
}
// Constants
for (ScriptConstantsMap::Element *sc = constants.front(); sc; sc = sc->next()) {
for (Map<StringName, Variant>::Element *E = sc->get().front(); E; E = E->next()) {
String script_path = sc->key() == p_script ? "" : sc->key()->get_path().get_file() + "/";
if (E->value().get_type() == Variant::OBJECT) {
Variant id = ((Object *)E->value())->get_instance_id();
PropertyInfo pi(id.get_type(), "Constants/" + E->key(), PROPERTY_HINT_OBJECT_ID, "Object");
properties.push_back(SceneDebuggerProperty(pi, id));
} else {
PropertyInfo pi(E->value().get_type(), "Constants/" + script_path + E->key());
properties.push_back(SceneDebuggerProperty(pi, E->value()));
}
}
}
}
void SceneDebuggerObject::serialize(Array &r_arr, int p_max_size) {
Array send_props;
for (int i = 0; i < properties.size(); i++) {
const PropertyInfo &pi = properties[i].first;
Variant &var = properties[i].second;
WeakRef *ref = Object::cast_to<WeakRef>(var);
if (ref) {
var = ref->get_ref();
}
RES res = var;
Array prop;
prop.push_back(pi.name);
prop.push_back(pi.type);
PropertyHint hint = pi.hint;
String hint_string = pi.hint_string;
if (!res.is_null()) {
var = res->get_path();
} else { //only send information that can be sent..
int len = 0; //test how big is this to encode
encode_variant(var, NULL, len);
if (len > p_max_size) { //limit to max size
hint = PROPERTY_HINT_OBJECT_TOO_BIG;
hint_string = "";
var = Variant();
}
}
prop.push_back(hint);
prop.push_back(hint_string);
prop.push_back(pi.usage);
prop.push_back(var);
send_props.push_back(prop);
}
r_arr.push_back(uint64_t(id));
r_arr.push_back(class_name);
r_arr.push_back(send_props);
}
void SceneDebuggerObject::deserialize(const Array &p_arr) {
#define CHECK_TYPE(p_what, p_type) ERR_FAIL_COND(p_what.get_type() != Variant::p_type);
ERR_FAIL_COND(p_arr.size() < 3);
CHECK_TYPE(p_arr[0], INT);
CHECK_TYPE(p_arr[1], STRING);
CHECK_TYPE(p_arr[2], ARRAY);
id = uint64_t(p_arr[0]);
class_name = p_arr[1];
Array props = p_arr[2];
for (int i = 0; i < props.size(); i++) {
CHECK_TYPE(props[i], ARRAY);
Array prop = props[i];
ERR_FAIL_COND(prop.size() != 6);
CHECK_TYPE(prop[0], STRING);
CHECK_TYPE(prop[1], INT);
CHECK_TYPE(prop[2], INT);
CHECK_TYPE(prop[3], STRING);
CHECK_TYPE(prop[4], INT);
PropertyInfo pinfo;
pinfo.name = prop[0];
pinfo.type = Variant::Type(int(prop[1]));
pinfo.hint = PropertyHint(int(prop[2]));
pinfo.hint_string = prop[3];
pinfo.usage = PropertyUsageFlags(int(prop[4]));
Variant var = prop[5];
if (pinfo.type == Variant::OBJECT) {
if (var.is_zero()) {
var = RES();
} else if (var.get_type() == Variant::OBJECT) {
if (((Object *)var)->is_class("EncodedObjectAsID")) {
var = Object::cast_to<EncodedObjectAsID>(var)->get_object_id();
pinfo.type = var.get_type();
pinfo.hint = PROPERTY_HINT_OBJECT_ID;
pinfo.hint_string = "Object";
}
}
}
properties.push_back(SceneDebuggerProperty(pinfo, var));
}
}
/// SceneDebuggerTree
SceneDebuggerTree::SceneDebuggerTree(Node *p_root) {
// Flatten tree into list, depth first, use stack to avoid recursion.
List<Node *> stack;
stack.push_back(p_root);
while (stack.size()) {
Node *n = stack[0];
stack.pop_front();
int count = n->get_child_count();
nodes.push_back(RemoteNode(count, n->get_name(), n->get_class(), n->get_instance_id()));
for (int i = 0; i < count; i++) {
stack.push_front(n->get_child(count - i - 1));
}
}
}
void SceneDebuggerTree::serialize(Array &p_arr) {
for (List<RemoteNode>::Element *E = nodes.front(); E; E = E->next()) {
RemoteNode &n = E->get();
p_arr.push_back(n.child_count);
p_arr.push_back(n.name);
p_arr.push_back(n.type_name);
p_arr.push_back(n.id);
}
}
void SceneDebuggerTree::deserialize(const Array &p_arr) {
int idx = 0;
while (p_arr.size() > idx) {
ERR_FAIL_COND(p_arr.size() < 4);
CHECK_TYPE(p_arr[idx], INT);
CHECK_TYPE(p_arr[idx + 1], STRING);
CHECK_TYPE(p_arr[idx + 2], STRING);
CHECK_TYPE(p_arr[idx + 3], INT);
nodes.push_back(RemoteNode(p_arr[idx], p_arr[idx + 1], p_arr[idx + 2], p_arr[idx + 3]));
idx += 4;
}
}
/// LiveEditor
LiveEditor *LiveEditor::singleton = NULL;
LiveEditor *LiveEditor::get_singleton() {
return singleton;
}
void LiveEditor::_send_tree() {
SceneTree *scene_tree = SceneTree::get_singleton();
if (!scene_tree)
return;
Array arr;
// Encoded as a flat list depth fist.
SceneDebuggerTree tree(scene_tree->root);
tree.serialize(arr);
ScriptDebugger::get_singleton()->send_message("scene_tree", arr);
}
void LiveEditor::_node_path_func(const NodePath &p_path, int p_id) {
live_edit_node_path_cache[p_id] = p_path;
}
void LiveEditor::_res_path_func(const String &p_path, int p_id) {
live_edit_resource_cache[p_id] = p_path;
}
void LiveEditor::_node_set_func(int p_id, const StringName &p_prop, const Variant &p_value) {
SceneTree *scene_tree = SceneTree::get_singleton();
if (!scene_tree)
return;
if (!live_edit_node_path_cache.has(p_id))
return;
NodePath np = live_edit_node_path_cache[p_id];
Node *base = NULL;
if (scene_tree->root->has_node(live_edit_root))
base = scene_tree->root->get_node(live_edit_root);
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
if (!E)
return; //scene not editable
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
Node *n = F->get();
if (base && !base->is_a_parent_of(n))
continue;
if (!n->has_node(np))
continue;
Node *n2 = n->get_node(np);
n2->set(p_prop, p_value);
}
}
void LiveEditor::_node_set_res_func(int p_id, const StringName &p_prop, const String &p_value) {
RES r = ResourceLoader::load(p_value);
if (!r.is_valid())
return;
_node_set_func(p_id, p_prop, r);
}
void LiveEditor::_node_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE) {
SceneTree *scene_tree = SceneTree::get_singleton();
if (!scene_tree)
return;
if (!live_edit_node_path_cache.has(p_id))
return;
NodePath np = live_edit_node_path_cache[p_id];
Node *base = NULL;
if (scene_tree->root->has_node(live_edit_root))
base = scene_tree->root->get_node(live_edit_root);
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
if (!E)
return; //scene not editable
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
Node *n = F->get();
if (base && !base->is_a_parent_of(n))
continue;
if (!n->has_node(np))
continue;
Node *n2 = n->get_node(np);
n2->call(p_method, VARIANT_ARG_PASS);
}
}
void LiveEditor::_res_set_func(int p_id, const StringName &p_prop, const Variant &p_value) {
if (!live_edit_resource_cache.has(p_id))
return;
String resp = live_edit_resource_cache[p_id];
if (!ResourceCache::has(resp))
return;
RES r = ResourceCache::get(resp);
if (!r.is_valid())
return;
r->set(p_prop, p_value);
}
void LiveEditor::_res_set_res_func(int p_id, const StringName &p_prop, const String &p_value) {
RES r = ResourceLoader::load(p_value);
if (!r.is_valid())
return;
_res_set_func(p_id, p_prop, r);
}
void LiveEditor::_res_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE) {
if (!live_edit_resource_cache.has(p_id))
return;
String resp = live_edit_resource_cache[p_id];
if (!ResourceCache::has(resp))
return;
RES r = ResourceCache::get(resp);
if (!r.is_valid())
return;
r->call(p_method, VARIANT_ARG_PASS);
}
void LiveEditor::_root_func(const NodePath &p_scene_path, const String &p_scene_from) {
live_edit_root = p_scene_path;
live_edit_scene = p_scene_from;
}
void LiveEditor::_create_node_func(const NodePath &p_parent, const String &p_type, const String &p_name) {
SceneTree *scene_tree = SceneTree::get_singleton();
if (!scene_tree)
return;
Node *base = NULL;
if (scene_tree->root->has_node(live_edit_root))
base = scene_tree->root->get_node(live_edit_root);
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
if (!E)
return; //scene not editable
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
Node *n = F->get();
if (base && !base->is_a_parent_of(n))
continue;
if (!n->has_node(p_parent))
continue;
Node *n2 = n->get_node(p_parent);
Node *no = Object::cast_to<Node>(ClassDB::instance(p_type));
if (!no) {
continue;
}
no->set_name(p_name);
n2->add_child(no);
}
}
void LiveEditor::_instance_node_func(const NodePath &p_parent, const String &p_path, const String &p_name) {
SceneTree *scene_tree = SceneTree::get_singleton();
if (!scene_tree)
return;
Ref<PackedScene> ps = ResourceLoader::load(p_path);
if (!ps.is_valid())
return;
Node *base = NULL;
if (scene_tree->root->has_node(live_edit_root))
base = scene_tree->root->get_node(live_edit_root);
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
if (!E)
return; //scene not editable
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
Node *n = F->get();
if (base && !base->is_a_parent_of(n))
continue;
if (!n->has_node(p_parent))
continue;
Node *n2 = n->get_node(p_parent);
Node *no = ps->instance();
if (!no) {
continue;
}
no->set_name(p_name);
n2->add_child(no);
}
}
void LiveEditor::_remove_node_func(const NodePath &p_at) {
SceneTree *scene_tree = SceneTree::get_singleton();
if (!scene_tree)
return;
Node *base = NULL;
if (scene_tree->root->has_node(live_edit_root))
base = scene_tree->root->get_node(live_edit_root);
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
if (!E)
return; //scene not editable
for (Set<Node *>::Element *F = E->get().front(); F;) {
Set<Node *>::Element *N = F->next();
Node *n = F->get();
if (base && !base->is_a_parent_of(n))
continue;
if (!n->has_node(p_at))
continue;
Node *n2 = n->get_node(p_at);
memdelete(n2);
F = N;
}
}
void LiveEditor::_remove_and_keep_node_func(const NodePath &p_at, ObjectID p_keep_id) {
SceneTree *scene_tree = SceneTree::get_singleton();
if (!scene_tree)
return;
Node *base = NULL;
if (scene_tree->root->has_node(live_edit_root))
base = scene_tree->root->get_node(live_edit_root);
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
if (!E)
return; //scene not editable
for (Set<Node *>::Element *F = E->get().front(); F;) {
Set<Node *>::Element *N = F->next();
Node *n = F->get();
if (base && !base->is_a_parent_of(n))
continue;
if (!n->has_node(p_at))
continue;
Node *n2 = n->get_node(p_at);
n2->get_parent()->remove_child(n2);
live_edit_remove_list[n][p_keep_id] = n2;
F = N;
}
}
void LiveEditor::_restore_node_func(ObjectID p_id, const NodePath &p_at, int p_at_pos) {
SceneTree *scene_tree = SceneTree::get_singleton();
if (!scene_tree)
return;
Node *base = NULL;
if (scene_tree->root->has_node(live_edit_root))
base = scene_tree->root->get_node(live_edit_root);
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
if (!E)
return; //scene not editable
for (Set<Node *>::Element *F = E->get().front(); F;) {
Set<Node *>::Element *N = F->next();
Node *n = F->get();
if (base && !base->is_a_parent_of(n))
continue;
if (!n->has_node(p_at))
continue;
Node *n2 = n->get_node(p_at);
Map<Node *, Map<ObjectID, Node *> >::Element *EN = live_edit_remove_list.find(n);
if (!EN)
continue;
Map<ObjectID, Node *>::Element *FN = EN->get().find(p_id);
if (!FN)
continue;
n2->add_child(FN->get());
EN->get().erase(FN);
if (EN->get().size() == 0) {
live_edit_remove_list.erase(EN);
}
F = N;
}
}
void LiveEditor::_duplicate_node_func(const NodePath &p_at, const String &p_new_name) {
SceneTree *scene_tree = SceneTree::get_singleton();
if (!scene_tree)
return;
Node *base = NULL;
if (scene_tree->root->has_node(live_edit_root))
base = scene_tree->root->get_node(live_edit_root);
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
if (!E)
return; //scene not editable
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
Node *n = F->get();
if (base && !base->is_a_parent_of(n))
continue;
if (!n->has_node(p_at))
continue;
Node *n2 = n->get_node(p_at);
Node *dup = n2->duplicate(Node::DUPLICATE_SIGNALS | Node::DUPLICATE_GROUPS | Node::DUPLICATE_SCRIPTS);
if (!dup)
continue;
dup->set_name(p_new_name);
n2->get_parent()->add_child(dup);
}
}
void LiveEditor::_reparent_node_func(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos) {
SceneTree *scene_tree = SceneTree::get_singleton();
if (!scene_tree)
return;
Node *base = NULL;
if (scene_tree->root->has_node(live_edit_root))
base = scene_tree->root->get_node(live_edit_root);
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
if (!E)
return; //scene not editable
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
Node *n = F->get();
if (base && !base->is_a_parent_of(n))
continue;
if (!n->has_node(p_at))
continue;
Node *nfrom = n->get_node(p_at);
if (!n->has_node(p_new_place))
continue;
Node *nto = n->get_node(p_new_place);
nfrom->get_parent()->remove_child(nfrom);
nfrom->set_name(p_new_name);
nto->add_child(nfrom);
if (p_at_pos >= 0)
nto->move_child(nfrom, p_at_pos);
}
}
#endif

View file

@ -0,0 +1,151 @@
/*************************************************************************/
/* scene_debugger.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef SCENE_DEBUGGER_H
#define SCENE_DEBUGGER_H
#include "core/array.h"
#include "core/object.h"
#include "core/pair.h"
#include "core/script_language.h"
#include "core/ustring.h"
class SceneDebugger {
public:
static void initialize();
static void deinitialize();
#ifdef DEBUG_ENABLED
private:
static void _save_node(ObjectID id, const String &p_path);
static void _set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value);
static void _send_object_id(ObjectID p_id, int p_max_size = 1 << 20);
public:
static Error parse_message(const String &p_msg, const Array &p_args);
static void add_to_cache(const String &p_filename, Node *p_node);
static void remove_from_cache(const String &p_filename, Node *p_node);
#endif
};
#ifdef DEBUG_ENABLED
class SceneDebuggerObject {
private:
void _parse_script_properties(Script *p_script, ScriptInstance *p_instance);
public:
typedef Pair<PropertyInfo, Variant> SceneDebuggerProperty;
ObjectID id;
String class_name;
List<SceneDebuggerProperty> properties;
SceneDebuggerObject(ObjectID p_id);
SceneDebuggerObject() {}
void serialize(Array &r_arr, int p_max_size = 1 << 20);
void deserialize(const Array &p_arr);
};
class SceneDebuggerTree {
public:
struct RemoteNode {
int child_count;
String name;
String type_name;
ObjectID id;
RemoteNode(int p_child, const String &p_name, const String &p_type, ObjectID p_id) {
child_count = p_child;
name = p_name;
type_name = p_type;
id = p_id;
}
RemoteNode() {}
};
List<RemoteNode> nodes;
void serialize(Array &r_arr);
void deserialize(const Array &p_arr);
SceneDebuggerTree(Node *p_root);
SceneDebuggerTree(){};
};
class LiveEditor {
private:
friend class SceneDebugger;
Map<int, NodePath> live_edit_node_path_cache;
Map<int, String> live_edit_resource_cache;
NodePath live_edit_root;
String live_edit_scene;
Map<String, Set<Node *> > live_scene_edit_cache;
Map<Node *, Map<ObjectID, Node *> > live_edit_remove_list;
void _send_tree();
void _node_path_func(const NodePath &p_path, int p_id);
void _res_path_func(const String &p_path, int p_id);
void _node_set_func(int p_id, const StringName &p_prop, const Variant &p_value);
void _node_set_res_func(int p_id, const StringName &p_prop, const String &p_value);
void _node_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE);
void _res_set_func(int p_id, const StringName &p_prop, const Variant &p_value);
void _res_set_res_func(int p_id, const StringName &p_prop, const String &p_value);
void _res_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE);
void _root_func(const NodePath &p_scene_path, const String &p_scene_from);
void _create_node_func(const NodePath &p_parent, const String &p_type, const String &p_name);
void _instance_node_func(const NodePath &p_parent, const String &p_path, const String &p_name);
void _remove_node_func(const NodePath &p_at);
void _remove_and_keep_node_func(const NodePath &p_at, ObjectID p_keep_id);
void _restore_node_func(ObjectID p_id, const NodePath &p_at, int p_at_pos);
void _duplicate_node_func(const NodePath &p_at, const String &p_new_name);
void _reparent_node_func(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos);
LiveEditor() {
singleton = this;
live_edit_root = NodePath("/root");
};
static LiveEditor *singleton;
public:
static LiveEditor *get_singleton();
};
#endif
#endif

File diff suppressed because it is too large Load diff

View file

@ -37,6 +37,7 @@
#include "core/message_queue.h"
#include "core/print_string.h"
#include "instance_placeholder.h"
#include "scene/debugger/scene_debugger.h"
#include "scene/resources/packed_scene.h"
#include "scene/scene_string_names.h"
#include "viewport.h"
@ -244,11 +245,7 @@ void Node::_propagate_enter_tree() {
data.blocked--;
#ifdef DEBUG_ENABLED
if (ScriptDebugger::get_singleton() && data.filename != String()) {
//used for live edit
data.tree->live_scene_edit_cache[data.filename].insert(this);
}
SceneDebugger::add_to_cache(data.filename, this);
#endif
// enter groups
}
@ -268,26 +265,7 @@ void Node::_propagate_exit_tree() {
//block while removing children
#ifdef DEBUG_ENABLED
if (ScriptDebugger::get_singleton() && data.filename != String()) {
//used for live edit
Map<String, Set<Node *> >::Element *E = data.tree->live_scene_edit_cache.find(data.filename);
if (E) {
E->get().erase(this);
if (E->get().size() == 0) {
data.tree->live_scene_edit_cache.erase(E);
}
}
Map<Node *, Map<ObjectID, Node *> >::Element *F = data.tree->live_edit_remove_list.find(this);
if (F) {
for (Map<ObjectID, Node *>::Element *G = F->get().front(); G; G = G->next()) {
memdelete(G->get());
}
data.tree->live_edit_remove_list.erase(F);
}
}
SceneDebugger::remove_from_cache(data.filename, this);
#endif
data.blocked++;

View file

@ -38,9 +38,10 @@
#include "core/os/os.h"
#include "core/print_string.h"
#include "core/project_settings.h"
#include "core/script_debugger_remote.h"
#include "main/input_default.h"
#include "node.h"
#include "scene/debugger/script_debugger_remote.h"
#include "scene/debugger/scene_debugger.h"
#include "scene/resources/dynamic_font.h"
#include "scene/resources/material.h"
#include "scene/resources/mesh.h"
@ -1329,380 +1330,6 @@ void SceneTree::add_current_scene(Node *p_current) {
root->add_child(p_current);
}
#ifdef DEBUG_ENABLED
static void _fill_array(Node *p_node, Array &array, int p_level) {
array.push_back(p_node->get_child_count());
array.push_back(p_node->get_name());
array.push_back(p_node->get_class());
array.push_back(p_node->get_instance_id());
for (int i = 0; i < p_node->get_child_count(); i++) {
_fill_array(p_node->get_child(i), array, p_level + 1);
}
}
void SceneTree::_debugger_request_tree() {
Array arr;
_fill_array(root, arr, 0);
ScriptDebugger::get_singleton()->send_message("scene_tree", arr);
}
void SceneTree::_live_edit_node_path_func(const NodePath &p_path, int p_id) {
live_edit_node_path_cache[p_id] = p_path;
}
void SceneTree::_live_edit_res_path_func(const String &p_path, int p_id) {
live_edit_resource_cache[p_id] = p_path;
}
void SceneTree::_live_edit_node_set_func(int p_id, const StringName &p_prop, const Variant &p_value) {
if (!live_edit_node_path_cache.has(p_id))
return;
NodePath np = live_edit_node_path_cache[p_id];
Node *base = NULL;
if (root->has_node(live_edit_root))
base = root->get_node(live_edit_root);
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
if (!E)
return; //scene not editable
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
Node *n = F->get();
if (base && !base->is_a_parent_of(n))
continue;
if (!n->has_node(np))
continue;
Node *n2 = n->get_node(np);
n2->set(p_prop, p_value);
}
}
void SceneTree::_live_edit_node_set_res_func(int p_id, const StringName &p_prop, const String &p_value) {
RES r = ResourceLoader::load(p_value);
if (!r.is_valid())
return;
_live_edit_node_set_func(p_id, p_prop, r);
}
void SceneTree::_live_edit_node_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE) {
if (!live_edit_node_path_cache.has(p_id))
return;
NodePath np = live_edit_node_path_cache[p_id];
Node *base = NULL;
if (root->has_node(live_edit_root))
base = root->get_node(live_edit_root);
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
if (!E)
return; //scene not editable
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
Node *n = F->get();
if (base && !base->is_a_parent_of(n))
continue;
if (!n->has_node(np))
continue;
Node *n2 = n->get_node(np);
n2->call(p_method, VARIANT_ARG_PASS);
}
}
void SceneTree::_live_edit_res_set_func(int p_id, const StringName &p_prop, const Variant &p_value) {
if (!live_edit_resource_cache.has(p_id))
return;
String resp = live_edit_resource_cache[p_id];
if (!ResourceCache::has(resp))
return;
RES r = ResourceCache::get(resp);
if (!r.is_valid())
return;
r->set(p_prop, p_value);
}
void SceneTree::_live_edit_res_set_res_func(int p_id, const StringName &p_prop, const String &p_value) {
RES r = ResourceLoader::load(p_value);
if (!r.is_valid())
return;
_live_edit_res_set_func(p_id, p_prop, r);
}
void SceneTree::_live_edit_res_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE) {
if (!live_edit_resource_cache.has(p_id))
return;
String resp = live_edit_resource_cache[p_id];
if (!ResourceCache::has(resp))
return;
RES r = ResourceCache::get(resp);
if (!r.is_valid())
return;
r->call(p_method, VARIANT_ARG_PASS);
}
void SceneTree::_live_edit_root_func(const NodePath &p_scene_path, const String &p_scene_from) {
live_edit_root = p_scene_path;
live_edit_scene = p_scene_from;
}
void SceneTree::_live_edit_create_node_func(const NodePath &p_parent, const String &p_type, const String &p_name) {
Node *base = NULL;
if (root->has_node(live_edit_root))
base = root->get_node(live_edit_root);
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
if (!E)
return; //scene not editable
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
Node *n = F->get();
if (base && !base->is_a_parent_of(n))
continue;
if (!n->has_node(p_parent))
continue;
Node *n2 = n->get_node(p_parent);
Node *no = Object::cast_to<Node>(ClassDB::instance(p_type));
if (!no) {
continue;
}
no->set_name(p_name);
n2->add_child(no);
}
}
void SceneTree::_live_edit_instance_node_func(const NodePath &p_parent, const String &p_path, const String &p_name) {
Ref<PackedScene> ps = ResourceLoader::load(p_path);
if (!ps.is_valid())
return;
Node *base = NULL;
if (root->has_node(live_edit_root))
base = root->get_node(live_edit_root);
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
if (!E)
return; //scene not editable
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
Node *n = F->get();
if (base && !base->is_a_parent_of(n))
continue;
if (!n->has_node(p_parent))
continue;
Node *n2 = n->get_node(p_parent);
Node *no = ps->instance();
if (!no) {
continue;
}
no->set_name(p_name);
n2->add_child(no);
}
}
void SceneTree::_live_edit_remove_node_func(const NodePath &p_at) {
Node *base = NULL;
if (root->has_node(live_edit_root))
base = root->get_node(live_edit_root);
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
if (!E)
return; //scene not editable
for (Set<Node *>::Element *F = E->get().front(); F;) {
Set<Node *>::Element *N = F->next();
Node *n = F->get();
if (base && !base->is_a_parent_of(n))
continue;
if (!n->has_node(p_at))
continue;
Node *n2 = n->get_node(p_at);
memdelete(n2);
F = N;
}
}
void SceneTree::_live_edit_remove_and_keep_node_func(const NodePath &p_at, ObjectID p_keep_id) {
Node *base = NULL;
if (root->has_node(live_edit_root))
base = root->get_node(live_edit_root);
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
if (!E)
return; //scene not editable
for (Set<Node *>::Element *F = E->get().front(); F;) {
Set<Node *>::Element *N = F->next();
Node *n = F->get();
if (base && !base->is_a_parent_of(n))
continue;
if (!n->has_node(p_at))
continue;
Node *n2 = n->get_node(p_at);
n2->get_parent()->remove_child(n2);
live_edit_remove_list[n][p_keep_id] = n2;
F = N;
}
}
void SceneTree::_live_edit_restore_node_func(ObjectID p_id, const NodePath &p_at, int p_at_pos) {
Node *base = NULL;
if (root->has_node(live_edit_root))
base = root->get_node(live_edit_root);
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
if (!E)
return; //scene not editable
for (Set<Node *>::Element *F = E->get().front(); F;) {
Set<Node *>::Element *N = F->next();
Node *n = F->get();
if (base && !base->is_a_parent_of(n))
continue;
if (!n->has_node(p_at))
continue;
Node *n2 = n->get_node(p_at);
Map<Node *, Map<ObjectID, Node *> >::Element *EN = live_edit_remove_list.find(n);
if (!EN)
continue;
Map<ObjectID, Node *>::Element *FN = EN->get().find(p_id);
if (!FN)
continue;
n2->add_child(FN->get());
EN->get().erase(FN);
if (EN->get().size() == 0) {
live_edit_remove_list.erase(EN);
}
F = N;
}
}
void SceneTree::_live_edit_duplicate_node_func(const NodePath &p_at, const String &p_new_name) {
Node *base = NULL;
if (root->has_node(live_edit_root))
base = root->get_node(live_edit_root);
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
if (!E)
return; //scene not editable
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
Node *n = F->get();
if (base && !base->is_a_parent_of(n))
continue;
if (!n->has_node(p_at))
continue;
Node *n2 = n->get_node(p_at);
Node *dup = n2->duplicate(Node::DUPLICATE_SIGNALS | Node::DUPLICATE_GROUPS | Node::DUPLICATE_SCRIPTS);
if (!dup)
continue;
dup->set_name(p_new_name);
n2->get_parent()->add_child(dup);
}
}
void SceneTree::_live_edit_reparent_node_func(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos) {
Node *base = NULL;
if (root->has_node(live_edit_root))
base = root->get_node(live_edit_root);
Map<String, Set<Node *> >::Element *E = live_scene_edit_cache.find(live_edit_scene);
if (!E)
return; //scene not editable
for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
Node *n = F->get();
if (base && !base->is_a_parent_of(n))
continue;
if (!n->has_node(p_at))
continue;
Node *nfrom = n->get_node(p_at);
if (!n->has_node(p_new_place))
continue;
Node *nto = n->get_node(p_new_place);
nfrom->get_parent()->remove_child(nfrom);
nfrom->set_name(p_new_name);
nto->add_child(nfrom);
if (p_at_pos >= 0)
nto->move_child(nfrom, p_at_pos);
}
}
#endif
void SceneTree::drop_files(const Vector<String> &p_files, int p_from_screen) {
emit_signal("files_dropped", p_files, p_from_screen);
@ -2116,11 +1743,6 @@ SceneTree::SceneTree() {
_update_root_rect();
if (ScriptDebugger::get_singleton()) {
if (ScriptDebugger::get_singleton()->is_remote()) {
ScriptDebuggerRemote *remote_debugger = static_cast<ScriptDebuggerRemote *>(ScriptDebugger::get_singleton());
remote_debugger->set_scene_tree(this);
}
ScriptDebugger::get_singleton()->set_multiplayer(multiplayer);
}
@ -2129,12 +1751,6 @@ SceneTree::SceneTree() {
#ifdef TOOLS_ENABLED
edited_scene_root = NULL;
#endif
#ifdef DEBUG_ENABLED
live_edit_root = NodePath("/root");
#endif
}
SceneTree::~SceneTree() {

View file

@ -44,6 +44,7 @@ class Node;
class Viewport;
class Material;
class Mesh;
class SceneDebugger;
class SceneTreeTimer : public Reference {
GDCLASS(SceneTreeTimer, Reference);
@ -219,39 +220,8 @@ private:
SelfList<Node>::List xform_change_list;
friend class ScriptDebuggerRemote;
#ifdef DEBUG_ENABLED
Map<int, NodePath> live_edit_node_path_cache;
Map<int, String> live_edit_resource_cache;
NodePath live_edit_root;
String live_edit_scene;
Map<String, Set<Node *> > live_scene_edit_cache;
Map<Node *, Map<ObjectID, Node *> > live_edit_remove_list;
void _debugger_request_tree();
void _live_edit_node_path_func(const NodePath &p_path, int p_id);
void _live_edit_res_path_func(const String &p_path, int p_id);
void _live_edit_node_set_func(int p_id, const StringName &p_prop, const Variant &p_value);
void _live_edit_node_set_res_func(int p_id, const StringName &p_prop, const String &p_value);
void _live_edit_node_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE);
void _live_edit_res_set_func(int p_id, const StringName &p_prop, const Variant &p_value);
void _live_edit_res_set_res_func(int p_id, const StringName &p_prop, const String &p_value);
void _live_edit_res_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE);
void _live_edit_root_func(const NodePath &p_scene_path, const String &p_scene_from);
void _live_edit_create_node_func(const NodePath &p_parent, const String &p_type, const String &p_name);
void _live_edit_instance_node_func(const NodePath &p_parent, const String &p_path, const String &p_name);
void _live_edit_remove_node_func(const NodePath &p_at);
void _live_edit_remove_and_keep_node_func(const NodePath &p_at, ObjectID p_keep_id);
void _live_edit_restore_node_func(ObjectID p_id, const NodePath &p_at, int p_at_pos);
void _live_edit_duplicate_node_func(const NodePath &p_at, const String &p_new_name);
void _live_edit_reparent_node_func(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos);
#ifdef DEBUG_ENABLED // No live editor in release build.
friend class LiveEditor;
#endif
enum {

View file

@ -76,6 +76,7 @@
#include "scene/animation/root_motion_view.h"
#include "scene/animation/tween.h"
#include "scene/audio/audio_stream_player.h"
#include "scene/debugger/scene_debugger.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/center_container.h"
@ -777,10 +778,12 @@ void register_scene_types() {
ERR_PRINT("Error loading custom theme '" + theme_path + "'");
}
}
SceneDebugger::initialize();
}
void unregister_scene_types() {
SceneDebugger::deinitialize();
clear_default_theme();
ResourceLoader::remove_resource_format_loader(resource_loader_dynamic_font);

View file

@ -56,6 +56,7 @@
#include "audio_server.h"
#include "camera/camera_feed.h"
#include "camera_server.h"
#include "core/script_debugger_remote.h"
#include "navigation_2d_server.h"
#include "navigation_server.h"
#include "physics/physics_server_sw.h"
@ -63,18 +64,17 @@
#include "physics_2d/physics_2d_server_wrap_mt.h"
#include "physics_2d_server.h"
#include "physics_server.h"
#include "scene/debugger/script_debugger_remote.h"
#include "visual/shader_types.h"
#include "visual_server.h"
static void _debugger_get_resource_usage(List<ScriptDebuggerRemote::ResourceUsage> *r_usage) {
static void _debugger_get_resource_usage(ScriptDebuggerRemote::ResourceUsage *r_usage) {
List<VS::TextureInfo> tinfo;
VS::get_singleton()->texture_debug_usage(&tinfo);
for (List<VS::TextureInfo>::Element *E = tinfo.front(); E; E = E->next()) {
ScriptDebuggerRemote::ResourceUsage usage;
ScriptDebuggerRemote::ResourceInfo usage;
usage.path = E->get().path;
usage.vram = E->get().bytes;
usage.id = E->get().texture;
@ -84,7 +84,7 @@ static void _debugger_get_resource_usage(List<ScriptDebuggerRemote::ResourceUsag
} else {
usage.format = itos(E->get().width) + "x" + itos(E->get().height) + "x" + itos(E->get().depth) + " " + Image::get_format_name(E->get().format);
}
r_usage->push_back(usage);
r_usage->infos.push_back(usage);
}
}