diff options
Diffstat (limited to 'core/extension')
-rw-r--r-- | core/extension/gdextension.cpp | 74 | ||||
-rw-r--r-- | core/extension/gdextension.h | 12 | ||||
-rw-r--r-- | core/extension/gdextension_interface.cpp | 2 | ||||
-rw-r--r-- | core/extension/gdextension_interface.h | 59 |
4 files changed, 128 insertions, 19 deletions
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 60487c5a52..c1298c8687 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -205,6 +205,7 @@ public: virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override { #ifdef TOOLS_ENABLED ERR_FAIL_COND_V_MSG(!valid, Variant(), vformat("Cannot call invalid GDExtension method bind '%s'. It's probably cached - you may need to restart Godot.", name)); + ERR_FAIL_COND_V_MSG(p_object && p_object->is_extension_placeholder(), Variant(), vformat("Cannot call GDExtension method bind '%s' on placeholder instance.", name)); #endif Variant ret; GDExtensionClassInstancePtr extension_instance = is_static() ? nullptr : p_object->_get_extension_instance(); @@ -218,6 +219,7 @@ public: virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override { #ifdef TOOLS_ENABLED ERR_FAIL_COND_MSG(!valid, vformat("Cannot call invalid GDExtension method bind '%s'. It's probably cached - you may need to restart Godot.", name)); + ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder(), vformat("Cannot call GDExtension method bind '%s' on placeholder instance.", name)); #endif ERR_FAIL_COND_MSG(vararg, "Vararg methods don't have validated call support. This is most likely an engine bug."); GDExtensionClassInstancePtr extension_instance = is_static() ? nullptr : p_object->_get_extension_instance(); @@ -249,6 +251,7 @@ public: virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override { #ifdef TOOLS_ENABLED ERR_FAIL_COND_MSG(!valid, vformat("Cannot call invalid GDExtension method bind '%s'. It's probably cached - you may need to restart Godot.", name)); + ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder(), vformat("Cannot call GDExtension method bind '%s' on placeholder instance.", name)); #endif ERR_FAIL_COND_MSG(vararg, "Vararg methods don't have ptrcall support. This is most likely an engine bug."); GDExtensionClassInstancePtr extension_instance = p_object->_get_extension_instance(); @@ -341,10 +344,11 @@ public: #ifndef DISABLE_DEPRECATED void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs) { - const GDExtensionClassCreationInfo2 class_info2 = { + const GDExtensionClassCreationInfo3 class_info3 = { p_extension_funcs->is_virtual, // GDExtensionBool is_virtual; p_extension_funcs->is_abstract, // GDExtensionBool is_abstract; true, // GDExtensionBool is_exposed; + false, // GDExtensionBool is_runtime; p_extension_funcs->set_func, // GDExtensionClassSet set_func; p_extension_funcs->get_func, // GDExtensionClassGet get_func; p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func; @@ -369,15 +373,45 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library const ClassCreationDeprecatedInfo legacy = { p_extension_funcs->notification_func, }; - _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info2, &legacy); + _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3, &legacy); } -#endif // DISABLE_DEPRECATED void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs) { + const GDExtensionClassCreationInfo3 class_info3 = { + p_extension_funcs->is_virtual, // GDExtensionBool is_virtual; + p_extension_funcs->is_abstract, // GDExtensionBool is_abstract; + p_extension_funcs->is_exposed, // GDExtensionBool is_exposed; + false, // GDExtensionBool is_runtime; + p_extension_funcs->set_func, // GDExtensionClassSet set_func; + p_extension_funcs->get_func, // GDExtensionClassGet get_func; + p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func; + p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func; + p_extension_funcs->property_can_revert_func, // GDExtensionClassPropertyCanRevert property_can_revert_func; + p_extension_funcs->property_get_revert_func, // GDExtensionClassPropertyGetRevert property_get_revert_func; + p_extension_funcs->validate_property_func, // GDExtensionClassValidateProperty validate_property_func; + p_extension_funcs->notification_func, // GDExtensionClassNotification2 notification_func; + p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func; + p_extension_funcs->reference_func, // GDExtensionClassReference reference_func; + p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func; + p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; /* this one is mandatory */ + p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */ + p_extension_funcs->recreate_instance_func, // GDExtensionClassRecreateInstance recreate_instance_func; + p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func; + p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func; + p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_func; + p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid; + p_extension_funcs->class_userdata, // void *class_userdata; + }; + + _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3); +} +#endif // DISABLE_DEPRECATED + +void GDExtension::_register_extension_class3(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs) { _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, p_extension_funcs); } -void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs) { +void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs) { GDExtension *self = reinterpret_cast<GDExtension *>(p_library); StringName class_name = *reinterpret_cast<const StringName *>(p_class_name); @@ -402,10 +436,15 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr #ifdef TOOLS_ENABLED Extension *extension = nullptr; + bool is_runtime = (bool)p_extension_funcs->is_runtime; if (self->is_reloading && self->extension_classes.has(class_name)) { extension = &self->extension_classes[class_name]; if (!parent_extension && parent_class_name != extension->gdextension.parent_class_name) { - ERR_FAIL_MSG(vformat("GDExtension class '%s' attempt to change parent type from '%s' to '%s' on hot reload. Restart Godot for this change to take effect.", class_name, extension->gdextension.parent_class_name, parent_class_name)); + ERR_FAIL_MSG(vformat("GDExtension class '%s' cannot change parent type from '%s' to '%s' on hot reload. Restart Godot for this change to take effect.", class_name, extension->gdextension.parent_class_name, parent_class_name)); + } + if (extension->gdextension.is_runtime != is_runtime) { + ERR_PRINT(vformat("GDExtension class '%s' cannot change to/from runtime class on hot reload. Restart Godot for this change to take effect.", class_name)); + is_runtime = extension->gdextension.is_runtime; } extension->is_reloading = false; } else { @@ -434,6 +473,9 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr extension->gdextension.is_virtual = p_extension_funcs->is_virtual; extension->gdextension.is_abstract = p_extension_funcs->is_abstract; extension->gdextension.is_exposed = p_extension_funcs->is_exposed; +#ifdef TOOLS_ENABLED + extension->gdextension.is_runtime = is_runtime; +#endif extension->gdextension.set = p_extension_funcs->set_func; extension->gdextension.get = p_extension_funcs->get_func; extension->gdextension.get_property_list = p_extension_funcs->get_property_list_func; @@ -836,8 +878,9 @@ void GDExtension::initialize_gdextensions() { #ifndef DISABLE_DEPRECATED register_interface_function("classdb_register_extension_class", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class); -#endif // DISABLE_DEPRECATED register_interface_function("classdb_register_extension_class2", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class2); +#endif // DISABLE_DEPRECATED + register_interface_function("classdb_register_extension_class3", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class3); register_interface_function("classdb_register_extension_class_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_method); register_interface_function("classdb_register_extension_class_virtual_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_virtual_method); register_interface_function("classdb_register_extension_class_integer_constant", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_integer_constant); @@ -1089,7 +1132,10 @@ void GDExtension::prepare_reload() { state.push_back(Pair<String, Variant>(P.name, value)); } - E.value.instance_state[obj_id] = state; + E.value.instance_state[obj_id] = { + state, // List<Pair<String, Variant>> properties; + obj->is_extension_placeholder(), // bool is_placeholder; + }; } } } @@ -1164,25 +1210,29 @@ void GDExtension::finish_reload() { for (KeyValue<StringName, Extension> &E : extension_classes) { // Loop over 'instance_state' rather than 'instance' because new instances // may have been created when re-initializing the extension. - for (const KeyValue<ObjectID, List<Pair<String, Variant>>> &S : E.value.instance_state) { + for (const KeyValue<ObjectID, Extension::InstanceState> &S : E.value.instance_state) { Object *obj = ObjectDB::get_instance(S.key); if (!obj) { continue; } - obj->reset_internal_extension(&E.value.gdextension); + if (S.value.is_placeholder) { + obj->reset_internal_extension(ClassDB::get_placeholder_extension(E.value.gdextension.class_name)); + } else { + obj->reset_internal_extension(&E.value.gdextension); + } } } // Now that all the classes are back, restore the state. for (KeyValue<StringName, Extension> &E : extension_classes) { - for (const KeyValue<ObjectID, List<Pair<String, Variant>>> &S : E.value.instance_state) { + for (const KeyValue<ObjectID, Extension::InstanceState> &S : E.value.instance_state) { Object *obj = ObjectDB::get_instance(S.key); if (!obj) { continue; } - for (const Pair<String, Variant> &state : S.value) { + for (const Pair<String, Variant> &state : S.value.properties) { obj->set(state.first, state.second); } } @@ -1190,7 +1240,7 @@ void GDExtension::finish_reload() { // Finally, let the objects know that we are done reloading them. for (KeyValue<StringName, Extension> &E : extension_classes) { - for (const KeyValue<ObjectID, List<Pair<String, Variant>>> &S : E.value.instance_state) { + for (const KeyValue<ObjectID, Extension::InstanceState> &S : E.value.instance_state) { Object *obj = ObjectDB::get_instance(S.key); if (!obj) { continue; diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h index dbb39acb2e..a2b948a38a 100644 --- a/core/extension/gdextension.h +++ b/core/extension/gdextension.h @@ -59,7 +59,12 @@ class GDExtension : public Resource { bool is_reloading = false; HashMap<StringName, GDExtensionMethodBind *> methods; HashSet<ObjectID> instances; - HashMap<ObjectID, List<Pair<String, Variant>>> instance_state; + + struct InstanceState { + List<Pair<String, Variant>> properties; + bool is_placeholder = false; + }; + HashMap<ObjectID, InstanceState> instance_state; #endif }; @@ -73,9 +78,10 @@ class GDExtension : public Resource { #ifndef DISABLE_DEPRECATED static void _register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs); -#endif // DISABLE_DEPRECATED static void _register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs); - static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs = nullptr); +#endif // DISABLE_DEPRECATED + static void _register_extension_class3(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs); + static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs = nullptr); static void _register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info); static void _register_extension_class_virtual_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info); static void _register_extension_class_integer_constant(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield); diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp index c6d7779473..0c96c32187 100644 --- a/core/extension/gdextension_interface.cpp +++ b/core/extension/gdextension_interface.cpp @@ -1379,7 +1379,7 @@ static GDExtensionMethodBindPtr gdextension_classdb_get_method_bind(GDExtensionC static GDExtensionObjectPtr gdextension_classdb_construct_object(GDExtensionConstStringNamePtr p_classname) { const StringName classname = *reinterpret_cast<const StringName *>(p_classname); - return (GDExtensionObjectPtr)ClassDB::instantiate(classname); + return (GDExtensionObjectPtr)ClassDB::instantiate_no_placeholders(classname); } static void *gdextension_classdb_get_class_tag(GDExtensionConstStringNamePtr p_classname) { diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index 48a66c9fae..65ee647a51 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -290,7 +290,7 @@ typedef struct { GDExtensionClassGetVirtual get_virtual_func; // Queries a virtual function by name and returns a callback to invoke the requested virtual function. GDExtensionClassGetRID get_rid_func; void *class_userdata; // Per-class user data, later accessible in instance bindings. -} GDExtensionClassCreationInfo; // Deprecated. Use GDExtensionClassCreationInfo2 instead. +} GDExtensionClassCreationInfo; // Deprecated. Use GDExtensionClassCreationInfo3 instead. typedef struct { GDExtensionBool is_virtual; @@ -323,7 +323,41 @@ typedef struct { GDExtensionClassCallVirtualWithData call_virtual_with_data_func; GDExtensionClassGetRID get_rid_func; void *class_userdata; // Per-class user data, later accessible in instance bindings. -} GDExtensionClassCreationInfo2; +} GDExtensionClassCreationInfo2; // Deprecated. Use GDExtensionClassCreationInfo3 instead. + +typedef struct { + GDExtensionBool is_virtual; + GDExtensionBool is_abstract; + GDExtensionBool is_exposed; + GDExtensionBool is_runtime; + GDExtensionClassSet set_func; + GDExtensionClassGet get_func; + GDExtensionClassGetPropertyList get_property_list_func; + GDExtensionClassFreePropertyList free_property_list_func; + GDExtensionClassPropertyCanRevert property_can_revert_func; + GDExtensionClassPropertyGetRevert property_get_revert_func; + GDExtensionClassValidateProperty validate_property_func; + GDExtensionClassNotification2 notification_func; + GDExtensionClassToString to_string_func; + GDExtensionClassReference reference_func; + GDExtensionClassUnreference unreference_func; + GDExtensionClassCreateInstance create_instance_func; // (Default) constructor; mandatory. If the class is not instantiable, consider making it virtual or abstract. + GDExtensionClassFreeInstance free_instance_func; // Destructor; mandatory. + GDExtensionClassRecreateInstance recreate_instance_func; + // Queries a virtual function by name and returns a callback to invoke the requested virtual function. + GDExtensionClassGetVirtual get_virtual_func; + // Paired with `call_virtual_with_data_func`, this is an alternative to `get_virtual_func` for extensions that + // need or benefit from extra data when calling virtual functions. + // Returns user data that will be passed to `call_virtual_with_data_func`. + // Returning `NULL` from this function signals to Godot that the virtual function is not overridden. + // Data returned from this function should be managed by the extension and must be valid until the extension is deinitialized. + // You should supply either `get_virtual_func`, or `get_virtual_call_data_func` with `call_virtual_with_data_func`. + GDExtensionClassGetVirtualCallData get_virtual_call_data_func; + // Used to call virtual functions when `get_virtual_call_data_func` is not null. + GDExtensionClassCallVirtualWithData call_virtual_with_data_func; + GDExtensionClassGetRID get_rid_func; + void *class_userdata; // Per-class user data, later accessible in instance bindings. +} GDExtensionClassCreationInfo3; typedef void *GDExtensionClassLibraryPtr; @@ -2240,6 +2274,9 @@ typedef void (*GDExtensionInterfaceObjectSetInstance)(GDExtensionObjectPtr p_o, * * Gets the class name of an Object. * + * If the GDExtension wraps the Godot object in an abstraction specific to its class, this is the + * function that should be used to determine which wrapper to use. + * * @param p_object A pointer to the Object. * @param p_library A pointer the library received by the GDExtension's entry point function. * @param r_class_name A pointer to a String to receive the class name. @@ -2486,7 +2523,7 @@ typedef void *(*GDExtensionInterfaceClassdbGetClassTag)(GDExtensionConstStringNa /** * @name classdb_register_extension_class * @since 4.1 - * @deprecated in Godot 4.2. Use `classdb_register_extension_class2` instead. + * @deprecated in Godot 4.2. Use `classdb_register_extension_class3` instead. * * Registers an extension class in the ClassDB. * @@ -2502,6 +2539,7 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass)(GDExtensionCla /** * @name classdb_register_extension_class2 * @since 4.2 + * @deprecated in Godot 4.3. Use `classdb_register_extension_class3` instead. * * Registers an extension class in the ClassDB. * @@ -2515,6 +2553,21 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass)(GDExtensionCla typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass2)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs); /** + * @name classdb_register_extension_class3 + * @since 4.3 + * + * Registers an extension class in the ClassDB. + * + * Provided struct can be safely freed once the function returns. + * + * @param p_library A pointer the library received by the GDExtension's entry point function. + * @param p_class_name A pointer to a StringName with the class name. + * @param p_parent_class_name A pointer to a StringName with the parent class name. + * @param p_extension_funcs A pointer to a GDExtensionClassCreationInfo2 struct. + */ +typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass3)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs); + +/** * @name classdb_register_extension_class_method * @since 4.1 * |