Browse Source

Merge pull request #1373 from dsnopek/4.1-cherrypicks-7

Cherry-picks for the godot-cpp 4.1 branch - 7th batch
David Snopek 1 year ago
parent
commit
4b63d795e4

+ 14 - 13
.github/workflows/ci.yml

@@ -65,7 +65,7 @@ jobs:
             platform: android
             platform: android
             artifact-name: godot-cpp-android-arm64-release
             artifact-name: godot-cpp-android-arm64-release
             artifact-path: bin/libgodot-cpp.android.template_release.arm64.a
             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
             run-tests: false
             cache-name: android-arm64
             cache-name: android-arm64
 
 
@@ -88,7 +88,7 @@ jobs:
 
 
     env:
     env:
       SCONS_CACHE: ${{ github.workspace }}/.scons-cache/
       SCONS_CACHE: ${{ github.workspace }}/.scons-cache/
-      EM_VERSION: 3.1.45
+      EM_VERSION: 3.1.39
       EM_CACHE_FOLDER: "emsdk-cache"
       EM_CACHE_FOLDER: "emsdk-cache"
 
 
     steps:
     steps:
@@ -104,33 +104,34 @@ jobs:
         continue-on-error: true
         continue-on-error: true
 
 
       - name: Set up Python (for SCons)
       - name: Set up Python (for SCons)
-        uses: actions/setup-python@v4
+        uses: actions/setup-python@v5
         with:
         with:
           python-version: '3.x'
           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
       - name: Web dependencies
         if: ${{ matrix.platform == 'web' }}
         if: ${{ matrix.platform == 'web' }}
-        uses: mymindstorm/setup-emsdk@v12
+        uses: mymindstorm/setup-emsdk@v13
         with:
         with:
           version: ${{env.EM_VERSION}}
           version: ${{env.EM_VERSION}}
           actions-cache-folder: ${{env.EM_CACHE_FOLDER}}
           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
       - name: Setup MinGW for Windows/MinGW build
         if: ${{ matrix.platform == 'windows' && matrix.flags == 'use_mingw=yes' }}
         if: ${{ matrix.platform == 'windows' && matrix.flags == 'use_mingw=yes' }}
         uses: egor-tensin/setup-mingw@v2
         uses: egor-tensin/setup-mingw@v2
         with:
         with:
           version: 12.2.0
           version: 12.2.0
 
 
+      - name: Install scons
+        run: |
+          python -m pip install scons==4.0.0
+
       - name: Generate godot-cpp sources only
       - name: Generate godot-cpp sources only
         run: |
         run: |
           scons platform=${{ matrix.platform }} build_library=no ${{ matrix.flags }}
           scons platform=${{ matrix.platform }} build_library=no ${{ matrix.flags }}

+ 2 - 14
CMakeLists.txt

@@ -47,11 +47,6 @@ option(GODOT_CPP_WARNING_AS_ERROR "Treat warnings as errors" OFF)
 # Add path to modules
 # Add path to modules
 list( APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" )
 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 some helper variables for readability
 set( compiler_is_clang "$<OR:$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:Clang>>" )
 set( compiler_is_clang "$<OR:$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:Clang>>" )
 set( compiler_is_gnu "$<CXX_COMPILER_ID:GNU>" )
 set( compiler_is_gnu "$<CXX_COMPILER_ID:GNU>" )
@@ -99,10 +94,8 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
 
 
 	add_definitions(-DNOMINMAX)
 	add_definitions(-DNOMINMAX)
 else()  # GCC/Clang
 else()  # GCC/Clang
-	set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -g")
-
 	if(CMAKE_BUILD_TYPE MATCHES Debug)
 	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()
 	else()
 		set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -O3")
 		set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -O3")
 	endif(CMAKE_BUILD_TYPE MATCHES Debug)
 	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)"
 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}
 	WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
 	OUTPUT_VARIABLE GENERATED_FILES_LIST
 	OUTPUT_VARIABLE GENERATED_FILES_LIST
