feat: updated engine

This commit is contained in:
Sara Gerretsen 2026-07-10 17:04:34 +02:00
parent cbe99774ff
commit f4cf6b3999
6607 changed files with 910135 additions and 430025 deletions

View file

@ -38,18 +38,26 @@ STATIC_ASSERT_INCOMPLETE_TYPE(class, Shader);
STATIC_ASSERT_INCOMPLETE_TYPE(class, OS);
STATIC_ASSERT_INCOMPLETE_TYPE(class, Engine);
#include "core/config/engine.h"
#include "core/config/project_settings.h"
#include "core/io/resource.h"
#include "core/io/resource_loader.h"
#include "core/object/class_db.h"
#include "core/object/message_queue.h"
#include "core/object/script_language.h"
#include "core/string/print_string.h"
#include "instance_placeholder.h"
#include "scene/animation/tween.h"
#include "scene/debugger/scene_debugger.h"
#include "scene/main/instance_placeholder.h"
#include "scene/main/multiplayer_api.h"
#include "scene/main/scene_tree.h"
#include "scene/main/viewport.h"
#include "scene/main/window.h"
#include "scene/resources/packed_scene.h"
#include "viewport.h"
#include "servers/display/accessibility_server.h"
#ifdef DEBUG_ENABLED
#include "scene/debugger/scene_debugger.h"
#endif
#ifdef DEBUG_ENABLED
SafeNumeric<uint64_t> Node::total_node_count{ 0 };
@ -61,7 +69,7 @@ void Node::_notification(int p_notification) {
switch (p_notification) {
case NOTIFICATION_ACCESSIBILITY_INVALIDATE: {
if (data.accessibility_element.is_valid()) {
DisplayServer::get_singleton()->accessibility_free_element(data.accessibility_element);
AccessibilityServer::get_singleton()->free_element(data.accessibility_element);
data.accessibility_element = RID();
}
} break;
@ -70,7 +78,7 @@ void Node::_notification(int p_notification) {
RID ae = get_accessibility_element();
ERR_FAIL_COND(ae.is_null());
DisplayServer::get_singleton()->accessibility_update_set_name(ae, get_name());
AccessibilityServer::get_singleton()->update_set_name(ae, get_name());
// Node children.
if (!accessibility_override_tree_hierarchy()) {
@ -83,7 +91,7 @@ void Node::_notification(int p_notification) {
if (child_node->is_part_of_edited_scene()) {
continue;
}
DisplayServer::get_singleton()->accessibility_update_add_child(ae, child_node->get_accessibility_element());
AccessibilityServer::get_singleton()->update_add_child(ae, child_node->get_accessibility_element());
}
}
} break;
@ -178,9 +186,7 @@ void Node::_notification(int p_notification) {
} break;
case NOTIFICATION_POST_ENTER_TREE: {
if (data.auto_translate_mode != AUTO_TRANSLATE_MODE_DISABLED) {
notification(NOTIFICATION_TRANSLATION_CHANGED);
}
notification(NOTIFICATION_TRANSLATION_CHANGED);
} break;
case NOTIFICATION_EXIT_TREE: {
@ -189,7 +195,7 @@ void Node::_notification(int p_notification) {
if (data.tree->is_accessibility_supported() && !is_part_of_edited_scene()) {
if (data.accessibility_element.is_valid()) {
DisplayServer::get_singleton()->accessibility_free_element(data.accessibility_element);
AccessibilityServer::get_singleton()->free_element(data.accessibility_element);
data.accessibility_element = RID();
}
data.tree->_accessibility_notify_change(this, true);
@ -899,8 +905,7 @@ bool Node::can_process_notification(int p_what) const {
}
bool Node::can_process() const {
ERR_FAIL_COND_V(!is_inside_tree(), false);
return !data.tree->is_suspended() && _can_process(data.tree->is_paused());
return is_inside_tree() && !data.tree->is_suspended() && _can_process(data.tree->is_paused());
}
bool Node::_can_process(bool p_paused) const {
@ -964,6 +969,10 @@ void Node::set_physics_interpolation_mode(PhysicsInterpolationMode p_mode) {
}
}
bool Node::is_physics_interpolated_and_enabled() const {
return SceneTree::is_fti_enabled() && is_physics_interpolated();
}
void Node::reset_physics_interpolation() {
if (SceneTree::is_fti_enabled() && is_inside_tree()) {
propagate_notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION);
@ -1096,10 +1105,6 @@ void Node::_remove_tree_from_process_thread_group() {
}
void Node::_add_tree_to_process_thread_group(Node *p_owner) {
if (_is_any_processing()) {
_add_to_process_thread_group();
}
data.process_thread_group_owner = p_owner;
if (p_owner != nullptr) {
data.process_group = p_owner->data.process_group;
@ -1107,12 +1112,16 @@ void Node::_add_tree_to_process_thread_group(Node *p_owner) {
data.process_group = &data.tree->default_process_group;
}
if (_is_any_processing()) {
_add_to_process_thread_group();
}
for (KeyValue<StringName, Node *> &K : data.children) {
if (K.value->data.process_thread_group != PROCESS_THREAD_GROUP_INHERIT) {
continue;
}
K.value->_add_to_process_thread_group();
K.value->_add_tree_to_process_thread_group(p_owner);
}
}
bool Node::is_processing_internal() const {
@ -1466,7 +1475,7 @@ void Node::set_name(const StringName &p_name) {
}
// Returns a clear description of this node depending on what is available. Useful for error messages.
String Node::get_description() const {
String Node::get_description(bool p_show_not_in_tree) const {
String description;
if (is_inside_tree()) {
description = String(get_path());
@ -1475,6 +1484,9 @@ String Node::get_description() const {
if (description.is_empty()) {
description = get_class();
}
if (p_show_not_in_tree) {
description += " (not inside tree)";
}
}
return description;
}
@ -1541,7 +1553,7 @@ void Node::_validate_child_name(Node *p_child, bool p_force_human_readable) {
// Optimized version of the code below:
// String name = "@" + String(p_child->get_name()) + "@" + itos(node_hrcr_count.get());
uint32_t c = node_hrcr_count.get();
String cn = p_child->get_class_name().operator String();
String cn = p_child->get_class_name().string();
const char32_t *cn_ptr = cn.ptr();
uint32_t cn_length = cn.length();
uint32_t c_chars = String::num_characters(c);
@ -1756,7 +1768,25 @@ void Node::remove_child(RequiredParam<Node> rp_child) {
data.blocked--;
data.children_cache_dirty = true;
if (!data.children_cache_dirty && !data.children_cache.is_empty() && data.children_cache[data.children_cache.size() - 1] == p_child) {
// Removing the last child keeps the cache and the counters valid, so pop it
// instead of dirtying the cache. This keeps interleaving removals with reads
// (like get_child()) linear, as long as children are removed back to front.
data.children_cache.resize(data.children_cache.size() - 1);
switch (p_child->data.internal_mode) {
case INTERNAL_MODE_DISABLED: {
data.external_children_count_cache--;
} break;
case INTERNAL_MODE_FRONT: {
data.internal_children_front_count_cache--;
} break;
case INTERNAL_MODE_BACK: {
data.internal_children_back_count_cache--;
} break;
}
} else {
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.");
@ -1984,7 +2014,7 @@ Node *Node::find_child(const String &p_pattern, bool p_recursive, bool p_owned)
if (p_owned && !cptr[i]->data.owner) {
continue;
}
if (cptr[i]->data.name.operator String().match(p_pattern)) {
if (cptr[i]->data.name.string().match(p_pattern)) {
return cptr[i];
}
@ -2015,7 +2045,7 @@ TypedArray<Node> Node::find_children(const String &p_pattern, const String &p_ty
continue;
}
if (p_pattern.is_empty() || cptr[i]->data.name.operator String().match(p_pattern)) {
if (p_pattern.is_empty() || cptr[i]->data.name.string().match(p_pattern)) {
if (p_type.is_empty() || cptr[i]->is_class(p_type)) {
ret.append(cptr[i]);
} else if (cptr[i]->get_script_instance()) {
@ -2093,7 +2123,7 @@ Node *Node::find_parent(const String &p_pattern) const {
ERR_THREAD_GUARD_V(nullptr);
Node *p = data.parent;
while (p) {
if (p->data.name.operator String().match(p_pattern)) {
if (p->data.name.string().match(p_pattern)) {
return p;
}
p = p->data.parent;
@ -2138,12 +2168,23 @@ Window *Node::get_last_exclusive_window() const {
bool Node::is_ancestor_of(RequiredParam<const Node> rp_node) const {
EXTRACT_PARAM_OR_FAIL_V(p_node, rp_node, false);
Node *p = p_node->data.parent;
while (p) {
if (p == this) {
const Node *n = p_node;
if (is_inside_tree() && p_node->data.tree == data.tree) {
const int depth = data.depth;
while (n->data.depth > depth) {
n = n->data.parent;
}
return n == this;
}
n = p_node->data.parent;
while (n) {
if (n == this) {
return true;
}
p = p->data.parent;
n = n->data.parent;
}
return false;
@ -2211,7 +2252,7 @@ void Node::_set_owner_nocheck(Node *p_owner) {
void Node::_release_unique_name_in_owner() {
ERR_FAIL_NULL(data.owner); // Safety check.
StringName key = StringName(UNIQUE_NODE_PREFIX + data.name.operator String());
StringName key = StringName(UNIQUE_NODE_PREFIX + data.name.string());
Node **which = data.owner->data.owned_unique_nodes.getptr(key);
if (which == nullptr || *which != this) {
return; // Ignore.
@ -2221,7 +2262,7 @@ void Node::_release_unique_name_in_owner() {
void Node::_acquire_unique_name_in_owner() {
ERR_FAIL_NULL(data.owner); // Safety check.
StringName key = StringName(UNIQUE_NODE_PREFIX + data.name.operator String());
StringName key = StringName(UNIQUE_NODE_PREFIX + data.name.string());
Node **which = data.owner->data.owned_unique_nodes.getptr(key);
if (which != nullptr && *which != this) {
String which_path = String(is_inside_tree() ? (*which)->get_path() : data.owner->get_path_to(*which));
@ -2351,7 +2392,7 @@ NodePath Node::get_path_to(RequiredParam<const Node> rp_node, bool p_use_unique_
common_parent = common_parent->data.parent;
}
ERR_FAIL_NULL_V(common_parent, NodePath()); //nodes not in the same tree
ERR_FAIL_NULL_V_MSG(common_parent, NodePath(), vformat("No path can be resolved between the nodes %s and %s as they share no common ancestor.", get_description(true), p_node->get_description(true)));
visited.clear();
@ -2562,37 +2603,6 @@ String Node::get_tree_string() {
return _get_tree_string(this);
}
void Node::_propagate_reverse_notification(int p_notification) {
data.blocked++;
for (HashMap<StringName, Node *>::Iterator I = data.children.last(); I; --I) {
I->value->_propagate_reverse_notification(p_notification);
}
notification(p_notification, true);
data.blocked--;
}
void Node::_propagate_deferred_notification(int p_notification, bool p_reverse) {
ERR_FAIL_COND(!is_inside_tree());
data.blocked++;
if (!p_reverse) {
MessageQueue::get_singleton()->push_notification(this, p_notification);
}
for (KeyValue<StringName, Node *> &K : data.children) {
K.value->_propagate_deferred_notification(p_notification, p_reverse);
}
if (p_reverse) {
MessageQueue::get_singleton()->push_notification(this, p_notification);
}
data.blocked--;
}
void Node::propagate_notification(int p_notification) {
ERR_THREAD_GUARD
data.blocked++;
@ -2751,6 +2761,7 @@ StringName Node::get_property_store_alias(const StringName &p_property) const {
bool Node::is_part_of_edited_scene() const {
return Engine::get_singleton()->is_editor_hint() && is_inside_tree() && data.tree->get_edited_scene_root() &&
data.tree->get_edited_scene_root()->get_parent() && // Defend against edge cases when creating new scenes and they are not fully added to the tree yet.
data.tree->get_edited_scene_root()->get_parent()->is_ancestor_of(this);
}
#endif
@ -3224,7 +3235,7 @@ void Node::replace_by(RequiredParam<Node> rp_node, bool p_keep_groups) {
EXTRACT_PARAM_OR_FAIL(p_node, rp_node);
ERR_FAIL_COND(p_node->data.parent);
List<Node *> owned = data.owned;
List<Node *> owned(data.owned);
List<Node *> owned_by_owner;
Node *owner = (data.owner == this) ? p_node : data.owner;
@ -3258,16 +3269,14 @@ void Node::replace_by(RequiredParam<Node> rp_node, bool p_keep_groups) {
emit_signal(SNAME("replacing_by"), p_node);
while (get_child_count()) {
Node *child = get_child(0);
// Move non-internal children to `p_node`.
while (get_child_count(false)) {
Node *child = get_child(0, false);
remove_child(child);
if (!child->is_internal()) {
// Add the custom children to the p_node.
Node *child_owner = child->get_owner() == this ? p_node : child->get_owner();
child->set_owner(nullptr);
p_node->add_child(child);
child->set_owner(child_owner);
}
Node *child_owner = child->get_owner() == this ? p_node : child->get_owner();
child->set_owner(nullptr);
p_node->add_child(child);
child->set_owner(child_owner);
}
p_node->set_owner(owner);
@ -3512,9 +3521,9 @@ void Node::get_argument_options(const StringName &p_function, int p_idx, List<St
if (p_idx == 0 && (pf == "has_node" || pf == "get_node" || pf == "get_node_or_null")) {
_add_nodes_to_options(this, this, r_options);
} else if (p_idx == 0 && (pf == "add_to_group" || pf == "remove_from_group" || pf == "is_in_group")) {
HashMap<StringName, String> global_groups = ProjectSettings::get_singleton()->get_global_groups_list();
HashMap<StringName, String> global_groups(ProjectSettings::get_singleton()->get_global_groups_list());
for (const KeyValue<StringName, String> &E : global_groups) {
r_options->push_back(E.key.operator String().quote());
r_options->push_back(E.key.string().quote());
}
}
Object::get_argument_options(p_function, p_idx, r_options);
@ -3624,7 +3633,7 @@ void Node::_call_unhandled_key_input(const Ref<InputEvent> &p_event) {
void Node::_validate_property(PropertyInfo &p_property) const {
if ((p_property.name == "process_thread_group_order" || p_property.name == "process_thread_messages") && data.process_thread_group == PROCESS_THREAD_GROUP_INHERIT) {
p_property.usage = 0;
p_property.usage = PROPERTY_USAGE_NONE;
}
}
@ -3746,6 +3755,14 @@ RID Node::get_focused_accessibility_element() const {
}
}
Transform2D Node::get_accessibility_transform() const {
if (is_inside_tree() && data.parent) {
return data.parent->get_accessibility_transform();
} else {
return Transform2D();
}
}
void Node::queue_accessibility_update() {
if (is_inside_tree() && !is_part_of_edited_scene()) {
data.tree->_accessibility_notify_change(this);
@ -3758,8 +3775,8 @@ RID Node::get_accessibility_element() const {
}
if (unlikely(data.accessibility_element.is_null())) {
Window *w = get_non_popup_window();
if (w && w->get_window_id() != DisplayServer::INVALID_WINDOW_ID && get_window()->is_visible()) {
data.accessibility_element = DisplayServer::get_singleton()->accessibility_create_element(w->get_window_id(), DisplayServer::ROLE_CONTAINER);
if (w && w->get_window_id() != DisplayServerEnums::INVALID_WINDOW_ID && get_window()->is_visible()) {
data.accessibility_element = AccessibilityServer::get_singleton()->create_element(w->get_window_id(), AccessibilityServerEnums::ROLE_CONTAINER);
}
}
return data.accessibility_element;
@ -3986,6 +4003,7 @@ void Node::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_VP_MOUSE_ENTER);
BIND_CONSTANT(NOTIFICATION_VP_MOUSE_EXIT);
BIND_CONSTANT(NOTIFICATION_WM_POSITION_CHANGED);
BIND_CONSTANT(NOTIFICATION_WM_OUTPUT_MAX_LINEAR_VALUE_CHANGED);
BIND_CONSTANT(NOTIFICATION_OS_MEMORY_WARNING);
BIND_CONSTANT(NOTIFICATION_TRANSLATION_CHANGED);
BIND_CONSTANT(NOTIFICATION_WM_ABOUT);
@ -3996,6 +4014,8 @@ void Node::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_IN);
BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_OUT);
BIND_CONSTANT(NOTIFICATION_TEXT_SERVER_CHANGED);
BIND_CONSTANT(NOTIFICATION_APPLICATION_PIP_MODE_ENTERED);
BIND_CONSTANT(NOTIFICATION_APPLICATION_PIP_MODE_EXITED);
BIND_CONSTANT(NOTIFICATION_ACCESSIBILITY_UPDATE);
BIND_CONSTANT(NOTIFICATION_ACCESSIBILITY_INVALIDATE);
@ -4049,8 +4069,8 @@ void Node::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_name", "get_name");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "unique_name_in_owner", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_unique_name_in_owner", "is_unique_name_in_owner");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "scene_file_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_scene_file_path", "get_scene_file_path");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_owner", "get_owner");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", PROPERTY_USAGE_NONE), "", "get_multiplayer");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, Node::get_class_static(), PROPERTY_USAGE_NONE), "set_owner", "get_owner");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, MultiplayerAPI::get_class_static(), PROPERTY_USAGE_NONE), "", "get_multiplayer");
ADD_GROUP("Process", "process_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Inherit,Pausable,When Paused,Always,Disabled"), "set_process_mode", "get_process_mode");