2023-01-05 13:25:55 +01:00
/**************************************************************************/
/* extension_api_dump.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. */
/**************************************************************************/
2021-06-19 17:58:49 +02:00
# include "extension_api_dump.h"
2022-11-07 14:31:10 +01:00
2021-06-19 17:58:49 +02:00
# include "core/config/engine.h"
# include "core/core_constants.h"
2023-09-10 19:36:44 +02:00
# include "core/extension/gdextension_compat_hashes.h"
2021-06-19 17:58:49 +02:00
# include "core/io/file_access.h"
# include "core/io/json.h"
# include "core/templates/pair.h"
# include "core/version.h"
# ifdef TOOLS_ENABLED
2023-09-25 22:22:06 +02:00
# include "editor/editor_help.h"
2021-06-19 17:58:49 +02:00
2022-10-02 18:57:48 +02:00
static String get_builtin_or_variant_type_name ( const Variant : : Type p_type ) {
if ( p_type = = Variant : : NIL ) {
return " Variant " ;
} else {
return Variant : : get_type_name ( p_type ) ;
}
}
static String get_property_info_type_name ( const PropertyInfo & p_info ) {
2021-08-23 19:53:27 +02:00
if ( p_info . type = = Variant : : INT & & ( p_info . hint = = PROPERTY_HINT_INT_IS_POINTER ) ) {
2021-12-09 10:42:46 +01:00
if ( p_info . hint_string . is_empty ( ) ) {
2021-08-23 19:53:27 +02:00
return " void* " ;
} else {
return p_info . hint_string + " * " ;
}
}
2022-08-25 11:35:30 +02:00
if ( p_info . type = = Variant : : ARRAY & & ( p_info . hint = = PROPERTY_HINT_ARRAY_TYPE ) ) {
return String ( " typedarray:: " ) + p_info . hint_string ;
}
2022-07-11 11:40:31 +02:00
if ( p_info . type = = Variant : : INT & & ( p_info . usage & ( PROPERTY_USAGE_CLASS_IS_ENUM ) ) ) {
2021-07-29 15:17:07 +02:00
return String ( " enum:: " ) + String ( p_info . class_name ) ;
}
2022-07-11 11:40:31 +02:00
if ( p_info . type = = Variant : : INT & & ( p_info . usage & ( PROPERTY_USAGE_CLASS_IS_BITFIELD ) ) ) {
return String ( " bitfield:: " ) + String ( p_info . class_name ) ;
}
2022-08-15 11:37:58 +02:00
if ( p_info . type = = Variant : : INT & & ( p_info . usage & PROPERTY_USAGE_ARRAY ) ) {
return " int " ;
}
2021-07-29 15:17:07 +02:00
if ( p_info . class_name ! = StringName ( ) ) {
return p_info . class_name ;
}
if ( p_info . hint = = PROPERTY_HINT_RESOURCE_TYPE ) {
return p_info . hint_string ;
}
if ( p_info . type = = Variant : : NIL & & ( p_info . usage & PROPERTY_USAGE_NIL_IS_VARIANT ) ) {
return " Variant " ;
}
if ( p_info . type = = Variant : : NIL ) {
return " void " ;
}
2022-10-02 18:57:48 +02:00
return get_builtin_or_variant_type_name ( p_info . type ) ;
2021-07-29 15:17:07 +02:00
}
2023-01-25 03:51:32 +01:00
static String get_type_meta_name ( const GodotTypeInfo : : Metadata metadata ) {
static const char * argmeta [ 11 ] = { " none " , " int8 " , " int16 " , " int32 " , " int64 " , " uint8 " , " uint16 " , " uint32 " , " uint64 " , " float " , " double " } ;
return argmeta [ metadata ] ;
}
2023-09-25 22:22:06 +02:00
static String fix_doc_description ( const String & p_bbcode ) {
// Based on what EditorHelp does.
return p_bbcode . dedent ( )
. replace ( " \t " , " " )
. replace ( " \r " , " " )
. strip_edges ( ) ;
}
Dictionary GDExtensionAPIDump : : generate_extension_api ( bool p_include_docs ) {
2021-06-19 17:58:49 +02:00
Dictionary api_dump ;
{
//header
Dictionary header ;
header [ " version_major " ] = VERSION_MAJOR ;
header [ " version_minor " ] = VERSION_MINOR ;
# if VERSION_PATCH
header [ " version_patch " ] = VERSION_PATCH ;
# else
header [ " version_patch " ] = 0 ;
# endif
header [ " version_status " ] = VERSION_STATUS ;
header [ " version_build " ] = VERSION_BUILD ;
header [ " version_full_name " ] = VERSION_FULL_NAME ;
api_dump [ " header " ] = header ;
}
const uint32_t vec3_elems = 3 ;
2022-08-21 12:49:28 +02:00
const uint32_t vec4_elems = 4 ;
2021-06-19 17:58:49 +02:00
const uint32_t ptrsize_32 = 4 ;
2021-07-29 15:17:07 +02:00
const uint32_t ptrsize_64 = 8 ;
2021-06-19 17:58:49 +02:00
static const char * build_config_name [ 4 ] = { " float_32 " , " float_64 " , " double_32 " , " double_64 " } ;
{
//type sizes
2021-07-29 15:17:07 +02:00
constexpr struct {
2021-06-19 17:58:49 +02:00
Variant : : Type type ;
uint32_t size_32_bits_real_float ;
uint32_t size_64_bits_real_float ;
uint32_t size_32_bits_real_double ;
uint32_t size_64_bits_real_double ;
2021-07-29 15:17:07 +02:00
// For compile-time size check.
constexpr uint32_t operator [ ] ( int index ) const {
switch ( index ) {
# ifndef REAL_T_IS_DOUBLE
case sizeof ( uint32_t ) :
return size_32_bits_real_float ;
case sizeof ( uint64_t ) :
return size_64_bits_real_float ;
# else // REAL_T_IS_DOUBLE
case sizeof ( uint32_t ) :
return size_32_bits_real_double ;
case sizeof ( uint64_t ) :
return size_64_bits_real_double ;
# endif
}
return - 1 ;
}
2021-06-19 17:58:49 +02:00
} type_size_array [ Variant : : VARIANT_MAX + 1 ] = {
{ Variant : : NIL , 0 , 0 , 0 , 0 } ,
2021-07-29 15:17:07 +02:00
{ Variant : : BOOL , sizeof ( uint8_t ) , sizeof ( uint8_t ) , sizeof ( uint8_t ) , sizeof ( uint8_t ) } ,
2021-06-19 17:58:49 +02:00
{ Variant : : INT , sizeof ( int64_t ) , sizeof ( int64_t ) , sizeof ( int64_t ) , sizeof ( int64_t ) } ,
{ Variant : : FLOAT , sizeof ( double ) , sizeof ( double ) , sizeof ( double ) , sizeof ( double ) } ,
{ Variant : : STRING , ptrsize_32 , ptrsize_64 , ptrsize_32 , ptrsize_64 } ,
{ Variant : : VECTOR2 , 2 * sizeof ( float ) , 2 * sizeof ( float ) , 2 * sizeof ( double ) , 2 * sizeof ( double ) } ,
{ Variant : : VECTOR2I , 2 * sizeof ( int32_t ) , 2 * sizeof ( int32_t ) , 2 * sizeof ( int32_t ) , 2 * sizeof ( int32_t ) } ,
{ Variant : : RECT2 , 4 * sizeof ( float ) , 4 * sizeof ( float ) , 4 * sizeof ( double ) , 4 * sizeof ( double ) } ,
{ Variant : : RECT2I , 4 * sizeof ( int32_t ) , 4 * sizeof ( int32_t ) , 4 * sizeof ( int32_t ) , 4 * sizeof ( int32_t ) } ,
{ Variant : : VECTOR3 , vec3_elems * sizeof ( float ) , vec3_elems * sizeof ( float ) , vec3_elems * sizeof ( double ) , vec3_elems * sizeof ( double ) } ,
{ Variant : : VECTOR3I , 3 * sizeof ( int32_t ) , 3 * sizeof ( int32_t ) , 3 * sizeof ( int32_t ) , 3 * sizeof ( int32_t ) } ,
{ Variant : : TRANSFORM2D , 6 * sizeof ( float ) , 6 * sizeof ( float ) , 6 * sizeof ( double ) , 6 * sizeof ( double ) } ,
Implement Vector4, Vector4i, Projection
Implement built-in classes Vector4, Vector4i and Projection.
* Two versions of Vector4 (float and integer).
* A Projection class, which is a 4x4 matrix specialized in projection types.
These types have been requested for a long time, but given they were very corner case they were not added before.
Because in Godot 4, reimplementing parts of the rendering engine is now possible, access to these types (heavily used by the rendering code) becomes a necessity.
**Q**: Why Projection and not Matrix4?
**A**: Godot does not use Matrix2, Matrix3, Matrix4x3, etc. naming convention because, within the engine, these types always have a *purpose*. As such, Godot names them: Transform2D, Transform3D or Basis. In this case, this 4x4 matrix is _always_ used as a _Projection_, hence the naming.
2022-07-20 01:11:13 +02:00
{ Variant : : VECTOR4 , 4 * sizeof ( float ) , 4 * sizeof ( float ) , 4 * sizeof ( double ) , 4 * sizeof ( double ) } ,
{ Variant : : VECTOR4I , 4 * sizeof ( int32_t ) , 4 * sizeof ( int32_t ) , 4 * sizeof ( int32_t ) , 4 * sizeof ( int32_t ) } ,
2021-06-19 17:58:49 +02:00
{ Variant : : PLANE , ( vec3_elems + 1 ) * sizeof ( float ) , ( vec3_elems + 1 ) * sizeof ( float ) , ( vec3_elems + 1 ) * sizeof ( double ) , ( vec3_elems + 1 ) * sizeof ( double ) } ,
{ Variant : : QUATERNION , 4 * sizeof ( float ) , 4 * sizeof ( float ) , 4 * sizeof ( double ) , 4 * sizeof ( double ) } ,
{ Variant : : AABB , ( vec3_elems * 2 ) * sizeof ( float ) , ( vec3_elems * 2 ) * sizeof ( float ) , ( vec3_elems * 2 ) * sizeof ( double ) , ( vec3_elems * 2 ) * sizeof ( double ) } ,
{ Variant : : BASIS , ( vec3_elems * 3 ) * sizeof ( float ) , ( vec3_elems * 3 ) * sizeof ( float ) , ( vec3_elems * 3 ) * sizeof ( double ) , ( vec3_elems * 3 ) * sizeof ( double ) } ,
{ Variant : : TRANSFORM3D , ( vec3_elems * 4 ) * sizeof ( float ) , ( vec3_elems * 4 ) * sizeof ( float ) , ( vec3_elems * 4 ) * sizeof ( double ) , ( vec3_elems * 4 ) * sizeof ( double ) } ,
2022-08-21 12:49:28 +02:00
{ Variant : : PROJECTION , ( vec4_elems * 4 ) * sizeof ( float ) , ( vec4_elems * 4 ) * sizeof ( float ) , ( vec4_elems * 4 ) * sizeof ( double ) , ( vec4_elems * 4 ) * sizeof ( double ) } ,
2021-06-19 17:58:49 +02:00
{ Variant : : COLOR , 4 * sizeof ( float ) , 4 * sizeof ( float ) , 4 * sizeof ( float ) , 4 * sizeof ( float ) } ,
{ Variant : : STRING_NAME , ptrsize_32 , ptrsize_64 , ptrsize_32 , ptrsize_64 } ,
{ Variant : : NODE_PATH , ptrsize_32 , ptrsize_64 , ptrsize_32 , ptrsize_64 } ,
{ Variant : : RID , sizeof ( uint64_t ) , sizeof ( uint64_t ) , sizeof ( uint64_t ) , sizeof ( uint64_t ) } ,
{ Variant : : OBJECT , ptrsize_32 , ptrsize_64 , ptrsize_32 , ptrsize_64 } ,
Fix various typos with codespell
Found via `codespell -q 3 -S ./thirdparty,*.po,./DONORS.md -L ackward,ang,ans,ba,beng,cas,childs,childrens,dof,doubleclick,fave,findn,hist,inout,leapyear,lod,nd,numer,ois,ony,paket,seeked,sinc,switchs,te,uint`
2021-07-07 17:17:32 +02:00
{ Variant : : CALLABLE , sizeof ( Callable ) , sizeof ( Callable ) , sizeof ( Callable ) , sizeof ( Callable ) } , // Hardcoded align.
{ Variant : : SIGNAL , sizeof ( Signal ) , sizeof ( Signal ) , sizeof ( Signal ) , sizeof ( Signal ) } , // Hardcoded align.
2021-06-19 17:58:49 +02:00
{ Variant : : DICTIONARY , ptrsize_32 , ptrsize_64 , ptrsize_32 , ptrsize_64 } ,
{ Variant : : ARRAY , ptrsize_32 , ptrsize_64 , ptrsize_32 , ptrsize_64 } ,
2021-07-29 15:17:07 +02:00
{ Variant : : PACKED_BYTE_ARRAY , ptrsize_32 * 2 , ptrsize_64 * 2 , ptrsize_32 * 2 , ptrsize_64 * 2 } ,
{ Variant : : PACKED_INT32_ARRAY , ptrsize_32 * 2 , ptrsize_64 * 2 , ptrsize_32 * 2 , ptrsize_64 * 2 } ,
{ Variant : : PACKED_INT64_ARRAY , ptrsize_32 * 2 , ptrsize_64 * 2 , ptrsize_32 * 2 , ptrsize_64 * 2 } ,
{ Variant : : PACKED_FLOAT32_ARRAY , ptrsize_32 * 2 , ptrsize_64 * 2 , ptrsize_32 * 2 , ptrsize_64 * 2 } ,
{ Variant : : PACKED_FLOAT64_ARRAY , ptrsize_32 * 2 , ptrsize_64 * 2 , ptrsize_32 * 2 , ptrsize_64 * 2 } ,
{ Variant : : PACKED_STRING_ARRAY , ptrsize_32 * 2 , ptrsize_64 * 2 , ptrsize_32 * 2 , ptrsize_64 * 2 } ,
{ Variant : : PACKED_VECTOR2_ARRAY , ptrsize_32 * 2 , ptrsize_64 * 2 , ptrsize_32 * 2 , ptrsize_64 * 2 } ,
{ Variant : : PACKED_VECTOR3_ARRAY , ptrsize_32 * 2 , ptrsize_64 * 2 , ptrsize_32 * 2 , ptrsize_64 * 2 } ,
{ Variant : : PACKED_COLOR_ARRAY , ptrsize_32 * 2 , ptrsize_64 * 2 , ptrsize_32 * 2 , ptrsize_64 * 2 } ,
2021-06-19 17:58:49 +02:00
{ Variant : : VARIANT_MAX , sizeof ( uint64_t ) + sizeof ( float ) * 4 , sizeof ( uint64_t ) + sizeof ( float ) * 4 , sizeof ( uint64_t ) + sizeof ( double ) * 4 , sizeof ( uint64_t ) + sizeof ( double ) * 4 } ,
} ;
2021-07-29 15:17:07 +02:00
// Validate sizes at compile time for the current build configuration.
2022-12-07 12:11:28 +01:00
static_assert ( type_size_array [ Variant : : BOOL ] [ sizeof ( void * ) ] = = sizeof ( GDExtensionBool ) , " Size of bool mismatch " ) ;
static_assert ( type_size_array [ Variant : : INT ] [ sizeof ( void * ) ] = = sizeof ( GDExtensionInt ) , " Size of int mismatch " ) ;
2021-07-29 15:17:07 +02:00
static_assert ( type_size_array [ Variant : : FLOAT ] [ sizeof ( void * ) ] = = sizeof ( double ) , " Size of float mismatch " ) ;
static_assert ( type_size_array [ Variant : : STRING ] [ sizeof ( void * ) ] = = sizeof ( String ) , " Size of String mismatch " ) ;
static_assert ( type_size_array [ Variant : : VECTOR2 ] [ sizeof ( void * ) ] = = sizeof ( Vector2 ) , " Size of Vector2 mismatch " ) ;
static_assert ( type_size_array [ Variant : : VECTOR2I ] [ sizeof ( void * ) ] = = sizeof ( Vector2i ) , " Size of Vector2i mismatch " ) ;
static_assert ( type_size_array [ Variant : : RECT2 ] [ sizeof ( void * ) ] = = sizeof ( Rect2 ) , " Size of Rect2 mismatch " ) ;
static_assert ( type_size_array [ Variant : : RECT2I ] [ sizeof ( void * ) ] = = sizeof ( Rect2i ) , " Size of Rect2i mismatch " ) ;
static_assert ( type_size_array [ Variant : : VECTOR3 ] [ sizeof ( void * ) ] = = sizeof ( Vector3 ) , " Size of Vector3 mismatch " ) ;
static_assert ( type_size_array [ Variant : : VECTOR3I ] [ sizeof ( void * ) ] = = sizeof ( Vector3i ) , " Size of Vector3i mismatch " ) ;
static_assert ( type_size_array [ Variant : : TRANSFORM2D ] [ sizeof ( void * ) ] = = sizeof ( Transform2D ) , " Size of Transform2D mismatch " ) ;
Implement Vector4, Vector4i, Projection
Implement built-in classes Vector4, Vector4i and Projection.
* Two versions of Vector4 (float and integer).
* A Projection class, which is a 4x4 matrix specialized in projection types.
These types have been requested for a long time, but given they were very corner case they were not added before.
Because in Godot 4, reimplementing parts of the rendering engine is now possible, access to these types (heavily used by the rendering code) becomes a necessity.
**Q**: Why Projection and not Matrix4?
**A**: Godot does not use Matrix2, Matrix3, Matrix4x3, etc. naming convention because, within the engine, these types always have a *purpose*. As such, Godot names them: Transform2D, Transform3D or Basis. In this case, this 4x4 matrix is _always_ used as a _Projection_, hence the naming.
2022-07-20 01:11:13 +02:00
static_assert ( type_size_array [ Variant : : VECTOR4 ] [ sizeof ( void * ) ] = = sizeof ( Vector4 ) , " Size of Vector4 mismatch " ) ;
static_assert ( type_size_array [ Variant : : VECTOR4I ] [ sizeof ( void * ) ] = = sizeof ( Vector4i ) , " Size of Vector4i mismatch " ) ;
2021-07-29 15:17:07 +02:00
static_assert ( type_size_array [ Variant : : PLANE ] [ sizeof ( void * ) ] = = sizeof ( Plane ) , " Size of Plane mismatch " ) ;
static_assert ( type_size_array [ Variant : : QUATERNION ] [ sizeof ( void * ) ] = = sizeof ( Quaternion ) , " Size of Quaternion mismatch " ) ;
static_assert ( type_size_array [ Variant : : AABB ] [ sizeof ( void * ) ] = = sizeof ( AABB ) , " Size of AABB mismatch " ) ;
static_assert ( type_size_array [ Variant : : BASIS ] [ sizeof ( void * ) ] = = sizeof ( Basis ) , " Size of Basis mismatch " ) ;
static_assert ( type_size_array [ Variant : : TRANSFORM3D ] [ sizeof ( void * ) ] = = sizeof ( Transform3D ) , " Size of Transform3D mismatch " ) ;
Implement Vector4, Vector4i, Projection
Implement built-in classes Vector4, Vector4i and Projection.
* Two versions of Vector4 (float and integer).
* A Projection class, which is a 4x4 matrix specialized in projection types.
These types have been requested for a long time, but given they were very corner case they were not added before.
Because in Godot 4, reimplementing parts of the rendering engine is now possible, access to these types (heavily used by the rendering code) becomes a necessity.
**Q**: Why Projection and not Matrix4?
**A**: Godot does not use Matrix2, Matrix3, Matrix4x3, etc. naming convention because, within the engine, these types always have a *purpose*. As such, Godot names them: Transform2D, Transform3D or Basis. In this case, this 4x4 matrix is _always_ used as a _Projection_, hence the naming.
2022-07-20 01:11:13 +02:00
static_assert ( type_size_array [ Variant : : PROJECTION ] [ sizeof ( void * ) ] = = sizeof ( Projection ) , " Size of Projection mismatch " ) ;
2021-07-29 15:17:07 +02:00
static_assert ( type_size_array [ Variant : : COLOR ] [ sizeof ( void * ) ] = = sizeof ( Color ) , " Size of Color mismatch " ) ;
static_assert ( type_size_array [ Variant : : STRING_NAME ] [ sizeof ( void * ) ] = = sizeof ( StringName ) , " Size of StringName mismatch " ) ;
static_assert ( type_size_array [ Variant : : NODE_PATH ] [ sizeof ( void * ) ] = = sizeof ( NodePath ) , " Size of NodePath mismatch " ) ;
static_assert ( type_size_array [ Variant : : RID ] [ sizeof ( void * ) ] = = sizeof ( RID ) , " Size of RID mismatch " ) ;
static_assert ( type_size_array [ Variant : : OBJECT ] [ sizeof ( void * ) ] = = sizeof ( Object * ) , " Size of Object mismatch " ) ;
static_assert ( type_size_array [ Variant : : CALLABLE ] [ sizeof ( void * ) ] = = sizeof ( Callable ) , " Size of Callable mismatch " ) ;
static_assert ( type_size_array [ Variant : : SIGNAL ] [ sizeof ( void * ) ] = = sizeof ( Signal ) , " Size of Signal mismatch " ) ;
static_assert ( type_size_array [ Variant : : DICTIONARY ] [ sizeof ( void * ) ] = = sizeof ( Dictionary ) , " Size of Dictionary mismatch " ) ;
static_assert ( type_size_array [ Variant : : ARRAY ] [ sizeof ( void * ) ] = = sizeof ( Array ) , " Size of Array mismatch " ) ;
static_assert ( type_size_array [ Variant : : PACKED_BYTE_ARRAY ] [ sizeof ( void * ) ] = = sizeof ( PackedByteArray ) , " Size of PackedByteArray mismatch " ) ;
static_assert ( type_size_array [ Variant : : PACKED_INT32_ARRAY ] [ sizeof ( void * ) ] = = sizeof ( PackedInt32Array ) , " Size of PackedInt32Array mismatch " ) ;
static_assert ( type_size_array [ Variant : : PACKED_INT64_ARRAY ] [ sizeof ( void * ) ] = = sizeof ( PackedInt64Array ) , " Size of PackedInt64Array mismatch " ) ;
static_assert ( type_size_array [ Variant : : PACKED_FLOAT32_ARRAY ] [ sizeof ( void * ) ] = = sizeof ( PackedFloat32Array ) , " Size of PackedFloat32Array mismatch " ) ;
static_assert ( type_size_array [ Variant : : PACKED_FLOAT64_ARRAY ] [ sizeof ( void * ) ] = = sizeof ( PackedFloat64Array ) , " Size of PackedFloat64Array mismatch " ) ;
static_assert ( type_size_array [ Variant : : PACKED_STRING_ARRAY ] [ sizeof ( void * ) ] = = sizeof ( PackedStringArray ) , " Size of PackedStringArray mismatch " ) ;
static_assert ( type_size_array [ Variant : : PACKED_VECTOR2_ARRAY ] [ sizeof ( void * ) ] = = sizeof ( PackedVector2Array ) , " Size of PackedVector2Array mismatch " ) ;
static_assert ( type_size_array [ Variant : : PACKED_VECTOR3_ARRAY ] [ sizeof ( void * ) ] = = sizeof ( PackedVector3Array ) , " Size of PackedVector3Array mismatch " ) ;
static_assert ( type_size_array [ Variant : : PACKED_COLOR_ARRAY ] [ sizeof ( void * ) ] = = sizeof ( PackedColorArray ) , " Size of PackedColorArray mismatch " ) ;
static_assert ( type_size_array [ Variant : : VARIANT_MAX ] [ sizeof ( void * ) ] = = sizeof ( Variant ) , " Size of Variant mismatch " ) ;
2021-06-19 17:58:49 +02:00
Array core_type_sizes ;
for ( int i = 0 ; i < 4 ; i + + ) {
Dictionary d ;
d [ " build_configuration " ] = build_config_name [ i ] ;
Array sizes ;
2021-07-29 15:17:07 +02:00
for ( int j = 0 ; j < = Variant : : VARIANT_MAX ; j + + ) {
2021-06-19 17:58:49 +02:00
Variant : : Type t = type_size_array [ j ] . type ;
String name = t = = Variant : : VARIANT_MAX ? String ( " Variant " ) : Variant : : get_type_name ( t ) ;
Dictionary d2 ;
d2 [ " name " ] = name ;
2022-09-22 09:25:47 +02:00
uint32_t size = 0 ;
2021-06-19 17:58:49 +02:00
switch ( i ) {
case 0 :
size = type_size_array [ j ] . size_32_bits_real_float ;
break ;
case 1 :
size = type_size_array [ j ] . size_64_bits_real_float ;
break ;
case 2 :
size = type_size_array [ j ] . size_32_bits_real_double ;
break ;
case 3 :
size = type_size_array [ j ] . size_64_bits_real_double ;
break ;
}
d2 [ " size " ] = size ;
sizes . push_back ( d2 ) ;
}
d [ " sizes " ] = sizes ;
core_type_sizes . push_back ( d ) ;
}
api_dump [ " builtin_class_sizes " ] = core_type_sizes ;
}
{
2022-08-13 16:02:00 +02:00
// Member offsets, meta types and sizes.
# define REAL_MEMBER_OFFSET(type, member) \
{ \
type , \
member , \
" float " , \
sizeof ( float ) , \
" float " , \
sizeof ( float ) , \
" double " , \
sizeof ( double ) , \
" double " , \
sizeof ( double ) , \
}
# define INT32_MEMBER_OFFSET(type, member) \
{ \
type , \
member , \
" int32 " , \
sizeof ( int32_t ) , \
" int32 " , \
sizeof ( int32_t ) , \
" int32 " , \
sizeof ( int32_t ) , \
" int32 " , \
sizeof ( int32_t ) , \
}
# define INT32_BASED_BUILTIN_MEMBER_OFFSET(type, member, member_type, member_elems) \
{ \
type , \
member , \
member_type , \
sizeof ( int32_t ) * member_elems , \
member_type , \
sizeof ( int32_t ) * member_elems , \
member_type , \
sizeof ( int32_t ) * member_elems , \
member_type , \
sizeof ( int32_t ) * member_elems , \
}
# define REAL_BASED_BUILTIN_MEMBER_OFFSET(type, member, member_type, member_elems) \
{ \
type , \
member , \
member_type , \
sizeof ( float ) * member_elems , \
member_type , \
sizeof ( float ) * member_elems , \
member_type , \
sizeof ( double ) * member_elems , \
member_type , \
sizeof ( double ) * member_elems , \
}
2021-06-19 17:58:49 +02:00
struct {
Variant : : Type type ;
const char * member ;
2022-08-13 16:02:00 +02:00
const char * member_meta_32_bits_real_float ;
const uint32_t member_size_32_bits_real_float ;
const char * member_meta_64_bits_real_float ;
const uint32_t member_size_64_bits_real_float ;
const char * member_meta_32_bits_real_double ;
const uint32_t member_size_32_bits_real_double ;
const char * member_meta_64_bits_real_double ;
const uint32_t member_size_64_bits_real_double ;
2021-06-19 17:58:49 +02:00
} member_offset_array [ ] = {
2022-08-13 16:02:00 +02:00
// Vector2
REAL_MEMBER_OFFSET ( Variant : : VECTOR2 , " x " ) ,
REAL_MEMBER_OFFSET ( Variant : : VECTOR2 , " y " ) ,
// Vector2i
INT32_MEMBER_OFFSET ( Variant : : VECTOR2I , " x " ) ,
INT32_MEMBER_OFFSET ( Variant : : VECTOR2I , " y " ) ,
// Rect2
REAL_BASED_BUILTIN_MEMBER_OFFSET ( Variant : : RECT2 , " position " , " Vector2 " , 2 ) ,
REAL_BASED_BUILTIN_MEMBER_OFFSET ( Variant : : RECT2 , " size " , " Vector2 " , 2 ) ,
// Rect2i
INT32_BASED_BUILTIN_MEMBER_OFFSET ( Variant : : RECT2I , " position " , " Vector2i " , 2 ) ,
INT32_BASED_BUILTIN_MEMBER_OFFSET ( Variant : : RECT2I , " size " , " Vector2i " , 2 ) ,
// Vector3
REAL_MEMBER_OFFSET ( Variant : : VECTOR3 , " x " ) ,
REAL_MEMBER_OFFSET ( Variant : : VECTOR3 , " y " ) ,
REAL_MEMBER_OFFSET ( Variant : : VECTOR3 , " z " ) ,
// Vector3i
INT32_MEMBER_OFFSET ( Variant : : VECTOR3I , " x " ) ,
INT32_MEMBER_OFFSET ( Variant : : VECTOR3I , " y " ) ,
INT32_MEMBER_OFFSET ( Variant : : VECTOR3I , " z " ) ,
// Transform2D
REAL_BASED_BUILTIN_MEMBER_OFFSET ( Variant : : TRANSFORM2D , " x " , " Vector2 " , 2 ) ,
REAL_BASED_BUILTIN_MEMBER_OFFSET ( Variant : : TRANSFORM2D , " y " , " Vector2 " , 2 ) ,
REAL_BASED_BUILTIN_MEMBER_OFFSET ( Variant : : TRANSFORM2D , " origin " , " Vector2 " , 2 ) ,
// Vector4
REAL_MEMBER_OFFSET ( Variant : : VECTOR4 , " x " ) ,
REAL_MEMBER_OFFSET ( Variant : : VECTOR4 , " y " ) ,
REAL_MEMBER_OFFSET ( Variant : : VECTOR4 , " z " ) ,
REAL_MEMBER_OFFSET ( Variant : : VECTOR4 , " w " ) ,
// Vector4i
INT32_MEMBER_OFFSET ( Variant : : VECTOR4I , " x " ) ,
INT32_MEMBER_OFFSET ( Variant : : VECTOR4I , " y " ) ,
INT32_MEMBER_OFFSET ( Variant : : VECTOR4I , " z " ) ,
INT32_MEMBER_OFFSET ( Variant : : VECTOR4I , " w " ) ,
// Plane
REAL_BASED_BUILTIN_MEMBER_OFFSET ( Variant : : PLANE , " normal " , " Vector3 " , vec3_elems ) ,
REAL_MEMBER_OFFSET ( Variant : : PLANE , " d " ) ,
// Quaternion
REAL_MEMBER_OFFSET ( Variant : : QUATERNION , " x " ) ,
REAL_MEMBER_OFFSET ( Variant : : QUATERNION , " y " ) ,
REAL_MEMBER_OFFSET ( Variant : : QUATERNION , " z " ) ,
REAL_MEMBER_OFFSET ( Variant : : QUATERNION , " w " ) ,
// AABB
REAL_BASED_BUILTIN_MEMBER_OFFSET ( Variant : : AABB , " position " , " Vector3 " , vec3_elems ) ,
REAL_BASED_BUILTIN_MEMBER_OFFSET ( Variant : : AABB , " size " , " Vector3 " , vec3_elems ) ,
// Basis (remember that basis vectors are flipped!)
REAL_BASED_BUILTIN_MEMBER_OFFSET ( Variant : : BASIS , " x " , " Vector3 " , vec3_elems ) ,
REAL_BASED_BUILTIN_MEMBER_OFFSET ( Variant : : BASIS , " y " , " Vector3 " , vec3_elems ) ,
REAL_BASED_BUILTIN_MEMBER_OFFSET ( Variant : : BASIS , " z " , " Vector3 " , vec3_elems ) ,
// Transform3D
REAL_BASED_BUILTIN_MEMBER_OFFSET ( Variant : : TRANSFORM3D , " basis " , " Basis " , vec3_elems * 3 ) ,
REAL_BASED_BUILTIN_MEMBER_OFFSET ( Variant : : TRANSFORM3D , " origin " , " Vector3 " , vec3_elems ) ,
// Projection
REAL_BASED_BUILTIN_MEMBER_OFFSET ( Variant : : PROJECTION , " x " , " Vector4 " , vec4_elems ) ,
REAL_BASED_BUILTIN_MEMBER_OFFSET ( Variant : : PROJECTION , " y " , " Vector4 " , vec4_elems ) ,
REAL_BASED_BUILTIN_MEMBER_OFFSET ( Variant : : PROJECTION , " z " , " Vector4 " , vec4_elems ) ,
REAL_BASED_BUILTIN_MEMBER_OFFSET ( Variant : : PROJECTION , " w " , " Vector4 " , vec4_elems ) ,
// Color (always composed of 4bytes floats)
{ Variant : : COLOR , " r " , " float " , sizeof ( float ) , " float " , sizeof ( float ) , " float " , sizeof ( float ) , " float " , sizeof ( float ) } ,
{ Variant : : COLOR , " g " , " float " , sizeof ( float ) , " float " , sizeof ( float ) , " float " , sizeof ( float ) , " float " , sizeof ( float ) } ,
{ Variant : : COLOR , " b " , " float " , sizeof ( float ) , " float " , sizeof ( float ) , " float " , sizeof ( float ) , " float " , sizeof ( float ) } ,
{ Variant : : COLOR , " a " , " float " , sizeof ( float ) , " float " , sizeof ( float ) , " float " , sizeof ( float ) , " float " , sizeof ( float ) } ,
// End marker, must stay last
{ Variant : : NIL , nullptr , nullptr , 0 , nullptr , 0 , nullptr , 0 , nullptr , 0 } ,
2021-06-19 17:58:49 +02:00
} ;
Array core_type_member_offsets ;
for ( int i = 0 ; i < 4 ; i + + ) {
Dictionary d ;
d [ " build_configuration " ] = build_config_name [ i ] ;
Array type_offsets ;
uint32_t idx = 0 ;
2022-08-13 16:02:00 +02:00
Variant : : Type previous_type = Variant : : NIL ;
2021-06-19 17:58:49 +02:00
Dictionary d2 ;
Array members ;
2022-08-13 16:02:00 +02:00
uint32_t offset = 0 ;
2021-06-19 17:58:49 +02:00
while ( true ) {
Variant : : Type t = member_offset_array [ idx ] . type ;
2022-08-13 16:02:00 +02:00
if ( t ! = previous_type ) {
if ( previous_type ! = Variant : : NIL ) {
2021-06-19 17:58:49 +02:00
d2 [ " members " ] = members ;
type_offsets . push_back ( d2 ) ;
}
if ( t = = Variant : : NIL ) {
break ;
}
String name = t = = Variant : : VARIANT_MAX ? String ( " Variant " ) : Variant : : get_type_name ( t ) ;
d2 = Dictionary ( ) ;
members = Array ( ) ;
2022-08-13 16:02:00 +02:00
offset = 0 ;
2021-06-19 17:58:49 +02:00
d2 [ " name " ] = name ;
2022-08-13 16:02:00 +02:00
previous_type = t ;
2021-06-19 17:58:49 +02:00
}
Dictionary d3 ;
2022-08-13 16:02:00 +02:00
const char * member_meta = nullptr ;
uint32_t member_size = 0 ;
2021-06-19 17:58:49 +02:00
switch ( i ) {
case 0 :
2022-08-13 16:02:00 +02:00
member_meta = member_offset_array [ idx ] . member_meta_32_bits_real_float ;
member_size = member_offset_array [ idx ] . member_size_32_bits_real_float ;
2021-06-19 17:58:49 +02:00
break ;
case 1 :
2022-08-13 16:02:00 +02:00
member_meta = member_offset_array [ idx ] . member_meta_64_bits_real_float ;
member_size = member_offset_array [ idx ] . member_size_64_bits_real_float ;
2021-06-19 17:58:49 +02:00
break ;
case 2 :
2022-08-13 16:02:00 +02:00
member_meta = member_offset_array [ idx ] . member_meta_32_bits_real_double ;
member_size = member_offset_array [ idx ] . member_size_32_bits_real_double ;
2021-06-19 17:58:49 +02:00
break ;
case 3 :
2022-08-13 16:02:00 +02:00
member_meta = member_offset_array [ idx ] . member_meta_64_bits_real_double ;
member_size = member_offset_array [ idx ] . member_size_64_bits_real_double ;
2021-06-19 17:58:49 +02:00
break ;
}
d3 [ " member " ] = member_offset_array [ idx ] . member ;
d3 [ " offset " ] = offset ;
2022-08-13 16:02:00 +02:00
d3 [ " meta " ] = member_meta ;
offset + = member_size ;
2021-06-19 17:58:49 +02:00
members . push_back ( d3 ) ;
idx + + ;
}
d [ " classes " ] = type_offsets ;
core_type_member_offsets . push_back ( d ) ;
}
api_dump [ " builtin_class_member_offsets " ] = core_type_member_offsets ;
}
2023-09-25 22:22:06 +02:00
if ( p_include_docs ) {
EditorHelp : : generate_doc ( false ) ;
}
2021-06-19 17:58:49 +02:00
{
Fix various typos with codespell
Found via `codespell -q 3 -S ./thirdparty,*.po,./DONORS.md -L ackward,ang,ans,ba,beng,cas,childs,childrens,dof,doubleclick,fave,findn,hist,inout,leapyear,lod,nd,numer,ois,ony,paket,seeked,sinc,switchs,te,uint`
2021-07-07 17:17:32 +02:00
// Global enums and constants.
2021-06-19 17:58:49 +02:00
Array constants ;
2022-05-09 11:47:10 +02:00
HashMap < String , List < Pair < String , int64_t > > > enum_list ;
2023-01-14 16:08:15 +01:00
HashMap < String , bool > enum_is_bitfield ;
2021-06-19 17:58:49 +02:00
2023-09-25 22:22:06 +02:00
const DocData : : ClassDoc * global_scope_doc = nullptr ;
if ( p_include_docs ) {
global_scope_doc = EditorHelp : : get_doc_data ( ) - > class_list . getptr ( " @GlobalScope " ) ;
CRASH_COND_MSG ( ! global_scope_doc , " Could not find '@GlobalScope' in DocData. " ) ;
}
2021-06-19 17:58:49 +02:00
for ( int i = 0 ; i < CoreConstants : : get_global_constant_count ( ) ; i + + ) {
2022-05-09 11:47:10 +02:00
int64_t value = CoreConstants : : get_global_constant_value ( i ) ;
2021-06-19 17:58:49 +02:00
String enum_name = CoreConstants : : get_global_constant_enum ( i ) ;
String name = CoreConstants : : get_global_constant_name ( i ) ;
2023-01-08 00:55:54 +01:00
bool bitfield = CoreConstants : : is_global_constant_bitfield ( i ) ;
2021-12-09 10:42:46 +01:00
if ( ! enum_name . is_empty ( ) ) {
2022-05-09 11:47:10 +02:00
enum_list [ enum_name ] . push_back ( Pair < String , int64_t > ( name , value ) ) ;
2023-01-14 16:08:15 +01:00
enum_is_bitfield [ enum_name ] = bitfield ;
2021-06-19 17:58:49 +02:00
} else {
Dictionary d ;
d [ " name " ] = name ;
d [ " value " ] = value ;
2023-01-08 00:55:54 +01:00
d [ " is_bitfield " ] = bitfield ;
2023-09-25 22:22:06 +02:00
if ( p_include_docs ) {
for ( const DocData : : ConstantDoc & constant_doc : global_scope_doc - > constants ) {
if ( constant_doc . name = = name ) {
2023-10-14 12:06:37 +02:00
d [ " description " ] = fix_doc_description ( constant_doc . description ) ;
2023-09-25 22:22:06 +02:00
break ;
}
}
}
2021-06-19 17:58:49 +02:00
constants . push_back ( d ) ;
}
}
api_dump [ " global_constants " ] = constants ;
Array enums ;
2022-05-09 11:47:10 +02:00
for ( const KeyValue < String , List < Pair < String , int64_t > > > & E : enum_list ) {
2021-06-19 17:58:49 +02:00
Dictionary d1 ;
2021-08-09 22:13:42 +02:00
d1 [ " name " ] = E . key ;
2023-01-14 16:08:15 +01:00
d1 [ " is_bitfield " ] = enum_is_bitfield [ E . key ] ;
2023-09-25 22:22:06 +02:00
if ( p_include_docs ) {
const DocData : : EnumDoc * enum_doc = global_scope_doc - > enums . getptr ( E . key ) ;
if ( enum_doc ) {
2023-10-14 12:06:37 +02:00
d1 [ " description " ] = fix_doc_description ( enum_doc - > description ) ;
2023-09-25 22:22:06 +02:00
}
}
2021-06-19 17:58:49 +02:00
Array values ;
2022-05-09 11:47:10 +02:00
for ( const Pair < String , int64_t > & F : E . value ) {
2021-06-19 17:58:49 +02:00
Dictionary d2 ;
2021-07-16 05:45:57 +02:00
d2 [ " name " ] = F . first ;
d2 [ " value " ] = F . second ;
2023-09-25 22:22:06 +02:00
if ( p_include_docs ) {
for ( const DocData : : ConstantDoc & constant_doc : global_scope_doc - > constants ) {
if ( constant_doc . name = = F . first ) {
2023-10-14 12:06:37 +02:00
d2 [ " description " ] = fix_doc_description ( constant_doc . description ) ;
2023-09-25 22:22:06 +02:00
break ;
}
}
}
2021-06-19 17:58:49 +02:00
values . push_back ( d2 ) ;
}
d1 [ " values " ] = values ;
enums . push_back ( d1 ) ;
}
api_dump [ " global_enums " ] = enums ;
}
{
Array utility_funcs ;
List < StringName > utility_func_names ;
Variant : : get_utility_function_list ( & utility_func_names ) ;
2023-09-25 22:22:06 +02:00
const DocData : : ClassDoc * global_scope_doc = nullptr ;
if ( p_include_docs ) {
global_scope_doc = EditorHelp : : get_doc_data ( ) - > class_list . getptr ( " @GlobalScope " ) ;
CRASH_COND_MSG ( ! global_scope_doc , " Could not find '@GlobalScope' in DocData. " ) ;
}
2021-07-24 15:46:25 +02:00
for ( const StringName & name : utility_func_names ) {
2021-06-19 17:58:49 +02:00
Dictionary func ;
func [ " name " ] = String ( name ) ;
if ( Variant : : has_utility_function_return_value ( name ) ) {
Variant : : Type rt = Variant : : get_utility_function_return_type ( name ) ;
func [ " return_type " ] = rt = = Variant : : NIL ? String ( " Variant " ) : Variant : : get_type_name ( rt ) ;
}
switch ( Variant : : get_utility_function_type ( name ) ) {
case Variant : : UTILITY_FUNC_TYPE_MATH :
func [ " category " ] = " math " ;
break ;
case Variant : : UTILITY_FUNC_TYPE_RANDOM :
func [ " category " ] = " random " ;
break ;
case Variant : : UTILITY_FUNC_TYPE_GENERAL :
func [ " category " ] = " general " ;
break ;
}
bool vararg = Variant : : is_utility_function_vararg ( name ) ;
func [ " is_vararg " ] = Variant : : is_utility_function_vararg ( name ) ;
func [ " hash " ] = Variant : : get_utility_function_hash ( name ) ;
Array arguments ;
int argcount = Variant : : get_utility_function_argument_count ( name ) ;
for ( int i = 0 ; i < argcount ; i + + ) {
Dictionary arg ;
String argname = vararg ? " arg " + itos ( i + 1 ) : Variant : : get_utility_function_argument_name ( name , i ) ;
arg [ " name " ] = argname ;
2022-10-02 18:57:48 +02:00
arg [ " type " ] = get_builtin_or_variant_type_name ( Variant : : get_utility_function_argument_type ( name , i ) ) ;
2021-06-19 17:58:49 +02:00
//no default value support in utility functions
arguments . push_back ( arg ) ;
}
if ( arguments . size ( ) ) {
func [ " arguments " ] = arguments ;
}
2023-09-25 22:22:06 +02:00
if ( p_include_docs ) {
for ( const DocData : : MethodDoc & method_doc : global_scope_doc - > methods ) {
if ( method_doc . name = = name ) {
2023-10-14 12:06:37 +02:00
func [ " description " ] = fix_doc_description ( method_doc . description ) ;
2023-09-25 22:22:06 +02:00
break ;
}
}
}
2021-06-19 17:58:49 +02:00
utility_funcs . push_back ( func ) ;
}
api_dump [ " utility_functions " ] = utility_funcs ;
}
{
// builtin types
Array builtins ;
for ( int i = 0 ; i < Variant : : VARIANT_MAX ; i + + ) {
if ( i = = Variant : : OBJECT ) {
continue ;
}
Variant : : Type type = Variant : : Type ( i ) ;
Dictionary d ;
d [ " name " ] = Variant : : get_type_name ( type ) ;
if ( Variant : : has_indexing ( type ) ) {
2022-10-02 18:57:48 +02:00
d [ " indexing_return_type " ] = get_builtin_or_variant_type_name ( Variant : : get_indexed_element_type ( type ) ) ;
2021-06-19 17:58:49 +02:00
}
2022-09-21 00:14:53 +02:00
d [ " is_keyed " ] = Variant : : is_keyed ( type ) ;
2021-07-29 15:17:07 +02:00
2023-09-25 22:22:06 +02:00
DocData : : ClassDoc * builtin_doc = nullptr ;
if ( p_include_docs & & d [ " name " ] ! = " Nil " ) {
builtin_doc = EditorHelp : : get_doc_data ( ) - > class_list . getptr ( d [ " name " ] ) ;
CRASH_COND_MSG ( ! builtin_doc , vformat ( " Could not find '%s' in DocData. " , d [ " name " ] ) ) ;
}
2021-06-19 17:58:49 +02:00
{
//members
Array members ;
List < StringName > member_names ;
Variant : : get_member_list ( type , & member_names ) ;
2021-07-24 15:46:25 +02:00
for ( const StringName & member_name : member_names ) {
2021-06-19 17:58:49 +02:00
Dictionary d2 ;
d2 [ " name " ] = String ( member_name ) ;
2022-10-02 18:57:48 +02:00
d2 [ " type " ] = get_builtin_or_variant_type_name ( Variant : : get_member_type ( type , member_name ) ) ;
2023-09-25 22:22:06 +02:00
if ( p_include_docs ) {
for ( const DocData : : PropertyDoc & property_doc : builtin_doc - > properties ) {
if ( property_doc . name = = member_name ) {
2023-10-14 12:06:37 +02:00
d2 [ " description " ] = fix_doc_description ( property_doc . description ) ;
2023-09-25 22:22:06 +02:00
break ;
}
}
}
2021-06-19 17:58:49 +02:00
members . push_back ( d2 ) ;
}
if ( members . size ( ) ) {
d [ " members " ] = members ;
}
}
{
//constants
Array constants ;
List < StringName > constant_names ;
Variant : : get_constants_for_type ( type , & constant_names ) ;
2021-07-24 15:46:25 +02:00
for ( const StringName & constant_name : constant_names ) {
2021-06-19 17:58:49 +02:00
Dictionary d2 ;
d2 [ " name " ] = String ( constant_name ) ;
Variant constant = Variant : : get_constant_value ( type , constant_name ) ;
2022-10-02 18:57:48 +02:00
d2 [ " type " ] = get_builtin_or_variant_type_name ( constant . get_type ( ) ) ;
2021-06-19 17:58:49 +02:00
d2 [ " value " ] = constant . get_construct_string ( ) ;
2023-09-25 22:22:06 +02:00
if ( p_include_docs ) {
for ( const DocData : : ConstantDoc & constant_doc : builtin_doc - > constants ) {
if ( constant_doc . name = = constant_name ) {
2023-10-14 12:06:37 +02:00
d2 [ " description " ] = fix_doc_description ( constant_doc . description ) ;
2023-09-25 22:22:06 +02:00
break ;
}
}
}
2021-06-19 17:58:49 +02:00
constants . push_back ( d2 ) ;
}
if ( constants . size ( ) ) {
d [ " constants " ] = constants ;
}
}
2022-04-18 20:17:03 +02:00
{
//enums
Array enums ;
List < StringName > enum_names ;
Variant : : get_enums_for_type ( type , & enum_names ) ;
for ( const StringName & enum_name : enum_names ) {
Dictionary enum_dict ;
enum_dict [ " name " ] = String ( enum_name ) ;
List < StringName > enumeration_names ;
Variant : : get_enumerations_for_enum ( type , enum_name , & enumeration_names ) ;
Array values ;
for ( const StringName & enumeration : enumeration_names ) {
Dictionary values_dict ;
values_dict [ " name " ] = String ( enumeration ) ;
values_dict [ " value " ] = Variant : : get_enum_value ( type , enum_name , enumeration ) ;
2023-09-25 22:22:06 +02:00
if ( p_include_docs ) {
for ( const DocData : : ConstantDoc & constant_doc : builtin_doc - > constants ) {
if ( constant_doc . name = = enumeration ) {
2023-10-14 12:06:37 +02:00
values_dict [ " description " ] = fix_doc_description ( constant_doc . description ) ;
2023-09-25 22:22:06 +02:00
break ;
}
}
}
2022-04-18 20:17:03 +02:00
values . push_back ( values_dict ) ;
}
2023-09-25 22:22:06 +02:00
if ( p_include_docs ) {
const DocData : : EnumDoc * enum_doc = builtin_doc - > enums . getptr ( enum_name ) ;
if ( enum_doc ) {
2023-10-14 12:06:37 +02:00
enum_dict [ " description " ] = fix_doc_description ( enum_doc - > description ) ;
2023-09-25 22:22:06 +02:00
}
}
2022-04-18 20:17:03 +02:00
if ( values . size ( ) ) {
enum_dict [ " values " ] = values ;
}
enums . push_back ( enum_dict ) ;
}
if ( enums . size ( ) ) {
d [ " enums " ] = enums ;
}
}
2021-06-19 17:58:49 +02:00
{
//operators
Array operators ;
for ( int j = 0 ; j < Variant : : VARIANT_MAX ; j + + ) {
for ( int k = 0 ; k < Variant : : OP_MAX ; k + + ) {
Variant : : Type rt = Variant : : get_operator_return_type ( Variant : : Operator ( k ) , type , Variant : : Type ( j ) ) ;
if ( rt ! = Variant : : NIL ) {
Dictionary d2 ;
2023-09-25 22:22:06 +02:00
String operator_name = Variant : : get_operator_name ( Variant : : Operator ( k ) ) ;
d2 [ " name " ] = operator_name ;
2021-06-19 17:58:49 +02:00
if ( k ! = Variant : : OP_NEGATE & & k ! = Variant : : OP_POSITIVE & & k ! = Variant : : OP_NOT & & k ! = Variant : : OP_BIT_NEGATE ) {
2022-10-02 18:57:48 +02:00
d2 [ " right_type " ] = get_builtin_or_variant_type_name ( Variant : : Type ( j ) ) ;
2021-06-19 17:58:49 +02:00
}
2022-10-02 18:57:48 +02:00
d2 [ " return_type " ] = get_builtin_or_variant_type_name ( Variant : : get_operator_return_type ( Variant : : Operator ( k ) , type , Variant : : Type ( j ) ) ) ;
2023-09-25 22:22:06 +02:00
if ( p_include_docs & & builtin_doc ! = nullptr ) {
for ( const DocData : : MethodDoc & operator_doc : builtin_doc - > operators ) {
if ( operator_doc . name = = " operator " + operator_name ) {
2023-10-14 12:06:37 +02:00
d2 [ " description " ] = fix_doc_description ( operator_doc . description ) ;
2023-09-25 22:22:06 +02:00
break ;
}
}
}
2021-06-19 17:58:49 +02:00
operators . push_back ( d2 ) ;
}
}
}
if ( operators . size ( ) ) {
d [ " operators " ] = operators ;
}
}
{
//methods
Array methods ;
List < StringName > method_names ;
Variant : : get_builtin_method_list ( type , & method_names ) ;
2021-07-24 15:46:25 +02:00
for ( const StringName & method_name : method_names ) {
2021-06-19 17:58:49 +02:00
Dictionary d2 ;
d2 [ " name " ] = String ( method_name ) ;
if ( Variant : : has_builtin_method_return_value ( type , method_name ) ) {
Variant : : Type ret_type = Variant : : get_builtin_method_return_type ( type , method_name ) ;
d2 [ " return_type " ] = ret_type = = Variant : : NIL ? String ( " Variant " ) : Variant : : get_type_name ( ret_type ) ;
}
d2 [ " is_vararg " ] = Variant : : is_builtin_method_vararg ( type , method_name ) ;
d2 [ " is_const " ] = Variant : : is_builtin_method_const ( type , method_name ) ;
d2 [ " is_static " ] = Variant : : is_builtin_method_static ( type , method_name ) ;
d2 [ " hash " ] = Variant : : get_builtin_method_hash ( type , method_name ) ;
Vector < Variant > default_args = Variant : : get_builtin_method_default_arguments ( type , method_name ) ;
Array arguments ;
int argcount = Variant : : get_builtin_method_argument_count ( type , method_name ) ;
for ( int j = 0 ; j < argcount ; j + + ) {
Dictionary d3 ;
d3 [ " name " ] = Variant : : get_builtin_method_argument_name ( type , method_name , j ) ;
2022-10-02 18:57:48 +02:00
d3 [ " type " ] = get_builtin_or_variant_type_name ( Variant : : get_builtin_method_argument_type ( type , method_name , j ) ) ;
2021-06-19 17:58:49 +02:00
if ( j > = ( argcount - default_args . size ( ) ) ) {
int dargidx = j - ( argcount - default_args . size ( ) ) ;
d3 [ " default_value " ] = default_args [ dargidx ] . get_construct_string ( ) ;
}
arguments . push_back ( d3 ) ;
}
if ( arguments . size ( ) ) {
d2 [ " arguments " ] = arguments ;
}
2023-09-25 22:22:06 +02:00
if ( p_include_docs ) {
for ( const DocData : : MethodDoc & method_doc : builtin_doc - > methods ) {
if ( method_doc . name = = method_name ) {
2023-10-14 12:06:37 +02:00
d2 [ " description " ] = fix_doc_description ( method_doc . description ) ;
2023-09-25 22:22:06 +02:00
break ;
}
}
}
2021-06-19 17:58:49 +02:00
methods . push_back ( d2 ) ;
}
if ( methods . size ( ) ) {
d [ " methods " ] = methods ;
}
}
{
//constructors
Array constructors ;
for ( int j = 0 ; j < Variant : : get_constructor_count ( type ) ; j + + ) {
Dictionary d2 ;
d2 [ " index " ] = j ;
Array arguments ;
int argcount = Variant : : get_constructor_argument_count ( type , j ) ;
for ( int k = 0 ; k < argcount ; k + + ) {
Dictionary d3 ;
d3 [ " name " ] = Variant : : get_constructor_argument_name ( type , j , k ) ;
2022-10-02 18:57:48 +02:00
d3 [ " type " ] = get_builtin_or_variant_type_name ( Variant : : get_constructor_argument_type ( type , j , k ) ) ;
2021-06-19 17:58:49 +02:00
arguments . push_back ( d3 ) ;
}
if ( arguments . size ( ) ) {
d2 [ " arguments " ] = arguments ;
}
2023-09-25 22:22:06 +02:00
if ( p_include_docs & & builtin_doc ) {
for ( const DocData : : MethodDoc & constructor_doc : builtin_doc - > constructors ) {
if ( constructor_doc . arguments . size ( ) ! = argcount ) {
continue ;
}
bool constructor_found = true ;
for ( int k = 0 ; k < argcount ; k + + ) {
const DocData : : ArgumentDoc & argument_doc = constructor_doc . arguments [ k ] ;
const Dictionary & argument_dict = arguments [ k ] ;
const String & argument_string = argument_dict [ " type " ] ;
if ( argument_doc . type ! = argument_string ) {
constructor_found = false ;
break ;
}
}
if ( constructor_found ) {
2023-10-14 12:06:37 +02:00
d2 [ " description " ] = fix_doc_description ( constructor_doc . description ) ;
2023-09-25 22:22:06 +02:00
}
}
}
2021-06-19 17:58:49 +02:00
constructors . push_back ( d2 ) ;
}
if ( constructors . size ( ) ) {
d [ " constructors " ] = constructors ;
}
}
2021-07-29 15:53:05 +02:00
{
//destructor
d [ " has_destructor " ] = Variant : : has_destructor ( type ) ;
}
2021-06-19 17:58:49 +02:00
2023-09-25 22:22:06 +02:00
if ( p_include_docs & & builtin_doc ! = nullptr ) {
2023-10-14 12:06:37 +02:00
d [ " brief_description " ] = fix_doc_description ( builtin_doc - > brief_description ) ;
d [ " description " ] = fix_doc_description ( builtin_doc - > description ) ;
2023-09-25 22:22:06 +02:00
}
2021-06-19 17:58:49 +02:00
builtins . push_back ( d ) ;
}
api_dump [ " builtin_classes " ] = builtins ;
}
{
// classes
Array classes ;
List < StringName > class_list ;
ClassDB : : get_class_list ( & class_list ) ;
class_list . sort_custom < StringName : : AlphCompare > ( ) ;
2021-07-24 15:46:25 +02:00
for ( const StringName & class_name : class_list ) {
2023-08-21 16:55:22 +02:00
if ( ! ClassDB : : is_class_exposed ( class_name ) ) {
continue ;
}
2021-06-19 17:58:49 +02:00
Dictionary d ;
d [ " name " ] = String ( class_name ) ;
d [ " is_refcounted " ] = ClassDB : : is_parent_class ( class_name , " RefCounted " ) ;
d [ " is_instantiable " ] = ClassDB : : can_instantiate ( class_name ) ;
StringName parent_class = ClassDB : : get_parent_class ( class_name ) ;
if ( parent_class ! = StringName ( ) ) {
d [ " inherits " ] = String ( parent_class ) ;
}
2023-09-25 22:22:06 +02:00
DocData : : ClassDoc * class_doc = nullptr ;
if ( p_include_docs ) {
class_doc = EditorHelp : : get_doc_data ( ) - > class_list . getptr ( class_name ) ;
CRASH_COND_MSG ( ! class_doc , vformat ( " Could not find '%s' in DocData. " , class_name ) ) ;
}
2021-06-19 17:58:49 +02:00
{
ClassDB : : APIType api = ClassDB : : get_api_type ( class_name ) ;
static const char * api_type [ 5 ] = { " core " , " editor " , " extension " , " editor_extension " } ;
d [ " api_type " ] = api_type [ api ] ;
}
{
//constants
Array constants ;
List < String > constant_list ;
ClassDB : : get_integer_constant_list ( class_name , & constant_list , true ) ;
2021-07-24 15:46:25 +02:00
for ( const String & F : constant_list ) {
2021-07-16 05:45:57 +02:00
StringName enum_name = ClassDB : : get_integer_constant_enum ( class_name , F ) ;
2021-06-19 17:58:49 +02:00
if ( enum_name ! = StringName ( ) ) {
continue ; //enums will be handled on their own
}
Dictionary d2 ;
2021-07-16 05:45:57 +02:00
d2 [ " name " ] = String ( F ) ;
d2 [ " value " ] = ClassDB : : get_integer_constant ( class_name , F ) ;
2021-06-19 17:58:49 +02:00
2023-09-25 22:22:06 +02:00
if ( p_include_docs ) {
for ( const DocData : : ConstantDoc & constant_doc : class_doc - > constants ) {
if ( constant_doc . name = = F ) {
2023-10-14 12:06:37 +02:00
d2 [ " description " ] = fix_doc_description ( constant_doc . description ) ;
2023-09-25 22:22:06 +02:00
break ;
}
}
}
2021-06-19 17:58:49 +02:00
constants . push_back ( d2 ) ;
}
if ( constants . size ( ) ) {
d [ " constants " ] = constants ;
}
}
{
//enum
Array enums ;
List < StringName > enum_list ;
ClassDB : : get_enum_list ( class_name , & enum_list , true ) ;
2021-07-24 15:46:25 +02:00
for ( const StringName & F : enum_list ) {
2021-06-19 17:58:49 +02:00
Dictionary d2 ;
2021-07-16 05:45:57 +02:00
d2 [ " name " ] = String ( F ) ;
2022-06-24 11:16:37 +02:00
d2 [ " is_bitfield " ] = ClassDB : : is_enum_bitfield ( class_name , F ) ;
2021-06-19 17:58:49 +02:00
Array values ;
List < StringName > enum_constant_list ;
2021-07-16 05:45:57 +02:00
ClassDB : : get_enum_constants ( class_name , F , & enum_constant_list , true ) ;
2021-06-19 17:58:49 +02:00
for ( List < StringName > : : Element * G = enum_constant_list . front ( ) ; G ; G = G - > next ( ) ) {
Dictionary d3 ;
d3 [ " name " ] = String ( G - > get ( ) ) ;
d3 [ " value " ] = ClassDB : : get_integer_constant ( class_name , G - > get ( ) ) ;
2023-09-25 22:22:06 +02:00
if ( p_include_docs ) {
for ( const DocData : : ConstantDoc & constant_doc : class_doc - > constants ) {
if ( constant_doc . name = = G - > get ( ) ) {
2023-10-14 12:06:37 +02:00
d3 [ " description " ] = fix_doc_description ( constant_doc . description ) ;
2023-09-25 22:22:06 +02:00
break ;
}
}
}
2021-06-19 17:58:49 +02:00
values . push_back ( d3 ) ;
}
d2 [ " values " ] = values ;
2023-09-25 22:22:06 +02:00
if ( p_include_docs ) {
const DocData : : EnumDoc * enum_doc = class_doc - > enums . getptr ( F ) ;
if ( enum_doc ) {
2023-10-14 12:06:37 +02:00
d2 [ " description " ] = fix_doc_description ( enum_doc - > description ) ;
2023-09-25 22:22:06 +02:00
}
}
2021-06-19 17:58:49 +02:00
enums . push_back ( d2 ) ;
}
if ( enums . size ( ) ) {
d [ " enums " ] = enums ;
}
}
{
//methods
Array methods ;
List < MethodInfo > method_list ;
ClassDB : : get_method_list ( class_name , & method_list , true ) ;
2021-07-24 15:46:25 +02:00
for ( const MethodInfo & F : method_list ) {
2021-07-16 05:45:57 +02:00
StringName method_name = F . name ;
2021-08-22 03:52:44 +02:00
if ( ( F . flags & METHOD_FLAG_VIRTUAL ) & & ! ( F . flags & METHOD_FLAG_OBJECT_CORE ) ) {
2021-06-19 17:58:49 +02:00
//virtual method
2021-07-16 05:45:57 +02:00
const MethodInfo & mi = F ;
2021-06-19 17:58:49 +02:00
Dictionary d2 ;
d2 [ " name " ] = String ( method_name ) ;
2021-07-16 05:45:57 +02:00
d2 [ " is_const " ] = ( F . flags & METHOD_FLAG_CONST ) ? true : false ;
2022-03-19 13:18:52 +01:00
d2 [ " is_static " ] = ( F . flags & METHOD_FLAG_STATIC ) ? true : false ;
2021-06-19 17:58:49 +02:00
d2 [ " is_vararg " ] = false ;
d2 [ " is_virtual " ] = true ;
// virtual functions have no hash since no MethodBind is involved
bool has_return = mi . return_val . type ! = Variant : : NIL | | ( mi . return_val . usage & PROPERTY_USAGE_NIL_IS_VARIANT ) ;
Array arguments ;
for ( int i = ( has_return ? - 1 : 0 ) ; i < mi . arguments . size ( ) ; i + + ) {
PropertyInfo pinfo = i = = - 1 ? mi . return_val : mi . arguments [ i ] ;
Dictionary d3 ;
if ( i > = 0 ) {
d3 [ " name " ] = pinfo . name ;
}
2021-07-29 15:17:07 +02:00
2022-10-02 18:57:48 +02:00
d3 [ " type " ] = get_property_info_type_name ( pinfo ) ;
2021-06-19 17:58:49 +02:00
2023-01-25 03:51:32 +01:00
if ( mi . get_argument_meta ( i ) > 0 ) {
d3 [ " meta " ] = get_type_meta_name ( ( GodotTypeInfo : : Metadata ) mi . get_argument_meta ( i ) ) ;
}
2021-06-19 17:58:49 +02:00
if ( i = = - 1 ) {
d2 [ " return_value " ] = d3 ;
} else {
arguments . push_back ( d3 ) ;
}
}
if ( arguments . size ( ) ) {
d2 [ " arguments " ] = arguments ;
}
2023-09-25 22:22:06 +02:00
if ( p_include_docs ) {
for ( const DocData : : MethodDoc & method_doc : class_doc - > methods ) {
if ( method_doc . name = = method_name ) {
2023-10-14 12:06:37 +02:00
d2 [ " description " ] = fix_doc_description ( method_doc . description ) ;
2023-09-25 22:22:06 +02:00
break ;
}
}
}
2021-06-19 17:58:49 +02:00
methods . push_back ( d2 ) ;
2021-07-16 05:45:57 +02:00
} else if ( F . name . begins_with ( " _ " ) ) {
2021-06-19 17:58:49 +02:00
//hidden method, ignore
} else {
Dictionary d2 ;
d2 [ " name " ] = String ( method_name ) ;
MethodBind * method = ClassDB : : get_method ( class_name , method_name ) ;
if ( ! method ) {
continue ;
}
d2 [ " is_const " ] = method - > is_const ( ) ;
d2 [ " is_vararg " ] = method - > is_vararg ( ) ;
2022-03-19 13:18:52 +01:00
d2 [ " is_static " ] = method - > is_static ( ) ;
2021-06-19 17:58:49 +02:00
d2 [ " is_virtual " ] = false ;
d2 [ " hash " ] = method - > get_hash ( ) ;
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
Vector < uint32_t > compat_hashes = ClassDB : : get_method_compatibility_hashes ( class_name , method_name ) ;
2023-09-10 19:36:44 +02:00
Array compatibility ;
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
if ( compat_hashes . size ( ) ) {
for ( int i = 0 ; i < compat_hashes . size ( ) ; i + + ) {
compatibility . push_back ( compat_hashes [ i ] ) ;
}
2023-09-10 19:36:44 +02:00
}
# ifndef DISABLE_DEPRECATED
GDExtensionCompatHashes : : get_legacy_hashes ( class_name , method_name , compatibility ) ;
# endif
if ( compatibility . size ( ) > 0 ) {
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
d2 [ " hash_compatibility " ] = compatibility ;
}
2021-06-19 17:58:49 +02:00
Vector < Variant > default_args = method - > get_default_arguments ( ) ;
Array arguments ;
for ( int i = ( method - > has_return ( ) ? - 1 : 0 ) ; i < method - > get_argument_count ( ) ; i + + ) {
PropertyInfo pinfo = i = = - 1 ? method - > get_return_info ( ) : method - > get_argument_info ( i ) ;
Dictionary d3 ;
if ( i > = 0 ) {
d3 [ " name " ] = pinfo . name ;
}
2022-10-02 18:57:48 +02:00
d3 [ " type " ] = get_property_info_type_name ( pinfo ) ;
2021-06-19 17:58:49 +02:00
if ( method - > get_argument_meta ( i ) > 0 ) {
2023-01-25 03:51:32 +01:00
d3 [ " meta " ] = get_type_meta_name ( method - > get_argument_meta ( i ) ) ;
2021-06-19 17:58:49 +02:00
}
if ( i > = 0 & & i > = ( method - > get_argument_count ( ) - default_args . size ( ) ) ) {
int dargidx = i - ( method - > get_argument_count ( ) - default_args . size ( ) ) ;
d3 [ " default_value " ] = default_args [ dargidx ] . get_construct_string ( ) ;
}
if ( i = = - 1 ) {
d2 [ " return_value " ] = d3 ;
} else {
arguments . push_back ( d3 ) ;
}
}
if ( arguments . size ( ) ) {
d2 [ " arguments " ] = arguments ;
}
2023-09-25 22:22:06 +02:00
if ( p_include_docs ) {
for ( const DocData : : MethodDoc & method_doc : class_doc - > methods ) {
if ( method_doc . name = = method_name ) {
2023-10-14 12:06:37 +02:00
d2 [ " description " ] = fix_doc_description ( method_doc . description ) ;
2023-09-25 22:22:06 +02:00
break ;
}
}
}
2021-06-19 17:58:49 +02:00
methods . push_back ( d2 ) ;
}
}
if ( methods . size ( ) ) {
d [ " methods " ] = methods ;
}
}
{
//signals
Array signals ;
List < MethodInfo > signal_list ;
ClassDB : : get_signal_list ( class_name , & signal_list , true ) ;
2021-07-24 15:46:25 +02:00
for ( const MethodInfo & F : signal_list ) {
2021-07-16 05:45:57 +02:00
StringName signal_name = F . name ;
2021-06-19 17:58:49 +02:00
Dictionary d2 ;
d2 [ " name " ] = String ( signal_name ) ;
Array arguments ;
2021-07-16 05:45:57 +02:00
for ( int i = 0 ; i < F . arguments . size ( ) ; i + + ) {
2021-06-19 17:58:49 +02:00
Dictionary d3 ;
2021-07-16 05:45:57 +02:00
d3 [ " name " ] = F . arguments [ i ] . name ;
2022-10-02 18:57:48 +02:00
d3 [ " type " ] = get_property_info_type_name ( F . arguments [ i ] ) ;
2023-01-25 03:51:32 +01:00
if ( F . get_argument_meta ( i ) > 0 ) {
d3 [ " meta " ] = get_type_meta_name ( ( GodotTypeInfo : : Metadata ) F . get_argument_meta ( i ) ) ;
}
2021-06-19 17:58:49 +02:00
arguments . push_back ( d3 ) ;
}
if ( arguments . size ( ) ) {
d2 [ " arguments " ] = arguments ;
}
2023-09-25 22:22:06 +02:00
if ( p_include_docs ) {
for ( const DocData : : MethodDoc & signal_doc : class_doc - > signals ) {
if ( signal_doc . name = = signal_name ) {
2023-10-14 12:06:37 +02:00
d2 [ " description " ] = fix_doc_description ( signal_doc . description ) ;
2023-09-25 22:22:06 +02:00
break ;
}
}
}
2021-06-19 17:58:49 +02:00
signals . push_back ( d2 ) ;
}
if ( signals . size ( ) ) {
d [ " signals " ] = signals ;
}
}
{
//properties
Array properties ;
List < PropertyInfo > property_list ;
ClassDB : : get_property_list ( class_name , & property_list , true ) ;
2021-07-24 15:46:25 +02:00
for ( const PropertyInfo & F : property_list ) {
2022-08-15 11:37:58 +02:00
if ( F . usage & PROPERTY_USAGE_CATEGORY | | F . usage & PROPERTY_USAGE_GROUP | | F . usage & PROPERTY_USAGE_SUBGROUP | | ( F . type = = Variant : : NIL & & F . usage & PROPERTY_USAGE_ARRAY ) ) {
2021-06-19 17:58:49 +02:00
continue ; //not real properties
}
2021-07-16 05:45:57 +02:00
if ( F . name . begins_with ( " _ " ) ) {
2021-06-19 17:58:49 +02:00
continue ; //hidden property
}
2022-08-15 11:37:58 +02:00
if ( F . name . find ( " / " ) > = 0 ) {
// Ignore properties with '/' (slash) in the name. These are only meant for use in the inspector.
continue ;
}
2021-07-16 05:45:57 +02:00
StringName property_name = F . name ;
2021-06-19 17:58:49 +02:00
Dictionary d2 ;
2022-10-02 18:57:48 +02:00
d2 [ " type " ] = get_property_info_type_name ( F ) ;
2021-06-19 17:58:49 +02:00
d2 [ " name " ] = String ( property_name ) ;
2022-08-15 11:09:14 +02:00
StringName setter = ClassDB : : get_property_setter ( class_name , F . name ) ;
if ( ! ( setter = = " " ) ) {
d2 [ " setter " ] = setter ;
}
StringName getter = ClassDB : : get_property_getter ( class_name , F . name ) ;
if ( ! ( getter = = " " ) ) {
d2 [ " getter " ] = getter ;
}
int index = ClassDB : : get_property_index ( class_name , F . name ) ;
if ( index ! = - 1 ) {
d2 [ " index " ] = index ;
}
2023-09-25 22:22:06 +02:00
if ( p_include_docs ) {
for ( const DocData : : PropertyDoc & property_doc : class_doc - > properties ) {
if ( property_doc . name = = property_name ) {
2023-10-14 12:06:37 +02:00
d2 [ " description " ] = fix_doc_description ( property_doc . description ) ;
2023-09-25 22:22:06 +02:00
break ;
}
}
}
2021-06-19 17:58:49 +02:00
properties . push_back ( d2 ) ;
}
if ( properties . size ( ) ) {
d [ " properties " ] = properties ;
}
}
2023-09-25 22:22:06 +02:00
if ( p_include_docs & & class_doc ! = nullptr ) {
2023-10-14 12:06:37 +02:00
d [ " brief_description " ] = fix_doc_description ( class_doc - > brief_description ) ;
d [ " description " ] = fix_doc_description ( class_doc - > description ) ;
2023-09-25 22:22:06 +02:00
}
2021-06-19 17:58:49 +02:00
classes . push_back ( d ) ;
}
api_dump [ " classes " ] = classes ;
}
{
// singletons
Array singletons ;
List < Engine : : Singleton > singleton_list ;
Engine : : get_singleton ( ) - > get_singletons ( & singleton_list ) ;
2021-07-24 15:46:25 +02:00
for ( const Engine : : Singleton & s : singleton_list ) {
2021-06-19 17:58:49 +02:00
Dictionary d ;
d [ " name " ] = s . name ;
if ( s . class_name ! = StringName ( ) ) {
d [ " type " ] = String ( s . class_name ) ;
} else {
d [ " type " ] = String ( s . ptr - > get_class ( ) ) ;
}
singletons . push_back ( d ) ;
}
if ( singletons . size ( ) ) {
api_dump [ " singletons " ] = singletons ;
}
}
2021-08-23 19:53:27 +02:00
{
Array native_structures ;
2022-03-14 15:52:03 +01:00
List < StringName > native_structs ;
ClassDB : : get_native_struct_list ( & native_structs ) ;
native_structs . sort_custom < StringName : : AlphCompare > ( ) ;
2021-08-23 19:53:27 +02:00
2022-03-14 15:52:03 +01:00
for ( const StringName & E : native_structs ) {
String code = ClassDB : : get_native_struct_code ( E ) ;
2021-08-23 19:53:27 +02:00
2021-08-27 23:19:51 +02:00
Dictionary d ;
2022-03-14 15:52:03 +01:00
d [ " name " ] = String ( E ) ;
d [ " format " ] = code ;
2021-08-27 23:19:51 +02:00
native_structures . push_back ( d ) ;
}
2021-08-23 19:53:27 +02:00
api_dump [ " native_structures " ] = native_structures ;
}
2021-06-19 17:58:49 +02:00
return api_dump ;
}
2023-09-25 22:22:06 +02:00
void GDExtensionAPIDump : : generate_extension_json_file ( const String & p_path , bool p_include_docs ) {
Dictionary api = generate_extension_api ( p_include_docs ) ;
2021-06-19 17:58:49 +02:00
Ref < JSON > json ;
json . instantiate ( ) ;
2022-11-07 14:31:10 +01:00
String text = json - > stringify ( api , " \t " , false ) + " \n " ;
2022-03-23 10:08:58 +01:00
Ref < FileAccess > fa = FileAccess : : open ( p_path , FileAccess : : WRITE ) ;
2023-03-08 05:30:37 +01:00
ERR_FAIL_COND_MSG ( fa . is_null ( ) , vformat ( " Cannot open file '%s' for writing. " , p_path ) ) ;
2022-11-07 14:31:10 +01:00
fa - > store_string ( text ) ;
2021-06-19 17:58:49 +02:00
}
2022-11-07 14:31:10 +01:00
2023-05-16 13:03:52 +02:00
static bool compare_value ( const String & p_path , const String & p_field , const Variant & p_old_value , const Variant & p_new_value , bool p_allow_name_change ) {
bool failed = false ;
String path = p_path + " / " + p_field ;
if ( p_old_value . get_type ( ) = = Variant : : ARRAY & & p_new_value . get_type ( ) = = Variant : : ARRAY ) {
Array old_array = p_old_value ;
Array new_array = p_new_value ;
if ( ! compare_value ( path , " size " , old_array . size ( ) , new_array . size ( ) , p_allow_name_change ) ) {
failed = true ;
}
for ( int i = 0 ; i < old_array . size ( ) & & i < new_array . size ( ) ; i + + ) {
if ( ! compare_value ( path , itos ( i ) , old_array [ i ] , new_array [ i ] , p_allow_name_change ) ) {
failed = true ;
}
}
} else if ( p_old_value . get_type ( ) = = Variant : : DICTIONARY & & p_new_value . get_type ( ) = = Variant : : DICTIONARY ) {
Dictionary old_dict = p_old_value ;
Dictionary new_dict = p_new_value ;
Array old_keys = old_dict . keys ( ) ;
for ( int i = 0 ; i < old_keys . size ( ) ; i + + ) {
Variant key = old_keys [ i ] ;
if ( ! new_dict . has ( key ) ) {
failed = true ;
print_error ( vformat ( " Validate extension JSON: Error: Field '%s': %s was removed. " , p_path , key ) ) ;
continue ;
}
if ( p_allow_name_change & & key = = " name " ) {
continue ;
}
if ( ! compare_value ( path , key , old_dict [ key ] , new_dict [ key ] , p_allow_name_change ) ) {
failed = true ;
}
}
Array new_keys = old_dict . keys ( ) ;
for ( int i = 0 ; i < new_keys . size ( ) ; i + + ) {
Variant key = new_keys [ i ] ;
if ( ! old_dict . has ( key ) ) {
failed = true ;
print_error ( vformat ( " Validate extension JSON: Error: Field '%s': %s was added with value %s. " , p_path , key , new_dict [ key ] ) ) ;
}
}
} else {
bool equal = Variant : : evaluate ( Variant : : OP_EQUAL , p_old_value , p_new_value ) ;
if ( ! equal ) {
print_error ( vformat ( " Validate extension JSON: Error: Field '%s': %s changed value in new API, from %s to %s. " , p_path , p_field , p_old_value . get_construct_string ( ) , p_new_value . get_construct_string ( ) ) ) ;
return false ;
}
}
return ! failed ;
}
static bool compare_dict_array ( const Dictionary & p_old_api , const Dictionary & p_new_api , const String & p_base_array , const String & p_name_field , const Vector < String > & p_fields_to_compare , bool p_compare_hashes , const String & p_outer_class = String ( ) , bool p_compare_operators = false , bool p_compare_enum_value = false ) {
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
String base_array = p_outer_class + p_base_array ;
if ( ! p_old_api . has ( p_base_array ) ) {
return true ; // May just not have this array and its still good. Probably added recently.
}
bool failed = false ;
ERR_FAIL_COND_V_MSG ( ! p_new_api . has ( p_base_array ) , false , " New API lacks base array: " + p_base_array ) ;
Array new_api = p_new_api [ p_base_array ] ;
HashMap < String , Dictionary > new_api_assoc ;
for ( int i = 0 ; i < new_api . size ( ) ; i + + ) {
Dictionary elem = new_api [ i ] ;
2023-05-16 13:03:52 +02:00
ERR_FAIL_COND_V_MSG ( ! elem . has ( p_name_field ) , false , vformat ( " Validate extension JSON: Element of base_array '%s' is missing field '%s'. This is a bug. " , base_array , p_name_field ) ) ;
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
String name = elem [ p_name_field ] ;
if ( p_compare_operators & & elem . has ( " right_type " ) ) {
name + = " " + String ( elem [ " right_type " ] ) ;
}
new_api_assoc . insert ( name , elem ) ;
}
Array old_api = p_old_api [ p_base_array ] ;
for ( int i = 0 ; i < old_api . size ( ) ; i + + ) {
Dictionary old_elem = old_api [ i ] ;
if ( ! old_elem . has ( p_name_field ) ) {
failed = true ;
2023-05-16 13:03:52 +02:00
print_error ( vformat ( " Validate extension JSON: JSON file: element of base array '%s' is missing the field: '%s'. " , base_array , p_name_field ) ) ;
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
continue ;
}
String name = old_elem [ p_name_field ] ;
if ( p_compare_operators & & old_elem . has ( " right_type " ) ) {
name + = " " + String ( old_elem [ " right_type " ] ) ;
}
if ( ! new_api_assoc . has ( name ) ) {
failed = true ;
2023-05-16 13:03:52 +02:00
print_error ( vformat ( " Validate extension JSON: API was removed: %s/%s " , base_array , name ) ) ;
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
continue ;
}
Dictionary new_elem = new_api_assoc [ name ] ;
for ( int j = 0 ; j < p_fields_to_compare . size ( ) ; j + + ) {
String field = p_fields_to_compare [ j ] ;
bool optional = field . begins_with ( " * " ) ;
if ( optional ) {
// This is an optional field, but if exists it has to exist in both.
field = field . substr ( 1 , field . length ( ) ) ;
}
bool added = field . begins_with ( " + " ) ;
if ( added ) {
// Meaning this field must either exist or contents may not exist.
field = field . substr ( 1 , field . length ( ) ) ;
}
2023-05-16 13:03:52 +02:00
bool enum_values = field . begins_with ( " $ " ) ;
if ( enum_values ) {
// Meaning this field is a list of enum values.
field = field . substr ( 1 , field . length ( ) ) ;
}
bool allow_name_change = field . begins_with ( " @ " ) ;
if ( allow_name_change ) {
// Meaning that when structurally comparing the old and new value, the dictionary entry 'name' may change.
field = field . substr ( 1 , field . length ( ) ) ;
}
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
Variant old_value ;
if ( ! old_elem . has ( field ) ) {
if ( optional ) {
if ( new_elem . has ( field ) ) {
failed = true ;
2023-05-16 13:03:52 +02:00
print_error ( vformat ( " Validate extension JSON: JSON file: Field was added in a way that breaks compatibility '%s/%s': %s " , base_array , name , field ) ) ;
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
}
} else if ( added & & new_elem . has ( field ) ) {
// Should be ok, field now exists, should not be verified in prior versions where it does not.
} else {
failed = true ;
2023-05-16 13:03:52 +02:00
print_error ( vformat ( " Validate extension JSON: JSON file: Missing field in '%s/%s': %s " , base_array , name , field ) ) ;
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
}
continue ;
} else {
old_value = old_elem [ field ] ;
}
if ( ! new_elem . has ( field ) ) {
failed = true ;
2023-05-16 13:03:52 +02:00
ERR_PRINT ( vformat ( " Validate extension JSON: Missing field in current API '%s/%s': %s. This is a bug. " , base_array , name , field ) ) ;
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
continue ;
}
Variant new_value = new_elem [ field ] ;
2023-05-16 13:03:52 +02:00
if ( p_compare_enum_value & & name . ends_with ( " _MAX " ) ) {
if ( static_cast < int64_t > ( new_value ) > static_cast < int64_t > ( old_value ) ) {
// Ignore the _MAX value of an enum increasing.
continue ;
}
}
if ( enum_values ) {
if ( ! compare_dict_array ( old_elem , new_elem , field , " name " , { " value " } , false , base_array + " / " + name + " / " , false , true ) ) {
failed = true ;
}
} else if ( ! compare_value ( base_array + " / " + name , field , old_value , new_value , allow_name_change ) ) {
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
failed = true ;
}
}
if ( p_compare_hashes ) {
if ( ! old_elem . has ( " hash " ) ) {
if ( old_elem . has ( " is_virtual " ) & & bool ( old_elem [ " is_virtual " ] ) & & ! new_elem . has ( " hash " ) ) {
continue ; // No hash for virtual methods, go on.
}
failed = true ;
2023-05-16 13:03:52 +02:00
print_error ( vformat ( " Validate extension JSON: JSON file: element of base array '%s' is missing the field: 'hash'. " , base_array ) ) ;
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
continue ;
}
uint64_t old_hash = old_elem [ " hash " ] ;
if ( ! new_elem . has ( " hash " ) ) {
failed = true ;
2023-05-16 13:03:52 +02:00
print_error ( vformat ( " Validate extension JSON: Error: Field '%s' is missing the field: 'hash'. " , base_array ) ) ;
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
continue ;
}
uint64_t new_hash = new_elem [ " hash " ] ;
bool hash_found = false ;
if ( old_hash = = new_hash ) {
hash_found = true ;
} else if ( new_elem . has ( " hash_compatibility " ) ) {
Array compatibility = new_elem [ " hash_compatibility " ] ;
for ( int j = 0 ; j < compatibility . size ( ) ; j + + ) {
new_hash = compatibility [ j ] ;
if ( new_hash = = old_hash ) {
hash_found = true ;
break ;
}
}
}
if ( ! hash_found ) {
failed = true ;
2023-05-16 13:03:52 +02:00
print_error ( vformat ( " Validate extension JSON: Error: Hash changed for '%s/%s', from %08X to %08X. This means that the function has changed and no compatibility function was provided. " , base_array , name , old_hash , new_hash ) ) ;
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
continue ;
}
}
}
return ! failed ;
}
static bool compare_sub_dict_array ( HashSet < String > & r_removed_classes_registered , const String & p_outer , const String & p_outer_name , const Dictionary & p_old_api , const Dictionary & p_new_api , const String & p_base_array , const String & p_name_field , const Vector < String > & p_fields_to_compare , bool p_compare_hashes , bool p_compare_operators = false ) {
if ( ! p_old_api . has ( p_outer ) ) {
return true ; // May just not have this array and its still good. Probably added recently or optional.
}
bool failed = false ;
ERR_FAIL_COND_V_MSG ( ! p_new_api . has ( p_outer ) , false , " New API lacks base array: " + p_outer ) ;
Array new_api = p_new_api [ p_outer ] ;
HashMap < String , Dictionary > new_api_assoc ;
for ( int i = 0 ; i < new_api . size ( ) ; i + + ) {
Dictionary elem = new_api [ i ] ;
2023-05-16 13:03:52 +02:00
ERR_FAIL_COND_V_MSG ( ! elem . has ( p_outer_name ) , false , vformat ( " Validate extension JSON: Element of base_array '%s' is missing field '%s'. This is a bug. " , p_outer , p_outer_name ) ) ;
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
new_api_assoc . insert ( elem [ p_outer_name ] , elem ) ;
}
Array old_api = p_old_api [ p_outer ] ;
for ( int i = 0 ; i < old_api . size ( ) ; i + + ) {
Dictionary old_elem = old_api [ i ] ;
if ( ! old_elem . has ( p_outer_name ) ) {
failed = true ;
2023-05-16 13:03:52 +02:00
print_error ( vformat ( " Validate extension JSON: JSON file: element of base array '%s' is missing the field: '%s'. " , p_outer , p_outer_name ) ) ;
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
continue ;
}
String name = old_elem [ p_outer_name ] ;
if ( ! new_api_assoc . has ( name ) ) {
failed = true ;
if ( ! r_removed_classes_registered . has ( name ) ) {
2023-05-16 13:03:52 +02:00
print_error ( vformat ( " Validate extension JSON: API was removed: %s/%s " , p_outer , name ) ) ;
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
r_removed_classes_registered . insert ( name ) ;
}
continue ;
}
Dictionary new_elem = new_api_assoc [ name ] ;
if ( ! compare_dict_array ( old_elem , new_elem , p_base_array , p_name_field , p_fields_to_compare , p_compare_hashes , p_outer + " / " + name + " / " , p_compare_operators ) ) {
failed = true ;
}
}
return ! failed ;
}
Error GDExtensionAPIDump : : validate_extension_json_file ( const String & p_path ) {
Error error ;
String text = FileAccess : : get_file_as_string ( p_path , & error ) ;
if ( error ! = OK ) {
2023-05-16 13:03:52 +02:00
ERR_PRINT ( vformat ( " Validate extension JSON: Could not open file '%s'. " , p_path ) ) ;
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
return error ;
}
Ref < JSON > json ;
json . instantiate ( ) ;
error = json - > parse ( text ) ;
if ( error ! = OK ) {
2023-05-16 13:03:52 +02:00
ERR_PRINT ( vformat ( " Validate extension JSON: Error parsing '%s' at line %d: %s " , p_path , json - > get_error_line ( ) , json - > get_error_message ( ) ) ) ;
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
return error ;
}
Dictionary old_api = json - > get_data ( ) ;
Dictionary new_api = generate_extension_api ( ) ;
{ // Validate header:
Dictionary header = old_api [ " header " ] ;
ERR_FAIL_COND_V ( ! header . has ( " version_major " ) , ERR_INVALID_DATA ) ;
ERR_FAIL_COND_V ( ! header . has ( " version_minor " ) , ERR_INVALID_DATA ) ;
int major = header [ " version_major " ] ;
int minor = header [ " version_minor " ] ;
2023-05-16 13:03:52 +02:00
ERR_FAIL_COND_V_MSG ( major ! = VERSION_MAJOR , ERR_INVALID_DATA , vformat ( " JSON API dump is for a different engine version (%d) than this one (%d) " , major , VERSION_MAJOR ) ) ;
ERR_FAIL_COND_V_MSG ( minor > VERSION_MINOR , ERR_INVALID_DATA , vformat ( " JSON API dump is for a newer version of the engine: %d.%d " , major , minor ) ) ;
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
}
bool failed = false ;
HashSet < String > removed_classes_registered ;
2023-05-16 13:03:52 +02:00
if ( ! compare_dict_array ( old_api , new_api , " global_constants " , " name " , Vector < String > ( { " value " , " is_bitfield " } ) , false ) ) {
failed = true ;
}
if ( ! compare_dict_array ( old_api , new_api , " global_enums " , " name " , Vector < String > ( { " $values " , " is_bitfield " } ) , false ) ) {
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
failed = true ;
}
2023-05-16 13:03:52 +02:00
if ( ! compare_dict_array ( old_api , new_api , " utility_functions " , " name " , Vector < String > ( { " category " , " is_vararg " , " *return_type " , " *@arguments " } ) , true ) ) {
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
failed = true ;
}
if ( ! compare_sub_dict_array ( removed_classes_registered , " builtin_classes " , " name " , old_api , new_api , " members " , " name " , { " type " } , false ) ) {
failed = true ;
}
if ( ! compare_sub_dict_array ( removed_classes_registered , " builtin_classes " , " name " , old_api , new_api , " constants " , " name " , { " type " , " value " } , false ) ) {
failed = true ;
}
if ( ! compare_sub_dict_array ( removed_classes_registered , " builtin_classes " , " name " , old_api , new_api , " operators " , " name " , { " return_type " } , false , true ) ) {
failed = true ;
}
2023-05-16 13:03:52 +02:00
if ( ! compare_sub_dict_array ( removed_classes_registered , " builtin_classes " , " name " , old_api , new_api , " methods " , " name " , { " is_vararg " , " is_static " , " is_const " , " *return_type " , " *@arguments " } , true ) ) {
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
failed = true ;
}
2023-05-16 13:03:52 +02:00
if ( ! compare_sub_dict_array ( removed_classes_registered , " builtin_classes " , " name " , old_api , new_api , " constructors " , " index " , { " *@arguments " } , false ) ) {
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
failed = true ;
}
if ( ! compare_sub_dict_array ( removed_classes_registered , " classes " , " name " , old_api , new_api , " constants " , " name " , { " value " } , false ) ) {
failed = true ;
}
2023-05-16 13:03:52 +02:00
if ( ! compare_sub_dict_array ( removed_classes_registered , " classes " , " name " , old_api , new_api , " enums " , " name " , { " is_bitfield " , " $values " } , false ) ) {
failed = true ;
}
if ( ! compare_sub_dict_array ( removed_classes_registered , " classes " , " name " , old_api , new_api , " methods " , " name " , { " is_virtual " , " is_vararg " , " is_static " , " is_const " , " *return_value " , " *@arguments " } , true ) ) {
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
failed = true ;
}
2023-05-16 13:03:52 +02:00
if ( ! compare_sub_dict_array ( removed_classes_registered , " classes " , " name " , old_api , new_api , " signals " , " name " , { " *@arguments " } , false ) ) {
Add a backwards-compatibility system for GDExtension method
This adds a way to ensure that methods that were modified in the Godot API will continue working in older builds of GDExtension even if the new signature is different.
```C++
// New version (changed)
ClassDB::bind_method(D_METHOD("add_sphere","radius","position"),&MyShapes::add_sphere);
// Compatibility version (still available to extensions).
ClassDB::bind_compatibility_method(D_METHOD("add_sphere","radius"),&MyShapes::_compat_add_sphere);
```
**Q**: If I add an extra argument and provide a default value (hence can still be called the same), do I still have to provide the compatibility version?
**A**: Yes, you must still provide a compatibility method. Most language bindings use the raw method pointer to do the call and process the default parameters in the binding language, hence if the actual method signature changes it will no longer work.
**Q**: If I removed a method, can I still bind a compatibility version even though the main method no longer exists?
**A**: Yes, for methods that were removed or renamed, compatibility versions can still be provided.
**Q**: Would it be possible to automate checking that methods were removed by mistake?
**A**: Yes, as part of a future PR, the idea is to add a a command line option to Godot that can be run like : `$ godot --test-api-compatibility older_api_dump.json`, which will also be integrated to the CI runs.
2023-04-25 20:53:07 +02:00
failed = true ;
}
if ( ! compare_sub_dict_array ( removed_classes_registered , " classes " , " name " , old_api , new_api , " properties " , " name " , { " type " , " *setter " , " *getter " , " *index " } , false ) ) {
failed = true ;
}
if ( ! compare_dict_array ( old_api , new_api , " singletons " , " name " , Vector < String > ( { " type " } ) , false ) ) {
failed = true ;
}
if ( ! compare_dict_array ( old_api , new_api , " native_structures " , " name " , Vector < String > ( { " format " } ) , false ) ) {
failed = true ;
}
if ( failed ) {
return ERR_INVALID_DATA ;
} else {
return OK ;
}
}
2022-11-07 14:31:10 +01:00
# endif // TOOLS_ENABLED