+	OUTPUT_STRIP_TRAILING_WHITESPACE
 )
 )
 
 
 add_custom_command(OUTPUT ${GENERATED_FILES_LIST}
 add_custom_command(OUTPUT ${GENERATED_FILES_LIST}
@@ -159,12 +153,6 @@ add_library(godot::cpp ALIAS ${PROJECT_NAME})
 
 
 include(GodotCompilerWarnings)
 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}
 target_compile_features(${PROJECT_NAME}
 	PRIVATE
 	PRIVATE
 		cxx_std_17
 		cxx_std_17

+ 5 - 2
README.md

@@ -57,7 +57,7 @@ first-party `godot-cpp` extension.
 Some compatibility breakage is to be expected as GDExtension and `godot-cpp`
 Some compatibility breakage is to be expected as GDExtension and `godot-cpp`
 get more used, documented, and critical issues get resolved. See the
 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)
 [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
 for a list of known issues, and be sure to provide feedback on issues and PRs
 which affect your use of this extension.
 which affect your use of this extension.
 
 
@@ -73,7 +73,10 @@ so formatting is done before your changes are submitted.
 
 
 ## Getting started
 ## 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.
 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,
 Compiling this repository generates a static library to be linked with your shared lib,

+ 4 - 5
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):
 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):
 def scons_emit_files(target, source, env):
@@ -1718,9 +1716,9 @@ def generate_global_constant_binds(api, output_dir):
             continue
             continue
 
 
         if enum_def["is_bitfield"]:
         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:
         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.
     # 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);")
     header.append(f"VARIANT_ENUM_CAST(godot::Variant::Type);")
@@ -2336,6 +2334,7 @@ def get_operator_id_name(op):
         "unary-": "negate",
         "unary-": "negate",
         "unary+": "positive",
         "unary+": "positive",
         "%": "module",
         "%": "module",
+        "**": "power",
         "<<": "shift_left",
         "<<": "shift_left",
         ">>": "shift_right",
         ">>": "shift_right",
         "&": "bit_and",
         "&": "bit_and",

+ 25 - 7
include/godot_cpp/godot.hpp

