From 5ff42e29e48b5277d4b81bba07149099353cfc3b Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Apr 2024 01:11:51 +0200 Subject: [PATCH 1/4] Always duplicate `BlackboardPlan` props --- bt/bt_player.cpp | 2 +- hsm/limbo_state.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index df22d403..54608b4d 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -244,7 +244,7 @@ void BTPlayer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Idle,Physics,Manual"), "set_update_mode", "get_update_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "get_active"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard", PROPERTY_HINT_NONE, "Blackboard", 0), "set_blackboard", "get_blackboard"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_plan", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardPlan", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_blackboard_plan", "get_blackboard_plan"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_plan", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardPlan", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT | PROPERTY_USAGE_ALWAYS_DUPLICATE), "set_blackboard_plan", "get_blackboard_plan"); BIND_ENUM_CONSTANT(IDLE); BIND_ENUM_CONSTANT(PHYSICS); diff --git a/hsm/limbo_state.cpp b/hsm/limbo_state.cpp index 882530d5..62e451d5 100644 --- a/hsm/limbo_state.cpp +++ b/hsm/limbo_state.cpp @@ -206,7 +206,7 @@ void LimboState::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "EVENT_FINISHED", PROPERTY_HINT_NONE, "", 0), "", "event_finished"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "agent", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_agent", "get_agent"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard", PROPERTY_HINT_RESOURCE_TYPE, "Blackboard", 0), "", "get_blackboard"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_plan", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardPlan", PROPERTY_USAGE_DEFAULT), "set_blackboard_plan", "get_blackboard_plan"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_plan", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardPlan", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ALWAYS_DUPLICATE), "set_blackboard_plan", "get_blackboard_plan"); ADD_SIGNAL(MethodInfo("setup")); ADD_SIGNAL(MethodInfo("entered")); From d48daf2135dd136298f2aea8d4768214ce261a9d Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Apr 2024 01:45:40 +0200 Subject: [PATCH 2/4] BlackboardPlan: Avoid circular references in derived mode If the same plan resource is assigned in BehaviorTree and in BTPlayer, simply use the resource as is. Using the same resource for `BehaviorTree` and `BTPlayer` will disable derived mode, and allow managing from `BTPlayer` . There is a risk of using `NodePath` variables with different scenes: the path may actually be different if node structure is not the same. It will lead to fetching breaking for some of those scenes. --- blackboard/blackboard_plan.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index a4301870..4545a6b6 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -121,7 +121,11 @@ bool BlackboardPlan::_property_get_revert(const StringName &p_name, Variant &r_p } void BlackboardPlan::set_base_plan(const Ref &p_base) { - base = p_base; + if (p_base == this) { + base.unref(); + } else { + base = p_base; + } sync_with_base_plan(); notify_property_list_changed(); } From 302de87e32549ef52a6a8f58d7b50e144d8d7520 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Apr 2024 15:06:06 +0200 Subject: [PATCH 3/4] Prevent using external resources for derived blackboard plans in `BTPlayer` and `BTState` --- blackboard/blackboard_plan.cpp | 1 + bt/bt_player.cpp | 7 ++++++- bt/bt_state.cpp | 12 +++++++++--- hsm/limbo_state.h | 2 +- util/limbo_compat.h | 1 + 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index 4545a6b6..d55cd030 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -122,6 +122,7 @@ bool BlackboardPlan::_property_get_revert(const StringName &p_name, Variant &r_p void BlackboardPlan::set_base_plan(const Ref &p_base) { if (p_base == this) { + WARN_PRINT_ED("BlackboardPlan: Using same resource for derived blackboard plan is not supported."); base.unref(); } else { base = p_base; diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index 54608b4d..7840400f 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -86,7 +86,12 @@ void BTPlayer::set_behavior_tree(const Ref &p_tree) { } void BTPlayer::set_blackboard_plan(const Ref &p_plan) { - blackboard_plan = p_plan; + if (p_plan.is_valid() && !RESOURCE_IS_BUILT_IN(p_plan)) { + WARN_PRINT_ED("BTPlayer: Using external resource for derived blackboard plan is not supported. Converted to built-in resource."); + blackboard_plan = p_plan->duplicate(); + } else { + blackboard_plan = p_plan; + } _update_blackboard_plan(); } diff --git a/bt/bt_state.cpp b/bt/bt_state.cpp index 749762f9..ec1c64f7 100644 --- a/bt/bt_state.cpp +++ b/bt/bt_state.cpp @@ -31,16 +31,22 @@ void BTState::set_behavior_tree(const Ref &p_tree) { if (p_tree.is_valid()) { p_tree->connect(LW_NAME(changed), callable_mp(this, &BTState::_update_blackboard_plan)); } + behavior_tree = p_tree; _update_blackboard_plan(); + } else { + behavior_tree = p_tree; } - behavior_tree = p_tree; } void BTState::_update_blackboard_plan() { if (get_blackboard_plan().is_null()) { - set_blackboard_plan(Ref(memnew(BlackboardPlan))); + set_blackboard_plan(memnew(BlackboardPlan)); + } else if (!RESOURCE_IS_BUILT_IN(get_blackboard_plan())) { + WARN_PRINT_ED("BTState: Using external resource for derived blackboard plan is not supported. Converted to built-in resource."); + set_blackboard_plan(get_blackboard_plan()->duplicate()); + } else { + get_blackboard_plan()->set_base_plan(behavior_tree.is_valid() ? behavior_tree->get_blackboard_plan() : nullptr); } - get_blackboard_plan()->set_base_plan(behavior_tree.is_valid() ? behavior_tree->get_blackboard_plan() : nullptr); } void BTState::_setup() { diff --git a/hsm/limbo_state.h b/hsm/limbo_state.h index 706045e0..69bc2cfe 100644 --- a/hsm/limbo_state.h +++ b/hsm/limbo_state.h @@ -67,7 +67,7 @@ class LimboState : public Node { public: void set_blackboard_plan(const Ref &p_plan); - Ref get_blackboard_plan() const { return blackboard_plan; } + _FORCE_INLINE_ Ref get_blackboard_plan() const { return blackboard_plan; } Ref get_blackboard() const { return blackboard; } diff --git a/util/limbo_compat.h b/util/limbo_compat.h index 690682b3..321dc40b 100644 --- a/util/limbo_compat.h +++ b/util/limbo_compat.h @@ -234,6 +234,7 @@ Variant VARIANT_DEFAULT(Variant::Type p_type); #define PROJECT_CONFIG_FILE() GET_PROJECT_SETTINGS_DIR().path_join("limbo_ai.cfg") #define IS_RESOURCE_FILE(m_path) (m_path.begins_with("res://") && m_path.find("::") == -1) #define RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type) +#define RESOURCE_IS_BUILT_IN(m_res) (m_res->get_path().is_empty() || m_res->get_path().contains("::")) #ifdef TOOLS_ENABLED From 39182722272566ce8486e6619c87a65e0b084afb Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 1 Apr 2024 16:34:36 +0200 Subject: [PATCH 4/4] BehaviorTree: New signal for when BB plan changes Utilize `plan_changed` signal in `BTPlayer` and `BTState`. --- bt/behavior_tree.cpp | 5 ++++- bt/bt_player.cpp | 21 ++++++++++----------- bt/bt_state.cpp | 10 +++++----- doc/source/classes/class_behaviortree.rst | 17 +++++++++++++++++ doc_classes/BehaviorTree.xml | 7 +++++++ util/limbo_string_names.cpp | 1 + util/limbo_string_names.h | 1 + 7 files changed, 45 insertions(+), 17 deletions(-) diff --git a/bt/behavior_tree.cpp b/bt/behavior_tree.cpp index f5bfc1d6..4b5e5f03 100644 --- a/bt/behavior_tree.cpp +++ b/bt/behavior_tree.cpp @@ -48,7 +48,7 @@ void BehaviorTree::set_blackboard_plan(const Ref &p_plan) { blackboard_plan->connect(LW_NAME(changed), callable_mp(this, &BehaviorTree::_plan_changed)); } - emit_changed(); + _plan_changed(); } void BehaviorTree::set_root_task(const Ref &p_value) { @@ -79,6 +79,7 @@ Ref BehaviorTree::instantiate(Node *p_agent, const Ref &p_bl } void BehaviorTree::_plan_changed() { + emit_signal(LW_NAME(plan_changed)); emit_changed(); } @@ -96,6 +97,8 @@ void BehaviorTree::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "description", PROPERTY_HINT_MULTILINE_TEXT), "set_description", "get_description"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_plan", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardPlan", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_blackboard_plan", "get_blackboard_plan"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_task", PROPERTY_HINT_RESOURCE_TYPE, "BTTask", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_root_task", "get_root_task"); + + ADD_SIGNAL(MethodInfo("plan_changed")); } BehaviorTree::BehaviorTree() { diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index 7840400f..626962ac 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -63,17 +63,21 @@ void BTPlayer::_load_tree() { void BTPlayer::_update_blackboard_plan() { if (blackboard_plan.is_null()) { blackboard_plan = Ref(memnew(BlackboardPlan)); + } else if (!RESOURCE_IS_BUILT_IN(blackboard_plan)) { + WARN_PRINT_ED("BTPlayer: Using external resource for derived blackboard plan is not supported. Converted to built-in resource."); + blackboard_plan = blackboard_plan->duplicate(); } + blackboard_plan->set_base_plan(behavior_tree.is_valid() ? behavior_tree->get_blackboard_plan() : nullptr); } void BTPlayer::set_behavior_tree(const Ref &p_tree) { if (Engine::get_singleton()->is_editor_hint()) { - if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(changed), callable_mp(this, &BTPlayer::_update_blackboard_plan))) { - behavior_tree->disconnect(LW_NAME(changed), callable_mp(this, &BTPlayer::_update_blackboard_plan)); + if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(plan_changed), callable_mp(this, &BTPlayer::_update_blackboard_plan))) { + behavior_tree->disconnect(LW_NAME(plan_changed), callable_mp(this, &BTPlayer::_update_blackboard_plan)); } if (p_tree.is_valid()) { - p_tree->connect(LW_NAME(changed), callable_mp(this, &BTPlayer::_update_blackboard_plan)); + p_tree->connect(LW_NAME(plan_changed), callable_mp(this, &BTPlayer::_update_blackboard_plan)); } behavior_tree = p_tree; _update_blackboard_plan(); @@ -86,12 +90,7 @@ void BTPlayer::set_behavior_tree(const Ref &p_tree) { } void BTPlayer::set_blackboard_plan(const Ref &p_plan) { - if (p_plan.is_valid() && !RESOURCE_IS_BUILT_IN(p_plan)) { - WARN_PRINT_ED("BTPlayer: Using external resource for derived blackboard plan is not supported. Converted to built-in resource."); - blackboard_plan = p_plan->duplicate(); - } else { - blackboard_plan = p_plan; - } + blackboard_plan = p_plan; _update_blackboard_plan(); } @@ -217,8 +216,8 @@ void BTPlayer::_notification(int p_notification) { #endif // DEBUG_ENABLED if (Engine::get_singleton()->is_editor_hint()) { - if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(changed), callable_mp(this, &BTPlayer::_update_blackboard_plan))) { - behavior_tree->disconnect(LW_NAME(changed), callable_mp(this, &BTPlayer::_update_blackboard_plan)); + if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(plan_changed), callable_mp(this, &BTPlayer::_update_blackboard_plan))) { + behavior_tree->disconnect(LW_NAME(plan_changed), callable_mp(this, &BTPlayer::_update_blackboard_plan)); } } diff --git a/bt/bt_state.cpp b/bt/bt_state.cpp index ec1c64f7..84974082 100644 --- a/bt/bt_state.cpp +++ b/bt/bt_state.cpp @@ -25,11 +25,11 @@ void BTState::set_behavior_tree(const Ref &p_tree) { if (Engine::get_singleton()->is_editor_hint()) { - if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(changed), callable_mp(this, &BTState::_update_blackboard_plan))) { - behavior_tree->disconnect(LW_NAME(changed), callable_mp(this, &BTState::_update_blackboard_plan)); + if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(plan_changed), callable_mp(this, &BTState::_update_blackboard_plan))) { + behavior_tree->disconnect(LW_NAME(plan_changed), callable_mp(this, &BTState::_update_blackboard_plan)); } if (p_tree.is_valid()) { - p_tree->connect(LW_NAME(changed), callable_mp(this, &BTState::_update_blackboard_plan)); + p_tree->connect(LW_NAME(plan_changed), callable_mp(this, &BTState::_update_blackboard_plan)); } behavior_tree = p_tree; _update_blackboard_plan(); @@ -97,8 +97,8 @@ void BTState::_notification(int p_notification) { #endif // DEBUG_ENABLED if (Engine::get_singleton()->is_editor_hint()) { - if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(changed), callable_mp(this, &BTState::_update_blackboard_plan))) { - behavior_tree->disconnect(LW_NAME(changed), callable_mp(this, &BTState::_update_blackboard_plan)); + if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(plan_changed), callable_mp(this, &BTState::_update_blackboard_plan))) { + behavior_tree->disconnect(LW_NAME(plan_changed), callable_mp(this, &BTState::_update_blackboard_plan)); } } } break; diff --git a/doc/source/classes/class_behaviortree.rst b/doc/source/classes/class_behaviortree.rst index a5b153f8..b91f6629 100644 --- a/doc/source/classes/class_behaviortree.rst +++ b/doc/source/classes/class_behaviortree.rst @@ -73,6 +73,23 @@ Methods .. rst-class:: classref-descriptions-group +Signals +------- + +.. _class_BehaviorTree_signal_plan_changed: + +.. rst-class:: classref-signal + +**plan_changed** **(** **)** + +Emitted when the :ref:`BlackboardPlan` changes. + +.. rst-class:: classref-section-separator + +---- + +.. rst-class:: classref-descriptions-group + Property Descriptions --------------------- diff --git a/doc_classes/BehaviorTree.xml b/doc_classes/BehaviorTree.xml index d36063f4..e5f9645e 100644 --- a/doc_classes/BehaviorTree.xml +++ b/doc_classes/BehaviorTree.xml @@ -58,4 +58,11 @@ User-provided description of the BehaviorTree. + + + + Emitted when the [BlackboardPlan] changes. + + + diff --git a/util/limbo_string_names.cpp b/util/limbo_string_names.cpp index c1ad2d68..0f1dd67b 100644 --- a/util/limbo_string_names.cpp +++ b/util/limbo_string_names.cpp @@ -122,6 +122,7 @@ LimboStringNames::LimboStringNames() { NonFavorite = SN("NonFavorite"); normal = SN("normal"); panel = SN("panel"); + plan_changed = SN("plan_changed"); popup_hide = SN("popup_hide"); pressed = SN("pressed"); probability_clicked = SN("probability_clicked"); diff --git a/util/limbo_string_names.h b/util/limbo_string_names.h index 38147852..fe62c273 100644 --- a/util/limbo_string_names.h +++ b/util/limbo_string_names.h @@ -137,6 +137,7 @@ class LimboStringNames { StringName NonFavorite; StringName normal; StringName panel; + StringName plan_changed; StringName popup_hide; StringName pressed; StringName probability_clicked;