diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 8521acd2223..9881357ca6c 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -495,6 +495,13 @@ Fetches a node by [NodePath]. Similar to [method get_node], but does not generate an error if [param path] does not point to a valid node. + + + + Returns object IDs of all orphan nodes (nodes outside the [SceneTree]). Used for debugging. + [b]Note:[/b] [method get_orphan_node_ids] only works in debug builds. When called in a project exported in release mode, [method get_orphan_node_ids] will return an empty array. + + diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 5217e682bf6..e50623d865b 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -3482,9 +3482,17 @@ static void _print_orphan_nodes_routine(Object *p_obj) { path = String(p->get_name()) + "/" + p->get_path_to(n); } + String source; + Variant script = n->get_script(); + if (!script.is_null()) { + Resource *obj = Object::cast_to(script); + source = obj->get_path(); + } + List info_strings; info_strings.push_back(path); info_strings.push_back(n->get_class()); + info_strings.push_back(source); _print_orphan_nodes_map[p_obj->get_instance_id()] = info_strings; } @@ -3499,13 +3507,31 @@ void Node::print_orphan_nodes() { ObjectDB::debug_objects(_print_orphan_nodes_routine); for (const KeyValue> &E : _print_orphan_nodes_map) { - print_line(itos(E.key) + " - Stray Node: " + E.value.get(0) + " (Type: " + E.value.get(1) + ")"); + print_line(itos(E.key) + " - Stray Node: " + E.value.get(0) + " (Type: " + E.value.get(1) + ") (Source:" + E.value.get(2) + ")"); } // Flush it after use. _print_orphan_nodes_map.clear(); #endif } +TypedArray Node::get_orphan_node_ids() { + TypedArray ret; +#ifdef DEBUG_ENABLED + // Make sure it's empty. + _print_orphan_nodes_map.clear(); + + // Collect and return information about orphan nodes. + ObjectDB::debug_objects(_print_orphan_nodes_routine); + + for (const KeyValue> &E : _print_orphan_nodes_map) { + ret.push_back(E.key); + } + + // Flush it after use. + _print_orphan_nodes_map.clear(); +#endif + return ret; +} void Node::queue_free() { // There are users which instantiate multiple scene trees for their games. @@ -3815,6 +3841,7 @@ void Node::_bind_methods() { GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/node_name_casing", PROPERTY_HINT_ENUM, "PascalCase,camelCase,snake_case,kebab-case"), NAME_CASING_PASCAL_CASE); ClassDB::bind_static_method("Node", D_METHOD("print_orphan_nodes"), &Node::print_orphan_nodes); + ClassDB::bind_static_method("Node", D_METHOD("get_orphan_node_ids"), &Node::get_orphan_node_ids); ClassDB::bind_method(D_METHOD("add_sibling", "sibling", "force_readable_name"), &Node::add_sibling, DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_name", "name"), &Node::set_name); diff --git a/scene/main/node.h b/scene/main/node.h index d7a0de24957..54ef13ed4e2 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -742,6 +742,7 @@ public: ProcessThreadGroup get_process_thread_group() const; static void print_orphan_nodes(); + static TypedArray get_orphan_node_ids(); #ifdef TOOLS_ENABLED String validate_child_name(Node *p_child);