Browse Source

Merge pull request #51928 from reduz/extension-loader

Implement Extension Loader
Juan Linietsky 4 năm trước cách đây
mục cha
commit
eb940ca2a0

+ 2 - 0
core/extension/native_extension.cpp

@@ -35,6 +35,8 @@
 #include "core/object/method_bind.h"
 #include "core/os/os.h"
 
+const char *NativeExtension::EXTENSION_LIST_CONFIG_FILE = "res://.godot/extension_list.cfg";
+
 class NativeExtensionMethodBind : public MethodBind {
 	GDNativeExtensionClassMethodCall call_func;
 	GDNativeExtensionClassMethodPtrCall ptrcall_func;

+ 2 - 0
core/extension/native_extension.h

@@ -60,6 +60,8 @@ protected:
 	static void _bind_methods();
 
 public:
+	static const char *EXTENSION_LIST_CONFIG_FILE;
+
 	Error open_library(const String &p_path, const String &p_entry_symbol);
 	void close_library();
 

+ 19 - 0
core/extension/native_extension_manager.cpp

@@ -29,6 +29,7 @@
 /*************************************************************************/
 
 #include "native_extension_manager.h"
+#include "core/io/file_access.h"
 
 NativeExtensionManager::LoadStatus NativeExtensionManager::load_extension(const String &p_path) {
 	if (native_extension_map.has(p_path)) {
@@ -76,6 +77,11 @@ NativeExtensionManager::LoadStatus NativeExtensionManager::unload_extension(cons
 	native_extension_map.erase(p_path);
 	return LOAD_STATUS_OK;
 }
+
+bool NativeExtensionManager::is_extension_loaded(const String &p_path) const {
+	return native_extension_map.has(p_path);
+}
+
 Vector<String> NativeExtensionManager::get_loaded_extensions() const {
 	Vector<String> ret;
 	for (const Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.front(); E; E = E->next()) {
@@ -105,6 +111,17 @@ void NativeExtensionManager::deinitialize_extensions(NativeExtension::Initializa
 	level = int32_t(p_level) - 1;
 }
 
+void NativeExtensionManager::load_extensions() {
+	FileAccessRef f = FileAccess::open(NativeExtension::EXTENSION_LIST_CONFIG_FILE, FileAccess::READ);
+	while (f && !f->eof_reached()) {
+		String s = f->get_line().strip_edges();
+		if (s != String()) {
+			LoadStatus err = load_extension(s);
+			ERR_CONTINUE_MSG(err == LOAD_STATUS_FAILED, "Error loading extension: " + s);
+		}
+	}
+}
+
 NativeExtensionManager *NativeExtensionManager::get_singleton() {
 	return singleton;
 }
@@ -112,6 +129,8 @@ void NativeExtensionManager::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("load_extension", "path"), &NativeExtensionManager::load_extension);
 	ClassDB::bind_method(D_METHOD("reload_extension", "path"), &NativeExtensionManager::reload_extension);
 	ClassDB::bind_method(D_METHOD("unload_extension", "path"), &NativeExtensionManager::unload_extension);
+	ClassDB::bind_method(D_METHOD("is_extension_loaded", "path"), &NativeExtensionManager::is_extension_loaded);
+
 	ClassDB::bind_method(D_METHOD("get_loaded_extensions"), &NativeExtensionManager::get_loaded_extensions);
 	ClassDB::bind_method(D_METHOD("get_extension", "path"), &NativeExtensionManager::get_extension);
 

+ 3 - 0
core/extension/native_extension_manager.h

@@ -55,6 +55,7 @@ public:
 	LoadStatus load_extension(const String &p_path);
 	LoadStatus reload_extension(const String &p_path);
 	LoadStatus unload_extension(const String &p_path);
+	bool is_extension_loaded(const String &p_path) const;
 	Vector<String> get_loaded_extensions() const;
 	Ref<NativeExtension> get_extension(const String &p_path);
 
@@ -63,6 +64,8 @@ public:
 
 	static NativeExtensionManager *get_singleton();
 
+	void load_extensions();
+
 	NativeExtensionManager();
 };
 

+ 1 - 7
core/register_core_types.cpp

@@ -308,13 +308,7 @@ void register_core_singletons() {
 void register_core_extensions() {
 	// Hardcoded for now.
 	NativeExtension::initialize_native_extensions();
-	if (ProjectSettings::get_singleton()->has_setting("native_extensions/paths")) {
-		Vector<String> paths = ProjectSettings::get_singleton()->get("native_extensions/paths");
-		for (int i = 0; i < paths.size(); i++) {
-			NativeExtensionManager::LoadStatus status = native_extension_manager->load_extension(paths[i]);
-			ERR_CONTINUE_MSG(status != NativeExtensionManager::LOAD_STATUS_OK, "Error loading extension: " + paths[i]);
-		}
-	}
+	native_extension_manager->load_extensions();
 	native_extension_manager->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_CORE);
 }
 

+ 6 - 0
doc/classes/NativeExtensionManager.xml

@@ -18,6 +18,12 @@
 			<description>
 			</description>
 		</method>
+		<method name="is_extension_loaded" qualifiers="const">
+			<return type="bool" />
+			<argument index="0" name="path" type="String" />
+			<description>
+			</description>
+		</method>
 		<method name="load_extension">
 			<return type="int" enum="NativeExtensionManager.LoadStatus" />
 			<argument index="0" name="path" type="String" />

+ 9 - 0
editor/editor_export.cpp

@@ -32,6 +32,7 @@
 
 #include "core/config/project_settings.h"
 #include "core/crypto/crypto_core.h"
+#include "core/extension/native_extension.h"
 #include "core/io/config_file.h"
 #include "core/io/dir_access.h"
 #include "core/io/file_access.h"
@@ -1050,6 +1051,14 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
 		}
 	}
 
+	if (FileAccess::exists(NativeExtension::EXTENSION_LIST_CONFIG_FILE)) {
+		Vector<uint8_t> array = FileAccess::get_file_as_array(NativeExtension::EXTENSION_LIST_CONFIG_FILE);
+		err = p_func(p_udata, NativeExtension::EXTENSION_LIST_CONFIG_FILE, array, idx, total, enc_in_filters, enc_ex_filters, key);
+		if (err != OK) {
+			return err;
+		}
+	}
+
 	// Store text server data if it is supported.
 	if (TS->has_feature(TextServer::FEATURE_USE_SUPPORT_DATA)) {
 		bool use_data = ProjectSettings::get_singleton()->get("internationalization/locale/include_text_server_data");

+ 83 - 0
editor/editor_file_system.cpp

@@ -31,6 +31,7 @@
 #include "editor_file_system.h"
 
 #include "core/config/project_settings.h"
+#include "core/extension/native_extension_manager.h"
 #include "core/io/file_access.h"
 #include "core/io/resource_importer.h"
 #include "core/io/resource_loader.h"
@@ -605,6 +606,18 @@ bool EditorFileSystem::_update_scan_actions() {
 		}
 	}
 
+	if (_scan_extensions()) {
+		//needs editor restart
+		//extensions also may provide filetypes to be imported, so they must run before importing
+		if (EditorNode::immediate_confirmation_dialog(TTR("Some extensions need the editor to restart to take effect."), first_scan ? TTR("Restart") : TTR("Save&Restart"), TTR("Continue"))) {
+			if (!first_scan) {
+				EditorNode::get_singleton()->save_all_scenes();
+			}
+			EditorNode::get_singleton()->restart_editor();
+			//do not import
+			return true;
+		}
+	}
 	if (reimports.size()) {
 		reimport_files(reimports);
 	} else {
@@ -2222,6 +2235,76 @@ ResourceUID::ID EditorFileSystem::_resource_saver_get_resource_id_for_path(const
 	}
 }
 
+static void _scan_extensions_dir(EditorFileSystemDirectory *d, Set<String> &extensions) {
+	int fc = d->get_file_count();
+	for (int i = 0; i < fc; i++) {
+		if (d->get_file_type(i) == SNAME("NativeExtension")) {
+			extensions.insert(d->get_file_path(i));
+		}
+	}
+	int dc = d->get_subdir_count();
+	for (int i = 0; i < dc; i++) {
+		_scan_extensions_dir(d->get_subdir(i), extensions);
+	}
+}
+bool EditorFileSystem::_scan_extensions() {
+	EditorFileSystemDirectory *d = get_filesystem();
+	Set<String> extensions;
+	_scan_extensions_dir(d, extensions);
+
+	//verify against loaded extensions
+
+	Vector<String> extensions_added;
+	Vector<String> extensions_removed;
+
+	for (const String &E : extensions) {
+		if (!NativeExtensionManager::get_singleton()->is_extension_loaded(E)) {
+			extensions_added.push_back(E);
+		}
+	}
+
+	Vector<String> loaded_extensions = NativeExtensionManager::get_singleton()->get_loaded_extensions();
+	for (int i = 0; i < loaded_extensions.size(); i++) {
+		if (!extensions.has(loaded_extensions[i])) {
+			extensions_removed.push_back(loaded_extensions[i]);
+		}
+	}
+
+	if (extensions.size()) {
+		if (extensions_added.size() || extensions_removed.size()) { //extensions were added or removed
+			FileAccessRef f = FileAccess::open(NativeExtension::EXTENSION_LIST_CONFIG_FILE, FileAccess::WRITE);
+			for (const String &E : extensions) {
+				f->store_line(E);
+			}
+		}
+	} else {
+		if (loaded_extensions.size() || FileAccess::exists(NativeExtension::EXTENSION_LIST_CONFIG_FILE)) { //extensions were removed
+			DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+			da->remove(NativeExtension::EXTENSION_LIST_CONFIG_FILE);
+		}
+	}
+
+	bool needs_restart = false;
+	for (int i = 0; i < extensions_added.size(); i++) {
+		NativeExtensionManager::LoadStatus st = NativeExtensionManager::get_singleton()->load_extension(extensions_added[i]);
+		if (st == NativeExtensionManager::LOAD_STATUS_FAILED) {
+			EditorNode::get_singleton()->add_io_error("Error loading extension: " + extensions_added[i]);
+		} else if (st == NativeExtensionManager::LOAD_STATUS_NEEDS_RESTART) {
+			needs_restart = true;
+		}
+	}
+	for (int i = 0; i < extensions_removed.size(); i++) {
+		NativeExtensionManager::LoadStatus st = NativeExtensionManager::get_singleton()->unload_extension(extensions_removed[i]);
+		if (st == NativeExtensionManager::LOAD_STATUS_FAILED) {
+			EditorNode::get_singleton()->add_io_error("Error removing extension: " + extensions_added[i]);
+		} else if (st == NativeExtensionManager::LOAD_STATUS_NEEDS_RESTART) {
+			needs_restart = true;
+		}
+	}
+
+	return needs_restart;
+}
+
 void EditorFileSystem::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_filesystem"), &EditorFileSystem::get_filesystem);
 	ClassDB::bind_method(D_METHOD("is_scanning"), &EditorFileSystem::is_scanning);

+ 2 - 0
editor/editor_file_system.h

@@ -255,6 +255,8 @@ class EditorFileSystem : public Node {
 
 	static ResourceUID::ID _resource_saver_get_resource_id_for_path(const String &p_path, bool p_generate);
 
+	bool _scan_extensions();
+
 protected:
 	void _notification(int p_what);
 	static void _bind_methods();

+ 26 - 1
editor/editor_node.cpp

@@ -4800,6 +4800,32 @@ String EditorNode::get_run_playing_scene() const {
 	return run_filename;
 }
 
+void EditorNode::_immediate_dialog_confirmed() {
+	immediate_dialog_confirmed = true;
+}
+bool EditorNode::immediate_confirmation_dialog(const String &p_text, const String &p_ok_text, const String &p_cancel_text) {
+	ConfirmationDialog *cd = memnew(ConfirmationDialog);
+	cd->set_text(p_text);
+	cd->get_ok_button()->set_text(p_ok_text);
+	cd->get_cancel_button()->set_text(p_cancel_text);
+	cd->connect("confirmed", callable_mp(singleton, &EditorNode::_immediate_dialog_confirmed));
+	singleton->gui_base->add_child(cd);
+
+	cd->popup_centered();
+
+	while (true) {
+		OS::get_singleton()->delay_usec(1);
+		DisplayServer::get_singleton()->process_events();
+		Main::iteration();
+		if (singleton->immediate_dialog_confirmed || !cd->is_visible()) {
+			break;
+		}
+	}
+
+	memdelete(cd);
+	return singleton->immediate_dialog_confirmed;
+}
+
 int EditorNode::get_current_tab() {
 	return scene_tabs->get_current_tab();
 }
@@ -6793,7 +6819,6 @@ EditorNode::EditorNode() {
 
 	preview_gen = memnew(AudioStreamPreviewGenerator);
 	add_child(preview_gen);
-	//plugin stuff
 
 	add_editor_plugin(memnew(DebuggerEditorPlugin(this, debug_menu)));
 	add_editor_plugin(memnew(DebugAdapterServer()));

+ 6 - 0
editor/editor_node.h

@@ -92,6 +92,7 @@ class VSplitContainer;
 class Window;
 class SubViewport;
 class SceneImportSettings;
+class EditorExtensionManager;
 
 class EditorNode : public Node {
 	GDCLASS(EditorNode, Node);
@@ -675,6 +676,9 @@ private:
 
 	void _pick_main_scene_custom_action(const String &p_custom_action_name);
 
+	bool immediate_dialog_confirmed = false;
+	void _immediate_dialog_confirmed();
+
 protected:
 	void _notification(int p_what);
 
@@ -898,6 +902,8 @@ public:
 	void run_stop();
 	bool is_run_playing() const;
 	String get_run_playing_scene() const;
+
+	static bool immediate_confirmation_dialog(const String &p_text, const String &p_ok_text = TTR("Ok"), const String &p_cancel_text = TTR("Cancel"));
 };
 
 struct EditorProgress {