@@ -187,26 +187,44 @@ enum ModuleInitializationLevel {
 	MODULE_INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE,
 	MODULE_INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE,
 	MODULE_INITIALIZATION_LEVEL_SERVERS = GDEXTENSION_INITIALIZATION_SERVERS,
 	MODULE_INITIALIZATION_LEVEL_SERVERS = GDEXTENSION_INITIALIZATION_SERVERS,
 	MODULE_INITIALIZATION_LEVEL_SCENE = GDEXTENSION_INITIALIZATION_SCENE,
 	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 {
 class GDExtensionBinding {
 public:
 public:
 	using Callback = void (*)(ModuleInitializationLevel p_level);
 	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:
 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 {
 	class InitObject {
 		GDExtensionInterfaceGetProcAddress get_proc_address;
 		GDExtensionInterfaceGetProcAddress get_proc_address;
 		GDExtensionClassLibraryPtr library;
 		GDExtensionClassLibraryPtr library;
 		GDExtensionInitialization *initialization;
 		GDExtensionInitialization *initialization;
+		mutable InitData *init_data = nullptr;
 
 
 	public:
 	public:
 		InitObject(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization);
 		InitObject(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization);

+ 14 - 1
include/godot_cpp/variant/variant.hpp

@@ -122,6 +122,7 @@ public:
 		OP_NEGATE,
 		OP_NEGATE,
 		OP_POSITIVE,
 		OP_POSITIVE,
 		OP_MODULE,
 		OP_MODULE,
+		OP_POWER,
 		// bitwise
 		// bitwise
 		OP_SHIFT_LEFT,
 		OP_SHIFT_LEFT,
 		OP_SHIFT_RIGHT,
 		OP_SHIFT_RIGHT,
@@ -154,10 +155,18 @@ public:
 	Variant(int64_t v);
 	Variant(int64_t v);
 	Variant(int32_t v) :
 	Variant(int32_t v) :
 			Variant(static_cast<int64_t>(v)) {}
 			Variant(static_cast<int64_t>(v)) {}
-	Variant(uint32_t v) :
+	Variant(int16_t v) :
+			Variant(static_cast<int64_t>(v)) {}
+	Variant(int8_t v) :
 			Variant(static_cast<int64_t>(v)) {}
 			Variant(static_cast<int64_t>(v)) {}
 	Variant(uint64_t v) :
 	Variant(uint64_t v) :
 			Variant(static_cast<int64_t>(v)) {}
 			Variant(static_cast<int64_t>(v)) {}
+	Variant(uint32_t v) :
+			Variant(static_cast<int64_t>(v)) {}
+	Variant(uint16_t v) :
+			Variant(static_cast<int64_t>(v)) {}
+	Variant(uint8_t v) :
+			Variant(static_cast<int64_t>(v)) {}
 	Variant(double v);
 	Variant(double v);
 	Variant(float v) :
 	Variant(float v) :
 			Variant((double)v) {}
 			Variant((double)v) {}
@@ -209,8 +218,12 @@ public:
 	operator bool() const;
 	operator bool() const;
 	operator int64_t() const;
 	operator int64_t() const;
 	operator int32_t() const;
 	operator int32_t() const;
+	operator int16_t() const;
+	operator int8_t() const;
 	operator uint64_t() const;
 	operator uint64_t() const;
 	operator uint32_t() const;
 	operator uint32_t() const;
+	operator uint16_t() const;
+	operator uint8_t() const;
 	operator double() const;
 	operator double() const;
 	operator float() const;
 	operator float() const;
 	operator String() const;
 	operator String() const;

+ 6 - 0
src/classes/wrapped.cpp

@@ -50,6 +50,12 @@ void Wrapped::_postinitialize() {
 		godot::internal::gdextension_interface_object_set_instance(_owner, reinterpret_cast<GDExtensionConstStringNamePtr>(extension_class), this);
 		godot::internal::gdextension_interface_object_set_instance(_owner, reinterpret_cast<GDExtensionConstStringNamePtr>(extension_class), this);
 	}
 	}
 	godot::internal::gdextension_interface_object_set_instance_binding(_owner, godot::internal::token, this, _get_bindings_callbacks());
 	godot::internal::gdextension_interface_object_set_instance_binding(_owner, godot::internal::token, this, _get_bindings_callbacks());
+	if (extension_class) {
+		Object *obj = dynamic_cast<Object *>(this);
+		if (obj) {
+			obj->notification(Object::NOTIFICATION_POSTINITIALIZE);
+		}
+	}
 }
 }
 
 
 Wrapped::Wrapped(const StringName p_godot_class) {
 Wrapped::Wrapped(const StringName p_godot_class) {

+ 75 - 20
src/godot.cpp

@@ -189,9 +189,9 @@ GDExtensionInterfaceEditorRemovePlugin gdextension_interface_editor_remove_plugi
 
 
 } // namespace internal
 } // 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) \
 #define ERR_PRINT_EARLY(m_msg) \
 	internal::gdextension_interface_print_error(m_msg, FUNCTION_STR, __FILE__, __LINE__, false)
 	internal::gdextension_interface_print_error(m_msg, FUNCTION_STR, __FILE__, __LINE__, false)
@@ -218,7 +218,20 @@ typedef struct {
 	GDExtensionInterfacePrintErrorWithMessage print_error_with_message;
 	GDExtensionInterfacePrintErrorWithMessage print_error_with_message;
 } LegacyGDExtensionInterface;
 } 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.
 	// Make sure we weren't passed the legacy struct.
 	uint32_t *raw_interface = (uint32_t *)(void *)p_get_proc_address;
 	uint32_t *raw_interface = (uint32_t *)(void *)p_get_proc_address;
 	if (raw_interface[0] == 4 && raw_interface[1] == 0) {
 	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) {
 	} else if (internal::godot_version.minor != GODOT_VERSION_MINOR) {
 		compatible = internal::godot_version.minor > GODOT_VERSION_MINOR;
 		compatible = internal::godot_version.minor > GODOT_VERSION_MINOR;
 	} else {
 	} else {
+#if GODOT_VERSION_PATCH > 0
 		compatible = internal::godot_version.patch >= GODOT_VERSION_PATCH;
 		compatible = internal::godot_version.patch >= GODOT_VERSION_PATCH;
+#else
+		// Prevent -Wtype-limits warning due to unsigned comparison.
+		compatible = true;
+#endif
 	}
 	}
 	if (!compatible) {
 	if (!compatible) {
 		// We need to use snprintf() here because vformat() uses Variant, and we haven't loaded
 		// 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->initialize = initialize_level;
 	r_initialization->deinitialize = deinitialize_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();
 	Variant::init_bindings();
 	godot::internal::register_engine_classes();
 	godot::internal::register_engine_classes();
 
 
+	api_initialized = true;
 	return true;
 	return true;
 }
 }
 
 
 #undef LOAD_PROC_ADDRESS
 #undef LOAD_PROC_ADDRESS
 #undef ERR_PRINT_EARLY
 #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<ModuleInitializationLevel>(p_level) >= MODULE_INITIALIZATION_LEVEL_MAX);
 	ClassDB::current_level = p_level;
 	ClassDB::current_level = p_level;
 
 
