gd_mono.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. /*************************************************************************/
  2. /* gd_mono.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
  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. #include "gd_mono.h"
  31. #include "core/config/project_settings.h"
  32. #include "core/debugger/engine_debugger.h"
  33. #include "core/io/dir_access.h"
  34. #include "core/io/file_access.h"
  35. #include "core/os/os.h"
  36. #include "core/os/thread.h"
  37. #include "../csharp_script.h"
  38. #include "../glue/runtime_interop.h"
  39. #include "../godotsharp_dirs.h"
  40. #include "../utils/path_utils.h"
  41. #include "gd_mono_cache.h"
  42. #ifdef TOOLS_ENABLED
  43. #include <nethost.h>
  44. #endif
  45. #include <coreclr_delegates.h>
  46. #include <hostfxr.h>
  47. #ifdef UNIX_ENABLED
  48. #include <dlfcn.h>
  49. #endif
  50. // TODO mobile
  51. #if 0
  52. #ifdef ANDROID_ENABLED
  53. #include "support/android_support.h"
  54. #elif defined(IOS_ENABLED)
  55. #include "support/ios_support.h"
  56. #endif
  57. #endif
  58. GDMono *GDMono::singleton = nullptr;
  59. namespace {
  60. hostfxr_initialize_for_dotnet_command_line_fn hostfxr_initialize_for_dotnet_command_line = nullptr;
  61. hostfxr_initialize_for_runtime_config_fn hostfxr_initialize_for_runtime_config = nullptr;
  62. hostfxr_get_runtime_delegate_fn hostfxr_get_runtime_delegate = nullptr;
  63. hostfxr_close_fn hostfxr_close = nullptr;
  64. #ifdef _WIN32
  65. static_assert(sizeof(char_t) == sizeof(char16_t));
  66. using HostFxrCharString = Char16String;
  67. #define HOSTFXR_STR(m_str) L##m_str
  68. #else
  69. static_assert(sizeof(char_t) == sizeof(char));
  70. using HostFxrCharString = CharString;
  71. #define HOSTFXR_STR(m_str) m_str
  72. #endif
  73. HostFxrCharString str_to_hostfxr(const String &p_str) {
  74. #ifdef _WIN32
  75. return p_str.utf16();
  76. #else
  77. return p_str.utf8();
  78. #endif
  79. }
  80. #ifdef TOOLS_ENABLED
  81. String str_from_hostfxr(const char_t *p_buffer) {
  82. #ifdef _WIN32
  83. return String::utf16((const char16_t *)p_buffer);
  84. #else
  85. return String::utf8((const char *)p_buffer);
  86. #endif
  87. }
  88. #endif
  89. const char_t *get_data(const HostFxrCharString &p_char_str) {
  90. return (const char_t *)p_char_str.get_data();
  91. }
  92. #ifdef TOOLS_ENABLED
  93. String find_hostfxr(size_t p_known_buffet_size, get_hostfxr_parameters *p_get_hostfxr_params) {
  94. // Pre-allocate a large buffer for the path to hostfxr
  95. Vector<char_t> buffer;
  96. buffer.resize(p_known_buffet_size);
  97. int rc = get_hostfxr_path(buffer.ptrw(), &p_known_buffet_size, p_get_hostfxr_params);
  98. ERR_FAIL_COND_V_MSG(rc != 0, String(), "get_hostfxr_path failed with code: " + itos(rc));
  99. return str_from_hostfxr(buffer.ptr());
  100. }
  101. #endif
  102. String find_hostfxr() {
  103. #ifdef TOOLS_ENABLED
  104. const int CoreHostLibMissingFailure = 0x80008083;
  105. const int HostApiBufferTooSmall = 0x80008098;
  106. size_t buffer_size = 0;
  107. int rc = get_hostfxr_path(nullptr, &buffer_size, nullptr);
  108. if (rc == HostApiBufferTooSmall) {
  109. return find_hostfxr(buffer_size, nullptr);
  110. }
  111. if (rc == CoreHostLibMissingFailure) {
  112. // Apparently `get_hostfxr_path` doesn't look for dotnet in `PATH`? (I suppose it needs the
  113. // `DOTNET_ROOT` environment variable). If it fails, we try to find the dotnet executable
  114. // in `PATH` ourselves and pass its location as `dotnet_root` to `get_hostfxr_path`.
  115. String dotnet_exe = path::find_executable("dotnet");
  116. if (!dotnet_exe.is_empty()) {
  117. // The file found in PATH may be a symlink
  118. dotnet_exe = path::abspath(path::realpath(dotnet_exe));
  119. // TODO:
  120. // Sometimes, the symlink may not point to the dotnet executable in the dotnet root.
  121. // That's the case with snaps. The snap install should have been found with the
  122. // previous `get_hostfxr_path`, but it would still be better to do this properly
  123. // and use something like `dotnet --list-sdks/runtimes` to find the actual location.
  124. // This way we could also check if the proper sdk or runtime is installed. This would
  125. // allow us to fail gracefully and show some helpful information in the editor.
  126. HostFxrCharString dotnet_root = str_to_hostfxr(dotnet_exe.get_base_dir());
  127. get_hostfxr_parameters get_hostfxr_parameters = {
  128. sizeof(get_hostfxr_parameters),
  129. nullptr,
  130. get_data(dotnet_root)
  131. };
  132. buffer_size = 0;
  133. rc = get_hostfxr_path(nullptr, &buffer_size, &get_hostfxr_parameters);
  134. if (rc == HostApiBufferTooSmall) {
  135. return find_hostfxr(buffer_size, &get_hostfxr_parameters);
  136. }
  137. }
  138. }
  139. if (rc == CoreHostLibMissingFailure) {
  140. ERR_PRINT(String() + ".NET: One of the dependent libraries is missing. " +
  141. "Typically when the `hostfxr`, `hostpolicy` or `coreclr` dynamic " +
  142. "libraries are not present in the expected locations.");
  143. }
  144. return String();
  145. #else
  146. #if defined(WINDOWS_ENABLED)
  147. String probe_path = GodotSharpDirs::get_api_assemblies_dir()
  148. .plus_file("hostfxr.dll");
  149. #elif defined(MACOS_ENABLED)
  150. String probe_path = GodotSharpDirs::get_api_assemblies_dir()
  151. .plus_file("libhostfxr.dylib");
  152. #elif defined(UNIX_ENABLED)
  153. String probe_path = GodotSharpDirs::get_api_assemblies_dir()
  154. .plus_file("libhostfxr.so");
  155. #else
  156. #error "Platform not supported (yet?)"
  157. #endif
  158. if (FileAccess::exists(probe_path)) {
  159. return probe_path;
  160. }
  161. return String();
  162. #endif
  163. }
  164. bool load_hostfxr(void *&r_hostfxr_dll_handle) {
  165. String hostfxr_path = find_hostfxr();
  166. if (hostfxr_path.is_empty()) {
  167. return false;
  168. }
  169. print_verbose("Found hostfxr: " + hostfxr_path);
  170. Error err = OS::get_singleton()->open_dynamic_library(hostfxr_path, r_hostfxr_dll_handle);
  171. if (err != OK) {
  172. return false;
  173. }
  174. void *lib = r_hostfxr_dll_handle;
  175. void *symbol = nullptr;
  176. err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_initialize_for_dotnet_command_line", symbol);
  177. ERR_FAIL_COND_V(err != OK, false);
  178. hostfxr_initialize_for_dotnet_command_line = (hostfxr_initialize_for_dotnet_command_line_fn)symbol;
  179. err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_initialize_for_runtime_config", symbol);
  180. ERR_FAIL_COND_V(err != OK, false);
  181. hostfxr_initialize_for_runtime_config = (hostfxr_initialize_for_runtime_config_fn)symbol;
  182. err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_get_runtime_delegate", symbol);
  183. ERR_FAIL_COND_V(err != OK, false);
  184. hostfxr_get_runtime_delegate = (hostfxr_get_runtime_delegate_fn)symbol;
  185. err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_close", symbol);
  186. ERR_FAIL_COND_V(err != OK, false);
  187. hostfxr_close = (hostfxr_close_fn)symbol;
  188. return (hostfxr_initialize_for_runtime_config &&
  189. hostfxr_get_runtime_delegate &&
  190. hostfxr_close);
  191. }
  192. #ifdef TOOLS_ENABLED
  193. load_assembly_and_get_function_pointer_fn initialize_hostfxr_for_config(const char_t *p_config_path) {
  194. hostfxr_handle cxt = nullptr;
  195. int rc = hostfxr_initialize_for_runtime_config(p_config_path, nullptr, &cxt);
  196. if (rc != 0 || cxt == nullptr) {
  197. hostfxr_close(cxt);
  198. ERR_FAIL_V_MSG(nullptr, "hostfxr_initialize_for_runtime_config failed with code: " + itos(rc));
  199. }
  200. void *load_assembly_and_get_function_pointer = nullptr;
  201. rc = hostfxr_get_runtime_delegate(cxt,
  202. hdt_load_assembly_and_get_function_pointer, &load_assembly_and_get_function_pointer);
  203. if (rc != 0 || load_assembly_and_get_function_pointer == nullptr) {
  204. ERR_FAIL_V_MSG(nullptr, "hostfxr_get_runtime_delegate failed with code: " + itos(rc));
  205. }
  206. hostfxr_close(cxt);
  207. return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer;
  208. }
  209. #else
  210. load_assembly_and_get_function_pointer_fn initialize_hostfxr_self_contained(
  211. const char_t *p_main_assembly_path) {
  212. hostfxr_handle cxt = nullptr;
  213. List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
  214. List<HostFxrCharString> argv_store;
  215. Vector<const char_t *> argv;
  216. argv.resize(cmdline_args.size() + 1);
  217. argv.write[0] = p_main_assembly_path;
  218. int i = 1;
  219. for (const String &E : cmdline_args) {
  220. HostFxrCharString &stored = argv_store.push_back(str_to_hostfxr(E))->get();
  221. argv.write[i] = get_data(stored);
  222. i++;
  223. }
  224. int rc = hostfxr_initialize_for_dotnet_command_line(argv.size(), argv.ptrw(), nullptr, &cxt);
  225. if (rc != 0 || cxt == nullptr) {
  226. hostfxr_close(cxt);
  227. ERR_FAIL_V_MSG(nullptr, "hostfxr_initialize_for_dotnet_command_line failed with code: " + itos(rc));
  228. }
  229. void *load_assembly_and_get_function_pointer = nullptr;
  230. rc = hostfxr_get_runtime_delegate(cxt,
  231. hdt_load_assembly_and_get_function_pointer, &load_assembly_and_get_function_pointer);
  232. if (rc != 0 || load_assembly_and_get_function_pointer == nullptr) {
  233. ERR_FAIL_V_MSG(nullptr, "hostfxr_get_runtime_delegate failed with code: " + itos(rc));
  234. }
  235. hostfxr_close(cxt);
  236. return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer;
  237. }
  238. #endif
  239. #ifdef TOOLS_ENABLED
  240. using godot_plugins_initialize_fn = bool (*)(void *, bool, gdmono::PluginCallbacks *, GDMonoCache::ManagedCallbacks *, const void **, int32_t);
  241. #else
  242. using godot_plugins_initialize_fn = bool (*)(void *, GDMonoCache::ManagedCallbacks *, const void **, int32_t);
  243. #endif
  244. #ifdef TOOLS_ENABLED
  245. godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime_initialized) {
  246. godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
  247. HostFxrCharString godot_plugins_path = str_to_hostfxr(
  248. GodotSharpDirs::get_api_assemblies_dir().plus_file("GodotPlugins.dll"));
  249. HostFxrCharString config_path = str_to_hostfxr(
  250. GodotSharpDirs::get_api_assemblies_dir().plus_file("GodotPlugins.runtimeconfig.json"));
  251. load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer =
  252. initialize_hostfxr_for_config(get_data(config_path));
  253. ERR_FAIL_NULL_V(load_assembly_and_get_function_pointer, nullptr);
  254. r_runtime_initialized = true;
  255. print_verbose(".NET: hostfxr initialized");
  256. int rc = load_assembly_and_get_function_pointer(get_data(godot_plugins_path),
  257. HOSTFXR_STR("GodotPlugins.Main, GodotPlugins"),
  258. HOSTFXR_STR("InitializeFromEngine"),
  259. UNMANAGEDCALLERSONLY_METHOD,
  260. nullptr,
  261. (void **)&godot_plugins_initialize);
  262. ERR_FAIL_COND_V_MSG(rc != 0, nullptr, ".NET: Failed to get GodotPlugins initialization function pointer");
  263. return godot_plugins_initialize;
  264. }
  265. #else
  266. static String get_assembly_name() {
  267. String assembly_name = ProjectSettings::get_singleton()->get_setting("dotnet/project/assembly_name");
  268. if (assembly_name.is_empty()) {
  269. assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
  270. }
  271. return assembly_name;
  272. }
  273. godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime_initialized) {
  274. godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
  275. String assembly_name = get_assembly_name();
  276. HostFxrCharString assembly_path = str_to_hostfxr(GodotSharpDirs::get_api_assemblies_dir()
  277. .plus_file(assembly_name + ".dll"));
  278. load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer =
  279. initialize_hostfxr_self_contained(get_data(assembly_path));
  280. ERR_FAIL_NULL_V(load_assembly_and_get_function_pointer, nullptr);
  281. r_runtime_initialized = true;
  282. print_verbose(".NET: hostfxr initialized");
  283. int rc = load_assembly_and_get_function_pointer(get_data(assembly_path),
  284. get_data(str_to_hostfxr("GodotPlugins.Game.Main, " + assembly_name)),
  285. HOSTFXR_STR("InitializeFromGameProject"),
  286. UNMANAGEDCALLERSONLY_METHOD,
  287. nullptr,
  288. (void **)&godot_plugins_initialize);
  289. ERR_FAIL_COND_V_MSG(rc != 0, nullptr, ".NET: Failed to get GodotPlugins initialization function pointer");
  290. return godot_plugins_initialize;
  291. }
  292. godot_plugins_initialize_fn try_load_native_aot_library(void *&r_aot_dll_handle) {
  293. String assembly_name = get_assembly_name();
  294. #if defined(WINDOWS_ENABLED)
  295. String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().plus_file(assembly_name + ".dll");
  296. #elif defined(MACOS_ENABLED)
  297. String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().plus_file(assembly_name + ".dylib");
  298. #elif defined(UNIX_ENABLED)
  299. String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().plus_file(assembly_name + ".so");
  300. #else
  301. #error "Platform not supported (yet?)"
  302. #endif
  303. if (FileAccess::exists(native_aot_so_path)) {
  304. Error err = OS::get_singleton()->open_dynamic_library(native_aot_so_path, r_aot_dll_handle);
  305. if (err != OK) {
  306. return nullptr;
  307. }
  308. void *lib = r_aot_dll_handle;
  309. void *symbol = nullptr;
  310. err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "godotsharp_game_main_init", symbol);
  311. ERR_FAIL_COND_V(err != OK, nullptr);
  312. return (godot_plugins_initialize_fn)symbol;
  313. }
  314. return nullptr;
  315. }
  316. #endif
  317. } // namespace
  318. static bool _on_core_api_assembly_loaded() {
  319. if (!GDMonoCache::godot_api_cache_updated) {
  320. return false;
  321. }
  322. bool debug;
  323. #ifdef DEBUG_ENABLED
  324. debug = true;
  325. #else
  326. debug = false;
  327. #endif
  328. GDMonoCache::managed_callbacks.GD_OnCoreApiAssemblyLoaded(debug);
  329. return true;
  330. }
  331. void GDMono::initialize() {
  332. print_verbose(".NET: Initializing module...");
  333. _init_godot_api_hashes();
  334. godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
  335. if (!load_hostfxr(hostfxr_dll_handle)) {
  336. #if !defined(TOOLS_ENABLED)
  337. godot_plugins_initialize = try_load_native_aot_library(hostfxr_dll_handle);
  338. if (godot_plugins_initialize != nullptr) {
  339. is_native_aot = true;
  340. } else {
  341. ERR_FAIL_MSG(".NET: Failed to load hostfxr");
  342. }
  343. #else
  344. ERR_FAIL_MSG(".NET: Failed to load hostfxr");
  345. #endif
  346. }
  347. if (!is_native_aot) {
  348. godot_plugins_initialize = initialize_hostfxr_and_godot_plugins(runtime_initialized);
  349. ERR_FAIL_NULL(godot_plugins_initialize);
  350. }
  351. int32_t interop_funcs_size = 0;
  352. const void **interop_funcs = godotsharp::get_runtime_interop_funcs(interop_funcs_size);
  353. GDMonoCache::ManagedCallbacks managed_callbacks{};
  354. void *godot_dll_handle = nullptr;
  355. #if defined(UNIX_ENABLED) && !defined(MACOS_ENABLED) && !defined(IOS_ENABLED)
  356. // Managed code can access it on its own on other platforms
  357. godot_dll_handle = dlopen(nullptr, RTLD_NOW);
  358. #endif
  359. #ifdef TOOLS_ENABLED
  360. gdmono::PluginCallbacks plugin_callbacks_res;
  361. bool init_ok = godot_plugins_initialize(godot_dll_handle,
  362. Engine::get_singleton()->is_editor_hint(),
  363. &plugin_callbacks_res, &managed_callbacks,
  364. interop_funcs, interop_funcs_size);
  365. ERR_FAIL_COND_MSG(!init_ok, ".NET: GodotPlugins initialization failed");
  366. plugin_callbacks = plugin_callbacks_res;
  367. #else
  368. bool init_ok = godot_plugins_initialize(godot_dll_handle, &managed_callbacks,
  369. interop_funcs, interop_funcs_size);
  370. ERR_FAIL_COND_MSG(!init_ok, ".NET: GodotPlugins initialization failed");
  371. #endif
  372. GDMonoCache::update_godot_api_cache(managed_callbacks);
  373. print_verbose(".NET: GodotPlugins initialized");
  374. _on_core_api_assembly_loaded();
  375. }
  376. #ifdef TOOLS_ENABLED
  377. void GDMono::initialize_load_assemblies() {
  378. if (Engine::get_singleton()->is_project_manager_hint()) {
  379. return;
  380. }
  381. // Load the project's main assembly. This doesn't necessarily need to succeed.
  382. // The game may not be using .NET at all, or if the project does use .NET and
  383. // we're running in the editor, it may just happen to be it wasn't built yet.
  384. if (!_load_project_assembly()) {
  385. if (OS::get_singleton()->is_stdout_verbose()) {
  386. print_error(".NET: Failed to load project assembly");
  387. }
  388. }
  389. }
  390. #endif
  391. void GDMono::_init_godot_api_hashes() {
  392. #ifdef DEBUG_METHODS_ENABLED
  393. get_api_core_hash();
  394. #ifdef TOOLS_ENABLED
  395. get_api_editor_hash();
  396. #endif // TOOLS_ENABLED
  397. #endif // DEBUG_METHODS_ENABLED
  398. }
  399. #ifdef TOOLS_ENABLED
  400. bool GDMono::_load_project_assembly() {
  401. String assembly_name = ProjectSettings::get_singleton()->get_setting("dotnet/project/assembly_name");
  402. if (assembly_name.is_empty()) {
  403. assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
  404. }
  405. String assembly_path = GodotSharpDirs::get_res_temp_assemblies_dir()
  406. .plus_file(assembly_name + ".dll");
  407. assembly_path = ProjectSettings::get_singleton()->globalize_path(assembly_path);
  408. if (!FileAccess::exists(assembly_path)) {
  409. return false;
  410. }
  411. String loaded_assembly_path;
  412. bool success = plugin_callbacks.LoadProjectAssemblyCallback(assembly_path.utf16(), &loaded_assembly_path);
  413. if (success) {
  414. project_assembly_path = loaded_assembly_path.simplify_path();
  415. project_assembly_modified_time = FileAccess::get_modified_time(loaded_assembly_path);
  416. }
  417. return success;
  418. }
  419. #endif
  420. #ifdef GD_MONO_HOT_RELOAD
  421. Error GDMono::reload_project_assemblies() {
  422. ERR_FAIL_COND_V(!runtime_initialized, ERR_BUG);
  423. finalizing_scripts_domain = true;
  424. CSharpLanguage::get_singleton()->_on_scripts_domain_about_to_unload();
  425. if (!get_plugin_callbacks().UnloadProjectPluginCallback()) {
  426. ERR_FAIL_V_MSG(Error::FAILED, ".NET: Failed to unload assemblies.");
  427. }
  428. finalizing_scripts_domain = false;
  429. // Load the project's main assembly. Here, during hot-reloading, we do
  430. // consider failing to load the project's main assembly to be an error.
  431. if (!_load_project_assembly()) {
  432. print_error(".NET: Failed to load project assembly.");
  433. return ERR_CANT_OPEN;
  434. }
  435. return OK;
  436. }
  437. #endif
  438. GDMono::GDMono() {
  439. singleton = this;
  440. runtime_initialized = false;
  441. finalizing_scripts_domain = false;
  442. api_core_hash = 0;
  443. #ifdef TOOLS_ENABLED
  444. api_editor_hash = 0;
  445. #endif
  446. }
  447. GDMono::~GDMono() {
  448. finalizing_scripts_domain = true;
  449. if (is_runtime_initialized()) {
  450. if (GDMonoCache::godot_api_cache_updated) {
  451. GDMonoCache::managed_callbacks.DisposablesTracker_OnGodotShuttingDown();
  452. }
  453. }
  454. if (hostfxr_dll_handle) {
  455. OS::get_singleton()->close_dynamic_library(hostfxr_dll_handle);
  456. }
  457. finalizing_scripts_domain = false;
  458. runtime_initialized = false;
  459. #if defined(ANDROID_ENABLED)
  460. gdmono::android::support::cleanup();
  461. #endif
  462. singleton = nullptr;
  463. }
  464. namespace mono_bind {
  465. GodotSharp *GodotSharp::singleton = nullptr;
  466. bool GodotSharp::_is_runtime_initialized() {
  467. return GDMono::get_singleton() != nullptr && GDMono::get_singleton()->is_runtime_initialized();
  468. }
  469. void GodotSharp::_reload_assemblies(bool p_soft_reload) {
  470. #ifdef GD_MONO_HOT_RELOAD
  471. CRASH_COND(CSharpLanguage::get_singleton() == nullptr);
  472. // This method may be called more than once with `call_deferred`, so we need to check
  473. // again if reloading is needed to avoid reloading multiple times unnecessarily.
  474. if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed()) {
  475. CSharpLanguage::get_singleton()->reload_assemblies(p_soft_reload);
  476. }
  477. #endif
  478. }
  479. void GodotSharp::_bind_methods() {
  480. ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &GodotSharp::_is_runtime_initialized);
  481. ClassDB::bind_method(D_METHOD("_reload_assemblies"), &GodotSharp::_reload_assemblies);
  482. }
  483. GodotSharp::GodotSharp() {
  484. singleton = this;
  485. }
  486. GodotSharp::~GodotSharp() {
  487. singleton = nullptr;
  488. }
  489. } // namespace mono_bind