Merge pull request #75627 from reduz/faster-node-child-management
Optimize Node children management
This commit is contained in:
commit
3683b040ed
2 changed files with 242 additions and 218 deletions
|
@ -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<StringName, Node *> &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<StringName, Node *> &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<StringName, Node *>::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<StringName, Node *>::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<StringName, Node *> &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<StringName, Node *> &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<StringName, Node *> &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<StringName, Node *> &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<StringName, Node *> &K : data.children) {
|
||||
data.children_cache[idx] = K.value;
|
||||
idx++;
|
||||
}
|
||||
// Sort them
|
||||
data.children_cache.sort_custom<ComparatorByIndex>();
|
||||
// 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 *>(*node);
|
||||
} else {
|
||||
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 *>(*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> Node::find_children(const String &p_pattern, const String &p_type, bool p_recursive, bool p_owned) const {
|
||||
TypedArray<Node> 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<Node *> *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<StringName, Node *> &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<StringName, Node *>::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<StringName, Node *> &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<StringName, Node *> &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<StringName, Node *> &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<StringName, Node *> &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<Tween> Node::create_tween() {
|
||||
ERR_FAIL_COND_V_MSG(!data.tree, nullptr, "Can't create Tween when not inside scene tree.");
|
||||
Ref<Tween> 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, List<St
|
|||
|
||||
void Node::clear_internal_tree_resource_paths() {
|
||||
clear_internal_resource_paths();
|
||||
for (int i = 0; i < data.children.size(); i++) {
|
||||
data.children[i]->clear_internal_tree_resource_paths();
|
||||
for (KeyValue<StringName, Node *> &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--;
|
||||
}
|
||||
|
|
|
@ -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<Node *> children;
|
||||
HashMap<StringName, Node *> children;
|
||||
mutable bool children_cache_dirty = true;
|
||||
mutable LocalVector<Node *> children_cache;
|
||||
HashMap<StringName, Node *> 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<Tween> create_tween();
|
||||
|
||||
|
|
Loading…
Reference in a new issue