diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 3ef7b46a1db..b27123da067 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -166,7 +166,7 @@ void Node::_notification(int p_notification) { // kill children as cleanly as possible while (data.children.size()) { - Node *child = data.children[data.children.size() - 1]; //begin from the end because its faster and more consistent with creation + Node *child = data.children.last()->value; // begin from the end because its faster and more consistent with creation memdelete(child); } } break; @@ -176,9 +176,10 @@ void Node::_notification(int p_notification) { void Node::_propagate_ready() { data.ready_notified = true; data.blocked++; - for (int i = 0; i < data.children.size(); i++) { - data.children[i]->_propagate_ready(); + for (KeyValue &K : data.children) { + K.value->_propagate_ready(); } + data.blocked--; notification(NOTIFICATION_POST_ENTER_TREE); @@ -228,9 +229,9 @@ void Node::_propagate_enter_tree() { data.blocked++; //block while adding children - for (int i = 0; i < data.children.size(); i++) { - if (!data.children[i]->is_inside_tree()) { // could have been added in enter_tree - data.children[i]->_propagate_enter_tree(); + for (KeyValue &K : data.children) { + if (!K.value->is_inside_tree()) { // could have been added in enter_tree + K.value->_propagate_enter_tree(); } } @@ -267,9 +268,11 @@ void Node::_propagate_after_exit_tree() { } data.blocked++; - for (int i = data.children.size() - 1; i >= 0; i--) { - data.children[i]->_propagate_after_exit_tree(); + + for (HashMap::Iterator I = data.children.last(); I; --I) { + I->value->_propagate_after_exit_tree(); } + data.blocked--; emit_signal(SceneStringNames::get_singleton()->tree_exited); @@ -286,8 +289,8 @@ void Node::_propagate_exit_tree() { #endif data.blocked++; - for (int i = data.children.size() - 1; i >= 0; i--) { - data.children[i]->_propagate_exit_tree(); + for (HashMap::Iterator I = data.children.last(); I; --I) { + I->value->_propagate_exit_tree(); } data.blocked--; @@ -329,25 +332,26 @@ void Node::move_child(Node *p_child, int p_index) { ERR_FAIL_NULL(p_child); ERR_FAIL_COND_MSG(p_child->data.parent != this, "Child is not a child of this node."); + _update_children_cache(); // We need to check whether node is internal and move it only in the relevant node range. - if (p_child->_is_internal_front()) { + if (p_child->data.internal_mode == INTERNAL_MODE_FRONT) { if (p_index < 0) { - p_index += data.internal_children_front; + p_index += data.internal_children_front_count_cache; } - ERR_FAIL_INDEX_MSG(p_index, data.internal_children_front, vformat("Invalid new child index: %d. Child is internal.", p_index)); + ERR_FAIL_INDEX_MSG(p_index, data.internal_children_front_count_cache, vformat("Invalid new child index: %d. Child is internal.", p_index)); _move_child(p_child, p_index); - } else if (p_child->_is_internal_back()) { + } else if (p_child->data.internal_mode == INTERNAL_MODE_BACK) { if (p_index < 0) { - p_index += data.internal_children_back; + p_index += data.internal_children_back_count_cache; } - ERR_FAIL_INDEX_MSG(p_index, data.internal_children_back, vformat("Invalid new child index: %d. Child is internal.", p_index)); - _move_child(p_child, data.children.size() - data.internal_children_back + p_index); + ERR_FAIL_INDEX_MSG(p_index, data.internal_children_back_count_cache, vformat("Invalid new child index: %d. Child is internal.", p_index)); + _move_child(p_child, (int)data.children_cache.size() - data.internal_children_back_count_cache + p_index); } else { if (p_index < 0) { p_index += get_child_count(false); } - ERR_FAIL_INDEX_MSG(p_index, data.children.size() + 1 - data.internal_children_front - data.internal_children_back, vformat("Invalid new child index: %d.", p_index)); - _move_child(p_child, p_index + data.internal_children_front); + ERR_FAIL_INDEX_MSG(p_index, (int)data.children_cache.size() + 1 - data.internal_children_front_count_cache - data.internal_children_back_count_cache, vformat("Invalid new child index: %d.", p_index)); + _move_child(p_child, p_index + data.internal_children_front_count_cache); } } @@ -357,30 +361,32 @@ void Node::_move_child(Node *p_child, int p_index, bool p_ignore_end) { // Specifying one place beyond the end // means the same as moving to the last index if (!p_ignore_end) { // p_ignore_end is a little hack to make back internal children work properly. - if (p_child->_is_internal_front()) { - if (p_index == data.internal_children_front) { + if (p_child->data.internal_mode == INTERNAL_MODE_FRONT) { + if (p_index == data.internal_children_front_count_cache) { p_index--; } - } else if (p_child->_is_internal_back()) { - if (p_index == data.children.size()) { + } else if (p_child->data.internal_mode == INTERNAL_MODE_BACK) { + if (p_index == (int)data.children_cache.size()) { p_index--; } } else { - if (p_index == data.children.size() - data.internal_children_back) { + if (p_index == (int)data.children_cache.size() - data.internal_children_back_count_cache) { p_index--; } } } - if (p_child->data.index == p_index) { + int child_index = p_child->get_index(); + + if (child_index == p_index) { return; //do nothing } - int motion_from = MIN(p_index, p_child->data.index); - int motion_to = MAX(p_index, p_child->data.index); + int motion_from = MIN(p_index, child_index); + int motion_to = MAX(p_index, child_index); - data.children.remove_at(p_child->data.index); - data.children.insert(p_index, p_child); + data.children_cache.remove_at(child_index); + data.children_cache.insert(p_index, p_child); if (data.tree) { data.tree->tree_changed(); @@ -389,13 +395,18 @@ void Node::_move_child(Node *p_child, int p_index, bool p_ignore_end) { data.blocked++; //new pos first for (int i = motion_from; i <= motion_to; i++) { - data.children[i]->data.index = i; + if (data.children_cache[i]->data.internal_mode == INTERNAL_MODE_DISABLED) { + data.children_cache[i]->data.index = i - data.internal_children_front_count_cache; + } else if (data.children_cache[i]->data.internal_mode == INTERNAL_MODE_BACK) { + data.children_cache[i]->data.index = i - data.internal_children_front_count_cache - data.external_children_count_cache; + } else { + data.children_cache[i]->data.index = i; + } } // notification second move_child_notify(p_child); notification(NOTIFICATION_CHILD_ORDER_CHANGED); emit_signal(SNAME("child_order_changed")); - p_child->_propagate_groups_dirty(); data.blocked--; @@ -408,8 +419,8 @@ void Node::_propagate_groups_dirty() { } } - for (int i = 0; i < data.children.size(); i++) { - data.children[i]->_propagate_groups_dirty(); + for (KeyValue &K : data.children) { + K.value->_propagate_groups_dirty(); } } @@ -529,8 +540,8 @@ void Node::_propagate_pause_notification(bool p_enable) { notification(NOTIFICATION_UNPAUSED); } - for (int i = 0; i < data.children.size(); i++) { - data.children[i]->_propagate_pause_notification(p_enable); + for (KeyValue &K : data.children) { + K.value->_propagate_pause_notification(p_enable); } } @@ -549,8 +560,8 @@ void Node::_propagate_process_owner(Node *p_owner, int p_pause_notification, int notification(p_enabled_notification); } - for (int i = 0; i < data.children.size(); i++) { - Node *c = data.children[i]; + for (KeyValue &K : data.children) { + Node *c = K.value; if (c->data.process_mode == PROCESS_MODE_INHERIT) { c->_propagate_process_owner(p_owner, p_pause_notification, p_enabled_notification); } @@ -561,8 +572,8 @@ void Node::set_multiplayer_authority(int p_peer_id, bool p_recursive) { data.multiplayer_authority = p_peer_id; if (p_recursive) { - for (int i = 0; i < data.children.size(); i++) { - data.children[i]->set_multiplayer_authority(p_peer_id, true); + for (KeyValue &K : data.children) { + K.value->set_multiplayer_authority(p_peer_id, true); } } } @@ -912,10 +923,13 @@ void Node::set_name(const String &p_name) { if (data.unique_name_in_owner && data.owner) { _release_unique_name_in_owner(); } + String old_name = data.name; data.name = name; if (data.parent) { + data.parent->data.children.erase(old_name); data.parent->_validate_child_name(this, true); + data.parent->data.children.insert(data.name, this); } if (data.unique_name_in_owner && data.owner) { @@ -977,19 +991,8 @@ void Node::_validate_child_name(Node *p_child, bool p_force_human_readable) { //new unique name must be assigned unique = false; } else { - //check if exists - Node **children = data.children.ptrw(); - int cc = data.children.size(); - - for (int i = 0; i < cc; i++) { - if (children[i] == p_child) { - continue; - } - if (children[i]->data.name == p_child->data.name) { - unique = false; - break; - } - } + const Node *const *existing = data.children.getptr(p_child->data.name); + unique = !existing || *existing == p_child; } if (!unique) { @@ -1053,25 +1056,9 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co name = p_child->get_class(); } - //quickly test if proposed name exists - int cc = data.children.size(); //children count - const Node *const *children_ptr = data.children.ptr(); - - { - bool exists = false; - - for (int i = 0; i < cc; i++) { - if (children_ptr[i] == p_child) { //exclude self in renaming if it's already a child - continue; - } - if (children_ptr[i]->data.name == name) { - exists = true; - } - } - - if (!exists) { - return; //if it does not exist, it does not need validation - } + const Node *const *existing = data.children.getptr(name); + if (!existing || *existing == p_child) { // Unused, or is current node. + return; } // Extract trailing number @@ -1098,16 +1085,9 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co for (;;) { StringName attempt = name_string + nums; - bool exists = false; - for (int i = 0; i < cc; i++) { - if (children_ptr[i] == p_child) { - continue; - } - if (children_ptr[i]->data.name == attempt) { - exists = true; - } - } + existing = data.children.getptr(attempt); + bool exists = existing != nullptr && *existing != p_child; if (!exists) { name = attempt; @@ -1124,17 +1104,34 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co } } -void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) { +void Node::_add_child_nocheck(Node *p_child, const StringName &p_name, InternalMode p_internal_mode) { //add a child node quickly, without name validation p_child->data.name = p_name; - p_child->data.index = data.children.size(); - data.children.push_back(p_child); + data.children.insert(p_name, p_child); + + p_child->data.internal_mode = p_internal_mode; + switch (p_internal_mode) { + case INTERNAL_MODE_FRONT: { + p_child->data.index = data.internal_children_front_count_cache++; + } break; + case INTERNAL_MODE_BACK: { + p_child->data.index = data.internal_children_back_count_cache++; + } break; + case INTERNAL_MODE_DISABLED: { + p_child->data.index = data.external_children_count_cache++; + } break; + } + p_child->data.parent = this; - if (data.internal_children_back > 0) { - _move_child(p_child, data.children.size() - data.internal_children_back - 1); + if (!data.children_cache_dirty && p_internal_mode == INTERNAL_MODE_DISABLED && data.internal_children_back_count_cache == 0) { + // Special case, also add to the cached children array since its cheap. + data.children_cache.push_back(p_child); + } else { + data.children_cache_dirty = true; } + p_child->notification(NOTIFICATION_PARENTED); if (data.tree) { @@ -1159,17 +1156,7 @@ void Node::add_child(Node *p_child, bool p_force_readable_name, InternalMode p_i ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, `add_child()` failed. Consider using `add_child.call_deferred(child)` instead."); _validate_child_name(p_child, p_force_readable_name); - _add_child_nocheck(p_child, p_child->data.name); - - if (p_internal == INTERNAL_MODE_FRONT) { - _move_child(p_child, data.internal_children_front); - data.internal_children_front++; - } else if (p_internal == INTERNAL_MODE_BACK) { - if (data.internal_children_back > 0) { - _move_child(p_child, data.children.size() - 1, true); - } - data.internal_children_back++; - } + _add_child_nocheck(p_child, p_child->data.name, p_internal); } void Node::add_sibling(Node *p_sibling, bool p_force_readable_name) { @@ -1178,49 +1165,25 @@ void Node::add_sibling(Node *p_sibling, bool p_force_readable_name) { ERR_FAIL_COND_MSG(p_sibling == this, vformat("Can't add sibling '%s' to itself.", p_sibling->get_name())); // adding to itself! ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, `add_sibling()` failed. Consider using `add_sibling.call_deferred(sibling)` instead."); - InternalMode internal = INTERNAL_MODE_DISABLED; - if (_is_internal_front()) { // The sibling will have the same internal status. - internal = INTERNAL_MODE_FRONT; - } else if (_is_internal_back()) { - internal = INTERNAL_MODE_BACK; - } - - data.parent->add_child(p_sibling, p_force_readable_name, internal); + data.parent->add_child(p_sibling, p_force_readable_name, data.internal_mode); + data.parent->_update_children_cache(); data.parent->_move_child(p_sibling, get_index() + 1); } void Node::remove_child(Node *p_child) { ERR_FAIL_NULL(p_child); ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy adding/removing children, `remove_child()` can't be called at this time. Consider using `remove_child.call_deferred(child)` instead."); + ERR_FAIL_COND(p_child->data.parent != this); - int child_count = data.children.size(); - Node **children = data.children.ptrw(); - int idx = -1; - - if (p_child->data.index >= 0 && p_child->data.index < child_count) { - if (children[p_child->data.index] == p_child) { - idx = p_child->data.index; - } - } - - if (idx == -1) { //maybe removed while unparenting or something and index was not updated, so just in case the above fails, try this. - for (int i = 0; i < child_count; i++) { - if (children[i] == p_child) { - idx = i; - break; - } - } - } - - ERR_FAIL_COND_MSG(idx == -1, vformat("Cannot remove child node '%s' as it is not a child of this node.", p_child->get_name())); - //ERR_FAIL_COND( p_child->data.blocked > 0 ); - - // If internal child, update the counter. - if (p_child->_is_internal_front()) { - data.internal_children_front--; - } else if (p_child->_is_internal_back()) { - data.internal_children_back--; - } + /** + * Do not change the data.internal_children*cache counters here. + * Because if nodes are re-added, the indices can remain + * greater-than-everything indices and children added remain + * properly ordered. + * + * All children indices and counters will be updated next time the + * cache is re-generated. + */ data.blocked++; p_child->_set_tree(nullptr); @@ -1228,17 +1191,12 @@ void Node::remove_child(Node *p_child) { remove_child_notify(p_child); p_child->notification(NOTIFICATION_UNPARENTED); + data.blocked--; - data.children.remove_at(idx); - - //update pointer and size - child_count = data.children.size(); - children = data.children.ptrw(); - - for (int i = idx; i < child_count; i++) { - children[i]->data.index = i; - } + data.children_cache_dirty = true; + bool success = data.children.erase(p_child->data.name); + ERR_FAIL_COND_MSG(!success, "Children name does not match parent name in hashtable, this is a bug."); notification(NOTIFICATION_CHILD_ORDER_CHANGED); emit_signal(SNAME("child_order_changed")); @@ -1251,42 +1209,73 @@ void Node::remove_child(Node *p_child) { } } +void Node::_update_children_cache_impl() const { + // Assign children + data.children_cache.resize(data.children.size()); + int idx = 0; + for (const KeyValue &K : data.children) { + data.children_cache[idx] = K.value; + idx++; + } + // Sort them + data.children_cache.sort_custom(); + // Update indices + data.external_children_count_cache = 0; + data.internal_children_back_count_cache = 0; + data.internal_children_front_count_cache = 0; + + for (uint32_t i = 0; i < data.children_cache.size(); i++) { + switch (data.children_cache[i]->data.internal_mode) { + case INTERNAL_MODE_DISABLED: { + data.children_cache[i]->data.index = data.external_children_count_cache++; + } break; + case INTERNAL_MODE_FRONT: { + data.children_cache[i]->data.index = data.internal_children_front_count_cache++; + } break; + case INTERNAL_MODE_BACK: { + data.children_cache[i]->data.index = data.internal_children_back_count_cache++; + } break; + } + } + data.children_cache_dirty = false; +} + int Node::get_child_count(bool p_include_internal) const { + _update_children_cache(); + if (p_include_internal) { - return data.children.size(); + return data.children_cache.size(); } else { - return data.children.size() - data.internal_children_front - data.internal_children_back; + return data.children_cache.size() - data.internal_children_front_count_cache - data.internal_children_back_count_cache; } } Node *Node::get_child(int p_index, bool p_include_internal) const { + _update_children_cache(); + if (p_include_internal) { if (p_index < 0) { - p_index += data.children.size(); + p_index += data.children_cache.size(); } - ERR_FAIL_INDEX_V(p_index, data.children.size(), nullptr); - return data.children[p_index]; + ERR_FAIL_INDEX_V(p_index, (int)data.children_cache.size(), nullptr); + return data.children_cache[p_index]; } else { if (p_index < 0) { - p_index += data.children.size() - data.internal_children_front - data.internal_children_back; + p_index += (int)data.children_cache.size() - data.internal_children_front_count_cache - data.internal_children_back_count_cache; } - ERR_FAIL_INDEX_V(p_index, data.children.size() - data.internal_children_front - data.internal_children_back, nullptr); - p_index += data.internal_children_front; - return data.children[p_index]; + ERR_FAIL_INDEX_V(p_index, (int)data.children_cache.size() - data.internal_children_front_count_cache - data.internal_children_back_count_cache, nullptr); + p_index += data.internal_children_front_count_cache; + return data.children_cache[p_index]; } } Node *Node::_get_child_by_name(const StringName &p_name) const { - int cc = data.children.size(); - Node *const *cd = data.children.ptr(); - - for (int i = 0; i < cc; i++) { - if (cd[i]->data.name == p_name) { - return cd[i]; - } + const Node *const *node = data.children.getptr(p_name); + if (node) { + return const_cast(*node); + } else { + return nullptr; } - - return nullptr; } Node *Node::get_node_or_null(const NodePath &p_path) const { @@ -1348,18 +1337,12 @@ Node *Node::get_node_or_null(const NodePath &p_path) const { } else { next = nullptr; - - for (int j = 0; j < current->data.children.size(); j++) { - Node *child = current->data.children[j]; - - if (child->data.name == name) { - next = child; - break; - } - } - if (next == nullptr) { + const Node *const *node = current->data.children.getptr(name); + if (node) { + next = const_cast(*node); + } else { return nullptr; - }; + } } current = next; } @@ -1402,9 +1385,9 @@ bool Node::has_node(const NodePath &p_path) const { // Can be recursive or not, and limited to owned nodes. Node *Node::find_child(const String &p_pattern, bool p_recursive, bool p_owned) const { ERR_FAIL_COND_V(p_pattern.is_empty(), nullptr); - - Node *const *cptr = data.children.ptr(); - int ccount = data.children.size(); + _update_children_cache(); + Node *const *cptr = data.children_cache.ptr(); + int ccount = data.children_cache.size(); for (int i = 0; i < ccount; i++) { if (p_owned && !cptr[i]->data.owner) { continue; @@ -1431,9 +1414,9 @@ Node *Node::find_child(const String &p_pattern, bool p_recursive, bool p_owned) TypedArray Node::find_children(const String &p_pattern, const String &p_type, bool p_recursive, bool p_owned) const { TypedArray ret; ERR_FAIL_COND_V(p_pattern.is_empty() && p_type.is_empty(), ret); - - Node *const *cptr = data.children.ptr(); - int ccount = data.children.size(); + _update_children_cache(); + Node *const *cptr = data.children_cache.ptr(); + int ccount = data.children_cache.size(); for (int i = 0; i < ccount; i++) { if (p_owned && !cptr[i]->data.owner) { continue; @@ -1526,6 +1509,8 @@ bool Node::is_greater_than(const Node *p_node) const { ERR_FAIL_COND_V(data.depth < 0, false); ERR_FAIL_COND_V(p_node->data.depth < 0, false); + _update_children_cache(); + int *this_stack = (int *)alloca(sizeof(int) * data.depth); int *that_stack = (int *)alloca(sizeof(int) * p_node->data.depth); @@ -1534,15 +1519,16 @@ bool Node::is_greater_than(const Node *p_node) const { int idx = data.depth - 1; while (n) { ERR_FAIL_INDEX_V(idx, data.depth, false); - this_stack[idx--] = n->data.index; + this_stack[idx--] = n->get_index(); n = n->data.parent; } + ERR_FAIL_COND_V(idx != -1, false); n = p_node; idx = p_node->data.depth - 1; while (n) { ERR_FAIL_INDEX_V(idx, p_node->data.depth, false); - that_stack[idx--] = n->data.index; + that_stack[idx--] = n->get_index(); n = n->data.parent; } @@ -1576,8 +1562,8 @@ void Node::get_owned_by(Node *p_by, List *p_owned) { p_owned->push_back(this); } - for (int i = 0; i < get_child_count(); i++) { - get_child(i)->get_owned_by(p_by, p_owned); + for (KeyValue &K : data.children) { + K.value->get_owned_by(p_by, p_owned); } } @@ -1895,9 +1881,10 @@ int Node::get_persistent_group_count() const { void Node::_print_tree_pretty(const String &prefix, const bool last) { String new_prefix = last ? String::utf8(" ┖╴") : String::utf8(" ┠╴"); print_line(prefix + new_prefix + String(get_name())); - for (int i = 0; i < data.children.size(); i++) { + _update_children_cache(); + for (uint32_t i = 0; i < data.children_cache.size(); i++) { new_prefix = last ? String::utf8(" ") : String::utf8(" ┃ "); - data.children[i]->_print_tree_pretty(prefix + new_prefix, i == data.children.size() - 1); + data.children_cache[i]->_print_tree_pretty(prefix + new_prefix, i == data.children_cache.size() - 1); } } @@ -1911,15 +1898,17 @@ void Node::print_tree() { void Node::_print_tree(const Node *p_node) { print_line(String(p_node->get_path_to(this))); - for (int i = 0; i < data.children.size(); i++) { - data.children[i]->_print_tree(p_node); + _update_children_cache(); + for (uint32_t i = 0; i < data.children_cache.size(); i++) { + data.children_cache[i]->_print_tree(p_node); } } void Node::_propagate_reverse_notification(int p_notification) { data.blocked++; - for (int i = data.children.size() - 1; i >= 0; i--) { - data.children[i]->_propagate_reverse_notification(p_notification); + + for (HashMap::Iterator I = data.children.last(); I; --I) { + I->value->_propagate_reverse_notification(p_notification); } notification(p_notification, true); @@ -1935,8 +1924,8 @@ void Node::_propagate_deferred_notification(int p_notification, bool p_reverse) MessageQueue::get_singleton()->push_notification(this, p_notification); } - for (int i = 0; i < data.children.size(); i++) { - data.children[i]->_propagate_deferred_notification(p_notification, p_reverse); + for (KeyValue &K : data.children) { + K.value->_propagate_deferred_notification(p_notification, p_reverse); } if (p_reverse) { @@ -1950,8 +1939,8 @@ void Node::propagate_notification(int p_notification) { data.blocked++; notification(p_notification); - for (int i = 0; i < data.children.size(); i++) { - data.children[i]->propagate_notification(p_notification); + for (KeyValue &K : data.children) { + K.value->propagate_notification(p_notification); } data.blocked--; } @@ -1963,8 +1952,8 @@ void Node::propagate_call(const StringName &p_method, const Array &p_args, const callv(p_method, p_args); } - for (int i = 0; i < data.children.size(); i++) { - data.children[i]->propagate_call(p_method, p_args, p_parent_first); + for (KeyValue &K : data.children) { + K.value->propagate_call(p_method, p_args, p_parent_first); } if (!p_parent_first && has_method(p_method)) { @@ -1980,22 +1969,12 @@ void Node::_propagate_replace_owner(Node *p_owner, Node *p_by_owner) { } data.blocked++; - for (int i = 0; i < data.children.size(); i++) { - data.children[i]->_propagate_replace_owner(p_owner, p_by_owner); + for (KeyValue &K : data.children) { + K.value->_propagate_replace_owner(p_owner, p_by_owner); } data.blocked--; } -int Node::get_index(bool p_include_internal) const { - // p_include_internal = false doesn't make sense if the node is internal. - ERR_FAIL_COND_V_MSG(!p_include_internal && (_is_internal_front() || _is_internal_back()), -1, "Node is internal. Can't get index with 'include_internal' being false."); - - if (data.parent && !p_include_internal) { - return data.index - data.parent->data.internal_children_front; - } - return data.index; -} - Ref Node::create_tween() { ERR_FAIL_COND_V_MSG(!data.tree, nullptr, "Can't create Tween when not inside scene tree."); Ref tween = get_tree()->create_tween(); @@ -2502,7 +2481,7 @@ void Node::replace_by(Node *p_node, bool p_keep_groups) { } Node *parent = data.parent; - int index_in_parent = data.index; + int index_in_parent = get_index(); if (data.parent) { parent->remove_child(this); @@ -2754,8 +2733,8 @@ void Node::get_argument_options(const StringName &p_function, int p_idx, Listclear_internal_tree_resource_paths(); + for (KeyValue &K : data.children) { + K.value->clear_internal_tree_resource_paths(); } } @@ -3102,9 +3081,10 @@ Node::~Node() { data.grouped.clear(); data.owned.clear(); data.children.clear(); + data.children_cache.clear(); ERR_FAIL_COND(data.parent); - ERR_FAIL_COND(data.children.size()); + ERR_FAIL_COND(data.children_cache.size()); orphan_node_count--; } diff --git a/scene/main/node.h b/scene/main/node.h index 939632770e0..8b3c07b39bc 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -92,6 +92,18 @@ private: SceneTree::Group *group = nullptr; }; + struct ComparatorByIndex { + bool operator()(const Node *p_left, const Node *p_right) const { + static const uint32_t order[3] = { 1, 0, 2 }; + uint32_t order_left = order[p_left->data.internal_mode]; + uint32_t order_right = order[p_right->data.internal_mode]; + if (order_left == order_right) { + return p_left->data.index < p_right->data.index; + } + return order_left < order_right; + } + }; + // This Data struct is to avoid namespace pollution in derived classes. struct Data { String scene_file_path; @@ -100,13 +112,16 @@ private: Node *parent = nullptr; Node *owner = nullptr; - Vector children; + HashMap children; + mutable bool children_cache_dirty = true; + mutable LocalVector children_cache; HashMap owned_unique_nodes; bool unique_name_in_owner = false; - - int internal_children_front = 0; - int internal_children_back = 0; - int index = -1; + InternalMode internal_mode = INTERNAL_MODE_DISABLED; + mutable int internal_children_front_count_cache = 0; + mutable int internal_children_back_count_cache = 0; + mutable int external_children_count_cache = 0; + mutable int index = -1; // relative to front, normal or back. int depth = -1; int blocked = 0; // Safeguard that throws an error when attempting to modify the tree in a harmful way while being traversed. StringName name; @@ -187,9 +202,6 @@ private: Error _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); Error _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); - _FORCE_INLINE_ bool _is_internal_front() const { return data.parent && data.index < data.parent->data.internal_children_front; } - _FORCE_INLINE_ bool _is_internal_back() const { return data.parent && data.index >= data.parent->data.children.size() - data.parent->data.internal_children_back; } - friend class SceneTree; void _set_tree(SceneTree *p_tree); @@ -201,6 +213,14 @@ private: void _release_unique_name_in_owner(); void _acquire_unique_name_in_owner(); + _FORCE_INLINE_ void _update_children_cache() const { + if (unlikely(data.children_cache_dirty)) { + _update_children_cache_impl(); + } + } + + void _update_children_cache_impl() const; + protected: void _block() { data.blocked++; } void _unblock() { data.blocked--; } @@ -219,7 +239,7 @@ protected: friend class SceneState; - void _add_child_nocheck(Node *p_child, const StringName &p_name); + void _add_child_nocheck(Node *p_child, const StringName &p_name, InternalMode p_internal_mode = INTERNAL_MODE_DISABLED); void _set_owner_nocheck(Node *p_owner); void _set_name_nocheck(const StringName &p_name); @@ -361,7 +381,31 @@ public: void set_unique_name_in_owner(bool p_enabled); bool is_unique_name_in_owner() const; - int get_index(bool p_include_internal = true) const; + _FORCE_INLINE_ int get_index(bool p_include_internal = true) const { + // p_include_internal = false doesn't make sense if the node is internal. + ERR_FAIL_COND_V_MSG(!p_include_internal && data.internal_mode != INTERNAL_MODE_DISABLED, -1, "Node is internal. Can't get index with 'include_internal' being false."); + if (!data.parent) { + return data.index; + } + data.parent->_update_children_cache(); + + if (!p_include_internal) { + return data.index; + } else { + switch (data.internal_mode) { + case INTERNAL_MODE_DISABLED: { + return data.parent->data.internal_children_front_count_cache + data.index; + } break; + case INTERNAL_MODE_FRONT: { + return data.index; + } break; + case INTERNAL_MODE_BACK: { + return data.parent->data.internal_children_front_count_cache + data.parent->data.external_children_count_cache + data.index; + } break; + } + return -1; + } + } Ref create_tween();