feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
394
engine/core/extension/gdextension_library_loader.cpp
Normal file
394
engine/core/extension/gdextension_library_loader.cpp
Normal 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;
|
||||
}
|
||||
85
engine/core/extension/gdextension_library_loader.h
Normal file
85
engine/core/extension/gdextension_library_loader.h
Normal 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
|
||||
48
engine/core/extension/gdextension_loader.h
Normal file
48
engine/core/extension/gdextension_loader.h
Normal 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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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\\
|
||||
}
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue