feat: updated engine version to 4.4-rc1

This commit is contained in:
Sara 2025-02-23 14:38:14 +01:00
parent ee00efde1f
commit 21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -32,7 +32,7 @@
#include "core/config/engine.h"
#include "core/core_constants.h"
#include "core/extension/gdextension_compat_hashes.h"
#include "core/extension/gdextension_special_compat_hashes.h"
#include "core/io/file_access.h"
#include "core/io/json.h"
#include "core/templates/pair.h"
@ -60,6 +60,9 @@ static String get_property_info_type_name(const PropertyInfo &p_info) {
if (p_info.type == Variant::ARRAY && (p_info.hint == PROPERTY_HINT_ARRAY_TYPE)) {
return String("typedarray::") + p_info.hint_string;
}
if (p_info.type == Variant::DICTIONARY && (p_info.hint == PROPERTY_HINT_DICTIONARY_TYPE)) {
return String("typeddictionary::") + p_info.hint_string;
}
if (p_info.type == Variant::INT && (p_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM))) {
return String("enum::") + String(p_info.class_name);
}
@ -85,7 +88,7 @@ static String get_property_info_type_name(const PropertyInfo &p_info) {
}
static String get_type_meta_name(const GodotTypeInfo::Metadata metadata) {
static const char *argmeta[11] = { "none", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "double" };
static const char *argmeta[13] = { "none", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "double", "char16", "char32" };
return argmeta[metadata];
}
@ -1014,9 +1017,22 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
d2["name"] = String(method_name);
d2["is_const"] = (F.flags & METHOD_FLAG_CONST) ? true : false;
d2["is_static"] = (F.flags & METHOD_FLAG_STATIC) ? true : false;
d2["is_required"] = (F.flags & METHOD_FLAG_VIRTUAL_REQUIRED) ? true : false;
d2["is_vararg"] = false;
d2["is_virtual"] = true;
// virtual functions have no hash since no MethodBind is involved
d2["hash"] = mi.get_compatibility_hash();
Vector<uint32_t> compat_hashes = ClassDB::get_virtual_method_compatibility_hashes(class_name, method_name);
Array compatibility;
if (compat_hashes.size()) {
for (int i = 0; i < compat_hashes.size(); i++) {
compatibility.push_back(compat_hashes[i]);
}
}
if (compatibility.size() > 0) {
d2["hash_compatibility"] = compatibility;
}
bool has_return = mi.return_val.type != Variant::NIL || (mi.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT);
if (has_return) {
PropertyInfo pinfo = mi.return_val;
@ -1090,7 +1106,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
}
#ifndef DISABLE_DEPRECATED
GDExtensionCompatHashes::get_legacy_hashes(class_name, method_name, compatibility);
GDExtensionSpecialCompatHashes::get_legacy_hashes(class_name, method_name, compatibility);
#endif
if (compatibility.size() > 0) {
@ -1201,7 +1217,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
if (F.name.begins_with("_")) {
continue; //hidden property
}
if (F.name.contains("/")) {
if (F.name.contains_char('/')) {
// Ignore properties with '/' (slash) in the name. These are only meant for use in the inspector.
continue;
}
@ -1359,7 +1375,7 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_
return true; // May just not have this array and its still good. Probably added recently.
}
bool failed = false;
ERR_FAIL_COND_V_MSG(!p_new_api.has(p_base_array), false, "New API lacks base array: " + p_base_array);
ERR_FAIL_COND_V_MSG(!p_new_api.has(p_base_array), false, vformat("New API lacks base array: %s", p_base_array));
Array new_api = p_new_api[p_base_array];
HashMap<String, Dictionary> new_api_assoc;
@ -1367,6 +1383,9 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_
Dictionary elem = var;
ERR_FAIL_COND_V_MSG(!elem.has(p_name_field), false, vformat("Validate extension JSON: Element of base_array '%s' is missing field '%s'. This is a bug.", base_array, p_name_field));
String name = elem[p_name_field];
if (name.is_valid_float()) {
name = name.trim_suffix(".0"); // Make "integers" stringified as integers.
}
if (p_compare_operators && elem.has("right_type")) {
name += " " + String(elem["right_type"]);
}
@ -1382,6 +1401,9 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_
continue;
}
String name = old_elem[p_name_field];
if (name.is_valid_float()) {
name = name.trim_suffix(".0"); // Make "integers" stringified as integers.
}
if (p_compare_operators && old_elem.has("right_type")) {
name += " " + String(old_elem["right_type"]);
}
@ -1463,8 +1485,8 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_
if (p_compare_hashes) {
if (!old_elem.has("hash")) {
if (old_elem.has("is_virtual") && bool(old_elem["is_virtual"]) && !new_elem.has("hash")) {
continue; // No hash for virtual methods, go on.
if (old_elem.has("is_virtual") && bool(old_elem["is_virtual"]) && !old_elem.has("hash")) {
continue; // Virtual methods didn't use to have hashes, so skip check if it's missing in the old file.
}
failed = true;
@ -1511,7 +1533,7 @@ static bool compare_sub_dict_array(HashSet<String> &r_removed_classes_registered
return true; // May just not have this array and its still good. Probably added recently or optional.
}
bool failed = false;
ERR_FAIL_COND_V_MSG(!p_new_api.has(p_outer), false, "New API lacks base array: " + p_outer);
ERR_FAIL_COND_V_MSG(!p_new_api.has(p_outer), false, vformat("New API lacks base array: %s", p_outer));
Array new_api = p_new_api[p_outer];
HashMap<String, Dictionary> new_api_assoc;

View file

@ -32,11 +32,9 @@
#include "gdextension.compat.inc"
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
#include "core/object/class_db.h"
#include "core/object/method_bind.h"
#include "core/os/os.h"
#include "core/version.h"
#include "gdextension_library_loader.h"
#include "gdextension_manager.h"
extern void gdextension_setup_interface();
@ -48,146 +46,6 @@ String GDExtension::get_extension_list_config_file() {
return ProjectSettings::get_singleton()->get_project_data_path().path_join("extension_list.cfg");
}
Vector<SharedObject> GDExtension::find_extension_dependencies(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature) {
Vector<SharedObject> dependencies_shared_objects;
if (p_config->has_section("dependencies")) {
List<String> config_dependencies;
p_config->get_section_keys("dependencies", &config_dependencies);
for (const String &dependency : config_dependencies) {
Vector<String> dependency_tags = dependency.split(".");
bool all_tags_met = true;
for (int i = 0; i < dependency_tags.size(); i++) {
String tag = dependency_tags[i].strip_edges();
if (!p_has_feature(tag)) {
all_tags_met = false;
break;
}
}
if (all_tags_met) {
Dictionary dependency_value = p_config->get_value("dependencies", dependency);
for (const Variant *key = dependency_value.next(nullptr); key; key = dependency_value.next(key)) {
String dependency_path = *key;
String target_path = dependency_value[*key];
if (dependency_path.is_relative_path()) {
dependency_path = p_path.get_base_dir().path_join(dependency_path);
}
dependencies_shared_objects.push_back(SharedObject(dependency_path, dependency_tags, target_path));
}
break;
}
}
}
return dependencies_shared_objects;
}
String GDExtension::find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags) {
// First, check the explicit libraries.
if (p_config->has_section("libraries")) {
List<String> libraries;
p_config->get_section_keys("libraries", &libraries);
// Iterate the libraries, finding the best matching tags.
String best_library_path;
Vector<String> best_library_tags;
for (const String &E : libraries) {
Vector<String> tags = E.split(".");
bool all_tags_met = true;
for (int i = 0; i < tags.size(); i++) {
String tag = tags[i].strip_edges();
if (!p_has_feature(tag)) {
all_tags_met = false;
break;
}
}
if (all_tags_met && tags.size() > best_library_tags.size()) {
best_library_path = p_config->get_value("libraries", E);
best_library_tags = tags;
}
}
if (!best_library_path.is_empty()) {
if (best_library_path.is_relative_path()) {
best_library_path = p_path.get_base_dir().path_join(best_library_path);
}
if (r_tags != nullptr) {
r_tags->append_array(best_library_tags);
}
return best_library_path;
}
}
// Second, try to autodetect
String autodetect_library_prefix;
if (p_config->has_section_key("configuration", "autodetect_library_prefix")) {
autodetect_library_prefix = p_config->get_value("configuration", "autodetect_library_prefix");
}
if (!autodetect_library_prefix.is_empty()) {
String autodetect_path = autodetect_library_prefix;
if (autodetect_path.is_relative_path()) {
autodetect_path = p_path.get_base_dir().path_join(autodetect_path);
}
// Find the folder and file parts of the prefix.
String folder;
String file_prefix;
if (DirAccess::dir_exists_absolute(autodetect_path)) {
folder = autodetect_path;
} else if (DirAccess::dir_exists_absolute(autodetect_path.get_base_dir())) {
folder = autodetect_path.get_base_dir();
file_prefix = autodetect_path.get_file();
} else {
ERR_FAIL_V_MSG(String(), vformat("Error in extension: %s. Could not find folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
}
// Open the folder.
Ref<DirAccess> dir = DirAccess::open(folder);
ERR_FAIL_COND_V_MSG(!dir.is_valid(), String(), vformat("Error in extension: %s. Could not open folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
// Iterate the files and check the prefixes, finding the best matching file.
String best_file;
Vector<String> best_file_tags;
dir->list_dir_begin();
String file_name = dir->_get_next();
while (file_name != "") {
if (!dir->current_is_dir() && file_name.begins_with(file_prefix)) {
// Check if the files matches all requested feature tags.
String tags_str = file_name.trim_prefix(file_prefix);
tags_str = tags_str.trim_suffix(tags_str.get_extension());
Vector<String> tags = tags_str.split(".", false);
bool all_tags_met = true;
for (int i = 0; i < tags.size(); i++) {
String tag = tags[i].strip_edges();
if (!p_has_feature(tag)) {
all_tags_met = false;
break;
}
}
// If all tags are found in the feature list, and we found more tags than before, use this file.
if (all_tags_met && tags.size() > best_file_tags.size()) {
best_file_tags = tags;
best_file = file_name;
}
}
file_name = dir->_get_next();
}
if (!best_file.is_empty()) {
String library_path = folder.path_join(best_file);
if (r_tags != nullptr) {
r_tags->append_array(best_file_tags);
}
return library_path;
}
}
return String();
}
class GDExtensionMethodBind : public MethodBind {
GDExtensionClassMethodCall call_func;
GDExtensionClassMethodValidatedCall validated_call_func;
@ -296,7 +154,7 @@ public:
}
virtual bool is_vararg() const override {
return false;
return vararg;
}
#ifdef TOOLS_ENABLED
@ -382,11 +240,12 @@ 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 GDExtensionClassCreationInfo3 class_info3 = {
const GDExtensionClassCreationInfo4 class_info4 = {
p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
true, // GDExtensionBool is_exposed;
false, // GDExtensionBool is_runtime;
nullptr, // GDExtensionConstStringPtr icon_path;
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;
@ -398,29 +257,33 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library
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 */
nullptr, // GDExtensionClassCreateInstance2 create_instance_func; /* this one is mandatory */
p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
nullptr, // GDExtensionClassRecreateInstance recreate_instance_func;
p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
nullptr, // GDExtensionClassGetVirtual get_virtual_func;
nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
nullptr, // GDExtensionClassCallVirtualWithData call_virtual_func;
p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
p_extension_funcs->class_userdata, // void *class_userdata;
};
const ClassCreationDeprecatedInfo legacy = {
p_extension_funcs->notification_func, // GDExtensionClassNotification notification_func;
p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func;
p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
nullptr,
};
_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3, &legacy);
_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info4, &legacy);
}
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 = {
const GDExtensionClassCreationInfo4 class_info4 = {
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;
nullptr, // GDExtensionConstStringPtr icon_path;
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;
@ -432,35 +295,77 @@ void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_librar
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 */
nullptr, // GDExtensionClassCreateInstance2 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;
nullptr, // GDExtensionClassGetVirtual get_virtual_func;
nullptr, // 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;
};
const ClassCreationDeprecatedInfo legacy = {
nullptr, // GDExtensionClassNotification notification_func;
p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func;
p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtual get_virtual_func;
};
_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3, &legacy);
_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info4, &legacy);
}
#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) {
const GDExtensionClassCreationInfo4 class_info4 = {
p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
p_extension_funcs->is_exposed, // GDExtensionBool is_exposed;
p_extension_funcs->is_runtime, // GDExtensionBool is_runtime;
nullptr, // GDExtensionConstStringPtr icon_path;
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;
nullptr, // GDExtensionClassCreateInstance2 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;
nullptr, // GDExtensionClassGetVirtual get_virtual_func;
nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_func;
p_extension_funcs->class_userdata, // void *class_userdata;
};
const ClassCreationDeprecatedInfo legacy = {
nullptr, // GDExtensionClassNotification notification_func;
nullptr, // GDExtensionClassFreePropertyList free_property_list_func;
p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance2 create_instance_func;
p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtual get_virtual_func;
};
_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info4, &legacy);
}
#endif // DISABLE_DEPRECATED
void GDExtension::_register_extension_class4(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *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 GDExtensionClassCreationInfo3 *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 GDExtensionClassCreationInfo4 *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);
StringName parent_class_name = *reinterpret_cast<const StringName *>(p_parent_class_name);
ERR_FAIL_COND_MSG(!String(class_name).is_valid_identifier(), "Attempt to register extension class '" + class_name + "', which is not a valid class identifier.");
ERR_FAIL_COND_MSG(ClassDB::class_exists(class_name), "Attempt to register extension class '" + class_name + "', which appears to be already registered.");
ERR_FAIL_COND_MSG(!String(class_name).is_valid_unicode_identifier(), vformat("Attempt to register extension class '%s', which is not a valid class identifier.", class_name));
ERR_FAIL_COND_MSG(ClassDB::class_exists(class_name), vformat("Attempt to register extension class '%s', which appears to be already registered.", class_name));
Extension *parent_extension = nullptr;
@ -474,7 +379,7 @@ 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(vformat("Attempt to register an extension class '%s' using non-existing parent class '%s'.", String(class_name), String(parent_class_name)));
}
#ifdef TOOLS_ENABLED
@ -530,6 +435,10 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr
if (p_deprecated_funcs) {
extension->gdextension.notification = p_deprecated_funcs->notification_func;
extension->gdextension.free_property_list = p_deprecated_funcs->free_property_list_func;
extension->gdextension.create_instance = p_deprecated_funcs->create_instance_func;
extension->gdextension.get_rid = p_deprecated_funcs->get_rid_func;
extension->gdextension.get_virtual = p_deprecated_funcs->get_virtual_func;
extension->gdextension.get_virtual_call_data = p_deprecated_funcs->get_virtual_call_data_func;
}
#endif // DISABLE_DEPRECATED
extension->gdextension.notification2 = p_extension_funcs->notification_func;
@ -537,13 +446,12 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr
extension->gdextension.reference = p_extension_funcs->reference_func;
extension->gdextension.unreference = p_extension_funcs->unreference_func;
extension->gdextension.class_userdata = p_extension_funcs->class_userdata;
extension->gdextension.create_instance = p_extension_funcs->create_instance_func;
extension->gdextension.create_instance2 = p_extension_funcs->create_instance_func;
extension->gdextension.free_instance = p_extension_funcs->free_instance_func;
extension->gdextension.recreate_instance = p_extension_funcs->recreate_instance_func;
extension->gdextension.get_virtual = p_extension_funcs->get_virtual_func;
extension->gdextension.get_virtual_call_data = p_extension_funcs->get_virtual_call_data_func;
extension->gdextension.get_virtual2 = p_extension_funcs->get_virtual_func;
extension->gdextension.get_virtual_call_data2 = p_extension_funcs->get_virtual_call_data_func;
extension->gdextension.call_virtual_with_data = p_extension_funcs->call_virtual_with_data_func;
extension->gdextension.get_rid = p_extension_funcs->get_rid_func;
extension->gdextension.reloadable = self->reloadable;
#ifdef TOOLS_ENABLED
@ -559,6 +467,13 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr
#endif
ClassDB::register_extension_class(&extension->gdextension);
if (p_extension_funcs->icon_path != nullptr) {
const String icon_path = *reinterpret_cast<const String *>(p_extension_funcs->icon_path);
if (!icon_path.is_empty()) {
self->class_icon_paths[class_name] = icon_path;
}
}
}
void GDExtension::_register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info) {
@ -566,7 +481,7 @@ void GDExtension::_register_extension_class_method(GDExtensionClassLibraryPtr p_
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
StringName method_name = *reinterpret_cast<const StringName *>(p_method_info->name);
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension method '" + String(method_name) + "' for unexisting class '" + class_name + "'.");
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to register extension method '%s' for unexisting class '%s'.", String(method_name), class_name));
#ifdef TOOLS_ENABLED
Extension *extension = &self->extension_classes[class_name];
@ -616,7 +531,7 @@ void GDExtension::_register_extension_class_integer_constant(GDExtensionClassLib
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
StringName enum_name = *reinterpret_cast<const StringName *>(p_enum_name);
StringName constant_name = *reinterpret_cast<const StringName *>(p_constant_name);
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension constant '" + constant_name + "' for unexisting class '" + class_name + "'.");
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to register extension constant '%s' for unexisting class '%s'.", constant_name, class_name));
#ifdef TOOLS_ENABLED
// If the extension is still marked as reloading, that means it failed to register again.
@ -640,7 +555,7 @@ void GDExtension::_register_extension_class_property_indexed(GDExtensionClassLib
StringName setter = *reinterpret_cast<const StringName *>(p_setter);
StringName getter = *reinterpret_cast<const StringName *>(p_getter);
String property_name = *reinterpret_cast<const StringName *>(p_info->name);
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property '" + property_name + "' for unexisting class '" + class_name + "'.");
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to register extension class property '%s' for unexisting class '%s'.", property_name, class_name));
#ifdef TOOLS_ENABLED
// If the extension is still marked as reloading, that means it failed to register again.
@ -661,7 +576,7 @@ void GDExtension::_register_extension_class_property_group(GDExtensionClassLibra
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
String group_name = *reinterpret_cast<const String *>(p_group_name);
String prefix = *reinterpret_cast<const String *>(p_prefix);
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property group '" + group_name + "' for unexisting class '" + class_name + "'.");
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to register extension class property group '%s' for unexisting class '%s'.", group_name, class_name));
#ifdef TOOLS_ENABLED
// If the extension is still marked as reloading, that means it failed to register again.
@ -680,7 +595,7 @@ void GDExtension::_register_extension_class_property_subgroup(GDExtensionClassLi
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
String subgroup_name = *reinterpret_cast<const String *>(p_subgroup_name);
String prefix = *reinterpret_cast<const String *>(p_prefix);
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property subgroup '" + subgroup_name + "' for unexisting class '" + class_name + "'.");
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to register extension class property subgroup '%s' for unexisting class '%s'.", subgroup_name, class_name));
#ifdef TOOLS_ENABLED
// If the extension is still marked as reloading, that means it failed to register again.
@ -698,7 +613,7 @@ void GDExtension::_register_extension_class_signal(GDExtensionClassLibraryPtr p_
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
StringName signal_name = *reinterpret_cast<const StringName *>(p_signal_name);
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class signal '" + signal_name + "' for unexisting class '" + class_name + "'.");
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to register extension class signal '%s' for unexisting class '%s'.", signal_name, class_name));
#ifdef TOOLS_ENABLED
// If the extension is still marked as reloading, that means it failed to register again.
@ -721,7 +636,7 @@ void GDExtension::_unregister_extension_class(GDExtensionClassLibraryPtr p_libra
GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to unregister unexisting extension class '" + class_name + "'.");
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to unregister unexisting extension class '%s'.", class_name));
Extension *ext = &self->extension_classes[class_name];
#ifdef TOOLS_ENABLED
@ -729,7 +644,7 @@ void GDExtension::_unregister_extension_class(GDExtensionClassLibraryPtr p_libra
self->_clear_extension(ext);
}
#endif
ERR_FAIL_COND_MSG(ext->gdextension.children.size(), "Attempt to unregister class '" + class_name + "' while other extension classes inherit from it.");
ERR_FAIL_COND_MSG(ext->gdextension.children.size(), vformat("Attempt to unregister class '%s' while other extension classes inherit from it.", class_name));
#ifdef TOOLS_ENABLED
ClassDB::unregister_extension_class(class_name, !ext->is_reloading);
@ -755,80 +670,54 @@ void GDExtension::_unregister_extension_class(GDExtensionClassLibraryPtr p_libra
void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExtensionUninitializedStringPtr r_path) {
GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
memnew_placement(r_path, String(self->library_path));
Ref<GDExtensionLibraryLoader> library_loader = self->loader;
String library_path;
if (library_loader.is_valid()) {
library_path = library_loader->library_path;
}
memnew_placement(r_path, String(library_path));
}
HashMap<StringName, GDExtensionInterfaceFunctionPtr> GDExtension::gdextension_interface_functions;
void GDExtension::register_interface_function(const StringName &p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer) {
ERR_FAIL_COND_MSG(gdextension_interface_functions.has(p_function_name), "Attempt to register interface function '" + p_function_name + "', which appears to be already registered.");
ERR_FAIL_COND_MSG(gdextension_interface_functions.has(p_function_name), vformat("Attempt to register interface function '%s', which appears to be already registered.", p_function_name));
gdextension_interface_functions.insert(p_function_name, p_function_pointer);
}
GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(const StringName &p_function_name) {
GDExtensionInterfaceFunctionPtr *function = gdextension_interface_functions.getptr(p_function_name);
ERR_FAIL_NULL_V_MSG(function, nullptr, "Attempt to get non-existent interface function: " + String(p_function_name) + ".");
ERR_FAIL_NULL_V_MSG(function, nullptr, vformat("Attempt to get non-existent interface function: '%s'.", String(p_function_name)));
return *function;
}
Error GDExtension::open_library(const String &p_path, const String &p_entry_symbol, Vector<SharedObject> *p_dependencies) {
String abs_path = ProjectSettings::get_singleton()->globalize_path(p_path);
Error GDExtension::open_library(const String &p_path, const Ref<GDExtensionLoader> &p_loader) {
ERR_FAIL_COND_V_MSG(p_loader.is_null(), FAILED, "Can't open GDExtension without a loader.");
loader = p_loader;
Vector<String> abs_dependencies_paths;
if (p_dependencies != nullptr && !p_dependencies->is_empty()) {
for (const SharedObject &dependency : *p_dependencies) {
abs_dependencies_paths.push_back(ProjectSettings::get_singleton()->globalize_path(dependency.path));
}
}
Error err = loader->open_library(p_path);
String actual_lib_path;
OS::GDExtensionData data = {
true, // also_set_library_path
&actual_lib_path, // r_resolved_path
Engine::get_singleton()->is_editor_hint(), // generate_temp_files
&abs_dependencies_paths, // library_dependencies
};
Error err = OS::get_singleton()->open_dynamic_library(abs_path, library, &data);
ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, vformat("GDExtension dynamic library not found: '%s'.", p_path));
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Can't open GDExtension dynamic library: '%s'.", p_path));
if (actual_lib_path.get_file() != abs_path.get_file()) {
// If temporary files are generated, let's change the library path to point at the original,
// because that's what we want to check to see if it's changed.
library_path = actual_lib_path.get_base_dir().path_join(p_path.get_file());
} else {
library_path = actual_lib_path;
}
ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, "GDExtension dynamic library not found: " + abs_path);
ERR_FAIL_COND_V_MSG(err != OK, err, "Can't open GDExtension dynamic library: " + abs_path);
void *entry_funcptr = nullptr;
err = OS::get_singleton()->get_dynamic_library_symbol_handle(library, p_entry_symbol, entry_funcptr, false);
err = loader->initialize(&gdextension_get_proc_address, this, &initialization);
if (err != OK) {
ERR_PRINT("GDExtension entry point '" + p_entry_symbol + "' not found in library " + abs_path);
OS::get_singleton()->close_dynamic_library(library);
// Errors already logged in initialize().
loader->close_library();
return err;
}
GDExtensionInitializationFunction initialization_function = (GDExtensionInitializationFunction)entry_funcptr;
GDExtensionBool ret = initialization_function(&gdextension_get_proc_address, this, &initialization);
level_initialized = -1;
if (ret) {
level_initialized = -1;
return OK;
} else {
ERR_PRINT("GDExtension initialization function '" + p_entry_symbol + "' returned an error.");
OS::get_singleton()->close_dynamic_library(library);
return FAILED;
}
return OK;
}
void GDExtension::close_library() {
ERR_FAIL_NULL(library);
OS::get_singleton()->close_dynamic_library(library);
ERR_FAIL_COND(!is_library_open());
loader->close_library();
library = nullptr;
class_icon_paths.clear();
#ifdef TOOLS_ENABLED
@ -837,16 +726,16 @@ void GDExtension::close_library() {
}
bool GDExtension::is_library_open() const {
return library != nullptr;
return loader.is_valid() && loader->is_library_open();
}
GDExtension::InitializationLevel GDExtension::get_minimum_library_initialization_level() const {
ERR_FAIL_NULL_V(library, INITIALIZATION_LEVEL_CORE);
ERR_FAIL_COND_V(!is_library_open(), INITIALIZATION_LEVEL_CORE);
return InitializationLevel(initialization.minimum_initialization_level);
}
void GDExtension::initialize_library(InitializationLevel p_level) {
ERR_FAIL_NULL(library);
ERR_FAIL_COND(!is_library_open());
ERR_FAIL_COND_MSG(p_level <= int32_t(level_initialized), vformat("Level '%d' must be higher than the current level '%d'", p_level, level_initialized));
level_initialized = int32_t(p_level);
@ -856,7 +745,7 @@ void GDExtension::initialize_library(InitializationLevel p_level) {
initialization.initialize(initialization.userdata, GDExtensionInitializationLevel(p_level));
}
void GDExtension::deinitialize_library(InitializationLevel p_level) {
ERR_FAIL_NULL(library);
ERR_FAIL_COND(!is_library_open());
ERR_FAIL_COND(p_level > int32_t(level_initialized));
level_initialized = int32_t(p_level) - 1;
@ -880,7 +769,7 @@ GDExtension::GDExtension() {
}
GDExtension::~GDExtension() {
if (library != nullptr) {
if (is_library_open()) {
close_library();
}
#ifdef TOOLS_ENABLED
@ -897,8 +786,9 @@ void GDExtension::initialize_gdextensions() {
#ifndef DISABLE_DEPRECATED
register_interface_function("classdb_register_extension_class", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class);
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);
#endif // DISABLE_DEPRECATED
register_interface_function("classdb_register_extension_class4", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class4);
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);
@ -918,142 +808,15 @@ void GDExtension::finalize_gdextensions() {
Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path, Ref<GDExtension> &p_extension) {
ERR_FAIL_COND_V_MSG(p_extension.is_valid() && p_extension->is_library_open(), ERR_ALREADY_IN_USE, "Cannot load GDExtension resource into already opened library.");
Ref<ConfigFile> config;
config.instantiate();
GDExtensionManager *extension_manager = GDExtensionManager::get_singleton();
Error err = config->load(p_path);
if (err != OK) {
ERR_PRINT("Error loading GDExtension configuration file: " + p_path);
return err;
}
if (!config->has_section_key("configuration", "entry_symbol")) {
ERR_PRINT("GDExtension configuration file must contain a \"configuration/entry_symbol\" key: " + p_path);
return ERR_INVALID_DATA;
}
String entry_symbol = config->get_value("configuration", "entry_symbol");
uint32_t compatibility_minimum[3] = { 0, 0, 0 };
if (config->has_section_key("configuration", "compatibility_minimum")) {
String compat_string = config->get_value("configuration", "compatibility_minimum");
Vector<int> parts = compat_string.split_ints(".");
for (int i = 0; i < parts.size(); i++) {
if (i >= 3) {
break;
}
if (parts[i] >= 0) {
compatibility_minimum[i] = parts[i];
}
}
} else {
ERR_PRINT("GDExtension configuration file must contain a \"configuration/compatibility_minimum\" key: " + p_path);
return ERR_INVALID_DATA;
}
if (compatibility_minimum[0] < 4 || (compatibility_minimum[0] == 4 && compatibility_minimum[1] == 0)) {
ERR_PRINT(vformat("GDExtension's compatibility_minimum (%d.%d.%d) must be at least 4.1.0: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
return ERR_INVALID_DATA;
}
bool compatible = true;
// Check version lexicographically.
if (VERSION_MAJOR != compatibility_minimum[0]) {
compatible = VERSION_MAJOR > compatibility_minimum[0];
} else if (VERSION_MINOR != compatibility_minimum[1]) {
compatible = VERSION_MINOR > compatibility_minimum[1];
} else {
compatible = VERSION_PATCH >= compatibility_minimum[2];
}
if (!compatible) {
ERR_PRINT(vformat("GDExtension only compatible with Godot version %d.%d.%d or later: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], 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()) {
const String os_arch = OS::get_singleton()->get_name().to_lower() + "." + Engine::get_singleton()->get_architecture_name();
ERR_PRINT(vformat("No GDExtension library found for current OS and architecture (%s) in configuration file: %s", os_arch, p_path));
return ERR_FILE_NOT_FOUND;
}
bool is_static_library = library_path.ends_with(".a") || library_path.ends_with(".xcframework");
if (!library_path.is_resource_file() && !library_path.is_absolute_path()) {
library_path = p_path.get_base_dir().path_join(library_path);
}
if (p_extension.is_null()) {
p_extension.instantiate();
}
#ifdef TOOLS_ENABLED
p_extension->set_reloadable(config->get_value("configuration", "reloadable", false) && Engine::get_singleton()->is_extension_reloading_enabled());
p_extension->update_last_modified_time(
FileAccess::get_modified_time(p_path),
FileAccess::get_modified_time(library_path));
#endif
Vector<SharedObject> library_dependencies = GDExtension::find_extension_dependencies(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); });
err = p_extension->open_library(is_static_library ? String() : library_path, entry_symbol, &library_dependencies);
if (err != OK) {
// Unreference the extension so that this loading can be considered a failure.
p_extension.unref();
// Errors already logged in open_library()
return err;
}
// Handle icons if any are specified.
if (config->has_section("icons")) {
List<String> keys;
config->get_section_keys("icons", &keys);
for (const String &key : keys) {
String icon_path = config->get_value("icons", key);
if (icon_path.is_relative_path()) {
icon_path = p_path.get_base_dir().path_join(icon_path);
}
p_extension->class_icon_paths[key] = icon_path;
}
GDExtensionManager::LoadStatus status = extension_manager->load_extension(p_path);
if (status != GDExtensionManager::LOAD_STATUS_OK && status != GDExtensionManager::LOAD_STATUS_ALREADY_LOADED) {
// Errors already logged in load_extension().
return FAILED;
}
p_extension = extension_manager->get_extension(p_path);
return OK;
}
@ -1094,16 +857,7 @@ String GDExtensionResourceLoader::get_resource_type(const String &p_path) const
#ifdef TOOLS_ENABLED
bool GDExtension::has_library_changed() const {
// Check only that the last modified time is different (rather than checking
// that it's newer) since some OS's (namely Windows) will preserve the modified
// time by default when copying files.
if (FileAccess::get_modified_time(get_path()) != resource_last_modified_time) {
return true;
}
if (FileAccess::get_modified_time(library_path) != library_last_modified_time) {
return true;
}
return false;
return loader->has_library_changed();
}
void GDExtension::prepare_reload() {

View file

@ -31,13 +31,11 @@
#ifndef GDEXTENSION_H
#define GDEXTENSION_H
#include <functional>
#include "core/extension/gdextension_interface.h"
#include "core/extension/gdextension_loader.h"
#include "core/io/config_file.h"
#include "core/io/resource_loader.h"
#include "core/object/ref_counted.h"
#include "core/os/shared_object.h"
class GDExtensionMethodBind;
@ -46,8 +44,8 @@ class GDExtension : public Resource {
friend class GDExtensionManager;
void *library = nullptr; // pointer if valid,
String library_path;
Ref<GDExtensionLoader> loader;
bool reloadable = false;
struct Extension {
@ -72,15 +70,20 @@ class GDExtension : public Resource {
#ifndef DISABLE_DEPRECATED
GDExtensionClassNotification notification_func = nullptr;
GDExtensionClassFreePropertyList free_property_list_func = nullptr;
GDExtensionClassCreateInstance create_instance_func = nullptr;
GDExtensionClassGetRID get_rid_func = nullptr;
GDExtensionClassGetVirtual get_virtual_func = nullptr;
GDExtensionClassGetVirtualCallData get_virtual_call_data_func = nullptr;
#endif // DISABLE_DEPRECATED
};
#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);
static void _register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs);
#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);
#endif // DISABLE_DEPRECATED
static void _register_extension_class4(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs);
static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *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);
@ -96,8 +99,6 @@ class GDExtension : public Resource {
int32_t level_initialized = -1;
#ifdef TOOLS_ENABLED
uint64_t resource_last_modified_time = 0;
uint64_t library_last_modified_time = 0;
bool is_reloading = false;
Vector<GDExtensionMethodBind *> invalid_methods;
Vector<ObjectID> instance_bindings;
@ -124,11 +125,12 @@ public:
virtual bool editor_can_reload_from_file() override { return false; } // Reloading is handled in a special way.
static String get_extension_list_config_file();
static String find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags = nullptr);
static Vector<SharedObject> find_extension_dependencies(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature);
Error open_library(const String &p_path, const String &p_entry_symbol, Vector<SharedObject> *p_dependencies = nullptr);
const Ref<GDExtensionLoader> get_loader() const { return loader; }
Error open_library(const String &p_path, const Ref<GDExtensionLoader> &p_loader);
void close_library();
bool is_library_open() const;
enum InitializationLevel {
INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE,
@ -146,17 +148,11 @@ protected:
#endif
public:
bool is_library_open() const;
#ifdef TOOLS_ENABLED
bool is_reloadable() const { return reloadable; }
void set_reloadable(bool p_reloadable) { reloadable = p_reloadable; }
bool has_library_changed() const;
void update_last_modified_time(uint64_t p_resource_last_modified_time, uint64_t p_library_last_modified_time) {
resource_last_modified_time = p_resource_last_modified_time;
library_last_modified_time = p_library_last_modified_time;
}
void track_instance_binding(Object *p_object);
void untrack_instance_binding(Object *p_object);

View file

@ -32,8 +32,9 @@
#include "core/config/engine.h"
#include "core/extension/gdextension.h"
#include "core/extension/gdextension_compat_hashes.h"
#include "core/extension/gdextension_special_compat_hashes.h"
#include "core/io/file_access.h"
#include "core/io/image.h"
#include "core/io/xml_parser.h"
#include "core/object/class_db.h"
#include "core/object/script_language_extension.h"
@ -507,6 +508,14 @@ static GDExtensionBool gdextension_variant_has_key(GDExtensionConstVariantPtr p_
return ret;
}
static GDObjectInstanceID gdextension_variant_get_object_instance_id(GDExtensionConstVariantPtr p_self) {
const Variant *self = (const Variant *)p_self;
if (likely(self->get_type() == Variant::OBJECT)) {
return self->operator ObjectID();
}
return 0;
}
static void gdextension_variant_get_type_name(GDExtensionVariantType p_type, GDExtensionUninitializedVariantPtr r_ret) {
String name = Variant::get_type_name((Variant::Type)p_type);
memnew_placement(r_ret, String(name));
@ -691,6 +700,91 @@ static GDExtensionTypeFromVariantConstructorFunc gdextension_get_variant_to_type
ERR_FAIL_V_MSG(nullptr, "Getting Variant conversion function with invalid type");
}
static GDExtensionVariantGetInternalPtrFunc gdextension_variant_get_ptr_internal_getter(GDExtensionVariantType p_type) {
switch (p_type) {
case GDEXTENSION_VARIANT_TYPE_BOOL:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<bool *(*)(Variant *)>(VariantInternal::get_bool));
case GDEXTENSION_VARIANT_TYPE_INT:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<int64_t *(*)(Variant *)>(VariantInternal::get_int));
case GDEXTENSION_VARIANT_TYPE_FLOAT:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<double *(*)(Variant *)>(VariantInternal::get_float));
case GDEXTENSION_VARIANT_TYPE_STRING:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<String *(*)(Variant *)>(VariantInternal::get_string));
case GDEXTENSION_VARIANT_TYPE_VECTOR2:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Vector2 *(*)(Variant *)>(VariantInternal::get_vector2));
case GDEXTENSION_VARIANT_TYPE_VECTOR2I:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Vector2i *(*)(Variant *)>(VariantInternal::get_vector2i));
case GDEXTENSION_VARIANT_TYPE_RECT2:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Rect2 *(*)(Variant *)>(VariantInternal::get_rect2));
case GDEXTENSION_VARIANT_TYPE_RECT2I:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Rect2i *(*)(Variant *)>(VariantInternal::get_rect2i));
case GDEXTENSION_VARIANT_TYPE_VECTOR3:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Vector3 *(*)(Variant *)>(VariantInternal::get_vector3));
case GDEXTENSION_VARIANT_TYPE_VECTOR3I:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Vector3i *(*)(Variant *)>(VariantInternal::get_vector3i));
case GDEXTENSION_VARIANT_TYPE_TRANSFORM2D:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Transform2D *(*)(Variant *)>(VariantInternal::get_transform2d));
case GDEXTENSION_VARIANT_TYPE_VECTOR4:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Vector4 *(*)(Variant *)>(VariantInternal::get_vector4));
case GDEXTENSION_VARIANT_TYPE_VECTOR4I:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Vector4i *(*)(Variant *)>(VariantInternal::get_vector4i));
case GDEXTENSION_VARIANT_TYPE_PLANE:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Plane *(*)(Variant *)>(VariantInternal::get_plane));
case GDEXTENSION_VARIANT_TYPE_QUATERNION:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Quaternion *(*)(Variant *)>(VariantInternal::get_quaternion));
case GDEXTENSION_VARIANT_TYPE_AABB:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<AABB *(*)(Variant *)>(VariantInternal::get_aabb));
case GDEXTENSION_VARIANT_TYPE_BASIS:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Basis *(*)(Variant *)>(VariantInternal::get_basis));
case GDEXTENSION_VARIANT_TYPE_TRANSFORM3D:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Transform3D *(*)(Variant *)>(VariantInternal::get_transform));
case GDEXTENSION_VARIANT_TYPE_PROJECTION:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Projection *(*)(Variant *)>(VariantInternal::get_projection));
case GDEXTENSION_VARIANT_TYPE_COLOR:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Color *(*)(Variant *)>(VariantInternal::get_color));
case GDEXTENSION_VARIANT_TYPE_STRING_NAME:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<StringName *(*)(Variant *)>(VariantInternal::get_string_name));
case GDEXTENSION_VARIANT_TYPE_NODE_PATH:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<NodePath *(*)(Variant *)>(VariantInternal::get_node_path));
case GDEXTENSION_VARIANT_TYPE_RID:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<RID *(*)(Variant *)>(VariantInternal::get_rid));
case GDEXTENSION_VARIANT_TYPE_OBJECT:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Object **(*)(Variant *)>(VariantInternal::get_object));
case GDEXTENSION_VARIANT_TYPE_CALLABLE:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Callable *(*)(Variant *)>(VariantInternal::get_callable));
case GDEXTENSION_VARIANT_TYPE_SIGNAL:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Signal *(*)(Variant *)>(VariantInternal::get_signal));
case GDEXTENSION_VARIANT_TYPE_DICTIONARY:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Dictionary *(*)(Variant *)>(VariantInternal::get_dictionary));
case GDEXTENSION_VARIANT_TYPE_ARRAY:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Array *(*)(Variant *)>(VariantInternal::get_array));
case GDEXTENSION_VARIANT_TYPE_PACKED_BYTE_ARRAY:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedByteArray *(*)(Variant *)>(VariantInternal::get_byte_array));
case GDEXTENSION_VARIANT_TYPE_PACKED_INT32_ARRAY:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedInt32Array *(*)(Variant *)>(VariantInternal::get_int32_array));
case GDEXTENSION_VARIANT_TYPE_PACKED_INT64_ARRAY:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedInt64Array *(*)(Variant *)>(VariantInternal::get_int64_array));
case GDEXTENSION_VARIANT_TYPE_PACKED_FLOAT32_ARRAY:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedFloat32Array *(*)(Variant *)>(VariantInternal::get_float32_array));
case GDEXTENSION_VARIANT_TYPE_PACKED_FLOAT64_ARRAY:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedFloat64Array *(*)(Variant *)>(VariantInternal::get_float64_array));
case GDEXTENSION_VARIANT_TYPE_PACKED_STRING_ARRAY:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedStringArray *(*)(Variant *)>(VariantInternal::get_string_array));
case GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR2_ARRAY:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedVector2Array *(*)(Variant *)>(VariantInternal::get_vector2_array));
case GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR3_ARRAY:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedVector3Array *(*)(Variant *)>(VariantInternal::get_vector3_array));
case GDEXTENSION_VARIANT_TYPE_PACKED_COLOR_ARRAY:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedColorArray *(*)(Variant *)>(VariantInternal::get_color_array));
case GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR4_ARRAY:
return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedVector4Array *(*)(Variant *)>(VariantInternal::get_vector4_array));
case GDEXTENSION_VARIANT_TYPE_NIL:
case GDEXTENSION_VARIANT_TYPE_VARIANT_MAX:
ERR_FAIL_V_MSG(nullptr, "Getting Variant get internal pointer function with invalid type.");
}
ERR_FAIL_V_MSG(nullptr, "Getting Variant get internal pointer function with invalid type.");
}
// ptrcalls
static GDExtensionPtrOperatorEvaluator gdextension_variant_get_ptr_operator_evaluator(GDExtensionVariantOperator p_operator, GDExtensionVariantType p_type_a, GDExtensionVariantType p_type_b) {
return (GDExtensionPtrOperatorEvaluator)Variant::get_ptr_operator_evaluator(Variant::Operator(p_operator), Variant::Type(p_type_a), Variant::Type(p_type_b));
@ -1199,6 +1293,15 @@ static GDExtensionVariantPtr gdextension_dictionary_operator_index_const(GDExten
return (GDExtensionVariantPtr)&self->operator[](*(const Variant *)p_key);
}
void gdextension_dictionary_set_typed(GDExtensionTypePtr p_self, GDExtensionVariantType p_key_type, GDExtensionConstStringNamePtr p_key_class_name, GDExtensionConstVariantPtr p_key_script, GDExtensionVariantType p_value_type, GDExtensionConstStringNamePtr p_value_class_name, GDExtensionConstVariantPtr p_value_script) {
Dictionary *self = reinterpret_cast<Dictionary *>(p_self);
const StringName *key_class_name = reinterpret_cast<const StringName *>(p_key_class_name);
const Variant *key_script = reinterpret_cast<const Variant *>(p_key_script);
const StringName *value_class_name = reinterpret_cast<const StringName *>(p_value_class_name);
const Variant *value_script = reinterpret_cast<const Variant *>(p_value_script);
self->set_typed((uint32_t)p_key_type, *key_class_name, *key_script, (uint32_t)p_value_type, *value_class_name, *value_script);
}
/* OBJECT API */
static void gdextension_object_method_bind_call(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error) {
@ -1299,7 +1402,7 @@ static void gdextension_object_call_script_method(GDExtensionObjectPtr p_object,
const StringName method = *reinterpret_cast<const StringName *>(p_method);
const Variant **args = (const Variant **)p_args;
Callable::CallError error;
Callable::CallError error; // TODO: Check `error`?
memnew_placement(r_return, Variant);
*(Variant *)r_return = o->callp(method, args, p_argument_count, error);
@ -1501,24 +1604,31 @@ static GDExtensionMethodBindPtr gdextension_classdb_get_method_bind(GDExtensionC
// If lookup failed, see if this is one of the broken hashes from issue #81386.
if (!mb && exists) {
uint32_t mapped_hash;
if (GDExtensionCompatHashes::lookup_current_hash(classname, methodname, p_hash, &mapped_hash)) {
if (GDExtensionSpecialCompatHashes::lookup_current_hash(classname, methodname, p_hash, &mapped_hash)) {
mb = ClassDB::get_method_with_compatibility(classname, methodname, mapped_hash, &exists);
}
}
#endif
if (!mb && exists) {
ERR_PRINT("Method '" + classname + "." + methodname + "' has changed and no compatibility fallback has been provided. Please open an issue.");
ERR_PRINT(vformat("Method '%s.%s' has changed and no compatibility fallback has been provided. Please open an issue.", classname, methodname));
return nullptr;
}
ERR_FAIL_NULL_V(mb, nullptr);
return (GDExtensionMethodBindPtr)mb;
}
#ifndef DISABLE_DEPRECATED
static GDExtensionObjectPtr gdextension_classdb_construct_object(GDExtensionConstStringNamePtr p_classname) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
return (GDExtensionObjectPtr)ClassDB::instantiate_no_placeholders(classname);
}
#endif
static GDExtensionObjectPtr gdextension_classdb_construct_object2(GDExtensionConstStringNamePtr p_classname) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
return (GDExtensionObjectPtr)ClassDB::instantiate_without_postinitialization(classname);
}
static void *gdextension_classdb_get_class_tag(GDExtensionConstStringNamePtr p_classname) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
@ -1594,11 +1704,13 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(variant_has_method);
REGISTER_INTERFACE_FUNC(variant_has_member);
REGISTER_INTERFACE_FUNC(variant_has_key);
REGISTER_INTERFACE_FUNC(variant_get_object_instance_id);
REGISTER_INTERFACE_FUNC(variant_get_type_name);
REGISTER_INTERFACE_FUNC(variant_can_convert);
REGISTER_INTERFACE_FUNC(variant_can_convert_strict);
REGISTER_INTERFACE_FUNC(get_variant_from_type_constructor);
REGISTER_INTERFACE_FUNC(get_variant_to_type_constructor);
REGISTER_INTERFACE_FUNC(variant_get_ptr_internal_getter);
REGISTER_INTERFACE_FUNC(variant_get_ptr_operator_evaluator);
REGISTER_INTERFACE_FUNC(variant_get_ptr_builtin_method);
REGISTER_INTERFACE_FUNC(variant_get_ptr_constructor);
@ -1672,6 +1784,7 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(array_set_typed);
REGISTER_INTERFACE_FUNC(dictionary_operator_index);
REGISTER_INTERFACE_FUNC(dictionary_operator_index_const);
REGISTER_INTERFACE_FUNC(dictionary_set_typed);
REGISTER_INTERFACE_FUNC(object_method_bind_call);
REGISTER_INTERFACE_FUNC(object_method_bind_ptrcall);
REGISTER_INTERFACE_FUNC(object_destroy);
@ -1701,7 +1814,10 @@ void gdextension_setup_interface() {
#endif // DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(callable_custom_create2);
REGISTER_INTERFACE_FUNC(callable_custom_get_userdata);
#ifndef DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(classdb_construct_object);
#endif // DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(classdb_construct_object2);
REGISTER_INTERFACE_FUNC(classdb_get_method_bind);
REGISTER_INTERFACE_FUNC(classdb_get_class_tag);
REGISTER_INTERFACE_FUNC(editor_add_plugin);

View file

@ -198,6 +198,7 @@ typedef struct {
typedef void (*GDExtensionVariantFromTypeConstructorFunc)(GDExtensionUninitializedVariantPtr, GDExtensionTypePtr);
typedef void (*GDExtensionTypeFromVariantConstructorFunc)(GDExtensionUninitializedTypePtr, GDExtensionVariantPtr);
typedef void *(*GDExtensionVariantGetInternalPtrFunc)(GDExtensionVariantPtr);
typedef void (*GDExtensionPtrOperatorEvaluator)(GDExtensionConstTypePtr p_left, GDExtensionConstTypePtr p_right, GDExtensionTypePtr r_result);
typedef void (*GDExtensionPtrBuiltInMethod)(GDExtensionTypePtr p_base, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_return, int p_argument_count);
typedef void (*GDExtensionPtrConstructor)(GDExtensionUninitializedTypePtr p_base, const GDExtensionConstTypePtr *p_args);
@ -268,10 +269,13 @@ typedef void (*GDExtensionClassReference)(GDExtensionClassInstancePtr p_instance
typedef void (*GDExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance);
typedef void (*GDExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance)(void *p_class_userdata);
typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance2)(void *p_class_userdata, GDExtensionBool p_notify_postinitialize);
typedef void (*GDExtensionClassFreeInstance)(void *p_class_userdata, GDExtensionClassInstancePtr p_instance);
typedef GDExtensionClassInstancePtr (*GDExtensionClassRecreateInstance)(void *p_class_userdata, GDExtensionObjectPtr p_object);
typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name);
typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual2)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name, uint32_t p_hash);
typedef void *(*GDExtensionClassGetVirtualCallData)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name);
typedef void *(*GDExtensionClassGetVirtualCallData2)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name, uint32_t p_hash);
typedef void (*GDExtensionClassCallVirtualWithData)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, void *p_virtual_call_userdata, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
typedef struct {
@ -292,7 +296,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 GDExtensionClassCreationInfo3 instead.
} GDExtensionClassCreationInfo; // Deprecated. Use GDExtensionClassCreationInfo4 instead.
typedef struct {
GDExtensionBool is_virtual;
@ -325,7 +329,7 @@ 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; // Deprecated. Use GDExtensionClassCreationInfo3 instead.
} GDExtensionClassCreationInfo2; // Deprecated. Use GDExtensionClassCreationInfo4 instead.
typedef struct {
GDExtensionBool is_virtual;
@ -359,7 +363,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.
} GDExtensionClassCreationInfo3;
} GDExtensionClassCreationInfo3; // Deprecated. Use GDExtensionClassCreationInfo4 instead.
typedef struct {
GDExtensionBool is_virtual;
GDExtensionBool is_abstract;
GDExtensionBool is_exposed;
GDExtensionBool is_runtime;
GDExtensionConstStringPtr icon_path;
GDExtensionClassSet set_func;
GDExtensionClassGet get_func;
GDExtensionClassGetPropertyList get_property_list_func;
GDExtensionClassFreePropertyList2 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;
GDExtensionClassCreateInstance2 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.
GDExtensionClassGetVirtual2 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`.
GDExtensionClassGetVirtualCallData2 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;
void *class_userdata; // Per-class user data, later accessible in instance bindings.
} GDExtensionClassCreationInfo4;
typedef void *GDExtensionClassLibraryPtr;
@ -386,7 +424,9 @@ typedef enum {
GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT32,
GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT64,
GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_FLOAT,
GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE
GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE,
GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_CHAR16,
GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_CHAR32,
} GDExtensionClassMethodArgumentMetadata;
typedef void (*GDExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
@ -805,7 +845,7 @@ typedef void (*GDExtensionInterfaceMemFree)(void *p_ptr);
*
* Logs an error to Godot's built-in debugger and to the OS terminal.
*
* @param p_description The code trigging the error.
* @param p_description The code triggering the error.
* @param p_function The function name where the error occurred.
* @param p_file The file where the error occurred.
* @param p_line The line where the error occurred.
@ -819,7 +859,7 @@ typedef void (*GDExtensionInterfacePrintError)(const char *p_description, const
*
* Logs an error with a message to Godot's built-in debugger and to the OS terminal.
*
* @param p_description The code trigging the error.
* @param p_description The code triggering the error.
* @param p_message The message to show along with the error.
* @param p_function The function name where the error occurred.
* @param p_file The file where the error occurred.
@ -834,7 +874,7 @@ typedef void (*GDExtensionInterfacePrintErrorWithMessage)(const char *p_descript
*
* Logs a warning to Godot's built-in debugger and to the OS terminal.
*
* @param p_description The code trigging the warning.
* @param p_description The code triggering the warning.
* @param p_function The function name where the warning occurred.
* @param p_file The file where the warning occurred.
* @param p_line The line where the warning occurred.
@ -848,7 +888,7 @@ typedef void (*GDExtensionInterfacePrintWarning)(const char *p_description, cons
*
* Logs a warning with a message to Godot's built-in debugger and to the OS terminal.
*
* @param p_description The code trigging the warning.
* @param p_description The code triggering the warning.
* @param p_message The message to show along with the warning.
* @param p_function The function name where the warning occurred.
* @param p_file The file where the warning occurred.
@ -863,7 +903,7 @@ typedef void (*GDExtensionInterfacePrintWarningWithMessage)(const char *p_descri
*
* Logs a script error to Godot's built-in debugger and to the OS terminal.
*
* @param p_description The code trigging the error.
* @param p_description The code triggering the error.
* @param p_function The function name where the error occurred.
* @param p_file The file where the error occurred.
* @param p_line The line where the error occurred.
@ -877,7 +917,7 @@ typedef void (*GDExtensionInterfacePrintScriptError)(const char *p_description,
*
* Logs a script error with a message to Godot's built-in debugger and to the OS terminal.
*
* @param p_description The code trigging the error.
* @param p_description The code triggering the error.
* @param p_message The message to show along with the error.
* @param p_function The function name where the error occurred.
* @param p_file The file where the error occurred.
@ -1271,6 +1311,21 @@ typedef GDExtensionBool (*GDExtensionInterfaceVariantHasMember)(GDExtensionVaria
*/
typedef GDExtensionBool (*GDExtensionInterfaceVariantHasKey)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionBool *r_valid);
/**
* @name variant_get_object_instance_id
* @since 4.4
*
* Gets the object instance ID from a variant of type GDEXTENSION_VARIANT_TYPE_OBJECT.
*
* If the variant isn't of type GDEXTENSION_VARIANT_TYPE_OBJECT, then zero will be returned.
* The instance ID will be returned even if the object is no longer valid - use `object_get_instance_by_id()` to check if the object is still valid.
*
* @param p_self A pointer to the Variant.
*
* @return The instance ID for the contained object.
*/
typedef GDObjectInstanceID (*GDExtensionInterfaceVariantGetObjectInstanceId)(GDExtensionConstVariantPtr p_self);
/**
* @name variant_get_type_name
* @since 4.1
@ -1332,6 +1387,23 @@ typedef GDExtensionVariantFromTypeConstructorFunc (*GDExtensionInterfaceGetVaria
*/
typedef GDExtensionTypeFromVariantConstructorFunc (*GDExtensionInterfaceGetVariantToTypeConstructor)(GDExtensionVariantType p_type);
/**
* @name variant_get_ptr_internal_getter
* @since 4.4
*
* Provides a function pointer for retrieving a pointer to a variant's internal value.
* Access to a variant's internal value can be used to modify it in-place, or to retrieve its value without the overhead of variant conversion functions.
* It is recommended to cache the getter for all variant types in a function table to avoid retrieval overhead upon use.
*
* @note Each function assumes the variant's type has already been determined and matches the function.
* Invoking the function with a variant of a mismatched type has undefined behavior, and may lead to a segmentation fault.
*
* @param p_type The Variant type.
*
* @return A pointer to a type-specific function that returns a pointer to the internal value of a variant. Check the implementation of this function (gdextension_variant_get_ptr_internal_getter) for pointee type info of each variant type.
*/
typedef GDExtensionVariantGetInternalPtrFunc (*GDExtensionInterfaceGetVariantGetInternalPtrFunc)(GDExtensionVariantType p_type);
/**
* @name variant_get_ptr_operator_evaluator
* @since 4.1
@ -2337,6 +2409,22 @@ typedef GDExtensionVariantPtr (*GDExtensionInterfaceDictionaryOperatorIndex)(GDE
*/
typedef GDExtensionVariantPtr (*GDExtensionInterfaceDictionaryOperatorIndexConst)(GDExtensionConstTypePtr p_self, GDExtensionConstVariantPtr p_key);
/**
* @name dictionary_set_typed
* @since 4.4
*
* Makes a Dictionary into a typed Dictionary.
*
* @param p_self A pointer to the Dictionary.
* @param p_key_type The type of Variant the Dictionary key will store.
* @param p_key_class_name A pointer to a StringName with the name of the object (if p_key_type is GDEXTENSION_VARIANT_TYPE_OBJECT).
* @param p_key_script A pointer to a Script object (if p_key_type is GDEXTENSION_VARIANT_TYPE_OBJECT and the base class is extended by a script).
* @param p_value_type The type of Variant the Dictionary value will store.
* @param p_value_class_name A pointer to a StringName with the name of the object (if p_value_type is GDEXTENSION_VARIANT_TYPE_OBJECT).
* @param p_value_script A pointer to a Script object (if p_value_type is GDEXTENSION_VARIANT_TYPE_OBJECT and the base class is extended by a script).
*/
typedef void (*GDExtensionInterfaceDictionarySetTyped)(GDExtensionTypePtr p_self, GDExtensionVariantType p_key_type, GDExtensionConstStringNamePtr p_key_class_name, GDExtensionConstVariantPtr p_key_script, GDExtensionVariantType p_value_type, GDExtensionConstStringNamePtr p_value_class_name, GDExtensionConstVariantPtr p_value_script);
/* INTERFACE: Object */
/**
@ -2680,6 +2768,7 @@ typedef void *(*GDExtensionInterfaceCallableCustomGetUserData)(GDExtensionConstT
/**
* @name classdb_construct_object
* @since 4.1
* @deprecated in Godot 4.4. Use `classdb_construct_object2` instead.
*
* Constructs an Object of the requested class.
*
@ -2691,6 +2780,22 @@ typedef void *(*GDExtensionInterfaceCallableCustomGetUserData)(GDExtensionConstT
*/
typedef GDExtensionObjectPtr (*GDExtensionInterfaceClassdbConstructObject)(GDExtensionConstStringNamePtr p_classname);
/**
* @name classdb_construct_object2
* @since 4.4
*
* Constructs an Object of the requested class.
*
* The passed class must be a built-in godot class, or an already-registered extension class. In both cases, object_set_instance() should be called to fully initialize the object.
*
* "NOTIFICATION_POSTINITIALIZE" must be sent after construction.
*
* @param p_classname A pointer to a StringName with the class name.
*
* @return A pointer to the newly created Object.
*/
typedef GDExtensionObjectPtr (*GDExtensionInterfaceClassdbConstructObject2)(GDExtensionConstStringNamePtr p_classname);
/**
* @name classdb_get_method_bind
* @since 4.1
@ -2722,7 +2827,7 @@ typedef void *(*GDExtensionInterfaceClassdbGetClassTag)(GDExtensionConstStringNa
/**
* @name classdb_register_extension_class
* @since 4.1
* @deprecated in Godot 4.2. Use `classdb_register_extension_class3` instead.
* @deprecated in Godot 4.2. Use `classdb_register_extension_class4` instead.
*
* Registers an extension class in the ClassDB.
*
@ -2738,7 +2843,7 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass)(GDExtensionCla
/**
* @name classdb_register_extension_class2
* @since 4.2
* @deprecated in Godot 4.3. Use `classdb_register_extension_class3` instead.
* @deprecated in Godot 4.3. Use `classdb_register_extension_class4` instead.
*
* Registers an extension class in the ClassDB.
*
@ -2754,6 +2859,7 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass2)(GDExtensionCl
/**
* @name classdb_register_extension_class3
* @since 4.3
* @deprecated in Godot 4.4. Use `classdb_register_extension_class4` instead.
*
* Registers an extension class in the ClassDB.
*
@ -2766,6 +2872,21 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass2)(GDExtensionCl
*/
typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass3)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs);
/**
* @name classdb_register_extension_class4
* @since 4.4
*
* 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 (*GDExtensionInterfaceClassdbRegisterExtensionClass4)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs);
/**
* @name classdb_register_extension_class_method
* @since 4.1

View file

@ -0,0 +1,394 @@
/**************************************************************************/
/* gdextension_library_loader.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 "gdextension_library_loader.h"
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
#include "core/version.h"
#include "gdextension.h"
Vector<SharedObject> GDExtensionLibraryLoader::find_extension_dependencies(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature) {
Vector<SharedObject> dependencies_shared_objects;
if (p_config->has_section("dependencies")) {
List<String> config_dependencies;
p_config->get_section_keys("dependencies", &config_dependencies);
for (const String &dependency : config_dependencies) {
Vector<String> dependency_tags = dependency.split(".");
bool all_tags_met = true;
for (int i = 0; i < dependency_tags.size(); i++) {
String tag = dependency_tags[i].strip_edges();
if (!p_has_feature(tag)) {
all_tags_met = false;
break;
}
}
if (all_tags_met) {
Dictionary dependency_value = p_config->get_value("dependencies", dependency);
for (const Variant *key = dependency_value.next(nullptr); key; key = dependency_value.next(key)) {
String dependency_path = *key;
String target_path = dependency_value[*key];
if (dependency_path.is_relative_path()) {
dependency_path = p_path.get_base_dir().path_join(dependency_path);
}
dependencies_shared_objects.push_back(SharedObject(dependency_path, dependency_tags, target_path));
}
break;
}
}
}
return dependencies_shared_objects;
}
String GDExtensionLibraryLoader::find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags) {
// First, check the explicit libraries.
if (p_config->has_section("libraries")) {
List<String> libraries;
p_config->get_section_keys("libraries", &libraries);
// Iterate the libraries, finding the best matching tags.
String best_library_path;
Vector<String> best_library_tags;
for (const String &E : libraries) {
Vector<String> tags = E.split(".");
bool all_tags_met = true;
for (int i = 0; i < tags.size(); i++) {
String tag = tags[i].strip_edges();
if (!p_has_feature(tag)) {
all_tags_met = false;
break;
}
}
if (all_tags_met && tags.size() > best_library_tags.size()) {
best_library_path = p_config->get_value("libraries", E);
best_library_tags = tags;
}
}
if (!best_library_path.is_empty()) {
if (best_library_path.is_relative_path()) {
best_library_path = p_path.get_base_dir().path_join(best_library_path);
}
if (r_tags != nullptr) {
r_tags->append_array(best_library_tags);
}
return best_library_path;
}
}
// Second, try to autodetect.
String autodetect_library_prefix;
if (p_config->has_section_key("configuration", "autodetect_library_prefix")) {
autodetect_library_prefix = p_config->get_value("configuration", "autodetect_library_prefix");
}
if (!autodetect_library_prefix.is_empty()) {
String autodetect_path = autodetect_library_prefix;
if (autodetect_path.is_relative_path()) {
autodetect_path = p_path.get_base_dir().path_join(autodetect_path);
}
// Find the folder and file parts of the prefix.
String folder;
String file_prefix;
if (DirAccess::dir_exists_absolute(autodetect_path)) {
folder = autodetect_path;
} else if (DirAccess::dir_exists_absolute(autodetect_path.get_base_dir())) {
folder = autodetect_path.get_base_dir();
file_prefix = autodetect_path.get_file();
} else {
ERR_FAIL_V_MSG(String(), vformat("Error in extension: %s. Could not find folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
}
// Open the folder.
Ref<DirAccess> dir = DirAccess::open(folder);
ERR_FAIL_COND_V_MSG(dir.is_null(), String(), vformat("Error in extension: %s. Could not open folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
// Iterate the files and check the prefixes, finding the best matching file.
String best_file;
Vector<String> best_file_tags;
dir->list_dir_begin();
String file_name = dir->_get_next();
while (file_name != "") {
if (!dir->current_is_dir() && file_name.begins_with(file_prefix)) {
// Check if the files matches all requested feature tags.
String tags_str = file_name.trim_prefix(file_prefix);
tags_str = tags_str.trim_suffix(tags_str.get_extension());
Vector<String> tags = tags_str.split(".", false);
bool all_tags_met = true;
for (int i = 0; i < tags.size(); i++) {
String tag = tags[i].strip_edges();
if (!p_has_feature(tag)) {
all_tags_met = false;
break;
}
}
// If all tags are found in the feature list, and we found more tags than before, use this file.
if (all_tags_met && tags.size() > best_file_tags.size()) {
best_file_tags = tags;
best_file = file_name;
}
}
file_name = dir->_get_next();
}
if (!best_file.is_empty()) {
String library_path = folder.path_join(best_file);
if (r_tags != nullptr) {
r_tags->append_array(best_file_tags);
}
return library_path;
}
}
return String();
}
Error GDExtensionLibraryLoader::open_library(const String &p_path) {
Error err = parse_gdextension_file(p_path);
if (err != OK) {
return err;
}
String abs_path = ProjectSettings::get_singleton()->globalize_path(library_path);
Vector<String> abs_dependencies_paths;
if (!library_dependencies.is_empty()) {
for (const SharedObject &dependency : library_dependencies) {
abs_dependencies_paths.push_back(ProjectSettings::get_singleton()->globalize_path(dependency.path));
}
}
OS::GDExtensionData data = {
true, // also_set_library_path
&library_path, // r_resolved_path
Engine::get_singleton()->is_editor_hint(), // generate_temp_files
&abs_dependencies_paths, // library_dependencies
};
err = OS::get_singleton()->open_dynamic_library(is_static_library ? String() : abs_path, library, &data);
if (err != OK) {
return err;
}
return OK;
}
Error GDExtensionLibraryLoader::initialize(GDExtensionInterfaceGetProcAddress p_get_proc_address, const Ref<GDExtension> &p_extension, GDExtensionInitialization *r_initialization) {
#ifdef TOOLS_ENABLED
p_extension->set_reloadable(is_reloadable && Engine::get_singleton()->is_extension_reloading_enabled());
#endif
for (const KeyValue<String, String> &icon : class_icon_paths) {
p_extension->class_icon_paths[icon.key] = icon.value;
}
void *entry_funcptr = nullptr;
Error err = OS::get_singleton()->get_dynamic_library_symbol_handle(library, entry_symbol, entry_funcptr, false);
if (err != OK) {
ERR_PRINT(vformat("GDExtension entry point '%s' not found in library %s.", entry_symbol, library_path));
return err;
}
GDExtensionInitializationFunction initialization_function = (GDExtensionInitializationFunction)entry_funcptr;
GDExtensionBool ret = initialization_function(p_get_proc_address, p_extension.ptr(), r_initialization);
if (ret) {
return OK;
} else {
ERR_PRINT(vformat("GDExtension initialization function '%s' returned an error.", entry_symbol));
return FAILED;
}
}
void GDExtensionLibraryLoader::close_library() {
OS::get_singleton()->close_dynamic_library(library);
library = nullptr;
}
bool GDExtensionLibraryLoader::is_library_open() const {
return library != nullptr;
}
bool GDExtensionLibraryLoader::has_library_changed() const {
#ifdef TOOLS_ENABLED
// Check only that the last modified time is different (rather than checking
// that it's newer) since some OS's (namely Windows) will preserve the modified
// time by default when copying files.
if (FileAccess::get_modified_time(resource_path) != resource_last_modified_time) {
return true;
}
if (FileAccess::get_modified_time(library_path) != library_last_modified_time) {
return true;
}
#endif
return false;
}
bool GDExtensionLibraryLoader::library_exists() const {
return FileAccess::exists(resource_path);
}
Error GDExtensionLibraryLoader::parse_gdextension_file(const String &p_path) {
resource_path = p_path;
Ref<ConfigFile> config;
config.instantiate();
Error err = config->load(p_path);
if (err != OK) {
ERR_PRINT(vformat("Error loading GDExtension configuration file: '%s'.", p_path));
return err;
}
if (!config->has_section_key("configuration", "entry_symbol")) {
ERR_PRINT(vformat("GDExtension configuration file must contain a \"configuration/entry_symbol\" key: '%s'.", p_path));
return ERR_INVALID_DATA;
}
entry_symbol = config->get_value("configuration", "entry_symbol");
uint32_t compatibility_minimum[3] = { 0, 0, 0 };
if (config->has_section_key("configuration", "compatibility_minimum")) {
String compat_string = config->get_value("configuration", "compatibility_minimum");
Vector<int> parts = compat_string.split_ints(".");
for (int i = 0; i < parts.size(); i++) {
if (i >= 3) {
break;
}
if (parts[i] >= 0) {
compatibility_minimum[i] = parts[i];
}
}
} else {
ERR_PRINT(vformat("GDExtension configuration file must contain a \"configuration/compatibility_minimum\" key: '%s'.", p_path));
return ERR_INVALID_DATA;
}
if (compatibility_minimum[0] < 4 || (compatibility_minimum[0] == 4 && compatibility_minimum[1] == 0)) {
ERR_PRINT(vformat("GDExtension's compatibility_minimum (%d.%d.%d) must be at least 4.1.0: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
return ERR_INVALID_DATA;
}
bool compatible = true;
// Check version lexicographically.
if (VERSION_MAJOR != compatibility_minimum[0]) {
compatible = VERSION_MAJOR > compatibility_minimum[0];
} else if (VERSION_MINOR != compatibility_minimum[1]) {
compatible = VERSION_MINOR > compatibility_minimum[1];
} else {
compatible = VERSION_PATCH >= compatibility_minimum[2];
}
if (!compatible) {
ERR_PRINT(vformat("GDExtension only compatible with Godot version %d.%d.%d or later: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], 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;
}
}
library_path = find_extension_library(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); });
if (library_path.is_empty()) {
const String os_arch = OS::get_singleton()->get_name().to_lower() + "." + Engine::get_singleton()->get_architecture_name();
ERR_PRINT(vformat("No GDExtension library found for current OS and architecture (%s) in configuration file: %s", os_arch, p_path));
return ERR_FILE_NOT_FOUND;
}
is_static_library = library_path.ends_with(".a") || library_path.ends_with(".xcframework");
if (!library_path.is_resource_file() && !library_path.is_absolute_path()) {
library_path = p_path.get_base_dir().path_join(library_path);
}
#ifdef TOOLS_ENABLED
is_reloadable = config->get_value("configuration", "reloadable", false);
update_last_modified_time(
FileAccess::get_modified_time(resource_path),
FileAccess::get_modified_time(library_path));
#endif
library_dependencies = find_extension_dependencies(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); });
// Handle icons if any are specified.
if (config->has_section("icons")) {
List<String> keys;
config->get_section_keys("icons", &keys);
for (const String &key : keys) {
String icon_path = config->get_value("icons", key);
if (icon_path.is_relative_path()) {
icon_path = p_path.get_base_dir().path_join(icon_path);
}
class_icon_paths[key] = icon_path;
}
}
return OK;
}

View file

@ -0,0 +1,85 @@
/**************************************************************************/
/* gdextension_library_loader.h */
/**************************************************************************/
/* 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 GDEXTENSION_LIBRARY_LOADER_H
#define GDEXTENSION_LIBRARY_LOADER_H
#include <functional>
#include "core/extension/gdextension_loader.h"
#include "core/io/config_file.h"
#include "core/os/shared_object.h"
class GDExtensionLibraryLoader : public GDExtensionLoader {
friend class GDExtensionManager;
friend class GDExtension;
private:
String resource_path;
void *library = nullptr; // pointer if valid.
String library_path;
String entry_symbol;
bool is_static_library = false;
#ifdef TOOLS_ENABLED
bool is_reloadable = false;
#endif
Vector<SharedObject> library_dependencies;
HashMap<String, String> class_icon_paths;
#ifdef TOOLS_ENABLED
uint64_t resource_last_modified_time = 0;
uint64_t library_last_modified_time = 0;
void update_last_modified_time(uint64_t p_resource_last_modified_time, uint64_t p_library_last_modified_time) {
resource_last_modified_time = p_resource_last_modified_time;
library_last_modified_time = p_library_last_modified_time;
}
#endif
public:
static String find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags = nullptr);
static Vector<SharedObject> find_extension_dependencies(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature);
virtual Error open_library(const String &p_path) override;
virtual Error initialize(GDExtensionInterfaceGetProcAddress p_get_proc_address, const Ref<GDExtension> &p_extension, GDExtensionInitialization *r_initialization) override;
virtual void close_library() override;
virtual bool is_library_open() const override;
virtual bool has_library_changed() const override;
virtual bool library_exists() const override;
Error parse_gdextension_file(const String &p_path);
};
#endif // GDEXTENSION_LIBRARY_LOADER_H

View file

@ -0,0 +1,48 @@
/**************************************************************************/
/* gdextension_loader.h */
/**************************************************************************/
/* 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 GDEXTENSION_LOADER_H
#define GDEXTENSION_LOADER_H
#include "core/object/ref_counted.h"
class GDExtension;
class GDExtensionLoader : public RefCounted {
public:
virtual Error open_library(const String &p_path) = 0;
virtual Error initialize(GDExtensionInterfaceGetProcAddress p_get_proc_address, const Ref<GDExtension> &p_extension, GDExtensionInitialization *r_initialization) = 0;
virtual void close_library() = 0;
virtual bool is_library_open() const = 0;
virtual bool has_library_changed() const = 0;
virtual bool library_exists() const = 0;
};
#endif // GDEXTENSION_LOADER_H

View file

@ -30,15 +30,20 @@
#include "gdextension_manager.h"
#include "core/extension/gdextension_compat_hashes.h"
#include "core/extension/gdextension_library_loader.h"
#include "core/extension/gdextension_special_compat_hashes.h"
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
#include "core/object/script_language.h"
GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(const Ref<GDExtension> &p_extension) {
GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(const Ref<GDExtension> &p_extension, bool p_first_load) {
if (level >= 0) { // Already initialized up to some level.
int32_t minimum_level = p_extension->get_minimum_library_initialization_level();
if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) {
return LOAD_STATUS_NEEDS_RESTART;
int32_t minimum_level = 0;
if (!p_first_load) {
minimum_level = p_extension->get_minimum_library_initialization_level();
if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) {
return LOAD_STATUS_NEEDS_RESTART;
}
}
// Initialize up to current level.
for (int32_t i = minimum_level; i <= level; i++) {
@ -50,10 +55,20 @@ GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(cons
gdextension_class_icon_paths[kv.key] = kv.value;
}
#ifdef TOOLS_ENABLED
// Signals that a new extension is loaded so GDScript can register new class names.
emit_signal("extension_loaded", p_extension);
#endif
return LOAD_STATUS_OK;
}
GDExtensionManager::LoadStatus GDExtensionManager::_unload_extension_internal(const Ref<GDExtension> &p_extension) {
#ifdef TOOLS_ENABLED
// Signals that a new extension is unloading so GDScript can unregister class names.
emit_signal("extension_unloading", p_extension);
#endif
if (level >= 0) { // Already initialized up to some level.
// Deinitialize down from current level.
for (int32_t i = level; i >= GDExtension::INITIALIZATION_LEVEL_CORE; i--) {
@ -69,19 +84,35 @@ GDExtensionManager::LoadStatus GDExtensionManager::_unload_extension_internal(co
}
GDExtensionManager::LoadStatus GDExtensionManager::load_extension(const String &p_path) {
if (gdextension_map.has(p_path)) {
return LOAD_STATUS_ALREADY_LOADED;
}
Ref<GDExtension> extension = ResourceLoader::load(p_path);
if (extension.is_null()) {
if (Engine::get_singleton()->is_recovery_mode_hint()) {
return LOAD_STATUS_FAILED;
}
LoadStatus status = _load_extension_internal(extension);
Ref<GDExtensionLibraryLoader> loader;
loader.instantiate();
return GDExtensionManager::get_singleton()->load_extension_with_loader(p_path, loader);
}
GDExtensionManager::LoadStatus GDExtensionManager::load_extension_with_loader(const String &p_path, const Ref<GDExtensionLoader> &p_loader) {
DEV_ASSERT(p_loader.is_valid());
if (gdextension_map.has(p_path)) {
return LOAD_STATUS_ALREADY_LOADED;
}
Ref<GDExtension> extension;
extension.instantiate();
Error err = extension->open_library(p_path, p_loader);
if (err != OK) {
return LOAD_STATUS_FAILED;
}
LoadStatus status = _load_extension_internal(extension, true);
if (status != LOAD_STATUS_OK) {
return status;
}
extension->set_path(p_path);
gdextension_map[p_path] = extension;
return LOAD_STATUS_OK;
}
@ -92,6 +123,10 @@ GDExtensionManager::LoadStatus GDExtensionManager::reload_extension(const String
#else
ERR_FAIL_COND_V_MSG(!Engine::get_singleton()->is_extension_reloading_enabled(), LOAD_STATUS_FAILED, "GDExtension reloading is disabled.");
if (Engine::get_singleton()->is_recovery_mode_hint()) {
return LOAD_STATUS_FAILED;
}
if (!gdextension_map.has(p_path)) {
return LOAD_STATUS_NOT_LOADED;
}
@ -117,12 +152,12 @@ GDExtensionManager::LoadStatus GDExtensionManager::reload_extension(const String
extension->close_library();
}
Error err = GDExtensionResourceLoader::load_gdextension_resource(p_path, extension);
Error err = extension->open_library(p_path, extension->loader);
if (err != OK) {
return LOAD_STATUS_FAILED;
}
status = _load_extension_internal(extension);
status = _load_extension_internal(extension, false);
if (status != LOAD_STATUS_OK) {
return status;
}
@ -134,6 +169,10 @@ GDExtensionManager::LoadStatus GDExtensionManager::reload_extension(const String
}
GDExtensionManager::LoadStatus GDExtensionManager::unload_extension(const String &p_path) {
if (Engine::get_singleton()->is_recovery_mode_hint()) {
return LOAD_STATUS_FAILED;
}
if (!gdextension_map.has(p_path)) {
return LOAD_STATUS_NOT_LOADED;
}
@ -180,14 +219,28 @@ String GDExtensionManager::class_get_icon_path(const String &p_class) const {
}
void GDExtensionManager::initialize_extensions(GDExtension::InitializationLevel p_level) {
if (Engine::get_singleton()->is_recovery_mode_hint()) {
return;
}
ERR_FAIL_COND(int32_t(p_level) - 1 != level);
for (KeyValue<String, Ref<GDExtension>> &E : gdextension_map) {
E.value->initialize_library(p_level);
if (p_level == GDExtension::INITIALIZATION_LEVEL_EDITOR) {
for (const KeyValue<String, String> &kv : E.value->class_icon_paths) {
gdextension_class_icon_paths[kv.key] = kv.value;
}
}
}
level = p_level;
}
void GDExtensionManager::deinitialize_extensions(GDExtension::InitializationLevel p_level) {
if (Engine::get_singleton()->is_recovery_mode_hint()) {
return;
}
ERR_FAIL_COND(int32_t(p_level) != level);
for (KeyValue<String, Ref<GDExtension>> &E : gdextension_map) {
E.value->deinitialize_library(p_level);
@ -226,12 +279,16 @@ void GDExtensionManager::_reload_all_scripts() {
#endif // TOOLS_ENABLED
void GDExtensionManager::load_extensions() {
if (Engine::get_singleton()->is_recovery_mode_hint()) {
return;
}
Ref<FileAccess> f = FileAccess::open(GDExtension::get_extension_list_config_file(), FileAccess::READ);
while (f.is_valid() && !f->eof_reached()) {
String s = f->get_line().strip_edges();
if (!s.is_empty()) {
LoadStatus err = load_extension(s);
ERR_CONTINUE_MSG(err == LOAD_STATUS_FAILED, "Error loading extension: " + s);
ERR_CONTINUE_MSG(err == LOAD_STATUS_FAILED, vformat("Error loading extension: '%s'.", s));
}
}
@ -240,6 +297,9 @@ void GDExtensionManager::load_extensions() {
void GDExtensionManager::reload_extensions() {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_recovery_mode_hint()) {
return;
}
bool reloaded = false;
for (const KeyValue<String, Ref<GDExtension>> &E : gdextension_map) {
if (!E.value->is_reloadable()) {
@ -261,6 +321,72 @@ void GDExtensionManager::reload_extensions() {
#endif
}
bool GDExtensionManager::ensure_extensions_loaded(const HashSet<String> &p_extensions) {
Vector<String> extensions_added;
Vector<String> extensions_removed;
for (const String &E : p_extensions) {
if (!is_extension_loaded(E)) {
extensions_added.push_back(E);
}
}
Vector<String> loaded_extensions = get_loaded_extensions();
for (const String &loaded_extension : loaded_extensions) {
if (!p_extensions.has(loaded_extension)) {
// The extension may not have a .gdextension file.
const Ref<GDExtension> extension = GDExtensionManager::get_singleton()->get_extension(loaded_extension);
if (!extension->get_loader()->library_exists()) {
extensions_removed.push_back(loaded_extension);
}
}
}
String extension_list_config_file = GDExtension::get_extension_list_config_file();
if (p_extensions.size()) {
if (extensions_added.size() || extensions_removed.size()) {
// Extensions were added or removed.
Ref<FileAccess> f = FileAccess::open(extension_list_config_file, FileAccess::WRITE);
for (const String &E : p_extensions) {
f->store_line(E);
}
}
} else {
if (loaded_extensions.size() || FileAccess::exists(extension_list_config_file)) {
// Extensions were removed.
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
da->remove(extension_list_config_file);
}
}
bool needs_restart = false;
for (const String &extension : extensions_added) {
GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->load_extension(extension);
if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) {
needs_restart = true;
}
}
for (const String &extension : extensions_removed) {
GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->unload_extension(extension);
if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) {
needs_restart = true;
}
}
#ifdef TOOLS_ENABLED
if (extensions_added.size() || extensions_removed.size()) {
// Emitting extensions_reloaded so EditorNode can reload Inspector and regenerate documentation.
emit_signal("extensions_reloaded");
// Reload all scripts to clear out old references.
callable_mp_static(&GDExtensionManager::_reload_all_scripts).call_deferred();
}
#endif
return needs_restart;
}
GDExtensionManager *GDExtensionManager::get_singleton() {
return singleton;
}
@ -281,6 +407,8 @@ void GDExtensionManager::_bind_methods() {
BIND_ENUM_CONSTANT(LOAD_STATUS_NEEDS_RESTART);
ADD_SIGNAL(MethodInfo("extensions_reloaded"));
ADD_SIGNAL(MethodInfo("extension_loaded", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, "GDExtension")));
ADD_SIGNAL(MethodInfo("extension_unloading", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, "GDExtension")));
}
GDExtensionManager *GDExtensionManager::singleton = nullptr;
@ -290,7 +418,7 @@ GDExtensionManager::GDExtensionManager() {
singleton = this;
#ifndef DISABLE_DEPRECATED
GDExtensionCompatHashes::initialize();
GDExtensionSpecialCompatHashes::initialize();
#endif
}
@ -299,6 +427,6 @@ GDExtensionManager::~GDExtensionManager() {
singleton = nullptr;
}
#ifndef DISABLE_DEPRECATED
GDExtensionCompatHashes::finalize();
GDExtensionSpecialCompatHashes::finalize();
#endif
}

View file

@ -54,7 +54,7 @@ public:
};
private:
LoadStatus _load_extension_internal(const Ref<GDExtension> &p_extension);
LoadStatus _load_extension_internal(const Ref<GDExtension> &p_extension, bool p_first_load);
LoadStatus _unload_extension_internal(const Ref<GDExtension> &p_extension);
#ifdef TOOLS_ENABLED
@ -63,6 +63,7 @@ private:
public:
LoadStatus load_extension(const String &p_path);
LoadStatus load_extension_with_loader(const String &p_path, const Ref<GDExtensionLoader> &p_loader);
LoadStatus reload_extension(const String &p_path);
LoadStatus unload_extension(const String &p_path);
bool is_extension_loaded(const String &p_path) const;
@ -84,6 +85,7 @@ public:
void load_extensions();
void reload_extensions();
bool ensure_extensions_loaded(const HashSet<String> &p_extensions);
GDExtensionManager();
~GDExtensionManager();

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* gdextension_compat_hashes.cpp */
/* gdextension_special_compat_hashes.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,16 +28,16 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "gdextension_compat_hashes.h"
#include "gdextension_special_compat_hashes.h"
#ifndef DISABLE_DEPRECATED
#include "core/object/class_db.h"
#include "core/variant/variant.h"
HashMap<StringName, LocalVector<GDExtensionCompatHashes::Mapping>> GDExtensionCompatHashes::mappings;
HashMap<StringName, LocalVector<GDExtensionSpecialCompatHashes::Mapping>> GDExtensionSpecialCompatHashes::mappings;
bool GDExtensionCompatHashes::lookup_current_hash(const StringName &p_class, const StringName &p_method, uint32_t p_legacy_hash, uint32_t *r_current_hash) {
bool GDExtensionSpecialCompatHashes::lookup_current_hash(const StringName &p_class, const StringName &p_method, uint32_t p_legacy_hash, uint32_t *r_current_hash) {
LocalVector<Mapping> *methods = mappings.getptr(p_class);
if (!methods) {
return false;
@ -53,7 +53,7 @@ bool GDExtensionCompatHashes::lookup_current_hash(const StringName &p_class, con
return false;
}
bool GDExtensionCompatHashes::get_legacy_hashes(const StringName &p_class, const StringName &p_method, Array &r_hashes, bool p_check_valid) {
bool GDExtensionSpecialCompatHashes::get_legacy_hashes(const StringName &p_class, const StringName &p_method, Array &r_hashes, bool p_check_valid) {
LocalVector<Mapping> *methods = mappings.getptr(p_class);
if (!methods) {
return false;
@ -65,7 +65,7 @@ bool GDExtensionCompatHashes::get_legacy_hashes(const StringName &p_class, const
if (p_check_valid) {
MethodBind *mb = ClassDB::get_method_with_compatibility(p_class, p_method, mapping.current_hash);
if (!mb) {
WARN_PRINT(vformat("Compatibility hash %d for %s::%s() mapped to non-existent hash %d. Please update gdextension_compat_hashes.cpp.", mapping.legacy_hash, p_class, p_method, mapping.current_hash));
WARN_PRINT(vformat("Compatibility hash %d for %s::%s() mapped to non-existent hash %d. Please update gdextension_special_compat_hashes.cpp.", mapping.legacy_hash, p_class, p_method, mapping.current_hash));
continue;
}
}
@ -77,7 +77,7 @@ bool GDExtensionCompatHashes::get_legacy_hashes(const StringName &p_class, const
return found;
}
void GDExtensionCompatHashes::initialize() {
void GDExtensionSpecialCompatHashes::initialize() {
// clang-format off
mappings.insert("AESContext", {
{ "start", 3167574919, 3122411423 },
@ -103,6 +103,14 @@ void GDExtensionCompatHashes::initialize() {
mappings.insert("AcceptDialog", {
{ "add_button", 4158837846, 3328440682 },
});
mappings.insert("AnimatedSprite2D", {
{ "play", 2372066587, 3269405555 },
{ "play_backwards", 1421762485, 3323268493 },
});
mappings.insert("AnimatedSprite3D", {
{ "play", 2372066587, 3269405555 },
{ "play_backwards", 1421762485, 3323268493 },
});
mappings.insert("Animation", {
{ "add_track", 2393815928, 3843682357 },
{ "track_insert_key", 1985425300, 808952278 },
@ -146,6 +154,12 @@ void GDExtensionCompatHashes::initialize() {
{ "travel", 3683006648, 3823612587 },
{ "start", 3683006648, 3823612587 },
});
mappings.insert("AnimationPlayer", {
{ "play", 3697947785, 3118260607 },
{ "play", 2221377757, 3118260607 },
{ "play_backwards", 3890664824, 2787282401 },
{ "play_with_capture", 3180464118, 1572969103 },
});
mappings.insert("ArrayMesh", {
{ "add_surface_from_arrays", 172284304, 1796411378 },
});
@ -241,22 +255,31 @@ void GDExtensionCompatHashes::initialize() {
#endif
});
mappings.insert("DirAccess", {
{ "list_dir_begin", 2018049411, 2610976713 },
{ "list_dir_begin", 2018049411, 166280745 },
{ "list_dir_begin", 2610976713, 166280745 },
{ "copy", 198434953, 1063198817 },
{ "copy_absolute", 198434953, 1063198817 },
});
mappings.insert("DisplayServer", {
{ "global_menu_add_submenu_item", 3806306913, 2828985934 },
{ "global_menu_add_item", 3415468211, 3401266716 },
{ "global_menu_add_check_item", 3415468211, 3401266716 },
{ "global_menu_add_icon_item", 1700867534, 4245856523 },
{ "global_menu_add_icon_check_item", 1700867534, 4245856523 },
{ "global_menu_add_radio_check_item", 3415468211, 3401266716 },
{ "global_menu_add_icon_radio_check_item", 1700867534, 4245856523 },
{ "global_menu_add_multistate_item", 635750054, 3431222859 },
{ "global_menu_add_item", 3415468211, 3616842746 },
{ "global_menu_add_item", 3401266716, 3616842746 },
{ "global_menu_add_check_item", 3415468211, 3616842746 },
{ "global_menu_add_check_item", 3401266716, 3616842746 },
{ "global_menu_add_icon_item", 1700867534, 3867083847 },
{ "global_menu_add_icon_item", 4245856523, 3867083847 },
{ "global_menu_add_icon_check_item", 1700867534, 3867083847 },
{ "global_menu_add_icon_check_item", 4245856523, 3867083847 },
{ "global_menu_add_radio_check_item", 3415468211, 3616842746 },
{ "global_menu_add_radio_check_item", 3401266716, 3616842746 },
{ "global_menu_add_icon_radio_check_item", 1700867534, 3867083847 },
{ "global_menu_add_icon_radio_check_item", 4245856523, 3867083847 },
{ "global_menu_add_multistate_item", 635750054, 3297554655 },
{ "global_menu_add_multistate_item", 3431222859, 3297554655 },
{ "global_menu_add_separator", 1041533178, 3214812433 },
{ "tts_speak", 3741216677, 903992738 },
{ "is_touchscreen_available", 4162880507, 3323674545 },
{ "is_touchscreen_available", 4162880507, 36873697 },
{ "is_touchscreen_available", 3323674545, 36873697 },
{ "screen_set_orientation", 2629526904, 2211511631 },
{ "window_get_native_handle", 2709193271, 1096425680 },
{ "window_set_title", 3043792800, 441246282 },
@ -286,6 +309,12 @@ void GDExtensionCompatHashes::initialize() {
{ "virtual_keyboard_show", 860410478, 3042891259 },
#endif
});
mappings.insert("EditorExportPlatform", {
{ "export_project_files", 425454869, 1063735070 },
});
mappings.insert("EditorProperty", {
{ "emit_changed", 3069422438, 1822500399 },
});
mappings.insert("ENetConnection", {
{ "create_host_bound", 866250949, 1515002313 },
{ "connect_to_host", 385984708, 2171300490 },
@ -453,18 +482,35 @@ void GDExtensionCompatHashes::initialize() {
mappings.insert("MultiplayerAPI", {
{ "rpc", 1833408346, 2077486355 },
});
mappings.insert("NativeMenu", {
{ "add_item", 2553375659, 980552939 },
{ "add_check_item", 2553375659, 980552939 },
{ "add_icon_item", 2987595282, 1372188274 },
{ "add_icon_check_item", 2987595282, 1372188274 },
{ "add_radio_check_item", 2553375659, 980552939 },
{ "add_icon_radio_check_item", 2987595282, 1372188274 },
{ "add_multistate_item", 1558592568, 2674635658 },
});
mappings.insert("NavigationMeshGenerator", {
{ "parse_source_geometry_data", 3703028813, 685862123 },
{ "bake_from_source_geometry_data", 3669016597, 2469318639 },
{ "parse_source_geometry_data", 3703028813, 3172802542 },
{ "parse_source_geometry_data", 685862123, 3172802542 },
{ "bake_from_source_geometry_data", 3669016597, 1286748856 },
{ "bake_from_source_geometry_data", 2469318639, 1286748856 },
});
mappings.insert("NavigationServer2D", {
{ "map_get_path", 56240621, 3146466012 },
{ "parse_source_geometry_data", 1176164995, 1766905497 },
{ "bake_from_source_geometry_data", 2909414286, 2179660022 },
{ "bake_from_source_geometry_data_async", 2909414286, 2179660022 },
});
mappings.insert("NavigationServer3D", {
{ "map_get_path", 2121045993, 1187418690 },
{ "parse_source_geometry_data", 3703028813, 685862123 },
{ "bake_from_source_geometry_data", 3669016597, 2469318639 },
{ "bake_from_source_geometry_data_async", 3669016597, 2469318639 },
{ "parse_source_geometry_data", 3703028813, 3172802542 },
{ "parse_source_geometry_data", 685862123, 3172802542 },
{ "bake_from_source_geometry_data", 3669016597, 1286748856 },
{ "bake_from_source_geometry_data", 2469318639, 1286748856 },
{ "bake_from_source_geometry_data_async", 3669016597, 1286748856 },
{ "bake_from_source_geometry_data_async", 2469318639, 1286748856 },
});
mappings.insert("Node", {
{ "add_child", 3070154285, 3863233950 },
@ -504,6 +550,9 @@ void GDExtensionCompatHashes::initialize() {
{ "tr", 2475554935, 1195764410 },
{ "tr_n", 4021311862, 162698058 },
});
mappings.insert("OpenXRAPIExtension", {
{ "transform_from_pose", 3255299855, 2963875352 },
});
mappings.insert("OptionButton", {
{ "add_item", 3043792800, 2697778442 },
{ "add_icon_item", 3944051090, 3781678508 },
@ -631,6 +680,11 @@ void GDExtensionCompatHashes::initialize() {
mappings.insert("ProjectSettings", {
{ "load_resource_pack", 3001721055, 708980503 },
});
mappings.insert("RDShaderFile", {
{ "bake_from_source_geometry_data_async", 2469318639, 1286748856 },
{ "set_bytecode", 1558064255, 1526857008 },
{ "get_spirv", 3340165340, 2689310080 },
});
mappings.insert("RegEx", {
{ "search", 4087180739, 3365977994 },
{ "search_all", 3354100289, 849021363 },
@ -722,7 +776,7 @@ void GDExtensionCompatHashes::initialize() {
{ "push_paragraph", 3218895358, 3089306873 },
{ "push_list", 4036303897, 3017143144 },
{ "push_table", 1125058220, 2623499273 },
{ "set_table_column_expand", 4132157579, 2185176273 },
{ "set_table_column_expand", 4258957458, 2185176273 },
#ifdef REAL_T_IS_DOUBLE
{ "add_image", 3346058748, 1507062345 },
{ "push_dropcap", 981432822, 763534173 },
@ -863,6 +917,9 @@ void GDExtensionCompatHashes::initialize() {
{ "set_cells_terrain_path", 3072115677, 3578627656 },
{ "get_used_cells_by_id", 4152068407, 2931012785 },
});
mappings.insert("TileMapLayer", {
{ "notify_runtime_tile_data_update", 2275361663, 3218959716 },
});
mappings.insert("TileMapPattern", {
{ "set_cell", 634000503, 2224802556 },
});
@ -964,7 +1021,7 @@ void GDExtensionCompatHashes::initialize() {
// clang-format on
}
void GDExtensionCompatHashes::finalize() {
void GDExtensionSpecialCompatHashes::finalize() {
mappings.clear();
}

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* gdextension_compat_hashes.h */
/* gdextension_special_compat_hashes.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef GDEXTENSION_COMPAT_HASHES_H
#define GDEXTENSION_COMPAT_HASHES_H
#ifndef GDEXTENSION_SPECIAL_COMPAT_HASHES_H
#define GDEXTENSION_SPECIAL_COMPAT_HASHES_H
#ifndef DISABLE_DEPRECATED
@ -37,7 +37,11 @@
#include "core/templates/hash_map.h"
#include "core/templates/local_vector.h"
class GDExtensionCompatHashes {
// Note: In most situations, compatibility methods should be registered via ClassDB::bind_compatibility_method().
// This class is only meant to be used in exceptional circumstances, for example, when Godot's hashing
// algorithm changes and registering compatibility methods for all affect methods would be onerous.
class GDExtensionSpecialCompatHashes {
struct Mapping {
StringName method;
uint32_t legacy_hash;
@ -55,4 +59,4 @@ public:
#endif // DISABLE_DEPRECATED
#endif // GDEXTENSION_COMPAT_HASHES_H
#endif // GDEXTENSION_SPECIAL_COMPAT_HASHES_H

View file

@ -55,10 +55,10 @@ def generate_mod_version(argcount, const=False, returns=False):
proto_ex = """
#define EXBIND$VER($RETTYPE m_name$ARG) \\
GDVIRTUAL$VER($RETTYPE_##m_name$ARG)\\
GDVIRTUAL$VER_REQUIRED($RETTYPE_##m_name$ARG)\\
virtual $RETVAL m_name($FUNCARGS) $CONST override { \\
$RETPRE\\
GDVIRTUAL_REQUIRED_CALL(_##m_name$CALLARGS$RETREF);\\
GDVIRTUAL_CALL(_##m_name$CALLARGS$RETREF);\\
$RETPOST\\
}
"""