1/**************************************************************************/
2/* gdextension_manager.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "gdextension_manager.h"
32#include "core/io/file_access.h"
33
34GDExtensionManager::LoadStatus GDExtensionManager::load_extension(const String &p_path) {
35 if (gdextension_map.has(p_path)) {
36 return LOAD_STATUS_ALREADY_LOADED;
37 }
38 Ref<GDExtension> extension = ResourceLoader::load(p_path);
39 if (extension.is_null()) {
40 return LOAD_STATUS_FAILED;
41 }
42
43 if (level >= 0) { // Already initialized up to some level.
44 int32_t minimum_level = extension->get_minimum_library_initialization_level();
45 if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) {
46 return LOAD_STATUS_NEEDS_RESTART;
47 }
48 // Initialize up to current level.
49 for (int32_t i = minimum_level; i <= level; i++) {
50 extension->initialize_library(GDExtension::InitializationLevel(i));
51 }
52 }
53
54 for (const KeyValue<String, String> &kv : extension->class_icon_paths) {
55 gdextension_class_icon_paths[kv.key] = kv.value;
56 }
57
58 gdextension_map[p_path] = extension;
59 return LOAD_STATUS_OK;
60}
61
62GDExtensionManager::LoadStatus GDExtensionManager::reload_extension(const String &p_path) {
63 return LOAD_STATUS_OK; //TODO
64}
65GDExtensionManager::LoadStatus GDExtensionManager::unload_extension(const String &p_path) {
66 if (!gdextension_map.has(p_path)) {
67 return LOAD_STATUS_NOT_LOADED;
68 }
69
70 Ref<GDExtension> extension = gdextension_map[p_path];
71
72 if (level >= 0) { // Already initialized up to some level.
73 int32_t minimum_level = extension->get_minimum_library_initialization_level();
74 if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) {
75 return LOAD_STATUS_NEEDS_RESTART;
76 }
77 // Deinitialize down to current level.
78 for (int32_t i = level; i >= minimum_level; i--) {
79 extension->deinitialize_library(GDExtension::InitializationLevel(i));
80 }
81 }
82
83 for (const KeyValue<String, String> &kv : extension->class_icon_paths) {
84 gdextension_class_icon_paths.erase(kv.key);
85 }
86
87 gdextension_map.erase(p_path);
88 return LOAD_STATUS_OK;
89}
90
91bool GDExtensionManager::is_extension_loaded(const String &p_path) const {
92 return gdextension_map.has(p_path);
93}
94
95Vector<String> GDExtensionManager::get_loaded_extensions() const {
96 Vector<String> ret;
97 for (const KeyValue<String, Ref<GDExtension>> &E : gdextension_map) {
98 ret.push_back(E.key);
99 }
100 return ret;
101}
102Ref<GDExtension> GDExtensionManager::get_extension(const String &p_path) {
103 HashMap<String, Ref<GDExtension>>::Iterator E = gdextension_map.find(p_path);
104 ERR_FAIL_COND_V(!E, Ref<GDExtension>());
105 return E->value;
106}
107
108bool GDExtensionManager::class_has_icon_path(const String &p_class) const {
109 // TODO: Check that the icon belongs to a registered class somehow.
110 return gdextension_class_icon_paths.has(p_class);
111}
112
113String GDExtensionManager::class_get_icon_path(const String &p_class) const {
114 // TODO: Check that the icon belongs to a registered class somehow.
115 if (gdextension_class_icon_paths.has(p_class)) {
116 return gdextension_class_icon_paths[p_class];
117 }
118 return "";
119}
120
121void GDExtensionManager::initialize_extensions(GDExtension::InitializationLevel p_level) {
122 ERR_FAIL_COND(int32_t(p_level) - 1 != level);
123 for (KeyValue<String, Ref<GDExtension>> &E : gdextension_map) {
124 E.value->initialize_library(p_level);
125 }
126 level = p_level;
127}
128
129void GDExtensionManager::deinitialize_extensions(GDExtension::InitializationLevel p_level) {
130 ERR_FAIL_COND(int32_t(p_level) != level);
131 for (KeyValue<String, Ref<GDExtension>> &E : gdextension_map) {
132 E.value->deinitialize_library(p_level);
133 }
134 level = int32_t(p_level) - 1;
135}
136
137void GDExtensionManager::load_extensions() {
138 Ref<FileAccess> f = FileAccess::open(GDExtension::get_extension_list_config_file(), FileAccess::READ);
139 while (f.is_valid() && !f->eof_reached()) {
140 String s = f->get_line().strip_edges();
141 if (!s.is_empty()) {
142 LoadStatus err = load_extension(s);
143 ERR_CONTINUE_MSG(err == LOAD_STATUS_FAILED, "Error loading extension: " + s);
144 }
145 }
146
147 OS::get_singleton()->load_platform_gdextensions();
148}
149
150GDExtensionManager *GDExtensionManager::get_singleton() {
151 return singleton;
152}
153void GDExtensionManager::_bind_methods() {
154 ClassDB::bind_method(D_METHOD("load_extension", "path"), &GDExtensionManager::load_extension);
155 ClassDB::bind_method(D_METHOD("reload_extension", "path"), &GDExtensionManager::reload_extension);
156 ClassDB::bind_method(D_METHOD("unload_extension", "path"), &GDExtensionManager::unload_extension);
157 ClassDB::bind_method(D_METHOD("is_extension_loaded", "path"), &GDExtensionManager::is_extension_loaded);
158
159 ClassDB::bind_method(D_METHOD("get_loaded_extensions"), &GDExtensionManager::get_loaded_extensions);
160 ClassDB::bind_method(D_METHOD("get_extension", "path"), &GDExtensionManager::get_extension);
161
162 BIND_ENUM_CONSTANT(LOAD_STATUS_OK);
163 BIND_ENUM_CONSTANT(LOAD_STATUS_FAILED);
164 BIND_ENUM_CONSTANT(LOAD_STATUS_ALREADY_LOADED);
165 BIND_ENUM_CONSTANT(LOAD_STATUS_NOT_LOADED);
166 BIND_ENUM_CONSTANT(LOAD_STATUS_NEEDS_RESTART);
167}
168
169GDExtensionManager *GDExtensionManager::singleton = nullptr;
170
171GDExtensionManager::GDExtensionManager() {
172 ERR_FAIL_COND(singleton != nullptr);
173 singleton = this;
174}
175