diff --git a/blackboard/bb_variable.cpp b/blackboard/bb_variable.cpp index fac7d08..092357c 100644 --- a/blackboard/bb_variable.cpp +++ b/blackboard/bb_variable.cpp @@ -25,7 +25,7 @@ void BBVariable::set_value(const Variant &p_value) { data->value_changed = true; if (is_bound()) { - Object *obj = ObjectDB::get_instance(ObjectID(data->bound_object)); + Object *obj = OBJECT_DB_GET_INSTANCE(data->bound_object); ERR_FAIL_COND_MSG(!obj, "Blackboard: Failed to get bound object."); #ifdef LIMBOAI_MODULE bool r_valid; @@ -39,7 +39,7 @@ void BBVariable::set_value(const Variant &p_value) { Variant BBVariable::get_value() const { if (is_bound()) { - Object *obj = ObjectDB::get_instance(ObjectID(data->bound_object)); + Object *obj = OBJECT_DB_GET_INSTANCE(data->bound_object); ERR_FAIL_COND_V_MSG(!obj, data->value, "Blackboard: Failed to get bound object."); #ifdef LIMBOAI_MODULE bool r_valid; diff --git a/bt/behavior_tree.cpp b/bt/behavior_tree.cpp index b15f480..c6a5cf3 100644 --- a/bt/behavior_tree.cpp +++ b/bt/behavior_tree.cpp @@ -77,13 +77,15 @@ void BehaviorTree::copy_other(const Ref &p_other) { root_task = p_other->get_root_task(); } -Ref BehaviorTree::instantiate(Node *p_agent, const Ref &p_blackboard, Node *p_scene_root) const { - ERR_FAIL_COND_V_MSG(root_task == nullptr, memnew(BTTask), "Trying to instance a behavior tree with no valid root task."); - ERR_FAIL_NULL_V_MSG(p_agent, memnew(BTTask), "Trying to instance a behavior tree with no valid agent."); - ERR_FAIL_NULL_V_MSG(p_scene_root, memnew(BTTask), "Trying to instance a behavior tree with no valid scene root."); - Ref inst = root_task->clone(); - inst->initialize(p_agent, p_blackboard, p_scene_root); - return inst; +Ref BehaviorTree::instantiate(Node *p_agent, const Ref &p_blackboard, Node *p_instance_owner) const { + ERR_FAIL_COND_V_MSG(root_task == nullptr, nullptr, "BehaviorTree: Instantiation failed - BT has no valid root task."); + ERR_FAIL_NULL_V_MSG(p_agent, nullptr, "BehaviorTree: Instantiation failed - agent can't be null."); + ERR_FAIL_NULL_V_MSG(p_instance_owner, nullptr, "BehaviorTree: Instantiation failed -- instance owner can't be null."); + Node *scene_root = p_instance_owner->get_owner(); + ERR_FAIL_NULL_V_MSG(scene_root, nullptr, "BehaviorTree: Instantiation failed - can't get scene root, because instance_owner not owned by a scene node. Hint: Try my_player.set_owner(get_owner())."); + Ref root_copy = root_task->clone(); + root_copy->initialize(p_agent, p_blackboard, scene_root); + return BTInstance::create(root_copy, get_path(), p_instance_owner); } void BehaviorTree::_plan_changed() { @@ -116,7 +118,7 @@ void BehaviorTree::_bind_methods() { ClassDB::bind_method(D_METHOD("get_root_task"), &BehaviorTree::get_root_task); ClassDB::bind_method(D_METHOD("clone"), &BehaviorTree::clone); ClassDB::bind_method(D_METHOD("copy_other", "other"), &BehaviorTree::copy_other); - ClassDB::bind_method(D_METHOD("instantiate", "agent", "blackboard", "scene_root"), &BehaviorTree::instantiate); + ClassDB::bind_method(D_METHOD("instantiate", "agent", "blackboard", "instance_owner"), &BehaviorTree::instantiate); 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"); diff --git a/bt/behavior_tree.h b/bt/behavior_tree.h index 46507c5..58b2db7 100644 --- a/bt/behavior_tree.h +++ b/bt/behavior_tree.h @@ -13,6 +13,7 @@ #define BEHAVIOR_TREE_H #include "../blackboard/blackboard_plan.h" +#include "bt_instance.h" #include "tasks/bt_task.h" #ifdef LIMBOAI_MODULE @@ -58,7 +59,7 @@ public: Ref clone() const; void copy_other(const Ref &p_other); - Ref instantiate(Node *p_agent, const Ref &p_blackboard, Node *p_scene_root) const; + Ref instantiate(Node *p_agent, const Ref &p_blackboard, Node *p_instance_owner) const; BehaviorTree(); ~BehaviorTree(); diff --git a/bt/bt_instance.cpp b/bt/bt_instance.cpp new file mode 100644 index 0000000..50011b8 --- /dev/null +++ b/bt/bt_instance.cpp @@ -0,0 +1,150 @@ +/** + * bt_instance.cpp + * ============================================================================= + * Copyright 2021-2024 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ + +#include "bt_instance.h" + +#include "../editor/debugger/limbo_debugger.h" +#include "behavior_tree.h" + +#ifdef LIMBOAI_MODULE +#include "core/os/time.h" +#include "main/performance.h" +#endif + +#ifdef LIMBOAI_GDEXTENSION +#include +#include +#endif + +Ref BTInstance::create(Ref p_root_task, String p_source_bt_path, Node *p_owner_node) { + ERR_FAIL_NULL_V(p_root_task, nullptr); + ERR_FAIL_NULL_V(p_owner_node, nullptr); + Ref inst; + inst.instantiate(); + inst->root_task = p_root_task; + inst->owner_node_id = p_owner_node->get_instance_id(); + inst->source_bt_path = p_source_bt_path; + return inst; +} + +BT::Status BTInstance::update(double p_delta) { + ERR_FAIL_COND_V(!root_task.is_valid(), BT::FRESH); + +#ifdef DEBUG_ENABLED + double start = Time::get_singleton()->get_ticks_usec(); +#endif + + last_status = root_task->execute(p_delta); + emit_signal(LimboStringNames::get_singleton()->updated, last_status); + +#ifdef DEBUG_ENABLED + double end = Time::get_singleton()->get_ticks_usec(); + update_time_acc += (end - start); + update_time_n += 1.0; +#endif + return last_status; +} + +void BTInstance::set_monitor_performance(bool p_monitor) { +#ifdef DEBUG_ENABLED + monitor_performance = p_monitor; + if (monitor_performance) { + _add_custom_monitor(); + } else { + _remove_custom_monitor(); + } +#endif +} + +bool BTInstance::get_monitor_performance() const { +#ifdef DEBUG_ENABLED + return monitor_performance; +#else + return false; +#endif +} + +void BTInstance::register_with_debugger() { +#ifdef DEBUG_ENABLED + if (LimboDebugger::get_singleton()->is_active()) { + LimboDebugger::get_singleton()->register_bt_instance(get_instance_id()); + } +#endif +} + +void BTInstance::unregister_with_debugger() { +#ifdef DEBUG_ENABLED + if (LimboDebugger::get_singleton()->is_active()) { + LimboDebugger::get_singleton()->unregister_bt_instance(get_instance_id()); + } +#endif +} + +#ifdef DEBUG_ENABLED + +double BTInstance::_get_mean_update_time_msec_and_reset() { + if (update_time_n) { + double mean_time_msec = (update_time_acc * 0.001) / update_time_n; + update_time_acc = 0.0; + update_time_n = 0.0; + return mean_time_msec; + } + return 0.0; +} + +void BTInstance::_add_custom_monitor() { + ERR_FAIL_NULL(get_owner_node()); + ERR_FAIL_NULL(root_task); + ERR_FAIL_NULL(root_task->get_agent()); + + if (monitor_id == StringName()) { + monitor_id = vformat("LimboAI/update_ms|%s_%s_%s", root_task->get_agent()->get_name(), get_owner_node()->get_name(), + String(itos(get_instance_id())).md5_text().substr(0, 4)); + } + if (!Performance::get_singleton()->has_custom_monitor(monitor_id)) { + PERFORMANCE_ADD_CUSTOM_MONITOR(monitor_id, callable_mp(this, &BTInstance::_get_mean_update_time_msec_and_reset)); + } +} + +void BTInstance::_remove_custom_monitor() { + if (monitor_id != StringName() && Performance::get_singleton()->has_custom_monitor(monitor_id)) { + Performance::get_singleton()->remove_custom_monitor(monitor_id); + } +} + +#endif // * DEBUG_ENABLED + +void BTInstance::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_root_task"), &BTInstance::get_root_task); + ClassDB::bind_method(D_METHOD("get_owner_node"), &BTInstance::get_owner_node); + ClassDB::bind_method(D_METHOD("get_last_status"), &BTInstance::get_last_status); + ClassDB::bind_method(D_METHOD("get_source_bt_path"), &BTInstance::get_source_bt_path); + + ClassDB::bind_method(D_METHOD("set_monitor_performance", "monitor"), &BTInstance::set_monitor_performance); + ClassDB::bind_method(D_METHOD("get_monitor_performance"), &BTInstance::get_monitor_performance); + + ClassDB::bind_method(D_METHOD("update", "delta"), &BTInstance::update); + + ClassDB::bind_method(D_METHOD("register_with_debugger"), &BTInstance::register_with_debugger); + ClassDB::bind_method(D_METHOD("unregister_with_debugger"), &BTInstance::unregister_with_debugger); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitor_performance"), "set_monitor_performance", "get_monitor_performance"); + + ADD_SIGNAL(MethodInfo("updated", PropertyInfo(Variant::INT, "status"))); + ADD_SIGNAL(MethodInfo("freed")); +} + +BTInstance::~BTInstance() { + emit_signal(LW_NAME(freed)); +#ifdef DEBUG_ENABLED + _remove_custom_monitor(); +#endif +} diff --git a/bt/bt_instance.h b/bt/bt_instance.h new file mode 100644 index 0000000..83dbf78 --- /dev/null +++ b/bt/bt_instance.h @@ -0,0 +1,61 @@ +/** + * bt_instance.h + * ============================================================================= + * Copyright 2021-2024 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ +#ifndef BT_INSTANCE_H +#define BT_INSTANCE_H + +#include "tasks/bt_task.h" + +class BTInstance : public RefCounted { + GDCLASS(BTInstance, RefCounted); + +private: + Ref root_task; + uint64_t owner_node_id = 0; + String source_bt_path; + BT::Status last_status = BT::FRESH; + +#ifdef DEBUG_ENABLED + bool monitor_performance = false; + StringName monitor_id; + double update_time_acc = 0.0; + double update_time_n = 0.0; + + double _get_mean_update_time_msec_and_reset(); + void _add_custom_monitor(); + void _remove_custom_monitor(); + +#endif // * DEBUG_ENABLED + +protected: + static void _bind_methods(); + +public: + _FORCE_INLINE_ Ref get_root_task() const { return root_task; } + _FORCE_INLINE_ Node *get_owner_node() const { return owner_node_id ? Object::cast_to(OBJECT_DB_GET_INSTANCE(owner_node_id)) : nullptr; } + _FORCE_INLINE_ BT::Status get_last_status() const { return last_status; } + _FORCE_INLINE_ bool is_instance_valid() const { return root_task.is_valid(); } + _FORCE_INLINE_ String get_source_bt_path() const { return source_bt_path; } + + BT::Status update(double p_delta); + + void set_monitor_performance(bool p_monitor); + bool get_monitor_performance() const; + + void register_with_debugger(); + void unregister_with_debugger(); + + static Ref create(Ref p_root_task, String p_source_bt_path, Node *p_owner_node); + + BTInstance() = default; + ~BTInstance(); +}; + +#endif // BT_INSTANCE_H diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index ad0b515..72d1736 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -11,7 +11,6 @@ #include "bt_player.h" -#include "../editor/debugger/limbo_debugger.h" #include "../util/limbo_compat.h" #include "../util/limbo_string_names.h" @@ -44,24 +43,19 @@ VARIANT_ENUM_CAST(BTPlayer::UpdateMode); void BTPlayer::_load_tree() { -#ifdef DEBUG_ENABLED - if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) { - LimboDebugger::get_singleton()->unregister_bt_instance(tree_instance, get_path()); - } -#endif - tree_instance.unref(); + bt_instance.unref(); ERR_FAIL_COND_MSG(!behavior_tree.is_valid(), "BTPlayer: Initialization failed - needs a valid behavior 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 = get_owner(); ERR_FAIL_NULL_MSG(scene_root, "BTPlayer: Initialization failed - can't get scene root (make sure the BTPlayer's owner property is set)."); - tree_instance = behavior_tree->instantiate(agent, blackboard, scene_root); + bt_instance = behavior_tree->instantiate(agent, blackboard, this); + ERR_FAIL_COND_MSG(bt_instance.is_null(), "BTPlayer: Failed to instantiate behavior tree."); #ifdef DEBUG_ENABLED - if (IS_DEBUGGER_ACTIVE()) { - LimboDebugger::get_singleton()->register_bt_instance(tree_instance, get_path()); - } -#endif + bt_instance->set_monitor_performance(monitor_performance); + bt_instance->register_with_debugger(); +#endif // DEBUG_ENABLED } void BTPlayer::_update_blackboard_plan() { @@ -87,7 +81,7 @@ void BTPlayer::set_behavior_tree(const Ref &p_tree) { _update_blackboard_plan(); } else { behavior_tree = p_tree; - if (get_owner()) { + if (get_owner() && is_inside_tree()) { _load_tree(); } } @@ -95,7 +89,7 @@ void BTPlayer::set_behavior_tree(const Ref &p_tree) { void BTPlayer::set_agent_node(const NodePath &p_agent_node) { agent_node = p_agent_node; - if (tree_instance.is_valid()) { + if (bt_instance.is_valid()) { ERR_PRINT("BTPlayer: Agent node cannot be set after the behavior tree is instantiated. This change will not affect the behavior tree instance."); } } @@ -119,33 +113,25 @@ void BTPlayer::set_active(bool p_active) { } void BTPlayer::update(double p_delta) { - if (!tree_instance.is_valid()) { + if (!bt_instance.is_valid()) { ERR_PRINT_ONCE(vformat("BTPlayer doesn't have a behavior tree with a valid root task to execute (owner: %s)", get_owner())); return; } -#ifdef DEBUG_ENABLED - double start = GET_TICKS_USEC(); -#endif - if (active) { - last_status = tree_instance->execute(p_delta); - emit_signal(LimboStringNames::get_singleton()->updated, last_status); - if (last_status == BTTask::SUCCESS || last_status == BTTask::FAILURE) { - emit_signal(LimboStringNames::get_singleton()->behavior_tree_finished, last_status); + BT::Status status = bt_instance->update(p_delta); + emit_signal(LW_NAME(updated), status); +#ifndef DISABLE_DEPRECATED + if (status == BTTask::SUCCESS || status == BTTask::FAILURE) { + emit_signal(LW_NAME(behavior_tree_finished), status); } +#endif // DISABLE_DEPRECATED } - -#ifdef DEBUG_ENABLED - double end = GET_TICKS_USEC(); - update_time_acc += (end - start); - update_time_n += 1.0; -#endif } void BTPlayer::restart() { - ERR_FAIL_COND_MSG(tree_instance.is_null(), "BTPlayer: Restart failed - no valid tree instance. Make sure the BTPlayer has a valid behavior tree with a valid root task."); - tree_instance->abort(); + ERR_FAIL_COND_MSG(bt_instance.is_null(), "BTPlayer: Restart failed - no valid tree instance. Make sure the BTPlayer has a valid behavior tree with a valid root task."); + bt_instance->get_root_task()->abort(); set_active(true); } @@ -154,42 +140,9 @@ void BTPlayer::restart() { void BTPlayer::_set_monitor_performance(bool p_monitor_performance) { monitor_performance = p_monitor_performance; - if (!get_owner() && monitor_performance) { - // Don't add custom monitor if not in scene. - return; + if (bt_instance.is_valid()) { + bt_instance->set_monitor_performance(monitor_performance); } - - if (monitor_performance) { - _add_custom_monitor(); - } else { - _remove_custom_monitor(); - } -} - -void BTPlayer::_add_custom_monitor() { - if (monitor_id == StringName()) { - monitor_id = vformat("LimboAI/update_ms|%s_%s_%s", get_owner()->get_name(), get_name(), - String(itos(get_instance_id())).md5_text().substr(0, 4)); - } - if (!Performance::get_singleton()->has_custom_monitor(monitor_id)) { - PERFORMANCE_ADD_CUSTOM_MONITOR(monitor_id, callable_mp(this, &BTPlayer::_get_mean_update_time_msec)); - } -} - -void BTPlayer::_remove_custom_monitor() { - if (monitor_id != StringName() && Performance::get_singleton()->has_custom_monitor(monitor_id)) { - Performance::get_singleton()->remove_custom_monitor(monitor_id); - } -} - -double BTPlayer::_get_mean_update_time_msec() { - if (update_time_n) { - double mean_time_msec = (update_time_acc * 0.001) / update_time_n; - update_time_acc = 0.0; - update_time_n = 0.0; - return mean_time_msec; - } - return 0.0; } #endif // ! DEBUG_ENABLED @@ -222,21 +175,17 @@ void BTPlayer::_notification(int p_notification) { } break; case NOTIFICATION_ENTER_TREE: { #ifdef DEBUG_ENABLED - if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) { - LimboDebugger::get_singleton()->register_bt_instance(tree_instance, get_path()); - } - if (monitor_performance) { - _add_custom_monitor(); + if (bt_instance.is_valid()) { + bt_instance->set_monitor_performance(monitor_performance); + bt_instance->register_with_debugger(); } #endif // DEBUG_ENABLED } break; case NOTIFICATION_EXIT_TREE: { #ifdef DEBUG_ENABLED - if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) { - LimboDebugger::get_singleton()->unregister_bt_instance(tree_instance, get_path()); - } - if (monitor_performance) { - _remove_custom_monitor(); + if (bt_instance.is_valid()) { + bt_instance->set_monitor_performance(false); + bt_instance->unregister_with_debugger(); } #endif // DEBUG_ENABLED @@ -266,9 +215,8 @@ void BTPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("update", "delta"), &BTPlayer::update); ClassDB::bind_method(D_METHOD("restart"), &BTPlayer::restart); - ClassDB::bind_method(D_METHOD("get_last_status"), &BTPlayer::get_last_status); - ClassDB::bind_method(D_METHOD("get_tree_instance"), &BTPlayer::get_tree_instance); + ClassDB::bind_method(D_METHOD("get_bt_instance"), &BTPlayer::get_bt_instance); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "behavior_tree", PROPERTY_HINT_RESOURCE_TYPE, "BehaviorTree"), "set_behavior_tree", "get_behavior_tree"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "agent_node"), "set_agent_node", "get_agent_node"); @@ -281,9 +229,12 @@ void BTPlayer::_bind_methods() { BIND_ENUM_CONSTANT(PHYSICS); BIND_ENUM_CONSTANT(MANUAL); - ADD_SIGNAL(MethodInfo("behavior_tree_finished", PropertyInfo(Variant::INT, "status"))); ADD_SIGNAL(MethodInfo("updated", PropertyInfo(Variant::INT, "status"))); +#ifndef DISABLE_DEPRECATED + ADD_SIGNAL(MethodInfo("behavior_tree_finished", PropertyInfo(Variant::INT, "status"))); +#endif + #ifdef DEBUG_ENABLED ClassDB::bind_method(D_METHOD("_set_monitor_performance", "enable"), &BTPlayer::_set_monitor_performance); ClassDB::bind_method(D_METHOD("_get_monitor_performance"), &BTPlayer::_get_monitor_performance); diff --git a/bt/bt_player.h b/bt/bt_player.h index 9ff705c..62f6d9f 100644 --- a/bt/bt_player.h +++ b/bt/bt_player.h @@ -15,6 +15,7 @@ #include "../blackboard/blackboard.h" #include "../blackboard/blackboard_plan.h" #include "behavior_tree.h" +#include "bt_instance.h" #include "tasks/bt_task.h" #ifdef LIMBOAI_MODULE @@ -42,9 +43,8 @@ private: UpdateMode update_mode = UpdateMode::PHYSICS; bool active = true; Ref blackboard; - int last_status = -1; - Ref tree_instance; + Ref bt_instance; void _load_tree(); void _update_blackboard_plan(); @@ -75,9 +75,8 @@ public: void update(double p_delta); void restart(); - int get_last_status() const { return last_status; } - Ref get_tree_instance() { return tree_instance; } + Ref get_bt_instance() { return bt_instance; } BTPlayer(); ~BTPlayer(); @@ -86,19 +85,11 @@ public: private: bool monitor_performance = false; - StringName monitor_id; - double update_time_acc = 0.0; - double update_time_n = 0.0; void _set_monitor_performance(bool p_monitor_performance); bool _get_monitor_performance() const { return monitor_performance; } - void _add_custom_monitor(); - void _remove_custom_monitor(); - - double _get_mean_update_time_msec(); - -#endif // DEBUG_ENABLED +#endif // * DEBUG_ENABLED }; #endif // BT_PLAYER_H diff --git a/bt/bt_state.cpp b/bt/bt_state.cpp index dee8e34..9620687 100644 --- a/bt/bt_state.cpp +++ b/bt/bt_state.cpp @@ -11,7 +11,6 @@ #include "bt_state.h" -#include "../editor/debugger/limbo_debugger.h" #include "../util/limbo_compat.h" #include "../util/limbo_string_names.h" @@ -54,18 +53,17 @@ void BTState::_setup() { ERR_FAIL_COND_MSG(behavior_tree.is_null(), "BTState: BehaviorTree is not assigned."); Node *scene_root = get_owner(); ERR_FAIL_NULL_MSG(scene_root, "BTState: Initialization failed - can't get scene root (make sure the BTState's owner property is set)."); - tree_instance = behavior_tree->instantiate(get_agent(), get_blackboard(), scene_root); + bt_instance = behavior_tree->instantiate(get_agent(), get_blackboard(), this); + ERR_FAIL_COND_MSG(bt_instance.is_null(), "BTState: Initialization failed - can't instantiate behavior tree."); #ifdef DEBUG_ENABLED - if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) { - LimboDebugger::get_singleton()->register_bt_instance(tree_instance, get_path()); - } + bt_instance->register_with_debugger(); #endif } void BTState::_exit() { - if (tree_instance.is_valid()) { - tree_instance->abort(); + if (bt_instance.is_valid()) { + bt_instance->get_root_task()->abort(); } else { ERR_PRINT_ONCE("BTState: BehaviorTree is not assigned."); } @@ -78,8 +76,8 @@ void BTState::_update(double p_delta) { // Bail out if a transition happened in the meantime. return; } - ERR_FAIL_NULL(tree_instance); - int status = tree_instance->execute(p_delta); + ERR_FAIL_NULL(bt_instance); + BT::Status status = bt_instance->update(p_delta); if (status == BTTask::SUCCESS) { get_root()->dispatch(success_event, Variant()); } else if (status == BTTask::FAILURE) { @@ -92,15 +90,15 @@ void BTState::_notification(int p_notification) { switch (p_notification) { #ifdef DEBUG_ENABLED case NOTIFICATION_ENTER_TREE: { - if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) { - LimboDebugger::get_singleton()->register_bt_instance(tree_instance, get_path()); + if (bt_instance.is_valid()) { + bt_instance->register_with_debugger(); } } break; #endif // DEBUG_ENABLED case NOTIFICATION_EXIT_TREE: { #ifdef DEBUG_ENABLED - if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) { - LimboDebugger::get_singleton()->unregister_bt_instance(tree_instance, get_path()); + if (bt_instance.is_valid()) { + bt_instance->unregister_with_debugger(); } #endif // DEBUG_ENABLED @@ -117,7 +115,7 @@ void BTState::_bind_methods() { ClassDB::bind_method(D_METHOD("set_behavior_tree", "behavior_tree"), &BTState::set_behavior_tree); ClassDB::bind_method(D_METHOD("get_behavior_tree"), &BTState::get_behavior_tree); - ClassDB::bind_method(D_METHOD("get_tree_instance"), &BTState::get_tree_instance); + ClassDB::bind_method(D_METHOD("get_bt_instance"), &BTState::get_bt_instance); ClassDB::bind_method(D_METHOD("set_success_event", "event"), &BTState::set_success_event); ClassDB::bind_method(D_METHOD("get_success_event"), &BTState::get_success_event); diff --git a/bt/bt_state.h b/bt/bt_state.h index 717b975..94b1c4f 100644 --- a/bt/bt_state.h +++ b/bt/bt_state.h @@ -22,7 +22,7 @@ class BTState : public LimboState { private: Ref behavior_tree; - Ref tree_instance; + Ref bt_instance; StringName success_event; StringName failure_event; @@ -42,7 +42,7 @@ public: void set_behavior_tree(const Ref &p_value); Ref get_behavior_tree() const { return behavior_tree; } - Ref get_tree_instance() const { return tree_instance; } + Ref get_bt_instance() const { return bt_instance; } void set_success_event(const StringName &p_success_event) { success_event = p_success_event; } StringName get_success_event() const { return success_event; } diff --git a/config.py b/config.py index fe81b90f..8bbe518 100644 --- a/config.py +++ b/config.py @@ -83,6 +83,7 @@ def get_doc_classes(): "BTDynamicSequence", "BTFail", "BTForEach", + "BTInstance", "BTInvert", "BTNewScope", "BTParallel", diff --git a/demo/demo/scenes/showcase.gd b/demo/demo/scenes/showcase.gd index 680652a..2b09558 100644 --- a/demo/demo/scenes/showcase.gd +++ b/demo/demo/scenes/showcase.gd @@ -42,8 +42,8 @@ func _ready() -> void: func _physics_process(_delta: float) -> void: - var inst: BTTask = bt_player.get_tree_instance() - var bt_data: BehaviorTreeData = BehaviorTreeData.create_from_tree_instance(inst) + var inst: BTInstance = bt_player.get_bt_instance() + var bt_data: BehaviorTreeData = BehaviorTreeData.create_from_bt_instance(inst) behavior_tree_view.update_tree(bt_data) diff --git a/demo/demo/scenes/showcase.tscn b/demo/demo/scenes/showcase.tscn index fa864e3..4c23f0f 100644 --- a/demo/demo/scenes/showcase.tscn +++ b/demo/demo/scenes/showcase.tscn @@ -257,8 +257,8 @@ anchor_right = 1.0 anchor_bottom = 1.0 offset_left = 4.0 offset_top = 4.0 -offset_right = -4.0 -offset_bottom = -4.0 +offset_right = 1020.0 +offset_bottom = 704.0 grow_horizontal = 2 grow_vertical = 2 gutters_draw_line_numbers = true diff --git a/doc/source/classes/class_behaviortree.rst b/doc/source/classes/class_behaviortree.rst index 4148cf5..f35f715 100644 --- a/doc/source/classes/class_behaviortree.rst +++ b/doc/source/classes/class_behaviortree.rst @@ -55,17 +55,17 @@ Methods .. table:: :widths: auto - +-----------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`BehaviorTree` | :ref:`clone`\ (\ ) |const| | - +-----------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | |void| | :ref:`copy_other`\ (\ other\: :ref:`BehaviorTree`\ ) | - +-----------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`BTTask` | :ref:`get_root_task`\ (\ ) |const| | - +-----------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`BTTask` | :ref:`instantiate`\ (\ agent\: ``Node``, blackboard\: :ref:`Blackboard`, scene_root\: ``Node``\ ) |const| | - +-----------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | |void| | :ref:`set_root_task`\ (\ task\: :ref:`BTTask`\ ) | - +-----------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`BehaviorTree` | :ref:`clone`\ (\ ) |const| | + +-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | |void| | :ref:`copy_other`\ (\ other\: :ref:`BehaviorTree`\ ) | + +-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`BTTask` | :ref:`get_root_task`\ (\ ) |const| | + +-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`BTInstance` | :ref:`instantiate`\ (\ agent\: ``Node``, blackboard\: :ref:`Blackboard`, instance_owner\: ``Node``\ ) |const| | + +-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | |void| | :ref:`set_root_task`\ (\ task\: :ref:`BTTask`\ ) | + +-----------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ .. rst-class:: classref-section-separator @@ -121,7 +121,7 @@ Stores and manages variables that will be used in constructing new :ref:`Blackbo - |void| **set_description**\ (\ value\: ``String``\ ) - ``String`` **get_description**\ (\ ) -User-provided description of the BehaviorTree. +User-provided description of the **BehaviorTree**. .. rst-class:: classref-section-separator @@ -172,9 +172,9 @@ Returns the root task of the BehaviorTree resource. .. rst-class:: classref-method -:ref:`BTTask` **instantiate**\ (\ agent\: ``Node``, blackboard\: :ref:`Blackboard`, scene_root\: ``Node``\ ) |const| :ref:`🔗` +:ref:`BTInstance` **instantiate**\ (\ agent\: ``Node``, blackboard\: :ref:`Blackboard`, instance_owner\: ``Node``\ ) |const| :ref:`🔗` -Instantiates the behavior tree and returns the root :ref:`BTTask`. ``scene_root`` should be the root node of the scene that the Behavior Tree will be used in (e.g., the owner of the node that contains the behavior tree). +Instantiates the behavior tree and returns :ref:`BTInstance`. ``instance_owner`` should be the scene node that will own the behavior tree instance. This is typically a :ref:`BTPlayer`, :ref:`BTState`, or a custom player node that controls the behavior tree execution. .. rst-class:: classref-item-separator diff --git a/doc/source/classes/class_behaviortreedata.rst b/doc/source/classes/class_behaviortreedata.rst index 607312e..1f157bf 100644 --- a/doc/source/classes/class_behaviortreedata.rst +++ b/doc/source/classes/class_behaviortreedata.rst @@ -31,9 +31,9 @@ Methods .. table:: :widths: auto - +-------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`BehaviorTreeData` | :ref:`create_from_tree_instance`\ (\ tree_instance\: :ref:`BTTask`\ ) |static| | - +-------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------+ + +-------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`BehaviorTreeData` | :ref:`create_from_bt_instance`\ (\ bt_instance\: :ref:`BTInstance`\ ) |static| | + +-------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ .. rst-class:: classref-section-separator @@ -44,15 +44,15 @@ Methods Method Descriptions ------------------- -.. _class_BehaviorTreeData_method_create_from_tree_instance: +.. _class_BehaviorTreeData_method_create_from_bt_instance: .. rst-class:: classref-method -:ref:`BehaviorTreeData` **create_from_tree_instance**\ (\ tree_instance\: :ref:`BTTask`\ ) |static| :ref:`🔗` +:ref:`BehaviorTreeData` **create_from_bt_instance**\ (\ bt_instance\: :ref:`BTInstance`\ ) |static| :ref:`🔗` -Returns current state of the ``tree_instance`` encoded as a **BehaviorTreeData**, suitable for use with :ref:`BehaviorTreeView`. +Returns current state of the ``bt_instance`` encoded as a **BehaviorTreeData**, suitable for use with :ref:`BehaviorTreeView`. -Behavior tree instance can be acquired with :ref:`BTPlayer.get_tree_instance`. +Behavior tree instance can be acquired with :ref:`BTPlayer.get_bt_instance`. .. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)` .. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)` diff --git a/doc/source/classes/class_btinstance.rst b/doc/source/classes/class_btinstance.rst new file mode 100644 index 0000000..0a698f5 --- /dev/null +++ b/doc/source/classes/class_btinstance.rst @@ -0,0 +1,207 @@ +:github_url: hide + +.. DO NOT EDIT THIS FILE!!! +.. Generated automatically from Godot engine sources. +.. Generator: https://github.com/godotengine/godot/tree/master/doc/tools/make_rst.py. +.. XML source: https://github.com/godotengine/godot/tree/master/modules/limboai/doc_classes/BTInstance.xml. + +.. _class_BTInstance: + +BTInstance +========== + +**Inherits:** + +Represents a runtime instance of a :ref:`BehaviorTree` resource. + +.. rst-class:: classref-introduction-group + +Description +----------- + +Can be created using the :ref:`BehaviorTree.instantiate` method. + +.. rst-class:: classref-reftable-group + +Properties +---------- + +.. table:: + :widths: auto + + +----------+---------------------------------------------------------------------------+-----------+ + | ``bool`` | :ref:`monitor_performance` | ``false`` | + +----------+---------------------------------------------------------------------------+-----------+ + +.. rst-class:: classref-reftable-group + +Methods +------- + +.. table:: + :widths: auto + + +-------------------------------+-----------------------------------------------------------------------------------------+ + | :ref:`Status` | :ref:`get_last_status`\ (\ ) |const| | + +-------------------------------+-----------------------------------------------------------------------------------------+ + | ``Node`` | :ref:`get_owner_node`\ (\ ) |const| | + +-------------------------------+-----------------------------------------------------------------------------------------+ + | :ref:`BTTask` | :ref:`get_root_task`\ (\ ) |const| | + +-------------------------------+-----------------------------------------------------------------------------------------+ + | ``String`` | :ref:`get_source_bt_path`\ (\ ) |const| | + +-------------------------------+-----------------------------------------------------------------------------------------+ + | |void| | :ref:`register_with_debugger`\ (\ ) | + +-------------------------------+-----------------------------------------------------------------------------------------+ + | |void| | :ref:`unregister_with_debugger`\ (\ ) | + +-------------------------------+-----------------------------------------------------------------------------------------+ + | :ref:`Status` | :ref:`update`\ (\ delta\: ``float``\ ) | + +-------------------------------+-----------------------------------------------------------------------------------------+ + +.. rst-class:: classref-section-separator + +---- + +.. rst-class:: classref-descriptions-group + +Signals +------- + +.. _class_BTInstance_signal_freed: + +.. rst-class:: classref-signal + +**freed**\ (\ ) :ref:`🔗` + +Emitted when the behavior tree instance is freed. Used by debugger to unregister. + +.. rst-class:: classref-item-separator + +---- + +.. _class_BTInstance_signal_updated: + +.. rst-class:: classref-signal + +**updated**\ (\ status\: ``int``\ ) :ref:`🔗` + +Emitted when the behavior tree instance has finished updating. + +.. rst-class:: classref-section-separator + +---- + +.. rst-class:: classref-descriptions-group + +Property Descriptions +--------------------- + +.. _class_BTInstance_property_monitor_performance: + +.. rst-class:: classref-property + +``bool`` **monitor_performance** = ``false`` :ref:`🔗` + +.. rst-class:: classref-property-setget + +- |void| **set_monitor_performance**\ (\ value\: ``bool``\ ) +- ``bool`` **get_monitor_performance**\ (\ ) + +If ``true``, adds a performance monitor for this instance to "Debugger->Monitors" in the editor. + +.. rst-class:: classref-section-separator + +---- + +.. rst-class:: classref-descriptions-group + +Method Descriptions +------------------- + +.. _class_BTInstance_method_get_last_status: + +.. rst-class:: classref-method + +:ref:`Status` **get_last_status**\ (\ ) |const| :ref:`🔗` + +Returns the execution status of the last update. + +.. rst-class:: classref-item-separator + +---- + +.. _class_BTInstance_method_get_owner_node: + +.. rst-class:: classref-method + +``Node`` **get_owner_node**\ (\ ) |const| :ref:`🔗` + +Returns the scene ``Node`` that owns this behavior tree instance. + +.. rst-class:: classref-item-separator + +---- + +.. _class_BTInstance_method_get_root_task: + +.. rst-class:: classref-method + +:ref:`BTTask` **get_root_task**\ (\ ) |const| :ref:`🔗` + +Returns the root task of the behavior tree instance. + +.. rst-class:: classref-item-separator + +---- + +.. _class_BTInstance_method_get_source_bt_path: + +.. rst-class:: classref-method + +``String`` **get_source_bt_path**\ (\ ) |const| :ref:`🔗` + +Returns the file path to the behavior tree resource that was used to create this instance. + +.. rst-class:: classref-item-separator + +---- + +.. _class_BTInstance_method_register_with_debugger: + +.. rst-class:: classref-method + +|void| **register_with_debugger**\ (\ ) :ref:`🔗` + +Registers the behavior tree instance with the debugger. + +.. rst-class:: classref-item-separator + +---- + +.. _class_BTInstance_method_unregister_with_debugger: + +.. rst-class:: classref-method + +|void| **unregister_with_debugger**\ (\ ) :ref:`🔗` + +Unregisters the behavior tree instance from the debugger. This is typically not necessary, as the debugger will automatically unregister the instance when it is freed. + +.. rst-class:: classref-item-separator + +---- + +.. _class_BTInstance_method_update: + +.. rst-class:: classref-method + +:ref:`Status` **update**\ (\ delta\: ``float``\ ) :ref:`🔗` + +Ticks the behavior tree instance and returns its status. + +.. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)` +.. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)` +.. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)` +.. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)` +.. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)` +.. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)` +.. |bitfield| replace:: :abbr:`BitField (This value is an integer composed as a bitmask of the following flags.)` +.. |void| replace:: :abbr:`void (No return value.)` diff --git a/doc/source/classes/class_btplayer.rst b/doc/source/classes/class_btplayer.rst index 802e3fa..ed71cad 100644 --- a/doc/source/classes/class_btplayer.rst +++ b/doc/source/classes/class_btplayer.rst @@ -55,15 +55,13 @@ Methods .. table:: :widths: auto - +-----------------------------+-----------------------------------------------------------------------------+ - | ``int`` | :ref:`get_last_status`\ (\ ) |const| | - +-----------------------------+-----------------------------------------------------------------------------+ - | :ref:`BTTask` | :ref:`get_tree_instance`\ (\ ) | - +-----------------------------+-----------------------------------------------------------------------------+ - | |void| | :ref:`restart`\ (\ ) | - +-----------------------------+-----------------------------------------------------------------------------+ - | |void| | :ref:`update`\ (\ delta\: ``float``\ ) | - +-----------------------------+-----------------------------------------------------------------------------+ + +-------------------------------------+----------------------------------------------------------------------+ + | :ref:`BTInstance` | :ref:`get_bt_instance`\ (\ ) | + +-------------------------------------+----------------------------------------------------------------------+ + | |void| | :ref:`restart`\ (\ ) | + +-------------------------------------+----------------------------------------------------------------------+ + | |void| | :ref:`update`\ (\ delta\: ``float``\ ) | + +-------------------------------------+----------------------------------------------------------------------+ .. rst-class:: classref-section-separator @@ -80,6 +78,8 @@ Signals **behavior_tree_finished**\ (\ status\: ``int``\ ) :ref:`🔗` +**Deprecated:** Use :ref:`updated` signal instead. + Emitted when the behavior tree has finished executing and returned ``SUCCESS`` or ``FAILURE``. Argument ``status`` holds the status returned by the behavior tree. See :ref:`Status`. @@ -265,25 +265,13 @@ Determines when the behavior tree is executed. See :ref:`UpdateMode` +:ref:`BTInstance` **get_bt_instance**\ (\ ) :ref:`🔗` -Returns the behavior tree's last execution status. See :ref:`Status`. - -.. rst-class:: classref-item-separator - ----- - -.. _class_BTPlayer_method_get_tree_instance: - -.. rst-class:: classref-method - -:ref:`BTTask` **get_tree_instance**\ (\ ) :ref:`🔗` - -Returns the root task of the instantiated behavior tree. +Returns the behavior tree instance. .. rst-class:: classref-item-separator diff --git a/doc/source/classes/class_btstate.rst b/doc/source/classes/class_btstate.rst index 01a7938..dd6c5fd 100644 --- a/doc/source/classes/class_btstate.rst +++ b/doc/source/classes/class_btstate.rst @@ -45,9 +45,9 @@ Methods .. table:: :widths: auto - +-----------------------------+--------------------------------------------------------------------------------+ - | :ref:`BTTask` | :ref:`get_tree_instance`\ (\ ) |const| | - +-----------------------------+--------------------------------------------------------------------------------+ + +-------------------------------------+----------------------------------------------------------------------------+ + | :ref:`BTInstance` | :ref:`get_bt_instance`\ (\ ) |const| | + +-------------------------------------+----------------------------------------------------------------------------+ .. rst-class:: classref-section-separator @@ -114,13 +114,13 @@ HSM event that will be dispatched when the behavior tree results in ``SUCCESS``. Method Descriptions ------------------- -.. _class_BTState_method_get_tree_instance: +.. _class_BTState_method_get_bt_instance: .. rst-class:: classref-method -:ref:`BTTask` **get_tree_instance**\ (\ ) |const| :ref:`🔗` +:ref:`BTInstance` **get_bt_instance**\ (\ ) |const| :ref:`🔗` -Returns the root task of the instantiated behavior tree. +Returns the behavior tree instance. .. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)` .. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)` diff --git a/doc_classes/BTInstance.xml b/doc_classes/BTInstance.xml new file mode 100644 index 0000000..faf2a24 --- /dev/null +++ b/doc_classes/BTInstance.xml @@ -0,0 +1,74 @@ + + + + Represents a runtime instance of a [BehaviorTree] resource. + + + Can be created using the [method BehaviorTree.instantiate] method. + + + + + + + + Returns the execution status of the last update. + + + + + + Returns the scene [Node] that owns this behavior tree instance. + + + + + + Returns the root task of the behavior tree instance. + + + + + + Returns the file path to the behavior tree resource that was used to create this instance. + + + + + + Registers the behavior tree instance with the debugger. + + + + + + Unregisters the behavior tree instance from the debugger. This is typically not necessary, as the debugger will automatically unregister the instance when it is freed. + + + + + + + Ticks the behavior tree instance and returns its status. + + + + + + If [code]true[/code], adds a performance monitor for this instance to "Debugger->Monitors" in the editor. + + + + + + Emitted when the behavior tree instance is freed. Used by debugger to unregister. + + + + + + Emitted when the behavior tree instance has finished updating. + + + + diff --git a/doc_classes/BTPlayer.xml b/doc_classes/BTPlayer.xml index 060a2f7..5c0cb5c 100644 --- a/doc_classes/BTPlayer.xml +++ b/doc_classes/BTPlayer.xml @@ -10,16 +10,10 @@ - - + + - Returns the behavior tree's last execution status. See [enum BT.Status]. - - - - - - Returns the root task of the instantiated behavior tree. + Returns the behavior tree instance. @@ -60,7 +54,7 @@ - + Emitted when the behavior tree has finished executing and returned [code]SUCCESS[/code] or [code]FAILURE[/code]. diff --git a/doc_classes/BTState.xml b/doc_classes/BTState.xml index d7d5d1a..9824b37 100644 --- a/doc_classes/BTState.xml +++ b/doc_classes/BTState.xml @@ -9,10 +9,10 @@ - - + + - Returns the root task of the instantiated behavior tree. + Returns the behavior tree instance. diff --git a/doc_classes/BehaviorTree.xml b/doc_classes/BehaviorTree.xml index 7cd61fc..58a1fbf 100644 --- a/doc_classes/BehaviorTree.xml +++ b/doc_classes/BehaviorTree.xml @@ -35,12 +35,12 @@ - + - + - Instantiates the behavior tree and returns the root [BTTask]. [param scene_root] should be the root node of the scene that the Behavior Tree will be used in (e.g., the owner of the node that contains the behavior tree). + Instantiates the behavior tree and returns [BTInstance]. [param instance_owner] should be the scene node that will own the behavior tree instance. This is typically a [BTPlayer], [BTState], or a custom player node that controls the behavior tree execution. @@ -56,7 +56,7 @@ Stores and manages variables that will be used in constructing new [Blackboard] instances. - User-provided description of the BehaviorTree. + User-provided description of the [BehaviorTree]. diff --git a/doc_classes/BehaviorTreeData.xml b/doc_classes/BehaviorTreeData.xml index ae8a1e1..203f059 100644 --- a/doc_classes/BehaviorTreeData.xml +++ b/doc_classes/BehaviorTreeData.xml @@ -10,12 +10,12 @@ - + - + - Returns current state of the [param tree_instance] encoded as a [BehaviorTreeData], suitable for use with [BehaviorTreeView]. - Behavior tree instance can be acquired with [method BTPlayer.get_tree_instance]. + Returns current state of the [param bt_instance] encoded as a [BehaviorTreeData], suitable for use with [BehaviorTreeView]. + Behavior tree instance can be acquired with [method BTPlayer.get_bt_instance]. diff --git a/editor/debugger/behavior_tree_data.cpp b/editor/debugger/behavior_tree_data.cpp index c82312f..efe4ea8 100644 --- a/editor/debugger/behavior_tree_data.cpp +++ b/editor/debugger/behavior_tree_data.cpp @@ -17,14 +17,15 @@ //**** BehaviorTreeData -Array BehaviorTreeData::serialize(const Ref &p_tree_instance, const NodePath &p_player_path, const String &p_bt_resource_path) { +Array BehaviorTreeData::serialize(const Ref &p_instance) { Array arr; - arr.push_back(p_player_path); - arr.push_back(p_bt_resource_path); + arr.push_back(uint64_t(p_instance->get_instance_id())); + arr.push_back(p_instance->get_owner_node() ? p_instance->get_owner_node()->get_path() : NodePath()); + arr.push_back(p_instance->get_source_bt_path()); // Flatten tree into list depth first List> stack; - stack.push_back(p_tree_instance); + stack.push_back(p_instance->get_root_task()); while (stack.size()) { Ref task = stack.front()->get(); stack.pop_front(); @@ -54,15 +55,17 @@ Array BehaviorTreeData::serialize(const Ref &p_tree_instance, const Node } Ref BehaviorTreeData::deserialize(const Array &p_array) { - ERR_FAIL_COND_V(p_array.size() < 2, nullptr); - ERR_FAIL_COND_V(p_array[0].get_type() != Variant::NODE_PATH, nullptr); - ERR_FAIL_COND_V(p_array[1].get_type() != Variant::STRING, nullptr); + ERR_FAIL_COND_V(p_array.size() < 3, nullptr); + ERR_FAIL_COND_V(p_array[0].get_type() != Variant::INT, nullptr); + ERR_FAIL_COND_V(p_array[1].get_type() != Variant::NODE_PATH, nullptr); + ERR_FAIL_COND_V(p_array[2].get_type() != Variant::STRING, nullptr); Ref data = memnew(BehaviorTreeData); - data->bt_player_path = p_array[0]; - data->bt_resource_path = p_array[1]; + data->bt_instance_id = uint64_t(p_array[0]); + data->node_owner_path = p_array[1]; + data->source_bt_path = p_array[2]; - int idx = 2; + int idx = 3; while (p_array.size() > idx + 1) { ERR_FAIL_COND_V(p_array.size() < idx + 7, nullptr); ERR_FAIL_COND_V(p_array[idx].get_type() != Variant::INT, nullptr); @@ -80,12 +83,16 @@ Ref BehaviorTreeData::deserialize(const Array &p_array) { return data; } -Ref BehaviorTreeData::create_from_tree_instance(const Ref &p_tree_instance) { +Ref BehaviorTreeData::create_from_bt_instance(const Ref &p_bt_instance) { Ref data = memnew(BehaviorTreeData); + data->bt_instance_id = p_bt_instance->get_instance_id(); + data->node_owner_path = p_bt_instance->get_owner_node() ? p_bt_instance->get_owner_node()->get_path() : NodePath(); + data->source_bt_path = p_bt_instance->get_source_bt_path(); + // Flatten tree into list depth first List> stack; - stack.push_back(p_tree_instance); + stack.push_back(p_bt_instance->get_root_task()); while (stack.size()) { Ref task = stack.front()->get(); stack.pop_front(); @@ -115,9 +122,7 @@ Ref BehaviorTreeData::create_from_tree_instance(const Ref tasks; - NodePath bt_player_path; - String bt_resource_path; + uint64_t bt_instance_id = 0; + NodePath node_owner_path; + String source_bt_path; public: - static Array serialize(const Ref &p_tree_instance, const NodePath &p_player_path, const String &p_bt_resource_path); + static Array serialize(const Ref &p_instance); static Ref deserialize(const Array &p_array); - static Ref create_from_tree_instance(const Ref &p_tree_instance); + static Ref create_from_bt_instance(const Ref &p_bt_instance); BehaviorTreeData(); }; diff --git a/editor/debugger/limbo_debugger.cpp b/editor/debugger/limbo_debugger.cpp index 9b28333..e4abe35 100644 --- a/editor/debugger/limbo_debugger.cpp +++ b/editor/debugger/limbo_debugger.cpp @@ -11,13 +11,13 @@ #include "limbo_debugger.h" +#include "../../bt/bt_instance.h" #include "../../bt/tasks/bt_task.h" #include "../../util/limbo_compat.h" #include "behavior_tree_data.h" #ifdef LIMBOAI_MODULE #include "core/debugger/engine_debugger.h" -#include "core/error/error_macros.h" #include "core/io/resource.h" #include "core/string/node_path.h" #include "scene/main/scene_tree.h" @@ -33,9 +33,6 @@ //**** LimboDebugger LimboDebugger *LimboDebugger::singleton = nullptr; -LimboDebugger *LimboDebugger::get_singleton() { - return singleton; -} LimboDebugger::LimboDebugger() { singleton = this; @@ -63,14 +60,6 @@ void LimboDebugger::deinitialize() { } void LimboDebugger::_bind_methods() { -#ifdef DEBUG_ENABLED - -#ifdef LIMBOAI_GDEXTENSION - ClassDB::bind_method(D_METHOD("parse_message_gdext"), &LimboDebugger::parse_message_gdext); -#endif - ClassDB::bind_method(D_METHOD("_on_bt_updated", "status", "path"), &LimboDebugger::_on_bt_updated); - ClassDB::bind_method(D_METHOD("_on_state_updated", "delta", "path"), &LimboDebugger::_on_state_updated); -#endif // ! DEBUG_ENABLED } #ifdef DEBUG_ENABLED @@ -99,100 +88,96 @@ bool LimboDebugger::parse_message_gdext(const String &p_msg, const Array &p_args } #endif // LIMBOAI_GDEXTENSION -void LimboDebugger::register_bt_instance(Ref p_instance, NodePath p_player_path) { - ERR_FAIL_COND(p_instance.is_null()); - ERR_FAIL_COND(p_player_path.is_empty()); - if (active_trees.has(p_player_path)) { +void LimboDebugger::register_bt_instance(uint64_t p_instance_id) { + ERR_FAIL_COND(p_instance_id == 0); + if (!IS_DEBUGGER_ACTIVE()) { return; } - active_trees.insert(p_player_path, p_instance); + BTInstance *inst = Object::cast_to(OBJECT_DB_GET_INSTANCE(p_instance_id)); + ERR_FAIL_NULL(inst); + ERR_FAIL_COND(!inst->is_instance_valid()); + + if (active_bt_instances.has(p_instance_id)) { + return; + } + + if (!inst->is_connected(LW_NAME(freed), callable_mp(this, &LimboDebugger::unregister_bt_instance).bind(p_instance_id))) { + inst->connect(LW_NAME(freed), callable_mp(this, &LimboDebugger::unregister_bt_instance).bind(p_instance_id)); + } + + active_bt_instances.insert(p_instance_id); if (session_active) { _send_active_bt_players(); } } -void LimboDebugger::unregister_bt_instance(Ref p_instance, NodePath p_player_path) { - ERR_FAIL_COND(p_instance.is_null()); - ERR_FAIL_COND(p_player_path.is_empty()); - ERR_FAIL_COND(!active_trees.has(p_player_path)); +void LimboDebugger::unregister_bt_instance(uint64_t p_instance_id) { + if (!active_bt_instances.has(p_instance_id)) { + return; + } - if (tracked_player == p_player_path) { + if (tracked_instance_id == p_instance_id) { _untrack_tree(); } - active_trees.erase(p_player_path); + active_bt_instances.erase(p_instance_id); if (session_active) { _send_active_bt_players(); } } -void LimboDebugger::_track_tree(NodePath p_path) { - ERR_FAIL_COND(!active_trees.has(p_path)); +bool LimboDebugger::is_active() const { + return IS_DEBUGGER_ACTIVE(); +} - if (!tracked_player.is_empty()) { - _untrack_tree(); - } +void LimboDebugger::_track_tree(uint64_t p_instance_id) { + ERR_FAIL_COND(p_instance_id == 0); + ERR_FAIL_COND(!active_bt_instances.has(p_instance_id)); - Node *node = SCENE_TREE()->get_root()->get_node_or_null(p_path); - ERR_FAIL_COND(node == nullptr); + _untrack_tree(); - tracked_player = p_path; + tracked_instance_id = p_instance_id; - Ref bt = node->get(LW_NAME(behavior_tree)); - - if (bt.is_valid()) { - bt_resource_path = bt->get_path(); - } else { - bt_resource_path = ""; - } - - if (node->is_class("BTPlayer")) { - node->connect(LW_NAME(updated), callable_mp(this, &LimboDebugger::_on_bt_updated).bind(p_path)); - } else if (node->is_class("BTState")) { - node->connect(LW_NAME(updated), callable_mp(this, &LimboDebugger::_on_state_updated).bind(p_path)); - } + BTInstance *inst = Object::cast_to(OBJECT_DB_GET_INSTANCE(p_instance_id)); + ERR_FAIL_NULL(inst); + inst->connect(LW_NAME(updated), callable_mp(this, &LimboDebugger::_on_bt_instance_updated).bind(p_instance_id)); } void LimboDebugger::_untrack_tree() { - if (tracked_player.is_empty()) { + if (tracked_instance_id == 0) { return; } - NodePath was_tracking = tracked_player; - tracked_player = NodePath(); - - Node *node = SCENE_TREE()->get_root()->get_node_or_null(was_tracking); - ERR_FAIL_COND(node == nullptr); - - if (node->is_class("BTPlayer")) { - node->disconnect(LW_NAME(updated), callable_mp(this, &LimboDebugger::_on_bt_updated)); - } else if (node->is_class("BTState")) { - node->disconnect(LW_NAME(updated), callable_mp(this, &LimboDebugger::_on_state_updated)); + BTInstance *inst = Object::cast_to(OBJECT_DB_GET_INSTANCE(tracked_instance_id)); + if (inst) { + inst->disconnect(LW_NAME(updated), callable_mp(this, &LimboDebugger::_on_bt_instance_updated)); } + tracked_instance_id = 0; } void LimboDebugger::_send_active_bt_players() { Array arr; - for (KeyValue> kv : active_trees) { - arr.append(kv.key); + for (uint64_t instance_id : active_bt_instances) { + arr.append(instance_id); + BTInstance *inst = Object::cast_to(OBJECT_DB_GET_INSTANCE(instance_id)); + if (inst == nullptr) { + ERR_PRINT("LimboDebugger::_send_active_bt_players: Registered BTInstance not found (no longer exists?)."); + continue; + } + Node *owner_node = inst->get_owner_node(); + arr.append(owner_node ? owner_node->get_path() : NodePath()); } EngineDebugger::get_singleton()->send_message("limboai:active_bt_players", arr); } -void LimboDebugger::_on_bt_updated(int _status, NodePath p_path) { - if (p_path != tracked_player) { +void LimboDebugger::_on_bt_instance_updated(int _status, uint64_t p_instance_id) { + if (p_instance_id != tracked_instance_id) { return; } - Array arr = BehaviorTreeData::serialize(active_trees.get(tracked_player), tracked_player, bt_resource_path); - EngineDebugger::get_singleton()->send_message("limboai:bt_update", arr); -} - -void LimboDebugger::_on_state_updated(float _delta, NodePath p_path) { - if (p_path != tracked_player) { - return; - } - Array arr = BehaviorTreeData::serialize(active_trees.get(tracked_player), tracked_player, bt_resource_path); + BTInstance *inst = Object::cast_to(OBJECT_DB_GET_INSTANCE(p_instance_id)); + ERR_FAIL_NULL(inst); + Array arr = BehaviorTreeData::serialize(inst); EngineDebugger::get_singleton()->send_message("limboai:bt_update", arr); } diff --git a/editor/debugger/limbo_debugger.h b/editor/debugger/limbo_debugger.h index 1130d30..9149026 100644 --- a/editor/debugger/limbo_debugger.h +++ b/editor/debugger/limbo_debugger.h @@ -12,6 +12,7 @@ #ifndef LIMBO_DEBUGGER_H #define LIMBO_DEBUGGER_H +#include "../../bt/bt_instance.h" #include "../../bt/tasks/bt_task.h" #ifdef LIMBOAI_MODULE @@ -23,6 +24,7 @@ #ifdef LIMBOAI_GDEXTENSION #include #include +#include #include #endif // LIMBOAI_GDEXTENSION @@ -37,7 +39,7 @@ private: public: static void initialize(); static void deinitialize(); - static LimboDebugger *get_singleton(); + _FORCE_INLINE_ static LimboDebugger *get_singleton() { return singleton; } ~LimboDebugger(); @@ -46,17 +48,15 @@ protected: #ifdef DEBUG_ENABLED private: - HashMap> active_trees; - NodePath tracked_player; - String bt_resource_path; + HashSet active_bt_instances; + uint64_t tracked_instance_id = 0; bool session_active = false; - void _track_tree(NodePath p_path); + void _track_tree(uint64_t p_instance_id); void _untrack_tree(); void _send_active_bt_players(); - void _on_bt_updated(int status, NodePath p_path); - void _on_state_updated(float _delta, NodePath p_path); + void _on_bt_instance_updated(int status, uint64_t p_instance_id); public: static Error parse_message(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured); @@ -64,8 +64,9 @@ public: bool parse_message_gdext(const String &p_msg, const Array &p_args); #endif - void register_bt_instance(Ref p_instance, NodePath p_player_path); - void unregister_bt_instance(Ref p_instance, NodePath p_player_path); + void register_bt_instance(uint64_t p_instance_id); + void unregister_bt_instance(uint64_t p_instance_id); + bool is_active() const; #endif // ! DEBUG_ENABLED }; diff --git a/editor/debugger/limbo_debugger_plugin.cpp b/editor/debugger/limbo_debugger_plugin.cpp index 697796f..53ae161 100644 --- a/editor/debugger/limbo_debugger_plugin.cpp +++ b/editor/debugger/limbo_debugger_plugin.cpp @@ -58,7 +58,7 @@ //**** LimboDebuggerTab void LimboDebuggerTab::_reset_controls() { - bt_player_list->clear(); + bt_instance_list->clear(); bt_view->clear(); alert_box->hide(); info_message->set_text(TTR("Run project to start debugging.")); @@ -68,7 +68,7 @@ void LimboDebuggerTab::_reset_controls() { } void LimboDebuggerTab::start_session() { - bt_player_list->clear(); + bt_instance_list->clear(); bt_view->clear(); alert_box->hide(); info_message->set_text(TTR("Pick a player from the list to display behavior tree.")); @@ -81,23 +81,25 @@ void LimboDebuggerTab::stop_session() { session->send_message("limboai:stop_session", Array()); } -void LimboDebuggerTab::update_active_bt_players(const Array &p_node_paths) { - active_bt_players.clear(); - for (int i = 0; i < p_node_paths.size(); i++) { - active_bt_players.push_back(p_node_paths[i]); +void LimboDebuggerTab::update_active_bt_instances(const Array &p_data) { + active_bt_instances.clear(); + for (int i = 0; i < p_data.size(); i += 2) { + BTInstanceInfo info{ p_data[i], p_data[i + 1] }; + + active_bt_instances.push_back(info); } - _update_bt_player_list(active_bt_players, filter_players->get_text()); + _update_bt_instance_list(active_bt_instances, filter_players->get_text()); } -String LimboDebuggerTab::get_selected_bt_player() { - if (!bt_player_list->is_anything_selected()) { - return ""; +uint64_t LimboDebuggerTab::get_selected_bt_instance_id() { + if (!bt_instance_list->is_anything_selected()) { + return 0; } - return bt_player_list->get_item_text(bt_player_list->get_selected_items()[0]); + return bt_instance_list->get_item_metadata(bt_instance_list->get_selected_items()[0]); } void LimboDebuggerTab::update_behavior_tree(const Ref &p_data) { - resource_header->set_text(p_data->bt_resource_path); + resource_header->set_text(p_data->source_bt_path); resource_header->set_disabled(false); bt_view->update_tree(p_data); info_message->hide(); @@ -108,58 +110,58 @@ void LimboDebuggerTab::_show_alert(const String &p_message) { alert_box->set_visible(!p_message.is_empty()); } -void LimboDebuggerTab::_update_bt_player_list(const List &p_node_paths, const String &p_filter) { +void LimboDebuggerTab::_update_bt_instance_list(const Vector &p_instances, const String &p_filter) { // Remember selected item. - String selected_player = ""; - if (bt_player_list->is_anything_selected()) { - selected_player = bt_player_list->get_item_text(bt_player_list->get_selected_items()[0]); + uint64_t selected_instance_id = 0; + if (bt_instance_list->is_anything_selected()) { + selected_instance_id = uint64_t(bt_instance_list->get_item_metadata(bt_instance_list->get_selected_items()[0])); } - bt_player_list->clear(); + bt_instance_list->clear(); int select_idx = -1; bool selection_filtered_out = false; - for (const String &p : p_node_paths) { - if (p_filter.is_empty() || p.contains(p_filter)) { - int idx = bt_player_list->add_item(p); + for (const BTInstanceInfo &info : p_instances) { + if (p_filter.is_empty() || info.owner_node_path.contains(p_filter)) { + int idx = bt_instance_list->add_item(info.owner_node_path); + bt_instance_list->set_item_metadata(idx, info.instance_id); // Make item text shortened from the left, e.g ".../Agent/BTPlayer". - bt_player_list->set_item_text_direction(idx, TEXT_DIRECTION_RTL); - if (p == selected_player) { + bt_instance_list->set_item_text_direction(idx, TEXT_DIRECTION_RTL); + if (info.instance_id == selected_instance_id) { select_idx = idx; } - } else if (p == selected_player) { + } else if (info.instance_id == selected_instance_id) { selection_filtered_out = true; } } // Restore selected item. if (select_idx > -1) { - bt_player_list->select(select_idx); - } else if (!selected_player.is_empty()) { + bt_instance_list->select(select_idx); + } else if (selected_instance_id != 0) { if (selection_filtered_out) { session->send_message("limboai:untrack_bt_player", Array()); bt_view->clear(); _show_alert(""); } else { - _show_alert("BehaviorTree player is no longer present."); + _show_alert(TTR("Behavior tree instance is no longer present.")); } } } -void LimboDebuggerTab::_bt_selected(int p_idx) { +void LimboDebuggerTab::_bt_instance_selected(int p_idx) { alert_box->hide(); bt_view->clear(); info_message->set_text(TTR("Waiting for behavior tree update.")); info_message->show(); resource_header->set_text(TTR("Waiting for data")); resource_header->set_disabled(true); - NodePath path = bt_player_list->get_item_text(p_idx); Array msg_data; - msg_data.push_back(path); + msg_data.push_back(bt_instance_list->get_item_metadata(p_idx)); session->send_message("limboai:track_bt_player", msg_data); } void LimboDebuggerTab::_filter_changed(String p_text) { - _update_bt_player_list(active_bt_players, p_text); + _update_bt_instance_list(active_bt_instances, p_text); } void LimboDebuggerTab::_window_visibility_changed(bool p_visible) { @@ -185,7 +187,7 @@ void LimboDebuggerTab::_notification(int p_what) { case NOTIFICATION_READY: { resource_header->connect(LW_NAME(pressed), callable_mp(this, &LimboDebuggerTab::_resource_header_pressed)); filter_players->connect(LW_NAME(text_changed), callable_mp(this, &LimboDebuggerTab::_filter_changed)); - bt_player_list->connect(LW_NAME(item_selected), callable_mp(this, &LimboDebuggerTab::_bt_selected)); + bt_instance_list->connect(LW_NAME(item_selected), callable_mp(this, &LimboDebuggerTab::_bt_instance_selected)); update_interval->connect("value_changed", callable_mp(bt_view, &BehaviorTreeView::set_update_interval_msec)); Ref cf; @@ -271,11 +273,11 @@ LimboDebuggerTab::LimboDebuggerTab() { filter_players->set_placeholder(TTR("Filter Players")); list_box->add_child(filter_players); - bt_player_list = memnew(ItemList); - bt_player_list->set_custom_minimum_size(Size2(240.0 * EDSCALE, 0.0)); - bt_player_list->set_h_size_flags(SIZE_FILL); - bt_player_list->set_v_size_flags(SIZE_EXPAND_FILL); - list_box->add_child(bt_player_list); + bt_instance_list = memnew(ItemList); + bt_instance_list->set_custom_minimum_size(Size2(240.0 * EDSCALE, 0.0)); + bt_instance_list->set_h_size_flags(SIZE_FILL); + bt_instance_list->set_v_size_flags(SIZE_EXPAND_FILL); + list_box->add_child(bt_instance_list); view_box = memnew(VBoxContainer); hsc->add_child(view_box); @@ -352,10 +354,10 @@ bool LimboDebuggerPlugin::_capture(const String &p_message, const Array &p_data, ERR_FAIL_NULL_V(tab, false); bool captured = true; if (p_message == "limboai:active_bt_players") { - tab->update_active_bt_players(p_data); + tab->update_active_bt_instances(p_data); } else if (p_message == "limboai:bt_update") { Ref data = BehaviorTreeData::deserialize(p_data); - if (data->bt_player_path == NodePath(tab->get_selected_bt_player())) { + if (data->bt_instance_id == tab->get_selected_bt_instance_id()) { tab->update_behavior_tree(data); } } else { diff --git a/editor/debugger/limbo_debugger_plugin.h b/editor/debugger/limbo_debugger_plugin.h index ca48099..8ef3db9 100644 --- a/editor/debugger/limbo_debugger_plugin.h +++ b/editor/debugger/limbo_debugger_plugin.h @@ -51,13 +51,18 @@ class LimboDebuggerTab : public PanelContainer { GDCLASS(LimboDebuggerTab, PanelContainer); private: - List active_bt_players; + struct BTInstanceInfo { + uint64_t instance_id = 0; + String owner_node_path; + }; + + Vector active_bt_instances; Ref session; VBoxContainer *root_vb = nullptr; HBoxContainer *toolbar = nullptr; HSplitContainer *hsc = nullptr; Label *info_message = nullptr; - ItemList *bt_player_list = nullptr; + ItemList *bt_instance_list = nullptr; BehaviorTreeView *bt_view = nullptr; VBoxContainer *view_box = nullptr; HBoxContainer *alert_box = nullptr; @@ -71,8 +76,8 @@ private: void _reset_controls(); void _show_alert(const String &p_message); - void _update_bt_player_list(const List &p_node_paths, const String &p_filter); - void _bt_selected(int p_idx); + void _update_bt_instance_list(const Vector &p_instances, const String &p_filter); + void _bt_instance_selected(int p_idx); void _filter_changed(String p_text); void _window_visibility_changed(bool p_visible); void _resource_header_pressed(); @@ -84,9 +89,9 @@ protected: public: void start_session(); void stop_session(); - void update_active_bt_players(const Array &p_node_paths); + void update_active_bt_instances(const Array &p_data); BehaviorTreeView *get_behavior_tree_view() const { return bt_view; } - String get_selected_bt_player(); + uint64_t get_selected_bt_instance_id(); void update_behavior_tree(const Ref &p_data); void setup(Ref p_session, CompatWindowWrapper *p_wrapper); diff --git a/register_types.cpp b/register_types.cpp index 967f7e9..a04ef19 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -148,6 +148,7 @@ void initialize_limboai_module(ModuleInitializationLevel p_level) { GDREGISTER_ABSTRACT_CLASS(BT); GDREGISTER_ABSTRACT_CLASS(BTTask); GDREGISTER_CLASS(BehaviorTree); + GDREGISTER_CLASS(BTInstance); GDREGISTER_CLASS(BTPlayer); GDREGISTER_CLASS(BTState); diff --git a/util/limbo_compat.h b/util/limbo_compat.h index 21172e4..e75bacc 100644 --- a/util/limbo_compat.h +++ b/util/limbo_compat.h @@ -54,6 +54,7 @@ #define GET_SCRIPT(m_obj) (m_obj->get_script_instance() ? m_obj->get_script_instance()->get_script() : nullptr) #define ADD_STYLEBOX_OVERRIDE(m_control, m_name, m_stylebox) (m_control->add_theme_style_override(m_name, m_stylebox)) #define GET_NODE(m_parent, m_path) m_parent->get_node(m_path) +#define OBJECT_DB_GET_INSTANCE(m_id) ObjectDB::get_instance(ObjectID(m_id)) _FORCE_INLINE_ bool OBJECT_HAS_PROPERTY(Object *p_obj, const StringName &p_prop) { bool r_valid; @@ -112,6 +113,7 @@ using namespace godot; #define GET_SCRIPT(m_obj) (m_obj->get_script()) #define ADD_STYLEBOX_OVERRIDE(m_control, m_name, m_stylebox) (m_control->add_theme_stylebox_override(m_name, m_stylebox)) #define GET_NODE(m_parent, m_path) m_parent->get_node_internal(m_path) +#define OBJECT_DB_GET_INSTANCE(m_id) ObjectDB::get_instance(m_id) _FORCE_INLINE_ bool OBJECT_HAS_PROPERTY(Object *p_obj, const StringName &p_prop) { return Variant(p_obj).has_key(p_prop); diff --git a/util/limbo_string_names.cpp b/util/limbo_string_names.cpp index c4f7d50..ee8d5f5 100644 --- a/util/limbo_string_names.cpp +++ b/util/limbo_string_names.cpp @@ -85,6 +85,7 @@ LimboStringNames::LimboStringNames() { font_color = SN("font_color"); font_size = SN("font_size"); Forward = SN("Forward"); + freed = SN("freed"); gui_input = SN("gui_input"); GuiOptionArrow = SN("GuiOptionArrow"); GuiTreeArrowDown = SN("GuiTreeArrowDown"); diff --git a/util/limbo_string_names.h b/util/limbo_string_names.h index d7ee502..9194b81 100644 --- a/util/limbo_string_names.h +++ b/util/limbo_string_names.h @@ -101,6 +101,7 @@ public: StringName font_size; StringName font; StringName Forward; + StringName freed; StringName gui_input; StringName GuiOptionArrow; StringName GuiTreeArrowDown;