Merge pull request #8979 from GodotExplorer/pr-enhanced-debugger
[2.1] Enhanced debugger
This commit is contained in:
commit
57dc285051
|
@ -28,10 +28,12 @@
|
||||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
#include "script_debugger_remote.h"
|
#include "script_debugger_remote.h"
|
||||||
|
#include "core/io/marshalls.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "io/ip.h"
|
#include "io/ip.h"
|
||||||
#include "os/input.h"
|
#include "os/input.h"
|
||||||
#include "os/os.h"
|
#include "os/os.h"
|
||||||
|
|
||||||
void ScriptDebuggerRemote::_send_video_memory() {
|
void ScriptDebuggerRemote::_send_video_memory() {
|
||||||
|
|
||||||
List<ResourceUsage> usage;
|
List<ResourceUsage> usage;
|
||||||
|
@ -183,6 +185,8 @@ void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue)
|
||||||
ERR_CONTINUE(cmd.size() != 1);
|
ERR_CONTINUE(cmd.size() != 1);
|
||||||
int lv = cmd[0];
|
int lv = cmd[0];
|
||||||
|
|
||||||
|
ScriptInstance *self_instance = p_script->debug_get_stack_level_instance(lv);
|
||||||
|
|
||||||
List<String> members;
|
List<String> members;
|
||||||
List<Variant> member_vals;
|
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());
|
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("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());
|
||||||
|
|
||||||
{ //members
|
{ //locals
|
||||||
packet_peer_stream->put_var(members.size());
|
List<String>::Element *E = locals.front();
|
||||||
|
List<Variant>::Element *F = local_vals.front();
|
||||||
List<String>::Element *E = members.front();
|
|
||||||
List<Variant>::Element *F = member_vals.front();
|
|
||||||
|
|
||||||
while (E) {
|
while (E) {
|
||||||
|
|
||||||
if (F->get().get_type() == Variant::OBJECT) {
|
PropertyInfo pi(var.get_type(), String("locals/") + E->get());
|
||||||
packet_peer_stream->put_var("*" + E->get());
|
packet_peer_stream->put_var(_serialize(F->get(), pi));
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
E = E->next();
|
E = E->next();
|
||||||
F = F->next();
|
F = F->next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{ //locals
|
{ //members
|
||||||
packet_peer_stream->put_var(locals.size());
|
|
||||||
|
|
||||||
List<String>::Element *E = locals.front();
|
if (self_instance) { // self
|
||||||
List<Variant>::Element *F = local_vals.front();
|
|
||||||
|
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) {
|
while (E) {
|
||||||
|
|
||||||
if (F->get().get_type() == Variant::OBJECT) {
|
PropertyInfo pi(var.get_type(), String("members/") + E->get());
|
||||||
packet_peer_stream->put_var("*" + E->get());
|
packet_peer_stream->put_var(_serialize(F->get(), pi));
|
||||||
String pretty_print = F->get().operator String();
|
|
||||||
packet_peer_stream->put_var(pretty_print.ascii().get_data());
|
E = E->next();
|
||||||
} else {
|
F = F->next();
|
||||||
packet_peer_stream->put_var(E->get());
|
}
|
||||||
packet_peer_stream->put_var(F->get());
|
}
|
||||||
|
|
||||||
|
if (self_instance) { // constants
|
||||||
|
|
||||||
|
Ref<Script> script = self_instance->get_script();
|
||||||
|
if (!script.is_null()) {
|
||||||
|
|
||||||
|
const Map<StringName, Variant> &constants = script->get_constants();
|
||||||
|
|
||||||
|
for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
|
||||||
|
|
||||||
|
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();
|
E = E->next();
|
||||||
F = F->next();
|
F = F->next();
|
||||||
|
@ -531,69 +562,81 @@ void ScriptDebuggerRemote::_send_object_id(ObjectID p_id) {
|
||||||
if (!obj)
|
if (!obj)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Array props;
|
||||||
|
const uint32_t SEND_PROPERTIES = 0xFFFFFFFF; // All kind of properties are allowed
|
||||||
List<PropertyInfo> pinfo;
|
List<PropertyInfo> pinfo;
|
||||||
|
Set<StringName> ignored_properties;
|
||||||
obj->get_property_list(&pinfo, true);
|
obj->get_property_list(&pinfo, true);
|
||||||
|
bool query_props = true;
|
||||||
|
|
||||||
int props_to_send = 0;
|
if (ScriptInstance *si = obj->get_script_instance()) {
|
||||||
for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
|
if (!si->get_script().is_null()) {
|
||||||
|
|
||||||
if (E->get().usage & (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CATEGORY)) {
|
const Map<StringName, Variant> &constants = si->get_script()->get_constants();
|
||||||
props_to_send++;
|
for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
|
||||||
|
|
||||||
|
PropertyInfo pi(E->value().get_type(), String("constants/") + E->key());
|
||||||
|
props.push_back(_serialize(E->value(), pi));
|
||||||
|
}
|
||||||
|
|
||||||
|
const Set<StringName> &members = si->get_script()->get_members();
|
||||||
|
for (Set<StringName>::Element *E = members.front(); E; E = E->next()) {
|
||||||
|
|
||||||
|
Variant m;
|
||||||
|
if (si->get(E->get(), m)) {
|
||||||
|
|
||||||
|
PropertyInfo pi(m.get_type(), String("members/") + E->get());
|
||||||
|
props.push_back(_serialize(m, pi));
|
||||||
|
|
||||||
|
ignored_properties.insert(E->get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyInfo pi(Variant::OBJECT, String("Resource/script"));
|
||||||
|
props.push_back(_serialize(si->get_script(), pi));
|
||||||
|
}
|
||||||
|
} else if (Resource *res = obj->cast_to<Resource>()) {
|
||||||
|
|
||||||
|
if (res->cast_to<Script>()) {
|
||||||
|
|
||||||
|
const Map<StringName, Variant> &constants = res->cast_to<Script>()->get_constants();
|
||||||
|
for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
|
||||||
|
|
||||||
|
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("message:inspect_object");
|
||||||
packet_peer_stream->put_var(props_to_send * 5 + 4);
|
packet_peer_stream->put_var(2);
|
||||||
packet_peer_stream->put_var(p_id);
|
packet_peer_stream->put_var(p_id);
|
||||||
packet_peer_stream->put_var(obj->get_type());
|
packet_peer_stream->put_var(props);
|
||||||
if (obj->is_type("Resource") || obj->is_type("Node"))
|
|
||||||
packet_peer_stream->put_var(obj->call("get_path"));
|
|
||||||
else
|
|
||||||
packet_peer_stream->put_var("");
|
|
||||||
|
|
||||||
packet_peer_stream->put_var(props_to_send);
|
|
||||||
|
|
||||||
for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
|
|
||||||
|
|
||||||
if (E->get().usage & (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CATEGORY)) {
|
|
||||||
|
|
||||||
if (E->get().usage & PROPERTY_USAGE_CATEGORY) {
|
|
||||||
packet_peer_stream->put_var("*" + E->get().name);
|
|
||||||
} else {
|
|
||||||
packet_peer_stream->put_var(E->get().name);
|
|
||||||
}
|
|
||||||
|
|
||||||
Variant var = obj->get(E->get().name);
|
|
||||||
|
|
||||||
if (E->get().type == Variant::OBJECT || var.get_type() == Variant::OBJECT) {
|
|
||||||
|
|
||||||
ObjectID id2;
|
|
||||||
Object *obj = var;
|
|
||||||
if (obj) {
|
|
||||||
id2 = obj->get_instance_ID();
|
|
||||||
} else {
|
|
||||||
id2 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptDebuggerRemote::_set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value) {
|
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)
|
if (!obj)
|
||||||
return;
|
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() {
|
void ScriptDebuggerRemote::_poll_events() {
|
||||||
|
@ -648,6 +848,9 @@ void ScriptDebuggerRemote::_poll_events() {
|
||||||
} else if (command == "set_object_property") {
|
} else if (command == "set_object_property") {
|
||||||
|
|
||||||
_set_object_property(cmd[1], cmd[2], cmd[3]);
|
_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") {
|
} else if (command == "start_profiling") {
|
||||||
|
|
||||||
|
@ -945,6 +1148,7 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() {
|
||||||
tcp_client = StreamPeerTCP::create_ref();
|
tcp_client = StreamPeerTCP::create_ref();
|
||||||
packet_peer_stream = Ref<PacketPeerStream>(memnew(PacketPeerStream));
|
packet_peer_stream = Ref<PacketPeerStream>(memnew(PacketPeerStream));
|
||||||
packet_peer_stream->set_stream_peer(tcp_client);
|
packet_peer_stream->set_stream_peer(tcp_client);
|
||||||
|
packet_peer_stream->set_input_buffer_max_size(pow(2, 20));
|
||||||
mutex = Mutex::create();
|
mutex = Mutex::create();
|
||||||
locking = false;
|
locking = false;
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,6 @@ class ScriptDebuggerRemote : public ScriptDebugger {
|
||||||
void *request_scene_tree_ud;
|
void *request_scene_tree_ud;
|
||||||
|
|
||||||
void _set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value);
|
void _set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value);
|
||||||
|
|
||||||
void _send_object_id(ObjectID p_id);
|
void _send_object_id(ObjectID p_id);
|
||||||
void _send_video_memory();
|
void _send_video_memory();
|
||||||
LiveEditFuncs *live_edit_funcs;
|
LiveEditFuncs *live_edit_funcs;
|
||||||
|
@ -118,6 +117,9 @@ class ScriptDebuggerRemote : public ScriptDebugger {
|
||||||
|
|
||||||
void _send_profiling_data(bool p_for_frame);
|
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 {
|
struct FrameData {
|
||||||
|
|
||||||
StringName name;
|
StringName name;
|
||||||
|
|
|
@ -90,6 +90,9 @@ public:
|
||||||
virtual ScriptInstance *instance_create(Object *p_this) = 0;
|
virtual ScriptInstance *instance_create(Object *p_this) = 0;
|
||||||
virtual bool instance_has(const Object *p_this) const = 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 bool has_source_code() const = 0;
|
||||||
virtual String get_source_code() const = 0;
|
virtual String get_source_code() const = 0;
|
||||||
virtual void set_source_code(const String &p_code) = 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(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 call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount);
|
||||||
virtual void notification(int p_notification) = 0;
|
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
|
//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
|
//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 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_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 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 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;
|
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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -29,6 +29,7 @@
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
#include "property_editor.h"
|
#include "property_editor.h"
|
||||||
#include "array_property_edit.h"
|
#include "array_property_edit.h"
|
||||||
|
#include "dictionary_property_edit.h"
|
||||||
#include "editor_file_system.h"
|
#include "editor_file_system.h"
|
||||||
#include "editor_help.h"
|
#include "editor_help.h"
|
||||||
#include "editor_import_export.h"
|
#include "editor_import_export.h"
|
||||||
|
@ -2885,7 +2886,7 @@ void PropertyEditor::update_tree() {
|
||||||
if (type == "")
|
if (type == "")
|
||||||
type = "Object";
|
type = "Object";
|
||||||
|
|
||||||
ObjectID id = obj->get(p.name);
|
ObjectID id = _get_curent_remote_object_id(p.name);
|
||||||
if (id != 0) {
|
if (id != 0) {
|
||||||
item->set_text(1, type + " ID: " + itos(id));
|
item->set_text(1, type + " ID: " + itos(id));
|
||||||
item->add_button(1, get_icon("EditResource", "EditorIcons"));
|
item->add_button(1, get_icon("EditResource", "EditorIcons"));
|
||||||
|
@ -2993,10 +2994,10 @@ void PropertyEditor::update_tree() {
|
||||||
} break;
|
} break;
|
||||||
case Variant::DICTIONARY: {
|
case Variant::DICTIONARY: {
|
||||||
|
|
||||||
item->set_cell_mode(1, TreeItem::CELL_MODE_STRING);
|
item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM);
|
||||||
item->set_editable(1, false);
|
item->add_button(1, get_icon("EditResource", "EditorIcons"));
|
||||||
item->set_text(1, obj->get(p.name).operator String());
|
Dictionary d = obj->get(p.name);
|
||||||
|
item->set_text(1, "Dictionary{" + itos(d.size()) + "}");
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Variant::INT_ARRAY: {
|
case Variant::INT_ARRAY: {
|
||||||
|
@ -3208,8 +3209,16 @@ void PropertyEditor::update_tree() {
|
||||||
type = p.hint_string;
|
type = p.hint_string;
|
||||||
|
|
||||||
if (obj->get(p.name).get_type() == Variant::NIL || obj->get(p.name).operator RefPtr().is_null()) {
|
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 {
|
} else {
|
||||||
RES res = obj->get(p.name).operator RefPtr();
|
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);
|
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() {
|
void PropertyEditor::_item_selected() {
|
||||||
|
|
||||||
TreeItem *item = tree->get_selected();
|
TreeItem *item = tree->get_selected();
|
||||||
|
@ -3573,12 +3611,16 @@ void PropertyEditor::edit(Object *p_object) {
|
||||||
|
|
||||||
if (obj == p_object)
|
if (obj == p_object)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
obj = p_object;
|
||||||
|
|
||||||
if (obj) {
|
if (obj) {
|
||||||
|
|
||||||
obj->remove_change_receptor(this);
|
obj->remove_change_receptor(this);
|
||||||
}
|
|
||||||
|
|
||||||
obj = p_object;
|
if (obj->is_type("ScriptEditorDebuggerInspectedObject"))
|
||||||
|
set_enable_capitalize_paths(false);
|
||||||
|
}
|
||||||
|
|
||||||
evaluator->edit(p_object);
|
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) {
|
} else if (t == Variant::OBJECT) {
|
||||||
|
|
||||||
RES r = obj->get(n);
|
Variant var = obj->get(n);
|
||||||
if (r.is_valid()) {
|
|
||||||
|
|
||||||
|
RES r = var;
|
||||||
|
if (r.is_valid()) {
|
||||||
emit_signal("resource_selected", r, n);
|
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) {
|
} else if (t == Variant::INT && h == PROPERTY_HINT_OBJECT_ID) {
|
||||||
|
|
||||||
emit_signal("object_id_selected", obj->get(n));
|
ObjectID id = _get_curent_remote_object_id(n);
|
||||||
print_line("OBJ ID SELECTED");
|
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) {
|
} 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));
|
ape->edit(obj, n, Variant::Type(t));
|
||||||
|
|
||||||
EditorNode::get_singleton()->push_item(ape.ptr());
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 _resource_preview_done(const String &p_path, const Ref<Texture> &p_preview, Variant p_ud);
|
||||||
void _draw_transparency(Object *t, const Rect2 &p_rect);
|
void _draw_transparency(Object *t, const Rect2 &p_rect);
|
||||||
|
|
||||||
|
ObjectID _get_curent_remote_object_id(const StringName &p_name);
|
||||||
|
|
||||||
UndoRedo *undo_redo;
|
UndoRedo *undo_redo;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
#include "script_editor_debugger.h"
|
#include "script_editor_debugger.h"
|
||||||
|
#include "core/io/marshalls.h"
|
||||||
#include "editor_node.h"
|
#include "editor_node.h"
|
||||||
#include "editor_profiler.h"
|
#include "editor_profiler.h"
|
||||||
#include "editor_settings.h"
|
#include "editor_settings.h"
|
||||||
|
@ -55,9 +56,17 @@ class ScriptEditorDebuggerVariables : public Object {
|
||||||
protected:
|
protected:
|
||||||
bool _set(const StringName &p_name, const Variant &p_value) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _get(const StringName &p_name, Variant &r_ret) const {
|
bool _get(const StringName &p_name, Variant &r_ret) const {
|
||||||
|
|
||||||
if (!values.has(p_name))
|
if (!values.has(p_name))
|
||||||
|
@ -67,10 +76,19 @@ protected:
|
||||||
}
|
}
|
||||||
void _get_property_list(List<PropertyInfo> *p_list) const {
|
void _get_property_list(List<PropertyInfo> *p_list) const {
|
||||||
|
|
||||||
|
p_list->clear();
|
||||||
for (const List<PropertyInfo>::Element *E = props.front(); E; E = E->next())
|
for (const List<PropertyInfo>::Element *E = props.front(); E; E = E->next())
|
||||||
p_list->push_back(E->get());
|
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:
|
public:
|
||||||
void clear() {
|
void clear() {
|
||||||
|
|
||||||
|
@ -78,34 +96,30 @@ public:
|
||||||
values.clear();
|
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()) {
|
Variant var;
|
||||||
String v = E->key().operator String().get_slice("/", 1);
|
if (values.has(p_var))
|
||||||
if (v == p_var)
|
var = values[p_var];
|
||||||
return E->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
props.push_back(p_info);
|
||||||
pinfo.name = p_name;
|
values[p_info.name] = p_value;
|
||||||
pinfo.type = p_value.get_type();
|
|
||||||
pinfo.hint = p_hint;
|
|
||||||
pinfo.hint_string = p_hint_string;
|
|
||||||
props.push_back(pinfo);
|
|
||||||
values[p_name] = p_value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void update() {
|
void update() {
|
||||||
_change_notify();
|
_change_notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptEditorDebuggerVariables() {
|
void update_single(const char *p_prop) {
|
||||||
|
_change_notify(p_prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScriptEditorDebuggerVariables() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class ScriptEditorDebuggerInspectedObject : public Object {
|
class ScriptEditorDebuggerInspectedObject : public Object {
|
||||||
|
@ -115,14 +129,13 @@ class ScriptEditorDebuggerInspectedObject : public Object {
|
||||||
protected:
|
protected:
|
||||||
bool _set(const StringName &p_name, const Variant &p_value) {
|
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;
|
return false;
|
||||||
|
|
||||||
emit_signal("value_edited", p_name, p_value);
|
emit_signal("value_edited", p_name, p_value);
|
||||||
prop_values[p_name] = p_value;
|
prop_values[p_name] = p_value;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _get(const StringName &p_name, Variant &r_ret) const {
|
bool _get(const StringName &p_name, Variant &r_ret) const {
|
||||||
|
|
||||||
if (!prop_values.has(p_name))
|
if (!prop_values.has(p_name))
|
||||||
|
@ -141,23 +154,52 @@ protected:
|
||||||
|
|
||||||
static void _bind_methods() {
|
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"));
|
ADD_SIGNAL(MethodInfo("value_edited"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ObjectID last_edited_id;
|
ObjectID remote_object_id;
|
||||||
List<PropertyInfo> prop_list;
|
List<PropertyInfo> prop_list;
|
||||||
Map<StringName, Variant> prop_values;
|
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() {
|
void update() {
|
||||||
_change_notify();
|
_change_notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_single(const char *p_prop) {
|
void update_single(const char *p_prop) {
|
||||||
_change_notify(p_prop);
|
_change_notify(p_prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptEditorDebuggerInspectedObject() { last_edited_id = 0; }
|
ScriptEditorDebuggerInspectedObject() {
|
||||||
|
remote_object_id = 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void ScriptEditorDebugger::debug_next() {
|
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
|
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) {
|
void ScriptEditorDebugger::_scene_tree_property_select_object(ObjectID p_object) {
|
||||||
|
|
||||||
inspected_object_id = p_object;
|
inspected_object_id = p_object;
|
||||||
|
@ -293,10 +345,15 @@ Size2 ScriptEditorDebugger::get_minimum_size() const {
|
||||||
ms.y = MAX(ms.y, 250);
|
ms.y = MAX(ms.y, 250);
|
||||||
return ms;
|
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) {
|
void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_data) {
|
||||||
|
|
||||||
if (p_msg == "debug_enter") {
|
if (p_msg == "debug_enter") {
|
||||||
|
|
||||||
|
_clear_remote_objects();
|
||||||
|
|
||||||
Array msg;
|
Array msg;
|
||||||
msg.push_back("get_stack_dump");
|
msg.push_back("get_stack_dump");
|
||||||
ppeer->put_var(msg);
|
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") {
|
} else if (p_msg == "debug_exit") {
|
||||||
|
|
||||||
|
_clear_remote_objects();
|
||||||
|
|
||||||
breaked = false;
|
breaked = false;
|
||||||
step->set_disabled(true);
|
step->set_disabled(true);
|
||||||
next->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);
|
le_set->set_disabled(false);
|
||||||
} else if (p_msg == "message:inspect_object") {
|
} else if (p_msg == "message:inspect_object") {
|
||||||
|
|
||||||
|
ScriptEditorDebuggerInspectedObject *debugObj = NULL;
|
||||||
|
|
||||||
ObjectID id = p_data[0];
|
ObjectID id = p_data[0];
|
||||||
String type = p_data[1];
|
if (remote_objects.has(id)) {
|
||||||
Variant path = p_data[2]; //what to do yet, i don't know
|
debugObj = remote_objects[id];
|
||||||
int prop_count = p_data[3];
|
} else {
|
||||||
|
debugObj = memnew(ScriptEditorDebuggerInspectedObject);
|
||||||
int idx = 4;
|
debugObj->remote_object_id = id;
|
||||||
|
debugObj->connect("value_edited", this, "_scene_tree_property_value_edited");
|
||||||
if (inspected_object->last_edited_id != id) {
|
|
||||||
inspected_object->prop_list.clear();
|
|
||||||
inspected_object->prop_values.clear();
|
|
||||||
}
|
}
|
||||||
|
debugObj->clear();
|
||||||
|
|
||||||
for (int i = 0; i < prop_count; i++) {
|
String title = String("Object ID: ") + itos(id);
|
||||||
|
debugObj->prop_list.push_back(PropertyInfo(Variant::STRING, title, PROPERTY_HINT_NONE, title, PROPERTY_USAGE_CATEGORY));
|
||||||
|
|
||||||
|
Array props = p_data[1];
|
||||||
|
for (int i = 0; i < props.size(); i++) {
|
||||||
|
|
||||||
PropertyInfo pinfo;
|
PropertyInfo pinfo;
|
||||||
pinfo.name = p_data[idx++];
|
int len = 0;
|
||||||
pinfo.type = Variant::Type(int(p_data[idx++]));
|
Variant value = _unserialize_variant(props[i], pinfo, len);
|
||||||
pinfo.hint = PropertyHint(int(p_data[idx++]));
|
|
||||||
pinfo.hint_string = p_data[idx++];
|
if (value.get_type() == Variant::STRING && pinfo.hint_string == "REMOTE:RES") {
|
||||||
if (pinfo.name.begins_with("*")) {
|
|
||||||
pinfo.name = pinfo.name.substr(1, pinfo.name.length());
|
pinfo.type = Variant::OBJECT;
|
||||||
pinfo.usage = PROPERTY_USAGE_CATEGORY;
|
pinfo.hint_string = "";
|
||||||
|
RES res = ResourceLoader::load(value);
|
||||||
|
debugObj->prop_list.push_back(pinfo);
|
||||||
|
debugObj->prop_values[pinfo.name] = ResourceLoader::load(value);
|
||||||
} else {
|
} else {
|
||||||
pinfo.usage = PROPERTY_USAGE_EDITOR;
|
|
||||||
|
debugObj->prop_list.push_back(pinfo);
|
||||||
|
debugObj->prop_values[pinfo.name] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inspected_object->last_edited_id != id) {
|
debugObj->update_single(pinfo.name.ascii().get_data());
|
||||||
//don't update.. it's the same, instead refresh
|
|
||||||
inspected_object->prop_list.push_back(pinfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
inspected_object->prop_values[pinfo.name] = p_data[idx++];
|
|
||||||
|
|
||||||
if (inspected_object->last_edited_id == id) {
|
|
||||||
//same, just update value, don't rebuild
|
|
||||||
inspected_object->update_single(pinfo.name.ascii().get_data());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inspected_object->last_edited_id != id) {
|
debugObj->update();
|
||||||
//only if different
|
editor->push_item(debugObj, "");
|
||||||
inspected_object->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
inspected_object->last_edited_id = id;
|
|
||||||
|
|
||||||
if (tabs->get_current_tab() == 2) {
|
|
||||||
inspect_properties->edit(inspected_object);
|
|
||||||
} else {
|
|
||||||
editor->push_item(inspected_object);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (p_msg == "message:video_mem") {
|
} 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") {
|
} else if (p_msg == "stack_dump") {
|
||||||
|
|
||||||
stack_dump->clear();
|
stack_dump->clear();
|
||||||
|
_clear_remote_objects();
|
||||||
|
|
||||||
TreeItem *r = stack_dump->create_item();
|
TreeItem *r = stack_dump->create_item();
|
||||||
|
|
||||||
for (int i = 0; i < p_data.size(); i++) {
|
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") {
|
} else if (p_msg == "stack_frame_vars") {
|
||||||
|
|
||||||
variables->clear();
|
variables->clear();
|
||||||
|
_clear_remote_objects();
|
||||||
|
|
||||||
int ofs = 0;
|
for (int i = 0; i < p_data.size(); i++) {
|
||||||
int mcount = p_data[ofs];
|
|
||||||
|
|
||||||
ofs++;
|
PropertyInfo pinfo;
|
||||||
for (int i = 0; i < mcount; i++) {
|
int len = 0;
|
||||||
|
Variant value = _unserialize_variant(p_data[i], pinfo, len);
|
||||||
String n = p_data[ofs + i * 2 + 0];
|
variables->add_property(pinfo, value);
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
variables->add_property("locals/" + n, v, h, hs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
variables->update();
|
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) {
|
void ScriptEditorDebugger::_performance_select(Object *, int, bool) {
|
||||||
|
|
||||||
perf_draw->update();
|
perf_draw->update();
|
||||||
|
@ -986,7 +1120,7 @@ void ScriptEditorDebugger::_notification(int p_what) {
|
||||||
ERR_FAIL_COND(ret != OK);
|
ERR_FAIL_COND(ret != OK);
|
||||||
}
|
}
|
||||||
if (cmd.get_type() != Variant::STRING) {
|
if (cmd.get_type() != Variant::STRING) {
|
||||||
stop();
|
//stop();
|
||||||
ERR_FAIL_COND(cmd.get_type() != Variant::STRING);
|
ERR_FAIL_COND(cmd.get_type() != Variant::STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -995,11 +1129,11 @@ void ScriptEditorDebugger::_notification(int p_what) {
|
||||||
|
|
||||||
ret = ppeer->get_var(cmd);
|
ret = ppeer->get_var(cmd);
|
||||||
if (ret != OK) {
|
if (ret != OK) {
|
||||||
stop();
|
//stop();
|
||||||
ERR_FAIL_COND(ret != OK);
|
ERR_FAIL_COND(ret != OK);
|
||||||
}
|
}
|
||||||
if (cmd.get_type() != Variant::INT) {
|
if (cmd.get_type() != Variant::INT) {
|
||||||
stop();
|
//stop();
|
||||||
ERR_FAIL_COND(cmd.get_type() != Variant::INT);
|
ERR_FAIL_COND(cmd.get_type() != Variant::INT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1053,6 +1187,8 @@ void ScriptEditorDebugger::stop() {
|
||||||
|
|
||||||
set_process(false);
|
set_process(false);
|
||||||
|
|
||||||
|
_clear_remote_objects();
|
||||||
|
|
||||||
server->stop();
|
server->stop();
|
||||||
|
|
||||||
ppeer->set_stream_peer(Ref<StreamPeer>());
|
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() {
|
void ScriptEditorDebugger::_bind_methods() {
|
||||||
|
|
||||||
ObjectTypeDB::bind_method(_MD("_stack_dump_frame_selected"), &ScriptEditorDebugger::_stack_dump_frame_selected);
|
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("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_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_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("goto_script_line"));
|
||||||
ADD_SIGNAL(MethodInfo("breaked", PropertyInfo(Variant::BOOL, "reallydid"), PropertyInfo(Variant::BOOL, "can_debug")));
|
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) {
|
ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
|
||||||
|
|
||||||
ppeer = Ref<PacketPeerStream>(memnew(PacketPeerStream));
|
ppeer = Ref<PacketPeerStream>(memnew(PacketPeerStream));
|
||||||
|
ppeer->set_input_buffer_max_size(pow(2, 20));
|
||||||
|
|
||||||
editor = p_editor;
|
editor = p_editor;
|
||||||
|
editor->get_property_editor()->connect("object_id_selected", this, "_scene_tree_property_select_object");
|
||||||
|
|
||||||
tabs = memnew(TabContainer);
|
tabs = memnew(TabContainer);
|
||||||
tabs->set_v_size_flags(SIZE_EXPAND_FILL);
|
tabs->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||||
|
@ -1660,6 +1818,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
|
||||||
pending_in_queue = 0;
|
pending_in_queue = 0;
|
||||||
|
|
||||||
variables = memnew(ScriptEditorDebuggerVariables);
|
variables = memnew(ScriptEditorDebuggerVariables);
|
||||||
|
variables->connect("value_edited", this, "_scene_tree_variable_value_edited");
|
||||||
|
|
||||||
breaked = false;
|
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("cell_selected", this, "_scene_tree_selected");
|
||||||
inspect_scene_tree->connect("item_collapsed", this, "_scene_tree_folded");
|
inspect_scene_tree->connect("item_collapsed", this, "_scene_tree_folded");
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
VBoxContainer *info_right = memnew(VBoxContainer);
|
VBoxContainer *info_right = memnew(VBoxContainer);
|
||||||
info_right->set_h_size_flags(SIZE_EXPAND_FILL);
|
info_right->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||||
inspect_info->add_child(info_right);
|
inspect_info->add_child(info_right);
|
||||||
|
|
||||||
inspect_properties = memnew(PropertyEditor);
|
inspect_properties = memnew(PropertyEditor);
|
||||||
inspect_properties->hide_top_label();
|
//inspect_properties->hide_top_label();
|
||||||
inspect_properties->set_show_categories(true);
|
inspect_properties->set_show_categories(true);
|
||||||
inspect_properties->connect("object_id_selected", this, "_scene_tree_property_select_object");
|
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);
|
inspect_edited_object_timeout = EDITOR_DEF("debugger/remote_inspect_refresh_interval", 0.2);
|
||||||
inspected_object_id = 0;
|
inspected_object_id = 0;
|
||||||
updating_scene_tree = false;
|
updating_scene_tree = false;
|
||||||
|
|
||||||
inspected_object = memnew(ScriptEditorDebuggerInspectedObject);
|
|
||||||
inspected_object->connect("value_edited", this, "_scene_tree_property_value_edited");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{ //profiler
|
{ //profiler
|
||||||
|
@ -1874,5 +2028,5 @@ ScriptEditorDebugger::~ScriptEditorDebugger() {
|
||||||
ppeer->set_stream_peer(Ref<StreamPeer>());
|
ppeer->set_stream_peer(Ref<StreamPeer>());
|
||||||
|
|
||||||
server->stop();
|
server->stop();
|
||||||
memdelete(inspected_object);
|
_clear_remote_objects();
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ class ScriptEditorDebugger : public Control {
|
||||||
float inspect_scene_tree_timeout;
|
float inspect_scene_tree_timeout;
|
||||||
float inspect_edited_object_timeout;
|
float inspect_edited_object_timeout;
|
||||||
ObjectID inspected_object_id;
|
ObjectID inspected_object_id;
|
||||||
ScriptEditorDebuggerInspectedObject *inspected_object;
|
Map<ObjectID, ScriptEditorDebuggerInspectedObject *> remote_objects;
|
||||||
bool updating_scene_tree;
|
bool updating_scene_tree;
|
||||||
Set<ObjectID> unfold_cache;
|
Set<ObjectID> unfold_cache;
|
||||||
|
|
||||||
|
@ -142,9 +142,10 @@ class ScriptEditorDebugger : public Control {
|
||||||
void _scene_tree_selected();
|
void _scene_tree_selected();
|
||||||
void _scene_tree_request();
|
void _scene_tree_request();
|
||||||
void _parse_message(const String &p_msg, const Array &p_data);
|
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_select_object(ObjectID p_object);
|
||||||
void _scene_tree_property_value_edited(const String &p_prop, const Variant &p_value);
|
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();
|
void _video_mem_request();
|
||||||
|
|
||||||
int _get_node_path_cache(const NodePath &p_path);
|
int _get_node_path_cache(const NodePath &p_path);
|
||||||
|
@ -168,6 +169,9 @@ class ScriptEditorDebugger : public Control {
|
||||||
|
|
||||||
void _paused();
|
void _paused();
|
||||||
|
|
||||||
|
void _set_remote_object(ObjectID p_id, ScriptEditorDebuggerInspectedObject *p_obj);
|
||||||
|
void _clear_remote_objects();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void _notification(int p_what);
|
void _notification(int p_what);
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
#include "gd_compiler.h"
|
#include "gd_compiler.h"
|
||||||
#include "gd_script.h"
|
#include "gd_script.h"
|
||||||
|
#include "global_constants.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "os/file_access.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));
|
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) {
|
String GDScriptLanguage::debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) {
|
||||||
|
|
||||||
|
|
|
@ -138,8 +138,8 @@ public:
|
||||||
bool is_valid() const { return valid; }
|
bool is_valid() const { return valid; }
|
||||||
|
|
||||||
const Map<StringName, Ref<GDScript> > &get_subclasses() const { return subclasses; }
|
const Map<StringName, Ref<GDScript> > &get_subclasses() const { return subclasses; }
|
||||||
const Map<StringName, Variant> &get_constants() const { return constants; }
|
virtual const Map<StringName, Variant> &get_constants() const { return constants; }
|
||||||
const Set<StringName> &get_members() const { return members; }
|
virtual const Set<StringName> &get_members() const { return members; }
|
||||||
const Map<StringName, GDFunction *> &get_member_functions() const { return member_functions; }
|
const Map<StringName, GDFunction *> &get_member_functions() const { return member_functions; }
|
||||||
const Ref<GDNativeClass> &get_native() const { return native; }
|
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);
|
void _ml_call_reversed(GDScript *sptr, const StringName &p_method, const Variant **p_args, int p_argcount);
|
||||||
|
|
||||||
public:
|
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 set(const StringName &p_name, const Variant &p_value);
|
||||||
virtual bool get(const StringName &p_name, Variant &r_ret) const;
|
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 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_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_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 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();
|
virtual void reload_all_scripts();
|
||||||
|
|
Loading…
Reference in New Issue