From a8a0f24492a3b475981220884a9ec160f6b63d67 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sun, 15 Sep 2024 14:16:54 +0200 Subject: [PATCH 1/3] BlackboardPlan: Allow passing different prefetch root for the base plan --- blackboard/blackboard_plan.cpp | 47 ++++++++++++++++------------------ blackboard/blackboard_plan.h | 5 ++-- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index 806d153..d987450 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -387,33 +387,16 @@ void BlackboardPlan::sync_with_base_plan() { } } -// Add a variable duplicate to the blackboard, optionally with NodePath prefetch. -inline void bb_add_var_dup_with_prefetch(const Ref &p_blackboard, const StringName &p_name, const BBVariable &p_var, bool p_prefetch, Node *p_node) { - if (unlikely(p_prefetch && p_var.get_type() == Variant::NODE_PATH)) { - Node *n = p_node->get_node_or_null(p_var.get_value()); - BBVariable var = p_var.duplicate(true); - if (n != nullptr) { - var.set_value(n); - } else { - ERR_PRINT(vformat("BlackboardPlan: Prefetch failed for variable $%s with value: %s", p_name, p_var.get_value())); - var.set_value(Variant()); - } - p_blackboard->assign_var(p_name, var); - } else { - p_blackboard->assign_var(p_name, p_var.duplicate(true)); - } -} - -Ref BlackboardPlan::create_blackboard(Node *p_node, const Ref &p_parent_scope) { - ERR_FAIL_COND_V(p_node == nullptr && prefetch_nodepath_vars, memnew(Blackboard)); +Ref BlackboardPlan::create_blackboard(Node *p_prefetch_root, const Ref &p_parent_scope, Node *p_prefetch_root_for_base_plan) { + ERR_FAIL_COND_V(p_prefetch_root == nullptr && prefetch_nodepath_vars, memnew(Blackboard)); Ref bb = memnew(Blackboard); bb->set_parent(p_parent_scope); - populate_blackboard(bb, true, p_node); + populate_blackboard(bb, true, p_prefetch_root, p_prefetch_root_for_base_plan); return bb; } -void BlackboardPlan::populate_blackboard(const Ref &p_blackboard, bool overwrite, Node *p_node) { - ERR_FAIL_COND(p_node == nullptr && prefetch_nodepath_vars); +void BlackboardPlan::populate_blackboard(const Ref &p_blackboard, bool overwrite, Node *p_prefetch_root, Node *p_prefetch_root_for_base_plan) { + ERR_FAIL_COND(p_prefetch_root == nullptr && prefetch_nodepath_vars); ERR_FAIL_COND(p_blackboard.is_null()); for (const Pair &p : var_list) { if (p_blackboard->has_local_var(p.first) && !overwrite) { @@ -429,7 +412,21 @@ void BlackboardPlan::populate_blackboard(const Ref &p_blackboard, bo } bool has_mapping = parent_scope_mapping.has(p.first); bool do_prefetch = !has_mapping && prefetch_nodepath_vars; - bb_add_var_dup_with_prefetch(p_blackboard, p.first, p.second, do_prefetch, p_node); + + // Add a variable duplicate to the blackboard, optionally with NodePath prefetch. + BBVariable var = p.second.duplicate(true); + if (unlikely(do_prefetch && p.second.get_type() == Variant::NODE_PATH)) { + Node *prefetch_root = !is_derived() || is_derived_var_changed(p.first) ? p_prefetch_root : p_prefetch_root_for_base_plan; + Node *n = prefetch_root->get_node_or_null(p.second.get_value()); + if (n != nullptr) { + var.set_value(n); + } else { + ERR_PRINT(vformat("BlackboardPlan: Prefetch failed for variable $%s with value: %s", p.first, p.second.get_value())); + var.set_value(Variant()); + } + } + p_blackboard->assign_var(p.first, var); + if (has_mapping) { StringName target_var = parent_scope_mapping[p.first]; if (target_var != StringName()) { @@ -450,8 +447,8 @@ void BlackboardPlan::_bind_methods() { ClassDB::bind_method(D_METHOD("sync_with_base_plan"), &BlackboardPlan::sync_with_base_plan); ClassDB::bind_method(D_METHOD("set_parent_scope_plan_provider", "callable"), &BlackboardPlan::set_parent_scope_plan_provider); ClassDB::bind_method(D_METHOD("get_parent_scope_plan_provider"), &BlackboardPlan::get_parent_scope_plan_provider); - ClassDB::bind_method(D_METHOD("create_blackboard", "node", "parent_scope"), &BlackboardPlan::create_blackboard, DEFVAL(Ref())); - ClassDB::bind_method(D_METHOD("populate_blackboard", "blackboard", "overwrite", "node"), &BlackboardPlan::populate_blackboard); + ClassDB::bind_method(D_METHOD("create_blackboard", "prefetch_root", "parent_scope", "prefetch_root_for_base_plan"), &BlackboardPlan::create_blackboard, DEFVAL(Ref()), DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("populate_blackboard", "blackboard", "overwrite", "prefetch_root", "prefetch_root_for_base_plan"), &BlackboardPlan::populate_blackboard, DEFVAL(Variant())); // To avoid cluttering the member namespace, we do not export unnecessary properties in this class. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "prefetch_nodepath_vars", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_prefetch_nodepath_vars", "is_prefetching_nodepath_vars"); diff --git a/blackboard/blackboard_plan.h b/blackboard/blackboard_plan.h index a4e2071..804f325 100644 --- a/blackboard/blackboard_plan.h +++ b/blackboard/blackboard_plan.h @@ -88,9 +88,10 @@ public: void sync_with_base_plan(); _FORCE_INLINE_ bool is_derived() const { return base.is_valid(); } + _FORCE_INLINE_ bool is_derived_var_changed(const StringName &p_name) const { return base.is_valid() && var_map.has(p_name) && var_map[p_name].is_value_changed(); } - Ref create_blackboard(Node *p_agent, const Ref &p_parent_scope = Ref()); - void populate_blackboard(const Ref &p_blackboard, bool overwrite, Node *p_node); + Ref create_blackboard(Node *p_prefetch_root, const Ref &p_parent_scope = Ref(), Node *p_prefetch_root_for_base_plan = nullptr); + void populate_blackboard(const Ref &p_blackboard, bool overwrite, Node *p_prefetch_root, Node *p_prefetch_root_for_base_plan = nullptr); BlackboardPlan(); }; From ac2d734122218b78f768813e7a993ae66820c7e5 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sun, 15 Sep 2024 14:29:23 +0200 Subject: [PATCH 2/3] Use different prefetch root for the base plan in BTPlayer and BTState --- blackboard/blackboard_plan.cpp | 2 +- bt/bt_player.cpp | 4 ++-- bt/bt_player.h | 1 + bt/bt_state.cpp | 6 +++++- bt/bt_state.h | 3 +++ hsm/limbo_state.cpp | 6 +++++- hsm/limbo_state.h | 1 + 7 files changed, 18 insertions(+), 5 deletions(-) diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index d987450..c4bee42 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -416,7 +416,7 @@ void BlackboardPlan::populate_blackboard(const Ref &p_blackboard, bo // Add a variable duplicate to the blackboard, optionally with NodePath prefetch. BBVariable var = p.second.duplicate(true); if (unlikely(do_prefetch && p.second.get_type() == Variant::NODE_PATH)) { - Node *prefetch_root = !is_derived() || is_derived_var_changed(p.first) ? p_prefetch_root : p_prefetch_root_for_base_plan; + Node *prefetch_root = !p_prefetch_root_for_base_plan || !is_derived() || is_derived_var_changed(p.first) ? p_prefetch_root : p_prefetch_root_for_base_plan; Node *n = prefetch_root->get_node_or_null(p.second.get_value()); if (n != nullptr) { var.set_value(n); diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index 4457ab4..eb9ca07 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -48,7 +48,7 @@ void BTPlayer::_load_tree() { ERR_FAIL_COND_MSG(!behavior_tree->get_root_task().is_valid(), "BTPlayer: Initialization failed - behavior tree has no valid root task."); Node *agent = GET_NODE(this, agent_node); ERR_FAIL_NULL_MSG(agent, vformat("BTPlayer: Initialization failed - can't get agent with path '%s'.", agent_node)); - Node *scene_root = scene_root_hint ? scene_root_hint : get_owner(); + Node *scene_root = _get_scene_root(); ERR_FAIL_COND_MSG(scene_root == nullptr, "BTPlayer: Initialization failed - unable to establish scene root. This is likely due to BTPlayer not being owned by a scene node. Check BTPlayer.set_scene_root_hint()."); bt_instance = behavior_tree->instantiate(agent, blackboard, this, scene_root); @@ -184,7 +184,7 @@ void BTPlayer::_notification(int p_notification) { } if (blackboard_plan.is_valid()) { // Don't overwrite existing blackboard values as they may be initialized from code. - blackboard_plan->populate_blackboard(blackboard, false, this); + blackboard_plan->populate_blackboard(blackboard, false, this, _get_scene_root()); } if (behavior_tree.is_valid()) { _load_tree(); diff --git a/bt/bt_player.h b/bt/bt_player.h index 8789314..bd7fdf4 100644 --- a/bt/bt_player.h +++ b/bt/bt_player.h @@ -50,6 +50,7 @@ private: void _load_tree(); void _update_blackboard_plan(); + _FORCE_INLINE_ Node *_get_scene_root() const { return scene_root_hint ? scene_root_hint : get_owner(); } protected: static void _bind_methods(); diff --git a/bt/bt_state.cpp b/bt/bt_state.cpp index 55a760d..5449943 100644 --- a/bt/bt_state.cpp +++ b/bt/bt_state.cpp @@ -65,10 +65,14 @@ void BTState::_update_blackboard_plan() { } } +Node *BTState::_get_prefetch_root_for_base_plan() { + return _get_scene_root(); +} + void BTState::_setup() { LimboState::_setup(); ERR_FAIL_COND_MSG(behavior_tree.is_null(), "BTState: BehaviorTree is not assigned."); - Node *scene_root = scene_root_hint ? scene_root_hint : get_owner(); + Node *scene_root = _get_scene_root(); ERR_FAIL_NULL_MSG(scene_root, "BTState: Initialization failed - unable to establish scene root. This is likely due to BTState not being owned by a scene node. Check BTState.set_scene_root_hint()."); bt_instance = behavior_tree->instantiate(get_agent(), get_blackboard(), this, scene_root); ERR_FAIL_COND_MSG(bt_instance.is_null(), "BTState: Initialization failed - failed to instantiate behavior tree."); diff --git a/bt/bt_state.h b/bt/bt_state.h index 7cc12f3..fd05879 100644 --- a/bt/bt_state.h +++ b/bt/bt_state.h @@ -28,6 +28,8 @@ private: Node *scene_root_hint = nullptr; bool monitor_performance = false; + _FORCE_INLINE_ Node *_get_scene_root() const { return scene_root_hint ? scene_root_hint : get_owner(); } + protected: static void _bind_methods(); @@ -35,6 +37,7 @@ protected: virtual bool _should_use_new_scope() const override { return true; } virtual void _update_blackboard_plan() override; + virtual Node *_get_prefetch_root_for_base_plan() override; virtual void _setup() override; virtual void _exit() override; diff --git a/hsm/limbo_state.cpp b/hsm/limbo_state.cpp index a1c2017..69b2426 100644 --- a/hsm/limbo_state.cpp +++ b/hsm/limbo_state.cpp @@ -33,6 +33,10 @@ void LimboState::set_blackboard_plan(const Ref &p_plan) { void LimboState::_update_blackboard_plan() { } +Node *LimboState::_get_prefetch_root_for_base_plan() { + return this; +} + Ref LimboState::_get_parent_scope_plan() const { BlackboardPlan *parent_plan = nullptr; const LimboState *state = this; @@ -96,7 +100,7 @@ void LimboState::_initialize(Node *p_agent, const Ref &p_blackboard) } if (blackboard_plan.is_valid() && !blackboard_plan->is_empty()) { // Don't overwrite existing blackboard values as they may be initialized from code. - blackboard_plan->populate_blackboard(blackboard, false, this); + blackboard_plan->populate_blackboard(blackboard, false, this, _get_prefetch_root_for_base_plan()); } _setup(); diff --git a/hsm/limbo_state.h b/hsm/limbo_state.h index 31eed04..004afce 100644 --- a/hsm/limbo_state.h +++ b/hsm/limbo_state.h @@ -54,6 +54,7 @@ protected: virtual bool _should_use_new_scope() const { return blackboard_plan.is_valid() || is_root(); } virtual void _update_blackboard_plan(); + virtual Node *_get_prefetch_root_for_base_plan(); virtual void _setup(); virtual void _enter(); From 0e4c06f3b81dbba3d5041b1fe1152f548a9ddece Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sun, 15 Sep 2024 14:35:48 +0200 Subject: [PATCH 3/3] Update class docs --- doc_classes/BTPlayer.xml | 2 +- doc_classes/BTState.xml | 2 +- doc_classes/BlackboardPlan.xml | 10 ++++++---- doc_classes/LimboState.xml | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/doc_classes/BTPlayer.xml b/doc_classes/BTPlayer.xml index 7ce6ffd..4181c55 100644 --- a/doc_classes/BTPlayer.xml +++ b/doc_classes/BTPlayer.xml @@ -60,7 +60,7 @@ Stores and manages variables that will be used in constructing new [Blackboard] instances. - + If [code]true[/code], adds a performance monitor to "Debugger->Monitors" for each instance of this [BTPlayer] node. diff --git a/doc_classes/BTState.xml b/doc_classes/BTState.xml index d9d644d..ad80af1 100644 --- a/doc_classes/BTState.xml +++ b/doc_classes/BTState.xml @@ -30,7 +30,7 @@ HSM event that will be dispatched when the behavior tree results in [code]FAILURE[/code]. See [method LimboState.dispatch]. - + If [code]true[/code], adds a performance monitor to "Debugger->Monitors" for each instance of this [BTState] node. diff --git a/doc_classes/BlackboardPlan.xml b/doc_classes/BlackboardPlan.xml index 80de4d5..7bb5028 100644 --- a/doc_classes/BlackboardPlan.xml +++ b/doc_classes/BlackboardPlan.xml @@ -10,10 +10,11 @@ - + + - Constructs a new instance of a [Blackboard] using this plan. If [NodePath] prefetching is enabled, [param node] will be used to retrieve node instances for [NodePath] variables and substitute their values. + Constructs a new instance of a [Blackboard] using this plan. If [NodePath] prefetching is enabled, [param prefetch_root] will be used to retrieve node instances for [NodePath] variables and substitute their values. @@ -38,9 +39,10 @@ - + + - Populates [param blackboard] with the variables from this plan. If [param overwrite] is [code]true[/code], existing variables with the same names will be overwritten. If [NodePath] prefetching is enabled, [param node] will be used to retrieve node instances for [NodePath] variables and substitute their values. + Populates [param blackboard] with the variables from this plan. If [param overwrite] is [code]true[/code], existing variables with the same names will be overwritten. If [NodePath] prefetching is enabled, [param prefetch_root] will be used to retrieve node instances for [NodePath] variables and substitute their values. diff --git a/doc_classes/LimboState.xml b/doc_classes/LimboState.xml index dee6f47..34550c6 100644 --- a/doc_classes/LimboState.xml +++ b/doc_classes/LimboState.xml @@ -43,7 +43,7 @@ Registers a [param handler] to be called when [param event] is dispatched. The handler function should have the following signature: [codeblock] - func my_event_handler(cargo=null) -> bool: + func my_event_handler(cargo=null) -> bool: [/codeblock] If the handler returns [code]true[/code], the event will be consumed. Cargo is an optional parameter that can be passed to the handler. See also [method dispatch].