[GDNative] use feature tags, added load once option
This commit is contained in:
parent
cefdb34f6c
commit
ffc2669298
@ -44,7 +44,7 @@ void GDNativeLibraryEditor::_find_gdnative_singletons(EditorFileSystemDirectory
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ref<GDNativeLibrary> lib = ResourceLoader::load(p_dir->get_file_path(i));
|
Ref<GDNativeLibrary> lib = ResourceLoader::load(p_dir->get_file_path(i));
|
||||||
if (lib.is_valid() && lib->is_singleton_gdnative()) {
|
if (lib.is_valid() && lib->is_singleton()) {
|
||||||
String path = p_dir->get_file_path(i);
|
String path = p_dir->get_file_path(i);
|
||||||
TreeItem *ti = libraries->create_item(libraries->get_root());
|
TreeItem *ti = libraries->create_item(libraries->get_root());
|
||||||
ti->set_text(0, path.get_file());
|
ti->set_text(0, path.get_file());
|
||||||
|
@ -37,150 +37,51 @@
|
|||||||
|
|
||||||
#include "scene/main/scene_tree.h"
|
#include "scene/main/scene_tree.h"
|
||||||
|
|
||||||
const String init_symbol = "godot_gdnative_init";
|
const String init_symbol = "gdnative_init";
|
||||||
const String terminate_symbol = "godot_gdnative_terminate";
|
const String terminate_symbol = "gdnative_terminate";
|
||||||
|
const String default_symbol_prefix = "godot_";
|
||||||
|
|
||||||
// Defined in gdnative_api_struct.gen.cpp
|
// Defined in gdnative_api_struct.gen.cpp
|
||||||
extern const godot_gdnative_api_struct api_struct;
|
extern const godot_gdnative_api_struct api_struct;
|
||||||
|
|
||||||
String GDNativeLibrary::platform_names[NUM_PLATFORMS + 1] = {
|
Map<String, Vector<Ref<GDNative> > > *GDNativeLibrary::loaded_libraries = NULL;
|
||||||
"X11_32bit",
|
|
||||||
"X11_64bit",
|
|
||||||
"Windows_32bit",
|
|
||||||
"Windows_64bit",
|
|
||||||
"OSX",
|
|
||||||
|
|
||||||
"Android",
|
GDNativeLibrary::GDNativeLibrary() {
|
||||||
|
config_file.instance();
|
||||||
|
|
||||||
"iOS_32bit",
|
symbol_prefix = default_symbol_prefix;
|
||||||
"iOS_64bit",
|
|
||||||
|
|
||||||
"WebAssembly",
|
if (GDNativeLibrary::loaded_libraries == NULL) {
|
||||||
|
GDNativeLibrary::loaded_libraries = memnew((Map<String, Vector<Ref<GDNative> > >));
|
||||||
""
|
}
|
||||||
};
|
|
||||||
String GDNativeLibrary::platform_lib_ext[NUM_PLATFORMS + 1] = {
|
|
||||||
"so",
|
|
||||||
"so",
|
|
||||||
"dll",
|
|
||||||
"dll",
|
|
||||||
"dylib",
|
|
||||||
|
|
||||||
"so",
|
|
||||||
|
|
||||||
"dylib",
|
|
||||||
"dylib",
|
|
||||||
|
|
||||||
"wasm",
|
|
||||||
|
|
||||||
""
|
|
||||||
};
|
|
||||||
|
|
||||||
GDNativeLibrary::Platform GDNativeLibrary::current_platform =
|
|
||||||
#if defined(X11_ENABLED)
|
|
||||||
(sizeof(void *) == 8 ? X11_64BIT : X11_32BIT);
|
|
||||||
#elif defined(WINDOWS_ENABLED)
|
|
||||||
(sizeof(void *) == 8 ? WINDOWS_64BIT : WINDOWS_32BIT);
|
|
||||||
#elif defined(OSX_ENABLED)
|
|
||||||
OSX;
|
|
||||||
#elif defined(IPHONE_ENABLED)
|
|
||||||
(sizeof(void *) == 8 ? IOS_64BIT : IOS_32BIT);
|
|
||||||
#elif defined(ANDROID_ENABLED)
|
|
||||||
ANDROID;
|
|
||||||
#elif defined(JAVASCRIPT_ENABLED)
|
|
||||||
WASM;
|
|
||||||
#else
|
|
||||||
NUM_PLATFORMS;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
GDNativeLibrary::GDNativeLibrary()
|
|
||||||
: library_paths(), singleton_gdnative(false) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GDNativeLibrary::~GDNativeLibrary() {
|
GDNativeLibrary::~GDNativeLibrary() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDNativeLibrary::_bind_methods() {
|
void GDNativeLibrary::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_library_path", "platform", "path"), &GDNativeLibrary::set_library_path);
|
ClassDB::bind_method(D_METHOD("get_config_file"), &GDNativeLibrary::get_config_file);
|
||||||
ClassDB::bind_method(D_METHOD("get_library_path", "platform"), &GDNativeLibrary::get_library_path);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_active_library_path"), &GDNativeLibrary::get_active_library_path);
|
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("is_singleton_gdnative"), &GDNativeLibrary::is_singleton_gdnative);
|
ClassDB::bind_method(D_METHOD("get_current_library_path"), &GDNativeLibrary::get_current_library_path);
|
||||||
ClassDB::bind_method(D_METHOD("set_singleton_gdnative", "singleton"), &GDNativeLibrary::set_singleton_gdnative);
|
ClassDB::bind_method(D_METHOD("get_current_dependencies"), &GDNativeLibrary::get_current_dependencies);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_current_library_statically_linked"), &GDNativeLibrary::is_current_library_statically_linked);
|
||||||
|
|
||||||
ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "singleton_gdnative"), "set_singleton_gdnative", "is_singleton_gdnative");
|
ClassDB::bind_method(D_METHOD("should_load_once"), &GDNativeLibrary::should_load_once);
|
||||||
}
|
ClassDB::bind_method(D_METHOD("is_singleton"), &GDNativeLibrary::is_singleton);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_symbol_prefix"), &GDNativeLibrary::get_symbol_prefix);
|
||||||
|
|
||||||
bool GDNativeLibrary::_set(const StringName &p_name, const Variant &p_value) {
|
ClassDB::bind_method(D_METHOD("set_load_once", "load_once"), &GDNativeLibrary::set_load_once);
|
||||||
String name = p_name;
|
ClassDB::bind_method(D_METHOD("set_singleton", "singleton"), &GDNativeLibrary::set_singleton);
|
||||||
if (name.begins_with("platform/")) {
|
ClassDB::bind_method(D_METHOD("set_symbol_prefix", "symbol_prefix"), &GDNativeLibrary::set_symbol_prefix);
|
||||||
set_library_path(name.get_slice("/", 1), p_value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GDNativeLibrary::_get(const StringName &p_name, Variant &r_ret) const {
|
ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "load_once"), "set_load_once", "should_load_once");
|
||||||
String name = p_name;
|
ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "singleton"), "set_singleton", "is_singleton");
|
||||||
if (name.begins_with("platform/")) {
|
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "symbol_prefix"), "set_symbol_prefix", "get_symbol_prefix");
|
||||||
r_ret = get_library_path(name.get_slice("/", 1));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GDNativeLibrary::_get_property_list(List<PropertyInfo> *p_list) const {
|
|
||||||
for (int i = 0; i < NUM_PLATFORMS; i++) {
|
|
||||||
p_list->push_back(PropertyInfo(Variant::STRING,
|
|
||||||
"platform/" + platform_names[i],
|
|
||||||
PROPERTY_HINT_FILE,
|
|
||||||
"*." + platform_lib_ext[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GDNativeLibrary::set_library_path(StringName p_platform, String p_path) {
|
|
||||||
int i;
|
|
||||||
for (i = 0; i <= NUM_PLATFORMS; i++) {
|
|
||||||
if (i == NUM_PLATFORMS) break;
|
|
||||||
if (platform_names[i] == p_platform) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == NUM_PLATFORMS) {
|
|
||||||
ERR_EXPLAIN(String("No such platform: ") + p_platform);
|
|
||||||
ERR_FAIL();
|
|
||||||
}
|
|
||||||
|
|
||||||
library_paths[i] = p_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
String GDNativeLibrary::get_library_path(StringName p_platform) const {
|
|
||||||
int i;
|
|
||||||
for (i = 0; i <= NUM_PLATFORMS; i++) {
|
|
||||||
if (i == NUM_PLATFORMS) break;
|
|
||||||
if (platform_names[i] == p_platform) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == NUM_PLATFORMS) {
|
|
||||||
ERR_EXPLAIN(String("No such platform: ") + p_platform);
|
|
||||||
ERR_FAIL_V("");
|
|
||||||
}
|
|
||||||
|
|
||||||
return library_paths[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
String GDNativeLibrary::get_active_library_path() const {
|
|
||||||
if (GDNativeLibrary::current_platform != NUM_PLATFORMS) {
|
|
||||||
return library_paths[GDNativeLibrary::current_platform];
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GDNative::GDNative() {
|
GDNative::GDNative() {
|
||||||
native_handle = NULL;
|
native_handle = NULL;
|
||||||
|
initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GDNative::~GDNative() {
|
GDNative::~GDNative() {
|
||||||
@ -220,8 +121,8 @@ bool GDNative::initialize() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String lib_path = library->get_active_library_path();
|
String lib_path = library->get_current_library_path();
|
||||||
if (lib_path.empty()) {
|
if (lib_path.empty() && !library->is_current_library_statically_linked()) {
|
||||||
ERR_PRINT("No library set for this platform");
|
ERR_PRINT("No library set for this platform");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -230,16 +131,34 @@ bool GDNative::initialize() {
|
|||||||
#else
|
#else
|
||||||
String path = ProjectSettings::get_singleton()->globalize_path(lib_path);
|
String path = ProjectSettings::get_singleton()->globalize_path(lib_path);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (library->should_load_once()) {
|
||||||
|
if (GDNativeLibrary::loaded_libraries->has(lib_path)) {
|
||||||
|
// already loaded. Don't load again.
|
||||||
|
// copy some of the stuff instead
|
||||||
|
this->native_handle = (*GDNativeLibrary::loaded_libraries)[lib_path][0]->native_handle;
|
||||||
|
initialized = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Error err = OS::get_singleton()->open_dynamic_library(path, native_handle);
|
Error err = OS::get_singleton()->open_dynamic_library(path, native_handle);
|
||||||
if (err != OK) {
|
if (err != OK && !library->is_current_library_statically_linked()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *library_init;
|
void *library_init;
|
||||||
err = get_symbol(init_symbol, library_init);
|
|
||||||
|
// we cheat here a little bit. you saw nothing
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
|
err = get_symbol(library->get_symbol_prefix() + init_symbol, library_init);
|
||||||
|
|
||||||
|
initialized = false;
|
||||||
|
|
||||||
if (err || !library_init) {
|
if (err || !library_init) {
|
||||||
OS::get_singleton()->close_dynamic_library(native_handle);
|
if (!library->is_current_library_statically_linked())
|
||||||
|
OS::get_singleton()->close_dynamic_library(native_handle);
|
||||||
native_handle = NULL;
|
native_handle = NULL;
|
||||||
ERR_PRINT("Failed to obtain godot_gdnative_init symbol");
|
ERR_PRINT("Failed to obtain godot_gdnative_init symbol");
|
||||||
return false;
|
return false;
|
||||||
@ -260,18 +179,42 @@ bool GDNative::initialize() {
|
|||||||
|
|
||||||
library_init_fpointer(&options);
|
library_init_fpointer(&options);
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
|
if (library->should_load_once() && !GDNativeLibrary::loaded_libraries->has(lib_path)) {
|
||||||
|
Vector<Ref<GDNative> > gdnatives;
|
||||||
|
gdnatives.resize(1);
|
||||||
|
gdnatives[0] = Ref<GDNative>(this);
|
||||||
|
GDNativeLibrary::loaded_libraries->insert(lib_path, gdnatives);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GDNative::terminate() {
|
bool GDNative::terminate() {
|
||||||
|
|
||||||
if (native_handle == NULL) {
|
if (!initialized) {
|
||||||
ERR_PRINT("No valid library handle, can't terminate GDNative object");
|
ERR_PRINT("No valid library handle, can't terminate GDNative object");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (library->should_load_once()) {
|
||||||
|
Vector<Ref<GDNative> > *gdnatives = &(*GDNativeLibrary::loaded_libraries)[library->get_current_library_path()];
|
||||||
|
if (gdnatives->size() > 1) {
|
||||||
|
// there are other GDNative's still using this library, so we actually don't terminte
|
||||||
|
gdnatives->erase(Ref<GDNative>(this));
|
||||||
|
initialized = false;
|
||||||
|
return true;
|
||||||
|
} else if (gdnatives->size() == 1) {
|
||||||
|
// we're the last one, terminate!
|
||||||
|
gdnatives->clear();
|
||||||
|
// wew this looks scary, but all it does is remove the entry completely
|
||||||
|
GDNativeLibrary::loaded_libraries->erase(GDNativeLibrary::loaded_libraries->find(library->get_current_library_path()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void *library_terminate;
|
void *library_terminate;
|
||||||
Error error = get_symbol(terminate_symbol, library_terminate);
|
Error error = get_symbol(library->get_symbol_prefix() + terminate_symbol, library_terminate);
|
||||||
if (error || !library_terminate) {
|
if (error || !library_terminate) {
|
||||||
OS::get_singleton()->close_dynamic_library(native_handle);
|
OS::get_singleton()->close_dynamic_library(native_handle);
|
||||||
native_handle = NULL;
|
native_handle = NULL;
|
||||||
@ -288,6 +231,8 @@ bool GDNative::terminate() {
|
|||||||
|
|
||||||
library_terminate_pointer(&options);
|
library_terminate_pointer(&options);
|
||||||
|
|
||||||
|
initialized = false;
|
||||||
|
|
||||||
// GDNativeScriptLanguage::get_singleton()->initialized_libraries.erase(p_native_lib->path);
|
// GDNativeScriptLanguage::get_singleton()->initialized_libraries.erase(p_native_lib->path);
|
||||||
|
|
||||||
OS::get_singleton()->close_dynamic_library(native_handle);
|
OS::get_singleton()->close_dynamic_library(native_handle);
|
||||||
@ -297,7 +242,7 @@ bool GDNative::terminate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool GDNative::is_initialized() {
|
bool GDNative::is_initialized() {
|
||||||
return (native_handle != NULL);
|
return initialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDNativeCallRegistry::register_native_call_type(StringName p_call_type, native_call_cb p_callback) {
|
void GDNativeCallRegistry::register_native_call_type(StringName p_call_type, native_call_cb p_callback) {
|
||||||
@ -342,7 +287,7 @@ Variant GDNative::call_native(StringName p_native_call_type, StringName p_proced
|
|||||||
|
|
||||||
Error GDNative::get_symbol(StringName p_procedure_name, void *&r_handle) {
|
Error GDNative::get_symbol(StringName p_procedure_name, void *&r_handle) {
|
||||||
|
|
||||||
if (native_handle == NULL) {
|
if (!initialized) {
|
||||||
ERR_PRINT("No valid library handle, can't get symbol from GDNative object");
|
ERR_PRINT("No valid library handle, can't get symbol from GDNative object");
|
||||||
return ERR_CANT_OPEN;
|
return ERR_CANT_OPEN;
|
||||||
}
|
}
|
||||||
@ -355,3 +300,163 @@ Error GDNative::get_symbol(StringName p_procedure_name, void *&r_handle) {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RES GDNativeLibraryResourceLoader::load(const String &p_path, const String &p_original_path, Error *r_error) {
|
||||||
|
Ref<GDNativeLibrary> lib;
|
||||||
|
lib.instance();
|
||||||
|
|
||||||
|
Ref<ConfigFile> config = lib->get_config_file();
|
||||||
|
|
||||||
|
Error err = config->load(p_path);
|
||||||
|
|
||||||
|
if (r_error) {
|
||||||
|
*r_error = err;
|
||||||
|
}
|
||||||
|
|
||||||
|
lib->set_singleton(config->get_value("general", "singleton", false));
|
||||||
|
lib->set_load_once(config->get_value("general", "load_once", true));
|
||||||
|
lib->set_symbol_prefix(config->get_value("general", "symbol_prefix", default_symbol_prefix));
|
||||||
|
|
||||||
|
String entry_lib_path;
|
||||||
|
{
|
||||||
|
|
||||||
|
List<String> entry_keys;
|
||||||
|
config->get_section_keys("entry", &entry_keys);
|
||||||
|
|
||||||
|
for (List<String>::Element *E = entry_keys.front(); E; E = E->next()) {
|
||||||
|
String key = E->get();
|
||||||
|
|
||||||
|
Vector<String> tags = key.split(".");
|
||||||
|
|
||||||
|
bool skip = false;
|
||||||
|
for (int i = 0; i < tags.size(); i++) {
|
||||||
|
bool has_feature = OS::get_singleton()->has_feature(tags[i]);
|
||||||
|
|
||||||
|
if (!has_feature) {
|
||||||
|
skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skip) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry_lib_path = config->get_value("entry", key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<String> dependency_paths;
|
||||||
|
{
|
||||||
|
|
||||||
|
List<String> dependency_keys;
|
||||||
|
config->get_section_keys("dependencies", &dependency_keys);
|
||||||
|
|
||||||
|
for (List<String>::Element *E = dependency_keys.front(); E; E = E->next()) {
|
||||||
|
String key = E->get();
|
||||||
|
|
||||||
|
Vector<String> tags = key.split(".");
|
||||||
|
|
||||||
|
bool skip = false;
|
||||||
|
for (int i = 0; i < tags.size(); i++) {
|
||||||
|
bool has_feature = OS::get_singleton()->has_feature(tags[i]);
|
||||||
|
|
||||||
|
if (!has_feature) {
|
||||||
|
skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skip) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
dependency_paths = config->get_value("dependencies", key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_statically_linked = false;
|
||||||
|
{
|
||||||
|
|
||||||
|
List<String> static_linking_keys;
|
||||||
|
config->get_section_keys("static_linking", &static_linking_keys);
|
||||||
|
|
||||||
|
for (List<String>::Element *E = static_linking_keys.front(); E; E = E->next()) {
|
||||||
|
String key = E->get();
|
||||||
|
|
||||||
|
Vector<String> tags = key.split(".");
|
||||||
|
|
||||||
|
bool skip = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < tags.size(); i++) {
|
||||||
|
bool has_feature = OS::get_singleton()->has_feature(tags[i]);
|
||||||
|
|
||||||
|
if (!has_feature) {
|
||||||
|
skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skip) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_statically_linked = config->get_value("static_linking", key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lib->current_library_path = entry_lib_path;
|
||||||
|
lib->current_dependencies = dependency_paths;
|
||||||
|
lib->current_library_statically_linked = is_statically_linked;
|
||||||
|
|
||||||
|
print_line(String("lib path: ") + entry_lib_path);
|
||||||
|
print_line(String("dependencies: ") + Variant(dependency_paths));
|
||||||
|
print_line(String("static: ") + (is_statically_linked ? "true" : "false"));
|
||||||
|
|
||||||
|
return lib;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDNativeLibraryResourceLoader::get_recognized_extensions(List<String> *p_extensions) const {
|
||||||
|
p_extensions->push_back("gdnlib");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDNativeLibraryResourceLoader::handles_type(const String &p_type) const {
|
||||||
|
return p_type == "Resource" || p_type == "GDNativeLibrary";
|
||||||
|
}
|
||||||
|
|
||||||
|
String GDNativeLibraryResourceLoader::get_resource_type(const String &p_path) const {
|
||||||
|
String el = p_path.get_extension().to_lower();
|
||||||
|
if (el == "gdnlib")
|
||||||
|
return "GDNativeLibrary";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Error GDNativeLibraryResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t p_flags) {
|
||||||
|
|
||||||
|
Ref<GDNativeLibrary> lib = p_resource;
|
||||||
|
|
||||||
|
if (lib.is_null()) {
|
||||||
|
return ERR_INVALID_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<ConfigFile> config = lib->get_config_file();
|
||||||
|
|
||||||
|
config->set_value("general", "singleton", lib->is_singleton());
|
||||||
|
config->set_value("general", "load_once", lib->should_load_once());
|
||||||
|
config->set_value("general", "symbol_prefix", lib->get_symbol_prefix());
|
||||||
|
|
||||||
|
return config->save(p_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDNativeLibraryResourceSaver::recognize(const RES &p_resource) const {
|
||||||
|
return Object::cast_to<GDNativeLibrary>(*p_resource) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDNativeLibraryResourceSaver::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const {
|
||||||
|
if (Object::cast_to<GDNativeLibrary>(*p_resource) != NULL) {
|
||||||
|
p_extensions->push_back("gdnlib");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -38,66 +38,69 @@
|
|||||||
#include "gdnative/gdnative.h"
|
#include "gdnative/gdnative.h"
|
||||||
#include "gdnative_api_struct.gen.h"
|
#include "gdnative_api_struct.gen.h"
|
||||||
|
|
||||||
|
#include "io/config_file.h"
|
||||||
|
|
||||||
|
class GDNativeLibraryResourceLoader;
|
||||||
|
class GDNative;
|
||||||
|
|
||||||
class GDNativeLibrary : public Resource {
|
class GDNativeLibrary : public Resource {
|
||||||
GDCLASS(GDNativeLibrary, Resource)
|
GDCLASS(GDNativeLibrary, Resource)
|
||||||
|
|
||||||
enum Platform {
|
static Map<String, Vector<Ref<GDNative> > > *loaded_libraries;
|
||||||
X11_32BIT,
|
|
||||||
X11_64BIT,
|
|
||||||
WINDOWS_32BIT,
|
|
||||||
WINDOWS_64BIT,
|
|
||||||
// NOTE(karroffel): I heard OSX 32 bit is dead, so 64 only
|
|
||||||
OSX,
|
|
||||||
|
|
||||||
// Android .so files must be located in directories corresponding to Android ABI names:
|
friend class GDNativeLibraryResourceLoader;
|
||||||
// https://developer.android.com/ndk/guides/abis.html
|
friend class GDNative;
|
||||||
// Android runtime will select the matching library depending on the device.
|
|
||||||
// The value here must simply point to the .so name, for example:
|
|
||||||
// "res://libmy_gdnative.so" or "libmy_gdnative.so",
|
|
||||||
// while in the project the actual paths can be "lib/android/armeabi-v7a/libmy_gdnative.so",
|
|
||||||
// "lib/android/arm64-v8a/libmy_gdnative.so".
|
|
||||||
ANDROID,
|
|
||||||
|
|
||||||
IOS_32BIT,
|
Ref<ConfigFile> config_file;
|
||||||
IOS_64BIT,
|
|
||||||
|
|
||||||
// TODO(karroffel): figure out how to deal with web stuff at all...
|
String current_library_path;
|
||||||
WASM,
|
Vector<String> current_dependencies;
|
||||||
|
bool current_library_statically_linked;
|
||||||
|
|
||||||
// TODO(karroffel): does UWP have different libs??
|
bool singleton;
|
||||||
// UWP,
|
bool load_once;
|
||||||
|
String symbol_prefix;
|
||||||
NUM_PLATFORMS
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
static String platform_names[NUM_PLATFORMS + 1];
|
|
||||||
static String platform_lib_ext[NUM_PLATFORMS + 1];
|
|
||||||
|
|
||||||
static Platform current_platform;
|
|
||||||
|
|
||||||
String library_paths[NUM_PLATFORMS];
|
|
||||||
|
|
||||||
bool singleton_gdnative;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool _set(const StringName &p_name, const Variant &p_value);
|
|
||||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
|
||||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GDNativeLibrary();
|
GDNativeLibrary();
|
||||||
~GDNativeLibrary();
|
~GDNativeLibrary();
|
||||||
|
|
||||||
|
_FORCE_INLINE_ Ref<ConfigFile> get_config_file() { return config_file; }
|
||||||
|
|
||||||
|
// things that change per-platform
|
||||||
|
// so there are no setters for this
|
||||||
|
_FORCE_INLINE_ String get_current_library_path() const {
|
||||||
|
return current_library_path;
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ Vector<String> get_current_dependencies() const {
|
||||||
|
return current_dependencies;
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ bool is_current_library_statically_linked() const {
|
||||||
|
return current_library_statically_linked;
|
||||||
|
}
|
||||||
|
|
||||||
|
// things that are a property of the library itself, not platform specific
|
||||||
|
_FORCE_INLINE_ bool should_load_once() const {
|
||||||
|
return load_once;
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ bool is_singleton() const {
|
||||||
|
return singleton;
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ String get_symbol_prefix() const {
|
||||||
|
return symbol_prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void set_load_once(bool p_load_once) {
|
||||||
|
load_once = p_load_once;
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ void set_singleton(bool p_singleton) {
|
||||||
|
singleton = p_singleton;
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ void set_symbol_prefix(String p_symbol_prefix) {
|
||||||
|
symbol_prefix = p_symbol_prefix;
|
||||||
|
}
|
||||||
|
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
||||||
void set_library_path(StringName p_platform, String p_path);
|
|
||||||
String get_library_path(StringName p_platform) const;
|
|
||||||
|
|
||||||
String get_active_library_path() const;
|
|
||||||
|
|
||||||
_FORCE_INLINE_ bool is_singleton_gdnative() const { return singleton_gdnative; }
|
|
||||||
_FORCE_INLINE_ void set_singleton_gdnative(bool p_singleton) { singleton_gdnative = p_singleton; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef godot_variant (*native_call_cb)(void *, godot_array *);
|
typedef godot_variant (*native_call_cb)(void *, godot_array *);
|
||||||
@ -124,9 +127,10 @@ class GDNative : public Reference {
|
|||||||
|
|
||||||
Ref<GDNativeLibrary> library;
|
Ref<GDNativeLibrary> library;
|
||||||
|
|
||||||
// TODO(karroffel): different platforms? WASM????
|
|
||||||
void *native_handle;
|
void *native_handle;
|
||||||
|
|
||||||
|
bool initialized;
|
||||||
|
|
||||||
void _compile_dummy_for_api();
|
void _compile_dummy_for_api();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -148,4 +152,19 @@ public:
|
|||||||
Error get_symbol(StringName p_procedure_name, void *&r_handle);
|
Error get_symbol(StringName p_procedure_name, void *&r_handle);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class GDNativeLibraryResourceLoader : public ResourceFormatLoader {
|
||||||
|
public:
|
||||||
|
virtual RES load(const String &p_path, const String &p_original_path, Error *r_error);
|
||||||
|
virtual void get_recognized_extensions(List<String> *p_extensions) const;
|
||||||
|
virtual bool handles_type(const String &p_type) const;
|
||||||
|
virtual String get_resource_type(const String &p_path) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GDNativeLibraryResourceSaver : public ResourceFormatSaver {
|
||||||
|
public:
|
||||||
|
virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags);
|
||||||
|
virtual bool recognize(const RES &p_resource) const;
|
||||||
|
virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const;
|
||||||
|
};
|
||||||
|
|
||||||
#endif // GDNATIVE_H
|
#endif // GDNATIVE_H
|
||||||
|
@ -108,7 +108,7 @@ void NativeScript::set_library(Ref<GDNativeLibrary> p_library) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
library = p_library;
|
library = p_library;
|
||||||
lib_path = library->get_active_library_path();
|
lib_path = library->get_current_library_path();
|
||||||
|
|
||||||
#ifndef NO_THREADS
|
#ifndef NO_THREADS
|
||||||
if (Thread::get_caller_id() != Thread::get_main_id()) {
|
if (Thread::get_caller_id() != Thread::get_main_id()) {
|
||||||
@ -433,7 +433,7 @@ NativeScript::~NativeScript() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
////// ScriptInstance stuff
|
////// ScriptInstance stuff
|
||||||
|
|
||||||
#define GET_SCRIPT_DESC() script->get_script_desc()
|
#define GET_SCRIPT_DESC() script->get_script_desc()
|
||||||
|
|
||||||
@ -988,7 +988,7 @@ void NativeScriptLanguage::init_library(const Ref<GDNativeLibrary> &lib) {
|
|||||||
MutexLock lock(mutex);
|
MutexLock lock(mutex);
|
||||||
#endif
|
#endif
|
||||||
// See if this library was "registered" already.
|
// See if this library was "registered" already.
|
||||||
const String &lib_path = lib->get_active_library_path();
|
const String &lib_path = lib->get_current_library_path();
|
||||||
ERR_EXPLAIN(lib->get_name() + " does not have a library for the current platform");
|
ERR_EXPLAIN(lib->get_name() + " does not have a library for the current platform");
|
||||||
ERR_FAIL_COND(lib_path.length() == 0);
|
ERR_FAIL_COND(lib_path.length() == 0);
|
||||||
Map<String, Ref<GDNative> >::Element *E = library_gdnatives.find(lib_path);
|
Map<String, Ref<GDNative> >::Element *E = library_gdnatives.find(lib_path);
|
||||||
@ -1010,7 +1010,7 @@ void NativeScriptLanguage::init_library(const Ref<GDNativeLibrary> &lib) {
|
|||||||
|
|
||||||
void *proc_ptr;
|
void *proc_ptr;
|
||||||
|
|
||||||
Error err = gdn->get_symbol(_init_call_name, proc_ptr);
|
Error err = gdn->get_symbol(lib->get_symbol_prefix() + _init_call_name, proc_ptr);
|
||||||
|
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
ERR_PRINT(String("No " + _init_call_name + " in \"" + lib_path + "\" found").utf8().get_data());
|
ERR_PRINT(String("No " + _init_call_name + " in \"" + lib_path + "\" found").utf8().get_data());
|
||||||
@ -1051,7 +1051,7 @@ void NativeScriptLanguage::call_libraries_cb(const StringName &name) {
|
|||||||
if (L->get()->is_initialized()) {
|
if (L->get()->is_initialized()) {
|
||||||
|
|
||||||
void *proc_ptr;
|
void *proc_ptr;
|
||||||
Error err = L->get()->get_symbol(name, proc_ptr);
|
Error err = L->get()->get_symbol(L->get()->get_library()->get_symbol_prefix() + name, proc_ptr);
|
||||||
|
|
||||||
if (!err) {
|
if (!err) {
|
||||||
((void (*)())proc_ptr)();
|
((void (*)())proc_ptr)();
|
||||||
@ -1140,7 +1140,7 @@ void NativeReloadNode::_notification(int p_what) {
|
|||||||
// here the library registers all the classes and stuff.
|
// here the library registers all the classes and stuff.
|
||||||
|
|
||||||
void *proc_ptr;
|
void *proc_ptr;
|
||||||
Error err = L->get()->get_symbol("godot_nativescript_init", proc_ptr);
|
Error err = L->get()->get_symbol(L->get()->get_library()->get_symbol_prefix() + "nativescript_init", proc_ptr);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
ERR_PRINT(String("No godot_nativescript_init in \"" + L->key() + "\" found").utf8().get_data());
|
ERR_PRINT(String("No godot_nativescript_init in \"" + L->key() + "\" found").utf8().get_data());
|
||||||
} else {
|
} else {
|
||||||
|
@ -81,7 +81,7 @@ Set<String> get_gdnative_singletons(EditorFileSystemDirectory *p_dir) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ref<GDNativeLibrary> lib = ResourceLoader::load(p_dir->get_file_path(i));
|
Ref<GDNativeLibrary> lib = ResourceLoader::load(p_dir->get_file_path(i));
|
||||||
if (lib.is_valid() && lib->is_singleton_gdnative()) {
|
if (lib.is_valid() && lib->is_singleton()) {
|
||||||
file_paths.insert(p_dir->get_file_path(i));
|
file_paths.insert(p_dir->get_file_path(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,6 +141,9 @@ GDNativeCallRegistry *GDNativeCallRegistry::singleton;
|
|||||||
|
|
||||||
Vector<Ref<GDNative> > singleton_gdnatives;
|
Vector<Ref<GDNative> > singleton_gdnatives;
|
||||||
|
|
||||||
|
GDNativeLibraryResourceLoader *resource_loader_gdnlib = NULL;
|
||||||
|
GDNativeLibraryResourceSaver *resource_saver_gdnlib = NULL;
|
||||||
|
|
||||||
void register_gdnative_types() {
|
void register_gdnative_types() {
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
@ -153,6 +156,12 @@ void register_gdnative_types() {
|
|||||||
ClassDB::register_class<GDNativeLibrary>();
|
ClassDB::register_class<GDNativeLibrary>();
|
||||||
ClassDB::register_class<GDNative>();
|
ClassDB::register_class<GDNative>();
|
||||||
|
|
||||||
|
resource_loader_gdnlib = memnew(GDNativeLibraryResourceLoader);
|
||||||
|
resource_saver_gdnlib = memnew(GDNativeLibraryResourceSaver);
|
||||||
|
|
||||||
|
ResourceLoader::add_resource_format_loader(resource_loader_gdnlib);
|
||||||
|
ResourceSaver::add_resource_format_saver(resource_saver_gdnlib);
|
||||||
|
|
||||||
GDNativeCallRegistry::singleton = memnew(GDNativeCallRegistry);
|
GDNativeCallRegistry::singleton = memnew(GDNativeCallRegistry);
|
||||||
|
|
||||||
GDNativeCallRegistry::singleton->register_native_call_type("standard_varcall", cb_standard_varcall);
|
GDNativeCallRegistry::singleton->register_native_call_type("standard_varcall", cb_standard_varcall);
|
||||||
@ -185,11 +194,11 @@ void register_gdnative_types() {
|
|||||||
|
|
||||||
void *proc_ptr;
|
void *proc_ptr;
|
||||||
Error err = singleton_gdnatives[i]->get_symbol(
|
Error err = singleton_gdnatives[i]->get_symbol(
|
||||||
"godot_gdnative_singleton",
|
lib->get_symbol_prefix() + "gdnative_singleton",
|
||||||
proc_ptr);
|
proc_ptr);
|
||||||
|
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
ERR_PRINT((String("No godot_gdnative_singleton in \"" + singleton_gdnatives[i]->get_library()->get_active_library_path()) + "\" found").utf8().get_data());
|
ERR_PRINT((String("No godot_gdnative_singleton in \"" + singleton_gdnatives[i]->get_library()->get_current_library_path()) + "\" found").utf8().get_data());
|
||||||
} else {
|
} else {
|
||||||
((void (*)())proc_ptr)();
|
((void (*)())proc_ptr)();
|
||||||
}
|
}
|
||||||
@ -224,6 +233,9 @@ void unregister_gdnative_types() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
memdelete(resource_loader_gdnlib);
|
||||||
|
memdelete(resource_saver_gdnlib);
|
||||||
|
|
||||||
// This is for printing out the sizes of the core types
|
// This is for printing out the sizes of the core types
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
Reference in New Issue
Block a user