2014-02-10 01:10:30 +00:00
/**************************************************************************/
/* object.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-01-04 23:50:27 +00:00
2014-02-10 01:10:30 +00:00
# include "object.h"
2024-01-30 20:03:28 +00:00
# include "object.compat.inc"
2017-01-16 07:04:19 +00:00
2018-09-11 16:13:45 +00:00
# include "core/core_string_names.h"
2023-08-05 01:34:14 +00:00
# include "core/extension/gdextension_manager.h"
2020-11-07 22:33:38 +00:00
# include "core/io/resource.h"
# include "core/object/class_db.h"
# include "core/object/message_queue.h"
# include "core/object/script_language.h"
2018-09-11 16:13:45 +00:00
# include "core/os/os.h"
2020-11-07 22:33:38 +00:00
# include "core/string/print_string.h"
# include "core/string/translation.h"
2023-01-30 16:46:56 +00:00
# include "core/templates/local_vector.h"
2022-08-05 18:35:08 +00:00
# include "core/variant/typed_array.h"
2014-04-05 15:39:30 +00:00
# ifdef DEBUG_ENABLED
struct _ObjectDebugLock {
Object * obj ;
_ObjectDebugLock ( Object * p_obj ) {
obj = p_obj ;
obj - > _lock_index . ref ( ) ;
}
~ _ObjectDebugLock ( ) {
obj - > _lock_index . unref ( ) ;
}
} ;
# define OBJ_DEBUG_LOCK _ObjectDebugLock _debug_lock(this);
# else
# define OBJ_DEBUG_LOCK
# endif
2016-08-25 20:45:20 +00:00
PropertyInfo : : operator Dictionary ( ) const {
Dictionary d ;
d [ " name " ] = name ;
2017-08-23 22:10:32 +00:00
d [ " class_name " ] = class_name ;
2016-08-25 20:45:20 +00:00
d [ " type " ] = type ;
d [ " hint " ] = hint ;
d [ " hint_string " ] = hint_string ;
d [ " usage " ] = usage ;
return d ;
}
PropertyInfo PropertyInfo : : from_dict ( const Dictionary & p_dict ) {
PropertyInfo pi ;
2020-05-14 14:41:43 +00:00
if ( p_dict . has ( " type " ) ) {
2016-08-25 20:45:20 +00:00
pi . type = Variant : : Type ( int ( p_dict [ " type " ] ) ) ;
2020-05-14 14:41:43 +00:00
}
2016-08-25 20:45:20 +00:00
2020-05-14 14:41:43 +00:00
if ( p_dict . has ( " name " ) ) {
2016-08-25 20:45:20 +00:00
pi . name = p_dict [ " name " ] ;
2020-05-14 14:41:43 +00:00
}
2016-08-25 20:45:20 +00:00
2020-05-14 14:41:43 +00:00
if ( p_dict . has ( " class_name " ) ) {
2017-08-23 22:10:32 +00:00
pi . class_name = p_dict [ " class_name " ] ;
2020-05-14 14:41:43 +00:00
}
2017-08-23 22:10:32 +00:00
2020-05-14 14:41:43 +00:00
if ( p_dict . has ( " hint " ) ) {
2016-08-25 20:45:20 +00:00
pi . hint = PropertyHint ( int ( p_dict [ " hint " ] ) ) ;
2020-05-14 14:41:43 +00:00
}
2016-08-25 20:45:20 +00:00
2020-05-14 14:41:43 +00:00
if ( p_dict . has ( " hint_string " ) ) {
2016-08-25 20:45:20 +00:00
pi . hint_string = p_dict [ " hint_string " ] ;
2020-05-14 14:41:43 +00:00
}
2016-08-25 20:45:20 +00:00
2020-05-14 14:41:43 +00:00
if ( p_dict . has ( " usage " ) ) {
2016-08-25 20:45:20 +00:00
pi . usage = p_dict [ " usage " ] ;
2020-05-14 14:41:43 +00:00
}
2016-08-25 20:45:20 +00:00
return pi ;
}
2022-08-05 18:35:08 +00:00
TypedArray < Dictionary > convert_property_list ( const List < PropertyInfo > * p_list ) {
TypedArray < Dictionary > va ;
2014-02-10 01:10:30 +00:00
for ( const List < PropertyInfo > : : Element * E = p_list - > front ( ) ; E ; E = E - > next ( ) ) {
2016-08-25 20:45:20 +00:00
va . push_back ( Dictionary ( E - > get ( ) ) ) ;
2014-02-10 01:10:30 +00:00
}
return va ;
}
2016-08-25 20:45:20 +00:00
MethodInfo : : operator Dictionary ( ) const {
Dictionary d ;
d [ " name " ] = name ;
d [ " args " ] = convert_property_list ( & arguments ) ;
Array da ;
2020-05-14 14:41:43 +00:00
for ( int i = 0 ; i < default_arguments . size ( ) ; i + + ) {
2016-08-25 20:45:20 +00:00
da . push_back ( default_arguments [ i ] ) ;
2020-05-14 14:41:43 +00:00
}
2016-08-25 20:45:20 +00:00
d [ " default_args " ] = da ;
d [ " flags " ] = flags ;
d [ " id " ] = id ;
Dictionary r = return_val ;
d [ " return " ] = r ;
return d ;
}
MethodInfo MethodInfo : : from_dict ( const Dictionary & p_dict ) {
MethodInfo mi ;
2020-05-14 14:41:43 +00:00
if ( p_dict . has ( " name " ) ) {
2016-08-25 20:45:20 +00:00
mi . name = p_dict [ " name " ] ;
2020-05-14 14:41:43 +00:00
}
2016-08-25 20:45:20 +00:00
Array args ;
if ( p_dict . has ( " args " ) ) {
args = p_dict [ " args " ] ;
}
2023-12-24 12:44:21 +00:00
for ( const Variant & arg : args ) {
Dictionary d = arg ;
2016-08-25 20:45:20 +00:00
mi . arguments . push_back ( PropertyInfo : : from_dict ( d ) ) ;
}
Array defargs ;
if ( p_dict . has ( " default_args " ) ) {
defargs = p_dict [ " default_args " ] ;
}
2023-12-24 12:44:21 +00:00
for ( const Variant & defarg : defargs ) {
mi . default_arguments . push_back ( defarg ) ;
2016-08-25 20:45:20 +00:00
}
if ( p_dict . has ( " return " ) ) {
mi . return_val = PropertyInfo : : from_dict ( p_dict [ " return " ] ) ;
}
2020-05-14 14:41:43 +00:00
if ( p_dict . has ( " flags " ) ) {
2016-08-25 20:45:20 +00:00
mi . flags = p_dict [ " flags " ] ;
2020-05-14 14:41:43 +00:00
}
2016-08-25 20:45:20 +00:00
return mi ;
}
2014-02-10 01:10:30 +00:00
Object : : Connection : : operator Variant ( ) const {
Dictionary d ;
d [ " signal " ] = signal ;
2020-02-19 19:27:19 +00:00
d [ " callable " ] = callable ;
2014-02-10 01:10:30 +00:00
d [ " flags " ] = flags ;
return d ;
}
bool Object : : Connection : : operator < ( const Connection & p_conn ) const {
2020-02-19 19:27:19 +00:00
if ( signal = = p_conn . signal ) {
return callable < p_conn . callable ;
2014-02-10 01:10:30 +00:00
} else {
2020-02-19 19:27:19 +00:00
return signal < p_conn . signal ;
2014-02-10 01:10:30 +00:00
}
}
2020-05-14 12:29:06 +00:00
2014-02-10 01:10:30 +00:00
Object : : Connection : : Connection ( const Variant & p_variant ) {
Dictionary d = p_variant ;
2020-05-14 14:41:43 +00:00
if ( d . has ( " signal " ) ) {
2014-02-10 01:10:30 +00:00
signal = d [ " signal " ] ;
2020-05-14 14:41:43 +00:00
}
if ( d . has ( " callable " ) ) {
2020-02-19 19:27:19 +00:00
callable = d [ " callable " ] ;
2020-05-14 14:41:43 +00:00
}
if ( d . has ( " flags " ) ) {
2014-02-10 01:10:30 +00:00
flags = d [ " flags " ] ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
bool Object : : _predelete ( ) {
_predelete_ok = 1 ;
notification ( NOTIFICATION_PREDELETE , true ) ;
2015-06-29 03:29:49 +00:00
if ( _predelete_ok ) {
2023-04-07 21:32:37 +00:00
_class_name_ptr = nullptr ; // Must restore, so constructors/destructors have proper class name access at each stage.
2023-10-20 11:43:42 +00:00
notification ( NOTIFICATION_PREDELETE_CLEANUP , true ) ;
2015-06-29 03:29:49 +00:00
}
2014-02-10 01:10:30 +00:00
return _predelete_ok ;
}
2023-04-10 16:45:53 +00:00
void Object : : cancel_free ( ) {
_predelete_ok = false ;
}
2014-02-10 01:10:30 +00:00
void Object : : _postinitialize ( ) {
2023-04-07 21:32:37 +00:00
_class_name_ptr = _get_class_namev ( ) ; // Set the direct pointer, which is much faster to obtain, but can only happen after postinitialize.
2017-01-03 02:03:46 +00:00
_initialize_classv ( ) ;
2023-04-07 21:32:37 +00:00
_class_name_ptr = nullptr ; // May have been called from a constructor.
2014-02-10 01:10:30 +00:00
notification ( NOTIFICATION_POSTINITIALIZE ) ;
}
void Object : : get_valid_parents_static ( List < String > * p_parents ) {
}
2020-05-14 12:29:06 +00:00
2014-02-10 01:10:30 +00:00
void Object : : _get_valid_parents_static ( List < String > * p_parents ) {
}
void Object : : set ( const StringName & p_name , const Variant & p_value , bool * r_valid ) {
# ifdef TOOLS_ENABLED
_edited = true ;
# endif
2015-12-05 17:18:22 +00:00
2014-02-10 01:10:30 +00:00
if ( script_instance ) {
if ( script_instance - > set ( p_name , p_value ) ) {
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2014-02-10 01:10:30 +00:00
* r_valid = true ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
return ;
}
}
2021-06-04 17:33:48 +00:00
if ( _extension & & _extension - > set ) {
2021-06-19 15:58:49 +00:00
// C style pointer casts should never trigger a compiler warning because the risk is assumed by the user, so GCC should keep quiet about it.
# if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wignored-qualifiers"
# endif
2022-12-07 11:11:28 +00:00
if ( _extension - > set ( _extension_instance , ( const GDExtensionStringNamePtr ) & p_name , ( const GDExtensionVariantPtr ) & p_value ) ) {
2021-06-04 17:33:48 +00:00
if ( r_valid ) {
* r_valid = true ;
}
return ;
}
2021-06-19 15:58:49 +00:00
# if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
# endif
2021-06-04 17:33:48 +00:00
}
2022-02-16 12:56:32 +00:00
// Try built-in setter.
2014-02-10 01:10:30 +00:00
{
2017-01-03 02:03:46 +00:00
if ( ClassDB : : set_property ( this , p_name , p_value , r_valid ) ) {
2014-02-10 01:10:30 +00:00
return ;
}
}
if ( p_name = = CoreStringNames : : get_singleton ( ) - > _script ) {
set_script ( p_value ) ;
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2014-02-10 01:10:30 +00:00
* r_valid = true ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
return ;
2022-03-23 20:08:54 +00:00
} else {
2022-05-08 08:09:19 +00:00
Variant * * V = metadata_properties . getptr ( p_name ) ;
if ( V ) {
* * V = p_value ;
2022-03-23 20:08:54 +00:00
if ( r_valid ) {
* r_valid = true ;
}
return ;
} else if ( p_name . operator String ( ) . begins_with ( " metadata/ " ) ) {
// Must exist, otherwise duplicate() will not work.
set_meta ( p_name . operator String ( ) . replace_first ( " metadata/ " , " " ) , p_value ) ;
if ( r_valid ) {
* r_valid = true ;
}
return ;
2020-05-14 14:41:43 +00:00
}
2018-07-29 20:40:09 +00:00
}
# ifdef TOOLS_ENABLED
if ( script_instance ) {
bool valid ;
script_instance - > property_set_fallback ( p_name , p_value , & valid ) ;
if ( valid ) {
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2014-02-10 01:10:30 +00:00
* r_valid = true ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
return ;
}
}
2018-07-29 20:40:09 +00:00
# endif
2022-04-28 20:49:10 +00:00
// Something inside the object... :|
bool success = _setv ( p_name , p_value ) ;
if ( success ) {
if ( r_valid ) {
* r_valid = true ;
}
return ;
}
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2018-07-29 20:40:09 +00:00
* r_valid = false ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
Variant Object : : get ( const StringName & p_name , bool * r_valid ) const {
Variant ret ;
if ( script_instance ) {
if ( script_instance - > get ( p_name , ret ) ) {
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2014-02-10 01:10:30 +00:00
* r_valid = true ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
return ret ;
}
}
2021-06-04 17:33:48 +00:00
if ( _extension & & _extension - > get ) {
2021-06-19 15:58:49 +00:00
// C style pointer casts should never trigger a compiler warning because the risk is assumed by the user, so GCC should keep quiet about it.
# if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wignored-qualifiers"
# endif
2022-12-07 11:11:28 +00:00
if ( _extension - > get ( _extension_instance , ( const GDExtensionStringNamePtr ) & p_name , ( GDExtensionVariantPtr ) & ret ) ) {
2021-06-04 17:33:48 +00:00
if ( r_valid ) {
* r_valid = true ;
}
return ret ;
}
2021-06-19 15:58:49 +00:00
# if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
# endif
2021-06-04 17:33:48 +00:00
}
2022-02-16 12:56:32 +00:00
// Try built-in getter.
2014-02-10 01:10:30 +00:00
{
2017-01-03 02:03:46 +00:00
if ( ClassDB : : get_property ( const_cast < Object * > ( this ) , p_name , ret ) ) {
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2014-02-10 01:10:30 +00:00
* r_valid = true ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
return ret ;
}
}
if ( p_name = = CoreStringNames : : get_singleton ( ) - > _script ) {
ret = get_script ( ) ;
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2014-02-10 01:10:30 +00:00
* r_valid = true ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
return ret ;
2022-03-23 20:08:54 +00:00
}
2022-05-08 08:09:19 +00:00
const Variant * const * V = metadata_properties . getptr ( p_name ) ;
2014-02-10 01:10:30 +00:00
2022-05-08 08:09:19 +00:00
if ( V ) {
ret = * * V ;
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2014-02-10 01:10:30 +00:00
* r_valid = true ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
return ret ;
2018-10-29 19:36:31 +00:00
2014-02-10 01:10:30 +00:00
} else {
2018-07-29 20:40:09 +00:00
# ifdef TOOLS_ENABLED
if ( script_instance ) {
bool valid ;
ret = script_instance - > property_get_fallback ( p_name , & valid ) ;
if ( valid ) {
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2018-07-29 20:40:09 +00:00
* r_valid = true ;
2020-05-14 14:41:43 +00:00
}
2018-07-29 20:40:09 +00:00
return ret ;
}
}
# endif
2022-04-28 20:49:10 +00:00
// Something inside the object... :|
bool success = _getv ( p_name , ret ) ;
if ( success ) {
if ( r_valid ) {
* r_valid = true ;
}
return ret ;
}
2018-07-29 20:40:09 +00:00
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2018-07-29 20:40:09 +00:00
* r_valid = false ;
2020-05-14 14:41:43 +00:00
}
2018-07-29 20:40:09 +00:00
return Variant ( ) ;
2014-02-10 01:10:30 +00:00
}
}
2017-05-30 20:20:15 +00:00
void Object : : set_indexed ( const Vector < StringName > & p_names , const Variant & p_value , bool * r_valid ) {
2020-12-15 12:04:21 +00:00
if ( p_names . is_empty ( ) ) {
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2017-05-30 20:20:15 +00:00
* r_valid = false ;
2020-05-14 14:41:43 +00:00
}
2017-05-30 20:20:15 +00:00
return ;
}
if ( p_names . size ( ) = = 1 ) {
set ( p_names [ 0 ] , p_value , r_valid ) ;
return ;
}
bool valid = false ;
2020-05-14 14:41:43 +00:00
if ( ! r_valid ) {
2017-05-30 20:20:15 +00:00
r_valid = & valid ;
2020-05-14 14:41:43 +00:00
}
2017-05-30 20:20:15 +00:00
List < Variant > value_stack ;
value_stack . push_back ( get ( p_names [ 0 ] , r_valid ) ) ;
if ( ! * r_valid ) {
value_stack . clear ( ) ;
return ;
}
for ( int i = 1 ; i < p_names . size ( ) - 1 ; i + + ) {
2020-11-07 01:29:22 +00:00
value_stack . push_back ( value_stack . back ( ) - > get ( ) . get_named ( p_names [ i ] , valid ) ) ;
if ( r_valid ) {
* r_valid = valid ;
}
2017-05-30 20:20:15 +00:00
2020-11-07 01:29:22 +00:00
if ( ! valid ) {
2017-05-30 20:20:15 +00:00
value_stack . clear ( ) ;
return ;
}
}
value_stack . push_back ( p_value ) ; // p_names[p_names.size() - 1]
for ( int i = p_names . size ( ) - 1 ; i > 0 ; i - - ) {
2020-11-07 01:29:22 +00:00
value_stack . back ( ) - > prev ( ) - > get ( ) . set_named ( p_names [ i ] , value_stack . back ( ) - > get ( ) , valid ) ;
2017-05-30 20:20:15 +00:00
value_stack . pop_back ( ) ;
2020-11-07 01:29:22 +00:00
if ( r_valid ) {
* r_valid = valid ;
}
if ( ! valid ) {
2017-05-30 20:20:15 +00:00
value_stack . clear ( ) ;
return ;
}
}
set ( p_names [ 0 ] , value_stack . back ( ) - > get ( ) , r_valid ) ;
value_stack . pop_back ( ) ;
2020-12-15 12:04:21 +00:00
ERR_FAIL_COND ( ! value_stack . is_empty ( ) ) ;
2017-05-30 20:20:15 +00:00
}
Variant Object : : get_indexed ( const Vector < StringName > & p_names , bool * r_valid ) const {
2020-12-15 12:04:21 +00:00
if ( p_names . is_empty ( ) ) {
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2017-05-30 20:20:15 +00:00
* r_valid = false ;
2020-05-14 14:41:43 +00:00
}
2017-05-30 20:20:15 +00:00
return Variant ( ) ;
}
bool valid = false ;
2019-05-16 21:22:52 +00:00
Variant current_value = get ( p_names [ 0 ] , & valid ) ;
2017-05-30 20:20:15 +00:00
for ( int i = 1 ; i < p_names . size ( ) ; i + + ) {
2020-11-07 01:29:22 +00:00
current_value = current_value . get_named ( p_names [ i ] , valid ) ;
2017-05-30 20:20:15 +00:00
2020-05-14 14:41:43 +00:00
if ( ! valid ) {
2019-05-16 21:22:52 +00:00
break ;
2020-05-14 14:41:43 +00:00
}
2017-05-30 20:20:15 +00:00
}
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2019-05-16 21:22:52 +00:00
* r_valid = valid ;
2020-05-14 14:41:43 +00:00
}
2019-05-16 21:22:52 +00:00
2017-05-30 20:20:15 +00:00
return current_value ;
}
2014-02-10 01:10:30 +00:00
void Object : : get_property_list ( List < PropertyInfo > * p_list , bool p_reversed ) const {
if ( script_instance & & p_reversed ) {
script_instance - > get_property_list ( p_list ) ;
}
2021-10-12 12:19:42 +00:00
if ( _extension ) {
2022-12-07 11:11:28 +00:00
const ObjectGDExtension * current_extension = _extension ;
2022-03-02 17:50:00 +00:00
while ( current_extension ) {
2023-08-31 19:20:39 +00:00
p_list - > push_back ( PropertyInfo ( Variant : : NIL , current_extension - > class_name , PROPERTY_HINT_NONE , current_extension - > class_name , PROPERTY_USAGE_CATEGORY ) ) ;
2023-07-20 01:24:04 +00:00
2022-03-02 17:50:00 +00:00
ClassDB : : get_property_list ( current_extension - > class_name , p_list , true , this ) ;
2016-03-08 23:00:52 +00:00
2023-07-20 01:24:04 +00:00
if ( current_extension - > get_property_list ) {
2024-01-02 20:10:54 +00:00
# ifdef TOOLS_ENABLED
// If this is a placeholder, we can't call into the GDExtension on the parent class,
// because we don't have a real instance of the class to give it.
if ( likely ( ! _extension - > is_placeholder ) ) {
# endif
uint32_t pcount ;
const GDExtensionPropertyInfo * pinfo = current_extension - > get_property_list ( _extension_instance , & pcount ) ;
for ( uint32_t i = 0 ; i < pcount ; i + + ) {
p_list - > push_back ( PropertyInfo ( pinfo [ i ] ) ) ;
}
2024-04-25 20:19:19 +00:00
if ( current_extension - > free_property_list2 ) {
current_extension - > free_property_list2 ( _extension_instance , pinfo , pcount ) ;
}
# ifndef DISABLE_DEPRECATED
else if ( current_extension - > free_property_list ) {
2024-01-02 20:10:54 +00:00
current_extension - > free_property_list ( _extension_instance , pinfo ) ;
}
2024-04-25 20:19:19 +00:00
# endif // DISABLE_DEPRECATED
2024-01-02 20:10:54 +00:00
# ifdef TOOLS_ENABLED
2023-07-20 01:24:04 +00:00
}
2024-01-02 20:10:54 +00:00
# endif
2023-07-20 01:24:04 +00:00
}
current_extension = current_extension - > parent ;
2021-06-04 17:33:48 +00:00
}
}
2021-10-12 12:19:42 +00:00
_get_property_listv ( p_list , p_reversed ) ;
2021-03-12 13:35:16 +00:00
if ( ! is_class ( " Script " ) ) { // can still be set, but this is for user-friendliness
2023-01-10 20:42:05 +00:00
p_list - > push_back ( PropertyInfo ( Variant : : OBJECT , " script " , PROPERTY_HINT_RESOURCE_TYPE , " Script " , PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NEVER_DUPLICATE ) ) ;
2018-11-08 14:30:02 +00:00
}
2022-03-23 20:08:54 +00:00
2014-02-10 01:10:30 +00:00
if ( script_instance & & ! p_reversed ) {
script_instance - > get_property_list ( p_list ) ;
2016-03-08 23:00:52 +00:00
}
2022-03-23 20:08:54 +00:00
2022-05-08 08:09:19 +00:00
for ( const KeyValue < StringName , Variant > & K : metadata ) {
PropertyInfo pi = PropertyInfo ( K . value . get_type ( ) , " metadata/ " + K . key . operator String ( ) ) ;
if ( K . value . get_type ( ) = = Variant : : OBJECT ) {
2022-03-23 20:08:54 +00:00
pi . hint = PROPERTY_HINT_RESOURCE_TYPE ;
pi . hint_string = " Resource " ;
}
p_list - > push_back ( pi ) ;
}
2014-02-10 01:10:30 +00:00
}
2016-05-15 02:48:23 +00:00
2022-08-12 20:57:11 +00:00
void Object : : validate_property ( PropertyInfo & p_property ) const {
_validate_propertyv ( p_property ) ;
2023-04-07 12:01:57 +00:00
2023-09-09 18:05:16 +00:00
if ( _extension & & _extension - > validate_property ) {
// GDExtension uses a StringName rather than a String for property name.
StringName prop_name = p_property . name ;
GDExtensionPropertyInfo gdext_prop = {
( GDExtensionVariantType ) p_property . type ,
& prop_name ,
& p_property . class_name ,
( uint32_t ) p_property . hint ,
& p_property . hint_string ,
p_property . usage ,
} ;
if ( _extension - > validate_property ( _extension_instance , & gdext_prop ) ) {
p_property . type = ( Variant : : Type ) gdext_prop . type ;
p_property . name = * reinterpret_cast < StringName * > ( gdext_prop . name ) ;
p_property . class_name = * reinterpret_cast < StringName * > ( gdext_prop . class_name ) ;
p_property . hint = ( PropertyHint ) gdext_prop . hint ;
p_property . hint_string = * reinterpret_cast < String * > ( gdext_prop . hint_string ) ;
p_property . usage = gdext_prop . usage ;
} ;
}
2023-04-07 12:01:57 +00:00
if ( script_instance ) { // Call it last to allow user altering already validated properties.
script_instance - > validate_property ( p_property ) ;
}
2016-05-15 02:48:23 +00:00
}
2022-08-22 09:43:14 +00:00
bool Object : : property_can_revert ( const StringName & p_name ) const {
2022-08-12 18:43:14 +00:00
if ( script_instance ) {
if ( script_instance - > property_can_revert ( p_name ) ) {
return true ;
}
}
// C style pointer casts should never trigger a compiler warning because the risk is assumed by the user, so GCC should keep quiet about it.
# if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wignored-qualifiers"
# endif
if ( _extension & & _extension - > property_can_revert ) {
2022-12-07 11:11:28 +00:00
if ( _extension - > property_can_revert ( _extension_instance , ( const GDExtensionStringNamePtr ) & p_name ) ) {
2022-08-12 18:43:14 +00:00
return true ;
}
}
# if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
# endif
return _property_can_revertv ( p_name ) ;
}
2022-08-22 09:43:14 +00:00
Variant Object : : property_get_revert ( const StringName & p_name ) const {
2022-08-12 18:43:14 +00:00
Variant ret ;
if ( script_instance ) {
if ( script_instance - > property_get_revert ( p_name , ret ) ) {
return ret ;
}
}
// C style pointer casts should never trigger a compiler warning because the risk is assumed by the user, so GCC should keep quiet about it.
# if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wignored-qualifiers"
# endif
if ( _extension & & _extension - > property_get_revert ) {
2022-12-07 11:11:28 +00:00
if ( _extension - > property_get_revert ( _extension_instance , ( const GDExtensionStringNamePtr ) & p_name , ( GDExtensionVariantPtr ) & ret ) ) {
2022-08-12 18:43:14 +00:00
return ret ;
}
}
# if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
# endif
if ( _property_get_revertv ( p_name , ret ) ) {
return ret ;
}
return Variant ( ) ;
}
2014-02-10 01:10:30 +00:00
void Object : : get_method_list ( List < MethodInfo > * p_list ) const {
2017-01-03 02:03:46 +00:00
ClassDB : : get_method_list ( get_class_name ( ) , p_list ) ;
2014-02-10 01:10:30 +00:00
if ( script_instance ) {
script_instance - > get_method_list ( p_list ) ;
2016-03-08 23:00:52 +00:00
}
2014-02-10 01:10:30 +00:00
}
2020-02-19 19:27:19 +00:00
Variant Object : : _call_bind ( const Variant * * p_args , int p_argcount , Callable : : CallError & r_error ) {
2014-02-10 01:10:30 +00:00
if ( p_argcount < 1 ) {
2020-02-19 19:27:19 +00:00
r_error . error = Callable : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ;
2023-09-29 16:19:46 +00:00
r_error . expected = 1 ;
2014-02-10 01:10:30 +00:00
return Variant ( ) ;
}
2020-02-20 21:58:05 +00:00
if ( p_args [ 0 ] - > get_type ( ) ! = Variant : : STRING_NAME & & p_args [ 0 ] - > get_type ( ) ! = Variant : : STRING ) {
2020-02-19 19:27:19 +00:00
r_error . error = Callable : : CallError : : CALL_ERROR_INVALID_ARGUMENT ;
2014-02-10 01:10:30 +00:00
r_error . argument = 0 ;
2020-02-20 21:58:05 +00:00
r_error . expected = Variant : : STRING_NAME ;
2014-02-10 01:10:30 +00:00
return Variant ( ) ;
}
StringName method = * p_args [ 0 ] ;
2022-03-09 13:58:40 +00:00
return callp ( method , & p_args [ 1 ] , p_argcount - 1 , r_error ) ;
2014-02-10 01:10:30 +00:00
}
2020-02-19 19:27:19 +00:00
Variant Object : : _call_deferred_bind ( const Variant * * p_args , int p_argcount , Callable : : CallError & r_error ) {
2014-02-10 01:10:30 +00:00
if ( p_argcount < 1 ) {
2020-02-19 19:27:19 +00:00
r_error . error = Callable : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ;
2023-09-29 16:19:46 +00:00
r_error . expected = 1 ;
2014-02-10 01:10:30 +00:00
return Variant ( ) ;
}
2020-02-20 21:58:05 +00:00
if ( p_args [ 0 ] - > get_type ( ) ! = Variant : : STRING_NAME & & p_args [ 0 ] - > get_type ( ) ! = Variant : : STRING ) {
2020-02-19 19:27:19 +00:00
r_error . error = Callable : : CallError : : CALL_ERROR_INVALID_ARGUMENT ;
2014-02-10 01:10:30 +00:00
r_error . argument = 0 ;
2020-02-20 21:58:05 +00:00
r_error . expected = Variant : : STRING_NAME ;
2014-02-10 01:10:30 +00:00
return Variant ( ) ;
}
2020-02-19 19:27:19 +00:00
r_error . error = Callable : : CallError : : CALL_OK ;
2014-02-10 01:10:30 +00:00
2016-01-04 12:35:21 +00:00
StringName method = * p_args [ 0 ] ;
2014-02-10 01:10:30 +00:00
2022-03-09 13:58:40 +00:00
MessageQueue : : get_singleton ( ) - > push_callp ( get_instance_id ( ) , method , & p_args [ 1 ] , p_argcount - 1 , true ) ;
2014-02-10 01:10:30 +00:00
return Variant ( ) ;
}
bool Object : : has_method ( const StringName & p_method ) const {
if ( p_method = = CoreStringNames : : get_singleton ( ) - > _free ) {
return true ;
}
if ( script_instance & & script_instance - > has_method ( p_method ) ) {
return true ;
}
2017-01-03 02:03:46 +00:00
MethodBind * method = ClassDB : : get_method ( get_class_name ( ) , p_method ) ;
2023-10-04 04:54:03 +00:00
if ( method ! = nullptr ) {
return true ;
}
const Script * scr = Object : : cast_to < Script > ( this ) ;
if ( scr ! = nullptr ) {
return scr - > has_static_method ( p_method ) ;
}
2014-02-10 01:10:30 +00:00
2023-10-04 04:54:03 +00:00
return false ;
2014-02-10 01:10:30 +00:00
}
2024-01-28 14:16:09 +00:00
int Object : : _get_method_argument_count_bind ( const StringName & p_method ) const {
return get_method_argument_count ( p_method ) ;
}
int Object : : get_method_argument_count ( const StringName & p_method , bool * r_is_valid ) const {
if ( p_method = = CoreStringNames : : get_singleton ( ) - > _free ) {
if ( r_is_valid ) {
* r_is_valid = true ;
}
return 0 ;
}
if ( script_instance ) {
bool valid = false ;
int ret = script_instance - > get_method_argument_count ( p_method , & valid ) ;
if ( valid ) {
if ( r_is_valid ) {
* r_is_valid = true ;
}
return ret ;
}
}
{
bool valid = false ;
int ret = ClassDB : : get_method_argument_count ( get_class_name ( ) , p_method , & valid ) ;
if ( valid ) {
if ( r_is_valid ) {
* r_is_valid = true ;
}
return ret ;
}
}
const Script * scr = Object : : cast_to < Script > ( this ) ;
while ( scr ! = nullptr ) {
bool valid = false ;
int ret = scr - > get_script_method_argument_count ( p_method , & valid ) ;
if ( valid ) {
if ( r_is_valid ) {
* r_is_valid = true ;
}
return ret ;
}
scr = scr - > get_base_script ( ) . ptr ( ) ;
}
if ( r_is_valid ) {
* r_is_valid = false ;
}
return 0 ;
}
2014-02-10 01:10:30 +00:00
Variant Object : : getvar ( const Variant & p_key , bool * r_valid ) const {
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2014-02-10 01:10:30 +00:00
* r_valid = false ;
2020-05-14 14:41:43 +00:00
}
2020-11-07 01:29:22 +00:00
if ( p_key . get_type ( ) = = Variant : : STRING_NAME | | p_key . get_type ( ) = = Variant : : STRING ) {
return get ( p_key , r_valid ) ;
}
2014-02-10 01:10:30 +00:00
return Variant ( ) ;
}
2020-05-14 12:29:06 +00:00
2014-02-10 01:10:30 +00:00
void Object : : setvar ( const Variant & p_key , const Variant & p_value , bool * r_valid ) {
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2014-02-10 01:10:30 +00:00
* r_valid = false ;
2020-05-14 14:41:43 +00:00
}
2020-11-07 01:29:22 +00:00
if ( p_key . get_type ( ) = = Variant : : STRING_NAME | | p_key . get_type ( ) = = Variant : : STRING ) {
return set ( p_key , p_value , r_valid ) ;
}
2014-02-10 01:10:30 +00:00
}
Variant Object : : callv ( const StringName & p_method , const Array & p_args ) {
2020-04-01 23:20:12 +00:00
const Variant * * argptrs = nullptr ;
2014-02-10 01:10:30 +00:00
2019-03-05 10:24:21 +00:00
if ( p_args . size ( ) > 0 ) {
argptrs = ( const Variant * * ) alloca ( sizeof ( Variant * ) * p_args . size ( ) ) ;
for ( int i = 0 ; i < p_args . size ( ) ; i + + ) {
argptrs [ i ] = & p_args [ i ] ;
}
2014-02-10 01:10:30 +00:00
}
2020-02-19 19:27:19 +00:00
Callable : : CallError ce ;
2022-03-09 13:58:40 +00:00
Variant ret = callp ( p_method , argptrs , p_args . size ( ) , ce ) ;
2020-02-19 19:27:19 +00:00
if ( ce . error ! = Callable : : CallError : : CALL_OK ) {
2019-08-15 02:57:49 +00:00
ERR_FAIL_V_MSG ( Variant ( ) , " Error calling method from 'callv': " + Variant : : get_call_error_text ( this , p_method , argptrs , p_args . size ( ) , ce ) + " . " ) ;
2019-03-05 10:24:21 +00:00
}
return ret ;
2014-02-10 01:10:30 +00:00
}
2022-03-09 13:58:40 +00:00
Variant Object : : callp ( const StringName & p_method , const Variant * * p_args , int p_argcount , Callable : : CallError & r_error ) {
2020-02-19 19:27:19 +00:00
r_error . error = Callable : : CallError : : CALL_OK ;
2014-02-10 01:10:30 +00:00
if ( p_method = = CoreStringNames : : get_singleton ( ) - > _free ) {
//free must be here, before anything, always ready
# ifdef DEBUG_ENABLED
if ( p_argcount ! = 0 ) {
2020-02-19 19:27:19 +00:00
r_error . error = Callable : : CallError : : CALL_ERROR_TOO_MANY_ARGUMENTS ;
2023-09-29 16:19:46 +00:00
r_error . expected = 0 ;
2014-02-10 01:10:30 +00:00
return Variant ( ) ;
}
2024-01-18 11:29:58 +00:00
if ( is_ref_counted ( ) ) {
2020-02-19 19:27:19 +00:00
r_error . error = Callable : : CallError : : CALL_ERROR_INVALID_METHOD ;
2019-08-15 02:57:49 +00:00
ERR_FAIL_V_MSG ( Variant ( ) , " Can't 'free' a reference. " ) ;
2014-02-10 01:10:30 +00:00
}
2014-04-05 15:39:30 +00:00
if ( _lock_index . get ( ) > 1 ) {
r_error . argument = 0 ;
2020-02-19 19:27:19 +00:00
r_error . error = Callable : : CallError : : CALL_ERROR_INVALID_METHOD ;
2019-08-15 02:57:49 +00:00
ERR_FAIL_V_MSG ( Variant ( ) , " Object is locked and can't be freed. " ) ;
2014-04-05 15:39:30 +00:00
}
2014-02-10 01:10:30 +00:00
# endif
//must be here, must be before everything,
memdelete ( this ) ;
2020-02-19 19:27:19 +00:00
r_error . error = Callable : : CallError : : CALL_OK ;
2014-02-10 01:10:30 +00:00
return Variant ( ) ;
}
Variant ret ;
2014-04-05 15:39:30 +00:00
OBJ_DEBUG_LOCK
2021-06-04 17:33:48 +00:00
2014-02-10 01:10:30 +00:00
if ( script_instance ) {
2022-03-09 13:58:40 +00:00
ret = script_instance - > callp ( p_method , p_args , p_argcount , r_error ) ;
2014-02-10 01:10:30 +00:00
//force jumptable
switch ( r_error . error ) {
2020-02-19 19:27:19 +00:00
case Callable : : CallError : : CALL_OK :
2014-02-10 01:10:30 +00:00
return ret ;
2020-02-19 19:27:19 +00:00
case Callable : : CallError : : CALL_ERROR_INVALID_METHOD :
2014-02-10 01:10:30 +00:00
break ;
2020-02-19 19:27:19 +00:00
case Callable : : CallError : : CALL_ERROR_INVALID_ARGUMENT :
case Callable : : CallError : : CALL_ERROR_TOO_MANY_ARGUMENTS :
case Callable : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS :
2022-06-27 20:10:04 +00:00
case Callable : : CallError : : CALL_ERROR_METHOD_NOT_CONST :
2014-02-10 01:10:30 +00:00
return ret ;
2020-02-19 19:27:19 +00:00
case Callable : : CallError : : CALL_ERROR_INSTANCE_IS_NULL : {
2014-02-10 01:10:30 +00:00
}
}
}
2021-06-04 17:33:48 +00:00
//extension does not need this, because all methods are registered in MethodBind
2017-01-03 02:03:46 +00:00
MethodBind * method = ClassDB : : get_method ( get_class_name ( ) , p_method ) ;
2014-02-10 01:10:30 +00:00
if ( method ) {
ret = method - > call ( this , p_args , p_argcount , r_error ) ;
} else {
2020-02-19 19:27:19 +00:00
r_error . error = Callable : : CallError : : CALL_ERROR_INVALID_METHOD ;
2014-02-10 01:10:30 +00:00
}
return ret ;
}
2022-06-27 20:10:04 +00:00
Variant Object : : call_const ( const StringName & p_method , const Variant * * p_args , int p_argcount , Callable : : CallError & r_error ) {
r_error . error = Callable : : CallError : : CALL_OK ;
if ( p_method = = CoreStringNames : : get_singleton ( ) - > _free ) {
// Free is not const, so fail.
r_error . error = Callable : : CallError : : CALL_ERROR_METHOD_NOT_CONST ;
return Variant ( ) ;
}
Variant ret ;
OBJ_DEBUG_LOCK
if ( script_instance ) {
ret = script_instance - > call_const ( p_method , p_args , p_argcount , r_error ) ;
//force jumptable
switch ( r_error . error ) {
case Callable : : CallError : : CALL_OK :
return ret ;
case Callable : : CallError : : CALL_ERROR_INVALID_METHOD :
break ;
case Callable : : CallError : : CALL_ERROR_METHOD_NOT_CONST :
break ;
case Callable : : CallError : : CALL_ERROR_INVALID_ARGUMENT :
case Callable : : CallError : : CALL_ERROR_TOO_MANY_ARGUMENTS :
case Callable : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS :
return ret ;
case Callable : : CallError : : CALL_ERROR_INSTANCE_IS_NULL : {
}
}
}
//extension does not need this, because all methods are registered in MethodBind
MethodBind * method = ClassDB : : get_method ( get_class_name ( ) , p_method ) ;
if ( method ) {
if ( ! method - > is_const ( ) ) {
r_error . error = Callable : : CallError : : CALL_ERROR_METHOD_NOT_CONST ;
return ret ;
}
ret = method - > call ( this , p_args , p_argcount , r_error ) ;
} else {
r_error . error = Callable : : CallError : : CALL_ERROR_INVALID_METHOD ;
}
return ret ;
}
2014-02-10 01:10:30 +00:00
void Object : : notification ( int p_notification , bool p_reversed ) {
2023-06-24 01:07:22 +00:00
if ( p_reversed ) {
if ( script_instance ) {
script_instance - > notification ( p_notification , p_reversed ) ;
}
} else {
_notificationv ( p_notification , p_reversed ) ;
}
2016-03-08 23:00:52 +00:00
2023-06-24 01:07:22 +00:00
if ( _extension ) {
if ( _extension - > notification2 ) {
2023-09-02 09:38:57 +00:00
_extension - > notification2 ( _extension_instance , p_notification , static_cast < GDExtensionBool > ( p_reversed ) ) ;
2023-06-24 01:07:22 +00:00
# ifndef DISABLE_DEPRECATED
} else if ( _extension - > notification ) {
_extension - > notification ( _extension_instance , p_notification ) ;
# endif // DISABLE_DEPRECATED
}
2014-02-10 01:10:30 +00:00
}
2021-06-04 17:33:48 +00:00
2023-06-24 01:07:22 +00:00
if ( p_reversed ) {
_notificationv ( p_notification , p_reversed ) ;
} else {
if ( script_instance ) {
script_instance - > notification ( p_notification , p_reversed ) ;
}
2021-06-04 17:33:48 +00:00
}
2014-02-10 01:10:30 +00:00
}
2019-04-10 05:07:40 +00:00
String Object : : to_string ( ) {
if ( script_instance ) {
bool valid ;
String ret = script_instance - > to_string ( & valid ) ;
2020-05-14 14:41:43 +00:00
if ( valid ) {
2019-04-10 05:07:40 +00:00
return ret ;
2020-05-14 14:41:43 +00:00
}
2019-04-10 05:07:40 +00:00
}
2021-06-04 17:33:48 +00:00
if ( _extension & & _extension - > to_string ) {
2022-03-27 19:31:00 +00:00
String ret ;
2022-12-07 11:11:28 +00:00
GDExtensionBool is_valid ;
2022-10-23 08:43:09 +00:00
_extension - > to_string ( _extension_instance , & is_valid , & ret ) ;
2022-03-27 19:31:00 +00:00
return ret ;
2021-06-04 17:33:48 +00:00
}
2022-07-24 22:15:20 +00:00
return " < " + get_class ( ) + " # " + itos ( get_instance_id ( ) ) + " > " ;
2019-04-10 05:07:40 +00:00
}
2020-02-13 19:03:10 +00:00
void Object : : set_script_and_instance ( const Variant & p_script , ScriptInstance * p_instance ) {
2017-07-22 19:57:56 +00:00
//this function is not meant to be used in any of these ways
ERR_FAIL_COND ( p_script . is_null ( ) ) ;
2023-09-09 14:11:33 +00:00
ERR_FAIL_NULL ( p_instance ) ;
2020-04-01 23:20:12 +00:00
ERR_FAIL_COND ( script_instance ! = nullptr | | ! script . is_null ( ) ) ;
2017-07-22 19:57:56 +00:00
script = p_script ;
script_instance = p_instance ;
}
2020-02-13 19:03:10 +00:00
void Object : : set_script ( const Variant & p_script ) {
2020-05-14 14:41:43 +00:00
if ( script = = p_script ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2021-02-17 15:47:57 +00:00
Ref < Script > s = p_script ;
2023-05-23 16:25:34 +00:00
if ( ! p_script . is_null ( ) ) {
ERR_FAIL_COND_MSG ( s . is_null ( ) , " Cannot set object script. Parameter should be null or a reference to a valid script. " ) ;
ERR_FAIL_COND_MSG ( s - > is_abstract ( ) , vformat ( " Cannot set object script. Script '%s' should not be abstract. " , s - > get_path ( ) ) ) ;
}
2021-02-17 15:47:57 +00:00
script = p_script ;
2014-02-10 01:10:30 +00:00
if ( script_instance ) {
memdelete ( script_instance ) ;
2020-04-01 23:20:12 +00:00
script_instance = nullptr ;
2014-02-10 01:10:30 +00:00
}
2016-03-08 23:00:52 +00:00
2018-07-29 20:40:09 +00:00
if ( ! s . is_null ( ) ) {
2021-06-17 22:03:09 +00:00
if ( s - > can_instantiate ( ) ) {
2018-07-29 20:40:09 +00:00
OBJ_DEBUG_LOCK
script_instance = s - > instance_create ( this ) ;
} else if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
OBJ_DEBUG_LOCK
script_instance = s - > placeholder_instance_create ( this ) ;
}
2014-02-10 01:10:30 +00:00
}
2021-02-10 20:18:45 +00:00
notify_property_list_changed ( ) ; //scripts may add variables, so refresh is desired
2014-02-10 01:10:30 +00:00
emit_signal ( CoreStringNames : : get_singleton ( ) - > script_changed ) ;
}
void Object : : set_script_instance ( ScriptInstance * p_instance ) {
2020-05-14 14:41:43 +00:00
if ( script_instance = = p_instance ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2020-05-14 14:41:43 +00:00
if ( script_instance ) {
2014-02-10 01:10:30 +00:00
memdelete ( script_instance ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
script_instance = p_instance ;
2020-05-14 14:41:43 +00:00
if ( p_instance ) {
2020-02-13 19:03:10 +00:00
script = p_instance - > get_script ( ) ;
2020-05-14 14:41:43 +00:00
} else {
2020-02-13 19:03:10 +00:00
script = Variant ( ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
2020-02-13 19:03:10 +00:00
Variant Object : : get_script ( ) const {
2014-02-10 01:10:30 +00:00
return script ;
}
2021-07-22 20:37:17 +00:00
bool Object : : has_meta ( const StringName & p_name ) const {
2014-02-10 01:10:30 +00:00
return metadata . has ( p_name ) ;
}
2021-07-22 20:37:17 +00:00
void Object : : set_meta ( const StringName & p_name , const Variant & p_value ) {
2014-02-10 01:10:30 +00:00
if ( p_value . get_type ( ) = = Variant : : NIL ) {
2022-03-23 20:08:54 +00:00
if ( metadata . has ( p_name ) ) {
metadata . erase ( p_name ) ;
2023-05-07 20:12:22 +00:00
const String & sname = p_name ;
metadata_properties . erase ( " metadata/ " + sname ) ;
if ( ! sname . begins_with ( " _ " ) ) {
// Metadata starting with _ don't show up in the inspector, so no need to update.
notify_property_list_changed ( ) ;
}
2022-03-23 20:08:54 +00:00
}
2014-02-10 01:10:30 +00:00
return ;
2020-05-19 13:46:49 +00:00
}
2014-02-10 01:10:30 +00:00
2022-05-08 08:09:19 +00:00
HashMap < StringName , Variant > : : Iterator E = metadata . find ( p_name ) ;
2022-03-23 20:08:54 +00:00
if ( E ) {
2022-05-08 08:09:19 +00:00
E - > value = p_value ;
2022-03-23 20:08:54 +00:00
} else {
2022-12-21 04:12:33 +00:00
ERR_FAIL_COND_MSG ( ! p_name . operator String ( ) . is_valid_identifier ( ) , " Invalid metadata identifier: ' " + p_name + " '. " ) ;
2022-05-08 08:09:19 +00:00
Variant * V = & metadata . insert ( p_name , p_value ) - > value ;
2023-05-07 20:12:22 +00:00
const String & sname = p_name ;
metadata_properties [ " metadata/ " + sname ] = V ;
if ( ! sname . begins_with ( " _ " ) ) {
notify_property_list_changed ( ) ;
}
2022-03-23 20:08:54 +00:00
}
2014-02-10 01:10:30 +00:00
}
2022-02-27 21:03:33 +00:00
Variant Object : : get_meta ( const StringName & p_name , const Variant & p_default ) const {
if ( ! metadata . has ( p_name ) ) {
if ( p_default ! = Variant ( ) ) {
return p_default ;
} else {
ERR_FAIL_V_MSG ( Variant ( ) , " The object does not have any 'meta' values with the key ' " + p_name + " '. " ) ;
}
}
2014-02-10 01:10:30 +00:00
return metadata [ p_name ] ;
}
2021-07-22 20:37:17 +00:00
void Object : : remove_meta ( const StringName & p_name ) {
2022-03-23 20:08:54 +00:00
set_meta ( p_name , Variant ( ) ) ;
2019-04-26 17:23:50 +00:00
}
2022-08-05 18:35:08 +00:00
TypedArray < Dictionary > Object : : _get_property_list_bind ( ) const {
2014-02-10 01:10:30 +00:00
List < PropertyInfo > lpi ;
get_property_list ( & lpi ) ;
return convert_property_list ( & lpi ) ;
}
2015-05-25 04:46:45 +00:00
2022-08-05 18:35:08 +00:00
TypedArray < Dictionary > Object : : _get_method_list_bind ( ) const {
2015-05-25 04:46:45 +00:00
List < MethodInfo > ml ;
get_method_list ( & ml ) ;
2022-08-05 18:35:08 +00:00
TypedArray < Dictionary > ret ;
2015-05-25 04:46:45 +00:00
for ( List < MethodInfo > : : Element * E = ml . front ( ) ; E ; E = E - > next ( ) ) {
2016-08-25 20:45:20 +00:00
Dictionary d = E - > get ( ) ;
2015-05-25 04:46:45 +00:00
//va.push_back(d);
ret . push_back ( d ) ;
}
return ret ;
}
2023-04-24 22:21:32 +00:00
TypedArray < StringName > Object : : _get_meta_list_bind ( ) const {
TypedArray < StringName > _metaret ;
2014-02-10 01:10:30 +00:00
2022-05-08 08:09:19 +00:00
for ( const KeyValue < StringName , Variant > & K : metadata ) {
_metaret . push_back ( K . key ) ;
2014-02-10 01:10:30 +00:00
}
return _metaret ;
}
2020-05-14 12:29:06 +00:00
2021-07-22 20:37:17 +00:00
void Object : : get_meta_list ( List < StringName > * p_list ) const {
2022-05-08 08:09:19 +00:00
for ( const KeyValue < StringName , Variant > & K : metadata ) {
p_list - > push_back ( K . key ) ;
2014-02-10 01:10:30 +00:00
}
}
void Object : : add_user_signal ( const MethodInfo & p_signal ) {
2021-12-09 09:42:46 +00:00
ERR_FAIL_COND_MSG ( p_signal . name . is_empty ( ) , " Signal name cannot be empty. " ) ;
2019-09-25 08:28:50 +00:00
ERR_FAIL_COND_MSG ( ClassDB : : has_signal ( get_class_name ( ) , p_signal . name ) , " User signal's name conflicts with a built-in signal of ' " + get_class_name ( ) + " '. " ) ;
ERR_FAIL_COND_MSG ( signal_map . has ( p_signal . name ) , " Trying to add already existing signal ' " + p_signal . name + " '. " ) ;
2020-02-19 19:27:19 +00:00
SignalData s ;
2014-02-10 01:10:30 +00:00
s . user = p_signal ;
signal_map [ p_signal . name ] = s ;
}
2015-03-03 17:39:13 +00:00
bool Object : : _has_user_signal ( const StringName & p_name ) const {
2020-05-14 14:41:43 +00:00
if ( ! signal_map . has ( p_name ) ) {
2015-03-03 17:39:13 +00:00
return false ;
2020-05-14 14:41:43 +00:00
}
2015-03-03 17:39:13 +00:00
return signal_map [ p_name ] . user . name . length ( ) > 0 ;
}
2024-04-18 08:22:59 +00:00
void Object : : _remove_user_signal ( const StringName & p_name ) {
SignalData * s = signal_map . getptr ( p_name ) ;
ERR_FAIL_NULL_MSG ( s , " Provided signal does not exist. " ) ;
ERR_FAIL_COND_MSG ( ! s - > removable , " Signal is not removable (not added with add_user_signal). " ) ;
for ( const KeyValue < Callable , SignalData : : Slot > & slot_kv : s - > slot_map ) {
Object * target = slot_kv . key . get_object ( ) ;
if ( likely ( target ) ) {
target - > connections . erase ( slot_kv . value . cE ) ;
}
}
signal_map . erase ( p_name ) ;
}
2022-02-22 11:15:43 +00:00
Error Object : : _emit_signal ( const Variant * * p_args , int p_argcount , Callable : : CallError & r_error ) {
2023-09-29 16:19:46 +00:00
if ( unlikely ( p_argcount < 1 ) ) {
r_error . error = Callable : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ;
r_error . expected = 1 ;
ERR_FAIL_V ( Error : : ERR_INVALID_PARAMETER ) ;
}
2014-02-10 01:10:30 +00:00
2023-09-29 16:19:46 +00:00
if ( unlikely ( p_args [ 0 ] - > get_type ( ) ! = Variant : : STRING_NAME & & p_args [ 0 ] - > get_type ( ) ! = Variant : : STRING ) ) {
2020-02-19 19:27:19 +00:00
r_error . error = Callable : : CallError : : CALL_ERROR_INVALID_ARGUMENT ;
2014-02-10 01:10:30 +00:00
r_error . argument = 0 ;
2020-02-20 21:58:05 +00:00
r_error . expected = Variant : : STRING_NAME ;
2023-09-29 16:19:46 +00:00
ERR_FAIL_V ( Error : : ERR_INVALID_PARAMETER ) ;
2014-02-10 01:10:30 +00:00
}
2020-02-19 19:27:19 +00:00
r_error . error = Callable : : CallError : : CALL_OK ;
2014-02-10 01:10:30 +00:00
StringName signal = * p_args [ 0 ] ;
2020-04-01 23:20:12 +00:00
const Variant * * args = nullptr ;
2014-02-10 01:10:30 +00:00
2016-01-04 12:35:21 +00:00
int argc = p_argcount - 1 ;
if ( argc ) {
args = & p_args [ 1 ] ;
2014-02-10 01:10:30 +00:00
}
2022-02-22 11:15:43 +00:00
return emit_signalp ( signal , args , argc ) ;
2016-01-04 12:35:21 +00:00
}
2014-02-10 01:10:30 +00:00
2022-03-09 13:58:40 +00:00
Error Object : : emit_signalp ( const StringName & p_name , const Variant * * p_args , int p_argcount ) {
2020-05-14 14:41:43 +00:00
if ( _block_signals ) {
2017-09-22 03:58:29 +00:00
return ERR_CANT_ACQUIRE_RESOURCE ; //no emit, signals blocked
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2020-02-19 19:27:19 +00:00
SignalData * s = signal_map . getptr ( p_name ) ;
2014-02-10 01:10:30 +00:00
if ( ! s ) {
2016-09-25 17:21:21 +00:00
# ifdef DEBUG_ENABLED
2017-01-03 02:03:46 +00:00
bool signal_is_valid = ClassDB : : has_signal ( get_class_name ( ) , p_name ) ;
2016-09-25 17:21:21 +00:00
//check in script
2019-08-15 02:57:49 +00:00
ERR_FAIL_COND_V_MSG ( ! signal_is_valid & & ! script . is_null ( ) & & ! Ref < Script > ( script ) - > has_script_signal ( p_name ) , ERR_UNAVAILABLE , " Can't emit non-existing signal " + String ( " \" " ) + p_name + " \" . " ) ;
2016-09-25 17:21:21 +00:00
# endif
//not connected? just return
2017-08-05 22:48:29 +00:00
return ERR_UNAVAILABLE ;
2014-02-10 01:10:30 +00:00
}
2023-04-11 15:20:03 +00:00
// If this is a ref-counted object, prevent it from being destroyed during signal emission,
// which is needed in certain edge cases; e.g., https://github.com/godotengine/godot/issues/73889.
Ref < RefCounted > rc = Ref < RefCounted > ( Object : : cast_to < RefCounted > ( this ) ) ;
2023-01-30 16:46:56 +00:00
// Ensure that disconnecting the signal or even deleting the object
// will not affect the signal calling.
2024-03-13 16:57:22 +00:00
Callable * slot_callables = ( Callable * ) alloca ( sizeof ( Callable ) * s - > slot_map . size ( ) ) ;
uint32_t * slot_flags = ( uint32_t * ) alloca ( sizeof ( uint32_t ) * s - > slot_map . size ( ) ) ;
uint32_t slot_count = 0 ;
for ( const KeyValue < Callable , SignalData : : Slot > & slot_kv : s - > slot_map ) {
memnew_placement ( & slot_callables [ slot_count ] , Callable ( slot_kv . value . conn . callable ) ) ;
slot_flags [ slot_count ] = slot_kv . value . conn . flags ;
+ + slot_count ;
}
DEV_ASSERT ( slot_count = = s - > slot_map . size ( ) ) ;
// Disconnect all one-shot connections before emitting to prevent recursion.
for ( uint32_t i = 0 ; i < slot_count ; + + i ) {
bool disconnect = slot_flags [ i ] & CONNECT_ONE_SHOT ;
# ifdef TOOLS_ENABLED
if ( disconnect & & ( slot_flags [ i ] & CONNECT_PERSIST ) & & Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
// This signal was connected from the editor, and is being edited. Just don't disconnect for now.
disconnect = false ;
}
# endif
if ( disconnect ) {
_disconnect ( p_name , slot_callables [ i ] ) ;
2023-01-30 16:46:56 +00:00
}
}
2014-02-10 01:10:30 +00:00
2014-04-05 15:39:30 +00:00
OBJ_DEBUG_LOCK
2017-08-05 22:48:29 +00:00
Error err = OK ;
2024-03-13 16:57:22 +00:00
for ( uint32_t i = 0 ; i < slot_count ; + + i ) {
const Callable & callable = slot_callables [ i ] ;
const uint32_t & flags = slot_flags [ i ] ;
if ( ! callable . is_valid ( ) ) {
2020-01-13 21:11:03 +00:00
// Target might have been deleted during signal callback, this is expected and OK.
continue ;
}
2014-02-10 01:10:30 +00:00
2016-01-04 12:35:21 +00:00
const Variant * * args = p_args ;
int argc = p_argcount ;
2014-02-10 01:10:30 +00:00
2024-03-13 16:57:22 +00:00
if ( flags & CONNECT_DEFERRED ) {
MessageQueue : : get_singleton ( ) - > push_callablep ( callable , args , argc , true ) ;
2014-02-10 01:10:30 +00:00
} else {
2020-02-19 19:27:19 +00:00
Callable : : CallError ce ;
2020-01-22 00:45:06 +00:00
_emitting = true ;
2020-02-19 19:27:19 +00:00
Variant ret ;
2024-03-13 16:57:22 +00:00
callable . callp ( args , argc , ret , ce ) ;
2020-01-22 00:45:06 +00:00
_emitting = false ;
2017-08-05 22:48:29 +00:00
2020-02-19 19:27:19 +00:00
if ( ce . error ! = Callable : : CallError : : CALL_OK ) {
2018-09-20 22:05:39 +00:00
# ifdef DEBUG_ENABLED
2024-03-13 16:57:22 +00:00
if ( flags & CONNECT_PERSIST & & Engine : : get_singleton ( ) - > is_editor_hint ( ) & & ( script . is_null ( ) | | ! Ref < Script > ( script ) - > is_tool ( ) ) ) {
2018-09-20 22:05:39 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2018-09-20 22:05:39 +00:00
# endif
2024-03-13 16:57:22 +00:00
Object * target = callable . get_object ( ) ;
2023-09-26 19:58:57 +00:00
if ( ce . error = = Callable : : CallError : : CALL_ERROR_INVALID_METHOD & & target & & ! ClassDB : : class_exists ( target - > get_class_name ( ) ) ) {
2016-01-04 12:35:21 +00:00
//most likely object is not initialized yet, do not throw error.
} else {
2024-03-13 16:57:22 +00:00
ERR_PRINT ( " Error calling from signal ' " + String ( p_name ) + " ' to callable: " + Variant : : get_callable_error_text ( callable , args , argc , ce ) + " . " ) ;
2017-08-05 22:48:29 +00:00
err = ERR_METHOD_NOT_FOUND ;
2016-01-04 12:35:21 +00:00
}
}
2014-02-10 01:10:30 +00:00
}
}
2024-03-13 16:57:22 +00:00
for ( uint32_t i = 0 ; i < slot_count ; + + i ) {
slot_callables [ i ] . ~ Callable ( ) ;
2016-01-04 12:35:21 +00:00
}
2017-08-05 22:48:29 +00:00
return err ;
2016-01-04 12:35:21 +00:00
}
2014-02-10 01:10:30 +00:00
void Object : : _add_user_signal ( const String & p_name , const Array & p_args ) {
// this version of add_user_signal is meant to be used from scripts or external apis
// without access to ADD_SIGNAL in bind_methods
// added events are per instance, as opposed to the other ones, which are global
MethodInfo mi ;
mi . name = p_name ;
2023-12-24 12:44:21 +00:00
for ( const Variant & arg : p_args ) {
Dictionary d = arg ;
2014-02-10 01:10:30 +00:00
PropertyInfo param ;
2020-05-14 14:41:43 +00:00
if ( d . has ( " name " ) ) {
2014-02-10 01:10:30 +00:00
param . name = d [ " name " ] ;
2020-05-14 14:41:43 +00:00
}
if ( d . has ( " type " ) ) {
2014-02-10 01:10:30 +00:00
param . type = ( Variant : : Type ) ( int ) d [ " type " ] ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
mi . arguments . push_back ( param ) ;
}
add_user_signal ( mi ) ;
2024-04-18 08:22:59 +00:00
if ( signal_map . has ( p_name ) ) {
signal_map . getptr ( p_name ) - > removable = true ;
}
2014-02-10 01:10:30 +00:00
}
2022-08-05 18:35:08 +00:00
TypedArray < Dictionary > Object : : _get_signal_list ( ) const {
2016-01-01 13:27:25 +00:00
List < MethodInfo > signal_list ;
get_signal_list ( & signal_list ) ;
2022-08-05 18:35:08 +00:00
TypedArray < Dictionary > ret ;
2021-07-24 13:46:25 +00:00
for ( const MethodInfo & E : signal_list ) {
2021-07-16 03:45:57 +00:00
ret . push_back ( Dictionary ( E ) ) ;
2016-01-01 13:27:25 +00:00
}
return ret ;
2014-02-10 01:10:30 +00:00
}
2017-08-27 19:07:15 +00:00
2022-08-05 18:35:08 +00:00
TypedArray < Dictionary > Object : : _get_signal_connection_list ( const StringName & p_signal ) const {
2016-01-01 13:27:25 +00:00
List < Connection > conns ;
get_all_signal_connections ( & conns ) ;
2022-08-05 18:35:08 +00:00
TypedArray < Dictionary > ret ;
2016-01-01 13:27:25 +00:00
2021-07-24 13:46:25 +00:00
for ( const Connection & c : conns ) {
2020-02-19 19:27:19 +00:00
if ( c . signal . get_name ( ) = = p_signal ) {
ret . push_back ( c ) ;
2016-06-21 11:07:50 +00:00
}
2016-01-01 13:27:25 +00:00
}
return ret ;
2014-02-10 01:10:30 +00:00
}
2022-08-05 18:35:08 +00:00
TypedArray < Dictionary > Object : : _get_incoming_connections ( ) const {
TypedArray < Dictionary > ret ;
2024-04-15 13:18:34 +00:00
for ( const Object : : Connection & connection : connections ) {
ret . push_back ( connection ) ;
2017-06-15 13:31:57 +00:00
}
return ret ;
}
2019-11-10 05:59:44 +00:00
bool Object : : has_signal ( const StringName & p_name ) const {
if ( ! script . is_null ( ) ) {
Ref < Script > scr = script ;
if ( scr . is_valid ( ) & & scr - > has_script_signal ( p_name ) ) {
return true ;
}
}
if ( ClassDB : : has_signal ( get_class_name ( ) , p_name ) ) {
return true ;
}
if ( _has_user_signal ( p_name ) ) {
return true ;
}
return false ;
}
2014-02-10 01:10:30 +00:00
void Object : : get_signal_list ( List < MethodInfo > * p_signals ) const {
2015-06-24 16:29:23 +00:00
if ( ! script . is_null ( ) ) {
2019-04-19 20:03:00 +00:00
Ref < Script > scr = script ;
if ( scr . is_valid ( ) ) {
scr - > get_script_signal_list ( p_signals ) ;
}
2015-06-24 16:29:23 +00:00
}
2017-01-03 02:03:46 +00:00
ClassDB : : get_signal_list ( get_class_name ( ) , p_signals ) ;
2014-02-10 01:10:30 +00:00
//find maybe usersignals?
2022-05-08 08:09:19 +00:00
for ( const KeyValue < StringName , SignalData > & E : signal_map ) {
if ( ! E . value . user . name . is_empty ( ) ) {
2014-02-10 01:10:30 +00:00
//user signal
2022-05-08 08:09:19 +00:00
p_signals - > push_back ( E . value . user ) ;
2014-02-10 01:10:30 +00:00
}
}
}
2015-05-10 18:45:33 +00:00
void Object : : get_all_signal_connections ( List < Connection > * p_connections ) const {
2022-05-08 08:09:19 +00:00
for ( const KeyValue < StringName , SignalData > & E : signal_map ) {
const SignalData * s = & E . value ;
2015-05-10 18:45:33 +00:00
2023-01-30 16:46:56 +00:00
for ( const KeyValue < Callable , SignalData : : Slot > & slot_kv : s - > slot_map ) {
p_connections - > push_back ( slot_kv . value . conn ) ;
2015-05-10 18:45:33 +00:00
}
}
}
2014-02-10 01:10:30 +00:00
void Object : : get_signal_connection_list ( const StringName & p_signal , List < Connection > * p_connections ) const {
2020-02-19 19:27:19 +00:00
const SignalData * s = signal_map . getptr ( p_signal ) ;
2020-05-14 14:41:43 +00:00
if ( ! s ) {
2014-02-10 01:10:30 +00:00
return ; //nothing
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2023-01-30 16:46:56 +00:00
for ( const KeyValue < Callable , SignalData : : Slot > & slot_kv : s - > slot_map ) {
p_connections - > push_back ( slot_kv . value . conn ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
2019-08-16 20:30:31 +00:00
int Object : : get_persistent_signal_connection_count ( ) const {
int count = 0 ;
2016-06-04 16:17:56 +00:00
2022-05-08 08:09:19 +00:00
for ( const KeyValue < StringName , SignalData > & E : signal_map ) {
const SignalData * s = & E . value ;
2016-06-04 16:17:56 +00:00
2023-01-30 16:46:56 +00:00
for ( const KeyValue < Callable , SignalData : : Slot > & slot_kv : s - > slot_map ) {
if ( slot_kv . value . conn . flags & CONNECT_PERSIST ) {
2019-08-16 20:30:31 +00:00
count + = 1 ;
}
2016-06-04 16:17:56 +00:00
}
}
2019-08-16 20:30:31 +00:00
return count ;
2016-06-04 16:17:56 +00:00
}
2016-06-06 22:55:50 +00:00
void Object : : get_signals_connected_to_this ( List < Connection > * p_connections ) const {
2021-07-16 03:45:57 +00:00
for ( const Connection & E : connections ) {
p_connections - > push_back ( E ) ;
2016-06-07 05:39:40 +00:00
}
2016-06-06 22:55:50 +00:00
}
2022-07-28 20:56:41 +00:00
Error Object : : connect ( const StringName & p_signal , const Callable & p_callable , uint32_t p_flags ) {
2021-06-21 10:10:40 +00:00
ERR_FAIL_COND_V_MSG ( p_callable . is_null ( ) , ERR_INVALID_PARAMETER , " Cannot connect to ' " + p_signal + " ': the provided callable is null. " ) ;
2014-02-10 01:10:30 +00:00
2023-09-26 19:58:57 +00:00
if ( p_callable . is_standard ( ) ) {
// FIXME: This branch should probably removed in favor of the `is_valid()` branch, but there exist some classes
// that call `connect()` before they are fully registered with ClassDB. Until all such classes can be found
// and registered soon enough this branch is needed to allow `connect()` to succeed.
ERR_FAIL_NULL_V_MSG ( p_callable . get_object ( ) , ERR_INVALID_PARAMETER , " Cannot connect to ' " + p_signal + " ' to callable ' " + p_callable + " ': the callable object is null. " ) ;
} else {
ERR_FAIL_COND_V_MSG ( ! p_callable . is_valid ( ) , ERR_INVALID_PARAMETER , " Cannot connect to ' " + p_signal + " ': the provided callable is not valid: " + p_callable ) ;
}
2020-02-23 09:06:13 +00:00
2020-02-19 19:27:19 +00:00
SignalData * s = signal_map . getptr ( p_signal ) ;
2014-02-10 01:10:30 +00:00
if ( ! s ) {
2017-01-03 02:03:46 +00:00
bool signal_is_valid = ClassDB : : has_signal ( get_class_name ( ) , p_signal ) ;
2015-06-24 16:29:23 +00:00
//check in script
2018-11-27 22:55:37 +00:00
if ( ! signal_is_valid & & ! script . is_null ( ) ) {
if ( Ref < Script > ( script ) - > has_script_signal ( p_signal ) ) {
signal_is_valid = true ;
}
# ifdef TOOLS_ENABLED
else {
//allow connecting signals anyway if script is invalid, see issue #17070
if ( ! Ref < Script > ( script ) - > is_valid ( ) ) {
signal_is_valid = true ;
}
}
# endif
}
2015-06-24 16:29:23 +00:00
2020-02-19 19:27:19 +00:00
ERR_FAIL_COND_V_MSG ( ! signal_is_valid , ERR_INVALID_PARAMETER , " In Object of type ' " + String ( get_class ( ) ) + " ': Attempt to connect nonexistent signal ' " + p_signal + " ' to callable ' " + p_callable + " '. " ) ;
2019-08-15 02:57:49 +00:00
2020-02-19 19:27:19 +00:00
signal_map [ p_signal ] = SignalData ( ) ;
2014-02-10 01:10:30 +00:00
s = & signal_map [ p_signal ] ;
}
2020-10-09 21:41:53 +00:00
//compare with the base callable, so binds can be ignored
2023-12-13 16:06:14 +00:00
if ( s - > slot_map . has ( * p_callable . get_base_comparator ( ) ) ) {
2018-08-20 16:38:18 +00:00
if ( p_flags & CONNECT_REFERENCE_COUNTED ) {
2023-12-13 16:06:14 +00:00
s - > slot_map [ * p_callable . get_base_comparator ( ) ] . reference_count + + ;
2018-08-20 16:38:18 +00:00
return OK ;
} else {
2020-02-19 19:27:19 +00:00
ERR_FAIL_V_MSG ( ERR_INVALID_PARAMETER , " Signal ' " + p_signal + " ' is already connected to given callable ' " + p_callable + " ' in that object. " ) ;
2018-08-20 16:38:18 +00:00
}
2014-02-10 01:10:30 +00:00
}
2023-09-26 19:58:57 +00:00
Object * target_object = p_callable . get_object ( ) ;
2020-02-19 19:27:19 +00:00
SignalData : : Slot slot ;
2014-02-10 01:10:30 +00:00
Connection conn ;
2023-12-13 16:06:14 +00:00
conn . callable = p_callable ;
2020-02-19 19:27:19 +00:00
conn . signal = : : Signal ( this , p_signal ) ;
2014-02-10 01:10:30 +00:00
conn . flags = p_flags ;
slot . conn = conn ;
2023-09-26 19:58:57 +00:00
if ( target_object ) {
slot . cE = target_object - > connections . push_back ( conn ) ;
}
2018-08-20 16:38:18 +00:00
if ( p_flags & CONNECT_REFERENCE_COUNTED ) {
slot . reference_count = 1 ;
}
2020-10-09 21:41:53 +00:00
//use callable version as key, so binds can be ignored
2023-12-13 16:06:14 +00:00
s - > slot_map [ * p_callable . get_base_comparator ( ) ] = slot ;
2014-09-15 14:33:30 +00:00
return OK ;
2014-02-10 01:10:30 +00:00
}
2020-02-19 19:27:19 +00:00
bool Object : : is_connected ( const StringName & p_signal , const Callable & p_callable ) const {
2024-04-27 09:56:39 +00:00
ERR_FAIL_COND_V_MSG ( p_callable . is_null ( ) , false , " Cannot determine if connected to ' " + p_signal + " ': the provided callable is null. " ) ; // Should use `is_null`, see note in `connect` about the use of `is_valid`.
2020-02-19 19:27:19 +00:00
const SignalData * s = signal_map . getptr ( p_signal ) ;
2014-02-10 01:10:30 +00:00
if ( ! s ) {
2017-01-03 02:03:46 +00:00
bool signal_is_valid = ClassDB : : has_signal ( get_class_name ( ) , p_signal ) ;
2020-05-14 14:41:43 +00:00
if ( signal_is_valid ) {
2014-02-10 01:10:30 +00:00
return false ;
2020-05-14 14:41:43 +00:00
}
2015-12-11 10:53:40 +00:00
2020-05-14 14:41:43 +00:00
if ( ! script . is_null ( ) & & Ref < Script > ( script ) - > has_script_signal ( p_signal ) ) {
2015-12-11 10:53:40 +00:00
return false ;
2020-05-14 14:41:43 +00:00
}
2015-12-11 10:53:40 +00:00
2019-08-15 02:57:49 +00:00
ERR_FAIL_V_MSG ( false , " Nonexistent signal: " + p_signal + " . " ) ;
2014-02-10 01:10:30 +00:00
}
2023-12-13 16:06:14 +00:00
return s - > slot_map . has ( * p_callable . get_base_comparator ( ) ) ;
2014-02-10 01:10:30 +00:00
}
2020-02-19 19:27:19 +00:00
void Object : : disconnect ( const StringName & p_signal , const Callable & p_callable ) {
_disconnect ( p_signal , p_callable ) ;
}
2023-05-03 23:20:15 +00:00
bool Object : : _disconnect ( const StringName & p_signal , const Callable & p_callable , bool p_force ) {
2024-04-27 09:56:39 +00:00
ERR_FAIL_COND_V_MSG ( p_callable . is_null ( ) , false , " Cannot disconnect from ' " + p_signal + " ': the provided callable is null. " ) ; // Should use `is_null`, see note in `connect` about the use of `is_valid`.
2020-02-23 09:06:13 +00:00
2020-02-19 19:27:19 +00:00
SignalData * s = signal_map . getptr ( p_signal ) ;
2020-12-31 10:33:39 +00:00
if ( ! s ) {
bool signal_is_valid = ClassDB : : has_signal ( get_class_name ( ) , p_signal ) | |
2021-10-28 13:19:35 +00:00
( ! script . is_null ( ) & & Ref < Script > ( script ) - > has_script_signal ( p_signal ) ) ;
2023-05-03 23:20:15 +00:00
ERR_FAIL_COND_V_MSG ( signal_is_valid , false , " Attempt to disconnect a nonexistent connection from ' " + to_string ( ) + " '. Signal: ' " + p_signal + " ', callable: ' " + p_callable + " '. " ) ;
2020-12-31 10:33:39 +00:00
}
2023-09-09 14:11:33 +00:00
ERR_FAIL_NULL_V_MSG ( s , false , vformat ( " Disconnecting nonexistent signal '%s' in %s. " , p_signal , to_string ( ) ) ) ;
2014-02-10 01:10:30 +00:00
2023-05-17 22:46:13 +00:00
ERR_FAIL_COND_V_MSG ( ! s - > slot_map . has ( * p_callable . get_base_comparator ( ) ) , false , " Attempt to disconnect a nonexistent connection from ' " + to_string ( ) + " '. Signal: ' " + p_signal + " ', callable: ' " + p_callable + " '. " ) ;
2016-07-07 22:32:28 +00:00
2021-11-17 08:29:29 +00:00
SignalData : : Slot * slot = & s - > slot_map [ * p_callable . get_base_comparator ( ) ] ;
2018-08-20 16:38:18 +00:00
2018-08-20 19:35:36 +00:00
if ( ! p_force ) {
slot - > reference_count - - ; // by default is zero, if it was not referenced it will go below it
2021-03-28 17:00:13 +00:00
if ( slot - > reference_count > 0 ) {
2023-05-03 23:20:15 +00:00
return false ;
2018-08-20 19:35:36 +00:00
}
2018-08-20 16:38:18 +00:00
}
2020-02-23 09:06:13 +00:00
2023-09-26 19:58:57 +00:00
if ( slot - > cE ) {
Object * target_object = p_callable . get_object ( ) ;
if ( target_object ) {
target_object - > connections . erase ( slot - > cE ) ;
}
}
2020-10-09 21:41:53 +00:00
s - > slot_map . erase ( * p_callable . get_base_comparator ( ) ) ;
2014-02-10 01:10:30 +00:00
2020-12-15 12:04:21 +00:00
if ( s - > slot_map . is_empty ( ) & & ClassDB : : has_signal ( get_class_name ( ) , p_signal ) ) {
2014-02-10 01:10:30 +00:00
//not user signal, delete
signal_map . erase ( p_signal ) ;
}
2023-05-03 23:20:15 +00:00
return true ;
2014-02-10 01:10:30 +00:00
}
2022-06-14 13:28:59 +00:00
void Object : : _set_bind ( const StringName & p_set , const Variant & p_value ) {
2014-02-10 01:10:30 +00:00
set ( p_set , p_value ) ;
}
2022-06-14 13:28:59 +00:00
Variant Object : : _get_bind ( const StringName & p_name ) const {
2014-02-10 01:10:30 +00:00
return get ( p_name ) ;
}
2017-05-30 20:20:15 +00:00
void Object : : _set_indexed_bind ( const NodePath & p_name , const Variant & p_value ) {
set_indexed ( p_name . get_as_property_path ( ) . get_subnames ( ) , p_value ) ;
}
Variant Object : : _get_indexed_bind ( const NodePath & p_name ) const {
return get_indexed ( p_name . get_as_property_path ( ) . get_subnames ( ) ) ;
}
2017-01-03 02:03:46 +00:00
void Object : : initialize_class ( ) {
2014-02-10 01:10:30 +00:00
static bool initialized = false ;
2020-05-14 14:41:43 +00:00
if ( initialized ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2017-01-03 02:03:46 +00:00
ClassDB : : _add_class < Object > ( ) ;
2014-02-10 01:10:30 +00:00
_bind_methods ( ) ;
2024-01-30 20:03:28 +00:00
_bind_compatibility_methods ( ) ;
2014-02-10 01:10:30 +00:00
initialized = true ;
}
2020-08-07 11:17:12 +00:00
String Object : : tr ( const StringName & p_message , const StringName & p_context ) const {
2020-05-14 14:41:43 +00:00
if ( ! _can_translate | | ! TranslationServer : : get_singleton ( ) ) {
2014-02-10 01:10:30 +00:00
return p_message ;
2020-05-14 14:41:43 +00:00
}
2023-03-17 00:58:30 +00:00
2024-03-15 15:00:20 +00:00
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) | | Engine : : get_singleton ( ) - > is_project_manager_hint ( ) ) {
2023-12-15 23:56:06 +00:00
String tr_msg = TranslationServer : : get_singleton ( ) - > extractable_translate ( p_message , p_context ) ;
2024-03-14 19:09:39 +00:00
if ( ! tr_msg . is_empty ( ) & & tr_msg ! = p_message ) {
2023-12-15 23:56:06 +00:00
return tr_msg ;
}
2023-03-17 00:58:30 +00:00
return TranslationServer : : get_singleton ( ) - > tool_translate ( p_message , p_context ) ;
}
2023-12-15 23:56:06 +00:00
return TranslationServer : : get_singleton ( ) - > translate ( p_message , p_context ) ;
2020-07-16 08:52:06 +00:00
}
2014-02-10 01:10:30 +00:00
2020-07-16 08:52:06 +00:00
String Object : : tr_n ( const StringName & p_message , const StringName & p_message_plural , int p_n , const StringName & p_context ) const {
if ( ! _can_translate | | ! TranslationServer : : get_singleton ( ) ) {
// Return message based on English plural rule if translation is not possible.
if ( p_n = = 1 ) {
return p_message ;
}
2020-08-07 11:17:12 +00:00
return p_message_plural ;
2020-07-16 08:52:06 +00:00
}
2023-03-17 00:58:30 +00:00
2024-03-15 15:00:20 +00:00
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) | | Engine : : get_singleton ( ) - > is_project_manager_hint ( ) ) {
2023-12-15 23:56:06 +00:00
String tr_msg = TranslationServer : : get_singleton ( ) - > extractable_translate_plural ( p_message , p_message_plural , p_n , p_context ) ;
2024-03-14 19:09:39 +00:00
if ( ! tr_msg . is_empty ( ) & & tr_msg ! = p_message & & tr_msg ! = p_message_plural ) {
2023-12-15 23:56:06 +00:00
return tr_msg ;
}
2023-03-17 00:58:30 +00:00
return TranslationServer : : get_singleton ( ) - > tool_translate_plural ( p_message , p_message_plural , p_n , p_context ) ;
}
2023-12-15 23:56:06 +00:00
return TranslationServer : : get_singleton ( ) - > translate_plural ( p_message , p_message_plural , p_n , p_context ) ;
2014-02-10 01:10:30 +00:00
}
2015-06-22 03:03:19 +00:00
void Object : : _clear_internal_resource_paths ( const Variant & p_var ) {
switch ( p_var . get_type ( ) ) {
case Variant : : OBJECT : {
2022-05-02 23:43:50 +00:00
Ref < Resource > r = p_var ;
2020-05-14 14:41:43 +00:00
if ( ! r . is_valid ( ) ) {
2015-06-22 03:03:19 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2015-06-22 03:03:19 +00:00
2021-07-10 19:17:41 +00:00
if ( ! r - > is_built_in ( ) ) {
2015-06-22 03:03:19 +00:00
return ; //not an internal resource
2020-05-14 14:41:43 +00:00
}
2015-06-22 03:03:19 +00:00
Object * object = p_var ;
2020-05-14 14:41:43 +00:00
if ( ! object ) {
2015-06-22 03:03:19 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2015-06-22 03:03:19 +00:00
r - > set_path ( " " ) ;
r - > clear_internal_resource_paths ( ) ;
} break ;
case Variant : : ARRAY : {
Array a = p_var ;
2023-12-24 12:44:21 +00:00
for ( const Variant & var : a ) {
_clear_internal_resource_paths ( var ) ;
2015-06-22 03:03:19 +00:00
}
} break ;
case Variant : : DICTIONARY : {
Dictionary d = p_var ;
List < Variant > keys ;
d . get_key_list ( & keys ) ;
2021-07-24 13:46:25 +00:00
for ( const Variant & E : keys ) {
2021-07-16 03:45:57 +00:00
_clear_internal_resource_paths ( E ) ;
_clear_internal_resource_paths ( d [ E ] ) ;
2015-06-22 03:03:19 +00:00
}
} break ;
2019-04-09 15:08:36 +00:00
default : {
}
2015-06-22 03:03:19 +00:00
}
}
2017-06-25 20:30:28 +00:00
# ifdef TOOLS_ENABLED
void Object : : editor_set_section_unfold ( const String & p_section , bool p_unfolded ) {
set_edited ( true ) ;
2020-05-14 14:41:43 +00:00
if ( p_unfolded ) {
2017-06-25 20:30:28 +00:00
editor_section_folding . insert ( p_section ) ;
2020-05-14 14:41:43 +00:00
} else {
2017-06-25 20:30:28 +00:00
editor_section_folding . erase ( p_section ) ;
2020-05-14 14:41:43 +00:00
}
2017-06-25 20:30:28 +00:00
}
bool Object : : editor_is_section_unfolded ( const String & p_section ) {
return editor_section_folding . has ( p_section ) ;
}
# endif
2015-06-22 03:03:19 +00:00
void Object : : clear_internal_resource_paths ( ) {
List < PropertyInfo > pinfo ;
get_property_list ( & pinfo ) ;
2021-07-24 13:46:25 +00:00
for ( const PropertyInfo & E : pinfo ) {
2021-07-16 03:45:57 +00:00
_clear_internal_resource_paths ( get ( E . name ) ) ;
2015-06-22 03:03:19 +00:00
}
}
2021-02-10 20:18:45 +00:00
void Object : : notify_property_list_changed ( ) {
emit_signal ( CoreStringNames : : get_singleton ( ) - > property_list_changed ) ;
}
2014-02-10 01:10:30 +00:00
void Object : : _bind_methods ( ) {
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_class " ) , & Object : : get_class ) ;
2019-06-26 13:57:13 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_class " , " class " ) , & Object : : is_class ) ;
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " set " , " property " , " value " ) , & Object : : _set_bind ) ;
2017-08-09 11:19:41 +00:00
ClassDB : : bind_method ( D_METHOD ( " get " , " property " ) , & Object : : _get_bind ) ;
2022-10-12 08:27:44 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_indexed " , " property_path " , " value " ) , & Object : : _set_indexed_bind ) ;
ClassDB : : bind_method ( D_METHOD ( " get_indexed " , " property_path " ) , & Object : : _get_indexed_bind ) ;
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_property_list " ) , & Object : : _get_property_list_bind ) ;
ClassDB : : bind_method ( D_METHOD ( " get_method_list " ) , & Object : : _get_method_list_bind ) ;
2022-11-29 15:55:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " property_can_revert " , " property " ) , & Object : : property_can_revert ) ;
ClassDB : : bind_method ( D_METHOD ( " property_get_revert " , " property " ) , & Object : : property_get_revert ) ;
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " notification " , " what " , " reversed " ) , & Object : : notification , DEFVAL ( false ) ) ;
2019-04-10 05:07:40 +00:00
ClassDB : : bind_method ( D_METHOD ( " to_string " ) , & Object : : to_string ) ;
2017-08-07 10:17:31 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_instance_id " ) , & Object : : get_instance_id ) ;
2017-02-13 11:47:24 +00:00
2017-08-09 11:19:41 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_script " , " script " ) , & Object : : set_script ) ;
ClassDB : : bind_method ( D_METHOD ( " get_script " ) , & Object : : get_script ) ;
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_meta " , " name " , " value " ) , & Object : : set_meta ) ;
2019-04-26 17:23:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " remove_meta " , " name " ) , & Object : : remove_meta ) ;
2022-02-27 21:03:33 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_meta " , " name " , " default " ) , & Object : : get_meta , DEFVAL ( Variant ( ) ) ) ;
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " has_meta " , " name " ) , & Object : : has_meta ) ;
ClassDB : : bind_method ( D_METHOD ( " get_meta_list " ) , & Object : : _get_meta_list_bind ) ;
2014-02-10 01:10:30 +00:00
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_user_signal " , " signal " , " arguments " ) , & Object : : _add_user_signal , DEFVAL ( Array ( ) ) ) ;
ClassDB : : bind_method ( D_METHOD ( " has_user_signal " , " signal " ) , & Object : : _has_user_signal ) ;
2024-04-18 08:22:59 +00:00
ClassDB : : bind_method ( D_METHOD ( " remove_user_signal " , " signal " ) , & Object : : _remove_user_signal ) ;
2014-02-10 01:10:30 +00:00
{
MethodInfo mi ;
mi . name = " emit_signal " ;
2020-02-20 21:58:05 +00:00
mi . arguments . push_back ( PropertyInfo ( Variant : : STRING_NAME , " signal " ) ) ;
2014-02-10 01:10:30 +00:00
2020-01-02 08:31:43 +00:00
ClassDB : : bind_vararg_method ( METHOD_FLAGS_DEFAULT , " emit_signal " , & Object : : _emit_signal , mi , varray ( ) , false ) ;
2014-02-10 01:10:30 +00:00
}
{
MethodInfo mi ;
mi . name = " call " ;
2020-02-20 21:58:05 +00:00
mi . arguments . push_back ( PropertyInfo ( Variant : : STRING_NAME , " method " ) ) ;
2014-02-10 01:10:30 +00:00
2017-08-09 11:20:24 +00:00
ClassDB : : bind_vararg_method ( METHOD_FLAGS_DEFAULT , " call " , & Object : : _call_bind , mi ) ;
2014-02-10 01:10:30 +00:00
}
{
MethodInfo mi ;
mi . name = " call_deferred " ;
2020-02-20 21:58:05 +00:00
mi . arguments . push_back ( PropertyInfo ( Variant : : STRING_NAME , " method " ) ) ;
2014-02-10 01:10:30 +00:00
2020-01-02 08:31:43 +00:00
ClassDB : : bind_vararg_method ( METHOD_FLAGS_DEFAULT , " call_deferred " , & Object : : _call_deferred_bind , mi , varray ( ) , false ) ;
2014-02-10 01:10:30 +00:00
}
2018-11-16 11:49:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_deferred " , " property " , " value " ) , & Object : : set_deferred ) ;
2017-08-09 11:19:41 +00:00
ClassDB : : bind_method ( D_METHOD ( " callv " , " method " , " arg_array " ) , & Object : : callv ) ;
2014-02-10 01:10:30 +00:00
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " has_method " , " method " ) , & Object : : has_method ) ;
2014-02-10 01:10:30 +00:00
2024-01-28 14:16:09 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_method_argument_count " , " method " ) , & Object : : _get_method_argument_count_bind ) ;
2019-11-10 05:59:44 +00:00
ClassDB : : bind_method ( D_METHOD ( " has_signal " , " signal " ) , & Object : : has_signal ) ;
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_signal_list " ) , & Object : : _get_signal_list ) ;
ClassDB : : bind_method ( D_METHOD ( " get_signal_connection_list " , " signal " ) , & Object : : _get_signal_connection_list ) ;
2017-06-15 13:31:57 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_incoming_connections " ) , & Object : : _get_incoming_connections ) ;
2014-02-10 01:10:30 +00:00
2022-07-28 20:56:41 +00:00
ClassDB : : bind_method ( D_METHOD ( " connect " , " signal " , " callable " , " flags " ) , & Object : : connect , DEFVAL ( 0 ) ) ;
2020-02-19 19:27:19 +00:00
ClassDB : : bind_method ( D_METHOD ( " disconnect " , " signal " , " callable " ) , & Object : : disconnect ) ;
ClassDB : : bind_method ( D_METHOD ( " is_connected " , " signal " , " callable " ) , & Object : : is_connected ) ;
2014-02-10 01:10:30 +00:00
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_block_signals " , " enable " ) , & Object : : set_block_signals ) ;
ClassDB : : bind_method ( D_METHOD ( " is_blocking_signals " ) , & Object : : is_blocking_signals ) ;
2021-02-10 20:18:45 +00:00
ClassDB : : bind_method ( D_METHOD ( " notify_property_list_changed " ) , & Object : : notify_property_list_changed ) ;
2014-02-10 01:10:30 +00:00
2017-08-18 20:29:15 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_message_translation " , " enable " ) , & Object : : set_message_translation ) ;
ClassDB : : bind_method ( D_METHOD ( " can_translate_messages " ) , & Object : : can_translate_messages ) ;
2024-01-30 20:03:28 +00:00
ClassDB : : bind_method ( D_METHOD ( " tr " , " message " , " context " ) , & Object : : tr , DEFVAL ( StringName ( ) ) ) ;
ClassDB : : bind_method ( D_METHOD ( " tr_n " , " message " , " plural_message " , " n " , " context " ) , & Object : : tr_n , DEFVAL ( StringName ( ) ) ) ;
2014-02-10 01:10:30 +00:00
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_queued_for_deletion " ) , & Object : : is_queued_for_deletion ) ;
2023-04-10 16:45:53 +00:00
ClassDB : : bind_method ( D_METHOD ( " cancel_free " ) , & Object : : cancel_free ) ;
2015-03-28 17:34:28 +00:00
2017-01-03 02:03:46 +00:00
ClassDB : : add_virtual_method ( " Object " , MethodInfo ( " free " ) , false ) ;
2015-04-13 01:22:44 +00:00
2014-02-10 01:10:30 +00:00
ADD_SIGNAL ( MethodInfo ( " script_changed " ) ) ;
2021-02-10 20:18:45 +00:00
ADD_SIGNAL ( MethodInfo ( " property_list_changed " ) ) ;
2014-02-10 01:10:30 +00:00
2021-08-22 01:52:44 +00:00
# define BIND_OBJ_CORE_METHOD(m_method) \
: : ClassDB : : add_virtual_method ( get_class_static ( ) , m_method , true , Vector < String > ( ) , true ) ;
2023-01-25 02:51:32 +00:00
MethodInfo notification_mi ( " _notification " , PropertyInfo ( Variant : : INT , " what " ) ) ;
notification_mi . arguments_metadata . push_back ( GodotTypeInfo : : Metadata : : METADATA_INT_IS_INT32 ) ;
BIND_OBJ_CORE_METHOD ( notification_mi ) ;
2021-08-22 01:52:44 +00:00
BIND_OBJ_CORE_METHOD ( MethodInfo ( Variant : : BOOL , " _set " , PropertyInfo ( Variant : : STRING_NAME , " property " ) , PropertyInfo ( Variant : : NIL , " value " ) ) ) ;
2014-02-10 01:10:30 +00:00
# ifdef TOOLS_ENABLED
2020-02-20 21:58:05 +00:00
MethodInfo miget ( " _get " , PropertyInfo ( Variant : : STRING_NAME , " property " ) ) ;
2017-07-18 15:39:46 +00:00
miget . return_val . name = " Variant " ;
2018-05-30 02:30:01 +00:00
miget . return_val . usage | = PROPERTY_USAGE_NIL_IS_VARIANT ;
2021-08-22 01:52:44 +00:00
BIND_OBJ_CORE_METHOD ( miget ) ;
2014-02-10 01:10:30 +00:00
2022-08-05 18:35:08 +00:00
MethodInfo plget ( " _get_property_list " ) ;
plget . return_val . type = Variant : : ARRAY ;
plget . return_val . hint = PROPERTY_HINT_ARRAY_TYPE ;
plget . return_val . hint_string = " Dictionary " ;
BIND_OBJ_CORE_METHOD ( plget ) ;
2023-04-07 12:01:57 +00:00
BIND_OBJ_CORE_METHOD ( MethodInfo ( Variant : : NIL , " _validate_property " , PropertyInfo ( Variant : : DICTIONARY , " property " ) ) ) ;
2022-08-12 18:43:14 +00:00
BIND_OBJ_CORE_METHOD ( MethodInfo ( Variant : : BOOL , " _property_can_revert " , PropertyInfo ( Variant : : STRING_NAME , " property " ) ) ) ;
MethodInfo mipgr ( " _property_get_revert " , PropertyInfo ( Variant : : STRING_NAME , " property " ) ) ;
mipgr . return_val . name = " Variant " ;
mipgr . return_val . usage | = PROPERTY_USAGE_NIL_IS_VARIANT ;
BIND_OBJ_CORE_METHOD ( mipgr ) ;
2014-02-10 01:10:30 +00:00
# endif
2021-08-22 01:52:44 +00:00
BIND_OBJ_CORE_METHOD ( MethodInfo ( " _init " ) ) ;
BIND_OBJ_CORE_METHOD ( MethodInfo ( Variant : : STRING , " _to_string " ) ) ;
2014-02-10 01:10:30 +00:00
BIND_CONSTANT ( NOTIFICATION_POSTINITIALIZE ) ;
BIND_CONSTANT ( NOTIFICATION_PREDELETE ) ;
2024-02-02 09:35:53 +00:00
BIND_CONSTANT ( NOTIFICATION_EXTENSION_RELOADED ) ;
2014-02-10 01:10:30 +00:00
2017-08-20 15:45:01 +00:00
BIND_ENUM_CONSTANT ( CONNECT_DEFERRED ) ;
BIND_ENUM_CONSTANT ( CONNECT_PERSIST ) ;
2022-09-01 13:44:42 +00:00
BIND_ENUM_CONSTANT ( CONNECT_ONE_SHOT ) ;
2018-08-20 16:38:18 +00:00
BIND_ENUM_CONSTANT ( CONNECT_REFERENCE_COUNTED ) ;
2014-02-10 01:10:30 +00:00
}
2018-11-16 11:49:26 +00:00
void Object : : set_deferred ( const StringName & p_property , const Variant & p_value ) {
MessageQueue : : get_singleton ( ) - > push_set ( this , p_property , p_value ) ;
}
2014-02-10 01:10:30 +00:00
void Object : : set_block_signals ( bool p_block ) {
_block_signals = p_block ;
}
bool Object : : is_blocking_signals ( ) const {
return _block_signals ;
}
2015-12-05 17:18:22 +00:00
Variant : : Type Object : : get_static_property_type ( const StringName & p_property , bool * r_valid ) const {
bool valid ;
2017-01-03 02:03:46 +00:00
Variant : : Type t = ClassDB : : get_property_type ( get_class_name ( ) , p_property , & valid ) ;
2015-12-05 17:18:22 +00:00
if ( valid ) {
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2015-12-05 17:18:22 +00:00
* r_valid = true ;
2020-05-14 14:41:43 +00:00
}
2015-12-05 17:18:22 +00:00
return t ;
}
if ( get_script_instance ( ) ) {
return get_script_instance ( ) - > get_property_type ( p_property , r_valid ) ;
}
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2015-12-05 17:18:22 +00:00
* r_valid = false ;
2020-05-14 14:41:43 +00:00
}
2015-12-05 17:18:22 +00:00
return Variant : : NIL ;
}
2017-05-30 20:20:15 +00:00
Variant : : Type Object : : get_static_property_type_indexed ( const Vector < StringName > & p_path , bool * r_valid ) const {
2017-11-24 13:26:32 +00:00
if ( p_path . size ( ) = = 0 ) {
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2017-11-24 13:26:32 +00:00
* r_valid = false ;
2020-05-14 14:41:43 +00:00
}
2017-11-24 13:26:32 +00:00
return Variant : : NIL ;
}
2017-05-30 20:20:15 +00:00
bool valid = false ;
Variant : : Type t = get_static_property_type ( p_path [ 0 ] , & valid ) ;
if ( ! valid ) {
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2017-05-30 20:20:15 +00:00
* r_valid = false ;
2020-05-14 14:41:43 +00:00
}
2017-05-30 20:20:15 +00:00
return Variant : : NIL ;
}
2020-02-19 19:27:19 +00:00
Callable : : CallError ce ;
2020-11-09 03:19:09 +00:00
Variant check ;
Variant : : construct ( t , check , nullptr , 0 , ce ) ;
2017-05-30 20:20:15 +00:00
for ( int i = 1 ; i < p_path . size ( ) ; i + + ) {
if ( check . get_type ( ) = = Variant : : OBJECT | | check . get_type ( ) = = Variant : : DICTIONARY | | check . get_type ( ) = = Variant : : ARRAY ) {
2021-03-12 13:35:16 +00:00
// We cannot be sure about the type of properties this type can have
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2017-05-30 20:20:15 +00:00
* r_valid = false ;
2020-05-14 14:41:43 +00:00
}
2017-05-30 20:20:15 +00:00
return Variant : : NIL ;
}
2020-11-07 01:29:22 +00:00
check = check . get_named ( p_path [ i ] , valid ) ;
2017-05-30 20:20:15 +00:00
if ( ! valid ) {
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2017-05-30 20:20:15 +00:00
* r_valid = false ;
2020-05-14 14:41:43 +00:00
}
2017-05-30 20:20:15 +00:00
return Variant : : NIL ;
}
}
2020-05-14 14:41:43 +00:00
if ( r_valid ) {
2017-05-30 20:20:15 +00:00
* r_valid = true ;
2020-05-14 14:41:43 +00:00
}
2017-05-30 20:20:15 +00:00
return check . get_type ( ) ;
}
2015-03-28 17:34:28 +00:00
bool Object : : is_queued_for_deletion ( ) const {
return _is_queued_for_deletion ;
}
2014-02-10 01:10:30 +00:00
# ifdef TOOLS_ENABLED
void Object : : set_edited ( bool p_edited ) {
_edited = p_edited ;
2016-05-27 17:18:40 +00:00
_edited_version + + ;
2014-02-10 01:10:30 +00:00
}
bool Object : : is_edited ( ) const {
return _edited ;
}
2016-05-27 17:18:40 +00:00
uint32_t Object : : get_edited_version ( ) const {
return _edited_version ;
}
2014-02-10 01:10:30 +00:00
# endif
2023-06-09 17:30:39 +00:00
StringName Object : : get_class_name_for_extension ( const GDExtension * p_library ) const {
2024-01-02 20:10:54 +00:00
# ifdef TOOLS_ENABLED
// If this is the library this extension comes from and it's a placeholder, we
// have to return the closest native parent's class name, so that it doesn't try to
// use this like the real object.
if ( unlikely ( _extension & & _extension - > library = = p_library & & _extension - > is_placeholder ) ) {
const StringName * class_name = _get_class_namev ( ) ;
return * class_name ;
}
# endif
2023-06-09 17:30:39 +00:00
// Only return the class name per the extension if it matches the given p_library.
if ( _extension & & _extension - > library = = p_library ) {
return _extension - > class_name ;
}
// Extensions only have wrapper classes for classes exposed in ClassDB.
const StringName * class_name = _get_class_namev ( ) ;
if ( ClassDB : : is_class_exposed ( * class_name ) ) {
return * class_name ;
}
// Find the nearest parent class that's exposed.
StringName parent_class = ClassDB : : get_parent_class ( * class_name ) ;
while ( parent_class ! = StringName ( ) ) {
if ( ClassDB : : is_class_exposed ( parent_class ) ) {
return parent_class ;
}
parent_class = ClassDB : : get_parent_class ( parent_class ) ;
}
return SNAME ( " Object " ) ;
}
2022-12-07 11:11:28 +00:00
void Object : : set_instance_binding ( void * p_token , void * p_binding , const GDExtensionInstanceBindingCallbacks * p_callbacks ) {
2023-08-05 01:34:14 +00:00
// This is only meant to be used on creation by the binder, but we also
// need to account for reloading (where the 'binding' will be cleared).
ERR_FAIL_COND ( _instance_bindings ! = nullptr & & _instance_bindings [ 0 ] . binding ! = nullptr ) ;
if ( _instance_bindings = = nullptr ) {
_instance_bindings = ( InstanceBinding * ) memalloc ( sizeof ( InstanceBinding ) ) ;
_instance_binding_count = 1 ;
}
2021-07-15 14:41:57 +00:00
_instance_bindings [ 0 ] . binding = p_binding ;
_instance_bindings [ 0 ] . free_callback = p_callbacks - > free_callback ;
_instance_bindings [ 0 ] . reference_callback = p_callbacks - > reference_callback ;
_instance_bindings [ 0 ] . token = p_token ;
}
2022-12-07 11:11:28 +00:00
void * Object : : get_instance_binding ( void * p_token , const GDExtensionInstanceBindingCallbacks * p_callbacks ) {
2021-07-08 19:16:02 +00:00
void * binding = nullptr ;
_instance_binding_mutex . lock ( ) ;
for ( uint32_t i = 0 ; i < _instance_binding_count ; i + + ) {
if ( _instance_bindings [ i ] . token = = p_token ) {
binding = _instance_bindings [ i ] . binding ;
break ;
2018-02-22 14:34:08 +00:00
}
2017-07-16 15:39:23 +00:00
}
2023-02-16 15:38:57 +00:00
if ( unlikely ( ! binding & & p_callbacks ) ) {
2021-07-08 19:16:02 +00:00
uint32_t current_size = next_power_of_2 ( _instance_binding_count ) ;
uint32_t new_size = next_power_of_2 ( _instance_binding_count + 1 ) ;
2017-07-16 15:39:23 +00:00
2021-07-08 19:16:02 +00:00
if ( current_size = = 0 | | new_size > current_size ) {
_instance_bindings = ( InstanceBinding * ) memrealloc ( _instance_bindings , new_size * sizeof ( InstanceBinding ) ) ;
}
2017-07-16 15:39:23 +00:00
2021-07-08 19:16:02 +00:00
_instance_bindings [ _instance_binding_count ] . free_callback = p_callbacks - > free_callback ;
_instance_bindings [ _instance_binding_count ] . reference_callback = p_callbacks - > reference_callback ;
_instance_bindings [ _instance_binding_count ] . token = p_token ;
2019-02-03 05:35:22 +00:00
2021-07-08 19:16:02 +00:00
binding = p_callbacks - > create_callback ( p_token , this ) ;
_instance_bindings [ _instance_binding_count ] . binding = binding ;
2023-08-05 01:34:14 +00:00
# ifdef TOOLS_ENABLED
if ( ! _extension & & Engine : : get_singleton ( ) - > is_extension_reloading_enabled ( ) ) {
GDExtensionManager : : get_singleton ( ) - > track_instance_binding ( p_token , this ) ;
}
# endif
2021-07-08 19:16:02 +00:00
_instance_binding_count + + ;
}
_instance_binding_mutex . unlock ( ) ;
return binding ;
2019-02-04 19:39:02 +00:00
}
2021-08-16 15:16:36 +00:00
bool Object : : has_instance_binding ( void * p_token ) {
bool found = false ;
_instance_binding_mutex . lock ( ) ;
for ( uint32_t i = 0 ; i < _instance_binding_count ; i + + ) {
if ( _instance_bindings [ i ] . token = = p_token ) {
found = true ;
break ;
}
}
_instance_binding_mutex . unlock ( ) ;
return found ;
}
2023-08-05 01:34:14 +00:00
void Object : : free_instance_binding ( void * p_token ) {
bool found = false ;
_instance_binding_mutex . lock ( ) ;
for ( uint32_t i = 0 ; i < _instance_binding_count ; i + + ) {
if ( ! found & & _instance_bindings [ i ] . token = = p_token ) {
if ( _instance_bindings [ i ] . free_callback ) {
_instance_bindings [ i ] . free_callback ( _instance_bindings [ i ] . token , this , _instance_bindings [ i ] . binding ) ;
}
found = true ;
}
if ( found ) {
if ( i + 1 < _instance_binding_count ) {
_instance_bindings [ i ] = _instance_bindings [ i + 1 ] ;
} else {
_instance_bindings [ i ] = { nullptr } ;
}
}
}
if ( found ) {
_instance_binding_count - - ;
}
_instance_binding_mutex . unlock ( ) ;
}
2023-10-04 17:13:07 +00:00
# ifdef TOOLS_ENABLED
2023-08-05 01:34:14 +00:00
void Object : : clear_internal_extension ( ) {
ERR_FAIL_NULL ( _extension ) ;
// Free the instance inside the GDExtension.
if ( _extension - > free_instance ) {
_extension - > free_instance ( _extension - > class_userdata , _extension_instance ) ;
}
_extension = nullptr ;
_extension_instance = nullptr ;
// Clear the instance bindings.
_instance_binding_mutex . lock ( ) ;
2024-01-02 20:10:54 +00:00
if ( _instance_bindings ) {
if ( _instance_bindings [ 0 ] . free_callback ) {
_instance_bindings [ 0 ] . free_callback ( _instance_bindings [ 0 ] . token , this , _instance_bindings [ 0 ] . binding ) ;
}
_instance_bindings [ 0 ] . binding = nullptr ;
_instance_bindings [ 0 ] . token = nullptr ;
_instance_bindings [ 0 ] . free_callback = nullptr ;
_instance_bindings [ 0 ] . reference_callback = nullptr ;
2023-08-05 01:34:14 +00:00
}
_instance_binding_mutex . unlock ( ) ;
// Clear the virtual methods.
while ( virtual_method_list ) {
( * virtual_method_list - > method ) = nullptr ;
( * virtual_method_list - > initialized ) = false ;
virtual_method_list = virtual_method_list - > next ;
}
}
void Object : : reset_internal_extension ( ObjectGDExtension * p_extension ) {
ERR_FAIL_COND ( _extension ! = nullptr ) ;
if ( p_extension ) {
_extension_instance = p_extension - > recreate_instance ? p_extension - > recreate_instance ( p_extension - > class_userdata , ( GDExtensionObjectPtr ) this ) : nullptr ;
ERR_FAIL_NULL_MSG ( _extension_instance , " Unable to recreate GDExtension instance - does this extension support hot reloading? " ) ;
_extension = p_extension ;
}
}
# endif
2020-02-13 19:03:10 +00:00
void Object : : _construct_object ( bool p_reference ) {
type_is_reference = p_reference ;
2019-05-09 09:21:49 +00:00
_instance_id = ObjectDB : : add_instance ( this ) ;
2014-02-10 01:10:30 +00:00
2014-04-05 15:39:30 +00:00
# ifdef DEBUG_ENABLED
_lock_index . init ( 1 ) ;
# endif
2014-02-10 01:10:30 +00:00
}
2020-05-12 15:01:17 +00:00
2020-02-13 19:03:10 +00:00
Object : : Object ( bool p_reference ) {
_construct_object ( p_reference ) ;
}
Object : : Object ( ) {
_construct_object ( false ) ;
}
2014-02-10 01:10:30 +00:00
2022-03-23 09:08:58 +00:00
void Object : : detach_from_objectdb ( ) {
if ( _instance_id ! = ObjectID ( ) ) {
ObjectDB : : remove_instance ( this ) ;
_instance_id = ObjectID ( ) ;
}
}
2014-02-10 01:10:30 +00:00
Object : : ~ Object ( ) {
2020-05-14 14:41:43 +00:00
if ( script_instance ) {
2014-02-10 01:10:30 +00:00
memdelete ( script_instance ) ;
2020-05-14 14:41:43 +00:00
}
2020-04-01 23:20:12 +00:00
script_instance = nullptr ;
2014-02-10 01:10:30 +00:00
2023-08-05 01:34:14 +00:00
if ( _extension ) {
# ifdef TOOLS_ENABLED
if ( _extension - > untrack_instance ) {
_extension - > untrack_instance ( _extension - > tracking_userdata , this ) ;
}
# endif
if ( _extension - > free_instance ) {
_extension - > free_instance ( _extension - > class_userdata , _extension_instance ) ;
}
2021-06-04 17:33:48 +00:00
_extension = nullptr ;
_extension_instance = nullptr ;
}
2023-08-05 01:34:14 +00:00
# ifdef TOOLS_ENABLED
else if ( _instance_bindings ! = nullptr & & Engine : : get_singleton ( ) - > is_extension_reloading_enabled ( ) ) {
for ( uint32_t i = 0 ; i < _instance_binding_count ; i + + ) {
GDExtensionManager : : get_singleton ( ) - > untrack_instance_binding ( _instance_bindings [ i ] . token , this ) ;
}
}
# endif
2021-06-04 17:33:48 +00:00
2020-01-22 00:45:06 +00:00
if ( _emitting ) {
//@todo this may need to actually reach the debugger prioritarily somehow because it may crash before
2019-11-06 16:03:04 +00:00
ERR_PRINT ( " Object " + to_string ( ) + " was freed or unreferenced while a signal is being emitted from it. Try connecting to the signal using 'CONNECT_DEFERRED' flag, or use queue_free() to free the object (if this object is a Node) to avoid this error and potential crashes. " ) ;
2020-01-22 00:45:06 +00:00
}
2023-05-03 23:20:15 +00:00
// Drop all connections to the signals of this object.
2022-05-08 08:09:19 +00:00
while ( signal_map . size ( ) ) {
// Avoid regular iteration so erasing is safe.
KeyValue < StringName , SignalData > & E = * signal_map . begin ( ) ;
SignalData * s = & E . value ;
2014-02-10 01:10:30 +00:00
2023-01-30 16:46:56 +00:00
for ( const KeyValue < Callable , SignalData : : Slot > & slot_kv : s - > slot_map ) {
2023-05-03 23:20:15 +00:00
Object * target = slot_kv . value . conn . callable . get_object ( ) ;
if ( likely ( target ) ) {
target - > connections . erase ( slot_kv . value . cE ) ;
}
2018-11-18 14:47:19 +00:00
}
2014-02-10 01:10:30 +00:00
2022-05-08 08:09:19 +00:00
signal_map . erase ( E . key ) ;
2014-02-10 01:10:30 +00:00
}
2023-05-03 23:20:15 +00:00
// Disconnect signals that connect to this object.
2014-02-10 01:10:30 +00:00
while ( connections . size ( ) ) {
Connection c = connections . front ( ) - > get ( ) ;
2023-05-03 23:20:15 +00:00
bool disconnected = c . signal . get_object ( ) - > _disconnect ( c . signal . get_name ( ) , c . callable , true ) ;
if ( unlikely ( ! disconnected ) ) {
// If the disconnect has failed, abandon the connection to avoid getting trapped in an infinite loop here.
connections . pop_front ( ) ;
}
2014-02-10 01:10:30 +00:00
}
2022-03-23 09:08:58 +00:00
if ( _instance_id ! = ObjectID ( ) ) {
ObjectDB : : remove_instance ( this ) ;
_instance_id = ObjectID ( ) ;
}
2014-02-10 01:10:30 +00:00
_predelete_ok = 2 ;
2017-07-16 15:39:23 +00:00
2021-07-08 19:16:02 +00:00
if ( _instance_bindings ! = nullptr ) {
for ( uint32_t i = 0 ; i < _instance_binding_count ; i + + ) {
if ( _instance_bindings [ i ] . free_callback ) {
2021-07-29 13:19:28 +00:00
_instance_bindings [ i ] . free_callback ( _instance_bindings [ i ] . token , this , _instance_bindings [ i ] . binding ) ;
2019-02-04 19:39:02 +00:00
}
2017-07-16 15:39:23 +00:00
}
2021-07-08 19:16:02 +00:00
memfree ( _instance_bindings ) ;
2017-07-16 15:39:23 +00:00
}
2014-02-10 01:10:30 +00:00
}
bool predelete_handler ( Object * p_object ) {
return p_object - > _predelete ( ) ;
}
void postinitialize_handler ( Object * p_object ) {
p_object - > _postinitialize ( ) ;
}
2020-02-13 19:03:10 +00:00
void ObjectDB : : debug_objects ( DebugFunc p_func ) {
spin_lock . lock ( ) ;
2020-10-25 16:16:29 +00:00
for ( uint32_t i = 0 , count = slot_count ; i < slot_max & & count ! = 0 ; i + + ) {
if ( object_slots [ i ] . validator ) {
p_func ( object_slots [ i ] . object ) ;
count - - ;
}
2020-02-13 19:03:10 +00:00
}
spin_lock . unlock ( ) ;
2014-02-10 01:10:30 +00:00
}
2024-01-03 11:10:11 +00:00
# ifdef TOOLS_ENABLED
2020-02-13 19:03:10 +00:00
void Object : : get_argument_options ( const StringName & p_function , int p_idx , List < String > * r_options ) const {
2024-01-03 11:10:11 +00:00
const String pf = p_function ;
Add autocompletion to several Object methods
Add autocompletion for the following Object methods:
- `connect`, `is_connected`, `disconnect`, `emit_signal`, `has_signal`;
- `call`, `call_deferred`, `callv`, `has_method`;
- `set`, `set_deferred`, `get`;
- `set_meta`, `remove_meta`, `has_meta`, `remove_meta`.
2022-09-25 09:36:11 +00:00
if ( p_idx = = 0 ) {
2024-01-03 11:10:11 +00:00
if ( pf = = " connect " | | pf = = " is_connected " | | pf = = " disconnect " | | pf = = " emit_signal " | | pf = = " has_signal " ) {
Add autocompletion to several Object methods
Add autocompletion for the following Object methods:
- `connect`, `is_connected`, `disconnect`, `emit_signal`, `has_signal`;
- `call`, `call_deferred`, `callv`, `has_method`;
- `set`, `set_deferred`, `get`;
- `set_meta`, `remove_meta`, `has_meta`, `remove_meta`.
2022-09-25 09:36:11 +00:00
List < MethodInfo > signals ;
get_signal_list ( & signals ) ;
for ( const MethodInfo & E : signals ) {
r_options - > push_back ( E . name . quote ( ) ) ;
}
2024-01-03 11:10:11 +00:00
} else if ( pf = = " call " | | pf = = " call_deferred " | | pf = = " callv " | | pf = = " has_method " ) {
Add autocompletion to several Object methods
Add autocompletion for the following Object methods:
- `connect`, `is_connected`, `disconnect`, `emit_signal`, `has_signal`;
- `call`, `call_deferred`, `callv`, `has_method`;
- `set`, `set_deferred`, `get`;
- `set_meta`, `remove_meta`, `has_meta`, `remove_meta`.
2022-09-25 09:36:11 +00:00
List < MethodInfo > methods ;
get_method_list ( & methods ) ;
for ( const MethodInfo & E : methods ) {
if ( E . name . begins_with ( " _ " ) & & ! ( E . flags & METHOD_FLAG_VIRTUAL ) ) {
continue ;
}
r_options - > push_back ( E . name . quote ( ) ) ;
}
2024-01-03 11:10:11 +00:00
} else if ( pf = = " set " | | pf = = " set_deferred " | | pf = = " get " ) {
Add autocompletion to several Object methods
Add autocompletion for the following Object methods:
- `connect`, `is_connected`, `disconnect`, `emit_signal`, `has_signal`;
- `call`, `call_deferred`, `callv`, `has_method`;
- `set`, `set_deferred`, `get`;
- `set_meta`, `remove_meta`, `has_meta`, `remove_meta`.
2022-09-25 09:36:11 +00:00
List < PropertyInfo > properties ;
get_property_list ( & properties ) ;
for ( const PropertyInfo & E : properties ) {
if ( E . usage & PROPERTY_USAGE_DEFAULT & & ! ( E . usage & PROPERTY_USAGE_INTERNAL ) ) {
r_options - > push_back ( E . name . quote ( ) ) ;
}
}
2024-01-03 11:10:11 +00:00
} else if ( pf = = " set_meta " | | pf = = " get_meta " | | pf = = " has_meta " | | pf = = " remove_meta " ) {
Add autocompletion to several Object methods
Add autocompletion for the following Object methods:
- `connect`, `is_connected`, `disconnect`, `emit_signal`, `has_signal`;
- `call`, `call_deferred`, `callv`, `has_method`;
- `set`, `set_deferred`, `get`;
- `set_meta`, `remove_meta`, `has_meta`, `remove_meta`.
2022-09-25 09:36:11 +00:00
for ( const KeyValue < StringName , Variant > & K : metadata ) {
r_options - > push_back ( String ( K . key ) . quote ( ) ) ;
}
}
} else if ( p_idx = = 2 ) {
2024-01-03 11:10:11 +00:00
if ( pf = = " connect " ) {
Add autocompletion to several Object methods
Add autocompletion for the following Object methods:
- `connect`, `is_connected`, `disconnect`, `emit_signal`, `has_signal`;
- `call`, `call_deferred`, `callv`, `has_method`;
- `set`, `set_deferred`, `get`;
- `set_meta`, `remove_meta`, `has_meta`, `remove_meta`.
2022-09-25 09:36:11 +00:00
// Ideally, the constants should be inferred by the parameter.
// But a parameter's PropertyInfo does not store the enum they come from, so this will do for now.
List < StringName > constants ;
ClassDB : : get_enum_constants ( " Object " , " ConnectFlags " , & constants ) ;
for ( const StringName & E : constants ) {
r_options - > push_back ( String ( E ) ) ;
}
}
}
2020-02-13 19:03:10 +00:00
}
2024-01-03 11:10:11 +00:00
# endif
2014-02-10 01:10:30 +00:00
2020-02-13 19:03:10 +00:00
SpinLock ObjectDB : : spin_lock ;
uint32_t ObjectDB : : slot_count = 0 ;
uint32_t ObjectDB : : slot_max = 0 ;
ObjectDB : : ObjectSlot * ObjectDB : : object_slots = nullptr ;
uint64_t ObjectDB : : validator_counter = 0 ;
2017-01-07 21:25:37 +00:00
2020-02-13 19:03:10 +00:00
int ObjectDB : : get_object_count ( ) {
return slot_count ;
2014-02-10 01:10:30 +00:00
}
2020-02-13 19:03:10 +00:00
ObjectID ObjectDB : : add_instance ( Object * p_object ) {
spin_lock . lock ( ) ;
if ( unlikely ( slot_count = = slot_max ) ) {
CRASH_COND ( slot_count = = ( 1 < < OBJECTDB_SLOT_MAX_COUNT_BITS ) ) ;
uint32_t new_slot_max = slot_max > 0 ? slot_max * 2 : 1 ;
object_slots = ( ObjectSlot * ) memrealloc ( object_slots , sizeof ( ObjectSlot ) * new_slot_max ) ;
for ( uint32_t i = slot_max ; i < new_slot_max ; i + + ) {
object_slots [ i ] . object = nullptr ;
2021-06-04 16:03:15 +00:00
object_slots [ i ] . is_ref_counted = false ;
2020-02-13 19:03:10 +00:00
object_slots [ i ] . next_free = i ;
object_slots [ i ] . validator = 0 ;
}
slot_max = new_slot_max ;
}
2014-02-10 01:10:30 +00:00
2020-02-13 19:03:10 +00:00
uint32_t slot = object_slots [ slot_count ] . next_free ;
if ( object_slots [ slot ] . object ! = nullptr ) {
spin_lock . unlock ( ) ;
ERR_FAIL_COND_V ( object_slots [ slot ] . object ! = nullptr , ObjectID ( ) ) ;
}
object_slots [ slot ] . object = p_object ;
2021-06-04 16:03:15 +00:00
object_slots [ slot ] . is_ref_counted = p_object - > is_ref_counted ( ) ;
2020-02-13 19:03:10 +00:00
validator_counter = ( validator_counter + 1 ) & OBJECTDB_VALIDATOR_MASK ;
if ( unlikely ( validator_counter = = 0 ) ) {
validator_counter = 1 ;
}
object_slots [ slot ] . validator = validator_counter ;
2014-02-10 01:10:30 +00:00
2020-02-13 19:03:10 +00:00
uint64_t id = validator_counter ;
id < < = OBJECTDB_SLOT_MAX_COUNT_BITS ;
id | = uint64_t ( slot ) ;
2014-02-10 01:10:30 +00:00
2021-06-04 16:03:15 +00:00
if ( p_object - > is_ref_counted ( ) ) {
2020-02-13 19:03:10 +00:00
id | = OBJECTDB_REFERENCE_BIT ;
2014-02-10 01:10:30 +00:00
}
2017-01-07 21:25:37 +00:00
2020-02-13 19:03:10 +00:00
slot_count + + ;
2014-02-10 01:10:30 +00:00
2020-02-13 19:03:10 +00:00
spin_lock . unlock ( ) ;
return ObjectID ( id ) ;
2014-12-17 01:31:57 +00:00
}
2020-02-13 19:03:10 +00:00
void ObjectDB : : remove_instance ( Object * p_object ) {
uint64_t t = p_object - > get_instance_id ( ) ;
uint32_t slot = t & OBJECTDB_SLOT_MAX_COUNT_MASK ; //slot is always valid on valid object
2014-02-10 01:10:30 +00:00
2020-02-13 19:03:10 +00:00
spin_lock . lock ( ) ;
2017-01-07 21:25:37 +00:00
2020-02-13 19:03:10 +00:00
# ifdef DEBUG_ENABLED
2017-01-07 21:25:37 +00:00
2020-02-13 19:03:10 +00:00
if ( object_slots [ slot ] . object ! = p_object ) {
spin_lock . unlock ( ) ;
ERR_FAIL_COND ( object_slots [ slot ] . object ! = p_object ) ;
}
{
uint64_t validator = ( t > > OBJECTDB_SLOT_MAX_COUNT_BITS ) & OBJECTDB_VALIDATOR_MASK ;
if ( object_slots [ slot ] . validator ! = validator ) {
spin_lock . unlock ( ) ;
ERR_FAIL_COND ( object_slots [ slot ] . validator ! = validator ) ;
}
}
# endif
//decrease slot count
slot_count - - ;
//set the free slot properly
object_slots [ slot_count ] . next_free = slot ;
//invalidate, so checks against it fail
object_slots [ slot ] . validator = 0 ;
2021-06-04 16:03:15 +00:00
object_slots [ slot ] . is_ref_counted = false ;
2020-02-13 19:03:10 +00:00
object_slots [ slot ] . object = nullptr ;
spin_lock . unlock ( ) ;
}
2014-02-10 01:10:30 +00:00
2017-01-07 21:25:37 +00:00
void ObjectDB : : setup ( ) {
2020-02-13 19:03:10 +00:00
//nothing to do now
2014-02-10 01:10:30 +00:00
}
void ObjectDB : : cleanup ( ) {
2024-04-18 15:42:55 +00:00
spin_lock . lock ( ) ;
2016-03-08 23:00:52 +00:00
2024-04-18 15:42:55 +00:00
if ( slot_count > 0 ) {
2020-06-08 14:39:08 +00:00
WARN_PRINT ( " ObjectDB instances leaked at exit (run with --verbose for details). " ) ;
2015-04-20 22:38:02 +00:00
if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) ) {
2020-05-06 12:19:01 +00:00
// Ensure calling the native classes because if a leaked instance has a script
// that overrides any of those methods, it'd not be OK to call them at this point,
// now the scripting languages have already been terminated.
MethodBind * node_get_name = ClassDB : : get_method ( " Node " , " get_name " ) ;
MethodBind * resource_get_path = ClassDB : : get_method ( " Resource " , " get_path " ) ;
Callable : : CallError call_error ;
2020-10-25 16:16:29 +00:00
for ( uint32_t i = 0 , count = slot_count ; i < slot_max & & count ! = 0 ; i + + ) {
if ( object_slots [ i ] . validator ) {
Object * obj = object_slots [ i ] . object ;
2015-04-20 22:38:02 +00:00
2020-10-25 16:16:29 +00:00
String extra_info ;
if ( obj - > is_class ( " Node " ) ) {
extra_info = " - Node name: " + String ( node_get_name - > call ( obj , nullptr , 0 , call_error ) ) ;
}
if ( obj - > is_class ( " Resource " ) ) {
extra_info = " - Resource path: " + String ( resource_get_path - > call ( obj , nullptr , 0 , call_error ) ) ;
}
2024-01-15 15:36:15 +00:00
uint64_t id = uint64_t ( i ) | ( uint64_t ( object_slots [ i ] . validator ) < < OBJECTDB_SLOT_MAX_COUNT_BITS ) | ( object_slots [ i ] . is_ref_counted ? OBJECTDB_REFERENCE_BIT : 0 ) ;
DEV_ASSERT ( id = = ( uint64_t ) obj - > get_instance_id ( ) ) ; // We could just use the id from the object, but this check may help catching memory corruption catastrophes.
print_line ( " Leaked instance: " + String ( obj - > get_class ( ) ) + " : " + uitos ( id ) + extra_info ) ;
2020-02-13 19:03:10 +00:00
2020-10-25 16:16:29 +00:00
count - - ;
}
2015-04-20 22:38:02 +00:00
}
2020-06-08 14:39:08 +00:00
print_line ( " Hint: Leaked instances typically happen when nodes are removed from the scene tree (with `remove_child()`) but not freed (with `free()` or `queue_free()`). " ) ;
2015-04-20 22:38:02 +00:00
}
2020-02-13 19:03:10 +00:00
}
if ( object_slots ) {
memfree ( object_slots ) ;
2014-02-10 01:10:30 +00:00
}
2024-04-18 15:42:55 +00:00
spin_lock . unlock ( ) ;
2014-02-10 01:10:30 +00:00
}