Browse Source

[GDNative] added singleton GDNativeLibraries

A GDNativeLibrary now has a field "gdnative_singleton" which can be
used to let the `godot_gdnative_singleton` procedure be executed on
Godot's startup. In future this can be used to register new
scripting languages or resource importer types.
Karroffel 8 years ago
parent
commit
54a9c1ee43
3 changed files with 177 additions and 1 deletions
  1. 5 1
      modules/gdnative/gdnative.cpp
  2. 5 0
      modules/gdnative/gdnative.h
  3. 167 0
      modules/gdnative/register_types.cpp

+ 5 - 1
modules/gdnative/gdnative.cpp

@@ -100,6 +100,11 @@ GDNativeLibrary::~GDNativeLibrary() {
 void GDNativeLibrary::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_library_path", "platform", "path"), &GDNativeLibrary::set_library_path);
 	ClassDB::bind_method(D_METHOD("get_library_path", "platform"), &GDNativeLibrary::get_library_path);
+
+	ClassDB::bind_method(D_METHOD("is_singleton_gdnative"), &GDNativeLibrary::is_singleton_gdnative);
+	ClassDB::bind_method(D_METHOD("set_singleton_gdnative", "singleton"), &GDNativeLibrary::set_singleton_gdnative);
+
+	ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "singleton_gdnative"), "set_singleton_gdnative", "is_singleton_gdnative");
 }
 
 bool GDNativeLibrary::_set(const StringName &p_name, const Variant &p_value) {
@@ -175,7 +180,6 @@ GDNative::GDNative() {
 }
 
 GDNative::~GDNative() {
-	// TODO(karroffel): implement ALL the things!
 }
 
 extern "C" void _api_anchor();

+ 5 - 0
modules/gdnative/gdnative.h

@@ -77,6 +77,8 @@ class GDNativeLibrary : public Resource {
 
 	String library_paths[NUM_PLATFORMS];
 
+	bool singleton_gdnative = false;
+
 protected:
 	bool _set(const StringName &p_name, const Variant &p_value);
 	bool _get(const StringName &p_name, Variant &r_ret) const;
@@ -92,6 +94,9 @@ public:
 	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_string *, godot_array *);

+ 167 - 0
modules/gdnative/register_types.cpp

@@ -37,7 +37,90 @@
 
 #include "nativescript/register_types.h"
 
+#include "core/engine.h"
 #include "core/os/os.h"
+#include "core/project_settings.h"
+
+#ifdef TOOLS_ENABLED
+#include "editor/editor_node.h"
+
+// Class used to discover singleton gdnative files
+
+void actual_discoverer_handler();
+
+class GDNativeSingletonDiscover : public Object {
+	// GDCLASS(GDNativeSingletonDiscover, Object)
+
+	virtual String get_class() const {
+		// okay, this is a really dirty hack.
+		// We're overriding get_class so we can connect it to a signal
+		// This works because get_class is a virtual method, so we don't
+		// need to register a new class to ClassDB just for this one
+		// little signal.
+
+		actual_discoverer_handler();
+
+		return "Object";
+	}
+};
+
+Set<String> get_gdnative_singletons(EditorFileSystemDirectory *p_dir) {
+
+	Set<String> file_paths;
+
+	// check children
+
+	for (int i = 0; i < p_dir->get_file_count(); i++) {
+		String file_name = p_dir->get_file(i);
+		String file_type = p_dir->get_file_type(i);
+
+		if (file_type != "GDNativeLibrary") {
+			continue;
+		}
+
+		Ref<GDNativeLibrary> lib = ResourceLoader::load(p_dir->get_file_path(i));
+		if (lib.is_valid() && lib->is_singleton_gdnative()) {
+			file_paths.insert(p_dir->get_file_path(i));
+		}
+	}
+
+	// check subdirectories
+	for (int i = 0; i < p_dir->get_subdir_count(); i++) {
+		Set<String> paths = get_gdnative_singletons(p_dir->get_subdir(i));
+
+		for (Set<String>::Element *E = paths.front(); E; E = E->next()) {
+			file_paths.insert(E->get());
+		}
+	}
+
+	return file_paths;
+}
+
+void actual_discoverer_handler() {
+	EditorFileSystemDirectory *dir = EditorFileSystem::get_singleton()->get_filesystem();
+
+	Set<String> file_paths = get_gdnative_singletons(dir);
+
+	Array files;
+	files.resize(file_paths.size());
+	int i = 0;
+	for (Set<String>::Element *E = file_paths.front(); E; i++, E = E->next()) {
+		files.set(i, E->get());
+	}
+
+	ProjectSettings::get_singleton()->set("gdnative/singletons", files);
+
+	ProjectSettings::get_singleton()->save();
+}
+
+GDNativeSingletonDiscover *discoverer = NULL;
+
+void discoverer_callback() {
+	discoverer = memnew(GDNativeSingletonDiscover);
+	EditorFileSystem::get_singleton()->connect("filesystem_changed", discoverer, "get_class");
+}
+
+#endif
 
 godot_variant cb_standard_varcall(void *handle, godot_string *p_procedure, godot_array *p_args) {
 	if (handle == NULL) {
@@ -66,10 +149,45 @@ godot_variant cb_standard_varcall(void *handle, godot_string *p_procedure, godot
 	return proc(NULL, p_args);
 }
 
+void cb_singleton_call(
+		void *p_handle,
+		godot_string *p_proc_name,
+		void *p_data,
+		int p_num_args,
+		void **p_args,
+		void *r_return) {
+	if (p_handle == NULL) {
+		ERR_PRINT("No valid library handle, can't call singleton procedure");
+		return;
+	}
+
+	void *singleton_proc;
+	Error err = OS::get_singleton()->get_dynamic_library_symbol_handle(
+			p_handle,
+			*(String *)p_proc_name,
+			singleton_proc);
+
+	if (err != OK) {
+		return;
+	}
+
+	void (*singleton_procedure_ptr)() = (void (*)())singleton_proc;
+	singleton_procedure_ptr();
+}
+
 GDNativeCallRegistry *GDNativeCallRegistry::singleton;
 
+Vector<Ref<GDNative> > singleton_gdnatives;
+
 void register_gdnative_types() {
 
+#ifdef TOOLS_ENABLED
+
+	if (Engine::get_singleton()->is_editor_hint()) {
+		EditorNode::add_init_callback(discoverer_callback);
+	}
+#endif
+
 	ClassDB::register_class<GDNativeLibrary>();
 	ClassDB::register_class<GDNative>();
 
@@ -77,15 +195,64 @@ void register_gdnative_types() {
 
 	GDNativeCallRegistry::singleton->register_native_call_type("standard_varcall", cb_standard_varcall);
 
+	GDNativeCallRegistry::singleton->register_native_raw_call_type("gdnative_singleton_call", cb_singleton_call);
+
 	register_nativescript_types();
+
+	// run singletons
+
+	Array singletons = ProjectSettings::get_singleton()->get("gdnative/singletons");
+
+	singleton_gdnatives.resize(singletons.size());
+
+	for (int i = 0; i < singletons.size(); i++) {
+		String path = singletons[i];
+
+		Ref<GDNativeLibrary> lib = ResourceLoader::load(path);
+
+		singleton_gdnatives[i].instance();
+		singleton_gdnatives[i]->set_library(lib);
+
+		if (!singleton_gdnatives[i]->initialize()) {
+			// Can't initialize. Don't make a native_call then
+			continue;
+		}
+
+		singleton_gdnatives[i]->call_native_raw(
+				"gdnative_singleton_call",
+				"godot_gdnative_singleton",
+				NULL,
+				0,
+				NULL,
+				NULL);
+	}
 }
 
 void unregister_gdnative_types() {
 
+	for (int i = 0; i < singleton_gdnatives.size(); i++) {
+
+		if (singleton_gdnatives[i].is_null()) {
+			continue;
+		}
+
+		if (!singleton_gdnatives[i]->is_initialized()) {
+			continue;
+		}
+
+		singleton_gdnatives[i]->terminate();
+	}
+
 	unregister_nativescript_types();
 
 	memdelete(GDNativeCallRegistry::singleton);
 
+#ifdef TOOLS_ENABLED
+	if (Engine::get_singleton()->is_editor_hint()) {
+		memdelete(discoverer);
+	}
+#endif
+
 	// This is for printing out the sizes of the core types
 
 	/*