initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
858
modules/mono/mono_gd/gd_mono.cpp
Normal file
858
modules/mono/mono_gd/gd_mono.cpp
Normal file
@@ -0,0 +1,858 @@
|
||||
/**************************************************************************/
|
||||
/* gd_mono.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "gd_mono.h"
|
||||
|
||||
#include "../csharp_script.h"
|
||||
#include "../glue/runtime_interop.h"
|
||||
#include "../godotsharp_dirs.h"
|
||||
#include "../thirdparty/coreclr_delegates.h"
|
||||
#include "../thirdparty/hostfxr.h"
|
||||
#include "../utils/path_utils.h"
|
||||
#include "gd_mono_cache.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "../editor/hostfxr_resolver.h"
|
||||
#include "../editor/semver.h"
|
||||
#endif
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/os/thread.h"
|
||||
|
||||
#ifdef UNIX_ENABLED
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#ifndef TOOLS_ENABLED
|
||||
#ifdef ANDROID_ENABLED
|
||||
#include "../thirdparty/mono_delegates.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
GDMono *GDMono::singleton = nullptr;
|
||||
|
||||
namespace {
|
||||
hostfxr_initialize_for_dotnet_command_line_fn hostfxr_initialize_for_dotnet_command_line = nullptr;
|
||||
hostfxr_initialize_for_runtime_config_fn hostfxr_initialize_for_runtime_config = nullptr;
|
||||
hostfxr_get_runtime_delegate_fn hostfxr_get_runtime_delegate = nullptr;
|
||||
hostfxr_close_fn hostfxr_close = nullptr;
|
||||
|
||||
#ifndef TOOLS_ENABLED
|
||||
typedef int(CORECLR_DELEGATE_CALLTYPE *coreclr_create_delegate_fn)(void *hostHandle, unsigned int domainId, const char *entryPointAssemblyName, const char *entryPointTypeName, const char *entryPointMethodName, void **delegate);
|
||||
typedef int(CORECLR_DELEGATE_CALLTYPE *coreclr_initialize_fn)(const char *exePath, const char *appDomainFriendlyName, int propertyCount, const char **propertyKeys, const char **propertyValues, void **hostHandle, unsigned int *domainId);
|
||||
|
||||
coreclr_create_delegate_fn coreclr_create_delegate = nullptr;
|
||||
coreclr_initialize_fn coreclr_initialize = nullptr;
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
mono_install_assembly_preload_hook_fn mono_install_assembly_preload_hook = nullptr;
|
||||
mono_assembly_name_get_name_fn mono_assembly_name_get_name = nullptr;
|
||||
mono_assembly_name_get_culture_fn mono_assembly_name_get_culture = nullptr;
|
||||
mono_image_open_from_data_with_name_fn mono_image_open_from_data_with_name = nullptr;
|
||||
mono_assembly_load_from_full_fn mono_assembly_load_from_full = nullptr;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
static_assert(sizeof(char_t) == sizeof(char16_t));
|
||||
using HostFxrCharString = Char16String;
|
||||
#define HOSTFXR_STR(m_str) L##m_str
|
||||
#else
|
||||
static_assert(sizeof(char_t) == sizeof(char));
|
||||
using HostFxrCharString = CharString;
|
||||
#define HOSTFXR_STR(m_str) m_str
|
||||
#endif
|
||||
|
||||
HostFxrCharString str_to_hostfxr(const String &p_str) {
|
||||
#ifdef _WIN32
|
||||
return p_str.utf16();
|
||||
#else
|
||||
return p_str.utf8();
|
||||
#endif
|
||||
}
|
||||
|
||||
const char_t *get_data(const HostFxrCharString &p_char_str) {
|
||||
return (const char_t *)p_char_str.get_data();
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
bool try_get_dotnet_root_from_command_line(String &r_dotnet_root) {
|
||||
String pipe;
|
||||
List<String> args;
|
||||
args.push_back("--list-sdks");
|
||||
|
||||
int exitcode;
|
||||
Error err = OS::get_singleton()->execute("dotnet", args, &pipe, &exitcode, true);
|
||||
|
||||
ERR_FAIL_COND_V_MSG(err != OK, false, String(".NET failed to get list of installed SDKs. Error: ") + error_names[err]);
|
||||
ERR_FAIL_COND_V_MSG(exitcode != 0, false, pipe);
|
||||
|
||||
Vector<String> sdks = pipe.strip_edges().replace("\r\n", "\n").split("\n", false);
|
||||
|
||||
godotsharp::SemVerParser sem_ver_parser;
|
||||
|
||||
godotsharp::SemVer latest_sdk_version;
|
||||
String latest_sdk_path;
|
||||
|
||||
for (const String &sdk : sdks) {
|
||||
// The format of the SDK lines is:
|
||||
// 8.0.401 [/usr/share/dotnet/sdk]
|
||||
String version_string = sdk.get_slice(" ", 0);
|
||||
String path = sdk.get_slice(" ", 1);
|
||||
path = path.substr(1, path.length() - 2);
|
||||
|
||||
godotsharp::SemVer version;
|
||||
if (!sem_ver_parser.parse(version_string, version)) {
|
||||
WARN_PRINT("Unable to parse .NET SDK version '" + version_string + "'.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!DirAccess::exists(path)) {
|
||||
WARN_PRINT("Found .NET SDK version '" + version_string + "' with invalid path '" + path + "'.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (version > latest_sdk_version) {
|
||||
latest_sdk_version = version;
|
||||
latest_sdk_path = path;
|
||||
}
|
||||
}
|
||||
|
||||
if (!latest_sdk_path.is_empty()) {
|
||||
print_verbose("Found .NET SDK at " + latest_sdk_path);
|
||||
// The `dotnet_root` is the parent directory.
|
||||
r_dotnet_root = latest_sdk_path.path_join("..").simplify_path();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
String find_hostfxr() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
String dotnet_root;
|
||||
String fxr_path;
|
||||
if (godotsharp::hostfxr_resolver::try_get_path(dotnet_root, fxr_path)) {
|
||||
return fxr_path;
|
||||
}
|
||||
|
||||
// hostfxr_resolver doesn't look for dotnet in `PATH`. If it fails, we try to use the dotnet
|
||||
// executable in `PATH` to find the `dotnet_root` and get the `hostfxr_path` from there.
|
||||
if (try_get_dotnet_root_from_command_line(dotnet_root)) {
|
||||
if (godotsharp::hostfxr_resolver::try_get_path_from_dotnet_root(dotnet_root, fxr_path)) {
|
||||
return fxr_path;
|
||||
}
|
||||
}
|
||||
|
||||
ERR_PRINT(String() + ".NET: One of the dependent libraries is missing. " +
|
||||
"Typically when the `hostfxr`, `hostpolicy` or `coreclr` dynamic " +
|
||||
"libraries are not present in the expected locations.");
|
||||
|
||||
return String();
|
||||
#else
|
||||
|
||||
#if defined(WINDOWS_ENABLED)
|
||||
String probe_path = GodotSharpDirs::get_api_assemblies_dir()
|
||||
.path_join("hostfxr.dll");
|
||||
#elif defined(MACOS_ENABLED)
|
||||
String probe_path = GodotSharpDirs::get_api_assemblies_dir()
|
||||
.path_join("libhostfxr.dylib");
|
||||
#elif defined(UNIX_ENABLED)
|
||||
String probe_path = GodotSharpDirs::get_api_assemblies_dir()
|
||||
.path_join("libhostfxr.so");
|
||||
#else
|
||||
#error "Platform not supported (yet?)"
|
||||
#endif
|
||||
|
||||
if (FileAccess::exists(probe_path)) {
|
||||
return probe_path;
|
||||
}
|
||||
|
||||
return String();
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef TOOLS_ENABLED
|
||||
String find_monosgen() {
|
||||
#if defined(ANDROID_ENABLED)
|
||||
// Android includes all native libraries in the libs directory of the APK
|
||||
// so we assume it exists and use only the name to dlopen it.
|
||||
return "libmonosgen-2.0.so";
|
||||
#else
|
||||
#if defined(WINDOWS_ENABLED)
|
||||
String probe_path = GodotSharpDirs::get_api_assemblies_dir()
|
||||
.path_join("monosgen-2.0.dll");
|
||||
#elif defined(MACOS_ENABLED)
|
||||
String probe_path = GodotSharpDirs::get_api_assemblies_dir()
|
||||
.path_join("libmonosgen-2.0.dylib");
|
||||
#elif defined(UNIX_ENABLED)
|
||||
String probe_path = GodotSharpDirs::get_api_assemblies_dir()
|
||||
.path_join("libmonosgen-2.0.so");
|
||||
#else
|
||||
#error "Platform not supported (yet?)"
|
||||
#endif
|
||||
|
||||
if (FileAccess::exists(probe_path)) {
|
||||
return probe_path;
|
||||
}
|
||||
|
||||
return String();
|
||||
#endif
|
||||
}
|
||||
|
||||
String find_coreclr() {
|
||||
#if defined(WINDOWS_ENABLED)
|
||||
String probe_path = GodotSharpDirs::get_api_assemblies_dir()
|
||||
.path_join("coreclr.dll");
|
||||
#elif defined(MACOS_ENABLED)
|
||||
String probe_path = GodotSharpDirs::get_api_assemblies_dir()
|
||||
.path_join("libcoreclr.dylib");
|
||||
#elif defined(UNIX_ENABLED)
|
||||
String probe_path = GodotSharpDirs::get_api_assemblies_dir()
|
||||
.path_join("libcoreclr.so");
|
||||
#else
|
||||
#error "Platform not supported (yet?)"
|
||||
#endif
|
||||
|
||||
if (FileAccess::exists(probe_path)) {
|
||||
return probe_path;
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
#endif
|
||||
|
||||
bool load_hostfxr(void *&r_hostfxr_dll_handle) {
|
||||
String hostfxr_path = find_hostfxr();
|
||||
|
||||
if (hostfxr_path.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
print_verbose("Found hostfxr: " + hostfxr_path);
|
||||
|
||||
Error err = OS::get_singleton()->open_dynamic_library(hostfxr_path, r_hostfxr_dll_handle);
|
||||
|
||||
if (err != OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void *lib = r_hostfxr_dll_handle;
|
||||
|
||||
void *symbol = nullptr;
|
||||
|
||||
err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_initialize_for_dotnet_command_line", symbol);
|
||||
ERR_FAIL_COND_V(err != OK, false);
|
||||
hostfxr_initialize_for_dotnet_command_line = (hostfxr_initialize_for_dotnet_command_line_fn)symbol;
|
||||
|
||||
err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_initialize_for_runtime_config", symbol);
|
||||
ERR_FAIL_COND_V(err != OK, false);
|
||||
hostfxr_initialize_for_runtime_config = (hostfxr_initialize_for_runtime_config_fn)symbol;
|
||||
|
||||
err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_get_runtime_delegate", symbol);
|
||||
ERR_FAIL_COND_V(err != OK, false);
|
||||
hostfxr_get_runtime_delegate = (hostfxr_get_runtime_delegate_fn)symbol;
|
||||
|
||||
err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_close", symbol);
|
||||
ERR_FAIL_COND_V(err != OK, false);
|
||||
hostfxr_close = (hostfxr_close_fn)symbol;
|
||||
|
||||
return (hostfxr_initialize_for_runtime_config &&
|
||||
hostfxr_get_runtime_delegate &&
|
||||
hostfxr_close);
|
||||
}
|
||||
|
||||
#ifndef TOOLS_ENABLED
|
||||
bool load_coreclr(void *&r_coreclr_dll_handle) {
|
||||
String coreclr_path = find_coreclr();
|
||||
|
||||
bool is_monovm = false;
|
||||
if (coreclr_path.is_empty()) {
|
||||
// Fallback to MonoVM (should have the same API as CoreCLR).
|
||||
coreclr_path = find_monosgen();
|
||||
is_monovm = true;
|
||||
}
|
||||
|
||||
if (coreclr_path.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const String coreclr_name = is_monovm ? "monosgen" : "coreclr";
|
||||
print_verbose("Found " + coreclr_name + ": " + coreclr_path);
|
||||
|
||||
Error err = OS::get_singleton()->open_dynamic_library(coreclr_path, r_coreclr_dll_handle);
|
||||
|
||||
if (err != OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void *lib = r_coreclr_dll_handle;
|
||||
|
||||
void *symbol = nullptr;
|
||||
|
||||
err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "coreclr_initialize", symbol);
|
||||
ERR_FAIL_COND_V(err != OK, false);
|
||||
coreclr_initialize = (coreclr_initialize_fn)symbol;
|
||||
|
||||
err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "coreclr_create_delegate", symbol);
|
||||
ERR_FAIL_COND_V(err != OK, false);
|
||||
coreclr_create_delegate = (coreclr_create_delegate_fn)symbol;
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "mono_install_assembly_preload_hook", symbol);
|
||||
ERR_FAIL_COND_V(err != OK, false);
|
||||
mono_install_assembly_preload_hook = (mono_install_assembly_preload_hook_fn)symbol;
|
||||
|
||||
err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "mono_assembly_name_get_name", symbol);
|
||||
ERR_FAIL_COND_V(err != OK, false);
|
||||
mono_assembly_name_get_name = (mono_assembly_name_get_name_fn)symbol;
|
||||
|
||||
err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "mono_assembly_name_get_culture", symbol);
|
||||
ERR_FAIL_COND_V(err != OK, false);
|
||||
mono_assembly_name_get_culture = (mono_assembly_name_get_culture_fn)symbol;
|
||||
|
||||
err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "mono_image_open_from_data_with_name", symbol);
|
||||
ERR_FAIL_COND_V(err != OK, false);
|
||||
mono_image_open_from_data_with_name = (mono_image_open_from_data_with_name_fn)symbol;
|
||||
|
||||
err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "mono_assembly_load_from_full", symbol);
|
||||
ERR_FAIL_COND_V(err != OK, false);
|
||||
mono_assembly_load_from_full = (mono_assembly_load_from_full_fn)symbol;
|
||||
#endif
|
||||
|
||||
return (coreclr_initialize &&
|
||||
coreclr_create_delegate);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
load_assembly_and_get_function_pointer_fn initialize_hostfxr_for_config(const char_t *p_config_path) {
|
||||
hostfxr_handle cxt = nullptr;
|
||||
int rc = hostfxr_initialize_for_runtime_config(p_config_path, nullptr, &cxt);
|
||||
if (rc != 0 || cxt == nullptr) {
|
||||
hostfxr_close(cxt);
|
||||
ERR_FAIL_V_MSG(nullptr, "hostfxr_initialize_for_runtime_config failed with code: " + itos(rc));
|
||||
}
|
||||
|
||||
void *load_assembly_and_get_function_pointer = nullptr;
|
||||
|
||||
rc = hostfxr_get_runtime_delegate(cxt,
|
||||
hdt_load_assembly_and_get_function_pointer, &load_assembly_and_get_function_pointer);
|
||||
if (rc != 0 || load_assembly_and_get_function_pointer == nullptr) {
|
||||
ERR_FAIL_V_MSG(nullptr, "hostfxr_get_runtime_delegate failed with code: " + itos(rc));
|
||||
}
|
||||
|
||||
hostfxr_close(cxt);
|
||||
|
||||
return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer;
|
||||
}
|
||||
#else
|
||||
load_assembly_and_get_function_pointer_fn initialize_hostfxr_self_contained(
|
||||
const char_t *p_main_assembly_path) {
|
||||
hostfxr_handle cxt = nullptr;
|
||||
|
||||
List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
|
||||
|
||||
List<HostFxrCharString> argv_store;
|
||||
Vector<const char_t *> argv;
|
||||
argv.resize(cmdline_args.size() + 1);
|
||||
|
||||
argv.write[0] = p_main_assembly_path;
|
||||
|
||||
int i = 1;
|
||||
for (const String &E : cmdline_args) {
|
||||
HostFxrCharString &stored = argv_store.push_back(str_to_hostfxr(E))->get();
|
||||
argv.write[i] = get_data(stored);
|
||||
i++;
|
||||
}
|
||||
|
||||
int rc = hostfxr_initialize_for_dotnet_command_line(argv.size(), argv.ptrw(), nullptr, &cxt);
|
||||
if (rc != 0 || cxt == nullptr) {
|
||||
hostfxr_close(cxt);
|
||||
ERR_FAIL_V_MSG(nullptr, "hostfxr_initialize_for_dotnet_command_line failed with code: " + itos(rc));
|
||||
}
|
||||
|
||||
void *load_assembly_and_get_function_pointer = nullptr;
|
||||
|
||||
rc = hostfxr_get_runtime_delegate(cxt,
|
||||
hdt_load_assembly_and_get_function_pointer, &load_assembly_and_get_function_pointer);
|
||||
if (rc != 0 || load_assembly_and_get_function_pointer == nullptr) {
|
||||
ERR_FAIL_V_MSG(nullptr, "hostfxr_get_runtime_delegate failed with code: " + itos(rc));
|
||||
}
|
||||
|
||||
hostfxr_close(cxt);
|
||||
|
||||
return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
using godot_plugins_initialize_fn = bool (*)(void *, bool, gdmono::PluginCallbacks *, GDMonoCache::ManagedCallbacks *, const void **, int32_t);
|
||||
#else
|
||||
using godot_plugins_initialize_fn = bool (*)(void *, GDMonoCache::ManagedCallbacks *, const void **, int32_t);
|
||||
#endif
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime_initialized) {
|
||||
godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
|
||||
|
||||
HostFxrCharString godot_plugins_path = str_to_hostfxr(
|
||||
GodotSharpDirs::get_api_assemblies_dir().path_join("GodotPlugins.dll"));
|
||||
|
||||
HostFxrCharString config_path = str_to_hostfxr(
|
||||
GodotSharpDirs::get_api_assemblies_dir().path_join("GodotPlugins.runtimeconfig.json"));
|
||||
|
||||
load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer =
|
||||
initialize_hostfxr_for_config(get_data(config_path));
|
||||
|
||||
if (load_assembly_and_get_function_pointer == nullptr) {
|
||||
// Show a message box to the user to make the problem explicit (and explain a potential crash).
|
||||
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 8.0 or later from https://get.dot.net and restart Godot."), TTR("Failed to load .NET runtime"));
|
||||
ERR_FAIL_V_MSG(nullptr, ".NET: Failed to load compatible .NET runtime");
|
||||
}
|
||||
|
||||
r_runtime_initialized = true;
|
||||
|
||||
print_verbose(".NET: hostfxr initialized");
|
||||
|
||||
int rc = load_assembly_and_get_function_pointer(get_data(godot_plugins_path),
|
||||
HOSTFXR_STR("GodotPlugins.Main, GodotPlugins"),
|
||||
HOSTFXR_STR("InitializeFromEngine"),
|
||||
UNMANAGEDCALLERSONLY_METHOD,
|
||||
nullptr,
|
||||
(void **)&godot_plugins_initialize);
|
||||
ERR_FAIL_COND_V_MSG(rc != 0, nullptr, ".NET: Failed to get GodotPlugins initialization function pointer");
|
||||
|
||||
return godot_plugins_initialize;
|
||||
}
|
||||
#else
|
||||
godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime_initialized) {
|
||||
godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
|
||||
|
||||
String assembly_name = Path::get_csharp_project_name();
|
||||
|
||||
HostFxrCharString assembly_path = str_to_hostfxr(GodotSharpDirs::get_api_assemblies_dir()
|
||||
.path_join(assembly_name + ".dll"));
|
||||
|
||||
load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer =
|
||||
initialize_hostfxr_self_contained(get_data(assembly_path));
|
||||
ERR_FAIL_NULL_V(load_assembly_and_get_function_pointer, nullptr);
|
||||
|
||||
r_runtime_initialized = true;
|
||||
|
||||
print_verbose(".NET: hostfxr initialized");
|
||||
|
||||
int rc = load_assembly_and_get_function_pointer(get_data(assembly_path),
|
||||
get_data(str_to_hostfxr("GodotPlugins.Game.Main, " + assembly_name)),
|
||||
HOSTFXR_STR("InitializeFromGameProject"),
|
||||
UNMANAGEDCALLERSONLY_METHOD,
|
||||
nullptr,
|
||||
(void **)&godot_plugins_initialize);
|
||||
ERR_FAIL_COND_V_MSG(rc != 0, nullptr, ".NET: Failed to get GodotPlugins initialization function pointer");
|
||||
|
||||
return godot_plugins_initialize;
|
||||
}
|
||||
|
||||
godot_plugins_initialize_fn try_load_native_aot_library(void *&r_aot_dll_handle) {
|
||||
String assembly_name = Path::get_csharp_project_name();
|
||||
|
||||
#if defined(WINDOWS_ENABLED)
|
||||
String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".dll");
|
||||
#elif defined(MACOS_ENABLED) || defined(APPLE_EMBEDDED_ENABLED)
|
||||
String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".dylib");
|
||||
#elif defined(ANDROID_ENABLED)
|
||||
String native_aot_so_path = "lib" + assembly_name + ".so";
|
||||
#elif defined(UNIX_ENABLED)
|
||||
String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".so");
|
||||
#else
|
||||
#error "Platform not supported (yet?)"
|
||||
#endif
|
||||
|
||||
Error err = OS::get_singleton()->open_dynamic_library(native_aot_so_path, r_aot_dll_handle);
|
||||
|
||||
if (err != OK) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *lib = r_aot_dll_handle;
|
||||
|
||||
void *symbol = nullptr;
|
||||
|
||||
err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "godotsharp_game_main_init", symbol);
|
||||
ERR_FAIL_COND_V(err != OK, nullptr);
|
||||
return (godot_plugins_initialize_fn)symbol;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef TOOLS_ENABLED
|
||||
#ifdef ANDROID_ENABLED
|
||||
MonoAssembly *load_assembly_from_pck(MonoAssemblyName *p_assembly_name, char **p_assemblies_path, void *p_user_data) {
|
||||
constexpr bool ref_only = false;
|
||||
|
||||
const char *name = mono_assembly_name_get_name(p_assembly_name);
|
||||
const char *culture = mono_assembly_name_get_culture(p_assembly_name);
|
||||
|
||||
String assembly_name;
|
||||
if (culture && strcmp(culture, "")) {
|
||||
assembly_name += culture;
|
||||
assembly_name += "/";
|
||||
}
|
||||
assembly_name += name;
|
||||
if (!assembly_name.ends_with(".dll")) {
|
||||
assembly_name += ".dll";
|
||||
}
|
||||
|
||||
String path = GodotSharpDirs::get_api_assemblies_dir();
|
||||
path = path.path_join(assembly_name);
|
||||
|
||||
print_verbose(".NET: Loading assembly '" + assembly_name + "' from '" + path + "'.");
|
||||
|
||||
if (!FileAccess::exists(path)) {
|
||||
// We could not find the assembly, return null so another hook may find it.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Vector<uint8_t> data = FileAccess::get_file_as_bytes(path);
|
||||
ERR_FAIL_COND_V_MSG(data.is_empty(), nullptr, ".NET: Could not read assembly in '" + path + "'.");
|
||||
|
||||
MonoImageOpenStatus status = MONO_IMAGE_OK;
|
||||
|
||||
MonoImage *image = mono_image_open_from_data_with_name(
|
||||
reinterpret_cast<char *>(data.ptrw()), data.size(),
|
||||
/*need_copy*/ true,
|
||||
&status,
|
||||
ref_only,
|
||||
assembly_name.utf8().get_data());
|
||||
|
||||
ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || image == nullptr, nullptr, ".NET: Failed to open assembly image.");
|
||||
|
||||
status = MONO_IMAGE_OK;
|
||||
|
||||
MonoAssembly *assembly = mono_assembly_load_from_full(
|
||||
image, assembly_name.utf8().get_data(),
|
||||
&status,
|
||||
ref_only);
|
||||
|
||||
ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || assembly == nullptr, nullptr, ".NET: Failed to load assembly from image.");
|
||||
|
||||
return assembly;
|
||||
}
|
||||
#endif
|
||||
|
||||
godot_plugins_initialize_fn initialize_coreclr_and_godot_plugins(bool &r_runtime_initialized) {
|
||||
godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
|
||||
|
||||
String assembly_name = Path::get_csharp_project_name();
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
// Android requires installing a preload hook to load assemblies from inside the APK,
|
||||
// other platforms can find the assemblies with the default lookup.
|
||||
if (mono_install_assembly_preload_hook != nullptr) {
|
||||
mono_install_assembly_preload_hook(&load_assembly_from_pck, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
void *coreclr_handle = nullptr;
|
||||
unsigned int domain_id = 0;
|
||||
int rc = coreclr_initialize(nullptr, nullptr, 0, nullptr, nullptr, &coreclr_handle, &domain_id);
|
||||
ERR_FAIL_COND_V_MSG(rc != 0, nullptr, ".NET: Failed to initialize CoreCLR.");
|
||||
|
||||
r_runtime_initialized = true;
|
||||
|
||||
print_verbose(".NET: CoreCLR initialized");
|
||||
|
||||
coreclr_create_delegate(coreclr_handle, domain_id,
|
||||
assembly_name.utf8().get_data(),
|
||||
"GodotPlugins.Game.Main",
|
||||
"InitializeFromGameProject",
|
||||
(void **)&godot_plugins_initialize);
|
||||
ERR_FAIL_NULL_V_MSG(godot_plugins_initialize, nullptr, ".NET: Failed to get GodotPlugins initialization function pointer");
|
||||
|
||||
return godot_plugins_initialize;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
bool GDMono::should_initialize() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
// The editor always needs to initialize the .NET module for now.
|
||||
return true;
|
||||
#else
|
||||
return OS::get_singleton()->has_feature("dotnet");
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool _on_core_api_assembly_loaded() {
|
||||
if (!GDMonoCache::godot_api_cache_updated) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool debug;
|
||||
#ifdef DEBUG_ENABLED
|
||||
debug = true;
|
||||
#else
|
||||
debug = false;
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
GDMonoCache::managed_callbacks.GD_OnCoreApiAssemblyLoaded(debug);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GDMono::initialize() {
|
||||
print_verbose(".NET: Initializing module...");
|
||||
|
||||
_init_godot_api_hashes();
|
||||
|
||||
godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
|
||||
|
||||
#if !defined(APPLE_EMBEDDED_ENABLED)
|
||||
// Check that the .NET assemblies directory exists before trying to use it.
|
||||
if (!DirAccess::exists(GodotSharpDirs::get_api_assemblies_dir())) {
|
||||
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"));
|
||||
ERR_FAIL_MSG(".NET: Assemblies not found");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (load_hostfxr(hostfxr_dll_handle)) {
|
||||
godot_plugins_initialize = initialize_hostfxr_and_godot_plugins(runtime_initialized);
|
||||
ERR_FAIL_NULL(godot_plugins_initialize);
|
||||
} else {
|
||||
#if !defined(TOOLS_ENABLED)
|
||||
if (load_coreclr(coreclr_dll_handle)) {
|
||||
godot_plugins_initialize = initialize_coreclr_and_godot_plugins(runtime_initialized);
|
||||
} else {
|
||||
void *dll_handle = nullptr;
|
||||
godot_plugins_initialize = try_load_native_aot_library(dll_handle);
|
||||
if (godot_plugins_initialize != nullptr) {
|
||||
runtime_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (godot_plugins_initialize == nullptr) {
|
||||
ERR_FAIL_MSG(".NET: Failed to load hostfxr");
|
||||
}
|
||||
#else
|
||||
|
||||
// Show a message box to the user to make the problem explicit (and explain a potential crash).
|
||||
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 8.0 or later from https://get.dot.net and restart Godot."), TTR("Failed to load .NET runtime"));
|
||||
ERR_FAIL_MSG(".NET: Failed to load hostfxr");
|
||||
#endif
|
||||
}
|
||||
|
||||
int32_t interop_funcs_size = 0;
|
||||
const void **interop_funcs = godotsharp::get_runtime_interop_funcs(interop_funcs_size);
|
||||
|
||||
GDMonoCache::ManagedCallbacks managed_callbacks{};
|
||||
|
||||
void *godot_dll_handle = nullptr;
|
||||
|
||||
#if defined(UNIX_ENABLED) && !defined(MACOS_ENABLED) && !defined(APPLE_EMBEDDED_ENABLED)
|
||||
// Managed code can access it on its own on other platforms
|
||||
godot_dll_handle = dlopen(nullptr, RTLD_NOW);
|
||||
#endif
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
gdmono::PluginCallbacks plugin_callbacks_res;
|
||||
bool init_ok = godot_plugins_initialize(godot_dll_handle,
|
||||
Engine::get_singleton()->is_editor_hint(),
|
||||
&plugin_callbacks_res, &managed_callbacks,
|
||||
interop_funcs, interop_funcs_size);
|
||||
ERR_FAIL_COND_MSG(!init_ok, ".NET: GodotPlugins initialization failed");
|
||||
|
||||
plugin_callbacks = plugin_callbacks_res;
|
||||
#else
|
||||
bool init_ok = godot_plugins_initialize(godot_dll_handle, &managed_callbacks,
|
||||
interop_funcs, interop_funcs_size);
|
||||
ERR_FAIL_COND_MSG(!init_ok, ".NET: GodotPlugins initialization failed");
|
||||
#endif
|
||||
|
||||
GDMonoCache::update_godot_api_cache(managed_callbacks);
|
||||
|
||||
print_verbose(".NET: GodotPlugins initialized");
|
||||
|
||||
_on_core_api_assembly_loaded();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
_try_load_project_assembly();
|
||||
#endif
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
void GDMono::_try_load_project_assembly() {
|
||||
if (Engine::get_singleton()->is_project_manager_hint()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the project's main assembly. This doesn't necessarily need to succeed.
|
||||
// The game may not be using .NET at all, or if the project does use .NET and
|
||||
// we're running in the editor, it may just happen to be it wasn't built yet.
|
||||
if (!_load_project_assembly()) {
|
||||
if (OS::get_singleton()->is_stdout_verbose()) {
|
||||
print_error(".NET: Failed to load project assembly");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void GDMono::_init_godot_api_hashes() {
|
||||
#ifdef DEBUG_ENABLED
|
||||
get_api_core_hash();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
get_api_editor_hash();
|
||||
#endif // TOOLS_ENABLED
|
||||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
bool GDMono::_load_project_assembly() {
|
||||
String assembly_name = Path::get_csharp_project_name();
|
||||
|
||||
String assembly_path = GodotSharpDirs::get_res_temp_assemblies_dir()
|
||||
.path_join(assembly_name + ".dll");
|
||||
assembly_path = ProjectSettings::get_singleton()->globalize_path(assembly_path);
|
||||
|
||||
if (!FileAccess::exists(assembly_path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String loaded_assembly_path;
|
||||
bool success = plugin_callbacks.LoadProjectAssemblyCallback(assembly_path.utf16().get_data(), &loaded_assembly_path);
|
||||
|
||||
if (success) {
|
||||
project_assembly_path = loaded_assembly_path.simplify_path();
|
||||
project_assembly_modified_time = FileAccess::get_modified_time(loaded_assembly_path);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GD_MONO_HOT_RELOAD
|
||||
void GDMono::reload_failure() {
|
||||
if (++project_load_failure_count >= (int)GLOBAL_GET("dotnet/project/assembly_reload_attempts")) {
|
||||
// After reloading a project has failed n times in a row, update the path and modification time
|
||||
// to stop any further attempts at loading this assembly, which probably is never going to work anyways.
|
||||
project_load_failure_count = 0;
|
||||
|
||||
ERR_PRINT_ED(".NET: Giving up on assembly reloading. Please restart the editor if unloading was failing.");
|
||||
|
||||
String assembly_name = Path::get_csharp_project_name();
|
||||
String assembly_path = GodotSharpDirs::get_res_temp_assemblies_dir().path_join(assembly_name + ".dll");
|
||||
assembly_path = ProjectSettings::get_singleton()->globalize_path(assembly_path);
|
||||
project_assembly_path = assembly_path.simplify_path();
|
||||
project_assembly_modified_time = FileAccess::get_modified_time(assembly_path);
|
||||
}
|
||||
}
|
||||
|
||||
Error GDMono::reload_project_assemblies() {
|
||||
ERR_FAIL_COND_V(!runtime_initialized, ERR_BUG);
|
||||
|
||||
finalizing_scripts_domain = true;
|
||||
|
||||
if (!get_plugin_callbacks().UnloadProjectPluginCallback()) {
|
||||
ERR_PRINT_ED(".NET: Failed to unload assemblies. Please check https://github.com/godotengine/godot/issues/78513 for more information.");
|
||||
reload_failure();
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
finalizing_scripts_domain = false;
|
||||
|
||||
// Load the project's main assembly. Here, during hot-reloading, we do
|
||||
// consider failing to load the project's main assembly to be an error.
|
||||
if (!_load_project_assembly()) {
|
||||
ERR_PRINT_ED(".NET: Failed to load project assembly.");
|
||||
reload_failure();
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
|
||||
if (project_load_failure_count > 0) {
|
||||
project_load_failure_count = 0;
|
||||
ERR_PRINT_ED(".NET: Assembly reloading succeeded after failures.");
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
GDMono::GDMono() {
|
||||
singleton = this;
|
||||
}
|
||||
|
||||
GDMono::~GDMono() {
|
||||
finalizing_scripts_domain = true;
|
||||
|
||||
if (hostfxr_dll_handle) {
|
||||
OS::get_singleton()->close_dynamic_library(hostfxr_dll_handle);
|
||||
}
|
||||
if (coreclr_dll_handle) {
|
||||
OS::get_singleton()->close_dynamic_library(coreclr_dll_handle);
|
||||
}
|
||||
|
||||
finalizing_scripts_domain = false;
|
||||
runtime_initialized = false;
|
||||
|
||||
singleton = nullptr;
|
||||
}
|
||||
|
||||
namespace MonoBind {
|
||||
|
||||
GodotSharp *GodotSharp::singleton = nullptr;
|
||||
|
||||
void GodotSharp::reload_assemblies(bool p_soft_reload) {
|
||||
#ifdef GD_MONO_HOT_RELOAD
|
||||
CRASH_COND(CSharpLanguage::get_singleton() == nullptr);
|
||||
// This method may be called more than once with `call_deferred`, so we need to check
|
||||
// again if reloading is needed to avoid reloading multiple times unnecessarily.
|
||||
if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed()) {
|
||||
CSharpLanguage::get_singleton()->reload_assemblies(p_soft_reload);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
GodotSharp::GodotSharp() {
|
||||
singleton = this;
|
||||
}
|
||||
|
||||
GodotSharp::~GodotSharp() {
|
||||
singleton = nullptr;
|
||||
}
|
||||
|
||||
} // namespace MonoBind
|
181
modules/mono/mono_gd/gd_mono.h
Normal file
181
modules/mono/mono_gd/gd_mono.h
Normal file
@@ -0,0 +1,181 @@
|
||||
/**************************************************************************/
|
||||
/* gd_mono.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../godotsharp_defs.h"
|
||||
|
||||
#include "core/io/config_file.h"
|
||||
|
||||
#ifndef GD_CLR_STDCALL
|
||||
#ifdef WIN32
|
||||
#define GD_CLR_STDCALL __stdcall
|
||||
#else
|
||||
#define GD_CLR_STDCALL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace gdmono {
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
struct PluginCallbacks {
|
||||
using FuncLoadProjectAssemblyCallback = bool(GD_CLR_STDCALL *)(const char16_t *, String *);
|
||||
using FuncLoadToolsAssemblyCallback = Object *(GD_CLR_STDCALL *)(const char16_t *, const void **, int32_t);
|
||||
using FuncUnloadProjectPluginCallback = bool(GD_CLR_STDCALL *)();
|
||||
FuncLoadProjectAssemblyCallback LoadProjectAssemblyCallback = nullptr;
|
||||
FuncLoadToolsAssemblyCallback LoadToolsAssemblyCallback = nullptr;
|
||||
FuncUnloadProjectPluginCallback UnloadProjectPluginCallback = nullptr;
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace gdmono
|
||||
|
||||
class GDMono {
|
||||
bool initialized = false;
|
||||
bool runtime_initialized = false;
|
||||
bool finalizing_scripts_domain = false;
|
||||
|
||||
void *hostfxr_dll_handle = nullptr;
|
||||
void *coreclr_dll_handle = nullptr;
|
||||
|
||||
String project_assembly_path;
|
||||
uint64_t project_assembly_modified_time = 0;
|
||||
#ifdef GD_MONO_HOT_RELOAD
|
||||
int project_load_failure_count = 0;
|
||||
#endif
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
bool _load_project_assembly();
|
||||
void _try_load_project_assembly();
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
uint64_t api_core_hash = 0;
|
||||
#endif // DEBUG_ENABLED
|
||||
#ifdef TOOLS_ENABLED
|
||||
uint64_t api_editor_hash = 0;
|
||||
#endif
|
||||
void _init_godot_api_hashes();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
gdmono::PluginCallbacks plugin_callbacks;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
static GDMono *singleton;
|
||||
|
||||
public:
|
||||
#ifdef DEBUG_ENABLED
|
||||
uint64_t get_api_core_hash() {
|
||||
if (api_core_hash == 0) {
|
||||
api_core_hash = ClassDB::get_api_hash(ClassDB::API_CORE);
|
||||
}
|
||||
return api_core_hash;
|
||||
}
|
||||
#ifdef TOOLS_ENABLED
|
||||
uint64_t get_api_editor_hash() {
|
||||
if (api_editor_hash == 0) {
|
||||
api_editor_hash = ClassDB::get_api_hash(ClassDB::API_EDITOR);
|
||||
}
|
||||
return api_editor_hash;
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
_FORCE_INLINE_ static String get_expected_api_build_config() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
return "Debug";
|
||||
#else
|
||||
#ifdef DEBUG_ENABLED
|
||||
return "Debug";
|
||||
#else
|
||||
return "Release";
|
||||
#endif // DEBUG_ENABLED
|
||||
#endif
|
||||
}
|
||||
|
||||
static GDMono *get_singleton() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool is_initialized() const {
|
||||
return initialized;
|
||||
}
|
||||
_FORCE_INLINE_ bool is_runtime_initialized() const {
|
||||
return runtime_initialized;
|
||||
}
|
||||
_FORCE_INLINE_ bool is_finalizing_scripts_domain() {
|
||||
return finalizing_scripts_domain;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ const String &get_project_assembly_path() const {
|
||||
return project_assembly_path;
|
||||
}
|
||||
_FORCE_INLINE_ uint64_t get_project_assembly_modified_time() const {
|
||||
return project_assembly_modified_time;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
const gdmono::PluginCallbacks &get_plugin_callbacks() {
|
||||
return plugin_callbacks;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GD_MONO_HOT_RELOAD
|
||||
void reload_failure();
|
||||
Error reload_project_assemblies();
|
||||
#endif
|
||||
|
||||
bool should_initialize();
|
||||
|
||||
void initialize();
|
||||
|
||||
GDMono();
|
||||
~GDMono();
|
||||
};
|
||||
|
||||
namespace MonoBind {
|
||||
|
||||
class GodotSharp : public Object {
|
||||
GDCLASS(GodotSharp, Object);
|
||||
|
||||
protected:
|
||||
static GodotSharp *singleton;
|
||||
|
||||
public:
|
||||
static GodotSharp *get_singleton() { return singleton; }
|
||||
|
||||
void reload_assemblies(bool p_soft_reload);
|
||||
|
||||
GodotSharp();
|
||||
~GodotSharp();
|
||||
};
|
||||
|
||||
} // namespace MonoBind
|
100
modules/mono/mono_gd/gd_mono_cache.cpp
Normal file
100
modules/mono/mono_gd/gd_mono_cache.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/**************************************************************************/
|
||||
/* gd_mono_cache.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "gd_mono_cache.h"
|
||||
|
||||
#include "core/error/error_macros.h"
|
||||
|
||||
namespace GDMonoCache {
|
||||
|
||||
ManagedCallbacks managed_callbacks;
|
||||
bool godot_api_cache_updated = false;
|
||||
|
||||
void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks) {
|
||||
int checked_count = 0;
|
||||
|
||||
#define CHECK_CALLBACK_NOT_NULL_IMPL(m_var, m_class, m_method) \
|
||||
{ \
|
||||
ERR_FAIL_NULL_MSG(m_var, \
|
||||
"Mono Cache: Managed callback for '" #m_class "_" #m_method "' is null."); \
|
||||
checked_count += 1; \
|
||||
}
|
||||
|
||||
#define CHECK_CALLBACK_NOT_NULL(m_class, m_method) CHECK_CALLBACK_NOT_NULL_IMPL(p_managed_callbacks.m_class##_##m_method, m_class, m_method)
|
||||
|
||||
CHECK_CALLBACK_NOT_NULL(SignalAwaiter, SignalCallback);
|
||||
CHECK_CALLBACK_NOT_NULL(DelegateUtils, InvokeWithVariantArgs);
|
||||
CHECK_CALLBACK_NOT_NULL(DelegateUtils, DelegateEquals);
|
||||
CHECK_CALLBACK_NOT_NULL(DelegateUtils, DelegateHash);
|
||||
CHECK_CALLBACK_NOT_NULL(DelegateUtils, GetArgumentCount);
|
||||
CHECK_CALLBACK_NOT_NULL(DelegateUtils, TrySerializeDelegateWithGCHandle);
|
||||
CHECK_CALLBACK_NOT_NULL(DelegateUtils, TryDeserializeDelegateWithGCHandle);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, FrameCallback);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, CreateManagedForGodotObjectBinding);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, CreateManagedForGodotObjectScriptInstance);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetScriptNativeName);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetGlobalClassName);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, SetGodotObjectPtr);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, RaiseEventSignal);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, ScriptIsOrInherits);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, AddScriptBridge);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetOrCreateScriptBridgeForPath);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, RemoveScriptBridge);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, TryReloadRegisteredScriptWithClass);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, UpdateScriptClassInfo);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, SwapGCHandleForType);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetPropertyInfoList);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetPropertyDefaultValues);
|
||||
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, CallStatic);
|
||||
CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, Call);
|
||||
CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, Set);
|
||||
CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, Get);
|
||||
CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, CallDispose);
|
||||
CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, CallToString);
|
||||
CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, HasMethodUnknownParams);
|
||||
CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, SerializeState);
|
||||
CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, DeserializeState);
|
||||
CHECK_CALLBACK_NOT_NULL(GCHandleBridge, FreeGCHandle);
|
||||
CHECK_CALLBACK_NOT_NULL(GCHandleBridge, GCHandleIsTargetCollectible);
|
||||
CHECK_CALLBACK_NOT_NULL(DebuggingUtils, GetCurrentStackInfo);
|
||||
CHECK_CALLBACK_NOT_NULL(DisposablesTracker, OnGodotShuttingDown);
|
||||
CHECK_CALLBACK_NOT_NULL(GD, OnCoreApiAssemblyLoaded);
|
||||
|
||||
managed_callbacks = p_managed_callbacks;
|
||||
|
||||
// It's easy to forget to add new callbacks here, so this should help
|
||||
if (checked_count * sizeof(void *) != sizeof(ManagedCallbacks)) {
|
||||
int missing_count = (sizeof(ManagedCallbacks) / sizeof(void *)) - checked_count;
|
||||
WARN_PRINT("The presence of " + itos(missing_count) + " callback(s) was not validated");
|
||||
}
|
||||
|
||||
godot_api_cache_updated = true;
|
||||
}
|
||||
} // namespace GDMonoCache
|
156
modules/mono/mono_gd/gd_mono_cache.h
Normal file
156
modules/mono/mono_gd/gd_mono_cache.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/**************************************************************************/
|
||||
/* gd_mono_cache.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../csharp_script.h"
|
||||
#include "../interop_types.h"
|
||||
#include "../mono_gc_handle.h"
|
||||
|
||||
#include "core/object/object.h"
|
||||
#include "core/string/string_name.h"
|
||||
#include "core/string/ustring.h"
|
||||
#include "core/variant/callable.h"
|
||||
#include "core/variant/dictionary.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
class CSharpScript;
|
||||
|
||||
namespace GDMonoCache {
|
||||
|
||||
#ifndef GD_CLR_STDCALL
|
||||
#ifdef WIN32
|
||||
#define GD_CLR_STDCALL __stdcall
|
||||
#else
|
||||
#define GD_CLR_STDCALL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct godotsharp_property_info {
|
||||
godot_string_name name; // Not owned
|
||||
godot_string hint_string;
|
||||
Variant::Type type;
|
||||
PropertyHint hint;
|
||||
PropertyUsageFlags usage;
|
||||
bool exported;
|
||||
};
|
||||
|
||||
struct godotsharp_property_def_val_pair {
|
||||
godot_string_name name; // Not owned
|
||||
godot_variant value;
|
||||
};
|
||||
|
||||
struct ManagedCallbacks {
|
||||
using Callback_ScriptManagerBridge_GetPropertyInfoList_Add = void(GD_CLR_STDCALL *)(CSharpScript *p_script, const String *, void *p_props, int32_t p_count);
|
||||
using Callback_ScriptManagerBridge_GetPropertyDefaultValues_Add = void(GD_CLR_STDCALL *)(CSharpScript *p_script, void *p_def_vals, int32_t p_count);
|
||||
|
||||
using FuncSignalAwaiter_SignalCallback = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Variant **, int32_t, bool *);
|
||||
using FuncDelegateUtils_InvokeWithVariantArgs = void(GD_CLR_STDCALL *)(GCHandleIntPtr, void *, const Variant **, int32_t, const Variant *);
|
||||
using FuncDelegateUtils_DelegateEquals = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr);
|
||||
using FuncDelegateUtils_DelegateHash = int32_t(GD_CLR_STDCALL *)(GCHandleIntPtr);
|
||||
using FuncDelegateUtils_GetArgumentCount = int32_t(GD_CLR_STDCALL *)(GCHandleIntPtr, bool *);
|
||||
using FuncDelegateUtils_TrySerializeDelegateWithGCHandle = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const Array *);
|
||||
using FuncDelegateUtils_TryDeserializeDelegateWithGCHandle = bool(GD_CLR_STDCALL *)(const Array *, GCHandleIntPtr *);
|
||||
using FuncScriptManagerBridge_FrameCallback = void(GD_CLR_STDCALL *)();
|
||||
using FuncScriptManagerBridge_CreateManagedForGodotObjectBinding = GCHandleIntPtr(GD_CLR_STDCALL *)(const StringName *, Object *);
|
||||
using FuncScriptManagerBridge_CreateManagedForGodotObjectScriptInstance = bool(GD_CLR_STDCALL *)(const CSharpScript *, Object *, const Variant **, int32_t);
|
||||
using FuncScriptManagerBridge_GetScriptNativeName = void(GD_CLR_STDCALL *)(const CSharpScript *, StringName *);
|
||||
using FuncScriptManagerBridge_GetGlobalClassName = void(GD_CLR_STDCALL *)(const String *, String *, String *, bool *, bool *, String *);
|
||||
using FuncScriptManagerBridge_SetGodotObjectPtr = void(GD_CLR_STDCALL *)(GCHandleIntPtr, Object *);
|
||||
using FuncScriptManagerBridge_RaiseEventSignal = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, const Variant **, int32_t, bool *);
|
||||
using FuncScriptManagerBridge_ScriptIsOrInherits = bool(GD_CLR_STDCALL *)(const CSharpScript *, const CSharpScript *);
|
||||
using FuncScriptManagerBridge_AddScriptBridge = bool(GD_CLR_STDCALL *)(const CSharpScript *, const String *);
|
||||
using FuncScriptManagerBridge_GetOrCreateScriptBridgeForPath = void(GD_CLR_STDCALL *)(const String *, Ref<CSharpScript> *);
|
||||
using FuncScriptManagerBridge_RemoveScriptBridge = void(GD_CLR_STDCALL *)(const CSharpScript *);
|
||||
using FuncScriptManagerBridge_TryReloadRegisteredScriptWithClass = bool(GD_CLR_STDCALL *)(const CSharpScript *);
|
||||
using FuncScriptManagerBridge_UpdateScriptClassInfo = void(GD_CLR_STDCALL *)(const CSharpScript *, CSharpScript::TypeInfo *, Array *, Dictionary *, Dictionary *, Ref<CSharpScript> *);
|
||||
using FuncScriptManagerBridge_SwapGCHandleForType = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr *, bool);
|
||||
using FuncScriptManagerBridge_GetPropertyInfoList = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyInfoList_Add);
|
||||
using FuncScriptManagerBridge_GetPropertyDefaultValues = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyDefaultValues_Add);
|
||||
using FuncScriptManagerBridge_CallStatic = bool(GD_CLR_STDCALL *)(const CSharpScript *, const StringName *, const Variant **, int32_t, Callable::CallError *, Variant *);
|
||||
using FuncCSharpInstanceBridge_Call = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, const Variant **, int32_t, Callable::CallError *, Variant *);
|
||||
using FuncCSharpInstanceBridge_Set = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, const Variant *);
|
||||
using FuncCSharpInstanceBridge_Get = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, Variant *);
|
||||
using FuncCSharpInstanceBridge_CallDispose = void(GD_CLR_STDCALL *)(GCHandleIntPtr, bool);
|
||||
using FuncCSharpInstanceBridge_CallToString = void(GD_CLR_STDCALL *)(GCHandleIntPtr, String *, bool *);
|
||||
using FuncCSharpInstanceBridge_HasMethodUnknownParams = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *);
|
||||
using FuncCSharpInstanceBridge_SerializeState = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Dictionary *, const Dictionary *);
|
||||
using FuncCSharpInstanceBridge_DeserializeState = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Dictionary *, const Dictionary *);
|
||||
using FuncGCHandleBridge_FreeGCHandle = void(GD_CLR_STDCALL *)(GCHandleIntPtr);
|
||||
using FuncGCHandleBridge_GCHandleIsTargetCollectible = bool(GD_CLR_STDCALL *)(GCHandleIntPtr);
|
||||
using FuncDebuggingUtils_GetCurrentStackInfo = void(GD_CLR_STDCALL *)(Vector<ScriptLanguage::StackInfo> *);
|
||||
using FuncDisposablesTracker_OnGodotShuttingDown = void(GD_CLR_STDCALL *)();
|
||||
using FuncGD_OnCoreApiAssemblyLoaded = void(GD_CLR_STDCALL *)(bool);
|
||||
|
||||
FuncSignalAwaiter_SignalCallback SignalAwaiter_SignalCallback;
|
||||
FuncDelegateUtils_InvokeWithVariantArgs DelegateUtils_InvokeWithVariantArgs;
|
||||
FuncDelegateUtils_DelegateEquals DelegateUtils_DelegateEquals;
|
||||
FuncDelegateUtils_DelegateHash DelegateUtils_DelegateHash;
|
||||
FuncDelegateUtils_GetArgumentCount DelegateUtils_GetArgumentCount;
|
||||
FuncDelegateUtils_TrySerializeDelegateWithGCHandle DelegateUtils_TrySerializeDelegateWithGCHandle;
|
||||
FuncDelegateUtils_TryDeserializeDelegateWithGCHandle DelegateUtils_TryDeserializeDelegateWithGCHandle;
|
||||
FuncScriptManagerBridge_FrameCallback ScriptManagerBridge_FrameCallback;
|
||||
FuncScriptManagerBridge_CreateManagedForGodotObjectBinding ScriptManagerBridge_CreateManagedForGodotObjectBinding;
|
||||
FuncScriptManagerBridge_CreateManagedForGodotObjectScriptInstance ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance;
|
||||
FuncScriptManagerBridge_GetScriptNativeName ScriptManagerBridge_GetScriptNativeName;
|
||||
FuncScriptManagerBridge_GetGlobalClassName ScriptManagerBridge_GetGlobalClassName;
|
||||
FuncScriptManagerBridge_SetGodotObjectPtr ScriptManagerBridge_SetGodotObjectPtr;
|
||||
FuncScriptManagerBridge_RaiseEventSignal ScriptManagerBridge_RaiseEventSignal;
|
||||
FuncScriptManagerBridge_ScriptIsOrInherits ScriptManagerBridge_ScriptIsOrInherits;
|
||||
FuncScriptManagerBridge_AddScriptBridge ScriptManagerBridge_AddScriptBridge;
|
||||
FuncScriptManagerBridge_GetOrCreateScriptBridgeForPath ScriptManagerBridge_GetOrCreateScriptBridgeForPath;
|
||||
FuncScriptManagerBridge_RemoveScriptBridge ScriptManagerBridge_RemoveScriptBridge;
|
||||
FuncScriptManagerBridge_TryReloadRegisteredScriptWithClass ScriptManagerBridge_TryReloadRegisteredScriptWithClass;
|
||||
FuncScriptManagerBridge_UpdateScriptClassInfo ScriptManagerBridge_UpdateScriptClassInfo;
|
||||
FuncScriptManagerBridge_SwapGCHandleForType ScriptManagerBridge_SwapGCHandleForType;
|
||||
FuncScriptManagerBridge_GetPropertyInfoList ScriptManagerBridge_GetPropertyInfoList;
|
||||
FuncScriptManagerBridge_GetPropertyDefaultValues ScriptManagerBridge_GetPropertyDefaultValues;
|
||||
FuncScriptManagerBridge_CallStatic ScriptManagerBridge_CallStatic;
|
||||
FuncCSharpInstanceBridge_Call CSharpInstanceBridge_Call;
|
||||
FuncCSharpInstanceBridge_Set CSharpInstanceBridge_Set;
|
||||
FuncCSharpInstanceBridge_Get CSharpInstanceBridge_Get;
|
||||
FuncCSharpInstanceBridge_CallDispose CSharpInstanceBridge_CallDispose;
|
||||
FuncCSharpInstanceBridge_CallToString CSharpInstanceBridge_CallToString;
|
||||
FuncCSharpInstanceBridge_HasMethodUnknownParams CSharpInstanceBridge_HasMethodUnknownParams;
|
||||
FuncCSharpInstanceBridge_SerializeState CSharpInstanceBridge_SerializeState;
|
||||
FuncCSharpInstanceBridge_DeserializeState CSharpInstanceBridge_DeserializeState;
|
||||
FuncGCHandleBridge_FreeGCHandle GCHandleBridge_FreeGCHandle;
|
||||
FuncGCHandleBridge_GCHandleIsTargetCollectible GCHandleBridge_GCHandleIsTargetCollectible;
|
||||
FuncDebuggingUtils_GetCurrentStackInfo DebuggingUtils_GetCurrentStackInfo;
|
||||
FuncDisposablesTracker_OnGodotShuttingDown DisposablesTracker_OnGodotShuttingDown;
|
||||
FuncGD_OnCoreApiAssemblyLoaded GD_OnCoreApiAssemblyLoaded;
|
||||
};
|
||||
|
||||
extern ManagedCallbacks managed_callbacks;
|
||||
extern bool godot_api_cache_updated;
|
||||
|
||||
void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks);
|
||||
|
||||
} // namespace GDMonoCache
|
Reference in New Issue
Block a user