Merge pull request #52610 from neikeq/dotnet6_wip2

This commit is contained in:
Rémi Verschelde 2021-10-03 10:14:09 +02:00 committed by GitHub
commit ca0ce5b46e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
129 changed files with 5065 additions and 11323 deletions

View File

@ -12,6 +12,10 @@ jobs:
runs-on: "ubuntu-20.04"
name: Editor (target=release_debug, tools=yes, tests=yes)
env:
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: false
steps:
- uses: actions/checkout@v2
@ -58,18 +62,31 @@ jobs:
python --version
scons --version
- name: Set up .NET Sdk
uses: actions/setup-dotnet@v1
with:
dotnet-version: '5.0.x'
# We should always be explicit with our flags usage here since it's gonna be sure to always set those flags
- name: Compilation
env:
SCONS_CACHE: ${{github.workspace}}/.scons_cache/
run: |
scons tools=yes tests=yes target=release_debug
scons tools=yes tests=yes target=release_debug module_mono_enabled=yes
ls -l bin/
- name: Generate C# glue
run: |
./bin/godot.linuxbsd.opt.tools.64.mono --generate-mono-glue ./modules/mono/glue
- name: Build .NET solutions
run: |
./modules/mono/build_scripts/build_assemblies.py --godot-output-dir=./bin --godot-target=release_debug --godot-platform=linuxbsd
# Execute unit tests for the editor
- name: Unit Tests
run: |
./bin/godot.linuxbsd.opt.tools.64 --test
./bin/godot.linuxbsd.opt.tools.64.mono --test
# Download, unzip and setup SwiftShader library [4466040]
- name: Download SwiftShader
@ -85,7 +102,7 @@ jobs:
run: |
echo "Running --doctool to see if this changes the public API without updating the documentation."
echo -e "If a diff is shown, it means that your code/doc changes are incomplete and you should update the class reference with --doctool.\n\n"
VK_ICD_FILENAMES=$(pwd)/vk_swiftshader_icd.json DRI_PRIME=0 xvfb-run bin/godot.linuxbsd.opt.tools.64 --doctool . 2>&1 > /dev/null || true
VK_ICD_FILENAMES=$(pwd)/vk_swiftshader_icd.json DRI_PRIME=0 xvfb-run bin/godot.linuxbsd.opt.tools.64.mono --doctool . 2>&1 > /dev/null || true
git diff --color --exit-code && ! git ls-files --others --exclude-standard | sed -e 's/^/New doc file missing in PR: /' | grep 'xml$'
- uses: actions/upload-artifact@v2
@ -190,6 +207,10 @@ jobs:
runs-on: "ubuntu-20.04"
name: Template w/ Mono (target=release, tools=no)
env:
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: false
steps:
- uses: actions/checkout@v2
@ -236,11 +257,16 @@ jobs:
python --version
scons --version
- name: Set up .NET Sdk
uses: actions/setup-dotnet@v1
with:
dotnet-version: '5.0.x'
- name: Compilation
env:
SCONS_CACHE: ${{github.workspace}}/.scons_cache/
run: |
scons target=release tools=no module_mono_enabled=yes mono_glue=no
scons target=release tools=no module_mono_enabled=yes
ls -l bin/
- uses: actions/upload-artifact@v2

View File

@ -1293,20 +1293,6 @@
<member name="memory/limits/multithreaded_server/rid_pool_prealloc" type="int" setter="" getter="" default="60">
This is used by servers when used in multi-threading mode (servers and visual). RIDs are preallocated to avoid stalling the server requesting them on threads. If servers get stalled too often when loading resources in a thread, increase this number.
</member>
<member name="mono/debugger_agent/port" type="int" setter="" getter="" default="23685">
</member>
<member name="mono/debugger_agent/wait_for_debugger" type="bool" setter="" getter="" default="false">
</member>
<member name="mono/debugger_agent/wait_timeout" type="int" setter="" getter="" default="3000">
</member>
<member name="mono/profiler/args" type="String" setter="" getter="" default="&quot;log:calls,alloc,sample,output=output.mlpd&quot;">
</member>
<member name="mono/profiler/enabled" type="bool" setter="" getter="" default="false">
</member>
<member name="mono/project/auto_update_project" type="bool" setter="" getter="" default="true">
</member>
<member name="mono/unhandled_exception_policy" type="int" setter="" getter="" default="0">
</member>
<member name="navigation/2d/default_cell_size" type="int" setter="" getter="" default="10">
Default cell size for 2D navigation maps. See [method NavigationServer2D.map_set_cell_size].
</member>

View File

@ -1944,20 +1944,6 @@ bool Main::start() {
ERR_FAIL_COND_V_MSG(!da, false, "Argument supplied to --doctool must be a valid directory path.");
}
#ifndef MODULE_MONO_ENABLED
// Hack to define Mono-specific project settings even on non-Mono builds,
// so that we don't lose their descriptions and default values in DocData.
// Default values should be synced with mono_gd/gd_mono.cpp.
GLOBAL_DEF("mono/debugger_agent/port", 23685);
GLOBAL_DEF("mono/debugger_agent/wait_for_debugger", false);
GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000);
GLOBAL_DEF("mono/profiler/args", "log:calls,alloc,sample,output=output.mlpd");
GLOBAL_DEF("mono/profiler/enabled", false);
GLOBAL_DEF("mono/unhandled_exception_policy", 0);
// From editor/csharp_project.cpp.
GLOBAL_DEF("mono/project/auto_update_project", true);
#endif
DocTools doc;
doc.generate(doc_base);

View File

@ -203,6 +203,9 @@ def build_godot_api(msbuild_tool, module_dir, output_dir):
"GodotSharpEditor.dll",
"GodotSharpEditor.pdb",
"GodotSharpEditor.xml",
"GodotPlugins.dll",
"GodotPlugins.pdb",
"GodotPlugins.runtimeconfig.json",
]
for build_config in ["Debug", "Release"]:
@ -223,6 +226,7 @@ def build_godot_api(msbuild_tool, module_dir, output_dir):
core_src_dir = os.path.abspath(os.path.join(sln, os.pardir, "GodotSharp", "bin", build_config))
editor_src_dir = os.path.abspath(os.path.join(sln, os.pardir, "GodotSharpEditor", "bin", build_config))
plugins_src_dir = os.path.abspath(os.path.join(sln, os.pardir, "GodotPlugins", "bin", build_config, "net5.0"))
if not os.path.isdir(editor_api_dir):
assert not os.path.isfile(editor_api_dir)
@ -236,6 +240,8 @@ def build_godot_api(msbuild_tool, module_dir, output_dir):
src_path = os.path.join(core_src_dir, filename)
if not os.path.isfile(src_path):
src_path = os.path.join(editor_src_dir, filename)
if not os.path.isfile(src_path):
src_path = os.path.join(plugins_src_dir, filename)
print(f"Copying assembly to {target_path}...")
copy(src_path, target_path)

View File

@ -1,53 +0,0 @@
def generate_compressed_config(config_src, output_dir):
import os.path
# Source file
with open(os.path.join(output_dir, "android_mono_config.gen.cpp"), "w") as cpp:
with open(config_src, "rb") as f:
buf = f.read()
decompr_size = len(buf)
import zlib
buf = zlib.compress(buf)
compr_size = len(buf)
bytes_seq_str = ""
for i, buf_idx in enumerate(range(compr_size)):
if i > 0:
bytes_seq_str += ", "
bytes_seq_str += str(buf[buf_idx])
cpp.write(
"""/* THIS FILE IS GENERATED DO NOT EDIT */
#include "android_mono_config.h"
#ifdef ANDROID_ENABLED
#include "core/io/compression.h"
namespace {
// config
static const int config_compressed_size = %d;
static const int config_uncompressed_size = %d;
static const unsigned char config_compressed_data[] = { %s };
} // namespace
String get_godot_android_mono_config() {
Vector<uint8_t> data;
data.resize(config_uncompressed_size);
uint8_t* w = data.ptrw();
Compression::decompress(w, config_uncompressed_size, config_compressed_data,
config_compressed_size, Compression::MODE_DEFLATE);
String s;
if (s.parse_utf8((const char *)w, data.size())) {
ERR_FAIL_V(String());
}
return s;
}
#endif // ANDROID_ENABLED
"""
% (compr_size, decompr_size, bytes_seq_str)
)

View File

@ -1,28 +0,0 @@
<configuration>
<dllmap wordsize="32" dll="i:cygwin1.dll" target="/system/lib/libc.so" />
<dllmap wordsize="64" dll="i:cygwin1.dll" target="/system/lib64/libc.so" />
<dllmap wordsize="32" dll="libc" target="/system/lib/libc.so" />
<dllmap wordsize="64" dll="libc" target="/system/lib64/libc.so" />
<dllmap wordsize="32" dll="intl" target="/system/lib/libc.so" />
<dllmap wordsize="64" dll="intl" target="/system/lib64/libc.so" />
<dllmap wordsize="32" dll="libintl" target="/system/lib/libc.so" />
<dllmap wordsize="64" dll="libintl" target="/system/lib64/libc.so" />
<dllmap dll="MonoPosixHelper" target="libMonoPosixHelper.so" />
<dllmap dll="System.Native" target="libmono-native.so" />
<dllmap wordsize="32" dll="i:msvcrt" target="/system/lib/libc.so" />
<dllmap wordsize="64" dll="i:msvcrt" target="/system/lib64/libc.so" />
<dllmap wordsize="32" dll="i:msvcrt.dll" target="/system/lib/libc.so" />
<dllmap wordsize="64" dll="i:msvcrt.dll" target="/system/lib64/libc.so" />
<dllmap wordsize="32" dll="sqlite" target="/system/lib/libsqlite.so" />
<dllmap wordsize="64" dll="sqlite" target="/system/lib64/libsqlite.so" />
<dllmap wordsize="32" dll="sqlite3" target="/system/lib/libsqlite.so" />
<dllmap wordsize="64" dll="sqlite3" target="/system/lib64/libsqlite.so" />
<dllmap wordsize="32" dll="liblog" target="/system/lib/liblog.so" />
<dllmap wordsize="64" dll="liblog" target="/system/lib64/liblog.so" />
<dllmap dll="i:kernel32.dll">
<dllentry dll="__Internal" name="CopyMemory" target="mono_win32_compat_CopyMemory"/>
<dllentry dll="__Internal" name="FillMemory" target="mono_win32_compat_FillMemory"/>
<dllentry dll="__Internal" name="MoveMemory" target="mono_win32_compat_MoveMemory"/>
<dllentry dll="__Internal" name="ZeroMemory" target="mono_win32_compat_ZeroMemory"/>
</dllmap>
</configuration>

View File

@ -1,65 +1,5 @@
import os
import os.path
import subprocess
from SCons.Script import Dir, Environment
if os.name == "nt":
from . import mono_reg_utils as monoreg
android_arch_dirs = {
"armv7": "armeabi-v7a",
"arm64v8": "arm64-v8a",
"x86": "x86",
"x86_64": "x86_64",
}
def get_android_out_dir(env):
return os.path.join(
Dir("#platform/android/java/lib/libs").abspath,
"release" if env["target"] == "release" else "debug",
android_arch_dirs[env["android_arch"]],
)
def find_name_in_dir_files(directory, names, prefixes=[""], extensions=[""]):
for extension in extensions:
if extension and not extension.startswith("."):
extension = "." + extension
for prefix in prefixes:
for curname in names:
if os.path.isfile(os.path.join(directory, prefix + curname + extension)):
return curname
return ""
def find_file_in_dir(directory, names, prefixes=[""], extensions=[""]):
for extension in extensions:
if extension and not extension.startswith("."):
extension = "." + extension
for prefix in prefixes:
for curname in names:
filename = prefix + curname + extension
if os.path.isfile(os.path.join(directory, filename)):
return filename
return ""
def copy_file(src_dir, dst_dir, src_name, dst_name=""):
from shutil import copy
src_path = os.path.join(Dir(src_dir).abspath, src_name)
dst_dir = Dir(dst_dir).abspath
if not os.path.isdir(dst_dir):
os.makedirs(dst_dir)
if dst_name:
copy(src_path, os.path.join(dst_dir, dst_name))
else:
copy(src_path, dst_dir)
def is_desktop(platform):
@ -71,504 +11,162 @@ def is_unix_like(platform):
def module_supports_tools_on(platform):
return platform not in ["android", "javascript", "iphone"]
def find_wasm_src_dir(mono_root):
hint_dirs = [
os.path.join(mono_root, "src"),
os.path.join(mono_root, "../src"),
]
for hint_dir in hint_dirs:
if os.path.isfile(os.path.join(hint_dir, "driver.c")):
return hint_dir
return ""
return is_desktop(platform)
def configure(env, env_mono):
bits = env["bits"]
is_android = env["platform"] == "android"
is_javascript = env["platform"] == "javascript"
is_ios = env["platform"] == "iphone"
is_ios_sim = is_ios and env["arch"] in ["x86", "x86_64"]
# is_android = env["platform"] == "android"
# is_javascript = env["platform"] == "javascript"
# is_ios = env["platform"] == "iphone"
# is_ios_sim = is_ios and env["arch"] in ["x86", "x86_64"]
tools_enabled = env["tools"]
mono_static = env["mono_static"]
copy_mono_root = env["copy_mono_root"]
mono_prefix = env["mono_prefix"]
mono_bcl = env["mono_bcl"]
mono_lib_names = ["mono-2.0-sgen", "monosgen-2.0"]
if is_android and not env["android_arch"] in android_arch_dirs:
raise RuntimeError("This module does not support the specified 'android_arch': " + env["android_arch"])
if tools_enabled and not module_supports_tools_on(env["platform"]):
# TODO:
# Android: We have to add the data directory to the apk, concretely the Api and Tools folders.
raise RuntimeError("This module does not currently support building for this platform with tools enabled")
if is_android and mono_static:
# FIXME: When static linking and doing something that requires libmono-native, we get a dlopen error as 'libmono-native'
# seems to depend on 'libmonosgen-2.0'. Could be fixed by re-directing to '__Internal' with a dllmap or in the dlopen hook.
raise RuntimeError("Statically linking Mono is not currently supported for this platform")
if not mono_static and (is_javascript or is_ios):
raise RuntimeError("Dynamically linking Mono is not currently supported for this platform")
if not mono_prefix and (os.getenv("MONO32_PREFIX") or os.getenv("MONO64_PREFIX")):
print(
"WARNING: The environment variables 'MONO32_PREFIX' and 'MONO64_PREFIX' are deprecated; use the"
" 'mono_prefix' SCons parameter instead"
)
# Although we don't support building with tools for any platform where we currently use static AOT,
# if these are supported in the future, we won't be using static AOT for them as that would be
# too restrictive for the editor. These builds would probably be made to only use the interpreter.
mono_aot_static = (is_ios and not is_ios_sim) and not env["tools"]
# Static AOT is only supported on the root domain
mono_single_appdomain = mono_aot_static
if mono_single_appdomain:
env_mono.Append(CPPDEFINES=["GD_MONO_SINGLE_APPDOMAIN"])
if (env["tools"] or env["target"] != "release") and not mono_single_appdomain:
if env["tools"] or env["target"] != "release":
env_mono.Append(CPPDEFINES=["GD_MONO_HOT_RELOAD"])
if env["platform"] == "windows":
mono_root = mono_prefix
dotnet_root = env["dotnet_root"]
if not mono_root and os.name == "nt":
mono_root = monoreg.find_mono_root_dir(bits)
if not mono_root:
raise RuntimeError(
"Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter"
)
print("Found Mono root directory: " + mono_root)
mono_lib_path = os.path.join(mono_root, "lib")
env.Append(LIBPATH=mono_lib_path)
env_mono.Prepend(CPPPATH=os.path.join(mono_root, "include", "mono-2.0"))
lib_suffixes = [".lib"]
if not env.msvc:
# MingW supports both '.a' and '.lib'
lib_suffixes.insert(0, ".a")
if mono_static:
if env.msvc:
mono_static_lib_name = "libmono-static-sgen"
else:
mono_static_lib_name = "libmonosgen-2.0"
mono_static_lib_file = find_file_in_dir(mono_lib_path, [mono_static_lib_name], extensions=lib_suffixes)
if not mono_static_lib_file:
raise RuntimeError("Could not find static mono library in: " + mono_lib_path)
if env.msvc:
env.Append(LINKFLAGS=mono_static_lib_file)
env.Append(LINKFLAGS="Mincore.lib")
env.Append(LINKFLAGS="msvcrt.lib")
env.Append(LINKFLAGS="LIBCMT.lib")
env.Append(LINKFLAGS="Psapi.lib")
else:
mono_static_lib_file_path = os.path.join(mono_lib_path, mono_static_lib_file)
env.Append(LINKFLAGS=["-Wl,-whole-archive", mono_static_lib_file_path, "-Wl,-no-whole-archive"])
env.Append(LIBS=["psapi"])
env.Append(LIBS=["version"])
if not dotnet_root:
dotnet_exe = find_executable("dotnet")
if dotnet_exe:
dotnet_exe_realpath = os.path.realpath(dotnet_exe) # Eliminate symbolic links
dotnet_root = os.path.abspath(os.path.join(dotnet_exe_realpath, os.pardir))
else:
mono_lib_file = find_file_in_dir(mono_lib_path, mono_lib_names, extensions=lib_suffixes)
raise RuntimeError("Cannot find .NET Core Sdk")
if not mono_lib_file:
raise RuntimeError("Could not find mono library in: " + mono_lib_path)
print("Found .NET Core Sdk root directory: " + dotnet_root)
if env.msvc:
env.Append(LINKFLAGS=mono_lib_file)
else:
mono_lib_file_path = os.path.join(mono_lib_path, mono_lib_file)
env.Append(LINKFLAGS=mono_lib_file_path)
dotnet_cmd = os.path.join(dotnet_root, "dotnet.exe" if os.name == "nt" else "dotnet")
mono_bin_path = os.path.join(mono_root, "bin")
runtime_identifier = determine_runtime_identifier(env)
mono_dll_file = find_file_in_dir(mono_bin_path, mono_lib_names, prefixes=["", "lib"], extensions=[".dll"])
# TODO: In the future, if it can't be found this way, we want to obtain it
# from the runtime.{runtime_identifier}.Microsoft.NETCore.DotNetAppHost NuGet package.
app_host_search_version = "5.0"
app_host_version = find_app_host_version(dotnet_cmd, app_host_search_version)
if not app_host_version:
raise RuntimeError("Cannot find .NET app host for version: " + app_host_search_version)
if not mono_dll_file:
raise RuntimeError("Could not find mono shared library in: " + mono_bin_path)
app_host_dir = os.path.join(
dotnet_root,
"packs",
"Microsoft.NETCore.App.Host." + runtime_identifier,
app_host_version,
"runtimes",
runtime_identifier,
"native",
)
copy_file(mono_bin_path, "#bin", mono_dll_file)
def check_app_host_file_exists(file):
file_path = os.path.join(app_host_dir, file)
if not os.path.isfile(file_path):
raise RuntimeError("File not found: " + file_path)
# TODO:
# All libnethost does for us is provide a function to find hostfxr.
# If we could handle that logic ourselves we could void linking it.
# nethost file names:
# static: libnethost.a/lib
# shared: libnethost.a/dylib and nethost.dll
check_app_host_file_exists("libnethost.lib" if os.name == "nt" else "libnethost.a")
check_app_host_file_exists("nethost.h")
check_app_host_file_exists("hostfxr.h")
check_app_host_file_exists("coreclr_delegates.h")
env.Append(LIBPATH=[app_host_dir])
env_mono.Prepend(CPPPATH=app_host_dir)
libnethost_path = os.path.join(app_host_dir, "libnethost.lib" if os.name == "nt" else "libnethost.a")
if env["platform"] == "windows":
if env.msvc:
env.Append(LINKFLAGS="libnethost.lib")
else:
env.Append(LINKFLAGS=["-Wl,-whole-archive", libnethost_path, "-Wl,-no-whole-archive"])
else:
is_apple = env["platform"] in ["osx", "iphone"]
is_macos = is_apple and not is_ios
# is_macos = is_apple and not is_ios
sharedlib_ext = ".dylib" if is_apple else ".so"
# if is_ios and not is_ios_sim:
# env_mono.Append(CPPDEFINES=["IOS_DEVICE"])
mono_root = mono_prefix
mono_lib_path = ""
mono_so_file = ""
if not mono_root and (is_android or is_javascript or is_ios):
raise RuntimeError(
"Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter"
)
if not mono_root and is_macos:
# Try with some known directories under OSX
hint_dirs = ["/Library/Frameworks/Mono.framework/Versions/Current", "/usr/local/var/homebrew/linked/mono"]
for hint_dir in hint_dirs:
if os.path.isdir(hint_dir):
mono_root = hint_dir
break
# We can't use pkg-config to link mono statically,
# but we can still use it to find the mono root directory
if not mono_root and mono_static:
mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext)
if not mono_root:
raise RuntimeError(
"Building with mono_static=yes, but failed to find the mono prefix with pkg-config; "
+ "specify one manually with the 'mono_prefix' SCons parameter"
)
if is_ios and not is_ios_sim:
env_mono.Append(CPPDEFINES=["IOS_DEVICE"])
if mono_root:
print("Found Mono root directory: " + mono_root)
mono_lib_path = os.path.join(mono_root, "lib")
env.Append(LIBPATH=[mono_lib_path])
env_mono.Prepend(CPPPATH=os.path.join(mono_root, "include", "mono-2.0"))
mono_lib = find_name_in_dir_files(mono_lib_path, mono_lib_names, prefixes=["lib"], extensions=[".a"])
if not mono_lib:
raise RuntimeError("Could not find mono library in: " + mono_lib_path)
env_mono.Append(CPPDEFINES=["_REENTRANT"])
if mono_static:
if not is_javascript:
env.Append(LINKFLAGS=["-rdynamic"])
mono_lib_file = os.path.join(mono_lib_path, "lib" + mono_lib + ".a")
if is_apple:
if is_macos:
env.Append(LINKFLAGS=["-Wl,-force_load," + mono_lib_file])
else:
arch = env["arch"]
def copy_mono_lib(libname_wo_ext):
copy_file(
mono_lib_path, "#bin", libname_wo_ext + ".a", "%s.iphone.%s.a" % (libname_wo_ext, arch)
)
# Copy Mono libraries to the output folder. These are meant to be bundled with
# the export templates and added to the Xcode project when exporting a game.
copy_mono_lib("lib" + mono_lib)
copy_mono_lib("libmono-native")
copy_mono_lib("libmono-profiler-log")
if not is_ios_sim:
copy_mono_lib("libmono-ee-interp")
copy_mono_lib("libmono-icall-table")
copy_mono_lib("libmono-ilgen")
else:
assert is_desktop(env["platform"]) or is_android or is_javascript
env.Append(LINKFLAGS=["-Wl,-whole-archive", mono_lib_file, "-Wl,-no-whole-archive"])
if is_javascript:
env.Append(LIBS=["mono-icall-table", "mono-native", "mono-ilgen", "mono-ee-interp"])
wasm_src_dir = os.path.join(mono_root, "src")
if not os.path.isdir(wasm_src_dir):
raise RuntimeError("Could not find mono wasm src directory")
# Ideally this should be defined only for 'driver.c', but I can't fight scons for another 2 hours
env_mono.Append(CPPDEFINES=["CORE_BINDINGS"])
env_mono.add_source_files(
env.modules_sources,
[
os.path.join(wasm_src_dir, "driver.c"),
os.path.join(wasm_src_dir, "zlib-helper.c"),
os.path.join(wasm_src_dir, "corebindings.c"),
],
)
env.Append(
LINKFLAGS=[
"--js-library",
os.path.join(wasm_src_dir, "library_mono.js"),
"--js-library",
os.path.join(wasm_src_dir, "binding_support.js"),
"--js-library",
os.path.join(wasm_src_dir, "dotnet_support.js"),
]
)
else:
env.Append(LIBS=[mono_lib])
if is_macos:
env.Append(LIBS=["iconv", "pthread"])
elif is_android:
pass # Nothing
elif is_ios:
pass # Nothing, linking is delegated to the exported Xcode project
elif is_javascript:
env.Append(LIBS=["m", "rt", "dl", "pthread"])
else:
env.Append(LIBS=["m", "rt", "dl", "pthread"])
if not mono_static:
mono_so_file = find_file_in_dir(
mono_lib_path, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext]
)
if not mono_so_file:
raise RuntimeError("Could not find mono shared library in: " + mono_lib_path)
if is_apple:
env.Append(LINKFLAGS=["-Wl,-force_load," + libnethost_path])
else:
assert not mono_static
# TODO: Add option to force using pkg-config
print("Mono root directory not found. Using pkg-config instead")
env.ParseConfig("pkg-config monosgen-2 --libs")
env_mono.ParseConfig("pkg-config monosgen-2 --cflags")
tmpenv = Environment()
tmpenv.AppendENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH"))
tmpenv.ParseConfig("pkg-config monosgen-2 --libs-only-L")
for hint_dir in tmpenv["LIBPATH"]:
file_found = find_file_in_dir(hint_dir, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext])
if file_found:
mono_lib_path = hint_dir
mono_so_file = file_found
break
if not mono_so_file:
raise RuntimeError("Could not find mono shared library in: " + str(tmpenv["LIBPATH"]))
if not mono_static:
libs_output_dir = get_android_out_dir(env) if is_android else "#bin"
copy_file(mono_lib_path, libs_output_dir, mono_so_file)
if not tools_enabled:
if is_desktop(env["platform"]):
if not mono_root:
mono_root = (
subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip()
)
make_template_dir(env, mono_root)
elif is_android:
# Compress Android Mono Config
from . import make_android_mono_config
module_dir = os.getcwd()
config_file_path = os.path.join(module_dir, "build_scripts", "mono_android_config.xml")
make_android_mono_config.generate_compressed_config(config_file_path, "mono_gd/")
# Copy the required shared libraries
copy_mono_shared_libs(env, mono_root, None)
elif is_javascript:
pass # No data directory for this platform
elif is_ios:
pass # No data directory for this platform
if copy_mono_root:
if not mono_root:
mono_root = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip()
if tools_enabled:
# Only supported for editor builds.
copy_mono_root_files(env, mono_root, mono_bcl)
env.Append(LINKFLAGS=["-Wl,-whole-archive", libnethost_path, "-Wl,-no-whole-archive"])
def make_template_dir(env, mono_root):
from shutil import rmtree
def determine_runtime_identifier(env):
names_map = {
"windows": "win",
"osx": "osx",
"linuxbsd": "linux",
"server": "linux", # FIXME: Is server linux only, or also macos?
}
platform = env["platform"]
target = env["target"]
template_dir_name = ""
assert is_desktop(platform)
template_dir_name = "data.mono.%s.%s.%s" % (platform, env["bits"], target)
output_dir = Dir("#bin").abspath
template_dir = os.path.join(output_dir, template_dir_name)
template_mono_root_dir = os.path.join(template_dir, "Mono")
if os.path.isdir(template_mono_root_dir):
rmtree(template_mono_root_dir) # Clean first
# Copy etc/mono/
template_mono_config_dir = os.path.join(template_mono_root_dir, "etc", "mono")
copy_mono_etc_dir(mono_root, template_mono_config_dir, platform)
# Copy the required shared libraries
copy_mono_shared_libs(env, mono_root, template_mono_root_dir)
def copy_mono_root_files(env, mono_root, mono_bcl):
from glob import glob
from shutil import copy
from shutil import rmtree
if not mono_root:
raise RuntimeError("Mono installation directory not found")
output_dir = Dir("#bin").abspath
editor_mono_root_dir = os.path.join(output_dir, "GodotSharp", "Mono")
if os.path.isdir(editor_mono_root_dir):
rmtree(editor_mono_root_dir) # Clean first
# Copy etc/mono/
editor_mono_config_dir = os.path.join(editor_mono_root_dir, "etc", "mono")
copy_mono_etc_dir(mono_root, editor_mono_config_dir, env["platform"])
# Copy the required shared libraries
copy_mono_shared_libs(env, mono_root, editor_mono_root_dir)
# Copy framework assemblies
mono_framework_dir = mono_bcl or os.path.join(mono_root, "lib", "mono", "4.5")
mono_framework_facades_dir = os.path.join(mono_framework_dir, "Facades")
editor_mono_framework_dir = os.path.join(editor_mono_root_dir, "lib", "mono", "4.5")
editor_mono_framework_facades_dir = os.path.join(editor_mono_framework_dir, "Facades")
if not os.path.isdir(editor_mono_framework_dir):
os.makedirs(editor_mono_framework_dir)
if not os.path.isdir(editor_mono_framework_facades_dir):
os.makedirs(editor_mono_framework_facades_dir)
for assembly in glob(os.path.join(mono_framework_dir, "*.dll")):
copy(assembly, editor_mono_framework_dir)
for assembly in glob(os.path.join(mono_framework_facades_dir, "*.dll")):
copy(assembly, editor_mono_framework_facades_dir)
def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform):
from distutils.dir_util import copy_tree
from glob import glob
from shutil import copy
if not os.path.isdir(target_mono_config_dir):
os.makedirs(target_mono_config_dir)
mono_etc_dir = os.path.join(mono_root, "etc", "mono")
if not os.path.isdir(mono_etc_dir):
mono_etc_dir = ""
etc_hint_dirs = []
if platform != "windows":
etc_hint_dirs += ["/etc/mono", "/usr/local/etc/mono"]
if "MONO_CFG_DIR" in os.environ:
etc_hint_dirs += [os.path.join(os.environ["MONO_CFG_DIR"], "mono")]
for etc_hint_dir in etc_hint_dirs:
if os.path.isdir(etc_hint_dir):
mono_etc_dir = etc_hint_dir
break
if not mono_etc_dir:
raise RuntimeError("Mono installation etc directory not found")
copy_tree(os.path.join(mono_etc_dir, "2.0"), os.path.join(target_mono_config_dir, "2.0"))
copy_tree(os.path.join(mono_etc_dir, "4.0"), os.path.join(target_mono_config_dir, "4.0"))
copy_tree(os.path.join(mono_etc_dir, "4.5"), os.path.join(target_mono_config_dir, "4.5"))
if os.path.isdir(os.path.join(mono_etc_dir, "mconfig")):
copy_tree(os.path.join(mono_etc_dir, "mconfig"), os.path.join(target_mono_config_dir, "mconfig"))
for file in glob(os.path.join(mono_etc_dir, "*")):
if os.path.isfile(file):
copy(file, target_mono_config_dir)
def copy_mono_shared_libs(env, mono_root, target_mono_root_dir):
from shutil import copy
def copy_if_exists(src, dst):
if os.path.isfile(src):
copy(src, dst)
# architectures names: x86, x64, arm, or arm64
platform = env["platform"]
if platform == "windows":
src_mono_bin_dir = os.path.join(mono_root, "bin")
target_mono_bin_dir = os.path.join(target_mono_root_dir, "bin")
if not os.path.isdir(target_mono_bin_dir):
os.makedirs(target_mono_bin_dir)
mono_posix_helper_file = find_file_in_dir(
src_mono_bin_dir, ["MonoPosixHelper"], prefixes=["", "lib"], extensions=[".dll"]
)
copy(
os.path.join(src_mono_bin_dir, mono_posix_helper_file),
os.path.join(target_mono_bin_dir, "MonoPosixHelper.dll"),
)
# For newer versions
btls_dll_path = os.path.join(src_mono_bin_dir, "libmono-btls-shared.dll")
if os.path.isfile(btls_dll_path):
copy(btls_dll_path, target_mono_bin_dir)
if is_desktop(platform):
bits = env["bits"]
bit_arch_map = {"64": "x64", "32": "x86"}
return "%s-%s" % (names_map[platform], bit_arch_map[bits])
else:
target_mono_lib_dir = (
get_android_out_dir(env) if platform == "android" else os.path.join(target_mono_root_dir, "lib")
)
if not os.path.isdir(target_mono_lib_dir):
os.makedirs(target_mono_lib_dir)
lib_file_names = []
if platform == "osx":
lib_file_names = [
lib_name + ".dylib"
for lib_name in ["libmono-btls-shared", "libmono-native-compat", "libMonoPosixHelper"]
]
elif is_unix_like(platform):
lib_file_names = [
lib_name + ".so"
for lib_name in [
"libmono-btls-shared",
"libmono-ee-interp",
"libmono-native",
"libMonoPosixHelper",
"libmono-profiler-aot",
"libmono-profiler-coverage",
"libmono-profiler-log",
"libMonoSupportW",
]
]
for lib_file_name in lib_file_names:
copy_if_exists(os.path.join(mono_root, "lib", lib_file_name), target_mono_lib_dir)
raise NotImplementedError()
def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext):
tmpenv = Environment()
tmpenv.AppendENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH"))
tmpenv.ParseConfig("pkg-config monosgen-2 --libs-only-L")
for hint_dir in tmpenv["LIBPATH"]:
name_found = find_name_in_dir_files(hint_dir, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext])
if name_found and os.path.isdir(os.path.join(hint_dir, "..", "include", "mono-2.0")):
return os.path.join(hint_dir, "..")
def find_app_host_version(dotnet_cmd, search_version):
import subprocess
try:
lines = subprocess.check_output([dotnet_cmd, "--list-runtimes"]).splitlines()
for line_bytes in lines:
line = line_bytes.decode("utf-8")
if not line.startswith("Microsoft.NETCore.App "):
continue
parts = line.split(" ")
if len(parts) < 2:
continue
version = parts[1]
# Look for 6.0.0 or 6.0.0-*
if version.startswith(search_version + "."):
return version
except (subprocess.CalledProcessError, OSError):
pass
return ""
ENV_PATH_SEP = ";" if os.name == "nt" else ":"
def find_executable(name):
is_windows = os.name == "nt"
windows_exts = os.environ["PATHEXT"].split(ENV_PATH_SEP) if is_windows else None
path_dirs = os.environ["PATH"].split(ENV_PATH_SEP)
search_dirs = path_dirs + [os.getcwd()] # cwd is last in the list
for dir in search_dirs:
path = os.path.join(dir, name)
if is_windows:
for extension in windows_exts:
path_with_ext = path + extension
if os.path.isfile(path_with_ext) and os.access(path_with_ext, os.X_OK):
return path_with_ext
else:
if os.path.isfile(path) and os.access(path, os.X_OK):
return path
return ""

View File

@ -1,113 +0,0 @@
import os
import platform
if os.name == "nt":
import sys
import winreg
def _reg_open_key(key, subkey):
try:
return winreg.OpenKey(key, subkey)
except OSError:
if platform.architecture()[0] == "32bit":
bitness_sam = winreg.KEY_WOW64_64KEY
else:
bitness_sam = winreg.KEY_WOW64_32KEY
return winreg.OpenKey(key, subkey, 0, winreg.KEY_READ | bitness_sam)
def _reg_open_key_bits(key, subkey, bits):
sam = winreg.KEY_READ
if platform.architecture()[0] == "32bit":
if bits == "64":
# Force 32bit process to search in 64bit registry
sam |= winreg.KEY_WOW64_64KEY
else:
if bits == "32":
# Force 64bit process to search in 32bit registry
sam |= winreg.KEY_WOW64_32KEY
return winreg.OpenKey(key, subkey, 0, sam)
def _find_mono_in_reg(subkey, bits):
try:
with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey:
value = winreg.QueryValueEx(hKey, "SdkInstallRoot")[0]
return value
except OSError:
return None
def _find_mono_in_reg_old(subkey, bits):
try:
with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey:
default_clr = winreg.QueryValueEx(hKey, "DefaultCLR")[0]
if default_clr:
return _find_mono_in_reg(subkey + "\\" + default_clr, bits)
return None
except OSError:
return None
def find_mono_root_dir(bits):
root_dir = _find_mono_in_reg(r"SOFTWARE\Mono", bits)
if root_dir is not None:
return str(root_dir)
root_dir = _find_mono_in_reg_old(r"SOFTWARE\Novell\Mono", bits)
if root_dir is not None:
return str(root_dir)
return ""
def find_msbuild_tools_path_reg():
import subprocess
vswhere = os.getenv("PROGRAMFILES(X86)")
if not vswhere:
vswhere = os.getenv("PROGRAMFILES")
vswhere += r"\Microsoft Visual Studio\Installer\vswhere.exe"
vswhere_args = ["-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild"]
try:
lines = subprocess.check_output([vswhere] + vswhere_args).splitlines()
for line in lines:
parts = line.decode("utf-8").split(":", 1)
if len(parts) < 2 or parts[0] != "installationPath":
continue
val = parts[1].strip()
if not val:
raise ValueError("Value of `installationPath` entry is empty")
# Since VS2019, the directory is simply named "Current"
msbuild_dir = os.path.join(val, "MSBuild\\Current\\Bin")
if os.path.isdir(msbuild_dir):
return msbuild_dir
# Directory name "15.0" is used in VS 2017
return os.path.join(val, "MSBuild\\15.0\\Bin")
raise ValueError("Cannot find `installationPath` entry")
except ValueError as e:
print("Error reading output from vswhere: " + e.message)
except OSError:
pass # Fine, vswhere not found
except (subprocess.CalledProcessError, OSError):
pass
# Try to find 14.0 in the Registry
try:
subkey = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0"
with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey:
value = winreg.QueryValueEx(hKey, "MSBuildToolsPath")[0]
return value
except OSError:
return ""

View File

@ -1,4 +1,6 @@
supported_platforms = ["windows", "osx", "linuxbsd", "server", "android", "haiku", "javascript", "iphone"]
# Prior to .NET Core, we supported these: ["windows", "osx", "linuxbsd", "server", "android", "haiku", "javascript", "iphone"]
# Eventually support for each them should be added back (except Haiku if not supported by .NET Core)
supported_platforms = ["windows", "osx", "linuxbsd", "server"]
def can_build(env, platform):
@ -13,51 +15,21 @@ def configure(env):
env.add_module_version_string("mono")
from SCons.Script import BoolVariable, PathVariable, Variables, Help
default_mono_static = platform in ["iphone", "javascript"]
default_mono_bundles_zlib = platform in ["javascript"]
from SCons.Script import PathVariable, Variables, Help
envvars = Variables()
envvars.Add(
PathVariable(
"mono_prefix",
"Path to the Mono installation directory for the target platform and architecture",
"dotnet_root",
"Path to the .NET Sdk installation directory for the target platform and architecture",
"",
PathVariable.PathAccept,
)
)
envvars.Add(
PathVariable(
"mono_bcl",
"Path to a custom Mono BCL (Base Class Library) directory for the target platform",
"",
PathVariable.PathAccept,
)
)
envvars.Add(BoolVariable("mono_static", "Statically link Mono", default_mono_static))
envvars.Add(BoolVariable("build_cil", "Build C# solutions", True))
envvars.Add(
BoolVariable("copy_mono_root", "Make a copy of the Mono installation directory to bundle with the editor", True)
)
# TODO: It would be great if this could be detected automatically instead
envvars.Add(
BoolVariable(
"mono_bundles_zlib", "Specify if the Mono runtime was built with bundled zlib", default_mono_bundles_zlib
)
)
envvars.Update(env)
Help(envvars.GenerateHelpText(env))
if env["mono_bundles_zlib"]:
# Mono may come with zlib bundled for WASM or on newer version when built with MinGW.
print("This Mono runtime comes with zlib bundled. Disabling 'builtin_zlib'...")
env["builtin_zlib"] = False
thirdparty_zlib_dir = "#thirdparty/zlib/"
env.Prepend(CPPPATH=[thirdparty_zlib_dir])
def get_doc_classes():
return [

File diff suppressed because it is too large Load Diff

View File

@ -39,8 +39,6 @@
#include "mono_gc_handle.h"
#include "mono_gd/gd_mono.h"
#include "mono_gd/gd_mono_header.h"
#include "mono_gd/gd_mono_internals.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_plugin.h"
@ -66,18 +64,6 @@ TScriptInstance *cast_script_instance(ScriptInstance *p_inst) {
#define CAST_CSHARP_INSTANCE(m_inst) (cast_script_instance<CSharpInstance, CSharpLanguage>(m_inst))
struct DotNetScriptLookupInfo {
String class_namespace;
String class_name;
GDMonoClass *script_class = nullptr;
DotNetScriptLookupInfo() {} // Required by HashMap...
DotNetScriptLookupInfo(const String &p_class_namespace, const String &p_class_name, GDMonoClass *p_script_class) :
class_namespace(p_class_namespace), class_name(p_class_name), script_class(p_script_class) {
}
};
class CSharpScript : public Script {
GDCLASS(CSharpScript, Script);
@ -88,26 +74,13 @@ public:
bool nil_is_variant = false;
};
struct EventSignal {
GDMonoField *field = nullptr;
GDMonoMethod *invoke_method = nullptr;
Vector<SignalParameter> parameters;
};
private:
friend class CSharpInstance;
friend class CSharpLanguage;
friend struct CSharpScriptDepSort;
bool tool = false;
bool valid = false;
bool builtin;
GDMonoClass *base = nullptr;
GDMonoClass *native = nullptr;
GDMonoClass *script_class = nullptr;
Ref<CSharpScript> base_cache; // TODO what's this for?
Set<Object *> instances;
@ -128,14 +101,9 @@ private:
#endif
String source;
StringName name;
SelfList<CSharpScript> script_list = this;
Map<StringName, Vector<SignalParameter>> _signals;
Map<StringName, EventSignal> event_signals;
bool signals_invalidated = true;
Vector<MultiplayerAPI::RPCConfig> rpc_functions;
#ifdef TOOLS_ENABLED
@ -158,34 +126,26 @@ private:
void _clear();
void _update_name();
void load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class);
bool _get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> &params);
bool _update_exports(PlaceHolderScriptInstance *p_instance_to_update = nullptr);
#warning TODO
#if 0
bool _get_member_export(IMonoClassMember *p_member, bool p_inspect_export, PropertyInfo &r_prop_info, bool &r_exported);
#ifdef TOOLS_ENABLED
static int _try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string);
#endif
#endif
CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error);
Variant _new(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
// Do not use unless you know what you are doing
friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *);
static Ref<CSharpScript> create_for_managed_type(GDMonoClass *p_class, GDMonoClass *p_native);
static void update_script_class_info(Ref<CSharpScript> p_script);
static void initialize_for_managed_type(Ref<CSharpScript> p_script, GDMonoClass *p_class, GDMonoClass *p_native);
MultiplayerAPI::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const;
static void initialize_for_managed_type(Ref<CSharpScript> p_script);
protected:
static void _bind_methods();
Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
void _resource_path_changed() override;
bool _get(const StringName &p_name, Variant &r_ret) const;
bool _set(const StringName &p_name, const Variant &p_value);
void _get_property_list(List<PropertyInfo> *p_properties) const;
@ -270,22 +230,21 @@ class CSharpInstance : public ScriptInstance {
bool _unreference_owner_unsafe();
/*
* If nullptr is returned, the caller must destroy the script instance by removing it from its owner.
* If false is returned, the caller must destroy the script instance by removing it from its owner.
*/
MonoObject *_internal_new_managed();
bool _internal_new_managed();
// Do not use unless you know what you are doing
friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *);
static CSharpInstance *create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle);
void get_properties_state_for_reloading(List<Pair<StringName, Variant>> &r_state);
void get_event_signals_state_for_reloading(List<Pair<StringName, Array>> &r_state);
public:
MonoObject *get_mono_object() const;
_FORCE_INLINE_ bool is_destructing_script_instance() { return destructing_script_instance; }
_FORCE_INLINE_ GCHandleIntPtr get_gchandle_intptr() { return gchandle.get_intptr(); }
Object *get_owner() override;
bool set(const StringName &p_name, const Variant &p_value) override;
@ -297,15 +256,15 @@ public:
bool has_method(const StringName &p_method) const override;
Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
void mono_object_disposed(MonoObject *p_obj);
void mono_object_disposed();
/*
* If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, if
* 'r_remove_script_instance' is set to true, the caller must destroy the script instance by removing it from its owner.
*/
void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance);
void mono_object_disposed_baseref(bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance);
void connect_event_signals();
void connect_event_signal(const StringName &p_event_signal);
void disconnect_event_signals();
void refcount_incremented() override;
@ -329,7 +288,6 @@ public:
struct CSharpScriptBinding {
bool inited = false;
StringName type_name;
GDMonoClass *wrapper_class = nullptr;
MonoGCHandleData gchandle;
Object *owner = nullptr;
@ -367,26 +325,15 @@ class CSharpLanguage : public ScriptLanguage {
ManagedCallableMiddleman *managed_callable_middleman = memnew(ManagedCallableMiddleman);
struct StringNameCache {
StringName _signal_callback;
StringName _set;
StringName _get;
StringName _get_property_list;
StringName _notification;
StringName _script_source;
StringName dotctor; // .ctor
StringName on_before_serialize; // OnBeforeSerialize
StringName on_after_deserialize; // OnAfterDeserialize
StringName delegate_invoke_method_name;
StringNameCache();
};
int lang_idx = -1;
HashMap<String, DotNetScriptLookupInfo> dotnet_script_lookup_map;
void lookup_script_for_class(GDMonoClass *p_class);
// For debug_break and debug_break_parse
int _debug_parse_err_line = -1;
String _debug_parse_err_file;
@ -416,6 +363,7 @@ public:
StringNameCache string_names;
const Mutex &get_language_bind_mutex() { return language_bind_mutex; }
const Mutex &get_script_instances_mutex() { return script_instances_mutex; }
_FORCE_INLINE_ int get_language_index() { return lang_idx; }
void set_language_index(int p_idx);
@ -429,7 +377,7 @@ public:
#endif
static void release_script_gchandle(MonoGCHandleData &p_gchandle);
static void release_script_gchandle(MonoObject *p_expected_obj, MonoGCHandleData &p_gchandle);
static void release_script_gchandle(void *p_expected_mono_obj_unused, MonoGCHandleData &p_gchandle);
bool debug_break(const String &p_error, bool p_allow_continue = true);
bool debug_break_parse(const String &p_file, int p_line, const String &p_error);
@ -441,12 +389,6 @@ public:
_FORCE_INLINE_ ManagedCallableMiddleman *get_managed_callable_middleman() const { return managed_callable_middleman; }
void lookup_scripts_in_assembly(GDMonoAssembly *p_assembly);
const DotNetScriptLookupInfo *lookup_dotnet_script(const String &p_script_path) const {
return dotnet_script_lookup_map.getptr(p_script_path);
}
String get_name() const override;
/* LANGUAGE FUNCTIONS */
@ -514,15 +456,18 @@ public:
bool overrides_external_editor() override;
#endif
/* THREAD ATTACHING */
void thread_enter() override;
void thread_exit() override;
Map<Object *, CSharpScriptBinding>::Element *insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding);
bool setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object);
static void tie_native_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, const StringName *p_native_name, bool p_ref_counted);
static void tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, CSharpScript *p_script, bool p_ref_counted);
static void tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged);
#warning TODO
#if 0
#ifdef DEBUG_ENABLED
Vector<StackInfo> stack_trace_get_info(MonoObject *p_stack_trace);
#endif
#endif
void post_unsafe_reference(Object *p_obj);

View File

@ -12,8 +12,7 @@
</tutorials>
<methods>
<method name="new" qualifiers="vararg">
<return type="Variant">
</return>
<return type="Variant" />
<description>
Returns a new instance of the script.
</description>

View File

@ -10,64 +10,10 @@
<tutorials>
</tutorials>
<methods>
<method name="attach_thread">
<return type="void">
</return>
<description>
Attaches the current thread to the Mono runtime.
</description>
</method>
<method name="detach_thread">
<return type="void">
</return>
<description>
Detaches the current thread from the Mono runtime.
</description>
</method>
<method name="get_domain_id">
<return type="int">
</return>
<description>
Returns the current MonoDomain ID.
[b]Note:[/b] The Mono runtime must be initialized for this method to work (use [method is_runtime_initialized] to check). If the Mono runtime isn't initialized at the time this method is called, the engine will crash.
</description>
</method>
<method name="get_scripts_domain_id">
<return type="int">
</return>
<description>
Returns the scripts MonoDomain's ID. This will be the same MonoDomain ID as [method get_domain_id], unless the scripts domain isn't loaded.
[b]Note:[/b] The Mono runtime must be initialized for this method to work (use [method is_runtime_initialized] to check). If the Mono runtime isn't initialized at the time this method is called, the engine will crash.
</description>
</method>
<method name="is_domain_finalizing_for_unload">
<return type="bool">
</return>
<argument index="0" name="domain_id" type="int">
</argument>
<description>
Returns [code]true[/code] if the domain is being finalized, [code]false[/code] otherwise.
</description>
</method>
<method name="is_runtime_initialized">
<return type="bool">
</return>
<return type="bool" />
<description>
Returns [code]true[/code] if the Mono runtime is initialized, [code]false[/code] otherwise.
</description>
</method>
<method name="is_runtime_shutting_down">
<return type="bool">
</return>
<description>
Returns [code]true[/code] if the Mono runtime is shutting down, [code]false[/code] otherwise.
</description>
</method>
<method name="is_scripts_domain_loaded">
<return type="bool">
</return>
<description>
Returns [code]true[/code] if the scripts domain is loaded, [code]false[/code] otherwise.
Returns [code]true[/code] if the .NET runtime is initialized, [code]false[/code] otherwise.
</description>
</method>
</methods>

View File

@ -1,4 +1,4 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.NET.Sdk", "Godot.NET.Sdk\Godot.NET.Sdk.csproj", "{31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}"
EndProject

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>

View File

@ -5,6 +5,6 @@
<LangVersion>7.2</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Framework" Version="16.5.0" />
<PackageReference Include="Microsoft.Build.Framework" Version="15.1.548" ExcludeAssets="runtime" />
</ItemGroup>
</Project>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</ProjectGuid>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>7.2</LangVersion>
</PropertyGroup>
</Project>

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{B06C2951-C8E3-4F28-80B2-717CF327EB19}</ProjectGuid>
<OutputType>Exe</OutputType>

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{EAFFF236-FA96-4A4D-BD23-0E51EF988277}</ProjectGuid>
<OutputType>Exe</OutputType>

View File

@ -1,32 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</ProjectGuid>
<TargetFramework>net472</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>7.2</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build" Version="16.5.0" />
<PackageReference Include="Microsoft.Build" Version="15.1.548" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Locator" Version="1.2.6" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" />
<ProjectReference Include="..\GodotTools.Shared\GodotTools.Shared.csproj" />
</ItemGroup>
<!--
The Microsoft.Build.Runtime package is too problematic so we create a MSBuild.exe stub. The workaround described
here doesn't work with Microsoft.NETFramework.ReferenceAssemblies: https://github.com/microsoft/msbuild/issues/3486
We need a MSBuild.exe file as there's an issue in Microsoft.Build where it executes platform dependent code when
searching for MSBuild.exe before the fallback to not using it. A stub is fine as it should never be executed.
-->
<ItemGroup>
<None Include="MSBuild.exe" CopyToOutputDirectory="Always" />
</ItemGroup>
<Target Name="CopyMSBuildStubWindows" AfterTargets="Build" Condition=" '$(GodotPlatform)' == 'windows' Or ( '$(GodotPlatform)' == '' And '$(OS)' == 'Windows_NT' ) ">
<PropertyGroup>
<GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>
<GodotOutputDataDir>$(GodotSourceRootPath)/bin/GodotSharp</GodotOutputDataDir>
</PropertyGroup>
<!-- Need to copy it here as well on Windows -->
<Copy SourceFiles="MSBuild.exe" DestinationFiles="$(GodotOutputDataDir)\Mono\lib\mono\v4.0\MSBuild.exe" />
</Target>
</Project>

View File

@ -21,7 +21,8 @@ namespace GodotTools.ProjectEditor
root.Sdk = GodotSdkAttrValue;
var mainGroup = root.AddPropertyGroup();
mainGroup.AddProperty("TargetFramework", "netstandard2.1");
mainGroup.AddProperty("TargetFramework", "net5.0");
mainGroup.AddProperty("EnableDynamicLoading", "true");
string sanitizedName = IdentifierUtils.SanitizeQualifiedIdentifier(name, allowEmptyIdentifiers: true);

View File

@ -19,6 +19,9 @@ namespace GodotTools.ProjectEditor
public static class ProjectUtils
{
public static void MSBuildLocatorRegisterDefaults()
=> Microsoft.Build.Locator.MSBuildLocator.RegisterDefaults();
public static MSBuildProject Open(string path)
{
var root = ProjectRootElement.Open(path);
@ -42,7 +45,8 @@ namespace GodotTools.ProjectEditor
var root = project.Root;
string godotSdkAttrValue = ProjectGenerator.GodotSdkAttrValue;
if (!string.IsNullOrEmpty(root.Sdk) && root.Sdk.Trim().Equals(godotSdkAttrValue, StringComparison.OrdinalIgnoreCase))
if (!string.IsNullOrEmpty(root.Sdk) &&
root.Sdk.Trim().Equals(godotSdkAttrValue, StringComparison.OrdinalIgnoreCase))
return;
root.Sdk = godotSdkAttrValue;

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<Import Project="GenerateGodotNupkgsVersions.targets" />
</Project>

View File

@ -1,4 +1,4 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.ProjectEditor", "GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj", "{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}"

View File

@ -1,9 +1,9 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Threading.Tasks;
using GodotTools.Ides.Rider;
using GodotTools.Internals;
using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
using OS = GodotTools.Utils.OS;
@ -159,7 +159,7 @@ namespace GodotTools.Build
}
}
public static bool BuildProjectBlocking(string config, [CanBeNull] string[] targets = null, [CanBeNull] string platform = null)
public static bool BuildProjectBlocking(string config, [MaybeNull] string[] targets = null, [MaybeNull] string platform = null)
{
var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, targets ?? new[] {"Build"}, config, restore: true);
@ -178,17 +178,6 @@ namespace GodotTools.Build
if (!File.Exists(buildInfo.Solution))
return true; // No solution to build
// Make sure the API assemblies are up to date before building the project.
// We may not have had the chance to update the release API assemblies, and the debug ones
// may have been deleted by the user at some point after they were loaded by the Godot editor.
string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(buildInfo.Configuration == "ExportRelease" ? "Release" : "Debug");
if (!string.IsNullOrEmpty(apiAssembliesUpdateError))
{
ShowBuildErrorDialog("Failed to update the Godot API assemblies");
return false;
}
using (var pr = new EditorProgress("mono_project_debug_build", "Building project solution...", 1))
{
pr.Step("Building project solution", 0);

View File

@ -1,8 +1,8 @@
using Godot;
using System;
using System.Diagnostics.CodeAnalysis;
using Godot.Collections;
using GodotTools.Internals;
using JetBrains.Annotations;
using File = GodotTools.Utils.File;
using Path = System.IO.Path;

View File

@ -1,7 +1,6 @@
using System;
using Godot;
using GodotTools.Internals;
using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
@ -28,7 +27,6 @@ namespace GodotTools.Build
BuildOutputView.UpdateIssuesList();
}
[UsedImplicitly]
public void BuildSolution()
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
@ -57,7 +55,6 @@ namespace GodotTools.Build
Internal.ReloadAssemblies(softReload: false);
}
[UsedImplicitly]
private void RebuildSolution()
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
@ -86,7 +83,6 @@ namespace GodotTools.Build
Internal.ReloadAssemblies(softReload: false);
}
[UsedImplicitly]
private void CleanSolution()
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))

View File

@ -1,14 +1,15 @@
using Godot;
using Godot.NativeInterop;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using GodotTools.Build;
using GodotTools.Core;
using GodotTools.Internals;
using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
using Directory = GodotTools.Utils.Directory;
using File = GodotTools.Utils.File;
@ -100,7 +101,9 @@ namespace GodotTools.Export
return;
if (Path.GetExtension(path) != Internal.CSharpLanguageExtension)
throw new ArgumentException($"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}", nameof(path));
throw new ArgumentException(
$"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}",
nameof(path));
// TODO What if the source file is not part of the game's C# project
@ -194,7 +197,8 @@ namespace GodotTools.Export
{
string thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName + ".dll");
if (!File.Exists(thisWasmFrameworkAssemblyPath))
throw new FileNotFoundException($"Assembly not found: '{thisWasmFrameworkAssemblyName}'", thisWasmFrameworkAssemblyPath);
throw new FileNotFoundException($"Assembly not found: '{thisWasmFrameworkAssemblyName}'",
thisWasmFrameworkAssemblyPath);
assemblies[thisWasmFrameworkAssemblyName] = thisWasmFrameworkAssemblyPath;
}
@ -206,18 +210,21 @@ namespace GodotTools.Export
foreach (var thisWasmFrameworkAssemblyName in wasmFrameworkAssembliesOneOf)
{
string thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName.newName + ".dll");
string thisWasmFrameworkAssemblyPath =
Path.Combine(bclDir, thisWasmFrameworkAssemblyName.newName + ".dll");
if (File.Exists(thisWasmFrameworkAssemblyPath))
{
assemblies[thisWasmFrameworkAssemblyName.newName] = thisWasmFrameworkAssemblyPath;
}
else
{
thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName.oldName + ".dll");
thisWasmFrameworkAssemblyPath =
Path.Combine(bclDir, thisWasmFrameworkAssemblyName.oldName + ".dll");
if (!File.Exists(thisWasmFrameworkAssemblyPath))
{
throw new FileNotFoundException("Expected one of the following assemblies but none were found: " +
$"'{thisWasmFrameworkAssemblyName.newName}' / '{thisWasmFrameworkAssemblyName.oldName}'",
throw new FileNotFoundException(
"Expected one of the following assemblies but none were found: " +
$"'{thisWasmFrameworkAssemblyName.newName}' / '{thisWasmFrameworkAssemblyName.oldName}'",
thisWasmFrameworkAssemblyPath);
}
@ -227,7 +234,13 @@ namespace GodotTools.Export
}
var initialAssemblies = assemblies.Duplicate();
internal_GetExportedAssemblyDependencies(initialAssemblies, buildConfig, bclDir, assemblies);
godot_dictionary initialAssembliesAux = ((Godot.Collections.Dictionary)initialAssemblies).NativeValue;
using godot_string buildConfigAux = Marshaling.mono_string_to_godot(buildConfig);
using godot_string bclDirAux = Marshaling.mono_string_to_godot(bclDir);
godot_dictionary assembliesAux = ((Godot.Collections.Dictionary)assemblies).NativeValue;
// TODO
throw new NotImplementedException();
//internal_GetExportedAssemblyDependencies(initialAssembliesAux, buildConfigAux, bclDirAux, ref assembliesAux);
AddI18NAssemblies(assemblies, bclDir);
@ -237,9 +250,12 @@ namespace GodotTools.Export
outputDataDir = ExportDataDirectory(features, platform, isDebug, outputDir);
string apiConfig = isDebug ? "Debug" : "Release";
string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, apiConfig);
// TODO
throw new NotImplementedException();
string resAssembliesDir = null; // Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, apiConfig);
bool assembliesInsidePck = (bool)ProjectSettings.GetSetting("mono/export/export_assemblies_inside_pck") || outputDataDir == null;
bool assembliesInsidePck = (bool)ProjectSettings.GetSetting("mono/export/export_assemblies_inside_pck") ||
outputDataDir == null;
if (!assembliesInsidePck)
{
@ -277,7 +293,8 @@ namespace GodotTools.Export
}
// AOT compilation
bool aotEnabled = platform == OS.Platforms.iOS || (bool)ProjectSettings.GetSetting("mono/export/aot/enabled");
bool aotEnabled = platform == OS.Platforms.iOS ||
(bool)ProjectSettings.GetSetting("mono/export/aot/enabled");
if (aotEnabled)
{
@ -296,14 +313,19 @@ namespace GodotTools.Export
LLVMOnly = false,
LLVMPath = "",
LLVMOutputPath = "",
FullAot = platform == OS.Platforms.iOS || (bool)(ProjectSettings.GetSetting("mono/export/aot/full_aot") ?? false),
FullAot = platform == OS.Platforms.iOS ||
(bool)(ProjectSettings.GetSetting("mono/export/aot/full_aot") ?? false),
UseInterpreter = (bool)ProjectSettings.GetSetting("mono/export/aot/use_interpreter"),
ExtraAotOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_aot_options") ?? Array.Empty<string>(),
ExtraOptimizerOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_optimizer_options") ?? Array.Empty<string>(),
ExtraAotOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_aot_options") ??
Array.Empty<string>(),
ExtraOptimizerOptions =
(string[])ProjectSettings.GetSetting("mono/export/aot/extra_optimizer_options") ??
Array.Empty<string>(),
ToolchainPath = aotToolchainPath
};
AotBuilder.CompileAssemblies(this, aotOpts, features, platform, isDebug, bclDir, outputDir, outputDataDir, assemblies);
AotBuilder.CompileAssemblies(this, aotOpts, features, platform, isDebug, bclDir, outputDir,
outputDataDir, assemblies);
}
}
@ -317,7 +339,9 @@ namespace GodotTools.Export
Directory.Delete(aotTempDir, recursive: true);
// TODO: Just a workaround until the export plugins can be made to abort with errors
if (!string.IsNullOrEmpty(maybeLastExportError)) // Check empty as well, because it's set to empty after hot-reloading
if (
!string.IsNullOrEmpty(
maybeLastExportError)) // Check empty as well, because it's set to empty after hot-reloading
{
string lastExportError = maybeLastExportError;
maybeLastExportError = null;
@ -326,7 +350,7 @@ namespace GodotTools.Export
}
}
[NotNull]
[return: NotNull]
private static string ExportDataDirectory(string[] features, string platform, bool isDebug, string outputDir)
{
string target = isDebug ? "release_debug" : "release";
@ -381,7 +405,8 @@ namespace GodotTools.Export
private static bool PlatformHasTemplateDir(string platform)
{
// OSX export templates are contained in a zip, so we place our custom template inside it and let Godot do the rest.
return !new[] { OS.Platforms.MacOS, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(platform);
return !new[] { OS.Platforms.MacOS, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }
.Contains(platform);
}
private static bool DeterminePlatformFromFeatures(IEnumerable<string> features, out string platform)
@ -474,9 +499,5 @@ namespace GodotTools.Export
string appNameSafe = appName.ToSafeDirName();
return $"data_{appNameSafe}";
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_GetExportedAssemblyDependencies(Godot.Collections.Dictionary<string, string> initialAssemblies,
string buildConfig, string customBclDir, Godot.Collections.Dictionary<string, string> dependencyAssemblies);
}
}

View File

@ -50,9 +50,10 @@ namespace GodotTools
}
}
[UsedImplicitly]
private bool CreateProjectSolution()
{
using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 3))
using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 2))
{
pr.Step("Generating C# project...".TTR());
@ -74,7 +75,7 @@ namespace GodotTools
{
Guid = guid,
PathRelativeToSolution = name + ".csproj",
Configs = new List<string> {"Debug", "ExportDebug", "ExportRelease"}
Configs = new List<string> { "Debug", "ExportDebug", "ExportRelease" }
};
solution.AddNewProject(name, projectInfo);
@ -89,24 +90,6 @@ namespace GodotTools
return false;
}
pr.Step("Updating Godot API assemblies...".TTR());
string debugApiAssembliesError = Internal.UpdateApiAssembliesFromPrebuilt("Debug");
if (!string.IsNullOrEmpty(debugApiAssembliesError))
{
ShowErrorDialog("Failed to update the Godot API assemblies: " + debugApiAssembliesError);
return false;
}
string releaseApiAssembliesError = Internal.UpdateApiAssembliesFromPrebuilt("Release");
if (!string.IsNullOrEmpty(releaseApiAssembliesError))
{
ShowErrorDialog("Failed to update the Godot API assemblies: " + releaseApiAssembliesError);
return false;
}
pr.Step("Done".TTR());
// Here, after all calls to progress_task_step
@ -140,7 +123,8 @@ namespace GodotTools
try
{
string fallbackFolder = NuGetUtils.GodotFallbackFolderPath;
NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, fallbackFolder);
NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
fallbackFolder);
NuGetUtils.AddBundledPackagesToFallbackFolder(fallbackFolder);
}
catch (Exception e)
@ -218,13 +202,15 @@ namespace GodotTools
try
{
if (Godot.OS.IsStdoutVerbose())
Console.WriteLine($"Running: \"{command}\" {string.Join(" ", args.Select(a => $"\"{a}\""))}");
Console.WriteLine(
$"Running: \"{command}\" {string.Join(" ", args.Select(a => $"\"{a}\""))}");
OS.RunProcess(command, args);
}
catch (Exception e)
{
GD.PushError($"Error when trying to run code editor: VisualStudio. Exception message: '{e.Message}'");
GD.PushError(
$"Error when trying to run code editor: VisualStudio. Exception message: '{e.Message}'");
}
break;
@ -395,6 +381,8 @@ namespace GodotTools
{
base._EnablePlugin();
ProjectUtils.MSBuildLocatorRegisterDefaults();
if (Instance != null)
throw new InvalidOperationException();
Instance = this;
@ -410,7 +398,7 @@ namespace GodotTools
MSBuildPanel = new MSBuildPanel();
bottomPanelBtn = AddControlToBottomPanel(MSBuildPanel, "MSBuild".TTR());
AddChild(new HotReloadAssemblyWatcher {Name = "HotReloadAssemblyWatcher"});
AddChild(new HotReloadAssemblyWatcher { Name = "HotReloadAssemblyWatcher" });
menuPopup = new PopupMenu();
menuPopup.Hide();
@ -482,7 +470,8 @@ namespace GodotTools
try
{
// At startup we make sure NuGet.Config files have our Godot NuGet fallback folder included
NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, NuGetUtils.GodotFallbackFolderPath);
NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
NuGetUtils.GodotFallbackFolderPath);
}
catch (Exception e)
{
@ -528,8 +517,9 @@ namespace GodotTools
public static GodotSharpEditor Instance { get; private set; }
[UsedImplicitly]
private GodotSharpEditor()
private static IntPtr InternalCreateInstance()
{
return new GodotSharpEditor().NativeInstance;
}
}
}

View File

@ -1,13 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{27B00618-A6F2-4828-B922-05CAEB08C286}</ProjectGuid>
<TargetFramework>net472</TargetFramework>
<LangVersion>7.2</LangVersion>
<TargetFramework>net5.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
<LangVersion>8</LangVersion>
<!-- The Godot editor uses the Debug Godot API assemblies -->
<GodotApiConfiguration>Debug</GodotApiConfiguration>
<GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>
<GodotOutputDataDir>$(GodotSourceRootPath)/bin/GodotSharp</GodotOutputDataDir>
<GodotApiAssembliesDir>$(GodotOutputDataDir)/Api/$(GodotApiConfiguration)</GodotApiAssembliesDir>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" Exists('$(GodotApiAssembliesDir)/GodotSharp.dll') ">
<!-- The project is part of the Godot source tree -->
@ -20,6 +22,8 @@
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3.0" ExcludeAssets="runtime" PrivateAssets="all" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<!-- For RiderPathLocator -->
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<Reference Include="GodotSharp">
<HintPath>$(GodotApiAssembliesDir)/GodotSharp.dll</HintPath>
<Private>False</Private>

View File

@ -1,5 +1,6 @@
using Godot;
using GodotTools.Internals;
using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
namespace GodotTools
@ -25,6 +26,7 @@ namespace GodotTools
Internal.ReloadAssemblies(softReload: false);
}
[UsedImplicitly]
public void RestartTimer()
{
watchTimer.Stop();

View File

@ -1,9 +1,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.Versioning;
using Godot;
using JetBrains.Annotations;
using Microsoft.Win32;
using Newtonsoft.Json;
using Directory = System.IO.Directory;
@ -112,6 +113,7 @@ namespace GodotTools.Ides.Rider
return installInfos.ToArray();
}
[SupportedOSPlatform("windows")]
private static RiderInfo[] CollectRiderInfosWindows()
{
var installInfos = new List<RiderInfo>();
@ -216,6 +218,7 @@ namespace GodotTools.Ides.Rider
throw new Exception("Unknown OS.");
}
[SupportedOSPlatform("windows")]
private static void CollectPathsFromRegistry(string registryKey, List<string> installPaths)
{
using (var key = Registry.CurrentUser.OpenSubKey(registryKey))
@ -228,6 +231,7 @@ namespace GodotTools.Ides.Rider
}
}
[SupportedOSPlatform("windows")]
private static void CollectPathsFromRegistry(List<string> installPaths, RegistryKey key)
{
if (key == null) return;
@ -323,7 +327,7 @@ namespace GodotTools.Ides.Rider
{
public string install_location;
[CanBeNull]
[return: MaybeNull]
public static string GetInstallLocationFromJson(string json)
{
try
@ -377,7 +381,7 @@ namespace GodotTools.Ides.Rider
public string version;
public string versionSuffix;
[CanBeNull]
[return: MaybeNull]
internal static ProductInfo GetProductInfo(string json)
{
try
@ -401,7 +405,7 @@ namespace GodotTools.Ides.Rider
// ReSharper disable once InconsistentNaming
public ActiveApplication active_application;
[CanBeNull]
[return: MaybeNull]
public static string GetLatestBuildFromJson(string json)
{
try

View File

@ -1,6 +1,7 @@
using System;
using System.Runtime.CompilerServices;
using Godot;
using Godot.NativeInterop;
namespace GodotTools.Internals
{
@ -8,19 +9,12 @@ namespace GodotTools.Internals
{
public string Task { get; }
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_Create(string task, string label, int amount, bool canCancel);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_Dispose(string task);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool internal_Step(string task, string state, int step, bool forceRefresh);
public EditorProgress(string task, string label, int amount, bool canCancel = false)
{
Task = task;
internal_Create(task, label, amount, canCancel);
using godot_string taskIn = Marshaling.mono_string_to_godot(task);
using godot_string labelIn = Marshaling.mono_string_to_godot(label);
Internal.godot_icall_EditorProgress_Create(taskIn, labelIn, amount, canCancel);
}
~EditorProgress()
@ -33,18 +27,23 @@ namespace GodotTools.Internals
public void Dispose()
{
internal_Dispose(Task);
using godot_string taskIn = Marshaling.mono_string_to_godot(Task);
Internal.godot_icall_EditorProgress_Dispose(taskIn);
GC.SuppressFinalize(this);
}
public void Step(string state, int step = -1, bool forceRefresh = true)
{
internal_Step(Task, state, step, forceRefresh);
using godot_string taskIn = Marshaling.mono_string_to_godot(Task);
using godot_string stateIn = Marshaling.mono_string_to_godot(state);
Internal.godot_icall_EditorProgress_Step(taskIn, stateIn, step, forceRefresh);
}
public bool TryStep(string state, int step = -1, bool forceRefresh = true)
{
return internal_Step(Task, state, step, forceRefresh);
using godot_string taskIn = Marshaling.mono_string_to_godot(Task);
using godot_string stateIn = Marshaling.mono_string_to_godot(state);
return Internal.godot_icall_EditorProgress_Step(taskIn, stateIn, step, forceRefresh);
}
}
}

View File

@ -1,3 +1,4 @@
using Godot.NativeInterop;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
@ -5,29 +6,33 @@ namespace GodotTools.Internals
{
public static class Globals
{
public static float EditorScale => internal_EditorScale();
public static float EditorScale => Internal.godot_icall_Globals_EditorScale();
public static object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false) =>
internal_GlobalDef(setting, defaultValue, restartIfChanged);
public static unsafe object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false)
{
using godot_string settingIn = Marshaling.mono_string_to_godot(setting);
using godot_variant defaultValueIn = Marshaling.mono_object_to_variant(defaultValue);
Internal.godot_icall_Globals_GlobalDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result);
using (result)
return Marshaling.variant_to_mono_object(&result);
}
public static object EditorDef(string setting, object defaultValue, bool restartIfChanged = false) =>
internal_EditorDef(setting, defaultValue, restartIfChanged);
public static unsafe object EditorDef(string setting, object defaultValue, bool restartIfChanged = false)
{
using godot_string settingIn = Marshaling.mono_string_to_godot(setting);
using godot_variant defaultValueIn = Marshaling.mono_object_to_variant(defaultValue);
Internal.godot_icall_Globals_EditorDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result);
using (result)
return Marshaling.variant_to_mono_object(&result);
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
public static string TTR(this string text) => internal_TTR(text);
// Internal Calls
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern float internal_EditorScale();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern object internal_GlobalDef(string setting, object defaultValue, bool restartIfChanged);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern object internal_EditorDef(string setting, object defaultValue, bool restartIfChanged);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_TTR(string text);
public static string TTR(this string text)
{
using godot_string textIn = Marshaling.mono_string_to_godot(text);
Internal.godot_icall_Globals_TTR(textIn, out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
}
}

View File

@ -1,91 +1,78 @@
using System.Runtime.CompilerServices;
using Godot.NativeInterop;
namespace GodotTools.Internals
{
public static class GodotSharpDirs
{
public static string ResDataDir => internal_ResDataDir();
public static string ResMetadataDir => internal_ResMetadataDir();
public static string ResAssembliesBaseDir => internal_ResAssembliesBaseDir();
public static string ResAssembliesDir => internal_ResAssembliesDir();
public static string ResConfigDir => internal_ResConfigDir();
public static string ResTempDir => internal_ResTempDir();
public static string ResTempAssembliesBaseDir => internal_ResTempAssembliesBaseDir();
public static string ResTempAssembliesDir => internal_ResTempAssembliesDir();
public static string ResMetadataDir
{
get
{
Internal.godot_icall_GodotSharpDirs_ResMetadataDir(out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
}
public static string MonoUserDir => internal_MonoUserDir();
public static string MonoLogsDir => internal_MonoLogsDir();
public static string ResTempAssembliesBaseDir
{
get
{
Internal.godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir(out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
}
#region Tools-only
public static string MonoSolutionsDir => internal_MonoSolutionsDir();
public static string BuildLogsDirs => internal_BuildLogsDirs();
public static string MonoUserDir
{
get
{
Internal.godot_icall_GodotSharpDirs_MonoUserDir(out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
}
public static string ProjectSlnPath => internal_ProjectSlnPath();
public static string ProjectCsProjPath => internal_ProjectCsProjPath();
public static string BuildLogsDirs
{
get
{
Internal.godot_icall_GodotSharpDirs_BuildLogsDirs(out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
}
public static string DataEditorToolsDir => internal_DataEditorToolsDir();
public static string DataEditorPrebuiltApiDir => internal_DataEditorPrebuiltApiDir();
#endregion
public static string ProjectSlnPath
{
get
{
Internal.godot_icall_GodotSharpDirs_ProjectSlnPath(out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
}
public static string DataMonoEtcDir => internal_DataMonoEtcDir();
public static string DataMonoLibDir => internal_DataMonoLibDir();
public static string ProjectCsProjPath
{
get
{
Internal.godot_icall_GodotSharpDirs_ProjectCsProjPath(out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
}
#region Windows-only
public static string DataMonoBinDir => internal_DataMonoBinDir();
#endregion
#region Internal
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResDataDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResMetadataDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResAssembliesBaseDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResAssembliesDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResConfigDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResTempDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResTempAssembliesBaseDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResTempAssembliesDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_MonoUserDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_MonoLogsDir();
#region Tools-only
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_MonoSolutionsDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_BuildLogsDirs();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ProjectSlnPath();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ProjectCsProjPath();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_DataEditorToolsDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_DataEditorPrebuiltApiDir();
#endregion
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_DataMonoEtcDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_DataMonoLibDir();
#region Windows-only
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_DataMonoBinDir();
#endregion
#endregion
public static string DataEditorToolsDir
{
get
{
Internal.godot_icall_GodotSharpDirs_DataEditorToolsDir(out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
}
}
}

View File

@ -1,114 +1,189 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Godot;
using Godot.NativeInterop;
using GodotTools.IdeMessaging.Requests;
namespace GodotTools.Internals
{
public static class Internal
internal static class Internal
{
public const string CSharpLanguageType = "CSharpScript";
public const string CSharpLanguageExtension = ".cs";
public static string UpdateApiAssembliesFromPrebuilt(string config) =>
internal_UpdateApiAssembliesFromPrebuilt(config);
public static string FullTemplatesDir
{
get
{
godot_icall_Internal_FullTemplatesDir(out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
}
public static string FullTemplatesDir =>
internal_FullTemplatesDir();
public static string SimplifyGodotPath(this string path)
{
using godot_string pathIn = Marshaling.mono_string_to_godot(path);
godot_icall_Internal_SimplifyGodotPath(pathIn, out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
public static string SimplifyGodotPath(this string path) => internal_SimplifyGodotPath(path);
public static bool IsOsxAppBundleInstalled(string bundleId)
{
using godot_string bundleIdIn = Marshaling.mono_string_to_godot(bundleId);
return godot_icall_Internal_IsOsxAppBundleInstalled(bundleIdIn);
}
public static bool IsOsxAppBundleInstalled(string bundleId) => internal_IsOsxAppBundleInstalled(bundleId);
public static bool GodotIs32Bits() => godot_icall_Internal_GodotIs32Bits();
public static bool GodotIs32Bits() => internal_GodotIs32Bits();
public static bool GodotIsRealTDouble() => godot_icall_Internal_GodotIsRealTDouble();
public static bool GodotIsRealTDouble() => internal_GodotIsRealTDouble();
public static void GodotMainIteration() => godot_icall_Internal_GodotMainIteration();
public static void GodotMainIteration() => internal_GodotMainIteration();
public static bool IsAssembliesReloadingNeeded() => godot_icall_Internal_IsAssembliesReloadingNeeded();
public static ulong GetCoreApiHash() => internal_GetCoreApiHash();
public static void ReloadAssemblies(bool softReload) => godot_icall_Internal_ReloadAssemblies(softReload);
public static ulong GetEditorApiHash() => internal_GetEditorApiHash();
public static bool IsAssembliesReloadingNeeded() => internal_IsAssembliesReloadingNeeded();
public static void ReloadAssemblies(bool softReload) => internal_ReloadAssemblies(softReload);
public static void EditorDebuggerNodeReloadScripts() => internal_EditorDebuggerNodeReloadScripts();
public static void EditorDebuggerNodeReloadScripts() => godot_icall_Internal_EditorDebuggerNodeReloadScripts();
public static bool ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus = true) =>
internal_ScriptEditorEdit(resource, line, col, grabFocus);
godot_icall_Internal_ScriptEditorEdit(resource.NativeInstance, line, col, grabFocus);
public static void EditorNodeShowScriptScreen() => internal_EditorNodeShowScriptScreen();
public static void EditorNodeShowScriptScreen() => godot_icall_Internal_EditorNodeShowScriptScreen();
public static string MonoWindowsInstallRoot => internal_MonoWindowsInstallRoot();
public static string MonoWindowsInstallRoot
{
get
{
godot_icall_Internal_MonoWindowsInstallRoot(out godot_string dest);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
}
public static void EditorRunPlay() => internal_EditorRunPlay();
public static void EditorRunPlay() => godot_icall_Internal_EditorRunPlay();
public static void EditorRunStop() => internal_EditorRunStop();
public static void EditorRunStop() => godot_icall_Internal_EditorRunStop();
public static void ScriptEditorDebugger_ReloadScripts() => internal_ScriptEditorDebugger_ReloadScripts();
public static void ScriptEditorDebugger_ReloadScripts() =>
godot_icall_Internal_ScriptEditorDebugger_ReloadScripts();
public static string[] CodeCompletionRequest(CodeCompletionRequest.CompletionKind kind, string scriptFile) =>
internal_CodeCompletionRequest((int)kind, scriptFile);
public static unsafe string[] CodeCompletionRequest(CodeCompletionRequest.CompletionKind kind,
string scriptFile)
{
using godot_string scriptFileIn = Marshaling.mono_string_to_godot(scriptFile);
godot_icall_Internal_CodeCompletionRequest((int)kind, scriptFileIn, out godot_packed_string_array res);
using (res)
return Marshaling.PackedStringArray_to_mono_array(&res);
}
#region Internal
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_UpdateApiAssembliesFromPrebuilt(string config);
private const string GodotDllName = "__Internal";
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_FullTemplatesDir();
[DllImport(GodotDllName)]
public static extern void godot_icall_GodotSharpDirs_ResMetadataDir(out godot_string r_dest);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_SimplifyGodotPath(this string path);
[DllImport(GodotDllName)]
public static extern void godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir(out godot_string r_dest);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool internal_IsOsxAppBundleInstalled(string bundleId);
[DllImport(GodotDllName)]
public static extern void godot_icall_GodotSharpDirs_MonoUserDir(out godot_string r_dest);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool internal_GodotIs32Bits();
[DllImport(GodotDllName)]
public static extern void godot_icall_GodotSharpDirs_BuildLogsDirs(out godot_string r_dest);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool internal_GodotIsRealTDouble();
[DllImport(GodotDllName)]
public static extern void godot_icall_GodotSharpDirs_ProjectSlnPath(out godot_string r_dest);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_GodotMainIteration();
[DllImport(GodotDllName)]
public static extern void godot_icall_GodotSharpDirs_ProjectCsProjPath(out godot_string r_dest);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern ulong internal_GetCoreApiHash();
[DllImport(GodotDllName)]
public static extern void godot_icall_GodotSharpDirs_DataEditorToolsDir(out godot_string r_dest);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern ulong internal_GetEditorApiHash();
[DllImport(GodotDllName)]
public static extern void godot_icall_EditorProgress_Create(in godot_string task, in godot_string label,
int amount, bool canCancel);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool internal_IsAssembliesReloadingNeeded();
[DllImport(GodotDllName)]
public static extern void godot_icall_EditorProgress_Dispose(in godot_string task);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_ReloadAssemblies(bool softReload);
[DllImport(GodotDllName)]
public static extern bool godot_icall_EditorProgress_Step(in godot_string task, in godot_string state, int step,
bool forceRefresh);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_EditorDebuggerNodeReloadScripts();
[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_FullTemplatesDir(out godot_string dest);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool internal_ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus);
[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_SimplifyGodotPath(in godot_string path, out godot_string dest);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_EditorNodeShowScriptScreen();
[DllImport(GodotDllName)]
private static extern bool godot_icall_Internal_IsOsxAppBundleInstalled(in godot_string bundleId);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_MonoWindowsInstallRoot();
[DllImport(GodotDllName)]
private static extern bool godot_icall_Internal_GodotIs32Bits();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_EditorRunPlay();
[DllImport(GodotDllName)]
private static extern bool godot_icall_Internal_GodotIsRealTDouble();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_EditorRunStop();
[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_GodotMainIteration();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_ScriptEditorDebugger_ReloadScripts();
[DllImport(GodotDllName)]
private static extern bool godot_icall_Internal_IsAssembliesReloadingNeeded();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string[] internal_CodeCompletionRequest(int kind, string scriptFile);
[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_ReloadAssemblies(bool softReload);
[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_EditorDebuggerNodeReloadScripts();
[DllImport(GodotDllName)]
private static extern bool godot_icall_Internal_ScriptEditorEdit(IntPtr resource, int line, int col,
bool grabFocus);
[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_EditorNodeShowScriptScreen();
[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_MonoWindowsInstallRoot(out godot_string dest);
[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_EditorRunPlay();
[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_EditorRunStop();
[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts();
[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_CodeCompletionRequest(int kind, in godot_string scriptFile,
out godot_packed_string_array res);
[DllImport(GodotDllName)]
public static extern float godot_icall_Globals_EditorScale();
[DllImport(GodotDllName)]
public static extern void godot_icall_Globals_GlobalDef(in godot_string setting, in godot_variant defaultValue,
bool restartIfChanged, out godot_variant result);
[DllImport(GodotDllName)]
public static extern void godot_icall_Globals_EditorDef(in godot_string setting, in godot_variant defaultValue,
bool restartIfChanged, out godot_variant result);
[DllImport(GodotDllName)]
public static extern void godot_icall_Globals_TTR(in godot_string text, out godot_string dest);
[DllImport(GodotDllName)]
public static extern void godot_icall_Utils_OS_GetPlatformName(out godot_string dest);
[DllImport(GodotDllName)]
public static extern bool godot_icall_Utils_OS_UnixFileHasExecutableAccess(in godot_string filePath);
#endregion
}

View File

@ -1,8 +1,8 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Godot;
using GodotTools.Core;
using JetBrains.Annotations;
namespace GodotTools.Utils
{
@ -30,7 +30,7 @@ namespace GodotTools.Utils
return childPathNorm.PathStartsWithAlreadyNorm(parentPathNorm);
}
[CanBeNull]
[return: MaybeNull]
public static string LocalizePathWithCaseChecked(string path)
{
string pathNorm = path.NormalizePath() + Path.DirectorySeparatorChar;

View File

@ -1,3 +1,4 @@
using Godot.NativeInterop;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@ -5,19 +6,13 @@ using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
using GodotTools.Internals;
namespace GodotTools.Utils
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
public static class OS
{
[MethodImpl(MethodImplOptions.InternalCall)]
static extern string GetPlatformName();
[MethodImpl(MethodImplOptions.InternalCall)]
static extern bool UnixFileHasExecutableAccess(string filePath);
public static class Names
{
public const string Windows = "Windows";
@ -63,14 +58,24 @@ namespace GodotTools.Utils
[Names.HTML5] = Platforms.HTML5
};
private static bool IsOS(string name)
private static unsafe bool IsOS(string name)
{
return name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
Internal.godot_icall_Utils_OS_GetPlatformName(out godot_string dest);
using (dest)
{
string platformName = Marshaling.mono_string_from_godot(dest);
return name.Equals(platformName, StringComparison.OrdinalIgnoreCase);
}
}
private static bool IsAnyOS(IEnumerable<string> names)
private static unsafe bool IsAnyOS(IEnumerable<string> names)
{
return names.Any(p => p.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase));
Internal.godot_icall_Utils_OS_GetPlatformName(out godot_string dest);
using (dest)
{
string platformName = Marshaling.mono_string_from_godot(dest);
return names.Any(p => p.Equals(platformName, StringComparison.OrdinalIgnoreCase));
}
}
private static readonly IEnumerable<string> LinuxBSDPlatforms =
@ -91,14 +96,23 @@ namespace GodotTools.Utils
private static readonly Lazy<bool> _isHTML5 = new Lazy<bool>(() => IsOS(Names.HTML5));
private static readonly Lazy<bool> _isUnixLike = new Lazy<bool>(() => IsAnyOS(UnixLikePlatforms));
// TODO SupportedOSPlatformGuard once we target .NET 6
// [SupportedOSPlatformGuard("windows")]
public static bool IsWindows => _isWindows.Value || IsUWP;
// [SupportedOSPlatformGuard("osx")]
public static bool IsMacOS => _isMacOS.Value;
// [SupportedOSPlatformGuard("linux")]
public static bool IsLinuxBSD => _isLinuxBSD.Value;
// [SupportedOSPlatformGuard("linux")]
public static bool IsServer => _isServer.Value;
// [SupportedOSPlatformGuard("windows")]
public static bool IsUWP => _isUWP.Value;
public static bool IsHaiku => _isHaiku.Value;
// [SupportedOSPlatformGuard("android")]
public static bool IsAndroid => _isAndroid.Value;
// [SupportedOSPlatformGuard("ios")]
public static bool IsiOS => _isiOS.Value;
// [SupportedOSPlatformGuard("browser")]
public static bool IsHTML5 => _isHTML5.Value;
public static bool IsUnixLike => _isUnixLike.Value;
@ -111,7 +125,8 @@ namespace GodotTools.Utils
private static string PathWhichWindows([NotNull] string name)
{
string[] windowsExts = Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) ?? Array.Empty<string>();
string[] windowsExts =
Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) ?? Array.Empty<string>();
string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
char[] invalidPathChars = Path.GetInvalidPathChars();
@ -129,7 +144,8 @@ namespace GodotTools.Utils
}
string nameExt = Path.GetExtension(name);
bool hasPathExt = !string.IsNullOrEmpty(nameExt) && windowsExts.Contains(nameExt, StringComparer.OrdinalIgnoreCase);
bool hasPathExt = !string.IsNullOrEmpty(nameExt) &&
windowsExts.Contains(nameExt, StringComparer.OrdinalIgnoreCase);
searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list
@ -137,10 +153,10 @@ namespace GodotTools.Utils
return searchDirs.Select(dir => Path.Combine(dir, name)).FirstOrDefault(File.Exists);
return (from dir in searchDirs
select Path.Combine(dir, name)
select Path.Combine(dir, name)
into path
from ext in windowsExts
select path + ext).FirstOrDefault(File.Exists);
from ext in windowsExts
select path + ext).FirstOrDefault(File.Exists);
}
private static string PathWhichUnix([NotNull] string name)
@ -164,7 +180,11 @@ namespace GodotTools.Utils
searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list
return searchDirs.Select(dir => Path.Combine(dir, name))
.FirstOrDefault(path => File.Exists(path) && UnixFileHasExecutableAccess(path));
.FirstOrDefault(path =>
{
using godot_string pathIn = Marshaling.mono_string_to_godot(path);
return File.Exists(path) && Internal.godot_icall_Utils_OS_UnixFileHasExecutableAccess(pathIn);
});
}
public static void RunProcess(string command, IEnumerable<string> arguments)

View File

@ -96,9 +96,9 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) {
#define C_CLASS_NATIVE_FUNCS "NativeFuncs"
#define C_NS_MONOUTILS "InteropUtils"
#define C_METHOD_TIE_MANAGED_TO_UNMANAGED "NativeInterop." C_NS_MONOUTILS ".TieManagedToUnmanaged"
#define C_METHOD_TIE_MANAGED_TO_UNMANAGED C_NS_MONOUTILS ".TieManagedToUnmanaged"
#define C_METHOD_UNMANAGED_GET_MANAGED C_NS_MONOUTILS ".UnmanagedGetManaged"
#define C_METHOD_ENGINE_GET_SINGLETON "NativeInterop." C_NS_MONOUTILS ".EngineGetSingleton"
#define C_METHOD_ENGINE_GET_SINGLETON C_NS_MONOUTILS ".EngineGetSingleton"
#define C_NS_MONOMARSHAL "Marshaling"
#define C_METHOD_MANAGED_TO_VARIANT C_NS_MONOMARSHAL ".mono_object_to_variant"
@ -1050,9 +1050,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"InconsistentNaming\")]\n");
cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"RedundantUnsafeContext\")]\n");
cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"RedundantNameQualifier\")]\n");
cs_icalls_content.append("#if NET\n");
cs_icalls_content.append(INDENT1 "[System.Runtime.CompilerServices.SkipLocalsInit]\n");
cs_icalls_content.append("#endif\n");
cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 "{");
cs_icalls_content.append(MEMBER_BEGIN "internal static ulong godot_api_hash = ");
@ -1157,9 +1155,7 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"InconsistentNaming\")]\n");
cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"RedundantUnsafeContext\")]\n");
cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"RedundantNameQualifier\")]\n");
cs_icalls_content.append("#if NET\n");
cs_icalls_content.append(INDENT1 "[System.Runtime.CompilerServices.SkipLocalsInit]\n");
cs_icalls_content.append("#endif\n");
cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" OPEN_BLOCK_L1);
cs_icalls_content.append(INDENT2 "internal static ulong godot_api_hash = ");
@ -1278,6 +1274,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
output.append("using System;\n"); // IntPtr
output.append("using System.Diagnostics;\n"); // DebuggerBrowsable
output.append("using Godot.NativeInterop;\n");
output.append("\n"
"#pragma warning disable CS1591 // Disable warning: "
@ -1310,7 +1307,11 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
if (itype.is_singleton) {
output.append("static partial class ");
} else {
output.append(itype.is_instantiable ? "partial class " : "abstract partial class ");
// Even if the class is not instantiable, we can't declare it abstract because
// the engine can still instantiate them and return them via the scripting API.
// Example: `SceneTreeTimer` returned from `SceneTree.create_timer`.
// See the reverted commit: ef5672d3f94a7321ed779c922088bb72adbb1521
output.append("partial class ");
}
output.append(itype.proxy_name);
@ -1430,6 +1431,10 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
// Add native name static field
if (is_derived_type) {
output << MEMBER_BEGIN "private static readonly System.Type _cachedType = typeof(" << itype.proxy_name << ");\n";
}
output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
output.append(itype.name);
output.append("\";\n");
@ -1437,29 +1442,10 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
if (itype.is_instantiable) {
// Add native constructor static field
String get_constructor_method = ICALL_CLASSDB_GET_CONSTRUCTOR;
if (itype.is_singleton) {
// Singletons are static classes. They don't derive Godot.Object,
// so we need to specify the type to call the static method.
get_constructor_method = "Object." + get_constructor_method;
}
output << MEMBER_BEGIN << "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
<< "#if NET\n"
<< INDENT2 "private static unsafe readonly delegate* unmanaged<IntPtr> "
<< CS_STATIC_FIELD_NATIVE_CTOR " = " << get_constructor_method
<< "(" BINDINGS_NATIVE_NAME_FIELD ");\n"
<< "#else\n"
// Get rid of this one once we switch to .NET 5/6
<< INDENT2 "private static readonly IntPtr " CS_STATIC_FIELD_NATIVE_CTOR
<< " = " << get_constructor_method << "(" BINDINGS_NATIVE_NAME_FIELD ");\n"
<< "#endif\n";
<< CS_STATIC_FIELD_NATIVE_CTOR " = " ICALL_CLASSDB_GET_CONSTRUCTOR
<< "(" BINDINGS_NATIVE_NAME_FIELD ");\n";
}
if (is_derived_type) {
@ -1472,22 +1458,15 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
// The engine will initialize the pointer field of the managed side before calling the constructor
// This is why we only allocate a new native object from the constructor if the pointer field is not set
output << INDENT3 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" OPEN_BLOCK_L3
<< "#if NET\n"
<< INDENT4 "unsafe\n" INDENT4 OPEN_BLOCK
<< INDENT5 BINDINGS_PTR_FIELD " = " CS_STATIC_FIELD_NATIVE_CTOR "();\n"
<< CLOSE_BLOCK_L4
<< "#else\n"
// Get rid of this one once we switch to .NET 5/6
<< INDENT4 BINDINGS_PTR_FIELD " = _gd__invoke_class_constructor(" CS_STATIC_FIELD_NATIVE_CTOR ");\n"
<< "#endif\n"
<< INDENT4 C_METHOD_TIE_MANAGED_TO_UNMANAGED "(this, " BINDINGS_PTR_FIELD ");\n"
<< CLOSE_BLOCK_L3
<< INDENT4 C_METHOD_TIE_MANAGED_TO_UNMANAGED "(this, " BINDINGS_PTR_FIELD ", "
<< BINDINGS_NATIVE_NAME_FIELD << ", refCounted: " << (itype.is_ref_counted ? "true" : "false")
<< ", ((object)this).GetType(), _cachedType);\n" CLOSE_BLOCK_L3
<< INDENT3 "else\n" INDENT3 OPEN_BLOCK
<< INDENT4 "InteropUtils.TieManagedToUnmanagedWithPreSetup(this, "
<< BINDINGS_PTR_FIELD ");\n" CLOSE_BLOCK_L3
<< INDENT3 "_InitializeGodotScriptInstanceInternals();\n" CLOSE_BLOCK_L2;
} else {
// Hide the constructor
@ -1503,6 +1482,8 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
}
}
// Methods
int method_bind_count = 0;
for (const MethodInterface &imethod : itype.methods) {
Error method_err = _generate_cs_method(itype, imethod, method_bind_count, output);
@ -1510,12 +1491,82 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
"Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'.");
}
// Signals
for (const SignalInterface &isignal : itype.signals_) {
Error method_err = _generate_cs_signal(itype, isignal, output);
ERR_FAIL_COND_V_MSG(method_err != OK, method_err,
"Failed to generate signal '" + isignal.name + "' for class '" + itype.name + "'.");
}
// Script calls
if (!itype.is_singleton && (is_derived_type || itype.has_virtual_methods)) {
// TODO: string is ok for now. But should be replaced with StringName in the future for performance.
output << MEMBER_BEGIN "internal " << (is_derived_type ? "override" : "virtual")
<< " unsafe bool InternalGodotScriptCall(string method, godot_variant** args, "
<< "int argCount, out godot_variant ret)\n"
<< INDENT2 "{\n";
for (const MethodInterface &imethod : itype.methods) {
if (!imethod.is_virtual) {
continue;
}
// TODO:
// Compare with cached StringName. We already have a cached StringName
// field for the proxy name. We need one for the original snake_case name.
output << INDENT3 "if ((method == nameof(" << imethod.proxy_name << ") || method == \"" << imethod.name
<< "\") && argCount == " << itos(imethod.arguments.size()) << ")\n"
<< INDENT3 "{\n";
if (imethod.return_type.cname != name_cache.type_void) {
output << INDENT4 "object retBoxed = ";
} else {
output << INDENT4;
}
output << imethod.proxy_name << "(";
for (int i = 0; i < imethod.arguments.size(); i++) {
const ArgumentInterface &iarg = imethod.arguments[i];
const TypeInterface *arg_type = _get_type_or_null(iarg.type);
ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
if (i != 0) {
output << ", ";
}
// TODO: static marshaling (no reflection, no runtime type checks)
output << "(" << arg_type->cs_type << ")Marshaling.variant_to_mono_object_of_type(args["
<< itos(i) << "], typeof(" << arg_type->cs_type << "))";
}
output << ");\n";
if (imethod.return_type.cname != name_cache.type_void) {
// TODO: static marshaling (no reflection, no runtime type checks)
output << INDENT4 "ret = Marshaling.mono_object_to_variant(retBoxed);\n";
output << INDENT4 "return true;\n";
} else {
output << INDENT4 "ret = default;\n";
output << INDENT4 "return true;\n";
}
output << INDENT3 "}\n";
}
if (is_derived_type) {
output << INDENT3 "return base.InternalGodotScriptCall(method, args, argCount, out ret);\n";
} else {
output << INDENT3 "return InternalGodotScriptCallViaReflection(method, args, argCount, out ret);\n";
}
output << INDENT2 "}\n";
}
output.append(INDENT1 CLOSE_BLOCK /* class */
CLOSE_BLOCK /* namespace */);
@ -2556,6 +2607,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
if (method_info.flags & METHOD_FLAG_VIRTUAL) {
imethod.is_virtual = true;
itype.has_virtual_methods = true;
}
PropertyInfo return_info = method_info.return_val;
@ -2689,7 +2741,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
ERR_FAIL_COND_V_MSG(itype.find_property_by_name(imethod.cname), false,
"Method name conflicts with property: '" + itype.name + "." + imethod.name + "'.");
// Classes starting with an underscore are ignored unless they're used as a property setter or getter
// Methods starting with an underscore are ignored unless they're used as a property setter or getter
if (!imethod.is_virtual && imethod.name[0] == '_') {
for (const PropertyInterface &iprop : itype.properties) {
if (iprop.setter == imethod.name || iprop.getter == imethod.name) {
@ -3057,6 +3109,8 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
// bool
itype = TypeInterface::create_value_type(String("bool"));
itype.cs_in = "%s.ToGodotBool()";
itype.cs_out = "%5return %0(%1).ToBool();";
itype.c_type = "godot_bool";
itype.c_type_in = itype.c_type;
itype.c_type_out = itype.c_type;
@ -3140,7 +3194,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.proxy_name = "string";
itype.cs_type = itype.proxy_name;
itype.c_in = "%5using %0 %1_in = " C_METHOD_MONOSTR_TO_GODOT "(%1);\n";
itype.c_out = "%5return " C_METHOD_MONOSTR_FROM_GODOT "(&%1);\n";
itype.c_out = "%5return " C_METHOD_MONOSTR_FROM_GODOT "(%1);\n";
itype.c_arg_in = "&%s_in";
itype.c_type = "godot_string";
itype.c_type_in = itype.cs_type;

View File

@ -358,6 +358,8 @@ class BindingsGenerator {
List<MethodInterface> methods;
List<SignalInterface> signals_;
bool has_virtual_methods = false;
const MethodInterface *find_method_by_name(const StringName &p_cname) const {
for (const MethodInterface &E : methods) {
if (E.cname == p_cname) {

View File

@ -44,177 +44,119 @@
#include "../csharp_script.h"
#include "../godotsharp_dirs.h"
#include "../mono_gd/gd_mono_marshal.h"
#include "../utils/osx_utils.h"
#include "code_completion.h"
#include "godotsharp_export.h"
MonoString *godot_icall_GodotSharpDirs_ResDataDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_data_dir());
#include <gdnative/gdnative.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
#define MAYBE_UNUSED [[maybe_unused]]
#else
#define MAYBE_UNUSED
#endif
#ifdef __GNUC__
#define GD_PINVOKE_EXPORT MAYBE_UNUSED __attribute__((visibility("default")))
#elif defined(_WIN32)
#define GD_PINVOKE_EXPORT MAYBE_UNUSED __declspec(dllexport)
#else
#define GD_PINVOKE_EXPORT MAYBE_UNUSED
#endif
void godot_icall_GodotSharpDirs_ResMetadataDir(godot_string *r_dest) {
memnew_placement(r_dest, String(GodotSharpDirs::get_res_metadata_dir()));
}
MonoString *godot_icall_GodotSharpDirs_ResMetadataDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_metadata_dir());
void godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir(godot_string *r_dest) {
memnew_placement(r_dest, String(GodotSharpDirs::get_res_temp_assemblies_base_dir()));
}
MonoString *godot_icall_GodotSharpDirs_ResAssembliesBaseDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_assemblies_base_dir());
void godot_icall_GodotSharpDirs_MonoUserDir(godot_string *r_dest) {
memnew_placement(r_dest, String(GodotSharpDirs::get_mono_user_dir()));
}
MonoString *godot_icall_GodotSharpDirs_ResAssembliesDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_assemblies_dir());
}
MonoString *godot_icall_GodotSharpDirs_ResConfigDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_config_dir());
}
MonoString *godot_icall_GodotSharpDirs_ResTempDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_dir());
}
MonoString *godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_assemblies_base_dir());
}
MonoString *godot_icall_GodotSharpDirs_ResTempAssembliesDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_assemblies_dir());
}
MonoString *godot_icall_GodotSharpDirs_MonoUserDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_user_dir());
}
MonoString *godot_icall_GodotSharpDirs_MonoLogsDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_logs_dir());
}
MonoString *godot_icall_GodotSharpDirs_MonoSolutionsDir() {
void godot_icall_GodotSharpDirs_BuildLogsDirs(godot_string *r_dest) {
#ifdef TOOLS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_solutions_dir());
memnew_placement(r_dest, String(GodotSharpDirs::get_build_logs_dir()));
#else
return nullptr;
#endif
}
MonoString *godot_icall_GodotSharpDirs_BuildLogsDirs() {
void godot_icall_GodotSharpDirs_ProjectSlnPath(godot_string *r_dest) {
#ifdef TOOLS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_build_logs_dir());
memnew_placement(r_dest, String(GodotSharpDirs::get_project_sln_path()));
#else
return nullptr;
#endif
}
MonoString *godot_icall_GodotSharpDirs_ProjectSlnPath() {
void godot_icall_GodotSharpDirs_ProjectCsProjPath(godot_string *r_dest) {
#ifdef TOOLS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_sln_path());
memnew_placement(r_dest, String(GodotSharpDirs::get_project_csproj_path()));
#else
return nullptr;
#endif
}
MonoString *godot_icall_GodotSharpDirs_ProjectCsProjPath() {
void godot_icall_GodotSharpDirs_DataEditorToolsDir(godot_string *r_dest) {
#ifdef TOOLS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_csproj_path());
memnew_placement(r_dest, String(GodotSharpDirs::get_data_editor_tools_dir()));
#else
return nullptr;
#endif
}
MonoString *godot_icall_GodotSharpDirs_DataEditorToolsDir() {
#ifdef TOOLS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_editor_tools_dir());
#else
return nullptr;
#endif
}
MonoString *godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir() {
#ifdef TOOLS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_editor_prebuilt_api_dir());
#else
return nullptr;
#endif
}
MonoString *godot_icall_GodotSharpDirs_DataMonoEtcDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_etc_dir());
}
MonoString *godot_icall_GodotSharpDirs_DataMonoLibDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_lib_dir());
}
MonoString *godot_icall_GodotSharpDirs_DataMonoBinDir() {
#ifdef WINDOWS_ENABLED
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_bin_dir());
#else
return nullptr;
#endif
}
void godot_icall_EditorProgress_Create(MonoString *p_task, MonoString *p_label, int32_t p_amount, MonoBoolean p_can_cancel) {
String task = GDMonoMarshal::mono_string_to_godot(p_task);
String label = GDMonoMarshal::mono_string_to_godot(p_label);
void godot_icall_EditorProgress_Create(const godot_string *p_task, const godot_string *p_label, int32_t p_amount, bool p_can_cancel) {
String task = *reinterpret_cast<const String *>(p_task);
String label = *reinterpret_cast<const String *>(p_label);
EditorNode::progress_add_task(task, label, p_amount, (bool)p_can_cancel);
}
void godot_icall_EditorProgress_Dispose(MonoString *p_task) {
String task = GDMonoMarshal::mono_string_to_godot(p_task);
void godot_icall_EditorProgress_Dispose(const godot_string *p_task) {
String task = *reinterpret_cast<const String *>(p_task);
EditorNode::progress_end_task(task);
}
MonoBoolean godot_icall_EditorProgress_Step(MonoString *p_task, MonoString *p_state, int32_t p_step, MonoBoolean p_force_refresh) {
String task = GDMonoMarshal::mono_string_to_godot(p_task);
String state = GDMonoMarshal::mono_string_to_godot(p_state);
bool godot_icall_EditorProgress_Step(const godot_string *p_task, const godot_string *p_state, int32_t p_step, bool p_force_refresh) {
String task = *reinterpret_cast<const String *>(p_task);
String state = *reinterpret_cast<const String *>(p_state);
return EditorNode::progress_task_step(task, state, p_step, (bool)p_force_refresh);
}
uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(MonoObject *p_initial_assemblies,
MonoString *p_build_config, MonoString *p_custom_bcl_dir, MonoObject *r_assembly_dependencies) {
Dictionary initial_dependencies = GDMonoMarshal::mono_object_to_variant(p_initial_assemblies);
String build_config = GDMonoMarshal::mono_string_to_godot(p_build_config);
String custom_bcl_dir = GDMonoMarshal::mono_string_to_godot(p_custom_bcl_dir);
Dictionary assembly_dependencies = GDMonoMarshal::mono_object_to_variant(r_assembly_dependencies);
return GodotSharpExport::get_exported_assembly_dependencies(initial_dependencies, build_config, custom_bcl_dir, assembly_dependencies);
}
MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_config) {
String config = GDMonoMarshal::mono_string_to_godot(p_config);
String error_str = GDMono::get_singleton()->update_api_assemblies_from_prebuilt(config);
return GDMonoMarshal::mono_string_from_godot(error_str);
}
MonoString *godot_icall_Internal_FullTemplatesDir() {
void godot_icall_Internal_FullTemplatesDir(godot_string *r_dest) {
String full_templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG);
return GDMonoMarshal::mono_string_from_godot(full_templates_dir);
memnew_placement(r_dest, String(full_templates_dir));
}
MonoString *godot_icall_Internal_SimplifyGodotPath(MonoString *p_path) {
String path = GDMonoMarshal::mono_string_to_godot(p_path);
return GDMonoMarshal::mono_string_from_godot(path.simplify_path());
void godot_icall_Internal_SimplifyGodotPath(const godot_string *p_path, godot_string *r_dest) {
String path = *reinterpret_cast<const String *>(p_path);
memnew_placement(r_dest, String(path.simplify_path()));
}
MonoBoolean godot_icall_Internal_IsOsxAppBundleInstalled(MonoString *p_bundle_id) {
bool godot_icall_Internal_IsOsxAppBundleInstalled(const godot_string *p_bundle_id) {
#ifdef OSX_ENABLED
String bundle_id = GDMonoMarshal::mono_string_to_godot(p_bundle_id);
return (MonoBoolean)osx_is_app_bundle_installed(bundle_id);
String bundle_id = *reinterpret_cast<const String *>(p_bundle_id);
return (bool)osx_is_app_bundle_installed(bundle_id);
#else
(void)p_bundle_id; // UNUSED
return (MonoBoolean) false;
return (bool)false;
#endif
}
MonoBoolean godot_icall_Internal_GodotIs32Bits() {
bool godot_icall_Internal_GodotIs32Bits() {
return sizeof(void *) == 4;
}
MonoBoolean godot_icall_Internal_GodotIsRealTDouble() {
bool godot_icall_Internal_GodotIsRealTDouble() {
#ifdef REAL_T_IS_DOUBLE
return (MonoBoolean) true;
return (bool)true;
#else
return (MonoBoolean) false;
return (bool)false;
#endif
}
@ -222,23 +164,15 @@ void godot_icall_Internal_GodotMainIteration() {
Main::iteration();
}
uint64_t godot_icall_Internal_GetCoreApiHash() {
return ClassDB::get_api_hash(ClassDB::API_CORE);
}
uint64_t godot_icall_Internal_GetEditorApiHash() {
return ClassDB::get_api_hash(ClassDB::API_EDITOR);
}
MonoBoolean godot_icall_Internal_IsAssembliesReloadingNeeded() {
bool godot_icall_Internal_IsAssembliesReloadingNeeded() {
#ifdef GD_MONO_HOT_RELOAD
return (MonoBoolean)CSharpLanguage::get_singleton()->is_assembly_reloading_needed();
return (bool)CSharpLanguage::get_singleton()->is_assembly_reloading_needed();
#else
return (MonoBoolean) false;
return (bool)false;
#endif
}
void godot_icall_Internal_ReloadAssemblies(MonoBoolean p_soft_reload) {
void godot_icall_Internal_ReloadAssemblies(bool p_soft_reload) {
#ifdef GD_MONO_HOT_RELOAD
mono_bind::GodotSharp::get_singleton()->call_deferred(SNAME("_reload_assemblies"), (bool)p_soft_reload);
#endif
@ -248,21 +182,22 @@ void godot_icall_Internal_EditorDebuggerNodeReloadScripts() {
EditorDebuggerNode::get_singleton()->reload_scripts();
}
MonoBoolean godot_icall_Internal_ScriptEditorEdit(MonoObject *p_resource, int32_t p_line, int32_t p_col, MonoBoolean p_grab_focus) {
Ref<Resource> resource = GDMonoMarshal::mono_object_to_variant(p_resource);
return (MonoBoolean)ScriptEditor::get_singleton()->edit(resource, p_line, p_col, (bool)p_grab_focus);
bool godot_icall_Internal_ScriptEditorEdit(Resource *p_resource, int32_t p_line, int32_t p_col, bool p_grab_focus) {
Ref<Resource> resource = p_resource;
return (bool)ScriptEditor::get_singleton()->edit(resource, p_line, p_col, (bool)p_grab_focus);
}
void godot_icall_Internal_EditorNodeShowScriptScreen() {
EditorNode::get_singleton()->call("_editor_select", EditorNode::EDITOR_SCRIPT);
}
MonoString *godot_icall_Internal_MonoWindowsInstallRoot() {
void godot_icall_Internal_MonoWindowsInstallRoot(godot_string *r_dest) {
#ifdef WINDOWS_ENABLED
String install_root_dir = GDMono::get_singleton()->get_mono_reg_info().install_root_dir;
return GDMonoMarshal::mono_string_from_godot(install_root_dir);
memnew_placement(r_dest, String(install_root_dir));
#else
return nullptr;
memnew_placement(r_dest, String);
return;
#endif
}
@ -281,107 +216,84 @@ void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() {
}
}
MonoArray *godot_icall_Internal_CodeCompletionRequest(int32_t p_kind, MonoString *p_script_file) {
String script_file = GDMonoMarshal::mono_string_to_godot(p_script_file);
void godot_icall_Internal_CodeCompletionRequest(int32_t p_kind, const godot_string *p_script_file, godot_packed_string_array *r_ret) {
String script_file = *reinterpret_cast<const String *>(p_script_file);
PackedStringArray suggestions = gdmono::get_code_completion((gdmono::CompletionKind)p_kind, script_file);
return GDMonoMarshal::PackedStringArray_to_mono_array(suggestions);
memnew_placement(r_ret, PackedStringArray(suggestions));
}
float godot_icall_Globals_EditorScale() {
return EDSCALE;
}
MonoObject *godot_icall_Globals_GlobalDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) {
String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value);
void godot_icall_Globals_GlobalDef(const godot_string *p_setting, const godot_variant *p_default_value, bool p_restart_if_changed, godot_variant *r_result) {
String setting = *reinterpret_cast<const String *>(p_setting);
Variant default_value = *reinterpret_cast<const Variant *>(p_default_value);
Variant result = _GLOBAL_DEF(setting, default_value, (bool)p_restart_if_changed);
return GDMonoMarshal::variant_to_mono_object(result);
memnew_placement(r_result, Variant(result));
}
MonoObject *godot_icall_Globals_EditorDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) {
String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value);
void godot_icall_Globals_EditorDef(const godot_string *p_setting, const godot_variant *p_default_value, bool p_restart_if_changed, godot_variant *r_result) {
String setting = *reinterpret_cast<const String *>(p_setting);
Variant default_value = *reinterpret_cast<const Variant *>(p_default_value);
Variant result = _EDITOR_DEF(setting, default_value, (bool)p_restart_if_changed);
return GDMonoMarshal::variant_to_mono_object(result);
memnew_placement(r_result, Variant(result));
}
MonoString *godot_icall_Globals_TTR(MonoString *p_text) {
String text = GDMonoMarshal::mono_string_to_godot(p_text);
return GDMonoMarshal::mono_string_from_godot(TTR(text));
void godot_icall_Globals_TTR(const godot_string *p_text, godot_string *r_dest) {
String text = *reinterpret_cast<const String *>(p_text);
memnew_placement(r_dest, String(TTR(text)));
}
MonoString *godot_icall_Utils_OS_GetPlatformName() {
void godot_icall_Utils_OS_GetPlatformName(godot_string *r_dest) {
String os_name = OS::get_singleton()->get_name();
return GDMonoMarshal::mono_string_from_godot(os_name);
memnew_placement(r_dest, String(os_name));
}
MonoBoolean godot_icall_Utils_OS_UnixFileHasExecutableAccess(MonoString *p_file_path) {
bool godot_icall_Utils_OS_UnixFileHasExecutableAccess(const godot_string *p_file_path) {
#ifdef UNIX_ENABLED
String file_path = GDMonoMarshal::mono_string_to_godot(p_file_path);
String file_path = *reinterpret_cast<const String *>(p_file_path);
return access(file_path.utf8().get_data(), X_OK) == 0;
#else
ERR_FAIL_V(false);
#endif
}
void register_editor_internal_calls() {
// GodotSharpDirs
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResDataDir", godot_icall_GodotSharpDirs_ResDataDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResMetadataDir", godot_icall_GodotSharpDirs_ResMetadataDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesBaseDir", godot_icall_GodotSharpDirs_ResAssembliesBaseDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesDir", godot_icall_GodotSharpDirs_ResAssembliesDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResConfigDir", godot_icall_GodotSharpDirs_ResConfigDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempDir", godot_icall_GodotSharpDirs_ResTempDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesBaseDir", godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesDir", godot_icall_GodotSharpDirs_ResTempAssembliesDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoUserDir", godot_icall_GodotSharpDirs_MonoUserDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoLogsDir", godot_icall_GodotSharpDirs_MonoLogsDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoSolutionsDir", godot_icall_GodotSharpDirs_MonoSolutionsDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_BuildLogsDirs", godot_icall_GodotSharpDirs_BuildLogsDirs);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectSlnPath", godot_icall_GodotSharpDirs_ProjectSlnPath);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectCsProjPath", godot_icall_GodotSharpDirs_ProjectCsProjPath);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorToolsDir", godot_icall_GodotSharpDirs_DataEditorToolsDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorPrebuiltApiDir", godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoEtcDir", godot_icall_GodotSharpDirs_DataMonoEtcDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoLibDir", godot_icall_GodotSharpDirs_DataMonoLibDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoBinDir", godot_icall_GodotSharpDirs_DataMonoBinDir);
// EditorProgress
GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Create", godot_icall_EditorProgress_Create);
GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Dispose", godot_icall_EditorProgress_Dispose);
GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Step", godot_icall_EditorProgress_Step);
// ExportPlugin
GDMonoUtils::add_internal_call("GodotTools.Export.ExportPlugin::internal_GetExportedAssemblyDependencies", godot_icall_ExportPlugin_GetExportedAssemblyDependencies);
// Internals
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_UpdateApiAssembliesFromPrebuilt", godot_icall_Internal_UpdateApiAssembliesFromPrebuilt);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_FullTemplatesDir", godot_icall_Internal_FullTemplatesDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_SimplifyGodotPath", godot_icall_Internal_SimplifyGodotPath);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsOsxAppBundleInstalled", godot_icall_Internal_IsOsxAppBundleInstalled);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIs32Bits", godot_icall_Internal_GodotIs32Bits);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIsRealTDouble", godot_icall_Internal_GodotIsRealTDouble);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotMainIteration", godot_icall_Internal_GodotMainIteration);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GetCoreApiHash", godot_icall_Internal_GetCoreApiHash);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GetEditorApiHash", godot_icall_Internal_GetEditorApiHash);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsAssembliesReloadingNeeded", godot_icall_Internal_IsAssembliesReloadingNeeded);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ReloadAssemblies", godot_icall_Internal_ReloadAssemblies);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorDebuggerNodeReloadScripts", godot_icall_Internal_EditorDebuggerNodeReloadScripts);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorEdit", godot_icall_Internal_ScriptEditorEdit);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorNodeShowScriptScreen", godot_icall_Internal_EditorNodeShowScriptScreen);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_MonoWindowsInstallRoot", godot_icall_Internal_MonoWindowsInstallRoot);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorRunPlay", godot_icall_Internal_EditorRunPlay);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorRunStop", godot_icall_Internal_EditorRunStop);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorDebugger_ReloadScripts", godot_icall_Internal_ScriptEditorDebugger_ReloadScripts);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_CodeCompletionRequest", godot_icall_Internal_CodeCompletionRequest);
// Globals
GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorScale", godot_icall_Globals_EditorScale);
GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_GlobalDef", godot_icall_Globals_GlobalDef);
GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorDef", godot_icall_Globals_EditorDef);
GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_TTR", godot_icall_Globals_TTR);
// Utils.OS
GDMonoUtils::add_internal_call("GodotTools.Utils.OS::GetPlatformName", godot_icall_Utils_OS_GetPlatformName);
GDMonoUtils::add_internal_call("GodotTools.Utils.OS::UnixFileHasExecutableAccess", godot_icall_Utils_OS_UnixFileHasExecutableAccess);
#ifdef __cplusplus
}
#endif
void *godotsharp_editor_pinvoke_funcs[32] = {
(void *)godot_icall_GodotSharpDirs_ResMetadataDir,
(void *)godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir,
(void *)godot_icall_GodotSharpDirs_MonoUserDir,
(void *)godot_icall_GodotSharpDirs_BuildLogsDirs,
(void *)godot_icall_GodotSharpDirs_ProjectSlnPath,
(void *)godot_icall_GodotSharpDirs_ProjectCsProjPath,
(void *)godot_icall_GodotSharpDirs_DataEditorToolsDir,
(void *)godot_icall_EditorProgress_Create,
(void *)godot_icall_EditorProgress_Dispose,
(void *)godot_icall_EditorProgress_Step,
(void *)godot_icall_Internal_FullTemplatesDir,
(void *)godot_icall_Internal_SimplifyGodotPath,
(void *)godot_icall_Internal_IsOsxAppBundleInstalled,
(void *)godot_icall_Internal_GodotIs32Bits,
(void *)godot_icall_Internal_GodotIsRealTDouble,
(void *)godot_icall_Internal_GodotMainIteration,
(void *)godot_icall_Internal_IsAssembliesReloadingNeeded,
(void *)godot_icall_Internal_ReloadAssemblies,
(void *)godot_icall_Internal_EditorDebuggerNodeReloadScripts,
(void *)godot_icall_Internal_ScriptEditorEdit,
(void *)godot_icall_Internal_EditorNodeShowScriptScreen,
(void *)godot_icall_Internal_MonoWindowsInstallRoot,
(void *)godot_icall_Internal_EditorRunPlay,
(void *)godot_icall_Internal_EditorRunStop,
(void *)godot_icall_Internal_ScriptEditorDebugger_ReloadScripts,
(void *)godot_icall_Internal_CodeCompletionRequest,
(void *)godot_icall_Globals_EditorScale,
(void *)godot_icall_Globals_GlobalDef,
(void *)godot_icall_Globals_EditorDef,
(void *)godot_icall_Globals_TTR,
(void *)godot_icall_Utils_OS_GetPlatformName,
(void *)godot_icall_Utils_OS_UnixFileHasExecutableAccess,
};

View File

@ -1,144 +0,0 @@
/*************************************************************************/
/* godotsharp_export.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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 "godotsharp_export.h"
#include <mono/metadata/image.h>
#include "core/config/project_settings.h"
#include "core/io/file_access_pack.h"
#include "core/os/os.h"
#include "../mono_gd/gd_mono.h"
#include "../mono_gd/gd_mono_assembly.h"
#include "../mono_gd/gd_mono_cache.h"
#include "../utils/macros.h"
namespace GodotSharpExport {
MonoAssemblyName *new_mono_assembly_name() {
// Mono has no public API to create an empty MonoAssemblyName and the struct is private.
// As such the only way to create it is with a stub name and then clear it.
MonoAssemblyName *aname = mono_assembly_name_new("stub");
CRASH_COND(aname == nullptr);
mono_assembly_name_free(aname); // Frees the string fields, not the struct
return aname;
}
struct AssemblyRefInfo {
String name;
uint16_t major = 0;
uint16_t minor = 0;
uint16_t build = 0;
uint16_t revision = 0;
};
AssemblyRefInfo get_assemblyref_name(MonoImage *p_image, int index) {
const MonoTableInfo *table_info = mono_image_get_table_info(p_image, MONO_TABLE_ASSEMBLYREF);
uint32_t cols[MONO_ASSEMBLYREF_SIZE];
mono_metadata_decode_row(table_info, index, cols, MONO_ASSEMBLYREF_SIZE);
return {
String::utf8(mono_metadata_string_heap(p_image, cols[MONO_ASSEMBLYREF_NAME])),
(uint16_t)cols[MONO_ASSEMBLYREF_MAJOR_VERSION],
(uint16_t)cols[MONO_ASSEMBLYREF_MINOR_VERSION],
(uint16_t)cols[MONO_ASSEMBLYREF_BUILD_NUMBER],
(uint16_t)cols[MONO_ASSEMBLYREF_REV_NUMBER]
};
}
Error get_assembly_dependencies(GDMonoAssembly *p_assembly, MonoAssemblyName *reusable_aname, const Vector<String> &p_search_dirs, Dictionary &r_assembly_dependencies) {
MonoImage *image = p_assembly->get_image();
for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) {
AssemblyRefInfo ref_info = get_assemblyref_name(image, i);
const String &ref_name = ref_info.name;
if (r_assembly_dependencies.has(ref_name)) {
continue;
}
mono_assembly_get_assemblyref(image, i, reusable_aname);
GDMonoAssembly *ref_assembly = nullptr;
if (!GDMono::get_singleton()->load_assembly(ref_name, reusable_aname, &ref_assembly, /* refonly: */ true, p_search_dirs)) {
ERR_FAIL_V_MSG(ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'.");
}
r_assembly_dependencies[ref_name] = ref_assembly->get_path();
Error err = get_assembly_dependencies(ref_assembly, reusable_aname, p_search_dirs, r_assembly_dependencies);
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot load one of the dependencies for the assembly: '" + ref_name + "'.");
}
return OK;
}
Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies,
const String &p_build_config, const String &p_custom_bcl_dir, Dictionary &r_assembly_dependencies) {
MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.Domain.ProjectExport");
ERR_FAIL_NULL_V(export_domain, FAILED);
_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain);
_GDMONO_SCOPE_DOMAIN_(export_domain);
Vector<String> search_dirs;
GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_bcl_dir);
if (p_custom_bcl_dir.length()) {
// Only one mscorlib can be loaded. We need this workaround to make sure we get it from the right BCL directory.
r_assembly_dependencies["mscorlib"] = p_custom_bcl_dir.plus_file("mscorlib.dll").simplify_path();
}
for (const Variant *key = p_initial_assemblies.next(); key; key = p_initial_assemblies.next(key)) {
String assembly_name = *key;
String assembly_path = p_initial_assemblies[*key];
GDMonoAssembly *assembly = nullptr;
bool load_success = GDMono::get_singleton()->load_assembly_from(assembly_name, assembly_path, &assembly, /* refonly: */ true);
ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + assembly_name + "'.");
MonoAssemblyName *reusable_aname = new_mono_assembly_name();
SCOPE_EXIT { mono_free(reusable_aname); };
Error err = get_assembly_dependencies(assembly, reusable_aname, search_dirs, r_assembly_dependencies);
if (err != OK) {
return err;
}
}
return OK;
}
} // namespace GodotSharpExport

View File

@ -1,48 +0,0 @@
/*************************************************************************/
/* godotsharp_export.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
#ifndef GODOTSHARP_EXPORT_H
#define GODOTSHARP_EXPORT_H
#include "core/error/error_list.h"
#include "core/string/ustring.h"
#include "core/variant/dictionary.h"
#include "../mono_gd/gd_mono_header.h"
namespace GodotSharpExport {
Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies);
Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies,
const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_assembly_dependencies);
} // namespace GodotSharpExport
#endif // GODOTSHARP_EXPORT_H

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>9</LangVersion>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<!-- To generate the .runtimeconfig.json file-->
<EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\GodotSharp\GodotSharp.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,195 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Loader;
using Godot.NativeInterop;
namespace GodotPlugins
{
public static class Main
{
private static readonly List<AssemblyName> SharedAssemblies = new();
private static readonly Assembly CoreApiAssembly = typeof(Godot.Object).Assembly;
private static Assembly? _editorApiAssembly;
private static readonly AssemblyLoadContext MainLoadContext =
AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()) ??
AssemblyLoadContext.Default;
// Right now we do it this way for simplicity as hot-reload is disabled. It will need to be changed later.
[UnmanagedCallersOnly]
internal static unsafe godot_bool Initialize(godot_bool editorHint,
PluginsCallbacks* pluginsCallbacks, Godot.Bridge.ManagedCallbacks* managedCallbacks)
{
try
{
SharedAssemblies.Add(CoreApiAssembly.GetName());
if (editorHint.ToBool())
{
_editorApiAssembly = Assembly.Load("GodotSharpEditor");
SharedAssemblies.Add(_editorApiAssembly.GetName());
}
NativeLibrary.SetDllImportResolver(CoreApiAssembly, OnResolveDllImport);
*pluginsCallbacks = new()
{
LoadProjectAssemblyCallback = &LoadProjectAssembly,
LoadToolsAssemblyCallback = &LoadToolsAssembly,
};
*managedCallbacks = Godot.Bridge.ManagedCallbacks.Create();
return godot_bool.True;
}
catch (Exception e)
{
Console.Error.WriteLine(e);
*pluginsCallbacks = default;
*managedCallbacks = default;
return false.ToGodotBool();
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct PluginsCallbacks
{
public unsafe delegate* unmanaged<char*, godot_bool> LoadProjectAssemblyCallback;
public unsafe delegate* unmanaged<char*, IntPtr> LoadToolsAssemblyCallback;
}
[UnmanagedCallersOnly]
internal static unsafe godot_bool LoadProjectAssembly(char* nAssemblyPath)
{
try
{
string assemblyPath = new(nAssemblyPath);
var assembly = LoadPlugin(assemblyPath);
var method = CoreApiAssembly.GetType("Godot.Bridge.ScriptManagerBridge")?
.GetMethod("LookupScriptsInAssembly",
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
if (method == null)
{
throw new MissingMethodException("Godot.Bridge.ScriptManagerBridge",
"LookupScriptsInAssembly");
}
method.Invoke(null, new object[] { assembly });
return godot_bool.True;
}
catch (Exception e)
{
Console.Error.WriteLine(e);
return false.ToGodotBool();
}
}
[UnmanagedCallersOnly]
internal static unsafe IntPtr LoadToolsAssembly(char* nAssemblyPath)
{
try
{
string assemblyPath = new(nAssemblyPath);
if (_editorApiAssembly == null)
throw new InvalidOperationException("The Godot editor API assembly is not loaded");
var assembly = LoadPlugin(assemblyPath);
NativeLibrary.SetDllImportResolver(assembly, OnResolveDllImport);
var method = assembly.GetType("GodotTools.GodotSharpEditor")?
.GetMethod("InternalCreateInstance",
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
if (method == null)
{
throw new MissingMethodException("GodotTools.GodotSharpEditor",
"InternalCreateInstance");
}
return (IntPtr?)method.Invoke(null, null) ?? IntPtr.Zero;
}
catch (Exception e)
{
Console.Error.WriteLine(e);
return IntPtr.Zero;
}
}
private static Assembly LoadPlugin(string assemblyPath)
{
string assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
var sharedAssemblies = new List<string>();
foreach (var sharedAssembly in SharedAssemblies)
{
string? sharedAssemblyName = sharedAssembly.Name;
if (sharedAssemblyName != null)
sharedAssemblies.Add(sharedAssemblyName);
}
var loadContext = new PluginLoadContext(assemblyPath, sharedAssemblies, MainLoadContext);
return loadContext.LoadFromAssemblyName(new AssemblyName(assemblyName));
}
public static IntPtr OnResolveDllImport(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
{
if (libraryName == "__Internal")
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return Win32.GetModuleHandle(null);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return Linux.dlopen(null, Linux.RTLD_LAZY);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return MacOS.dlopen(null, MacOS.RTLD_LAZY);
}
}
return IntPtr.Zero;
}
// ReSharper disable InconsistentNaming
private static class MacOS
{
private const string SystemLibrary = "/usr/lib/libSystem.dylib";
public const int RTLD_LAZY = 1;
[DllImport(SystemLibrary)]
public static extern IntPtr dlopen(string? path, int mode);
}
private static class Linux
{
private const string SystemLibrary = "dl";
public const int RTLD_LAZY = 1;
[DllImport(SystemLibrary)]
public static extern IntPtr dlopen(string? path, int mode);
}
private static class Win32
{
private const string SystemLibrary = "Kernel32.dll";
[DllImport(SystemLibrary)]
public static extern IntPtr GetModuleHandle(string? lpModuleName);
}
// ReSharper restore InconsistentNaming
}
}

View File

@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Loader;
namespace GodotPlugins
{
public class PluginLoadContext : AssemblyLoadContext
{
private readonly AssemblyDependencyResolver _resolver;
private readonly ICollection<string> _sharedAssemblies;
private readonly AssemblyLoadContext _mainLoadContext;
public PluginLoadContext(string pluginPath, ICollection<string> sharedAssemblies,
AssemblyLoadContext mainLoadContext)
{
Console.WriteLine(pluginPath);
Console.Out.Flush();
_resolver = new AssemblyDependencyResolver(pluginPath);
_sharedAssemblies = sharedAssemblies;
_mainLoadContext = mainLoadContext;
}
protected override Assembly? Load(AssemblyName assemblyName)
{
if (assemblyName.Name == null)
return null;
if (_sharedAssemblies.Contains(assemblyName.Name))
return _mainLoadContext.LoadFromAssemblyName(assemblyName);
string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
if (assemblyPath != null)
{
// Load in memory to prevent locking the file
using var assemblyFile = File.Open(assemblyPath, FileMode.Open, FileAccess.Read, FileShare.Read);
string pdbPath = Path.ChangeExtension(assemblyPath, ".pdb");
if (File.Exists(pdbPath))
{
using var pdbFile = File.Open(pdbPath, FileMode.Open, FileAccess.Read, FileShare.Read);
return LoadFromStream(assemblyFile, pdbFile);
}
return LoadFromStream(assemblyFile);
}
return null;
}
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
string? libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
if (libraryPath != null)
return LoadUnmanagedDllFromPath(libraryPath);
return IntPtr.Zero;
}
}
}

View File

@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharp", "GodotSharp\Go
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharpEditor", "GodotSharpEditor\GodotSharpEditor.csproj", "{8FBEC238-D944-4074-8548-B3B524305905}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotPlugins", "GodotPlugins\GodotPlugins.csproj", "{944B77DB-497B-47F5-A1E3-81C35E3E9D4E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -18,5 +20,9 @@ Global
{8FBEC238-D944-4074-8548-B3B524305905}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8FBEC238-D944-4074-8548-B3B524305905}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8FBEC238-D944-4074-8548-B3B524305905}.Release|Any CPU.Build.0 = Release|Any CPU
{944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Godot.NativeInterop;
namespace Godot.Collections
@ -15,14 +14,14 @@ namespace Godot.Collections
/// </summary>
public sealed class Array : IList, IDisposable
{
internal godot_array NativeValue;
public godot_array NativeValue;
/// <summary>
/// Constructs a new empty <see cref="Array"/>.
/// </summary>
public Array()
{
godot_icall_Array_Ctor(out NativeValue);
NativeValue = NativeFuncs.godotsharp_array_new();
}
/// <summary>
@ -48,11 +47,15 @@ namespace Godot.Collections
public Array(params object[] array) : this()
{
if (array == null)
{
throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'");
}
godot_icall_Array_Ctor_MonoArray(array, out NativeValue);
NativeValue = NativeFuncs.godotsharp_array_new();
int length = array.Length;
Resize(length);
for (int i = 0; i < length; i++)
this[i] = array[i];
}
private Array(godot_array nativeValueToOwn)
@ -92,7 +95,7 @@ namespace Godot.Collections
public Array Duplicate(bool deep = false)
{
godot_array newArray;
godot_icall_Array_Duplicate(ref NativeValue, deep, out newArray);
NativeFuncs.godotsharp_array_duplicate(ref NativeValue, deep.ToGodotBool(), out newArray);
return CreateTakingOwnershipOfDisposableValue(newArray);
}
@ -101,18 +104,12 @@ namespace Godot.Collections
/// </summary>
/// <param name="newSize">The new size of the array.</param>
/// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns>
public Error Resize(int newSize)
{
return godot_icall_Array_Resize(ref NativeValue, newSize);
}
public Error Resize(int newSize) => NativeFuncs.godotsharp_array_resize(ref NativeValue, newSize);
/// <summary>
/// Shuffles the contents of this <see cref="Array"/> into a random order.
/// </summary>
public void Shuffle()
{
godot_icall_Array_Shuffle(ref NativeValue);
}
public void Shuffle() => NativeFuncs.godotsharp_array_shuffle(ref NativeValue);
/// <summary>
/// Concatenates these two <see cref="Array"/>s.
@ -122,9 +119,16 @@ namespace Godot.Collections
/// <returns>A new Godot Array with the contents of both arrays.</returns>
public static Array operator +(Array left, Array right)
{
godot_array newArray;
godot_icall_Array_Concatenate(ref left.NativeValue, ref right.NativeValue, out newArray);
return CreateTakingOwnershipOfDisposableValue(newArray);
int leftCount = left.Count;
int rightCount = right.Count;
Array newArray = left.Duplicate(deep: false);
newArray.Resize(leftCount + rightCount);
for (int i = 0; i < rightCount; i++)
newArray[i + leftCount] = right[i];
return newArray;
}
// IList
@ -137,18 +141,20 @@ namespace Godot.Collections
/// Returns the object at the given index.
/// </summary>
/// <value>The object at the given index.</value>
public object this[int index]
public unsafe object this[int index]
{
get
{
godot_icall_Array_At(ref NativeValue, index, out godot_variant elem);
unsafe
{
using (elem)
return Marshaling.variant_to_mono_object(&elem);
}
GetVariantBorrowElementAt(index, out godot_variant borrowElem);
return Marshaling.variant_to_mono_object(&borrowElem);
}
set
{
if (index < 0 || index >= Count)
throw new IndexOutOfRangeException();
godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref NativeValue);
ptrw[index] = Marshaling.mono_object_to_variant(value);
}
set => godot_icall_Array_SetAt(ref NativeValue, index, value);
}
/// <summary>
@ -157,19 +163,23 @@ namespace Godot.Collections
/// </summary>
/// <param name="value">The object to add.</param>
/// <returns>The new size after adding the object.</returns>
public int Add(object value) => godot_icall_Array_Add(ref NativeValue, value);
public unsafe int Add(object value)
{
using godot_variant variantValue = Marshaling.mono_object_to_variant(value);
return NativeFuncs.godotsharp_array_add(ref NativeValue, &variantValue);
}
/// <summary>
/// Checks if this <see cref="Array"/> contains the given object.
/// </summary>
/// <param name="value">The item to look for.</param>
/// <returns>Whether or not this array contains the given object.</returns>
public bool Contains(object value) => godot_icall_Array_Contains(ref NativeValue, value);
public bool Contains(object value) => IndexOf(value) != -1;
/// <summary>
/// Erases all items from this <see cref="Array"/>.
/// </summary>
public void Clear() => godot_icall_Array_Clear(ref NativeValue);
public void Clear() => Resize(0);
/// <summary>
/// Searches this <see cref="Array"/> for an object
@ -177,7 +187,11 @@ namespace Godot.Collections
/// </summary>
/// <param name="value">The object to search for.</param>
/// <returns>The index of the object, or -1 if not found.</returns>
public int IndexOf(object value) => godot_icall_Array_IndexOf(ref NativeValue, value);
public unsafe int IndexOf(object value)
{
using godot_variant variantValue = Marshaling.mono_object_to_variant(value);
return NativeFuncs.godotsharp_array_index_of(ref NativeValue, &variantValue);
}
/// <summary>
/// Inserts a new object at a given position in the array.
@ -187,20 +201,38 @@ namespace Godot.Collections
/// </summary>
/// <param name="index">The index to insert at.</param>
/// <param name="value">The object to insert.</param>
public void Insert(int index, object value) => godot_icall_Array_Insert(ref NativeValue, index, value);
public unsafe void Insert(int index, object value)
{
if (index < 0 || index > Count)
throw new IndexOutOfRangeException();
using godot_variant variantValue = Marshaling.mono_object_to_variant(value);
NativeFuncs.godotsharp_array_insert(ref NativeValue, index, &variantValue);
}
/// <summary>
/// Removes the first occurrence of the specified value
/// from this <see cref="Array"/>.
/// </summary>
/// <param name="value">The value to remove.</param>
public void Remove(object value) => godot_icall_Array_Remove(ref NativeValue, value);
public void Remove(object value)
{
int index = IndexOf(value);
if (index >= 0)
RemoveAt(index);
}
/// <summary>
/// Removes an element from this <see cref="Array"/> by index.
/// </summary>
/// <param name="index">The index of the element to remove.</param>
public void RemoveAt(int index) => godot_icall_Array_RemoveAt(ref NativeValue, index);
public void RemoveAt(int index)
{
if (index < 0 || index > Count)
throw new IndexOutOfRangeException();
NativeFuncs.godotsharp_array_remove_at(ref NativeValue, index);
}
// ICollection
@ -209,7 +241,7 @@ namespace Godot.Collections
/// This is also known as the size or length of the array.
/// </summary>
/// <returns>The number of elements.</returns>
public int Count => godot_icall_Array_Count(ref NativeValue);
public int Count => NativeValue.Size;
object ICollection.SyncRoot => this;
@ -220,17 +252,35 @@ namespace Godot.Collections
/// untyped C# array, starting at the given index.
/// </summary>
/// <param name="array">The array to copy to.</param>
/// <param name="index">The index to start at.</param>
public void CopyTo(System.Array array, int index)
/// <param name="destIndex">The index to start at.</param>
public void CopyTo(System.Array array, int destIndex)
{
if (array == null)
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
if (index < 0)
throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension.");
if (destIndex < 0)
{
throw new ArgumentOutOfRangeException(nameof(destIndex),
"Number was less than the array's lower bound in the first dimension.");
}
// Internal call may throw ArgumentException
godot_icall_Array_CopyTo(ref NativeValue, array, index);
int count = Count;
if (array.Length < (destIndex + count))
{
throw new ArgumentException(
"Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
}
unsafe
{
for (int i = 0; i < count; i++)
{
object obj = Marshaling.variant_to_mono_object(&(*NativeValue._p)._arrayVector._ptr[i]);
array.SetValue(obj, destIndex);
destIndex++;
}
}
}
// IEnumerable
@ -253,64 +303,30 @@ namespace Godot.Collections
/// Converts this <see cref="Array"/> to a string.
/// </summary>
/// <returns>A string representation of this array.</returns>
public override string ToString()
public override unsafe string ToString()
{
return godot_icall_Array_ToString(ref NativeValue);
using godot_string str = default;
NativeFuncs.godotsharp_array_to_string(ref NativeValue, &str);
return Marshaling.mono_string_from_godot(str);
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Array_Ctor(out godot_array dest);
/// <summary>
/// The variant returned via the <paramref name="elem"/> parameter is owned by the Array and must not be disposed.
/// </summary>
internal void GetVariantBorrowElementAt(int index, out godot_variant elem)
{
if (index < 0 || index >= Count)
throw new IndexOutOfRangeException();
GetVariantBorrowElementAtUnchecked(index, out elem);
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Array_Ctor_MonoArray(System.Array array, out godot_array dest);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Array_At(ref godot_array ptr, int index, out godot_variant elem);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Array_SetAt(ref godot_array ptr, int index, object value);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int godot_icall_Array_Count(ref godot_array ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int godot_icall_Array_Add(ref godot_array ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Array_Clear(ref godot_array ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Array_Concatenate(ref godot_array left, ref godot_array right, out godot_array dest);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool godot_icall_Array_Contains(ref godot_array ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Array_CopyTo(ref godot_array ptr, System.Array array, int arrayIndex);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Array_Duplicate(ref godot_array ptr, bool deep, out godot_array dest);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int godot_icall_Array_IndexOf(ref godot_array ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Array_Insert(ref godot_array ptr, int index, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool godot_icall_Array_Remove(ref godot_array ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Array_RemoveAt(ref godot_array ptr, int index);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern Error godot_icall_Array_Resize(ref godot_array ptr, int newSize);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern Error godot_icall_Array_Shuffle(ref godot_array ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern string godot_icall_Array_ToString(ref godot_array ptr);
/// <summary>
/// The variant returned via the <paramref name="elem"/> parameter is owned by the Array and must not be disposed.
/// </summary>
internal unsafe void GetVariantBorrowElementAtUnchecked(int index, out godot_variant elem)
{
elem = (*NativeValue._p)._arrayVector._ptr[index];
}
}
internal interface IGenericGodotArray
@ -449,11 +465,10 @@ namespace Godot.Collections
{
get
{
Array.godot_icall_Array_At(ref _underlyingArray.NativeValue, index, out godot_variant elem);
_underlyingArray.GetVariantBorrowElementAt(index, out godot_variant borrowElem);
unsafe
{
using (elem)
return (T)Marshaling.variant_to_mono_object_of_type(&elem, TypeOfElements);
return (T)Marshaling.variant_to_mono_object_of_type(&borrowElem, TypeOfElements);
}
}
set => _underlyingArray[index] = value;
@ -544,12 +559,14 @@ namespace Godot.Collections
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
if (arrayIndex < 0)
throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension.");
throw new ArgumentOutOfRangeException(nameof(arrayIndex),
"Number was less than the array's lower bound in the first dimension.");
int count = _underlyingArray.Count;
if (array.Length < (arrayIndex + count))
throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
throw new ArgumentException(
"Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
for (int i = 0; i < count; i++)
{
@ -566,7 +583,14 @@ namespace Godot.Collections
/// <returns>A bool indicating success or failure.</returns>
public bool Remove(T item)
{
return Array.godot_icall_Array_Remove(ref _underlyingArray.NativeValue, item);
int index = IndexOf(item);
if (index >= 0)
{
RemoveAt(index);
return true;
}
return false;
}
// IEnumerable<T>

View File

@ -1,22 +1,27 @@
using System;
#nullable enable
namespace Godot
{
[AttributeUsage(AttributeTargets.Assembly)]
public class AssemblyHasScriptsAttribute : Attribute
{
private readonly bool requiresLookup;
private readonly System.Type[] scriptTypes;
public bool RequiresLookup { get; private set; }
public Type[]? ScriptTypes { get; private set; }
public AssemblyHasScriptsAttribute()
{
requiresLookup = true;
RequiresLookup = true;
ScriptTypes = null;
}
public AssemblyHasScriptsAttribute(System.Type[] scriptTypes)
public AssemblyHasScriptsAttribute(Type[] scriptTypes)
{
requiresLookup = false;
this.scriptTypes = scriptTypes;
RequiresLookup = false;
ScriptTypes = scriptTypes;
}
}
}
#nullable restore

View File

@ -5,11 +5,11 @@ namespace Godot
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class ScriptPathAttribute : Attribute
{
private string path;
public string Path { get; private set; }
public ScriptPathAttribute(string path)
{
this.path = path;
Path = path;
}
}
}

View File

@ -0,0 +1,162 @@
using System;
using System.Runtime.InteropServices;
using Godot.NativeInterop;
namespace Godot.Bridge
{
internal static class CSharpInstanceBridge
{
[UnmanagedCallersOnly]
internal static unsafe godot_bool Call(IntPtr godotObjectGCHandle, godot_string_name* method,
godot_variant** args, int argCount, godot_variant_call_error* refCallError, godot_variant* ret)
{
try
{
// Performance is not critical here as this will be replaced with source generators.
var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
if (godotObject == null)
{
*ret = default;
(*refCallError).error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL;
return false.ToGodotBool();
}
using godot_string dest = default;
NativeFuncs.godotsharp_string_name_as_string(&dest, method);
string methodStr = Marshaling.mono_string_from_godot(dest);
_ = godotObject.InternalGodotScriptCall(methodStr, args, argCount, out godot_variant retValue);
*ret = retValue;
return true.ToGodotBool();
}
catch (Exception e)
{
ExceptionUtils.DebugPrintUnhandledException(e);
*ret = default;
return false.ToGodotBool();
}
}
[UnmanagedCallersOnly]
internal static unsafe godot_bool Set(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* value)
{
try
{
// Performance is not critical here as this will be replaced with source generators.
var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
if (godotObject == null)
throw new InvalidOperationException();
var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_string_name_new_copy(name));
if (godotObject.InternalGodotScriptSetFieldOrPropViaReflection(nameManaged.ToString(), value))
return true.ToGodotBool();
object valueManaged = Marshaling.variant_to_mono_object(value);
return godotObject._Set(nameManaged, valueManaged).ToGodotBool();
}
catch (Exception e)
{
ExceptionUtils.DebugPrintUnhandledException(e);
return false.ToGodotBool();
}
}
[UnmanagedCallersOnly]
internal static unsafe godot_bool Get(IntPtr godotObjectGCHandle, godot_string_name* name,
godot_variant* outRet)
{
try
{
// Performance is not critical here as this will be replaced with source generators.
var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
if (godotObject == null)
throw new InvalidOperationException();
var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_string_name_new_copy(name));
if (godotObject.InternalGodotScriptGetFieldOrPropViaReflection(nameManaged.ToString(),
out godot_variant outRetValue))
{
*outRet = outRetValue;
return true.ToGodotBool();
}
object ret = godotObject._Get(nameManaged);
if (ret == null)
{
*outRet = default;
return false.ToGodotBool();
}
*outRet = Marshaling.mono_object_to_variant(ret);
return true.ToGodotBool();
}
catch (Exception e)
{
ExceptionUtils.DebugPrintUnhandledException(e);
*outRet = default;
return false.ToGodotBool();
}
}
[UnmanagedCallersOnly]
internal static void CallDispose(IntPtr godotObjectGCHandle, godot_bool okIfNull)
{
try
{
var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
if (okIfNull.ToBool())
godotObject?.Dispose();
else
godotObject!.Dispose();
}
catch (Exception e)
{
ExceptionUtils.DebugPrintUnhandledException(e);
}
}
[UnmanagedCallersOnly]
internal static unsafe void CallToString(IntPtr godotObjectGCHandle, godot_string* outRes, godot_bool* outValid)
{
try
{
var self = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
if (self == null)
{
*outRes = default;
*outValid = false.ToGodotBool();
return;
}
var resultStr = self.ToString();
if (resultStr == null)
{
*outRes = default;
*outValid = false.ToGodotBool();
return;
}
*outRes = Marshaling.mono_string_to_godot(resultStr);
*outValid = true.ToGodotBool();
}
catch (Exception e)
{
ExceptionUtils.DebugPrintUnhandledException(e);
*outRes = default;
*outValid = false.ToGodotBool();
}
}
}
}

View File

@ -0,0 +1,22 @@
using System;
using System.Runtime.InteropServices;
using Godot.NativeInterop;
namespace Godot.Bridge
{
internal static class GCHandleBridge
{
[UnmanagedCallersOnly]
internal static void FreeGCHandle(IntPtr gcHandlePtr)
{
try
{
GCHandle.FromIntPtr(gcHandlePtr).Free();
}
catch (Exception e)
{
ExceptionUtils.DebugPrintUnhandledException(e);
}
}
}
}

View File

@ -0,0 +1,72 @@
using System;
using System.Runtime.InteropServices;
using Godot.NativeInterop;
namespace Godot.Bridge
{
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct ManagedCallbacks
{
// @formatter:off
public delegate* unmanaged<IntPtr, godot_variant**, int, godot_bool*, void> SignalAwaiter_SignalCallback;
public delegate* unmanaged<IntPtr, godot_variant**, uint, godot_variant*, void> DelegateUtils_InvokeWithVariantArgs;
public delegate* unmanaged<IntPtr, IntPtr, godot_bool> DelegateUtils_DelegateEquals;
public delegate* unmanaged<void> ScriptManagerBridge_FrameCallback;
public delegate* unmanaged<godot_string_name*, IntPtr, IntPtr> ScriptManagerBridge_CreateManagedForGodotObjectBinding;
public delegate* unmanaged<IntPtr, IntPtr, godot_variant**, int, godot_bool> ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance;
public delegate* unmanaged<IntPtr, godot_string_name*, void> ScriptManagerBridge_GetScriptNativeName;
public delegate* unmanaged<IntPtr, IntPtr, void> ScriptManagerBridge_SetGodotObjectPtr;
public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant**, int, godot_bool*, void> ScriptManagerBridge_RaiseEventSignal;
public delegate* unmanaged<IntPtr, godot_dictionary*, void> ScriptManagerBridge_GetScriptSignalList;
public delegate* unmanaged<IntPtr, godot_string*, godot_bool> ScriptManagerBridge_HasScriptSignal;
public delegate* unmanaged<IntPtr, godot_string*, godot_bool, godot_bool> ScriptManagerBridge_HasMethodUnknownParams;
public delegate* unmanaged<IntPtr, IntPtr, godot_bool> ScriptManagerBridge_ScriptIsOrInherits;
public delegate* unmanaged<IntPtr, godot_string*, godot_bool> ScriptManagerBridge_AddScriptBridge;
public delegate* unmanaged<IntPtr, void> ScriptManagerBridge_RemoveScriptBridge;
public delegate* unmanaged<IntPtr, godot_bool*, godot_dictionary*, void> ScriptManagerBridge_UpdateScriptClassInfo;
public delegate* unmanaged<IntPtr, IntPtr*, godot_bool, godot_bool> ScriptManagerBridge_SwapGCHandleForType;
public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant**, int, godot_variant_call_error*, godot_variant*, godot_bool> CSharpInstanceBridge_Call;
public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant*, godot_bool> CSharpInstanceBridge_Set;
public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant*, godot_bool> CSharpInstanceBridge_Get;
public delegate* unmanaged<IntPtr, godot_bool, void> CSharpInstanceBridge_CallDispose;
public delegate* unmanaged<IntPtr, godot_string*, godot_bool*, void> CSharpInstanceBridge_CallToString;
public delegate* unmanaged<IntPtr, void> GCHandleBridge_FreeGCHandle;
public delegate* unmanaged<void> DebuggingUtils_InstallTraceListener;
public delegate* unmanaged<void> Dispatcher_InitializeDefaultGodotTaskScheduler;
// @formatter:on
public static ManagedCallbacks Create()
{
return new()
{
// @formatter:off
SignalAwaiter_SignalCallback = &SignalAwaiter.SignalCallback,
DelegateUtils_InvokeWithVariantArgs = &DelegateUtils.InvokeWithVariantArgs,
DelegateUtils_DelegateEquals = &DelegateUtils.DelegateEquals,
ScriptManagerBridge_FrameCallback = &ScriptManagerBridge.FrameCallback,
ScriptManagerBridge_CreateManagedForGodotObjectBinding = &ScriptManagerBridge.CreateManagedForGodotObjectBinding,
ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance = &ScriptManagerBridge.CreateManagedForGodotObjectScriptInstance,
ScriptManagerBridge_GetScriptNativeName = &ScriptManagerBridge.GetScriptNativeName,
ScriptManagerBridge_SetGodotObjectPtr = &ScriptManagerBridge.SetGodotObjectPtr,
ScriptManagerBridge_RaiseEventSignal = &ScriptManagerBridge.RaiseEventSignal,
ScriptManagerBridge_GetScriptSignalList = &ScriptManagerBridge.GetScriptSignalList,
ScriptManagerBridge_HasScriptSignal = &ScriptManagerBridge.HasScriptSignal,
ScriptManagerBridge_HasMethodUnknownParams = &ScriptManagerBridge.HasMethodUnknownParams,
ScriptManagerBridge_ScriptIsOrInherits = &ScriptManagerBridge.ScriptIsOrInherits,
ScriptManagerBridge_AddScriptBridge = &ScriptManagerBridge.AddScriptBridge,
ScriptManagerBridge_RemoveScriptBridge = &ScriptManagerBridge.RemoveScriptBridge,
ScriptManagerBridge_UpdateScriptClassInfo = &ScriptManagerBridge.UpdateScriptClassInfo,
ScriptManagerBridge_SwapGCHandleForType = &ScriptManagerBridge.SwapGCHandleForType,
CSharpInstanceBridge_Call = &CSharpInstanceBridge.Call,
CSharpInstanceBridge_Set = &CSharpInstanceBridge.Set,
CSharpInstanceBridge_Get = &CSharpInstanceBridge.Get,
CSharpInstanceBridge_CallDispose = &CSharpInstanceBridge.CallDispose,
CSharpInstanceBridge_CallToString = &CSharpInstanceBridge.CallToString,
GCHandleBridge_FreeGCHandle = &GCHandleBridge.FreeGCHandle,
DebuggingUtils_InstallTraceListener = &DebuggingUtils.InstallTraceListener,
Dispatcher_InitializeDefaultGodotTaskScheduler = &Dispatcher.InitializeDefaultGodotTaskScheduler,
// @formatter:on
};
}
}
}

View File

@ -0,0 +1,668 @@
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using Godot.Collections;
using Godot.NativeInterop;
namespace Godot.Bridge
{
internal static class ScriptManagerBridge
{
private static System.Collections.Generic.Dictionary<string, ScriptLookupInfo> _scriptLookupMap = new();
private static System.Collections.Generic.Dictionary<IntPtr, Type> _scriptBridgeMap = new();
private struct ScriptLookupInfo
{
public string ClassNamespace { get; private set; }
public string ClassName { get; private set; }
public Type ScriptType { get; private set; }
public ScriptLookupInfo(string classNamespace, string className, Type scriptType)
{
ClassNamespace = classNamespace;
ClassName = className;
ScriptType = scriptType;
}
};
[UnmanagedCallersOnly]
internal static void FrameCallback()
{
try
{
Dispatcher.DefaultGodotTaskScheduler?.Activate();
}
catch (Exception e)
{
ExceptionUtils.DebugUnhandledException(e);
}
}
[UnmanagedCallersOnly]
internal static unsafe IntPtr CreateManagedForGodotObjectBinding(godot_string_name* nativeTypeName,
IntPtr godotObject)
{
try
{
Type nativeType = TypeGetProxyClass(nativeTypeName);
var obj = (Object)FormatterServices.GetUninitializedObject(nativeType);
obj.NativePtr = godotObject;
var ctor = nativeType.GetConstructor(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
null, Type.EmptyTypes, null);
_ = ctor!.Invoke(obj, null);
return GCHandle.ToIntPtr(GCHandle.Alloc(obj));
}
catch (Exception e)
{
ExceptionUtils.DebugPrintUnhandledException(e);
return IntPtr.Zero;
}
}
[UnmanagedCallersOnly]
internal static unsafe godot_bool CreateManagedForGodotObjectScriptInstance(IntPtr scriptPtr,
IntPtr godotObject,
godot_variant** args, int argCount)
{
try
{
// Performance is not critical here as this will be replaced with source generators.
Type scriptType = _scriptBridgeMap[scriptPtr];
var obj = (Object)FormatterServices.GetUninitializedObject(scriptType);
obj.NativePtr = godotObject;
var ctor = scriptType
.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(c => c.GetParameters().Length == argCount)
.FirstOrDefault();
if (ctor == null)
{
if (argCount == 0)
{
throw new MissingMemberException(
$"Cannot create script instance. The class '{scriptType.FullName}' does not define a parameterless constructor.");
}
else
{
throw new MissingMemberException(
$"The class '{scriptType.FullName}' does not define a constructor that takes x parameters.");
}
}
var parameters = ctor.GetParameters();
int paramCount = parameters.Length;
object[] invokeParams = new object[paramCount];
for (int i = 0; i < paramCount; i++)
{
invokeParams[i] = Marshaling.variant_to_mono_object_of_type(
args[i], parameters[i].ParameterType);
}
ctor.Invoke(obj, invokeParams);
return true.ToGodotBool();
}
catch (Exception e)
{
ExceptionUtils.DebugPrintUnhandledException(e);
return false.ToGodotBool();
}
}
[UnmanagedCallersOnly]
internal static unsafe void GetScriptNativeName(IntPtr scriptPtr, godot_string_name* outRes)
{
try
{
// Performance is not critical here as this will be replaced with source generators.
if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType))
{
*outRes = default;
return;
}
var native = Object.InternalGetClassNativeBase(scriptType);
var field = native?.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static |
BindingFlags.Public | BindingFlags.NonPublic);
if (field == null)
{
*outRes = default;
return;
}
var nativeName = (StringName)field.GetValue(null);
if (nativeName == null)
{
*outRes = default;
return;
}
*outRes = NativeFuncs.godotsharp_string_name_new_copy(nativeName.NativeValue);
}
catch (Exception e)
{
ExceptionUtils.DebugUnhandledException(e);
*outRes = default;
}
}
[UnmanagedCallersOnly]
internal static void SetGodotObjectPtr(IntPtr gcHandlePtr, IntPtr newPtr)
{
try
{
var target = (Object)GCHandle.FromIntPtr(gcHandlePtr).Target;
if (target != null)
target.NativePtr = newPtr;
}
catch (Exception e)
{
ExceptionUtils.DebugUnhandledException(e);
}
}
private static unsafe Type TypeGetProxyClass(godot_string_name* nativeTypeName)
{
// Performance is not critical here as this will be replaced with a generated dictionary.
using var stringName = StringName.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_string_name_new_copy(nativeTypeName));
string nativeTypeNameStr = stringName.ToString();
if (nativeTypeNameStr[0] == '_')
nativeTypeNameStr = nativeTypeNameStr.Substring(1);
Type wrapperType = typeof(Object).Assembly.GetType("Godot." + nativeTypeNameStr);
if (wrapperType == null)
{
wrapperType = AppDomain.CurrentDomain.GetAssemblies()
.First(a => a.GetName().Name == "GodotSharpEditor")
.GetType("Godot." + nativeTypeNameStr);
}
static bool IsStatic(Type type) => type.IsAbstract && type.IsSealed;
if (wrapperType != null && IsStatic(wrapperType))
{
// A static class means this is a Godot singleton class. If an instance is needed we use Godot.Object.
return typeof(Object);
}
return wrapperType;
}
// Called from GodotPlugins
// ReSharper disable once UnusedMember.Local
private static void LookupScriptsInAssembly(Assembly assembly)
{
static void LookupScriptForClass(Type type)
{
var scriptPathAttr = type.GetCustomAttributes(inherit: false)
.OfType<ScriptPathAttribute>()
.FirstOrDefault();
if (scriptPathAttr == null)
return;
_scriptLookupMap[scriptPathAttr.Path] = new ScriptLookupInfo(type.Namespace, type.Name, type);
}
var assemblyHasScriptsAttr = assembly.GetCustomAttributes(inherit: false)
.OfType<AssemblyHasScriptsAttribute>()
.FirstOrDefault();
if (assemblyHasScriptsAttr == null)
return;
if (assemblyHasScriptsAttr.RequiresLookup)
{
// This is supported for scenarios where specifying all types would be cumbersome,
// such as when disabling C# source generators (for whatever reason) or when using a
// language other than C# that has nothing similar to source generators to automate it.
var typeOfGodotObject = typeof(Object);
foreach (var type in assembly.GetTypes())
{
if (type.IsNested)
continue;
if (!typeOfGodotObject.IsAssignableFrom(type))
continue;
LookupScriptForClass(type);
}
}
else
{
// This is the most likely scenario as we use C# source generators
var scriptTypes = assemblyHasScriptsAttr.ScriptTypes;
if (scriptTypes != null)
{
for (int i = 0; i < scriptTypes.Length; i++)
{
LookupScriptForClass(scriptTypes[i]);
}
}
}
}
[UnmanagedCallersOnly]
internal static unsafe void RaiseEventSignal(IntPtr ownerGCHandlePtr,
godot_string_name* eventSignalName, godot_variant** args, int argCount, godot_bool* outOwnerIsNull)
{
try
{
var owner = (Object)GCHandle.FromIntPtr(ownerGCHandlePtr).Target;
if (owner == null)
{
*outOwnerIsNull = true.ToGodotBool();
return;
}
*outOwnerIsNull = false.ToGodotBool();
owner.InternalRaiseEventSignal(eventSignalName, args, argCount);
}
catch (Exception e)
{
ExceptionUtils.DebugPrintUnhandledException(e);
*outOwnerIsNull = false.ToGodotBool();
}
}
[UnmanagedCallersOnly]
internal static unsafe void GetScriptSignalList(IntPtr scriptPtr, godot_dictionary* outRetSignals)
{
try
{
// Performance is not critical here as this will be replaced with source generators.
using var signals = new Dictionary();
Type top = _scriptBridgeMap[scriptPtr];
Type native = Object.InternalGetClassNativeBase(top);
while (top != null && top != native)
{
// Legacy signals
foreach (var signalDelegate in top
.GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public)
.Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType))
.Where(@delegate => @delegate.GetCustomAttributes().OfType<SignalAttribute>().Any()))
{
var invokeMethod = signalDelegate.GetMethod("Invoke");
if (invokeMethod == null)
throw new MissingMethodException(signalDelegate.FullName, "Invoke");
var signalParams = new Collections.Array();
foreach (var parameters in invokeMethod.GetParameters())
{
var paramType = Marshaling.managed_to_variant_type(
parameters.ParameterType, out bool nilIsVariant);
signalParams.Add(new Dictionary()
{
{ "name", parameters.Name },
{ "type", paramType },
{ "nil_is_variant", nilIsVariant }
});
}
signals.Add(signalDelegate.Name, signalParams);
}
// Event signals
var foundEventSignals = top.GetEvents(
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public)
.Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any())
.Select(ev => ev.Name);
var fields = top.GetFields(
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
foreach (var eventSignalField in fields
.Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType))
.Where(f => foundEventSignals.Contains(f.Name)))
{
var delegateType = eventSignalField.FieldType;
var invokeMethod = delegateType.GetMethod("Invoke");
if (invokeMethod == null)
throw new MissingMethodException(delegateType.FullName, "Invoke");
var signalParams = new Collections.Array();
foreach (var parameters in invokeMethod.GetParameters())
{
var paramType = Marshaling.managed_to_variant_type(
parameters.ParameterType, out bool nilIsVariant);
signalParams.Add(new Dictionary()
{
{ "name", parameters.Name },
{ "type", paramType },
{ "nil_is_variant", nilIsVariant }
});
}
signals.Add(eventSignalField.Name, signalParams);
}
top = top.BaseType;
}
*outRetSignals = NativeFuncs.godotsharp_dictionary_new_copy(signals.NativeValue);
}
catch (Exception e)
{
ExceptionUtils.DebugUnhandledException(e);
*outRetSignals = NativeFuncs.godotsharp_dictionary_new();
}
}
[UnmanagedCallersOnly]
internal static unsafe godot_bool HasScriptSignal(IntPtr scriptPtr, godot_string* signalName)
{
try
{
// Performance is not critical here as this will be replaced with source generators.
using var signals = new Dictionary();
string signalNameStr = Marshaling.mono_string_from_godot(*signalName);
Type top = _scriptBridgeMap[scriptPtr];
Type native = Object.InternalGetClassNativeBase(top);
while (top != null && top != native)
{
// Legacy signals
if (top
.GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public)
.Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType))
.Where(@delegate => @delegate.GetCustomAttributes().OfType<SignalAttribute>().Any())
.Any(signalDelegate => signalDelegate.Name == signalNameStr)
)
{
return true.ToGodotBool();
}
// Event signals
if (top.GetEvents(
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public)
.Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any())
.Any(eventSignal => eventSignal.Name == signalNameStr)
)
{
return true.ToGodotBool();
}
top = top.BaseType;
}
return false.ToGodotBool();
}
catch (Exception e)
{
ExceptionUtils.DebugUnhandledException(e);
return false.ToGodotBool();
}
}
[UnmanagedCallersOnly]
internal static unsafe godot_bool HasMethodUnknownParams(IntPtr scriptPtr, godot_string* method,
godot_bool deep)
{
try
{
// Performance is not critical here as this will be replaced with source generators.
if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType))
return false.ToGodotBool();
string methodStr = Marshaling.mono_string_from_godot(*method);
if (deep.ToBool())
{
Type top = scriptType;
Type native = Object.InternalGetClassNativeBase(scriptType);
while (top != null && top != native)
{
var methodInfo = top.GetMethod(methodStr,
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
if (methodInfo != null)
return true.ToGodotBool();
top = top.BaseType;
}
top = native;
Type typeOfSystemObject = typeof(System.Object);
while (top != null && top != typeOfSystemObject)
{
bool found = top.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public)
.Where(m => m.GetCustomAttributes(false).OfType<GodotMethodAttribute>()
.Where(a => a.MethodName == methodStr)
.Any())
.Any();
if (found)
return true.ToGodotBool();
top = top.BaseType;
}
return false.ToGodotBool();
}
else
{
var methodInfo = scriptType.GetMethod(methodStr, BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
return (methodInfo != null).ToGodotBool();
}
}
catch (Exception e)
{
ExceptionUtils.DebugUnhandledException(e);
return false.ToGodotBool();
}
}
[UnmanagedCallersOnly]
internal static godot_bool ScriptIsOrInherits(IntPtr scriptPtr, IntPtr scriptPtrMaybeBase)
{
try
{
if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType))
return false.ToGodotBool();
if (!_scriptBridgeMap.TryGetValue(scriptPtrMaybeBase, out var maybeBaseType))
return false.ToGodotBool();
return (scriptType == maybeBaseType || maybeBaseType.IsAssignableFrom(scriptType)).ToGodotBool();
}
catch (Exception e)
{
ExceptionUtils.DebugUnhandledException(e);
return false.ToGodotBool();
}
}
[UnmanagedCallersOnly]
internal static unsafe godot_bool AddScriptBridge(IntPtr scriptPtr, godot_string* scriptPath)
{
try
{
string scriptPathStr = Marshaling.mono_string_from_godot(*scriptPath);
if (!_scriptLookupMap.TryGetValue(scriptPathStr, out var lookupInfo))
return false.ToGodotBool();
_scriptBridgeMap.Add(scriptPtr, lookupInfo.ScriptType);
return true.ToGodotBool();
}
catch (Exception e)
{
ExceptionUtils.DebugUnhandledException(e);
return false.ToGodotBool();
}
}
internal static void AddScriptBridgeWithType(IntPtr scriptPtr, Type scriptType)
=> _scriptBridgeMap.Add(scriptPtr, scriptType);
[UnmanagedCallersOnly]
internal static void RemoveScriptBridge(IntPtr scriptPtr)
{
try
{
_scriptBridgeMap.Remove(scriptPtr);
}
catch (Exception e)
{
ExceptionUtils.DebugUnhandledException(e);
}
}
[UnmanagedCallersOnly]
internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_bool* outTool,
godot_dictionary* outRpcFunctionsDest)
{
try
{
// Performance is not critical here as this will be replaced with source generators.
var scriptType = _scriptBridgeMap[scriptPtr];
*outTool = scriptType.GetCustomAttributes(inherit: false)
.OfType<ToolAttribute>()
.Any().ToGodotBool();
if (!(*outTool).ToBool() && scriptType.IsNested)
{
*outTool = (scriptType.DeclaringType?.GetCustomAttributes(inherit: false)
.OfType<ToolAttribute>()
.Any() ?? false).ToGodotBool();
}
if (!(*outTool).ToBool() && scriptType.Assembly.GetName().Name == "GodotTools")
*outTool = true.ToGodotBool();
// RPC functions
static MultiplayerAPI.RPCMode MemberGetRpcMode(MemberInfo memberInfo)
{
if (memberInfo.GetCustomAttributes(inherit: false).OfType<RemoteAttribute>().Any())
return MultiplayerAPI.RPCMode.Remote;
if (memberInfo.GetCustomAttributes(inherit: false).OfType<MasterAttribute>().Any())
return MultiplayerAPI.RPCMode.Master;
if (memberInfo.GetCustomAttributes(inherit: false).OfType<PuppetAttribute>().Any())
return MultiplayerAPI.RPCMode.Puppet;
return MultiplayerAPI.RPCMode.Disabled;
}
Dictionary<string, Dictionary> rpcFunctions = new();
Type top = _scriptBridgeMap[scriptPtr];
Type native = Object.InternalGetClassNativeBase(top);
while (top != null && top != native)
{
foreach (var method in top.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public))
{
if (method.IsStatic)
continue;
string methodName = method.Name;
if (rpcFunctions.ContainsKey(methodName))
continue;
var rpcMode = MemberGetRpcMode(method);
if (rpcMode == MultiplayerAPI.RPCMode.Disabled)
continue;
var rpcConfig = new Dictionary();
rpcConfig["rpc_mode"] = (int)rpcMode;
// TODO Transfer mode, channel
rpcConfig["transfer_mode"] = (int)MultiplayerPeer.TransferModeEnum.Reliable;
rpcConfig["channel"] = 0;
rpcFunctions.Add(methodName, rpcConfig);
}
top = top.BaseType;
}
*outRpcFunctionsDest =
NativeFuncs.godotsharp_dictionary_new_copy(((Dictionary)rpcFunctions).NativeValue);
}
catch (Exception e)
{
ExceptionUtils.DebugUnhandledException(e);
*outTool = false.ToGodotBool();
*outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new();
}
}
[UnmanagedCallersOnly]
internal static unsafe godot_bool SwapGCHandleForType(IntPtr oldGCHandlePtr, IntPtr* outNewGCHandlePtr,
godot_bool createWeak)
{
try
{
var oldGCHandle = GCHandle.FromIntPtr(oldGCHandlePtr);
object target = oldGCHandle.Target;
if (target == null)
{
*outNewGCHandlePtr = IntPtr.Zero;
return false.ToGodotBool(); // Called after the managed side was collected, so nothing to do here
}
// Release the current weak handle and replace it with a strong handle.
var newGCHandle = GCHandle.Alloc(target,
createWeak.ToBool() ? GCHandleType.Weak : GCHandleType.Normal);
*outNewGCHandlePtr = GCHandle.ToIntPtr(newGCHandle);
return true.ToGodotBool();
}
catch (Exception e)
{
ExceptionUtils.DebugUnhandledException(e);
*outNewGCHandlePtr = IntPtr.Zero;
return false.ToGodotBool();
}
}
}
}

View File

@ -1,7 +1,9 @@
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using Godot.NativeInterop;
namespace Godot
{
@ -19,13 +21,23 @@ namespace Godot
sb.Append(" ");
}
public static void InstallTraceListener()
[UnmanagedCallersOnly]
internal static void InstallTraceListener()
{
Trace.Listeners.Clear();
Trace.Listeners.Add(new GodotTraceListener());
try
{
Trace.Listeners.Clear();
Trace.Listeners.Add(new GodotTraceListener());
}
catch (Exception e)
{
ExceptionUtils.DebugPrintUnhandledException(e);
ExceptionUtils.PushError("Failed to install 'System.Diagnostics.Trace' listener.");
}
}
public static void GetStackFrameInfo(StackFrame frame, out string fileName, out int fileLineNumber, out string methodDecl)
public static void GetStackFrameInfo(StackFrame frame, out string fileName, out int fileLineNumber,
out string methodDecl)
{
fileName = frame.GetFileName();
fileLineNumber = frame.GetFileLineNumber();

View File

@ -11,42 +11,56 @@ namespace Godot
{
internal static class DelegateUtils
{
// TODO: Move somewhere else once we need to for things other than delegates
internal static void FreeGCHandle(IntPtr delegateGCHandle)
=> GCHandle.FromIntPtr(delegateGCHandle).Free();
internal static bool DelegateEquals(IntPtr delegateGCHandleA, IntPtr delegateGCHandleB)
[UnmanagedCallersOnly]
internal static godot_bool DelegateEquals(IntPtr delegateGCHandleA, IntPtr delegateGCHandleB)
{
var @delegateA = (Delegate)GCHandle.FromIntPtr(delegateGCHandleA).Target;
var @delegateB = (Delegate)GCHandle.FromIntPtr(delegateGCHandleB).Target;
return @delegateA == @delegateB;
try
{
var @delegateA = (Delegate)GCHandle.FromIntPtr(delegateGCHandleA).Target;
var @delegateB = (Delegate)GCHandle.FromIntPtr(delegateGCHandleB).Target;
return (@delegateA == @delegateB).ToGodotBool();
}
catch (Exception e)
{
ExceptionUtils.DebugUnhandledException(e);
return false.ToGodotBool();
}
}
internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, godot_variant** args, uint argc, godot_variant* ret)
[UnmanagedCallersOnly]
internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, godot_variant** args, uint argc,
godot_variant* outRet)
{
// TODO: Optimize
var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target;
var managedArgs = new object[argc];
var parameterInfos = @delegate.Method.GetParameters();
var paramsLength = parameterInfos.Length;
if (argc != paramsLength)
try
{
throw new InvalidOperationException(
$"The delegate expects {paramsLength} arguments, but received {argc}.");
}
// TODO: Optimize
var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target;
var managedArgs = new object[argc];
for (uint i = 0; i < argc; i++)
var parameterInfos = @delegate!.Method.GetParameters();
var paramsLength = parameterInfos.Length;
if (argc != paramsLength)
{
throw new InvalidOperationException(
$"The delegate expects {paramsLength} arguments, but received {argc}.");
}
for (uint i = 0; i < argc; i++)
{
managedArgs[i] = Marshaling.variant_to_mono_object_of_type(
args[i], parameterInfos[i].ParameterType);
}
object invokeRet = @delegate.DynamicInvoke(managedArgs);
*outRet = Marshaling.mono_object_to_variant(invokeRet);
}
catch (Exception e)
{
managedArgs[i] = Marshaling.variant_to_mono_object_of_type(
args[i], parameterInfos[i].ParameterType);
ExceptionUtils.DebugPrintUnhandledException(e);
*outRet = default;
}
object invokeRet = @delegate.DynamicInvoke(managedArgs);
*ret = Marshaling.mono_object_to_variant(invokeRet);
}
// TODO: Check if we should be using BindingFlags.DeclaredOnly (would give better reflection performance).
@ -260,7 +274,8 @@ namespace Godot
}
}
private static bool TryDeserializeDelegateWithGCHandle(Collections.Array serializedData, out IntPtr delegateGCHandle)
private static bool TryDeserializeDelegateWithGCHandle(Collections.Array serializedData,
out IntPtr delegateGCHandle)
{
bool res = TryDeserializeDelegate(serializedData, out Delegate @delegate);
delegateGCHandle = GCHandle.ToIntPtr(GCHandle.Alloc(@delegate));
@ -368,7 +383,8 @@ namespace Godot
int valueBufferLength = reader.ReadInt32();
byte[] valueBuffer = reader.ReadBytes(valueBufferLength);
FieldInfo fieldInfo = targetType.GetField(name, BindingFlags.Instance | BindingFlags.Public);
FieldInfo fieldInfo =
targetType.GetField(name, BindingFlags.Instance | BindingFlags.Public);
fieldInfo?.SetValue(recreatedTarget, GD.Bytes2Var(valueBuffer));
}

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections;
using System.Runtime.CompilerServices;
using Godot.NativeInterop;
using System.Diagnostics.CodeAnalysis;
@ -16,14 +15,14 @@ namespace Godot.Collections
IDictionary,
IDisposable
{
internal godot_dictionary NativeValue;
public godot_dictionary NativeValue;
/// <summary>
/// Constructs a new empty <see cref="Dictionary"/>.
/// </summary>
public Dictionary()
{
godot_icall_Dictionary_Ctor(out NativeValue);
NativeValue = NativeFuncs.godotsharp_dictionary_new();
}
/// <summary>
@ -77,7 +76,7 @@ namespace Godot.Collections
public Dictionary Duplicate(bool deep = false)
{
godot_dictionary newDictionary;
godot_icall_Dictionary_Duplicate(ref NativeValue, deep, out newDictionary);
NativeFuncs.godotsharp_dictionary_duplicate(ref NativeValue, deep.ToGodotBool(), out newDictionary);
return CreateTakingOwnershipOfDisposableValue(newDictionary);
}
@ -91,7 +90,7 @@ namespace Godot.Collections
get
{
godot_array keysArray;
godot_icall_Dictionary_Keys(ref NativeValue, out keysArray);
NativeFuncs.godotsharp_dictionary_keys(ref NativeValue, out keysArray);
return Array.CreateTakingOwnershipOfDisposableValue(keysArray);
}
}
@ -104,7 +103,7 @@ namespace Godot.Collections
get
{
godot_array valuesArray;
godot_icall_Dictionary_Values(ref NativeValue, out valuesArray);
NativeFuncs.godotsharp_dictionary_values(ref NativeValue, out valuesArray);
return Array.CreateTakingOwnershipOfDisposableValue(valuesArray);
}
}
@ -112,10 +111,15 @@ namespace Godot.Collections
private (Array keys, Array values, int count) GetKeyValuePairs()
{
godot_array keysArray;
godot_array valuesArray;
int count = godot_icall_Dictionary_KeyValuePairs(ref NativeValue, out keysArray, out valuesArray);
NativeFuncs.godotsharp_dictionary_keys(ref NativeValue, out keysArray);
var keys = Array.CreateTakingOwnershipOfDisposableValue(keysArray);
godot_array valuesArray;
NativeFuncs.godotsharp_dictionary_keys(ref NativeValue, out valuesArray);
var values = Array.CreateTakingOwnershipOfDisposableValue(valuesArray);
int count = NativeFuncs.godotsharp_dictionary_count(ref NativeValue);
return (keys, values, count);
}
@ -127,18 +131,28 @@ namespace Godot.Collections
/// Returns the object at the given <paramref name="key"/>.
/// </summary>
/// <value>The object at the given <paramref name="key"/>.</value>
public object this[object key]
public unsafe object this[object key]
{
get
{
godot_icall_Dictionary_GetValue(ref NativeValue, key, out godot_variant value);
unsafe
using godot_variant variantKey = Marshaling.mono_object_to_variant(key);
if (NativeFuncs.godotsharp_dictionary_try_get_value(ref NativeValue, &variantKey,
out godot_variant value).ToBool())
{
using (value)
return Marshaling.variant_to_mono_object(&value);
}
else
{
throw new KeyNotFoundException();
}
}
set
{
using godot_variant variantKey = Marshaling.mono_object_to_variant(key);
using godot_variant variantValue = Marshaling.mono_object_to_variant(value);
NativeFuncs.godotsharp_dictionary_set_value(ref NativeValue, &variantKey, &variantValue);
}
set => godot_icall_Dictionary_SetValue(ref NativeValue, key, value);
}
/// <summary>
@ -147,19 +161,32 @@ namespace Godot.Collections
/// </summary>
/// <param name="key">The key at which to add the object.</param>
/// <param name="value">The object to add.</param>
public void Add(object key, object value) => godot_icall_Dictionary_Add(ref NativeValue, key, value);
public unsafe void Add(object key, object value)
{
using godot_variant variantKey = Marshaling.mono_object_to_variant(key);
if (NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey).ToBool())
throw new ArgumentException("An element with the same key already exists", nameof(key));
using godot_variant variantValue = Marshaling.mono_object_to_variant(value);
NativeFuncs.godotsharp_dictionary_add(ref NativeValue, &variantKey, &variantValue);
}
/// <summary>
/// Erases all items from this <see cref="Dictionary"/>.
/// </summary>
public void Clear() => godot_icall_Dictionary_Clear(ref NativeValue);
public void Clear() => NativeFuncs.godotsharp_dictionary_clear(ref NativeValue);
/// <summary>
/// Checks if this <see cref="Dictionary"/> contains the given key.
/// </summary>
/// <param name="key">The key to look for.</param>
/// <returns>Whether or not this dictionary contains the given key.</returns>
public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(ref NativeValue, key);
public unsafe bool Contains(object key)
{
using godot_variant variantKey = Marshaling.mono_object_to_variant(key);
return NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey).ToBool();
}
/// <summary>
/// Gets an enumerator for this <see cref="Dictionary"/>.
@ -171,7 +198,11 @@ namespace Godot.Collections
/// Removes an element from this <see cref="Dictionary"/> by key.
/// </summary>
/// <param name="key">The key of the element to remove.</param>
public void Remove(object key) => godot_icall_Dictionary_RemoveKey(ref NativeValue, key);
public unsafe void Remove(object key)
{
using godot_variant variantKey = Marshaling.mono_object_to_variant(key);
NativeFuncs.godotsharp_dictionary_remove_key(ref NativeValue, &variantKey);
}
// ICollection
@ -184,7 +215,7 @@ namespace Godot.Collections
/// This is also known as the size or length of the dictionary.
/// </summary>
/// <returns>The number of elements.</returns>
public int Count => godot_icall_Dictionary_Count(ref NativeValue);
public int Count => NativeFuncs.godotsharp_dictionary_count(ref NativeValue);
/// <summary>
/// Copies the elements of this <see cref="Dictionary"/> to the given
@ -198,12 +229,14 @@ namespace Godot.Collections
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
if (index < 0)
throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension.");
throw new ArgumentOutOfRangeException(nameof(index),
"Number was less than the array's lower bound in the first dimension.");
var (keys, values, count) = GetKeyValuePairs();
if (array.Length < (index + count))
throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
throw new ArgumentException(
"Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
for (int i = 0; i < count; i++)
{
@ -241,15 +274,23 @@ namespace Godot.Collections
{
UpdateEntry();
}
return entry;
}
}
private void UpdateEntry()
private unsafe void UpdateEntry()
{
dirty = false;
godot_icall_Dictionary_KeyValuePairAt(ref dictionary.NativeValue, index, out object key, out object value);
entry = new DictionaryEntry(key, value);
NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref dictionary.NativeValue, index,
out godot_variant key,
out godot_variant value);
using (key)
using (value)
{
entry = new DictionaryEntry(Marshaling.variant_to_mono_object(&key),
Marshaling.variant_to_mono_object(&value));
}
}
public object Key => Entry.Key;
@ -274,61 +315,12 @@ namespace Godot.Collections
/// Converts this <see cref="Dictionary"/> to a string.
/// </summary>
/// <returns>A string representation of this dictionary.</returns>
public override string ToString()
public override unsafe string ToString()
{
return godot_icall_Dictionary_ToString(ref NativeValue);
using godot_string str = default;
NativeFuncs.godotsharp_dictionary_to_string(ref NativeValue, &str);
return Marshaling.mono_string_from_godot(str);
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Dictionary_Ctor(out godot_dictionary dest);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Dictionary_GetValue(ref godot_dictionary ptr, object key, out godot_variant value);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Dictionary_SetValue(ref godot_dictionary ptr, object key, object value);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Dictionary_Keys(ref godot_dictionary ptr, out godot_array dest);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Dictionary_Values(ref godot_dictionary ptr, out godot_array dest);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int godot_icall_Dictionary_KeyValuePairs(ref godot_dictionary ptr, out godot_array keys, out godot_array values);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Dictionary_KeyValuePairAt(ref godot_dictionary ptr, int index, out object key, out object value);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Dictionary_Add(ref godot_dictionary ptr, object key, object value);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int godot_icall_Dictionary_Count(ref godot_dictionary ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Dictionary_Clear(ref godot_dictionary ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool godot_icall_Dictionary_Contains(ref godot_dictionary ptr, object key, object value);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool godot_icall_Dictionary_ContainsKey(ref godot_dictionary ptr, object key);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Dictionary_Duplicate(ref godot_dictionary ptr, bool deep, out godot_dictionary dest);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool godot_icall_Dictionary_RemoveKey(ref godot_dictionary ptr, object key);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool godot_icall_Dictionary_Remove(ref godot_dictionary ptr, object key, object value);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool godot_icall_Dictionary_TryGetValue(ref godot_dictionary ptr, object key, out godot_variant value);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern string godot_icall_Dictionary_ToString(ref godot_dictionary ptr);
}
internal interface IGenericGodotDictionary
@ -401,7 +393,8 @@ namespace Godot.Collections
}
// Explicit name to make it very clear
internal static Dictionary<TKey, TValue> CreateTakingOwnershipOfDisposableValue(godot_dictionary nativeValueToOwn)
internal static Dictionary<TKey, TValue> CreateTakingOwnershipOfDisposableValue(
godot_dictionary nativeValueToOwn)
=> new Dictionary<TKey, TValue>(Dictionary.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn));
/// <summary>
@ -433,11 +426,19 @@ namespace Godot.Collections
{
get
{
Dictionary.godot_icall_Dictionary_GetValue(ref _underlyingDict.NativeValue, key, out godot_variant value);
unsafe
{
using (value)
return (TValue)Marshaling.variant_to_mono_object_of_type(&value, TypeOfValues);
using godot_variant variantKey = Marshaling.mono_object_to_variant(key);
if (NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue,
&variantKey, out godot_variant value).ToBool())
{
using (value)
return (TValue)Marshaling.variant_to_mono_object_of_type(&value, TypeOfValues);
}
else
{
throw new KeyNotFoundException();
}
}
}
set => _underlyingDict[key] = value;
@ -451,7 +452,7 @@ namespace Godot.Collections
get
{
godot_array keyArray;
Dictionary.godot_icall_Dictionary_Keys(ref _underlyingDict.NativeValue, out keyArray);
NativeFuncs.godotsharp_dictionary_keys(ref _underlyingDict.NativeValue, out keyArray);
return Array<TKey>.CreateTakingOwnershipOfDisposableValue(keyArray);
}
}
@ -464,15 +465,22 @@ namespace Godot.Collections
get
{
godot_array valuesArray;
Dictionary.godot_icall_Dictionary_Values(ref _underlyingDict.NativeValue, out valuesArray);
NativeFuncs.godotsharp_dictionary_values(ref _underlyingDict.NativeValue, out valuesArray);
return Array<TValue>.CreateTakingOwnershipOfDisposableValue(valuesArray);
}
}
private KeyValuePair<TKey, TValue> GetKeyValuePair(int index)
private unsafe KeyValuePair<TKey, TValue> GetKeyValuePair(int index)
{
Dictionary.godot_icall_Dictionary_KeyValuePairAt(ref _underlyingDict.NativeValue, index, out object key, out object value);
return new KeyValuePair<TKey, TValue>((TKey)key, (TValue)value);
NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref _underlyingDict.NativeValue, index,
out godot_variant key,
out godot_variant value);
using (key)
using (value)
{
return new KeyValuePair<TKey, TValue>((TKey)Marshaling.variant_to_mono_object(&key),
(TValue)Marshaling.variant_to_mono_object(&value));
}
}
/// <summary>
@ -500,9 +508,10 @@ namespace Godot.Collections
/// Removes an element from this <see cref="Dictionary{TKey, TValue}"/> by key.
/// </summary>
/// <param name="key">The key of the element to remove.</param>
public bool Remove(TKey key)
public unsafe bool Remove(TKey key)
{
return Dictionary.godot_icall_Dictionary_RemoveKey(ref _underlyingDict.NativeValue, key);
using godot_variant variantKey = Marshaling.mono_object_to_variant(key);
return NativeFuncs.godotsharp_dictionary_remove_key(ref _underlyingDict.NativeValue, &variantKey).ToBool();
}
/// <summary>
@ -511,18 +520,17 @@ namespace Godot.Collections
/// <param name="key">The key of the element to get.</param>
/// <param name="value">The value at the given <paramref name="key"/>.</param>
/// <returns>If an object was found for the given <paramref name="key"/>.</returns>
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
public unsafe bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
{
bool found = Dictionary.godot_icall_Dictionary_TryGetValue(ref _underlyingDict.NativeValue, key, out godot_variant retValue);
using godot_variant variantKey = Marshaling.mono_object_to_variant(key);
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue,
&variantKey, out godot_variant retValue).ToBool();
unsafe
using (retValue)
{
using (retValue)
{
value = found ?
(TValue)Marshaling.variant_to_mono_object_of_type(&retValue, TypeOfValues) :
default;
}
value = found ?
(TValue)Marshaling.variant_to_mono_object_of_type(&retValue, TypeOfValues) :
default;
}
return found;
@ -552,9 +560,19 @@ namespace Godot.Collections
_underlyingDict.Clear();
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
unsafe bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
return _underlyingDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value));
using godot_variant variantKey = Marshaling.mono_object_to_variant(item.Key);
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue,
&variantKey, out godot_variant retValue).ToBool();
using (retValue)
{
if (!found)
return false;
return NativeFuncs.godotsharp_variant_equals(&variantKey, &retValue).ToBool();
}
}
/// <summary>
@ -569,12 +587,14 @@ namespace Godot.Collections
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
if (arrayIndex < 0)
throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension.");
throw new ArgumentOutOfRangeException(nameof(arrayIndex),
"Number was less than the array's lower bound in the first dimension.");
int count = Count;
if (array.Length < (arrayIndex + count))
throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
throw new ArgumentException(
"Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
for (int i = 0; i < count; i++)
{
@ -583,9 +603,25 @@ namespace Godot.Collections
}
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
unsafe bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
return Dictionary.godot_icall_Dictionary_Remove(ref _underlyingDict.NativeValue, item.Key, item.Value);
using godot_variant variantKey = Marshaling.mono_object_to_variant(item.Key);
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref _underlyingDict.NativeValue,
&variantKey, out godot_variant retValue).ToBool();
using (retValue)
{
if (!found)
return false;
if (NativeFuncs.godotsharp_variant_equals(&variantKey, &retValue).ToBool())
{
return NativeFuncs.godotsharp_dictionary_remove_key(
ref _underlyingDict.NativeValue, &variantKey).ToBool();
}
return false;
}
}
// IEnumerable<KeyValuePair<TKey, TValue>>

View File

@ -1,13 +1,26 @@
using System.Runtime.CompilerServices;
using System;
using System.Runtime.InteropServices;
using Godot.NativeInterop;
namespace Godot
{
public static class Dispatcher
{
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern GodotTaskScheduler godot_icall_DefaultGodotTaskScheduler();
internal static GodotTaskScheduler DefaultGodotTaskScheduler;
public static GodotSynchronizationContext SynchronizationContext =>
godot_icall_DefaultGodotTaskScheduler().Context;
[UnmanagedCallersOnly]
internal static void InitializeDefaultGodotTaskScheduler()
{
try
{
DefaultGodotTaskScheduler = new GodotTaskScheduler();
}
catch (Exception e)
{
ExceptionUtils.DebugUnhandledException(e);
}
}
public static GodotSynchronizationContext SynchronizationContext => DefaultGodotTaskScheduler.Context;
}
}

View File

@ -1,212 +0,0 @@
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
namespace Godot
{
/// <summary>
/// Represents an <see cref="Godot.Object"/> whose members can be dynamically accessed at runtime through the Variant API.
/// </summary>
/// <remarks>
/// <para>
/// The <see cref="Godot.DynamicGodotObject"/> class enables access to the Variant
/// members of a <see cref="Godot.Object"/> instance at runtime.
/// </para>
/// <para>
/// This allows accessing the class members using their original names in the engine as well as the members from the
/// script attached to the <see cref="Godot.Object"/>, regardless of the scripting language it was written in.
/// </para>
/// </remarks>
/// <example>
/// This sample shows how to use <see cref="Godot.DynamicGodotObject"/> to dynamically access the engine members of a <see cref="Godot.Object"/>.
/// <code>
/// dynamic sprite = GetNode("Sprite2D").DynamicGodotObject;
/// sprite.add_child(this);
///
/// if ((sprite.hframes * sprite.vframes) &gt; 0)
/// sprite.frame = 0;
/// </code>
/// </example>
/// <example>
/// This sample shows how to use <see cref="Godot.DynamicGodotObject"/> to dynamically access the members of the script attached to a <see cref="Godot.Object"/>.
/// <code>
/// dynamic childNode = GetNode("ChildNode").DynamicGodotObject;
///
/// if (childNode.print_allowed)
/// {
/// childNode.message = "Hello from C#";
/// childNode.print_message(3);
/// }
/// </code>
/// The <c>ChildNode</c> node has the following GDScript script attached:
/// <code>
/// // # ChildNode.gd
/// // var print_allowed = true
/// // var message = ""
/// //
/// // func print_message(times):
/// // for i in times:
/// // print(message)
/// </code>
/// </example>
public class DynamicGodotObject : DynamicObject
{
/// <summary>
/// Gets the <see cref="Godot.Object"/> associated with this <see cref="Godot.DynamicGodotObject"/>.
/// </summary>
public Object Value { get; }
/// <summary>
/// Initializes a new instance of the <see cref="Godot.DynamicGodotObject"/> class.
/// </summary>
/// <param name="godotObject">
/// The <see cref="Godot.Object"/> that will be associated with this <see cref="Godot.DynamicGodotObject"/>.
/// </param>
/// <exception cref="System.ArgumentNullException">
/// Thrown when the <paramref name="godotObject"/> parameter is null.
/// </exception>
public DynamicGodotObject(Object godotObject)
{
if (godotObject == null)
throw new ArgumentNullException(nameof(godotObject));
this.Value = godotObject;
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return godot_icall_DynamicGodotObject_SetMemberList(Object.GetPtr(Value));
}
public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result)
{
switch (binder.Operation)
{
case ExpressionType.Equal:
case ExpressionType.NotEqual:
if (binder.ReturnType == typeof(bool) || binder.ReturnType.IsAssignableFrom(typeof(bool)))
{
if (arg == null)
{
bool boolResult = Object.IsInstanceValid(Value);
if (binder.Operation == ExpressionType.Equal)
boolResult = !boolResult;
result = boolResult;
return true;
}
if (arg is Object other)
{
bool boolResult = (Value == other);
if (binder.Operation == ExpressionType.NotEqual)
boolResult = !boolResult;
result = boolResult;
return true;
}
}
break;
default:
// We're not implementing operators <, <=, >, and >= (LessThan, LessThanOrEqual, GreaterThan, GreaterThanOrEqual).
// These are used on the actual pointers in variant_op.cpp. It's better to let the user do that explicitly.
break;
}
return base.TryBinaryOperation(binder, arg, out result);
}
public override bool TryConvert(ConvertBinder binder, out object result)
{
if (binder.Type == typeof(Object))
{
result = Value;
return true;
}
if (typeof(Object).IsAssignableFrom(binder.Type))
{
// Throws InvalidCastException when the cast fails
result = Convert.ChangeType(Value, binder.Type);
return true;
}
return base.TryConvert(binder, out result);
}
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
if (indexes.Length == 1)
{
if (indexes[0] is string name)
{
return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), name, out result);
}
}
return base.TryGetIndex(binder, indexes, out result);
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), binder.Name, out result);
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
return godot_icall_DynamicGodotObject_InvokeMember(Object.GetPtr(Value), binder.Name, args, out result);
}
public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
{
if (indexes.Length == 1)
{
if (indexes[0] is string name)
{
return godot_icall_DynamicGodotObject_SetMember(Object.GetPtr(Value), name, value);
}
}
return base.TrySetIndex(binder, indexes, value);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
return godot_icall_DynamicGodotObject_SetMember(Object.GetPtr(Value), binder.Name, value);
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static string[] godot_icall_DynamicGodotObject_SetMemberList(IntPtr godotObject);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static bool godot_icall_DynamicGodotObject_InvokeMember(IntPtr godotObject, string name, object[] args, out object result);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static bool godot_icall_DynamicGodotObject_GetMember(IntPtr godotObject, string name, out object result);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static bool godot_icall_DynamicGodotObject_SetMember(IntPtr godotObject, string name, object value);
#region We don't override these methods
// Looks like this is not usable from C#
//public override bool TryCreateInstance(CreateInstanceBinder binder, object[] args, out object result);
// Object members cannot be deleted
//public override bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes);
//public override bool TryDeleteMember(DeleteMemberBinder binder);
// Invocation on the object itself, e.g.: obj(param)
//public override bool TryInvoke(InvokeBinder binder, object[] args, out object result);
// No unnary operations to handle
//public override bool TryUnaryOperation(UnaryOperationBinder binder, out object result);
#endregion
}
}

View File

@ -1,5 +1,5 @@
using System;
using System.Runtime.CompilerServices;
using Godot.NativeInterop;
namespace Godot
{
@ -12,10 +12,20 @@ namespace Godot
public static WeakRef WeakRef(Object obj)
{
return godot_icall_Object_weakref(Object.GetPtr(obj));
}
if (!IsInstanceValid(obj))
return null;
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static WeakRef godot_icall_Object_weakref(IntPtr obj);
using godot_ref weakRef = default;
unsafe
{
NativeFuncs.godotsharp_weakref(GetPtr(obj), &weakRef);
}
if (weakRef.IsNull)
return null;
return (WeakRef)InteropUtils.UnmanagedGetManaged(weakRef._reference);
}
}
}

View File

@ -1,4 +1,4 @@
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using Godot.Collections;
using Godot.NativeInterop;
@ -7,14 +7,58 @@ namespace Godot
{
public partial class SceneTree
{
public Array<T> GetNodesInGroup<T>(StringName group) where T : class
public unsafe Array<T> GetNodesInGroup<T>(StringName group) where T : class
{
godot_array array;
godot_icall_SceneTree_get_nodes_in_group_Generic(GetPtr(this), ref group.NativeValue, typeof(T), out array);
return Array<T>.CreateTakingOwnershipOfDisposableValue(array);
}
var array = GetNodesInGroup(group);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, ref godot_string_name group, Type elemType, out godot_array dest);
if (array.Count == 0)
return new Array<T>(array);
var typeOfT = typeof(T);
bool nativeBase = InternalIsClassNativeBase(typeOfT);
if (nativeBase)
{
// Native type
var field = typeOfT.GetField("NativeName",
BindingFlags.DeclaredOnly | BindingFlags.Static |
BindingFlags.Public | BindingFlags.NonPublic);
var nativeName = (StringName)field!.GetValue(null);
godot_string_name nativeNameAux = nativeName.NativeValue;
godot_array inputAux = array.NativeValue;
godot_array filteredArray;
NativeFuncs.godotsharp_array_filter_godot_objects_by_native(
&nativeNameAux, &inputAux, &filteredArray);
return Array<T>.CreateTakingOwnershipOfDisposableValue(filteredArray);
}
else
{
// Custom derived type
godot_array inputAux = array.NativeValue;
godot_array filteredArray;
NativeFuncs.godotsharp_array_filter_godot_objects_by_non_native(&inputAux, &filteredArray);
var filteredArrayWrapped = Array.CreateTakingOwnershipOfDisposableValue(filteredArray);
// Re-use first array as its size is the same or greater than the filtered one
var resWrapped = new Array<T>(array);
int j = 0;
for (int i = 0; i < filteredArrayWrapped.Count; i++)
{
if (filteredArrayWrapped[i] is T t)
{
resWrapped[j] = t;
j++;
}
}
// Remove trailing elements, since this was re-used
resWrapped.Resize(j);
return resWrapped;
}
}
}
}

View File

@ -5,7 +5,6 @@ using real_t = System.Single;
#endif
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Godot.NativeInterop;
// TODO: Add comments describing what this class does. It is not obvious.
@ -17,12 +16,17 @@ namespace Godot
public static unsafe object Bytes2Var(byte[] bytes, bool allowObjects = false)
{
using var varBytes = Marshaling.mono_array_to_PackedByteArray(bytes);
return godot_icall_GD_bytes2var(&varBytes, allowObjects);
using godot_variant ret = default;
NativeFuncs.godotsharp_bytes2var(&varBytes, allowObjects.ToGodotBool(), &ret);
return Marshaling.variant_to_mono_object(&ret);
}
public static object Convert(object what, Variant.Type type)
public static unsafe object Convert(object what, Variant.Type type)
{
return godot_icall_GD_convert(what, type);
using var whatVariant = Marshaling.mono_object_to_variant(what);
using godot_variant ret = default;
NativeFuncs.godotsharp_convert(&whatVariant, type, &ret);
return Marshaling.variant_to_mono_object(&ret);
}
public static real_t Db2Linear(real_t db)
@ -30,7 +34,7 @@ namespace Godot
return (real_t)Math.Exp(db * 0.11512925464970228420089957273422);
}
private static object[] GetPrintParams(object[] parameters)
private static string[] GetPrintParams(object[] parameters)
{
if (parameters == null)
{
@ -40,14 +44,15 @@ namespace Godot
return Array.ConvertAll(parameters, x => x?.ToString() ?? "null");
}
public static int Hash(object var)
public static unsafe int Hash(object var)
{
return godot_icall_GD_hash(var);
using var variant = Marshaling.mono_object_to_variant(var);
return NativeFuncs.godotsharp_hash(&variant);
}
public static Object InstanceFromId(ulong instanceId)
{
return godot_icall_GD_instance_from_id(instanceId);
return InteropUtils.UnmanagedGetManaged(NativeFuncs.godotsharp_instance_from_id(instanceId));
}
public static real_t Linear2Db(real_t linear)
@ -65,19 +70,23 @@ namespace Godot
return ResourceLoader.Load<T>(path);
}
public static void PushError(string message)
public static unsafe void PushError(string message)
{
godot_icall_GD_pusherror(message);
using var godotStr = Marshaling.mono_string_to_godot(message);
NativeFuncs.godotsharp_pusherror(&godotStr);
}
public static void PushWarning(string message)
public static unsafe void PushWarning(string message)
{
godot_icall_GD_pushwarning(message);
using var godotStr = Marshaling.mono_string_to_godot(message);
NativeFuncs.godotsharp_pushwarning(&godotStr);
}
public static void Print(params object[] what)
public static unsafe void Print(params object[] what)
{
godot_icall_GD_print(GetPrintParams(what));
string str = string.Concat(GetPrintParams(what));
using var godotStr = Marshaling.mono_string_to_godot(str);
NativeFuncs.godotsharp_print(&godotStr);
}
public static void PrintStack()
@ -85,54 +94,62 @@ namespace Godot
Print(System.Environment.StackTrace);
}
public static void PrintErr(params object[] what)
public static unsafe void PrintErr(params object[] what)
{
godot_icall_GD_printerr(GetPrintParams(what));
string str = string.Concat(GetPrintParams(what));
using var godotStr = Marshaling.mono_string_to_godot(str);
NativeFuncs.godotsharp_printerr(&godotStr);
}
public static void PrintRaw(params object[] what)
public static unsafe void PrintRaw(params object[] what)
{
godot_icall_GD_printraw(GetPrintParams(what));
string str = string.Concat(GetPrintParams(what));
using var godotStr = Marshaling.mono_string_to_godot(str);
NativeFuncs.godotsharp_printraw(&godotStr);
}
public static void PrintS(params object[] what)
public static unsafe void PrintS(params object[] what)
{
godot_icall_GD_prints(GetPrintParams(what));
string str = string.Join(' ', GetPrintParams(what));
using var godotStr = Marshaling.mono_string_to_godot(str);
NativeFuncs.godotsharp_prints(&godotStr);
}
public static void PrintT(params object[] what)
public static unsafe void PrintT(params object[] what)
{
godot_icall_GD_printt(GetPrintParams(what));
string str = string.Join('\t', GetPrintParams(what));
using var godotStr = Marshaling.mono_string_to_godot(str);
NativeFuncs.godotsharp_printt(&godotStr);
}
public static float Randf()
{
return godot_icall_GD_randf();
return NativeFuncs.godotsharp_randf();
}
public static uint Randi()
{
return godot_icall_GD_randi();
return NativeFuncs.godotsharp_randi();
}
public static void Randomize()
{
godot_icall_GD_randomize();
NativeFuncs.godotsharp_randomize();
}
public static double RandRange(double from, double to)
{
return godot_icall_GD_randf_range(from, to);
return NativeFuncs.godotsharp_randf_range(from, to);
}
public static int RandRange(int from, int to)
{
return godot_icall_GD_randi_range(from, to);
return NativeFuncs.godotsharp_randi_range(from, to);
}
public static uint RandFromSeed(ref ulong seed)
{
return godot_icall_GD_rand_seed(seed, out seed);
return NativeFuncs.godotsharp_rand_from_seed(seed, out seed);
}
public static IEnumerable<int> Range(int end)
@ -167,114 +184,45 @@ namespace Godot
public static void Seed(ulong seed)
{
godot_icall_GD_seed(seed);
NativeFuncs.godotsharp_seed(seed);
}
public static string Str(params object[] what)
public static unsafe string Str(params object[] what)
{
return godot_icall_GD_str(what);
using var whatGodotArray = Marshaling.mono_array_to_Array(what);
using godot_string ret = default;
NativeFuncs.godotsharp_str(&whatGodotArray, &ret);
return Marshaling.mono_string_from_godot(ret);
}
public static object Str2Var(string str)
public static unsafe object Str2Var(string str)
{
return godot_icall_GD_str2var(str);
}
public static bool TypeExists(StringName type)
{
return godot_icall_GD_type_exists(ref type.NativeValue);
using var godotStr = Marshaling.mono_string_to_godot(str);
using godot_variant ret = default;
NativeFuncs.godotsharp_str2var(&godotStr, &ret);
return Marshaling.variant_to_mono_object(&ret);
}
public static unsafe byte[] Var2Bytes(object var, bool fullObjects = false)
{
godot_packed_byte_array varBytes;
godot_icall_GD_var2bytes(var, fullObjects, &varBytes);
using var variant = Marshaling.mono_object_to_variant(var);
using godot_packed_byte_array varBytes = default;
NativeFuncs.godotsharp_var2bytes(&variant, fullObjects.ToGodotBool(), &varBytes);
using (varBytes)
{
return Marshaling.PackedByteArray_to_mono_array(&varBytes);
}
}
public static string Var2Str(object var)
public static unsafe string Var2Str(object var)
{
return godot_icall_GD_var2str(var);
using var variant = Marshaling.mono_object_to_variant(var);
using godot_string ret = default;
NativeFuncs.godotsharp_var2str(&variant, &ret);
return Marshaling.mono_string_from_godot(ret);
}
public static Variant.Type TypeToVariantType(Type type)
{
return godot_icall_TypeToVariantType(type);
return Marshaling.managed_to_variant_type(type, out bool _);
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern unsafe object godot_icall_GD_bytes2var(godot_packed_byte_array* bytes, bool allowObjects);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern object godot_icall_GD_convert(object what, Variant.Type type);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int godot_icall_GD_hash(object var);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern Object godot_icall_GD_instance_from_id(ulong instanceId);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_GD_print(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_GD_printerr(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_GD_printraw(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_GD_prints(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_GD_printt(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern float godot_icall_GD_randf();
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern uint godot_icall_GD_randi();
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_GD_randomize();
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern double godot_icall_GD_randf_range(double from, double to);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int godot_icall_GD_randi_range(int from, int to);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern uint godot_icall_GD_rand_seed(ulong seed, out ulong newSeed);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_GD_seed(ulong seed);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern string godot_icall_GD_str(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern object godot_icall_GD_str2var(string str);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool godot_icall_GD_type_exists(ref godot_string_name type);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern unsafe void godot_icall_GD_var2bytes(object what, bool fullObjects, godot_packed_byte_array* bytes);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern string godot_icall_GD_var2str(object var);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_GD_pusherror(string type);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_GD_pushwarning(string type);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern Variant.Type godot_icall_TypeToVariantType(Type type);
}
}

View File

@ -17,10 +17,7 @@ namespace Godot
public override void Fail(string message, string detailMessage)
{
GD.PrintErr("Assertion failed: ", message);
if (detailMessage != null)
{
GD.PrintErr(" Details: ", detailMessage);
}
GD.PrintErr(" Details: ", detailMessage);
try
{

View File

@ -0,0 +1,74 @@
using System;
namespace Godot.NativeInterop
{
internal static class ExceptionUtils
{
public static void PushError(string message)
{
GD.PushError(message);
}
private static void OnExceptionLoggerException(Exception loggerException, Exception exceptionToLog)
{
// This better not throw
PushError("Exception thrown when trying to log another exception...");
PushError("Exception:");
PushError(exceptionToLog.ToString());
PushError("Logger exception:");
PushError(loggerException.ToString());
}
public static void DebugPrintUnhandledException(Exception e)
{
try
{
// TODO Not implemented (debug_print_unhandled_exception)
GD.PushError(e.ToString());
}
catch (Exception unexpected)
{
OnExceptionLoggerException(unexpected, e);
}
}
public static void DebugSendUnhandledExceptionError(Exception e)
{
try
{
// TODO Not implemented (debug_send_unhandled_exception_error)
GD.PushError(e.ToString());
}
catch (Exception unexpected)
{
OnExceptionLoggerException(unexpected, e);
}
}
public static void DebugUnhandledException(Exception e)
{
try
{
// TODO Not implemented (debug_unhandled_exception)
GD.PushError(e.ToString());
}
catch (Exception unexpected)
{
OnExceptionLoggerException(unexpected, e);
}
}
public static void PrintUnhandledException(Exception e)
{
try
{
// TODO Not implemented (print_unhandled_exception)
GD.PushError(e.ToString());
}
catch (Exception unexpected)
{
OnExceptionLoggerException(unexpected, e);
}
}
}
}

View File

@ -1,31 +1,41 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Godot.NativeInterop
{
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_bool
internal static class GodotBoolExtensions
{
public byte _value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe godot_bool ToGodotBool(this bool @bool)
{
return *(godot_bool*)&@bool;
}
public unsafe godot_bool(bool value) => _value = *(byte*)&value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool ToBool(this godot_bool godotBool)
{
return *(bool*)&godotBool;
}
}
public static unsafe implicit operator bool(godot_bool godotBool) => *(bool*)&godotBool._value;
public static implicit operator godot_bool(bool @bool) => new godot_bool(@bool);
// Apparently a struct with a byte is not blittable? It crashes when calling a UnmanagedCallersOnly function ptr.
// ReSharper disable once InconsistentNaming
public enum godot_bool : byte
{
True = 1,
False = 0
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_ref : IDisposable
public struct godot_ref : IDisposable
{
internal IntPtr _reference;
@ -36,10 +46,12 @@ namespace Godot.NativeInterop
NativeFuncs.godotsharp_ref_destroy(ref this);
_reference = IntPtr.Zero;
}
public bool IsNull => _reference == IntPtr.Zero;
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
internal enum godot_variant_call_error_error
public enum godot_variant_call_error_error
{
GODOT_CALL_ERROR_CALL_OK = 0,
GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD,
@ -51,16 +63,16 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_variant_call_error
public struct godot_variant_call_error
{
godot_variant_call_error_error error;
int argument;
Godot.Variant.Type expected;
public godot_variant_call_error_error error;
public int argument;
public Godot.Variant.Type expected;
}
[StructLayout(LayoutKind.Explicit)]
// ReSharper disable once InconsistentNaming
internal struct godot_variant : IDisposable
public struct godot_variant : IDisposable
{
[FieldOffset(0)] public Godot.Variant.Type _type;
@ -150,7 +162,7 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_string : IDisposable
public struct godot_string : IDisposable
{
internal IntPtr _ptr;
@ -161,11 +173,14 @@ namespace Godot.NativeInterop
NativeFuncs.godotsharp_string_destroy(ref this);
_ptr = IntPtr.Zero;
}
// Size including the null termination character
public unsafe int Size => _ptr != IntPtr.Zero ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_string_name : IDisposable
public struct godot_string_name : IDisposable
{
internal IntPtr _data;
@ -186,7 +201,7 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_node_path : IDisposable
public struct godot_node_path : IDisposable
{
internal IntPtr _data;
@ -207,7 +222,7 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Explicit)]
// ReSharper disable once InconsistentNaming
internal struct godot_signal : IDisposable
public struct godot_signal : IDisposable
{
[FieldOffset(0)] public godot_string_name _name;
@ -226,7 +241,7 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Explicit)]
// ReSharper disable once InconsistentNaming
internal struct godot_callable : IDisposable
public struct godot_callable : IDisposable
{
[FieldOffset(0)] public godot_string_name _method;
@ -250,16 +265,36 @@ namespace Godot.NativeInterop
// be re-assigned a new value (the copy constructor checks if `_p` is null so that's fine).
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_array : IDisposable
public struct godot_array : IDisposable
{
internal IntPtr _p;
internal unsafe ArrayPrivate* _p;
public void Dispose()
[StructLayout(LayoutKind.Sequential)]
internal struct ArrayPrivate
{
if (_p == IntPtr.Zero)
private uint _safeRefCount;
internal VariantVector _arrayVector;
// There's more here, but we don't care as we never store this in C#
}
[StructLayout(LayoutKind.Sequential)]
internal struct VariantVector
{
internal IntPtr _writeProxy;
internal unsafe godot_variant* _ptr;
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
public unsafe int Size => _p != null ? _p->_arrayVector.Size : 0;
public unsafe void Dispose()
{
if (_p == null)
return;
NativeFuncs.godotsharp_array_destroy(ref this);
_p = IntPtr.Zero;
_p = null;
}
}
@ -269,7 +304,7 @@ namespace Godot.NativeInterop
// be re-assigned a new value (the copy constructor checks if `_p` is null so that's fine).
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_dictionary : IDisposable
public struct godot_dictionary : IDisposable
{
internal IntPtr _p;
@ -284,145 +319,163 @@ namespace Godot.NativeInterop
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_packed_byte_array : IDisposable
public struct godot_packed_byte_array : IDisposable
{
internal IntPtr _writeProxy;
internal IntPtr _ptr;
internal unsafe byte* _ptr;
public void Dispose()
public unsafe void Dispose()
{
if (_ptr == IntPtr.Zero)
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_byte_array_destroy(ref this);
_ptr = IntPtr.Zero;
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_packed_int32_array : IDisposable
public struct godot_packed_int32_array : IDisposable
{
internal IntPtr _writeProxy;
internal IntPtr _ptr;
internal unsafe int* _ptr;
public void Dispose()
public unsafe void Dispose()
{
if (_ptr == IntPtr.Zero)
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_int32_array_destroy(ref this);
_ptr = IntPtr.Zero;
_ptr = null;
}
public unsafe int Size => _ptr != null ? *(_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_packed_int64_array : IDisposable
public struct godot_packed_int64_array : IDisposable
{
internal IntPtr _writeProxy;
internal IntPtr _ptr;
internal unsafe long* _ptr;
public void Dispose()
public unsafe void Dispose()
{
if (_ptr == IntPtr.Zero)
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_int64_array_destroy(ref this);
_ptr = IntPtr.Zero;
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_packed_float32_array : IDisposable
public struct godot_packed_float32_array : IDisposable
{
internal IntPtr _writeProxy;
internal IntPtr _ptr;
internal unsafe float* _ptr;
public void Dispose()
public unsafe void Dispose()
{
if (_ptr == IntPtr.Zero)
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_float32_array_destroy(ref this);
_ptr = IntPtr.Zero;
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_packed_float64_array : IDisposable
public struct godot_packed_float64_array : IDisposable
{
internal IntPtr _writeProxy;
internal IntPtr _ptr;
internal unsafe double* _ptr;
public void Dispose()
public unsafe void Dispose()
{
if (_ptr == IntPtr.Zero)
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_float64_array_destroy(ref this);
_ptr = IntPtr.Zero;
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_packed_string_array : IDisposable
public struct godot_packed_string_array : IDisposable
{
internal IntPtr _writeProxy;
internal IntPtr _ptr;
internal unsafe godot_string* _ptr;
public void Dispose()
public unsafe void Dispose()
{
if (_ptr == IntPtr.Zero)
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_string_array_destroy(ref this);
_ptr = IntPtr.Zero;
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_packed_vector2_array : IDisposable
public struct godot_packed_vector2_array : IDisposable
{
internal IntPtr _writeProxy;
internal IntPtr _ptr;
internal unsafe Vector2* _ptr;
public void Dispose()
public unsafe void Dispose()
{
if (_ptr == IntPtr.Zero)
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_vector2_array_destroy(ref this);
_ptr = IntPtr.Zero;
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_packed_vector3_array : IDisposable
public struct godot_packed_vector3_array : IDisposable
{
internal IntPtr _writeProxy;
internal IntPtr _ptr;
internal unsafe Vector3* _ptr;
public void Dispose()
public unsafe void Dispose()
{
if (_ptr == IntPtr.Zero)
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_vector3_array_destroy(ref this);
_ptr = IntPtr.Zero;
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_packed_color_array : IDisposable
public struct godot_packed_color_array : IDisposable
{
internal IntPtr _writeProxy;
internal IntPtr _ptr;
internal unsafe Color* _ptr;
public void Dispose()
public unsafe void Dispose()
{
if (_ptr == IntPtr.Zero)
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_color_array_destroy(ref this);
_ptr = IntPtr.Zero;
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
}

View File

@ -1,5 +1,9 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Godot.Bridge;
// ReSharper disable InconsistentNaming
namespace Godot.NativeInterop
{
@ -7,21 +11,78 @@ namespace Godot.NativeInterop
{
public static Object UnmanagedGetManaged(IntPtr unmanaged)
{
// TODO: Move to C#
return internal_unmanaged_get_managed(unmanaged);
// The native pointer may be null
if (unmanaged == IntPtr.Zero)
return null;
IntPtr gcHandlePtr;
godot_bool has_cs_script_instance = false.ToGodotBool();
// First try to get the tied managed instance from a CSharpInstance script instance
unsafe
{
gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_get_script_instance_managed(
unmanaged, &has_cs_script_instance);
}
if (gcHandlePtr != IntPtr.Zero)
return (Object)GCHandle.FromIntPtr(gcHandlePtr).Target;
// Otherwise, if the object has a CSharpInstance script instance, return null
if (has_cs_script_instance.ToBool())
return null;
// If it doesn't have a CSharpInstance script instance, try with native instance bindings
gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_get_instance_binding_managed(unmanaged);
object target = gcHandlePtr != IntPtr.Zero ? GCHandle.FromIntPtr(gcHandlePtr).Target : null;
if (target != null)
return (Object)target;
// If the native instance binding GC handle target was collected, create a new one
gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_instance_binding_create_managed(
unmanaged, gcHandlePtr);
return gcHandlePtr != IntPtr.Zero ? (Object)GCHandle.FromIntPtr(gcHandlePtr).Target : null;
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern Object internal_unmanaged_get_managed(IntPtr unmanaged);
public static void TieManagedToUnmanaged(Object managed, IntPtr unmanaged)
public static void TieManagedToUnmanaged(Object managed, IntPtr unmanaged,
StringName nativeName, bool refCounted, Type type, Type nativeType)
{
// TODO: Move to C#
internal_tie_managed_to_unmanaged(managed, unmanaged);
var gcHandle = GCHandle.Alloc(managed, refCounted ? GCHandleType.Weak : GCHandleType.Normal);
if (type == nativeType)
{
unsafe
{
godot_string_name nativeNameAux = nativeName.NativeValue;
NativeFuncs.godotsharp_internal_tie_native_managed_to_unmanaged(
GCHandle.ToIntPtr(gcHandle), unmanaged, &nativeNameAux, refCounted.ToGodotBool());
}
}
else
{
IntPtr scriptPtr = NativeFuncs.godotsharp_internal_new_csharp_script();
ScriptManagerBridge.AddScriptBridgeWithType(scriptPtr, type);
// IMPORTANT: This must be called after AddScriptWithTypeBridge
NativeFuncs.godotsharp_internal_tie_user_managed_to_unmanaged(
GCHandle.ToIntPtr(gcHandle), unmanaged, scriptPtr, refCounted.ToGodotBool());
}
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_tie_managed_to_unmanaged(Object managed, IntPtr unmanaged);
public static void TieManagedToUnmanagedWithPreSetup(Object managed, IntPtr unmanaged)
{
var strongGCHandle = GCHandle.Alloc(managed, GCHandleType.Normal);
NativeFuncs.godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup(
GCHandle.ToIntPtr(strongGCHandle), unmanaged);
}
public static unsafe Object EngineGetSingleton(string name)
{

View File

@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
@ -10,16 +11,12 @@ namespace Godot.NativeInterop
{
// We want to use full name qualifiers here even if redundant for clarity
[SuppressMessage("ReSharper", "RedundantNameQualifier")]
internal static class Marshaling
public static class Marshaling
{
public static unsafe void SetFieldValue(FieldInfo fieldInfo, object obj, godot_variant* value)
public static Variant.Type managed_to_variant_type(Type type, out bool r_nil_is_variant)
{
var valueObj = variant_to_mono_object_of_type(value, fieldInfo.FieldType);
fieldInfo.SetValue(obj, valueObj);
}
r_nil_is_variant = false;
public static Variant.Type managed_to_variant_type(Type type, ref bool r_nil_is_variant)
{
switch (Type.GetTypeCode(type))
{
case TypeCode.Boolean:
@ -152,7 +149,8 @@ namespace Godot.NativeInterop
if (genericTypeDefinition == typeof(IDictionary<,>))
return Variant.Type.Dictionary;
if (genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IEnumerable<>))
if (genericTypeDefinition == typeof(ICollection<>) ||
genericTypeDefinition == typeof(IEnumerable<>))
return Variant.Type.Array;
}
else if (type == typeof(object))
@ -189,8 +187,6 @@ namespace Godot.NativeInterop
}
}
r_nil_is_variant = false;
// Unknown
return Variant.Type.Nil;
}
@ -232,10 +228,6 @@ namespace Godot.NativeInterop
return mono_object_to_variant_impl(p_obj);
}
// TODO: Only called from C++. Remove once no longer needed.
private static unsafe void mono_object_to_variant_out(object p_obj, bool p_fail_with_err, godot_variant* r_ret)
=> *r_ret = mono_object_to_variant_impl(p_obj, p_fail_with_err);
private static unsafe godot_variant mono_object_to_variant_impl(object p_obj, bool p_fail_with_err = true)
{
if (p_obj == null)
@ -414,42 +406,18 @@ namespace Godot.NativeInterop
if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>))
{
// TODO: Validate key and value types are compatible with Variant
#if NET
Collections.IGenericGodotDictionary genericGodotDictionary = IDictionaryToGenericGodotDictionary((dynamic)p_obj);
#else
var genericArguments = type.GetGenericArguments();
var godotDict = new Collections.Dictionary();
// With .NET Standard we need a package reference for Microsoft.CSharp in order to
// use dynamic, so we have this workaround for now until we switch to .NET 5/6.
var method = typeof(Marshaling).GetMethod(nameof(IDictionaryToGenericGodotDictionary),
BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly)!
.MakeGenericMethod(genericArguments[0], genericArguments[1]);
foreach (KeyValuePair<object, object> entry in (IDictionary)p_obj)
godotDict.Add(entry.Key, entry.Value);
var genericGodotDictionary = (Collections.IGenericGodotDictionary)method
.Invoke(null, new[] {p_obj});
#endif
var godotDict = genericGodotDictionary.UnderlyingDictionary;
if (godotDict == null)
return new godot_variant();
return VariantUtils.CreateFromDictionary(godotDict.NativeValue);
}
if (genericTypeDefinition == typeof(System.Collections.Generic.List<>))
{
// TODO: Validate element type is compatible with Variant
#if NET
var nativeGodotArray = mono_array_to_Array(System.Runtime.InteropServices.CollectionsMarshal.AsSpan((dynamic)p_obj));
#else
// With .NET Standard we need a package reference for Microsoft.CSharp in order to
// use dynamic, so we have this workaround for now until we switch to .NET 5/6.
// Also CollectionsMarshal.AsSpan is not available with .NET Standard.
var collection = (System.Collections.ICollection)p_obj;
var array = new object[collection.Count];
collection.CopyTo(array, 0);
var nativeGodotArray = mono_array_to_Array(array);
#endif
var nativeGodotArray = mono_array_to_Array((IList)p_obj);
return VariantUtils.CreateFromArray(&nativeGodotArray);
}
}
@ -470,9 +438,6 @@ namespace Godot.NativeInterop
}
}
private static Collections.Dictionary<TKey, TValue> IDictionaryToGenericGodotDictionary<TKey, TValue>
(IDictionary<TKey, TValue> dictionary) => new(dictionary);
public static unsafe string variant_to_mono_string(godot_variant* p_var)
{
switch ((*p_var)._type)
@ -482,12 +447,12 @@ namespace Godot.NativeInterop
case Variant.Type.String:
{
// We avoid the internal call if the stored type is the same we want.
return mono_string_from_godot(&(*p_var)._data._m_string);
return mono_string_from_godot((*p_var)._data._m_string);
}
default:
{
using godot_string godotString = NativeFuncs.godotsharp_variant_as_string(p_var);
return mono_string_from_godot(&godotString);
return mono_string_from_godot(godotString);
}
}
}
@ -599,8 +564,9 @@ namespace Godot.NativeInterop
return VariantUtils.ConvertToUInt64(p_var);
default:
{
GD.PushError("Attempted to convert Variant to enum value of unsupported underlying type. Name: " +
type.FullName + " : " + enumUnderlyingType.FullName + ".");
GD.PushError(
"Attempted to convert Variant to enum value of unsupported underlying type. Name: " +
type.FullName + " : " + enumUnderlyingType.FullName + ".");
return null;
}
}
@ -683,7 +649,7 @@ namespace Godot.NativeInterop
if (typeof(Godot.Object[]).IsAssignableFrom(type))
{
using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
return Array_to_mono_array_of_type(&godotArray, type);
return Array_to_mono_array_of_godot_object_type(&godotArray, type);
}
if (type == typeof(object[]))
@ -753,7 +719,7 @@ namespace Godot.NativeInterop
VariantUtils.ConvertToDictionary(p_var));
return Activator.CreateInstance(fullType,
BindingFlags.Public | BindingFlags.Instance, null,
args: new object[] {underlyingDict}, null);
args: new object[] { underlyingDict }, null);
}
static object variant_to_generic_godot_collections_array(godot_variant* p_var, Type fullType)
@ -762,7 +728,7 @@ namespace Godot.NativeInterop
VariantUtils.ConvertToArray(p_var));
return Activator.CreateInstance(fullType,
BindingFlags.Public | BindingFlags.Instance, null,
args: new object[] {underlyingArray}, null);
args: new object[] { underlyingArray }, null);
}
var genericTypeDefinition = type.GetGenericTypeDefinition();
@ -837,7 +803,7 @@ namespace Godot.NativeInterop
switch ((*p_var)._type)
{
case Variant.Type.Bool:
return (bool)(*p_var)._data._bool;
return (*p_var)._data._bool.ToBool();
case Variant.Type.Int:
return (*p_var)._data._int;
case Variant.Type.Float:
@ -849,7 +815,7 @@ namespace Godot.NativeInterop
#endif
}
case Variant.Type.String:
return mono_string_from_godot(&(*p_var)._data._m_string);
return mono_string_from_godot((*p_var)._data._m_string);
case Variant.Type.Vector2:
return (*p_var)._data._m_vector2;
case Variant.Type.Vector2i:
@ -973,14 +939,14 @@ namespace Godot.NativeInterop
}
}
public static unsafe string mono_string_from_godot(godot_string* p_string)
public static unsafe string mono_string_from_godot(in godot_string p_string)
{
if ((*p_string)._ptr == IntPtr.Zero)
if (p_string._ptr == IntPtr.Zero)
return string.Empty;
const int sizeOfChar32 = 4;
byte* bytes = (byte*)(*p_string)._ptr;
int size = *((int*)(*p_string)._ptr - 1);
byte* bytes = (byte*)p_string._ptr;
int size = p_string.Size;
if (size == 0)
return string.Empty;
size -= 1; // zero at the end
@ -1034,7 +1000,7 @@ namespace Godot.NativeInterop
godot_string_name name;
if (NativeFuncs.godotsharp_callable_get_data_for_marshalling(
p_callable, &delegateGCHandle, &godotObject, &name))
p_callable, &delegateGCHandle, &godotObject, &name).ToBool())
{
if (delegateGCHandle != IntPtr.Zero)
{
@ -1102,7 +1068,7 @@ namespace Godot.NativeInterop
return ret;
}
public static unsafe object Array_to_mono_array_of_type(godot_array* p_array, Type type)
public static unsafe object Array_to_mono_array_of_godot_object_type(godot_array* p_array, Type type)
{
var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_array_new_copy(p_array));
@ -1110,24 +1076,44 @@ namespace Godot.NativeInterop
int length = array.Count;
object ret = Activator.CreateInstance(type, length);
array.CopyTo((object[])ret, 0); // variant_to_mono_object handled by Collections.Array
// variant_to_mono_object handled by Collections.Array
// variant_to_mono_object_of_type is not needed because target element types are Godot.Object (or derived)
array.CopyTo((object[])ret, 0);
return ret;
}
public static godot_array mono_array_to_Array(Span<object> p_array)
public static godot_array mono_array_to_Array(object[] p_array)
{
if (p_array.IsEmpty)
{
godot_array ret;
Collections.Array.godot_icall_Array_Ctor(out ret);
return ret;
}
int length = p_array.Length;
if (length == 0)
return NativeFuncs.godotsharp_array_new();
using var array = new Collections.Array();
array.Resize(p_array.Length);
array.Resize(length);
for (int i = 0; i < p_array.Length; i++)
for (int i = 0; i < length; i++)
array[i] = p_array[i];
godot_array src = array.NativeValue;
unsafe
{
return NativeFuncs.godotsharp_array_new_copy(&src);
}
}
public static godot_array mono_array_to_Array(IList p_array)
{
int length = p_array.Count;
if (length == 0)
return NativeFuncs.godotsharp_array_new();
using var array = new Collections.Array();
array.Resize(length);
for (int i = 0; i < length; i++)
array[i] = p_array[i];
godot_array src = array.NativeValue;
@ -1141,8 +1127,8 @@ namespace Godot.NativeInterop
public static unsafe byte[] PackedByteArray_to_mono_array(godot_packed_byte_array* p_array)
{
byte* buffer = (byte*)(*p_array)._ptr;
int size = *((int*)(*p_array)._ptr - 1);
byte* buffer = (*p_array)._ptr;
int size = (*p_array).Size;
var array = new byte[size];
fixed (byte* dest = array)
Buffer.MemoryCopy(buffer, dest, size, size);
@ -1161,8 +1147,8 @@ namespace Godot.NativeInterop
public static unsafe int[] PackedInt32Array_to_mono_array(godot_packed_int32_array* p_array)
{
int* buffer = (int*)(*p_array)._ptr;
int size = *((int*)(*p_array)._ptr - 1);
int* buffer = (*p_array)._ptr;
int size = (*p_array).Size;
int sizeInBytes = size * sizeof(int);
var array = new int[size];
fixed (int* dest = array)
@ -1182,8 +1168,8 @@ namespace Godot.NativeInterop
public static unsafe long[] PackedInt64Array_to_mono_array(godot_packed_int64_array* p_array)
{
long* buffer = (long*)(*p_array)._ptr;
int size = *((int*)(*p_array)._ptr - 1);
long* buffer = (*p_array)._ptr;
int size = (*p_array).Size;
int sizeInBytes = size * sizeof(long);
var array = new long[size];
fixed (long* dest = array)
@ -1203,8 +1189,8 @@ namespace Godot.NativeInterop
public static unsafe float[] PackedFloat32Array_to_mono_array(godot_packed_float32_array* p_array)
{
float* buffer = (float*)(*p_array)._ptr;
int size = *((int*)(*p_array)._ptr - 1);
float* buffer = (*p_array)._ptr;
int size = (*p_array).Size;
int sizeInBytes = size * sizeof(float);
var array = new float[size];
fixed (float* dest = array)
@ -1224,8 +1210,8 @@ namespace Godot.NativeInterop
public static unsafe double[] PackedFloat64Array_to_mono_array(godot_packed_float64_array* p_array)
{
double* buffer = (double*)(*p_array)._ptr;
int size = *((int*)(*p_array)._ptr - 1);
double* buffer = (*p_array)._ptr;
int size = (*p_array).Size;
int sizeInBytes = size * sizeof(double);
var array = new double[size];
fixed (double* dest = array)
@ -1245,13 +1231,13 @@ namespace Godot.NativeInterop
public static unsafe string[] PackedStringArray_to_mono_array(godot_packed_string_array* p_array)
{
godot_string* buffer = (godot_string*)(*p_array)._ptr;
godot_string* buffer = (*p_array)._ptr;
if (buffer == null)
return new string[] { };
int size = *((int*)(*p_array)._ptr - 1);
int size = (*p_array).Size;
var array = new string[size];
for (int i = 0; i < size; i++)
array[i] = mono_string_from_godot(&buffer[i]);
array[i] = mono_string_from_godot(buffer[i]);
return array;
}
@ -1278,8 +1264,8 @@ namespace Godot.NativeInterop
public static unsafe Vector2[] PackedVector2Array_to_mono_array(godot_packed_vector2_array* p_array)
{
Vector2* buffer = (Vector2*)(*p_array)._ptr;
int size = *((int*)(*p_array)._ptr - 1);
Vector2* buffer = (*p_array)._ptr;
int size = (*p_array).Size;
int sizeInBytes = size * sizeof(Vector2);
var array = new Vector2[size];
fixed (Vector2* dest = array)
@ -1299,8 +1285,8 @@ namespace Godot.NativeInterop
public static unsafe Vector3[] PackedVector3Array_to_mono_array(godot_packed_vector3_array* p_array)
{
Vector3* buffer = (Vector3*)(*p_array)._ptr;
int size = *((int*)(*p_array)._ptr - 1);
Vector3* buffer = (*p_array)._ptr;
int size = (*p_array).Size;
int sizeInBytes = size * sizeof(Vector3);
var array = new Vector3[size];
fixed (Vector3* dest = array)
@ -1320,8 +1306,8 @@ namespace Godot.NativeInterop
public static unsafe Color[] PackedColorArray_to_mono_array(godot_packed_color_array* p_array)
{
Color* buffer = (Color*)(*p_array)._ptr;
int size = *((int*)(*p_array)._ptr - 1);
Color* buffer = (*p_array)._ptr;
int size = (*p_array).Size;
int sizeInBytes = size * sizeof(Color);
var array = new Color[size];
fixed (Color* dest = array)

View File

@ -10,30 +10,72 @@ namespace Godot.NativeInterop
// The attribute is not available with .NET Core and it's not needed there.
[System.Security.SuppressUnmanagedCodeSecurity]
#endif
internal static unsafe partial class NativeFuncs
public static unsafe partial class NativeFuncs
{
private const string GodotDllName = "__Internal";
// Custom functions
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_method_bind_get_method(ref godot_string_name p_classname, char* p_methodname);
#if NET
[DllImport(GodotDllName)]
public static extern delegate* unmanaged<IntPtr> godotsharp_get_class_constructor(ref godot_string_name p_classname);
#else
// Workaround until we switch to .NET 5/6
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_get_class_constructor(ref godot_string_name p_classname);
public static extern IntPtr godotsharp_method_bind_get_method(ref godot_string_name p_classname,
char* p_methodname);
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_invoke_class_constructor(IntPtr p_creation_func);
#endif
public static extern delegate* unmanaged<IntPtr> godotsharp_get_class_constructor(
ref godot_string_name p_classname);
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_engine_get_singleton(godot_string* p_name);
[DllImport(GodotDllName)]
internal static extern void godotsharp_internal_object_disposed(IntPtr ptr);
[DllImport(GodotDllName)]
internal static extern void godotsharp_internal_refcounted_disposed(IntPtr ptr, godot_bool isFinalizer);
[DllImport(GodotDllName)]
internal static extern void godotsharp_internal_object_connect_event_signal(IntPtr obj,
godot_string_name* eventSignal);
[DllImport(GodotDllName)]
internal static extern Error godotsharp_internal_signal_awaiter_connect(IntPtr source,
ref godot_string_name signal,
IntPtr target, IntPtr awaiterHandlePtr);
[DllImport(GodotDllName)]
public static extern void godotsharp_internal_tie_native_managed_to_unmanaged(IntPtr gcHandleIntPtr,
IntPtr unmanaged, godot_string_name* nativeName, godot_bool refCounted);
[DllImport(GodotDllName)]
public static extern void godotsharp_internal_tie_user_managed_to_unmanaged(IntPtr gcHandleIntPtr,
IntPtr unmanaged, IntPtr scriptPtr, godot_bool refCounted);
[DllImport(GodotDllName)]
public static extern void godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup(
IntPtr gcHandleIntPtr, IntPtr unmanaged);
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_internal_unmanaged_get_script_instance_managed(IntPtr p_unmanaged,
godot_bool* r_has_cs_script_instance);
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_internal_unmanaged_get_instance_binding_managed(IntPtr p_unmanaged);
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_internal_unmanaged_instance_binding_create_managed(IntPtr p_unmanaged,
IntPtr oldGCHandlePtr);
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_internal_new_csharp_script();
[DllImport(GodotDllName)]
public static extern void godotsharp_array_filter_godot_objects_by_native(godot_string_name* p_native_name,
godot_array* p_input, godot_array* r_output);
[DllImport(GodotDllName)]
public static extern void godotsharp_array_filter_godot_objects_by_non_native(godot_array* p_input,
godot_array* r_output);
[DllImport(GodotDllName)]
public static extern void godotsharp_ref_destroy(ref godot_ref p_instance);
@ -50,47 +92,60 @@ namespace Godot.NativeInterop
public static extern void godotsharp_node_path_as_string(godot_string* r_dest, godot_node_path* p_np);
[DllImport(GodotDllName)]
public static extern godot_packed_byte_array godotsharp_packed_byte_array_new_mem_copy(byte* p_src, int p_length);
public static extern godot_packed_byte_array godotsharp_packed_byte_array_new_mem_copy(byte* p_src,
int p_length);
[DllImport(GodotDllName)]
public static extern godot_packed_int32_array godotsharp_packed_int32_array_new_mem_copy(int* p_src, int p_length);
public static extern godot_packed_int32_array godotsharp_packed_int32_array_new_mem_copy(int* p_src,
int p_length);
[DllImport(GodotDllName)]
public static extern godot_packed_int64_array godotsharp_packed_int64_array_new_mem_copy(long* p_src, int p_length);
public static extern godot_packed_int64_array godotsharp_packed_int64_array_new_mem_copy(long* p_src,
int p_length);
[DllImport(GodotDllName)]
public static extern godot_packed_float32_array godotsharp_packed_float32_array_new_mem_copy(float* p_src, int p_length);
public static extern godot_packed_float32_array godotsharp_packed_float32_array_new_mem_copy(float* p_src,
int p_length);
[DllImport(GodotDllName)]
public static extern godot_packed_float64_array godotsharp_packed_float64_array_new_mem_copy(double* p_src, int p_length);
public static extern godot_packed_float64_array godotsharp_packed_float64_array_new_mem_copy(double* p_src,
int p_length);
[DllImport(GodotDllName)]
public static extern godot_packed_vector2_array godotsharp_packed_vector2_array_new_mem_copy(Vector2* p_src, int p_length);
public static extern godot_packed_vector2_array godotsharp_packed_vector2_array_new_mem_copy(Vector2* p_src,
int p_length);
[DllImport(GodotDllName)]
public static extern godot_packed_vector3_array godotsharp_packed_vector3_array_new_mem_copy(Vector3* p_src, int p_length);
public static extern godot_packed_vector3_array godotsharp_packed_vector3_array_new_mem_copy(Vector3* p_src,
int p_length);
[DllImport(GodotDllName)]
public static extern godot_packed_color_array godotsharp_packed_color_array_new_mem_copy(Color* p_src, int p_length);
public static extern godot_packed_color_array godotsharp_packed_color_array_new_mem_copy(Color* p_src,
int p_length);
[DllImport(GodotDllName)]
public static extern void godotsharp_packed_string_array_add(godot_packed_string_array* r_dest, godot_string* p_element);
public static extern void godotsharp_packed_string_array_add(godot_packed_string_array* r_dest,
godot_string* p_element);
[DllImport(GodotDllName)]
public static extern void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, godot_callable* r_callable);
public static extern void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle,
godot_callable* r_callable);
[DllImport(GodotDllName)]
public static extern bool godotsharp_callable_get_data_for_marshalling(godot_callable* p_callable, IntPtr* r_delegate_handle, IntPtr* r_object, godot_string_name* r_name);
public static extern godot_bool godotsharp_callable_get_data_for_marshalling(godot_callable* p_callable,
IntPtr* r_delegate_handle, IntPtr* r_object, godot_string_name* r_name);
// GDNative functions
// gdnative.h
[DllImport(GodotDllName)]
public static extern void godotsharp_method_bind_ptrcall(IntPtr p_method_bind, IntPtr p_instance, void** p_args, void* p_ret);
public static extern void godotsharp_method_bind_ptrcall(IntPtr p_method_bind, IntPtr p_instance, void** p_args,
void* p_ret);
[DllImport(GodotDllName)]
public static extern godot_variant godotsharp_method_bind_call(IntPtr p_method_bind, IntPtr p_instance, godot_variant** p_args, int p_arg_count, godot_variant_call_error* p_call_error);
public static extern godot_variant godotsharp_method_bind_call(IntPtr p_method_bind, IntPtr p_instance,
godot_variant** p_args, int p_arg_count, godot_variant_call_error* p_call_error);
// variant.h
@ -122,34 +177,43 @@ namespace Godot.NativeInterop
public static extern void godotsharp_variant_new_array(godot_variant* r_dest, godot_array* p_arr);
[DllImport(GodotDllName)]
public static extern void godotsharp_variant_new_packed_byte_array(godot_variant* r_dest, godot_packed_byte_array* p_pba);
public static extern void godotsharp_variant_new_packed_byte_array(godot_variant* r_dest,
godot_packed_byte_array* p_pba);
[DllImport(GodotDllName)]
public static extern void godotsharp_variant_new_packed_int32_array(godot_variant* r_dest, godot_packed_int32_array* p_pia);
public static extern void godotsharp_variant_new_packed_int32_array(godot_variant* r_dest,
godot_packed_int32_array* p_pia);
[DllImport(GodotDllName)]
public static extern void godotsharp_variant_new_packed_int64_array(godot_variant* r_dest, godot_packed_int64_array* p_pia);
public static extern void godotsharp_variant_new_packed_int64_array(godot_variant* r_dest,
godot_packed_int64_array* p_pia);
[DllImport(GodotDllName)]
public static extern void godotsharp_variant_new_packed_float32_array(godot_variant* r_dest, godot_packed_float32_array* p_pra);
public static extern void godotsharp_variant_new_packed_float32_array(godot_variant* r_dest,
godot_packed_float32_array* p_pra);
[DllImport(GodotDllName)]
public static extern void godotsharp_variant_new_packed_float64_array(godot_variant* r_dest, godot_packed_float64_array* p_pra);
public static extern void godotsharp_variant_new_packed_float64_array(godot_variant* r_dest,
godot_packed_float64_array* p_pra);
[DllImport(GodotDllName)]
public static extern void godotsharp_variant_new_packed_string_array(godot_variant* r_dest, godot_packed_string_array* p_psa);
public static extern void godotsharp_variant_new_packed_string_array(godot_variant* r_dest,
godot_packed_string_array* p_psa);
[DllImport(GodotDllName)]
public static extern void godotsharp_variant_new_packed_vector2_array(godot_variant* r_dest, godot_packed_vector2_array* p_pv2a);
public static extern void godotsharp_variant_new_packed_vector2_array(godot_variant* r_dest,
godot_packed_vector2_array* p_pv2a);
[DllImport(GodotDllName)]
public static extern void godotsharp_variant_new_packed_vector3_array(godot_variant* r_dest, godot_packed_vector3_array* p_pv3a);
public static extern void godotsharp_variant_new_packed_vector3_array(godot_variant* r_dest,
godot_packed_vector3_array* p_pv3a);
[DllImport(GodotDllName)]
public static extern void godotsharp_variant_new_packed_color_array(godot_variant* r_dest, godot_packed_color_array* p_pca);
public static extern void godotsharp_variant_new_packed_color_array(godot_variant* r_dest,
godot_packed_color_array* p_pca);
[DllImport(GodotDllName)]
public static extern bool godotsharp_variant_as_bool(godot_variant* p_self);
public static extern godot_bool godotsharp_variant_as_bool(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern Int64 godotsharp_variant_as_int(godot_variant* p_self);
@ -230,23 +294,30 @@ namespace Godot.NativeInterop
public static extern godot_packed_int64_array godotsharp_variant_as_packed_int64_array(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern godot_packed_float32_array godotsharp_variant_as_packed_float32_array(godot_variant* p_self);
public static extern godot_packed_float32_array godotsharp_variant_as_packed_float32_array(
godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern godot_packed_float64_array godotsharp_variant_as_packed_float64_array(godot_variant* p_self);
public static extern godot_packed_float64_array godotsharp_variant_as_packed_float64_array(
godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern godot_packed_string_array godotsharp_variant_as_packed_string_array(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern godot_packed_vector2_array godotsharp_variant_as_packed_vector2_array(godot_variant* p_self);
public static extern godot_packed_vector2_array godotsharp_variant_as_packed_vector2_array(
godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern godot_packed_vector3_array godotsharp_variant_as_packed_vector3_array(godot_variant* p_self);
public static extern godot_packed_vector3_array godotsharp_variant_as_packed_vector3_array(
godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern godot_packed_color_array godotsharp_variant_as_packed_color_array(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern godot_bool godotsharp_variant_equals(godot_variant* p_a, godot_variant* p_b);
// string.h
[DllImport(GodotDllName)]
@ -264,11 +335,20 @@ namespace Godot.NativeInterop
// array.h
[DllImport(GodotDllName)]
public static extern void godotsharp_array_new(godot_array* p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_array_new_copy(godot_array* r_dest, godot_array* p_src);
[DllImport(GodotDllName)]
public static extern godot_variant* godotsharp_array_ptrw(ref godot_array p_self);
// dictionary.h
[DllImport(GodotDllName)]
public static extern void godotsharp_dictionary_new(godot_dictionary* p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_dictionary_new_copy(godot_dictionary* r_dest, godot_dictionary* p_src);
@ -324,5 +404,205 @@ namespace Godot.NativeInterop
[DllImport(GodotDllName)]
public static extern void godotsharp_dictionary_destroy(ref godot_dictionary p_self);
// Array
[DllImport(GodotDllName)]
public static extern int godotsharp_array_add(ref godot_array p_self, godot_variant* p_item);
[DllImport(GodotDllName)]
public static extern void
godotsharp_array_duplicate(ref godot_array p_self, godot_bool p_deep, out godot_array r_dest);
[DllImport(GodotDllName)]
public static extern int godotsharp_array_index_of(ref godot_array p_self, godot_variant* p_item);
[DllImport(GodotDllName)]
public static extern void godotsharp_array_insert(ref godot_array p_self, int p_index, godot_variant* p_item);
[DllImport(GodotDllName)]
public static extern void godotsharp_array_remove_at(ref godot_array p_self, int p_index);
[DllImport(GodotDllName)]
public static extern Error godotsharp_array_resize(ref godot_array p_self, int p_new_size);
[DllImport(GodotDllName)]
public static extern Error godotsharp_array_shuffle(ref godot_array p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_array_to_string(ref godot_array p_self, godot_string* r_str);
// Dictionary
[DllImport(GodotDllName)]
public static extern godot_bool godotsharp_dictionary_try_get_value(ref godot_dictionary p_self,
godot_variant* p_key,
out godot_variant r_value);
[DllImport(GodotDllName)]
public static extern void godotsharp_dictionary_set_value(ref godot_dictionary p_self, godot_variant* p_key,
godot_variant* p_value);
[DllImport(GodotDllName)]
public static extern void godotsharp_dictionary_keys(ref godot_dictionary p_self, out godot_array r_dest);
[DllImport(GodotDllName)]
public static extern void godotsharp_dictionary_values(ref godot_dictionary p_self, out godot_array r_dest);
[DllImport(GodotDllName)]
public static extern int godotsharp_dictionary_count(ref godot_dictionary p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_dictionary_key_value_pair_at(ref godot_dictionary p_self, int p_index,
out godot_variant r_key, out godot_variant r_value);
[DllImport(GodotDllName)]
public static extern void godotsharp_dictionary_add(ref godot_dictionary p_self, godot_variant* p_key,
godot_variant* p_value);
[DllImport(GodotDllName)]
public static extern void godotsharp_dictionary_clear(ref godot_dictionary p_self);
[DllImport(GodotDllName)]
public static extern godot_bool godotsharp_dictionary_contains_key(ref godot_dictionary p_self,
godot_variant* p_key);
[DllImport(GodotDllName)]
public static extern void godotsharp_dictionary_duplicate(ref godot_dictionary p_self, godot_bool p_deep,
out godot_dictionary r_dest);
[DllImport(GodotDllName)]
public static extern godot_bool godotsharp_dictionary_remove_key(ref godot_dictionary p_self,
godot_variant* p_key);
[DllImport(GodotDllName)]
public static extern void godotsharp_dictionary_to_string(ref godot_dictionary p_self, godot_string* r_str);
// StringExtensions
[DllImport(GodotDllName)]
public static extern void godotsharp_string_md5_buffer(godot_string* p_self,
godot_packed_byte_array* r_md5_buffer);
[DllImport(GodotDllName)]
public static extern void godotsharp_string_md5_text(godot_string* p_self, godot_string* r_md5_text);
[DllImport(GodotDllName)]
public static extern int godotsharp_string_rfind(godot_string* p_self, godot_string* p_what, int p_from);
[DllImport(GodotDllName)]
public static extern int godotsharp_string_rfindn(godot_string* p_self, godot_string* p_what, int p_from);
[DllImport(GodotDllName)]
public static extern void godotsharp_string_sha256_buffer(godot_string* p_self,
godot_packed_byte_array* r_sha256_buffer);
[DllImport(GodotDllName)]
public static extern void godotsharp_string_sha256_text(godot_string* p_self, godot_string* r_sha256_text);
// NodePath
[DllImport(GodotDllName)]
public static extern void godotsharp_node_path_get_as_property_path(ref godot_node_path p_self,
ref godot_node_path r_dest);
[DllImport(GodotDllName)]
public static extern void godotsharp_node_path_get_concatenated_subnames(ref godot_node_path p_self,
godot_string* r_subnames);
[DllImport(GodotDllName)]
public static extern void godotsharp_node_path_get_name(ref godot_node_path p_self, int p_idx,
godot_string* r_name);
[DllImport(GodotDllName)]
public static extern int godotsharp_node_path_get_name_count(ref godot_node_path p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_node_path_get_subname(ref godot_node_path p_self, int p_idx,
godot_string* r_subname);
[DllImport(GodotDllName)]
public static extern int godotsharp_node_path_get_subname_count(ref godot_node_path p_self);
[DllImport(GodotDllName)]
public static extern godot_bool godotsharp_node_path_is_absolute(ref godot_node_path p_self);
// GD, etc
[DllImport(GodotDllName)]
public static extern void godotsharp_bytes2var(godot_packed_byte_array* p_bytes, godot_bool p_allow_objects,
godot_variant* r_ret);
[DllImport(GodotDllName)]
public static extern void godotsharp_convert(godot_variant* p_what, Variant.Type p_type, godot_variant* r_ret);
[DllImport(GodotDllName)]
public static extern int godotsharp_hash(godot_variant* var);
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_instance_from_id(ulong instanceId);
[DllImport(GodotDllName)]
public static extern void godotsharp_print(godot_string* p_what);
[DllImport(GodotDllName)]
public static extern void godotsharp_printerr(godot_string* p_what);
[DllImport(GodotDllName)]
public static extern void godotsharp_printraw(godot_string* p_what);
[DllImport(GodotDllName)]
public static extern void godotsharp_prints(godot_string* p_what);
[DllImport(GodotDllName)]
public static extern void godotsharp_printt(godot_string* p_what);
[DllImport(GodotDllName)]
public static extern float godotsharp_randf();
[DllImport(GodotDllName)]
public static extern uint godotsharp_randi();
[DllImport(GodotDllName)]
public static extern void godotsharp_randomize();
[DllImport(GodotDllName)]
public static extern double godotsharp_randf_range(double from, double to);
[DllImport(GodotDllName)]
public static extern int godotsharp_randi_range(int from, int to);
[DllImport(GodotDllName)]
public static extern uint godotsharp_rand_from_seed(ulong seed, out ulong newSeed);
[DllImport(GodotDllName)]
public static extern void godotsharp_seed(ulong seed);
[DllImport(GodotDllName)]
public static extern void godotsharp_weakref(IntPtr obj, godot_ref* r_weak_ref);
[DllImport(GodotDllName)]
public static extern string godotsharp_str(godot_array* p_what, godot_string* r_ret);
[DllImport(GodotDllName)]
public static extern void godotsharp_str2var(godot_string* p_str, godot_variant* r_ret);
[DllImport(GodotDllName)]
public static extern void godotsharp_var2bytes(godot_variant* what, godot_bool fullObjects,
godot_packed_byte_array* bytes);
[DllImport(GodotDllName)]
public static extern void godotsharp_var2str(godot_variant* var, godot_string* r_ret);
[DllImport(GodotDllName)]
public static extern void godotsharp_pusherror(godot_string* type);
[DllImport(GodotDllName)]
public static extern void godotsharp_pushwarning(godot_string* type);
// Object
[DllImport(GodotDllName)]
public static extern string godotsharp_object_to_string(IntPtr ptr, godot_string* r_str);
}
}

View File

@ -5,7 +5,7 @@ using System.Runtime.CompilerServices;
namespace Godot.NativeInterop
{
internal static unsafe partial class NativeFuncs
public static unsafe partial class NativeFuncs
{
public static godot_string_name godotsharp_string_name_new_copy(godot_string_name* src)
{
@ -29,6 +29,13 @@ namespace Godot.NativeInterop
public static godot_node_path godotsharp_node_path_new_copy(godot_node_path src)
=> godotsharp_node_path_new_copy(&src);
public static godot_array godotsharp_array_new()
{
godot_array ret;
godotsharp_array_new(&ret);
return ret;
}
public static godot_array godotsharp_array_new_copy(godot_array* src)
{
godot_array ret;
@ -40,6 +47,13 @@ namespace Godot.NativeInterop
public static godot_array godotsharp_array_new_copy(godot_array src)
=> godotsharp_array_new_copy(&src);
public static godot_dictionary godotsharp_dictionary_new()
{
godot_dictionary ret;
godotsharp_dictionary_new(&ret);
return ret;
}
public static godot_dictionary godotsharp_dictionary_new_copy(godot_dictionary* src)
{
godot_dictionary ret;

View File

@ -5,49 +5,49 @@ using System.Runtime.CompilerServices;
namespace Godot.NativeInterop
{
internal static class VariantUtils
public static class VariantUtils
{
public static godot_variant CreateFromRID(RID from)
=> new() {_type = Variant.Type.Rid, _data = {_m_rid = from}};
=> new() { _type = Variant.Type.Rid, _data = { _m_rid = from } };
public static godot_variant CreateFromBool(bool from)
=> new() {_type = Variant.Type.Bool, _data = {_bool = from}};
=> new() { _type = Variant.Type.Bool, _data = { _bool = from.ToGodotBool() } };
public static godot_variant CreateFromInt(long from)
=> new() {_type = Variant.Type.Int, _data = {_int = from}};
=> new() { _type = Variant.Type.Int, _data = { _int = from } };
public static godot_variant CreateFromInt(ulong from)
=> new() {_type = Variant.Type.Int, _data = {_int = (long)from}};
=> new() { _type = Variant.Type.Int, _data = { _int = (long)from } };
public static godot_variant CreateFromFloat(double from)
=> new() {_type = Variant.Type.Float, _data = {_float = from}};
=> new() { _type = Variant.Type.Float, _data = { _float = from } };
public static godot_variant CreateFromVector2(Vector2 from)
=> new() {_type = Variant.Type.Vector2, _data = {_m_vector2 = from}};
=> new() { _type = Variant.Type.Vector2, _data = { _m_vector2 = from } };
public static godot_variant CreateFromVector2i(Vector2i from)
=> new() {_type = Variant.Type.Vector2i, _data = {_m_vector2i = from}};
=> new() { _type = Variant.Type.Vector2i, _data = { _m_vector2i = from } };
public static godot_variant CreateFromVector3(Vector3 from)
=> new() {_type = Variant.Type.Vector3, _data = {_m_vector3 = from}};
=> new() { _type = Variant.Type.Vector3, _data = { _m_vector3 = from } };
public static godot_variant CreateFromVector3i(Vector3i from)
=> new() {_type = Variant.Type.Vector3i, _data = {_m_vector3i = from}};
=> new() { _type = Variant.Type.Vector3i, _data = { _m_vector3i = from } };
public static godot_variant CreateFromRect2(Rect2 from)
=> new() {_type = Variant.Type.Rect2, _data = {_m_rect2 = from}};
=> new() { _type = Variant.Type.Rect2, _data = { _m_rect2 = from } };
public static godot_variant CreateFromRect2i(Rect2i from)
=> new() {_type = Variant.Type.Rect2i, _data = {_m_rect2i = from}};
=> new() { _type = Variant.Type.Rect2i, _data = { _m_rect2i = from } };
public static godot_variant CreateFromQuaternion(Quaternion from)
=> new() {_type = Variant.Type.Quaternion, _data = {_m_quaternion = from}};
=> new() { _type = Variant.Type.Quaternion, _data = { _m_quaternion = from } };
public static godot_variant CreateFromColor(Color from)
=> new() {_type = Variant.Type.Color, _data = {_m_color = from}};
=> new() { _type = Variant.Type.Color, _data = { _m_color = from } };
public static godot_variant CreateFromPlane(Plane from)
=> new() {_type = Variant.Type.Plane, _data = {_m_plane = from}};
=> new() { _type = Variant.Type.Plane, _data = { _m_plane = from } };
public static unsafe godot_variant CreateFromTransform2D(Transform2D from)
{
@ -79,15 +79,15 @@ namespace Godot.NativeInterop
// Explicit name to make it very clear
public static godot_variant CreateFromCallableTakingOwnershipOfDisposableValue(godot_callable from)
=> new() {_type = Variant.Type.Callable, _data = {_m_callable = from}};
=> new() { _type = Variant.Type.Callable, _data = { _m_callable = from } };
// Explicit name to make it very clear
public static godot_variant CreateFromSignalTakingOwnershipOfDisposableValue(godot_signal from)
=> new() {_type = Variant.Type.Signal, _data = {_m_signal = from}};
=> new() { _type = Variant.Type.Signal, _data = { _m_signal = from } };
// Explicit name to make it very clear
public static godot_variant CreateFromStringTakingOwnershipOfDisposableValue(godot_string from)
=> new() {_type = Variant.Type.String, _data = {_m_string = from}};
=> new() { _type = Variant.Type.String, _data = { _m_string = from } };
public static unsafe godot_variant CreateFromPackedByteArray(godot_packed_byte_array* from)
{
@ -202,85 +202,135 @@ namespace Godot.NativeInterop
// We avoid the internal call if the stored type is the same we want.
public static unsafe bool ConvertToBool(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Bool ? (*p_var)._data._bool : NativeFuncs.godotsharp_variant_as_bool(p_var);
=> (*p_var)._type == Variant.Type.Bool ?
(*p_var)._data._bool.ToBool() :
NativeFuncs.godotsharp_variant_as_bool(p_var).ToBool();
public static unsafe char ConvertToChar(godot_variant* p_var)
=> (char)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var));
=> (char)((*p_var)._type == Variant.Type.Int ?
(*p_var)._data._int :
NativeFuncs.godotsharp_variant_as_int(p_var));
public static unsafe sbyte ConvertToInt8(godot_variant* p_var)
=> (sbyte)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var));
=> (sbyte)((*p_var)._type == Variant.Type.Int ?
(*p_var)._data._int :
NativeFuncs.godotsharp_variant_as_int(p_var));
public static unsafe Int16 ConvertToInt16(godot_variant* p_var)
=> (Int16)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var));
=> (Int16)((*p_var)._type == Variant.Type.Int ?
(*p_var)._data._int :
NativeFuncs.godotsharp_variant_as_int(p_var));
public static unsafe Int32 ConvertToInt32(godot_variant* p_var)
=> (Int32)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var));
=> (Int32)((*p_var)._type == Variant.Type.Int ?
(*p_var)._data._int :
NativeFuncs.godotsharp_variant_as_int(p_var));
public static unsafe Int64 ConvertToInt64(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var);
public static unsafe byte ConvertToUInt8(godot_variant* p_var)
=> (byte)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var));
=> (byte)((*p_var)._type == Variant.Type.Int ?
(*p_var)._data._int :
NativeFuncs.godotsharp_variant_as_int(p_var));
public static unsafe UInt16 ConvertToUInt16(godot_variant* p_var)
=> (UInt16)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var));
=> (UInt16)((*p_var)._type == Variant.Type.Int ?
(*p_var)._data._int :
NativeFuncs.godotsharp_variant_as_int(p_var));
public static unsafe UInt32 ConvertToUInt32(godot_variant* p_var)
=> (UInt32)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var));
=> (UInt32)((*p_var)._type == Variant.Type.Int ?
(*p_var)._data._int :
NativeFuncs.godotsharp_variant_as_int(p_var));
public static unsafe UInt64 ConvertToUInt64(godot_variant* p_var)
=> (UInt64)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var));
=> (UInt64)((*p_var)._type == Variant.Type.Int ?
(*p_var)._data._int :
NativeFuncs.godotsharp_variant_as_int(p_var));
public static unsafe float ConvertToFloat32(godot_variant* p_var)
=> (float)((*p_var)._type == Variant.Type.Float ? (*p_var)._data._float : NativeFuncs.godotsharp_variant_as_float(p_var));
=> (float)((*p_var)._type == Variant.Type.Float ?
(*p_var)._data._float :
NativeFuncs.godotsharp_variant_as_float(p_var));
public static unsafe double ConvertToFloat64(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Float ? (*p_var)._data._float : NativeFuncs.godotsharp_variant_as_float(p_var);
=> (*p_var)._type == Variant.Type.Float ?
(*p_var)._data._float :
NativeFuncs.godotsharp_variant_as_float(p_var);
public static unsafe Vector2 ConvertToVector2(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Vector2 ? (*p_var)._data._m_vector2 : NativeFuncs.godotsharp_variant_as_vector2(p_var);
=> (*p_var)._type == Variant.Type.Vector2 ?
(*p_var)._data._m_vector2 :
NativeFuncs.godotsharp_variant_as_vector2(p_var);
public static unsafe Vector2i ConvertToVector2i(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Vector2i ? (*p_var)._data._m_vector2i : NativeFuncs.godotsharp_variant_as_vector2i(p_var);
=> (*p_var)._type == Variant.Type.Vector2i ?
(*p_var)._data._m_vector2i :
NativeFuncs.godotsharp_variant_as_vector2i(p_var);
public static unsafe Rect2 ConvertToRect2(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Rect2 ? (*p_var)._data._m_rect2 : NativeFuncs.godotsharp_variant_as_rect2(p_var);
=> (*p_var)._type == Variant.Type.Rect2 ?
(*p_var)._data._m_rect2 :
NativeFuncs.godotsharp_variant_as_rect2(p_var);
public static unsafe Rect2i ConvertToRect2i(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Rect2i ? (*p_var)._data._m_rect2i : NativeFuncs.godotsharp_variant_as_rect2i(p_var);
=> (*p_var)._type == Variant.Type.Rect2i ?
(*p_var)._data._m_rect2i :
NativeFuncs.godotsharp_variant_as_rect2i(p_var);
public static unsafe Transform2D ConvertToTransform2D(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Transform2d ? *(*p_var)._data._transform2d : NativeFuncs.godotsharp_variant_as_transform2d(p_var);
=> (*p_var)._type == Variant.Type.Transform2d ?
*(*p_var)._data._transform2d :
NativeFuncs.godotsharp_variant_as_transform2d(p_var);
public static unsafe Vector3 ConvertToVector3(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Vector3 ? (*p_var)._data._m_vector3 : NativeFuncs.godotsharp_variant_as_vector3(p_var);
=> (*p_var)._type == Variant.Type.Vector3 ?
(*p_var)._data._m_vector3 :
NativeFuncs.godotsharp_variant_as_vector3(p_var);
public static unsafe Vector3i ConvertToVector3i(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Vector3i ? (*p_var)._data._m_vector3i : NativeFuncs.godotsharp_variant_as_vector3i(p_var);
=> (*p_var)._type == Variant.Type.Vector3i ?
(*p_var)._data._m_vector3i :
NativeFuncs.godotsharp_variant_as_vector3i(p_var);
public static unsafe Basis ConvertToBasis(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Basis ? *(*p_var)._data._basis : NativeFuncs.godotsharp_variant_as_basis(p_var);
=> (*p_var)._type == Variant.Type.Basis ?
*(*p_var)._data._basis :
NativeFuncs.godotsharp_variant_as_basis(p_var);
public static unsafe Quaternion ConvertToQuaternion(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Quaternion ? (*p_var)._data._m_quaternion : NativeFuncs.godotsharp_variant_as_quaternion(p_var);
=> (*p_var)._type == Variant.Type.Quaternion ?
(*p_var)._data._m_quaternion :
NativeFuncs.godotsharp_variant_as_quaternion(p_var);
public static unsafe Transform3D ConvertToTransform3D(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Transform3d ? *(*p_var)._data._transform3d : NativeFuncs.godotsharp_variant_as_transform3d(p_var);
=> (*p_var)._type == Variant.Type.Transform3d ?
*(*p_var)._data._transform3d :
NativeFuncs.godotsharp_variant_as_transform3d(p_var);
public static unsafe AABB ConvertToAABB(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Aabb ? *(*p_var)._data._aabb : NativeFuncs.godotsharp_variant_as_aabb(p_var);
=> (*p_var)._type == Variant.Type.Aabb ?
*(*p_var)._data._aabb :
NativeFuncs.godotsharp_variant_as_aabb(p_var);
public static unsafe Color ConvertToColor(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Color ? (*p_var)._data._m_color : NativeFuncs.godotsharp_variant_as_color(p_var);
=> (*p_var)._type == Variant.Type.Color ?
(*p_var)._data._m_color :
NativeFuncs.godotsharp_variant_as_color(p_var);
public static unsafe Plane ConvertToPlane(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Plane ? (*p_var)._data._m_plane : NativeFuncs.godotsharp_variant_as_plane(p_var);
=> (*p_var)._type == Variant.Type.Plane ?
(*p_var)._data._m_plane :
NativeFuncs.godotsharp_variant_as_plane(p_var);
public static unsafe IntPtr ConvertToGodotObject(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Object ? (*p_var)._data._m_obj_data.obj : IntPtr.Zero;
public static unsafe RID ConvertToRID(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Rid ? (*p_var)._data._m_rid : NativeFuncs.godotsharp_variant_as_rid(p_var);
=> (*p_var)._type == Variant.Type.Rid ?
(*p_var)._data._m_rid :
NativeFuncs.godotsharp_variant_as_rid(p_var);
public static unsafe godot_string_name ConvertToStringName(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.StringName ?

View File

@ -6,7 +6,7 @@ namespace Godot
{
public sealed class NodePath : IDisposable
{
internal godot_node_path NativeValue;
public godot_node_path NativeValue;
~NodePath()
{
@ -57,67 +57,52 @@ namespace Godot
godot_node_path src = NativeValue;
NativeFuncs.godotsharp_node_path_as_string(&dest, &src);
using (dest)
return Marshaling.mono_string_from_godot(&dest);
return Marshaling.mono_string_from_godot(dest);
}
public NodePath GetAsPropertyPath()
{
godot_node_path propertyPath = default;
godot_icall_NodePath_get_as_property_path(ref NativeValue, ref propertyPath);
NativeFuncs.godotsharp_node_path_get_as_property_path(ref NativeValue, ref propertyPath);
return CreateTakingOwnershipOfDisposableValue(propertyPath);
}
public string GetConcatenatedSubNames()
public unsafe string GetConcatenatedSubNames()
{
return godot_icall_NodePath_get_concatenated_subnames(ref NativeValue);
using godot_string subNames = default;
NativeFuncs.godotsharp_node_path_get_concatenated_subnames(ref NativeValue, &subNames);
return Marshaling.mono_string_from_godot(subNames);
}
public string GetName(int idx)
public unsafe string GetName(int idx)
{
return godot_icall_NodePath_get_name(ref NativeValue, idx);
using godot_string name = default;
NativeFuncs.godotsharp_node_path_get_name(ref NativeValue, idx, &name);
return Marshaling.mono_string_from_godot(name);
}
public int GetNameCount()
{
return godot_icall_NodePath_get_name_count(ref NativeValue);
return NativeFuncs.godotsharp_node_path_get_name_count(ref NativeValue);
}
public string GetSubName(int idx)
public unsafe string GetSubName(int idx)
{
return godot_icall_NodePath_get_subname(ref NativeValue, idx);
using godot_string subName = default;
NativeFuncs.godotsharp_node_path_get_subname(ref NativeValue, idx, &subName);
return Marshaling.mono_string_from_godot(subName);
}
public int GetSubNameCount()
{
return godot_icall_NodePath_get_subname_count(ref NativeValue);
return NativeFuncs.godotsharp_node_path_get_subname_count(ref NativeValue);
}
public bool IsAbsolute()
{
return godot_icall_NodePath_is_absolute(ref NativeValue);
return NativeFuncs.godotsharp_node_path_is_absolute(ref NativeValue).ToBool();
}
public bool IsEmpty => godot_node_path.IsEmpty(in NativeValue);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void godot_icall_NodePath_get_as_property_path(ref godot_node_path ptr, ref godot_node_path dest);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string godot_icall_NodePath_get_concatenated_subnames(ref godot_node_path ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string godot_icall_NodePath_get_name(ref godot_node_path ptr, int arg1);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern int godot_icall_NodePath_get_name_count(ref godot_node_path ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string godot_icall_NodePath_get_subname(ref godot_node_path ptr, int arg1);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern int godot_icall_NodePath_get_subname_count(ref godot_node_path ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool godot_icall_NodePath_is_absolute(ref godot_node_path ptr);
}
}

View File

@ -1,11 +1,15 @@
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Godot.NativeInterop;
namespace Godot
{
public partial class Object : IDisposable
{
private bool _disposed = false;
private Type _cachedType = typeof(Object);
internal IntPtr NativePtr;
internal bool MemoryOwn;
@ -14,15 +18,17 @@ namespace Godot
{
if (NativePtr == IntPtr.Zero)
{
#if NET
unsafe
{
ptr = NativeCtor();
NativePtr = NativeCtor();
}
#else
NativePtr = _gd__invoke_class_constructor(NativeCtor);
#endif
NativeInterop.InteropUtils.TieManagedToUnmanaged(this, NativePtr);
InteropUtils.TieManagedToUnmanaged(this, NativePtr,
NativeName, refCounted: false, GetType(), _cachedType);
}
else
{
InteropUtils.TieManagedToUnmanagedWithPreSetup(this, NativePtr);
}
_InitializeGodotScriptInstanceInternals();
@ -30,12 +36,32 @@ namespace Godot
internal void _InitializeGodotScriptInstanceInternals()
{
godot_icall_Object_ConnectEventSignals(NativePtr);
// Performance is not critical here as this will be replaced with source generators.
Type top = GetType();
Type native = InternalGetClassNativeBase(top);
while (top != null && top != native)
{
foreach (var eventSignal in top.GetEvents(
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public)
.Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any()))
{
unsafe
{
using var eventSignalName = new StringName(eventSignal.Name);
godot_string_name eventSignalNameAux = eventSignalName.NativeValue;
NativeFuncs.godotsharp_internal_object_connect_event_signal(NativePtr, &eventSignalNameAux);
}
}
top = top.BaseType;
}
}
internal Object(bool memoryOwn)
{
this.MemoryOwn = memoryOwn;
MemoryOwn = memoryOwn;
}
public IntPtr NativeInstance => NativePtr;
@ -72,22 +98,24 @@ namespace Godot
if (MemoryOwn)
{
MemoryOwn = false;
godot_icall_RefCounted_Disposed(this, NativePtr, !disposing);
NativeFuncs.godotsharp_internal_refcounted_disposed(NativePtr, (!disposing).ToGodotBool());
}
else
{
godot_icall_Object_Disposed(this, NativePtr);
NativeFuncs.godotsharp_internal_object_disposed(NativePtr);
}
this.NativePtr = IntPtr.Zero;
NativePtr = IntPtr.Zero;
}
_disposed = true;
}
public override string ToString()
public override unsafe string ToString()
{
return godot_icall_Object_ToString(GetPtr(this));
using godot_string str = default;
NativeFuncs.godotsharp_object_to_string(GetPtr(this), &str);
return Marshaling.mono_string_from_godot(str);
}
/// <summary>
@ -118,18 +146,219 @@ namespace Godot
return new SignalAwaiter(source, signal, this);
}
/// <summary>
/// Gets a new <see cref="Godot.DynamicGodotObject"/> associated with this instance.
/// </summary>
public dynamic DynamicObject => new DynamicGodotObject(this);
internal static Type InternalGetClassNativeBase(Type t)
{
do
{
var assemblyName = t.Assembly.GetName();
if (assemblyName.Name == "GodotSharp")
return t;
if (assemblyName.Name == "GodotSharpEditor")
return t;
} while ((t = t.BaseType) != null);
return null;
}
internal static bool InternalIsClassNativeBase(Type t)
{
var assemblyName = t.Assembly.GetName();
return assemblyName.Name == "GodotSharp" || assemblyName.Name == "GodotSharpEditor";
}
internal unsafe bool InternalGodotScriptCallViaReflection(string method, godot_variant** args, int argCount,
out godot_variant ret)
{
// Performance is not critical here as this will be replaced with source generators.
Type top = GetType();
Type native = InternalGetClassNativeBase(top);
while (top != null && top != native)
{
var methodInfo = top.GetMethod(method,
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
if (methodInfo != null)
{
var parameters = methodInfo.GetParameters();
int paramCount = parameters.Length;
if (argCount == paramCount)
{
object[] invokeParams = new object[paramCount];
for (int i = 0; i < paramCount; i++)
{
invokeParams[i] = Marshaling.variant_to_mono_object_of_type(
args[i], parameters[i].ParameterType);
}
object retObj = methodInfo.Invoke(this, invokeParams);
ret = Marshaling.mono_object_to_variant(retObj);
return true;
}
}
top = top.BaseType;
}
ret = default;
return false;
}
internal unsafe bool InternalGodotScriptSetFieldOrPropViaReflection(string name, godot_variant* value)
{
// Performance is not critical here as this will be replaced with source generators.
Type top = GetType();
Type native = InternalGetClassNativeBase(top);
while (top != null && top != native)
{
var fieldInfo = top.GetField(name,
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
if (fieldInfo != null)
{
object valueManaged = Marshaling.variant_to_mono_object_of_type(value, fieldInfo.FieldType);
fieldInfo.SetValue(this, valueManaged);
return true;
}
var propertyInfo = top.GetProperty(name,
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
if (propertyInfo != null)
{
object valueManaged = Marshaling.variant_to_mono_object_of_type(value, propertyInfo.PropertyType);
propertyInfo.SetValue(this, valueManaged);
return true;
}
top = top.BaseType;
}
return false;
}
internal bool InternalGodotScriptGetFieldOrPropViaReflection(string name, out godot_variant value)
{
// Performance is not critical here as this will be replaced with source generators.
Type top = GetType();
Type native = InternalGetClassNativeBase(top);
while (top != null && top != native)
{
var fieldInfo = top.GetField(name,
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
if (fieldInfo != null)
{
object valueManaged = fieldInfo.GetValue(this);
value = Marshaling.mono_object_to_variant(valueManaged);
return true;
}
var propertyInfo = top.GetProperty(name,
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
if (propertyInfo != null)
{
object valueManaged = propertyInfo.GetValue(this);
value = Marshaling.mono_object_to_variant(valueManaged);
return true;
}
top = top.BaseType;
}
value = default;
return false;
}
internal unsafe void InternalRaiseEventSignal(godot_string_name* eventSignalName, godot_variant** args,
int argc)
{
// Performance is not critical here as this will be replaced with source generators.
using var stringName = StringName.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_string_name_new_copy(eventSignalName));
string eventSignalNameStr = stringName.ToString();
Type top = GetType();
Type native = InternalGetClassNativeBase(top);
while (top != null && top != native)
{
var foundEventSignals = top.GetEvents(
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public)
.Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any())
.Select(ev => ev.Name);
var fields = top.GetFields(
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
var eventSignalField = fields
.Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType))
.Where(f => foundEventSignals.Contains(f.Name))
.FirstOrDefault(f => f.Name == eventSignalNameStr);
if (eventSignalField != null)
{
var @delegate = (Delegate)eventSignalField.GetValue(this);
if (@delegate == null)
continue;
var delegateType = eventSignalField.FieldType;
var invokeMethod = delegateType.GetMethod("Invoke");
if (invokeMethod == null)
throw new MissingMethodException(delegateType.FullName, "Invoke");
var parameterInfos = invokeMethod.GetParameters();
var paramsLength = parameterInfos.Length;
if (argc != paramsLength)
{
throw new InvalidOperationException(
$"The event delegate expects {paramsLength} arguments, but received {argc}.");
}
var managedArgs = new object[argc];
for (uint i = 0; i < argc; i++)
{
managedArgs[i] = Marshaling.variant_to_mono_object_of_type(
args[i], parameterInfos[i].ParameterType);
}
invokeMethod.Invoke(@delegate, managedArgs);
return;
}
top = top.BaseType;
}
}
internal static unsafe IntPtr ClassDB_get_method(StringName type, string method)
{
IntPtr methodBind;
fixed (char* methodChars = method)
{
methodBind = NativeInterop.NativeFuncs
.godotsharp_method_bind_get_method(ref type.NativeValue, methodChars);
methodBind = NativeFuncs.godotsharp_method_bind_get_method(ref type.NativeValue, methodChars);
}
if (methodBind == IntPtr.Zero)
@ -138,45 +367,15 @@ namespace Godot
return methodBind;
}
#if NET
internal static unsafe delegate* unmanaged<IntPtr> _gd__ClassDB_get_constructor(StringName type)
internal static unsafe delegate* unmanaged<IntPtr> ClassDB_get_constructor(StringName type)
{
// for some reason the '??' operator doesn't support 'delegate*'
var nativeConstructor = NativeInterop.NativeFuncs
.godotsharp_get_class_constructor(ref type.NativeValue);
var nativeConstructor = NativeFuncs.godotsharp_get_class_constructor(ref type.NativeValue);
if (nativeConstructor == null)
throw new NativeConstructorNotFoundException(type);
return nativeConstructor;
}
#else
internal static IntPtr ClassDB_get_constructor(StringName type)
{
// for some reason the '??' operator doesn't support 'delegate*'
var nativeConstructor = NativeInterop.NativeFuncs
.godotsharp_get_class_constructor(ref type.NativeValue);
if (nativeConstructor == IntPtr.Zero)
throw new NativeConstructorNotFoundException(type);
return nativeConstructor;
}
internal static IntPtr _gd__invoke_class_constructor(IntPtr ctorFuncPtr)
=> NativeInterop.NativeFuncs.godotsharp_invoke_class_constructor(ctorFuncPtr);
#endif
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Object_Disposed(Object obj, IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_RefCounted_Disposed(Object obj, IntPtr ptr, bool isFinalizer);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Object_ConnectEventSignals(IntPtr obj);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern string godot_icall_Object_ToString(IntPtr ptr);
}
}

View File

@ -1,5 +1,5 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Godot.NativeInterop;
namespace Godot
@ -12,12 +12,10 @@ namespace Godot
public SignalAwaiter(Object source, StringName signal, Object target)
{
godot_icall_SignalAwaiter_connect(Object.GetPtr(source), ref signal.NativeValue, Object.GetPtr(target), this);
NativeFuncs.godotsharp_internal_signal_awaiter_connect(Object.GetPtr(source), ref signal.NativeValue,
Object.GetPtr(target), GCHandle.ToIntPtr(GCHandle.Alloc(this)));
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, ref godot_string_name signal, IntPtr target, SignalAwaiter awaiter);
public bool IsCompleted => _completed;
public void OnCompleted(Action action)
@ -29,14 +27,40 @@ namespace Godot
public IAwaiter<object[]> GetAwaiter() => this;
internal void SignalCallback(object[] args)
[UnmanagedCallersOnly]
internal static unsafe void SignalCallback(IntPtr awaiterGCHandlePtr, godot_variant** args, int argCount,
godot_bool* outAwaiterIsNull)
{
_completed = true;
_result = args;
if (_action != null)
try
{
_action();
var awaiter = (SignalAwaiter)GCHandle.FromIntPtr(awaiterGCHandlePtr).Target;
if (awaiter == null)
{
*outAwaiterIsNull = true.ToGodotBool();
return;
}
*outAwaiterIsNull = false.ToGodotBool();
awaiter._completed = true;
object[] signalArgs = new object[argCount];
for (int i = 0; i < argCount; i++)
signalArgs[i] = Marshaling.variant_to_mono_object(args[i]);
awaiter._result = signalArgs;
if (awaiter._action != null)
{
awaiter._action();
}
}
catch (Exception e)
{
ExceptionUtils.DebugPrintUnhandledException(e);
*outAwaiterIsNull = false.ToGodotBool();
}
}
}

View File

@ -5,6 +5,7 @@ using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using System.Text.RegularExpressions;
using Godot.NativeInterop;
namespace Godot
{
@ -157,6 +158,7 @@ namespace Godot
{
return 0;
}
if (from == 0 && to == len)
{
str = instance;
@ -355,7 +357,8 @@ namespace Godot
/// <returns>The starting position of the substring, or -1 if not found.</returns>
public static int Find(this string instance, string what, int from = 0, bool caseSensitive = true)
{
return instance.IndexOf(what, from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
return instance.IndexOf(what, from,
caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
}
/// <summary>
@ -366,7 +369,8 @@ namespace Godot
{
// TODO: Could be more efficient if we get a char version of `IndexOf`.
// See https://github.com/dotnet/runtime/issues/44116
return instance.IndexOf(what.ToString(), from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
return instance.IndexOf(what.ToString(), from,
caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
}
/// <summary>Find the last occurrence of a substring.</summary>
@ -380,7 +384,8 @@ namespace Godot
/// <returns>The starting position of the substring, or -1 if not found.</returns>
public static int FindLast(this string instance, string what, int from, bool caseSensitive = true)
{
return instance.LastIndexOf(what, from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
return instance.LastIndexOf(what, from,
caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
}
/// <summary>
@ -623,6 +628,7 @@ namespace Godot
{
match = instance[source] == text[target];
}
if (match)
{
source++;
@ -682,9 +688,9 @@ namespace Godot
}
bool validChar = instance[i] >= '0' &&
instance[i] <= '9' || instance[i] >= 'a' &&
instance[i] <= 'z' || instance[i] >= 'A' &&
instance[i] <= 'Z' || instance[i] == '_';
instance[i] <= '9' || instance[i] >= 'a' &&
instance[i] <= 'z' || instance[i] >= 'A' &&
instance[i] <= 'Z' || instance[i] == '_';
if (!validChar)
return false;
@ -808,15 +814,18 @@ namespace Godot
switch (expr[0])
{
case '*':
return ExprMatch(instance, expr.Substring(1), caseSensitive) || (instance.Length > 0 && ExprMatch(instance.Substring(1), expr, caseSensitive));
return ExprMatch(instance, expr.Substring(1), caseSensitive) || (instance.Length > 0 &&
ExprMatch(instance.Substring(1), expr, caseSensitive));
case '?':
return instance.Length > 0 && instance[0] != '.' && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
return instance.Length > 0 && instance[0] != '.' &&
ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
default:
if (instance.Length == 0)
return false;
if (caseSensitive)
return instance[0] == expr[0];
return (char.ToUpper(instance[0]) == char.ToUpper(expr[0])) && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
return (char.ToUpper(instance[0]) == char.ToUpper(expr[0])) &&
ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
}
}
@ -847,25 +856,25 @@ namespace Godot
/// <summary>
/// Return the MD5 hash of the string as an array of bytes.
/// </summary>
public static byte[] MD5Buffer(this string instance)
public static unsafe byte[] MD5Buffer(this string instance)
{
return godot_icall_String_md5_buffer(instance);
using godot_string instanceStr = Marshaling.mono_string_to_godot(instance);
using godot_packed_byte_array md5Buffer = default;
NativeFuncs.godotsharp_string_md5_buffer(&instanceStr, &md5Buffer);
return Marshaling.PackedByteArray_to_mono_array(&md5Buffer);
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static byte[] godot_icall_String_md5_buffer(string str);
/// <summary>
/// Return the MD5 hash of the string as a string.
/// </summary>
public static string MD5Text(this string instance)
public static unsafe string MD5Text(this string instance)
{
return godot_icall_String_md5_text(instance);
using godot_string instanceStr = Marshaling.mono_string_to_godot(instance);
using godot_string md5Text = default;
NativeFuncs.godotsharp_string_md5_text(&instanceStr, &md5Text);
return Marshaling.mono_string_from_godot(md5Text);
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static string godot_icall_String_md5_text(string str);
/// <summary>
/// Perform a case-insensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater.
/// </summary>
@ -981,26 +990,24 @@ namespace Godot
/// <summary>
/// Perform a search for a substring, but start from the end of the string instead of the beginning.
/// </summary>
public static int RFind(this string instance, string what, int from = -1)
public static unsafe int RFind(this string instance, string what, int from = -1)
{
return godot_icall_String_rfind(instance, what, from);
using godot_string instanceStr = Marshaling.mono_string_to_godot(instance);
using godot_string whatStr = Marshaling.mono_string_to_godot(instance);
return NativeFuncs.godotsharp_string_rfind(&instanceStr, &whatStr, from);
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static int godot_icall_String_rfind(string str, string what, int from);
/// <summary>
/// Perform a search for a substring, but start from the end of the string instead of the beginning.
/// Also search case-insensitive.
/// </summary>
public static int RFindN(this string instance, string what, int from = -1)
public static unsafe int RFindN(this string instance, string what, int from = -1)
{
return godot_icall_String_rfindn(instance, what, from);
using godot_string instanceStr = Marshaling.mono_string_to_godot(instance);
using godot_string whatStr = Marshaling.mono_string_to_godot(instance);
return NativeFuncs.godotsharp_string_rfindn(&instanceStr, &whatStr, from);
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static int godot_icall_String_rfindn(string str, string what, int from);
/// <summary>
/// Return the right side of the string from a given position.
/// </summary>
@ -1042,25 +1049,25 @@ namespace Godot
return instance.Substr(0, end + 1);
}
public static byte[] SHA256Buffer(this string instance)
public static unsafe byte[] SHA256Buffer(this string instance)
{
return godot_icall_String_sha256_buffer(instance);
using godot_string instanceStr = Marshaling.mono_string_to_godot(instance);
using godot_packed_byte_array sha256Buffer = default;
NativeFuncs.godotsharp_string_sha256_buffer(&instanceStr, &sha256Buffer);
return Marshaling.PackedByteArray_to_mono_array(&sha256Buffer);
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static byte[] godot_icall_String_sha256_buffer(string str);
/// <summary>
/// Return the SHA-256 hash of the string as a string.
/// </summary>
public static string SHA256Text(this string instance)
public static unsafe string SHA256Text(this string instance)
{
return godot_icall_String_sha256_text(instance);
using godot_string instanceStr = Marshaling.mono_string_to_godot(instance);
using godot_string sha256Text = default;
NativeFuncs.godotsharp_string_sha256_text(&instanceStr, &sha256Text);
return Marshaling.mono_string_from_godot(sha256Text);
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static string godot_icall_String_sha256_text(string str);
/// <summary>
/// Return the similarity index of the text compared to this string.
/// 1 means totally similar and 0 means totally dissimilar.
@ -1072,6 +1079,7 @@ namespace Godot
// Equal strings are totally similar
return 1.0f;
}
if (instance.Length < 2 || text.Length < 2)
{
// No way to calculate similarity without a single bigram
@ -1108,7 +1116,8 @@ namespace Godot
/// </summary>
public static string[] Split(this string instance, string divisor, bool allowEmpty = true)
{
return instance.Split(new[] { divisor }, allowEmpty ? StringSplitOptions.None : StringSplitOptions.RemoveEmptyEntries);
return instance.Split(new[] { divisor },
allowEmpty ? StringSplitOptions.None : StringSplitOptions.RemoveEmptyEntries);
}
/// <summary>
@ -1137,7 +1146,8 @@ namespace Godot
return ret.ToArray();
}
private static readonly char[] _nonPrintable = {
private static readonly char[] _nonPrintable =
{
(char)00, (char)01, (char)02, (char)03, (char)04, (char)05,
(char)06, (char)07, (char)08, (char)09, (char)10, (char)11,
(char)12, (char)13, (char)14, (char)15, (char)16, (char)17,

View File

@ -6,7 +6,7 @@ namespace Godot
{
public sealed class StringName : IDisposable
{
internal godot_string_name NativeValue;
public godot_string_name NativeValue;
~StringName()
{
@ -57,7 +57,7 @@ namespace Godot
godot_string_name src = NativeValue;
NativeFuncs.godotsharp_string_name_as_string(&dest, &src);
using (dest)
return Marshaling.mono_string_from_godot(&dest);
return Marshaling.mono_string_from_godot(dest);
}
public bool IsEmpty => godot_string_name.IsEmpty(in NativeValue);

View File

@ -4,7 +4,7 @@
<OutputPath>bin/$(Configuration)</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace>Godot</RootNamespace>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile>
<EnableDefaultItems>false</EnableDefaultItems>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@ -32,6 +32,10 @@
<Compile Include="Core\Attributes\SignalAttribute.cs" />
<Compile Include="Core\Attributes\ToolAttribute.cs" />
<Compile Include="Core\Basis.cs" />
<Compile Include="Core\Bridge\CSharpInstanceBridge.cs" />
<Compile Include="Core\Bridge\GCHandleBridge.cs" />
<Compile Include="Core\Bridge\ManagedCallbacks.cs" />
<Compile Include="Core\Bridge\ScriptManagerBridge.cs" />
<Compile Include="Core\Callable.cs" />
<Compile Include="Core\Color.cs" />
<Compile Include="Core\Colors.cs" />
@ -39,7 +43,6 @@
<Compile Include="Core\DelegateUtils.cs" />
<Compile Include="Core\Dictionary.cs" />
<Compile Include="Core\Dispatcher.cs" />
<Compile Include="Core\DynamicObject.cs" />
<Compile Include="Core\Extensions\NodeExtensions.cs" />
<Compile Include="Core\Extensions\ObjectExtensions.cs" />
<Compile Include="Core\Extensions\PackedSceneExtensions.cs" />
@ -55,6 +58,7 @@
<Compile Include="Core\Interfaces\ISerializationListener.cs" />
<Compile Include="Core\Mathf.cs" />
<Compile Include="Core\MathfEx.cs" />
<Compile Include="Core\NativeInterop\ExceptionUtils.cs" />
<Compile Include="Core\NativeInterop\InteropUtils.cs" />
<Compile Include="Core\NativeInterop\NativeFuncs.extended.cs" />
<Compile Include="Core\NativeInterop\VariantSpanHelpers.cs" />

View File

@ -1,3 +1,4 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("GodotSharpEditor")]
[assembly: InternalsVisibleTo("GodotPlugins")]

View File

@ -4,7 +4,7 @@
<OutputPath>bin/$(Configuration)</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace>Godot</RootNamespace>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile>
<EnableDefaultItems>false</EnableDefaultItems>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

View File

@ -1,67 +0,0 @@
/*************************************************************************/
/* arguments_vector.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
#ifndef ARGUMENTS_VECTOR_H
#define ARGUMENTS_VECTOR_H
#include "core/os/memory.h"
template <typename T, int POOL_SIZE = 5>
struct ArgumentsVector {
private:
T pool[POOL_SIZE];
T *_ptr;
int size;
ArgumentsVector() = delete;
ArgumentsVector(const ArgumentsVector &) = delete;
public:
T *ptr() { return _ptr; }
T &get(int p_idx) { return _ptr[p_idx]; }
void set(int p_idx, const T &p_value) { _ptr[p_idx] = p_value; }
explicit ArgumentsVector(int p_size) :
size(p_size) {
if (p_size <= POOL_SIZE) {
_ptr = pool;
} else {
_ptr = memnew_arr(T, p_size);
}
}
~ArgumentsVector() {
if (size > POOL_SIZE) {
memdelete_arr(_ptr);
}
}
};
#endif // ARGUMENTS_VECTOR_H

View File

@ -1,239 +0,0 @@
/*************************************************************************/
/* base_object_glue.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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 "core/object/class_db.h"
#include "core/object/ref_counted.h"
#include "core/string/string_name.h"
#include "../csharp_script.h"
#include "../mono_gd/gd_mono_cache.h"
#include "../mono_gd/gd_mono_class.h"
#include "../mono_gd/gd_mono_internals.h"
#include "../mono_gd/gd_mono_marshal.h"
#include "../mono_gd/gd_mono_utils.h"
#include "../signal_awaiter_utils.h"
#include "arguments_vector.h"
void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_ptr == nullptr);
#endif
if (p_ptr->get_script_instance()) {
CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
if (cs_instance) {
if (!cs_instance->is_destructing_script_instance()) {
cs_instance->mono_object_disposed(p_obj);
p_ptr->set_script_instance(nullptr);
}
return;
}
}
void *data = CSharpLanguage::get_existing_instance_binding(p_ptr);
if (data) {
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
if (script_binding.inited) {
MonoGCHandleData &gchandle = script_binding.gchandle;
if (!gchandle.is_released()) {
CSharpLanguage::release_script_gchandle(p_obj, gchandle);
}
}
}
}
void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolean p_is_finalizer) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_ptr == nullptr);
// This is only called with RefCounted derived classes
CRASH_COND(!Object::cast_to<RefCounted>(p_ptr));
#endif
RefCounted *rc = static_cast<RefCounted *>(p_ptr);
if (rc->get_script_instance()) {
CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(rc->get_script_instance());
if (cs_instance) {
if (!cs_instance->is_destructing_script_instance()) {
bool delete_owner;
bool remove_script_instance;
cs_instance->mono_object_disposed_baseref(p_obj, p_is_finalizer, delete_owner, remove_script_instance);
if (delete_owner) {
memdelete(rc);
} else if (remove_script_instance) {
rc->set_script_instance(nullptr);
}
}
return;
}
}
// Unsafe refcount decrement. The managed instance also counts as a reference.
// See: CSharpLanguage::alloc_instance_binding_data(Object *p_object)
CSharpLanguage::get_singleton()->pre_unsafe_unreference(rc);
if (rc->unreference()) {
memdelete(rc);
} else {
void *data = CSharpLanguage::get_existing_instance_binding(rc);
if (data) {
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
if (script_binding.inited) {
MonoGCHandleData &gchandle = script_binding.gchandle;
if (!gchandle.is_released()) {
CSharpLanguage::release_script_gchandle(p_obj, gchandle);
}
}
}
}
}
void godot_icall_Object_ConnectEventSignals(Object *p_ptr) {
CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
if (csharp_instance) {
csharp_instance->connect_event_signals();
}
}
MonoObject *godot_icall_Object_weakref(Object *p_ptr) {
if (!p_ptr) {
return nullptr;
}
Ref<WeakRef> wref;
RefCounted *rc = Object::cast_to<RefCounted>(p_ptr);
if (rc) {
REF r = rc;
if (!r.is_valid()) {
return nullptr;
}
wref.instantiate();
wref->set_ref(r);
} else {
wref.instantiate();
wref->set_obj(p_ptr);
}
return GDMonoUtils::unmanaged_get_managed(wref.ptr());
}
int32_t godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter) {
StringName signal = p_signal ? *p_signal : StringName();
return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter);
}
MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr) {
List<PropertyInfo> property_list;
p_ptr->get_property_list(&property_list);
MonoArray *result = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), property_list.size());
int i = 0;
for (const PropertyInfo &E : property_list) {
MonoString *boxed = GDMonoMarshal::mono_string_from_godot(E.name);
mono_array_setref(result, i, boxed);
i++;
}
return result;
}
MonoBoolean godot_icall_DynamicGodotObject_InvokeMember(Object *p_ptr, MonoString *p_name, MonoArray *p_args, MonoObject **r_result) {
String name = GDMonoMarshal::mono_string_to_godot(p_name);
int argc = mono_array_length(p_args);
ArgumentsVector<Variant> arg_store(argc);
ArgumentsVector<const Variant *> args(argc);
for (int i = 0; i < argc; i++) {
MonoObject *elem = mono_array_get(p_args, MonoObject *, i);
arg_store.set(i, GDMonoMarshal::mono_object_to_variant(elem));
args.set(i, &arg_store.get(i));
}
Callable::CallError error;
Variant result = p_ptr->call(StringName(name), args.ptr(), argc, error);
*r_result = GDMonoMarshal::variant_to_mono_object(result);
return error.error == Callable::CallError::CALL_OK;
}
MonoBoolean godot_icall_DynamicGodotObject_GetMember(Object *p_ptr, MonoString *p_name, MonoObject **r_result) {
String name = GDMonoMarshal::mono_string_to_godot(p_name);
bool valid;
Variant value = p_ptr->get(StringName(name), &valid);
if (valid) {
*r_result = GDMonoMarshal::variant_to_mono_object(value);
}
return valid;
}
MonoBoolean godot_icall_DynamicGodotObject_SetMember(Object *p_ptr, MonoString *p_name, MonoObject *p_value) {
String name = GDMonoMarshal::mono_string_to_godot(p_name);
Variant value = GDMonoMarshal::mono_object_to_variant(p_value);
bool valid;
p_ptr->set(StringName(name), value, &valid);
return valid;
}
MonoString *godot_icall_Object_ToString(Object *p_ptr) {
#ifdef DEBUG_ENABLED
// Cannot happen in C#; would get an ObjectDisposedException instead.
CRASH_COND(p_ptr == nullptr);
#endif
// Can't call 'Object::to_string()' here, as that can end up calling 'ToString' again resulting in an endless circular loop.
String result = "[" + p_ptr->get_class() + ":" + itos(p_ptr->get_instance_id()) + "]";
return GDMonoMarshal::mono_string_from_godot(result);
}
void godot_register_object_icalls() {
GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_Disposed", godot_icall_Object_Disposed);
GDMonoUtils::add_internal_call("Godot.Object::godot_icall_RefCounted_Disposed", godot_icall_RefCounted_Disposed);
GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ConnectEventSignals", godot_icall_Object_ConnectEventSignals);
GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ToString", godot_icall_Object_ToString);
GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_weakref", godot_icall_Object_weakref);
GDMonoUtils::add_internal_call("Godot.SignalAwaiter::godot_icall_SignalAwaiter_connect", godot_icall_SignalAwaiter_connect);
GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMemberList", godot_icall_DynamicGodotObject_SetMemberList);
GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_InvokeMember", godot_icall_DynamicGodotObject_InvokeMember);
GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_GetMember", godot_icall_DynamicGodotObject_GetMember);
GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMember", godot_icall_DynamicGodotObject_SetMember);
}

View File

@ -1,300 +0,0 @@
/*************************************************************************/
/* collections_glue.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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 <mono/metadata/exception.h>
#include "core/variant/array.h"
#include "../mono_gd/gd_mono_cache.h"
#include "../mono_gd/gd_mono_class.h"
#include "../mono_gd/gd_mono_marshal.h"
#include "../mono_gd/gd_mono_utils.h"
void godot_icall_Array_Ctor(Array *r_dest) {
memnew_placement(r_dest, Array);
}
void godot_icall_Array_At(Array *ptr, int32_t index, Variant *r_elem) {
if (index < 0 || index >= ptr->size()) {
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
*r_elem = Variant();
return;
}
*r_elem = ptr->operator[](index);
}
void godot_icall_Array_SetAt(Array *ptr, int32_t index, MonoObject *value) {
if (index < 0 || index >= ptr->size()) {
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
return;
}
ptr->operator[](index) = GDMonoMarshal::mono_object_to_variant(value);
}
int32_t godot_icall_Array_Count(Array *ptr) {
return ptr->size();
}
int32_t godot_icall_Array_Add(Array *ptr, MonoObject *item) {
ptr->append(GDMonoMarshal::mono_object_to_variant(item));
return ptr->size();
}
void godot_icall_Array_Clear(Array *ptr) {
ptr->clear();
}
MonoBoolean godot_icall_Array_Contains(Array *ptr, MonoObject *item) {
return ptr->find(GDMonoMarshal::mono_object_to_variant(item)) != -1;
}
void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int32_t array_index) {
unsigned int count = ptr->size();
if (mono_array_length(array) < (array_index + count)) {
MonoException *exc = mono_get_exception_argument("", "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
GDMonoUtils::set_pending_exception(exc);
return;
}
for (unsigned int i = 0; i < count; i++) {
MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(ptr->operator[](i));
mono_array_setref(array, array_index, boxed);
array_index++;
}
}
void godot_icall_Array_Ctor_MonoArray(MonoArray *mono_array, Array *r_dest) {
memnew_placement(r_dest, Array);
unsigned int count = mono_array_length(mono_array);
r_dest->resize(count);
for (unsigned int i = 0; i < count; i++) {
MonoObject *item = mono_array_get(mono_array, MonoObject *, i);
godot_icall_Array_SetAt(r_dest, i, item);
}
}
void godot_icall_Array_Duplicate(Array *ptr, MonoBoolean deep, Array *r_dest) {
memnew_placement(r_dest, Array(ptr->duplicate(deep)));
}
void godot_icall_Array_Concatenate(Array *left, Array *right, Array *r_dest) {
int count = left->size() + right->size();
memnew_placement(r_dest, Array(left->duplicate(false)));
r_dest->resize(count);
for (unsigned int i = 0; i < (unsigned int)right->size(); i++) {
r_dest->operator[](i + left->size()) = right->operator[](i);
}
}
int32_t godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) {
return ptr->find(GDMonoMarshal::mono_object_to_variant(item));
}
void godot_icall_Array_Insert(Array *ptr, int32_t index, MonoObject *item) {
if (index < 0 || index > ptr->size()) {
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
return;
}
ptr->insert(index, GDMonoMarshal::mono_object_to_variant(item));
}
MonoBoolean godot_icall_Array_Remove(Array *ptr, MonoObject *item) {
int idx = ptr->find(GDMonoMarshal::mono_object_to_variant(item));
if (idx >= 0) {
ptr->remove(idx);
return true;
}
return false;
}
void godot_icall_Array_RemoveAt(Array *ptr, int32_t index) {
if (index < 0 || index >= ptr->size()) {
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
return;
}
ptr->remove(index);
}
int32_t godot_icall_Array_Resize(Array *ptr, int32_t new_size) {
return (int32_t)ptr->resize(new_size);
}
void godot_icall_Array_Shuffle(Array *ptr) {
ptr->shuffle();
}
MonoString *godot_icall_Array_ToString(Array *ptr) {
return GDMonoMarshal::mono_string_from_godot(Variant(*ptr).operator String());
}
void godot_icall_Dictionary_Ctor(Dictionary *r_dest) {
memnew_placement(r_dest, Dictionary);
}
void godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key, Variant *r_value) {
Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
if (ret == nullptr) {
MonoObject *exc = mono_object_new(mono_domain_get(), CACHED_CLASS(KeyNotFoundException)->get_mono_ptr());
#ifdef DEBUG_ENABLED
CRASH_COND(!exc);
#endif
GDMonoUtils::runtime_object_init(exc, CACHED_CLASS(KeyNotFoundException));
GDMonoUtils::set_pending_exception((MonoException *)exc);
*r_value = Variant();
return;
}
*r_value = ret;
}
void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value) {
ptr->operator[](GDMonoMarshal::mono_object_to_variant(key)) = GDMonoMarshal::mono_object_to_variant(value);
}
void godot_icall_Dictionary_Keys(Dictionary *ptr, Array *r_dest) {
memnew_placement(r_dest, Array(ptr->keys()));
}
void godot_icall_Dictionary_Values(Dictionary *ptr, Array *r_dest) {
memnew_placement(r_dest, Array(ptr->values()));
}
int32_t godot_icall_Dictionary_Count(Dictionary *ptr) {
return ptr->size();
}
int32_t godot_icall_Dictionary_KeyValuePairs(Dictionary *ptr, Array **keys, Array **values) {
memnew_placement(*keys, Array(ptr->keys()));
memnew_placement(*values, Array(ptr->values()));
return ptr->size();
}
void godot_icall_Dictionary_KeyValuePairAt(Dictionary *ptr, int index, MonoObject **key, MonoObject **value) {
*key = GDMonoMarshal::variant_to_mono_object(ptr->get_key_at_index(index));
*value = GDMonoMarshal::variant_to_mono_object(ptr->get_value_at_index(index));
}
void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) {
Variant varKey = GDMonoMarshal::mono_object_to_variant(key);
Variant *ret = ptr->getptr(varKey);
if (ret != nullptr) {
GDMonoUtils::set_pending_exception(mono_get_exception_argument("key", "An element with the same key already exists"));
return;
}
ptr->operator[](varKey) = GDMonoMarshal::mono_object_to_variant(value);
}
void godot_icall_Dictionary_Clear(Dictionary *ptr) {
ptr->clear();
}
MonoBoolean godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value) {
// no dupes
Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
return ret != nullptr && *ret == GDMonoMarshal::mono_object_to_variant(value);
}
MonoBoolean godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key) {
return ptr->has(GDMonoMarshal::mono_object_to_variant(key));
}
void godot_icall_Dictionary_Duplicate(Dictionary *ptr, MonoBoolean deep, Dictionary *r_dest) {
memnew_placement(r_dest, Dictionary(ptr->duplicate(deep)));
}
MonoBoolean godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key) {
return ptr->erase(GDMonoMarshal::mono_object_to_variant(key));
}
MonoBoolean godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value) {
Variant varKey = GDMonoMarshal::mono_object_to_variant(key);
// no dupes
Variant *ret = ptr->getptr(varKey);
if (ret != nullptr && *ret == GDMonoMarshal::mono_object_to_variant(value)) {
ptr->erase(varKey);
return true;
}
return false;
}
MonoBoolean godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, Variant *value) {
Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
if (ret == nullptr) {
*value = Variant();
return false;
}
*value = ret;
return true;
}
MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr) {
return GDMonoMarshal::mono_string_from_godot(Variant(*ptr).operator String());
}
void godot_register_collections_icalls() {
GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", godot_icall_Array_Ctor);
GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor_MonoArray", godot_icall_Array_Ctor_MonoArray);
GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_At", godot_icall_Array_At);
GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_SetAt", godot_icall_Array_SetAt);
GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Count", godot_icall_Array_Count);
GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Add", godot_icall_Array_Add);
GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Clear", godot_icall_Array_Clear);
GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Concatenate", godot_icall_Array_Concatenate);
GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Contains", godot_icall_Array_Contains);
GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_CopyTo", godot_icall_Array_CopyTo);
GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Duplicate", godot_icall_Array_Duplicate);
GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_IndexOf", godot_icall_Array_IndexOf);
GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Insert", godot_icall_Array_Insert);
GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Remove", godot_icall_Array_Remove);
GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", godot_icall_Array_RemoveAt);
GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Resize", godot_icall_Array_Resize);
GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Shuffle", godot_icall_Array_Shuffle);
GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_ToString", godot_icall_Array_ToString);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Ctor", godot_icall_Dictionary_Ctor);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue", godot_icall_Dictionary_GetValue);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_SetValue", godot_icall_Dictionary_SetValue);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Keys", godot_icall_Dictionary_Keys);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Values", godot_icall_Dictionary_Values);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Count", godot_icall_Dictionary_Count);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairs", godot_icall_Dictionary_KeyValuePairs);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairAt", godot_icall_Dictionary_KeyValuePairAt);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Add", godot_icall_Dictionary_Add);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Clear", godot_icall_Dictionary_Clear);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", godot_icall_Dictionary_Contains);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ContainsKey", godot_icall_Dictionary_ContainsKey);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Duplicate", godot_icall_Dictionary_Duplicate);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_RemoveKey", godot_icall_Dictionary_RemoveKey);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Remove", godot_icall_Dictionary_Remove);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", godot_icall_Dictionary_TryGetValue);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ToString", godot_icall_Dictionary_ToString);
}

View File

@ -1,315 +0,0 @@
/*************************************************************************/
/* gd_glue.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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 "core/io/marshalls.h"
#include "core/os/os.h"
#include "core/string/ustring.h"
#include "core/variant/array.h"
#include "core/variant/variant.h"
#include "core/variant/variant_parser.h"
#include "../mono_gd/gd_mono_cache.h"
#include "../mono_gd/gd_mono_marshal.h"
#include "../mono_gd/gd_mono_utils.h"
MonoObject *godot_icall_GD_bytes2var(PackedByteArray *p_bytes, MonoBoolean p_allow_objects) {
Variant ret;
Error err = decode_variant(ret, p_bytes->ptr(), p_bytes->size(), nullptr, p_allow_objects);
if (err != OK) {
ret = RTR("Not enough bytes for decoding bytes, or invalid format.");
}
return GDMonoMarshal::variant_to_mono_object(ret);
}
MonoObject *godot_icall_GD_convert(MonoObject *p_what, int32_t p_type) {
Variant what = GDMonoMarshal::mono_object_to_variant(p_what);
const Variant *args[1] = { &what };
Callable::CallError ce;
Variant ret;
Variant::construct(Variant::Type(p_type), ret, args, 1, ce);
ERR_FAIL_COND_V(ce.error != Callable::CallError::CALL_OK, nullptr);
return GDMonoMarshal::variant_to_mono_object(ret);
}
int godot_icall_GD_hash(MonoObject *p_var) {
return GDMonoMarshal::mono_object_to_variant(p_var).hash();
}
MonoObject *godot_icall_GD_instance_from_id(uint64_t p_instance_id) {
return GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(ObjectID(p_instance_id)));
}
void godot_icall_GD_print(MonoArray *p_what) {
String str;
int length = mono_array_length(p_what);
for (int i = 0; i < length; i++) {
MonoObject *elem = mono_array_get(p_what, MonoObject *, i);
MonoException *exc = nullptr;
String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc);
if (exc) {
GDMonoUtils::set_pending_exception(exc);
return;
}
str += elem_str;
}
print_line(str);
}
void godot_icall_GD_printerr(MonoArray *p_what) {
String str;
int length = mono_array_length(p_what);
for (int i = 0; i < length; i++) {
MonoObject *elem = mono_array_get(p_what, MonoObject *, i);
MonoException *exc = nullptr;
String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc);
if (exc) {
GDMonoUtils::set_pending_exception(exc);
return;
}
str += elem_str;
}
print_error(str);
}
void godot_icall_GD_printraw(MonoArray *p_what) {
String str;
int length = mono_array_length(p_what);
for (int i = 0; i < length; i++) {
MonoObject *elem = mono_array_get(p_what, MonoObject *, i);
MonoException *exc = nullptr;
String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc);
if (exc) {
GDMonoUtils::set_pending_exception(exc);
return;
}
str += elem_str;
}
OS::get_singleton()->print("%s", str.utf8().get_data());
}
void godot_icall_GD_prints(MonoArray *p_what) {
String str;
int length = mono_array_length(p_what);
for (int i = 0; i < length; i++) {
MonoObject *elem = mono_array_get(p_what, MonoObject *, i);
MonoException *exc = nullptr;
String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc);
if (exc) {
GDMonoUtils::set_pending_exception(exc);
return;
}
if (i) {
str += " ";
}
str += elem_str;
}
print_line(str);
}
void godot_icall_GD_printt(MonoArray *p_what) {
String str;
int length = mono_array_length(p_what);
for (int i = 0; i < length; i++) {
MonoObject *elem = mono_array_get(p_what, MonoObject *, i);
MonoException *exc = nullptr;
String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc);
if (exc) {
GDMonoUtils::set_pending_exception(exc);
return;
}
if (i) {
str += "\t";
}
str += elem_str;
}
print_line(str);
}
float godot_icall_GD_randf() {
return Math::randf();
}
uint32_t godot_icall_GD_randi() {
return Math::rand();
}
void godot_icall_GD_randomize() {
Math::randomize();
}
double godot_icall_GD_randf_range(double from, double to) {
return Math::random(from, to);
}
int32_t godot_icall_GD_randi_range(int32_t from, int32_t to) {
return Math::random(from, to);
}
uint32_t godot_icall_GD_rand_seed(uint64_t seed, uint64_t *newSeed) {
uint32_t ret = Math::rand_from_seed(&seed);
*newSeed = seed;
return ret;
}
void godot_icall_GD_seed(uint64_t p_seed) {
Math::seed(p_seed);
}
MonoString *godot_icall_GD_str(MonoArray *p_what) {
String str;
Array what = GDMonoMarshal::mono_array_to_Array(p_what);
for (int i = 0; i < what.size(); i++) {
String os = what[i].operator String();
if (i == 0) {
str = os;
} else {
str += os;
}
}
return GDMonoMarshal::mono_string_from_godot(str);
}
MonoObject *godot_icall_GD_str2var(MonoString *p_str) {
Variant ret;
VariantParser::StreamString ss;
ss.s = GDMonoMarshal::mono_string_to_godot(p_str);
String errs;
int line;
Error err = VariantParser::parse(&ss, ret, errs, line);
if (err != OK) {
String err_str = "Parse error at line " + itos(line) + ": " + errs + ".";
ERR_PRINT(err_str);
ret = err_str;
}
return GDMonoMarshal::variant_to_mono_object(ret);
}
MonoBoolean godot_icall_GD_type_exists(StringName *p_type) {
StringName type = p_type ? *p_type : StringName();
return ClassDB::class_exists(type);
}
void godot_icall_GD_pusherror(MonoString *p_str) {
ERR_PRINT(GDMonoMarshal::mono_string_to_godot(p_str));
}
void godot_icall_GD_pushwarning(MonoString *p_str) {
WARN_PRINT(GDMonoMarshal::mono_string_to_godot(p_str));
}
void godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects, PackedByteArray *r_bytes) {
memnew_placement(r_bytes, PackedByteArray);
Variant var = GDMonoMarshal::mono_object_to_variant(p_var);
int len;
Error err = encode_variant(var, nullptr, len, p_full_objects);
ERR_FAIL_COND_MSG(err != OK, "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).");
r_bytes->resize(len);
encode_variant(var, r_bytes->ptrw(), len, p_full_objects);
}
MonoString *godot_icall_GD_var2str(MonoObject *p_var) {
String vars;
VariantWriter::write_to_string(GDMonoMarshal::mono_object_to_variant(p_var), vars);
return GDMonoMarshal::mono_string_from_godot(vars);
}
uint32_t godot_icall_TypeToVariantType(MonoReflectionType *p_refl_type) {
return (uint32_t)GDMonoMarshal::managed_to_variant_type(ManagedType::from_reftype(p_refl_type));
}
MonoObject *godot_icall_DefaultGodotTaskScheduler() {
return GDMonoCache::cached_data.task_scheduler_handle->get_target();
}
void godot_register_gd_icalls() {
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_bytes2var", godot_icall_GD_bytes2var);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_convert", godot_icall_GD_convert);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_hash", godot_icall_GD_hash);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_instance_from_id", godot_icall_GD_instance_from_id);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_pusherror", godot_icall_GD_pusherror);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_pushwarning", godot_icall_GD_pushwarning);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_print", godot_icall_GD_print);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printerr", godot_icall_GD_printerr);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printraw", godot_icall_GD_printraw);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_prints", godot_icall_GD_prints);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printt", godot_icall_GD_printt);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randf", godot_icall_GD_randf);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randi", godot_icall_GD_randi);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randomize", godot_icall_GD_randomize);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randf_range", godot_icall_GD_randf_range);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randi_range", godot_icall_GD_randi_range);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_rand_seed", godot_icall_GD_rand_seed);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_seed", godot_icall_GD_seed);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_str", godot_icall_GD_str);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_str2var", godot_icall_GD_str2var);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_type_exists", godot_icall_GD_type_exists);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_var2bytes", godot_icall_GD_var2bytes);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_var2str", godot_icall_GD_var2str);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_TypeToVariantType", godot_icall_TypeToVariantType);
// Dispatcher
GDMonoUtils::add_internal_call("Godot.Dispatcher::godot_icall_DefaultGodotTaskScheduler", godot_icall_DefaultGodotTaskScheduler);
}

View File

@ -1,72 +0,0 @@
/*************************************************************************/
/* node_path_glue.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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 "core/string/node_path.h"
#include "core/string/ustring.h"
#include "../mono_gd/gd_mono_marshal.h"
MonoBoolean godot_icall_NodePath_is_absolute(NodePath *p_ptr) {
return (MonoBoolean)p_ptr->is_absolute();
}
int32_t godot_icall_NodePath_get_name_count(NodePath *p_ptr) {
return p_ptr->get_name_count();
}
MonoString *godot_icall_NodePath_get_name(NodePath *p_ptr, uint32_t p_idx) {
return GDMonoMarshal::mono_string_from_godot(p_ptr->get_name(p_idx));
}
int32_t godot_icall_NodePath_get_subname_count(NodePath *p_ptr) {
return p_ptr->get_subname_count();
}
MonoString *godot_icall_NodePath_get_subname(NodePath *p_ptr, uint32_t p_idx) {
return GDMonoMarshal::mono_string_from_godot(p_ptr->get_subname(p_idx));
}
MonoString *godot_icall_NodePath_get_concatenated_subnames(NodePath *p_ptr) {
return GDMonoMarshal::mono_string_from_godot(p_ptr->get_concatenated_subnames());
}
void godot_icall_NodePath_get_as_property_path(NodePath *p_ptr, NodePath *r_dest) {
*r_dest = p_ptr->get_as_property_path();
}
void godot_register_node_path_icalls() {
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_as_property_path", godot_icall_NodePath_get_as_property_path);
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_subnames", godot_icall_NodePath_get_concatenated_subnames);
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name", godot_icall_NodePath_get_name);
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name_count", godot_icall_NodePath_get_name_count);
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname", godot_icall_NodePath_get_subname);
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname_count", godot_icall_NodePath_get_subname_count);
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_is_absolute", godot_icall_NodePath_is_absolute);
}

View File

@ -1,51 +0,0 @@
/*************************************************************************/
/* placeholder_glue.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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 "core/object/object.h"
#include "../mono_gd/gd_mono_internals.h"
#include "../mono_gd/gd_mono_utils.h"
MonoObject *godot_icall_InteropUtils_unmanaged_get_managed(Object *unmanaged) {
return GDMonoUtils::unmanaged_get_managed(unmanaged);
}
void godot_icall_InteropUtils_tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
GDMonoInternals::tie_managed_to_unmanaged(managed, unmanaged);
}
void godot_register_placeholder_icalls() {
GDMonoUtils::add_internal_call(
"Godot.NativeInterop.InteropUtils::internal_unmanaged_get_managed",
godot_icall_InteropUtils_unmanaged_get_managed);
GDMonoUtils::add_internal_call(
"Godot.NativeInterop.InteropUtils::internal_tie_managed_to_unmanaged",
godot_icall_InteropUtils_tie_managed_to_unmanaged);
}

View File

@ -29,13 +29,16 @@
/*************************************************************************/
#include "core/config/engine.h"
#include "core/io/marshalls.h"
#include "core/object/class_db.h"
#include "core/object/method_bind.h"
#include "core/os/os.h"
#include "core/string/string_name.h"
#include <gdnative/gdnative.h>
#include "modules/mono/managed_callable.h"
#include "modules/mono/mono_gd/gd_mono_cache.h"
#include "modules/mono/signal_awaiter_utils.h"
#ifdef __cplusplus
@ -56,6 +59,9 @@ extern "C" {
#define GD_PINVOKE_EXPORT MAYBE_UNUSED
#endif
// For ArrayPrivate and DictionaryPrivate
static_assert(sizeof(SafeRefCount) == sizeof(uint32_t));
typedef Object *(*godotsharp_class_creation_func)();
GD_PINVOKE_EXPORT MethodBind *godotsharp_method_bind_get_method(const StringName *p_classname, const char16_t *p_methodname) {
@ -70,14 +76,221 @@ GD_PINVOKE_EXPORT godotsharp_class_creation_func godotsharp_get_class_constructo
return nullptr;
}
GD_PINVOKE_EXPORT Object *godotsharp_invoke_class_constructor(godotsharp_class_creation_func p_creation_func) {
return p_creation_func();
}
GD_PINVOKE_EXPORT Object *godotsharp_engine_get_singleton(const String *p_name) {
return Engine::get_singleton()->get_singleton_object(*p_name);
}
GD_PINVOKE_EXPORT void godotsharp_internal_object_disposed(Object *p_ptr) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_ptr == nullptr);
#endif
if (p_ptr->get_script_instance()) {
CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
if (cs_instance) {
if (!cs_instance->is_destructing_script_instance()) {
cs_instance->mono_object_disposed();
p_ptr->set_script_instance(nullptr);
}
return;
}
}
void *data = CSharpLanguage::get_existing_instance_binding(p_ptr);
if (data) {
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
if (script_binding.inited) {
MonoGCHandleData &gchandle = script_binding.gchandle;
if (!gchandle.is_released()) {
CSharpLanguage::release_script_gchandle(nullptr, gchandle);
}
}
}
}
GD_PINVOKE_EXPORT void godotsharp_internal_refcounted_disposed(Object *p_ptr, bool p_is_finalizer) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_ptr == nullptr);
// This is only called with RefCounted derived classes
CRASH_COND(!Object::cast_to<RefCounted>(p_ptr));
#endif
RefCounted *rc = static_cast<RefCounted *>(p_ptr);
if (rc->get_script_instance()) {
CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(rc->get_script_instance());
if (cs_instance) {
if (!cs_instance->is_destructing_script_instance()) {
bool delete_owner;
bool remove_script_instance;
cs_instance->mono_object_disposed_baseref(p_is_finalizer, delete_owner, remove_script_instance);
if (delete_owner) {
memdelete(rc);
} else if (remove_script_instance) {
rc->set_script_instance(nullptr);
}
}
return;
}
}
// Unsafe refcount decrement. The managed instance also counts as a reference.
// See: CSharpLanguage::alloc_instance_binding_data(Object *p_object)
CSharpLanguage::get_singleton()->pre_unsafe_unreference(rc);
if (rc->unreference()) {
memdelete(rc);
} else {
void *data = CSharpLanguage::get_existing_instance_binding(rc);
if (data) {
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
if (script_binding.inited) {
MonoGCHandleData &gchandle = script_binding.gchandle;
if (!gchandle.is_released()) {
CSharpLanguage::release_script_gchandle(nullptr, gchandle);
}
}
}
}
}
GD_PINVOKE_EXPORT void godotsharp_internal_object_connect_event_signal(Object *p_ptr, const StringName *p_event_signal) {
CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
if (csharp_instance) {
csharp_instance->connect_event_signal(*p_event_signal);
}
}
GD_PINVOKE_EXPORT int32_t godotsharp_internal_signal_awaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, GCHandleIntPtr p_awaiter_handle_ptr) {
StringName signal = p_signal ? *p_signal : StringName();
return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter_handle_ptr);
}
GD_PINVOKE_EXPORT GCHandleIntPtr godotsharp_internal_unmanaged_get_script_instance_managed(Object *p_unmanaged, bool *r_has_cs_script_instance) {
#ifdef DEBUG_ENABLED
CRASH_COND(!p_unmanaged);
CRASH_COND(!r_has_cs_script_instance);
#endif
if (p_unmanaged->get_script_instance()) {
CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_unmanaged->get_script_instance());
if (cs_instance) {
*r_has_cs_script_instance = true;
return cs_instance->get_gchandle_intptr();
}
}
*r_has_cs_script_instance = false;
return GCHandleIntPtr();
}
GD_PINVOKE_EXPORT GCHandleIntPtr godotsharp_internal_unmanaged_get_instance_binding_managed(Object *p_unmanaged) {
#ifdef DEBUG_ENABLED
CRASH_COND(!p_unmanaged);
#endif
void *data = CSharpLanguage::get_instance_binding(p_unmanaged);
ERR_FAIL_NULL_V(data, GCHandleIntPtr());
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value();
ERR_FAIL_COND_V(!script_binding.inited, GCHandleIntPtr());
return script_binding.gchandle.get_intptr();
}
GD_PINVOKE_EXPORT GCHandleIntPtr godotsharp_internal_unmanaged_instance_binding_create_managed(Object *p_unmanaged, GCHandleIntPtr p_old_gchandle) {
#ifdef DEBUG_ENABLED
CRASH_COND(!p_unmanaged);
#endif
void *data = CSharpLanguage::get_instance_binding(p_unmanaged);
ERR_FAIL_NULL_V(data, GCHandleIntPtr());
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value();
ERR_FAIL_COND_V(!script_binding.inited, GCHandleIntPtr());
MonoGCHandleData &gchandle = script_binding.gchandle;
// TODO: Possible data race?
CRASH_COND(gchandle.get_intptr().value != p_old_gchandle.value);
CSharpLanguage::get_singleton()->release_script_gchandle(gchandle);
// Create a new one
#ifdef DEBUG_ENABLED
CRASH_COND(script_binding.type_name == StringName());
#endif
bool parent_is_object_class = ClassDB::is_parent_class(p_unmanaged->get_class_name(), script_binding.type_name);
ERR_FAIL_COND_V_MSG(!parent_is_object_class, GCHandleIntPtr(),
"Type inherits from native type '" + script_binding.type_name + "', so it can't be instantiated in object of type: '" + p_unmanaged->get_class() + "'.");
GCHandleIntPtr strong_gchandle =
GDMonoCache::managed_callbacks.ScriptManagerBridge_CreateManagedForGodotObjectBinding(
&script_binding.type_name, p_unmanaged);
ERR_FAIL_NULL_V(strong_gchandle.value, GCHandleIntPtr());
gchandle = MonoGCHandleData(strong_gchandle, gdmono::GCHandleType::STRONG_HANDLE);
// Tie managed to unmanaged
RefCounted *rc = Object::cast_to<RefCounted>(p_unmanaged);
if (rc) {
// Unsafe refcount increment. The managed instance also counts as a reference.
// This way if the unmanaged world has no references to our owner
// but the managed instance is alive, the refcount will be 1 instead of 0.
// See: godot_icall_RefCounted_Dtor(MonoObject *p_obj, Object *p_ptr)
rc->reference();
CSharpLanguage::get_singleton()->post_unsafe_reference(rc);
}
return gchandle.get_intptr();
}
GD_PINVOKE_EXPORT void godotsharp_internal_tie_native_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, const StringName *p_native_name, bool p_ref_counted) {
CSharpLanguage::tie_native_managed_to_unmanaged(p_gchandle_intptr, p_unmanaged, p_native_name, p_ref_counted);
}
GD_PINVOKE_EXPORT void godotsharp_internal_tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, CSharpScript *p_script, bool p_ref_counted) {
CSharpLanguage::tie_user_managed_to_unmanaged(p_gchandle_intptr, p_unmanaged, p_script, p_ref_counted);
}
GD_PINVOKE_EXPORT void godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged) {
CSharpLanguage::tie_managed_to_unmanaged_with_pre_setup(p_gchandle_intptr, p_unmanaged);
}
GD_PINVOKE_EXPORT CSharpScript *godotsharp_internal_new_csharp_script() {
CSharpScript *script = memnew(CSharpScript);
CRASH_COND(!script);
return script;
}
GD_PINVOKE_EXPORT void godotsharp_array_filter_godot_objects_by_native(StringName *p_native_name, const Array *p_input, Array *r_output) {
memnew_placement(r_output, Array);
for (int i = 0; i < p_input->size(); ++i) {
if (ClassDB::is_parent_class(((Object *)(*p_input)[i])->get_class(), *p_native_name)) {
r_output->push_back(p_input[i]);
}
}
}
GD_PINVOKE_EXPORT void godotsharp_array_filter_godot_objects_by_non_native(const Array *p_input, Array *r_output) {
memnew_placement(r_output, Array);
for (int i = 0; i < p_input->size(); ++i) {
CSharpInstance *si = CAST_CSHARP_INSTANCE(((Object *)(*p_input)[i])->get_script_instance());
if (si != nullptr) {
r_output->push_back(p_input[i]);
}
}
}
GD_PINVOKE_EXPORT void godotsharp_ref_destroy(Ref<RefCounted> *p_instance) {
p_instance->~Ref();
}
@ -182,14 +395,14 @@ GD_PINVOKE_EXPORT void godotsharp_packed_string_array_add(PackedStringArray *r_d
r_dest->append(*p_element);
}
GD_PINVOKE_EXPORT void godotsharp_callable_new_with_delegate(void *p_delegate_handle, Callable *r_callable) {
GD_PINVOKE_EXPORT void godotsharp_callable_new_with_delegate(GCHandleIntPtr p_delegate_handle, Callable *r_callable) {
// TODO: Use pooling for ManagedCallable instances.
CallableCustom *managed_callable = memnew(ManagedCallable(p_delegate_handle));
*r_callable = Callable(managed_callable);
memnew_placement(r_callable, Callable(managed_callable));
}
GD_PINVOKE_EXPORT bool godotsharp_callable_get_data_for_marshalling(const Callable *p_callable,
void **r_delegate_handle, Object **r_object, StringName *r_name) {
GCHandleIntPtr *r_delegate_handle, Object **r_object, StringName *r_name) {
if (p_callable->is_custom()) {
CallableCustom *custom = p_callable->get_custom();
CallableCustom::CompareEqualFunc compare_equal_func = custom->get_compare_equal_func();
@ -198,31 +411,31 @@ GD_PINVOKE_EXPORT bool godotsharp_callable_get_data_for_marshalling(const Callab
ManagedCallable *managed_callable = static_cast<ManagedCallable *>(custom);
*r_delegate_handle = managed_callable->get_delegate();
*r_object = nullptr;
*r_name = StringName();
memnew_placement(r_name, StringName());
return true;
} else if (compare_equal_func == SignalAwaiterCallable::compare_equal_func_ptr) {
SignalAwaiterCallable *signal_awaiter_callable = static_cast<SignalAwaiterCallable *>(custom);
*r_delegate_handle = nullptr;
*r_delegate_handle = GCHandleIntPtr();
*r_object = ObjectDB::get_instance(signal_awaiter_callable->get_object());
*r_name = signal_awaiter_callable->get_signal();
memnew_placement(r_name, StringName(signal_awaiter_callable->get_signal()));
return true;
} else if (compare_equal_func == EventSignalCallable::compare_equal_func_ptr) {
EventSignalCallable *event_signal_callable = static_cast<EventSignalCallable *>(custom);
*r_delegate_handle = nullptr;
*r_delegate_handle = GCHandleIntPtr();
*r_object = ObjectDB::get_instance(event_signal_callable->get_object());
*r_name = event_signal_callable->get_signal();
memnew_placement(r_name, StringName(event_signal_callable->get_signal()));
return true;
}
// Some other CallableCustom. We only support ManagedCallable.
*r_delegate_handle = nullptr;
*r_delegate_handle = GCHandleIntPtr();
*r_object = nullptr;
*r_name = StringName();
memnew_placement(r_name, StringName());
return false;
} else {
*r_delegate_handle = nullptr;
*r_delegate_handle = GCHandleIntPtr();
*r_object = ObjectDB::get_instance(p_callable->get_object_id());
*r_name = p_callable->get_method();
memnew_placement(r_name, StringName(p_callable->get_method()));
return true;
}
}
@ -235,7 +448,7 @@ GD_PINVOKE_EXPORT void godotsharp_method_bind_ptrcall(godot_method_bind *p_metho
godot_method_bind_ptrcall(p_method_bind, p_instance, p_args, p_ret);
}
GD_PINVOKE_EXPORT godot_variant godotsharp_method_bind_call(godot_method_bind *p_method_bind, godot_object *p_instance, const godot_variant **p_args, const int p_arg_count, godot_variant_call_error *p_call_error) {
GD_PINVOKE_EXPORT godot_variant godotsharp_method_bind_call(godot_method_bind *p_method_bind, godot_object *p_instance, const godot_variant **p_args, const int32_t p_arg_count, godot_variant_call_error *p_call_error) {
return godot_method_bind_call(p_method_bind, p_instance, p_args, p_arg_count, p_call_error);
}
@ -445,6 +658,10 @@ GD_PINVOKE_EXPORT godot_packed_color_array godotsharp_variant_as_packed_color_ar
return godot_variant_as_packed_color_array(p_self);
}
GD_PINVOKE_EXPORT bool godotsharp_variant_equals(const godot_variant *p_a, const godot_variant *p_b) {
return *reinterpret_cast<const Variant *>(p_a) == *reinterpret_cast<const Variant *>(p_b);
}
// string.h
GD_PINVOKE_EXPORT void godotsharp_string_new_with_utf16_chars(godot_string *r_dest, const char16_t *p_contents) {
@ -465,12 +682,24 @@ GD_PINVOKE_EXPORT void godotsharp_node_path_new_copy(godot_node_path *r_dest, co
// array.h
GD_PINVOKE_EXPORT void godotsharp_array_new(godot_array *r_dest) {
godot_array_new(r_dest);
}
GD_PINVOKE_EXPORT void godotsharp_array_new_copy(godot_array *r_dest, const godot_array *p_src) {
godot_array_new_copy(r_dest, p_src);
}
GD_PINVOKE_EXPORT godot_variant *godotsharp_array_ptrw(godot_array *p_self) {
return reinterpret_cast<godot_variant *>(&reinterpret_cast<Array *>(p_self)->operator[](0));
}
// dictionary.h
GD_PINVOKE_EXPORT void godotsharp_dictionary_new(godot_dictionary *r_dest) {
godot_dictionary_new(r_dest);
}
GD_PINVOKE_EXPORT void godotsharp_dictionary_new_copy(godot_dictionary *r_dest, const godot_dictionary *p_src) {
godot_dictionary_new_copy(r_dest, p_src);
}
@ -545,16 +774,338 @@ GD_PINVOKE_EXPORT void godotsharp_dictionary_destroy(godot_dictionary *p_self) {
godot_dictionary_destroy(p_self);
}
// Array
int32_t godotsharp_array_add(Array *p_self, const Variant *p_item) {
p_self->append(*p_item);
return p_self->size();
}
void godotsharp_array_duplicate(const Array *p_self, bool p_deep, Array *r_dest) {
memnew_placement(r_dest, Array(p_self->duplicate(p_deep)));
}
int32_t godotsharp_array_index_of(const Array *p_self, const Variant *p_item) {
return p_self->find(*p_item);
}
void godotsharp_array_insert(Array *p_self, int32_t p_index, const Variant *p_item) {
p_self->insert(p_index, *p_item);
}
void godotsharp_array_remove_at(Array *p_self, int32_t p_index) {
p_self->remove(p_index);
}
int32_t godotsharp_array_resize(Array *p_self, int32_t p_new_size) {
return (int32_t)p_self->resize(p_new_size);
}
void godotsharp_array_shuffle(Array *p_self) {
p_self->shuffle();
}
// Dictionary
bool godotsharp_dictionary_try_get_value(const Dictionary *p_self, const Variant *p_key, Variant *r_value) {
const Variant *ret = p_self->getptr(*p_key);
if (ret == nullptr) {
memnew_placement(r_value, Variant());
return false;
}
memnew_placement(r_value, Variant(*ret));
return true;
}
void godotsharp_dictionary_set_value(Dictionary *p_self, const Variant *p_key, const Variant *p_value) {
p_self->operator[](*p_key) = *p_value;
}
void godotsharp_dictionary_keys(const Dictionary *p_self, Array *r_dest) {
memnew_placement(r_dest, Array(p_self->keys()));
}
void godotsharp_dictionary_values(const Dictionary *p_self, Array *r_dest) {
memnew_placement(r_dest, Array(p_self->values()));
}
int32_t godotsharp_dictionary_count(const Dictionary *p_self) {
return p_self->size();
}
void godotsharp_dictionary_key_value_pair_at(const Dictionary *p_self, int32_t p_index, Variant *r_key, Variant *r_value) {
memnew_placement(r_key, Variant(p_self->get_key_at_index(p_index)));
memnew_placement(r_value, Variant(p_self->get_value_at_index(p_index)));
}
void godotsharp_dictionary_add(Dictionary *p_self, const Variant *p_key, const Variant *p_value) {
p_self->operator[](*p_key) = *p_value;
}
void godotsharp_dictionary_clear(Dictionary *p_self) {
p_self->clear();
}
bool godotsharp_dictionary_contains_key(const Dictionary *p_self, const Variant *p_key) {
return p_self->has(*p_key);
}
void godotsharp_dictionary_duplicate(const Dictionary *p_self, bool p_deep, Dictionary *r_dest) {
memnew_placement(r_dest, Dictionary(p_self->duplicate(p_deep)));
}
bool godotsharp_dictionary_remove_key(Dictionary *p_self, const Variant *p_key) {
return p_self->erase(*p_key);
}
void godotsharp_string_md5_buffer(const String *p_self, PackedByteArray *r_md5_buffer) {
memnew_placement(r_md5_buffer, PackedByteArray(p_self->md5_buffer()));
}
void godotsharp_string_md5_text(const String *p_self, String *r_md5_text) {
memnew_placement(r_md5_text, String(p_self->md5_text()));
}
int32_t godotsharp_string_rfind(const String *p_self, const String *p_what, int32_t p_from) {
return p_self->rfind(*p_what, p_from);
}
int32_t godotsharp_string_rfindn(const String *p_self, const String *p_what, int32_t p_from) {
return p_self->rfindn(*p_what, p_from);
}
void godotsharp_string_sha256_buffer(const String *p_self, PackedByteArray *r_sha256_buffer) {
memnew_placement(r_sha256_buffer, PackedByteArray(p_self->sha256_buffer()));
}
void godotsharp_string_sha256_text(const String *p_self, String *r_sha256_text) {
memnew_placement(r_sha256_text, String(p_self->sha256_text()));
}
void godotsharp_node_path_get_as_property_path(const NodePath *p_ptr, NodePath *r_dest) {
memnew_placement(r_dest, NodePath(p_ptr->get_as_property_path()));
}
void godotsharp_node_path_get_concatenated_subnames(const NodePath *p_self, String *r_subnames) {
memnew_placement(r_subnames, String(p_self->get_concatenated_subnames()));
}
void godotsharp_node_path_get_name(const NodePath *p_self, uint32_t p_idx, String *r_name) {
memnew_placement(r_name, String(p_self->get_name(p_idx)));
}
int32_t godotsharp_node_path_get_name_count(const NodePath *p_self) {
return p_self->get_name_count();
}
void godotsharp_node_path_get_subname(const NodePath *p_self, uint32_t p_idx, String *r_subname) {
memnew_placement(r_subname, String(p_self->get_subname(p_idx)));
}
int32_t godotsharp_node_path_get_subname_count(const NodePath *p_self) {
return p_self->get_subname_count();
}
bool godotsharp_node_path_is_absolute(const NodePath *p_self) {
return p_self->is_absolute();
}
void godotsharp_randomize() {
Math::randomize();
}
uint32_t godotsharp_randi() {
return Math::rand();
}
float godotsharp_randf() {
return Math::randf();
}
int32_t godotsharp_randi_range(int32_t p_from, int32_t p_to) {
return Math::random(p_from, p_to);
}
double godotsharp_randf_range(double p_from, double p_to) {
return Math::random(p_from, p_to);
}
void godotsharp_seed(uint64_t p_seed) {
Math::seed(p_seed);
}
uint32_t godotsharp_rand_from_seed(uint64_t p_seed, uint64_t *r_new_seed) {
uint32_t ret = Math::rand_from_seed(&p_seed);
*r_new_seed = p_seed;
return ret;
}
void godotsharp_weakref(Object *p_ptr, Ref<RefCounted> *r_weak_ref) {
if (!p_ptr) {
return;
}
Ref<WeakRef> wref;
RefCounted *rc = Object::cast_to<RefCounted>(p_ptr);
if (rc) {
REF r = rc;
if (!r.is_valid()) {
return;
}
wref.instantiate();
wref->set_ref(r);
} else {
wref.instantiate();
wref->set_obj(p_ptr);
}
memnew_placement(r_weak_ref, Ref<RefCounted>(wref));
}
void godotsharp_str(const godot_array *p_what, godot_string *r_ret) {
String &str = *memnew_placement(r_ret, String);
const Array &what = *reinterpret_cast<const Array *>(p_what);
for (int i = 0; i < what.size(); i++) {
String os = what[i].operator String();
if (i == 0) {
str = os;
} else {
str += os;
}
}
}
void godotsharp_print(const godot_string *p_what) {
print_line(*reinterpret_cast<const String *>(p_what));
}
void godotsharp_printerr(const godot_string *p_what) {
print_error(*reinterpret_cast<const String *>(p_what));
}
void godotsharp_printt(const godot_string *p_what) {
print_line(*reinterpret_cast<const String *>(p_what));
}
void godotsharp_prints(const godot_string *p_what) {
print_line(*reinterpret_cast<const String *>(p_what));
}
void godotsharp_printraw(const godot_string *p_what) {
OS::get_singleton()->print("%s", reinterpret_cast<const String *>(p_what)->utf8().get_data());
}
void godotsharp_pusherror(const godot_string *p_str) {
ERR_PRINT(*reinterpret_cast<const String *>(p_str));
}
void godotsharp_pushwarning(const godot_string *p_str) {
WARN_PRINT(*reinterpret_cast<const String *>(p_str));
}
void godotsharp_var2str(const godot_variant *p_var, godot_string *r_ret) {
const Variant &var = *reinterpret_cast<const Variant *>(p_var);
String &vars = *memnew_placement(r_ret, String);
VariantWriter::write_to_string(var, vars);
}
void godotsharp_str2var(const godot_string *p_str, godot_variant *r_ret) {
Variant ret;
VariantParser::StreamString ss;
ss.s = *reinterpret_cast<const String *>(p_str);
String errs;
int line;
Error err = VariantParser::parse(&ss, ret, errs, line);
if (err != OK) {
String err_str = "Parse error at line " + itos(line) + ": " + errs + ".";
ERR_PRINT(err_str);
ret = err_str;
}
memnew_placement(r_ret, Variant(ret));
}
void godotsharp_var2bytes(const godot_variant *p_var, bool p_full_objects, godot_packed_byte_array *r_bytes) {
const Variant &var = *reinterpret_cast<const Variant *>(p_var);
PackedByteArray &bytes = *memnew_placement(r_bytes, PackedByteArray);
int len;
Error err = encode_variant(var, nullptr, len, p_full_objects);
ERR_FAIL_COND_MSG(err != OK, "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).");
bytes.resize(len);
encode_variant(var, bytes.ptrw(), len, p_full_objects);
}
void godotsharp_bytes2var(const godot_packed_byte_array *p_bytes, bool p_allow_objects, godot_variant *r_ret) {
const PackedByteArray *bytes = reinterpret_cast<const PackedByteArray *>(p_bytes);
Variant ret;
Error err = decode_variant(ret, bytes->ptr(), bytes->size(), nullptr, p_allow_objects);
if (err != OK) {
ret = RTR("Not enough bytes for decoding bytes, or invalid format.");
}
memnew_placement(r_ret, Variant(ret));
}
int godotsharp_hash(const godot_variant *p_var) {
return reinterpret_cast<const Variant *>(p_var)->hash();
}
void godotsharp_convert(const godot_variant *p_what, int32_t p_type, godot_variant *r_ret) {
const Variant *args[1] = { reinterpret_cast<const Variant *>(p_what) };
Callable::CallError ce;
Variant ret;
Variant::construct(Variant::Type(p_type), ret, args, 1, ce);
if (ce.error != Callable::CallError::CALL_OK) {
memnew_placement(r_ret, Variant);
ERR_FAIL_MSG("Unable to convert parameter from '" +
Variant::get_type_name(reinterpret_cast<const Variant *>(p_what)->get_type()) +
"' to '" + Variant::get_type_name(Variant::Type(p_type)) + "'.");
}
memnew_placement(r_ret, Variant(ret));
}
Object *godotsharp_instance_from_id(uint64_t p_instance_id) {
return ObjectDB::get_instance(ObjectID(p_instance_id));
}
void godotsharp_object_to_string(Object *p_ptr, godot_string *r_str) {
#ifdef DEBUG_ENABLED
// Cannot happen in C#; would get an ObjectDisposedException instead.
CRASH_COND(p_ptr == nullptr);
#endif
// Can't call 'Object::to_string()' here, as that can end up calling 'ToString' again resulting in an endless circular loop.
memnew_placement(r_str,
String("[" + p_ptr->get_class() + ":" + itos(p_ptr->get_instance_id()) + "]"));
}
#ifdef __cplusplus
}
#endif
// We need this to prevent the functions from being stripped.
void *godotsharp_pinvoke_funcs[95] = {
void *godotsharp_pinvoke_funcs[166] = {
(void *)godotsharp_method_bind_get_method,
(void *)godotsharp_get_class_constructor,
(void *)godotsharp_invoke_class_constructor,
(void *)godotsharp_engine_get_singleton,
(void *)godotsharp_internal_object_disposed,
(void *)godotsharp_internal_refcounted_disposed,
(void *)godotsharp_internal_object_connect_event_signal,
(void *)godotsharp_internal_signal_awaiter_connect,
(void *)godotsharp_internal_unmanaged_get_script_instance_managed,
(void *)godotsharp_internal_unmanaged_get_instance_binding_managed,
(void *)godotsharp_internal_unmanaged_instance_binding_create_managed,
(void *)godotsharp_internal_tie_native_managed_to_unmanaged,
(void *)godotsharp_internal_tie_user_managed_to_unmanaged,
(void *)godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup,
(void *)godotsharp_internal_new_csharp_script,
(void *)godotsharp_array_filter_godot_objects_by_native,
(void *)godotsharp_array_filter_godot_objects_by_non_native,
(void *)godotsharp_ref_destroy,
(void *)godotsharp_string_name_new_from_string,
(void *)godotsharp_node_path_new_from_string,
@ -624,10 +1175,14 @@ void *godotsharp_pinvoke_funcs[95] = {
(void *)godotsharp_variant_as_packed_vector2_array,
(void *)godotsharp_variant_as_packed_vector3_array,
(void *)godotsharp_variant_as_packed_color_array,
(void *)godotsharp_variant_equals,
(void *)godotsharp_string_new_with_utf16_chars,
(void *)godotsharp_string_name_new_copy,
(void *)godotsharp_node_path_new_copy,
(void *)godotsharp_array_new,
(void *)godotsharp_array_new_copy,
(void *)godotsharp_array_ptrw,
(void *)godotsharp_dictionary_new,
(void *)godotsharp_dictionary_new_copy,
(void *)godotsharp_packed_byte_array_destroy,
(void *)godotsharp_packed_int32_array_destroy,
@ -645,5 +1200,60 @@ void *godotsharp_pinvoke_funcs[95] = {
(void *)godotsharp_signal_destroy,
(void *)godotsharp_callable_destroy,
(void *)godotsharp_array_destroy,
(void *)godotsharp_dictionary_destroy
(void *)godotsharp_dictionary_destroy,
(void *)godotsharp_array_add,
(void *)godotsharp_array_duplicate,
(void *)godotsharp_array_index_of,
(void *)godotsharp_array_insert,
(void *)godotsharp_array_remove_at,
(void *)godotsharp_array_resize,
(void *)godotsharp_array_shuffle,
(void *)godotsharp_dictionary_try_get_value,
(void *)godotsharp_dictionary_set_value,
(void *)godotsharp_dictionary_keys,
(void *)godotsharp_dictionary_values,
(void *)godotsharp_dictionary_count,
(void *)godotsharp_dictionary_key_value_pair_at,
(void *)godotsharp_dictionary_add,
(void *)godotsharp_dictionary_clear,
(void *)godotsharp_dictionary_contains_key,
(void *)godotsharp_dictionary_duplicate,
(void *)godotsharp_dictionary_remove_key,
(void *)godotsharp_string_md5_buffer,
(void *)godotsharp_string_md5_text,
(void *)godotsharp_string_rfind,
(void *)godotsharp_string_rfindn,
(void *)godotsharp_string_sha256_buffer,
(void *)godotsharp_string_sha256_text,
(void *)godotsharp_node_path_get_as_property_path,
(void *)godotsharp_node_path_get_concatenated_subnames,
(void *)godotsharp_node_path_get_name,
(void *)godotsharp_node_path_get_name_count,
(void *)godotsharp_node_path_get_subname,
(void *)godotsharp_node_path_get_subname_count,
(void *)godotsharp_node_path_is_absolute,
(void *)godotsharp_randomize,
(void *)godotsharp_randi,
(void *)godotsharp_randf,
(void *)godotsharp_randi_range,
(void *)godotsharp_randf_range,
(void *)godotsharp_seed,
(void *)godotsharp_rand_from_seed,
(void *)godotsharp_weakref,
(void *)godotsharp_str,
(void *)godotsharp_print,
(void *)godotsharp_printerr,
(void *)godotsharp_printt,
(void *)godotsharp_prints,
(void *)godotsharp_printraw,
(void *)godotsharp_pusherror,
(void *)godotsharp_pushwarning,
(void *)godotsharp_var2str,
(void *)godotsharp_str2var,
(void *)godotsharp_var2bytes,
(void *)godotsharp_bytes2var,
(void *)godotsharp_hash,
(void *)godotsharp_convert,
(void *)godotsharp_instance_from_id,
(void *)godotsharp_object_to_string,
};

View File

@ -1,81 +0,0 @@
/*************************************************************************/
/* scene_tree_glue.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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 "core/object/class_db.h"
#include "core/string/string_name.h"
#include "core/variant/array.h"
#include "scene/main/node.h"
#include "scene/main/scene_tree.h"
#include "../csharp_script.h"
#include "../mono_gd/gd_mono_marshal.h"
#include "../mono_gd/gd_mono_utils.h"
void godot_icall_SceneTree_get_nodes_in_group_Generic(SceneTree *ptr, StringName *group, MonoReflectionType *refltype, Array *r_dest) {
memnew_placement(r_dest, Array);
List<Node *> nodes;
// Retrieve all the nodes in the group
ptr->get_nodes_in_group(*group, &nodes);
// No need to bother if the group is empty
if (!nodes.is_empty()) {
MonoType *elem_type = mono_reflection_type_get_type(refltype);
MonoClass *mono_class = mono_class_from_mono_type(elem_type);
GDMonoClass *klass = GDMono::get_singleton()->get_class(mono_class);
if (klass == GDMonoUtils::get_class_native_base(klass)) {
// If we're trying to get native objects, just check the inheritance list
StringName native_class_name = GDMonoUtils::get_native_godot_class_name(klass);
for (int i = 0; i < nodes.size(); ++i) {
if (ClassDB::is_parent_class(nodes[i]->get_class(), native_class_name)) {
r_dest->push_back(nodes[i]);
}
}
} else {
// If we're trying to get csharpscript instances, get the mono object and compare the classes
for (int i = 0; i < nodes.size(); ++i) {
CSharpInstance *si = CAST_CSHARP_INSTANCE(nodes[i]->get_script_instance());
if (si != nullptr) {
MonoObject *obj = si->get_mono_object();
if (obj != nullptr && mono_object_get_class(obj) == mono_class) {
r_dest->push_back(nodes[i]);
}
}
}
}
}
}
void godot_register_scene_tree_icalls() {
GDMonoUtils::add_internal_call("Godot.SceneTree::godot_icall_SceneTree_get_nodes_in_group_Generic", godot_icall_SceneTree_get_nodes_in_group_Generic);
}

View File

@ -1,75 +0,0 @@
/*************************************************************************/
/* string_glue.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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 "core/string/ustring.h"
#include "core/templates/vector.h"
#include "core/variant/variant.h"
#include "../mono_gd/gd_mono_marshal.h"
MonoArray *godot_icall_String_md5_buffer(MonoString *p_str) {
Vector<uint8_t> ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_buffer();
// TODO Check possible Array/Vector<uint8_t> problem?
return GDMonoMarshal::Array_to_mono_array(Variant(ret));
}
MonoString *godot_icall_String_md5_text(MonoString *p_str) {
String ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_text();
return GDMonoMarshal::mono_string_from_godot(ret);
}
int godot_icall_String_rfind(MonoString *p_str, MonoString *p_what, int p_from) {
String what = GDMonoMarshal::mono_string_to_godot(p_what);
return GDMonoMarshal::mono_string_to_godot(p_str).rfind(what, p_from);
}
int godot_icall_String_rfindn(MonoString *p_str, MonoString *p_what, int p_from) {
String what = GDMonoMarshal::mono_string_to_godot(p_what);
return GDMonoMarshal::mono_string_to_godot(p_str).rfindn(what, p_from);
}
MonoArray *godot_icall_String_sha256_buffer(MonoString *p_str) {
Vector<uint8_t> ret = GDMonoMarshal::mono_string_to_godot(p_str).sha256_buffer();
return GDMonoMarshal::Array_to_mono_array(Variant(ret));
}
MonoString *godot_icall_String_sha256_text(MonoString *p_str) {
String ret = GDMonoMarshal::mono_string_to_godot(p_str).sha256_text();
return GDMonoMarshal::mono_string_from_godot(ret);
}
void godot_register_string_icalls() {
GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_buffer", godot_icall_String_md5_buffer);
GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_text", godot_icall_String_md5_text);
GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_rfind", godot_icall_String_rfind);
GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_rfindn", godot_icall_String_rfindn);
GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_buffer", godot_icall_String_sha256_buffer);
GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_text", godot_icall_String_sha256_text);
}

View File

@ -33,6 +33,7 @@
#define BINDINGS_NAMESPACE "Godot"
#define BINDINGS_NAMESPACE_COLLECTIONS BINDINGS_NAMESPACE ".Collections"
#define BINDINGS_NAMESPACE_BRIDGE BINDINGS_NAMESPACE ".Bridge"
#define BINDINGS_GLOBAL_SCOPE_CLASS "GD"
#define BINDINGS_PTR_FIELD "NativePtr"
#define BINDINGS_NATIVE_NAME_FIELD "NativeName"

View File

@ -89,8 +89,6 @@ class _GodotSharpDirs {
public:
String res_data_dir;
String res_metadata_dir;
String res_assemblies_base_dir;
String res_assemblies_dir;
String res_config_dir;
String res_temp_dir;
String res_temp_assemblies_base_dir;
@ -98,6 +96,9 @@ public:
String mono_user_dir;
String mono_logs_dir;
String api_assemblies_base_dir;
String api_assemblies_dir;
#ifdef TOOLS_ENABLED
String mono_solutions_dir;
String build_logs_dir;
@ -106,7 +107,6 @@ public:
String csproj_filepath;
String data_editor_tools_dir;
String data_editor_prebuilt_api_dir;
#else
// Equivalent of res_assemblies_dir, but in the data directory rather than in 'res://'.
// Only defined on export templates. Used when exporting assemblies outside of PCKs.
@ -124,8 +124,6 @@ private:
_GodotSharpDirs() {
res_data_dir = "res://.godot/mono";
res_metadata_dir = res_data_dir.plus_file("metadata");
res_assemblies_base_dir = res_data_dir.plus_file("assemblies");
res_assemblies_dir = res_assemblies_base_dir.plus_file(GDMono::get_expected_api_build_config());
res_config_dir = res_data_dir.plus_file("etc").plus_file("mono");
// TODO use paths from csproj
@ -133,6 +131,8 @@ private:
res_temp_assemblies_base_dir = res_temp_dir.plus_file("bin");
res_temp_assemblies_dir = res_temp_assemblies_base_dir.plus_file(_get_expected_build_config());
api_assemblies_base_dir = res_data_dir.plus_file("assemblies");
#ifdef JAVASCRIPT_ENABLED
mono_user_dir = "user://";
#else
@ -162,7 +162,7 @@ private:
String data_dir_root = exe_dir.plus_file("GodotSharp");
data_editor_tools_dir = data_dir_root.plus_file("Tools");
data_editor_prebuilt_api_dir = data_dir_root.plus_file("Api");
api_assemblies_base_dir = data_dir_root.plus_file("Api");
String data_mono_root_dir = data_dir_root.plus_file("Mono");
data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
@ -182,8 +182,8 @@ private:
data_editor_tools_dir = exe_dir.plus_file("../Resources/GodotSharp/Tools");
}
if (!DirAccess::exists(data_editor_prebuilt_api_dir)) {
data_editor_prebuilt_api_dir = exe_dir.plus_file("../Resources/GodotSharp/Api");
if (!DirAccess::exists(api_assemblies_base_dir)) {
api_assemblies_base_dir = exe_dir.plus_file("../Resources/GodotSharp/Api");
}
if (!DirAccess::exists(data_mono_root_dir)) {
@ -227,6 +227,8 @@ private:
#endif
#endif
api_assemblies_dir = api_assemblies_base_dir.plus_file(GDMono::get_expected_api_build_config());
}
_GodotSharpDirs(const _GodotSharpDirs &);
@ -247,14 +249,6 @@ String get_res_metadata_dir() {
return _GodotSharpDirs::get_singleton().res_metadata_dir;
}
String get_res_assemblies_base_dir() {
return _GodotSharpDirs::get_singleton().res_assemblies_base_dir;
}
String get_res_assemblies_dir() {
return _GodotSharpDirs::get_singleton().res_assemblies_dir;
}
String get_res_config_dir() {
return _GodotSharpDirs::get_singleton().res_config_dir;
}
@ -271,6 +265,14 @@ String get_res_temp_assemblies_dir() {
return _GodotSharpDirs::get_singleton().res_temp_assemblies_dir;
}
String get_api_assemblies_dir() {
return _GodotSharpDirs::get_singleton().api_assemblies_dir;
}
String get_api_assemblies_base_dir() {
return _GodotSharpDirs::get_singleton().api_assemblies_base_dir;
}
String get_mono_user_dir() {
return _GodotSharpDirs::get_singleton().mono_user_dir;
}
@ -299,10 +301,6 @@ String get_project_csproj_path() {
String get_data_editor_tools_dir() {
return _GodotSharpDirs::get_singleton().data_editor_tools_dir;
}
String get_data_editor_prebuilt_api_dir() {
return _GodotSharpDirs::get_singleton().data_editor_prebuilt_api_dir;
}
#else
String get_data_game_assemblies_dir() {
return _GodotSharpDirs::get_singleton().data_game_assemblies_dir;

View File

@ -37,13 +37,14 @@ namespace GodotSharpDirs {
String get_res_data_dir();
String get_res_metadata_dir();
String get_res_assemblies_base_dir();
String get_res_assemblies_dir();
String get_res_config_dir();
String get_res_temp_dir();
String get_res_temp_assemblies_base_dir();
String get_res_temp_assemblies_dir();
String get_api_assemblies_dir();
String get_api_assemblies_base_dir();
String get_mono_user_dir();
String get_mono_logs_dir();
@ -55,7 +56,6 @@ String get_project_sln_path();
String get_project_csproj_path();
String get_data_editor_tools_dir();
String get_data_editor_prebuilt_api_dir();
#else
String get_data_game_assemblies_dir();
#endif

View File

@ -32,8 +32,6 @@
#include "csharp_script.h"
#include "mono_gd/gd_mono_cache.h"
#include "mono_gd/gd_mono_marshal.h"
#include "mono_gd/gd_mono_utils.h"
#ifdef GD_MONO_HOT_RELOAD
SelfList<ManagedCallable>::List ManagedCallable::instances;
@ -45,20 +43,16 @@ bool ManagedCallable::compare_equal(const CallableCustom *p_a, const CallableCus
const ManagedCallable *a = static_cast<const ManagedCallable *>(p_a);
const ManagedCallable *b = static_cast<const ManagedCallable *>(p_b);
if (!a->delegate_handle || !b->delegate_handle) {
if (!a->delegate_handle && !b->delegate_handle) {
if (!a->delegate_handle.value || !b->delegate_handle.value) {
if (!a->delegate_handle.value && !b->delegate_handle.value) {
return true;
}
return false;
}
// Call Delegate's 'Equals'
MonoException *exc = nullptr;
MonoBoolean res = CACHED_METHOD_THUNK(DelegateUtils, DelegateEquals)
.invoke(a->delegate_handle,
b->delegate_handle, &exc);
UNHANDLED_EXCEPTION(exc);
return (bool)res;
return GDMonoCache::managed_callbacks.DelegateUtils_DelegateEquals(
a->delegate_handle, b->delegate_handle);
}
bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
@ -69,7 +63,7 @@ bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCust
}
uint32_t ManagedCallable::hash() const {
return hash_djb2_one_64((uint64_t)delegate_handle);
return hash_djb2_one_64((uint64_t)delegate_handle.value);
}
String ManagedCallable::get_as_text() const {
@ -93,34 +87,22 @@ void ManagedCallable::call(const Variant **p_arguments, int p_argcount, Variant
r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better
r_return_value = Variant();
MonoException *exc = nullptr;
CACHED_METHOD_THUNK(DelegateUtils, InvokeWithVariantArgs)
.invoke(delegate_handle, p_arguments,
p_argcount, &r_return_value, &exc);
GDMonoCache::managed_callbacks.DelegateUtils_InvokeWithVariantArgs(
delegate_handle, p_arguments, p_argcount, &r_return_value);
if (exc) {
GDMonoUtils::set_pending_exception(exc);
} else {
r_call_error.error = Callable::CallError::CALL_OK;
}
r_call_error.error = Callable::CallError::CALL_OK;
}
void ManagedCallable::release_delegate_handle() {
if (delegate_handle) {
MonoException *exc = nullptr;
CACHED_METHOD_THUNK(DelegateUtils, FreeGCHandle).invoke(delegate_handle, &exc);
if (exc) {
GDMonoUtils::debug_print_unhandled_exception(exc);
}
delegate_handle = nullptr;
if (delegate_handle.value) {
GDMonoCache::managed_callbacks.GCHandleBridge_FreeGCHandle(delegate_handle);
delegate_handle = GCHandleIntPtr();
}
}
// Why you do this clang-format...
/* clang-format off */
ManagedCallable::ManagedCallable(void *p_delegate_handle) : delegate_handle(p_delegate_handle) {
ManagedCallable::ManagedCallable(GCHandleIntPtr p_delegate_handle) : delegate_handle(p_delegate_handle) {
#ifdef GD_MONO_HOT_RELOAD
{
MutexLock lock(instances_mutex);

View File

@ -31,18 +31,15 @@
#ifndef MANAGED_CALLABLE_H
#define MANAGED_CALLABLE_H
#include <mono/metadata/object.h>
#include "core/os/mutex.h"
#include "core/templates/self_list.h"
#include "core/variant/callable.h"
#include "mono_gc_handle.h"
#include "mono_gd/gd_mono_method.h"
class ManagedCallable : public CallableCustom {
friend class CSharpLanguage;
void *delegate_handle;
GCHandleIntPtr delegate_handle;
#ifdef GD_MONO_HOT_RELOAD
SelfList<ManagedCallable> self_instance = this;
@ -59,7 +56,7 @@ public:
ObjectID get_object() const override;
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
_FORCE_INLINE_ void *get_delegate() const { return delegate_handle; }
_FORCE_INLINE_ GCHandleIntPtr get_delegate() const { return delegate_handle; }
static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
@ -69,7 +66,7 @@ public:
void release_delegate_handle();
ManagedCallable(void *p_delegate_handle);
ManagedCallable(GCHandleIntPtr p_delegate_handle);
~ManagedCallable();
};

View File

@ -31,34 +31,20 @@
#include "mono_gc_handle.h"
#include "mono_gd/gd_mono.h"
#include "mono_gd/gd_mono_cache.h"
void MonoGCHandleData::release() {
#ifdef DEBUG_ENABLED
CRASH_COND(handle && GDMono::get_singleton() == nullptr);
CRASH_COND(handle.value && GDMono::get_singleton() == nullptr);
#endif
if (handle && GDMono::get_singleton()->is_runtime_initialized()) {
GDMonoUtils::free_gchandle(handle);
handle = 0;
if (handle.value && GDMonoCache::godot_api_cache_updated &&
GDMono::get_singleton()->is_runtime_initialized()) {
free_gchandle(handle);
handle.value = nullptr;
}
}
MonoGCHandleData MonoGCHandleData::new_strong_handle(MonoObject *p_object) {
return MonoGCHandleData(GDMonoUtils::new_strong_gchandle(p_object), gdmono::GCHandleType::STRONG_HANDLE);
}
MonoGCHandleData MonoGCHandleData::new_strong_handle_pinned(MonoObject *p_object) {
return MonoGCHandleData(GDMonoUtils::new_strong_gchandle_pinned(p_object), gdmono::GCHandleType::STRONG_HANDLE);
}
MonoGCHandleData MonoGCHandleData::new_weak_handle(MonoObject *p_object) {
return MonoGCHandleData(GDMonoUtils::new_weak_gchandle(p_object), gdmono::GCHandleType::WEAK_HANDLE);
}
Ref<MonoGCHandleRef> MonoGCHandleRef::create_strong(MonoObject *p_object) {
return memnew(MonoGCHandleRef(MonoGCHandleData::new_strong_handle(p_object)));
}
Ref<MonoGCHandleRef> MonoGCHandleRef::create_weak(MonoObject *p_object) {
return memnew(MonoGCHandleRef(MonoGCHandleData::new_weak_handle(p_object)));
void MonoGCHandleData::free_gchandle(GCHandleIntPtr p_gchandle) {
CRASH_COND(!GDMonoCache::godot_api_cache_updated);
GDMonoCache::managed_callbacks.GCHandleBridge_FreeGCHandle(p_gchandle);
}

View File

@ -31,8 +31,6 @@
#ifndef CSHARP_GC_HANDLE_H
#define CSHARP_GC_HANDLE_H
#include <mono/jit/jit.h>
#include "core/object/ref_counted.h"
namespace gdmono {
@ -44,18 +42,27 @@ enum class GCHandleType : char {
};
}
extern "C" {
struct GCHandleIntPtr {
void *value = nullptr;
};
}
static_assert(sizeof(GCHandleIntPtr) == sizeof(void *));
// Manual release of the GC handle must be done when using this struct
struct MonoGCHandleData {
uint32_t handle = 0;
GCHandleIntPtr handle;
gdmono::GCHandleType type = gdmono::GCHandleType::NIL;
_FORCE_INLINE_ bool is_released() const { return !handle; }
_FORCE_INLINE_ bool is_released() const { return !handle.value; }
_FORCE_INLINE_ bool is_weak() const { return type == gdmono::GCHandleType::WEAK_HANDLE; }
_FORCE_INLINE_ MonoObject *get_target() const { return handle ? mono_gchandle_get_target(handle) : nullptr; }
_FORCE_INLINE_ GCHandleIntPtr get_intptr() const { return handle; }
void release();
static void free_gchandle(GCHandleIntPtr p_gchandle);
MonoGCHandleData &operator=(const MonoGCHandleData &p_other) {
#ifdef DEBUG_ENABLED
CRASH_COND(!is_released());
@ -69,40 +76,10 @@ struct MonoGCHandleData {
MonoGCHandleData() {}
MonoGCHandleData(uint32_t p_handle, gdmono::GCHandleType p_type) :
MonoGCHandleData(GCHandleIntPtr p_handle, gdmono::GCHandleType p_type) :
handle(p_handle),
type(p_type) {
}
static MonoGCHandleData new_strong_handle(MonoObject *p_object);
static MonoGCHandleData new_strong_handle_pinned(MonoObject *p_object);
static MonoGCHandleData new_weak_handle(MonoObject *p_object);
};
class MonoGCHandleRef : public RefCounted {
GDCLASS(MonoGCHandleRef, RefCounted);
MonoGCHandleData data;
public:
static Ref<MonoGCHandleRef> create_strong(MonoObject *p_object);
static Ref<MonoGCHandleRef> create_weak(MonoObject *p_object);
_FORCE_INLINE_ bool is_released() const { return data.is_released(); }
_FORCE_INLINE_ bool is_weak() const { return data.is_weak(); }
_FORCE_INLINE_ MonoObject *get_target() const { return data.get_target(); }
void release() { data.release(); }
_FORCE_INLINE_ void set_handle(uint32_t p_handle, gdmono::GCHandleType p_handle_type) {
data = MonoGCHandleData(p_handle, p_handle_type);
}
MonoGCHandleRef(const MonoGCHandleData &p_gc_handle_data) :
data(p_gc_handle_data) {
}
~MonoGCHandleRef() { release(); }
};
#endif // CSHARP_GC_HANDLE_H

File diff suppressed because it is too large Load Diff

View File

@ -34,122 +34,35 @@
#include "core/io/config_file.h"
#include "../godotsharp_defs.h"
#include "gd_mono_assembly.h"
#include "gd_mono_log.h"
#ifdef WINDOWS_ENABLED
#include "../utils/mono_reg_utils.h"
#endif
namespace ApiAssemblyInfo {
enum Type {
API_CORE,
API_EDITOR
};
struct Version {
uint64_t godot_api_hash = 0;
bool operator==(const Version &p_other) const {
return godot_api_hash == p_other.godot_api_hash;
}
Version() {}
Version(uint64_t p_godot_api_hash) :
godot_api_hash(p_godot_api_hash) {
}
static Version get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, Type p_api_type);
};
String to_string(Type p_type);
} // namespace ApiAssemblyInfo
class GDMono {
public:
enum UnhandledExceptionPolicy {
POLICY_TERMINATE_APP,
POLICY_LOG_ERROR
};
struct LoadedApiAssembly {
GDMonoAssembly *assembly = nullptr;
bool out_of_sync = false;
LoadedApiAssembly() {}
};
private:
bool runtime_initialized;
bool finalizing_scripts_domain;
UnhandledExceptionPolicy unhandled_exception_policy;
MonoDomain *root_domain;
MonoDomain *scripts_domain;
HashMap<int32_t, HashMap<String, GDMonoAssembly *>> assemblies;
GDMonoAssembly *corlib_assembly;
GDMonoAssembly *project_assembly;
#ifdef TOOLS_ENABLED
GDMonoAssembly *tools_assembly;
GDMonoAssembly *tools_project_editor_assembly;
#endif
LoadedApiAssembly core_api_assembly;
LoadedApiAssembly editor_api_assembly;
typedef bool (*CoreApiAssemblyLoadedCallback)();
bool _are_api_assemblies_out_of_sync();
bool _temp_domain_load_are_assemblies_out_of_sync(const String &p_config);
bool _load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly);
#ifdef TOOLS_ENABLED
bool _load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly);
#endif
static bool _on_core_api_assembly_loaded();
bool _load_corlib_assembly();
#ifdef TOOLS_ENABLED
bool _load_tools_assemblies();
#endif
bool _load_project_assembly();
bool _try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, LoadedApiAssembly &r_editor_api_assembly,
const String &p_config, bool p_refonly, CoreApiAssemblyLoadedCallback p_callback);
bool _try_load_api_assemblies_preset();
void _load_api_assemblies();
bool _try_load_api_assemblies();
void _install_trace_listener();
void _register_internal_calls();
#ifndef GD_MONO_SINGLE_APPDOMAIN
Error _load_scripts_domain();
Error _unload_scripts_domain();
#endif
void _domain_assemblies_cleanup(int32_t p_domain_id);
uint64_t api_core_hash;
#ifdef TOOLS_ENABLED
uint64_t api_editor_hash;
#endif
void _init_godot_api_hashes();
void _init_exception_policy();
GDMonoLog *gdmono_log;
#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
MonoRegInfo mono_reg_info;
friend class CSharpLanguage;
#ifdef WIN32
#define GD_CLR_STDCALL __stdcall
#else
#define GD_CLR_STDCALL
#endif
void add_mono_shared_libs_dir_to_path();
void determine_mono_dirs(String &r_assembly_rootdir, String &r_config_dir);
struct PluginCallbacks {
using FuncLoadProjectAssemblyCallback = bool(GD_CLR_STDCALL *)(const char16_t *);
using FuncLoadToolsAssemblyCallback = Object *(GD_CLR_STDCALL *)(const char16_t *);
FuncLoadProjectAssemblyCallback LoadProjectAssemblyCallback = nullptr;
FuncLoadToolsAssemblyCallback LoadToolsAssemblyCallback = nullptr;
} plugin_callbacks;
protected:
static GDMono *singleton;
@ -184,54 +97,15 @@ public:
#endif
}
#ifdef TOOLS_ENABLED
bool copy_prebuilt_api_assembly(ApiAssemblyInfo::Type p_api_type, const String &p_config);
String update_api_assemblies_from_prebuilt(const String &p_config, const bool *p_core_api_out_of_sync = nullptr, const bool *p_editor_api_out_of_sync = nullptr);
#endif
static GDMono *get_singleton() { return singleton; }
[[noreturn]] static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data);
UnhandledExceptionPolicy get_unhandled_exception_policy() const { return unhandled_exception_policy; }
// Do not use these, unless you know what you're doing
void add_assembly(int32_t p_domain_id, GDMonoAssembly *p_assembly);
GDMonoAssembly *get_loaded_assembly(const String &p_name);
_FORCE_INLINE_ bool is_runtime_initialized() const { return runtime_initialized && !mono_runtime_is_shutting_down() /* stays true after shutdown finished */; }
_FORCE_INLINE_ bool is_runtime_initialized() const { return runtime_initialized; }
_FORCE_INLINE_ bool is_finalizing_scripts_domain() { return finalizing_scripts_domain; }
_FORCE_INLINE_ MonoDomain *get_scripts_domain() { return scripts_domain; }
_FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; }
_FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly.assembly; }
_FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; }
#ifdef TOOLS_ENABLED
_FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly.assembly; }
_FORCE_INLINE_ GDMonoAssembly *get_tools_assembly() const { return tools_assembly; }
_FORCE_INLINE_ GDMonoAssembly *get_tools_project_editor_assembly() const { return tools_project_editor_assembly; }
#endif
#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
const MonoRegInfo &get_mono_reg_info() { return mono_reg_info; }
#endif
GDMonoClass *get_class(MonoClass *p_raw_class);
GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name);
#ifdef GD_MONO_HOT_RELOAD
Error reload_scripts_domain();
#endif
bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly = false);
bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false);
bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly, const Vector<String> &p_search_dirs);
bool load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly = false);
Error finalize_and_unload_domain(MonoDomain *p_domain);
void initialize();
void initialize_load_assemblies();
@ -239,52 +113,6 @@ public:
~GDMono();
};
namespace gdmono {
class ScopeDomain {
MonoDomain *prev_domain;
public:
ScopeDomain(MonoDomain *p_domain) {
prev_domain = mono_domain_get();
if (prev_domain != p_domain) {
mono_domain_set(p_domain, false);
} else {
prev_domain = nullptr;
}
}
~ScopeDomain() {
if (prev_domain) {
mono_domain_set(prev_domain, false);
}
}
};
class ScopeExitDomainUnload {
MonoDomain *domain;
public:
ScopeExitDomainUnload(MonoDomain *p_domain) :
domain(p_domain) {
}
~ScopeExitDomainUnload() {
if (domain) {
GDMono::get_singleton()->finalize_and_unload_domain(domain);
}
}
};
} // namespace gdmono
#define _GDMONO_SCOPE_DOMAIN_(m_mono_domain) \
gdmono::ScopeDomain __gdmono__scope__domain__(m_mono_domain); \
(void)__gdmono__scope__domain__;
#define _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(m_mono_domain) \
gdmono::ScopeExitDomainUnload __gdmono__scope__exit__domain__unload__(m_mono_domain); \
(void)__gdmono__scope__exit__domain__unload__;
namespace mono_bind {
class GodotSharp : public Object {
@ -292,9 +120,8 @@ class GodotSharp : public Object {
friend class GDMono;
bool _is_domain_finalizing_for_unload(int32_t p_domain_id);
void _reload_assemblies(bool p_soft_reload);
bool _is_runtime_initialized();
protected:
static GodotSharp *singleton;
@ -303,20 +130,6 @@ protected:
public:
static GodotSharp *get_singleton() { return singleton; }
void attach_thread();
void detach_thread();
int32_t get_domain_id();
int32_t get_scripts_domain_id();
bool is_scripts_domain_loaded();
bool is_domain_finalizing_for_unload(int32_t p_domain_id);
bool is_domain_finalizing_for_unload(MonoDomain *p_domain);
bool is_runtime_shutting_down();
bool is_runtime_initialized();
GodotSharp();
~GodotSharp();
};

View File

@ -1,482 +0,0 @@
/*************************************************************************/
/* gd_mono_assembly.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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_assembly.h"
#include <mono/metadata/mono-debug.h>
#include <mono/metadata/tokentype.h>
#include "core/config/project_settings.h"
#include "core/io/file_access.h"
#include "core/io/file_access_pack.h"
#include "core/os/os.h"
#include "core/templates/list.h"
#include "../godotsharp_dirs.h"
#include "gd_mono_cache.h"
#include "gd_mono_class.h"
Vector<String> GDMonoAssembly::search_dirs;
void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config, const String &p_custom_bcl_dir) {
String framework_dir;
if (!p_custom_bcl_dir.is_empty()) {
framework_dir = p_custom_bcl_dir;
} else if (mono_assembly_getrootdir()) {
framework_dir = String::utf8(mono_assembly_getrootdir()).plus_file("mono").plus_file("4.5");
}
if (!framework_dir.is_empty()) {
r_search_dirs.push_back(framework_dir);
r_search_dirs.push_back(framework_dir.plus_file("Facades"));
}
#if !defined(TOOLS_ENABLED)
String data_game_assemblies_dir = GodotSharpDirs::get_data_game_assemblies_dir();
if (!data_game_assemblies_dir.is_empty()) {
r_search_dirs.push_back(data_game_assemblies_dir);
}
#endif
if (p_custom_config.length()) {
r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(p_custom_config));
} else {
r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
}
if (p_custom_config.is_empty()) {
r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir());
} else {
String api_config = p_custom_config == "ExportRelease" ? "Release" : "Debug";
r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_base_dir().plus_file(api_config));
}
r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_base_dir());
r_search_dirs.push_back(OS::get_singleton()->get_resource_dir());
r_search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir());
#ifdef TOOLS_ENABLED
r_search_dirs.push_back(GodotSharpDirs::get_data_editor_tools_dir());
// For GodotTools to find the api assemblies
r_search_dirs.push_back(GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug"));
#endif
}
// This is how these assembly loading hooks work:
//
// - The 'search' hook checks if the assembly has already been loaded, to avoid loading again.
// - The 'preload' hook does the actual loading and is only called if the
// 'search' hook didn't find the assembly in the list of loaded assemblies.
// - The 'load' hook is called after the assembly has been loaded. Its job is to add the
// assembly to the list of loaded assemblies so that the 'search' hook can look it up.
void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, [[maybe_unused]] void *user_data) {
String name = String::utf8(mono_assembly_name_get_name(mono_assembly_get_name(assembly)));
MonoImage *image = mono_assembly_get_image(assembly);
GDMonoAssembly *gdassembly = memnew(GDMonoAssembly(name, image, assembly));
#ifdef GD_MONO_HOT_RELOAD
String path = String::utf8(mono_image_get_filename(image));
if (FileAccess::exists(path)) {
gdassembly->modified_time = FileAccess::get_modified_time(path);
}
#endif
MonoDomain *domain = mono_domain_get();
GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, gdassembly);
}
MonoAssembly *GDMonoAssembly::assembly_search_hook(MonoAssemblyName *aname, void *user_data) {
return GDMonoAssembly::_search_hook(aname, user_data, false);
}
MonoAssembly *GDMonoAssembly::assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data) {
return GDMonoAssembly::_search_hook(aname, user_data, true);
}
MonoAssembly *GDMonoAssembly::assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) {
return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, false);
}
MonoAssembly *GDMonoAssembly::assembly_refonly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) {
return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, true);
}
MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, [[maybe_unused]] void *user_data, bool refonly) {
String name = String::utf8(mono_assembly_name_get_name(aname));
bool has_extension = name.ends_with(".dll") || name.ends_with(".exe");
GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name);
if (loaded_asm) {
return loaded_asm->get_assembly();
}
return nullptr;
}
MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, [[maybe_unused]] void *user_data, bool refonly) {
String name = String::utf8(mono_assembly_name_get_name(aname));
return _load_assembly_search(name, aname, refonly, search_dirs);
}
MonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs) {
MonoAssembly *res = nullptr;
String path;
bool has_extension = p_name.ends_with(".dll") || p_name.ends_with(".exe");
for (int i = 0; i < p_search_dirs.size(); i++) {
const String &search_dir = p_search_dirs[i];
if (has_extension) {
path = search_dir.plus_file(p_name);
if (FileAccess::exists(path)) {
res = _real_load_assembly_from(path, p_refonly, p_aname);
if (res != nullptr) {
return res;
}
}
} else {
path = search_dir.plus_file(p_name + ".dll");
if (FileAccess::exists(path)) {
res = _real_load_assembly_from(path, p_refonly, p_aname);
if (res != nullptr) {
return res;
}
}
path = search_dir.plus_file(p_name + ".exe");
if (FileAccess::exists(path)) {
res = _real_load_assembly_from(path, p_refonly, p_aname);
if (res != nullptr) {
return res;
}
}
}
}
return nullptr;
}
String GDMonoAssembly::find_assembly(const String &p_name) {
String path;
bool has_extension = p_name.ends_with(".dll") || p_name.ends_with(".exe");
for (int i = 0; i < search_dirs.size(); i++) {
const String &search_dir = search_dirs[i];
if (has_extension) {
path = search_dir.plus_file(p_name);
if (FileAccess::exists(path)) {
return path;
}
} else {
path = search_dir.plus_file(p_name + ".dll");
if (FileAccess::exists(path)) {
return path;
}
path = search_dir.plus_file(p_name + ".exe");
if (FileAccess::exists(path)) {
return path;
}
}
}
return String();
}
void GDMonoAssembly::initialize() {
fill_search_dirs(search_dirs);
mono_install_assembly_search_hook(&assembly_search_hook, nullptr);
mono_install_assembly_refonly_search_hook(&assembly_refonly_search_hook, nullptr);
mono_install_assembly_preload_hook(&assembly_preload_hook, nullptr);
mono_install_assembly_refonly_preload_hook(&assembly_refonly_preload_hook, nullptr);
mono_install_assembly_load_hook(&assembly_load_hook, nullptr);
}
MonoAssembly *GDMonoAssembly::_real_load_assembly_from(const String &p_path, bool p_refonly, MonoAssemblyName *p_aname) {
Vector<uint8_t> data = FileAccess::get_file_as_array(p_path);
ERR_FAIL_COND_V_MSG(data.is_empty(), nullptr, "Could read the assembly in the specified location");
String image_filename;
#ifdef ANDROID_ENABLED
if (p_path.begins_with("res://")) {
image_filename = p_path.substr(6, p_path.length());
} else {
image_filename = ProjectSettings::get_singleton()->globalize_path(p_path);
}
#else
// FIXME: globalize_path does not work on exported games
image_filename = ProjectSettings::get_singleton()->globalize_path(p_path);
#endif
MonoImageOpenStatus status = MONO_IMAGE_OK;
MonoImage *image = mono_image_open_from_data_with_name(
(char *)&data[0], data.size(),
true, &status, p_refonly,
image_filename.utf8());
ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !image, nullptr, "Failed to open assembly image from memory: '" + p_path + "'.");
if (p_aname != nullptr) {
// Check assembly version
const MonoTableInfo *table = mono_image_get_table_info(image, MONO_TABLE_ASSEMBLY);
ERR_FAIL_NULL_V(table, nullptr);
if (mono_table_info_get_rows(table)) {
uint32_t cols[MONO_ASSEMBLY_SIZE];
mono_metadata_decode_row(table, 0, cols, MONO_ASSEMBLY_SIZE);
// Not sure about .NET's policy. We will only ensure major and minor are equal, and ignore build and revision.
uint16_t major = cols[MONO_ASSEMBLY_MAJOR_VERSION];
uint16_t minor = cols[MONO_ASSEMBLY_MINOR_VERSION];
uint16_t required_minor;
uint16_t required_major = mono_assembly_name_get_version(p_aname, &required_minor, nullptr, nullptr);
if (required_major != 0) {
if (major != required_major && minor != required_minor) {
mono_image_close(image);
return nullptr;
}
}
}
}
#ifdef DEBUG_ENABLED
Vector<uint8_t> pdb_data;
String pdb_path(p_path + ".pdb");
if (!FileAccess::exists(pdb_path)) {
pdb_path = p_path.get_basename() + ".pdb"; // without .dll
if (!FileAccess::exists(pdb_path)) {
goto no_pdb;
}
}
pdb_data = FileAccess::get_file_as_array(pdb_path);
// mono_debug_close_image doesn't seem to be needed
mono_debug_open_image_from_memory(image, &pdb_data[0], pdb_data.size());
no_pdb:
#endif
bool need_manual_load_hook = mono_image_get_assembly(image) != nullptr; // Re-using an existing image with an assembly loaded
status = MONO_IMAGE_OK;
MonoAssembly *assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, p_refonly);
ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !assembly, nullptr, "Failed to load assembly for image");
if (need_manual_load_hook) {
// For some reason if an assembly survived domain reloading (maybe because it's referenced somewhere else),
// the mono internal search hook don't detect it, yet mono_image_open_from_data_with_name re-uses the image
// and assembly, and mono_assembly_load_from_full doesn't call the load hook. We need to call it manually.
String name = String::utf8(mono_assembly_name_get_name(mono_assembly_get_name(assembly)));
bool has_extension = name.ends_with(".dll") || name.ends_with(".exe");
GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name);
if (!loaded_asm) {
assembly_load_hook(assembly, nullptr);
}
}
// Decrement refcount which was previously incremented by mono_image_open_from_data_with_name
mono_image_close(image);
return assembly;
}
void GDMonoAssembly::unload() {
ERR_FAIL_NULL(image); // Should not be called if already unloaded
for (const KeyValue<MonoClass *, GDMonoClass *> &E : cached_raw) {
memdelete(E.value);
}
cached_classes.clear();
cached_raw.clear();
assembly = nullptr;
image = nullptr;
}
String GDMonoAssembly::get_path() const {
return String::utf8(mono_image_get_filename(image));
}
bool GDMonoAssembly::has_attribute(GDMonoClass *p_attr_class) {
#ifdef DEBUG_ENABLED
ERR_FAIL_NULL_V(p_attr_class, false);
#endif
if (!attrs_fetched) {
fetch_attributes();
}
if (!attributes) {
return false;
}
return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
}
MonoObject *GDMonoAssembly::get_attribute(GDMonoClass *p_attr_class) {
#ifdef DEBUG_ENABLED
ERR_FAIL_NULL_V(p_attr_class, nullptr);
#endif
if (!attrs_fetched) {
fetch_attributes();
}
if (!attributes) {
return nullptr;
}
return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
}
void GDMonoAssembly::fetch_attributes() {
ERR_FAIL_COND(attributes != nullptr);
attributes = mono_custom_attrs_from_assembly(assembly);
attrs_fetched = true;
}
GDMonoClass *GDMonoAssembly::get_class(const StringName &p_namespace, const StringName &p_name) {
ERR_FAIL_NULL_V(image, nullptr);
ClassKey key(p_namespace, p_name);
GDMonoClass **match = cached_classes.getptr(key);
if (match) {
return *match;
}
MonoClass *mono_class = mono_class_from_name(image, String(p_namespace).utf8(), String(p_name).utf8());
if (!mono_class) {
return nullptr;
}
GDMonoClass *wrapped_class = memnew(GDMonoClass(p_namespace, p_name, mono_class, this));
cached_classes[key] = wrapped_class;
cached_raw[mono_class] = wrapped_class;
return wrapped_class;
}
GDMonoClass *GDMonoAssembly::get_class(MonoClass *p_mono_class) {
ERR_FAIL_NULL_V(image, nullptr);
Map<MonoClass *, GDMonoClass *>::Element *match = cached_raw.find(p_mono_class);
if (match) {
return match->value();
}
StringName namespace_name = String::utf8(mono_class_get_namespace(p_mono_class));
StringName class_name = String::utf8(mono_class_get_name(p_mono_class));
GDMonoClass *wrapped_class = memnew(GDMonoClass(namespace_name, class_name, p_mono_class, this));
cached_classes[ClassKey(namespace_name, class_name)] = wrapped_class;
cached_raw[p_mono_class] = wrapped_class;
return wrapped_class;
}
GDMonoAssembly *GDMonoAssembly::load(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs) {
if (GDMono::get_singleton()->get_corlib_assembly() && (p_name == "mscorlib" || p_name == "mscorlib.dll")) {
return GDMono::get_singleton()->get_corlib_assembly();
}
// We need to manually call the search hook in this case, as it won't be called in the next step
MonoAssembly *assembly = mono_assembly_invoke_search_hook(p_aname);
if (!assembly) {
assembly = _load_assembly_search(p_name, p_aname, p_refonly, p_search_dirs);
if (!assembly) {
return nullptr;
}
}
GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
ERR_FAIL_NULL_V_MSG(loaded_asm, nullptr, "Loaded assembly missing from table. Did we not receive the load hook?");
ERR_FAIL_COND_V(loaded_asm->get_assembly() != assembly, nullptr);
return loaded_asm;
}
GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) {
if (p_name == "mscorlib" || p_name == "mscorlib.dll") {
return GDMono::get_singleton()->get_corlib_assembly();
}
// We need to manually call the search hook in this case, as it won't be called in the next step
MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
MonoAssembly *assembly = mono_assembly_invoke_search_hook(aname);
mono_assembly_name_free(aname);
mono_free(aname);
if (!assembly) {
assembly = _real_load_assembly_from(p_path, p_refonly);
if (!assembly) {
return nullptr;
}
}
GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
ERR_FAIL_NULL_V_MSG(loaded_asm, nullptr, "Loaded assembly missing from table. Did we not receive the load hook?");
return loaded_asm;
}
GDMonoAssembly::~GDMonoAssembly() {
if (image) {
unload();
}
}

View File

@ -1,138 +0,0 @@
/*************************************************************************/
/* gd_mono_assembly.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
#ifndef GD_MONO_ASSEMBLY_H
#define GD_MONO_ASSEMBLY_H
#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>
#include "core/string/ustring.h"
#include "core/templates/hash_map.h"
#include "core/templates/map.h"
#include "gd_mono_utils.h"
class GDMonoAssembly {
struct ClassKey {
struct Hasher {
static _FORCE_INLINE_ uint32_t hash(const ClassKey &p_key) {
uint32_t hash = 0;
GDMonoUtils::hash_combine(hash, p_key.namespace_name.hash());
GDMonoUtils::hash_combine(hash, p_key.class_name.hash());
return hash;
}
};
_FORCE_INLINE_ bool operator==(const ClassKey &p_a) const {
return p_a.class_name == class_name && p_a.namespace_name == namespace_name;
}
ClassKey() {}
ClassKey(const StringName &p_namespace_name, const StringName &p_class_name) {
namespace_name = p_namespace_name;
class_name = p_class_name;
}
StringName namespace_name;
StringName class_name;
};
String name;
MonoImage *image;
MonoAssembly *assembly;
bool attrs_fetched = false;
MonoCustomAttrInfo *attributes = nullptr;
#ifdef GD_MONO_HOT_RELOAD
uint64_t modified_time = 0;
#endif
HashMap<ClassKey, GDMonoClass *, ClassKey::Hasher> cached_classes;
Map<MonoClass *, GDMonoClass *> cached_raw;
static Vector<String> search_dirs;
static void assembly_load_hook(MonoAssembly *assembly, void *user_data);
static MonoAssembly *assembly_search_hook(MonoAssemblyName *aname, void *user_data);
static MonoAssembly *assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data);
static MonoAssembly *assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data);
static MonoAssembly *assembly_refonly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data);
static MonoAssembly *_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly);
static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly);
static MonoAssembly *_real_load_assembly_from(const String &p_path, bool p_refonly, MonoAssemblyName *p_aname = nullptr);
static MonoAssembly *_load_assembly_search(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs);
friend class GDMono;
static void initialize();
public:
void unload();
_FORCE_INLINE_ MonoImage *get_image() const { return image; }
_FORCE_INLINE_ MonoAssembly *get_assembly() const { return assembly; }
_FORCE_INLINE_ String get_name() const { return name; }
#ifdef GD_MONO_HOT_RELOAD
_FORCE_INLINE_ uint64_t get_modified_time() const { return modified_time; }
#endif
String get_path() const;
bool has_attribute(GDMonoClass *p_attr_class);
MonoObject *get_attribute(GDMonoClass *p_attr_class);
void fetch_attributes();
GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name);
GDMonoClass *get_class(MonoClass *p_mono_class);
static String find_assembly(const String &p_name);
static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String(), const String &p_custom_bcl_dir = String());
static const Vector<String> &get_default_search_dirs() { return search_dirs; }
static GDMonoAssembly *load(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs);
static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly);
GDMonoAssembly(const String &p_name, MonoImage *p_image, MonoAssembly *p_assembly) :
name(p_name),
image(p_image),
assembly(p_assembly) {
}
~GDMonoAssembly();
};
#endif // GD_MONO_ASSEMBLY_H

View File

@ -30,203 +30,47 @@
#include "gd_mono_cache.h"
#include "gd_mono.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
#include "gd_mono_method.h"
#include "gd_mono_utils.h"
#include "core/error/error_macros.h"
namespace GDMonoCache {
CachedData cached_data;
ManagedCallbacks managed_callbacks;
bool godot_api_cache_updated = false;
#define CACHE_AND_CHECK(m_var, m_val) \
{ \
CRASH_COND(m_var != nullptr); \
m_var = m_val; \
ERR_FAIL_COND_MSG(m_var == nullptr, "Mono Cache: Member " #m_var " is null."); \
}
void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks) {
#define CHECK_CALLBACK_NOT_NULL_IMPL(m_var, m_class, m_method) ERR_FAIL_COND_MSG(m_var == nullptr, \
"Mono Cache: Managed callback for '" #m_class "_" #m_method "' is null.")
#define CACHE_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(cached_data.class_##m_class, m_val)
#define CACHE_FIELD_AND_CHECK(m_class, m_field, m_val) CACHE_AND_CHECK(cached_data.field_##m_class##_##m_field, m_val)
#define CACHE_METHOD_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(cached_data.method_##m_class##_##m_method, m_val)
#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)
#define CACHE_METHOD_THUNK_AND_CHECK_IMPL(m_var, m_val) \
{ \
CRASH_COND(!m_var.is_null()); \
ERR_FAIL_COND_MSG(m_val == nullptr, "Mono Cache: Method for member " #m_var " is null."); \
m_var.set_from_method(m_val); \
ERR_FAIL_COND_MSG(m_var.is_null(), "Mono Cache: Member " #m_var " is null."); \
}
CHECK_CALLBACK_NOT_NULL(SignalAwaiter, SignalCallback);
CHECK_CALLBACK_NOT_NULL(DelegateUtils, InvokeWithVariantArgs);
CHECK_CALLBACK_NOT_NULL(DelegateUtils, DelegateEquals);
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, SetGodotObjectPtr);
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, RaiseEventSignal);
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetScriptSignalList);
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, HasScriptSignal);
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, HasMethodUnknownParams);
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, ScriptIsOrInherits);
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, AddScriptBridge);
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, RemoveScriptBridge);
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, UpdateScriptClassInfo);
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, SwapGCHandleForType);
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(GCHandleBridge, FreeGCHandle);
CHECK_CALLBACK_NOT_NULL(DebuggingUtils, InstallTraceListener);
CHECK_CALLBACK_NOT_NULL(Dispatcher, InitializeDefaultGodotTaskScheduler);
#define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_METHOD_THUNK_AND_CHECK_IMPL(cached_data.methodthunk_##m_class##_##m_method, m_val)
managed_callbacks = p_managed_callbacks;
void CachedData::clear_corlib_cache() {
corlib_cache_updated = false;
class_MonoObject = nullptr;
class_String = nullptr;
#ifdef DEBUG_ENABLED
class_System_Diagnostics_StackTrace = nullptr;
methodthunk_System_Diagnostics_StackTrace_GetFrames.nullify();
method_System_Diagnostics_StackTrace_ctor_bool = nullptr;
method_System_Diagnostics_StackTrace_ctor_Exception_bool = nullptr;
#endif
class_KeyNotFoundException = nullptr;
}
void CachedData::clear_godot_api_cache() {
godot_api_cache_updated = false;
class_GodotObject = nullptr;
class_GodotResource = nullptr;
class_Control = nullptr;
class_Callable = nullptr;
class_SignalInfo = nullptr;
class_ISerializationListener = nullptr;
#ifdef DEBUG_ENABLED
class_DebuggingUtils = nullptr;
methodthunk_DebuggingUtils_GetStackFrameInfo.nullify();
#endif
class_ExportAttribute = nullptr;
field_ExportAttribute_hint = nullptr;
field_ExportAttribute_hintString = nullptr;
class_SignalAttribute = nullptr;
class_ToolAttribute = nullptr;
class_RemoteAttribute = nullptr;
class_MasterAttribute = nullptr;
class_PuppetAttribute = nullptr;
class_GodotMethodAttribute = nullptr;
field_GodotMethodAttribute_methodName = nullptr;
class_ScriptPathAttribute = nullptr;
field_ScriptPathAttribute_path = nullptr;
class_AssemblyHasScriptsAttribute = nullptr;
field_AssemblyHasScriptsAttribute_requiresLookup = nullptr;
field_AssemblyHasScriptsAttribute_scriptTypes = nullptr;
field_GodotObject_ptr = nullptr;
methodthunk_GodotObject_Dispose.nullify();
methodthunk_SignalAwaiter_SignalCallback.nullify();
methodthunk_GodotTaskScheduler_Activate.nullify();
methodthunk_Delegate_Equals.nullify();
methodthunk_DelegateUtils_TrySerializeDelegateWithGCHandle.nullify();
methodthunk_DelegateUtils_TryDeserializeDelegateWithGCHandle.nullify();
methodthunk_DelegateUtils_TrySerializeDelegate.nullify();
methodthunk_DelegateUtils_TryDeserializeDelegate.nullify();
methodthunk_DelegateUtils_InvokeWithVariantArgs.nullify();
methodthunk_DelegateUtils_DelegateEquals.nullify();
methodthunk_DelegateUtils_FreeGCHandle.nullify();
methodthunk_Marshaling_managed_to_variant_type.nullify();
methodthunk_Marshaling_try_get_array_element_type.nullify();
methodthunk_Marshaling_variant_to_mono_object_of_type.nullify();
methodthunk_Marshaling_variant_to_mono_object.nullify();
methodthunk_Marshaling_mono_object_to_variant_out.nullify();
methodthunk_Marshaling_SetFieldValue.nullify();
task_scheduler_handle = Ref<MonoGCHandleRef>();
}
#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class))
void update_corlib_cache() {
CACHE_CLASS_AND_CHECK(MonoObject, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_object_class()));
CACHE_CLASS_AND_CHECK(String, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_string_class()));
#ifdef DEBUG_ENABLED
CACHE_CLASS_AND_CHECK(System_Diagnostics_StackTrace, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Diagnostics", "StackTrace"));
CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method("GetFrames"));
CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(bool)", true));
CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_Exception_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(System.Exception,bool)", true));
#endif
CACHE_METHOD_THUNK_AND_CHECK(Delegate, Equals, GDMono::get_singleton()->get_corlib_assembly()->get_class("System", "Delegate")->get_method_with_desc("System.Delegate:Equals(object)", true));
CACHE_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException"));
cached_data.corlib_cache_updated = true;
}
void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object));
CACHE_CLASS_AND_CHECK(GodotResource, GODOT_API_CLASS(Resource));
CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control));
CACHE_CLASS_AND_CHECK(Callable, GODOT_API_CLASS(Callable));
CACHE_CLASS_AND_CHECK(SignalInfo, GODOT_API_CLASS(SignalInfo));
CACHE_CLASS_AND_CHECK(ISerializationListener, GODOT_API_CLASS(ISerializationListener));
#ifdef DEBUG_ENABLED
CACHE_CLASS_AND_CHECK(DebuggingUtils, GODOT_API_CLASS(DebuggingUtils));
#endif
// Attributes
CACHE_CLASS_AND_CHECK(ExportAttribute, GODOT_API_CLASS(ExportAttribute));
CACHE_FIELD_AND_CHECK(ExportAttribute, hint, CACHED_CLASS(ExportAttribute)->get_field("hint"));
CACHE_FIELD_AND_CHECK(ExportAttribute, hintString, CACHED_CLASS(ExportAttribute)->get_field("hintString"));
CACHE_CLASS_AND_CHECK(SignalAttribute, GODOT_API_CLASS(SignalAttribute));
CACHE_CLASS_AND_CHECK(ToolAttribute, GODOT_API_CLASS(ToolAttribute));
CACHE_CLASS_AND_CHECK(RemoteAttribute, GODOT_API_CLASS(RemoteAttribute));
CACHE_CLASS_AND_CHECK(MasterAttribute, GODOT_API_CLASS(MasterAttribute));
CACHE_CLASS_AND_CHECK(PuppetAttribute, GODOT_API_CLASS(PuppetAttribute));
CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute));
CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName"));
CACHE_CLASS_AND_CHECK(ScriptPathAttribute, GODOT_API_CLASS(ScriptPathAttribute));
CACHE_FIELD_AND_CHECK(ScriptPathAttribute, path, CACHED_CLASS(ScriptPathAttribute)->get_field("path"));
CACHE_CLASS_AND_CHECK(AssemblyHasScriptsAttribute, GODOT_API_CLASS(AssemblyHasScriptsAttribute));
CACHE_FIELD_AND_CHECK(AssemblyHasScriptsAttribute, requiresLookup, CACHED_CLASS(AssemblyHasScriptsAttribute)->get_field("requiresLookup"));
CACHE_FIELD_AND_CHECK(AssemblyHasScriptsAttribute, scriptTypes, CACHED_CLASS(AssemblyHasScriptsAttribute)->get_field("scriptTypes"));
CACHE_FIELD_AND_CHECK(GodotObject, ptr, CACHED_CLASS(GodotObject)->get_field(BINDINGS_PTR_FIELD));
CACHE_METHOD_THUNK_AND_CHECK(GodotObject, Dispose, CACHED_CLASS(GodotObject)->get_method("Dispose", 0));
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1));
CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0));
CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TrySerializeDelegateWithGCHandle, GODOT_API_CLASS(DelegateUtils)->get_method("TrySerializeDelegateWithGCHandle", 2));
CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TryDeserializeDelegateWithGCHandle, GODOT_API_CLASS(DelegateUtils)->get_method("TryDeserializeDelegateWithGCHandle", 2));
CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TrySerializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TrySerializeDelegate", 2));
CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TryDeserializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TryDeserializeDelegate", 2));
CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, InvokeWithVariantArgs, GODOT_API_CLASS(DelegateUtils)->get_method("InvokeWithVariantArgs", 4));
CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, DelegateEquals, GODOT_API_CLASS(DelegateUtils)->get_method("DelegateEquals", 2));
CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, FreeGCHandle, GODOT_API_CLASS(DelegateUtils)->get_method("FreeGCHandle", 1));
GDMonoClass *gd_mono_marshal_class = GDMono::get_singleton()->get_core_api_assembly()->get_class(
"Godot.NativeInterop", "Marshaling");
ERR_FAIL_COND_MSG(gd_mono_marshal_class == nullptr,
"Mono Cache: Class `Godot.NativeInterop.Marshaling` not found.");
CACHE_METHOD_THUNK_AND_CHECK(Marshaling, managed_to_variant_type,
gd_mono_marshal_class->get_method("managed_to_variant_type", 2));
CACHE_METHOD_THUNK_AND_CHECK(Marshaling, try_get_array_element_type,
gd_mono_marshal_class->get_method("try_get_array_element_type", 2));
CACHE_METHOD_THUNK_AND_CHECK(Marshaling, variant_to_mono_object_of_type,
gd_mono_marshal_class->get_method("variant_to_mono_object_of_type", 2));
CACHE_METHOD_THUNK_AND_CHECK(Marshaling, variant_to_mono_object,
gd_mono_marshal_class->get_method("variant_to_mono_object", 1));
CACHE_METHOD_THUNK_AND_CHECK(Marshaling, mono_object_to_variant_out,
gd_mono_marshal_class->get_method("mono_object_to_variant_out", 3));
CACHE_METHOD_THUNK_AND_CHECK(Marshaling, SetFieldValue,
gd_mono_marshal_class->get_method("SetFieldValue", 3));
#ifdef DEBUG_ENABLED
CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4));
#endif
// TODO Move to CSharpLanguage::init() and do handle disposal
MonoObject *task_scheduler = mono_object_new(mono_domain_get(), GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr());
GDMonoUtils::runtime_object_init(task_scheduler, GODOT_API_CLASS(GodotTaskScheduler));
cached_data.task_scheduler_handle = MonoGCHandleRef::create_strong(task_scheduler);
cached_data.godot_api_cache_updated = true;
godot_api_cache_updated = true;
}
} // namespace GDMonoCache

Some files were not shown because too many files have changed in this diff Show More