Browse Source

Merge pull request #78706 from RedworkDE/node-rename-inplace

Allow renaming child nodes in `_ready`
Yuri Sizov 2 years ago
parent
commit
4d42d6fdf6
2 changed files with 36 additions and 3 deletions
  1. 34 0
      core/templates/hash_map.h
  2. 2 3
      scene/main/node.cpp

+ 34 - 0
core/templates/hash_map.h

@@ -353,6 +353,40 @@ public:
 		return true;
 	}
 
+	// Replace the key of an entry in-place, without invalidating iterators or changing the entries position during iteration.
+	// p_old_key must exist in the map and p_new_key must not, unless it is equal to p_old_key.
+	bool replace_key(const TKey &p_old_key, const TKey &p_new_key) {
+		if (p_old_key == p_new_key) {
+			return true;
+		}
+		uint32_t pos = 0;
+		ERR_FAIL_COND_V(_lookup_pos(p_new_key, pos), false);
+		ERR_FAIL_COND_V(!_lookup_pos(p_old_key, pos), false);
+		HashMapElement<TKey, TValue> *element = elements[pos];
+
+		// Delete the old entries in hashes and elements.
+		const uint32_t capacity = hash_table_size_primes[capacity_index];
+		const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index];
+		uint32_t next_pos = fastmod((pos + 1), capacity_inv, capacity);
+		while (hashes[next_pos] != EMPTY_HASH && _get_probe_length(next_pos, hashes[next_pos], capacity, capacity_inv) != 0) {
+			SWAP(hashes[next_pos], hashes[pos]);
+			SWAP(elements[next_pos], elements[pos]);
+			pos = next_pos;
+			next_pos = fastmod((pos + 1), capacity_inv, capacity);
+		}
+		hashes[pos] = EMPTY_HASH;
+		elements[pos] = nullptr;
+		// _insert_with_hash will increment this again.
+		num_elements--;
+
+		// Update the HashMapElement with the new key and reinsert it.
+		const_cast<TKey &>(element->data.key) = p_new_key;
+		uint32_t hash = _hash(p_new_key);
+		_insert_with_hash(hash, element);
+
+		return true;
+	}
+
 	// Reserves space for a number of elements, useful to avoid many resizes and rehashes.
 	// If adding a known (possibly large) number of elements at once, must be larger than old capacity.
 	void reserve(uint32_t p_new_capacity) {

+ 2 - 3
scene/main/node.cpp

@@ -1139,7 +1139,6 @@ void Node::_set_name_nocheck(const StringName &p_name) {
 
 void Node::set_name(const String &p_name) {
 	ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Changing the name to nodes inside the SceneTree is only allowed from the main thread. Use `set_name.call_deferred(new_name)`.");
-	ERR_FAIL_COND_MSG(data.parent && data.parent->data.blocked > 0, "Parent node is busy setting up children, `set_name(new_name)` failed. Consider using `set_name.call_deferred(new_name)` instead.");
 	String name = p_name.validate_node_name();
 
 	ERR_FAIL_COND(name.is_empty());
@@ -1151,9 +1150,9 @@ void Node::set_name(const String &p_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);
+		bool success = data.parent->data.children.replace_key(old_name, data.name);
+		ERR_FAIL_COND_MSG(!success, "Renaming child in hashtable failed, this is a bug.");
 	}
 
 	if (data.unique_name_in_owner && data.owner) {