-	if (init_callback) {
-		init_callback(static_cast<ModuleInitializationLevel>(p_level));
+	InitData *init_data = static_cast<InitData *>(p_userdata);
+	if (init_data && init_data->init_callback) {
+		init_data->init_callback(static_cast<ModuleInitializationLevel>(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<ModuleInitializationLevel>(p_level) >= MODULE_INITIALIZATION_LEVEL_MAX);
 	ClassDB::current_level = p_level;
 	ClassDB::current_level = p_level;
 
 
-	if (terminate_callback) {
-		terminate_callback(static_cast<ModuleInitializationLevel>(p_level));
+	InitData *init_data = static_cast<InitData *>(p_userdata);
+	if (init_data && init_data->terminate_callback) {
+		init_data->terminate_callback(static_cast<ModuleInitializationLevel>(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;
+}
 
 
-	EditorPlugins::deinitialize(p_level);
-	ClassDB::deinitialize(p_level);
+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) {
 GDExtensionBinding::InitObject::InitObject(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
 	get_proc_address = p_get_proc_address;
 	get_proc_address = p_get_proc_address;
 	library = p_library;
 	library = p_library;
 	initialization = r_initialization;
 	initialization = r_initialization;
+	init_data = new InitData();
+	GDExtensionBinding::initdata.add(init_data);
 }
 }
 
 
 void GDExtensionBinding::InitObject::register_initializer(Callback p_init) const {
 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 {
 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 {
 void GDExtensionBinding::InitObject::set_minimum_library_initialization_level(ModuleInitializationLevel p_level) const {
-	GDExtensionBinding::minimum_initialization_level = static_cast<GDExtensionInitializationLevel>(p_level);
+	init_data->minimum_initialization_level = static_cast<GDExtensionInitializationLevel>(p_level);
 }
 }
 
 
 GDExtensionBool GDExtensionBinding::InitObject::init() const {
 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
 } // namespace godot

+ 16 - 0
src/variant/variant.cpp

@@ -268,6 +268,14 @@ Variant::operator int32_t() const {
 	return static_cast<int32_t>(operator int64_t());
 	return static_cast<int32_t>(operator int64_t());
 }
 }
 
 
+Variant::operator int16_t() const {
+	return static_cast<int16_t>(operator int64_t());
+}
+
+Variant::operator int8_t() const {
+	return static_cast<int8_t>(operator int64_t());
+}
+
 Variant::operator uint64_t() const {
 Variant::operator uint64_t() const {
 	return static_cast<uint64_t>(operator int64_t());
 	return static_cast<uint64_t>(operator int64_t());
 }
 }
@@ -276,6 +284,14 @@ Variant::operator uint32_t() const {
 	return static_cast<uint32_t>(operator int64_t());
 	return static_cast<uint32_t>(operator int64_t());
 }
 }
 
 
+Variant::operator uint16_t() const {
+	return static_cast<uint16_t>(operator int64_t());
+}
+
+Variant::operator uint8_t() const {
+	return static_cast<uint8_t>(operator int64_t());
+}
+
 Variant::operator double() const {
 Variant::operator double() const {
 	double result;
 	double result;
 	to_type_constructor[FLOAT](&result, _native_ptr());
 	to_type_constructor[FLOAT](&result, _native_ptr());

+ 0 - 22
test/CMakeLists.txt

@@ -59,31 +59,9 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
 
 
 else()
 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'")
 	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 "-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)
 	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")

+ 5 - 0
test/project/main.gd

@@ -169,6 +169,11 @@ func _ready():
 	get_viewport().push_input(event)
 	get_viewport().push_input(event)
 	assert_equal(custom_signal_emitted, ["_input: H", 72])
 	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()
 	exit_with_status()
 
 
 func _on_Example_custom_signal(signal_name, value):
 func _on_Example_custom_signal(signal_name, value):

+ 15 - 0
test/src/example.cpp

@@ -23,10 +23,18 @@ int ExampleRef::get_id() const {
 	return id;
 	return id;
 }
 }
 
 
+void ExampleRef::_notification(int p_what) {
+	if (p_what == NOTIFICATION_POSTINITIALIZE) {
+		post_initialized = true;
+	}
+}
+
 void ExampleRef::_bind_methods() {
 void ExampleRef::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_id", "id"), &ExampleRef::set_id);
 	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("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");
 	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("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("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_static", "a", "b"), &Example::test_static);
 	ClassDB::bind_static_method("Example", D_METHOD("test_static2"), &Example::test_static2);
 	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);
 	return Vector4(1.2, 3.4, 5.6, 7.8);
 }
 }
 
 
+bool Example::test_post_initialize() const {
+	Ref<ExampleRef> new_example_ref;
+	new_example_ref.instantiate();
+	return new_example_ref->was_post_initialized();
+}
+
 // Virtual function override.
 // Virtual function override.
 bool Example::_has_point(const Vector2 &point) const {
 bool Example::_has_point(const Vector2 &point) const {
 	Label *label = get_node<Label>("Label");
 	Label *label = get_node<Label>("Label");

+ 7 - 0
test/src/example.h

@@ -35,16 +35,21 @@ private:
 	static int last_id;
 	static int last_id;
 
 
 	int id;
 	int id;
+	bool post_initialized = false;
 
 
 protected:
 protected:
 	static void _bind_methods();
 	static void _bind_methods();
 
 
+	void _notification(int p_what);
+
 public:
 public:
 	ExampleRef();
 	ExampleRef();
 	~ExampleRef();
 	~ExampleRef();
 
 
 	void set_id(int p_id);
 	void set_id(int p_id);
 	int get_id() const;
 	int get_id() const;
+
+	bool was_post_initialized() const { return post_initialized; }
 };
 };
 
 
 class ExampleMin : public Control {
 class ExampleMin : public Control {
@@ -148,6 +153,8 @@ public:
 	Vector2 get_custom_position() const;
 	Vector2 get_custom_position() const;
 	Vector4 get_v4() const;
 	Vector4 get_v4() const;
 
 
+	bool test_post_initialize() const;
+
 	// Static method.
 	// Static method.
 	static int test_static(int p_a, int p_b);
 	static int test_static(int p_a, int p_b);
 	static void test_static2();
 	static void test_static2();

+ 9 - 5
tools/android.py

@@ -8,7 +8,7 @@ def options(opts):
     opts.Add(
     opts.Add(
         "android_api_level",
         "android_api_level",
         "Target Android API level",
         "Target Android API level",
-        "18" if "32" in ARGUMENTS.get("arch", "arm64") else "21",
+        "21",
     )
     )
     opts.Add(
     opts.Add(
         "ANDROID_HOME",
         "ANDROID_HOME",
@@ -47,11 +47,9 @@ def generate(env):
         my_spawn.configure(env)
         my_spawn.configure(env)
 
 
     # Validate API level
     # Validate API level
-    api_level = int(env["android_api_level"])
-    if "64" in env["arch"] and api_level < 21:
-        print("WARN: 64-bit Android architectures require an API level of at least 21; setting android_api_level=21")
+    if int(env["android_api_level"]) < 21:
+        print("WARNING: minimum supported Android target api is 21. Forcing target api 21.")
         env["android_api_level"] = "21"
         env["android_api_level"] = "21"
-        api_level = 21
 
 
     # Setup toolchain
     # Setup toolchain
     toolchain = get_android_ndk_root(env) + "/toolchains/llvm/prebuilt/"
     toolchain = get_android_ndk_root(env) + "/toolchains/llvm/prebuilt/"
@@ -66,6 +64,12 @@ def generate(env):
     elif sys.platform == "darwin":
     elif sys.platform == "darwin":
         toolchain += "darwin-x86_64"
         toolchain += "darwin-x86_64"
         env.Append(LINKFLAGS=["-shared"])
         env.Append(LINKFLAGS=["-shared"])
+
+    if not os.path.exists(toolchain):
+        print("ERROR: Could not find NDK toolchain at " + toolchain + ".")
+        print("Make sure NDK version " + get_ndk_version() + " is installed.")
+        env.Exit(1)
+
     env.PrependENVPath("PATH", toolchain + "/bin")  # This does nothing half of the time, but we'll put it here anyways
     env.PrependENVPath("PATH", toolchain + "/bin")  # This does nothing half of the time, but we'll put it here anyways
 
 
     # Get architecture info
     # Get architecture info

+ 3 - 0
tools/godotcpp.py

@@ -267,6 +267,9 @@ def generate(env):
     if env["precision"] == "double":
     if env["precision"] == "double":
         env.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"])
         env.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"])
 
 
+    # Allow detecting when building as a GDExtension.
+    env.Append(CPPDEFINES=["GDEXTENSION"])
+
     # Suffix
     # Suffix
     suffix = ".{}.{}".format(env["platform"], env["target"])
     suffix = ".{}.{}".format(env["platform"], env["target"])
     if env.dev_build:
     if env.dev_build: