You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-06 12:20:30 +00:00
Implement Extension Loader
* Extensions are now scanned and loaded on demand. * Extensions found are cached into a file that is used to load them (which is also exported). * Editor will ask to restart when an extension requires core functionality. * Editor will attempt to load extensions always before importing or loading scenes. This ensures extensions can register the relevant types.
This commit is contained in:
@@ -35,6 +35,8 @@
|
|||||||
#include "core/object/method_bind.h"
|
#include "core/object/method_bind.h"
|
||||||
#include "core/os/os.h"
|
#include "core/os/os.h"
|
||||||
|
|
||||||
|
const char *NativeExtension::EXTENSION_LIST_CONFIG_FILE = "res://.godot/extension_list.cfg";
|
||||||
|
|
||||||
class NativeExtensionMethodBind : public MethodBind {
|
class NativeExtensionMethodBind : public MethodBind {
|
||||||
GDNativeExtensionClassMethodCall call_func;
|
GDNativeExtensionClassMethodCall call_func;
|
||||||
GDNativeExtensionClassMethodPtrCall ptrcall_func;
|
GDNativeExtensionClassMethodPtrCall ptrcall_func;
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ protected:
|
|||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static const char *EXTENSION_LIST_CONFIG_FILE;
|
||||||
|
|
||||||
Error open_library(const String &p_path, const String &p_entry_symbol);
|
Error open_library(const String &p_path, const String &p_entry_symbol);
|
||||||
void close_library();
|
void close_library();
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
|
|
||||||
#include "native_extension_manager.h"
|
#include "native_extension_manager.h"
|
||||||
|
#include "core/io/file_access.h"
|
||||||
|
|
||||||
NativeExtensionManager::LoadStatus NativeExtensionManager::load_extension(const String &p_path) {
|
NativeExtensionManager::LoadStatus NativeExtensionManager::load_extension(const String &p_path) {
|
||||||
if (native_extension_map.has(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);
|
native_extension_map.erase(p_path);
|
||||||
return LOAD_STATUS_OK;
|
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> NativeExtensionManager::get_loaded_extensions() const {
|
||||||
Vector<String> ret;
|
Vector<String> ret;
|
||||||
for (const Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.front(); E; E = E->next()) {
|
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;
|
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() {
|
NativeExtensionManager *NativeExtensionManager::get_singleton() {
|
||||||
return 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("load_extension", "path"), &NativeExtensionManager::load_extension);
|
||||||
ClassDB::bind_method(D_METHOD("reload_extension", "path"), &NativeExtensionManager::reload_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("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_loaded_extensions"), &NativeExtensionManager::get_loaded_extensions);
|
||||||
ClassDB::bind_method(D_METHOD("get_extension", "path"), &NativeExtensionManager::get_extension);
|
ClassDB::bind_method(D_METHOD("get_extension", "path"), &NativeExtensionManager::get_extension);
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ public:
|
|||||||
LoadStatus load_extension(const String &p_path);
|
LoadStatus load_extension(const String &p_path);
|
||||||
LoadStatus reload_extension(const String &p_path);
|
LoadStatus reload_extension(const String &p_path);
|
||||||
LoadStatus unload_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;
|
Vector<String> get_loaded_extensions() const;
|
||||||
Ref<NativeExtension> get_extension(const String &p_path);
|
Ref<NativeExtension> get_extension(const String &p_path);
|
||||||
|
|
||||||
@@ -63,6 +64,8 @@ public:
|
|||||||
|
|
||||||
static NativeExtensionManager *get_singleton();
|
static NativeExtensionManager *get_singleton();
|
||||||
|
|
||||||
|
void load_extensions();
|
||||||
|
|
||||||
NativeExtensionManager();
|
NativeExtensionManager();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -305,13 +305,7 @@ void register_core_singletons() {
|
|||||||
void register_core_extensions() {
|
void register_core_extensions() {
|
||||||
// Hardcoded for now.
|
// Hardcoded for now.
|
||||||
NativeExtension::initialize_native_extensions();
|
NativeExtension::initialize_native_extensions();
|
||||||
if (ProjectSettings::get_singleton()->has_setting("native_extensions/paths")) {
|
native_extension_manager->load_extensions();
|
||||||
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->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_CORE);
|
native_extension_manager->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_CORE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,12 @@
|
|||||||
<description>
|
<description>
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</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">
|
<method name="load_extension">
|
||||||
<return type="int" enum="NativeExtensionManager.LoadStatus" />
|
<return type="int" enum="NativeExtensionManager.LoadStatus" />
|
||||||
<argument index="0" name="path" type="String" />
|
<argument index="0" name="path" type="String" />
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include "core/config/project_settings.h"
|
#include "core/config/project_settings.h"
|
||||||
#include "core/crypto/crypto_core.h"
|
#include "core/crypto/crypto_core.h"
|
||||||
|
#include "core/extension/native_extension.h"
|
||||||
#include "core/io/config_file.h"
|
#include "core/io/config_file.h"
|
||||||
#include "core/io/dir_access.h"
|
#include "core/io/dir_access.h"
|
||||||
#include "core/io/file_access.h"
|
#include "core/io/file_access.h"
|
||||||
@@ -1056,6 +1057,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.
|
// Store text server data if it is supported.
|
||||||
if (TS->has_feature(TextServer::FEATURE_USE_SUPPORT_DATA)) {
|
if (TS->has_feature(TextServer::FEATURE_USE_SUPPORT_DATA)) {
|
||||||
bool use_data = ProjectSettings::get_singleton()->get("internationalization/locale/include_text_server_data");
|
bool use_data = ProjectSettings::get_singleton()->get("internationalization/locale/include_text_server_data");
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "editor_file_system.h"
|
#include "editor_file_system.h"
|
||||||
|
|
||||||
#include "core/config/project_settings.h"
|
#include "core/config/project_settings.h"
|
||||||
|
#include "core/extension/native_extension_manager.h"
|
||||||
#include "core/io/file_access.h"
|
#include "core/io/file_access.h"
|
||||||
#include "core/io/resource_importer.h"
|
#include "core/io/resource_importer.h"
|
||||||
#include "core/io/resource_loader.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()) {
|
if (reimports.size()) {
|
||||||
reimport_files(reimports);
|
reimport_files(reimports);
|
||||||
} else {
|
} 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() {
|
void EditorFileSystem::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("get_filesystem"), &EditorFileSystem::get_filesystem);
|
ClassDB::bind_method(D_METHOD("get_filesystem"), &EditorFileSystem::get_filesystem);
|
||||||
ClassDB::bind_method(D_METHOD("is_scanning"), &EditorFileSystem::is_scanning);
|
ClassDB::bind_method(D_METHOD("is_scanning"), &EditorFileSystem::is_scanning);
|
||||||
|
|||||||
@@ -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);
|
static ResourceUID::ID _resource_saver_get_resource_id_for_path(const String &p_path, bool p_generate);
|
||||||
|
|
||||||
|
bool _scan_extensions();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void _notification(int p_what);
|
void _notification(int p_what);
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|||||||
@@ -4798,6 +4798,32 @@ String EditorNode::get_run_playing_scene() const {
|
|||||||
return run_filename;
|
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() {
|
int EditorNode::get_current_tab() {
|
||||||
return scene_tabs->get_current_tab();
|
return scene_tabs->get_current_tab();
|
||||||
}
|
}
|
||||||
@@ -6792,7 +6818,6 @@ EditorNode::EditorNode() {
|
|||||||
|
|
||||||
preview_gen = memnew(AudioStreamPreviewGenerator);
|
preview_gen = memnew(AudioStreamPreviewGenerator);
|
||||||
add_child(preview_gen);
|
add_child(preview_gen);
|
||||||
//plugin stuff
|
|
||||||
|
|
||||||
add_editor_plugin(memnew(DebuggerEditorPlugin(this, debug_menu)));
|
add_editor_plugin(memnew(DebuggerEditorPlugin(this, debug_menu)));
|
||||||
add_editor_plugin(memnew(DebugAdapterServer()));
|
add_editor_plugin(memnew(DebugAdapterServer()));
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ class VSplitContainer;
|
|||||||
class Window;
|
class Window;
|
||||||
class SubViewport;
|
class SubViewport;
|
||||||
class SceneImportSettings;
|
class SceneImportSettings;
|
||||||
|
class EditorExtensionManager;
|
||||||
|
|
||||||
class EditorNode : public Node {
|
class EditorNode : public Node {
|
||||||
GDCLASS(EditorNode, Node);
|
GDCLASS(EditorNode, Node);
|
||||||
@@ -675,6 +676,9 @@ private:
|
|||||||
|
|
||||||
void _pick_main_scene_custom_action(const String &p_custom_action_name);
|
void _pick_main_scene_custom_action(const String &p_custom_action_name);
|
||||||
|
|
||||||
|
bool immediate_dialog_confirmed = false;
|
||||||
|
void _immediate_dialog_confirmed();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void _notification(int p_what);
|
void _notification(int p_what);
|
||||||
|
|
||||||
@@ -898,6 +902,8 @@ public:
|
|||||||
void run_stop();
|
void run_stop();
|
||||||
bool is_run_playing() const;
|
bool is_run_playing() const;
|
||||||
String get_run_playing_scene() 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 {
|
struct EditorProgress {
|
||||||
|
|||||||
Reference in New Issue
Block a user