2023-07-21 09:50:06 +00:00
/**
* bt_player . cpp
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
2024-03-04 20:36:16 +00:00
* Copyright 2021 - 2024 Serhii Snitsaruk
2023-07-21 09:50:06 +00:00
*
* 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.
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
2022-08-30 16:48:49 +00:00
# include "bt_player.h"
2024-01-07 01:40:51 +00:00
# include "../editor/debugger/limbo_debugger.h"
2024-01-10 21:45:42 +00:00
# include "../util/limbo_compat.h"
2024-01-07 01:40:51 +00:00
# include "../util/limbo_string_names.h"
# ifdef LIMBOAI_MODULE
2022-12-15 07:26:52 +00:00
# include "core/config/engine.h"
2023-12-28 01:45:56 +00:00
# include "core/debugger/engine_debugger.h"
2022-12-16 14:29:36 +00:00
# include "core/error/error_macros.h"
2022-08-30 16:48:49 +00:00
# include "core/io/resource_loader.h"
2022-12-15 07:26:52 +00:00
# include "core/object/class_db.h"
2022-09-08 13:56:51 +00:00
# include "core/os/memory.h"
2023-04-15 13:49:34 +00:00
# include "core/string/string_name.h"
2022-12-15 07:26:52 +00:00
# include "core/variant/variant.h"
2023-04-15 13:49:34 +00:00
# include "main/performance.h"
2022-08-30 16:48:49 +00:00
2024-01-07 01:40:51 +00:00
# define IS_DEBUGGER_ACTIVE() (EngineDebugger::is_active())
# define GET_TICKS_USEC() (OS::get_singleton()->get_ticks_usec())
2024-01-13 16:10:42 +00:00
# endif // ! LIMBOAI_MODULE
2024-01-07 01:40:51 +00:00
# ifdef LIMBOAI_GDEXTENSION
# include <godot_cpp/classes/engine_debugger.hpp>
# include <godot_cpp/classes/performance.hpp>
# include <godot_cpp/classes/time.hpp>
# define IS_DEBUGGER_ACTIVE() (EngineDebugger::get_singleton()->is_active())
# define GET_TICKS_USEC() (Time::get_singleton()->get_ticks_usec())
2024-01-13 16:10:42 +00:00
# endif // ! LIMBOAI_GDEXTENSION
2024-01-07 01:40:51 +00:00
2022-08-30 16:48:49 +00:00
VARIANT_ENUM_CAST ( BTPlayer : : UpdateMode ) ;
void BTPlayer : : _load_tree ( ) {
2023-04-13 07:29:45 +00:00
# ifdef DEBUG_ENABLED
2024-01-07 01:40:51 +00:00
if ( tree_instance . is_valid ( ) & & IS_DEBUGGER_ACTIVE ( ) ) {
2023-04-13 07:29:45 +00:00
LimboDebugger : : get_singleton ( ) - > unregister_bt_instance ( tree_instance , get_path ( ) ) ;
}
# endif
2022-12-16 14:29:36 +00:00
tree_instance . unref ( ) ;
2024-05-01 21:20:17 +00:00
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. " ) ;
2024-05-02 10:11:59 +00:00
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 ) ) ;
2024-05-01 21:20:17 +00:00
Node * scene_root = get_owner ( ) ;
2024-05-02 12:10:29 +00:00
ERR_FAIL_NULL_MSG ( scene_root , " BTPlayer: Initialization failed - can't get scene root (make sure the BTPlayer's owner property is set). " ) ;
2024-05-02 10:11:59 +00:00
tree_instance = behavior_tree - > instantiate ( agent , blackboard , scene_root ) ;
2023-04-13 07:29:45 +00:00
# ifdef DEBUG_ENABLED
2024-01-07 01:40:51 +00:00
if ( IS_DEBUGGER_ACTIVE ( ) ) {
2023-12-28 01:45:56 +00:00
LimboDebugger : : get_singleton ( ) - > register_bt_instance ( tree_instance , get_path ( ) ) ;
}
2023-04-13 07:29:45 +00:00
# endif
2022-08-30 16:48:49 +00:00
}
2024-01-23 19:02:23 +00:00
void BTPlayer : : _update_blackboard_plan ( ) {
if ( blackboard_plan . is_null ( ) ) {
blackboard_plan = Ref < BlackboardPlan > ( memnew ( BlackboardPlan ) ) ;
2024-04-01 14:34:36 +00:00
} 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 ( ) ;
2024-01-23 19:02:23 +00:00
}
2024-04-01 14:34:36 +00:00
2024-01-24 22:11:09 +00:00
blackboard_plan - > set_base_plan ( behavior_tree . is_valid ( ) ? behavior_tree - > get_blackboard_plan ( ) : nullptr ) ;
2024-01-23 14:31:56 +00:00
}
2022-08-30 16:48:49 +00:00
void BTPlayer : : set_behavior_tree ( const Ref < BehaviorTree > & p_tree ) {
2024-01-24 23:23:34 +00:00
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
2024-04-01 14:34:36 +00:00
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 ) ) ;
2024-01-24 23:23:34 +00:00
}
if ( p_tree . is_valid ( ) ) {
2024-04-01 14:34:36 +00:00
p_tree - > connect ( LW_NAME ( plan_changed ) , callable_mp ( this , & BTPlayer : : _update_blackboard_plan ) ) ;
2024-01-24 23:23:34 +00:00
}
behavior_tree = p_tree ;
_update_blackboard_plan ( ) ;
} else {
behavior_tree = p_tree ;
if ( get_owner ( ) ) {
_load_tree ( ) ;
}
2022-08-30 16:48:49 +00:00
}
2024-01-23 14:31:56 +00:00
}
2024-05-02 10:49:32 +00:00
void BTPlayer : : set_agent_node ( const NodePath & p_agent_node ) {
agent_node = p_agent_node ;
if ( tree_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. " ) ;
}
}
2024-01-23 19:02:23 +00:00
void BTPlayer : : set_blackboard_plan ( const Ref < BlackboardPlan > & p_plan ) {
2024-04-01 14:34:36 +00:00
blackboard_plan = p_plan ;
2024-01-23 19:02:23 +00:00
_update_blackboard_plan ( ) ;
2022-08-30 16:48:49 +00:00
}
void BTPlayer : : set_update_mode ( UpdateMode p_mode ) {
update_mode = p_mode ;
set_active ( active ) ;
}
void BTPlayer : : set_active ( bool p_active ) {
active = p_active ;
2022-12-16 14:29:36 +00:00
bool is_not_editor = ! Engine : : get_singleton ( ) - > is_editor_hint ( ) ;
set_process ( update_mode = = UpdateMode : : IDLE & & active & & is_not_editor ) ;
set_physics_process ( update_mode = = UpdateMode : : PHYSICS & & active & & is_not_editor ) ;
set_process_input ( active & & is_not_editor ) ;
2022-08-30 16:48:49 +00:00
}
2023-04-10 08:08:11 +00:00
void BTPlayer : : update ( double p_delta ) {
2022-12-16 14:29:36 +00:00
if ( ! tree_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 ( ) ) ) ;
2022-08-30 16:48:49 +00:00
return ;
}
2023-04-15 13:49:34 +00:00
# ifdef DEBUG_ENABLED
2024-01-07 01:40:51 +00:00
double start = GET_TICKS_USEC ( ) ;
2023-04-15 13:49:34 +00:00
# endif
2022-08-30 16:48:49 +00:00
if ( active ) {
2022-12-16 14:29:36 +00:00
last_status = tree_instance - > execute ( p_delta ) ;
2023-04-13 07:29:45 +00:00
emit_signal ( LimboStringNames : : get_singleton ( ) - > updated , last_status ) ;
2022-12-16 14:29:36 +00:00
if ( last_status = = BTTask : : SUCCESS | | last_status = = BTTask : : FAILURE ) {
emit_signal ( LimboStringNames : : get_singleton ( ) - > behavior_tree_finished , last_status ) ;
2022-08-30 16:48:49 +00:00
}
}
2023-04-15 13:49:34 +00:00
# ifdef DEBUG_ENABLED
2024-01-07 01:40:51 +00:00
double end = GET_TICKS_USEC ( ) ;
2023-04-15 13:49:34 +00:00
update_time_acc + = ( end - start ) ;
update_time_n + = 1.0 ;
# endif
2022-08-30 16:48:49 +00:00
}
void BTPlayer : : restart ( ) {
2024-07-30 10:42:22 +00:00
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. " ) ;
2023-10-26 14:20:33 +00:00
tree_instance - > abort ( ) ;
2022-08-30 16:48:49 +00:00
set_active ( true ) ;
}
2023-04-15 13:49:34 +00:00
# ifdef DEBUG_ENABLED
void BTPlayer : : _set_monitor_performance ( bool p_monitor_performance ) {
monitor_performance = p_monitor_performance ;
2024-06-04 07:30:42 +00:00
if ( ! get_owner ( ) & & monitor_performance ) {
// Don't add custom monitor if not in scene.
2023-04-15 13:49:34 +00:00
return ;
}
if ( monitor_performance ) {
2024-06-04 07:30:42 +00:00
_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 ) ;
2023-04-15 13:49:34 +00:00
}
}
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 ;
}
2024-01-13 16:10:42 +00:00
# endif // ! DEBUG_ENABLED
2023-04-15 13:49:34 +00:00
2022-08-30 16:48:49 +00:00
void BTPlayer : : _notification ( int p_notification ) {
switch ( p_notification ) {
case NOTIFICATION_PROCESS : {
2022-12-16 14:29:36 +00:00
Variant time = get_process_delta_time ( ) ;
update ( time ) ;
2022-08-30 16:48:49 +00:00
} break ;
case NOTIFICATION_PHYSICS_PROCESS : {
2023-04-14 08:17:08 +00:00
Variant time = get_physics_process_delta_time ( ) ;
2022-12-16 14:29:36 +00:00
update ( time ) ;
2022-08-30 16:48:49 +00:00
} break ;
case NOTIFICATION_READY : {
if ( ! Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
2024-01-23 15:22:10 +00:00
if ( blackboard . is_null ( ) ) {
blackboard = Ref < Blackboard > ( memnew ( Blackboard ) ) ;
}
2024-01-23 19:02:23 +00:00
if ( blackboard_plan . is_valid ( ) ) {
2024-03-06 19:17:23 +00:00
blackboard_plan - > populate_blackboard ( blackboard , false , this ) ;
2024-01-23 11:05:54 +00:00
}
2022-08-30 16:48:49 +00:00
if ( behavior_tree . is_valid ( ) ) {
_load_tree ( ) ;
}
2024-03-11 17:58:40 +00:00
} else {
_update_blackboard_plan ( ) ;
2022-08-30 16:48:49 +00:00
}
2024-07-17 16:25:58 +00:00
set_active ( active ) ;
2022-08-30 16:48:49 +00:00
} break ;
2023-09-09 11:37:09 +00:00
case NOTIFICATION_ENTER_TREE : {
2024-01-24 23:23:34 +00:00
# ifdef DEBUG_ENABLED
2024-01-07 01:40:51 +00:00
if ( tree_instance . is_valid ( ) & & IS_DEBUGGER_ACTIVE ( ) ) {
2023-09-09 11:37:09 +00:00
LimboDebugger : : get_singleton ( ) - > register_bt_instance ( tree_instance , get_path ( ) ) ;
}
2024-06-04 07:30:42 +00:00
if ( monitor_performance ) {
_add_custom_monitor ( ) ;
}
2024-01-24 23:23:34 +00:00
# endif // DEBUG_ENABLED
2023-09-09 11:37:09 +00:00
} break ;
2023-04-13 07:29:45 +00:00
case NOTIFICATION_EXIT_TREE : {
2024-01-24 23:23:34 +00:00
# ifdef DEBUG_ENABLED
2024-01-07 01:40:51 +00:00
if ( tree_instance . is_valid ( ) & & IS_DEBUGGER_ACTIVE ( ) ) {
2023-04-13 07:29:45 +00:00
LimboDebugger : : get_singleton ( ) - > unregister_bt_instance ( tree_instance , get_path ( ) ) ;
}
2024-06-04 07:30:42 +00:00
if ( monitor_performance ) {
_remove_custom_monitor ( ) ;
}
2024-01-13 16:10:42 +00:00
# endif // DEBUG_ENABLED
2024-01-24 23:23:34 +00:00
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
2024-04-01 14:34:36 +00:00
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 ) ) ;
2024-01-24 23:23:34 +00:00
}
}
} break ;
2022-08-30 16:48:49 +00:00
}
}
void BTPlayer : : _bind_methods ( ) {
2024-03-04 20:36:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_behavior_tree " , " behavior_tree " ) , & BTPlayer : : set_behavior_tree ) ;
2022-08-30 16:48:49 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_behavior_tree " ) , & BTPlayer : : get_behavior_tree ) ;
2024-05-02 10:11:59 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_agent_node " , " agent_node " ) , & BTPlayer : : set_agent_node ) ;
ClassDB : : bind_method ( D_METHOD ( " get_agent_node " ) , & BTPlayer : : get_agent_node ) ;
2024-03-04 20:36:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_update_mode " , " update_mode " ) , & BTPlayer : : set_update_mode ) ;
2022-08-30 16:48:49 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_update_mode " ) , & BTPlayer : : get_update_mode ) ;
2024-03-04 20:36:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_active " , " active " ) , & BTPlayer : : set_active ) ;
2022-08-30 16:48:49 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_active " ) , & BTPlayer : : get_active ) ;
2024-03-04 20:36:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_blackboard " , " blackboard " ) , & BTPlayer : : set_blackboard ) ;
2022-09-05 14:30:41 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_blackboard " ) , & BTPlayer : : get_blackboard ) ;
2022-08-30 16:48:49 +00:00
2024-03-04 20:36:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_blackboard_plan " , " plan " ) , & BTPlayer : : set_blackboard_plan ) ;
2024-01-23 19:02:23 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_blackboard_plan " ) , & BTPlayer : : get_blackboard_plan ) ;
2022-09-08 13:56:51 +00:00
2024-03-04 20:36:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " update " , " delta " ) , & BTPlayer : : update ) ;
2022-08-30 16:48:49 +00:00
ClassDB : : bind_method ( D_METHOD ( " restart " ) , & BTPlayer : : restart ) ;
2022-12-16 14:29:36 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_last_status " ) , & BTPlayer : : get_last_status ) ;
2022-08-30 16:48:49 +00:00
2024-02-03 15:31:21 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_tree_instance " ) , & BTPlayer : : get_tree_instance ) ;
2022-08-30 16:48:49 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " behavior_tree " , PROPERTY_HINT_RESOURCE_TYPE , " BehaviorTree " ) , " set_behavior_tree " , " get_behavior_tree " ) ;
2024-05-02 10:11:59 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : NODE_PATH , " agent_node " ) , " set_agent_node " , " get_agent_node " ) ;
2022-08-30 16:48:49 +00:00
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 " ) ;
2023-10-26 11:44:09 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " blackboard " , PROPERTY_HINT_NONE , " Blackboard " , 0 ) , " set_blackboard " , " get_blackboard " ) ;
2024-03-31 23:11:51 +00:00
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 " ) ;
2022-08-30 16:48:49 +00:00
BIND_ENUM_CONSTANT ( IDLE ) ;
BIND_ENUM_CONSTANT ( PHYSICS ) ;
BIND_ENUM_CONSTANT ( MANUAL ) ;
2022-08-31 15:50:49 +00:00
2024-03-04 20:36:16 +00:00
ADD_SIGNAL ( MethodInfo ( " behavior_tree_finished " , PropertyInfo ( Variant : : INT , " status " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " updated " , PropertyInfo ( Variant : : INT , " status " ) ) ) ;
2023-04-15 13:49:34 +00:00
# ifdef DEBUG_ENABLED
2024-03-04 20:36:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " _set_monitor_performance " , " enable " ) , & BTPlayer : : _set_monitor_performance ) ;
2023-04-15 13:49:34 +00:00
ClassDB : : bind_method ( D_METHOD ( " _get_monitor_performance " ) , & BTPlayer : : _get_monitor_performance ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " monitor_performance " ) , " _set_monitor_performance " , " _get_monitor_performance " ) ;
# endif // DEBUG_ENABLED
2022-09-08 13:56:51 +00:00
}
BTPlayer : : BTPlayer ( ) {
2024-01-23 15:22:10 +00:00
blackboard = Ref < Blackboard > ( memnew ( Blackboard ) ) ;
2024-05-02 10:11:59 +00:00
agent_node = LW_NAME ( node_pp ) ;
2022-09-08 13:56:51 +00:00
}
BTPlayer : : ~ BTPlayer ( ) {
2023-04-15 13:49:34 +00:00
}