2023-01-05 13:25:55 +01: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-05 00:50:27 +01:00
2014-02-10 02:10:30 +01:00
# include "object.h"
2017-01-16 08:04:19 +01:00
2018-09-11 18:13:45 +02:00
# include "core/core_string_names.h"
2020-11-07 23:33:38 +01: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 18:13:45 +02:00
# include "core/os/os.h"
2020-11-07 23:33:38 +01:00
# include "core/string/print_string.h"
# include "core/string/translation.h"
2023-01-30 17:46:56 +01:00
# include "core/templates/local_vector.h"
2022-08-05 20:35:08 +02:00
# include "core/variant/typed_array.h"
2014-04-05 17:39:30 +02:00
# ifdef DEBUG_ENABLED
struct _ObjectDebugLock {
Object * obj ;
_ObjectDebugLock ( Object * p_obj ) {
2017-03-05 16:44:50 +01:00
obj = p_obj ;
2014-04-05 17:39:30 +02:00
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 22:45:20 +02:00
PropertyInfo : : operator Dictionary ( ) const {
Dictionary d ;
2017-03-05 16:44:50 +01:00
d [ " name " ] = name ;
2017-08-24 00:10:32 +02:00
d [ " class_name " ] = class_name ;
2017-03-05 16:44:50 +01:00
d [ " type " ] = type ;
d [ " hint " ] = hint ;
d [ " hint_string " ] = hint_string ;
d [ " usage " ] = usage ;
2016-08-25 22:45:20 +02:00
return d ;
}
2017-03-05 16:44:50 +01:00
PropertyInfo PropertyInfo : : from_dict ( const Dictionary & p_dict ) {
2016-08-25 22:45:20 +02:00
PropertyInfo pi ;
2020-05-14 16:41:43 +02:00
if ( p_dict . has ( " type " ) ) {
2017-03-05 16:44:50 +01:00
pi . type = Variant : : Type ( int ( p_dict [ " type " ] ) ) ;
2020-05-14 16:41:43 +02:00
}
2016-08-25 22:45:20 +02:00
2020-05-14 16:41:43 +02:00
if ( p_dict . has ( " name " ) ) {
2017-03-05 16:44:50 +01:00
pi . name = p_dict [ " name " ] ;
2020-05-14 16:41:43 +02:00
}
2016-08-25 22:45:20 +02:00
2020-05-14 16:41:43 +02:00
if ( p_dict . has ( " class_name " ) ) {
2017-08-24 00:10:32 +02:00
pi . class_name = p_dict [ " class_name " ] ;
2020-05-14 16:41:43 +02:00
}
2017-08-24 00:10:32 +02:00
2020-05-14 16:41:43 +02:00
if ( p_dict . has ( " hint " ) ) {
2017-03-05 16:44:50 +01:00
pi . hint = PropertyHint ( int ( p_dict [ " hint " ] ) ) ;
2020-05-14 16:41:43 +02:00
}
2016-08-25 22:45:20 +02:00
2020-05-14 16:41:43 +02:00
if ( p_dict . has ( " hint_string " ) ) {
2017-03-05 16:44:50 +01:00
pi . hint_string = p_dict [ " hint_string " ] ;
2020-05-14 16:41:43 +02:00
}
2016-08-25 22:45:20 +02:00
2020-05-14 16:41:43 +02:00
if ( p_dict . has ( " usage " ) ) {
2017-03-05 16:44:50 +01:00
pi . usage = p_dict [ " usage " ] ;
2020-05-14 16:41:43 +02:00
}
2016-08-25 22:45:20 +02:00
return pi ;
}
2022-08-05 20:35:08 +02:00
TypedArray < Dictionary > convert_property_list ( const List < PropertyInfo > * p_list ) {
TypedArray < Dictionary > va ;
2017-03-05 16:44:50 +01:00
for ( const List < PropertyInfo > : : Element * E = p_list - > front ( ) ; E ; E = E - > next ( ) ) {
2016-08-25 22:45:20 +02:00
va . push_back ( Dictionary ( E - > get ( ) ) ) ;
2014-02-10 02:10:30 +01:00
}
return va ;
}
2016-08-25 22:45:20 +02:00
MethodInfo : : operator Dictionary ( ) const {
Dictionary d ;
2017-03-05 16:44:50 +01:00
d [ " name " ] = name ;
d [ " args " ] = convert_property_list ( & arguments ) ;
2016-08-25 22:45:20 +02:00
Array da ;
2020-05-14 16:41:43 +02:00
for ( int i = 0 ; i < default_arguments . size ( ) ; i + + ) {
2016-08-25 22:45:20 +02:00
da . push_back ( default_arguments [ i ] ) ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
d [ " default_args " ] = da ;
d [ " flags " ] = flags ;
d [ " id " ] = id ;
2016-08-25 22:45:20 +02:00
Dictionary r = return_val ;
2017-03-05 16:44:50 +01:00
d [ " return " ] = r ;
2016-08-25 22:45:20 +02:00
return d ;
}
2017-03-05 16:44:50 +01:00
MethodInfo MethodInfo : : from_dict ( const Dictionary & p_dict ) {
2016-08-25 22:45:20 +02:00
MethodInfo mi ;
2020-05-14 16:41:43 +02:00
if ( p_dict . has ( " name " ) ) {
2017-03-05 16:44:50 +01:00
mi . name = p_dict [ " name " ] ;
2020-05-14 16:41:43 +02:00
}
2016-08-25 22:45:20 +02:00
Array args ;
if ( p_dict . has ( " args " ) ) {
2017-03-05 16:44:50 +01:00
args = p_dict [ " args " ] ;
2016-08-25 22:45:20 +02:00
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < args . size ( ) ; i + + ) {
2016-08-25 22:45:20 +02:00
Dictionary d = args [ i ] ;
mi . arguments . push_back ( PropertyInfo : : from_dict ( d ) ) ;
}
Array defargs ;
if ( p_dict . has ( " default_args " ) ) {
2017-03-05 16:44:50 +01:00
defargs = p_dict [ " default_args " ] ;
2016-08-25 22:45:20 +02:00
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < defargs . size ( ) ; i + + ) {
2016-08-25 22:45:20 +02:00
mi . default_arguments . push_back ( defargs [ i ] ) ;
}
if ( p_dict . has ( " return " ) ) {
2017-03-05 16:44:50 +01:00
mi . return_val = PropertyInfo : : from_dict ( p_dict [ " return " ] ) ;
2016-08-25 22:45:20 +02:00
}
2020-05-14 16:41:43 +02:00
if ( p_dict . has ( " flags " ) ) {
2017-03-05 16:44:50 +01:00
mi . flags = p_dict [ " flags " ] ;
2020-05-14 16:41:43 +02:00
}
2016-08-25 22:45:20 +02:00
return mi ;
}
2014-02-10 02:10:30 +01:00
Object : : Connection : : operator Variant ( ) const {
Dictionary d ;
2017-03-05 16:44:50 +01:00
d [ " signal " ] = signal ;
2020-02-19 20:27:19 +01:00
d [ " callable " ] = callable ;
2017-03-05 16:44:50 +01:00
d [ " flags " ] = flags ;
2014-02-10 02:10:30 +01:00
return d ;
}
2017-03-05 16:44:50 +01:00
bool Object : : Connection : : operator < ( const Connection & p_conn ) const {
2020-02-19 20:27:19 +01:00
if ( signal = = p_conn . signal ) {
return callable < p_conn . callable ;
2014-02-10 02:10:30 +01:00
} else {
2020-02-19 20:27:19 +01:00
return signal < p_conn . signal ;
2014-02-10 02:10:30 +01:00
}
}
2020-05-14 14:29:06 +02:00
2017-03-05 16:44:50 +01:00
Object : : Connection : : Connection ( const Variant & p_variant ) {
Dictionary d = p_variant ;
2020-05-14 16:41:43 +02:00
if ( d . has ( " signal " ) ) {
2017-03-05 16:44:50 +01:00
signal = d [ " signal " ] ;
2020-05-14 16:41:43 +02:00
}
if ( d . has ( " callable " ) ) {
2020-02-19 20:27:19 +01:00
callable = d [ " callable " ] ;
2020-05-14 16:41:43 +02:00
}
if ( d . has ( " flags " ) ) {
2017-03-05 16:44:50 +01:00
flags = d [ " flags " ] ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
bool Object : : _predelete ( ) {
2017-03-05 16:44:50 +01:00
_predelete_ok = 1 ;
notification ( NOTIFICATION_PREDELETE , true ) ;
2015-06-29 05:29:49 +02:00
if ( _predelete_ok ) {
2023-04-07 23:32:37 +02:00
_class_name_ptr = nullptr ; // Must restore, so constructors/destructors have proper class name access at each stage.
2015-06-29 05:29:49 +02:00
}
2014-02-10 02:10:30 +01:00
return _predelete_ok ;
}
2023-04-10 18:45:53 +02:00
void Object : : cancel_free ( ) {
_predelete_ok = false ;
}
2014-02-10 02:10:30 +01:00
void Object : : _postinitialize ( ) {
2023-04-07 23:32:37 +02: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 03:03:46 +01:00
_initialize_classv ( ) ;
2023-04-07 23:32:37 +02:00
_class_name_ptr = nullptr ; // May have been called from a constructor.
2014-02-10 02:10:30 +01:00
notification ( NOTIFICATION_POSTINITIALIZE ) ;
}
void Object : : get_valid_parents_static ( List < String > * p_parents ) {
}
2020-05-14 14:29:06 +02:00
2014-02-10 02:10:30 +01:00
void Object : : _get_valid_parents_static ( List < String > * p_parents ) {
}
2017-03-05 16:44:50 +01:00
void Object : : set ( const StringName & p_name , const Variant & p_value , bool * r_valid ) {
2014-02-10 02:10:30 +01:00
# ifdef TOOLS_ENABLED
2017-03-05 16:44:50 +01:00
_edited = true ;
2014-02-10 02:10:30 +01:00
# endif
2015-12-05 18:18:22 +01:00
2014-02-10 02:10:30 +01:00
if ( script_instance ) {
2017-03-05 16:44:50 +01:00
if ( script_instance - > set ( p_name , p_value ) ) {
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2017-03-05 16:44:50 +01:00
* r_valid = true ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return ;
}
}
2021-06-04 19:33:48 +02:00
if ( _extension & & _extension - > set ) {
2021-06-19 17:58:49 +02: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 12:11:28 +01:00
if ( _extension - > set ( _extension_instance , ( const GDExtensionStringNamePtr ) & p_name , ( const GDExtensionVariantPtr ) & p_value ) ) {
2021-06-04 19:33:48 +02:00
if ( r_valid ) {
* r_valid = true ;
}
return ;
}
2021-06-19 17:58:49 +02:00
# if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
# endif
2021-06-04 19:33:48 +02:00
}
2022-02-16 13:56:32 +01:00
// Try built-in setter.
2014-02-10 02:10:30 +01:00
{
2017-03-05 16:44:50 +01:00
if ( ClassDB : : set_property ( this , p_name , p_value , r_valid ) ) {
2014-02-10 02:10:30 +01:00
return ;
}
}
2017-03-05 16:44:50 +01:00
if ( p_name = = CoreStringNames : : get_singleton ( ) - > _script ) {
2014-02-10 02:10:30 +01:00
set_script ( p_value ) ;
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2017-03-05 16:44:50 +01:00
* r_valid = true ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return ;
2022-03-23 21:08:54 +01:00
} else {
2022-05-08 10:09:19 +02:00
Variant * * V = metadata_properties . getptr ( p_name ) ;
if ( V ) {
* * V = p_value ;
2022-03-23 21:08:54 +01: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 16:41:43 +02:00
}
2018-07-29 22:40:09 +02:00
}
# ifdef TOOLS_ENABLED
if ( script_instance ) {
bool valid ;
script_instance - > property_set_fallback ( p_name , p_value , & valid ) ;
if ( valid ) {
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2017-03-05 16:44:50 +01:00
* r_valid = true ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return ;
}
}
2018-07-29 22:40:09 +02:00
# endif
2022-04-28 22:49:10 +02:00
// Something inside the object... :|
bool success = _setv ( p_name , p_value ) ;
if ( success ) {
if ( r_valid ) {
* r_valid = true ;
}
return ;
}
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2018-07-29 22:40:09 +02:00
* r_valid = false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
Variant Object : : get ( const StringName & p_name , bool * r_valid ) const {
2014-02-10 02:10:30 +01:00
Variant ret ;
if ( script_instance ) {
2017-03-05 16:44:50 +01:00
if ( script_instance - > get ( p_name , ret ) ) {
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2017-03-05 16:44:50 +01:00
* r_valid = true ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return ret ;
}
}
2021-06-04 19:33:48 +02:00
if ( _extension & & _extension - > get ) {
2021-06-19 17:58:49 +02: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 12:11:28 +01:00
if ( _extension - > get ( _extension_instance , ( const GDExtensionStringNamePtr ) & p_name , ( GDExtensionVariantPtr ) & ret ) ) {
2021-06-04 19:33:48 +02:00
if ( r_valid ) {
* r_valid = true ;
}
return ret ;
}
2021-06-19 17:58:49 +02:00
# if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
# endif
2021-06-04 19:33:48 +02:00
}
2022-02-16 13:56:32 +01:00
// Try built-in getter.
2014-02-10 02:10:30 +01:00
{
2017-03-05 16:44:50 +01:00
if ( ClassDB : : get_property ( const_cast < Object * > ( this ) , p_name , ret ) ) {
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2017-03-05 16:44:50 +01:00
* r_valid = true ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return ret ;
}
}
2017-03-05 16:44:50 +01:00
if ( p_name = = CoreStringNames : : get_singleton ( ) - > _script ) {
2014-02-10 02:10:30 +01:00
ret = get_script ( ) ;
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2017-03-05 16:44:50 +01:00
* r_valid = true ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return ret ;
2022-03-23 21:08:54 +01:00
}
2022-05-08 10:09:19 +02:00
const Variant * const * V = metadata_properties . getptr ( p_name ) ;
2014-02-10 02:10:30 +01:00
2022-05-08 10:09:19 +02:00
if ( V ) {
ret = * * V ;
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2017-03-05 16:44:50 +01:00
* r_valid = true ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return ret ;
2018-10-29 20:36:31 +01:00
2014-02-10 02:10:30 +01:00
} else {
2018-07-29 22:40:09 +02:00
# ifdef TOOLS_ENABLED
if ( script_instance ) {
bool valid ;
ret = script_instance - > property_get_fallback ( p_name , & valid ) ;
if ( valid ) {
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2018-07-29 22:40:09 +02:00
* r_valid = true ;
2020-05-14 16:41:43 +02:00
}
2018-07-29 22:40:09 +02:00
return ret ;
}
}
# endif
2022-04-28 22:49:10 +02:00
// Something inside the object... :|
bool success = _getv ( p_name , ret ) ;
if ( success ) {
if ( r_valid ) {
* r_valid = true ;
}
return ret ;
}
2018-07-29 22:40:09 +02:00
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2018-07-29 22:40:09 +02:00
* r_valid = false ;
2020-05-14 16:41:43 +02:00
}
2018-07-29 22:40:09 +02:00
return Variant ( ) ;
2014-02-10 02:10:30 +01:00
}
}
2017-05-30 22:20:15 +02:00
void Object : : set_indexed ( const Vector < StringName > & p_names , const Variant & p_value , bool * r_valid ) {
2020-12-15 13:04:21 +01:00
if ( p_names . is_empty ( ) ) {
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2017-05-30 22:20:15 +02:00
* r_valid = false ;
2020-05-14 16:41:43 +02:00
}
2017-05-30 22:20:15 +02:00
return ;
}
if ( p_names . size ( ) = = 1 ) {
set ( p_names [ 0 ] , p_value , r_valid ) ;
return ;
}
bool valid = false ;
2020-05-14 16:41:43 +02:00
if ( ! r_valid ) {
2020-05-10 12:56:01 +02:00
r_valid = & valid ;
2020-05-14 16:41:43 +02:00
}
2017-05-30 22:20:15 +02: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 02:29:22 +01:00
value_stack . push_back ( value_stack . back ( ) - > get ( ) . get_named ( p_names [ i ] , valid ) ) ;
if ( r_valid ) {
* r_valid = valid ;
}
2017-05-30 22:20:15 +02:00
2020-11-07 02:29:22 +01:00
if ( ! valid ) {
2017-05-30 22:20:15 +02: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 02:29:22 +01:00
value_stack . back ( ) - > prev ( ) - > get ( ) . set_named ( p_names [ i ] , value_stack . back ( ) - > get ( ) , valid ) ;
2017-05-30 22:20:15 +02:00
value_stack . pop_back ( ) ;
2020-11-07 02:29:22 +01:00
if ( r_valid ) {
* r_valid = valid ;
}
if ( ! valid ) {
2017-05-30 22:20:15 +02:00
value_stack . clear ( ) ;
return ;
}
}
set ( p_names [ 0 ] , value_stack . back ( ) - > get ( ) , r_valid ) ;
value_stack . pop_back ( ) ;
2020-12-15 13:04:21 +01:00
ERR_FAIL_COND ( ! value_stack . is_empty ( ) ) ;
2017-05-30 22:20:15 +02:00
}
Variant Object : : get_indexed ( const Vector < StringName > & p_names , bool * r_valid ) const {
2020-12-15 13:04:21 +01:00
if ( p_names . is_empty ( ) ) {
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2017-05-30 22:20:15 +02:00
* r_valid = false ;
2020-05-14 16:41:43 +02:00
}
2017-05-30 22:20:15 +02:00
return Variant ( ) ;
}
bool valid = false ;
2019-05-16 23:22:52 +02:00
Variant current_value = get ( p_names [ 0 ] , & valid ) ;
2017-05-30 22:20:15 +02:00
for ( int i = 1 ; i < p_names . size ( ) ; i + + ) {
2020-11-07 02:29:22 +01:00
current_value = current_value . get_named ( p_names [ i ] , valid ) ;
2017-05-30 22:20:15 +02:00
2020-05-14 16:41:43 +02:00
if ( ! valid ) {
2019-05-16 23:22:52 +02:00
break ;
2020-05-14 16:41:43 +02:00
}
2017-05-30 22:20:15 +02:00
}
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2019-05-16 23:22:52 +02:00
* r_valid = valid ;
2020-05-14 16:41:43 +02:00
}
2019-05-16 23:22:52 +02:00
2017-05-30 22:20:15 +02:00
return current_value ;
}
2017-03-05 16:44:50 +01:00
void Object : : get_property_list ( List < PropertyInfo > * p_list , bool p_reversed ) const {
2014-02-10 02:10:30 +01:00
if ( script_instance & & p_reversed ) {
script_instance - > get_property_list ( p_list ) ;
}
2021-10-12 14:19:42 +02:00
if ( _extension ) {
2022-12-07 12:11:28 +01:00
const ObjectGDExtension * current_extension = _extension ;
2022-03-02 18:50:00 +01:00
while ( current_extension ) {
p_list - > push_back ( PropertyInfo ( Variant : : NIL , current_extension - > class_name , PROPERTY_HINT_NONE , String ( ) , PROPERTY_USAGE_CATEGORY ) ) ;
2023-07-20 03:24:04 +02:00
2022-03-02 18:50:00 +01:00
ClassDB : : get_property_list ( current_extension - > class_name , p_list , true , this ) ;
2016-03-09 00:00:52 +01:00
2023-07-20 03:24:04 +02:00
if ( current_extension - > get_property_list ) {
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 ] ) ) ;
}
if ( current_extension - > free_property_list ) {
current_extension - > free_property_list ( _extension_instance , pinfo ) ;
}
}
current_extension = current_extension - > parent ;
2021-06-04 19:33:48 +02:00
}
}
2021-10-12 14:19:42 +02:00
_get_property_listv ( p_list , p_reversed ) ;
2021-03-12 14:35:16 +01:00
if ( ! is_class ( " Script " ) ) { // can still be set, but this is for user-friendliness
2023-01-10 21:42:05 +01:00
p_list - > push_back ( PropertyInfo ( Variant : : OBJECT , " script " , PROPERTY_HINT_RESOURCE_TYPE , " Script " , PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NEVER_DUPLICATE ) ) ;
2018-11-08 15:30:02 +01:00
}
2022-03-23 21:08:54 +01:00
2014-02-10 02:10:30 +01:00
if ( script_instance & & ! p_reversed ) {
script_instance - > get_property_list ( p_list ) ;
2016-03-09 00:00:52 +01:00
}
2022-03-23 21:08:54 +01:00
2022-05-08 10:09:19 +02: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 21:08:54 +01:00
pi . hint = PROPERTY_HINT_RESOURCE_TYPE ;
pi . hint_string = " Resource " ;
}
p_list - > push_back ( pi ) ;
}
2014-02-10 02:10:30 +01:00
}
2016-05-15 04:48:23 +02:00
2022-08-12 22:57:11 +02:00
void Object : : validate_property ( PropertyInfo & p_property ) const {
_validate_propertyv ( p_property ) ;
2023-04-07 14:01:57 +02:00
if ( script_instance ) { // Call it last to allow user altering already validated properties.
script_instance - > validate_property ( p_property ) ;
}
2016-05-15 04:48:23 +02:00
}
2022-08-22 11:43:14 +02:00
bool Object : : property_can_revert ( const StringName & p_name ) const {
2022-08-12 20:43:14 +02: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 12:11:28 +01:00
if ( _extension - > property_can_revert ( _extension_instance , ( const GDExtensionStringNamePtr ) & p_name ) ) {
2022-08-12 20:43:14 +02:00
return true ;
}
}
# if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
# endif
return _property_can_revertv ( p_name ) ;
}
2022-08-22 11:43:14 +02:00
Variant Object : : property_get_revert ( const StringName & p_name ) const {
2022-08-12 20:43:14 +02: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 12:11:28 +01:00
if ( _extension - > property_get_revert ( _extension_instance , ( const GDExtensionStringNamePtr ) & p_name , ( GDExtensionVariantPtr ) & ret ) ) {
2022-08-12 20:43:14 +02: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 02:10:30 +01:00
void Object : : get_method_list ( List < MethodInfo > * p_list ) const {
2017-03-05 16:44:50 +01:00
ClassDB : : get_method_list ( get_class_name ( ) , p_list ) ;
2014-02-10 02:10:30 +01:00
if ( script_instance ) {
script_instance - > get_method_list ( p_list ) ;
2016-03-09 00:00:52 +01:00
}
2014-02-10 02:10:30 +01:00
}
2020-02-19 20:27:19 +01:00
Variant Object : : _call_bind ( const Variant * * p_args , int p_argcount , Callable : : CallError & r_error ) {
2017-03-05 16:44:50 +01:00
if ( p_argcount < 1 ) {
2020-02-19 20:27:19 +01:00
r_error . error = Callable : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ;
2017-03-05 16:44:50 +01:00
r_error . argument = 0 ;
2014-02-10 02:10:30 +01:00
return Variant ( ) ;
}
2020-02-20 22:58:05 +01:00
if ( p_args [ 0 ] - > get_type ( ) ! = Variant : : STRING_NAME & & p_args [ 0 ] - > get_type ( ) ! = Variant : : STRING ) {
2020-02-19 20:27:19 +01:00
r_error . error = Callable : : CallError : : CALL_ERROR_INVALID_ARGUMENT ;
2017-03-05 16:44:50 +01:00
r_error . argument = 0 ;
2020-02-20 22:58:05 +01:00
r_error . expected = Variant : : STRING_NAME ;
2014-02-10 02:10:30 +01:00
return Variant ( ) ;
}
StringName method = * p_args [ 0 ] ;
2022-03-09 14:58:40 +01:00
return callp ( method , & p_args [ 1 ] , p_argcount - 1 , r_error ) ;
2014-02-10 02:10:30 +01:00
}
2020-02-19 20:27:19 +01:00
Variant Object : : _call_deferred_bind ( const Variant * * p_args , int p_argcount , Callable : : CallError & r_error ) {
2017-03-05 16:44:50 +01:00
if ( p_argcount < 1 ) {
2020-02-19 20:27:19 +01:00
r_error . error = Callable : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ;
2017-03-05 16:44:50 +01:00
r_error . argument = 0 ;
2014-02-10 02:10:30 +01:00
return Variant ( ) ;
}
2020-02-20 22:58:05 +01:00
if ( p_args [ 0 ] - > get_type ( ) ! = Variant : : STRING_NAME & & p_args [ 0 ] - > get_type ( ) ! = Variant : : STRING ) {
2020-02-19 20:27:19 +01:00
r_error . error = Callable : : CallError : : CALL_ERROR_INVALID_ARGUMENT ;
2017-03-05 16:44:50 +01:00
r_error . argument = 0 ;
2020-02-20 22:58:05 +01:00
r_error . expected = Variant : : STRING_NAME ;
2014-02-10 02:10:30 +01:00
return Variant ( ) ;
}
2020-02-19 20:27:19 +01:00
r_error . error = Callable : : CallError : : CALL_OK ;
2014-02-10 02:10:30 +01:00
2016-01-04 13:35:21 +01:00
StringName method = * p_args [ 0 ] ;
2014-02-10 02:10:30 +01:00
2022-03-09 14:58:40 +01:00
MessageQueue : : get_singleton ( ) - > push_callp ( get_instance_id ( ) , method , & p_args [ 1 ] , p_argcount - 1 , true ) ;
2014-02-10 02:10:30 +01:00
return Variant ( ) ;
}
2017-03-05 16:44:50 +01:00
bool Object : : has_method ( const StringName & p_method ) const {
if ( p_method = = CoreStringNames : : get_singleton ( ) - > _free ) {
2014-02-10 02:10:30 +01:00
return true ;
}
if ( script_instance & & script_instance - > has_method ( p_method ) ) {
return true ;
}
2017-03-05 16:44:50 +01:00
MethodBind * method = ClassDB : : get_method ( get_class_name ( ) , p_method ) ;
2014-02-10 02:10:30 +01:00
2020-04-02 01:20:12 +02:00
return method ! = nullptr ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
Variant Object : : getvar ( const Variant & p_key , bool * r_valid ) const {
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2017-03-05 16:44:50 +01:00
* r_valid = false ;
2020-05-14 16:41:43 +02:00
}
2020-11-07 02:29:22 +01:00
if ( p_key . get_type ( ) = = Variant : : STRING_NAME | | p_key . get_type ( ) = = Variant : : STRING ) {
return get ( p_key , r_valid ) ;
}
2014-02-10 02:10:30 +01:00
return Variant ( ) ;
}
2020-05-14 14:29:06 +02:00
2017-03-05 16:44:50 +01:00
void Object : : setvar ( const Variant & p_key , const Variant & p_value , bool * r_valid ) {
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2017-03-05 16:44:50 +01:00
* r_valid = false ;
2020-05-14 16:41:43 +02:00
}
2020-11-07 02:29:22 +01: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 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
Variant Object : : callv ( const StringName & p_method , const Array & p_args ) {
2020-04-02 01:20:12 +02:00
const Variant * * argptrs = nullptr ;
2014-02-10 02:10:30 +01:00
2019-03-05 11:24:21 +01: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 02:10:30 +01:00
}
2020-02-19 20:27:19 +01:00
Callable : : CallError ce ;
2022-03-09 14:58:40 +01:00
Variant ret = callp ( p_method , argptrs , p_args . size ( ) , ce ) ;
2020-02-19 20:27:19 +01:00
if ( ce . error ! = Callable : : CallError : : CALL_OK ) {
2019-08-15 04:57:49 +02: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 11:24:21 +01:00
}
return ret ;
2014-02-10 02:10:30 +01:00
}
2022-03-09 14:58:40 +01:00
Variant Object : : callp ( const StringName & p_method , const Variant * * p_args , int p_argcount , Callable : : CallError & r_error ) {
2020-02-19 20:27:19 +01:00
r_error . error = Callable : : CallError : : CALL_OK ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( p_method = = CoreStringNames : : get_singleton ( ) - > _free ) {
//free must be here, before anything, always ready
2014-02-10 02:10:30 +01:00
# ifdef DEBUG_ENABLED
2017-03-05 16:44:50 +01:00
if ( p_argcount ! = 0 ) {
r_error . argument = 0 ;
2020-02-19 20:27:19 +01:00
r_error . error = Callable : : CallError : : CALL_ERROR_TOO_MANY_ARGUMENTS ;
2014-02-10 02:10:30 +01:00
return Variant ( ) ;
}
2021-06-04 18:03:15 +02:00
if ( Object : : cast_to < RefCounted > ( this ) ) {
2017-03-05 16:44:50 +01:00
r_error . argument = 0 ;
2020-02-19 20:27:19 +01:00
r_error . error = Callable : : CallError : : CALL_ERROR_INVALID_METHOD ;
2019-08-15 04:57:49 +02:00
ERR_FAIL_V_MSG ( Variant ( ) , " Can't 'free' a reference. " ) ;
2014-02-10 02:10:30 +01:00
}
2014-04-05 17:39:30 +02:00
2017-03-05 16:44:50 +01:00
if ( _lock_index . get ( ) > 1 ) {
r_error . argument = 0 ;
2020-02-19 20:27:19 +01:00
r_error . error = Callable : : CallError : : CALL_ERROR_INVALID_METHOD ;
2019-08-15 04:57:49 +02:00
ERR_FAIL_V_MSG ( Variant ( ) , " Object is locked and can't be freed. " ) ;
2014-04-05 17:39:30 +02:00
}
2014-02-10 02:10:30 +01:00
# endif
//must be here, must be before everything,
memdelete ( this ) ;
2020-02-19 20:27:19 +01:00
r_error . error = Callable : : CallError : : CALL_OK ;
2014-02-10 02:10:30 +01:00
return Variant ( ) ;
}
Variant ret ;
2014-04-05 17:39:30 +02:00
OBJ_DEBUG_LOCK
2021-06-04 19:33:48 +02:00
2014-02-10 02:10:30 +01:00
if ( script_instance ) {
2022-03-09 14:58:40 +01:00
ret = script_instance - > callp ( p_method , p_args , p_argcount , r_error ) ;
2014-02-10 02:10:30 +01:00
//force jumptable
2017-03-05 16:44:50 +01:00
switch ( r_error . error ) {
2020-02-19 20:27:19 +01:00
case Callable : : CallError : : CALL_OK :
2014-02-10 02:10:30 +01:00
return ret ;
2020-02-19 20:27:19 +01:00
case Callable : : CallError : : CALL_ERROR_INVALID_METHOD :
2014-02-10 02:10:30 +01:00
break ;
2020-02-19 20:27:19 +01: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 22:10:04 +02:00
case Callable : : CallError : : CALL_ERROR_METHOD_NOT_CONST :
2014-02-10 02:10:30 +01:00
return ret ;
2020-02-19 20:27:19 +01:00
case Callable : : CallError : : CALL_ERROR_INSTANCE_IS_NULL : {
2017-03-05 16:44:50 +01:00
}
2014-02-10 02:10:30 +01:00
}
}
2021-06-04 19:33:48 +02:00
//extension does not need this, because all methods are registered in MethodBind
2017-03-05 16:44:50 +01:00
MethodBind * method = ClassDB : : get_method ( get_class_name ( ) , p_method ) ;
2014-02-10 02:10:30 +01:00
if ( method ) {
2017-03-05 16:44:50 +01:00
ret = method - > call ( this , p_args , p_argcount , r_error ) ;
2014-02-10 02:10:30 +01:00
} else {
2020-02-19 20:27:19 +01:00
r_error . error = Callable : : CallError : : CALL_ERROR_INVALID_METHOD ;
2014-02-10 02:10:30 +01:00
}
return ret ;
}
2022-06-27 22:10:04 +02: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 ;
}
2017-03-05 16:44:50 +01:00
void Object : : notification ( int p_notification , bool p_reversed ) {
_notificationv ( p_notification , p_reversed ) ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
if ( script_instance ) {
script_instance - > notification ( p_notification ) ;
}
2021-06-04 19:33:48 +02:00
if ( _extension & & _extension - > notification ) {
_extension - > notification ( _extension_instance , p_notification ) ;
}
2014-02-10 02:10:30 +01:00
}
2019-04-10 07:07:40 +02:00
String Object : : to_string ( ) {
if ( script_instance ) {
bool valid ;
String ret = script_instance - > to_string ( & valid ) ;
2020-05-14 16:41:43 +02:00
if ( valid ) {
2019-04-10 07:07:40 +02:00
return ret ;
2020-05-14 16:41:43 +02:00
}
2019-04-10 07:07:40 +02:00
}
2021-06-04 19:33:48 +02:00
if ( _extension & & _extension - > to_string ) {
2022-03-27 21:31:00 +02:00
String ret ;
2022-12-07 12:11:28 +01:00
GDExtensionBool is_valid ;
2022-10-23 10:43:09 +02:00
_extension - > to_string ( _extension_instance , & is_valid , & ret ) ;
2022-03-27 21:31:00 +02:00
return ret ;
2021-06-04 19:33:48 +02:00
}
2022-07-25 00:15:20 +02:00
return " < " + get_class ( ) + " # " + itos ( get_instance_id ( ) ) + " > " ;
2019-04-10 07:07:40 +02:00
}
2020-02-13 20:03:10 +01:00
void Object : : set_script_and_instance ( const Variant & p_script , ScriptInstance * p_instance ) {
2017-07-22 21:57:56 +02:00
//this function is not meant to be used in any of these ways
ERR_FAIL_COND ( p_script . is_null ( ) ) ;
ERR_FAIL_COND ( ! p_instance ) ;
2020-04-02 01:20:12 +02:00
ERR_FAIL_COND ( script_instance ! = nullptr | | ! script . is_null ( ) ) ;
2017-07-22 21:57:56 +02:00
script = p_script ;
script_instance = p_instance ;
}
2020-02-13 20:03:10 +01:00
void Object : : set_script ( const Variant & p_script ) {
2020-05-14 16:41:43 +02:00
if ( script = = p_script ) {
2014-02-10 02:10:30 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2021-02-17 16:47:57 +01:00
Ref < Script > s = p_script ;
ERR_FAIL_COND_MSG ( s . is_null ( ) & & ! p_script . is_null ( ) , " Invalid parameter, it should be a reference to a valid script (or null). " ) ;
script = p_script ;
2014-02-10 02:10:30 +01:00
if ( script_instance ) {
memdelete ( script_instance ) ;
2020-04-02 01:20:12 +02:00
script_instance = nullptr ;
2014-02-10 02:10:30 +01:00
}
2016-03-09 00:00:52 +01:00
2018-07-29 22:40:09 +02:00
if ( ! s . is_null ( ) ) {
2021-06-18 00:03:09 +02:00
if ( s - > can_instantiate ( ) ) {
2018-07-29 22:40:09 +02: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 02:10:30 +01:00
}
2021-02-10 21:18:45 +01:00
notify_property_list_changed ( ) ; //scripts may add variables, so refresh is desired
2014-02-10 02:10:30 +01:00
emit_signal ( CoreStringNames : : get_singleton ( ) - > script_changed ) ;
}
void Object : : set_script_instance ( ScriptInstance * p_instance ) {
2020-05-14 16:41:43 +02:00
if ( script_instance = = p_instance ) {
2014-02-10 02:10:30 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( script_instance ) {
2014-02-10 02:10:30 +01:00
memdelete ( script_instance ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
script_instance = p_instance ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( p_instance ) {
2020-02-13 20:03:10 +01:00
script = p_instance - > get_script ( ) ;
2020-05-14 16:41:43 +02:00
} else {
2020-02-13 20:03:10 +01:00
script = Variant ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2020-02-13 20:03:10 +01:00
Variant Object : : get_script ( ) const {
2014-02-10 02:10:30 +01:00
return script ;
}
2021-07-22 22:37:17 +02:00
bool Object : : has_meta ( const StringName & p_name ) const {
2014-02-10 02:10:30 +01:00
return metadata . has ( p_name ) ;
}
2021-07-22 22:37:17 +02:00
void Object : : set_meta ( const StringName & p_name , const Variant & p_value ) {
2014-02-10 02:10:30 +01:00
if ( p_value . get_type ( ) = = Variant : : NIL ) {
2022-03-23 21:08:54 +01:00
if ( metadata . has ( p_name ) ) {
metadata . erase ( p_name ) ;
2023-05-07 22:12:22 +02: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 21:08:54 +01:00
}
2014-02-10 02:10:30 +01:00
return ;
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
2022-05-08 10:09:19 +02:00
HashMap < StringName , Variant > : : Iterator E = metadata . find ( p_name ) ;
2022-03-23 21:08:54 +01:00
if ( E ) {
2022-05-08 10:09:19 +02:00
E - > value = p_value ;
2022-03-23 21:08:54 +01:00
} else {
2022-12-21 05:12:33 +01:00
ERR_FAIL_COND_MSG ( ! p_name . operator String ( ) . is_valid_identifier ( ) , " Invalid metadata identifier: ' " + p_name + " '. " ) ;
2022-05-08 10:09:19 +02:00
Variant * V = & metadata . insert ( p_name , p_value ) - > value ;
2023-05-07 22:12:22 +02:00
const String & sname = p_name ;
metadata_properties [ " metadata/ " + sname ] = V ;
if ( ! sname . begins_with ( " _ " ) ) {
notify_property_list_changed ( ) ;
}
2022-03-23 21:08:54 +01:00
}
2014-02-10 02:10:30 +01:00
}
2022-02-27 22:03:33 +01: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 02:10:30 +01:00
return metadata [ p_name ] ;
}
2021-07-22 22:37:17 +02:00
void Object : : remove_meta ( const StringName & p_name ) {
2022-03-23 21:08:54 +01:00
set_meta ( p_name , Variant ( ) ) ;
2019-04-26 19:23:50 +02:00
}
2022-08-05 20:35:08 +02:00
TypedArray < Dictionary > Object : : _get_property_list_bind ( ) const {
2014-02-10 02:10:30 +01:00
List < PropertyInfo > lpi ;
get_property_list ( & lpi ) ;
return convert_property_list ( & lpi ) ;
}
2015-05-25 06:46:45 +02:00
2022-08-05 20:35:08 +02:00
TypedArray < Dictionary > Object : : _get_method_list_bind ( ) const {
2015-05-25 06:46:45 +02:00
List < MethodInfo > ml ;
get_method_list ( & ml ) ;
2022-08-05 20:35:08 +02:00
TypedArray < Dictionary > ret ;
2015-05-25 06:46:45 +02:00
2017-03-05 16:44:50 +01:00
for ( List < MethodInfo > : : Element * E = ml . front ( ) ; E ; E = E - > next ( ) ) {
2016-08-25 22:45:20 +02:00
Dictionary d = E - > get ( ) ;
2015-05-25 06:46:45 +02:00
//va.push_back(d);
ret . push_back ( d ) ;
}
return ret ;
}
2023-04-25 00:21:32 +02:00
TypedArray < StringName > Object : : _get_meta_list_bind ( ) const {
TypedArray < StringName > _metaret ;
2014-02-10 02:10:30 +01:00
2022-05-08 10:09:19 +02:00
for ( const KeyValue < StringName , Variant > & K : metadata ) {
_metaret . push_back ( K . key ) ;
2014-02-10 02:10:30 +01:00
}
return _metaret ;
}
2020-05-14 14:29:06 +02:00
2021-07-22 22:37:17 +02:00
void Object : : get_meta_list ( List < StringName > * p_list ) const {
2022-05-08 10:09:19 +02:00
for ( const KeyValue < StringName , Variant > & K : metadata ) {
p_list - > push_back ( K . key ) ;
2014-02-10 02:10:30 +01:00
}
}
2017-03-05 16:44:50 +01:00
void Object : : add_user_signal ( const MethodInfo & p_signal ) {
2021-12-09 10:42:46 +01:00
ERR_FAIL_COND_MSG ( p_signal . name . is_empty ( ) , " Signal name cannot be empty. " ) ;
2019-09-25 10:28:50 +02: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 20:27:19 +01:00
SignalData s ;
2017-03-05 16:44:50 +01:00
s . user = p_signal ;
signal_map [ p_signal . name ] = s ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
bool Object : : _has_user_signal ( const StringName & p_name ) const {
2020-05-14 16:41:43 +02:00
if ( ! signal_map . has ( p_name ) ) {
2015-03-03 18:39:13 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
return signal_map [ p_name ] . user . name . length ( ) > 0 ;
2015-03-03 18:39:13 +01:00
}
2014-02-10 02:10:30 +01:00
struct _ObjectSignalDisconnectData {
StringName signal ;
2020-02-19 20:27:19 +01:00
Callable callable ;
2014-02-10 02:10:30 +01:00
} ;
2022-02-22 12:15:43 +01:00
Error Object : : _emit_signal ( const Variant * * p_args , int p_argcount , Callable : : CallError & r_error ) {
2020-02-19 20:27:19 +01:00
r_error . error = Callable : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ;
2014-02-10 02:10:30 +01:00
2022-02-22 12:15:43 +01:00
ERR_FAIL_COND_V ( p_argcount < 1 , Error : : ERR_INVALID_PARAMETER ) ;
2020-02-20 22:58:05 +01:00
if ( p_args [ 0 ] - > get_type ( ) ! = Variant : : STRING_NAME & & p_args [ 0 ] - > get_type ( ) ! = Variant : : STRING ) {
2020-02-19 20:27:19 +01:00
r_error . error = Callable : : CallError : : CALL_ERROR_INVALID_ARGUMENT ;
2017-03-05 16:44:50 +01:00
r_error . argument = 0 ;
2020-02-20 22:58:05 +01:00
r_error . expected = Variant : : STRING_NAME ;
2022-02-22 12:15:43 +01:00
ERR_FAIL_COND_V ( p_args [ 0 ] - > get_type ( ) ! = Variant : : STRING_NAME & & p_args [ 0 ] - > get_type ( ) ! = Variant : : STRING , Error : : ERR_INVALID_PARAMETER ) ;
2014-02-10 02:10:30 +01:00
}
2020-02-19 20:27:19 +01:00
r_error . error = Callable : : CallError : : CALL_OK ;
2014-02-10 02:10:30 +01:00
StringName signal = * p_args [ 0 ] ;
2020-04-02 01:20:12 +02:00
const Variant * * args = nullptr ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int argc = p_argcount - 1 ;
2016-01-04 13:35:21 +01:00
if ( argc ) {
2017-03-05 16:44:50 +01:00
args = & p_args [ 1 ] ;
2014-02-10 02:10:30 +01:00
}
2022-02-22 12:15:43 +01:00
return emit_signalp ( signal , args , argc ) ;
2016-01-04 13:35:21 +01:00
}
2014-02-10 02:10:30 +01:00
2022-03-09 14:58:40 +01:00
Error Object : : emit_signalp ( const StringName & p_name , const Variant * * p_args , int p_argcount ) {
2020-05-14 16:41:43 +02:00
if ( _block_signals ) {
2017-09-22 05:58:29 +02:00
return ERR_CANT_ACQUIRE_RESOURCE ; //no emit, signals blocked
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-02-19 20:27:19 +01:00
SignalData * s = signal_map . getptr ( p_name ) ;
2014-02-10 02:10:30 +01:00
if ( ! s ) {
2016-09-25 19:21:21 +02:00
# ifdef DEBUG_ENABLED
2017-03-05 16:44:50 +01:00
bool signal_is_valid = ClassDB : : has_signal ( get_class_name ( ) , p_name ) ;
2016-09-25 19:21:21 +02:00
//check in script
2019-08-15 04:57:49 +02: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 19:21:21 +02:00
# endif
//not connected? just return
2017-08-06 00:48:29 +02:00
return ERR_UNAVAILABLE ;
2014-02-10 02:10:30 +01:00
}
2023-04-11 17:20:03 +02: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 ) ) ;
2014-02-10 02:10:30 +01:00
List < _ObjectSignalDisconnectData > disconnect_data ;
2023-01-30 17:46:56 +01:00
// Ensure that disconnecting the signal or even deleting the object
// will not affect the signal calling.
LocalVector < Connection > slot_conns ;
slot_conns . resize ( s - > slot_map . size ( ) ) ;
{
uint32_t idx = 0 ;
for ( const KeyValue < Callable , SignalData : : Slot > & slot_kv : s - > slot_map ) {
slot_conns [ idx + + ] = slot_kv . value . conn ;
}
DEV_ASSERT ( idx = = s - > slot_map . size ( ) ) ;
}
2014-02-10 02:10:30 +01:00
2014-04-05 17:39:30 +02:00
OBJ_DEBUG_LOCK
2017-08-06 00:48:29 +02:00
Error err = OK ;
2023-01-30 17:46:56 +01:00
for ( const Connection & c : slot_conns ) {
2020-02-19 20:27:19 +01:00
Object * target = c . callable . get_object ( ) ;
2020-01-13 22:11:03 +01:00
if ( ! target ) {
// Target might have been deleted during signal callback, this is expected and OK.
continue ;
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
const Variant * * args = p_args ;
int argc = p_argcount ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( c . flags & CONNECT_DEFERRED ) {
2022-03-09 14:58:40 +01:00
MessageQueue : : get_singleton ( ) - > push_callablep ( c . callable , args , argc , true ) ;
2014-02-10 02:10:30 +01:00
} else {
2020-02-19 20:27:19 +01:00
Callable : : CallError ce ;
2020-01-22 01:45:06 +01:00
_emitting = true ;
2020-02-19 20:27:19 +01:00
Variant ret ;
2022-07-28 22:56:41 +02:00
c . callable . callp ( args , argc , ret , ce ) ;
2020-01-22 01:45:06 +01:00
_emitting = false ;
2017-08-06 00:48:29 +02:00
2020-02-19 20:27:19 +01:00
if ( ce . error ! = Callable : : CallError : : CALL_OK ) {
2018-09-21 00:05:39 +02:00
# ifdef DEBUG_ENABLED
2020-05-14 16:41:43 +02:00
if ( c . flags & CONNECT_PERSIST & & Engine : : get_singleton ( ) - > is_editor_hint ( ) & & ( script . is_null ( ) | | ! Ref < Script > ( script ) - > is_tool ( ) ) ) {
2018-09-21 00:05:39 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-09-21 00:05:39 +02:00
# endif
2020-02-19 20:27:19 +01:00
if ( ce . error = = Callable : : CallError : : CALL_ERROR_INVALID_METHOD & & ! ClassDB : : class_exists ( target - > get_class_name ( ) ) ) {
2016-01-04 13:35:21 +01:00
//most likely object is not initialized yet, do not throw error.
} else {
2023-01-10 13:08:10 +01:00
ERR_PRINT ( " Error calling from signal ' " + String ( p_name ) + " ' to callable: " + Variant : : get_callable_error_text ( c . callable , args , argc , ce ) + " . " ) ;
2017-08-06 00:48:29 +02:00
err = ERR_METHOD_NOT_FOUND ;
2016-01-04 13:35:21 +01:00
}
}
2014-02-10 02:10:30 +01:00
}
2022-09-01 15:44:42 +02:00
bool disconnect = c . flags & CONNECT_ONE_SHOT ;
2018-07-30 02:05:31 +02:00
# ifdef TOOLS_ENABLED
if ( disconnect & & ( c . flags & CONNECT_PERSIST ) & & Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
2019-02-13 09:23:29 +01:00
//this signal was connected from the editor, and is being edited. just don't disconnect for now
2018-07-30 02:05:31 +02:00
disconnect = false ;
}
# endif
if ( disconnect ) {
2014-02-10 02:10:30 +01:00
_ObjectSignalDisconnectData dd ;
2017-03-05 16:44:50 +01:00
dd . signal = p_name ;
2020-02-19 20:27:19 +01:00
dd . callable = c . callable ;
2014-02-10 02:10:30 +01:00
disconnect_data . push_back ( dd ) ;
}
}
2020-12-15 13:04:21 +01:00
while ( ! disconnect_data . is_empty ( ) ) {
2016-01-04 13:35:21 +01:00
const _ObjectSignalDisconnectData & dd = disconnect_data . front ( ) - > get ( ) ;
2020-02-19 20:27:19 +01:00
_disconnect ( dd . signal , dd . callable ) ;
2016-01-04 13:35:21 +01:00
disconnect_data . pop_front ( ) ;
}
2017-08-06 00:48:29 +02:00
return err ;
2016-01-04 13:35:21 +01:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
void Object : : _add_user_signal ( const String & p_name , const Array & p_args ) {
2014-02-10 02:10:30 +01:00
// 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 ;
2017-03-05 16:44:50 +01:00
mi . name = p_name ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < p_args . size ( ) ; i + + ) {
Dictionary d = p_args [ i ] ;
2014-02-10 02:10:30 +01:00
PropertyInfo param ;
2020-05-14 16:41:43 +02:00
if ( d . has ( " name " ) ) {
2017-03-05 16:44:50 +01:00
param . name = d [ " name " ] ;
2020-05-14 16:41:43 +02:00
}
if ( d . has ( " type " ) ) {
2017-03-05 16:44:50 +01:00
param . type = ( Variant : : Type ) ( int ) d [ " type " ] ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
mi . arguments . push_back ( param ) ;
}
add_user_signal ( mi ) ;
}
2022-08-05 20:35:08 +02:00
TypedArray < Dictionary > Object : : _get_signal_list ( ) const {
2016-01-01 14:27:25 +01:00
List < MethodInfo > signal_list ;
get_signal_list ( & signal_list ) ;
2022-08-05 20:35:08 +02:00
TypedArray < Dictionary > ret ;
2021-07-24 15:46:25 +02:00
for ( const MethodInfo & E : signal_list ) {
2021-07-16 05:45:57 +02:00
ret . push_back ( Dictionary ( E ) ) ;
2016-01-01 14:27:25 +01:00
}
return ret ;
2014-02-10 02:10:30 +01:00
}
2017-08-27 21:07:15 +02:00
2022-08-05 20:35:08 +02:00
TypedArray < Dictionary > Object : : _get_signal_connection_list ( const StringName & p_signal ) const {
2016-01-01 14:27:25 +01:00
List < Connection > conns ;
get_all_signal_connections ( & conns ) ;
2022-08-05 20:35:08 +02:00
TypedArray < Dictionary > ret ;
2016-01-01 14:27:25 +01:00
2021-07-24 15:46:25 +02:00
for ( const Connection & c : conns ) {
2020-02-19 20:27:19 +01:00
if ( c . signal . get_name ( ) = = p_signal ) {
ret . push_back ( c ) ;
2016-06-21 13:07:50 +02:00
}
2016-01-01 14:27:25 +01:00
}
return ret ;
2014-02-10 02:10:30 +01:00
}
2022-08-05 20:35:08 +02:00
TypedArray < Dictionary > Object : : _get_incoming_connections ( ) const {
TypedArray < Dictionary > ret ;
2017-06-15 15:31:57 +02:00
int connections_amount = connections . size ( ) ;
for ( int idx_conn = 0 ; idx_conn < connections_amount ; idx_conn + + ) {
2020-02-19 20:27:19 +01:00
ret . push_back ( connections [ idx_conn ] ) ;
2017-06-15 15:31:57 +02:00
}
return ret ;
}
2019-11-10 06:59:44 +01: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 ;
}
2017-03-05 16:44:50 +01:00
void Object : : get_signal_list ( List < MethodInfo > * p_signals ) const {
2015-06-24 18:29:23 +02:00
if ( ! script . is_null ( ) ) {
2019-04-19 22:03:00 +02:00
Ref < Script > scr = script ;
if ( scr . is_valid ( ) ) {
scr - > get_script_signal_list ( p_signals ) ;
}
2015-06-24 18:29:23 +02:00
}
2017-03-05 16:44:50 +01:00
ClassDB : : get_signal_list ( get_class_name ( ) , p_signals ) ;
2014-02-10 02:10:30 +01:00
//find maybe usersignals?
2022-05-08 10:09:19 +02:00
for ( const KeyValue < StringName , SignalData > & E : signal_map ) {
if ( ! E . value . user . name . is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
//user signal
2022-05-08 10:09:19 +02:00
p_signals - > push_back ( E . value . user ) ;
2014-02-10 02:10:30 +01:00
}
}
}
2015-05-10 20:45:33 +02:00
void Object : : get_all_signal_connections ( List < Connection > * p_connections ) const {
2022-05-08 10:09:19 +02:00
for ( const KeyValue < StringName , SignalData > & E : signal_map ) {
const SignalData * s = & E . value ;
2015-05-10 20:45:33 +02:00
2023-01-30 17:46:56 +01:00
for ( const KeyValue < Callable , SignalData : : Slot > & slot_kv : s - > slot_map ) {
p_connections - > push_back ( slot_kv . value . conn ) ;
2015-05-10 20:45:33 +02:00
}
}
}
2017-03-05 16:44:50 +01:00
void Object : : get_signal_connection_list ( const StringName & p_signal , List < Connection > * p_connections ) const {
2020-02-19 20:27:19 +01:00
const SignalData * s = signal_map . getptr ( p_signal ) ;
2020-05-14 16:41:43 +02:00
if ( ! s ) {
2014-02-10 02:10:30 +01:00
return ; //nothing
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2023-01-30 17:46:56 +01:00
for ( const KeyValue < Callable , SignalData : : Slot > & slot_kv : s - > slot_map ) {
p_connections - > push_back ( slot_kv . value . conn ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2019-08-16 22:30:31 +02:00
int Object : : get_persistent_signal_connection_count ( ) const {
int count = 0 ;
2016-06-04 18:17:56 +02:00
2022-05-08 10:09:19 +02:00
for ( const KeyValue < StringName , SignalData > & E : signal_map ) {
const SignalData * s = & E . value ;
2016-06-04 18:17:56 +02:00
2023-01-30 17:46:56 +01:00
for ( const KeyValue < Callable , SignalData : : Slot > & slot_kv : s - > slot_map ) {
if ( slot_kv . value . conn . flags & CONNECT_PERSIST ) {
2019-08-16 22:30:31 +02:00
count + = 1 ;
}
2016-06-04 18:17:56 +02:00
}
}
2019-08-16 22:30:31 +02:00
return count ;
2016-06-04 18:17:56 +02:00
}
2016-06-07 00:55:50 +02:00
void Object : : get_signals_connected_to_this ( List < Connection > * p_connections ) const {
2021-07-16 05:45:57 +02:00
for ( const Connection & E : connections ) {
p_connections - > push_back ( E ) ;
2016-06-07 07:39:40 +02:00
}
2016-06-07 00:55:50 +02:00
}
2022-07-28 22:56:41 +02:00
Error Object : : connect ( const StringName & p_signal , const Callable & p_callable , uint32_t p_flags ) {
2021-06-21 12:10:40 +02: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 02:10:30 +01:00
2020-02-23 10:06:13 +01:00
Object * target_object = p_callable . get_object ( ) ;
2021-06-21 12:10:40 +02:00
ERR_FAIL_COND_V_MSG ( ! target_object , ERR_INVALID_PARAMETER , " Cannot connect to ' " + p_signal + " ' to callable ' " + p_callable + " ': the callable object is null. " ) ;
2020-02-23 10:06:13 +01:00
2020-02-19 20:27:19 +01:00
SignalData * s = signal_map . getptr ( p_signal ) ;
2014-02-10 02:10:30 +01:00
if ( ! s ) {
2017-03-05 16:44:50 +01:00
bool signal_is_valid = ClassDB : : has_signal ( get_class_name ( ) , p_signal ) ;
2015-06-24 18:29:23 +02:00
//check in script
2018-11-27 23:55:37 +01: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 18:29:23 +02:00
2020-02-19 20:27:19 +01: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 04:57:49 +02:00
2020-02-19 20:27:19 +01:00
signal_map [ p_signal ] = SignalData ( ) ;
2017-03-05 16:44:50 +01:00
s = & signal_map [ p_signal ] ;
2014-02-10 02:10:30 +01:00
}
2020-02-19 20:27:19 +01:00
Callable target = p_callable ;
2020-10-09 23:41:53 +02:00
//compare with the base callable, so binds can be ignored
if ( s - > slot_map . has ( * target . get_base_comparator ( ) ) ) {
2018-08-20 18:38:18 +02:00
if ( p_flags & CONNECT_REFERENCE_COUNTED ) {
2020-10-09 23:41:53 +02:00
s - > slot_map [ * target . get_base_comparator ( ) ] . reference_count + + ;
2018-08-20 18:38:18 +02:00
return OK ;
} else {
2020-02-19 20:27:19 +01: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 18:38:18 +02:00
}
2014-02-10 02:10:30 +01:00
}
2020-02-19 20:27:19 +01:00
SignalData : : Slot slot ;
2014-02-10 02:10:30 +01:00
Connection conn ;
2020-02-19 20:27:19 +01:00
conn . callable = target ;
conn . signal = : : Signal ( this , p_signal ) ;
2017-03-05 16:44:50 +01:00
conn . flags = p_flags ;
slot . conn = conn ;
2020-02-23 10:06:13 +01:00
slot . cE = target_object - > connections . push_back ( conn ) ;
2018-08-20 18:38:18 +02:00
if ( p_flags & CONNECT_REFERENCE_COUNTED ) {
slot . reference_count = 1 ;
}
2020-10-09 23:41:53 +02:00
//use callable version as key, so binds can be ignored
s - > slot_map [ * target . get_base_comparator ( ) ] = slot ;
2014-09-15 16:33:30 +02:00
return OK ;
2014-02-10 02:10:30 +01:00
}
2020-02-19 20:27:19 +01:00
bool Object : : is_connected ( const StringName & p_signal , const Callable & p_callable ) const {
2021-06-21 12:10:40 +02:00
ERR_FAIL_COND_V_MSG ( p_callable . is_null ( ) , false , " Cannot determine if connected to ' " + p_signal + " ': the provided callable is null. " ) ;
2020-02-19 20:27:19 +01:00
const SignalData * s = signal_map . getptr ( p_signal ) ;
2014-02-10 02:10:30 +01:00
if ( ! s ) {
2017-03-05 16:44:50 +01:00
bool signal_is_valid = ClassDB : : has_signal ( get_class_name ( ) , p_signal ) ;
2020-05-14 16:41:43 +02:00
if ( signal_is_valid ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2015-12-11 11:53:40 +01:00
2020-05-14 16:41:43 +02:00
if ( ! script . is_null ( ) & & Ref < Script > ( script ) - > has_script_signal ( p_signal ) ) {
2015-12-11 11:53:40 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2015-12-11 11:53:40 +01:00
2019-08-15 04:57:49 +02:00
ERR_FAIL_V_MSG ( false , " Nonexistent signal: " + p_signal + " . " ) ;
2014-02-10 02:10:30 +01:00
}
2020-02-19 20:27:19 +01:00
Callable target = p_callable ;
2014-02-10 02:10:30 +01:00
2020-10-09 23:41:53 +02:00
return s - > slot_map . has ( * target . get_base_comparator ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2020-02-19 20:27:19 +01:00
void Object : : disconnect ( const StringName & p_signal , const Callable & p_callable ) {
_disconnect ( p_signal , p_callable ) ;
}
2023-05-04 01:20:15 +02:00
bool Object : : _disconnect ( const StringName & p_signal , const Callable & p_callable , bool p_force ) {
ERR_FAIL_COND_V_MSG ( p_callable . is_null ( ) , false , " Cannot disconnect from ' " + p_signal + " ': the provided callable is null. " ) ;
2020-02-23 10:06:13 +01:00
Object * target_object = p_callable . get_object ( ) ;
2023-05-04 01:20:15 +02:00
ERR_FAIL_COND_V_MSG ( ! target_object , false , " Cannot disconnect ' " + p_signal + " ' from callable ' " + p_callable + " ': the callable object is null. " ) ;
2020-02-23 10:06:13 +01:00
2020-02-19 20:27:19 +01:00
SignalData * s = signal_map . getptr ( p_signal ) ;
2020-12-31 11:33:39 +01:00
if ( ! s ) {
bool signal_is_valid = ClassDB : : has_signal ( get_class_name ( ) , p_signal ) | |
2021-10-28 15:19:35 +02:00
( ! script . is_null ( ) & & Ref < Script > ( script ) - > has_script_signal ( p_signal ) ) ;
2023-05-04 01:20:15 +02: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 11:33:39 +01:00
}
2023-05-04 01:20:15 +02:00
ERR_FAIL_COND_V_MSG ( ! s , false , vformat ( " Disconnecting nonexistent signal '%s' in %s. " , p_signal , to_string ( ) ) ) ;
2014-02-10 02:10:30 +01:00
2023-05-18 00:46:13 +02: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-08 00:32:28 +02:00
2021-11-17 09:29:29 +01:00
SignalData : : Slot * slot = & s - > slot_map [ * p_callable . get_base_comparator ( ) ] ;
2018-08-20 18:38:18 +02:00
2018-08-20 21:35:36 +02:00
if ( ! p_force ) {
slot - > reference_count - - ; // by default is zero, if it was not referenced it will go below it
2021-03-28 19:00:13 +02:00
if ( slot - > reference_count > 0 ) {
2023-05-04 01:20:15 +02:00
return false ;
2018-08-20 21:35:36 +02:00
}
2018-08-20 18:38:18 +02:00
}
2020-02-23 10:06:13 +01:00
target_object - > connections . erase ( slot - > cE ) ;
2020-10-09 23:41:53 +02:00
s - > slot_map . erase ( * p_callable . get_base_comparator ( ) ) ;
2014-02-10 02:10:30 +01:00
2020-12-15 13:04:21 +01:00
if ( s - > slot_map . is_empty ( ) & & ClassDB : : has_signal ( get_class_name ( ) , p_signal ) ) {
2014-02-10 02:10:30 +01:00
//not user signal, delete
signal_map . erase ( p_signal ) ;
}
2023-05-04 01:20:15 +02:00
return true ;
2014-02-10 02:10:30 +01:00
}
2022-06-14 15:28:59 +02:00
void Object : : _set_bind ( const StringName & p_set , const Variant & p_value ) {
2017-03-05 16:44:50 +01:00
set ( p_set , p_value ) ;
2014-02-10 02:10:30 +01:00
}
2022-06-14 15:28:59 +02:00
Variant Object : : _get_bind ( const StringName & p_name ) const {
2014-02-10 02:10:30 +01:00
return get ( p_name ) ;
}
2017-05-30 22:20:15 +02: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 03:03:46 +01:00
void Object : : initialize_class ( ) {
2017-03-05 16:44:50 +01:00
static bool initialized = false ;
2020-05-14 16:41:43 +02:00
if ( initialized ) {
2014-02-10 02:10:30 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2017-01-03 03:03:46 +01:00
ClassDB : : _add_class < Object > ( ) ;
2014-02-10 02:10:30 +01:00
_bind_methods ( ) ;
2017-03-05 16:44:50 +01:00
initialized = true ;
2014-02-10 02:10:30 +01:00
}
2020-08-07 13:17:12 +02:00
String Object : : tr ( const StringName & p_message , const StringName & p_context ) const {
2020-05-14 16:41:43 +02:00
if ( ! _can_translate | | ! TranslationServer : : get_singleton ( ) ) {
2014-02-10 02:10:30 +01:00
return p_message ;
2020-05-14 16:41:43 +02:00
}
2023-03-17 01:58:30 +01:00
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
return TranslationServer : : get_singleton ( ) - > tool_translate ( p_message , p_context ) ;
} else {
return TranslationServer : : get_singleton ( ) - > translate ( p_message , p_context ) ;
}
2020-07-16 10:52:06 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-16 10:52:06 +02: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 13:17:12 +02:00
return p_message_plural ;
2020-07-16 10:52:06 +02:00
}
2023-03-17 01:58:30 +01:00
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
return TranslationServer : : get_singleton ( ) - > tool_translate_plural ( p_message , p_message_plural , p_n , p_context ) ;
} else {
return TranslationServer : : get_singleton ( ) - > translate_plural ( p_message , p_message_plural , p_n , p_context ) ;
}
2014-02-10 02:10:30 +01:00
}
2015-06-22 05:03:19 +02:00
void Object : : _clear_internal_resource_paths ( const Variant & p_var ) {
2017-03-05 16:44:50 +01:00
switch ( p_var . get_type ( ) ) {
2015-06-22 05:03:19 +02:00
case Variant : : OBJECT : {
2022-05-03 01:43:50 +02:00
Ref < Resource > r = p_var ;
2020-05-14 16:41:43 +02:00
if ( ! r . is_valid ( ) ) {
2015-06-22 05:03:19 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2015-06-22 05:03:19 +02:00
2021-07-10 21:17:41 +02:00
if ( ! r - > is_built_in ( ) ) {
2015-06-22 05:03:19 +02:00
return ; //not an internal resource
2020-05-14 16:41:43 +02:00
}
2015-06-22 05:03:19 +02:00
2017-03-05 16:44:50 +01:00
Object * object = p_var ;
2020-05-14 16:41:43 +02:00
if ( ! object ) {
2015-06-22 05:03:19 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2015-06-22 05:03:19 +02:00
r - > set_path ( " " ) ;
r - > clear_internal_resource_paths ( ) ;
} break ;
case Variant : : ARRAY : {
2017-03-05 16:44:50 +01:00
Array a = p_var ;
for ( int i = 0 ; i < a . size ( ) ; i + + ) {
2015-06-22 05:03:19 +02:00
_clear_internal_resource_paths ( a [ i ] ) ;
}
} break ;
case Variant : : DICTIONARY : {
2017-03-05 16:44:50 +01:00
Dictionary d = p_var ;
2015-06-22 05:03:19 +02:00
List < Variant > keys ;
d . get_key_list ( & keys ) ;
2021-07-24 15:46:25 +02:00
for ( const Variant & E : keys ) {
2021-07-16 05:45:57 +02:00
_clear_internal_resource_paths ( E ) ;
_clear_internal_resource_paths ( d [ E ] ) ;
2015-06-22 05:03:19 +02:00
}
} break ;
2019-04-09 17:08:36 +02:00
default : {
}
2015-06-22 05:03:19 +02:00
}
}
2017-06-25 22:30:28 +02:00
# ifdef TOOLS_ENABLED
void Object : : editor_set_section_unfold ( const String & p_section , bool p_unfolded ) {
set_edited ( true ) ;
2020-05-14 16:41:43 +02:00
if ( p_unfolded ) {
2017-06-25 22:30:28 +02:00
editor_section_folding . insert ( p_section ) ;
2020-05-14 16:41:43 +02:00
} else {
2017-06-25 22:30:28 +02:00
editor_section_folding . erase ( p_section ) ;
2020-05-14 16:41:43 +02:00
}
2017-06-25 22:30:28 +02:00
}
bool Object : : editor_is_section_unfolded ( const String & p_section ) {
return editor_section_folding . has ( p_section ) ;
}
# endif
2015-06-22 05:03:19 +02:00
void Object : : clear_internal_resource_paths ( ) {
List < PropertyInfo > pinfo ;
get_property_list ( & pinfo ) ;
2021-07-24 15:46:25 +02:00
for ( const PropertyInfo & E : pinfo ) {
2021-07-16 05:45:57 +02:00
_clear_internal_resource_paths ( get ( E . name ) ) ;
2015-06-22 05:03:19 +02:00
}
}
2021-02-10 21:18:45 +01:00
void Object : : notify_property_list_changed ( ) {
emit_signal ( CoreStringNames : : get_singleton ( ) - > property_list_changed ) ;
}
2014-02-10 02:10:30 +01:00
void Object : : _bind_methods ( ) {
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_class " ) , & Object : : get_class ) ;
2019-06-26 15:57:13 +02:00
ClassDB : : bind_method ( D_METHOD ( " is_class " , " class " ) , & Object : : is_class ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set " , " property " , " value " ) , & Object : : _set_bind ) ;
2017-08-09 13:19:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " get " , " property " ) , & Object : : _get_bind ) ;
2022-10-12 10:27:44 +02: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-03-05 16:44:50 +01: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 16:55:26 +01: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-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " notification " , " what " , " reversed " ) , & Object : : notification , DEFVAL ( false ) ) ;
2019-04-10 07:07:40 +02:00
ClassDB : : bind_method ( D_METHOD ( " to_string " ) , & Object : : to_string ) ;
2017-08-07 12:17:31 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_instance_id " ) , & Object : : get_instance_id ) ;
2017-02-13 12:47:24 +01:00
2017-08-09 13:19:41 +02: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 12:47:24 +01:00
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_meta " , " name " , " value " ) , & Object : : set_meta ) ;
2019-04-26 19:23:50 +02:00
ClassDB : : bind_method ( D_METHOD ( " remove_meta " , " name " ) , & Object : : remove_meta ) ;
2022-02-27 22:03:33 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_meta " , " name " , " default " ) , & Object : : get_meta , DEFVAL ( Variant ( ) ) ) ;
2017-03-05 16:44:50 +01: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 02:10:30 +01:00
2017-03-05 16:44:50 +01: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 ) ;
2014-02-10 02:10:30 +01:00
{
MethodInfo mi ;
2017-03-05 16:44:50 +01:00
mi . name = " emit_signal " ;
2020-02-20 22:58:05 +01:00
mi . arguments . push_back ( PropertyInfo ( Variant : : STRING_NAME , " signal " ) ) ;
2014-02-10 02:10:30 +01:00
2020-01-02 09:31:43 +01:00
ClassDB : : bind_vararg_method ( METHOD_FLAGS_DEFAULT , " emit_signal " , & Object : : _emit_signal , mi , varray ( ) , false ) ;
2014-02-10 02:10:30 +01:00
}
{
MethodInfo mi ;
2017-03-05 16:44:50 +01:00
mi . name = " call " ;
2020-02-20 22:58:05 +01:00
mi . arguments . push_back ( PropertyInfo ( Variant : : STRING_NAME , " method " ) ) ;
2014-02-10 02:10:30 +01:00
2017-08-09 13:20:24 +02:00
ClassDB : : bind_vararg_method ( METHOD_FLAGS_DEFAULT , " call " , & Object : : _call_bind , mi ) ;
2014-02-10 02:10:30 +01:00
}
{
MethodInfo mi ;
2017-03-05 16:44:50 +01:00
mi . name = " call_deferred " ;
2020-02-20 22:58:05 +01:00
mi . arguments . push_back ( PropertyInfo ( Variant : : STRING_NAME , " method " ) ) ;
2014-02-10 02:10:30 +01:00
2020-01-02 09:31:43 +01:00
ClassDB : : bind_vararg_method ( METHOD_FLAGS_DEFAULT , " call_deferred " , & Object : : _call_deferred_bind , mi , varray ( ) , false ) ;
2014-02-10 02:10:30 +01:00
}
2018-11-16 12:49:26 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_deferred " , " property " , " value " ) , & Object : : set_deferred ) ;
2017-08-09 13:19:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " callv " , " method " , " arg_array " ) , & Object : : callv ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " has_method " , " method " ) , & Object : : has_method ) ;
2014-02-10 02:10:30 +01:00
2019-11-10 06:59:44 +01:00
ClassDB : : bind_method ( D_METHOD ( " has_signal " , " signal " ) , & Object : : has_signal ) ;
2017-03-05 16:44:50 +01: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 15:31:57 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_incoming_connections " ) , & Object : : _get_incoming_connections ) ;
2014-02-10 02:10:30 +01:00
2022-07-28 22:56:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " connect " , " signal " , " callable " , " flags " ) , & Object : : connect , DEFVAL ( 0 ) ) ;
2020-02-19 20:27:19 +01: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 02:10:30 +01:00
2017-03-05 16:44:50 +01: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 21:18:45 +01:00
ClassDB : : bind_method ( D_METHOD ( " notify_property_list_changed " ) , & Object : : notify_property_list_changed ) ;
2014-02-10 02:10:30 +01:00
2017-08-18 22:29:15 +02: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 ) ;
2020-07-16 10:52:06 +02:00
ClassDB : : bind_method ( D_METHOD ( " tr " , " message " , " context " ) , & Object : : tr , DEFVAL ( " " ) ) ;
ClassDB : : bind_method ( D_METHOD ( " tr_n " , " message " , " plural_message " , " n " , " context " ) , & Object : : tr_n , DEFVAL ( " " ) ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " is_queued_for_deletion " ) , & Object : : is_queued_for_deletion ) ;
2023-04-10 18:45:53 +02:00
ClassDB : : bind_method ( D_METHOD ( " cancel_free " ) , & Object : : cancel_free ) ;
2015-03-28 18:34:28 +01:00
2017-03-05 16:44:50 +01:00
ClassDB : : add_virtual_method ( " Object " , MethodInfo ( " free " ) , false ) ;
2015-04-13 03:22:44 +02:00
2017-03-05 16:44:50 +01:00
ADD_SIGNAL ( MethodInfo ( " script_changed " ) ) ;
2021-02-10 21:18:45 +01:00
ADD_SIGNAL ( MethodInfo ( " property_list_changed " ) ) ;
2014-02-10 02:10:30 +01:00
2021-08-22 03:52:44 +02:00
# define BIND_OBJ_CORE_METHOD(m_method) \
: : ClassDB : : add_virtual_method ( get_class_static ( ) , m_method , true , Vector < String > ( ) , true ) ;
2023-01-25 03:51:32 +01: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 03:52:44 +02:00
BIND_OBJ_CORE_METHOD ( MethodInfo ( Variant : : BOOL , " _set " , PropertyInfo ( Variant : : STRING_NAME , " property " ) , PropertyInfo ( Variant : : NIL , " value " ) ) ) ;
2014-02-10 02:10:30 +01:00
# ifdef TOOLS_ENABLED
2020-02-20 22:58:05 +01:00
MethodInfo miget ( " _get " , PropertyInfo ( Variant : : STRING_NAME , " property " ) ) ;
2017-07-18 17:39:46 +02:00
miget . return_val . name = " Variant " ;
2018-05-30 04:30:01 +02:00
miget . return_val . usage | = PROPERTY_USAGE_NIL_IS_VARIANT ;
2021-08-22 03:52:44 +02:00
BIND_OBJ_CORE_METHOD ( miget ) ;
2014-02-10 02:10:30 +01:00
2022-08-05 20:35:08 +02: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 14:01:57 +02:00
BIND_OBJ_CORE_METHOD ( MethodInfo ( Variant : : NIL , " _validate_property " , PropertyInfo ( Variant : : DICTIONARY , " property " ) ) ) ;
2022-08-12 20:43:14 +02: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 02:10:30 +01:00
# endif
2021-08-22 03:52:44 +02:00
BIND_OBJ_CORE_METHOD ( MethodInfo ( " _init " ) ) ;
BIND_OBJ_CORE_METHOD ( MethodInfo ( Variant : : STRING , " _to_string " ) ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
BIND_CONSTANT ( NOTIFICATION_POSTINITIALIZE ) ;
BIND_CONSTANT ( NOTIFICATION_PREDELETE ) ;
2014-02-10 02:10:30 +01:00
2017-08-20 17:45:01 +02:00
BIND_ENUM_CONSTANT ( CONNECT_DEFERRED ) ;
BIND_ENUM_CONSTANT ( CONNECT_PERSIST ) ;
2022-09-01 15:44:42 +02:00
BIND_ENUM_CONSTANT ( CONNECT_ONE_SHOT ) ;
2018-08-20 18:38:18 +02:00
BIND_ENUM_CONSTANT ( CONNECT_REFERENCE_COUNTED ) ;
2014-02-10 02:10:30 +01:00
}
2018-11-16 12:49:26 +01: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 02:10:30 +01:00
void Object : : set_block_signals ( bool p_block ) {
2017-03-05 16:44:50 +01:00
_block_signals = p_block ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
bool Object : : is_blocking_signals ( ) const {
2014-02-10 02:10:30 +01:00
return _block_signals ;
}
2017-03-05 16:44:50 +01:00
Variant : : Type Object : : get_static_property_type ( const StringName & p_property , bool * r_valid ) const {
2015-12-05 18:18:22 +01:00
bool valid ;
2017-03-05 16:44:50 +01:00
Variant : : Type t = ClassDB : : get_property_type ( get_class_name ( ) , p_property , & valid ) ;
2015-12-05 18:18:22 +01:00
if ( valid ) {
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2017-03-05 16:44:50 +01:00
* r_valid = true ;
2020-05-14 16:41:43 +02:00
}
2015-12-05 18:18:22 +01:00
return t ;
}
if ( get_script_instance ( ) ) {
2017-03-05 16:44:50 +01:00
return get_script_instance ( ) - > get_property_type ( p_property , r_valid ) ;
2015-12-05 18:18:22 +01:00
}
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2017-03-05 16:44:50 +01:00
* r_valid = false ;
2020-05-14 16:41:43 +02:00
}
2015-12-05 18:18:22 +01:00
return Variant : : NIL ;
}
2017-05-30 22:20:15 +02:00
Variant : : Type Object : : get_static_property_type_indexed ( const Vector < StringName > & p_path , bool * r_valid ) const {
2017-11-24 14:26:32 +01:00
if ( p_path . size ( ) = = 0 ) {
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2017-11-24 14:26:32 +01:00
* r_valid = false ;
2020-05-14 16:41:43 +02:00
}
2017-11-24 14:26:32 +01:00
return Variant : : NIL ;
}
2017-05-30 22:20:15 +02:00
bool valid = false ;
Variant : : Type t = get_static_property_type ( p_path [ 0 ] , & valid ) ;
if ( ! valid ) {
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2017-05-30 22:20:15 +02:00
* r_valid = false ;
2020-05-14 16:41:43 +02:00
}
2017-05-30 22:20:15 +02:00
return Variant : : NIL ;
}
2020-02-19 20:27:19 +01:00
Callable : : CallError ce ;
2020-11-09 04:19:09 +01:00
Variant check ;
Variant : : construct ( t , check , nullptr , 0 , ce ) ;
2017-05-30 22:20:15 +02: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 14:35:16 +01:00
// We cannot be sure about the type of properties this type can have
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2017-05-30 22:20:15 +02:00
* r_valid = false ;
2020-05-14 16:41:43 +02:00
}
2017-05-30 22:20:15 +02:00
return Variant : : NIL ;
}
2020-11-07 02:29:22 +01:00
check = check . get_named ( p_path [ i ] , valid ) ;
2017-05-30 22:20:15 +02:00
if ( ! valid ) {
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2017-05-30 22:20:15 +02:00
* r_valid = false ;
2020-05-14 16:41:43 +02:00
}
2017-05-30 22:20:15 +02:00
return Variant : : NIL ;
}
}
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2017-05-30 22:20:15 +02:00
* r_valid = true ;
2020-05-14 16:41:43 +02:00
}
2017-05-30 22:20:15 +02:00
return check . get_type ( ) ;
}
2015-03-28 18:34:28 +01:00
bool Object : : is_queued_for_deletion ( ) const {
return _is_queued_for_deletion ;
}
2014-02-10 02:10:30 +01:00
# ifdef TOOLS_ENABLED
void Object : : set_edited ( bool p_edited ) {
2017-03-05 16:44:50 +01:00
_edited = p_edited ;
2016-05-27 19:18:40 +02:00
_edited_version + + ;
2014-02-10 02:10:30 +01:00
}
bool Object : : is_edited ( ) const {
return _edited ;
}
2016-05-27 19:18:40 +02:00
uint32_t Object : : get_edited_version ( ) const {
return _edited_version ;
}
2014-02-10 02:10:30 +01:00
# endif
2023-06-09 19:30:39 +02:00
StringName Object : : get_class_name_for_extension ( const GDExtension * p_library ) const {
// 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 12:11:28 +01:00
void Object : : set_instance_binding ( void * p_token , void * p_binding , const GDExtensionInstanceBindingCallbacks * p_callbacks ) {
2021-07-15 16:41:57 +02:00
// This is only meant to be used on creation by the binder.
ERR_FAIL_COND ( _instance_bindings ! = nullptr ) ;
_instance_bindings = ( InstanceBinding * ) memalloc ( sizeof ( InstanceBinding ) ) ;
_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 ;
_instance_binding_count = 1 ;
}
2022-12-07 12:11:28 +01:00
void * Object : : get_instance_binding ( void * p_token , const GDExtensionInstanceBindingCallbacks * p_callbacks ) {
2021-07-08 21:16:02 +02: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 15:34:08 +01:00
}
2017-07-16 17:39:23 +02:00
}
2023-02-16 16:38:57 +01:00
if ( unlikely ( ! binding & & p_callbacks ) ) {
2021-07-08 21:16:02 +02: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 17:39:23 +02:00
2021-07-08 21:16:02 +02:00
if ( current_size = = 0 | | new_size > current_size ) {
_instance_bindings = ( InstanceBinding * ) memrealloc ( _instance_bindings , new_size * sizeof ( InstanceBinding ) ) ;
}
2017-07-16 17:39:23 +02:00
2021-07-08 21:16:02 +02: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 06:35:22 +01:00
2021-07-08 21:16:02 +02:00
binding = p_callbacks - > create_callback ( p_token , this ) ;
_instance_bindings [ _instance_binding_count ] . binding = binding ;
_instance_binding_count + + ;
}
_instance_binding_mutex . unlock ( ) ;
return binding ;
2019-02-04 20:39:02 +01:00
}
2021-08-16 17:16:36 +02: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 ;
}
2020-02-13 20:03:10 +01:00
void Object : : _construct_object ( bool p_reference ) {
type_is_reference = p_reference ;
2019-05-09 11:21:49 +02:00
_instance_id = ObjectDB : : add_instance ( this ) ;
2014-02-10 02:10:30 +01:00
2014-04-05 17:39:30 +02:00
# ifdef DEBUG_ENABLED
_lock_index . init ( 1 ) ;
# endif
2014-02-10 02:10:30 +01:00
}
2020-05-12 17:01:17 +02:00
2020-02-13 20:03:10 +01:00
Object : : Object ( bool p_reference ) {
_construct_object ( p_reference ) ;
}
Object : : Object ( ) {
_construct_object ( false ) ;
}
2014-02-10 02:10:30 +01:00
2022-03-23 10:08:58 +01:00
void Object : : detach_from_objectdb ( ) {
if ( _instance_id ! = ObjectID ( ) ) {
ObjectDB : : remove_instance ( this ) ;
_instance_id = ObjectID ( ) ;
}
}
2014-02-10 02:10:30 +01:00
Object : : ~ Object ( ) {
2020-05-14 16:41:43 +02:00
if ( script_instance ) {
2014-02-10 02:10:30 +01:00
memdelete ( script_instance ) ;
2020-05-14 16:41:43 +02:00
}
2020-04-02 01:20:12 +02:00
script_instance = nullptr ;
2014-02-10 02:10:30 +01:00
2021-06-04 19:33:48 +02:00
if ( _extension & & _extension - > free_instance ) {
2021-06-19 17:58:49 +02:00
_extension - > free_instance ( _extension - > class_userdata , _extension_instance ) ;
2021-06-04 19:33:48 +02:00
_extension = nullptr ;
_extension_instance = nullptr ;
}
2020-01-22 01:45:06 +01:00
if ( _emitting ) {
//@todo this may need to actually reach the debugger prioritarily somehow because it may crash before
2019-11-06 17:03:04 +01: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 01:45:06 +01:00
}
2023-05-04 01:20:15 +02:00
// Drop all connections to the signals of this object.
2022-05-08 10:09:19 +02: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 02:10:30 +01:00
2023-01-30 17:46:56 +01:00
for ( const KeyValue < Callable , SignalData : : Slot > & slot_kv : s - > slot_map ) {
2023-05-04 01:20:15 +02:00
Object * target = slot_kv . value . conn . callable . get_object ( ) ;
if ( likely ( target ) ) {
target - > connections . erase ( slot_kv . value . cE ) ;
}
2018-11-18 15:47:19 +01:00
}
2014-02-10 02:10:30 +01:00
2022-05-08 10:09:19 +02:00
signal_map . erase ( E . key ) ;
2014-02-10 02:10:30 +01:00
}
2023-05-04 01:20:15 +02:00
// Disconnect signals that connect to this object.
2017-03-05 16:44:50 +01:00
while ( connections . size ( ) ) {
2014-02-10 02:10:30 +01:00
Connection c = connections . front ( ) - > get ( ) ;
2023-05-04 01:20:15 +02: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 02:10:30 +01:00
}
2022-03-23 10:08:58 +01:00
if ( _instance_id ! = ObjectID ( ) ) {
ObjectDB : : remove_instance ( this ) ;
_instance_id = ObjectID ( ) ;
}
2017-03-05 16:44:50 +01:00
_predelete_ok = 2 ;
2017-07-16 17:39:23 +02:00
2021-07-08 21:16:02 +02:00
if ( _instance_bindings ! = nullptr ) {
for ( uint32_t i = 0 ; i < _instance_binding_count ; i + + ) {
if ( _instance_bindings [ i ] . free_callback ) {
2021-07-29 15:19:28 +02:00
_instance_bindings [ i ] . free_callback ( _instance_bindings [ i ] . token , this , _instance_bindings [ i ] . binding ) ;
2019-02-04 20:39:02 +01:00
}
2017-07-16 17:39:23 +02:00
}
2021-07-08 21:16:02 +02:00
memfree ( _instance_bindings ) ;
2017-07-16 17:39:23 +02:00
}
2014-02-10 02:10:30 +01:00
}
bool predelete_handler ( Object * p_object ) {
return p_object - > _predelete ( ) ;
}
void postinitialize_handler ( Object * p_object ) {
p_object - > _postinitialize ( ) ;
}
2020-02-13 20:03:10 +01:00
void ObjectDB : : debug_objects ( DebugFunc p_func ) {
spin_lock . lock ( ) ;
2020-10-25 17:16:29 +01: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 20:03:10 +01:00
}
spin_lock . unlock ( ) ;
2014-02-10 02:10:30 +01:00
}
2020-02-13 20:03:10 +01:00
void Object : : get_argument_options ( const StringName & p_function , int p_idx , List < String > * r_options ) const {
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 11:36:11 +02:00
if ( p_idx = = 0 ) {
if ( p_function = = " connect " | | p_function = = " is_connected " | | p_function = = " disconnect " | | p_function = = " emit_signal " | | p_function = = " has_signal " ) {
List < MethodInfo > signals ;
get_signal_list ( & signals ) ;
for ( const MethodInfo & E : signals ) {
r_options - > push_back ( E . name . quote ( ) ) ;
}
} else if ( p_function = = " call " | | p_function = = " call_deferred " | | p_function = = " callv " | | p_function = = " has_method " ) {
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 ( ) ) ;
}
} else if ( p_function = = " set " | | p_function = = " set_deferred " | | p_function = = " get " ) {
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 ( ) ) ;
}
}
} else if ( p_function = = " set_meta " | | p_function = = " get_meta " | | p_function = = " has_meta " | | p_function = = " remove_meta " ) {
for ( const KeyValue < StringName , Variant > & K : metadata ) {
r_options - > push_back ( String ( K . key ) . quote ( ) ) ;
}
}
} else if ( p_idx = = 2 ) {
if ( p_function = = " connect " ) {
// 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 20:03:10 +01:00
}
2014-02-10 02:10:30 +01:00
2020-02-13 20:03:10 +01: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 22:25:37 +01:00
2020-02-13 20:03:10 +01:00
int ObjectDB : : get_object_count ( ) {
return slot_count ;
2014-02-10 02:10:30 +01:00
}
2020-02-13 20:03:10 +01: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 18:03:15 +02:00
object_slots [ i ] . is_ref_counted = false ;
2020-02-13 20:03:10 +01:00
object_slots [ i ] . next_free = i ;
object_slots [ i ] . validator = 0 ;
}
slot_max = new_slot_max ;
}
2014-02-10 02:10:30 +01:00
2020-02-13 20:03:10 +01: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 18:03:15 +02:00
object_slots [ slot ] . is_ref_counted = p_object - > is_ref_counted ( ) ;
2020-02-13 20:03:10 +01: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 02:10:30 +01:00
2020-02-13 20:03:10 +01:00
uint64_t id = validator_counter ;
id < < = OBJECTDB_SLOT_MAX_COUNT_BITS ;
id | = uint64_t ( slot ) ;
2014-02-10 02:10:30 +01:00
2021-06-04 18:03:15 +02:00
if ( p_object - > is_ref_counted ( ) ) {
2020-02-13 20:03:10 +01:00
id | = OBJECTDB_REFERENCE_BIT ;
2014-02-10 02:10:30 +01:00
}
2017-01-07 22:25:37 +01:00
2020-02-13 20:03:10 +01:00
slot_count + + ;
2014-02-10 02:10:30 +01:00
2020-02-13 20:03:10 +01:00
spin_lock . unlock ( ) ;
return ObjectID ( id ) ;
2014-12-17 02:31:57 +01:00
}
2020-02-13 20:03:10 +01: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 02:10:30 +01:00
2020-02-13 20:03:10 +01:00
spin_lock . lock ( ) ;
2017-01-07 22:25:37 +01:00
2020-02-13 20:03:10 +01:00
# ifdef DEBUG_ENABLED
2017-01-07 22:25:37 +01:00
2020-02-13 20:03:10 +01: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 18:03:15 +02:00
object_slots [ slot ] . is_ref_counted = false ;
2020-02-13 20:03:10 +01:00
object_slots [ slot ] . object = nullptr ;
spin_lock . unlock ( ) ;
}
2014-02-10 02:10:30 +01:00
2017-01-07 22:25:37 +01:00
void ObjectDB : : setup ( ) {
2020-02-13 20:03:10 +01:00
//nothing to do now
2014-02-10 02:10:30 +01:00
}
void ObjectDB : : cleanup ( ) {
2020-02-13 20:03:10 +01:00
if ( slot_count > 0 ) {
spin_lock . lock ( ) ;
2016-03-09 00:00:52 +01:00
2020-06-08 16:39:08 +02:00
WARN_PRINT ( " ObjectDB instances leaked at exit (run with --verbose for details). " ) ;
2015-04-21 00:38:02 +02:00
if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) ) {
2020-05-06 14:19:01 +02: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 17:16:29 +01: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-21 00:38:02 +02:00
2020-10-25 17:16:29 +01: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 ) ) ;
}
2021-06-04 18:03:15 +02:00
uint64_t id = uint64_t ( i ) | ( uint64_t ( object_slots [ i ] . validator ) < < OBJECTDB_VALIDATOR_BITS ) | ( object_slots [ i ] . is_ref_counted ? OBJECTDB_REFERENCE_BIT : 0 ) ;
2020-10-25 17:16:29 +01:00
print_line ( " Leaked instance: " + String ( obj - > get_class ( ) ) + " : " + itos ( id ) + extra_info ) ;
2020-02-13 20:03:10 +01:00
2020-10-25 17:16:29 +01:00
count - - ;
}
2015-04-21 00:38:02 +02:00
}
2020-06-08 16:39:08 +02: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-21 00:38:02 +02:00
}
2020-02-13 20:03:10 +01:00
spin_lock . unlock ( ) ;
}
if ( object_slots ) {
memfree ( object_slots ) ;
2014-02-10 02:10:30 +01:00
}
}