summaryrefslogtreecommitdiff
path: root/scene/main/node.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/main/node.cpp')
-rw-r--r--scene/main/node.cpp574
1 files changed, 305 insertions, 269 deletions
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 211667ce38..29f4d4fb1c 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -33,12 +33,12 @@
#include "core/config/project_settings.h"
#include "core/core_string_names.h"
#include "core/io/resource_loader.h"
-#include "core/multiplayer/multiplayer_api.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/main/multiplayer_api.h"
#include "scene/resources/packed_scene.h"
#include "scene/scene_string_names.h"
#include "viewport.h"
@@ -79,6 +79,9 @@ void Node::_notification(int p_notification) {
if (data.input) {
add_to_group("_vp_input" + itos(get_viewport()->get_instance_id()));
}
+ if (data.shortcut_input) {
+ add_to_group("_vp_shortcut_input" + itos(get_viewport()->get_instance_id()));
+ }
if (data.unhandled_input) {
add_to_group("_vp_unhandled_input" + itos(get_viewport()->get_instance_id()));
}
@@ -100,6 +103,9 @@ void Node::_notification(int p_notification) {
if (data.input) {
remove_from_group("_vp_input" + itos(get_viewport()->get_instance_id()));
}
+ if (data.shortcut_input) {
+ remove_from_group("_vp_shortcut_input" + itos(get_viewport()->get_instance_id()));
+ }
if (data.unhandled_input) {
remove_from_group("_vp_unhandled_input" + itos(get_viewport()->get_instance_id()));
}
@@ -126,6 +132,10 @@ void Node::_notification(int p_notification) {
set_process_input(true);
}
+ if (GDVIRTUAL_IS_OVERRIDDEN(_shortcut_input)) {
+ set_process_shortcut_input(true);
+ }
+
if (GDVIRTUAL_IS_OVERRIDDEN(_unhandled_input)) {
set_process_unhandled_input(true);
}
@@ -211,7 +221,7 @@ void Node::_propagate_enter_tree() {
if (data.parent) {
Variant c = this;
const Variant *cptr = &c;
- data.parent->emit_signal(SNAME("child_entered_tree"), &cptr, 1);
+ data.parent->emit_signalp(SNAME("child_entered_tree"), &cptr, 1);
}
data.blocked++;
@@ -247,6 +257,9 @@ void Node::_propagate_after_exit_tree() {
}
if (!found) {
+ if (data.unique_name_in_owner) {
+ _release_unique_name_in_owner();
+ }
data.owner->data.owned.erase(data.OW);
data.owner = nullptr;
}
@@ -287,7 +300,7 @@ void Node::_propagate_exit_tree() {
if (data.parent) {
Variant c = this;
const Variant *cptr = &c;
- data.parent->emit_signal(SNAME("child_exited_tree"), &cptr, 1);
+ data.parent->emit_signalp(SNAME("child_exiting_tree"), &cptr, 1);
}
// exit groups
@@ -315,12 +328,21 @@ void Node::move_child(Node *p_child, int p_pos) {
// We need to check whether node is internal and move it only in the relevant node range.
if (p_child->_is_internal_front()) {
+ if (p_pos < 0) {
+ p_pos += data.internal_children_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()) {
+ if (p_pos < 0) {
+ p_pos += data.internal_children_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 {
+ if (p_pos < 0) {
+ p_pos += get_child_count(false);
+ }
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);
}
@@ -371,27 +393,20 @@ void Node::_move_child(Node *p_child, int p_pos, bool p_ignore_end) {
for (int i = motion_from; i <= motion_to; i++) {
data.children[i]->notification(NOTIFICATION_MOVED_IN_PARENT);
}
- for (const KeyValue<StringName, GroupData> &E : p_child->data.grouped) {
- if (E.value.group) {
- E.value.group->changed = true;
- }
- }
+ p_child->_propagate_groups_dirty();
data.blocked--;
}
-void Node::raise() {
- if (!data.parent) {
- return;
+void Node::_propagate_groups_dirty() {
+ for (const KeyValue<StringName, GroupData> &E : data.grouped) {
+ if (E.value.group) {
+ E.value.group->changed = true;
+ }
}
- // 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);
+ for (int i = 0; i < data.children.size(); i++) {
+ data.children[i]->_propagate_groups_dirty();
}
}
@@ -407,6 +422,9 @@ void Node::move_child_notify(Node *p_child) {
// to be used when not wanted
}
+void Node::owner_changed_notify() {
+}
+
void Node::set_physics_process(bool p_process) {
if (data.physics_process == p_process) {
return;
@@ -415,9 +433,9 @@ void Node::set_physics_process(bool p_process) {
data.physics_process = p_process;
if (data.physics_process) {
- add_to_group("physics_process", false);
+ add_to_group(SNAME("_physics_process"), false);
} else {
- remove_from_group("physics_process");
+ remove_from_group(SNAME("_physics_process"));
}
}
@@ -433,9 +451,9 @@ void Node::set_physics_process_internal(bool p_process_internal) {
data.physics_process_internal = p_process_internal;
if (data.physics_process_internal) {
- add_to_group("physics_process_internal", false);
+ add_to_group(SNAME("_physics_process_internal"), false);
} else {
- remove_from_group("physics_process_internal");
+ remove_from_group(SNAME("_physics_process_internal"));
}
}
@@ -558,63 +576,30 @@ bool Node::is_multiplayer_authority() const {
/***** RPC CONFIG ********/
-uint16_t Node::rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, bool p_call_local, 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.call_local = p_call_local;
- nd.channel = p_channel;
- return i | (1 << 15);
- }
+void Node::rpc_config(const StringName &p_method, const Variant &p_config) {
+ if (data.rpc_config.get_type() != Variant::DICTIONARY) {
+ data.rpc_config = Dictionary();
}
- // 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;
- nd.call_local = p_call_local;
- data.rpc_methods.push_back(nd);
- return ((uint16_t)data.rpc_methods.size() - 1) | (1 << 15);
-}
-
-/***** 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) {
- break;
- }
- argc++;
+ Dictionary node_config = data.rpc_config;
+ if (p_config.get_type() == Variant::NIL) {
+ node_config.erase(p_method);
+ } else {
+ ERR_FAIL_COND(p_config.get_type() != Variant::DICTIONARY);
+ node_config[p_method] = p_config;
}
-
- rpcp(0, p_method, argptr, argc);
}
-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) {
- break;
- }
- argc++;
- }
-
- rpcp(p_peer_id, p_method, argptr, argc);
+const Variant Node::get_node_rpc_config() const {
+ return data.rpc_config;
}
-Variant Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+/***** RPC FUNCTIONS ********/
+
+Error 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();
+ return ERR_INVALID_PARAMETER;
}
Variant::Type type = p_args[0]->get_type();
@@ -622,29 +607,28 @@ Variant Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallEr
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::STRING_NAME;
- return Variant();
+ return ERR_INVALID_PARAMETER;
}
StringName method = (*p_args[0]).operator StringName();
- rpcp(0, method, &p_args[1], p_argcount - 1);
-
+ Error err = rpcp(0, method, &p_args[1], p_argcount - 1);
r_error.error = Callable::CallError::CALL_OK;
- return Variant();
+ return err;
}
-Variant Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+Error 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();
+ return ERR_INVALID_PARAMETER;
}
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();
+ return ERR_INVALID_PARAMETER;
}
Variant::Type type = p_args[1]->get_type();
@@ -652,43 +636,27 @@ Variant Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::Cal
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 1;
r_error.expected = Variant::STRING_NAME;
- return Variant();
+ return ERR_INVALID_PARAMETER;
}
int peer_id = *p_args[0];
StringName method = (*p_args[1]).operator StringName();
- rpcp(peer_id, method, &p_args[2], p_argcount - 2);
-
+ Error err = rpcp(peer_id, method, &p_args[2], p_argcount - 2);
r_error.error = Callable::CallError::CALL_OK;
- return Variant();
+ return err;
}
-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_method, p_arg, p_argcount);
+Error Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
+ ERR_FAIL_COND_V(!is_inside_tree(), ERR_UNCONFIGURED);
+ return get_multiplayer()->rpcp(this, p_peer_id, p_method, p_arg, p_argcount);
}
Ref<MultiplayerAPI> Node::get_multiplayer() const {
- if (multiplayer.is_valid()) {
- return multiplayer;
- }
if (!is_inside_tree()) {
return Ref<MultiplayerAPI>();
}
- return get_tree()->get_multiplayer();
-}
-
-Ref<MultiplayerAPI> Node::get_custom_multiplayer() const {
- return multiplayer;
-}
-
-void Node::set_custom_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
- multiplayer = p_multiplayer;
-}
-
-Vector<Multiplayer::RPCConfig> Node::get_node_rpc_methods() const {
- return data.rpc_methods;
+ return get_tree()->get_multiplayer(get_path());
}
//////////// end of rpc
@@ -787,9 +755,9 @@ void Node::set_process(bool p_process) {
data.process = p_process;
if (data.process) {
- add_to_group("process", false);
+ add_to_group(SNAME("_process"), false);
} else {
- remove_from_group("process");
+ remove_from_group(SNAME("_process"));
}
}
@@ -805,9 +773,9 @@ void Node::set_process_internal(bool p_process_internal) {
data.process_internal = p_process_internal;
if (data.process_internal) {
- add_to_group("process_internal", false);
+ add_to_group(SNAME("_process_internal"), false);
} else {
- remove_from_group("process_internal");
+ remove_from_group(SNAME("_process_internal"));
}
}
@@ -824,19 +792,19 @@ void Node::set_process_priority(int p_priority) {
}
if (is_processing()) {
- data.tree->make_group_changed("process");
+ data.tree->make_group_changed(SNAME("_process"));
}
if (is_processing_internal()) {
- data.tree->make_group_changed("process_internal");
+ data.tree->make_group_changed(SNAME("_process_internal"));
}
if (is_physics_processing()) {
- data.tree->make_group_changed("physics_process");
+ data.tree->make_group_changed(SNAME("_physics_process"));
}
if (is_physics_processing_internal()) {
- data.tree->make_group_changed("physics_process_internal");
+ data.tree->make_group_changed(SNAME("_physics_process_internal"));
}
}
@@ -865,6 +833,26 @@ bool Node::is_processing_input() const {
return data.input;
}
+void Node::set_process_shortcut_input(bool p_enable) {
+ if (p_enable == data.shortcut_input) {
+ return;
+ }
+ data.shortcut_input = p_enable;
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ if (p_enable) {
+ add_to_group("_vp_shortcut_input" + itos(get_viewport()->get_instance_id()));
+ } else {
+ remove_from_group("_vp_shortcut_input" + itos(get_viewport()->get_instance_id()));
+ }
+}
+
+bool Node::is_processing_shortcut_input() const {
+ return data.shortcut_input;
+}
+
void Node::set_process_unhandled_input(bool p_enable) {
if (p_enable == data.unhandled_input) {
return;
@@ -917,12 +905,20 @@ void Node::set_name(const String &p_name) {
String name = p_name.validate_node_name();
ERR_FAIL_COND(name.is_empty());
+
+ if (data.unique_name_in_owner && data.owner) {
+ _release_unique_name_in_owner();
+ }
data.name = name;
if (data.parent) {
data.parent->_validate_child_name(this, true);
}
+ if (data.unique_name_in_owner && data.owner) {
+ _acquire_unique_name_in_owner();
+ }
+
propagate_notification(NOTIFICATION_PATH_RENAMED);
if (is_inside_tree()) {
@@ -946,6 +942,18 @@ String Node::validate_child_name(Node *p_child) {
}
#endif
+String Node::adjust_name_casing(const String &p_name) {
+ switch (GLOBAL_GET("editor/node_naming/name_casing").operator int()) {
+ case NAME_CASING_PASCAL_CASE:
+ return p_name.to_pascal_case();
+ case NAME_CASING_CAMEL_CASE:
+ return p_name.to_camel_case();
+ case NAME_CASING_SNAKE_CASE:
+ return p_name.to_snake_case();
+ }
+ return p_name;
+}
+
void Node::_validate_child_name(Node *p_child, bool p_force_human_readable) {
/* Make sure the name is unique */
@@ -1019,19 +1027,8 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co
//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("editor/node_naming/name_casing").operator int()) {
- case NAME_CASING_PASCAL_CASE:
- break;
- case NAME_CASING_CAMEL_CASE: {
- String n = name;
- n[0] = n.to_lower()[0];
- name = n;
- } break;
- case NAME_CASING_SNAKE_CASE:
- name = String(name).camelcase_to_underscore(true);
- break;
- }
+ // Adjust casing according to project setting.
+ name = adjust_name_casing(name);
}
//quickly test if proposed name exists
@@ -1128,7 +1125,7 @@ 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, InternalMode p_internal) {
+void Node::add_child(Node *p_child, bool p_force_readable_name, InternalMode p_internal) {
ERR_FAIL_NULL(p_child);
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
@@ -1137,7 +1134,7 @@ void Node::add_child(Node *p_child, bool p_legible_unique_name, InternalMode p_i
#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_child_name(p_child, p_legible_unique_name);
+ _validate_child_name(p_child, p_force_readable_name);
_add_child_nocheck(p_child, p_child->data.name);
if (p_internal == INTERNAL_MODE_FRONT) {
@@ -1151,7 +1148,7 @@ void Node::add_child(Node *p_child, bool p_legible_unique_name, InternalMode p_i
}
}
-void Node::add_sibling(Node *p_sibling, bool p_legible_unique_name) {
+void Node::add_sibling(Node *p_sibling, bool p_force_readable_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!
@@ -1164,7 +1161,7 @@ void Node::add_sibling(Node *p_sibling, bool p_legible_unique_name) {
internal = INTERNAL_MODE_BACK;
}
- data.parent->add_child(p_sibling, p_legible_unique_name, internal);
+ data.parent->add_child(p_sibling, p_force_readable_name, internal);
data.parent->_move_child(p_sibling, get_index() + 1);
}
@@ -1303,6 +1300,24 @@ Node *Node::get_node_or_null(const NodePath &p_path) const {
next = root;
}
+ } else if (name.is_node_unique_name()) {
+ if (current->data.owned_unique_nodes.size()) {
+ // Has unique nodes in ownership
+ Node **unique = current->data.owned_unique_nodes.getptr(name);
+ if (!unique) {
+ return nullptr;
+ }
+ next = *unique;
+ } else if (current->data.owner) {
+ Node **unique = current->data.owner->data.owned_unique_nodes.getptr(name);
+ if (!unique) {
+ return nullptr;
+ }
+ next = *unique;
+ } else {
+ return nullptr;
+ }
+
} else {
next = nullptr;
@@ -1344,9 +1359,39 @@ bool Node::has_node(const NodePath &p_path) const {
return get_node_or_null(p_path) != nullptr;
}
-TypedArray<Node> Node::find_nodes(const String &p_mask, const String &p_type, bool p_recursive, bool p_owned) const {
+// Finds the first child node (in tree order) whose name matches the given pattern.
+// Can be recursive or not, and limited to owned nodes.
+Node *Node::find_child(const String &p_pattern, bool p_recursive, bool p_owned) const {
+ ERR_FAIL_COND_V(p_pattern.is_empty(), nullptr);
+
+ Node *const *cptr = data.children.ptr();
+ int ccount = data.children.size();
+ for (int i = 0; i < ccount; i++) {
+ if (p_owned && !cptr[i]->data.owner) {
+ continue;
+ }
+ if (cptr[i]->data.name.operator String().match(p_pattern)) {
+ return cptr[i];
+ }
+
+ if (!p_recursive) {
+ continue;
+ }
+
+ Node *ret = cptr[i]->find_child(p_pattern, true, p_owned);
+ if (ret) {
+ return ret;
+ }
+ }
+ return nullptr;
+}
+
+// Finds child nodes based on their name using pattern matching, or class name,
+// or both (either pattern or type can be left empty).
+// Can be recursive or not, and limited to owned nodes.
+TypedArray<Node> Node::find_children(const String &p_pattern, const String &p_type, bool p_recursive, bool p_owned) const {
TypedArray<Node> ret;
- ERR_FAIL_COND_V(p_mask.is_empty() && p_type.is_empty(), ret);
+ ERR_FAIL_COND_V(p_pattern.is_empty() && p_type.is_empty(), ret);
Node *const *cptr = data.children.ptr();
int ccount = data.children.size();
@@ -1355,8 +1400,8 @@ TypedArray<Node> Node::find_nodes(const String &p_mask, const String &p_type, bo
continue;
}
- if (!p_mask.is_empty()) {
- if (!cptr[i]->data.name.operator String().match(p_mask)) {
+ if (!p_pattern.is_empty()) {
+ if (!cptr[i]->data.name.operator String().match(p_pattern)) {
continue;
} else if (p_type.is_empty()) {
ret.append(cptr[i]);
@@ -1378,7 +1423,7 @@ TypedArray<Node> Node::find_nodes(const String &p_mask, const String &p_type, bo
}
if (p_recursive) {
- ret.append_array(cptr[i]->find_nodes(p_mask, p_type, true, p_owned));
+ ret.append_array(cptr[i]->find_children(p_pattern, p_type, true, p_owned));
}
}
@@ -1389,10 +1434,10 @@ Node *Node::get_parent() const {
return data.parent;
}
-Node *Node::find_parent(const String &p_mask) const {
+Node *Node::find_parent(const String &p_pattern) const {
Node *p = data.parent;
while (p) {
- if (p->data.name.operator String().match(p_mask)) {
+ if (p->data.name.operator String().match(p_pattern)) {
return p;
}
p = p->data.parent;
@@ -1496,10 +1541,60 @@ void Node::_set_owner_nocheck(Node *p_owner) {
data.owner = p_owner;
data.owner->data.owned.push_back(this);
data.OW = data.owner->data.owned.back();
+
+ owner_changed_notify();
+}
+
+void Node::_release_unique_name_in_owner() {
+ ERR_FAIL_NULL(data.owner); // Sanity check.
+ StringName key = StringName(UNIQUE_NODE_PREFIX + data.name.operator String());
+ Node **which = data.owner->data.owned_unique_nodes.getptr(key);
+ if (which == nullptr || *which != this) {
+ return; // Ignore.
+ }
+ data.owner->data.owned_unique_nodes.erase(key);
+}
+
+void Node::_acquire_unique_name_in_owner() {
+ ERR_FAIL_NULL(data.owner); // Sanity check.
+ StringName key = StringName(UNIQUE_NODE_PREFIX + data.name.operator String());
+ Node **which = data.owner->data.owned_unique_nodes.getptr(key);
+ if (which != nullptr && *which != this) {
+ String which_path = is_inside_tree() ? (*which)->get_path() : data.owner->get_path_to(*which);
+ WARN_PRINT(vformat(RTR("Setting node name '%s' to be unique within scene for '%s', but it's already claimed by '%s'.\n'%s' is no longer set as having a unique name."),
+ get_name(), is_inside_tree() ? get_path() : data.owner->get_path_to(this), which_path, which_path));
+ data.unique_name_in_owner = false;
+ return;
+ }
+ data.owner->data.owned_unique_nodes[key] = this;
+}
+
+void Node::set_unique_name_in_owner(bool p_enabled) {
+ if (data.unique_name_in_owner == p_enabled) {
+ return;
+ }
+
+ if (data.unique_name_in_owner && data.owner != nullptr) {
+ _release_unique_name_in_owner();
+ }
+ data.unique_name_in_owner = p_enabled;
+
+ if (data.unique_name_in_owner && data.owner != nullptr) {
+ _acquire_unique_name_in_owner();
+ }
+
+ update_configuration_warnings();
+}
+
+bool Node::is_unique_name_in_owner() const {
+ return data.unique_name_in_owner;
}
void Node::set_owner(Node *p_owner) {
if (data.owner) {
+ if (data.unique_name_in_owner) {
+ _release_unique_name_in_owner();
+ }
data.owner->data.owned.erase(data.OW);
data.OW = nullptr;
data.owner = nullptr;
@@ -1526,6 +1621,10 @@ void Node::set_owner(Node *p_owner) {
ERR_FAIL_COND(!owner_valid);
_set_owner_nocheck(p_owner);
+
+ if (data.unique_name_in_owner) {
+ _acquire_unique_name_in_owner();
+ }
}
Node *Node::get_owner() const {
@@ -1537,7 +1636,7 @@ Node *Node::find_common_parent_with(const Node *p_node) const {
return const_cast<Node *>(p_node);
}
- Set<const Node *> visited;
+ HashSet<const Node *> visited;
const Node *n = this;
@@ -1569,7 +1668,7 @@ NodePath Node::get_path_to(const Node *p_node) const {
return NodePath(".");
}
- Set<const Node *> visited;
+ HashSet<const Node *> visited;
const Node *n = this;
@@ -1663,19 +1762,19 @@ 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);
+ HashMap<StringName, GroupData>::Iterator E = data.grouped.find(p_identifier);
ERR_FAIL_COND(!E);
if (data.tree) {
- data.tree->remove_from_group(E->key(), this);
+ data.tree->remove_from_group(E->key, this);
}
- data.grouped.erase(E);
+ data.grouped.remove(E);
}
-Array Node::_get_groups() const {
- Array groups;
+TypedArray<StringName> Node::_get_groups() const {
+ TypedArray<StringName> groups;
List<GroupInfo> gi;
get_groups(&gi);
for (const GroupInfo &E : gi) {
@@ -1817,43 +1916,6 @@ Ref<Tween> Node::create_tween() {
return tween;
}
-void Node::remove_and_skip() {
- ERR_FAIL_COND(!data.parent);
-
- Node *new_owner = get_owner();
-
- 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()) {
- continue;
- }
-
- remove_child(c_node);
- c_node->_propagate_replace_owner(this, nullptr);
- children.push_back(c_node);
- clear = false;
- break;
- }
-
- if (clear) {
- break;
- }
- }
-
- while (!children.is_empty()) {
- Node *c_node = children.front()->get();
- data.parent->add_child(c_node);
- c_node->_propagate_replace_owner(nullptr, new_owner);
- children.pop_front();
- }
-
- data.parent->remove_child(this);
-}
-
void Node::set_scene_file_path(const String &p_scene_file_path) {
data.scene_file_path = p_scene_file_path;
}
@@ -1912,35 +1974,28 @@ Node *Node::get_deepest_editable_node(Node *p_start_node) const {
#ifdef TOOLS_ENABLED
void Node::set_property_pinned(const String &p_property, bool p_pinned) {
bool current_pinned = false;
- bool has_pinned = has_meta("_edit_pinned_properties_");
- Array pinned;
- String psa = get_property_store_alias(p_property);
- if (has_pinned) {
- pinned = get_meta("_edit_pinned_properties_");
- current_pinned = pinned.has(psa);
- }
+ Array pinned = get_meta("_edit_pinned_properties_", Array());
+ StringName psa = get_property_store_alias(p_property);
+ current_pinned = pinned.has(psa);
if (current_pinned != p_pinned) {
if (p_pinned) {
pinned.append(psa);
- if (!has_pinned) {
- set_meta("_edit_pinned_properties_", pinned);
- }
} else {
pinned.erase(psa);
- if (pinned.is_empty()) {
- remove_meta("_edit_pinned_properties_");
- }
}
}
+
+ if (pinned.is_empty()) {
+ remove_meta("_edit_pinned_properties_");
+ } else {
+ set_meta("_edit_pinned_properties_", pinned);
+ }
}
bool Node::is_property_pinned(const StringName &p_property) const {
- if (!has_meta("_edit_pinned_properties_")) {
- return false;
- }
- Array pinned = get_meta("_edit_pinned_properties_");
- String psa = get_property_store_alias(p_property);
+ Array pinned = get_meta("_edit_pinned_properties_", Array());
+ StringName psa = get_property_store_alias(p_property);
return pinned.has(psa);
}
@@ -1949,7 +2004,7 @@ StringName Node::get_property_store_alias(const StringName &p_property) const {
}
#endif
-void Node::get_storable_properties(Set<StringName> &r_storable_properties) const {
+void Node::get_storable_properties(HashSet<StringName> &r_storable_properties) const {
List<PropertyInfo> pi;
get_property_list(&pi);
for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) {
@@ -1995,7 +2050,7 @@ 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::_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap) const {
Node *node = nullptr;
bool instantiated = false;
@@ -2187,11 +2242,11 @@ 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(HashMap<const Node *, Node *> &r_duplimap) const {
+ return duplicate_from_editor(r_duplimap, HashMap<Ref<Resource>, Ref<Resource>>());
}
-Node *Node::duplicate_from_editor(Map<const Node *, Node *> &r_duplimap, const Map<RES, RES> &p_resource_remap) const {
+Node *Node::duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap, const HashMap<Ref<Resource>, Ref<Resource>> &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.
@@ -2207,7 +2262,7 @@ Node *Node::duplicate_from_editor(Map<const Node *, Node *> &r_duplimap, const M
return dupe;
}
-void Node::remap_node_resources(Node *p_node, const Map<RES, RES> &p_resource_remap) const {
+void Node::remap_node_resources(Node *p_node, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const {
List<PropertyInfo> props;
p_node->get_property_list(&props);
@@ -2218,7 +2273,7 @@ void Node::remap_node_resources(Node *p_node, const Map<RES, RES> &p_resource_re
Variant v = p_node->get(E.name);
if (v.is_ref_counted()) {
- RES res = v;
+ Ref<Resource> res = v;
if (res.is_valid()) {
if (p_resource_remap.has(res)) {
p_node->set(E.name, p_resource_remap[res]);
@@ -2233,7 +2288,7 @@ void Node::remap_node_resources(Node *p_node, const Map<RES, RES> &p_resource_re
}
}
-void Node::remap_nested_resources(RES p_resource, const Map<RES, RES> &p_resource_remap) const {
+void Node::remap_nested_resources(Ref<Resource> p_resource, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const {
List<PropertyInfo> props;
p_resource->get_property_list(&props);
@@ -2244,7 +2299,7 @@ void Node::remap_nested_resources(RES p_resource, const Map<RES, RES> &p_resourc
Variant v = p_resource->get(E.name);
if (v.is_ref_counted()) {
- RES res = v;
+ Ref<Resource> res = v;
if (res.is_valid()) {
if (p_resource_remap.has(res)) {
p_resource->set(E.name, p_resource_remap[res]);
@@ -2298,7 +2353,7 @@ void Node::_duplicate_signals(const Node *p_original, Node *p_copy) const {
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);
+ copy->connect(E.signal.get_name(), copy_callable, E.flags);
}
}
}
@@ -2384,52 +2439,16 @@ void Node::_replace_connections_target(Node *p_new_target) {
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, 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);
+ c.signal.get_object()->connect(c.signal.get_name(), Callable(p_new_target, c.callable.get_method()), c.flags);
}
}
}
-Vector<Variant> Node::make_binds(VARIANT_ARG_DECLARE) {
- Vector<Variant> ret;
-
- if (p_arg1.get_type() == Variant::NIL) {
- return ret;
- } else {
- ret.push_back(p_arg1);
- }
-
- if (p_arg2.get_type() == Variant::NIL) {
- return ret;
- } else {
- ret.push_back(p_arg2);
- }
-
- if (p_arg3.get_type() == Variant::NIL) {
- return ret;
- } else {
- ret.push_back(p_arg3);
- }
-
- if (p_arg4.get_type() == Variant::NIL) {
- return ret;
- } else {
- ret.push_back(p_arg4);
- }
-
- if (p_arg5.get_type() == Variant::NIL) {
- return ret;
- } else {
- ret.push_back(p_arg5);
- }
-
- return ret;
-}
-
bool Node::has_node_and_resource(const NodePath &p_path) const {
if (!has_node(p_path)) {
return false;
}
- RES res;
+ Ref<Resource> res;
Vector<StringName> leftover_path;
Node *node = get_node_and_resource(p_path, res, leftover_path, false);
@@ -2437,7 +2456,7 @@ bool Node::has_node_and_resource(const NodePath &p_path) const {
}
Array Node::_get_node_and_resource(const NodePath &p_path) {
- RES res;
+ Ref<Resource> res;
Vector<StringName> leftover_path;
Node *node = get_node_and_resource(p_path, res, leftover_path, false);
Array result;
@@ -2459,9 +2478,9 @@ Array Node::_get_node_and_resource(const NodePath &p_path) {
return result;
}
-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_and_resource(const NodePath &p_path, Ref<Resource> &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property) const {
Node *node = get_node(p_path);
- r_res = RES();
+ r_res = Ref<Resource>();
r_leftover_subpath = Vector<StringName>();
if (!node) {
return nullptr;
@@ -2471,13 +2490,14 @@ Node *Node::get_node_and_resource(const NodePath &p_path, RES &r_res, Vector<Str
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++) {
- Variant new_res_v = j == 0 ? node->get(p_path.get_subname(j)) : r_res->get(p_path.get_subname(j));
+ bool is_valid = false;
+ Variant new_res_v = j == 0 ? node->get(p_path.get_subname(j), &is_valid) : r_res->get(p_path.get_subname(j), &is_valid);
- if (new_res_v.get_type() == Variant::NIL) { // Found nothing on that path
+ if (!is_valid) { // Found nothing on that path
return nullptr;
}
- RES new_res = new_res_v;
+ Ref<Resource> new_res = new_res_v;
if (new_res.is_null()) { // No longer a resource, assume property
break;
@@ -2551,11 +2571,11 @@ static void _Node_debug_sn(Object *p_obj) {
}
#endif // DEBUG_ENABLED
-void Node::_print_stray_nodes() {
- print_stray_nodes();
+void Node::_print_orphan_nodes() {
+ print_orphan_nodes();
}
-void Node::print_stray_nodes() {
+void Node::print_orphan_nodes() {
#ifdef DEBUG_ENABLED
ObjectDB::debug_objects(_Node_debug_sn);
#endif
@@ -2621,16 +2641,16 @@ void Node::clear_internal_tree_resource_paths() {
}
TypedArray<String> Node::get_configuration_warnings() const {
+ TypedArray<String> ret;
+
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];
+ ret.push_back(warnings[i]);
}
- return ret;
}
- return Array();
+
+ return ret;
}
String Node::get_configuration_warnings_as_string() const {
@@ -2681,6 +2701,15 @@ void Node::_call_input(const Ref<InputEvent> &p_event) {
}
input(p_event);
}
+
+void Node::_call_shortcut_input(const Ref<InputEvent> &p_event) {
+ GDVIRTUAL_CALL(_shortcut_input, p_event);
+ if (!is_inside_tree() || !get_viewport() || get_viewport()->is_input_handled()) {
+ return;
+ }
+ shortcut_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()) {
@@ -2688,6 +2717,7 @@ void Node::_call_unhandled_input(const Ref<InputEvent> &p_event) {
}
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()) {
@@ -2699,6 +2729,9 @@ void Node::_call_unhandled_key_input(const Ref<InputEvent> &p_event) {
void Node::input(const Ref<InputEvent> &p_event) {
}
+void Node::shortcut_input(const Ref<InputEvent> &p_key_event) {
+}
+
void Node::unhandled_input(const Ref<InputEvent> &p_event) {
}
@@ -2711,11 +2744,11 @@ void Node::_bind_methods() {
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("add_sibling", "sibling", "force_readable_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", "internal"), &Node::add_child, DEFVAL(false), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("add_child", "node", "force_readable_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", "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));
@@ -2724,8 +2757,9 @@ void Node::_bind_methods() {
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);
ClassDB::bind_method(D_METHOD("get_parent"), &Node::get_parent);
- ClassDB::bind_method(D_METHOD("find_nodes", "mask", "type", "recursive", "owned"), &Node::find_nodes, DEFVAL(""), DEFVAL(true), DEFVAL(true));
- ClassDB::bind_method(D_METHOD("find_parent", "mask"), &Node::find_parent);
+ ClassDB::bind_method(D_METHOD("find_child", "pattern", "recursive", "owned"), &Node::find_child, DEFVAL(true), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("find_children", "pattern", "type", "recursive", "owned"), &Node::find_children, DEFVAL(""), DEFVAL(true), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("find_parent", "pattern"), &Node::find_parent);
ClassDB::bind_method(D_METHOD("has_node_and_resource", "path"), &Node::has_node_and_resource);
ClassDB::bind_method(D_METHOD("get_node_and_resource", "path"), &Node::_get_node_and_resource);
@@ -2739,10 +2773,8 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_in_group", "group"), &Node::is_in_group);
ClassDB::bind_method(D_METHOD("move_child", "child_node", "to_position"), &Node::move_child);
ClassDB::bind_method(D_METHOD("get_groups"), &Node::_get_groups);
- ClassDB::bind_method(D_METHOD("raise"), &Node::raise);
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", "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);
@@ -2760,6 +2792,8 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_processing"), &Node::is_processing);
ClassDB::bind_method(D_METHOD("set_process_input", "enable"), &Node::set_process_input);
ClassDB::bind_method(D_METHOD("is_processing_input"), &Node::is_processing_input);
+ ClassDB::bind_method(D_METHOD("set_process_shortcut_input", "enable"), &Node::set_process_shortcut_input);
+ ClassDB::bind_method(D_METHOD("is_processing_shortcut_input"), &Node::is_processing_shortcut_input);
ClassDB::bind_method(D_METHOD("set_process_unhandled_input", "enable"), &Node::set_process_unhandled_input);
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);
@@ -2767,7 +2801,7 @@ void Node::_bind_methods() {
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);
+ ClassDB::bind_method(D_METHOD("print_orphan_nodes"), &Node::_print_orphan_nodes);
ClassDB::bind_method(D_METHOD("set_display_folded", "fold"), &Node::set_display_folded);
ClassDB::bind_method(D_METHOD("is_displayed_folded"), &Node::is_displayed_folded);
@@ -2801,9 +2835,7 @@ void Node::_bind_methods() {
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", "rpc_mode", "call_local", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(false), DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("rpc_config", "method", "config"), &Node::rpc_config);
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);
@@ -2811,6 +2843,9 @@ void Node::_bind_methods() {
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);
+ ClassDB::bind_method(D_METHOD("set_unique_name_in_owner", "enable"), &Node::set_unique_name_in_owner);
+ ClassDB::bind_method(D_METHOD("is_unique_name_in_owner"), &Node::is_unique_name_in_owner);
+
#ifdef TOOLS_ENABLED
ClassDB::bind_method(D_METHOD("_set_property_pinned", "property", "pinned"), &Node::set_property_pinned);
#endif
@@ -2843,7 +2878,7 @@ void Node::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_PROCESS);
BIND_CONSTANT(NOTIFICATION_PARENTED);
BIND_CONSTANT(NOTIFICATION_UNPARENTED);
- BIND_CONSTANT(NOTIFICATION_INSTANCED);
+ BIND_CONSTANT(NOTIFICATION_SCENE_INSTANTIATED);
BIND_CONSTANT(NOTIFICATION_DRAG_BEGIN);
BIND_CONSTANT(NOTIFICATION_DRAG_END);
BIND_CONSTANT(NOTIFICATION_PATH_RENAMED);
@@ -2898,13 +2933,13 @@ void Node::_bind_methods() {
ADD_SIGNAL(MethodInfo("tree_exiting"));
ADD_SIGNAL(MethodInfo("tree_exited"));
ADD_SIGNAL(MethodInfo("child_entered_tree", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT, "Node")));
- ADD_SIGNAL(MethodInfo("child_exited_tree", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT, "Node")));
+ ADD_SIGNAL(MethodInfo("child_exiting_tree", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT, "Node")));
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_name", "get_name");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "unique_name_in_owner", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_unique_name_in_owner", "is_unique_name_in_owner");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "scene_file_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_scene_file_path", "get_scene_file_path");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_owner", "get_owner");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", PROPERTY_USAGE_NONE), "", "get_multiplayer");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", PROPERTY_USAGE_NONE), "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");
@@ -2920,6 +2955,7 @@ void Node::_bind_methods() {
GDVIRTUAL_BIND(_ready);
GDVIRTUAL_BIND(_get_configuration_warnings);
GDVIRTUAL_BIND(_input, "event");
+ GDVIRTUAL_BIND(_shortcut_input, "event");
GDVIRTUAL_BIND(_unhandled_input, "event");
GDVIRTUAL_BIND(_unhandled_key_input, "event");
}