diff options
Diffstat (limited to 'scene/main/node.cpp')
-rw-r--r-- | scene/main/node.cpp | 464 |
1 files changed, 433 insertions, 31 deletions
diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 1d7a100f7c..3aaafeae30 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -47,10 +47,14 @@ #include <stdint.h> VARIANT_ENUM_CAST(Node::ProcessMode); +VARIANT_ENUM_CAST(Node::ProcessThreadGroup); +VARIANT_BITFIELD_CAST(Node::ProcessThreadMessages); VARIANT_ENUM_CAST(Node::InternalMode); int Node::orphan_node_count = 0; +thread_local Node *Node::current_process_thread_group = nullptr; + void Node::_notification(int p_notification) { switch (p_notification) { case NOTIFICATION_PROCESS: { @@ -65,6 +69,7 @@ void Node::_notification(int p_notification) { ERR_FAIL_COND(!get_viewport()); ERR_FAIL_COND(!get_tree()); + // Update process mode. if (data.process_mode == PROCESS_MODE_INHERIT) { if (data.parent) { data.process_owner = data.parent->data.process_owner; @@ -77,6 +82,27 @@ void Node::_notification(int p_notification) { data.process_owner = this; } + { // Update threaded process mode. + if (data.process_thread_group == PROCESS_THREAD_GROUP_INHERIT) { + if (data.parent) { + data.process_thread_group_owner = data.parent->data.process_thread_group_owner; + } + + if (data.process_thread_group_owner) { + data.process_group = data.process_thread_group_owner->data.process_group; + } else { + data.process_group = &data.tree->default_process_group; + } + } else { + data.process_thread_group_owner = this; + _add_process_group(); + } + + if (_is_any_processing()) { + _add_to_process_thread_group(); + } + } + if (data.input) { add_to_group("_vp_input" + itos(get_viewport()->get_instance_id())); } @@ -90,7 +116,7 @@ void Node::_notification(int p_notification) { add_to_group("_vp_unhandled_key_input" + itos(get_viewport()->get_instance_id())); } - get_tree()->node_count++; + get_tree()->nodes_in_tree_count++; orphan_node_count--; } break; @@ -98,7 +124,7 @@ void Node::_notification(int p_notification) { ERR_FAIL_COND(!get_viewport()); ERR_FAIL_COND(!get_tree()); - get_tree()->node_count--; + get_tree()->nodes_in_tree_count--; orphan_node_count++; if (data.input) { @@ -114,7 +140,17 @@ void Node::_notification(int p_notification) { remove_from_group("_vp_unhandled_key_input" + itos(get_viewport()->get_instance_id())); } + // Remove from processing first + if (_is_any_processing()) { + _remove_from_process_thread_group(); + } + // Remove the process group + if (data.process_thread_group_owner == this) { + _remove_process_group(); + } + data.process_thread_group_owner = nullptr; data.process_owner = nullptr; + if (data.path_cache) { memdelete(data.path_cache); data.path_cache = nullptr; @@ -160,6 +196,12 @@ void Node::_notification(int p_notification) { } break; case NOTIFICATION_PREDELETE: { + if (data.inside_tree && !Thread::is_main_thread()) { + cancel_free(); + ERR_PRINT("Attempted to free a node that is currently added to the SceneTree from a thread. This is not permitted, use queue_free() instead. Node has not been freed."); + return; + } + if (data.parent) { data.parent->remove_child(this); } @@ -329,6 +371,7 @@ void Node::_propagate_exit_tree() { } void Node::move_child(Node *p_child, int p_index) { + ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Moving child node positions inside the SceneTree is only allowed from the main thread. Use call_deferred(\"move_child\",child,index)."); ERR_FAIL_NULL(p_child); ERR_FAIL_COND_MSG(p_child->data.parent != this, "Child is not a child of this node."); @@ -440,16 +483,24 @@ void Node::owner_changed_notify() { } void Node::set_physics_process(bool p_process) { + ERR_THREAD_GUARD if (data.physics_process == p_process) { return; } + if (!is_inside_tree()) { + data.physics_process = p_process; + return; + } + + if (_is_any_processing()) { + _remove_from_process_thread_group(); + } + data.physics_process = p_process; - if (data.physics_process) { - add_to_group(SNAME("_physics_process"), false); - } else { - remove_from_group(SNAME("_physics_process")); + if (_is_any_processing()) { + _add_to_process_thread_group(); } } @@ -458,16 +509,24 @@ bool Node::is_physics_processing() const { } void Node::set_physics_process_internal(bool p_process_internal) { + ERR_THREAD_GUARD if (data.physics_process_internal == p_process_internal) { return; } + if (!is_inside_tree()) { + data.physics_process_internal = p_process_internal; + return; + } + + if (_is_any_processing()) { + _remove_from_process_thread_group(); + } + data.physics_process_internal = p_process_internal; - if (data.physics_process_internal) { - add_to_group(SNAME("_physics_process_internal"), false); - } else { - remove_from_group(SNAME("_physics_process_internal")); + if (_is_any_processing()) { + _add_to_process_thread_group(); } } @@ -476,6 +535,7 @@ bool Node::is_physics_processing_internal() const { } void Node::set_process_mode(ProcessMode p_mode) { + ERR_THREAD_GUARD if (data.process_mode == p_mode) { return; } @@ -569,6 +629,7 @@ void Node::_propagate_process_owner(Node *p_owner, int p_pause_notification, int } void Node::set_multiplayer_authority(int p_peer_id, bool p_recursive) { + ERR_THREAD_GUARD data.multiplayer_authority = p_peer_id; if (p_recursive) { @@ -591,6 +652,7 @@ bool Node::is_multiplayer_authority() const { /***** RPC CONFIG ********/ void Node::rpc_config(const StringName &p_method, const Variant &p_config) { + ERR_THREAD_GUARD if (data.rpc_config.get_type() != Variant::DICTIONARY) { data.rpc_config = Dictionary(); } @@ -762,16 +824,24 @@ double Node::get_process_delta_time() const { } void Node::set_process(bool p_process) { + ERR_THREAD_GUARD if (data.process == p_process) { return; } + if (!is_inside_tree()) { + data.process = p_process; + return; + } + + if (_is_any_processing()) { + _remove_from_process_thread_group(); + } + data.process = p_process; - if (data.process) { - add_to_group(SNAME("_process"), false); - } else { - remove_from_group(SNAME("_process")); + if (_is_any_processing()) { + _add_to_process_thread_group(); } } @@ -780,53 +850,201 @@ bool Node::is_processing() const { } void Node::set_process_internal(bool p_process_internal) { + ERR_THREAD_GUARD if (data.process_internal == p_process_internal) { return; } + if (!is_inside_tree()) { + data.process_internal = p_process_internal; + return; + } + + if (_is_any_processing()) { + _remove_from_process_thread_group(); + } + data.process_internal = p_process_internal; - if (data.process_internal) { - add_to_group(SNAME("_process_internal"), false); - } else { - remove_from_group(SNAME("_process_internal")); + if (_is_any_processing()) { + _add_to_process_thread_group(); } } +void Node::_add_process_group() { + get_tree()->_add_process_group(this); +} + +void Node::_remove_process_group() { + get_tree()->_remove_process_group(this); +} + +void Node::_remove_from_process_thread_group() { + get_tree()->_remove_node_from_process_group(this, data.process_thread_group_owner); +} + +void Node::_add_to_process_thread_group() { + get_tree()->_add_node_to_process_group(this, data.process_thread_group_owner); +} + +void Node::_remove_tree_from_process_thread_group() { + if (!is_inside_tree()) { + return; // May not be initialized yet. + } + + for (KeyValue<StringName, Node *> &K : data.children) { + if (K.value->data.process_thread_group != PROCESS_THREAD_GROUP_INHERIT) { + continue; + } + + K.value->_remove_tree_from_process_thread_group(); + } + + if (_is_any_processing()) { + _remove_from_process_thread_group(); + } +} + +void Node::_add_tree_to_process_thread_group(Node *p_owner) { + if (_is_any_processing()) { + _add_to_process_thread_group(); + } + + data.process_thread_group_owner = p_owner; + if (p_owner != nullptr) { + data.process_group = p_owner->data.process_group; + } else { + data.process_group = &data.tree->default_process_group; + } + + for (KeyValue<StringName, Node *> &K : data.children) { + if (K.value->data.process_thread_group != PROCESS_THREAD_GROUP_INHERIT) { + continue; + } + + K.value->_add_to_process_thread_group(); + } +} bool Node::is_processing_internal() const { return data.process_internal; } +void Node::set_process_thread_group_order(int p_order) { + ERR_THREAD_GUARD + if (data.process_thread_group_order == p_order) { + return; + } + // Make sure we are in SceneTree and an actual process owner + if (!is_inside_tree() || data.process_thread_group_owner != this) { + data.process_thread_group_order = p_order; + return; + } + + get_tree()->process_groups_dirty = true; +} + +int Node::get_process_thread_group_order() const { + return data.process_thread_group_order; +} + void Node::set_process_priority(int p_priority) { - data.process_priority = p_priority; + ERR_THREAD_GUARD + if (data.process_priority == p_priority) { + return; + } + // Make sure we are in SceneTree and an actual process owner + if (!is_inside_tree()) { + data.process_priority = p_priority; + return; + } + + if (_is_any_processing()) { + _remove_from_process_thread_group(); + data.process_priority = p_priority; + _add_to_process_thread_group(); + } +} - // Make sure we are in SceneTree. - if (data.tree == nullptr) { +int Node::get_process_priority() const { + return data.process_priority; +} + +void Node::set_physics_process_priority(int p_priority) { + ERR_THREAD_GUARD + if (data.physics_process_priority == p_priority) { return; } + // Make sure we are in SceneTree and an actual physics_process owner + if (!is_inside_tree()) { + data.physics_process_priority = p_priority; + return; + } + + if (_is_any_processing()) { + _remove_from_process_thread_group(); + data.physics_process_priority = p_priority; + _add_to_process_thread_group(); + } +} + +int Node::get_physics_process_priority() const { + return data.physics_process_priority; +} - if (is_processing()) { - data.tree->make_group_changed(SNAME("_process")); +void Node::set_process_thread_group(ProcessThreadGroup p_mode) { + ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Changing the process thread group can only be done from the main thread. Use call_deferred(\"set_process_thread_group\",mode)."); + if (data.process_thread_group == p_mode) { + return; } - if (is_processing_internal()) { - data.tree->make_group_changed(SNAME("_process_internal")); + if (!is_inside_tree()) { + data.process_thread_group = p_mode; + return; } - if (is_physics_processing()) { - data.tree->make_group_changed(SNAME("_physics_process")); + // Mode changed, must update everything. + _remove_tree_from_process_thread_group(); + if (data.process_thread_group != PROCESS_THREAD_GROUP_INHERIT) { + _remove_process_group(); } - if (is_physics_processing_internal()) { - data.tree->make_group_changed(SNAME("_physics_process_internal")); + data.process_thread_group = p_mode; + + if (p_mode == PROCESS_THREAD_GROUP_INHERIT) { + if (data.parent) { + data.process_thread_group_owner = data.parent->data.process_thread_group_owner; + } else { + data.process_thread_group_owner = nullptr; + } + } else { + data.process_thread_group_owner = this; + _add_process_group(); } + + _add_tree_to_process_thread_group(data.process_thread_group_owner); + + notify_property_list_changed(); } -int Node::get_process_priority() const { - return data.process_priority; +Node::ProcessThreadGroup Node::get_process_thread_group() const { + return data.process_thread_group; +} + +void Node::set_process_thread_messages(BitField<ProcessThreadMessages> p_flags) { + ERR_THREAD_GUARD + if (data.process_thread_group_order == p_flags) { + return; + } + + data.process_thread_messages = p_flags; +} + +BitField<Node::ProcessThreadMessages> Node::get_process_thread_messages() const { + return data.process_thread_messages; } void Node::set_process_input(bool p_enable) { + ERR_THREAD_GUARD if (p_enable == data.input) { return; } @@ -848,6 +1066,7 @@ bool Node::is_processing_input() const { } void Node::set_process_shortcut_input(bool p_enable) { + ERR_THREAD_GUARD if (p_enable == data.shortcut_input) { return; } @@ -868,6 +1087,7 @@ bool Node::is_processing_shortcut_input() const { } void Node::set_process_unhandled_input(bool p_enable) { + ERR_THREAD_GUARD if (p_enable == data.unhandled_input) { return; } @@ -888,6 +1108,7 @@ bool Node::is_processing_unhandled_input() const { } void Node::set_process_unhandled_key_input(bool p_enable) { + ERR_THREAD_GUARD if (p_enable == data.unhandled_key_input) { return; } @@ -916,6 +1137,7 @@ void Node::_set_name_nocheck(const StringName &p_name) { } void Node::set_name(const String &p_name) { + ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Changing the name to nodes inside the SceneTree is only allowed from the main thread. Use call_deferred(\"set_name\",new_name)."); String name = p_name.validate_node_name(); ERR_FAIL_COND(name.is_empty()); @@ -1147,6 +1369,9 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name, InternalM } void Node::add_child(Node *p_child, bool p_force_readable_name, InternalMode p_internal) { + ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Adding children to a node inside the SceneTree is only allowed from the main thread. Use call_deferred(\"add_child\",node)."); + + ERR_THREAD_GUARD 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 @@ -1160,6 +1385,7 @@ void Node::add_child(Node *p_child, bool p_force_readable_name, InternalMode p_i } void Node::add_sibling(Node *p_sibling, bool p_force_readable_name) { + ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Adding a sibling to a node inside the SceneTree is only allowed from the main thread. Use call_deferred(\"add_sibling\",node)."); 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! @@ -1171,6 +1397,7 @@ void Node::add_sibling(Node *p_sibling, bool p_force_readable_name) { } void Node::remove_child(Node *p_child) { + ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Removing children from a node inside the SceneTree is only allowed from the main thread. Use call_deferred(\"remove_child\",node)."); ERR_FAIL_NULL(p_child); ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy adding/removing children, `remove_child()` can't be called at this time. Consider using `remove_child.call_deferred(child)` instead."); ERR_FAIL_COND(p_child->data.parent != this); @@ -1241,6 +1468,7 @@ void Node::_update_children_cache_impl() const { } int Node::get_child_count(bool p_include_internal) const { + ERR_THREAD_GUARD_V(0); _update_children_cache(); if (p_include_internal) { @@ -1251,6 +1479,7 @@ int Node::get_child_count(bool p_include_internal) const { } Node *Node::get_child(int p_index, bool p_include_internal) const { + ERR_THREAD_GUARD_V(nullptr); _update_children_cache(); if (p_include_internal) { @@ -1270,6 +1499,7 @@ Node *Node::get_child(int p_index, bool p_include_internal) const { } TypedArray<Node> Node::get_children(bool p_include_internal) const { + ERR_THREAD_GUARD_V(TypedArray<Node>()); TypedArray<Node> arr; int cc = get_child_count(p_include_internal); arr.resize(cc); @@ -1290,6 +1520,7 @@ Node *Node::_get_child_by_name(const StringName &p_name) const { } Node *Node::get_node_or_null(const NodePath &p_path) const { + ERR_THREAD_GUARD_V(nullptr); if (p_path.is_empty()) { return nullptr; } @@ -1395,6 +1626,7 @@ bool Node::has_node(const NodePath &p_path) 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_THREAD_GUARD_V(nullptr); ERR_FAIL_COND_V(p_pattern.is_empty(), nullptr); _update_children_cache(); Node *const *cptr = data.children_cache.ptr(); @@ -1423,6 +1655,7 @@ Node *Node::find_child(const String &p_pattern, bool p_recursive, bool p_owned) // 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 { + ERR_THREAD_GUARD_V(TypedArray<Node>()); TypedArray<Node> ret; ERR_FAIL_COND_V(p_pattern.is_empty() && p_type.is_empty(), ret); _update_children_cache(); @@ -1464,6 +1697,7 @@ TypedArray<Node> Node::find_children(const String &p_pattern, const String &p_ty } void Node::reparent(Node *p_parent, bool p_keep_global_transform) { + ERR_THREAD_GUARD ERR_FAIL_NULL(p_parent); ERR_FAIL_NULL_MSG(data.parent, "Node needs a parent to be reparented."); @@ -1480,6 +1714,7 @@ Node *Node::get_parent() const { } Node *Node::find_parent(const String &p_pattern) const { + ERR_THREAD_GUARD_V(nullptr); Node *p = data.parent; while (p) { if (p->data.name.operator String().match(p_pattern)) { @@ -1492,6 +1727,7 @@ Node *Node::find_parent(const String &p_pattern) const { } Window *Node::get_window() const { + ERR_THREAD_GUARD_V(nullptr); Viewport *vp = get_viewport(); if (vp) { return vp->get_base_window(); @@ -1616,6 +1852,7 @@ void Node::_acquire_unique_name_in_owner() { } void Node::set_unique_name_in_owner(bool p_enabled) { + ERR_MAIN_THREAD_GUARD if (data.unique_name_in_owner == p_enabled) { return; } @@ -1637,6 +1874,7 @@ bool Node::is_unique_name_in_owner() const { } void Node::set_owner(Node *p_owner) { + ERR_MAIN_THREAD_GUARD if (data.owner) { if (data.unique_name_in_owner) { _release_unique_name_in_owner(); @@ -1820,10 +2058,12 @@ NodePath Node::get_path() const { } bool Node::is_in_group(const StringName &p_identifier) const { + ERR_THREAD_GUARD_V(false); return data.grouped.has(p_identifier); } void Node::add_to_group(const StringName &p_identifier, bool p_persistent) { + ERR_THREAD_GUARD ERR_FAIL_COND(!p_identifier.operator String().length()); if (data.grouped.has(p_identifier)) { @@ -1844,6 +2084,7 @@ void Node::add_to_group(const StringName &p_identifier, bool p_persistent) { } void Node::remove_from_group(const StringName &p_identifier) { + ERR_THREAD_GUARD HashMap<StringName, GroupData>::Iterator E = data.grouped.find(p_identifier); if (!E) { @@ -1869,6 +2110,7 @@ TypedArray<StringName> Node::_get_groups() const { } void Node::get_groups(List<GroupInfo> *p_groups) const { + ERR_THREAD_GUARD for (const KeyValue<StringName, GroupData> &E : data.grouped) { GroupInfo gi; gi.name = E.key; @@ -1878,6 +2120,7 @@ void Node::get_groups(List<GroupInfo> *p_groups) const { } int Node::get_persistent_group_count() const { + ERR_THREAD_GUARD_V(0); int count = 0; for (const KeyValue<StringName, GroupData> &E : data.grouped) { @@ -1947,6 +2190,7 @@ void Node::_propagate_deferred_notification(int p_notification, bool p_reverse) } void Node::propagate_notification(int p_notification) { + ERR_THREAD_GUARD data.blocked++; notification(p_notification); @@ -1957,6 +2201,7 @@ void Node::propagate_notification(int p_notification) { } void Node::propagate_call(const StringName &p_method, const Array &p_args, const bool p_parent_first) { + ERR_THREAD_GUARD data.blocked++; if (p_parent_first && has_method(p_method)) { @@ -1987,6 +2232,7 @@ void Node::_propagate_replace_owner(Node *p_owner, Node *p_by_owner) { } Ref<Tween> Node::create_tween() { + ERR_THREAD_GUARD_V(Ref<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); @@ -1994,6 +2240,7 @@ Ref<Tween> Node::create_tween() { } void Node::set_scene_file_path(const String &p_scene_file_path) { + ERR_THREAD_GUARD data.scene_file_path = p_scene_file_path; } @@ -2002,6 +2249,7 @@ String Node::get_scene_file_path() const { } void Node::set_editor_description(const String &p_editor_description) { + ERR_THREAD_GUARD if (data.editor_description == p_editor_description) { return; } @@ -2019,6 +2267,7 @@ String Node::get_editor_description() const { } void Node::set_editable_instance(Node *p_node, bool p_editable) { + ERR_THREAD_GUARD ERR_FAIL_NULL(p_node); ERR_FAIL_COND(!is_ancestor_of(p_node)); if (!p_editable) { @@ -2040,6 +2289,7 @@ bool Node::is_editable_instance(const Node *p_node) const { } Node *Node::get_deepest_editable_node(Node *p_start_node) const { + ERR_THREAD_GUARD_V(nullptr); ERR_FAIL_NULL_V(p_start_node, nullptr); ERR_FAIL_COND_V(!is_ancestor_of(p_start_node), p_start_node); @@ -2059,6 +2309,7 @@ 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) { + ERR_THREAD_GUARD bool current_pinned = false; Array pinned = get_meta("_edit_pinned_properties_", Array()); StringName psa = get_property_store_alias(p_property); @@ -2096,6 +2347,7 @@ bool Node::is_part_of_edited_scene() const { #endif void Node::get_storable_properties(HashSet<StringName> &r_storable_properties) const { + ERR_THREAD_GUARD List<PropertyInfo> pi; get_property_list(&pi); for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) { @@ -2106,6 +2358,7 @@ void Node::get_storable_properties(HashSet<StringName> &r_storable_properties) c } String Node::to_string() { + ERR_THREAD_GUARD_V(String()); if (get_script_instance()) { bool valid; String ret = get_script_instance()->to_string(&valid); @@ -2118,6 +2371,7 @@ String Node::to_string() { } void Node::set_scene_instance_state(const Ref<SceneState> &p_state) { + ERR_THREAD_GUARD data.instance_state = p_state; } @@ -2126,6 +2380,7 @@ Ref<SceneState> Node::get_scene_instance_state() const { } void Node::set_scene_inherited_state(const Ref<SceneState> &p_state) { + ERR_THREAD_GUARD data.inherited_state = p_state; } @@ -2142,6 +2397,7 @@ bool Node::get_scene_instance_load_placeholder() const { } Node *Node::_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap) const { + ERR_THREAD_GUARD_V(nullptr); Node *node = nullptr; bool instantiated = false; @@ -2323,6 +2579,7 @@ Node *Node::_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap) c } Node *Node::duplicate(int p_flags) const { + ERR_THREAD_GUARD_V(nullptr); Node *dupe = _duplicate(p_flags); if (dupe && (p_flags & DUPLICATE_SIGNALS)) { @@ -2467,6 +2724,7 @@ static void find_owned_by(Node *p_by, Node *p_node, List<Node *> *p_owned) { } void Node::replace_by(Node *p_node, bool p_keep_groups) { + ERR_THREAD_GUARD ERR_FAIL_NULL(p_node); ERR_FAIL_COND(p_node->data.parent); @@ -2536,6 +2794,7 @@ void Node::_replace_connections_target(Node *p_new_target) { } bool Node::has_node_and_resource(const NodePath &p_path) const { + ERR_THREAD_GUARD_V(false); if (!has_node(p_path)) { return false; } @@ -2570,6 +2829,7 @@ Array Node::_get_node_and_resource(const NodePath &p_path) { } 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 { + ERR_THREAD_GUARD_V(nullptr); Node *node = get_node(p_path); r_res = Ref<Resource>(); r_leftover_subpath = Vector<StringName>(); @@ -2739,6 +2999,7 @@ void Node::clear_internal_tree_resource_paths() { } PackedStringArray Node::get_configuration_warnings() const { + ERR_THREAD_GUARD_V(PackedStringArray()); PackedStringArray ret; Vector<String> warnings; @@ -2764,6 +3025,7 @@ String Node::get_configuration_warnings_as_string() const { } void Node::update_configuration_warnings() { + ERR_THREAD_GUARD #ifdef TOOLS_ENABLED if (!is_inside_tree()) { return; @@ -2779,6 +3041,7 @@ bool Node::is_owned_by_parent() const { } void Node::set_display_folded(bool p_folded) { + ERR_THREAD_GUARD data.display_folded = p_folded; } @@ -2791,6 +3054,7 @@ bool Node::is_ready() const { } void Node::request_ready() { + ERR_THREAD_GUARD data.ready_first = true; } @@ -2834,6 +3098,12 @@ void Node::_call_unhandled_key_input(const Ref<InputEvent> &p_event) { unhandled_key_input(p_event); } +void Node::_validate_property(PropertyInfo &p_property) const { + if ((p_property.name == "process_thread_group_order" || p_property.name == "process_thread_messages") && data.process_thread_group == PROCESS_THREAD_GROUP_INHERIT) { + p_property.usage = 0; + } +} + void Node::input(const Ref<InputEvent> &p_event) { } @@ -2846,6 +3116,94 @@ void Node::unhandled_input(const Ref<InputEvent> &p_event) { void Node::unhandled_key_input(const Ref<InputEvent> &p_key_event) { } +Variant Node::_call_deferred_thread_group_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 = 0; + return Variant(); + } + + if (p_args[0]->get_type() != Variant::STRING_NAME && 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_NAME; + return Variant(); + } + + r_error.error = Callable::CallError::CALL_OK; + + StringName method = *p_args[0]; + + call_deferred_thread_groupp(method, &p_args[1], p_argcount - 1, true); + + return Variant(); +} + +Variant Node::_call_thread_safe_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 = 0; + return Variant(); + } + + if (p_args[0]->get_type() != Variant::STRING_NAME && 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_NAME; + return Variant(); + } + + r_error.error = Callable::CallError::CALL_OK; + + StringName method = *p_args[0]; + + call_thread_safep(method, &p_args[1], p_argcount - 1, true); + + return Variant(); +} + +void Node::call_deferred_thread_groupp(const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error) { + ERR_FAIL_COND(!is_inside_tree()); + SceneTree::ProcessGroup *pg = (SceneTree::ProcessGroup *)data.process_group; + pg->call_queue.push_callp(this, p_method, p_args, p_argcount, p_show_error); +} +void Node::set_deferred_thread_group(const StringName &p_property, const Variant &p_value) { + ERR_FAIL_COND(!is_inside_tree()); + SceneTree::ProcessGroup *pg = (SceneTree::ProcessGroup *)data.process_group; + pg->call_queue.push_set(this, p_property, p_value); +} +void Node::notify_deferred_thread_group(int p_notification) { + ERR_FAIL_COND(!is_inside_tree()); + SceneTree::ProcessGroup *pg = (SceneTree::ProcessGroup *)data.process_group; + pg->call_queue.push_notification(this, p_notification); +} + +void Node::call_thread_safep(const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error) { + if (is_accessible_from_caller_thread()) { + Callable::CallError ce; + callp(p_method, p_args, p_argcount, ce); + if (p_show_error && ce.error != Callable::CallError::CALL_OK) { + ERR_FAIL_MSG("Error calling method from 'call_threadp': " + Variant::get_call_error_text(this, p_method, p_args, p_argcount, ce) + "."); + } + } else { + call_deferred_thread_groupp(p_method, p_args, p_argcount, p_show_error); + } +} +void Node::set_thread_safe(const StringName &p_property, const Variant &p_value) { + if (is_accessible_from_caller_thread()) { + set(p_property, p_value); + } else { + set_deferred_thread_group(p_property, p_value); + } +} +void Node::notify_thread_safe(int p_notification) { + if (is_accessible_from_caller_thread()) { + notification(p_notification); + } else { + notify_deferred_thread_group(p_notification); + } +} + void Node::_bind_methods() { GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/node_name_num_separator", PROPERTY_HINT_ENUM, "None,Space,Underscore,Dash"), 0); GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/node_name_casing", PROPERTY_HINT_ENUM, "PascalCase,camelCase,snake_case"), NAME_CASING_PASCAL_CASE); @@ -2897,6 +3255,8 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("set_process", "enable"), &Node::set_process); ClassDB::bind_method(D_METHOD("set_process_priority", "priority"), &Node::set_process_priority); ClassDB::bind_method(D_METHOD("get_process_priority"), &Node::get_process_priority); + ClassDB::bind_method(D_METHOD("set_physics_process_priority", "priority"), &Node::set_physics_process_priority); + ClassDB::bind_method(D_METHOD("get_physics_process_priority"), &Node::get_physics_process_priority); 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); @@ -2910,6 +3270,15 @@ void Node::_bind_methods() { 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("set_process_thread_group", "mode"), &Node::set_process_thread_group); + ClassDB::bind_method(D_METHOD("get_process_thread_group"), &Node::get_process_thread_group); + + ClassDB::bind_method(D_METHOD("set_process_thread_messages", "flags"), &Node::set_process_thread_messages); + ClassDB::bind_method(D_METHOD("get_process_thread_messages"), &Node::get_process_thread_messages); + + ClassDB::bind_method(D_METHOD("set_process_thread_group_order", "order"), &Node::set_process_thread_group_order); + ClassDB::bind_method(D_METHOD("get_process_thread_group_order"), &Node::get_process_thread_group_order); + 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); @@ -2977,6 +3346,26 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("update_configuration_warnings"), &Node::update_configuration_warnings); + { + MethodInfo mi; + mi.name = "call_deferred_thread_group"; + mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method")); + + ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "call_deferred_thread_group", &Node::_call_deferred_thread_group_bind, mi, varray(), false); + } + ClassDB::bind_method(D_METHOD("set_deferred_thread_group", "property", "value"), &Node::set_deferred_thread_group); + ClassDB::bind_method(D_METHOD("notify_deferred_thread_group", "what"), &Node::notify_deferred_thread_group); + + { + MethodInfo mi; + mi.name = "call_thread_safe"; + mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method")); + + ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "call_thread_safe", &Node::_call_thread_safe_bind, mi, varray(), false); + } + ClassDB::bind_method(D_METHOD("set_thread_safe", "property", "value"), &Node::set_thread_safe); + ClassDB::bind_method(D_METHOD("notify_thread_safe", "what"), &Node::notify_thread_safe); + BIND_CONSTANT(NOTIFICATION_ENTER_TREE); BIND_CONSTANT(NOTIFICATION_EXIT_TREE); BIND_CONSTANT(NOTIFICATION_MOVED_IN_PARENT); @@ -3029,6 +3418,14 @@ void Node::_bind_methods() { BIND_ENUM_CONSTANT(PROCESS_MODE_ALWAYS); BIND_ENUM_CONSTANT(PROCESS_MODE_DISABLED); + BIND_ENUM_CONSTANT(PROCESS_THREAD_GROUP_INHERIT); + BIND_ENUM_CONSTANT(PROCESS_THREAD_GROUP_MAIN_THREAD); + BIND_ENUM_CONSTANT(PROCESS_THREAD_GROUP_SUB_THREAD); + + BIND_ENUM_CONSTANT(FLAG_PROCESS_THREAD_MESSAGES); + BIND_ENUM_CONSTANT(FLAG_PROCESS_THREAD_MESSAGES_PHYSICS); + BIND_ENUM_CONSTANT(FLAG_PROCESS_THREAD_MESSAGES_ALL); + BIND_ENUM_CONSTANT(DUPLICATE_SIGNALS); BIND_ENUM_CONSTANT(DUPLICATE_GROUPS); BIND_ENUM_CONSTANT(DUPLICATE_SCRIPTS); @@ -3056,6 +3453,11 @@ void Node::_bind_methods() { 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"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "process_physics_priority"), "set_physics_process_priority", "get_physics_process_priority"); + ADD_SUBGROUP("Thread Group", "process_thread"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "process_thread_group", PROPERTY_HINT_ENUM, "Inherit,Main Thread,Sub Thread"), "set_process_thread_group", "get_process_thread_group"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "process_thread_group_order"), "set_process_thread_group_order", "get_process_thread_group_order"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "process_thread_messages", PROPERTY_HINT_FLAGS, "Process,Physics Process"), "set_process_thread_messages", "get_process_thread_messages"); ADD_GROUP("Editor Description", "editor_"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "editor_description", PROPERTY_HINT_MULTILINE_TEXT), "set_editor_description", "get_editor_description"); |