gd_mono.cpp 21 KB

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