diff options
-rw-r--r-- | .github/workflows/ci.yml | 27 | ||||
-rw-r--r-- | CMakeLists.txt | 4 | ||||
-rw-r--r-- | binding_generator.py | 12 | ||||
-rw-r--r-- | gdextension/extension_api.json | 140 | ||||
-rw-r--r-- | include/godot_cpp/core/object.hpp | 24 | ||||
-rw-r--r-- | include/godot_cpp/core/object_id.hpp | 62 | ||||
-rw-r--r-- | include/godot_cpp/godot.hpp | 33 | ||||
-rw-r--r-- | include/godot_cpp/variant/callable_custom.hpp | 64 | ||||
-rw-r--r-- | include/godot_cpp/variant/callable_method_pointer.hpp | 120 | ||||
-rw-r--r-- | include/godot_cpp/variant/variant.hpp | 14 | ||||
-rw-r--r-- | src/godot.cpp | 92 | ||||
-rw-r--r-- | src/variant/callable_custom.cpp | 113 | ||||
-rw-r--r-- | src/variant/callable_method_pointer.cpp | 70 | ||||
-rw-r--r-- | src/variant/variant.cpp | 16 | ||||
-rw-r--r-- | test/project/main.gd | 31 | ||||
-rw-r--r-- | test/src/example.cpp | 45 | ||||
-rw-r--r-- | test/src/example.h | 1 | ||||
-rw-r--r-- | tools/android.py | 8 | ||||
-rw-r--r-- | tools/ios.py | 2 |
19 files changed, 685 insertions, 193 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f316ff..4bb1af3 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: @@ -108,29 +108,30 @@ jobs: 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 }} @@ -172,7 +173,7 @@ jobs: ./godot-artifacts/godot.linuxbsd.editor.x86_64.mono --headless --version cd test # Need to run the editor so .godot is generated... but it crashes! Ignore that :-) - (cd project && (../../godot-artifacts/godot.linuxbsd.editor.x86_64.mono --editor --headless --quit >/dev/null 2>&1 || true)) + (cd project && (timeout 10 ../../godot-artifacts/godot.linuxbsd.editor.x86_64.mono --editor --headless --quit >/dev/null 2>&1 || true)) GODOT=../godot-artifacts/godot.linuxbsd.editor.x86_64.mono ./run-tests.sh - name: Upload artifact diff --git a/CMakeLists.txt b/CMakeLists.txt index 6262fb5..75ff500 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,10 +99,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) diff --git a/binding_generator.py b/binding_generator.py index ed7a8d4..6ebee3d 100644 --- a/binding_generator.py +++ b/binding_generator.py @@ -110,6 +110,8 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False): for native_struct in api["native_structures"]: struct_name = native_struct["name"] + if struct_name == "ObjectID": + continue snake_struct_name = camel_to_snake(struct_name) header_filename = include_gen_folder / "classes" / (snake_struct_name + ".hpp") @@ -414,6 +416,9 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl if class_name == "Array": result.append("#include <godot_cpp/variant/array_helpers.hpp>") + if class_name == "Callable": + result.append("#include <godot_cpp/variant/callable_custom.hpp>") + for include in fully_used_classes: if include == "TypedArray": result.append("#include <godot_cpp/variant/typed_array.hpp>") @@ -523,6 +528,9 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl result.append(f"\t{class_name}(const wchar_t *from);") result.append(f"\t{class_name}(const char16_t *from);") result.append(f"\t{class_name}(const char32_t *from);") + if class_name == "Callable": + result.append("\tCallable(CallableCustom *p_custom);") + result.append("\tCallableCustom *get_custom() const;") if "constants" in builtin_api: axis_constants_count = 0 @@ -1081,6 +1089,8 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node): class_api["alias_for"] = "ClassDB" engine_classes[class_api["name"]] = class_api["is_refcounted"] for native_struct in api["native_structures"]: + if native_struct["name"] == "ObjectID": + continue engine_classes[native_struct["name"]] = False native_structures.append(native_struct["name"]) @@ -1208,6 +1218,8 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node): for native_struct in api["native_structures"]: struct_name = native_struct["name"] + if struct_name == "ObjectID": + continue snake_struct_name = camel_to_snake(struct_name) header_filename = include_gen_folder / (snake_struct_name + ".hpp") diff --git a/gdextension/extension_api.json b/gdextension/extension_api.json index c4d11e7..e8f02ed 100644 --- a/gdextension/extension_api.json +++ b/gdextension/extension_api.json @@ -3,9 +3,9 @@ "version_major": 4, "version_minor": 2, "version_patch": 0, - "version_status": "beta3", + "version_status": "rc1", "version_build": "official", - "version_full_name": "Godot Engine v4.2.beta3.official" + "version_full_name": "Godot Engine v4.2.rc1.official" }, "builtin_class_sizes": [ { @@ -67179,6 +67179,14 @@ "value": 42 }, { + "name": "NOTIFICATION_MOUSE_ENTER_SELF", + "value": 60 + }, + { + "name": "NOTIFICATION_MOUSE_EXIT_SELF", + "value": 61 + }, + { "name": "NOTIFICATION_FOCUS_ENTER", "value": 43 }, @@ -72951,28 +72959,28 @@ "getter": "get_size" }, { - "type": "Texture", + "type": "Texture2D", "name": "texture_albedo", "setter": "set_texture", "getter": "get_texture", "index": 0 }, { - "type": "Texture", + "type": "Texture2D", "name": "texture_normal", "setter": "set_texture", "getter": "get_texture", "index": 1 }, { - "type": "Texture", + "type": "Texture2D", "name": "texture_orm", "setter": "set_texture", "getter": "get_texture", "index": 2 }, { - "type": "Texture", + "type": "Texture2D", "name": "texture_emission", "setter": "set_texture", "getter": "get_texture", @@ -125379,28 +125387,28 @@ "api_type": "core", "methods": [ { - "name": "set_light_texture", + "name": "set_lightmap_textures", "is_const": false, "is_vararg": false, "is_static": false, "is_virtual": false, - "hash": 1278366092, + "hash": 381264803, "arguments": [ { - "name": "light_texture", - "type": "TextureLayered" + "name": "light_textures", + "type": "typedarray::TextureLayered" } ] }, { - "name": "get_light_texture", + "name": "get_lightmap_textures", "is_const": true, "is_vararg": false, "is_static": false, "is_virtual": false, - "hash": 3984243839, + "hash": 3995934104, "return_value": { - "type": "TextureLayered" + "type": "typedarray::TextureLayered" } }, { @@ -125493,20 +125501,39 @@ "is_static": false, "is_virtual": false, "hash": 3218959716 + }, + { + "name": "set_light_texture", + "is_const": false, + "is_vararg": false, + "is_static": false, + "is_virtual": false, + "hash": 1278366092, + "arguments": [ + { + "name": "light_texture", + "type": "TextureLayered" + } + ] + }, + { + "name": "get_light_texture", + "is_const": true, + "is_vararg": false, + "is_static": false, + "is_virtual": false, + "hash": 3984243839, + "return_value": { + "type": "TextureLayered" + } } ], "properties": [ { - "type": "TextureLayered", - "name": "light_texture", - "setter": "set_light_texture", - "getter": "get_light_texture" - }, - { - "type": "Array", - "name": "light_textures", - "setter": "_set_light_textures_data", - "getter": "_get_light_textures_data" + "type": "typedarray::TextureLayered", + "name": "lightmap_textures", + "setter": "set_lightmap_textures", + "getter": "get_lightmap_textures" }, { "type": "bool", @@ -125525,6 +125552,18 @@ "name": "probe_data", "setter": "_set_probe_data", "getter": "_get_probe_data" + }, + { + "type": "TextureLayered", + "name": "light_texture", + "setter": "set_light_texture", + "getter": "get_light_texture" + }, + { + "type": "Array", + "name": "light_textures", + "setter": "_set_light_textures_data", + "getter": "_get_light_textures_data" } ] }, @@ -127389,7 +127428,7 @@ "getter": "is_drag_and_drop_selection_enabled" }, { - "type": "Texture", + "type": "Texture2D", "name": "right_icon", "setter": "set_right_icon", "getter": "get_right_icon" @@ -128961,6 +129000,10 @@ { "name": "ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY", "value": 268435456 + }, + { + "name": "ARRAY_FLAG_COMPRESS_ATTRIBUTES", + "value": 536870912 } ] }, @@ -145289,10 +145332,6 @@ "value": 29 }, { - "name": "NOTIFICATION_NODE_RECACHE_REQUESTED", - "value": 30 - }, - { "name": "NOTIFICATION_EDITOR_PRE_SAVE", "value": 9001 }, @@ -225928,7 +225967,7 @@ ], "properties": [ { - "type": "Texture", + "type": "Texture2D", "name": "texture", "setter": "set_texture", "getter": "get_texture" @@ -232117,6 +232156,12 @@ "properties": [ { "type": "int", + "name": "tab_count", + "setter": "set_tab_count", + "getter": "get_tab_count" + }, + { + "type": "int", "name": "current_tab", "setter": "set_current_tab", "getter": "get_current_tab" @@ -232174,12 +232219,6 @@ "name": "select_with_rmb", "setter": "set_select_with_rmb", "getter": "get_select_with_rmb" - }, - { - "type": "int", - "name": "tab_count", - "setter": "set_tab_count", - "getter": "get_tab_count" } ] }, @@ -266708,31 +266747,6 @@ } }, { - "name": "set_disable_2d", - "is_const": false, - "is_vararg": false, - "is_static": false, - "is_virtual": false, - "hash": 2586408642, - "arguments": [ - { - "name": "disable", - "type": "bool" - } - ] - }, - { - "name": "is_2d_disabled", - "is_const": true, - "is_vararg": false, - "is_static": false, - "is_virtual": false, - "hash": 36873697, - "return_value": { - "type": "bool" - } - }, - { "name": "set_disable_3d", "is_const": false, "is_vararg": false, @@ -266956,12 +266970,6 @@ "properties": [ { "type": "bool", - "name": "disable_2d", - "setter": "set_disable_2d", - "getter": "is_2d_disabled" - }, - { - "type": "bool", "name": "disable_3d", "setter": "set_disable_3d", "getter": "is_3d_disabled" diff --git a/include/godot_cpp/core/object.hpp b/include/godot_cpp/core/object.hpp index 4e85e4d..79f8fbf 100644 --- a/include/godot_cpp/core/object.hpp +++ b/include/godot_cpp/core/object.hpp @@ -33,6 +33,8 @@ #include <godot_cpp/core/defs.hpp> +#include <godot_cpp/core/object_id.hpp> + #include <godot_cpp/core/property_info.hpp> #include <godot_cpp/variant/variant.hpp> @@ -106,28 +108,6 @@ MethodInfo::MethodInfo(const PropertyInfo &p_ret, StringName p_name, const Args arguments = { args... }; } -class ObjectID { - uint64_t id = 0; - -public: - _FORCE_INLINE_ bool is_ref_counted() const { return (id & (uint64_t(1) << 63)) != 0; } - _FORCE_INLINE_ bool is_valid() const { return id != 0; } - _FORCE_INLINE_ bool is_null() const { return id == 0; } - _FORCE_INLINE_ operator uint64_t() const { return id; } - _FORCE_INLINE_ operator int64_t() const { return id; } - - _FORCE_INLINE_ bool operator==(const ObjectID &p_id) const { return id == p_id.id; } - _FORCE_INLINE_ bool operator!=(const ObjectID &p_id) const { return id != p_id.id; } - _FORCE_INLINE_ bool operator<(const ObjectID &p_id) const { return id < p_id.id; } - - _FORCE_INLINE_ void operator=(int64_t p_int64) { id = p_int64; } - _FORCE_INLINE_ void operator=(uint64_t p_uint64) { id = p_uint64; } - - _FORCE_INLINE_ ObjectID() {} - _FORCE_INLINE_ explicit ObjectID(const uint64_t p_id) { id = p_id; } - _FORCE_INLINE_ explicit ObjectID(const int64_t p_id) { id = p_id; } -}; - class ObjectDB { public: static Object *get_instance(uint64_t p_object_id) { diff --git a/include/godot_cpp/core/object_id.hpp b/include/godot_cpp/core/object_id.hpp new file mode 100644 index 0000000..9f3bc96 --- /dev/null +++ b/include/godot_cpp/core/object_id.hpp @@ -0,0 +1,62 @@ +/**************************************************************************/ +/* object_id.hpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#ifndef GODOT_OBJECT_ID_HPP +#define GODOT_OBJECT_ID_HPP + +#include <godot_cpp/core/defs.hpp> + +namespace godot { + +class ObjectID { + uint64_t id = 0; + +public: + _FORCE_INLINE_ bool is_ref_counted() const { return (id & (uint64_t(1) << 63)) != 0; } + _FORCE_INLINE_ bool is_valid() const { return id != 0; } + _FORCE_INLINE_ bool is_null() const { return id == 0; } + _FORCE_INLINE_ operator uint64_t() const { return id; } + _FORCE_INLINE_ operator int64_t() const { return id; } + + _FORCE_INLINE_ bool operator==(const ObjectID &p_id) const { return id == p_id.id; } + _FORCE_INLINE_ bool operator!=(const ObjectID &p_id) const { return id != p_id.id; } + _FORCE_INLINE_ bool operator<(const ObjectID &p_id) const { return id < p_id.id; } + + _FORCE_INLINE_ void operator=(int64_t p_int64) { id = p_int64; } + _FORCE_INLINE_ void operator=(uint64_t p_uint64) { id = p_uint64; } + + _FORCE_INLINE_ ObjectID() {} + _FORCE_INLINE_ explicit ObjectID(const uint64_t p_id) { id = p_id; } + _FORCE_INLINE_ explicit ObjectID(const int64_t p_id) { id = p_id; } +}; + +} // namespace godot + +#endif // GODOT_OBJECT_ID_HPP diff --git a/include/godot_cpp/godot.hpp b/include/godot_cpp/godot.hpp index 46911dc..c9e9022 100644 --- a/include/godot_cpp/godot.hpp +++ b/include/godot_cpp/godot.hpp @@ -166,6 +166,7 @@ extern "C" GDExtensionInterfaceObjectCastTo gdextension_interface_object_cast_to extern "C" GDExtensionInterfaceObjectGetInstanceFromId gdextension_interface_object_get_instance_from_id; extern "C" GDExtensionInterfaceObjectGetInstanceId gdextension_interface_object_get_instance_id; extern "C" GDExtensionInterfaceCallableCustomCreate gdextension_interface_callable_custom_create; +extern "C" GDExtensionInterfaceCallableCustomGetUserData gdextension_interface_callable_custom_get_userdata; extern "C" GDExtensionInterfaceRefGetObject gdextension_interface_ref_get_object; extern "C" GDExtensionInterfaceRefSetObject gdextension_interface_ref_set_object; extern "C" GDExtensionInterfaceScriptInstanceCreate2 gdextension_interface_script_instance_create2; @@ -193,26 +194,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/callable_custom.hpp b/include/godot_cpp/variant/callable_custom.hpp new file mode 100644 index 0000000..34328f9 --- /dev/null +++ b/include/godot_cpp/variant/callable_custom.hpp @@ -0,0 +1,64 @@ +/**************************************************************************/ +/* callable_custom.hpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#ifndef GODOT_CALLABLE_CUSTOM_HPP +#define GODOT_CALLABLE_CUSTOM_HPP + +#include <godot_cpp/core/object_id.hpp> +#include <godot_cpp/variant/string_name.hpp> + +namespace godot { + +class Object; + +class CallableCustomBase { +public: + virtual ObjectID get_object() const = 0; + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const = 0; + virtual ~CallableCustomBase() {} +}; + +class CallableCustom : public CallableCustomBase { +public: + typedef bool (*CompareEqualFunc)(const CallableCustom *p_a, const CallableCustom *p_b); + typedef bool (*CompareLessFunc)(const CallableCustom *p_a, const CallableCustom *p_b); + + virtual uint32_t hash() const = 0; + virtual String get_as_text() const = 0; + virtual CompareEqualFunc get_compare_equal_func() const = 0; + virtual CompareLessFunc get_compare_less_func() const = 0; + virtual bool is_valid() const; + virtual ObjectID get_object() const = 0; + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const = 0; +}; + +} // namespace godot + +#endif // GODOT_CALLABLE_CUSTOM_HPP diff --git a/include/godot_cpp/variant/callable_method_pointer.hpp b/include/godot_cpp/variant/callable_method_pointer.hpp index d83bcba..159f976 100644 --- a/include/godot_cpp/variant/callable_method_pointer.hpp +++ b/include/godot_cpp/variant/callable_method_pointer.hpp @@ -36,16 +36,23 @@ namespace godot { -class CallableCustomMethodPointerBase { +class CallableCustomMethodPointerBase : public CallableCustomBase { + uint32_t *comp_ptr = nullptr; + uint32_t comp_size; + uint32_t h; + +protected: + void _setup(uint32_t *p_base_ptr, uint32_t p_ptr_size); + public: - virtual Object *get_object() const = 0; - virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const = 0; - virtual ~CallableCustomMethodPointerBase() {} + _FORCE_INLINE_ const uint32_t *get_comp_ptr() const { return comp_ptr; } + _FORCE_INLINE_ uint32_t get_comp_size() const { return comp_size; } + _FORCE_INLINE_ uint32_t get_hash() const { return h; } }; namespace internal { -Callable create_custom_callable(CallableCustomMethodPointerBase *p_callable_method_pointer); +Callable create_callable_from_ccmp(CallableCustomMethodPointerBase *p_callable_method_pointer); } // namespace internal @@ -55,21 +62,26 @@ Callable create_custom_callable(CallableCustomMethodPointerBase *p_callable_meth template <class T, class... P> class CallableCustomMethodPointer : public CallableCustomMethodPointerBase { - T *instance; - void (T::*method)(P...); + struct Data { + T *instance; + void (T::*method)(P...); + } data; + static_assert(sizeof(Data) % 4 == 0); public: - virtual Object *get_object() const override { - return instance; + virtual ObjectID get_object() const override { + return ObjectID(data.instance->get_instance_id()); } virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { - call_with_variant_args(instance, method, p_arguments, p_argcount, r_call_error); + call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error); } CallableCustomMethodPointer(T *p_instance, void (T::*p_method)(P...)) { - instance = p_instance; - method = p_method; + memset(&data, 0, sizeof(Data)); + data.instance = p_instance; + data.method = p_method; + _setup((uint32_t *)&data, sizeof(Data)); } }; @@ -77,7 +89,7 @@ template <class T, class... P> Callable create_custom_callable_function_pointer(T *p_instance, void (T::*p_method)(P...)) { typedef CallableCustomMethodPointer<T, P...> CCMP; CCMP *ccmp = memnew(CCMP(p_instance, p_method)); - return ::godot::internal::create_custom_callable(ccmp); + return ::godot::internal::create_callable_from_ccmp(ccmp); } // @@ -86,22 +98,27 @@ Callable create_custom_callable_function_pointer(T *p_instance, void (T::*p_meth template <class T, class R, class... P> class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase { - T *instance; - R(T::*method) - (P...); + struct Data { + T *instance; + R(T::*method) + (P...); + } data; + static_assert(sizeof(Data) % 4 == 0); public: - virtual Object *get_object() const override { - return instance; + virtual ObjectID get_object() const override { + return ObjectID(data.instance->get_instance_id()); } virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { - call_with_variant_args_ret(instance, method, p_arguments, p_argcount, r_return_value, r_call_error); + call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error); } CallableCustomMethodPointerRet(T *p_instance, R (T::*p_method)(P...)) { - instance = p_instance; - method = p_method; + memset(&data, 0, sizeof(Data)); + data.instance = p_instance; + data.method = p_method; + _setup((uint32_t *)&data, sizeof(Data)); } }; @@ -109,7 +126,7 @@ template <class T, class R, class... P> Callable create_custom_callable_function_pointer(T *p_instance, R (T::*p_method)(P...)) { typedef CallableCustomMethodPointerRet<T, R, P...> CCMP; // Messes with memnew otherwise. CCMP *ccmp = memnew(CCMP(p_instance, p_method)); - return ::godot::internal::create_custom_callable(ccmp); + return ::godot::internal::create_callable_from_ccmp(ccmp); } // @@ -118,22 +135,27 @@ Callable create_custom_callable_function_pointer(T *p_instance, R (T::*p_method) template <class T, class R, class... P> class CallableCustomMethodPointerRetC : public CallableCustomMethodPointerBase { - T *instance; - R(T::*method) - (P...) const; + struct Data { + T *instance; + R(T::*method) + (P...) const; + } data; + static_assert(sizeof(Data) % 4 == 0); public: - virtual Object *get_object() const override { - return instance; + virtual ObjectID get_object() const override { + return ObjectID(data.instance->get_instance_id()); } virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { - call_with_variant_args_retc(instance, method, p_arguments, p_argcount, r_return_value, r_call_error); + call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error); } CallableCustomMethodPointerRetC(const T *p_instance, R (T::*p_method)(P...) const) { - instance = const_cast<T *>(p_instance); - method = p_method; + memset(&data, 0, sizeof(Data)); + data.instance = const_cast<T *>(p_instance); + data.method = p_method; + _setup((uint32_t *)&data, sizeof(Data)); } }; @@ -141,7 +163,7 @@ template <class T, class R, class... P> Callable create_custom_callable_function_pointer(const T *p_instance, R (T::*p_method)(P...) const) { typedef CallableCustomMethodPointerRetC<T, R, P...> CCMP; // Messes with memnew otherwise. CCMP *ccmp = memnew(CCMP(p_instance, p_method)); - return ::godot::internal::create_custom_callable(ccmp); + return ::godot::internal::create_callable_from_ccmp(ccmp); } // @@ -150,20 +172,25 @@ Callable create_custom_callable_function_pointer(const T *p_instance, R (T::*p_m template <class... P> class CallableCustomStaticMethodPointer : public CallableCustomMethodPointerBase { - void (*method)(P...); + struct Data { + void (*method)(P...); + } data; + static_assert(sizeof(Data) % 4 == 0); public: - virtual Object *get_object() const override { - return nullptr; + virtual ObjectID get_object() const override { + return ObjectID(); } virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { - call_with_variant_args_static_ret(method, p_arguments, p_argcount, r_return_value, r_call_error); + call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error); r_return_value = Variant(); } CallableCustomStaticMethodPointer(void (*p_method)(P...)) { - method = p_method; + memset(&data, 0, sizeof(Data)); + data.method = p_method; + _setup((uint32_t *)&data, sizeof(Data)); } }; @@ -171,7 +198,7 @@ template <class... P> Callable create_custom_callable_static_function_pointer(void (*p_method)(P...)) { typedef CallableCustomStaticMethodPointer<P...> CCMP; CCMP *ccmp = memnew(CCMP(p_method)); - return ::godot::internal::create_custom_callable(ccmp); + return ::godot::internal::create_callable_from_ccmp(ccmp); } // @@ -180,20 +207,25 @@ Callable create_custom_callable_static_function_pointer(void (*p_method)(P...)) template <class R, class... P> class CallableCustomStaticMethodPointerRet : public CallableCustomMethodPointerBase { - R(*method) - (P...); + struct Data { + R(*method) + (P...); + } data; + static_assert(sizeof(Data) % 4 == 0); public: - virtual Object *get_object() const override { - return nullptr; + virtual ObjectID get_object() const override { + return ObjectID(); } virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { - call_with_variant_args_static_ret(method, p_arguments, p_argcount, r_return_value, r_call_error); + call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error); } CallableCustomStaticMethodPointerRet(R (*p_method)(P...)) { - method = p_method; + memset(&data, 0, sizeof(Data)); + data.method = p_method; + _setup((uint32_t *)&data, sizeof(Data)); } }; @@ -201,7 +233,7 @@ template <class R, class... P> Callable create_custom_callable_static_function_pointer(R (*p_method)(P...)) { typedef CallableCustomStaticMethodPointerRet<R, P...> CCMP; CCMP *ccmp = memnew(CCMP(p_method)); - return ::godot::internal::create_custom_callable(ccmp); + return ::godot::internal::create_callable_from_ccmp(ccmp); } // diff --git a/include/godot_cpp/variant/variant.hpp b/include/godot_cpp/variant/variant.hpp index 3e8738c..fc41014 100644 --- a/include/godot_cpp/variant/variant.hpp +++ b/include/godot_cpp/variant/variant.hpp @@ -154,10 +154,18 @@ public: Variant(int64_t v); Variant(int32_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(uint64_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(float v) : Variant((double)v) {} @@ -209,8 +217,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/godot.cpp b/src/godot.cpp index 7579cfd..ee4156b 100644 --- a/src/godot.cpp +++ b/src/godot.cpp @@ -172,6 +172,7 @@ GDExtensionInterfaceObjectCastTo gdextension_interface_object_cast_to = nullptr; GDExtensionInterfaceObjectGetInstanceFromId gdextension_interface_object_get_instance_from_id = nullptr; GDExtensionInterfaceObjectGetInstanceId gdextension_interface_object_get_instance_id = nullptr; GDExtensionInterfaceCallableCustomCreate gdextension_interface_callable_custom_create = nullptr; +GDExtensionInterfaceCallableCustomGetUserData gdextension_interface_callable_custom_get_userdata = nullptr; GDExtensionInterfaceRefGetObject gdextension_interface_ref_get_object = nullptr; GDExtensionInterfaceRefSetObject gdextension_interface_ref_set_object = nullptr; GDExtensionInterfaceScriptInstanceCreate2 gdextension_interface_script_instance_create2 = nullptr; @@ -195,9 +196,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) @@ -224,7 +225,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) { @@ -390,6 +404,7 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge LOAD_PROC_ADDRESS(object_get_instance_from_id, GDExtensionInterfaceObjectGetInstanceFromId); LOAD_PROC_ADDRESS(object_get_instance_id, GDExtensionInterfaceObjectGetInstanceId); LOAD_PROC_ADDRESS(callable_custom_create, GDExtensionInterfaceCallableCustomCreate); + LOAD_PROC_ADDRESS(callable_custom_get_userdata, GDExtensionInterfaceCallableCustomGetUserData); LOAD_PROC_ADDRESS(ref_get_object, GDExtensionInterfaceRefGetObject); LOAD_PROC_ADDRESS(ref_set_object, GDExtensionInterfaceRefSetObject); LOAD_PROC_ADDRESS(script_instance_create2, GDExtensionInterfaceScriptInstanceCreate2); @@ -413,59 +428,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<ModuleInitializationLevel>(p_level) >= MODULE_INITIALIZATION_LEVEL_MAX); 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; - 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) { 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<GDExtensionInitializationLevel>(p_level); + init_data->minimum_initialization_level = static_cast<GDExtensionInitializationLevel>(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/callable_custom.cpp b/src/variant/callable_custom.cpp new file mode 100644 index 0000000..e0540fa --- /dev/null +++ b/src/variant/callable_custom.cpp @@ -0,0 +1,113 @@ +/**************************************************************************/ +/* callable_custom.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. */ +/**************************************************************************/ + +#include <godot_cpp/variant/callable_custom.hpp> + +#include <godot_cpp/core/object.hpp> +#include <godot_cpp/variant/callable.hpp> + +namespace godot { + +static void callable_custom_call(void *p_userdata, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) { + CallableCustom *callable_custom = (CallableCustom *)p_userdata; + callable_custom->call((const Variant **)p_args, p_argument_count, *(Variant *)r_return, *r_error); +} + +static GDExtensionBool callable_custom_is_valid(void *p_userdata) { + CallableCustom *callable_custom = (CallableCustom *)p_userdata; + return callable_custom->is_valid(); +} + +static void callable_custom_free(void *p_userdata) { + CallableCustom *callable_custom = (CallableCustom *)p_userdata; + memdelete(callable_custom); +} + +static uint32_t callable_custom_hash(void *p_userdata) { + CallableCustom *callable_custom = (CallableCustom *)p_userdata; + return callable_custom->hash(); +} + +static void callable_custom_to_string(void *p_userdata, GDExtensionBool *r_is_valid, GDExtensionStringPtr r_out) { + CallableCustom *callable_custom = (CallableCustom *)p_userdata; + *((String *)r_out) = callable_custom->get_as_text(); + *r_is_valid = true; +} + +static GDExtensionBool callable_custom_equal_func(void *p_a, void *p_b) { + CallableCustom *a = (CallableCustom *)p_a; + CallableCustom *b = (CallableCustom *)p_b; + CallableCustom::CompareEqualFunc func_a = a->get_compare_equal_func(); + CallableCustom::CompareEqualFunc func_b = b->get_compare_equal_func(); + if (func_a != func_b) { + return false; + } + return func_a(a, b); +} + +static GDExtensionBool callable_custom_less_than_func(void *p_a, void *p_b) { + CallableCustom *a = (CallableCustom *)p_a; + CallableCustom *b = (CallableCustom *)p_b; + CallableCustom::CompareEqualFunc func_a = a->get_compare_less_func(); + CallableCustom::CompareEqualFunc func_b = b->get_compare_less_func(); + if (func_a != func_b) { + // Just compare the addresses. + return p_a < p_b; + } + return func_a(a, b); +} + +bool CallableCustom::is_valid() const { + // The same default implementation as in Godot. + return ObjectDB::get_instance(get_object()); +} + +Callable::Callable(CallableCustom *p_callable_custom) { + GDExtensionCallableCustomInfo info = {}; + info.callable_userdata = p_callable_custom; + info.token = internal::token; + info.object_id = p_callable_custom->get_object(); + info.call_func = &callable_custom_call; + info.is_valid_func = &callable_custom_is_valid; + info.free_func = &callable_custom_free; + info.hash_func = &callable_custom_hash; + info.equal_func = &callable_custom_equal_func; + info.less_than_func = &callable_custom_less_than_func; + info.to_string_func = &callable_custom_to_string; + + ::godot::internal::gdextension_interface_callable_custom_create(_native_ptr(), &info); +} + +CallableCustom *Callable::get_custom() const { + CallableCustomBase *callable_custom = (CallableCustomBase *)::godot::internal::gdextension_interface_callable_custom_get_userdata(_native_ptr(), internal::token); + return dynamic_cast<CallableCustom *>(callable_custom); +} + +} // namespace godot diff --git a/src/variant/callable_method_pointer.cpp b/src/variant/callable_method_pointer.cpp index ea43632..520ed05 100644 --- a/src/variant/callable_method_pointer.cpp +++ b/src/variant/callable_method_pointer.cpp @@ -30,31 +30,79 @@ #include <godot_cpp/variant/callable_method_pointer.hpp> -//#include <godot_cpp/godot.hpp> +#include <godot_cpp/templates/hashfuncs.hpp> namespace godot { -static void call_custom_callable(void *userdata, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) { - CallableCustomMethodPointerBase *callable_method_pointer = (CallableCustomMethodPointerBase *)userdata; +static void custom_callable_mp_call(void *p_userdata, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) { + CallableCustomMethodPointerBase *callable_method_pointer = (CallableCustomMethodPointerBase *)p_userdata; callable_method_pointer->call((const Variant **)p_args, p_argument_count, *(Variant *)r_return, *r_error); } -static void free_custom_callable(void *userdata) { - CallableCustomMethodPointerBase *callable_method_pointer = (CallableCustomMethodPointerBase *)userdata; +static GDExtensionBool custom_callable_mp_is_valid(void *p_userdata) { + CallableCustomMethodPointerBase *callable_method_pointer = (CallableCustomMethodPointerBase *)p_userdata; + ObjectID object = callable_method_pointer->get_object(); + return object == ObjectID() || ObjectDB::get_instance(object); +} + +static void custom_callable_mp_free(void *p_userdata) { + CallableCustomMethodPointerBase *callable_method_pointer = (CallableCustomMethodPointerBase *)p_userdata; memdelete(callable_method_pointer); } -namespace internal { +static uint32_t custom_callable_mp_hash(void *p_userdata) { + CallableCustomMethodPointerBase *callable_method_pointer = (CallableCustomMethodPointerBase *)p_userdata; + return callable_method_pointer->get_hash(); +} + +static GDExtensionBool custom_callable_mp_equal_func(void *p_a, void *p_b) { + CallableCustomMethodPointerBase *a = (CallableCustomMethodPointerBase *)p_a; + CallableCustomMethodPointerBase *b = (CallableCustomMethodPointerBase *)p_b; + + if (a->get_comp_size() != b->get_comp_size()) { + return false; + } + + return memcmp(a->get_comp_ptr(), b->get_comp_ptr(), a->get_comp_size() * 4) == 0; +} -Callable create_custom_callable(CallableCustomMethodPointerBase *p_callable_method_pointer) { - Object *object = p_callable_method_pointer->get_object(); +static GDExtensionBool custom_callable_mp_less_than_func(void *p_a, void *p_b) { + CallableCustomMethodPointerBase *a = (CallableCustomMethodPointerBase *)p_a; + CallableCustomMethodPointerBase *b = (CallableCustomMethodPointerBase *)p_b; + + if (a->get_comp_size() != b->get_comp_size()) { + return a->get_comp_size() < b->get_comp_size(); + } + + return memcmp(a->get_comp_ptr(), b->get_comp_ptr(), a->get_comp_size() * 4) < 0; +} + +void CallableCustomMethodPointerBase::_setup(uint32_t *p_base_ptr, uint32_t p_ptr_size) { + comp_ptr = p_base_ptr; + comp_size = p_ptr_size / 4; + + for (uint32_t i = 0; i < comp_size; i++) { + if (i == 0) { + h = hash_murmur3_one_32(comp_ptr[i]); + } else { + h = hash_murmur3_one_32(comp_ptr[i], h); + } + } +} + +namespace internal { +Callable create_callable_from_ccmp(CallableCustomMethodPointerBase *p_callable_method_pointer) { GDExtensionCallableCustomInfo info = {}; info.callable_userdata = p_callable_method_pointer; info.token = internal::token; - info.object_id = object ? object->get_instance_id() : 0; - info.call_func = &call_custom_callable; - info.free_func = &free_custom_callable; + info.object_id = p_callable_method_pointer->get_object(); + info.call_func = &custom_callable_mp_call; + info.is_valid_func = &custom_callable_mp_is_valid; + info.free_func = &custom_callable_mp_free; + info.hash_func = &custom_callable_mp_hash; + info.equal_func = &custom_callable_mp_equal_func; + info.less_than_func = &custom_callable_mp_less_than_func; Callable callable; ::godot::internal::gdextension_interface_callable_custom_create(callable._native_ptr(), &info); diff --git a/src/variant/variant.cpp b/src/variant/variant.cpp index db15be1..945d6f4 100644 --- a/src/variant/variant.cpp +++ b/src/variant/variant.cpp @@ -268,6 +268,14 @@ Variant::operator int32_t() const { 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 { 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()); } +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 { double result; to_type_constructor[FLOAT](&result, _native_ptr()); diff --git a/test/project/main.gd b/test/project/main.gd index 62bd19d..233ea41 100644 --- a/test/project/main.gd +++ b/test/project/main.gd @@ -101,9 +101,20 @@ func _ready(): # mp_callable() with void method. var mp_callable: Callable = example.test_callable_mp() + assert_equal(mp_callable.is_valid(), true) mp_callable.call(example, "void", 36) assert_equal(custom_signal_emitted, ["unbound_method1: Example - void", 36]) + # Check that it works with is_connected(). + assert_equal(example.renamed.is_connected(mp_callable), false) + example.renamed.connect(mp_callable) + assert_equal(example.renamed.is_connected(mp_callable), true) + # Make sure a new object is still treated as equivalent. + assert_equal(example.renamed.is_connected(example.test_callable_mp()), true) + assert_equal(mp_callable.hash(), example.test_callable_mp().hash()) + example.renamed.disconnect(mp_callable) + assert_equal(example.renamed.is_connected(mp_callable), false) + # mp_callable() with return value. var mp_callable_ret: Callable = example.test_callable_mp_ret() assert_equal(mp_callable_ret.call(example, "test", 77), "unbound_method2: Example - test - 77") @@ -117,10 +128,30 @@ func _ready(): mp_callable_static.call(example, "static", 83) assert_equal(custom_signal_emitted, ["unbound_static_method1: Example - static", 83]) + # Check that it works with is_connected(). + assert_equal(example.renamed.is_connected(mp_callable_static), false) + example.renamed.connect(mp_callable_static) + assert_equal(example.renamed.is_connected(mp_callable_static), true) + # Make sure a new object is still treated as equivalent. + assert_equal(example.renamed.is_connected(example.test_callable_mp_static()), true) + assert_equal(mp_callable_static.hash(), example.test_callable_mp_static().hash()) + example.renamed.disconnect(mp_callable_static) + assert_equal(example.renamed.is_connected(mp_callable_static), false) + # mp_callable_static() with return value. var mp_callable_static_ret: Callable = example.test_callable_mp_static_ret() assert_equal(mp_callable_static_ret.call(example, "static-ret", 84), "unbound_static_method2: Example - static-ret - 84") + # CallableCustom. + var custom_callable: Callable = example.test_custom_callable(); + assert_equal(custom_callable.is_custom(), true); + assert_equal(custom_callable.is_valid(), true); + assert_equal(custom_callable.call(), "Hi") + assert_equal(custom_callable.hash(), 27); + assert_equal(custom_callable.get_object(), null); + assert_equal(custom_callable.get_method(), ""); + assert_equal(str(custom_callable), "<MyCallableCustom>"); + # PackedArray iterators assert_equal(example.test_vector_ops(), 105) diff --git a/test/src/example.cpp b/test/src/example.cpp index dd58f37..2b8bef6 100644 --- a/test/src/example.cpp +++ b/test/src/example.cpp @@ -15,6 +15,46 @@ using namespace godot; +class MyCallableCustom : public CallableCustom { +public: + virtual uint32_t hash() const { + return 27; + } + + virtual String get_as_text() const { + return "<MyCallableCustom>"; + } + + static bool compare_equal_func(const CallableCustom *p_a, const CallableCustom *p_b) { + return p_a == p_b; + } + + virtual CompareEqualFunc get_compare_equal_func() const { + return &MyCallableCustom::compare_equal_func; + } + + static bool compare_less_func(const CallableCustom *p_a, const CallableCustom *p_b) { + return (void *)p_a < (void *)p_b; + } + + virtual CompareLessFunc get_compare_less_func() const { + return &MyCallableCustom::compare_less_func; + } + + bool is_valid() const { + return true; + } + + virtual ObjectID get_object() const { + return ObjectID(); + } + + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const { + r_return_value = "Hi"; + r_call_error.error = GDEXTENSION_CALL_OK; + } +}; + void ExampleRef::set_id(int p_id) { id = p_id; } @@ -168,6 +208,7 @@ void Example::_bind_methods() { ClassDB::bind_method(D_METHOD("test_callable_mp_retc"), &Example::test_callable_mp_retc); ClassDB::bind_method(D_METHOD("test_callable_mp_static"), &Example::test_callable_mp_static); ClassDB::bind_method(D_METHOD("test_callable_mp_static_ret"), &Example::test_callable_mp_static_ret); + ClassDB::bind_method(D_METHOD("test_custom_callable"), &Example::test_custom_callable); ClassDB::bind_method(D_METHOD("test_bitfield", "flags"), &Example::test_bitfield); @@ -378,6 +419,10 @@ Callable Example::test_callable_mp_static_ret() const { return callable_mp_static(&Example::unbound_static_method2); } +Callable Example::test_custom_callable() const { + return Callable(memnew(MyCallableCustom)); +} + void Example::unbound_method1(Object *p_object, String p_string, int p_int) { String test = "unbound_method1: "; test += p_object->get_class(); diff --git a/test/src/example.h b/test/src/example.h index 200c970..388cc8c 100644 --- a/test/src/example.h +++ b/test/src/example.h @@ -143,6 +143,7 @@ public: Callable test_callable_mp_retc() const; Callable test_callable_mp_static() const; Callable test_callable_mp_static_ret() const; + Callable test_custom_callable() const; void unbound_method1(Object *p_object, String p_string, int p_int); String unbound_method2(Object *p_object, String p_string, int p_int); diff --git a/tools/android.py b/tools/android.py index 479ee15..bee58c4 100644 --- a/tools/android.py +++ b/tools/android.py @@ -8,7 +8,7 @@ def options(opts): opts.Add( "android_api_level", "Target Android API level", - "18" if "32" in ARGUMENTS.get("arch", "arm64") else "21", + "21", ) opts.Add( "ANDROID_HOME", @@ -47,11 +47,9 @@ def generate(env): my_spawn.configure(env) # 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" - api_level = 21 # Setup toolchain toolchain = get_android_ndk_root(env) + "/toolchains/llvm/prebuilt/" diff --git a/tools/ios.py b/tools/ios.py index e387f42..9d37214 100644 --- a/tools/ios.py +++ b/tools/ios.py @@ -21,7 +21,7 @@ def has_ios_osxcross(): def options(opts): opts.Add(BoolVariable("ios_simulator", "Target iOS Simulator", False)) - opts.Add("ios_min_version", "Target minimum iphoneos/iphonesimulator version", "10.0") + opts.Add("ios_min_version", "Target minimum iphoneos/iphonesimulator version", "12.0") opts.Add( "IOS_TOOLCHAIN_PATH", "Path to iOS toolchain", |