Path: blob/master/scene/debugger/scene_debugger_object.cpp
20934 views
/**************************************************************************/1/* scene_debugger_object.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#ifdef DEBUG_ENABLED3132#include "scene_debugger_object.h"3334#include "core/io/marshalls.h"35#include "core/object/script_language.h"3637SceneDebuggerObject::SceneDebuggerObject(Object *p_obj) {38if (!p_obj) {39return;40}4142id = p_obj->get_instance_id();43class_name = p_obj->get_class();4445if (ScriptInstance *si = p_obj->get_script_instance()) {46// Read script instance constants and variables.47if (!si->get_script().is_null()) {48Script *s = si->get_script().ptr();49_parse_script_properties(s, si);50}51}5253if (Node *node = Object::cast_to<Node>(p_obj)) {54// For debugging multiplayer.55{56PropertyInfo pi(Variant::INT, String("Node/multiplayer_authority"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY);57properties.push_back(SceneDebuggerProperty(pi, node->get_multiplayer_authority()));58}5960// Add specialized NodePath info (if inside tree).61if (node->is_inside_tree()) {62PropertyInfo pi(Variant::NODE_PATH, String("Node/path"));63properties.push_back(SceneDebuggerProperty(pi, node->get_path()));64} else { // Can't ask for path if a node is not in tree.65PropertyInfo pi(Variant::STRING, String("Node/path"));66properties.push_back(SceneDebuggerProperty(pi, "[Orphan]"));67}68} else if (Script *s = Object::cast_to<Script>(p_obj)) {69// Add script constants (no instance).70_parse_script_properties(s, nullptr);71}7273// Add base object properties.74List<PropertyInfo> pinfo;75p_obj->get_property_list(&pinfo, true);76for (const PropertyInfo &E : pinfo) {77if (E.usage & (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CATEGORY)) {78properties.push_back(SceneDebuggerProperty(E, p_obj->get(E.name)));79}80}81}8283void SceneDebuggerObject::_parse_script_properties(Script *p_script, ScriptInstance *p_instance) {84typedef HashMap<const Script *, HashSet<StringName>> ScriptMemberMap;85typedef HashMap<const Script *, HashMap<StringName, Variant>> ScriptConstantsMap;8687ScriptMemberMap members;88if (p_instance) {89members[p_script] = HashSet<StringName>();90p_script->get_members(&(members[p_script]));91}9293ScriptConstantsMap constants;94constants[p_script] = HashMap<StringName, Variant>();95p_script->get_constants(&(constants[p_script]));9697Ref<Script> base = p_script->get_base_script();98while (base.is_valid()) {99if (p_instance) {100members[base.ptr()] = HashSet<StringName>();101base->get_members(&(members[base.ptr()]));102}103104constants[base.ptr()] = HashMap<StringName, Variant>();105base->get_constants(&(constants[base.ptr()]));106107base = base->get_base_script();108}109110HashSet<String> exported_members;111HashMap<String, PropertyInfo> non_exported_members;112113if (p_instance) {114List<PropertyInfo> pinfo;115p_instance->get_property_list(&pinfo);116for (const PropertyInfo &E : pinfo) {117if (E.usage & (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CATEGORY)) {118exported_members.insert(E.name);119} else {120PropertyInfo pi = E;121pi.usage |= PROPERTY_USAGE_EDITOR;122non_exported_members.insert(E.name, pi);123}124}125}126127// Members128for (KeyValue<const Script *, HashSet<StringName>> sm : members) {129for (const StringName &E : sm.value) {130if (exported_members.has(E)) {131continue; // Exported variables already show up in the inspector.132}133if (String(E).begins_with("@")) {134continue; // Skip groups.135}136137Variant m;138if (p_instance->get(E, m)) {139const String script_path = sm.key == p_script ? "" : sm.key->get_path().get_file() + "/";140141PropertyInfo pi;142const PropertyInfo *pi_ptr = non_exported_members.getptr(E);143if (pi_ptr == nullptr) {144pi.type = m.get_type();145} else {146pi = *pi_ptr;147}148pi.name = "Members/" + script_path + E;149150properties.push_back(SceneDebuggerProperty(pi, m));151}152}153}154// Constants155for (KeyValue<const Script *, HashMap<StringName, Variant>> &sc : constants) {156for (const KeyValue<StringName, Variant> &E : sc.value) {157String script_path = sc.key == p_script ? "" : sc.key->get_path().get_file() + "/";158if (E.value.get_type() == Variant::OBJECT) {159Variant inst_id = ((Object *)E.value)->get_instance_id();160PropertyInfo pi(inst_id.get_type(), "Constants/" + E.key, PROPERTY_HINT_OBJECT_ID, "Object");161properties.push_back(SceneDebuggerProperty(pi, inst_id));162} else {163PropertyInfo pi(E.value.get_type(), "Constants/" + script_path + E.key);164properties.push_back(SceneDebuggerProperty(pi, E.value));165}166}167}168}169170void SceneDebuggerObject::serialize(Array &r_arr, int p_max_size) {171Array send_props;172for (SceneDebuggerProperty &property : properties) {173const PropertyInfo &pi = property.first;174Variant &var = property.second;175176Ref<Resource> res = var;177178Array prop = { pi.name, pi.type };179PropertyHint hint = pi.hint;180String hint_string = pi.hint_string;181if (res.is_valid() && !res->get_path().is_empty()) {182var = res->get_path();183} else { //only send information that can be sent..184int len = 0; //test how big is this to encode185encode_variant(var, nullptr, len);186if (len > p_max_size) { //limit to max size187hint = PROPERTY_HINT_OBJECT_TOO_BIG;188hint_string = "";189var = Variant();190}191}192prop.push_back(hint);193prop.push_back(hint_string);194prop.push_back(pi.usage);195prop.push_back(var);196send_props.push_back(prop);197}198r_arr.push_back(uint64_t(id));199r_arr.push_back(class_name);200r_arr.push_back(send_props);201}202203#define CHECK_TYPE(p_what, p_type) ERR_FAIL_COND(p_what.get_type() != Variant::p_type)204205void SceneDebuggerObject::deserialize(const Array &p_arr) {206ERR_FAIL_COND(p_arr.size() < 3);207CHECK_TYPE(p_arr[0], INT);208CHECK_TYPE(p_arr[1], STRING);209CHECK_TYPE(p_arr[2], ARRAY);210211deserialize(uint64_t(p_arr[0]), p_arr[1], p_arr[2]);212}213214void SceneDebuggerObject::deserialize(uint64_t p_id, const String &p_class_name, const Array &p_props) {215id = p_id;216class_name = p_class_name;217218for (int i = 0; i < p_props.size(); i++) {219CHECK_TYPE(p_props[i], ARRAY);220Array prop = p_props[i];221222ERR_FAIL_COND(prop.size() != 6);223CHECK_TYPE(prop[0], STRING);224CHECK_TYPE(prop[1], INT);225CHECK_TYPE(prop[2], INT);226CHECK_TYPE(prop[3], STRING);227CHECK_TYPE(prop[4], INT);228229PropertyInfo pinfo;230pinfo.name = prop[0];231pinfo.type = Variant::Type(int(prop[1]));232pinfo.hint = PropertyHint(int(prop[2]));233pinfo.hint_string = prop[3];234pinfo.usage = PropertyUsageFlags(int(prop[4]));235Variant var = prop[5];236237if (pinfo.type == Variant::OBJECT) {238if (var.is_zero()) {239var = Ref<Resource>();240} else if (var.get_type() == Variant::OBJECT) {241if (((Object *)var)->is_class("EncodedObjectAsID")) {242var = Object::cast_to<EncodedObjectAsID>(var)->get_object_id();243pinfo.type = var.get_type();244pinfo.hint = PROPERTY_HINT_OBJECT_ID;245pinfo.hint_string = "Object";246}247}248}249properties.push_back(SceneDebuggerProperty(pinfo, var));250}251}252253SceneDebuggerTree::SceneDebuggerTree(Node *p_root) {254// Flatten tree into list, depth first, use stack to avoid recursion.255List<Node *> stack;256stack.push_back(p_root);257bool is_root = true;258const StringName &is_visible_sn = SNAME("is_visible");259const StringName &is_visible_in_tree_sn = SNAME("is_visible_in_tree");260while (stack.size()) {261Node *n = stack.front()->get();262stack.pop_front();263264int count = n->get_child_count();265for (int i = 0; i < count; i++) {266stack.push_front(n->get_child(count - i - 1));267}268269int view_flags = 0;270if (is_root) {271// Prevent root window visibility from being changed.272is_root = false;273} else if (n->has_method(is_visible_sn)) {274const Variant visible = n->call(is_visible_sn);275if (visible.get_type() == Variant::BOOL) {276view_flags = RemoteNode::VIEW_HAS_VISIBLE_METHOD;277view_flags |= uint8_t(visible) * RemoteNode::VIEW_VISIBLE;278}279if (n->has_method(is_visible_in_tree_sn)) {280const Variant visible_in_tree = n->call(is_visible_in_tree_sn);281if (visible_in_tree.get_type() == Variant::BOOL) {282view_flags |= uint8_t(visible_in_tree) * RemoteNode::VIEW_VISIBLE_IN_TREE;283}284}285}286287String class_name;288ScriptInstance *script_instance = n->get_script_instance();289if (script_instance) {290Ref<Script> script = script_instance->get_script();291if (script.is_valid()) {292class_name = script->get_global_name();293294if (class_name.is_empty()) {295// If there is no class_name in this script we just take the script path.296class_name = script->get_path();297}298}299}300nodes.push_back(RemoteNode(count, n->get_name(), class_name.is_empty() ? n->get_class() : class_name, n->get_instance_id(), n->get_scene_file_path(), view_flags));301}302}303304void SceneDebuggerTree::serialize(Array &p_arr) {305for (const RemoteNode &n : nodes) {306p_arr.push_back(n.child_count);307p_arr.push_back(n.name);308p_arr.push_back(n.type_name);309p_arr.push_back(n.id);310p_arr.push_back(n.scene_file_path);311p_arr.push_back(n.view_flags);312}313}314315void SceneDebuggerTree::deserialize(const Array &p_arr) {316int idx = 0;317while (p_arr.size() > idx) {318ERR_FAIL_COND(p_arr.size() < 6);319CHECK_TYPE(p_arr[idx], INT); // child_count.320CHECK_TYPE(p_arr[idx + 1], STRING); // name.321CHECK_TYPE(p_arr[idx + 2], STRING); // type_name.322CHECK_TYPE(p_arr[idx + 3], INT); // id.323CHECK_TYPE(p_arr[idx + 4], STRING); // scene_file_path.324CHECK_TYPE(p_arr[idx + 5], INT); // view_flags.325nodes.push_back(RemoteNode(p_arr[idx], p_arr[idx + 1], p_arr[idx + 2], p_arr[idx + 3], p_arr[idx + 4], p_arr[idx + 5]));326idx += 6;327}328}329330#undef CHECK_TYPE331332#endif // DEBUG_ENABLED333334335