diff options
| -rw-r--r-- | core/script_debugger_remote.cpp | 342 | ||||
| -rw-r--r-- | core/script_debugger_remote.h | 4 | ||||
| -rw-r--r-- | core/script_language.h | 5 | ||||
| -rw-r--r-- | editor/dictionary_property_edit.cpp | 187 | ||||
| -rw-r--r-- | editor/dictionary_property_edit.h | 62 | ||||
| -rw-r--r-- | editor/property_editor.cpp | 82 | ||||
| -rw-r--r-- | editor/property_editor.h | 2 | ||||
| -rw-r--r-- | editor/script_editor_debugger.cpp | 386 | ||||
| -rw-r--r-- | editor/script_editor_debugger.h | 8 | ||||
| -rw-r--r-- | modules/gdscript/gd_editor.cpp | 43 | ||||
| -rw-r--r-- | modules/gdscript/gd_script.h | 9 |
11 files changed, 923 insertions, 207 deletions
diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp index 190a2321e..801cb4254 100644 --- a/core/script_debugger_remote.cpp +++ b/core/script_debugger_remote.cpp @@ -28,10 +28,12 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "script_debugger_remote.h" +#include "core/io/marshalls.h" #include "globals.h" #include "io/ip.h" #include "os/input.h" #include "os/os.h" + void ScriptDebuggerRemote::_send_video_memory() { List<ResourceUsage> usage; @@ -183,6 +185,8 @@ void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue) ERR_CONTINUE(cmd.size() != 1); int lv = cmd[0]; + ScriptInstance *self_instance = p_script->debug_get_stack_level_instance(lv); + List<String> members; List<Variant> member_vals; @@ -197,47 +201,74 @@ void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue) ERR_CONTINUE(locals.size() != local_vals.size()); + List<String> globals; + List<Variant> global_vals; + + p_script->debug_get_globals(&globals, &global_vals); + + ERR_CONTINUE(globals.size() != global_vals.size()); + packet_peer_stream->put_var("stack_frame_vars"); - packet_peer_stream->put_var(2 + locals.size() * 2 + members.size() * 2); + packet_peer_stream->put_var(locals.size() + members.size() + globals.size()); + + { //locals + List<String>::Element *E = locals.front(); + List<Variant>::Element *F = local_vals.front(); + + while (E) { + + PropertyInfo pi(var.get_type(), String("locals/") + E->get()); + packet_peer_stream->put_var(_serialize(F->get(), pi)); + + E = E->next(); + F = F->next(); + } + } { //members - packet_peer_stream->put_var(members.size()); + + if (self_instance) { // self + + members.push_front("self"); + member_vals.push_front(self_instance->get_owner()); + } List<String>::Element *E = members.front(); List<Variant>::Element *F = member_vals.front(); while (E) { - if (F->get().get_type() == Variant::OBJECT) { - packet_peer_stream->put_var("*" + E->get()); - String pretty_print = F->get().operator String(); - packet_peer_stream->put_var(pretty_print.ascii().get_data()); - } else { - packet_peer_stream->put_var(E->get()); - packet_peer_stream->put_var(F->get()); - } + PropertyInfo pi(var.get_type(), String("members/") + E->get()); + packet_peer_stream->put_var(_serialize(F->get(), pi)); E = E->next(); F = F->next(); } } - { //locals - packet_peer_stream->put_var(locals.size()); + if (self_instance) { // constants - List<String>::Element *E = locals.front(); - List<Variant>::Element *F = local_vals.front(); + Ref<Script> script = self_instance->get_script(); + if (!script.is_null()) { - while (E) { + const Map<StringName, Variant> &constants = script->get_constants(); + + for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) { - if (F->get().get_type() == Variant::OBJECT) { - packet_peer_stream->put_var("*" + E->get()); - String pretty_print = F->get().operator String(); - packet_peer_stream->put_var(pretty_print.ascii().get_data()); - } else { - packet_peer_stream->put_var(E->get()); - packet_peer_stream->put_var(F->get()); + PropertyInfo pi(var.get_type(), String("constants/") + E->key()); + packet_peer_stream->put_var(_serialize(E->value(), pi)); } + } + } + + { //globals + List<String>::Element *E = globals.front(); + List<Variant>::Element *F = global_vals.front(); + + while (E) { + + PropertyInfo pi(var.get_type(), String("globals/") + E->get()); + packet_peer_stream->put_var(_serialize(F->get(), pi)); E = E->next(); F = F->next(); @@ -531,69 +562,81 @@ void ScriptDebuggerRemote::_send_object_id(ObjectID p_id) { if (!obj) return; + Array props; + const uint32_t SEND_PROPERTIES = 0xFFFFFFFF; // All kind of properties are allowed List<PropertyInfo> pinfo; + Set<StringName> ignored_properties; obj->get_property_list(&pinfo, true); + bool query_props = true; - int props_to_send = 0; - for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { + if (ScriptInstance *si = obj->get_script_instance()) { + if (!si->get_script().is_null()) { - if (E->get().usage & (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CATEGORY)) { - props_to_send++; - } - } + const Map<StringName, Variant> &constants = si->get_script()->get_constants(); + for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) { - packet_peer_stream->put_var("message:inspect_object"); - packet_peer_stream->put_var(props_to_send * 5 + 4); - packet_peer_stream->put_var(p_id); - packet_peer_stream->put_var(obj->get_type()); - if (obj->is_type("Resource") || obj->is_type("Node")) - packet_peer_stream->put_var(obj->call("get_path")); - else - packet_peer_stream->put_var(""); + PropertyInfo pi(E->value().get_type(), String("constants/") + E->key()); + props.push_back(_serialize(E->value(), pi)); + } - packet_peer_stream->put_var(props_to_send); + const Set<StringName> &members = si->get_script()->get_members(); + for (Set<StringName>::Element *E = members.front(); E; E = E->next()) { - for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { + Variant m; + if (si->get(E->get(), m)) { - if (E->get().usage & (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CATEGORY)) { + PropertyInfo pi(m.get_type(), String("members/") + E->get()); + props.push_back(_serialize(m, pi)); - if (E->get().usage & PROPERTY_USAGE_CATEGORY) { - packet_peer_stream->put_var("*" + E->get().name); - } else { - packet_peer_stream->put_var(E->get().name); + ignored_properties.insert(E->get()); + } } - Variant var = obj->get(E->get().name); + PropertyInfo pi(Variant::OBJECT, String("Resource/script")); + props.push_back(_serialize(si->get_script(), pi)); + } + } else if (Resource *res = obj->cast_to<Resource>()) { - if (E->get().type == Variant::OBJECT || var.get_type() == Variant::OBJECT) { + if (res->cast_to<Script>()) { - ObjectID id2; - Object *obj = var; - if (obj) { - id2 = obj->get_instance_ID(); - } else { - id2 = 0; - } + const Map<StringName, Variant> &constants = res->cast_to<Script>()->get_constants(); + for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) { - packet_peer_stream->put_var(Variant::INT); //hint string - packet_peer_stream->put_var(PROPERTY_HINT_OBJECT_ID); //hint - packet_peer_stream->put_var(E->get().hint_string); //hint string - packet_peer_stream->put_var(id2); //value - } else { - packet_peer_stream->put_var(E->get().type); - packet_peer_stream->put_var(E->get().hint); - packet_peer_stream->put_var(E->get().hint_string); - //only send information that can be sent.. - if (var.get_type() == Variant::IMAGE) { - var = Image(); - } - if (var.get_type() >= Variant::DICTIONARY) { - var = Array(); //send none for now, may be to big - } - packet_peer_stream->put_var(var); + PropertyInfo pi(E->value().get_type(), String("constants/") + E->key()); + props.push_back(_serialize(E->value(), pi)); + } + } + + PropertyInfo pi(Variant::OBJECT, res->get_type_name(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CATEGORY); + props.push_front(_serialize(res, pi)); + + PropertyInfo pathpi(Variant::STRING, "Resource/Resource"); + pathpi.hint_string = "REMOTE:RES"; + props.push_back(_serialize(res->get_path(), pathpi)); + + query_props = false; + } else if (obj->is_type("Node")) { + + String path = obj->call("get_path"); + PropertyInfo pi(Variant::OBJECT, String("Node/path")); + props.push_back(_serialize(path, pi)); + } + + if (query_props) { + for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { + + if (E->get().usage & SEND_PROPERTIES && !ignored_properties.has(E->get().name)) { + + Variant var = obj->get(E->get().name); + props.push_back(_serialize(var, E->get())); } } } + + packet_peer_stream->put_var("message:inspect_object"); + packet_peer_stream->put_var(2); + packet_peer_stream->put_var(p_id); + packet_peer_stream->put_var(props); } void ScriptDebuggerRemote::_set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value) { @@ -602,7 +645,164 @@ void ScriptDebuggerRemote::_set_object_property(ObjectID p_id, const String &p_p if (!obj) return; - obj->set(p_property, p_value); + String name = p_property; + const String member_prefix = String("members/"); + if (name.begins_with(member_prefix) && p_property.length() > member_prefix.length()) { + name = p_property.substr(member_prefix.length(), p_property.length()); + } + + obj->set(name, p_value); +} + +int __put_value_to_buff_at_pos(const Variant &value, DVector<uint8_t> &buff, int pos) { + + int size_required = 0; + + DVector<uint8_t> valuebuf; + if (value.get_type() == Variant::RAW_ARRAY) + valuebuf = value; + + String sub_contetn; + + switch (value.get_type()) { + case Variant::NIL: + case Variant::INT: + case Variant::BOOL: + size_required = sizeof(uint32_t); + break; + case Variant::REAL: + size_required = sizeof(uint32_t); + break; + case Variant::STRING: { + sub_contetn = value; + const int MAX_STR_LEN = 80; // More than 80 will not be send to the debugger + if (sub_contetn.length() > MAX_STR_LEN) + sub_contetn = sub_contetn.substr(0, MAX_STR_LEN) + "..."; + encode_variant(sub_contetn, NULL, size_required); + } break; + default: + encode_variant(value, NULL, size_required); + break; + } + if (buff.size() < pos + size_required) + buff.resize((pos + size_required) * 2); + + switch (value.get_type()) { + case Variant::INT: + case Variant::BOOL: + encode_uint32(value, &buff.write()[pos]); + break; + case Variant::REAL: + encode_float(value, &buff.write()[pos]); + break; + case Variant::STRING: + encode_variant(sub_contetn, &buff.write()[pos], size_required); + break; + default: + encode_variant(value, &buff.write()[pos], size_required); + break; + } + + return size_required; +} + +int ScriptDebuggerRemote::_serialize_variant(const Variant &var, const PropertyInfo &p_info, DVector<uint8_t> &buff) { + + int used_size = 0; + + used_size += __put_value_to_buff_at_pos(p_info.name, buff, used_size); + const int type_index = used_size; + used_size += __put_value_to_buff_at_pos(var.get_type(), buff, type_index); + const int hint_index = used_size; + used_size += __put_value_to_buff_at_pos(p_info.hint, buff, hint_index); + used_size += __put_value_to_buff_at_pos(p_info.usage, buff, used_size); + + StringName hint_string = p_info.hint_string; + + int value_len = 0; + switch (var.get_type()) { + case Variant::OBJECT: { + __put_value_to_buff_at_pos(Variant::INT, buff, type_index); + __put_value_to_buff_at_pos(PROPERTY_HINT_OBJECT_ID, buff, hint_index); + Object *obj = var; + uint32_t id = obj ? obj->get_instance_ID() : 0; + hint_string = obj ? obj->get_type_name() : hint_string; + value_len += __put_value_to_buff_at_pos(id, buff, used_size + value_len); + } break; + case Variant::IMAGE: + value_len += __put_value_to_buff_at_pos(Image(), buff, used_size + value_len); + break; + case Variant::ARRAY: { + Array arr = var; + value_len += __put_value_to_buff_at_pos(arr.size(), buff, used_size + value_len); + + for (int i = 0; i < arr.size(); i++) { + + const Variant &e = arr[i]; + PropertyInfo pi(e.get_type(), ""); + DVector<uint8_t> ebuff; + ebuff.resize(256); + ebuff.resize(_serialize_variant(e, pi, ebuff)); + + value_len += __put_value_to_buff_at_pos(ebuff, buff, used_size + value_len); + } + } break; + case Variant::STRING_ARRAY: { + + DVector<String> strarr = var; + Array arr; + arr.resize(strarr.size()); + + for (int i = 0; i < strarr.size(); i++) + arr[i] = strarr[i]; + + PropertyInfo pi(Variant::ARRAY, ""); + DVector<uint8_t> ebuff; + ebuff.resize(256); + ebuff.resize(_serialize_variant(arr, pi, ebuff)); + + value_len += __put_value_to_buff_at_pos(ebuff, buff, used_size + value_len); + } break; + case Variant::DICTIONARY: { + Dictionary dict = var; + value_len += __put_value_to_buff_at_pos(dict.size(), buff, used_size + value_len); + + const Array &keys = dict.keys(); + for (int i = 0; i < keys.size(); i++) { + + PropertyInfo pi; + const Variant &key = keys[i]; + + DVector<uint8_t> tmpbuff; + tmpbuff.resize(256); + pi.type = key.get_type(); + tmpbuff.resize(_serialize_variant(key, pi, tmpbuff)); + value_len += __put_value_to_buff_at_pos(tmpbuff, buff, used_size + value_len); + + const Variant &value = dict[key]; + pi.type = value.get_type(); + tmpbuff.resize(_serialize_variant(value, pi, tmpbuff)); + value_len += __put_value_to_buff_at_pos(tmpbuff, buff, used_size + value_len); + } + } break; + default: + value_len += __put_value_to_buff_at_pos(var, buff, used_size + value_len); + break; + } + used_size += value_len; + + used_size += __put_value_to_buff_at_pos(hint_string, buff, used_size); + + return used_size; +} + +DVector<uint8_t> ScriptDebuggerRemote::_serialize(const Variant &var, const PropertyInfo &p_info) { + + DVector<uint8_t> buff; + buff.resize(256); + buff.resize(_serialize_variant(var, p_info, buff)); + + return buff; } void ScriptDebuggerRemote::_poll_events() { @@ -648,6 +848,9 @@ void ScriptDebuggerRemote::_poll_events() { } else if (command == "set_object_property") { _set_object_property(cmd[1], cmd[2], cmd[3]); + } else if (command == "set_variable_value") { + + // TODO: Implement set the value stack variale } else if (command == "start_profiling") { @@ -945,6 +1148,7 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() { tcp_client = StreamPeerTCP::create_ref(); packet_peer_stream = Ref<PacketPeerStream>(memnew(PacketPeerStream)); packet_peer_stream->set_stream_peer(tcp_client); + packet_peer_stream->set_input_buffer_max_size(pow(2, 20)); mutex = Mutex::create(); locking = false; diff --git a/core/script_debugger_remote.h b/core/script_debugger_remote.h index 924e3774a..508f9b145 100644 --- a/core/script_debugger_remote.h +++ b/core/script_debugger_remote.h @@ -108,7 +108,6 @@ class ScriptDebuggerRemote : public ScriptDebugger { void *request_scene_tree_ud; void _set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value); - void _send_object_id(ObjectID p_id); void _send_video_memory(); LiveEditFuncs *live_edit_funcs; @@ -118,6 +117,9 @@ class ScriptDebuggerRemote : public ScriptDebugger { void _send_profiling_data(bool p_for_frame); + int _serialize_variant(const Variant &var, const PropertyInfo &p_info, DVector<uint8_t> &buff); + DVector<uint8_t> _serialize(const Variant &var, const PropertyInfo &p_info); + struct FrameData { StringName name; diff --git a/core/script_language.h b/core/script_language.h index f05245516..2cd0ca1b7 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -90,6 +90,9 @@ public: virtual ScriptInstance *instance_create(Object *p_this) = 0; virtual bool instance_has(const Object *p_this) const = 0; + virtual const Map<StringName, Variant> &get_constants() const = 0; + virtual const Set<StringName> &get_members() const = 0; + virtual bool has_source_code() const = 0; virtual String get_source_code() const = 0; virtual void set_source_code(const String &p_code) = 0; @@ -128,6 +131,7 @@ public: virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount); virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount); virtual void notification(int p_notification) = 0; + virtual Object *get_owner() { return NULL; } //this is used by script languages that keep a reference counter of their own //you can make make Ref<> not die when it reaches zero, so deleting the reference @@ -196,6 +200,7 @@ public: virtual String debug_get_stack_level_source(int p_level) const = 0; virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) = 0; virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) = 0; + virtual ScriptInstance *debug_get_stack_level_instance(int p_level) = 0; virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) = 0; virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1) = 0; diff --git a/editor/dictionary_property_edit.cpp b/editor/dictionary_property_edit.cpp new file mode 100644 index 000000000..66d0dad45 --- /dev/null +++ b/editor/dictionary_property_edit.cpp @@ -0,0 +1,187 @@ +/*************************************************************************/ +/* dictionary_property_edit.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 "dictionary_property_edit.h" +#include "editor_node.h" + +void DictionaryPropertyEdit::_notif_change() { + _change_notify(); +} + +void DictionaryPropertyEdit::_notif_changev(const String &p_v) { + _change_notify(p_v.utf8().get_data()); +} + +void DictionaryPropertyEdit::_set_key(int p_idx, const Variant &p_new_key) { + + // Set key of a dictionary is not allowd + return; +} + +void DictionaryPropertyEdit::_set_value(const Variant &p_key, const Variant &p_value) { + + Dictionary dict = get_dictionary(); + dict[p_key] = p_value; + Object *o = ObjectDB::get_instance(obj); + if (!o) + return; + + o->set(property, dict); +} + +Variant DictionaryPropertyEdit::get_dictionary() const { + + Object *o = ObjectDB::get_instance(obj); + if (!o) + return Dictionary(); + Variant dict = o->get(property); + if (dict.get_type() != Variant::DICTIONARY) + return Dictionary(); + return dict; +} + +void DictionaryPropertyEdit::_get_property_list(List<PropertyInfo> *p_list) const { + + Dictionary dict = get_dictionary(); + + const Array &keys = dict.keys(); + for (int i = 0; i < keys.size(); i++) { + + String index = itos(i); + + const Variant &key = keys[i]; + + PropertyInfo pi(key.get_type(), index + "/key"); + p_list->push_back(pi); + + const Variant &value = dict[key]; + pi = PropertyInfo(value.get_type(), index + "/value"); + p_list->push_back(pi); + } +} + +void DictionaryPropertyEdit::edit(Object *p_obj, const StringName &p_prop) { + + property = p_prop; + obj = p_obj->get_instance_ID(); +} + +Node *DictionaryPropertyEdit::get_node() { + + Object *o = ObjectDB::get_instance(obj); + if (!o) + return NULL; + + return o->cast_to<Node>(); +} + +void DictionaryPropertyEdit::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_set_key"), &DictionaryPropertyEdit::_set_key); + ObjectTypeDB::bind_method(_MD("_set_value"), &DictionaryPropertyEdit::_set_value); + ObjectTypeDB::bind_method(_MD("_notif_change"), &DictionaryPropertyEdit::_notif_change); + ObjectTypeDB::bind_method(_MD("_notif_changev"), &DictionaryPropertyEdit::_notif_changev); +} + +bool DictionaryPropertyEdit::_set(const StringName &p_name, const Variant &p_value) { + + Dictionary dict = get_dictionary(); + const Array &keys = dict.keys(); + + String pn = p_name; + int slash = pn.find("/"); + if (slash != -1 && pn.length() > slash) { + String type = pn.substr(slash + 1, pn.length()); + int index = pn.substr(0, slash).to_int(); + if (type == "key" && index < keys.size()) { + + const Variant &key = keys[index]; + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + + ur->create_action(TTR("Change Dictionary Key")); + ur->add_do_method(this, "_set_key", index, p_value); + ur->add_undo_method(this, "_set_key", index, key); + ur->add_do_method(this, "_notif_changev", p_name); + ur->add_undo_method(this, "_notif_changev", p_name); + ur->commit_action(); + + return true; + } else if (type == "value" && index < keys.size()) { + const Variant &key = keys[index]; + if (dict.has(key)) { + + Variant value = dict[key]; + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + + ur->create_action(TTR("Change Dictionary Value")); + ur->add_do_method(this, "_set_value", key, p_value); + ur->add_undo_method(this, "_set_value", key, value); + ur->add_do_method(this, "_notif_changev", p_name); + ur->add_undo_method(this, "_notif_changev", p_name); + ur->commit_action(); + + return true; + } + } + } + + return false; +} + +bool DictionaryPropertyEdit::_get(const StringName &p_name, Variant &r_ret) const { + + Dictionary dict = get_dictionary(); + const Array &keys = dict.keys(); + + String pn = p_name; + int slash = pn.find("/"); + + if (slash != -1 && pn.length() > slash) { + + String type = pn.substr(slash + 1, pn.length()); + int index = pn.substr(0, slash).to_int(); + + if (type == "key" && index < keys.size()) { + r_ret = keys[index]; + return true; + } else if (type == "value" && index < keys.size()) { + const Variant &key = keys[index]; + if (dict.has(key)) { + r_ret = dict[key]; + return true; + } + } + } + + return false; +} + +DictionaryPropertyEdit::DictionaryPropertyEdit() { + obj = 0; +} diff --git a/editor/dictionary_property_edit.h b/editor/dictionary_property_edit.h new file mode 100644 index 000000000..5f829d3fb --- /dev/null +++ b/editor/dictionary_property_edit.h @@ -0,0 +1,62 @@ +/*************************************************************************/ +/* dictionary_property_edit.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 DICTIONARY_PROPERTY_EDIT_H +#define DICTIONARY_PROPERTY_EDIT_H + +#include "scene/main/node.h" + +class DictionaryPropertyEdit : public Reference { + OBJ_TYPE(DictionaryPropertyEdit, Reference); + + ObjectID obj; + StringName property; + + void _notif_change(); + void _notif_changev(const String &p_v); + void _set_key(const int p_idx, const Variant &p_new_key); + void _set_value(const Variant &p_key, const Variant &p_value); + + Variant get_dictionary() const; + +protected: + static void _bind_methods(); + 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; + +public: + void edit(Object *p_obj, const StringName &p_prop); + + Node *get_node(); + + DictionaryPropertyEdit(); +}; + +#endif // DICTIONARY_PROPERTY_EDIT_H diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index 820711759..30d46933b 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "property_editor.h" #include "array_property_edit.h" +#include "dictionary_property_edit.h" #include "editor_file_system.h" #include "editor_help.h" #include "editor_import_export.h" @@ -2885,7 +2886,7 @@ void PropertyEditor::update_tree() { if (type == "") type = "Object"; - ObjectID id = obj->get(p.name); + ObjectID id = _get_curent_remote_object_id(p.name); if (id != 0) { item->set_text(1, type + " ID: " + itos(id)); item->add_button(1, get_icon("EditResource", "EditorIcons")); @@ -2993,10 +2994,10 @@ void PropertyEditor::update_tree() { } break; case Variant::DICTIONARY: { - item->set_cell_mode(1, TreeItem::CELL_MODE_STRING); - item->set_editable(1, false); - item->set_text(1, obj->get(p.name).operator String()); - + item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); + item->add_button(1, get_icon("EditResource", "EditorIcons")); + Dictionary d = obj->get(p.name); + item->set_text(1, "Dictionary{" + itos(d.size()) + "}"); } break; case Variant::INT_ARRAY: { @@ -3208,8 +3209,16 @@ void PropertyEditor::update_tree() { type = p.hint_string; if (obj->get(p.name).get_type() == Variant::NIL || obj->get(p.name).operator RefPtr().is_null()) { - item->set_text(1, "<null>"); - item->set_icon(1, Ref<Texture>()); + + if (Object *_o = obj->get(p.name)) { + if (_o->is_type("ScriptEditorDebuggerInspectedObject")) + item->set_text(1, _o->call("get_title")); + else + item->set_text(1, String(_o->get_type_name()) + " ID: " + itos(obj->get_instance_ID())); + } else { + item->set_text(1, "<null>"); + item->set_icon(1, Ref<Texture>()); + } } else { RES res = obj->get(p.name).operator RefPtr(); @@ -3331,6 +3340,35 @@ void PropertyEditor::_draw_transparency(Object *t, const Rect2 &p_rect) { tree->draw_rect(area, color); } +ObjectID PropertyEditor::_get_curent_remote_object_id(const StringName &p_name) { + + ObjectID id = 0; + if (obj) { + id = obj->get(p_name); + if (id == 0) { + + Object *debugObj = NULL; + + if (obj->is_type("ScriptEditorDebuggerVariables")) { + if (Object *oo = obj->call("get_var_value", p_name)) { + if (oo->is_type("ScriptEditorDebuggerInspectedObject")) + debugObj = oo; + } + } else if (obj->is_type("ScriptEditorDebuggerInspectedObject")) { + if (Object *oo = obj->call("get_variant", p_name)) { + if (oo->is_type("ScriptEditorDebuggerInspectedObject")) + debugObj = oo; + } + } + if (debugObj) { + id = debugObj->call("get_remote_object_id"); + } + } + } + + return id; +} + void PropertyEditor::_item_selected() { TreeItem *item = tree->get_selected(); @@ -3573,12 +3611,16 @@ void PropertyEditor::edit(Object *p_object) { if (obj == p_object) return; + + obj = p_object; + if (obj) { obj->remove_change_receptor(this); - } - obj = p_object; + if (obj->is_type("ScriptEditorDebuggerInspectedObject")) + set_enable_capitalize_paths(false); + } evaluator->edit(p_object); @@ -3687,15 +3729,24 @@ void PropertyEditor::_edit_button(Object *p_item, int p_column, int p_button) { } else if (t == Variant::OBJECT) { - RES r = obj->get(n); - if (r.is_valid()) { + Variant var = obj->get(n); + RES r = var; + if (r.is_valid()) { emit_signal("resource_selected", r, n); + } else if (Object *o = var) { + // Remote object clicked form property editor cell + if (o->is_type("ScriptEditorDebuggerInspectedObject")) { + ObjectID id = o->call("get_remote_object_id"); + emit_signal("object_id_selected", id); + print_line(String("OBJ ID SELECTED: ") + itos(id)); + } } } else if (t == Variant::INT && h == PROPERTY_HINT_OBJECT_ID) { - emit_signal("object_id_selected", obj->get(n)); - print_line("OBJ ID SELECTED"); + ObjectID id = _get_curent_remote_object_id(n); + emit_signal("object_id_selected", id); + print_line(String("OBJ ID SELECTED: ") + itos(id)); } else if (t == Variant::ARRAY || t == Variant::INT_ARRAY || t == Variant::REAL_ARRAY || t == Variant::STRING_ARRAY || t == Variant::VECTOR2_ARRAY || t == Variant::VECTOR3_ARRAY || t == Variant::COLOR_ARRAY || t == Variant::RAW_ARRAY) { @@ -3710,6 +3761,11 @@ void PropertyEditor::_edit_button(Object *p_item, int p_column, int p_button) { ape->edit(obj, n, Variant::Type(t)); EditorNode::get_singleton()->push_item(ape.ptr()); + } else if (t == Variant::DICTIONARY) { + + Ref<DictionaryPropertyEdit> dpe = memnew(DictionaryPropertyEdit); + dpe->edit(obj, n); + EditorNode::get_singleton()->push_item(dpe.ptr()); } } } diff --git a/editor/property_editor.h b/editor/property_editor.h index f6bca384a..53c24dfe7 100644 --- a/editor/property_editor.h +++ b/editor/property_editor.h @@ -236,6 +236,8 @@ class PropertyEditor : public Control { void _resource_preview_done(const String &p_path, const Ref<Texture> &p_preview, Variant p_ud); void _draw_transparency(Object *t, const Rect2 &p_rect); + ObjectID _get_curent_remote_object_id(const StringName &p_name); + UndoRedo *undo_redo; protected: diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index 5cbb07dd2..ac2230a28 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -28,6 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "script_editor_debugger.h" +#include "core/io/marshalls.h" #include "editor_node.h" #include "editor_profiler.h" #include "editor_settings.h" @@ -55,9 +56,17 @@ class ScriptEditorDebuggerVariables : public Object { protected: bool _set(const StringName &p_name, const Variant &p_value) { +#if 0 // Modify stack atomic variables is not supported yet + if(values.has(p_name)) { + + emit_signal("value_edited", p_name, p_value); + values[p_name] = p_value; + + return true; + } +#endif return false; } - bool _get(const StringName &p_name, Variant &r_ret) const { if (!values.has(p_name)) @@ -67,10 +76,19 @@ protected: } void _get_property_list(List<PropertyInfo> *p_list) const { + p_list->clear(); for (const List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) p_list->push_back(E->get()); } + static void _bind_methods() { + + ObjectTypeDB::bind_method(_MD("clear"), &ScriptEditorDebuggerVariables::clear); + ObjectTypeDB::bind_method(_MD("get_var_value"), &ScriptEditorDebuggerVariables::get_var_value); + + ADD_SIGNAL(MethodInfo("value_edited")); + } + public: void clear() { @@ -78,34 +96,30 @@ public: values.clear(); } - String get_var_value(const String &p_var) const { + Variant get_var_value(const String &p_var) const { - for (Map<StringName, Variant>::Element *E = values.front(); E; E = E->next()) { - String v = E->key().operator String().get_slice("/", 1); - if (v == p_var) - return E->get(); - } + Variant var; + if (values.has(p_var)) + var = values[p_var]; - return ""; + return var; } - void add_property(const String &p_name, const Variant &p_value, const PropertyHint &p_hint, const String p_hint_string) { + void add_property(const PropertyInfo &p_info, const Variant &p_value) { - PropertyInfo pinfo; - pinfo.name = p_name; - pinfo.type = p_value.get_type(); - pinfo.hint = p_hint; - pinfo.hint_string = p_hint_string; - props.push_back(pinfo); - values[p_name] = p_value; + props.push_back(p_info); + values[p_info.name] = p_value; } void update() { _change_notify(); } - ScriptEditorDebuggerVariables() { + void update_single(const char *p_prop) { + _change_notify(p_prop); } + + ScriptEditorDebuggerVariables() {} }; class ScriptEditorDebuggerInspectedObject : public Object { @@ -115,14 +129,13 @@ class ScriptEditorDebuggerInspectedObject : public Object { protected: bool _set(const StringName &p_name, const Variant &p_value) { - if (!prop_values.has(p_name)) + if (!prop_values.has(p_name) || String(p_name).begins_with("constants/")) return false; emit_signal("value_edited", p_name, p_value); prop_values[p_name] = p_value; return true; } - bool _get(const StringName &p_name, Variant &r_ret) const { if (!prop_values.has(p_name)) @@ -141,23 +154,52 @@ protected: static void _bind_methods() { + ObjectTypeDB::bind_method(_MD("get_title"), &ScriptEditorDebuggerInspectedObject::get_title); + ObjectTypeDB::bind_method(_MD("get_variant"), &ScriptEditorDebuggerInspectedObject::get_variant); + ObjectTypeDB::bind_method(_MD("clear"), &ScriptEditorDebuggerInspectedObject::clear); + ObjectTypeDB::bind_method(_MD("get_remote_object_id"), &ScriptEditorDebuggerInspectedObject::get_remote_object_id); + ADD_SIGNAL(MethodInfo("value_edited")); } public: - ObjectID last_edited_id; + ObjectID remote_object_id; List<PropertyInfo> prop_list; Map<StringName, Variant> prop_values; + StringName type_name; + + ObjectID get_remote_object_id() { + return remote_object_id; + } + + String get_title() { + if (remote_object_id) + return String(type_name) + " ID: " + itos(remote_object_id); + else + return "<null>"; + } + Variant get_variant(const StringName &p_name) { + + Variant var; + _get(p_name, var); + return var; + } + void clear() { + + prop_list.clear(); + prop_values.clear(); + } void update() { _change_notify(); } - void update_single(const char *p_prop) { _change_notify(p_prop); } - ScriptEditorDebuggerInspectedObject() { last_edited_id = 0; } + ScriptEditorDebuggerInspectedObject() { + remote_object_id = 0; + } }; void ScriptEditorDebugger::debug_next() { @@ -258,6 +300,16 @@ void ScriptEditorDebugger::_scene_tree_property_value_edited(const String &p_pro inspect_edited_object_timeout = 0.7; //avoid annoyance, don't request soon after editing } +void ScriptEditorDebugger::_scene_tree_variable_value_edited(const String &p_prop, const Variant &p_value) { + + Array msg; + msg.push_back("set_variable_value"); + msg.push_back(p_prop); + msg.push_back(p_value); + ppeer->put_var(msg); + inspect_edited_object_timeout = 0.7; //avoid annoyance, don't request soon after editing +} + void ScriptEditorDebugger::_scene_tree_property_select_object(ObjectID p_object) { inspected_object_id = p_object; @@ -293,10 +345,15 @@ Size2 ScriptEditorDebugger::get_minimum_size() const { ms.y = MAX(ms.y, 250); return ms; } + +Variant _unserial_variant(const DVector<uint8_t> &data, PropertyInfo &r_info); + void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_data) { if (p_msg == "debug_enter") { + _clear_remote_objects(); + Array msg; msg.push_back("get_stack_dump"); ppeer->put_var(msg); @@ -324,6 +381,8 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da } else if (p_msg == "debug_exit") { + _clear_remote_objects(); + breaked = false; step->set_disabled(true); next->set_disabled(true); @@ -392,57 +451,46 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da le_set->set_disabled(false); } else if (p_msg == "message:inspect_object") { - ObjectID id = p_data[0]; - String type = p_data[1]; - Variant path = p_data[2]; //what to do yet, i don't know - int prop_count = p_data[3]; - - int idx = 4; + ScriptEditorDebuggerInspectedObject *debugObj = NULL; - if (inspected_object->last_edited_id != id) { - inspected_object->prop_list.clear(); - inspected_object->prop_values.clear(); + ObjectID id = p_data[0]; + if (remote_objects.has(id)) { + debugObj = remote_objects[id]; + } else { + debugObj = memnew(ScriptEditorDebuggerInspectedObject); + debugObj->remote_object_id = id; + debugObj->connect("value_edited", this, "_scene_tree_property_value_edited"); } + debugObj->clear(); + + String title = String("Object ID: ") + itos(id); + debugObj->prop_list.push_back(PropertyInfo(Variant::STRING, title, PROPERTY_HINT_NONE, title, PROPERTY_USAGE_CATEGORY)); - for (int i = 0; i < prop_count; i++) { + Array props = p_data[1]; + for (int i = 0; i < props.size(); i++) { PropertyInfo pinfo; - pinfo.name = p_data[idx++]; - pinfo.type = Variant::Type(int(p_data[idx++])); - pinfo.hint = PropertyHint(int(p_data[idx++])); - pinfo.hint_string = p_data[idx++]; - if (pinfo.name.begins_with("*")) { - pinfo.name = pinfo.name.substr(1, pinfo.name.length()); - pinfo.usage = PROPERTY_USAGE_CATEGORY; - } else { - pinfo.usage = PROPERTY_USAGE_EDITOR; - } + int len = 0; + Variant value = _unserialize_variant(props[i], pinfo, len); - if (inspected_object->last_edited_id != id) { - //don't update.. it's the same, instead refresh - inspected_object->prop_list.push_back(pinfo); - } + if (value.get_type() == Variant::STRING && pinfo.hint_string == "REMOTE:RES") { - inspected_object->prop_values[pinfo.name] = p_data[idx++]; + pinfo.type = Variant::OBJECT; + pinfo.hint_string = ""; + RES res = ResourceLoader::load(value); + debugObj->prop_list.push_back(pinfo); + debugObj->prop_values[pinfo.name] = ResourceLoader::load(value); + } else { - if (inspected_object->last_edited_id == id) { - //same, just update value, don't rebuild - inspected_object->update_single(pinfo.name.ascii().get_data()); + debugObj->prop_list.push_back(pinfo); + debugObj->prop_values[pinfo.name] = value; } - } - if (inspected_object->last_edited_id != id) { - //only if different - inspected_object->update(); + debugObj->update_single(pinfo.name.ascii().get_data()); } - inspected_object->last_edited_id = id; - - if (tabs->get_current_tab() == 2) { - inspect_properties->edit(inspected_object); - } else { - editor->push_item(inspected_object); - } + debugObj->update(); + editor->push_item(debugObj, ""); } else if (p_msg == "message:video_mem") { @@ -472,6 +520,8 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da } else if (p_msg == "stack_dump") { stack_dump->clear(); + _clear_remote_objects(); + TreeItem *r = stack_dump->create_item(); for (int i = 0; i < p_data.size(); i++) { @@ -495,53 +545,14 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da } else if (p_msg == "stack_frame_vars") { variables->clear(); + _clear_remote_objects(); - int ofs = 0; - int mcount = p_data[ofs]; - - ofs++; - for (int i = 0; i < mcount; i++) { - - String n = p_data[ofs + i * 2 + 0]; - Variant v = p_data[ofs + i * 2 + 1]; - PropertyHint h = PROPERTY_HINT_NONE; - String hs = String(); - - if (n.begins_with("*")) { - - n = n.substr(1, n.length()); - h = PROPERTY_HINT_OBJECT_ID; - String s = v; - s = s.replace("[", ""); - hs = s.get_slice(":", 0); - v = s.get_slice(":", 1).to_int(); - } - - variables->add_property("members/" + n, v, h, hs); - } - ofs += mcount * 2; - - mcount = p_data[ofs]; - - ofs++; - for (int i = 0; i < mcount; i++) { - - String n = p_data[ofs + i * 2 + 0]; - Variant v = p_data[ofs + i * 2 + 1]; - PropertyHint h = PROPERTY_HINT_NONE; - String hs = String(); - - if (n.begins_with("*")) { - - n = n.substr(1, n.length()); - h = PROPERTY_HINT_OBJECT_ID; - String s = v; - s = s.replace("[", ""); - hs = s.get_slice(":", 0); - v = s.get_slice(":", 1).to_int(); - } + for (int i = 0; i < p_data.size(); i++) { - variables->add_property("locals/" + n, v, h, hs); + PropertyInfo pinfo; + int len = 0; + Variant value = _unserialize_variant(p_data[i], pinfo, len); + variables->add_property(pinfo, value); } variables->update(); @@ -757,6 +768,129 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da } } +Variant __get_value_from_buff_at_pos(Variant::Type type, const DVector<uint8_t> &buff, int pos, int &r_len) { + + Variant v; + ERR_FAIL_COND_V(pos >= buff.size(), v); + + int size = 0; + switch (type) { + case Variant::INT: + v = decode_uint32(&buff.read()[pos]); + size += sizeof(uint32_t); + break; + case Variant::BOOL: + v = bool(decode_uint32(&buff.read()[pos])); + size += sizeof(uint32_t); + break; + case Variant::REAL: + v = decode_float(&buff.read()[pos]); + size += sizeof(uint32_t); + break; + default: + decode_variant(v, &buff.read()[pos], buff.size() - pos, &size); + break; + } + + r_len += size; + + return v; +} + +Variant ScriptEditorDebugger::_unserialize_variant(const DVector<uint8_t> &data, PropertyInfo &r_info, int &r_len) { + + Variant v; + int read_len = 0; + + if (const int len_max = data.size()) { + + r_info.name = __get_value_from_buff_at_pos(Variant::STRING, data, read_len, read_len); + r_info.type = Variant::Type((int)__get_value_from_buff_at_pos(Variant::INT, data, read_len, read_len)); + r_info.hint = PropertyHint((int)__get_value_from_buff_at_pos(Variant::INT, data, read_len, read_len)); + r_info.usage = __get_value_from_buff_at_pos(Variant::INT, data, read_len, read_len); + + ScriptEditorDebuggerInspectedObject *eobj = NULL; + + switch (r_info.type) { + case Variant::INT: { + v = __get_value_from_buff_at_pos(Variant::INT, data, read_len, read_len); + + if (r_info.hint == PROPERTY_HINT_OBJECT_ID) { + + ObjectID eid = v; + + if (remote_objects.has(eid)) + eobj = remote_objects[eid]; + else { + eobj = memnew(ScriptEditorDebuggerInspectedObject); + eobj->connect("value_edited", this, "_scene_tree_property_value_edited"); + eobj->remote_object_id = eid; + remote_objects[eid] = eobj; + } + + v = eobj; + } + } break; + case Variant::ARRAY: { + int size = __get_value_from_buff_at_pos(Variant::INT, data, read_len, read_len); + + ERR_BREAK(size < 0); + + Array arr; + arr.resize(size); + + for (int i = 0; i < size; i++) { + int len = data.size() - read_len; + ERR_BREAK(len < 0); + DVector<uint8_t> ebuff = __get_value_from_buff_at_pos(Variant::RAW_ARRAY, data, read_len, read_len); + PropertyInfo pi; + arr[i] = _unserialize_variant(ebuff, pi, len); + } + + v = arr; + } break; + case Variant::DICTIONARY: { + int size = __get_value_from_buff_at_pos(Variant::INT, data, read_len, read_len); + + ERR_BREAK(size < 0); + + Dictionary dict; + for (int i = 0; i < size; i++) { + + int len = data.size() - read_len; + ERR_BREAK(len < 0); + + PropertyInfo pi; + DVector<uint8_t> tmpbuff = __get_value_from_buff_at_pos(Variant::RAW_ARRAY, data, read_len, read_len); + Variant key = _unserialize_variant(tmpbuff, pi, len); + + len = data.size() - read_len; + ERR_BREAK(len < 0); + + tmpbuff = __get_value_from_buff_at_pos(Variant::RAW_ARRAY, data, read_len, read_len); + Variant value = _unserialize_variant(tmpbuff, pi, len); + + dict[key] = value; + } + v = dict; + } break; + default: + v = __get_value_from_buff_at_pos(r_info.type, data, read_len, read_len); + break; + } + + r_info.hint_string = __get_value_from_buff_at_pos(Variant::STRING, data, read_len, read_len); + + if (eobj) { + eobj->type_name = r_info.hint_string; + } + } + + r_len = read_len; + + return v; +} + void ScriptEditorDebugger::_performance_select(Object *, int, bool) { perf_draw->update(); @@ -986,7 +1120,7 @@ void ScriptEditorDebugger::_notification(int p_what) { ERR_FAIL_COND(ret != OK); } if (cmd.get_type() != Variant::STRING) { - stop(); + //stop(); ERR_FAIL_COND(cmd.get_type() != Variant::STRING); } @@ -995,11 +1129,11 @@ void ScriptEditorDebugger::_notification(int p_what) { ret = ppeer->get_var(cmd); if (ret != OK) { - stop(); + //stop(); ERR_FAIL_COND(ret != OK); } if (cmd.get_type() != Variant::INT) { - stop(); + //stop(); ERR_FAIL_COND(cmd.get_type() != Variant::INT); } @@ -1053,6 +1187,8 @@ void ScriptEditorDebugger::stop() { set_process(false); + _clear_remote_objects(); + server->stop(); ppeer->set_stream_peer(Ref<StreamPeer>()); @@ -1530,6 +1666,24 @@ void ScriptEditorDebugger::_paused() { } } +void ScriptEditorDebugger::_set_remote_object(ObjectID p_id, ScriptEditorDebuggerInspectedObject *p_obj) { + + if (remote_objects.has(p_id)) + memdelete(remote_objects[p_id]); + remote_objects[p_id] = p_obj; +} + +void ScriptEditorDebugger::_clear_remote_objects() { + + inspector->edit(NULL); + inspect_properties->edit(NULL); + + for (Map<ObjectID, ScriptEditorDebuggerInspectedObject *>::Element *E = remote_objects.front(); E; E = E->next()) { + memdelete(E->value()); + } + remote_objects.clear(); +} + void ScriptEditorDebugger::_bind_methods() { ObjectTypeDB::bind_method(_MD("_stack_dump_frame_selected"), &ScriptEditorDebugger::_stack_dump_frame_selected); @@ -1564,6 +1718,7 @@ void ScriptEditorDebugger::_bind_methods() { ObjectTypeDB::bind_method(_MD("live_debug_reparent_node"), &ScriptEditorDebugger::live_debug_reparent_node); ObjectTypeDB::bind_method(_MD("_scene_tree_property_select_object"), &ScriptEditorDebugger::_scene_tree_property_select_object); ObjectTypeDB::bind_method(_MD("_scene_tree_property_value_edited"), &ScriptEditorDebugger::_scene_tree_property_value_edited); + ObjectTypeDB::bind_method(_MD("_scene_tree_variable_value_edited"), &ScriptEditorDebugger::_scene_tree_variable_value_edited); ADD_SIGNAL(MethodInfo("goto_script_line")); ADD_SIGNAL(MethodInfo("breaked", PropertyInfo(Variant::BOOL, "reallydid"), PropertyInfo(Variant::BOOL, "can_debug"))); @@ -1573,7 +1728,10 @@ void ScriptEditorDebugger::_bind_methods() { ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { ppeer = Ref<PacketPeerStream>(memnew(PacketPeerStream)); + ppeer->set_input_buffer_max_size(pow(2, 20)); + editor = p_editor; + editor->get_property_editor()->connect("object_id_selected", this, "_scene_tree_property_select_object"); tabs = memnew(TabContainer); tabs->set_v_size_flags(SIZE_EXPAND_FILL); @@ -1660,6 +1818,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { pending_in_queue = 0; variables = memnew(ScriptEditorDebuggerVariables); + variables->connect("value_edited", this, "_scene_tree_variable_value_edited"); breaked = false; @@ -1704,14 +1863,12 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { inspect_scene_tree->connect("cell_selected", this, "_scene_tree_selected"); inspect_scene_tree->connect("item_collapsed", this, "_scene_tree_folded"); - // - VBoxContainer *info_right = memnew(VBoxContainer); info_right->set_h_size_flags(SIZE_EXPAND_FILL); inspect_info->add_child(info_right); inspect_properties = memnew(PropertyEditor); - inspect_properties->hide_top_label(); + //inspect_properties->hide_top_label(); inspect_properties->set_show_categories(true); inspect_properties->connect("object_id_selected", this, "_scene_tree_property_select_object"); @@ -1721,9 +1878,6 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { inspect_edited_object_timeout = EDITOR_DEF("debugger/remote_inspect_refresh_interval", 0.2); inspected_object_id = 0; updating_scene_tree = false; - - inspected_object = memnew(ScriptEditorDebuggerInspectedObject); - inspected_object->connect("value_edited", this, "_scene_tree_property_value_edited"); } { //profiler @@ -1874,5 +2028,5 @@ ScriptEditorDebugger::~ScriptEditorDebugger() { ppeer->set_stream_peer(Ref<StreamPeer>()); server->stop(); - memdelete(inspected_object); + _clear_remote_objects(); } diff --git a/editor/script_editor_debugger.h b/editor/script_editor_debugger.h index 9cd8204a0..69e559427 100644 --- a/editor/script_editor_debugger.h +++ b/editor/script_editor_debugger.h @@ -72,7 +72,7 @@ class ScriptEditorDebugger : public Control { float inspect_scene_tree_timeout; float inspect_edited_object_timeout; ObjectID inspected_object_id; - ScriptEditorDebuggerInspectedObject *inspected_object; + Map<ObjectID, ScriptEditorDebuggerInspectedObject *> remote_objects; bool updating_scene_tree; Set<ObjectID> unfold_cache; @@ -142,9 +142,10 @@ class ScriptEditorDebugger : public Control { void _scene_tree_selected(); void _scene_tree_request(); void _parse_message(const String &p_msg, const Array &p_data); + Variant _unserialize_variant(const DVector<uint8_t> &data, PropertyInfo &r_info, int &r_len); void _scene_tree_property_select_object(ObjectID p_object); void _scene_tree_property_value_edited(const String &p_prop, const Variant &p_value); - + void _scene_tree_variable_value_edited(const String &p_prop, const Variant &p_value); void _video_mem_request(); int _get_node_path_cache(const NodePath &p_path); @@ -168,6 +169,9 @@ class ScriptEditorDebugger : public Control { void _paused(); + void _set_remote_object(ObjectID p_id, ScriptEditorDebuggerInspectedObject *p_obj); + void _clear_remote_objects(); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp index e8d1d9a66..7689172b4 100644 --- a/modules/gdscript/gd_editor.cpp +++ b/modules/gdscript/gd_editor.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "gd_compiler.h" #include "gd_script.h" +#include "global_constants.h" #include "globals.h" #include "os/file_access.h" @@ -242,9 +243,47 @@ void GDScriptLanguage::debug_get_stack_level_members(int p_level, List<String> * p_values->push_back(instance->debug_get_member_by_index(E->get().index)); } } -void GDScriptLanguage::debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) { - //no globals are really reachable in gdscript +ScriptInstance *GDScriptLanguage::debug_get_stack_level_instance(int p_level) { + + ERR_FAIL_COND_V(_debug_parse_err_line >= 0, NULL); + ERR_FAIL_INDEX_V(p_level, _debug_call_stack_pos, NULL); + + int l = _debug_call_stack_pos - p_level - 1; + GDInstance *instance = _call_stack[l].instance; + + return instance; +} + +void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) { + + const Map<StringName, int> &name_idx = GDScriptLanguage::get_singleton()->get_global_map(); + const Variant *globals = GDScriptLanguage::get_singleton()->get_global_array(); + + for (const Map<StringName, int>::Element *E = name_idx.front(); E; E = E->next()) { + + if (ObjectTypeDB::type_exists(E->key()) || Globals::get_singleton()->has_singleton(E->key()) || E->key() == "PI") + continue; + + const Variant &var = globals[E->value()]; + if (Object *obj = var) { + if (obj->cast_to<GDNativeClass>()) + continue; + } + + bool skip = false; + for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) { + if (E->key() == GlobalConstants::get_global_constant_name(i)) { + skip = true; + break; + } + } + if (skip) + continue; + + p_globals->push_back(E->key()); + p_values->push_back(var); + } } String GDScriptLanguage::debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) { diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h index 8998a5c4f..c7368cf4d 100644 --- a/modules/gdscript/gd_script.h +++ b/modules/gdscript/gd_script.h @@ -138,8 +138,8 @@ public: bool is_valid() const { return valid; } const Map<StringName, Ref<GDScript> > &get_subclasses() const { return subclasses; } - const Map<StringName, Variant> &get_constants() const { return constants; } - const Set<StringName> &get_members() const { return members; } + virtual const Map<StringName, Variant> &get_constants() const { return constants; } + virtual const Set<StringName> &get_members() const { return members; } const Map<StringName, GDFunction *> &get_member_functions() const { return member_functions; } const Ref<GDNativeClass> &get_native() const { return native; } @@ -199,7 +199,7 @@ class GDInstance : public ScriptInstance { void _ml_call_reversed(GDScript *sptr, const StringName &p_method, const Variant **p_args, int p_argcount); public: - _FORCE_INLINE_ Object *get_owner() { return owner; } + virtual Object *get_owner() { return owner; } virtual bool set(const StringName &p_name, const Variant &p_value); virtual bool get(const StringName &p_name, Variant &r_ret) const; @@ -374,7 +374,8 @@ public: virtual String debug_get_stack_level_source(int p_level) const; virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1); virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1); - virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1); + virtual void debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1); + virtual ScriptInstance *debug_get_stack_level_instance(int p_level); virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1); virtual void reload_all_scripts(); |
