diff options
Diffstat (limited to 'scene/main/node.cpp')
-rw-r--r-- | scene/main/node.cpp | 1856 |
1 files changed, 847 insertions, 1009 deletions
diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 50f3bf834f..05409b7bfe 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,9 +32,10 @@ #include "core/core_string_names.h" #include "core/io/resource_loader.h" -#include "core/message_queue.h" -#include "core/print_string.h" +#include "core/object/message_queue.h" +#include "core/string/print_string.h" #include "instance_placeholder.h" +#include "scene/animation/tween.h" #include "scene/debugger/scene_debugger.h" #include "scene/resources/packed_scene.h" #include "scene/scene_string_names.h" @@ -46,53 +47,44 @@ #include <stdint.h> -VARIANT_ENUM_CAST(Node::PauseMode); +VARIANT_ENUM_CAST(Node::ProcessMode); +VARIANT_ENUM_CAST(Node::InternalMode); int Node::orphan_node_count = 0; void Node::_notification(int p_notification) { - switch (p_notification) { - case NOTIFICATION_PROCESS: { + GDVIRTUAL_CALL(_process, get_process_delta_time()); - if (get_script_instance()) { - - Variant time = get_process_delta_time(); - const Variant *ptr[1] = { &time }; - get_script_instance()->call_multilevel(SceneStringNames::get_singleton()->_process, ptr, 1); - } } break; case NOTIFICATION_PHYSICS_PROCESS: { - - if (get_script_instance()) { - - Variant time = get_physics_process_delta_time(); - const Variant *ptr[1] = { &time }; - get_script_instance()->call_multilevel(SceneStringNames::get_singleton()->_physics_process, ptr, 1); - } + GDVIRTUAL_CALL(_physics_process, get_physics_process_delta_time()); } break; case NOTIFICATION_ENTER_TREE: { ERR_FAIL_COND(!get_viewport()); ERR_FAIL_COND(!get_tree()); - if (data.pause_mode == PAUSE_MODE_INHERIT) { - - if (data.parent) - data.pause_owner = data.parent->data.pause_owner; - else - data.pause_owner = nullptr; + if (data.process_mode == PROCESS_MODE_INHERIT) { + if (data.parent) { + data.process_owner = data.parent->data.process_owner; + } else { + data.process_owner = nullptr; + } } else { - data.pause_owner = this; + data.process_owner = this; } - if (data.input) + if (data.input) { add_to_group("_vp_input" + itos(get_viewport()->get_instance_id())); - if (data.unhandled_input) + } + if (data.unhandled_input) { add_to_group("_vp_unhandled_input" + itos(get_viewport()->get_instance_id())); - if (data.unhandled_key_input) + } + if (data.unhandled_key_input) { add_to_group("_vp_unhandled_key_input" + itos(get_viewport()->get_instance_id())); + } get_tree()->node_count++; orphan_node_count--; @@ -105,51 +97,56 @@ void Node::_notification(int p_notification) { get_tree()->node_count--; orphan_node_count++; - if (data.input) + if (data.input) { remove_from_group("_vp_input" + itos(get_viewport()->get_instance_id())); - if (data.unhandled_input) + } + if (data.unhandled_input) { remove_from_group("_vp_unhandled_input" + itos(get_viewport()->get_instance_id())); - if (data.unhandled_key_input) + } + if (data.unhandled_key_input) { remove_from_group("_vp_unhandled_key_input" + itos(get_viewport()->get_instance_id())); + } - data.pause_owner = nullptr; + data.process_owner = nullptr; if (data.path_cache) { memdelete(data.path_cache); data.path_cache = nullptr; } + if (data.filename.length()) { + get_multiplayer()->scene_enter_exit_notify(data.filename, this, false); + } } break; case NOTIFICATION_PATH_CHANGED: { - if (data.path_cache) { memdelete(data.path_cache); data.path_cache = nullptr; } } break; case NOTIFICATION_READY: { + if (GDVIRTUAL_IS_OVERRIDDEN(_input)) { + set_process_input(true); + } - if (get_script_instance()) { - - if (get_script_instance()->has_method(SceneStringNames::get_singleton()->_input)) { - set_process_input(true); - } - - if (get_script_instance()->has_method(SceneStringNames::get_singleton()->_unhandled_input)) { - set_process_unhandled_input(true); - } + if (GDVIRTUAL_IS_OVERRIDDEN(_unhandled_input)) { + set_process_unhandled_input(true); + } - if (get_script_instance()->has_method(SceneStringNames::get_singleton()->_unhandled_key_input)) { - set_process_unhandled_key_input(true); - } + if (GDVIRTUAL_IS_OVERRIDDEN(_unhandled_key_input)) { + set_process_unhandled_key_input(true); + } - if (get_script_instance()->has_method(SceneStringNames::get_singleton()->_process)) { - set_process(true); - } + if (GDVIRTUAL_IS_OVERRIDDEN(_process)) { + set_process(true); + } + if (GDVIRTUAL_IS_OVERRIDDEN(_physics_process)) { + set_physics_process(true); + } - if (get_script_instance()->has_method(SceneStringNames::get_singleton()->_physics_process)) { - set_physics_process(true); - } + GDVIRTUAL_CALL(_ready); - get_script_instance()->call_multilevel_reversed(SceneStringNames::get_singleton()->_ready, nullptr, 0); + if (data.filename.length()) { + ERR_FAIL_COND(!is_inside_tree()); + get_multiplayer()->scene_enter_exit_notify(data.filename, this, true); } } break; @@ -157,22 +154,18 @@ void Node::_notification(int p_notification) { data.in_constructor = false; } break; case NOTIFICATION_PREDELETE: { - set_owner(nullptr); while (data.owned.size()) { - data.owned.front()->get()->set_owner(nullptr); } if (data.parent) { - data.parent->remove_child(this); } // 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 remove_child(child); memdelete(child); @@ -183,11 +176,9 @@ 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(); } data.blocked--; @@ -208,13 +199,13 @@ void Node::_propagate_enter_tree() { data.tree = data.parent->data.tree; data.depth = data.parent->data.depth + 1; } else { - data.depth = 1; } data.viewport = Object::cast_to<Viewport>(this); - if (!data.viewport && data.parent) + if (!data.viewport && data.parent) { data.viewport = data.parent->data.viewport; + } data.inside_tree = true; @@ -224,10 +215,7 @@ void Node::_propagate_enter_tree() { notification(NOTIFICATION_ENTER_TREE); - if (get_script_instance()) { - - get_script_instance()->call_multilevel_reversed(SceneStringNames::get_singleton()->_enter_tree, nullptr, 0); - } + GDVIRTUAL_CALL(_enter_tree); emit_signal(SceneStringNames::get_singleton()->tree_entered); @@ -237,9 +225,9 @@ void Node::_propagate_enter_tree() { //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 + if (!data.children[i]->is_inside_tree()) { // could have been added in enter_tree data.children[i]->_propagate_enter_tree(); + } } data.blocked--; @@ -251,7 +239,6 @@ void Node::_propagate_enter_tree() { } void Node::_propagate_after_exit_tree() { - data.blocked++; for (int i = 0; i < data.children.size(); i++) { data.children[i]->_propagate_after_exit_tree(); @@ -261,7 +248,6 @@ void Node::_propagate_after_exit_tree() { } void Node::_propagate_exit_tree() { - //block while removing children #ifdef DEBUG_ENABLED @@ -270,21 +256,19 @@ void Node::_propagate_exit_tree() { data.blocked++; for (int i = data.children.size() - 1; i >= 0; i--) { - data.children[i]->_propagate_exit_tree(); } data.blocked--; - if (get_script_instance()) { + GDVIRTUAL_CALL(_exit_tree); - get_script_instance()->call_multilevel(SceneStringNames::get_singleton()->_exit_tree, nullptr, 0); - } emit_signal(SceneStringNames::get_singleton()->tree_exiting); notification(NOTIFICATION_EXIT_TREE, true); - if (data.tree) + if (data.tree) { data.tree->node_removed(this); + } // exit groups @@ -295,8 +279,9 @@ void Node::_propagate_exit_tree() { data.viewport = nullptr; - if (data.tree) + if (data.tree) { data.tree->tree_changed(); + } data.inside_tree = false; data.ready_notified = false; @@ -305,19 +290,46 @@ void Node::_propagate_exit_tree() { } void Node::move_child(Node *p_child, int p_pos) { - ERR_FAIL_NULL(p_child); - ERR_FAIL_INDEX_MSG(p_pos, data.children.size() + 1, "Invalid new child position: " + itos(p_pos) + "."); ERR_FAIL_COND_MSG(p_child->data.parent != this, "Child is not a child of this node."); + + // We need to check whether node is internal and move it only in the relevant node range. + if (p_child->_is_internal_front()) { + ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_front, vformat("Invalid new child position: %d. Child is internal.", p_pos)); + _move_child(p_child, p_pos); + } else if (p_child->_is_internal_back()) { + ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_back, vformat("Invalid new child position: %d. Child is internal.", p_pos)); + _move_child(p_child, data.children.size() - data.internal_children_back + p_pos); + } else { + ERR_FAIL_INDEX_MSG(p_pos, data.children.size() + 1 - data.internal_children_front - data.internal_children_back, vformat("Invalid new child position: %d.", p_pos)); + _move_child(p_child, p_pos + data.internal_children_front); + } +} + +void Node::_move_child(Node *p_child, int p_pos, bool p_ignore_end) { ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, move_child() failed. Consider using call_deferred(\"move_child\") instead (or \"popup\" if this is from a popup)."); // Specifying one place beyond the end // means the same as moving to the last position - if (p_pos == data.children.size()) - p_pos--; + 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_pos == data.internal_children_front) { + p_pos--; + } + } else if (p_child->_is_internal_back()) { + if (p_pos == data.children.size()) { + p_pos--; + } + } else { + if (p_pos == data.children.size() - data.internal_children_back) { + p_pos--; + } + } + } - if (p_child->data.pos == p_pos) + if (p_child->data.pos == p_pos) { return; //do nothing + } int motion_from = MIN(p_pos, p_child->data.pos); int motion_to = MAX(p_pos, p_child->data.pos); @@ -332,7 +344,6 @@ void Node::move_child(Node *p_child, int p_pos) { data.blocked++; //new pos first for (int i = motion_from; i <= motion_to; i++) { - data.children[i]->data.pos = i; } // notification second @@ -341,314 +352,265 @@ void Node::move_child(Node *p_child, int p_pos) { data.children[i]->notification(NOTIFICATION_MOVED_IN_PARENT); } for (const Map<StringName, GroupData>::Element *E = p_child->data.grouped.front(); E; E = E->next()) { - if (E->get().group) + if (E->get().group) { E->get().group->changed = true; + } } data.blocked--; } void Node::raise() { - - if (!data.parent) + if (!data.parent) { return; + } - data.parent->move_child(this, data.parent->data.children.size() - 1); + // Internal children move within a different index range. + if (_is_internal_front()) { + data.parent->move_child(this, data.parent->data.internal_children_front - 1); + } else if (_is_internal_back()) { + data.parent->move_child(this, data.parent->data.internal_children_back - 1); + } else { + data.parent->move_child(this, data.parent->get_child_count(false) - 1); + } } void Node::add_child_notify(Node *p_child) { - // to be used when not wanted } void Node::remove_child_notify(Node *p_child) { - // to be used when not wanted } void Node::move_child_notify(Node *p_child) { - // to be used when not wanted } void Node::set_physics_process(bool p_process) { - - if (data.physics_process == p_process) + if (data.physics_process == p_process) { return; + } data.physics_process = p_process; - if (data.physics_process) + if (data.physics_process) { add_to_group("physics_process", false); - else + } else { remove_from_group("physics_process"); - - _change_notify("physics_process"); + } } bool Node::is_physics_processing() const { - return data.physics_process; } void Node::set_physics_process_internal(bool p_process_internal) { - - if (data.physics_process_internal == p_process_internal) + if (data.physics_process_internal == p_process_internal) { return; + } data.physics_process_internal = p_process_internal; - if (data.physics_process_internal) + if (data.physics_process_internal) { add_to_group("physics_process_internal", false); - else + } else { remove_from_group("physics_process_internal"); - - _change_notify("physics_process_internal"); + } } bool Node::is_physics_processing_internal() const { - return data.physics_process_internal; } -void Node::set_pause_mode(PauseMode p_mode) { - - if (data.pause_mode == p_mode) +void Node::set_process_mode(ProcessMode p_mode) { + if (data.process_mode == p_mode) { return; + } - bool prev_inherits = data.pause_mode == PAUSE_MODE_INHERIT; - data.pause_mode = p_mode; - if (!is_inside_tree()) - return; //pointless - if ((data.pause_mode == PAUSE_MODE_INHERIT) == prev_inherits) - return; ///nothing changed + if (!is_inside_tree()) { + data.process_mode = p_mode; + return; + } - Node *owner = nullptr; + bool prev_can_process = can_process(); + bool prev_enabled = _is_enabled(); - if (data.pause_mode == PAUSE_MODE_INHERIT) { + data.process_mode = p_mode; - if (data.parent) - owner = data.parent->data.pause_owner; + if (data.process_mode == PROCESS_MODE_INHERIT) { + if (data.parent) { + data.process_owner = data.parent->data.owner; + } else { + data.process_owner = nullptr; + } } else { - owner = this; + data.process_owner = this; } - _propagate_pause_owner(owner); -} + bool next_can_process = can_process(); + bool next_enabled = _is_enabled(); -Node::PauseMode Node::get_pause_mode() const { + int pause_notification = 0; - return data.pause_mode; -} + if (prev_can_process && !next_can_process) { + pause_notification = NOTIFICATION_PAUSED; + } else if (!prev_can_process && next_can_process) { + pause_notification = NOTIFICATION_UNPAUSED; + } -void Node::_propagate_pause_owner(Node *p_owner) { + int enabled_notification = 0; - if (this != p_owner && data.pause_mode != PAUSE_MODE_INHERIT) - return; - data.pause_owner = p_owner; - for (int i = 0; i < data.children.size(); i++) { + if (prev_enabled && !next_enabled) { + enabled_notification = NOTIFICATION_DISABLED; + } else if (!prev_enabled && next_enabled) { + enabled_notification = NOTIFICATION_ENABLED; + } - data.children[i]->_propagate_pause_owner(p_owner); + _propagate_process_owner(data.process_owner, pause_notification, enabled_notification); + +#ifdef TOOLS_ENABLED + // This is required for the editor to update the visibility of disabled nodes + // It's very expensive during runtime to change, so editor-only + if (Engine::get_singleton()->is_editor_hint()) { + get_tree()->emit_signal(SNAME("tree_process_mode_changed")); } +#endif } -void Node::set_network_master(int p_peer_id, bool p_recursive) { +void Node::_propagate_pause_notification(bool p_enable) { + bool prev_can_process = _can_process(!p_enable); + bool next_can_process = _can_process(p_enable); - data.network_master = p_peer_id; - - if (p_recursive) { - for (int i = 0; i < data.children.size(); i++) { + if (prev_can_process && !next_can_process) { + notification(NOTIFICATION_PAUSED); + } else if (!prev_can_process && next_can_process) { + notification(NOTIFICATION_UNPAUSED); + } - data.children[i]->set_network_master(p_peer_id, true); - } + for (int i = 0; i < data.children.size(); i++) { + data.children[i]->_propagate_pause_notification(p_enable); } } -int Node::get_network_master() const { - - return data.network_master; +Node::ProcessMode Node::get_process_mode() const { + return data.process_mode; } -bool Node::is_network_master() const { +void Node::_propagate_process_owner(Node *p_owner, int p_pause_notification, int p_enabled_notification) { + data.process_owner = p_owner; - ERR_FAIL_COND_V(!is_inside_tree(), false); - - return get_multiplayer()->get_network_unique_id() == data.network_master; -} - -/***** RPC CONFIG ********/ + if (p_pause_notification != 0) { + notification(p_pause_notification); + } -uint16_t Node::rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_mode) { + if (p_enabled_notification != 0) { + notification(p_enabled_notification); + } - uint16_t mid = get_node_rpc_method_id(p_method); - if (mid == UINT16_MAX) { - // It's new - NetData nd; - nd.name = p_method; - nd.mode = p_mode; - data.rpc_methods.push_back(nd); - return ((uint16_t)data.rpc_properties.size() - 1) | (1 << 15); - } else { - int c_mid = (~(1 << 15)) & mid; - data.rpc_methods.write[c_mid].mode = p_mode; - return mid; + for (int i = 0; i < data.children.size(); i++) { + Node *c = data.children[i]; + if (c->data.process_mode == PROCESS_MODE_INHERIT) { + c->_propagate_process_owner(p_owner, p_pause_notification, p_enabled_notification); + } } } -uint16_t Node::rset_config(const StringName &p_property, MultiplayerAPI::RPCMode p_mode) { +void Node::set_multiplayer_authority(int p_peer_id, bool p_recursive) { + data.multiplayer_authority = p_peer_id; - uint16_t pid = get_node_rset_property_id(p_property); - if (pid == UINT16_MAX) { - // It's new - NetData nd; - nd.name = p_property; - nd.mode = p_mode; - data.rpc_properties.push_back(nd); - return ((uint16_t)data.rpc_properties.size() - 1) | (1 << 15); - } else { - int c_pid = (~(1 << 15)) & pid; - data.rpc_properties.write[c_pid].mode = p_mode; - return pid; + if (p_recursive) { + for (int i = 0; i < data.children.size(); i++) { + data.children[i]->set_multiplayer_authority(p_peer_id, true); + } } } -/***** RPC FUNCTIONS ********/ - -void Node::rpc(const StringName &p_method, VARIANT_ARG_DECLARE) { - - VARIANT_ARGPTRS; +int Node::get_multiplayer_authority() const { + return data.multiplayer_authority; +} - int argc = 0; - for (int i = 0; i < VARIANT_ARG_MAX; i++) { - if (argptr[i]->get_type() == Variant::NIL) - break; - argc++; - } +bool Node::is_multiplayer_authority() const { + ERR_FAIL_COND_V(!is_inside_tree(), false); - rpcp(0, false, p_method, argptr, argc); + return get_multiplayer()->get_unique_id() == data.multiplayer_authority; } -void Node::rpc_id(int p_peer_id, const StringName &p_method, VARIANT_ARG_DECLARE) { - - VARIANT_ARGPTRS; +/***** RPC CONFIG ********/ - int argc = 0; - for (int i = 0; i < VARIANT_ARG_MAX; i++) { - if (argptr[i]->get_type() == Variant::NIL) - break; - argc++; +uint16_t Node::rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, Multiplayer::TransferMode p_transfer_mode, int p_channel) { + for (int i = 0; i < data.rpc_methods.size(); i++) { + if (data.rpc_methods[i].name == p_method) { + Multiplayer::RPCConfig &nd = data.rpc_methods.write[i]; + nd.rpc_mode = p_rpc_mode; + nd.transfer_mode = p_transfer_mode; + nd.channel = p_channel; + return i | (1 << 15); + } } - - rpcp(p_peer_id, false, p_method, argptr, argc); + // New method + Multiplayer::RPCConfig nd; + nd.name = p_method; + nd.rpc_mode = p_rpc_mode; + nd.transfer_mode = p_transfer_mode; + nd.channel = p_channel; + data.rpc_methods.push_back(nd); + return ((uint16_t)data.rpc_methods.size() - 1) | (1 << 15); } -void Node::rpc_unreliable(const StringName &p_method, VARIANT_ARG_DECLARE) { +/***** RPC FUNCTIONS ********/ +void Node::rpc(const StringName &p_method, VARIANT_ARG_DECLARE) { VARIANT_ARGPTRS; int argc = 0; for (int i = 0; i < VARIANT_ARG_MAX; i++) { - if (argptr[i]->get_type() == Variant::NIL) + if (argptr[i]->get_type() == Variant::NIL) { break; + } argc++; } - rpcp(0, true, p_method, argptr, argc); + rpcp(0, p_method, argptr, argc); } -void Node::rpc_unreliable_id(int p_peer_id, const StringName &p_method, VARIANT_ARG_DECLARE) { - +void Node::rpc_id(int p_peer_id, const StringName &p_method, VARIANT_ARG_DECLARE) { VARIANT_ARGPTRS; int argc = 0; for (int i = 0; i < VARIANT_ARG_MAX; i++) { - if (argptr[i]->get_type() == Variant::NIL) + if (argptr[i]->get_type() == Variant::NIL) { break; + } argc++; } - rpcp(p_peer_id, true, p_method, argptr, argc); + rpcp(p_peer_id, p_method, argptr, argc); } Variant Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (p_argcount < 1) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_error.argument = 1; return Variant(); } - if (p_args[0]->get_type() != Variant::STRING) { + if (p_args[0]->get_type() != Variant::STRING_NAME) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; - r_error.expected = Variant::STRING; + r_error.expected = Variant::STRING_NAME; return Variant(); } StringName method = *p_args[0]; - rpcp(0, false, method, &p_args[1], p_argcount - 1); + rpcp(0, method, &p_args[1], p_argcount - 1); r_error.error = Callable::CallError::CALL_OK; return Variant(); } Variant Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - - if (p_argcount < 2) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 2; - return Variant(); - } - - if (p_args[0]->get_type() != Variant::INT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::INT; - return Variant(); - } - - if (p_args[1]->get_type() != Variant::STRING) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 1; - r_error.expected = Variant::STRING; - return Variant(); - } - - int peer_id = *p_args[0]; - StringName method = *p_args[1]; - - rpcp(peer_id, false, method, &p_args[2], p_argcount - 2); - - r_error.error = Callable::CallError::CALL_OK; - return Variant(); -} - -Variant Node::_rpc_unreliable_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - - if (p_argcount < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; - return Variant(); - } - - if (p_args[0]->get_type() != Variant::STRING) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::STRING; - return Variant(); - } - - StringName method = *p_args[0]; - - rpcp(0, true, method, &p_args[1], p_argcount - 1); - - r_error.error = Callable::CallError::CALL_OK; - return Variant(); -} - -Variant Node::_rpc_unreliable_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (p_argcount < 2) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_error.argument = 2; @@ -662,59 +624,34 @@ Variant Node::_rpc_unreliable_id_bind(const Variant **p_args, int p_argcount, Ca return Variant(); } - if (p_args[1]->get_type() != Variant::STRING) { + if (p_args[1]->get_type() != Variant::STRING_NAME) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 1; - r_error.expected = Variant::STRING; + r_error.expected = Variant::STRING_NAME; return Variant(); } int peer_id = *p_args[0]; StringName method = *p_args[1]; - rpcp(peer_id, true, method, &p_args[2], p_argcount - 2); + rpcp(peer_id, method, &p_args[2], p_argcount - 2); r_error.error = Callable::CallError::CALL_OK; return Variant(); } -void Node::rpcp(int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) { +void Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) { ERR_FAIL_COND(!is_inside_tree()); - get_multiplayer()->rpcp(this, p_peer_id, p_unreliable, p_method, p_arg, p_argcount); -} - -void Node::rsetp(int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) { - ERR_FAIL_COND(!is_inside_tree()); - get_multiplayer()->rsetp(this, p_peer_id, p_unreliable, p_property, p_value); -} - -/******** RSET *********/ -void Node::rset(const StringName &p_property, const Variant &p_value) { - - rsetp(0, false, p_property, p_value); -} - -void Node::rset_id(int p_peer_id, const StringName &p_property, const Variant &p_value) { - - rsetp(p_peer_id, false, p_property, p_value); -} - -void Node::rset_unreliable(const StringName &p_property, const Variant &p_value) { - - rsetp(0, true, p_property, p_value); -} - -void Node::rset_unreliable_id(int p_peer_id, const StringName &p_property, const Variant &p_value) { - - rsetp(p_peer_id, true, p_property, p_value); + get_multiplayer()->rpcp(this, p_peer_id, p_method, p_arg, p_argcount); } -//////////// end of rpc Ref<MultiplayerAPI> Node::get_multiplayer() const { - if (multiplayer.is_valid()) + if (multiplayer.is_valid()) { return multiplayer; - if (!is_inside_tree()) + } + if (!is_inside_tree()) { return Ref<MultiplayerAPI>(); + } return get_tree()->get_multiplayer(); } @@ -723,191 +660,132 @@ Ref<MultiplayerAPI> Node::get_custom_multiplayer() const { } void Node::set_custom_multiplayer(Ref<MultiplayerAPI> p_multiplayer) { - multiplayer = p_multiplayer; } -uint16_t Node::get_node_rpc_method_id(const StringName &p_method) const { - for (int i = 0; i < data.rpc_methods.size(); i++) { - if (data.rpc_methods[i].name == p_method) { - // Returns `i` with the high bit set to 1 so we know that this id comes - // from the node and not the script. - return i | (1 << 15); - } - } - return UINT16_MAX; +Vector<Multiplayer::RPCConfig> Node::get_node_rpc_methods() const { + return data.rpc_methods; } -StringName Node::get_node_rpc_method(const uint16_t p_rpc_method_id) const { - // Make sure this is a node generated ID. - if (((1 << 15) & p_rpc_method_id) > 0) { - int mid = (~(1 << 15)) & p_rpc_method_id; - if (mid < data.rpc_methods.size()) - return data.rpc_methods[mid].name; - } - return StringName(); -} +//////////// end of rpc -MultiplayerAPI::RPCMode Node::get_node_rpc_mode_by_id(const uint16_t p_rpc_method_id) const { - // Make sure this is a node generated ID. - if (((1 << 15) & p_rpc_method_id) > 0) { - int mid = (~(1 << 15)) & p_rpc_method_id; - if (mid < data.rpc_methods.size()) - return data.rpc_methods[mid].mode; +bool Node::can_process_notification(int p_what) const { + switch (p_what) { + case NOTIFICATION_PHYSICS_PROCESS: + return data.physics_process; + case NOTIFICATION_PROCESS: + return data.process; + case NOTIFICATION_INTERNAL_PROCESS: + return data.process_internal; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: + return data.physics_process_internal; } - return MultiplayerAPI::RPC_MODE_DISABLED; + + return true; } -MultiplayerAPI::RPCMode Node::get_node_rpc_mode(const StringName &p_method) const { - return get_node_rpc_mode_by_id(get_node_rpc_method_id(p_method)); +bool Node::can_process() const { + ERR_FAIL_COND_V(!is_inside_tree(), false); + return _can_process(get_tree()->is_paused()); } -uint16_t Node::get_node_rset_property_id(const StringName &p_property) const { - for (int i = 0; i < data.rpc_properties.size(); i++) { - if (data.rpc_properties[i].name == p_property) { - // Returns `i` with the high bit set to 1 so we know that this id comes - // from the node and not the script. - return i | (1 << 15); +bool Node::_can_process(bool p_paused) const { + ProcessMode process_mode; + + if (data.process_mode == PROCESS_MODE_INHERIT) { + if (!data.process_owner) { + process_mode = PROCESS_MODE_PAUSABLE; + } else { + process_mode = data.process_owner->data.process_mode; } + } else { + process_mode = data.process_mode; } - return UINT16_MAX; -} -StringName Node::get_node_rset_property(const uint16_t p_rset_property_id) const { - // Make sure this is a node generated ID. - if (((1 << 15) & p_rset_property_id) > 0) { - int mid = (~(1 << 15)) & p_rset_property_id; - if (mid < data.rpc_properties.size()) - return data.rpc_properties[mid].name; + if (process_mode == PROCESS_MODE_DISABLED) { + return false; + } else if (process_mode == PROCESS_MODE_ALWAYS) { + return true; } - return StringName(); -} -MultiplayerAPI::RPCMode Node::get_node_rset_mode_by_id(const uint16_t p_rset_property_id) const { - if (((1 << 15) & p_rset_property_id) > 0) { - int mid = (~(1 << 15)) & p_rset_property_id; - if (mid < data.rpc_properties.size()) - return data.rpc_properties[mid].mode; + if (p_paused) { + return process_mode == PROCESS_MODE_WHEN_PAUSED; + } else { + return process_mode == PROCESS_MODE_PAUSABLE; } - return MultiplayerAPI::RPC_MODE_DISABLED; } -MultiplayerAPI::RPCMode Node::get_node_rset_mode(const StringName &p_property) const { - return get_node_rset_mode_by_id(get_node_rset_property_id(p_property)); -} +bool Node::_is_enabled() const { + ProcessMode process_mode; -String Node::get_rpc_md5() const { - String rpc_list; - for (int i = 0; i < data.rpc_methods.size(); i += 1) { - rpc_list += String(data.rpc_methods[i].name); - } - for (int i = 0; i < data.rpc_properties.size(); i += 1) { - rpc_list += String(data.rpc_properties[i].name); - } - if (get_script_instance()) { - Vector<ScriptNetData> rpc = get_script_instance()->get_rpc_methods(); - for (int i = 0; i < rpc.size(); i += 1) { - rpc_list += String(rpc[i].name); - } - rpc = get_script_instance()->get_rset_properties(); - for (int i = 0; i < rpc.size(); i += 1) { - rpc_list += String(rpc[i].name); + if (data.process_mode == PROCESS_MODE_INHERIT) { + if (!data.process_owner) { + process_mode = PROCESS_MODE_PAUSABLE; + } else { + process_mode = data.process_owner->data.process_mode; } - } - return rpc_list.md5_text(); -} - -bool Node::can_process_notification(int p_what) const { - switch (p_what) { - case NOTIFICATION_PHYSICS_PROCESS: return data.physics_process; - case NOTIFICATION_PROCESS: return data.idle_process; - case NOTIFICATION_INTERNAL_PROCESS: return data.idle_process_internal; - case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: return data.physics_process_internal; + } else { + process_mode = data.process_mode; } - return true; + return (process_mode != PROCESS_MODE_DISABLED); } -bool Node::can_process() const { - +bool Node::is_enabled() const { ERR_FAIL_COND_V(!is_inside_tree(), false); - - if (get_tree()->is_paused()) { - - if (data.pause_mode == PAUSE_MODE_STOP) - return false; - if (data.pause_mode == PAUSE_MODE_PROCESS) - return true; - if (data.pause_mode == PAUSE_MODE_INHERIT) { - - if (!data.pause_owner) - return false; //clearly no pause owner by default - - if (data.pause_owner->data.pause_mode == PAUSE_MODE_PROCESS) - return true; - - if (data.pause_owner->data.pause_mode == PAUSE_MODE_STOP) - return false; - } - } - - return true; + return _is_enabled(); } -float Node::get_physics_process_delta_time() const { - - if (data.tree) +double Node::get_physics_process_delta_time() const { + if (data.tree) { return data.tree->get_physics_process_time(); - else + } else { return 0; + } } -float Node::get_process_delta_time() const { - - if (data.tree) - return data.tree->get_idle_process_time(); - else +double Node::get_process_delta_time() const { + if (data.tree) { + return data.tree->get_process_time(); + } else { return 0; + } } -void Node::set_process(bool p_idle_process) { - - if (data.idle_process == p_idle_process) +void Node::set_process(bool p_process) { + if (data.process == p_process) { return; + } - data.idle_process = p_idle_process; - - if (data.idle_process) - add_to_group("idle_process", false); - else - remove_from_group("idle_process"); + data.process = p_process; - _change_notify("idle_process"); + if (data.process) { + add_to_group("process", false); + } else { + remove_from_group("process"); + } } bool Node::is_processing() const { - - return data.idle_process; + return data.process; } -void Node::set_process_internal(bool p_idle_process_internal) { - - if (data.idle_process_internal == p_idle_process_internal) +void Node::set_process_internal(bool p_process_internal) { + if (data.process_internal == p_process_internal) { return; + } - data.idle_process_internal = p_idle_process_internal; - - if (data.idle_process_internal) - add_to_group("idle_process_internal", false); - else - remove_from_group("idle_process_internal"); + data.process_internal = p_process_internal; - _change_notify("idle_process_internal"); + if (data.process_internal) { + add_to_group("process_internal", false); + } else { + remove_from_group("process_internal"); + } } bool Node::is_processing_internal() const { - - return data.idle_process_internal; + return data.process_internal; } void Node::set_process_priority(int p_priority) { @@ -919,11 +797,11 @@ void Node::set_process_priority(int p_priority) { } if (is_processing()) { - data.tree->make_group_changed("idle_process"); + data.tree->make_group_changed("process"); } if (is_processing_internal()) { - data.tree->make_group_changed("idle_process_internal"); + data.tree->make_group_changed("process_internal"); } if (is_physics_processing()) { @@ -936,23 +814,24 @@ void Node::set_process_priority(int p_priority) { } int Node::get_process_priority() const { - return data.process_priority; } void Node::set_process_input(bool p_enable) { - - if (p_enable == data.input) + if (p_enable == data.input) { return; + } data.input = p_enable; - if (!is_inside_tree()) + if (!is_inside_tree()) { return; + } - if (p_enable) + if (p_enable) { add_to_group("_vp_input" + itos(get_viewport()->get_instance_id())); - else + } else { remove_from_group("_vp_input" + itos(get_viewport()->get_instance_id())); + } } bool Node::is_processing_input() const { @@ -960,17 +839,19 @@ bool Node::is_processing_input() const { } void Node::set_process_unhandled_input(bool p_enable) { - - if (p_enable == data.unhandled_input) + if (p_enable == data.unhandled_input) { return; + } data.unhandled_input = p_enable; - if (!is_inside_tree()) + if (!is_inside_tree()) { return; + } - if (p_enable) + if (p_enable) { add_to_group("_vp_unhandled_input" + itos(get_viewport()->get_instance_id())); - else + } else { remove_from_group("_vp_unhandled_input" + itos(get_viewport()->get_instance_id())); + } } bool Node::is_processing_unhandled_input() const { @@ -978,17 +859,19 @@ bool Node::is_processing_unhandled_input() const { } void Node::set_process_unhandled_key_input(bool p_enable) { - - if (p_enable == data.unhandled_key_input) + if (p_enable == data.unhandled_key_input) { return; + } data.unhandled_key_input = p_enable; - if (!is_inside_tree()) + if (!is_inside_tree()) { return; + } - if (p_enable) + if (p_enable) { add_to_group("_vp_unhandled_key_input" + itos(get_viewport()->get_instance_id())); - else + } else { remove_from_group("_vp_unhandled_key_input" + itos(get_viewport()->get_instance_id())); + } } bool Node::is_processing_unhandled_key_input() const { @@ -996,46 +879,27 @@ bool Node::is_processing_unhandled_key_input() const { } StringName Node::get_name() const { - return data.name; } void Node::_set_name_nocheck(const StringName &p_name) { - data.name = p_name; } -String Node::invalid_character = ". : @ / \""; - -bool Node::_validate_node_name(String &p_name) { - String name = p_name; - Vector<String> chars = Node::invalid_character.split(" "); - for (int i = 0; i < chars.size(); i++) { - name = name.replace(chars[i], ""); - } - bool is_valid = name == p_name; - p_name = name; - return is_valid; -} - void Node::set_name(const String &p_name) { - - String name = p_name; - _validate_node_name(name); + String name = p_name.validate_node_name(); ERR_FAIL_COND(name == ""); data.name = name; if (data.parent) { - data.parent->_validate_child_name(this); } propagate_notification(NOTIFICATION_PATH_CHANGED); if (is_inside_tree()) { - - emit_signal("renamed"); + emit_signal(SNAME("renamed")); get_tree()->node_renamed(this); get_tree()->tree_changed(); } @@ -1049,13 +913,11 @@ void Node::init_node_hrcr() { } void Node::set_human_readable_collision_renaming(bool p_enabled) { - node_hrcr = p_enabled; } #ifdef TOOLS_ENABLED String Node::validate_child_name(Node *p_child) { - StringName name = p_child->data.name; _generate_serial_child_name(p_child, name); return name; @@ -1063,11 +925,9 @@ String Node::validate_child_name(Node *p_child) { #endif void Node::_validate_child_name(Node *p_child, bool p_force_human_readable) { - /* Make sure the name is unique */ if (node_hrcr || p_force_human_readable) { - //this approach to autoset node names is human readable but very slow //it's turned on while running in the editor @@ -1076,13 +936,12 @@ void Node::_validate_child_name(Node *p_child, bool p_force_human_readable) { p_child->data.name = name; } else { - //this approach to autoset node names is fast but not as readable //it's the default and reserves the '@' character for unique names. bool unique = true; - if (p_child->data.name == StringName() || p_child->data.name.operator String()[0] == '@') { + if (p_child->data.name == StringName()) { //new unique name must be assigned unique = false; } else { @@ -1091,8 +950,9 @@ void Node::_validate_child_name(Node *p_child, bool p_force_human_readable) { int cc = data.children.size(); for (int i = 0; i < cc; i++) { - if (children[i] == p_child) + if (children[i] == p_child) { continue; + } if (children[i]->data.name == p_child->data.name) { unique = false; break; @@ -1101,7 +961,6 @@ void Node::_validate_child_name(Node *p_child, bool p_force_human_readable) { } if (!unique) { - ERR_FAIL_COND(!node_hrcr_count.ref()); String name = "@" + String(p_child->get_name()) + "@" + itos(node_hrcr_count.get()); p_child->data.name = name; @@ -1111,7 +970,6 @@ void Node::_validate_child_name(Node *p_child, bool p_force_human_readable) { // Return s + 1 as if it were an integer String increase_numeric_string(const String &s) { - String res = s; bool carry = res.length() > 0; @@ -1119,7 +977,7 @@ String increase_numeric_string(const String &s) { if (!carry) { break; } - CharType n = s[i]; + char32_t n = s[i]; if (n == '9') { // keep carry as true: 9 + 1 res[i] = '0'; } else { @@ -1136,13 +994,12 @@ String increase_numeric_string(const String &s) { } void Node::_generate_serial_child_name(const Node *p_child, StringName &name) const { - if (name == StringName()) { - //no name and a new nade is needed, create one. + //no name and a new name is needed, create one. name = p_child->get_class(); // Adjust casing according to project setting. The current type name is expected to be in PascalCase. - switch (ProjectSettings::get_singleton()->get("node/name_casing").operator int()) { + switch (ProjectSettings::get_singleton()->get("editor/node_naming/name_casing").operator int()) { case NAME_CASING_PASCAL_CASE: break; case NAME_CASING_CAMEL_CASE: { @@ -1161,11 +1018,10 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co 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 its already a child + if (children_ptr[i] == p_child) { //exclude self in renaming if it's already a child continue; } if (children_ptr[i]->data.name == name) { @@ -1182,7 +1038,7 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co String name_string = name; String nums; for (int i = name_string.length() - 1; i >= 0; i--) { - CharType n = name_string[i]; + char32_t n = name_string[i]; if (n >= '0' && n <= '9') { nums = String::chr(name_string[i]) + nums; } else { @@ -1235,6 +1091,10 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) { p_child->data.pos = data.children.size(); data.children.push_back(p_child); p_child->data.parent = this; + + if (data.internal_children_back > 0) { + _move_child(p_child, data.children.size() - data.internal_children_back - 1); + } p_child->notification(NOTIFICATION_PARENTED); if (data.tree) { @@ -1247,44 +1107,53 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) { add_child_notify(p_child); } -void Node::add_child(Node *p_child, bool p_legible_unique_name) { - +void Node::add_child(Node *p_child, bool p_legible_unique_name, InternalMode p_internal) { ERR_FAIL_NULL(p_child); - ERR_FAIL_COND_MSG(p_child == this, "Can't add child '" + p_child->get_name() + "' to itself."); // adding to itself! - ERR_FAIL_COND_MSG(p_child->data.parent, "Can't add child '" + p_child->get_name() + "' to '" + get_name() + "', already has a parent '" + p_child->data.parent->get_name() + "'."); //Fail if node has a parent + ERR_FAIL_COND_MSG(p_child == this, vformat("Can't add child '%s' to itself.", p_child->get_name())); // adding to itself! + ERR_FAIL_COND_MSG(p_child->data.parent, vformat("Can't add child '%s' to '%s', already has a parent '%s'.", p_child->get_name(), get_name(), p_child->data.parent->get_name())); //Fail if node has a parent +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(p_child->is_ancestor_of(this), vformat("Can't add child '%s' to '%s' as it would result in a cyclic dependency since '%s' is already a parent of '%s'.", p_child->get_name(), get_name(), p_child->get_name(), get_name())); +#endif ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, add_node() failed. Consider using call_deferred(\"add_child\", child) instead."); - /* Validate name */ _validate_child_name(p_child, p_legible_unique_name); - _add_child_nocheck(p_child, p_child->data.name); -} - -void Node::add_child_below_node(Node *p_node, Node *p_child, bool p_legible_unique_name) { - ERR_FAIL_NULL(p_node); - ERR_FAIL_NULL(p_child); + 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(p_child, p_legible_unique_name); +void Node::add_sibling(Node *p_sibling, bool p_legible_unique_name) { + ERR_FAIL_NULL(p_sibling); + ERR_FAIL_NULL(data.parent); + 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 call_deferred(\"add_sibling\", sibling) instead."); - if (is_a_parent_of(p_node)) { - move_child(p_child, p_node->get_index() + 1); - } else { - WARN_PRINT("Cannot move under node " + p_node->get_name() + " as " + p_child->get_name() + " does not share a parent."); + 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_legible_unique_name, internal); + data.parent->_move_child(p_sibling, get_index() + 1); } void Node::_propagate_validate_owner() { - if (data.owner) { - bool found = false; Node *parent = data.parent; while (parent) { - if (parent == data.owner) { - found = true; break; } @@ -1293,20 +1162,17 @@ void Node::_propagate_validate_owner() { } if (!found) { - data.owner->data.owned.erase(data.OW); data.owner = nullptr; } } for (int i = 0; i < data.children.size(); i++) { - data.children[i]->_propagate_validate_owner(); } } void Node::remove_child(Node *p_child) { - ERR_FAIL_NULL(p_child); ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, remove_node() failed. Consider using call_deferred(\"remove_child\", child) instead."); @@ -1322,19 +1188,22 @@ void Node::remove_child(Node *p_child) { 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, "Cannot remove child node " + p_child->get_name() + " as it is not a child of this node."); + 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 (data.scene) { does not matter + // 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--; + } p_child->_set_tree(nullptr); //} @@ -1349,7 +1218,6 @@ void Node::remove_child(Node *p_child) { children = data.children.ptrw(); for (int i = idx; i < child_count; i++) { - children[i]->data.pos = i; children[i]->notification(NOTIFICATION_MOVED_IN_PARENT); } @@ -1365,32 +1233,45 @@ void Node::remove_child(Node *p_child) { } } -int Node::get_child_count() const { - - return data.children.size(); +int Node::get_child_count(bool p_include_internal) const { + if (p_include_internal) { + return data.children.size(); + } else { + return data.children.size() - data.internal_children_front - data.internal_children_back; + } } -Node *Node::get_child(int p_index) const { - ERR_FAIL_INDEX_V(p_index, data.children.size(), nullptr); - - return data.children[p_index]; +Node *Node::get_child(int p_index, bool p_include_internal) const { + if (p_include_internal) { + if (p_index < 0) { + p_index += data.children.size(); + } + ERR_FAIL_INDEX_V(p_index, data.children.size(), nullptr); + return data.children[p_index]; + } else { + if (p_index < 0) { + p_index += data.children.size() - data.internal_children_front - data.internal_children_back; + } + 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]; + } } 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) + if (cd[i]->data.name == p_name) { return cd[i]; + } } return nullptr; } Node *Node::get_node_or_null(const NodePath &p_path) const { - if (p_path.is_empty()) { return nullptr; } @@ -1403,14 +1284,13 @@ Node *Node::get_node_or_null(const NodePath &p_path) const { if (!p_path.is_absolute()) { current = const_cast<Node *>(this); //start from this } else { - root = const_cast<Node *>(this); - while (root->data.parent) + while (root->data.parent) { root = root->data.parent; //start from root + } } for (int i = 0; i < p_path.get_name_count(); i++) { - StringName name = p_path.get_name(i); Node *next = nullptr; @@ -1420,25 +1300,23 @@ Node *Node::get_node_or_null(const NodePath &p_path) const { } else if (name == SceneStringNames::get_singleton()->doubledot) { // .. - if (current == nullptr || !current->data.parent) + if (current == nullptr || !current->data.parent) { return nullptr; + } next = current->data.parent; } else if (current == nullptr) { - - if (name == root->get_name()) + if (name == root->get_name()) { next = root; + } } 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; } @@ -1454,63 +1332,69 @@ Node *Node::get_node_or_null(const NodePath &p_path) const { } Node *Node::get_node(const NodePath &p_path) const { - Node *node = get_node_or_null(p_path); - ERR_FAIL_COND_V_MSG(!node, nullptr, "Node not found: " + p_path + "."); + + if (p_path.is_absolute()) { + ERR_FAIL_COND_V_MSG(!node, nullptr, + vformat(R"(Node not found: "%s" (absolute path attempted from "%s").)", p_path, get_path())); + } else { + ERR_FAIL_COND_V_MSG(!node, nullptr, + vformat(R"(Node not found: "%s" (relative to "%s").)", p_path, get_path())); + } + return node; } bool Node::has_node(const NodePath &p_path) const { - return get_node_or_null(p_path) != nullptr; } Node *Node::find_node(const String &p_mask, bool p_recursive, bool p_owned) const { - Node *const *cptr = data.children.ptr(); int ccount = data.children.size(); for (int i = 0; i < ccount; i++) { - if (p_owned && !cptr[i]->data.owner) + if (p_owned && !cptr[i]->data.owner) { continue; - if (cptr[i]->data.name.operator String().match(p_mask)) + } + if (cptr[i]->data.name.operator String().match(p_mask)) { return cptr[i]; + } - if (!p_recursive) + if (!p_recursive) { continue; + } Node *ret = cptr[i]->find_node(p_mask, true, p_owned); - if (ret) + if (ret) { return ret; + } } return nullptr; } Node *Node::get_parent() const { - return data.parent; } Node *Node::find_parent(const String &p_mask) const { - Node *p = data.parent; while (p) { - - if (p->data.name.operator String().match(p_mask)) + if (p->data.name.operator String().match(p_mask)) { return p; + } p = p->data.parent; } return nullptr; } -bool Node::is_a_parent_of(const Node *p_node) const { - +bool Node::is_ancestor_of(const Node *p_node) const { ERR_FAIL_NULL_V(p_node, false); Node *p = p_node->data.parent; while (p) { - - if (p == this) + if (p == this) { return true; + } p = p->data.parent; } @@ -1518,7 +1402,6 @@ bool Node::is_a_parent_of(const Node *p_node) const { } bool Node::is_greater_than(const Node *p_node) const { - ERR_FAIL_NULL_V(p_node, false); ERR_FAIL_COND_V(!data.inside_tree, false); ERR_FAIL_COND_V(!p_node->data.inside_tree, false); @@ -1561,7 +1444,6 @@ bool Node::is_greater_than(const Node *p_node) const { bool res; while (true) { - // using -2 since out-of-tree or nonroot nodes have -1 int this_idx = (idx >= data.depth) ? -2 : this_stack[idx]; int that_idx = (idx >= p_node->data.depth) ? -2 : that_stack[idx]; @@ -1583,18 +1465,19 @@ bool Node::is_greater_than(const Node *p_node) const { } void Node::get_owned_by(Node *p_by, List<Node *> *p_owned) { - - if (data.owner == p_by) + if (data.owner == p_by) { p_owned->push_back(this); + } - for (int i = 0; i < get_child_count(); i++) + for (int i = 0; i < get_child_count(); i++) { get_child(i)->get_owned_by(p_by, p_owned); + } } void Node::_set_owner_nocheck(Node *p_owner) { - - if (data.owner == p_owner) + if (data.owner == p_owner) { return; + } ERR_FAIL_COND(data.owner); data.owner = p_owner; @@ -1603,9 +1486,7 @@ void Node::_set_owner_nocheck(Node *p_owner) { } void Node::set_owner(Node *p_owner) { - if (data.owner) { - data.owner->data.owned.erase(data.OW); data.OW = nullptr; data.owner = nullptr; @@ -1613,14 +1494,14 @@ void Node::set_owner(Node *p_owner) { ERR_FAIL_COND(p_owner == this); - if (!p_owner) + if (!p_owner) { return; + } Node *check = this->get_parent(); bool owner_valid = false; while (check) { - if (check == p_owner) { owner_valid = true; break; @@ -1633,22 +1514,21 @@ void Node::set_owner(Node *p_owner) { _set_owner_nocheck(p_owner); } -Node *Node::get_owner() const { +Node *Node::get_owner() const { return data.owner; } Node *Node::find_common_parent_with(const Node *p_node) const { - - if (this == p_node) + if (this == p_node) { return const_cast<Node *>(p_node); + } Set<const Node *> visited; const Node *n = this; while (n) { - visited.insert(n); n = n->data.parent; } @@ -1656,31 +1536,31 @@ Node *Node::find_common_parent_with(const Node *p_node) const { const Node *common_parent = p_node; while (common_parent) { - - if (visited.has(common_parent)) + if (visited.has(common_parent)) { break; + } common_parent = common_parent->data.parent; } - if (!common_parent) + if (!common_parent) { return nullptr; + } return const_cast<Node *>(common_parent); } NodePath Node::get_path_to(const Node *p_node) const { - ERR_FAIL_NULL_V(p_node, NodePath()); - if (this == p_node) + if (this == p_node) { return NodePath("."); + } Set<const Node *> visited; const Node *n = this; while (n) { - visited.insert(n); n = n->data.parent; } @@ -1688,9 +1568,9 @@ NodePath Node::get_path_to(const Node *p_node) const { const Node *common_parent = p_node; while (common_parent) { - - if (visited.has(common_parent)) + if (visited.has(common_parent)) { break; + } common_parent = common_parent->data.parent; } @@ -1703,7 +1583,6 @@ NodePath Node::get_path_to(const Node *p_node) const { n = p_node; while (n != common_parent) { - path.push_back(n->get_name()); n = n->data.parent; } @@ -1712,22 +1591,21 @@ NodePath Node::get_path_to(const Node *p_node) const { StringName up = String(".."); while (n != common_parent) { - path.push_back(up); n = n->data.parent; } - path.invert(); + path.reverse(); return NodePath(path, false); } NodePath Node::get_path() const { - ERR_FAIL_COND_V_MSG(!is_inside_tree(), NodePath(), "Cannot get path of node as it is not in a scene tree."); - if (data.path_cache) + if (data.path_cache) { return *data.path_cache; + } const Node *n = this; @@ -1738,7 +1616,7 @@ NodePath Node::get_path() const { n = n->data.parent; } - path.invert(); + path.reverse(); data.path_cache = memnew(NodePath(path, true)); @@ -1746,16 +1624,15 @@ NodePath Node::get_path() const { } bool Node::is_in_group(const StringName &p_identifier) const { - return data.grouped.has(p_identifier); } void Node::add_to_group(const StringName &p_identifier, bool p_persistent) { - ERR_FAIL_COND(!p_identifier.operator String().length()); - if (data.grouped.has(p_identifier)) + if (data.grouped.has(p_identifier)) { return; + } GroupData gd; @@ -1771,33 +1648,31 @@ void Node::add_to_group(const StringName &p_identifier, bool p_persistent) { } void Node::remove_from_group(const StringName &p_identifier) { - ERR_FAIL_COND(!data.grouped.has(p_identifier)); Map<StringName, GroupData>::Element *E = data.grouped.find(p_identifier); ERR_FAIL_COND(!E); - if (data.tree) + if (data.tree) { data.tree->remove_from_group(E->key(), this); + } data.grouped.erase(E); } Array Node::_get_groups() const { - Array groups; List<GroupInfo> gi; get_groups(&gi); - for (List<GroupInfo>::Element *E = gi.front(); E; E = E->next()) { - groups.push_back(E->get().name); + for (const GroupInfo &E : gi) { + groups.push_back(E.name); } return groups; } void Node::get_groups(List<GroupInfo> *p_groups) const { - for (const Map<StringName, GroupData>::Element *E = data.grouped.front(); E; E = E->next()) { GroupInfo gi; gi.name = E->key(); @@ -1807,7 +1682,6 @@ void Node::get_groups(List<GroupInfo> *p_groups) const { } int Node::get_persistent_group_count() const { - int count = 0; for (const Map<StringName, GroupData>::Element *E = data.grouped.front(); E; E = E->next()) { @@ -1818,8 +1692,8 @@ int Node::get_persistent_group_count() const { return count; } -void Node::_print_tree_pretty(const String &prefix, const bool last) { +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++) { @@ -1833,21 +1707,19 @@ void Node::print_tree_pretty() { } void Node::print_tree() { - _print_tree(this); } 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++) + for (int i = 0; i < data.children.size(); i++) { data.children[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); } @@ -1856,71 +1728,83 @@ void Node::_propagate_reverse_notification(int p_notification) { } void Node::_propagate_deferred_notification(int p_notification, bool p_reverse) { - ERR_FAIL_COND(!is_inside_tree()); data.blocked++; - if (!p_reverse) + if (!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); } - if (p_reverse) + if (p_reverse) { MessageQueue::get_singleton()->push_notification(this, p_notification); + } data.blocked--; } 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); } data.blocked--; } void Node::propagate_call(const StringName &p_method, const Array &p_args, const bool p_parent_first) { - data.blocked++; - if (p_parent_first && has_method(p_method)) + if (p_parent_first && has_method(p_method)) { 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); } - if (!p_parent_first && has_method(p_method)) + if (!p_parent_first && has_method(p_method)) { callv(p_method, p_args); + } data.blocked--; } void Node::_propagate_replace_owner(Node *p_owner, Node *p_by_owner) { - if (get_owner() == p_owner) + if (get_owner() == p_owner) { set_owner(p_by_owner); + } data.blocked++; - for (int i = 0; i < data.children.size(); i++) + for (int i = 0; i < data.children.size(); i++) { data.children[i]->_propagate_replace_owner(p_owner, p_by_owner); + } data.blocked--; } -int Node::get_index() const { +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.pos - data.parent->data.internal_children_front; + } return data.pos; } -void Node::remove_and_skip() { +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(); + tween->bind_node(this); + return tween; +} +void Node::remove_and_skip() { ERR_FAIL_COND(!data.parent); Node *new_owner = get_owner(); @@ -1928,12 +1812,12 @@ void Node::remove_and_skip() { List<Node *> children; while (true) { - bool clear = true; for (int i = 0; i < data.children.size(); i++) { Node *c_node = data.children[i]; - if (!c_node->get_owner()) + if (!c_node->get_owner()) { continue; + } remove_child(c_node); c_node->_propagate_replace_owner(this, nullptr); @@ -1942,12 +1826,12 @@ void Node::remove_and_skip() { break; } - if (clear) + if (clear) { break; + } } - while (!children.empty()) { - + while (!children.is_empty()) { Node *c_node = children.front()->get(); data.parent->add_child(c_node); c_node->_propagate_replace_owner(nullptr, new_owner); @@ -1958,130 +1842,134 @@ void Node::remove_and_skip() { } void Node::set_filename(const String &p_filename) { - data.filename = p_filename; } -String Node::get_filename() const { +String Node::get_filename() const { return data.filename; } void Node::set_editor_description(const String &p_editor_description) { - - set_meta("_editor_description_", p_editor_description); + data.editor_description = p_editor_description; } -String Node::get_editor_description() const { - if (has_meta("_editor_description_")) { - return get_meta("_editor_description_"); - } else { - return ""; - } +String Node::get_editor_description() const { + return data.editor_description; } void Node::set_editable_instance(Node *p_node, bool p_editable) { - ERR_FAIL_NULL(p_node); - ERR_FAIL_COND(!is_a_parent_of(p_node)); - NodePath p = get_path_to(p_node); + ERR_FAIL_COND(!is_ancestor_of(p_node)); if (!p_editable) { - data.editable_instances.erase(p); + p_node->data.editable_instance = false; // Avoid this flag being needlessly saved; - // also give more visual feedback if editable children is re-enabled + // also give more visual feedback if editable children are re-enabled set_display_folded(false); } else { - data.editable_instances[p] = true; + p_node->data.editable_instance = true; } } bool Node::is_editable_instance(const Node *p_node) const { - - if (!p_node) - return false; //easier, null is never editable :) - ERR_FAIL_COND_V(!is_a_parent_of(p_node), false); - NodePath p = get_path_to(p_node); - return data.editable_instances.has(p); + if (!p_node) { + return false; // Easier, null is never editable. :) + } + ERR_FAIL_COND_V(!is_ancestor_of(p_node), false); + return p_node->data.editable_instance; } -void Node::set_editable_instances(const HashMap<NodePath, int> &p_editable_instances) { +Node *Node::get_deepest_editable_node(Node *p_start_node) const { + ERR_FAIL_NULL_V(p_start_node, nullptr); + ERR_FAIL_COND_V(!is_ancestor_of(p_start_node), p_start_node); - data.editable_instances = p_editable_instances; + Node const *iterated_item = p_start_node; + Node *node = p_start_node; + + while (iterated_item->get_owner() && iterated_item->get_owner() != this) { + if (!is_editable_instance(iterated_item->get_owner())) { + node = iterated_item->get_owner(); + } + + iterated_item = iterated_item->get_owner(); + } + + return node; } -HashMap<NodePath, int> Node::get_editable_instances() const { +String Node::to_string() { + if (get_script_instance()) { + bool valid; + String ret = get_script_instance()->to_string(&valid); + if (valid) { + return ret; + } + } - return data.editable_instances; + return (get_name() ? String(get_name()) + ":" : "") + Object::to_string(); } void Node::set_scene_instance_state(const Ref<SceneState> &p_state) { - data.instance_state = p_state; } Ref<SceneState> Node::get_scene_instance_state() const { - return data.instance_state; } void Node::set_scene_inherited_state(const Ref<SceneState> &p_state) { - data.inherited_state = p_state; } Ref<SceneState> Node::get_scene_inherited_state() const { - return data.inherited_state; } void Node::set_scene_instance_load_placeholder(bool p_enable) { - data.use_placeholder = p_enable; } bool Node::get_scene_instance_load_placeholder() const { - return data.use_placeholder; } Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const { - Node *node = nullptr; - bool instanced = false; + bool instantiated = false; if (Object::cast_to<InstancePlaceholder>(this)) { - const InstancePlaceholder *ip = Object::cast_to<const InstancePlaceholder>(this); InstancePlaceholder *nip = memnew(InstancePlaceholder); nip->set_instance_path(ip->get_instance_path()); node = nip; } else if ((p_flags & DUPLICATE_USE_INSTANCING) && get_filename() != String()) { - Ref<PackedScene> res = ResourceLoader::load(get_filename()); ERR_FAIL_COND_V(res.is_null(), nullptr); PackedScene::GenEditState ges = PackedScene::GEN_EDIT_STATE_DISABLED; #ifdef TOOLS_ENABLED - if (p_flags & DUPLICATE_FROM_EDITOR) + if (p_flags & DUPLICATE_FROM_EDITOR) { ges = PackedScene::GEN_EDIT_STATE_INSTANCE; + } #endif - node = res->instance(ges); + node = res->instantiate(ges); ERR_FAIL_COND_V(!node, nullptr); - instanced = true; + instantiated = true; } else { - - Object *obj = ClassDB::instance(get_class()); + Object *obj = ClassDB::instantiate(get_class()); ERR_FAIL_COND_V(!obj, nullptr); node = Object::cast_to<Node>(obj); - if (!node) + if (!node) { memdelete(obj); + } ERR_FAIL_COND_V(!node, nullptr); } if (get_filename() != "") { //an instance node->set_filename(get_filename()); + node->data.editable_instance = data.editable_instance; } StringName script_property_name = CoreStringNames::get_singleton()->_script; @@ -2090,29 +1978,35 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const List<const Node *> node_tree; node_tree.push_front(this); - if (instanced) { - // Since nodes in the instanced hierarchy won't be duplicated explicitly, we need to make an inventory - // of all the nodes in the tree of the instanced scene in order to transfer the values of the properties + if (instantiated) { + // Since nodes in the instantiated hierarchy won't be duplicated explicitly, we need to make an inventory + // of all the nodes in the tree of the instantiated scene in order to transfer the values of the properties + + Vector<const Node *> instance_roots; + instance_roots.push_back(this); for (List<const Node *>::Element *N = node_tree.front(); N; N = N->next()) { for (int i = 0; i < N->get()->get_child_count(); ++i) { - Node *descendant = N->get()->get_child(i); - // Skip nodes not really belonging to the instanced hierarchy; they'll be processed normally later - // but remember non-instanced nodes that are hidden below instanced ones - if (descendant->data.owner != this) { - if (descendant->get_parent() && descendant->get_parent() != this && descendant->get_parent()->data.owner == this && descendant->data.owner != descendant->get_parent()) + // Skip nodes not really belonging to the instantiated hierarchy; they'll be processed normally later + // but remember non-instantiated nodes that are hidden below instantiated ones + if (!instance_roots.has(descendant->get_owner())) { + if (descendant->get_parent() && descendant->get_parent() != this && descendant->data.owner != descendant->get_parent()) { hidden_roots.push_back(descendant); + } continue; } node_tree.push_back(descendant); + + if (descendant->get_filename() != "" && instance_roots.has(descendant->get_owner())) { + instance_roots.push_back(descendant); + } } } } for (List<const Node *>::Element *N = node_tree.front(); N; N = N->next()) { - Node *current_node = node->get_node(get_path_to(N->get())); ERR_CONTINUE(!current_node); @@ -2127,25 +2021,24 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const List<PropertyInfo> plist; N->get()->get_property_list(&plist); - for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) + for (const PropertyInfo &E : plist) { + if (!(E.usage & PROPERTY_USAGE_STORAGE)) { continue; - String name = E->get().name; - if (name == script_property_name) + } + String name = E.name; + if (name == script_property_name) { continue; + } Variant value = N->get()->get(name).duplicate(true); - if (E->get().usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE) { - + if (E.usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE) { Resource *res = Object::cast_to<Resource>(value); if (res) { // Duplicate only if it's a resource current_node->set(name, res->duplicate()); } } else { - current_node->set(name, value); } } @@ -2156,34 +2049,35 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const } #ifdef TOOLS_ENABLED - if ((p_flags & DUPLICATE_FROM_EDITOR) && r_duplimap) + if ((p_flags & DUPLICATE_FROM_EDITOR) && r_duplimap) { r_duplimap->insert(this, node); + } #endif if (p_flags & DUPLICATE_GROUPS) { List<GroupInfo> gi; get_groups(&gi); - for (List<GroupInfo>::Element *E = gi.front(); E; E = E->next()) { - + for (const GroupInfo &E : gi) { #ifdef TOOLS_ENABLED - if ((p_flags & DUPLICATE_FROM_EDITOR) && !E->get().persistent) + if ((p_flags & DUPLICATE_FROM_EDITOR) && !E.persistent) { continue; + } #endif - node->add_to_group(E->get().name, E->get().persistent); + node->add_to_group(E.name, E.persistent); } } for (int i = 0; i < get_child_count(); i++) { - - if (get_child(i)->data.parent_owned) + if (get_child(i)->data.parent_owned) { continue; - if (instanced && get_child(i)->data.owner == this) + } + if (instantiated && get_child(i)->data.owner == this) { continue; //part of instance + } Node *dup = get_child(i)->_duplicate(p_flags, r_duplimap); if (!dup) { - memdelete(node); return nullptr; } @@ -2194,27 +2088,23 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const } } - for (List<const Node *>::Element *E = hidden_roots.front(); E; E = E->next()) { - - Node *parent = node->get_node(get_path_to(E->get()->data.parent)); + for (const Node *&E : hidden_roots) { + Node *parent = node->get_node(get_path_to(E->data.parent)); if (!parent) { - memdelete(node); return nullptr; } - Node *dup = E->get()->_duplicate(p_flags, r_duplimap); + Node *dup = E->_duplicate(p_flags, r_duplimap); if (!dup) { - memdelete(node); return nullptr; } parent->add_child(dup); - int pos = E->get()->get_index(); + int pos = E->get_index(); if (pos < parent->get_child_count() - 1) { - parent->move_child(dup, pos); } } @@ -2223,7 +2113,6 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const } Node *Node::duplicate(int p_flags) const { - Node *dupe = _duplicate(p_flags); if (dupe && (p_flags & DUPLICATE_SIGNALS)) { @@ -2235,9 +2124,17 @@ Node *Node::duplicate(int p_flags) const { #ifdef TOOLS_ENABLED Node *Node::duplicate_from_editor(Map<const Node *, Node *> &r_duplimap) const { + return duplicate_from_editor(r_duplimap, Map<RES, RES>()); +} +Node *Node::duplicate_from_editor(Map<const Node *, Node *> &r_duplimap, const Map<RES, RES> &p_resource_remap) const { Node *dupe = _duplicate(DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_USE_INSTANCING | DUPLICATE_FROM_EDITOR, &r_duplimap); + // This is used by SceneTreeDock's paste functionality. When pasting to foreign scene, resources are duplicated. + if (!p_resource_remap.is_empty()) { + remap_node_resources(dupe, p_resource_remap); + } + // Duplication of signals must happen after all the node descendants have been copied, // because re-targeting of connections from some descendant to another is not possible // if the emitter node comes later in tree order than the receiver @@ -2245,187 +2142,121 @@ Node *Node::duplicate_from_editor(Map<const Node *, Node *> &r_duplimap) const { return dupe; } -#endif - -void Node::_duplicate_and_reown(Node *p_new_parent, const Map<Node *, Node *> &p_reown_map) const { - if (get_owner() != get_parent()->get_owner()) - return; - - Node *node = nullptr; +void Node::remap_node_resources(Node *p_node, const Map<RES, RES> &p_resource_remap) const { + List<PropertyInfo> props; + p_node->get_property_list(&props); - if (get_filename() != "") { - - Ref<PackedScene> res = ResourceLoader::load(get_filename()); - ERR_FAIL_COND_MSG(res.is_null(), "Cannot load scene: " + get_filename()); - node = res->instance(); - ERR_FAIL_COND(!node); - } else { + for (const PropertyInfo &E : props) { + if (!(E.usage & PROPERTY_USAGE_STORAGE)) { + continue; + } - Object *obj = ClassDB::instance(get_class()); - ERR_FAIL_COND_MSG(!obj, "Node: Could not duplicate: " + String(get_class()) + "."); - node = Object::cast_to<Node>(obj); - if (!node) { - memdelete(obj); - ERR_FAIL_MSG("Node: Could not duplicate: " + String(get_class()) + "."); + Variant v = p_node->get(E.name); + if (v.is_ref()) { + RES res = v; + if (res.is_valid()) { + if (p_resource_remap.has(res)) { + p_node->set(E.name, p_resource_remap[res]); + remap_nested_resources(res, p_resource_remap); + } + } } } - List<PropertyInfo> plist; - - get_property_list(&plist); - - for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) - continue; - String name = E->get().name; - - Variant value = get(name).duplicate(true); - - node->set(name, value); + for (int i = 0; i < p_node->get_child_count(); i++) { + remap_node_resources(p_node->get_child(i), p_resource_remap); } +} - List<GroupInfo> groups; - get_groups(&groups); - - for (List<GroupInfo>::Element *E = groups.front(); E; E = E->next()) - node->add_to_group(E->get().name, E->get().persistent); - - node->set_name(get_name()); - p_new_parent->add_child(node); - - Node *owner = get_owner(); +void Node::remap_nested_resources(RES p_resource, const Map<RES, RES> &p_resource_remap) const { + List<PropertyInfo> props; + p_resource->get_property_list(&props); - if (p_reown_map.has(owner)) - owner = p_reown_map[owner]; + for (const PropertyInfo &E : props) { + if (!(E.usage & PROPERTY_USAGE_STORAGE)) { + continue; + } - if (owner) { - NodePath p = get_path_to(owner); - if (owner != this) { - Node *new_owner = node->get_node(p); - if (new_owner) { - node->set_owner(new_owner); + Variant v = p_resource->get(E.name); + if (v.is_ref()) { + RES res = v; + if (res.is_valid()) { + if (p_resource_remap.has(res)) { + p_resource->set(E.name, p_resource_remap[res]); + remap_nested_resources(res, p_resource_remap); + } } } } - - for (int i = 0; i < get_child_count(); i++) { - - get_child(i)->_duplicate_and_reown(node, p_reown_map); - } } +#endif // Duplication of signals must happen after all the node descendants have been copied, // because re-targeting of connections from some descendant to another is not possible // if the emitter node comes later in tree order than the receiver void Node::_duplicate_signals(const Node *p_original, Node *p_copy) const { - - if (this != p_original && (get_owner() != p_original && get_owner() != p_original->get_owner())) + if ((this != p_original) && !(p_original->is_ancestor_of(this))) { return; + } - List<Connection> conns; - get_all_signal_connections(&conns); + List<const Node *> process_list; + process_list.push_back(this); + while (!process_list.is_empty()) { + const Node *n = process_list.front()->get(); + process_list.pop_front(); - for (List<Connection>::Element *E = conns.front(); E; E = E->next()) { + List<Connection> conns; + n->get_all_signal_connections(&conns); - if (E->get().flags & CONNECT_PERSIST) { - //user connected - NodePath p = p_original->get_path_to(this); - Node *copy = p_copy->get_node(p); + for (const Connection &E : conns) { + if (E.flags & CONNECT_PERSIST) { + //user connected + NodePath p = p_original->get_path_to(n); + Node *copy = p_copy->get_node(p); - Node *target = Object::cast_to<Node>(E->get().callable.get_object()); - if (!target) { - continue; - } - NodePath ptarget = p_original->get_path_to(target); + Node *target = Object::cast_to<Node>(E.callable.get_object()); + if (!target) { + continue; + } + NodePath ptarget = p_original->get_path_to(target); - Node *copytarget = target; + Node *copytarget = target; - // Attempt to find a path to the duplicate target, if it seems it's not part - // of the duplicated and not yet parented hierarchy then at least try to connect - // to the same target as the original + // Attempt to find a path to the duplicate target, if it seems it's not part + // of the duplicated and not yet parented hierarchy then at least try to connect + // to the same target as the original - if (p_copy->has_node(ptarget)) - copytarget = p_copy->get_node(ptarget); + if (p_copy->has_node(ptarget)) { + copytarget = p_copy->get_node(ptarget); + } - if (copy && copytarget) { - const Callable copy_callable = Callable(copytarget, E->get().callable.get_method()); - if (!copy->is_connected(E->get().signal.get_name(), copy_callable)) { - copy->connect(E->get().signal.get_name(), copy_callable, E->get().binds, E->get().flags); + if (copy && copytarget) { + const Callable copy_callable = Callable(copytarget, E.callable.get_method()); + if (!copy->is_connected(E.signal.get_name(), copy_callable)) { + copy->connect(E.signal.get_name(), copy_callable, E.binds, E.flags); + } } } } - } - - for (int i = 0; i < get_child_count(); i++) { - get_child(i)->_duplicate_signals(p_original, p_copy); - } -} - -Node *Node::duplicate_and_reown(const Map<Node *, Node *> &p_reown_map) const { - - ERR_FAIL_COND_V(get_filename() != "", nullptr); - - Object *obj = ClassDB::instance(get_class()); - ERR_FAIL_COND_V_MSG(!obj, nullptr, "Node: Could not duplicate: " + String(get_class()) + "."); - - Node *node = Object::cast_to<Node>(obj); - if (!node) { - memdelete(obj); - ERR_FAIL_V_MSG(nullptr, "Node: Could not duplicate: " + String(get_class()) + "."); - } - node->set_name(get_name()); - - List<PropertyInfo> plist; - - get_property_list(&plist); - - for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) - continue; - String name = E->get().name; - node->set(name, get(name)); - } - - List<GroupInfo> groups; - get_groups(&groups); - - for (List<GroupInfo>::Element *E = groups.front(); E; E = E->next()) - node->add_to_group(E->get().name, E->get().persistent); - - for (int i = 0; i < get_child_count(); i++) { - get_child(i)->_duplicate_and_reown(node, p_reown_map); + for (int i = 0; i < n->get_child_count(); i++) { + process_list.push_back(n->get_child(i)); + } } - - // Duplication of signals must happen after all the node descendants have been copied, - // because re-targeting of connections from some descendant to another is not possible - // if the emitter node comes later in tree order than the receiver - _duplicate_signals(this, node); - return node; } static void find_owned_by(Node *p_by, Node *p_node, List<Node *> *p_owned) { - - if (p_node->get_owner() == p_by) + if (p_node->get_owner() == p_by) { p_owned->push_back(p_node); + } for (int i = 0; i < p_node->get_child_count(); i++) { - find_owned_by(p_by, p_node->get_child(i), p_owned); } } -struct _NodeReplaceByPair { - - String name; - Variant value; -}; - -void Node::replace_by(Node *p_node, bool p_keep_data) { - +void Node::replace_by(Node *p_node, bool p_keep_groups) { ERR_FAIL_NULL(p_node); ERR_FAIL_COND(p_node->data.parent); @@ -2433,48 +2264,33 @@ void Node::replace_by(Node *p_node, bool p_keep_data) { List<Node *> owned_by_owner; Node *owner = (data.owner == this) ? p_node : data.owner; - List<_NodeReplaceByPair> replace_data; - - if (p_keep_data) { - - List<PropertyInfo> plist; - get_property_list(&plist); - - for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - - _NodeReplaceByPair rd; - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) - continue; - rd.name = E->get().name; - rd.value = get(rd.name); - } - + if (p_keep_groups) { List<GroupInfo> groups; get_groups(&groups); - for (List<GroupInfo>::Element *E = groups.front(); E; E = E->next()) - p_node->add_to_group(E->get().name, E->get().persistent); + for (const GroupInfo &E : groups) { + p_node->add_to_group(E.name, E.persistent); + } } _replace_connections_target(p_node); if (data.owner) { - for (int i = 0; i < get_child_count(); i++) + for (int i = 0; i < get_child_count(); i++) { find_owned_by(data.owner, get_child(i), &owned_by_owner); + } } Node *parent = data.parent; int pos_in_parent = data.pos; if (data.parent) { - parent->remove_child(this); parent->add_child(p_node); parent->move_child(p_node, pos_in_parent); } while (get_child_count()) { - Node *child = get_child(0); remove_child(child); if (!child->is_owned_by_parent()) { @@ -2484,74 +2300,71 @@ void Node::replace_by(Node *p_node, bool p_keep_data) { } p_node->set_owner(owner); - for (int i = 0; i < owned.size(); i++) + for (int i = 0; i < owned.size(); i++) { owned[i]->set_owner(p_node); + } - for (int i = 0; i < owned_by_owner.size(); i++) + for (int i = 0; i < owned_by_owner.size(); i++) { owned_by_owner[i]->set_owner(owner); + } p_node->set_filename(get_filename()); - - for (List<_NodeReplaceByPair>::Element *E = replace_data.front(); E; E = E->next()) { - - p_node->set(E->get().name, E->get().value); - } } void Node::_replace_connections_target(Node *p_new_target) { - List<Connection> cl; get_signals_connected_to_this(&cl); - for (List<Connection>::Element *E = cl.front(); E; E = E->next()) { - - Connection &c = E->get(); - + for (const Connection &c : cl) { if (c.flags & CONNECT_PERSIST) { c.signal.get_object()->disconnect(c.signal.get_name(), Callable(this, c.callable.get_method())); bool valid = p_new_target->has_method(c.callable.get_method()) || Ref<Script>(p_new_target->get_script()).is_null() || Ref<Script>(p_new_target->get_script())->has_method(c.callable.get_method()); - ERR_CONTINUE_MSG(!valid, "Attempt to connect signal '" + c.signal.get_object()->get_class() + "." + c.signal.get_name() + "' to nonexistent method '" + c.callable.get_object()->get_class() + "." + c.callable.get_method() + "'."); + ERR_CONTINUE_MSG(!valid, vformat("Attempt to connect signal '%s.%s' to nonexistent method '%s.%s'.", c.signal.get_object()->get_class(), c.signal.get_name(), c.callable.get_object()->get_class(), c.callable.get_method())); c.signal.get_object()->connect(c.signal.get_name(), Callable(p_new_target, c.callable.get_method()), c.binds, c.flags); } } } Vector<Variant> Node::make_binds(VARIANT_ARG_DECLARE) { - Vector<Variant> ret; - if (p_arg1.get_type() == Variant::NIL) + if (p_arg1.get_type() == Variant::NIL) { return ret; - else + } else { ret.push_back(p_arg1); + } - if (p_arg2.get_type() == Variant::NIL) + if (p_arg2.get_type() == Variant::NIL) { return ret; - else + } else { ret.push_back(p_arg2); + } - if (p_arg3.get_type() == Variant::NIL) + if (p_arg3.get_type() == Variant::NIL) { return ret; - else + } else { ret.push_back(p_arg3); + } - if (p_arg4.get_type() == Variant::NIL) + if (p_arg4.get_type() == Variant::NIL) { return ret; - else + } else { ret.push_back(p_arg4); + } - if (p_arg5.get_type() == Variant::NIL) + if (p_arg5.get_type() == Variant::NIL) { return ret; - else + } else { ret.push_back(p_arg5); + } return ret; } bool Node::has_node_and_resource(const NodePath &p_path) const { - - if (!has_node(p_path)) + if (!has_node(p_path)) { return false; + } RES res; Vector<StringName> leftover_path; Node *node = get_node_and_resource(p_path, res, leftover_path, false); @@ -2560,21 +2373,22 @@ bool Node::has_node_and_resource(const NodePath &p_path) const { } Array Node::_get_node_and_resource(const NodePath &p_path) { - RES res; Vector<StringName> leftover_path; Node *node = get_node_and_resource(p_path, res, leftover_path, false); Array result; - if (node) + if (node) { result.push_back(node); - else + } else { result.push_back(Variant()); + } - if (res.is_valid()) + if (res.is_valid()) { result.push_back(res); - else + } else { result.push_back(Variant()); + } result.push_back(NodePath(Vector<StringName>(), leftover_path, false)); @@ -2582,15 +2396,14 @@ Array Node::_get_node_and_resource(const NodePath &p_path) { } Node *Node::get_node_and_resource(const NodePath &p_path, RES &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property) const { - Node *node = get_node(p_path); r_res = RES(); r_leftover_subpath = Vector<StringName>(); - if (!node) + if (!node) { return nullptr; + } if (p_path.get_subname_count()) { - int j = 0; // If not p_last_is_property, we shouldn't consider the last one as part of the resource for (; j < p_path.get_subname_count() - (int)p_last_is_property; j++) { @@ -2618,7 +2431,6 @@ Node *Node::get_node_and_resource(const NodePath &p_path, RES &r_res, Vector<Str } void Node::_set_tree(SceneTree *p_tree) { - SceneTree *tree_changed_a = nullptr; SceneTree *tree_changed_b = nullptr; @@ -2633,7 +2445,6 @@ void Node::_set_tree(SceneTree *p_tree) { data.tree = p_tree; if (data.tree) { - _propagate_enter_tree(); if (!data.parent || data.parent->data.ready_notified) { // No parent (root) or parent ready _propagate_ready(); //reverse_notification(NOTIFICATION_READY); @@ -2642,21 +2453,24 @@ void Node::_set_tree(SceneTree *p_tree) { tree_changed_b = data.tree; } - if (tree_changed_a) + if (tree_changed_a) { tree_changed_a->tree_changed(); - if (tree_changed_b) + } + if (tree_changed_b) { tree_changed_b->tree_changed(); + } } #ifdef DEBUG_ENABLED static void _Node_debug_sn(Object *p_obj) { - Node *n = Object::cast_to<Node>(p_obj); - if (!n) + if (!n) { return; + } - if (n->is_inside_tree()) + if (n->is_inside_tree()) { return; + } Node *p = n; while (p->get_parent()) { @@ -2664,28 +2478,26 @@ static void _Node_debug_sn(Object *p_obj) { } String path; - if (p == n) + if (p == n) { path = n->get_name(); - else + } else { path = String(p->get_name()) + "/" + p->get_path_to(n); + } print_line(itos(p_obj->get_instance_id()) + " - Stray Node: " + path + " (Type: " + n->get_class() + ")"); } #endif // DEBUG_ENABLED void Node::_print_stray_nodes() { - print_stray_nodes(); } void Node::print_stray_nodes() { - #ifdef DEBUG_ENABLED ObjectDB::debug_objects(_Node_debug_sn); #endif } void Node::queue_delete() { - if (is_inside_tree()) { get_tree()->queue_delete(this); } else { @@ -2693,26 +2505,24 @@ void Node::queue_delete() { } } -Array Node::_get_children() const { - - Array arr; - int cc = get_child_count(); +TypedArray<Node> Node::_get_children(bool p_include_internal) const { + TypedArray<Node> arr; + int cc = get_child_count(p_include_internal); arr.resize(cc); - for (int i = 0; i < cc; i++) - arr[i] = get_child(i); + for (int i = 0; i < cc; i++) { + arr[i] = get_child(i, p_include_internal); + } return arr; } void Node::set_import_path(const NodePath &p_import_path) { - #ifdef TOOLS_ENABLED data.import_path = p_import_path; #endif } NodePath Node::get_import_path() const { - #ifdef TOOLS_ENABLED return data.import_path; #else @@ -2721,55 +2531,70 @@ NodePath Node::get_import_path() const { } static void _add_nodes_to_options(const Node *p_base, const Node *p_node, List<String> *r_options) { - #ifdef TOOLS_ENABLED - const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", 0) ? "'" : "\""; + const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\""; #else const String quote_style = "\""; #endif - if (p_node != p_base && !p_node->get_owner()) + if (p_node != p_base && !p_node->get_owner()) { return; + } String n = p_base->get_path_to(p_node); - r_options->push_back(quote_style + n + quote_style); + r_options->push_back(n.quote(quote_style)); for (int i = 0; i < p_node->get_child_count(); i++) { _add_nodes_to_options(p_base, p_node->get_child(i), r_options); } } void Node::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { - String pf = p_function; if ((pf == "has_node" || pf == "get_node") && p_idx == 0) { - _add_nodes_to_options(this, this, r_options); } Object::get_argument_options(p_function, p_idx, r_options); } 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(); } } -String Node::get_configuration_warning() const { - - if (get_script_instance() && get_script_instance()->get_script().is_valid() && - get_script_instance()->get_script()->is_tool() && get_script_instance()->has_method("_get_configuration_warning")) { - return get_script_instance()->call("_get_configuration_warning"); +TypedArray<String> Node::get_configuration_warnings() const { + Vector<String> warnings; + if (GDVIRTUAL_CALL(_get_configuration_warnings, warnings)) { + TypedArray<String> ret; + ret.resize(warnings.size()); + for (int i = 0; i < warnings.size(); i++) { + ret[i] = warnings[i]; + } + return ret; } - return String(); + return Array(); } -void Node::update_configuration_warning() { +String Node::get_configuration_warnings_as_string() const { + TypedArray<String> warnings = get_configuration_warnings(); + String all_warnings = String(); + for (int i = 0; i < warnings.size(); i++) { + if (i > 0) { + all_warnings += "\n\n"; + } + // Format as a bullet point list to make multiple warnings easier to distinguish + // from each other. + all_warnings += String::utf8("• ") + String(warnings[i]); + } + return all_warnings; +} +void Node::update_configuration_warnings() { #ifdef TOOLS_ENABLED - if (!is_inside_tree()) + if (!is_inside_tree()) { return; - if (get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root() == this || get_tree()->get_edited_scene_root()->is_a_parent_of(this))) { + } + if (get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root() == this || get_tree()->get_edited_scene_root()->is_ancestor_of(this))) { get_tree()->emit_signal(SceneStringNames::get_singleton()->node_configuration_warning_changed, this); } #endif @@ -2784,7 +2609,6 @@ void Node::set_display_folded(bool p_folded) { } bool Node::is_displayed_folded() const { - return data.display_folded; } @@ -2792,22 +2616,52 @@ void Node::request_ready() { data.ready_first = true; } -void Node::_bind_methods() { +void Node::_call_input(const Ref<InputEvent> &p_event) { + GDVIRTUAL_CALL(_input, p_event); + if (!is_inside_tree() || !get_viewport() || get_viewport()->is_input_handled()) { + return; + } + input(p_event); +} +void Node::_call_unhandled_input(const Ref<InputEvent> &p_event) { + GDVIRTUAL_CALL(_unhandled_input, p_event); + if (!is_inside_tree() || !get_viewport() || get_viewport()->is_input_handled()) { + return; + } + unhandled_input(p_event); +} +void Node::_call_unhandled_key_input(const Ref<InputEvent> &p_event) { + GDVIRTUAL_CALL(_unhandled_key_input, p_event); + if (!is_inside_tree() || !get_viewport() || get_viewport()->is_input_handled()) { + return; + } + unhandled_key_input(p_event); +} - GLOBAL_DEF("node/name_num_separator", 0); - ProjectSettings::get_singleton()->set_custom_property_info("node/name_num_separator", PropertyInfo(Variant::INT, "node/name_num_separator", PROPERTY_HINT_ENUM, "None,Space,Underscore,Dash")); - GLOBAL_DEF("node/name_casing", NAME_CASING_PASCAL_CASE); - ProjectSettings::get_singleton()->set_custom_property_info("node/name_casing", PropertyInfo(Variant::INT, "node/name_casing", PROPERTY_HINT_ENUM, "PascalCase,camelCase,snake_case")); +void Node::input(const Ref<InputEvent> &p_event) { +} - ClassDB::bind_method(D_METHOD("add_child_below_node", "preceding_node", "node", "legible_unique_name"), &Node::add_child_below_node, DEFVAL(false)); +void Node::unhandled_input(const Ref<InputEvent> &p_event) { +} + +void Node::unhandled_key_input(const Ref<InputEvent> &p_key_event) { +} + +void Node::_bind_methods() { + GLOBAL_DEF("editor/node_naming/name_num_separator", 0); + ProjectSettings::get_singleton()->set_custom_property_info("editor/node_naming/name_num_separator", PropertyInfo(Variant::INT, "editor/node_naming/name_num_separator", PROPERTY_HINT_ENUM, "None,Space,Underscore,Dash")); + GLOBAL_DEF("editor/node_naming/name_casing", NAME_CASING_PASCAL_CASE); + ProjectSettings::get_singleton()->set_custom_property_info("editor/node_naming/name_casing", PropertyInfo(Variant::INT, "editor/node_naming/name_casing", PROPERTY_HINT_ENUM, "PascalCase,camelCase,snake_case")); + + ClassDB::bind_method(D_METHOD("add_sibling", "sibling", "legible_unique_name"), &Node::add_sibling, DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_name", "name"), &Node::set_name); ClassDB::bind_method(D_METHOD("get_name"), &Node::get_name); - ClassDB::bind_method(D_METHOD("add_child", "node", "legible_unique_name"), &Node::add_child, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_child", "node", "legible_unique_name", "internal"), &Node::add_child, DEFVAL(false), DEFVAL(0)); ClassDB::bind_method(D_METHOD("remove_child", "node"), &Node::remove_child); - ClassDB::bind_method(D_METHOD("get_child_count"), &Node::get_child_count); - ClassDB::bind_method(D_METHOD("get_children"), &Node::_get_children); - ClassDB::bind_method(D_METHOD("get_child", "idx"), &Node::get_child); + ClassDB::bind_method(D_METHOD("get_child_count", "include_internal"), &Node::get_child_count, DEFVAL(false)); // Note that the default value bound for include_internal is false, while the method is declared with true. This is because internal nodes are irrelevant for GDSCript. + ClassDB::bind_method(D_METHOD("get_children", "include_internal"), &Node::_get_children, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_child", "idx", "include_internal"), &Node::get_child, DEFVAL(false)); ClassDB::bind_method(D_METHOD("has_node", "path"), &Node::has_node); ClassDB::bind_method(D_METHOD("get_node", "path"), &Node::get_node); ClassDB::bind_method(D_METHOD("get_node_or_null", "path"), &Node::get_node_or_null); @@ -2818,7 +2672,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("get_node_and_resource", "path"), &Node::_get_node_and_resource); ClassDB::bind_method(D_METHOD("is_inside_tree"), &Node::is_inside_tree); - ClassDB::bind_method(D_METHOD("is_a_parent_of", "node"), &Node::is_a_parent_of); + ClassDB::bind_method(D_METHOD("is_ancestor_of", "node"), &Node::is_ancestor_of); ClassDB::bind_method(D_METHOD("is_greater_than", "node"), &Node::is_greater_than); ClassDB::bind_method(D_METHOD("get_path"), &Node::get_path); ClassDB::bind_method(D_METHOD("get_path_to", "node"), &Node::get_path_to); @@ -2831,7 +2685,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("set_owner", "owner"), &Node::set_owner); ClassDB::bind_method(D_METHOD("get_owner"), &Node::get_owner); ClassDB::bind_method(D_METHOD("remove_and_skip"), &Node::remove_and_skip); - ClassDB::bind_method(D_METHOD("get_index"), &Node::get_index); + ClassDB::bind_method(D_METHOD("get_index", "include_internal"), &Node::get_index, DEFVAL(false)); ClassDB::bind_method(D_METHOD("print_tree"), &Node::print_tree); ClassDB::bind_method(D_METHOD("print_tree_pretty"), &Node::print_tree_pretty); ClassDB::bind_method(D_METHOD("set_filename", "filename"), &Node::set_filename); @@ -2852,8 +2706,8 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("is_processing_unhandled_input"), &Node::is_processing_unhandled_input); ClassDB::bind_method(D_METHOD("set_process_unhandled_key_input", "enable"), &Node::set_process_unhandled_key_input); ClassDB::bind_method(D_METHOD("is_processing_unhandled_key_input"), &Node::is_processing_unhandled_key_input); - ClassDB::bind_method(D_METHOD("set_pause_mode", "mode"), &Node::set_pause_mode); - ClassDB::bind_method(D_METHOD("get_pause_mode"), &Node::get_pause_mode); + ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &Node::set_process_mode); + ClassDB::bind_method(D_METHOD("get_process_mode"), &Node::get_process_mode); ClassDB::bind_method(D_METHOD("can_process"), &Node::can_process); ClassDB::bind_method(D_METHOD("print_stray_nodes"), &Node::_print_stray_nodes); @@ -2867,12 +2721,15 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("is_physics_processing_internal"), &Node::is_physics_processing_internal); ClassDB::bind_method(D_METHOD("get_tree"), &Node::get_tree); + ClassDB::bind_method(D_METHOD("create_tween"), &Node::create_tween); ClassDB::bind_method(D_METHOD("duplicate", "flags"), &Node::duplicate, DEFVAL(DUPLICATE_USE_INSTANCING | DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS)); - ClassDB::bind_method(D_METHOD("replace_by", "node", "keep_data"), &Node::replace_by, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("replace_by", "node", "keep_groups"), &Node::replace_by, DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_scene_instance_load_placeholder", "load_placeholder"), &Node::set_scene_instance_load_placeholder); ClassDB::bind_method(D_METHOD("get_scene_instance_load_placeholder"), &Node::get_scene_instance_load_placeholder); + ClassDB::bind_method(D_METHOD("set_editable_instance", "node", "is_editable"), &Node::set_editable_instance); + ClassDB::bind_method(D_METHOD("is_editable_instance", "node"), &Node::is_editable_instance); ClassDB::bind_method(D_METHOD("get_viewport"), &Node::get_viewport); @@ -2880,23 +2737,22 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("request_ready"), &Node::request_ready); - ClassDB::bind_method(D_METHOD("set_network_master", "id", "recursive"), &Node::set_network_master, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("get_network_master"), &Node::get_network_master); + ClassDB::bind_method(D_METHOD("set_multiplayer_authority", "id", "recursive"), &Node::set_multiplayer_authority, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_multiplayer_authority"), &Node::get_multiplayer_authority); - ClassDB::bind_method(D_METHOD("is_network_master"), &Node::is_network_master); + ClassDB::bind_method(D_METHOD("is_multiplayer_authority"), &Node::is_multiplayer_authority); ClassDB::bind_method(D_METHOD("get_multiplayer"), &Node::get_multiplayer); ClassDB::bind_method(D_METHOD("get_custom_multiplayer"), &Node::get_custom_multiplayer); ClassDB::bind_method(D_METHOD("set_custom_multiplayer", "api"), &Node::set_custom_multiplayer); - ClassDB::bind_method(D_METHOD("rpc_config", "method", "mode"), &Node::rpc_config); - ClassDB::bind_method(D_METHOD("rset_config", "property", "mode"), &Node::rset_config); + ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("_set_editor_description", "editor_description"), &Node::set_editor_description); - ClassDB::bind_method(D_METHOD("_get_editor_description"), &Node::get_editor_description); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "editor_description", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_editor_description", "_get_editor_description"); + ClassDB::bind_method(D_METHOD("set_editor_description", "editor_description"), &Node::set_editor_description); + ClassDB::bind_method(D_METHOD("get_editor_description"), &Node::get_editor_description); ClassDB::bind_method(D_METHOD("_set_import_path", "import_path"), &Node::set_import_path); ClassDB::bind_method(D_METHOD("_get_import_path"), &Node::get_import_path); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "_import_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_import_path", "_get_import_path"); { @@ -2906,23 +2762,14 @@ void Node::_bind_methods() { mi.name = "rpc"; ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "rpc", &Node::_rpc_bind, mi); - mi.name = "rpc_unreliable"; - ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "rpc_unreliable", &Node::_rpc_unreliable_bind, mi); mi.arguments.push_front(PropertyInfo(Variant::INT, "peer_id")); mi.name = "rpc_id"; ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "rpc_id", &Node::_rpc_id_bind, mi); - mi.name = "rpc_unreliable_id"; - ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "rpc_unreliable_id", &Node::_rpc_unreliable_id_bind, mi); } - ClassDB::bind_method(D_METHOD("rset", "property", "value"), &Node::rset); - ClassDB::bind_method(D_METHOD("rset_id", "peer_id", "property", "value"), &Node::rset_id); - ClassDB::bind_method(D_METHOD("rset_unreliable", "property", "value"), &Node::rset_unreliable); - ClassDB::bind_method(D_METHOD("rset_unreliable_id", "peer_id", "property", "value"), &Node::rset_unreliable_id); - - ClassDB::bind_method(D_METHOD("update_configuration_warning"), &Node::update_configuration_warning); + ClassDB::bind_method(D_METHOD("update_configuration_warnings"), &Node::update_configuration_warnings); BIND_CONSTANT(NOTIFICATION_ENTER_TREE); BIND_CONSTANT(NOTIFICATION_EXIT_TREE); @@ -2940,11 +2787,17 @@ void Node::_bind_methods() { BIND_CONSTANT(NOTIFICATION_PATH_CHANGED); BIND_CONSTANT(NOTIFICATION_INTERNAL_PROCESS); BIND_CONSTANT(NOTIFICATION_INTERNAL_PHYSICS_PROCESS); + BIND_CONSTANT(NOTIFICATION_POST_ENTER_TREE); + BIND_CONSTANT(NOTIFICATION_DISABLED); + BIND_CONSTANT(NOTIFICATION_ENABLED); + + BIND_CONSTANT(NOTIFICATION_EDITOR_PRE_SAVE); + BIND_CONSTANT(NOTIFICATION_EDITOR_POST_SAVE); BIND_CONSTANT(NOTIFICATION_WM_MOUSE_ENTER); BIND_CONSTANT(NOTIFICATION_WM_MOUSE_EXIT); - BIND_CONSTANT(NOTIFICATION_WM_FOCUS_IN); - BIND_CONSTANT(NOTIFICATION_WM_FOCUS_OUT); + BIND_CONSTANT(NOTIFICATION_WM_WINDOW_FOCUS_IN); + BIND_CONSTANT(NOTIFICATION_WM_WINDOW_FOCUS_OUT); BIND_CONSTANT(NOTIFICATION_WM_CLOSE_REQUEST); BIND_CONSTANT(NOTIFICATION_WM_GO_BACK_REQUEST); BIND_CONSTANT(NOTIFICATION_WM_SIZE_CHANGED); @@ -2953,91 +2806,76 @@ void Node::_bind_methods() { BIND_CONSTANT(NOTIFICATION_WM_ABOUT); BIND_CONSTANT(NOTIFICATION_CRASH); BIND_CONSTANT(NOTIFICATION_OS_IME_UPDATE); - BIND_CONSTANT(NOTIFICATION_APP_RESUMED); - BIND_CONSTANT(NOTIFICATION_APP_PAUSED); - - BIND_ENUM_CONSTANT(PAUSE_MODE_INHERIT); - BIND_ENUM_CONSTANT(PAUSE_MODE_STOP); - BIND_ENUM_CONSTANT(PAUSE_MODE_PROCESS); + BIND_CONSTANT(NOTIFICATION_APPLICATION_RESUMED); + BIND_CONSTANT(NOTIFICATION_APPLICATION_PAUSED); + BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_IN); + BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_OUT); + BIND_CONSTANT(NOTIFICATION_TEXT_SERVER_CHANGED); + + BIND_ENUM_CONSTANT(PROCESS_MODE_INHERIT); + BIND_ENUM_CONSTANT(PROCESS_MODE_PAUSABLE); + BIND_ENUM_CONSTANT(PROCESS_MODE_WHEN_PAUSED); + BIND_ENUM_CONSTANT(PROCESS_MODE_ALWAYS); + BIND_ENUM_CONSTANT(PROCESS_MODE_DISABLED); BIND_ENUM_CONSTANT(DUPLICATE_SIGNALS); BIND_ENUM_CONSTANT(DUPLICATE_GROUPS); BIND_ENUM_CONSTANT(DUPLICATE_SCRIPTS); BIND_ENUM_CONSTANT(DUPLICATE_USE_INSTANCING); + BIND_ENUM_CONSTANT(INTERNAL_MODE_DISABLED); + BIND_ENUM_CONSTANT(INTERNAL_MODE_FRONT); + BIND_ENUM_CONSTANT(INTERNAL_MODE_BACK); + ADD_SIGNAL(MethodInfo("ready")); ADD_SIGNAL(MethodInfo("renamed")); ADD_SIGNAL(MethodInfo("tree_entered")); ADD_SIGNAL(MethodInfo("tree_exiting")); ADD_SIGNAL(MethodInfo("tree_exited")); - ADD_GROUP("Pause", "pause_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "pause_mode", PROPERTY_HINT_ENUM, "Inherit,Stop,Process"), "set_pause_mode", "get_pause_mode"); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_name", "get_name"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "filename", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_filename", "get_filename"); + 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, "custom_multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", PROPERTY_USAGE_NONE), "set_custom_multiplayer", "get_custom_multiplayer"); - ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "name", PROPERTY_HINT_NONE, "", 0), "set_name", "get_name"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "filename", PROPERTY_HINT_NONE, "", 0), "set_filename", "get_filename"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_owner", "get_owner"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "", "get_multiplayer"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_custom_multiplayer", "get_custom_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"); ADD_PROPERTY(PropertyInfo(Variant::INT, "process_priority"), "set_process_priority", "get_process_priority"); - BIND_VMETHOD(MethodInfo("_process", PropertyInfo(Variant::FLOAT, "delta"))); - BIND_VMETHOD(MethodInfo("_physics_process", PropertyInfo(Variant::FLOAT, "delta"))); - BIND_VMETHOD(MethodInfo("_enter_tree")); - BIND_VMETHOD(MethodInfo("_exit_tree")); - BIND_VMETHOD(MethodInfo("_ready")); - BIND_VMETHOD(MethodInfo("_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); - BIND_VMETHOD(MethodInfo("_unhandled_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); - BIND_VMETHOD(MethodInfo("_unhandled_key_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEventKey"))); - BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_configuration_warning")); + ADD_GROUP("Editor Description", "editor_"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "editor_description", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "set_editor_description", "get_editor_description"); + + GDVIRTUAL_BIND(_process, "delta"); + GDVIRTUAL_BIND(_physics_process, "delta"); + GDVIRTUAL_BIND(_enter_tree); + GDVIRTUAL_BIND(_exit_tree); + GDVIRTUAL_BIND(_ready); + GDVIRTUAL_BIND(_get_configuration_warnings); + GDVIRTUAL_BIND(_input, "event"); + GDVIRTUAL_BIND(_unhandled_input, "event"); + GDVIRTUAL_BIND(_unhandled_key_input, "event"); } String Node::_get_name_num_separator() { - switch (ProjectSettings::get_singleton()->get("node/name_num_separator").operator int()) { - case 0: return ""; - case 1: return " "; - case 2: return "_"; - case 3: return "-"; + switch (ProjectSettings::get_singleton()->get("editor/node_naming/name_num_separator").operator int()) { + case 0: + return ""; + case 1: + return " "; + case 2: + return "_"; + case 3: + return "-"; } return " "; } Node::Node() { - - data.pos = -1; - data.depth = -1; - data.blocked = 0; - data.parent = nullptr; - data.tree = nullptr; - data.physics_process = false; - data.idle_process = false; - data.process_priority = 0; - data.physics_process_internal = false; - data.idle_process_internal = false; - data.inside_tree = false; - data.ready_notified = false; - - data.owner = nullptr; - data.OW = nullptr; - data.input = false; - data.unhandled_input = false; - data.unhandled_key_input = false; - data.pause_mode = PAUSE_MODE_INHERIT; - data.pause_owner = nullptr; - data.network_master = 1; //server by default - data.path_cache = nullptr; - data.parent_owned = false; - data.in_constructor = true; - data.viewport = nullptr; - data.use_placeholder = false; - data.display_folded = false; - data.ready_first = true; - orphan_node_count++; } Node::~Node() { - data.grouped.clear(); data.owned.clear(); data.children.clear(); |