diff options
Diffstat (limited to 'core/extension/gdextension.cpp')
-rw-r--r-- | core/extension/gdextension.cpp | 109 |
1 files changed, 96 insertions, 13 deletions
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 9b3282ecba..5d43dceece 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); @@ -397,15 +431,20 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr //inheriting from engine class } } else { - ERR_FAIL_MSG("Attempt to register an extension class '" + String(class_name) + "' using non-existing parent class '" + String(parent_class_name) + "'"); + ERR_FAIL_MSG("Attempt to register an extension class '" + String(class_name) + "' using non-existing parent class '" + String(parent_class_name) + "'."); } #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); @@ -910,6 +953,39 @@ Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path, return ERR_INVALID_DATA; } + // Optionally check maximum compatibility. + if (config->has_section_key("configuration", "compatibility_maximum")) { + uint32_t compatibility_maximum[3] = { 0, 0, 0 }; + String compat_string = config->get_value("configuration", "compatibility_maximum"); + Vector<int> parts = compat_string.split_ints("."); + for (int i = 0; i < 3; i++) { + if (i < parts.size() && parts[i] >= 0) { + compatibility_maximum[i] = parts[i]; + } else { + // If a version part is missing, set the maximum to an arbitrary high value. + compatibility_maximum[i] = 9999; + } + } + + compatible = true; + if (VERSION_MAJOR != compatibility_maximum[0]) { + compatible = VERSION_MAJOR < compatibility_maximum[0]; + } else if (VERSION_MINOR != compatibility_maximum[1]) { + compatible = VERSION_MINOR < compatibility_maximum[1]; + } +#if VERSION_PATCH + // #if check to avoid -Wtype-limits warning when 0. + else { + compatible = VERSION_PATCH <= compatibility_maximum[2]; + } +#endif + + if (!compatible) { + ERR_PRINT(vformat("GDExtension only compatible with Godot version %s or earlier: %s", compat_string, p_path)); + return ERR_INVALID_DATA; + } + } + String library_path = GDExtension::find_extension_library(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); }); if (library_path.is_empty()) { @@ -1056,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; + }; } } } @@ -1131,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); } } @@ -1157,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; |