2023-01-05 13:25:55 +01:00
/**************************************************************************/
/* gdscript.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
2017-11-16 18:38:18 +01:00
# include "gdscript.h"
2017-08-27 21:07:15 +02:00
2020-02-12 11:51:50 +01:00
# include <stdint.h>
2020-11-07 23:33:38 +01:00
# include "core/config/engine.h"
# include "core/config/project_settings.h"
# include "core/core_constants.h"
2019-04-10 07:07:40 +02:00
# include "core/core_string_names.h"
2021-06-11 14:51:48 +02:00
# include "core/io/file_access.h"
2018-09-11 18:13:45 +02:00
# include "core/io/file_access_encrypted.h"
# include "core/os/os.h"
2020-05-02 00:14:56 +02:00
# include "gdscript_analyzer.h"
2020-06-10 23:15:09 +02:00
# include "gdscript_cache.h"
2017-11-16 18:38:18 +01:00
# include "gdscript_compiler.h"
2020-05-02 00:14:56 +02:00
# include "gdscript_parser.h"
2021-10-07 14:39:52 +02:00
# include "gdscript_rpc_callable.h"
2020-06-12 00:31:28 +02:00
# include "gdscript_warning.h"
2014-02-10 02:10:30 +01:00
2021-04-07 15:12:51 +02:00
# ifdef TESTS_ENABLED
# include "tests/gdscript_test_runner.h"
# endif
2021-10-11 11:30:59 +02:00
# ifdef TOOLS_ENABLED
2022-07-29 02:36:26 +02:00
# include "editor/editor_paths.h"
2021-10-11 11:30:59 +02:00
# endif
2014-09-15 16:33:30 +02:00
///////////////////////////
2017-11-16 18:38:18 +01:00
GDScriptNativeClass : : GDScriptNativeClass ( const StringName & p_name ) {
2017-03-05 16:44:50 +01:00
name = p_name ;
2014-02-10 02:10:30 +01:00
}
2017-11-16 18:38:18 +01:00
bool GDScriptNativeClass : : _get ( const StringName & p_name , Variant & r_ret ) const {
2014-02-10 02:10:30 +01:00
bool ok ;
2022-05-09 11:47:10 +02:00
int64_t v = ClassDB : : get_integer_constant ( name , p_name , & ok ) ;
2014-02-10 02:10:30 +01:00
if ( ok ) {
2017-03-05 16:44:50 +01:00
r_ret = v ;
2014-02-10 02:10:30 +01:00
return true ;
} else {
return false ;
}
}
2017-11-16 18:38:18 +01:00
void GDScriptNativeClass : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " new " ) , & GDScriptNativeClass : : _new ) ;
2014-02-10 02:10:30 +01:00
}
2017-11-16 18:38:18 +01:00
Variant GDScriptNativeClass : : _new ( ) {
2021-06-18 00:03:09 +02:00
Object * o = instantiate ( ) ;
2019-08-09 06:49:33 +02:00
ERR_FAIL_COND_V_MSG ( ! o , Variant ( ) , " Class type: ' " + String ( name ) + " ' is not instantiable. " ) ;
2014-02-10 02:10:30 +01:00
2021-06-04 18:03:15 +02:00
RefCounted * rc = Object : : cast_to < RefCounted > ( o ) ;
if ( rc ) {
2022-05-03 01:43:50 +02:00
return Ref < RefCounted > ( rc ) ;
2014-02-10 02:10:30 +01:00
} else {
return o ;
}
}
2021-06-18 00:03:09 +02:00
Object * GDScriptNativeClass : : instantiate ( ) {
return ClassDB : : instantiate ( name ) ;
2014-02-10 02:10:30 +01:00
}
2022-04-06 19:14:38 +02:00
Variant GDScriptNativeClass : : callp ( const StringName & p_method , const Variant * * p_args , int p_argcount , Callable : : CallError & r_error ) {
if ( p_method = = SNAME ( " new " ) ) {
// Constructor.
return Object : : callp ( p_method , p_args , p_argcount , r_error ) ;
}
MethodBind * method = ClassDB : : get_method ( name , p_method ) ;
2023-01-29 00:33:01 +01:00
if ( method & & method - > is_static ( ) ) {
2022-04-06 19:14:38 +02:00
// Native static method.
return method - > call ( nullptr , p_args , p_argcount , r_error ) ;
}
r_error . error = Callable : : CallError : : CALL_ERROR_INVALID_METHOD ;
return Variant ( ) ;
}
2021-08-10 03:39:42 +02:00
GDScriptFunction * GDScript : : _super_constructor ( GDScript * p_script ) {
if ( p_script - > initializer ) {
return p_script - > initializer ;
} else {
2022-09-29 11:53:28 +02:00
GDScript * base_src = p_script - > _base ;
if ( base_src ! = nullptr ) {
return _super_constructor ( base_src ) ;
2021-08-10 03:39:42 +02:00
} else {
return nullptr ;
}
}
}
2020-05-02 00:14:56 +02:00
void GDScript : : _super_implicit_constructor ( GDScript * p_script , GDScriptInstance * p_instance , Callable : : CallError & r_error ) {
2022-09-29 11:53:28 +02:00
GDScript * base_src = p_script - > _base ;
if ( base_src ! = nullptr ) {
_super_implicit_constructor ( base_src , p_instance , r_error ) ;
2020-05-02 00:14:56 +02:00
if ( r_error . error ! = Callable : : CallError : : CALL_OK ) {
return ;
}
}
2022-06-15 16:28:40 +02:00
ERR_FAIL_NULL ( p_script - > implicit_initializer ) ;
2020-05-02 00:14:56 +02:00
p_script - > implicit_initializer - > call ( p_instance , nullptr , 0 , r_error ) ;
}
2021-06-04 18:03:15 +02:00
GDScriptInstance * GDScript : : _create_instance ( const Variant * * p_args , int p_argcount , Object * p_owner , bool p_is_ref_counted , Callable : : CallError & r_error ) {
2014-02-10 02:10:30 +01:00
/* STEP 1, CREATE */
2017-11-16 18:38:18 +01:00
GDScriptInstance * instance = memnew ( GDScriptInstance ) ;
2021-06-04 18:03:15 +02:00
instance - > base_ref_counted = p_is_ref_counted ;
2014-02-10 02:10:30 +01:00
instance - > members . resize ( member_indices . size ( ) ) ;
2017-03-05 16:44:50 +01:00
instance - > script = Ref < GDScript > ( this ) ;
instance - > owner = p_owner ;
2020-02-13 20:03:10 +01:00
instance - > owner_id = p_owner - > get_instance_id ( ) ;
2016-06-02 01:22:02 +02:00
# ifdef DEBUG_ENABLED
//needed for hot reloading
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , MemberInfo > & E : member_indices ) {
instance - > member_indices_cache [ E . key ] = E . value . index ;
2016-06-02 01:22:02 +02:00
}
# endif
2014-02-10 02:10:30 +01:00
instance - > owner - > set_script_instance ( instance ) ;
Fix misc. source comment typos
Found using `codespell -q 3 -S ./thirdparty,*.po -L ang,ba,cas,dof,doubleclick,fave,hist,leapyear,lod,nd,numer,ois,paket,seeked,sinc,switchs,te,uint -D ~/Projects/codespell/codespell_lib/data/dictionary.txt `
2019-09-19 20:36:39 +02:00
/* STEP 2, INITIALIZE AND CONSTRUCT */
2020-02-26 11:28:13 +01:00
{
2022-09-29 11:53:28 +02:00
MutexLock lock ( GDScriptLanguage : : singleton - > mutex ) ;
2020-02-26 11:28:13 +01:00
instances . insert ( instance - > owner ) ;
}
2020-05-02 00:14:56 +02:00
_super_implicit_constructor ( this , instance , r_error ) ;
2020-02-19 20:27:19 +01:00
if ( r_error . error ! = Callable : : CallError : : CALL_OK ) {
2017-03-05 16:44:50 +01:00
instance - > script = Ref < GDScript > ( ) ;
2020-04-02 01:20:12 +02:00
instance - > owner - > set_script_instance ( nullptr ) ;
2020-02-26 11:28:13 +01:00
{
2022-09-29 11:53:28 +02:00
MutexLock lock ( GDScriptLanguage : : singleton - > mutex ) ;
2020-02-26 11:28:13 +01:00
instances . erase ( p_owner ) ;
}
2019-10-03 23:01:12 +02:00
ERR_FAIL_V_MSG ( nullptr , " Error constructing a GDScriptInstance. " ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
if ( p_argcount < 0 ) {
return instance ;
}
2021-08-10 03:39:42 +02:00
initializer = _super_constructor ( this ) ;
2020-05-02 00:14:56 +02:00
if ( initializer ! = nullptr ) {
initializer - > call ( instance , p_args , p_argcount , r_error ) ;
if ( r_error . error ! = Callable : : CallError : : CALL_OK ) {
instance - > script = Ref < GDScript > ( ) ;
instance - > owner - > set_script_instance ( nullptr ) ;
{
2022-09-29 11:53:28 +02:00
MutexLock lock ( GDScriptLanguage : : singleton - > mutex ) ;
2020-05-02 00:14:56 +02:00
instances . erase ( p_owner ) ;
}
ERR_FAIL_V_MSG ( nullptr , " Error constructing a GDScriptInstance. " ) ;
}
}
2014-02-10 02:10:30 +01:00
//@TODO make thread safe
return instance ;
}
2020-02-19 20:27:19 +01:00
Variant GDScript : : _new ( const Variant * * p_args , int p_argcount , Callable : : CallError & r_error ) {
2014-02-10 02:10:30 +01:00
/* STEP 1, CREATE */
2016-02-28 03:10:44 +01:00
if ( ! valid ) {
2020-02-19 20:27:19 +01:00
r_error . error = Callable : : CallError : : CALL_ERROR_INVALID_METHOD ;
2016-02-28 03:10:44 +01:00
return Variant ( ) ;
}
2020-02-19 20:27:19 +01:00
r_error . error = Callable : : CallError : : CALL_OK ;
2022-05-03 01:43:50 +02:00
Ref < RefCounted > ref ;
2020-04-02 01:20:12 +02:00
Object * owner = nullptr ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
GDScript * _baseptr = this ;
2014-02-10 02:10:30 +01:00
while ( _baseptr - > _base ) {
2017-03-05 16:44:50 +01:00
_baseptr = _baseptr - > _base ;
2014-02-10 02:10:30 +01:00
}
2017-01-12 20:00:14 +01:00
ERR_FAIL_COND_V ( _baseptr - > native . is_null ( ) , Variant ( ) ) ;
2016-06-07 04:40:50 +02:00
if ( _baseptr - > native . ptr ( ) ) {
2021-06-18 00:03:09 +02:00
owner = _baseptr - > native - > instantiate ( ) ;
2016-06-07 04:40:50 +02:00
} else {
2021-06-04 18:03:15 +02:00
owner = memnew ( RefCounted ) ; //by default, no base means use reference
2016-06-07 04:40:50 +02:00
}
2019-08-09 06:49:33 +02:00
ERR_FAIL_COND_V_MSG ( ! owner , Variant ( ) , " Can't inherit from a virtual class. " ) ;
2014-02-10 02:10:30 +01:00
2021-06-04 18:03:15 +02:00
RefCounted * r = Object : : cast_to < RefCounted > ( owner ) ;
2014-02-10 02:10:30 +01:00
if ( r ) {
2022-05-03 01:43:50 +02:00
ref = Ref < RefCounted > ( r ) ;
2014-02-10 02:10:30 +01:00
}
2020-04-02 01:20:12 +02:00
GDScriptInstance * instance = _create_instance ( p_args , p_argcount , owner , r ! = nullptr , r_error ) ;
2014-02-10 02:10:30 +01:00
if ( ! instance ) {
if ( ref . is_null ( ) ) {
memdelete ( owner ) ; //no owner, sorry
}
return Variant ( ) ;
}
2016-03-09 00:00:52 +01:00
if ( ref . is_valid ( ) ) {
2014-02-10 02:10:30 +01:00
return ref ;
} else {
return owner ;
}
}
2021-06-18 00:03:09 +02:00
bool GDScript : : can_instantiate ( ) const {
2018-07-29 22:40:09 +02:00
# ifdef TOOLS_ENABLED
return valid & & ( tool | | ScriptServer : : is_scripting_enabled ( ) ) ;
# else
return valid ;
# endif
2014-02-10 02:10:30 +01:00
}
2016-08-25 22:45:20 +02:00
Ref < Script > GDScript : : get_base_script ( ) const {
if ( _base ) {
2017-03-05 16:44:50 +01:00
return Ref < GDScript > ( _base ) ;
2016-08-25 22:45:20 +02:00
} else {
return Ref < Script > ( ) ;
}
}
2023-01-19 19:12:25 +01:00
StringName GDScript : : get_global_name ( ) const {
return name ;
}
2014-02-10 02:10:30 +01:00
StringName GDScript : : get_instance_base_type ( ) const {
2020-05-14 16:41:43 +02:00
if ( native . is_valid ( ) ) {
2014-02-10 02:10:30 +01:00
return native - > get_name ( ) ;
2020-05-14 16:41:43 +02:00
}
if ( base . is_valid ( ) & & base - > is_valid ( ) ) {
2014-02-10 02:10:30 +01:00
return base - > get_instance_base_type ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return StringName ( ) ;
}
struct _GDScriptMemberSort {
2021-02-08 10:57:18 +01:00
int index = 0 ;
2014-02-10 02:10:30 +01:00
StringName name ;
2017-03-05 16:44:50 +01:00
_FORCE_INLINE_ bool operator < ( const _GDScriptMemberSort & p_member ) const { return index < p_member . index ; }
2014-02-10 02:10:30 +01:00
} ;
# ifdef TOOLS_ENABLED
void GDScript : : _placeholder_erased ( PlaceHolderScriptInstance * p_placeholder ) {
placeholders . erase ( p_placeholder ) ;
}
# endif
2016-01-03 00:17:31 +01:00
2020-11-29 03:37:57 +01:00
void GDScript : : _get_script_method_list ( List < MethodInfo > * r_list , bool p_include_base ) const {
2019-01-25 14:09:44 +01:00
const GDScript * current = this ;
while ( current ) {
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , GDScriptFunction * > & E : current - > member_functions ) {
GDScriptFunction * func = E . value ;
2019-01-25 14:09:44 +01:00
MethodInfo mi ;
2021-08-09 22:13:42 +02:00
mi . name = E . key ;
2022-07-17 07:26:57 +02:00
if ( func - > is_static ( ) ) {
mi . flags | = METHOD_FLAG_STATIC ;
}
2019-01-25 14:09:44 +01:00
for ( int i = 0 ; i < func - > get_argument_count ( ) ; i + + ) {
2020-11-29 03:37:57 +01:00
PropertyInfo arginfo = func - > get_argument_type ( i ) ;
2020-11-29 04:42:06 +01:00
# ifdef TOOLS_ENABLED
2020-11-29 03:37:57 +01:00
arginfo . name = func - > get_argument_name ( i ) ;
# endif
mi . arguments . push_back ( arginfo ) ;
2019-01-25 14:09:44 +01:00
}
2022-07-12 05:04:23 +02:00
# ifdef TOOLS_ENABLED
mi . default_arguments . append_array ( func - > get_default_arg_values ( ) ) ;
# endif
2019-01-25 14:09:44 +01:00
mi . return_val = func - > get_return_type ( ) ;
2020-11-29 03:37:57 +01:00
r_list - > push_back ( mi ) ;
}
if ( ! p_include_base ) {
return ;
2016-08-03 00:11:05 +02:00
}
2019-01-25 14:09:44 +01:00
current = current - > _base ;
2016-08-03 00:11:05 +02:00
}
}
2016-08-08 06:21:22 +02:00
2020-11-29 03:37:57 +01:00
void GDScript : : get_script_method_list ( List < MethodInfo > * r_list ) const {
_get_script_method_list ( r_list , true ) ;
}
void GDScript : : _get_script_property_list ( List < PropertyInfo > * r_list , bool p_include_base ) const {
2017-03-05 16:44:50 +01:00
const GDScript * sptr = this ;
2016-08-24 00:29:07 +02:00
List < PropertyInfo > props ;
2017-03-05 16:44:50 +01:00
while ( sptr ) {
2016-08-24 00:29:07 +02:00
Vector < _GDScriptMemberSort > msort ;
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , PropertyInfo > & E : sptr - > member_info ) {
2016-08-24 00:29:07 +02:00
_GDScriptMemberSort ms ;
2021-08-09 22:13:42 +02:00
ERR_CONTINUE ( ! sptr - > member_indices . has ( E . key ) ) ;
ms . index = sptr - > member_indices [ E . key ] . index ;
ms . name = E . key ;
2016-08-24 00:29:07 +02:00
msort . push_back ( ms ) ;
}
msort . sort ( ) ;
2021-03-14 08:21:32 +01:00
msort . reverse ( ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < msort . size ( ) ; i + + ) {
2016-08-24 00:29:07 +02:00
props . push_front ( sptr - > member_info [ msort [ i ] . name ] ) ;
}
2022-07-31 10:07:48 +02:00
# ifdef TOOLS_ENABLED
r_list - > push_back ( sptr - > get_class_category ( ) ) ;
# endif // TOOLS_ENABLED
for ( const PropertyInfo & E : props ) {
r_list - > push_back ( E ) ;
}
props . clear ( ) ;
2020-11-29 03:37:57 +01:00
if ( ! p_include_base ) {
break ;
}
2016-08-24 00:29:07 +02:00
sptr = sptr - > _base ;
}
}
2020-11-29 03:37:57 +01:00
void GDScript : : get_script_property_list ( List < PropertyInfo > * r_list ) const {
_get_script_property_list ( r_list , true ) ;
}
2017-03-05 16:44:50 +01:00
bool GDScript : : has_method ( const StringName & p_method ) const {
2016-08-08 06:21:22 +02:00
return member_functions . has ( p_method ) ;
}
2017-03-05 16:44:50 +01:00
MethodInfo GDScript : : get_method_info ( const StringName & p_method ) const {
2022-05-13 15:04:37 +02:00
HashMap < StringName , GDScriptFunction * > : : ConstIterator E = member_functions . find ( p_method ) ;
2020-05-14 16:41:43 +02:00
if ( ! E ) {
2016-08-08 06:21:22 +02:00
return MethodInfo ( ) ;
2020-05-14 16:41:43 +02:00
}
2016-08-08 06:21:22 +02:00
2022-05-13 15:04:37 +02:00
GDScriptFunction * func = E - > value ;
2016-08-08 06:21:22 +02:00
MethodInfo mi ;
2022-05-13 15:04:37 +02:00
mi . name = E - > key ;
2018-05-30 04:16:57 +02:00
for ( int i = 0 ; i < func - > get_argument_count ( ) ; i + + ) {
mi . arguments . push_back ( func - > get_argument_type ( i ) ) ;
2016-08-08 06:21:22 +02:00
}
2018-05-30 04:16:57 +02:00
mi . return_val = func - > get_return_type ( ) ;
2016-08-08 06:21:22 +02:00
return mi ;
}
2017-03-05 16:44:50 +01:00
bool GDScript : : get_property_default_value ( const StringName & p_property , Variant & r_value ) const {
2016-01-03 00:17:31 +01:00
# ifdef TOOLS_ENABLED
2022-05-13 15:04:37 +02:00
HashMap < StringName , Variant > : : ConstIterator E = member_default_values_cache . find ( p_property ) ;
2016-01-03 00:17:31 +01:00
if ( E ) {
2022-05-13 15:04:37 +02:00
r_value = E - > value ;
2016-01-03 00:17:31 +01:00
return true ;
}
if ( base_cache . is_valid ( ) ) {
2017-03-05 16:44:50 +01:00
return base_cache - > get_property_default_value ( p_property , r_value ) ;
2016-01-03 00:17:31 +01:00
}
# endif
return false ;
}
2017-03-05 16:44:50 +01:00
ScriptInstance * GDScript : : instance_create ( Object * p_this ) {
GDScript * top = this ;
2020-05-14 16:41:43 +02:00
while ( top - > _base ) {
2017-03-05 16:44:50 +01:00
top = top - > _base ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
if ( top - > native . is_valid ( ) ) {
2017-03-05 16:44:50 +01:00
if ( ! ClassDB : : is_parent_class ( p_this - > get_class_name ( ) , top - > native - > get_name ( ) ) ) {
2020-02-27 03:30:20 +01:00
if ( EngineDebugger : : is_active ( ) ) {
2022-10-04 12:56:49 +02:00
GDScriptLanguage : : get_singleton ( ) - > debug_break_parse ( _get_debug_path ( ) , 1 , " Script inherits from native type ' " + String ( top - > native - > get_name ( ) ) + " ', so it can't be assigned to an object of type: ' " + p_this - > get_class ( ) + " ' " ) ;
2014-02-10 02:10:30 +01:00
}
2022-10-04 12:56:49 +02:00
ERR_FAIL_V_MSG ( nullptr , " Script inherits from native type ' " + String ( top - > native - > get_name ( ) ) + " ', so it can't be assigned to an object of type ' " + p_this - > get_class ( ) + " ' " + " . " ) ;
2014-02-10 02:10:30 +01:00
}
}
2020-02-19 20:27:19 +01:00
Callable : : CallError unchecked_error ;
2021-06-04 18:03:15 +02:00
return _create_instance ( nullptr , 0 , p_this , Object : : cast_to < RefCounted > ( p_this ) ! = nullptr , unchecked_error ) ;
2014-02-10 02:10:30 +01:00
}
2018-07-29 22:40:09 +02:00
PlaceHolderScriptInstance * GDScript : : placeholder_instance_create ( Object * p_this ) {
# ifdef TOOLS_ENABLED
PlaceHolderScriptInstance * si = memnew ( PlaceHolderScriptInstance ( GDScriptLanguage : : get_singleton ( ) , Ref < Script > ( this ) , p_this ) ) ;
placeholders . insert ( si ) ;
2021-06-13 13:32:44 +02:00
_update_exports ( nullptr , false , si ) ;
2018-07-29 22:40:09 +02:00
return si ;
# else
2020-04-02 01:20:12 +02:00
return nullptr ;
2018-07-29 22:40:09 +02:00
# endif
}
2014-02-10 02:10:30 +01:00
bool GDScript : : instance_has ( const Object * p_this ) const {
2022-09-29 11:53:28 +02:00
MutexLock lock ( GDScriptLanguage : : singleton - > mutex ) ;
2017-01-10 23:02:52 +01:00
2020-02-26 11:28:13 +01:00
return instances . has ( ( Object * ) p_this ) ;
2014-02-10 02:10:30 +01:00
}
bool GDScript : : has_source_code ( ) const {
2021-12-09 10:42:46 +01:00
return ! source . is_empty ( ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 14:29:06 +02:00
2014-02-10 02:10:30 +01:00
String GDScript : : get_source_code ( ) const {
return source ;
}
2020-05-14 14:29:06 +02:00
2017-03-05 16:44:50 +01:00
void GDScript : : set_source_code ( const String & p_code ) {
2020-05-14 16:41:43 +02:00
if ( source = = p_code ) {
2014-12-07 06:04:20 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
source = p_code ;
2014-12-07 06:04:20 +01:00
# ifdef TOOLS_ENABLED
2017-03-05 16:44:50 +01:00
source_changed_cache = true ;
2014-12-07 06:04:20 +01:00
# endif
2014-09-22 05:50:48 +02:00
}
2014-12-07 06:04:20 +01:00
# ifdef TOOLS_ENABLED
2022-05-13 15:04:37 +02:00
void GDScript : : _update_exports_values ( HashMap < StringName , Variant > & values , List < PropertyInfo > & propnames ) {
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , Variant > & E : member_default_values_cache ) {
values [ E . key ] = E . value ;
2014-12-07 06:04:20 +01:00
}
2021-07-24 15:46:25 +02:00
for ( const PropertyInfo & E : members_cache ) {
2021-07-16 05:45:57 +02:00
propnames . push_back ( E ) ;
2014-12-07 06:04:20 +01:00
}
2022-07-31 10:07:48 +02:00
if ( base_cache . is_valid ( ) ) {
base_cache - > _update_exports_values ( values , propnames ) ;
}
2014-12-07 06:04:20 +01:00
}
2020-11-29 03:37:57 +01:00
void GDScript : : _add_doc ( const DocData : : ClassDoc & p_inner_class ) {
if ( _owner ) {
_owner - > _add_doc ( p_inner_class ) ;
} else {
for ( int i = 0 ; i < docs . size ( ) ; i + + ) {
if ( docs [ i ] . name = = p_inner_class . name ) {
2021-07-04 00:17:03 +02:00
docs . remove_at ( i ) ;
2020-11-29 03:37:57 +01:00
break ;
}
}
docs . append ( p_inner_class ) ;
}
}
void GDScript : : _clear_doc ( ) {
docs . clear ( ) ;
2020-11-29 04:42:06 +01:00
doc = DocData : : ClassDoc ( ) ;
2020-11-29 03:37:57 +01:00
}
void GDScript : : _update_doc ( ) {
_clear_doc ( ) ;
2022-12-01 11:20:42 +01:00
doc . script_path = vformat ( R " ( " % s " ) " , get_script_path ( ) . get_slice ( " :// " , 1 ) ) ;
2020-12-15 13:04:21 +01:00
if ( ! name . is_empty ( ) ) {
2020-11-29 03:37:57 +01:00
doc . name = name ;
} else {
doc . name = doc . script_path ;
}
if ( _owner ) {
doc . name = _owner - > doc . name + " . " + doc . name ;
doc . script_path = doc . script_path + " . " + doc . name ;
}
doc . is_script_doc = true ;
if ( base . is_valid ( ) & & base - > is_valid ( ) ) {
2021-12-09 10:42:46 +01:00
if ( ! base - > doc . name . is_empty ( ) ) {
2020-11-29 03:37:57 +01:00
doc . inherits = base - > doc . name ;
} else {
doc . inherits = base - > get_instance_base_type ( ) ;
}
} else if ( native . is_valid ( ) ) {
doc . inherits = native - > get_name ( ) ;
}
doc . brief_description = doc_brief_description ;
doc . description = doc_description ;
doc . tutorials = doc_tutorials ;
2021-08-09 22:13:42 +02:00
for ( const KeyValue < String , DocData : : EnumDoc > & E : doc_enums ) {
2021-12-09 10:42:46 +01:00
if ( ! E . value . description . is_empty ( ) ) {
2021-08-09 22:13:42 +02:00
doc . enums [ E . key ] = E . value . description ;
2020-11-29 03:37:57 +01:00
}
}
List < MethodInfo > methods ;
_get_script_method_list ( & methods , false ) ;
for ( int i = 0 ; i < methods . size ( ) ; i + + ) {
// Ignore internal methods.
if ( methods [ i ] . name [ 0 ] = = ' @ ' ) {
continue ;
}
DocData : : MethodDoc method_doc ;
const String & class_name = methods [ i ] . name ;
if ( member_functions . has ( class_name ) ) {
GDScriptFunction * fn = member_functions [ class_name ] ;
// Change class name if return type is script reference.
GDScriptDataType return_type = fn - > get_return_type ( ) ;
if ( return_type . kind = = GDScriptDataType : : GDSCRIPT ) {
methods [ i ] . return_val . class_name = _get_gdscript_reference_class_name ( Object : : cast_to < GDScript > ( return_type . script_type ) ) ;
}
2021-05-20 12:07:26 +02:00
// Change class name if argument is script reference.
2020-11-29 03:37:57 +01:00
for ( int j = 0 ; j < fn - > get_argument_count ( ) ; j + + ) {
GDScriptDataType arg_type = fn - > get_argument_type ( j ) ;
if ( arg_type . kind = = GDScriptDataType : : GDSCRIPT ) {
methods [ i ] . arguments [ j ] . class_name = _get_gdscript_reference_class_name ( Object : : cast_to < GDScript > ( arg_type . script_type ) ) ;
}
}
}
if ( doc_functions . has ( methods [ i ] . name ) ) {
DocData : : method_doc_from_methodinfo ( method_doc , methods [ i ] , doc_functions [ methods [ i ] . name ] ) ;
} else {
DocData : : method_doc_from_methodinfo ( method_doc , methods [ i ] , String ( ) ) ;
}
doc . methods . push_back ( method_doc ) ;
}
List < PropertyInfo > props ;
_get_script_property_list ( & props , false ) ;
for ( int i = 0 ; i < props . size ( ) ; i + + ) {
2022-07-15 06:00:21 +02:00
if ( props [ i ] . usage & PROPERTY_USAGE_CATEGORY | | props [ i ] . usage & PROPERTY_USAGE_GROUP | | props [ i ] . usage & PROPERTY_USAGE_SUBGROUP ) {
continue ;
}
2020-11-29 03:37:57 +01:00
ScriptMemberInfo scr_member_info ;
scr_member_info . propinfo = props [ i ] ;
scr_member_info . propinfo . usage | = PROPERTY_USAGE_NIL_IS_VARIANT ;
if ( member_indices . has ( props [ i ] . name ) ) {
const MemberInfo & mi = member_indices [ props [ i ] . name ] ;
scr_member_info . setter = mi . setter ;
scr_member_info . getter = mi . getter ;
if ( mi . data_type . kind = = GDScriptDataType : : GDSCRIPT ) {
scr_member_info . propinfo . class_name = _get_gdscript_reference_class_name (
Object : : cast_to < GDScript > ( mi . data_type . script_type ) ) ;
}
}
if ( member_default_values . has ( props [ i ] . name ) ) {
scr_member_info . has_default_value = true ;
scr_member_info . default_value = member_default_values [ props [ i ] . name ] ;
}
if ( doc_variables . has ( props [ i ] . name ) ) {
scr_member_info . doc_string = doc_variables [ props [ i ] . name ] ;
}
DocData : : PropertyDoc prop_doc ;
DocData : : property_doc_from_scriptmemberinfo ( prop_doc , scr_member_info ) ;
doc . properties . push_back ( prop_doc ) ;
}
List < MethodInfo > signals ;
_get_script_signal_list ( & signals , false ) ;
for ( int i = 0 ; i < signals . size ( ) ; i + + ) {
DocData : : MethodDoc signal_doc ;
if ( doc_signals . has ( signals [ i ] . name ) ) {
2021-10-17 05:54:27 +02:00
DocData : : signal_doc_from_methodinfo ( signal_doc , signals [ i ] , doc_signals [ signals [ i ] . name ] ) ;
2020-11-29 03:37:57 +01:00
} else {
DocData : : signal_doc_from_methodinfo ( signal_doc , signals [ i ] , String ( ) ) ;
}
doc . signals . push_back ( signal_doc ) ;
}
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , Variant > & E : constants ) {
if ( subclasses . has ( E . key ) ) {
2020-11-29 03:37:57 +01:00
continue ;
}
// Enums.
bool is_enum = false ;
2021-08-09 22:13:42 +02:00
if ( E . value . get_type ( ) = = Variant : : DICTIONARY ) {
if ( doc_enums . has ( E . key ) ) {
2020-11-29 03:37:57 +01:00
is_enum = true ;
2021-08-09 22:13:42 +02:00
for ( int i = 0 ; i < doc_enums [ E . key ] . values . size ( ) ; i + + ) {
doc_enums [ E . key ] . values . write [ i ] . enumeration = E . key ;
doc . constants . push_back ( doc_enums [ E . key ] . values [ i ] ) ;
2020-11-29 03:37:57 +01:00
}
}
}
if ( ! is_enum & & doc_enums . has ( " @unnamed_enums " ) ) {
for ( int i = 0 ; i < doc_enums [ " @unnamed_enums " ] . values . size ( ) ; i + + ) {
2021-08-09 22:13:42 +02:00
if ( E . key = = doc_enums [ " @unnamed_enums " ] . values [ i ] . name ) {
2020-11-29 03:37:57 +01:00
is_enum = true ;
DocData : : ConstantDoc constant_doc ;
constant_doc . enumeration = " @unnamed_enums " ;
2021-08-09 22:13:42 +02:00
DocData : : constant_doc_from_variant ( constant_doc , E . key , E . value , doc_enums [ " @unnamed_enums " ] . values [ i ] . description ) ;
2020-11-29 03:37:57 +01:00
doc . constants . push_back ( constant_doc ) ;
break ;
}
}
}
if ( ! is_enum ) {
DocData : : ConstantDoc constant_doc ;
2022-09-29 11:53:28 +02:00
String const_description ;
2021-08-09 22:13:42 +02:00
if ( doc_constants . has ( E . key ) ) {
2022-09-29 11:53:28 +02:00
const_description = doc_constants [ E . key ] ;
2020-11-29 03:37:57 +01:00
}
2022-09-29 11:53:28 +02:00
DocData : : constant_doc_from_variant ( constant_doc , E . key , E . value , const_description ) ;
2020-11-29 03:37:57 +01:00
doc . constants . push_back ( constant_doc ) ;
}
}
2022-12-23 23:39:24 +01:00
for ( KeyValue < StringName , Ref < GDScript > > & E : subclasses ) {
E . value - > _update_doc ( ) ;
}
2020-11-29 03:37:57 +01:00
_add_doc ( doc ) ;
}
2014-12-07 06:04:20 +01:00
# endif
2014-10-12 07:13:22 +02:00
2021-06-13 13:32:44 +02:00
bool GDScript : : _update_exports ( bool * r_err , bool p_recursive_call , PlaceHolderScriptInstance * p_instance_to_update ) {
2014-09-22 05:50:48 +02:00
# ifdef TOOLS_ENABLED
2014-10-12 07:13:22 +02:00
2020-03-19 07:18:46 +01:00
static Vector < GDScript * > base_caches ;
2020-05-14 16:41:43 +02:00
if ( ! p_recursive_call ) {
2020-03-19 07:18:46 +01:00
base_caches . clear ( ) ;
2020-05-14 16:41:43 +02:00
}
2020-03-19 07:18:46 +01:00
base_caches . append ( this ) ;
2017-03-05 16:44:50 +01:00
bool changed = false ;
2014-09-22 05:50:48 +02:00
2014-12-07 06:04:20 +01:00
if ( source_changed_cache ) {
2017-03-05 16:44:50 +01:00
source_changed_cache = false ;
changed = true ;
2014-09-22 05:50:48 +02:00
2017-03-05 16:44:50 +01:00
String basedir = path ;
2014-09-22 05:50:48 +02:00
2021-12-09 10:42:46 +01:00
if ( basedir . is_empty ( ) ) {
2017-03-05 16:44:50 +01:00
basedir = get_path ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-09-22 05:50:48 +02:00
2021-12-09 10:42:46 +01:00
if ( ! basedir . is_empty ( ) ) {
2017-03-05 16:44:50 +01:00
basedir = basedir . get_base_dir ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-09-22 05:50:48 +02:00
2017-11-16 18:38:18 +01:00
GDScriptParser parser ;
2020-05-02 00:14:56 +02:00
GDScriptAnalyzer analyzer ( & parser ) ;
Error err = parser . parse ( source , path , false ) ;
2014-09-22 05:50:48 +02:00
2020-05-02 00:14:56 +02:00
if ( err = = OK & & analyzer . analyze ( ) = = OK ) {
const GDScriptParser : : ClassNode * c = parser . get_tree ( ) ;
2014-10-12 07:13:22 +02:00
2014-12-07 06:04:20 +01:00
if ( base_cache . is_valid ( ) ) {
2017-08-07 12:17:31 +02:00
base_cache - > inheriters_cache . erase ( get_instance_id ( ) ) ;
2017-03-05 16:44:50 +01:00
base_cache = Ref < GDScript > ( ) ;
2014-12-07 06:04:20 +01:00
}
2014-10-12 07:13:22 +02:00
2022-11-08 12:51:20 +01:00
GDScriptParser : : DataType base_type = parser . get_tree ( ) - > base_type ;
if ( base_type . kind = = GDScriptParser : : DataType : : CLASS ) {
Ref < GDScript > bf = GDScriptCache : : get_full_script ( base_type . script_path , err , path ) ;
if ( err = = OK ) {
bf = Ref < GDScript > ( bf - > find_class ( base_type . class_type - > fqcn ) ) ;
if ( bf . is_valid ( ) ) {
base_cache = bf ;
bf - > inheriters_cache . insert ( get_instance_id ( ) ) ;
2016-01-02 17:56:58 +01:00
}
2014-12-07 06:04:20 +01:00
}
}
2017-01-14 18:03:38 +01:00
members_cache . clear ( ) ;
2014-12-07 06:04:20 +01:00
member_default_values_cache . clear ( ) ;
2020-05-02 00:14:56 +02:00
_signals . clear ( ) ;
2014-12-07 06:04:20 +01:00
2022-07-31 10:07:48 +02:00
members_cache . push_back ( get_class_category ( ) ) ;
2020-05-02 00:14:56 +02:00
for ( int i = 0 ; i < c - > members . size ( ) ; i + + ) {
const GDScriptParser : : ClassNode : : Member & member = c - > members [ i ] ;
2015-06-24 18:29:23 +02:00
2020-05-02 00:14:56 +02:00
switch ( member . type ) {
case GDScriptParser : : ClassNode : : Member : : VARIABLE : {
if ( ! member . variable - > exported ) {
continue ;
}
2015-06-24 18:29:23 +02:00
2020-05-02 00:14:56 +02:00
members_cache . push_back ( member . variable - > export_info ) ;
2022-11-27 08:56:53 +01:00
Variant default_value = analyzer . make_variable_default_value ( member . variable ) ;
2020-05-02 00:14:56 +02:00
member_default_values_cache [ member . variable - > identifier - > name ] = default_value ;
} break ;
case GDScriptParser : : ClassNode : : Member : : SIGNAL : {
// TODO: Cache this in parser to avoid loops like this.
Vector < StringName > parameters_names ;
parameters_names . resize ( member . signal - > parameters . size ( ) ) ;
for ( int j = 0 ; j < member . signal - > parameters . size ( ) ; j + + ) {
parameters_names . write [ j ] = member . signal - > parameters [ j ] - > identifier - > name ;
}
_signals [ member . signal - > identifier - > name ] = parameters_names ;
} break ;
2022-07-31 10:07:48 +02:00
case GDScriptParser : : ClassNode : : Member : : GROUP : {
members_cache . push_back ( member . annotation - > export_info ) ;
} break ;
2020-05-02 00:14:56 +02:00
default :
break ; // Nothing.
}
2015-06-24 18:29:23 +02:00
}
2018-07-29 22:40:09 +02:00
} else {
2019-01-10 00:26:00 +01:00
placeholder_fallback_enabled = true ;
2018-11-10 20:06:17 +01:00
return false ;
}
2019-02-14 02:36:19 +01:00
} else if ( placeholder_fallback_enabled ) {
2019-01-10 00:26:00 +01:00
return false ;
2014-09-22 05:50:48 +02:00
}
2019-01-10 00:26:00 +01:00
placeholder_fallback_enabled = false ;
2019-11-01 16:00:20 +01:00
if ( base_cache . is_valid ( ) & & base_cache - > is_valid ( ) ) {
2020-03-19 07:18:46 +01:00
for ( int i = 0 ; i < base_caches . size ( ) ; i + + ) {
if ( base_caches [ i ] = = base_cache . ptr ( ) ) {
2020-05-14 16:41:43 +02:00
if ( r_err ) {
2020-03-19 07:18:46 +01:00
* r_err = true ;
2020-05-14 16:41:43 +02:00
}
2020-03-19 07:18:46 +01:00
valid = false ; // to show error in the editor
base_cache - > valid = false ;
base_cache - > inheriters_cache . clear ( ) ; // to prevent future stackoverflows
base_cache . unref ( ) ;
base . unref ( ) ;
_base = nullptr ;
ERR_FAIL_V_MSG ( false , " Cyclic inheritance in script class. " ) ;
}
}
if ( base_cache - > _update_exports ( r_err , true ) ) {
2020-05-14 16:41:43 +02:00
if ( r_err & & * r_err ) {
2020-03-19 07:18:46 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-12-07 06:04:20 +01:00
changed = true ;
}
}
2021-06-13 13:32:44 +02:00
if ( ( changed | | p_instance_to_update ) & & placeholders . size ( ) ) { //hm :(
2014-12-07 06:04:20 +01:00
2018-08-24 09:35:07 +02:00
// update placeholders if any
2022-05-13 15:04:37 +02:00
HashMap < StringName , Variant > values ;
2014-12-07 06:04:20 +01:00
List < PropertyInfo > propnames ;
2017-03-05 16:44:50 +01:00
_update_exports_values ( values , propnames ) ;
2014-09-22 05:50:48 +02:00
2021-06-13 13:32:44 +02:00
if ( changed ) {
2022-05-19 01:43:40 +02:00
for ( PlaceHolderScriptInstance * E : placeholders ) {
E - > update ( propnames , values ) ;
2021-06-13 13:32:44 +02:00
}
} else {
p_instance_to_update - > update ( propnames , values ) ;
2014-12-07 06:04:20 +01:00
}
2014-09-22 05:50:48 +02:00
}
2014-12-07 06:04:20 +01:00
return changed ;
2017-08-22 19:01:57 +02:00
# else
2014-12-20 19:30:06 +01:00
return false ;
2017-08-22 19:01:57 +02:00
# endif
2014-10-12 07:13:22 +02:00
}
void GDScript : : update_exports ( ) {
# ifdef TOOLS_ENABLED
2020-03-19 07:18:46 +01:00
bool cyclic_error = false ;
_update_exports ( & cyclic_error ) ;
2020-05-14 16:41:43 +02:00
if ( cyclic_error ) {
2020-03-19 07:18:46 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-10-12 07:13:22 +02:00
2022-05-19 17:00:06 +02:00
HashSet < ObjectID > copy = inheriters_cache ; //might get modified
2014-12-07 06:04:20 +01:00
2022-05-19 01:43:40 +02:00
for ( const ObjectID & E : copy ) {
Object * id = ObjectDB : : get_instance ( E ) ;
2017-08-24 22:58:51 +02:00
GDScript * s = Object : : cast_to < GDScript > ( id ) ;
2020-05-14 16:41:43 +02:00
if ( ! s ) {
2014-12-07 06:04:20 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2014-12-07 06:04:20 +01:00
s - > update_exports ( ) ;
}
2014-09-22 05:50:48 +02:00
# endif
2014-02-10 02:10:30 +01:00
}
2021-11-11 19:50:27 +01:00
String GDScript : : _get_debug_path ( ) const {
if ( is_built_in ( ) & & ! get_name ( ) . is_empty ( ) ) {
2022-12-01 11:20:42 +01:00
return vformat ( " %s(%s) " , get_name(), get_script_path()) ;
2021-11-11 19:50:27 +01:00
} else {
2022-12-01 11:20:42 +01:00
return get_script_path ( ) ;
2021-11-11 19:50:27 +01:00
}
}
2016-06-02 01:22:02 +02:00
Error GDScript : : reload ( bool p_keep_state ) {
2022-10-09 18:41:28 +02:00
if ( reloading ) {
return OK ;
}
reloading = true ;
2020-02-26 11:28:13 +01:00
bool has_instances ;
{
2022-09-29 11:53:28 +02:00
MutexLock lock ( GDScriptLanguage : : singleton - > mutex ) ;
2017-01-10 23:02:52 +01:00
2020-02-26 11:28:13 +01:00
has_instances = instances . size ( ) ;
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( ! p_keep_state & & has_instances , ERR_ALREADY_IN_USE ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
String basedir = path ;
2014-02-10 02:10:30 +01:00
2021-12-09 10:42:46 +01:00
if ( basedir . is_empty ( ) ) {
2017-03-05 16:44:50 +01:00
basedir = get_path ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2021-12-09 10:42:46 +01:00
if ( ! basedir . is_empty ( ) ) {
2017-03-05 16:44:50 +01:00
basedir = basedir . get_base_dir ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2022-11-08 12:51:20 +01:00
// Loading a template, don't parse.
2021-10-11 11:30:59 +02:00
# ifdef TOOLS_ENABLED
2022-07-29 02:36:26 +02:00
if ( EditorPaths : : get_singleton ( ) & & basedir . begins_with ( EditorPaths : : get_singleton ( ) - > get_project_script_templates_dir ( ) ) ) {
2022-10-09 18:41:28 +02:00
reloading = false ;
2017-06-13 22:03:08 +02:00
return OK ;
}
2021-10-11 11:30:59 +02:00
# endif
2017-06-13 22:03:08 +02:00
2020-08-18 15:36:01 +02:00
{
String source_path = path ;
2020-12-15 13:04:21 +01:00
if ( source_path . is_empty ( ) ) {
2020-08-18 15:36:01 +02:00
source_path = get_path ( ) ;
}
2022-10-09 18:41:28 +02:00
Ref < GDScript > cached_script = GDScriptCache : : get_cached_script ( source_path ) ;
if ( ! source_path . is_empty ( ) & & cached_script . is_null ( ) ) {
MutexLock lock ( GDScriptCache : : singleton - > mutex ) ;
GDScriptCache : : singleton - > shallow_gdscript_cache [ source_path ] = Ref < GDScript > ( this ) ;
2020-08-18 15:36:01 +02:00
}
}
2017-03-05 16:44:50 +01:00
valid = false ;
2017-11-16 18:38:18 +01:00
GDScriptParser parser ;
2020-05-02 00:14:56 +02:00
Error err = parser . parse ( source , path , false ) ;
2014-02-10 02:10:30 +01:00
if ( err ) {
2020-02-27 03:30:20 +01:00
if ( EngineDebugger : : is_active ( ) ) {
2021-11-11 19:50:27 +01:00
GDScriptLanguage : : get_singleton ( ) - > debug_break_parse ( _get_debug_path ( ) , parser . get_errors ( ) . front ( ) - > get ( ) . line , " Parser Error: " + parser . get_errors ( ) . front ( ) - > get ( ) . message ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
// TODO: Show all error messages.
2021-09-22 17:36:40 +02:00
_err_print_error ( " GDScript::reload " , path . is_empty ( ) ? " built-in " : ( const char * ) path . utf8 ( ) . get_data ( ) , parser . get_errors ( ) . front ( ) - > get ( ) . line , ( " Parse Error: " + parser . get_errors ( ) . front ( ) - > get ( ) . message ) . utf8 ( ) . get_data ( ) , false , ERR_HANDLER_SCRIPT ) ;
2022-10-09 18:41:28 +02:00
reloading = false ;
2022-07-22 16:30:52 +02:00
return ERR_PARSE_ERROR ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
GDScriptAnalyzer analyzer ( & parser ) ;
err = analyzer . analyze ( ) ;
if ( err ) {
if ( EngineDebugger : : is_active ( ) ) {
2021-11-11 19:50:27 +01:00
GDScriptLanguage : : get_singleton ( ) - > debug_break_parse ( _get_debug_path ( ) , parser . get_errors ( ) . front ( ) - > get ( ) . line , " Parser Error: " + parser . get_errors ( ) . front ( ) - > get ( ) . message ) ;
2020-05-02 00:14:56 +02:00
}
2020-11-28 13:47:52 +01:00
const List < GDScriptParser : : ParserError > : : Element * e = parser . get_errors ( ) . front ( ) ;
while ( e ! = nullptr ) {
2021-09-22 17:36:40 +02:00
_err_print_error ( " GDScript::reload " , path . is_empty ( ) ? " built-in " : ( const char * ) path . utf8 ( ) . get_data ( ) , e - > get ( ) . line , ( " Parse Error: " + e - > get ( ) . message ) . utf8 ( ) . get_data ( ) , false , ERR_HANDLER_SCRIPT ) ;
2020-11-28 13:47:52 +01:00
e = e - > next ( ) ;
}
2022-10-09 18:41:28 +02:00
reloading = false ;
2022-07-22 16:30:52 +02:00
return ERR_PARSE_ERROR ;
2020-05-02 00:14:56 +02:00
}
bool can_run = ScriptServer : : is_scripting_enabled ( ) | | parser . is_tool ( ) ;
2016-01-23 19:36:03 +01:00
2017-11-16 18:38:18 +01:00
GDScriptCompiler compiler ;
2017-03-05 16:44:50 +01:00
err = compiler . compile ( & parser , this , p_keep_state ) ;
2014-02-10 02:10:30 +01:00
if ( err ) {
2016-01-23 19:36:03 +01:00
if ( can_run ) {
2020-02-27 03:30:20 +01:00
if ( EngineDebugger : : is_active ( ) ) {
2021-11-11 19:50:27 +01:00
GDScriptLanguage : : get_singleton ( ) - > debug_break_parse ( _get_debug_path ( ) , compiler . get_error_line ( ) , " Parser Error: " + compiler . get_error ( ) ) ;
2016-01-23 19:36:03 +01:00
}
2021-09-22 17:36:40 +02:00
_err_print_error ( " GDScript::reload " , path . is_empty ( ) ? " built-in " : ( const char * ) path . utf8 ( ) . get_data ( ) , compiler . get_error_line ( ) , ( " Compile Error: " + compiler . get_error ( ) ) . utf8 ( ) . get_data ( ) , false , ERR_HANDLER_SCRIPT ) ;
2022-10-09 18:41:28 +02:00
reloading = false ;
2022-07-22 16:30:52 +02:00
return ERR_COMPILATION_FAILED ;
2016-01-23 19:36:03 +01:00
} else {
2022-10-09 18:41:28 +02:00
reloading = false ;
2016-01-23 19:36:03 +01:00
return err ;
2014-02-10 02:10:30 +01:00
}
}
2019-04-05 23:20:20 +02:00
# ifdef DEBUG_ENABLED
2021-07-24 15:46:25 +02:00
for ( const GDScriptWarning & warning : parser . get_warnings ( ) ) {
2020-06-12 00:31:28 +02:00
if ( EngineDebugger : : is_active ( ) ) {
Vector < ScriptLanguage : : StackInfo > si ;
2022-12-01 11:20:42 +01:00
EngineDebugger : : get_script_debugger ( ) - > send_error ( " " , get_script_path ( ) , warning . start_line , warning . get_name ( ) , warning . get_message ( ) , false , ERR_HANDLER_WARNING , si ) ;
2020-06-12 00:31:28 +02:00
}
}
2018-07-01 18:17:40 +02:00
# endif
2014-02-10 02:10:30 +01:00
2022-10-09 18:41:28 +02:00
reloading = false ;
2014-02-10 02:10:30 +01:00
return OK ;
}
ScriptLanguage * GDScript : : get_language ( ) const {
return GDScriptLanguage : : get_singleton ( ) ;
}
2022-05-13 15:04:37 +02:00
void GDScript : : get_constants ( HashMap < StringName , Variant > * p_constants ) {
2017-10-17 16:35:49 +02:00
if ( p_constants ) {
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , Variant > & E : constants ) {
( * p_constants ) [ E . key ] = E . value ;
2017-10-17 16:35:49 +02:00
}
}
}
2022-05-19 17:00:06 +02:00
void GDScript : : get_members ( HashSet < StringName > * p_members ) {
2017-10-17 16:35:49 +02:00
if ( p_members ) {
2022-05-19 01:43:40 +02:00
for ( const StringName & E : members ) {
p_members - > insert ( E ) ;
2017-10-17 16:35:49 +02:00
}
}
}
2022-07-12 23:12:42 +02:00
const Variant GDScript : : get_rpc_config ( ) const {
return rpc_config ;
2020-02-12 11:51:50 +01:00
}
2022-03-09 14:58:40 +01:00
Variant GDScript : : callp ( const StringName & p_method , const Variant * * p_args , int p_argcount , Callable : : CallError & r_error ) {
2017-03-05 16:44:50 +01:00
GDScript * top = this ;
while ( top ) {
2022-05-13 15:04:37 +02:00
HashMap < StringName , GDScriptFunction * > : : Iterator E = top - > member_functions . find ( p_method ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2022-05-13 15:04:37 +02:00
ERR_FAIL_COND_V_MSG ( ! E - > value - > is_static ( ) , Variant ( ) , " Can't call non-static function ' " + String ( p_method ) + " ' in script. " ) ;
2014-02-10 02:10:30 +01:00
2022-05-13 15:04:37 +02:00
return E - > value - > call ( nullptr , p_args , p_argcount , r_error ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
top = top - > _base ;
2014-02-10 02:10:30 +01:00
}
//none found, regular
2022-03-09 14:58:40 +01:00
return Script : : callp ( p_method , p_args , p_argcount , r_error ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
bool GDScript : : _get ( const StringName & p_name , Variant & r_ret ) const {
2014-02-10 02:10:30 +01:00
{
2017-03-05 16:44:50 +01:00
const GDScript * top = this ;
while ( top ) {
2014-02-10 02:10:30 +01:00
{
2022-05-13 15:04:37 +02:00
HashMap < StringName , Variant > : : ConstIterator E = top - > constants . find ( p_name ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2022-05-13 15:04:37 +02:00
r_ret = E - > value ;
2014-02-10 02:10:30 +01:00
return true ;
}
}
{
2022-05-13 15:04:37 +02:00
HashMap < StringName , Ref < GDScript > > : : ConstIterator E = subclasses . find ( p_name ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2022-05-13 15:04:37 +02:00
r_ret = E - > value ;
2014-02-10 02:10:30 +01:00
return true ;
}
}
2017-03-05 16:44:50 +01:00
top = top - > _base ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
if ( p_name = = GDScriptLanguage : : get_singleton ( ) - > strings . _script_source ) {
r_ret = get_source_code ( ) ;
2014-02-16 01:16:33 +01:00
return true ;
}
}
2014-02-10 02:10:30 +01:00
2014-02-16 01:16:33 +01:00
return false ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 14:29:06 +02:00
2017-03-05 16:44:50 +01:00
bool GDScript : : _set ( const StringName & p_name , const Variant & p_value ) {
if ( p_name = = GDScriptLanguage : : get_singleton ( ) - > strings . _script_source ) {
2014-02-10 02:10:30 +01:00
set_source_code ( p_value ) ;
reload ( ) ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return true ;
}
void GDScript : : _get_property_list ( List < PropertyInfo > * p_properties ) const {
2021-11-03 23:06:17 +01:00
p_properties - > push_back ( PropertyInfo ( Variant : : STRING , " script/source " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL ) ) ;
2014-02-10 02:10:30 +01:00
}
void GDScript : : _bind_methods ( ) {
2019-08-26 18:36:51 +02:00
ClassDB : : bind_vararg_method ( METHOD_FLAGS_DEFAULT , " new " , & GDScript : : _new , MethodInfo ( " new " ) ) ;
2014-02-25 13:31:47 +01:00
}
2022-10-24 21:23:14 +02:00
void GDScript : : set_path ( const String & p_path , bool p_take_over ) {
2022-11-08 12:51:20 +01:00
if ( is_root_script ( ) ) {
Script : : set_path ( p_path , p_take_over ) ;
}
2023-03-08 19:46:55 +01:00
String old_path = path ;
path = p_path ;
2022-10-09 18:41:28 +02:00
GDScriptCache : : move_script ( old_path , p_path ) ;
2023-03-08 19:46:55 +01:00
2022-11-08 12:51:20 +01:00
for ( KeyValue < StringName , Ref < GDScript > > & kv : subclasses ) {
kv . value - > set_path ( p_path , p_take_over ) ;
}
2022-10-24 21:23:14 +02:00
}
2022-12-01 11:20:42 +01:00
String GDScript : : get_script_path ( ) const {
return path ;
}
2017-03-05 16:44:50 +01:00
Error GDScript : : load_source_code ( const String & p_path ) {
2023-01-11 02:08:46 +01:00
if ( p_path . is_empty ( ) | | p_path . begins_with ( " gdscript:// " ) | | ResourceLoader : : get_resource_type ( p_path . get_slice ( " :: " , 0 ) ) = = " PackedScene " ) {
2022-10-09 18:41:28 +02:00
return OK ;
}
2020-02-17 22:06:54 +01:00
Vector < uint8_t > sourcef ;
2014-02-10 02:10:30 +01:00
Error err ;
2022-03-23 10:08:58 +01:00
Ref < FileAccess > f = FileAccess : : open ( p_path , FileAccess : : READ , & err ) ;
2014-02-10 02:10:30 +01:00
if ( err ) {
2022-04-22 06:10:48 +02:00
const char * err_name ;
if ( err < 0 | | err > = ERR_MAX ) {
err_name = " (invalid error code) " ;
} else {
err_name = error_names [ err ] ;
}
ERR_FAIL_COND_V_MSG ( err , err , " Attempt to open script ' " + p_path + " ' resulted in error ' " + err_name + " '. " ) ;
2014-02-10 02:10:30 +01:00
}
2021-05-25 08:58:49 +02:00
uint64_t len = f - > get_length ( ) ;
2017-03-05 16:44:50 +01:00
sourcef . resize ( len + 1 ) ;
2020-02-17 22:06:54 +01:00
uint8_t * w = sourcef . ptrw ( ) ;
2019-03-26 18:51:13 +01:00
uint64_t r = f - > get_buffer ( w , len ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( r ! = len , ERR_CANT_OPEN ) ;
w [ len ] = 0 ;
2014-02-10 02:10:30 +01:00
String s ;
2022-07-05 14:18:29 +02:00
if ( s . parse_utf8 ( ( const char * ) w ) ! = OK ) {
2019-08-09 06:49:33 +02:00
ERR_FAIL_V_MSG ( ERR_INVALID_DATA , " Script ' " + p_path + " ' contains invalid unicode (UTF-8), so it was not loaded. Please ensure that scripts are saved in valid UTF-8 unicode. " ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
source = s ;
2022-09-12 08:21:00 +02:00
path = p_path ;
2014-12-07 06:39:51 +01:00
# ifdef TOOLS_ENABLED
2017-03-05 16:44:50 +01:00
source_changed_cache = true ;
2022-09-12 08:21:00 +02:00
set_edited ( false ) ;
set_last_modified_time ( FileAccess : : get_modified_time ( path ) ) ;
# endif // TOOLS_ENABLED
2014-02-10 02:10:30 +01:00
return OK ;
}
2022-05-13 15:04:37 +02:00
const HashMap < StringName , GDScriptFunction * > & GDScript : : debug_get_member_functions ( ) const {
2014-02-10 02:10:30 +01:00
return member_functions ;
}
StringName GDScript : : debug_get_member_by_index ( int p_idx ) const {
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , MemberInfo > & E : member_indices ) {
if ( E . value . index = = p_idx ) {
return E . key ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return " <error> " ;
}
Ref < GDScript > GDScript : : get_base ( ) const {
return base ;
}
2020-04-21 00:06:00 +02:00
bool GDScript : : inherits_script ( const Ref < Script > & p_script ) const {
Ref < GDScript > gd = p_script ;
if ( gd . is_null ( ) ) {
return false ;
}
const GDScript * s = this ;
while ( s ) {
if ( s = = p_script . ptr ( ) ) {
return true ;
}
s = s - > _base ;
}
return false ;
}
2022-11-08 12:51:20 +01:00
GDScript * GDScript : : find_class ( const String & p_qualified_name ) {
String first = p_qualified_name . get_slice ( " :: " , 0 ) ;
2022-12-12 13:35:55 +01:00
Vector < String > class_names ;
2022-11-08 12:51:20 +01:00
GDScript * result = nullptr ;
2022-12-12 13:35:55 +01:00
// Empty initial name means start here.
2022-11-08 12:51:20 +01:00
if ( first . is_empty ( ) | | first = = name ) {
2022-12-12 13:35:55 +01:00
class_names = p_qualified_name . split ( " :: " ) ;
2022-11-08 12:51:20 +01:00
result = this ;
2022-12-12 13:35:55 +01:00
} else if ( p_qualified_name . begins_with ( get_root_script ( ) - > path ) ) {
// Script path could have a class path separator("::") in it.
class_names = p_qualified_name . trim_prefix ( get_root_script ( ) - > path ) . split ( " :: " ) ;
2022-11-08 12:51:20 +01:00
result = get_root_script ( ) ;
} else if ( HashMap < StringName , Ref < GDScript > > : : Iterator E = subclasses . find ( first ) ) {
2022-12-12 13:35:55 +01:00
class_names = p_qualified_name . split ( " :: " ) ;
2022-11-08 12:51:20 +01:00
result = E - > value . ptr ( ) ;
} else if ( _owner ! = nullptr ) {
// Check parent scope.
return _owner - > find_class ( p_qualified_name ) ;
}
2022-12-12 13:35:55 +01:00
// Starts at index 1 because index 0 was handled above.
for ( int i = 1 ; result ! = nullptr & & i < class_names . size ( ) ; i + + ) {
String current_name = class_names [ i ] ;
2022-11-08 12:51:20 +01:00
if ( HashMap < StringName , Ref < GDScript > > : : Iterator E = result - > subclasses . find ( current_name ) ) {
result = E - > value . ptr ( ) ;
} else {
// Couldn't find inner class.
return nullptr ;
}
}
return result ;
}
2022-12-12 13:35:55 +01:00
bool GDScript : : has_class ( const GDScript * p_script ) {
2022-11-08 12:51:20 +01:00
String fqn = p_script - > fully_qualified_name ;
2022-12-12 13:35:55 +01:00
if ( fully_qualified_name . is_empty ( ) & & fqn . get_slice ( " :: " , 0 ) . is_empty ( ) ) {
return p_script = = this ;
} else if ( fqn . begins_with ( fully_qualified_name ) ) {
return p_script = = find_class ( fqn . trim_prefix ( fully_qualified_name ) ) ;
2022-11-08 12:51:20 +01:00
}
return false ;
}
GDScript * GDScript : : get_root_script ( ) {
GDScript * result = this ;
while ( result - > _owner ) {
result = result - > _owner ;
}
return result ;
}
2022-10-09 18:41:28 +02:00
RBSet < GDScript * > GDScript : : get_dependencies ( ) {
RBSet < GDScript * > dependencies ;
_get_dependencies ( dependencies , this ) ;
dependencies . erase ( this ) ;
return dependencies ;
}
RBSet < GDScript * > GDScript : : get_inverted_dependencies ( ) {
RBSet < GDScript * > inverted_dependencies ;
List < GDScript * > scripts ;
{
MutexLock lock ( GDScriptLanguage : : singleton - > mutex ) ;
SelfList < GDScript > * elem = GDScriptLanguage : : singleton - > script_list . first ( ) ;
while ( elem ) {
scripts . push_back ( elem - > self ( ) ) ;
elem = elem - > next ( ) ;
}
}
for ( GDScript * scr : scripts ) {
if ( scr = = nullptr | | scr = = this | | scr - > destructing ) {
continue ;
}
RBSet < GDScript * > scr_dependencies = scr - > get_dependencies ( ) ;
if ( scr_dependencies . has ( this ) ) {
inverted_dependencies . insert ( scr ) ;
}
}
return inverted_dependencies ;
}
RBSet < GDScript * > GDScript : : get_must_clear_dependencies ( ) {
RBSet < GDScript * > dependencies = get_dependencies ( ) ;
RBSet < GDScript * > must_clear_dependencies ;
HashMap < GDScript * , RBSet < GDScript * > > inverted_dependencies ;
for ( GDScript * E : dependencies ) {
inverted_dependencies . insert ( E , E - > get_inverted_dependencies ( ) ) ;
}
RBSet < GDScript * > cant_clear ;
for ( KeyValue < GDScript * , RBSet < GDScript * > > & E : inverted_dependencies ) {
for ( GDScript * F : E . value ) {
if ( ! dependencies . has ( F ) ) {
cant_clear . insert ( E . key ) ;
for ( GDScript * G : E . key - > get_dependencies ( ) ) {
cant_clear . insert ( G ) ;
}
break ;
}
}
}
for ( KeyValue < GDScript * , RBSet < GDScript * > > & E : inverted_dependencies ) {
if ( cant_clear . has ( E . key ) | | ScriptServer : : is_global_class ( E . key - > get_fully_qualified_name ( ) ) ) {
continue ;
}
must_clear_dependencies . insert ( E . key ) ;
}
cant_clear . clear ( ) ;
dependencies . clear ( ) ;
inverted_dependencies . clear ( ) ;
return must_clear_dependencies ;
}
2017-03-05 16:44:50 +01:00
bool GDScript : : has_script_signal ( const StringName & p_signal ) const {
2020-05-14 16:41:43 +02:00
if ( _signals . has ( p_signal ) ) {
2015-06-24 18:29:23 +02:00
return true ;
2020-05-14 16:41:43 +02:00
}
2015-06-24 18:29:23 +02:00
if ( base . is_valid ( ) ) {
return base - > has_script_signal ( p_signal ) ;
}
# ifdef TOOLS_ENABLED
2017-03-05 16:44:50 +01:00
else if ( base_cache . is_valid ( ) ) {
2015-06-24 18:29:23 +02:00
return base_cache - > has_script_signal ( p_signal ) ;
}
# endif
return false ;
}
2020-05-14 14:29:06 +02:00
2020-11-29 03:37:57 +01:00
void GDScript : : _get_script_signal_list ( List < MethodInfo > * r_list , bool p_include_base ) const {
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , Vector < StringName > > & E : _signals ) {
2015-06-24 18:29:23 +02:00
MethodInfo mi ;
2021-08-09 22:13:42 +02:00
mi . name = E . key ;
for ( int i = 0 ; i < E . value . size ( ) ; i + + ) {
2015-06-24 18:29:23 +02:00
PropertyInfo arg ;
2021-08-09 22:13:42 +02:00
arg . name = E . value [ i ] ;
2015-06-24 18:29:23 +02:00
mi . arguments . push_back ( arg ) ;
}
2020-11-29 03:37:57 +01:00
r_list - > push_back ( mi ) ;
}
if ( ! p_include_base ) {
return ;
2015-06-24 18:29:23 +02:00
}
if ( base . is_valid ( ) ) {
2020-11-29 03:37:57 +01:00
base - > get_script_signal_list ( r_list ) ;
2015-06-24 18:29:23 +02:00
}
# ifdef TOOLS_ENABLED
2017-03-05 16:44:50 +01:00
else if ( base_cache . is_valid ( ) ) {
2020-11-29 03:37:57 +01:00
base_cache - > get_script_signal_list ( r_list ) ;
2015-06-24 18:29:23 +02:00
}
# endif
}
2020-11-29 03:37:57 +01:00
void GDScript : : get_script_signal_list ( List < MethodInfo > * r_signals ) const {
_get_script_signal_list ( r_signals , true ) ;
}
String GDScript : : _get_gdscript_reference_class_name ( const GDScript * p_gdscript ) {
ERR_FAIL_NULL_V ( p_gdscript , String ( ) ) ;
String class_name ;
while ( p_gdscript ) {
2021-12-09 10:42:46 +01:00
if ( class_name . is_empty ( ) ) {
2020-11-29 03:37:57 +01:00
class_name = p_gdscript - > get_script_class_name ( ) ;
} else {
class_name = p_gdscript - > get_script_class_name ( ) + " . " + class_name ;
}
p_gdscript = p_gdscript - > _owner ;
}
return class_name ;
}
2022-10-09 18:41:28 +02:00
GDScript * GDScript : : _get_gdscript_from_variant ( const Variant & p_variant ) {
Object * obj = p_variant ;
2022-12-15 17:53:38 +01:00
if ( obj = = nullptr | | obj - > get_instance_id ( ) . is_null ( ) ) {
2022-10-09 18:41:28 +02:00
return nullptr ;
}
return Object : : cast_to < GDScript > ( obj ) ;
}
void GDScript : : _get_dependencies ( RBSet < GDScript * > & p_dependencies , const GDScript * p_except ) {
2023-01-07 16:13:45 +01:00
if ( p_dependencies . has ( this ) ) {
2022-10-09 18:41:28 +02:00
return ;
}
p_dependencies . insert ( this ) ;
for ( const KeyValue < StringName , GDScriptFunction * > & E : member_functions ) {
if ( E . value = = nullptr ) {
continue ;
}
for ( const Variant & V : E . value - > constants ) {
GDScript * scr = _get_gdscript_from_variant ( V ) ;
if ( scr ! = nullptr & & scr ! = p_except ) {
scr - > _get_dependencies ( p_dependencies , p_except ) ;
}
}
}
if ( implicit_initializer ) {
for ( const Variant & V : implicit_initializer - > constants ) {
GDScript * scr = _get_gdscript_from_variant ( V ) ;
if ( scr ! = nullptr & & scr ! = p_except ) {
scr - > _get_dependencies ( p_dependencies , p_except ) ;
}
}
}
if ( implicit_ready ) {
for ( const Variant & V : implicit_ready - > constants ) {
GDScript * scr = _get_gdscript_from_variant ( V ) ;
if ( scr ! = nullptr & & scr ! = p_except ) {
scr - > _get_dependencies ( p_dependencies , p_except ) ;
}
}
}
for ( KeyValue < StringName , Ref < GDScript > > & E : subclasses ) {
if ( E . value ! = p_except ) {
E . value - > _get_dependencies ( p_dependencies , p_except ) ;
}
}
for ( const KeyValue < StringName , Variant > & E : constants ) {
GDScript * scr = _get_gdscript_from_variant ( E . value ) ;
if ( scr ! = nullptr & & scr ! = p_except ) {
scr - > _get_dependencies ( p_dependencies , p_except ) ;
}
}
}
2017-12-06 21:36:34 +01:00
GDScript : : GDScript ( ) :
script_list ( this ) {
2020-02-26 11:28:13 +01:00
{
2022-09-29 11:53:28 +02:00
MutexLock lock ( GDScriptLanguage : : get_singleton ( ) - > mutex ) ;
2016-06-02 01:22:02 +02:00
2020-02-26 11:28:13 +01:00
GDScriptLanguage : : get_singleton ( ) - > script_list . add ( & script_list ) ;
2016-06-02 01:22:02 +02:00
}
2023-01-11 02:08:46 +01:00
path = vformat ( " gdscript://%d.gd " , get_instance_id ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2023-01-07 16:13:45 +01:00
void GDScript : : _save_orphaned_subclasses ( GDScript : : ClearData * p_clear_data ) {
2020-01-14 00:19:37 +01:00
struct ClassRefWithName {
ObjectID id ;
String fully_qualified_name ;
} ;
Vector < ClassRefWithName > weak_subclasses ;
// collect subclasses ObjectID and name
2021-08-09 22:13:42 +02:00
for ( KeyValue < StringName , Ref < GDScript > > & E : subclasses ) {
E . value - > _owner = nullptr ; //bye, you are no longer owned cause I died
2020-01-14 00:19:37 +01:00
ClassRefWithName subclass ;
2021-08-09 22:13:42 +02:00
subclass . id = E . value - > get_instance_id ( ) ;
subclass . fully_qualified_name = E . value - > fully_qualified_name ;
2020-01-14 00:19:37 +01:00
weak_subclasses . push_back ( subclass ) ;
}
// clear subclasses to allow unused subclasses to be deleted
2023-01-07 16:13:45 +01:00
for ( KeyValue < StringName , Ref < GDScript > > & E : subclasses ) {
p_clear_data - > scripts . insert ( E . value ) ;
}
2020-01-14 00:19:37 +01:00
subclasses . clear ( ) ;
// subclasses are also held by constants, clear those as well
2023-01-07 16:13:45 +01:00
for ( KeyValue < StringName , Variant > & E : constants ) {
GDScript * gdscr = _get_gdscript_from_variant ( E . value ) ;
if ( gdscr ! = nullptr ) {
p_clear_data - > scripts . insert ( gdscr ) ;
}
}
2020-01-14 00:19:37 +01:00
constants . clear ( ) ;
// keep orphan subclass only for subclasses that are still in use
for ( int i = 0 ; i < weak_subclasses . size ( ) ; i + + ) {
ClassRefWithName subclass = weak_subclasses [ i ] ;
Object * obj = ObjectDB : : get_instance ( subclass . id ) ;
2020-05-14 16:41:43 +02:00
if ( ! obj ) {
2020-01-14 00:19:37 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2020-01-14 00:19:37 +01:00
// subclass is not released
GDScriptLanguage : : get_singleton ( ) - > add_orphan_subclass ( subclass . fully_qualified_name , subclass . id ) ;
}
}
2020-02-27 16:06:39 +01:00
void GDScript : : _init_rpc_methods_properties ( ) {
// Copy the base rpc methods so we don't mask their IDs.
2022-07-12 23:12:42 +02:00
rpc_config . clear ( ) ;
2020-02-27 16:06:39 +01:00
if ( base . is_valid ( ) ) {
2022-07-12 23:12:42 +02:00
rpc_config = base - > rpc_config . duplicate ( ) ;
2020-02-27 16:06:39 +01:00
}
2022-11-08 12:51:20 +01:00
// RPC Methods
for ( KeyValue < StringName , GDScriptFunction * > & E : member_functions ) {
Variant config = E . value - > get_rpc_config ( ) ;
if ( config . get_type ( ) ! = Variant : : NIL ) {
rpc_config [ E . value - > get_name ( ) ] = config ;
2020-05-14 16:41:43 +02:00
}
2020-02-27 16:06:39 +01:00
}
}
2023-01-07 16:13:45 +01:00
void GDScript : : clear ( GDScript : : ClearData * p_clear_data ) {
2022-10-09 18:41:28 +02:00
if ( clearing ) {
return ;
}
clearing = true ;
2020-05-05 12:53:05 +02:00
2023-01-07 16:13:45 +01:00
GDScript : : ClearData data ;
GDScript : : ClearData * clear_data = p_clear_data ;
bool is_root = false ;
2022-10-09 18:41:28 +02:00
2023-01-07 16:13:45 +01:00
// If `clear_data` is `nullptr`, it means that it's the root.
// The root is in charge to clear functions and scripts of itself and its dependencies
if ( clear_data = = nullptr ) {
clear_data = & data ;
is_root = true ;
2022-10-09 18:41:28 +02:00
}
2023-01-07 16:13:45 +01:00
RBSet < GDScript * > must_clear_dependencies = get_must_clear_dependencies ( ) ;
2022-10-09 18:41:28 +02:00
for ( GDScript * E : must_clear_dependencies ) {
2023-01-07 16:13:45 +01:00
clear_data - > scripts . insert ( E ) ;
E - > clear ( clear_data ) ;
2020-05-05 12:53:05 +02:00
}
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , GDScriptFunction * > & E : member_functions ) {
2023-01-07 16:13:45 +01:00
clear_data - > functions . insert ( E . value ) ;
2022-10-09 18:41:28 +02:00
}
member_functions . clear ( ) ;
for ( KeyValue < StringName , GDScript : : MemberInfo > & E : member_indices ) {
2023-01-07 16:13:45 +01:00
clear_data - > scripts . insert ( E . value . data_type . script_type_ref ) ;
2022-10-09 18:41:28 +02:00
E . value . data_type . script_type_ref = Ref < Script > ( ) ;
2016-05-22 02:18:16 +02:00
}
2016-06-02 01:22:02 +02:00
2022-06-17 19:48:07 +02:00
if ( implicit_initializer ) {
2023-01-07 16:13:45 +01:00
clear_data - > functions . insert ( implicit_initializer ) ;
implicit_initializer = nullptr ;
2022-06-17 19:48:07 +02:00
}
2022-06-20 18:05:11 +02:00
if ( implicit_ready ) {
2023-01-07 16:13:45 +01:00
clear_data - > functions . insert ( implicit_ready ) ;
implicit_ready = nullptr ;
2022-06-20 18:05:11 +02:00
}
2020-06-10 23:15:09 +02:00
2023-01-07 16:13:45 +01:00
_save_orphaned_subclasses ( clear_data ) ;
2016-06-28 15:44:38 +02:00
2020-11-29 03:37:57 +01:00
# ifdef TOOLS_ENABLED
// Clearing inner class doc, script doc only cleared when the script source deleted.
if ( _owner ) {
_clear_doc ( ) ;
}
# endif
2023-01-07 16:13:45 +01:00
// If it's not the root, skip clearing the data
if ( is_root ) {
// All dependencies have been accounted for
for ( GDScriptFunction * E : clear_data - > functions ) {
memdelete ( E ) ;
}
for ( Ref < Script > & E : clear_data - > scripts ) {
Ref < GDScript > gdscr = E ;
if ( gdscr . is_valid ( ) ) {
GDScriptCache : : remove_script ( gdscr - > get_path ( ) ) ;
}
}
clear_data - > clear ( ) ;
}
2022-10-09 18:41:28 +02:00
}
GDScript : : ~ GDScript ( ) {
if ( destructing ) {
return ;
}
destructing = true ;
clear ( ) ;
{
MutexLock lock ( GDScriptLanguage : : get_singleton ( ) - > mutex ) ;
while ( SelfList < GDScriptFunctionState > * E = pending_func_states . first ( ) ) {
// Order matters since clearing the stack may already cause
// the GDScriptFunctionState to be destroyed and thus removed from the list.
pending_func_states . remove ( E ) ;
2022-09-16 17:42:06 +02:00
GDScriptFunctionState * state = E - > self ( ) ;
ObjectID state_id = state - > get_instance_id ( ) ;
state - > _clear_connections ( ) ;
if ( ObjectDB : : get_instance ( state_id ) ) {
state - > _clear_stack ( ) ;
}
2022-10-09 18:41:28 +02:00
}
}
2020-11-29 03:37:57 +01:00
2020-02-26 11:28:13 +01:00
{
2022-09-29 11:53:28 +02:00
MutexLock lock ( GDScriptLanguage : : get_singleton ( ) - > mutex ) ;
2016-06-02 01:22:02 +02:00
2020-02-26 11:28:13 +01:00
GDScriptLanguage : : get_singleton ( ) - > script_list . remove ( & script_list ) ;
2016-06-02 01:22:02 +02:00
}
2022-10-09 18:41:28 +02:00
if ( GDScriptCache : : singleton ) { // Cache may have been already destroyed at engine shutdown.
GDScriptCache : : remove_script ( get_path ( ) ) ;
}
2016-05-22 02:18:16 +02:00
}
2014-02-10 02:10:30 +01:00
//////////////////////////////
// INSTANCE //
//////////////////////////////
2017-11-16 18:38:18 +01:00
bool GDScriptInstance : : set ( const StringName & p_name , const Variant & p_value ) {
2014-02-10 02:10:30 +01:00
//member
{
2022-05-13 15:04:37 +02:00
HashMap < StringName , GDScript : : MemberInfo > : : Iterator E = script - > member_indices . find ( p_name ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2022-05-13 15:04:37 +02:00
const GDScript : : MemberInfo * member = & E - > value ;
2022-11-27 08:56:53 +01:00
Variant value = p_value ;
if ( member - > data_type . has_type & & ! member - > data_type . is_type ( value ) ) {
const Variant * args = & p_value ;
2020-02-19 20:27:19 +01:00
Callable : : CallError err ;
2022-11-27 08:56:53 +01:00
Variant : : construct ( member - > data_type . builtin_type , value , & args , 1 , err ) ;
if ( err . error ! = Callable : : CallError : : CALL_OK | | ! member - > data_type . is_type ( value ) ) {
2021-10-22 12:44:33 +02:00
return false ;
2014-10-28 02:54:32 +01:00
}
2022-11-27 08:56:53 +01:00
}
if ( member - > setter ) {
const Variant * args = & value ;
Callable : : CallError err ;
callp ( member - > setter , & args , 1 , err ) ;
return err . error = = Callable : : CallError : : CALL_OK ;
2018-05-30 04:16:56 +02:00
} else {
2022-11-27 08:56:53 +01:00
members . write [ member - > index ] = value ;
return true ;
2018-05-30 04:16:56 +02:00
}
2014-02-10 02:10:30 +01:00
}
}
2017-03-05 16:44:50 +01:00
GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
2022-05-13 15:04:37 +02:00
HashMap < StringName , GDScriptFunction * > : : Iterator E = sptr - > member_functions . find ( GDScriptLanguage : : get_singleton ( ) - > strings . _set ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2017-03-05 16:44:50 +01:00
Variant name = p_name ;
const Variant * args [ 2 ] = { & name , & p_value } ;
2014-02-10 02:10:30 +01:00
2020-02-19 20:27:19 +01:00
Callable : : CallError err ;
2022-05-13 15:04:37 +02:00
Variant ret = E - > value - > call ( this , ( const Variant * * ) args , 2 , err ) ;
2020-05-14 16:41:43 +02:00
if ( err . error = = Callable : : CallError : : CALL_OK & & ret . get_type ( ) = = Variant : : BOOL & & ret . operator bool ( ) ) {
2014-02-10 02:10:30 +01:00
return true ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
sptr = sptr - > _base ;
}
return false ;
}
2017-11-16 18:38:18 +01:00
bool GDScriptInstance : : get ( const StringName & p_name , Variant & r_ret ) const {
2017-03-05 16:44:50 +01:00
const GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
2014-02-10 02:10:30 +01:00
{
2022-05-13 15:04:37 +02:00
HashMap < StringName , GDScript : : MemberInfo > : : ConstIterator E = script - > member_indices . find ( p_name ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2022-05-13 15:04:37 +02:00
if ( E - > value . getter ) {
2020-02-19 20:27:19 +01:00
Callable : : CallError err ;
2022-05-13 15:04:37 +02:00
r_ret = const_cast < GDScriptInstance * > ( this ) - > callp ( E - > value . getter , nullptr , 0 , err ) ;
2020-02-19 20:27:19 +01:00
if ( err . error = = Callable : : CallError : : CALL_OK ) {
2014-10-28 02:54:32 +01:00
return true ;
}
}
2022-05-13 15:04:37 +02:00
r_ret = members [ E - > value . index ] ;
2014-02-10 02:10:30 +01:00
return true ; //index found
}
}
{
2016-06-28 16:15:55 +02:00
const GDScript * sl = sptr ;
2017-03-05 16:44:50 +01:00
while ( sl ) {
2022-05-13 15:04:37 +02:00
HashMap < StringName , Variant > : : ConstIterator E = sl - > constants . find ( p_name ) ;
2016-06-28 16:15:55 +02:00
if ( E ) {
2022-05-13 15:04:37 +02:00
r_ret = E - > value ;
2016-06-28 16:15:55 +02:00
return true ; //index found
}
2017-03-05 16:44:50 +01:00
sl = sl - > _base ;
2014-02-10 02:10:30 +01:00
}
}
2020-05-02 00:14:56 +02:00
{
// Signals.
const GDScript * sl = sptr ;
while ( sl ) {
2022-05-13 15:04:37 +02:00
HashMap < StringName , Vector < StringName > > : : ConstIterator E = sl - > _signals . find ( p_name ) ;
2020-05-02 00:14:56 +02:00
if ( E ) {
2022-05-13 15:04:37 +02:00
r_ret = Signal ( this - > owner , E - > key ) ;
2020-05-02 00:14:56 +02:00
return true ; //index found
}
sl = sl - > _base ;
}
}
{
// Methods.
const GDScript * sl = sptr ;
while ( sl ) {
2022-05-13 15:04:37 +02:00
HashMap < StringName , GDScriptFunction * > : : ConstIterator E = sl - > member_functions . find ( p_name ) ;
2020-05-02 00:14:56 +02:00
if ( E ) {
2022-07-12 23:12:42 +02:00
if ( sptr - > rpc_config . has ( p_name ) ) {
2022-05-13 15:04:37 +02:00
r_ret = Callable ( memnew ( GDScriptRPCCallable ( this - > owner , E - > key ) ) ) ;
2021-10-07 14:39:52 +02:00
} else {
2022-05-13 15:04:37 +02:00
r_ret = Callable ( this - > owner , E - > key ) ;
2021-10-07 14:39:52 +02:00
}
2020-05-02 00:14:56 +02:00
return true ; //index found
}
sl = sl - > _base ;
}
}
2014-02-10 02:10:30 +01:00
{
2022-05-13 15:04:37 +02:00
HashMap < StringName , GDScriptFunction * > : : ConstIterator E = sptr - > member_functions . find ( GDScriptLanguage : : get_singleton ( ) - > strings . _get ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2017-03-05 16:44:50 +01:00
Variant name = p_name ;
const Variant * args [ 1 ] = { & name } ;
2014-02-10 02:10:30 +01:00
2020-02-19 20:27:19 +01:00
Callable : : CallError err ;
2022-05-13 15:04:37 +02:00
Variant ret = const_cast < GDScriptFunction * > ( E - > value ) - > call ( const_cast < GDScriptInstance * > ( this ) , ( const Variant * * ) args , 1 , err ) ;
2020-02-19 20:27:19 +01:00
if ( err . error = = Callable : : CallError : : CALL_OK & & ret . get_type ( ) ! = Variant : : NIL ) {
2017-03-05 16:44:50 +01:00
r_ret = ret ;
2014-02-10 02:10:30 +01:00
return true ;
}
}
}
sptr = sptr - > _base ;
}
return false ;
}
2015-12-05 18:18:22 +01:00
2017-11-16 18:38:18 +01:00
Variant : : Type GDScriptInstance : : get_property_type ( const StringName & p_name , bool * r_is_valid ) const {
2017-03-05 16:44:50 +01:00
const GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
2015-12-05 18:18:22 +01:00
if ( sptr - > member_info . has ( p_name ) ) {
2020-05-14 16:41:43 +02:00
if ( r_is_valid ) {
2017-03-05 16:44:50 +01:00
* r_is_valid = true ;
2020-05-14 16:41:43 +02:00
}
2015-12-05 18:18:22 +01:00
return sptr - > member_info [ p_name ] . type ;
}
sptr = sptr - > _base ;
}
2020-05-14 16:41:43 +02:00
if ( r_is_valid ) {
2017-03-05 16:44:50 +01:00
* r_is_valid = false ;
2020-05-14 16:41:43 +02:00
}
2015-12-05 18:18:22 +01:00
return Variant : : NIL ;
}
2017-11-16 18:38:18 +01:00
void GDScriptInstance : : get_property_list ( List < PropertyInfo > * p_properties ) const {
2019-05-19 12:34:40 +02:00
// exported members, not done yet!
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
const GDScript * sptr = script . ptr ( ) ;
2014-02-10 02:10:30 +01:00
List < PropertyInfo > props ;
2017-03-05 16:44:50 +01:00
while ( sptr ) {
2022-05-13 15:04:37 +02:00
HashMap < StringName , GDScriptFunction * > : : ConstIterator E = sptr - > member_functions . find ( GDScriptLanguage : : get_singleton ( ) - > strings . _get_property_list ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2020-02-19 20:27:19 +01:00
Callable : : CallError err ;
2022-05-13 15:04:37 +02:00
Variant ret = const_cast < GDScriptFunction * > ( E - > value ) - > call ( const_cast < GDScriptInstance * > ( this ) , nullptr , 0 , err ) ;
2020-02-19 20:27:19 +01:00
if ( err . error = = Callable : : CallError : : CALL_OK ) {
2019-08-09 06:49:33 +02:00
ERR_FAIL_COND_MSG ( ret . get_type ( ) ! = Variant : : ARRAY , " Wrong type for _get_property_list, must be an array of dictionaries. " ) ;
2014-02-10 02:10:30 +01:00
Array arr = ret ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < arr . size ( ) ; i + + ) {
2014-02-10 02:10:30 +01:00
Dictionary d = arr [ i ] ;
ERR_CONTINUE ( ! d . has ( " name " ) ) ;
ERR_CONTINUE ( ! d . has ( " type " ) ) ;
PropertyInfo pinfo ;
2017-03-05 16:44:50 +01:00
pinfo . type = Variant : : Type ( d [ " type " ] . operator int ( ) ) ;
ERR_CONTINUE ( pinfo . type < 0 | | pinfo . type > = Variant : : VARIANT_MAX ) ;
2014-02-10 02:10:30 +01:00
pinfo . name = d [ " name " ] ;
2021-12-09 10:42:46 +01:00
ERR_CONTINUE ( pinfo . name . is_empty ( ) ) ;
2020-05-14 16:41:43 +02:00
if ( d . has ( " hint " ) ) {
2017-03-05 16:44:50 +01:00
pinfo . hint = PropertyHint ( d [ " hint " ] . operator int ( ) ) ;
2020-05-14 16:41:43 +02:00
}
if ( d . has ( " hint_string " ) ) {
2017-03-05 16:44:50 +01:00
pinfo . hint_string = d [ " hint_string " ] ;
2020-05-14 16:41:43 +02:00
}
if ( d . has ( " usage " ) ) {
2017-03-05 16:44:50 +01:00
pinfo . usage = d [ " usage " ] ;
2020-05-14 16:41:43 +02:00
}
2022-02-01 11:57:14 +01:00
if ( d . has ( " class_name " ) ) {
pinfo . class_name = d [ " class_name " ] ;
}
2014-02-10 02:10:30 +01:00
props . push_back ( pinfo ) ;
}
}
}
//instance a fake script for editing the values
Vector < _GDScriptMemberSort > msort ;
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , PropertyInfo > & F : sptr - > member_info ) {
2014-02-10 02:10:30 +01:00
_GDScriptMemberSort ms ;
2021-08-09 22:13:42 +02:00
ERR_CONTINUE ( ! sptr - > member_indices . has ( F . key ) ) ;
ms . index = sptr - > member_indices [ F . key ] . index ;
ms . name = F . key ;
2014-02-10 02:10:30 +01:00
msort . push_back ( ms ) ;
}
msort . sort ( ) ;
2021-03-14 08:21:32 +01:00
msort . reverse ( ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < msort . size ( ) ; i + + ) {
2014-02-10 02:10:30 +01:00
props . push_front ( sptr - > member_info [ msort [ i ] . name ] ) ;
}
2022-07-31 10:07:48 +02:00
# ifdef TOOLS_ENABLED
p_properties - > push_back ( sptr - > get_class_category ( ) ) ;
# endif // TOOLS_ENABLED
for ( const PropertyInfo & prop : props ) {
p_properties - > push_back ( prop ) ;
}
props . clear ( ) ;
2014-02-10 02:10:30 +01:00
2022-07-31 10:07:48 +02:00
sptr = sptr - > _base ;
2014-02-10 02:10:30 +01:00
}
}
2022-08-12 20:43:14 +02:00
bool GDScriptInstance : : property_can_revert ( const StringName & p_name ) const {
Variant name = p_name ;
const Variant * args [ 1 ] = { & name } ;
const GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
HashMap < StringName , GDScriptFunction * > : : ConstIterator E = sptr - > member_functions . find ( GDScriptLanguage : : get_singleton ( ) - > strings . _property_can_revert ) ;
if ( E ) {
Callable : : CallError err ;
Variant ret = E - > value - > call ( const_cast < GDScriptInstance * > ( this ) , args , 1 , err ) ;
if ( err . error = = Callable : : CallError : : CALL_OK & & ret . get_type ( ) = = Variant : : BOOL & & ret . operator bool ( ) ) {
return true ;
}
}
sptr = sptr - > _base ;
}
return false ;
}
bool GDScriptInstance : : property_get_revert ( const StringName & p_name , Variant & r_ret ) const {
Variant name = p_name ;
const Variant * args [ 1 ] = { & name } ;
const GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
HashMap < StringName , GDScriptFunction * > : : ConstIterator E = sptr - > member_functions . find ( GDScriptLanguage : : get_singleton ( ) - > strings . _property_get_revert ) ;
if ( E ) {
Callable : : CallError err ;
Variant ret = E - > value - > call ( const_cast < GDScriptInstance * > ( this ) , args , 1 , err ) ;
if ( err . error = = Callable : : CallError : : CALL_OK & & ret . get_type ( ) ! = Variant : : NIL ) {
r_ret = ret ;
return true ;
}
}
sptr = sptr - > _base ;
}
return false ;
}
2017-11-16 18:38:18 +01:00
void GDScriptInstance : : get_method_list ( List < MethodInfo > * p_list ) const {
2017-03-05 16:44:50 +01:00
const GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , GDScriptFunction * > & E : sptr - > member_functions ) {
2014-02-10 02:10:30 +01:00
MethodInfo mi ;
2021-08-09 22:13:42 +02:00
mi . name = E . key ;
for ( int i = 0 ; i < E . value - > get_argument_count ( ) ; i + + ) {
2017-03-05 16:44:50 +01:00
mi . arguments . push_back ( PropertyInfo ( Variant : : NIL , " arg " + itos ( i ) ) ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
p_list - > push_back ( mi ) ;
}
sptr = sptr - > _base ;
}
}
2017-11-16 18:38:18 +01:00
bool GDScriptInstance : : has_method ( const StringName & p_method ) const {
2017-03-05 16:44:50 +01:00
const GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
2022-05-13 15:04:37 +02:00
HashMap < StringName , GDScriptFunction * > : : ConstIterator E = sptr - > member_functions . find ( p_method ) ;
2020-05-14 16:41:43 +02:00
if ( E ) {
2014-02-10 02:10:30 +01:00
return true ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
sptr = sptr - > _base ;
}
return false ;
}
2020-05-14 14:29:06 +02:00
2022-03-09 14:58:40 +01:00
Variant GDScriptInstance : : callp ( const StringName & p_method , const Variant * * p_args , int p_argcount , Callable : : CallError & r_error ) {
2017-03-05 16:44:50 +01:00
GDScript * sptr = script . ptr ( ) ;
2022-06-20 18:05:11 +02:00
if ( unlikely ( p_method = = SNAME ( " _ready " ) ) ) {
// Call implicit ready first, including for the super classes.
while ( sptr ) {
if ( sptr - > implicit_ready ) {
sptr - > implicit_ready - > call ( this , nullptr , 0 , r_error ) ;
}
sptr = sptr - > _base ;
}
// Reset this back for the regular call.
sptr = script . ptr ( ) ;
}
2017-03-05 16:44:50 +01:00
while ( sptr ) {
2022-05-13 15:04:37 +02:00
HashMap < StringName , GDScriptFunction * > : : Iterator E = sptr - > member_functions . find ( p_method ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2022-05-13 15:04:37 +02:00
return E - > value - > call ( this , p_args , p_argcount , r_error ) ;
2014-02-10 02:10:30 +01:00
}
sptr = sptr - > _base ;
}
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 Variant ( ) ;
}
2017-11-16 18:38:18 +01:00
void GDScriptInstance : : notification ( int p_notification ) {
2017-03-24 21:45:31 +01:00
//notification is not virtual, it gets called at ALL levels just like in C.
2017-03-05 16:44:50 +01:00
Variant value = p_notification ;
const Variant * args [ 1 ] = { & value } ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
GDScript * sptr = script . ptr ( ) ;
while ( sptr ) {
2022-05-13 15:04:37 +02:00
HashMap < StringName , GDScriptFunction * > : : Iterator E = sptr - > member_functions . find ( GDScriptLanguage : : get_singleton ( ) - > strings . _notification ) ;
2014-02-10 02:10:30 +01:00
if ( E ) {
2020-02-19 20:27:19 +01:00
Callable : : CallError err ;
2022-05-13 15:04:37 +02:00
E - > value - > call ( this , args , 1 , err ) ;
2020-02-19 20:27:19 +01:00
if ( err . error ! = Callable : : CallError : : CALL_OK ) {
2014-02-10 02:10:30 +01:00
//print error about notification call
}
}
sptr = sptr - > _base ;
}
}
2019-04-10 07:07:40 +02:00
String GDScriptInstance : : to_string ( bool * r_valid ) {
if ( has_method ( CoreStringNames : : get_singleton ( ) - > _to_string ) ) {
2020-02-19 20:27:19 +01:00
Callable : : CallError ce ;
2022-03-09 14:58:40 +01:00
Variant ret = callp ( CoreStringNames : : get_singleton ( ) - > _to_string , nullptr , 0 , ce ) ;
2020-02-19 20:27:19 +01:00
if ( ce . error = = Callable : : CallError : : CALL_OK ) {
2019-04-10 07:07:40 +02:00
if ( ret . get_type ( ) ! = Variant : : STRING ) {
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2019-04-10 07:07:40 +02:00
* r_valid = false ;
2020-05-14 16:41:43 +02:00
}
2019-08-09 06:49:33 +02:00
ERR_FAIL_V_MSG ( String ( ) , " Wrong type for " + CoreStringNames : : get_singleton ( ) - > _to_string + " , must be a String. " ) ;
2019-04-10 07:07:40 +02:00
}
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2019-04-10 07:07:40 +02:00
* r_valid = true ;
2020-05-14 16:41:43 +02:00
}
2019-04-10 07:07:40 +02:00
return ret . operator String ( ) ;
}
}
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2019-04-10 07:07:40 +02:00
* r_valid = false ;
2020-05-14 16:41:43 +02:00
}
2019-04-10 07:07:40 +02:00
return String ( ) ;
}
2017-11-16 18:38:18 +01:00
Ref < Script > GDScriptInstance : : get_script ( ) const {
2014-02-10 02:10:30 +01:00
return script ;
}
2017-11-16 18:38:18 +01:00
ScriptLanguage * GDScriptInstance : : get_language ( ) {
2014-02-10 02:10:30 +01:00
return GDScriptLanguage : : get_singleton ( ) ;
}
2022-07-12 23:12:42 +02:00
const Variant GDScriptInstance : : get_rpc_config ( ) const {
return script - > get_rpc_config ( ) ;
2020-02-12 11:51:50 +01:00
}
2016-08-19 21:48:08 +02:00
2017-11-16 18:38:18 +01:00
void GDScriptInstance : : reload_members ( ) {
2016-06-02 01:22:02 +02:00
# ifdef DEBUG_ENABLED
Vector < Variant > new_members ;
new_members . resize ( script - > member_indices . size ( ) ) ;
//pass the values to the new indices
2021-08-09 22:13:42 +02:00
for ( KeyValue < StringName , GDScript : : MemberInfo > & E : script - > member_indices ) {
if ( member_indices_cache . has ( E . key ) ) {
Variant value = members [ member_indices_cache [ E . key ] ] ;
new_members . write [ E . value . index ] = value ;
2016-06-02 01:22:02 +02:00
}
}
2022-07-28 18:52:15 +02:00
members . resize ( new_members . size ( ) ) ; //resize
2016-06-02 01:22:02 +02:00
//apply
2017-03-05 16:44:50 +01:00
members = new_members ;
2016-06-02 01:22:02 +02:00
//pass the values to the new indices
member_indices_cache . clear ( ) ;
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , GDScript : : MemberInfo > & E : script - > member_indices ) {
member_indices_cache [ E . key ] = E . value . index ;
2016-06-02 01:22:02 +02:00
}
# endif
}
2014-02-10 02:10:30 +01:00
2017-11-16 18:38:18 +01:00
GDScriptInstance : : GDScriptInstance ( ) {
2020-04-02 01:20:12 +02:00
owner = nullptr ;
2021-06-04 18:03:15 +02:00
base_ref_counted = false ;
2014-02-10 02:10:30 +01:00
}
2017-11-16 18:38:18 +01:00
GDScriptInstance : : ~ GDScriptInstance ( ) {
2022-09-29 11:53:28 +02:00
MutexLock lock ( GDScriptLanguage : : get_singleton ( ) - > mutex ) ;
2020-05-05 12:53:05 +02:00
while ( SelfList < GDScriptFunctionState > * E = pending_func_states . first ( ) ) {
2020-09-06 22:30:46 +02:00
// Order matters since clearing the stack may already cause
// the GDSCriptFunctionState to be destroyed and thus removed from the list.
2020-05-05 12:53:05 +02:00
pending_func_states . remove ( E ) ;
2022-09-16 17:42:06 +02:00
GDScriptFunctionState * state = E - > self ( ) ;
ObjectID state_id = state - > get_instance_id ( ) ;
state - > _clear_connections ( ) ;
if ( ObjectDB : : get_instance ( state_id ) ) {
state - > _clear_stack ( ) ;
}
2020-05-05 12:53:05 +02:00
}
if ( script . is_valid ( ) & & owner ) {
2017-03-05 16:44:50 +01:00
script - > instances . erase ( owner ) ;
2014-02-10 02:10:30 +01:00
}
}
/************* SCRIPT LANGUAGE **************/
2020-04-02 01:20:12 +02:00
GDScriptLanguage * GDScriptLanguage : : singleton = nullptr ;
2014-02-10 02:10:30 +01:00
String GDScriptLanguage : : get_name ( ) const {
return " GDScript " ;
}
/* LANGUAGE FUNCTIONS */
2017-03-05 16:44:50 +01:00
void GDScriptLanguage : : _add_global ( const StringName & p_name , const Variant & p_value ) {
2014-02-10 02:10:30 +01:00
if ( globals . has ( p_name ) ) {
//overwrite existing
2018-07-25 03:11:03 +02:00
global_array . write [ globals [ p_name ] ] = p_value ;
2014-02-10 02:10:30 +01:00
return ;
}
2017-03-05 16:44:50 +01:00
globals [ p_name ] = global_array . size ( ) ;
2014-02-10 02:10:30 +01:00
global_array . push_back ( p_value ) ;
2017-11-25 04:07:54 +01:00
_global_array = global_array . ptrw ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void GDScriptLanguage : : add_global_constant ( const StringName & p_variable , const Variant & p_value ) {
_add_global ( p_variable , p_value ) ;
2015-12-28 19:59:20 +01:00
}
2018-05-01 16:06:23 +02:00
void GDScriptLanguage : : add_named_global_constant ( const StringName & p_name , const Variant & p_value ) {
named_globals [ p_name ] = p_value ;
}
2022-12-20 05:09:32 +01:00
Variant GDScriptLanguage : : get_any_global_constant ( const StringName & p_name ) {
if ( named_globals . has ( p_name ) ) {
return named_globals [ p_name ] ;
}
if ( globals . has ( p_name ) ) {
return _global_array [ globals [ p_name ] ] ;
}
ERR_FAIL_V_MSG ( Variant ( ) , vformat ( " Could not find any global constant with name: %s. " , p_name ) ) ;
}
2018-05-01 16:06:23 +02:00
void GDScriptLanguage : : remove_named_global_constant ( const StringName & p_name ) {
ERR_FAIL_COND ( ! named_globals . has ( p_name ) ) ;
named_globals . erase ( p_name ) ;
}
2014-02-10 02:10:30 +01:00
void GDScriptLanguage : : init ( ) {
//populate global constants
2020-11-07 23:33:38 +01:00
int gcc = CoreConstants : : get_global_constant_count ( ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < gcc ; i + + ) {
2020-11-07 23:33:38 +01:00
_add_global ( StaticCString : : create ( CoreConstants : : get_global_constant_name ( i ) ) , CoreConstants : : get_global_constant_value ( i ) ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
_add_global ( StaticCString : : create ( " PI " ) , Math_PI ) ;
2017-11-04 10:34:27 +01:00
_add_global ( StaticCString : : create ( " TAU " ) , Math_TAU ) ;
2021-07-21 10:40:31 +02:00
_add_global ( StaticCString : : create ( " INF " ) , INFINITY ) ;
_add_global ( StaticCString : : create ( " NAN " ) , NAN ) ;
2014-02-10 02:10:30 +01:00
//populate native classes
2015-06-29 05:29:49 +02:00
List < StringName > class_list ;
2017-01-03 03:03:46 +01:00
ClassDB : : get_class_list ( & class_list ) ;
2021-07-25 14:04:57 +02:00
for ( const StringName & n : class_list ) {
2021-08-17 15:06:54 +02:00
if ( globals . has ( n ) ) {
2014-02-10 02:10:30 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2021-07-16 05:45:57 +02:00
Ref < GDScriptNativeClass > nc = memnew ( GDScriptNativeClass ( n ) ) ;
2021-08-17 15:06:54 +02:00
_add_global ( n , nc ) ;
2014-02-10 02:10:30 +01:00
}
//populate singletons
2017-11-13 21:46:57 +01:00
List < Engine : : Singleton > singletons ;
Engine : : get_singleton ( ) - > get_singletons ( & singletons ) ;
2021-07-24 15:46:25 +02:00
for ( const Engine : : Singleton & E : singletons ) {
2021-07-16 05:45:57 +02:00
_add_global ( E . name , E . ptr ) ;
2014-02-10 02:10:30 +01:00
}
2021-04-07 15:12:51 +02:00
# ifdef TESTS_ENABLED
GDScriptTests : : GDScriptTestRunner : : handle_cmdline ( ) ;
# endif
2014-02-10 02:10:30 +01:00
}
String GDScriptLanguage : : get_type ( ) const {
return " GDScript " ;
}
2020-05-14 14:29:06 +02:00
2014-02-10 02:10:30 +01:00
String GDScriptLanguage : : get_extension ( ) const {
return " gd " ;
}
2020-05-14 14:29:06 +02:00
2017-03-05 16:44:50 +01:00
void GDScriptLanguage : : finish ( ) {
2022-12-02 19:27:26 +01:00
if ( _call_stack ) {
memdelete_arr ( _call_stack ) ;
_call_stack = nullptr ;
}
// Clear the cache before parsing the script_list
GDScriptCache : : clear ( ) ;
// Clear dependencies between scripts, to ensure cyclic references are broken
// (to avoid leaks at exit).
SelfList < GDScript > * s = script_list . first ( ) ;
while ( s ) {
// This ensures the current script is not released before we can check
// what's the next one in the list (we can't get the next upfront because we
// don't know if the reference breaking will cause it -or any other after
// it, for that matter- to be released so the next one is not the same as
// before).
Ref < GDScript > scr = s - > self ( ) ;
if ( scr . is_valid ( ) ) {
for ( KeyValue < StringName , GDScriptFunction * > & E : scr - > member_functions ) {
GDScriptFunction * func = E . value ;
for ( int i = 0 ; i < func - > argument_types . size ( ) ; i + + ) {
func - > argument_types . write [ i ] . script_type_ref = Ref < Script > ( ) ;
}
func - > return_type . script_type_ref = Ref < Script > ( ) ;
}
for ( KeyValue < StringName , GDScript : : MemberInfo > & E : scr - > member_indices ) {
E . value . data_type . script_type_ref = Ref < Script > ( ) ;
}
// Clear backup for scripts that could slip out of the cyclic reference
// check
scr - > clear ( ) ;
}
s = s - > next ( ) ;
}
2014-02-10 02:10:30 +01:00
}
2016-05-22 02:18:16 +02:00
void GDScriptLanguage : : profiling_start ( ) {
# ifdef DEBUG_ENABLED
2022-09-29 11:53:28 +02:00
MutexLock lock ( this - > mutex ) ;
2016-05-22 02:18:16 +02:00
2017-11-16 18:38:18 +01:00
SelfList < GDScriptFunction > * elem = function_list . first ( ) ;
2017-03-05 16:44:50 +01:00
while ( elem ) {
elem - > self ( ) - > profile . call_count = 0 ;
elem - > self ( ) - > profile . self_time = 0 ;
elem - > self ( ) - > profile . total_time = 0 ;
elem - > self ( ) - > profile . frame_call_count = 0 ;
elem - > self ( ) - > profile . frame_self_time = 0 ;
elem - > self ( ) - > profile . frame_total_time = 0 ;
elem - > self ( ) - > profile . last_frame_call_count = 0 ;
elem - > self ( ) - > profile . last_frame_self_time = 0 ;
elem - > self ( ) - > profile . last_frame_total_time = 0 ;
elem = elem - > next ( ) ;
2016-05-22 02:18:16 +02:00
}
2017-03-05 16:44:50 +01:00
profiling = true ;
2016-05-22 02:18:16 +02:00
# endif
}
void GDScriptLanguage : : profiling_stop ( ) {
# ifdef DEBUG_ENABLED
2022-09-29 11:53:28 +02:00
MutexLock lock ( this - > mutex ) ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
profiling = false ;
2016-05-22 02:18:16 +02:00
# endif
}
2017-03-05 16:44:50 +01:00
int GDScriptLanguage : : profiling_get_accumulated_data ( ProfilingInfo * p_info_arr , int p_info_max ) {
int current = 0 ;
2016-05-22 02:18:16 +02:00
# ifdef DEBUG_ENABLED
2020-02-26 11:28:13 +01:00
2022-09-29 11:53:28 +02:00
MutexLock lock ( this - > mutex ) ;
2016-05-22 02:18:16 +02:00
2017-11-16 18:38:18 +01:00
SelfList < GDScriptFunction > * elem = function_list . first ( ) ;
2017-03-05 16:44:50 +01:00
while ( elem ) {
2020-05-14 16:41:43 +02:00
if ( current > = p_info_max ) {
2016-05-22 02:18:16 +02:00
break ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
p_info_arr [ current ] . call_count = elem - > self ( ) - > profile . call_count ;
p_info_arr [ current ] . self_time = elem - > self ( ) - > profile . self_time ;
p_info_arr [ current ] . total_time = elem - > self ( ) - > profile . total_time ;
p_info_arr [ current ] . signature = elem - > self ( ) - > profile . signature ;
elem = elem - > next ( ) ;
2016-05-22 02:18:16 +02:00
current + + ;
}
# endif
return current ;
}
2017-03-05 16:44:50 +01:00
int GDScriptLanguage : : profiling_get_frame_data ( ProfilingInfo * p_info_arr , int p_info_max ) {
int current = 0 ;
2016-05-22 02:18:16 +02:00
# ifdef DEBUG_ENABLED
2022-09-29 11:53:28 +02:00
MutexLock lock ( this - > mutex ) ;
2016-05-22 02:18:16 +02:00
2017-11-16 18:38:18 +01:00
SelfList < GDScriptFunction > * elem = function_list . first ( ) ;
2017-03-05 16:44:50 +01:00
while ( elem ) {
2020-05-14 16:41:43 +02:00
if ( current > = p_info_max ) {
2016-05-22 02:18:16 +02:00
break ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
if ( elem - > self ( ) - > profile . last_frame_call_count > 0 ) {
p_info_arr [ current ] . call_count = elem - > self ( ) - > profile . last_frame_call_count ;
p_info_arr [ current ] . self_time = elem - > self ( ) - > profile . last_frame_self_time ;
p_info_arr [ current ] . total_time = elem - > self ( ) - > profile . last_frame_total_time ;
p_info_arr [ current ] . signature = elem - > self ( ) - > profile . signature ;
2016-05-22 02:18:16 +02:00
current + + ;
}
2017-03-05 16:44:50 +01:00
elem = elem - > next ( ) ;
2016-05-22 02:18:16 +02:00
}
# endif
return current ;
}
2016-06-02 01:22:02 +02:00
struct GDScriptDepSort {
//must support sorting so inheritance works properly (parent must be reloaded first)
2017-03-05 16:44:50 +01:00
bool operator ( ) ( const Ref < GDScript > & A , const Ref < GDScript > & B ) const {
2020-05-14 16:41:43 +02:00
if ( A = = B ) {
2016-06-02 01:22:02 +02:00
return false ; //shouldn't happen but..
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
const GDScript * I = B - > get_base ( ) . ptr ( ) ;
while ( I ) {
if ( I = = A . ptr ( ) ) {
2016-06-02 01:22:02 +02:00
// A is a base of B
return true ;
}
2017-03-05 16:44:50 +01:00
I = I - > get_base ( ) . ptr ( ) ;
2016-06-02 01:22:02 +02:00
}
return false ; //not a base
}
} ;
void GDScriptLanguage : : reload_all_scripts ( ) {
# ifdef DEBUG_ENABLED
2018-08-24 09:35:07 +02:00
print_verbose ( " GDScript: Reloading all scripts " ) ;
2020-03-17 07:33:00 +01:00
List < Ref < GDScript > > scripts ;
2020-02-26 11:28:13 +01:00
{
2022-09-29 11:53:28 +02:00
MutexLock lock ( this - > mutex ) ;
2016-06-02 01:22:02 +02:00
2020-02-26 11:28:13 +01:00
SelfList < GDScript > * elem = script_list . first ( ) ;
while ( elem ) {
2022-12-01 11:20:42 +01:00
// Scripts will reload all subclasses, so only reload root scripts.
if ( elem - > self ( ) - > is_root_script ( ) & & elem - > self ( ) - > get_path ( ) . is_resource_file ( ) ) {
2020-02-26 11:28:13 +01:00
print_verbose ( " GDScript: Found: " + elem - > self ( ) - > get_path ( ) ) ;
scripts . push_back ( Ref < GDScript > ( elem - > self ( ) ) ) ; //cast to gdscript to avoid being erased by accident
}
elem = elem - > next ( ) ;
2016-06-02 01:22:02 +02:00
}
}
//as scripts are going to be reloaded, must proceed without locking here
scripts . sort_custom < GDScriptDepSort > ( ) ; //update in inheritance dependency order
2022-09-29 11:53:28 +02:00
for ( Ref < GDScript > & scr : scripts ) {
print_verbose ( " GDScript: Reloading: " + scr - > get_path ( ) ) ;
scr - > load_source_code ( scr - > get_path ( ) ) ;
scr - > reload ( true ) ;
2016-06-02 01:22:02 +02:00
}
# endif
}
2017-03-05 16:44:50 +01:00
void GDScriptLanguage : : reload_tool_script ( const Ref < Script > & p_script , bool p_soft_reload ) {
2016-06-09 01:00:02 +02:00
# ifdef DEBUG_ENABLED
2020-03-17 07:33:00 +01:00
List < Ref < GDScript > > scripts ;
2020-02-26 11:28:13 +01:00
{
2022-09-29 11:53:28 +02:00
MutexLock lock ( this - > mutex ) ;
2016-06-09 01:00:02 +02:00
2020-02-26 11:28:13 +01:00
SelfList < GDScript > * elem = script_list . first ( ) ;
while ( elem ) {
2022-12-01 11:20:42 +01:00
// Scripts will reload all subclasses, so only reload root scripts.
if ( elem - > self ( ) - > is_root_script ( ) & & elem - > self ( ) - > get_path ( ) . is_resource_file ( ) ) {
2020-02-26 11:28:13 +01:00
scripts . push_back ( Ref < GDScript > ( elem - > self ( ) ) ) ; //cast to gdscript to avoid being erased by accident
}
elem = elem - > next ( ) ;
2016-06-09 01:00:02 +02:00
}
}
//when someone asks you why dynamically typed languages are easier to write....
2022-05-13 15:04:37 +02:00
HashMap < Ref < GDScript > , HashMap < ObjectID , List < Pair < StringName , Variant > > > > to_reload ;
2016-06-09 01:00:02 +02:00
//as scripts are going to be reloaded, must proceed without locking here
scripts . sort_custom < GDScriptDepSort > ( ) ; //update in inheritance dependency order
2022-09-29 11:53:28 +02:00
for ( Ref < GDScript > & scr : scripts ) {
bool reload = scr = = p_script | | to_reload . has ( scr - > get_base ( ) ) ;
2016-06-09 01:00:02 +02:00
2020-05-14 16:41:43 +02:00
if ( ! reload ) {
2016-06-09 01:00:02 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2016-06-09 01:00:02 +02:00
2022-09-29 11:53:28 +02:00
to_reload . insert ( scr , HashMap < ObjectID , List < Pair < StringName , Variant > > > ( ) ) ;
2016-06-09 01:00:02 +02:00
if ( ! p_soft_reload ) {
//save state and remove script from instances
2022-09-29 11:53:28 +02:00
HashMap < ObjectID , List < Pair < StringName , Variant > > > & map = to_reload [ scr ] ;
2016-06-09 01:00:02 +02:00
2022-09-29 11:53:28 +02:00
while ( scr - > instances . front ( ) ) {
Object * obj = scr - > instances . front ( ) - > get ( ) ;
2016-06-09 01:00:02 +02:00
//save instance info
2020-03-17 07:33:00 +01:00
List < Pair < StringName , Variant > > state ;
2016-06-09 01:00:02 +02:00
if ( obj - > get_script_instance ( ) ) {
obj - > get_script_instance ( ) - > get_property_state ( state ) ;
2017-08-07 12:17:31 +02:00
map [ obj - > get_instance_id ( ) ] = state ;
2020-02-13 20:03:10 +01:00
obj - > set_script ( Variant ( ) ) ;
2016-06-09 01:00:02 +02:00
}
}
2017-03-05 16:44:50 +01:00
//same thing for placeholders
2016-06-09 01:00:02 +02:00
# ifdef TOOLS_ENABLED
2022-09-29 11:53:28 +02:00
while ( scr - > placeholders . size ( ) ) {
Object * obj = ( * scr - > placeholders . begin ( ) ) - > get_owner ( ) ;
2016-06-09 01:00:02 +02:00
//save instance info
if ( obj - > get_script_instance ( ) ) {
2020-03-17 07:33:00 +01:00
map . insert ( obj - > get_instance_id ( ) , List < Pair < StringName , Variant > > ( ) ) ;
List < Pair < StringName , Variant > > & state = map [ obj - > get_instance_id ( ) ] ;
2016-06-09 01:00:02 +02:00
obj - > get_script_instance ( ) - > get_property_state ( state ) ;
2020-02-13 20:03:10 +01:00
obj - > set_script ( Variant ( ) ) ;
2017-04-24 18:51:39 +02:00
} else {
// no instance found. Let's remove it so we don't loop forever
2022-09-29 11:53:28 +02:00
scr - > placeholders . erase ( * scr - > placeholders . begin ( ) ) ;
2016-06-09 01:00:02 +02:00
}
}
2017-11-15 14:41:31 +01:00
2016-06-09 01:00:02 +02:00
# endif
2022-09-29 11:53:28 +02:00
for ( const KeyValue < ObjectID , List < Pair < StringName , Variant > > > & F : scr - > pending_reload_state ) {
2021-08-09 22:13:42 +02:00
map [ F . key ] = F . value ; //pending to reload, use this one instead
2016-07-21 03:37:48 +02:00
}
2016-06-09 01:00:02 +02:00
}
}
2022-05-13 15:04:37 +02:00
for ( KeyValue < Ref < GDScript > , HashMap < ObjectID , List < Pair < StringName , Variant > > > > & E : to_reload ) {
2021-08-09 22:13:42 +02:00
Ref < GDScript > scr = E . key ;
2016-07-21 03:37:48 +02:00
scr - > reload ( p_soft_reload ) ;
2016-06-09 01:00:02 +02:00
//restore state if saved
2021-08-09 22:13:42 +02:00
for ( KeyValue < ObjectID , List < Pair < StringName , Variant > > > & F : E . value ) {
List < Pair < StringName , Variant > > & saved_state = F . value ;
2019-01-10 00:26:00 +01:00
2021-08-09 22:13:42 +02:00
Object * obj = ObjectDB : : get_instance ( F . key ) ;
2020-05-14 16:41:43 +02:00
if ( ! obj ) {
2016-06-09 01:00:02 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2016-06-09 01:00:02 +02:00
2016-07-21 03:37:48 +02:00
if ( ! p_soft_reload ) {
//clear it just in case (may be a pending reload state)
2020-02-13 20:03:10 +01:00
obj - > set_script ( Variant ( ) ) ;
2016-07-21 03:37:48 +02:00
}
2020-02-13 20:03:10 +01:00
obj - > set_script ( scr ) ;
2019-01-10 00:26:00 +01:00
2022-09-29 11:53:28 +02:00
ScriptInstance * script_inst = obj - > get_script_instance ( ) ;
2019-01-10 00:26:00 +01:00
2022-09-29 11:53:28 +02:00
if ( ! script_inst ) {
2016-07-21 03:37:48 +02:00
//failed, save reload state for next time if not saved
2017-08-07 12:17:31 +02:00
if ( ! scr - > pending_reload_state . has ( obj - > get_instance_id ( ) ) ) {
2019-01-10 00:26:00 +01:00
scr - > pending_reload_state [ obj - > get_instance_id ( ) ] = saved_state ;
2016-07-21 03:37:48 +02:00
}
2016-06-09 01:00:02 +02:00
continue ;
2016-07-21 03:37:48 +02:00
}
2016-06-09 01:00:02 +02:00
2022-09-29 11:53:28 +02:00
if ( script_inst - > is_placeholder ( ) & & scr - > is_placeholder_fallback_enabled ( ) ) {
PlaceHolderScriptInstance * placeholder = static_cast < PlaceHolderScriptInstance * > ( script_inst ) ;
2020-03-17 07:33:00 +01:00
for ( List < Pair < StringName , Variant > > : : Element * G = saved_state . front ( ) ; G ; G = G - > next ( ) ) {
2019-01-10 00:26:00 +01:00
placeholder - > property_set_fallback ( G - > get ( ) . first , G - > get ( ) . second ) ;
}
} else {
2020-03-17 07:33:00 +01:00
for ( List < Pair < StringName , Variant > > : : Element * G = saved_state . front ( ) ; G ; G = G - > next ( ) ) {
2022-09-29 11:53:28 +02:00
script_inst - > set ( G - > get ( ) . first , G - > get ( ) . second ) ;
2019-01-10 00:26:00 +01:00
}
2016-06-09 01:00:02 +02:00
}
2016-07-21 03:37:48 +02:00
2017-08-07 12:17:31 +02:00
scr - > pending_reload_state . erase ( obj - > get_instance_id ( ) ) ; //as it reloaded, remove pending state
2016-06-09 01:00:02 +02:00
}
//if instance states were saved, set them!
}
# endif
}
2014-02-10 02:10:30 +01:00
void GDScriptLanguage : : frame ( ) {
2017-03-05 16:44:50 +01:00
calls = 0 ;
2016-05-22 02:18:16 +02:00
# ifdef DEBUG_ENABLED
if ( profiling ) {
2022-09-29 11:53:28 +02:00
MutexLock lock ( this - > mutex ) ;
2016-05-22 02:18:16 +02:00
2017-11-16 18:38:18 +01:00
SelfList < GDScriptFunction > * elem = function_list . first ( ) ;
2017-03-05 16:44:50 +01:00
while ( elem ) {
elem - > self ( ) - > profile . last_frame_call_count = elem - > self ( ) - > profile . frame_call_count ;
elem - > self ( ) - > profile . last_frame_self_time = elem - > self ( ) - > profile . frame_self_time ;
elem - > self ( ) - > profile . last_frame_total_time = elem - > self ( ) - > profile . frame_total_time ;
elem - > self ( ) - > profile . frame_call_count = 0 ;
elem - > self ( ) - > profile . frame_self_time = 0 ;
elem - > self ( ) - > profile . frame_total_time = 0 ;
elem = elem - > next ( ) ;
2016-05-22 02:18:16 +02:00
}
}
# endif
2014-02-10 02:10:30 +01:00
}
/* EDITOR FUNCTIONS */
2017-03-05 16:44:50 +01:00
void GDScriptLanguage : : get_reserved_words ( List < String > * p_words ) const {
2020-05-02 00:14:56 +02:00
// TODO: Add annotations here?
2017-03-05 16:44:50 +01:00
static const char * _reserved_words [ ] = {
2016-02-04 10:17:23 +01:00
// operators
2014-02-10 02:10:30 +01:00
" and " ,
2016-02-04 10:17:23 +01:00
" in " ,
" not " ,
2014-02-10 02:10:30 +01:00
" or " ,
2016-02-04 10:17:23 +01:00
// types and values
" false " ,
" float " ,
" int " ,
2016-06-24 17:45:59 +02:00
" bool " ,
2016-02-04 10:17:23 +01:00
" null " ,
2016-02-04 10:26:10 +01:00
" PI " ,
2017-11-04 10:34:27 +01:00
" TAU " ,
2017-02-06 23:44:22 +01:00
" INF " ,
" NAN " ,
2016-02-04 10:17:23 +01:00
" self " ,
" true " ,
2018-05-30 04:16:51 +02:00
" void " ,
2016-02-04 10:17:23 +01:00
// functions
2018-05-30 04:16:51 +02:00
" as " ,
2014-05-14 06:22:15 +02:00
" assert " ,
2020-05-02 00:14:56 +02:00
" await " ,
2015-12-29 16:11:21 +01:00
" breakpoint " ,
2016-02-04 10:17:23 +01:00
" class " ,
2018-07-16 00:29:00 +02:00
" class_name " ,
2016-02-04 10:17:23 +01:00
" extends " ,
2017-05-26 19:45:39 +02:00
" is " ,
2016-02-04 10:17:23 +01:00
" func " ,
2016-02-04 18:56:41 +01:00
" preload " ,
2016-02-04 10:17:23 +01:00
" signal " ,
2020-07-16 03:02:44 +02:00
" super " ,
2016-02-04 10:17:23 +01:00
// var
" const " ,
" enum " ,
2014-09-15 16:33:30 +02:00
" static " ,
2016-02-04 10:17:23 +01:00
" var " ,
// control flow
" break " ,
" continue " ,
" if " ,
" elif " ,
" else " ,
" for " ,
" pass " ,
" return " ,
2016-10-16 13:20:28 +02:00
" match " ,
2016-02-04 10:17:23 +01:00
" while " ,
2021-06-09 01:19:16 +02:00
// These keywords are not implemented currently, but reserved for (potential) future use.
// We highlight them as keywords to make errors easier to understand.
" trait " ,
" namespace " ,
" yield " ,
2020-04-02 01:20:12 +02:00
nullptr
2017-03-05 16:44:50 +01:00
} ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
const char * * w = _reserved_words ;
2014-02-10 02:10:30 +01:00
while ( * w ) {
p_words - > push_back ( * w ) ;
w + + ;
}
2020-11-26 15:56:32 +01:00
List < StringName > functions ;
GDScriptUtilityFunctions : : get_function_list ( & functions ) ;
2021-07-16 05:45:57 +02:00
for ( const StringName & E : functions ) {
p_words - > push_back ( String ( E ) ) ;
2014-02-10 02:10:30 +01:00
}
}
2021-04-08 16:12:22 +02:00
bool GDScriptLanguage : : is_control_flow_keyword ( String p_keyword ) const {
return p_keyword = = " break " | |
2021-10-28 15:19:35 +02:00
p_keyword = = " continue " | |
p_keyword = = " elif " | |
p_keyword = = " else " | |
p_keyword = = " if " | |
p_keyword = = " for " | |
p_keyword = = " match " | |
p_keyword = = " pass " | |
p_keyword = = " return " | |
p_keyword = = " while " ;
2021-04-08 16:12:22 +02:00
}
2018-07-16 00:29:00 +02:00
bool GDScriptLanguage : : handles_global_class_type ( const String & p_type ) const {
return p_type = = " GDScript " ;
}
2018-07-29 05:36:43 +02:00
String GDScriptLanguage : : get_global_class_name ( const String & p_path , String * r_base_type , String * r_icon_path ) const {
2018-07-16 00:29:00 +02:00
Error err ;
2022-03-23 10:08:58 +01:00
Ref < FileAccess > f = FileAccess : : open ( p_path , FileAccess : : READ , & err ) ;
2018-07-16 00:29:00 +02:00
if ( err ) {
return String ( ) ;
}
2019-03-03 20:36:42 +01:00
String source = f - > get_as_utf8_string ( ) ;
2018-07-16 00:29:00 +02:00
GDScriptParser parser ;
2020-05-02 00:14:56 +02:00
err = parser . parse ( source , p_path , false ) ;
2023-01-18 18:12:33 +01:00
const GDScriptParser : : ClassNode * c = parser . get_tree ( ) ;
2023-01-31 10:30:44 +01:00
if ( ! c ) {
return String ( ) ; // No class parsed.
}
/* **WARNING**
*
* This function is written with the goal to be * extremely * error tolerant , as such
* it should meet the following requirements :
*
* - It must not rely on the analyzer ( in fact , the analyzer must not be used here ) ,
* because at the time global classes are parsed , the dependencies may not be present
* yet , hence the function will fail ( which is unintended ) .
* - It must not fail even if the parsing fails , because even if the file is broken ,
* it should attempt its best to retrieve the inheritance metadata .
*
* Before changing this function , please ask the current maintainer of EditorFileSystem .
*/
2023-01-18 18:12:33 +01:00
if ( r_icon_path ) {
if ( c - > icon_path . is_empty ( ) | | c - > icon_path . is_absolute_path ( ) ) {
* r_icon_path = c - > icon_path . simplify_path ( ) ;
} else if ( c - > icon_path . is_relative_path ( ) ) {
* r_icon_path = p_path . get_base_dir ( ) . path_join ( c - > icon_path ) . simplify_path ( ) ;
2018-07-16 00:29:00 +02:00
}
}
2023-01-31 10:30:44 +01:00
if ( r_base_type ) {
const GDScriptParser : : ClassNode * subclass = c ;
String path = p_path ;
GDScriptParser subparser ;
while ( subclass ) {
if ( subclass - > extends_used ) {
if ( ! subclass - > extends_path . is_empty ( ) ) {
if ( subclass - > extends . size ( ) = = 0 ) {
get_global_class_name ( subclass - > extends_path , r_base_type ) ;
subclass = nullptr ;
break ;
} else {
Vector < StringName > extend_classes = subclass - > extends ;
Ref < FileAccess > subfile = FileAccess : : open ( subclass - > extends_path , FileAccess : : READ ) ;
if ( subfile . is_null ( ) ) {
break ;
}
String subsource = subfile - > get_as_utf8_string ( ) ;
if ( subsource . is_empty ( ) ) {
break ;
}
String subpath = subclass - > extends_path ;
if ( subpath . is_relative_path ( ) ) {
subpath = path . get_base_dir ( ) . path_join ( subpath ) . simplify_path ( ) ;
}
2018-07-16 00:29:00 +02:00
2023-01-31 10:30:44 +01:00
if ( OK ! = subparser . parse ( subsource , subpath , false ) ) {
break ;
}
path = subpath ;
subclass = subparser . get_tree ( ) ;
while ( extend_classes . size ( ) > 0 ) {
bool found = false ;
for ( int i = 0 ; i < subclass - > members . size ( ) ; i + + ) {
if ( subclass - > members [ i ] . type ! = GDScriptParser : : ClassNode : : Member : : CLASS ) {
continue ;
}
const GDScriptParser : : ClassNode * inner_class = subclass - > members [ i ] . m_class ;
if ( inner_class - > identifier - > name = = extend_classes [ 0 ] ) {
extend_classes . remove_at ( 0 ) ;
found = true ;
subclass = inner_class ;
break ;
}
}
if ( ! found ) {
subclass = nullptr ;
break ;
}
}
}
} else if ( subclass - > extends . size ( ) = = 1 ) {
* r_base_type = subclass - > extends [ 0 ] ;
subclass = nullptr ;
} else {
break ;
}
} else {
* r_base_type = " RefCounted " ;
subclass = nullptr ;
}
}
}
2023-01-18 18:12:33 +01:00
return c - > identifier ! = nullptr ? String ( c - > identifier - > name ) : String ( ) ;
2018-07-16 00:29:00 +02:00
}
2014-02-10 02:10:30 +01:00
GDScriptLanguage : : GDScriptLanguage ( ) {
2017-03-05 16:44:50 +01:00
calls = 0 ;
2014-02-10 02:10:30 +01:00
ERR_FAIL_COND ( singleton ) ;
2017-03-05 16:44:50 +01:00
singleton = this ;
2014-02-10 02:10:30 +01:00
strings . _init = StaticCString : : create ( " _init " ) ;
strings . _notification = StaticCString : : create ( " _notification " ) ;
2017-03-05 16:44:50 +01:00
strings . _set = StaticCString : : create ( " _set " ) ;
strings . _get = StaticCString : : create ( " _get " ) ;
strings . _get_property_list = StaticCString : : create ( " _get_property_list " ) ;
2022-08-12 20:43:14 +02:00
strings . _property_can_revert = StaticCString : : create ( " _property_can_revert " ) ;
strings . _property_get_revert = StaticCString : : create ( " _property_get_revert " ) ;
2017-03-05 16:44:50 +01:00
strings . _script_source = StaticCString : : create ( " script/source " ) ;
_debug_parse_err_line = - 1 ;
_debug_parse_err_file = " " ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
profiling = false ;
script_frame_time = 0 ;
2016-05-22 02:18:16 +02:00
2017-03-05 16:44:50 +01:00
_debug_call_stack_pos = 0 ;
2023-02-07 21:09:40 +01:00
int dmcs = GLOBAL_DEF ( PropertyInfo ( Variant : : INT , " debug/settings/gdscript/max_call_stack " , PROPERTY_HINT_RANGE , " 512, " + itos ( GDScriptFunction : : MAX_CALL_DEPTH - 1 ) + " ,1 " ) , 1024 ) ;
2018-10-05 18:43:53 +02:00
2020-02-27 03:30:20 +01:00
if ( EngineDebugger : : is_active ( ) ) {
2016-05-22 02:18:16 +02:00
//debugging enabled!
2014-02-10 02:10:30 +01:00
2016-05-22 02:18:16 +02:00
_debug_max_call_stack = dmcs ;
2017-03-05 16:44:50 +01:00
_call_stack = memnew_arr ( CallLevel , _debug_max_call_stack + 1 ) ;
2014-02-10 02:10:30 +01:00
2016-05-22 02:18:16 +02:00
} else {
2017-03-05 16:44:50 +01:00
_debug_max_call_stack = 0 ;
2020-04-02 01:20:12 +02:00
_call_stack = nullptr ;
2016-05-22 02:18:16 +02:00
}
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
GLOBAL_DEF ( " debug/gdscript/warnings/enable " , true ) ;
2019-11-08 05:01:22 +01:00
GLOBAL_DEF ( " debug/gdscript/warnings/exclude_addons " , true ) ;
2018-07-01 18:17:40 +02:00
for ( int i = 0 ; i < ( int ) GDScriptWarning : : WARNING_MAX ; i + + ) {
2022-03-23 03:44:30 +01:00
GDScriptWarning : : Code code = ( GDScriptWarning : : Code ) i ;
Variant default_enabled = GDScriptWarning : : get_default_value ( code ) ;
String path = GDScriptWarning : : get_settings_path_from_code ( code ) ;
2022-11-08 19:53:22 +01:00
GLOBAL_DEF ( GDScriptWarning : : get_property_info ( code ) , default_enabled ) ;
2018-07-01 18:17:40 +02:00
}
# endif // DEBUG_ENABLED
2014-02-10 02:10:30 +01:00
}
GDScriptLanguage : : ~ GDScriptLanguage ( ) {
2021-04-05 14:02:50 +02:00
singleton = nullptr ;
2014-02-10 02:10:30 +01:00
}
2020-01-14 00:19:37 +01:00
void GDScriptLanguage : : add_orphan_subclass ( const String & p_qualified_name , const ObjectID & p_subclass ) {
orphan_subclasses [ p_qualified_name ] = p_subclass ;
}
Ref < GDScript > GDScriptLanguage : : get_orphan_subclass ( const String & p_qualified_name ) {
2022-05-13 15:04:37 +02:00
HashMap < String , ObjectID > : : Iterator orphan_subclass_element = orphan_subclasses . find ( p_qualified_name ) ;
2020-05-14 16:41:43 +02:00
if ( ! orphan_subclass_element ) {
2020-01-14 00:19:37 +01:00
return Ref < GDScript > ( ) ;
2020-05-14 16:41:43 +02:00
}
2022-05-13 15:04:37 +02:00
ObjectID orphan_subclass = orphan_subclass_element - > value ;
2020-01-14 00:19:37 +01:00
Object * obj = ObjectDB : : get_instance ( orphan_subclass ) ;
2022-05-13 15:04:37 +02:00
orphan_subclasses . remove ( orphan_subclass_element ) ;
2020-05-14 16:41:43 +02:00
if ( ! obj ) {
2020-01-14 00:19:37 +01:00
return Ref < GDScript > ( ) ;
2020-05-14 16:41:43 +02:00
}
2020-01-14 00:19:37 +01:00
return Ref < GDScript > ( Object : : cast_to < GDScript > ( obj ) ) ;
}
2022-10-09 18:41:28 +02:00
Ref < GDScript > GDScriptLanguage : : get_script_by_fully_qualified_name ( const String & p_name ) {
{
MutexLock lock ( mutex ) ;
SelfList < GDScript > * elem = script_list . first ( ) ;
while ( elem ) {
GDScript * scr = elem - > self ( ) ;
2022-12-12 13:35:55 +01:00
if ( scr - > fully_qualified_name = = p_name ) {
2022-10-09 18:41:28 +02:00
return scr ;
}
elem = elem - > next ( ) ;
}
}
Ref < GDScript > scr ;
scr . instantiate ( ) ;
scr - > fully_qualified_name = p_name ;
return scr ;
}
2014-02-10 02:10:30 +01:00
/*************** RESOURCE ***************/
2022-05-03 01:43:50 +02:00
Ref < Resource > ResourceFormatLoaderGDScript : : load ( const String & p_path , const String & p_original_path , Error * r_error , bool p_use_sub_threads , float * r_progress , CacheMode p_cache_mode ) {
2020-05-14 16:41:43 +02:00
if ( r_error ) {
2017-03-05 16:44:50 +01:00
* r_error = ERR_FILE_CANT_OPEN ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-16 03:02:44 +02:00
Error err ;
2022-09-29 11:53:28 +02:00
Ref < GDScript > scr = GDScriptCache : : get_full_script ( p_path , err , " " , p_cache_mode = = CACHE_MODE_IGNORE ) ;
2014-02-25 13:31:47 +01:00
2022-09-29 11:53:28 +02:00
if ( scr . is_null ( ) ) {
2020-07-16 03:02:44 +02:00
// Don't fail loading because of parsing error.
2022-09-29 11:53:28 +02:00
scr . instantiate ( ) ;
2014-02-25 13:31:47 +01:00
}
2020-07-16 03:02:44 +02:00
2020-05-14 16:41:43 +02:00
if ( r_error ) {
2017-03-05 16:44:50 +01:00
* r_error = OK ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2022-09-29 11:53:28 +02:00
return scr ;
2014-02-10 02:10:30 +01:00
}
2018-08-26 03:19:02 +02:00
2014-02-10 02:10:30 +01:00
void ResourceFormatLoaderGDScript : : get_recognized_extensions ( List < String > * p_extensions ) const {
p_extensions - > push_back ( " gd " ) ;
}
2017-03-05 16:44:50 +01:00
bool ResourceFormatLoaderGDScript : : handles_type ( const String & p_type ) const {
return ( p_type = = " Script " | | p_type = = " GDScript " ) ;
2014-02-10 02:10:30 +01:00
}
String ResourceFormatLoaderGDScript : : get_resource_type ( const String & p_path ) const {
2017-01-14 04:51:09 +01:00
String el = p_path . get_extension ( ) . to_lower ( ) ;
2023-01-20 19:09:07 +01:00
if ( el = = " gd " ) {
2014-02-10 02:10:30 +01:00
return " GDScript " ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return " " ;
}
2019-03-03 20:36:42 +01:00
void ResourceFormatLoaderGDScript : : get_dependencies ( const String & p_path , List < String > * p_dependencies , bool p_add_types ) {
2022-03-23 10:08:58 +01:00
Ref < FileAccess > file = FileAccess : : open ( p_path , FileAccess : : READ ) ;
ERR_FAIL_COND_MSG ( file . is_null ( ) , " Cannot open file ' " + p_path + " '. " ) ;
2019-03-03 20:36:42 +01:00
String source = file - > get_as_utf8_string ( ) ;
2020-12-15 13:04:21 +01:00
if ( source . is_empty ( ) ) {
2019-03-03 20:36:42 +01:00
return ;
}
GDScriptParser parser ;
2020-05-02 00:14:56 +02:00
if ( OK ! = parser . parse ( source , p_path , false ) ) {
2019-03-03 20:36:42 +01:00
return ;
}
2021-07-16 05:45:57 +02:00
for ( const String & E : parser . get_dependencies ( ) ) {
p_dependencies - > push_back ( E ) ;
2019-03-03 20:36:42 +01:00
}
}
2022-06-03 01:33:42 +02:00
Error ResourceFormatSaverGDScript : : save ( const Ref < Resource > & p_resource , const String & p_path , uint32_t p_flags ) {
2014-02-10 02:10:30 +01:00
Ref < GDScript > sqscr = p_resource ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( sqscr . is_null ( ) , ERR_INVALID_PARAMETER ) ;
2014-02-10 02:10:30 +01:00
String source = sqscr - > get_source_code ( ) ;
2022-04-12 09:12:40 +02:00
{
Error err ;
Ref < FileAccess > file = FileAccess : : open ( p_path , FileAccess : : WRITE , & err ) ;
2014-02-10 02:10:30 +01:00
2022-04-12 09:12:40 +02:00
ERR_FAIL_COND_V_MSG ( err , err , " Cannot save GDScript file ' " + p_path + " '. " ) ;
2014-02-10 02:10:30 +01:00
2022-04-12 09:12:40 +02:00
file - > store_string ( source ) ;
if ( file - > get_error ( ) ! = OK & & file - > get_error ( ) ! = ERR_FILE_EOF ) {
return ERR_CANT_CREATE ;
}
2015-03-02 04:54:10 +01:00
}
2016-06-13 15:58:32 +02:00
if ( ScriptServer : : is_reload_scripts_on_save_enabled ( ) ) {
2021-11-19 17:45:16 +01:00
GDScriptLanguage : : get_singleton ( ) - > reload_tool_script ( p_resource , true ) ;
2016-06-13 15:58:32 +02:00
}
2014-02-10 02:10:30 +01:00
return OK ;
}
2022-05-03 01:43:50 +02:00
void ResourceFormatSaverGDScript : : get_recognized_extensions ( const Ref < Resource > & p_resource , List < String > * p_extensions ) const {
2017-08-24 22:58:51 +02:00
if ( Object : : cast_to < GDScript > ( * p_resource ) ) {
2014-02-10 02:10:30 +01:00
p_extensions - > push_back ( " gd " ) ;
}
}
2020-05-14 14:29:06 +02:00
2022-05-03 01:43:50 +02:00
bool ResourceFormatSaverGDScript : : recognize ( const Ref < Resource > & p_resource ) const {
2020-04-02 01:20:12 +02:00
return Object : : cast_to < GDScript > ( * p_resource ) ! = nullptr ;
2014-02-10 02:10:30 +01:00
}