diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ee04d90..ada01e93 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,7 +65,7 @@ jobs: platform: android artifact-name: godot-cpp-android-arm64-release artifact-path: bin/libgodot-cpp.android.template_release.arm64.a - flags: ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME arch=arm64 + flags: arch=arm64 run-tests: false cache-name: android-arm64 @@ -88,7 +88,7 @@ jobs: env: SCONS_CACHE: ${{ github.workspace }}/.scons-cache/ - EM_VERSION: 3.1.45 + EM_VERSION: 3.1.39 EM_CACHE_FOLDER: "emsdk-cache" steps: @@ -104,33 +104,34 @@ jobs: continue-on-error: true - name: Set up Python (for SCons) - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.x' - - name: Linux dependencies - if: ${{ matrix.platform == 'linux' }} - run: | - sudo apt-get update -qq - sudo apt-get install -qqq build-essential pkg-config + - name: Android dependencies + if: ${{ matrix.platform == 'android' }} + uses: nttld/setup-ndk@v1 + with: + ndk-version: r23c + link-to-sdk: true - name: Web dependencies if: ${{ matrix.platform == 'web' }} - uses: mymindstorm/setup-emsdk@v12 + uses: mymindstorm/setup-emsdk@v13 with: version: ${{env.EM_VERSION}} actions-cache-folder: ${{env.EM_CACHE_FOLDER}} - - name: Install scons - run: | - python -m pip install scons==4.0.0 - - name: Setup MinGW for Windows/MinGW build if: ${{ matrix.platform == 'windows' && matrix.flags == 'use_mingw=yes' }} uses: egor-tensin/setup-mingw@v2 with: version: 12.2.0 + - name: Install scons + run: | + python -m pip install scons==4.0.0 + - name: Generate godot-cpp sources only run: | scons platform=${{ matrix.platform }} build_library=no ${{ matrix.flags }} diff --git a/CMakeLists.txt b/CMakeLists.txt index c413cecc..a9603ab6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,11 +47,6 @@ option(GODOT_CPP_WARNING_AS_ERROR "Treat warnings as errors" OFF) # Add path to modules list( APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" ) -# Check if we are building ourself or being included -if(${PROJECT_NAME} STREQUAL ${CMAKE_PROJECT_NAME}) - set(GODOT_CPP_BUILDING_SELF ON) -endif() - # Set some helper variables for readability set( compiler_is_clang "$,$>" ) set( compiler_is_gnu "$" ) @@ -99,10 +94,8 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") add_definitions(-DNOMINMAX) else() # GCC/Clang - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -g") - if(CMAKE_BUILD_TYPE MATCHES Debug) - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-omit-frame-pointer -O0") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-omit-frame-pointer -O0 -g") else() set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -O3") endif(CMAKE_BUILD_TYPE MATCHES Debug) @@ -134,6 +127,7 @@ endif() execute_process(COMMAND "${Python3_EXECUTABLE}" "-c" "import binding_generator; binding_generator.print_file_list(\"${GODOT_GDEXTENSION_API_FILE}\", \"${CMAKE_CURRENT_BINARY_DIR}\", headers=True, sources=True)" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE GENERATED_FILES_LIST + OUTPUT_STRIP_TRAILING_WHITESPACE ) add_custom_command(OUTPUT ${GENERATED_FILES_LIST} @@ -159,12 +153,6 @@ add_library(godot::cpp ALIAS ${PROJECT_NAME}) include(GodotCompilerWarnings) -# Treat warnings as errors if we are building ourself -if(GODOT_CPP_BUILDING_SELF) - unset( GODOT_CPP_WARNING_AS_ERROR CACHE ) - set_warning_as_error() -endif() - target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17 diff --git a/README.md b/README.md index c36e8925..b1162840 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ first-party `godot-cpp` extension. Some compatibility breakage is to be expected as GDExtension and `godot-cpp` get more used, documented, and critical issues get resolved. See the [Godot issue tracker](https://github.com/godotengine/godot/issues?q=is%3Aissue+is%3Aopen+label%3Atopic%3Agdextension) -and the [godot-cpp issue tracker](https://github.com/godotengine/godot/issues) +and the [godot-cpp issue tracker](https://github.com/godotengine/godot-cpp/issues) for a list of known issues, and be sure to provide feedback on issues and PRs which affect your use of this extension. @@ -73,7 +73,10 @@ so formatting is done before your changes are submitted. ## Getting started -It's a bit similar to what it was for 3.x but also a bit different. +You need the same C++ pre-requisites installed that are required for the `godot` repository. Follow the [official build instructions for your target platform](https://docs.godotengine.org/en/latest/contributing/development/compiling/index.html#building-for-target-platforms). + +Getting started with GDExtensions is a bit similar to what it was for 3.x but also a bit different. + This new approach is much more akin to how core Godot modules are structured. Compiling this repository generates a static library to be linked with your shared lib, diff --git a/binding_generator.py b/binding_generator.py index 18db9fd0..aa77a5cb 100644 --- a/binding_generator.py +++ b/binding_generator.py @@ -135,9 +135,7 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False): def print_file_list(api_filepath, output_dir, headers=False, sources=False): - end = ";" - for f in get_file_list(api_filepath, output_dir, headers, sources): - print(f, end=end) + print(*get_file_list(api_filepath, output_dir, headers, sources), sep=";", end=None) def scons_emit_files(target, source, env): @@ -1718,9 +1716,9 @@ def generate_global_constant_binds(api, output_dir): continue if enum_def["is_bitfield"]: - header.append(f'VARIANT_BITFIELD_CAST(godot::{enum_def["name"]});') + header.append(f'VARIANT_BITFIELD_CAST({enum_def["name"]});') else: - header.append(f'VARIANT_ENUM_CAST(godot::{enum_def["name"]});') + header.append(f'VARIANT_ENUM_CAST({enum_def["name"]});') # Variant::Type is not a global enum, but only one line, it is worth to place in this file instead of creating new file. header.append(f"VARIANT_ENUM_CAST(godot::Variant::Type);") @@ -2336,6 +2334,7 @@ def get_operator_id_name(op): "unary-": "negate", "unary+": "positive", "%": "module", + "**": "power", "<<": "shift_left", ">>": "shift_right", "&": "bit_and", diff --git a/include/godot_cpp/godot.hpp b/include/godot_cpp/godot.hpp index 1478403b..0b7dd4a8 100644 --- a/include/godot_cpp/godot.hpp +++ b/include/godot_cpp/godot.hpp @@ -187,26 +187,44 @@ enum ModuleInitializationLevel { MODULE_INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE, MODULE_INITIALIZATION_LEVEL_SERVERS = GDEXTENSION_INITIALIZATION_SERVERS, MODULE_INITIALIZATION_LEVEL_SCENE = GDEXTENSION_INITIALIZATION_SCENE, - MODULE_INITIALIZATION_LEVEL_EDITOR = GDEXTENSION_INITIALIZATION_EDITOR + MODULE_INITIALIZATION_LEVEL_EDITOR = GDEXTENSION_INITIALIZATION_EDITOR, + MODULE_INITIALIZATION_LEVEL_MAX }; class GDExtensionBinding { public: using Callback = void (*)(ModuleInitializationLevel p_level); - static Callback init_callback; - static Callback terminate_callback; - static GDExtensionInitializationLevel minimum_initialization_level; - static GDExtensionBool init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization); + struct InitData { + GDExtensionInitializationLevel minimum_initialization_level = GDEXTENSION_INITIALIZATION_CORE; + Callback init_callback = nullptr; + Callback terminate_callback = nullptr; + }; + + class InitDataList { + int data_count = 0; + int data_capacity = 0; + InitData **data = nullptr; + + public: + void add(InitData *p_cb); + ~InitDataList(); + }; + + static bool api_initialized; + static int level_initialized[MODULE_INITIALIZATION_LEVEL_MAX]; + static InitDataList initdata; + static GDExtensionBool init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, InitData *p_init_data, GDExtensionInitialization *r_initialization); public: - static void initialize_level(void *userdata, GDExtensionInitializationLevel p_level); - static void deinitialize_level(void *userdata, GDExtensionInitializationLevel p_level); + static void initialize_level(void *p_userdata, GDExtensionInitializationLevel p_level); + static void deinitialize_level(void *p_userdata, GDExtensionInitializationLevel p_level); class InitObject { GDExtensionInterfaceGetProcAddress get_proc_address; GDExtensionClassLibraryPtr library; GDExtensionInitialization *initialization; + mutable InitData *init_data = nullptr; public: InitObject(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization); diff --git a/include/godot_cpp/variant/variant.hpp b/include/godot_cpp/variant/variant.hpp index 0a2b3d14..7b82d5eb 100644 --- a/include/godot_cpp/variant/variant.hpp +++ b/include/godot_cpp/variant/variant.hpp @@ -122,6 +122,7 @@ public: OP_NEGATE, OP_POSITIVE, OP_MODULE, + OP_POWER, // bitwise OP_SHIFT_LEFT, OP_SHIFT_RIGHT, @@ -154,10 +155,18 @@ public: Variant(int64_t v); Variant(int32_t v) : Variant(static_cast(v)) {} - Variant(uint32_t v) : + Variant(int16_t v) : + Variant(static_cast(v)) {} + Variant(int8_t v) : Variant(static_cast(v)) {} Variant(uint64_t v) : Variant(static_cast(v)) {} + Variant(uint32_t v) : + Variant(static_cast(v)) {} + Variant(uint16_t v) : + Variant(static_cast(v)) {} + Variant(uint8_t v) : + Variant(static_cast(v)) {} Variant(double v); Variant(float v) : Variant((double)v) {} @@ -209,8 +218,12 @@ public: operator bool() const; operator int64_t() const; operator int32_t() const; + operator int16_t() const; + operator int8_t() const; operator uint64_t() const; operator uint32_t() const; + operator uint16_t() const; + operator uint8_t() const; operator double() const; operator float() const; operator String() const; diff --git a/src/classes/wrapped.cpp b/src/classes/wrapped.cpp index 2c5e1b60..8a2092c1 100644 --- a/src/classes/wrapped.cpp +++ b/src/classes/wrapped.cpp @@ -50,6 +50,12 @@ void Wrapped::_postinitialize() { godot::internal::gdextension_interface_object_set_instance(_owner, reinterpret_cast(extension_class), this); } godot::internal::gdextension_interface_object_set_instance_binding(_owner, godot::internal::token, this, _get_bindings_callbacks()); + if (extension_class) { + Object *obj = dynamic_cast(this); + if (obj) { + obj->notification(Object::NOTIFICATION_POSTINITIALIZE); + } + } } Wrapped::Wrapped(const StringName p_godot_class) { diff --git a/src/godot.cpp b/src/godot.cpp index 14214c0d..1621b205 100644 --- a/src/godot.cpp +++ b/src/godot.cpp @@ -189,9 +189,9 @@ GDExtensionInterfaceEditorRemovePlugin gdextension_interface_editor_remove_plugi } // namespace internal -GDExtensionBinding::Callback GDExtensionBinding::init_callback = nullptr; -GDExtensionBinding::Callback GDExtensionBinding::terminate_callback = nullptr; -GDExtensionInitializationLevel GDExtensionBinding::minimum_initialization_level = GDEXTENSION_INITIALIZATION_CORE; +bool GDExtensionBinding::api_initialized = false; +int GDExtensionBinding::level_initialized[MODULE_INITIALIZATION_LEVEL_MAX] = { 0 }; +GDExtensionBinding::InitDataList GDExtensionBinding::initdata; #define ERR_PRINT_EARLY(m_msg) \ internal::gdextension_interface_print_error(m_msg, FUNCTION_STR, __FILE__, __LINE__, false) @@ -218,7 +218,20 @@ typedef struct { GDExtensionInterfacePrintErrorWithMessage print_error_with_message; } LegacyGDExtensionInterface; -GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) { +GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, InitData *p_init_data, GDExtensionInitialization *r_initialization) { + if (!p_init_data || !p_init_data->init_callback) { + ERR_FAIL_V_MSG(false, "Initialization callback must be defined."); + } + + if (api_initialized) { + r_initialization->initialize = initialize_level; + r_initialization->deinitialize = deinitialize_level; + r_initialization->userdata = p_init_data; + r_initialization->minimum_initialization_level = p_init_data->minimum_initialization_level; + + return true; + } + // Make sure we weren't passed the legacy struct. uint32_t *raw_interface = (uint32_t *)(void *)p_get_proc_address; if (raw_interface[0] == 4 && raw_interface[1] == 0) { @@ -251,7 +264,12 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge } else if (internal::godot_version.minor != GODOT_VERSION_MINOR) { compatible = internal::godot_version.minor > GODOT_VERSION_MINOR; } else { +#if GODOT_VERSION_PATCH > 0 compatible = internal::godot_version.patch >= GODOT_VERSION_PATCH; +#else + // Prevent -Wtype-limits warning due to unsigned comparison. + compatible = true; +#endif } if (!compatible) { // We need to use snprintf() here because vformat() uses Variant, and we haven't loaded @@ -401,59 +419,96 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge r_initialization->initialize = initialize_level; r_initialization->deinitialize = deinitialize_level; - r_initialization->minimum_initialization_level = minimum_initialization_level; - - ERR_FAIL_NULL_V_MSG(init_callback, false, "Initialization callback must be defined."); + r_initialization->userdata = p_init_data; + r_initialization->minimum_initialization_level = p_init_data->minimum_initialization_level; Variant::init_bindings(); godot::internal::register_engine_classes(); + api_initialized = true; return true; } #undef LOAD_PROC_ADDRESS #undef ERR_PRINT_EARLY -void GDExtensionBinding::initialize_level(void *userdata, GDExtensionInitializationLevel p_level) { +void GDExtensionBinding::initialize_level(void *p_userdata, GDExtensionInitializationLevel p_level) { + ERR_FAIL_COND(static_cast(p_level) >= MODULE_INITIALIZATION_LEVEL_MAX); ClassDB::current_level = p_level; - if (init_callback) { - init_callback(static_cast(p_level)); + InitData *init_data = static_cast(p_userdata); + if (init_data && init_data->init_callback) { + init_data->init_callback(static_cast(p_level)); } - ClassDB::initialize(p_level); + if (level_initialized[p_level] == 0) { + ClassDB::initialize(p_level); + } + level_initialized[p_level]++; } -void GDExtensionBinding::deinitialize_level(void *userdata, GDExtensionInitializationLevel p_level) { +void GDExtensionBinding::deinitialize_level(void *p_userdata, GDExtensionInitializationLevel p_level) { + ERR_FAIL_COND(static_cast(p_level) >= MODULE_INITIALIZATION_LEVEL_MAX); ClassDB::current_level = p_level; - if (terminate_callback) { - terminate_callback(static_cast(p_level)); + InitData *init_data = static_cast(p_userdata); + if (init_data && init_data->terminate_callback) { + init_data->terminate_callback(static_cast(p_level)); } - EditorPlugins::deinitialize(p_level); - ClassDB::deinitialize(p_level); + level_initialized[p_level]--; + if (level_initialized[p_level] == 0) { + EditorPlugins::deinitialize(p_level); + ClassDB::deinitialize(p_level); + } } + +void GDExtensionBinding::InitDataList::add(InitData *p_data) { + if (data_count == data_capacity) { + void *new_ptr = realloc(data, sizeof(InitData *) * (data_capacity + 32)); + if (new_ptr) { + data = (InitData **)(new_ptr); + data_capacity += 32; + } else { + ERR_FAIL_MSG("Unable to allocate memory for extension callbacks."); + } + } + data[data_count++] = p_data; +} + +GDExtensionBinding::InitDataList::~InitDataList() { + for (int i = 0; i < data_count; i++) { + if (data[i]) { + delete data[i]; + } + } + if (data) { + free(data); + } +} + GDExtensionBinding::InitObject::InitObject(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) { get_proc_address = p_get_proc_address; library = p_library; initialization = r_initialization; + init_data = new InitData(); + GDExtensionBinding::initdata.add(init_data); } void GDExtensionBinding::InitObject::register_initializer(Callback p_init) const { - GDExtensionBinding::init_callback = p_init; + init_data->init_callback = p_init; } void GDExtensionBinding::InitObject::register_terminator(Callback p_terminate) const { - GDExtensionBinding::terminate_callback = p_terminate; + init_data->terminate_callback = p_terminate; } void GDExtensionBinding::InitObject::set_minimum_library_initialization_level(ModuleInitializationLevel p_level) const { - GDExtensionBinding::minimum_initialization_level = static_cast(p_level); + init_data->minimum_initialization_level = static_cast(p_level); } GDExtensionBool GDExtensionBinding::InitObject::init() const { - return GDExtensionBinding::init(get_proc_address, library, initialization); + return GDExtensionBinding::init(get_proc_address, library, init_data, initialization); } } // namespace godot diff --git a/src/variant/variant.cpp b/src/variant/variant.cpp index db15be11..945d6f40 100644 --- a/src/variant/variant.cpp +++ b/src/variant/variant.cpp @@ -268,6 +268,14 @@ Variant::operator int32_t() const { return static_cast(operator int64_t()); } +Variant::operator int16_t() const { + return static_cast(operator int64_t()); +} + +Variant::operator int8_t() const { + return static_cast(operator int64_t()); +} + Variant::operator uint64_t() const { return static_cast(operator int64_t()); } @@ -276,6 +284,14 @@ Variant::operator uint32_t() const { return static_cast(operator int64_t()); } +Variant::operator uint16_t() const { + return static_cast(operator int64_t()); +} + +Variant::operator uint8_t() const { + return static_cast(operator int64_t()); +} + Variant::operator double() const { double result; to_type_constructor[FLOAT](&result, _native_ptr()); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9d692e6a..a17b9aac 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -59,31 +59,9 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") else() -#elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - # using Clang -#elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - # using GCC and maybe MinGW? - set(GODOT_LINKER_FLAGS "-static-libgcc -static-libstdc++ -Wl,-R,'$$ORIGIN'") - # Hmm.. maybe to strikt? set(GODOT_COMPILE_FLAGS "-fPIC -g -Wwrite-strings") - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wchar-subscripts -Wcomment -Wdisabled-optimization") - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wformat -Wformat=2 -Wformat-security -Wformat-y2k") - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wimport -Winit-self -Winline -Winvalid-pch -Werror") - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wmissing-braces -Wmissing-format-attribute") - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wmissing-include-dirs -Wmissing-noreturn -Wpacked -Wpointer-arith") - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wredundant-decls -Wreturn-type -Wsequence-point") - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wswitch -Wswitch-enum -Wtrigraphs") - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wuninitialized -Wunknown-pragmas -Wunreachable-code -Wunused-label") - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wunused-value -Wvariadic-macros -Wvolatile-register-var -Wno-error=attributes") - - # -Wshadow -Wextra -Wall -Weffc++ -Wfloat-equal -Wstack-protector -Wunused-parameter -Wsign-compare -Wunused-variable -Wcast-align - # -Wunused-function -Wstrict-aliasing -Wstrict-aliasing=2 -Wmissing-field-initializers - - if(NOT CMAKE_SYSTEM_NAME STREQUAL "Android") - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wno-ignored-attributes") - endif() if(CMAKE_BUILD_TYPE MATCHES Debug) set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-omit-frame-pointer -O0") diff --git a/test/project/main.gd b/test/project/main.gd index eb8dbb9d..e0b186bb 100644 --- a/test/project/main.gd +++ b/test/project/main.gd @@ -169,6 +169,11 @@ func _ready(): get_viewport().push_input(event) assert_equal(custom_signal_emitted, ["_input: H", 72]) + # Check NOTIFICATION_POST_INITIALIZED, both when created from GDScript and godot-cpp. + var new_example_ref = ExampleRef.new() + assert_equal(new_example_ref.was_post_initialized(), true) + assert_equal(example.test_post_initialize(), true) + exit_with_status() func _on_Example_custom_signal(signal_name, value): diff --git a/test/src/example.cpp b/test/src/example.cpp index 475eed2b..848f5035 100644 --- a/test/src/example.cpp +++ b/test/src/example.cpp @@ -23,10 +23,18 @@ int ExampleRef::get_id() const { return id; } +void ExampleRef::_notification(int p_what) { + if (p_what == NOTIFICATION_POSTINITIALIZE) { + post_initialized = true; + } +} + void ExampleRef::_bind_methods() { ClassDB::bind_method(D_METHOD("set_id", "id"), &ExampleRef::set_id); ClassDB::bind_method(D_METHOD("get_id"), &ExampleRef::get_id); + ClassDB::bind_method(D_METHOD("was_post_initialized"), &ExampleRef::was_post_initialized); + ADD_PROPERTY(PropertyInfo(Variant::INT, "id"), "set_id", "get_id"); } @@ -161,6 +169,7 @@ void Example::_bind_methods() { ClassDB::bind_method(D_METHOD("return_last_rpc_arg"), &Example::return_last_rpc_arg); ClassDB::bind_method(D_METHOD("def_args", "a", "b"), &Example::def_args, DEFVAL(100), DEFVAL(200)); + ClassDB::bind_method(D_METHOD("test_post_initialize"), &Example::test_post_initialize); ClassDB::bind_static_method("Example", D_METHOD("test_static", "a", "b"), &Example::test_static); ClassDB::bind_static_method("Example", D_METHOD("test_static2"), &Example::test_static2); @@ -426,6 +435,12 @@ Vector4 Example::get_v4() const { return Vector4(1.2, 3.4, 5.6, 7.8); } +bool Example::test_post_initialize() const { + Ref new_example_ref; + new_example_ref.instantiate(); + return new_example_ref->was_post_initialized(); +} + // Virtual function override. bool Example::_has_point(const Vector2 &point) const { Label *label = get_node