Compare commits

...

9 Commits

Author SHA1 Message Date
Rémi Verschelde ca0ce5b46e
Merge pull request #52610 from neikeq/dotnet6_wip2 2021-10-03 10:14:09 +02:00
Ignacio Roldán Etcheverry 3388d68dff CI: Setup .NET Sdk to fix CI and build C# code as well 2021-09-22 08:27:12 +02:00
Ignacio Roldán Etcheverry 50b603c7dc C#: Begin move to .NET Core
We're targeting .NET 5 for now to make development easier while
.NET 6 is not yet released.

TEMPORARY REGRESSIONS
---------------------

Assembly unloading is not implemented yet. As such, many Godot
resources are leaked at exit. This will be re-implemented later
together with assembly hot-reloading.
2021-09-22 08:27:12 +02:00
Ignacio Roldán Etcheverry f744d99179 C#: Restructure code prior move to .NET Core
The main focus here was to remove the majority of code that relied on
Mono's embedding APIs, specially the reflection APIs. The embedding
APIs we still use are the bare minimum we need for things to work.
A lot of code was moved to C#. We no longer deal with any managed
objects (`MonoObject*`, and such) in native code, and all marshaling
is done in C#.

The reason for restructuring the code and move away from embedding APIs
is that once we move to .NET Core, we will be limited by the much more
minimal .NET hosting.

PERFORMANCE REGRESSIONS
-----------------------

Some parts of the code were written with little to no concern about
performance. This includes code that calls into script methods and
accesses script fields, properties and events.
The reason for this is that all of that will be moved to source
generators, so any work prior to that would be a waste of time.

DISABLED FEATURES
-----------------

Some code was removed as it no longer makes sense (or won't make sense
in the future).
Other parts were commented out with `#if 0`s and TODO warnings because
it doesn't make much sense to work on them yet as those parts will
change heavily when we switch to .NET Core but also when we start
introducing source generators.
As such, the following features were disabled temporarily:
- Assembly-reloading (will be done with ALCs in .NET Core).
- Properties/fields exports and script method listing (will be
  handled by source generators in the future).
- Exception logging in the editor and stack info for errors.
- Exporting games.
- Building of C# projects. We no longer copy the Godot API assemblies
  to the project directory, so MSBuild won't be able to find them. The
  idea is to turn them into NuGet packages in the future, which could
  also be obtained from local NuGet sources during development.
2021-09-22 06:38:00 +02:00
Ignacio Roldán Etcheverry 3f1a620102 C#: Re-write GD and some other icalls as P/Invoke 2021-09-22 06:38:00 +02:00
Ignacio Roldán Etcheverry bbde1b1f09 C#: Re-write Array, Dictionary, NodePath, String icalls as P/Invoke 2021-09-12 19:49:39 +02:00
Ignacio Roldán Etcheverry 66a89c7925 C#: Remove DynamicGodotObject/Object.DynamicObject
We are moving in the direction of no dynamic code generation,
so this is no longer desired.

The feature can still be easily implemented by any project that
still want it.
2021-09-12 19:49:23 +02:00
Rémi Verschelde f580b1efdc
Merge pull request #48409 from neikeq/oh-im-die-ty-4ever
C#: Move marshaling logic and generated glue to C#
2021-08-20 11:32:27 +02:00
Ignacio Roldán Etcheverry 483071716e C#: Move marshaling logic and generated glue to C#
We will be progressively moving most code to C#.
The plan is to only use Mono's embedding APIs to set things at launch.
This will make it much easier to later support CoreCLR too which
doesn't have rich embedding APIs.

Additionally the code in C# is more maintainable and makes it easier
to implement new features, e.g.: runtime codegen which we could use to
avoid using reflection for marshaling everytime a field, property or
method is accessed.

SOME NOTES ON INTEROP

We make the same assumptions as GDNative about the size of the Godot
structures we use. We take it a bit further by also assuming the layout
of fields in some cases, which is riskier but let's us squeeze out some
performance by avoiding unnecessary managed to native calls.

Code that deals with native structs is less safe than before as there's
no RAII and copy constructors in C#. It's like using the GDNative C API
directly. One has to take special care to free values they own.
Perhaps we could use roslyn analyzers to check this, but I don't know
any that uses attributes to determine what's owned or borrowed.

As to why we maily use pointers for native structs instead of ref/out:
- AFAIK (and confirmed with a benchmark) ref/out are pinned
  during P/Invoke calls and that has a cost.
- Native struct fields can't be ref/out in the first place.
- A `using` local can't be passed as ref/out, only `in`. Calling a
  method or property on an `in` value makes a silent copy, so we want
  to avoid `in`.

REGARDING THE BUILD SYSTEM

There's no longer a `mono_glue=yes/no` SCons options. We no longer
need to build with `mono_glue=no`, generate the glue and then build
again with `mono_glue=yes`. We build only once and generate the glue
(which is in C# now).
However, SCons no longer builds the C# projects for us. Instead one
must run `build_assemblies.py`, e.g.:
```sh
%godot_src_root%/modules/mono/build_scripts/build_assemblies.py \
        --godot-output-dir=%godot_src_root%/bin \
        --godot-target=release_debug`
```
We could turn this into a custom build target, but I don't know how
to do that with SCons (it's possible with Meson).

OTHER NOTES

Most of the moved code doesn't follow the C# naming convention and
still has the word Mono in the names despite no longer dealing with
Mono's embedding APIs. This is just temporary while transitioning,
to make it easier to understand what was moved where.
2021-08-20 10:24:56 +02:00
147 changed files with 9430 additions and 15835 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

@ -7,21 +7,6 @@ Import("env_modules")
env_mono = env_modules.Clone()
if env_mono["tools"]:
# NOTE: It is safe to generate this file here, since this is still executed serially
import build_scripts.gen_cs_glue_version as gen_cs_glue_version
gen_cs_glue_version.generate_header("glue/GodotSharp", "glue/cs_glue_version.gen.h")
# Glue sources
if env_mono["mono_glue"]:
env_mono.Append(CPPDEFINES=["MONO_GLUE_ENABLED"])
import os.path
if not os.path.isfile("glue/mono_glue.gen.cpp"):
raise RuntimeError("Mono glue sources not found. Did you forget to run '--generate-mono-glue'?")
if env_mono["tools"] or env_mono["target"] != "release":
env_mono.Append(CPPDEFINES=["GD_MONO_HOT_RELOAD"])
@ -29,22 +14,6 @@ if env_mono["tools"] or env_mono["target"] != "release":
mono_configure.configure(env, env_mono)
if env_mono["tools"] and env_mono["mono_glue"] and env_mono["build_cil"]:
# Build Godot API solution
import build_scripts.api_solution_build as api_solution_build
api_sln_cmd = api_solution_build.build(env_mono)
# Build GodotTools
import build_scripts.godot_tools_build as godot_tools_build
godot_tools_build.build(env_mono, api_sln_cmd)
# Build Godot.NET.Sdk
import build_scripts.godot_net_sdk_build as godot_net_sdk_build
godot_net_sdk_build.build(env_mono)
# Add sources
env_mono.add_source_files(env.modules_sources, "*.cpp")
@ -60,3 +29,5 @@ if env["platform"] in ["osx", "iphone"]:
if env["tools"]:
env_mono.add_source_files(env.modules_sources, "editor/*.cpp")
env_mono.Prepend(CPPPATH=["#modules/gdnative/include/"])

View File

@ -1,80 +0,0 @@
# Build the Godot API solution
import os
from SCons.Script import Dir
def build_api_solution(source, target, env):
# source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
module_dir = env["module_dir"]
solution_path = os.path.join(module_dir, "glue/GodotSharp/GodotSharp.sln")
build_config = env["solution_build_config"]
extra_msbuild_args = ["/p:NoWarn=1591"] # Ignore missing documentation warnings
from .solution_builder import build_solution
build_solution(env, solution_path, build_config, extra_msbuild_args=extra_msbuild_args)
# Copy targets
core_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, "GodotSharp", "bin", build_config))
editor_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, "GodotSharpEditor", "bin", build_config))
dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir))
if not os.path.isdir(dst_dir):
assert not os.path.isfile(dst_dir)
os.makedirs(dst_dir)
def copy_target(target_path):
from shutil import copy
filename = os.path.basename(target_path)
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)
copy(src_path, target_path)
for scons_target in target:
copy_target(str(scons_target))
def build(env_mono):
assert env_mono["tools"]
target_filenames = [
"GodotSharp.dll",
"GodotSharp.pdb",
"GodotSharp.xml",
"GodotSharpEditor.dll",
"GodotSharpEditor.pdb",
"GodotSharpEditor.xml",
]
depend_cmd = []
for build_config in ["Debug", "Release"]:
output_dir = Dir("#bin").abspath
editor_api_dir = os.path.join(output_dir, "GodotSharp", "Api", build_config)
targets = [os.path.join(editor_api_dir, filename) for filename in target_filenames]
cmd = env_mono.CommandNoCache(
targets, depend_cmd, build_api_solution, module_dir=os.getcwd(), solution_build_config=build_config
)
env_mono.AlwaysBuild(cmd)
# Make the Release build of the API solution depend on the Debug build.
# We do this in order to prevent SCons from building them in parallel,
# which can freak out MSBuild. In many cases, one of the builds would
# hang indefinitely requiring a key to be pressed for it to continue.
depend_cmd = cmd
return depend_cmd

View File

@ -0,0 +1,307 @@
#!/usr/bin/python3
import os
import os.path
import shlex
import subprocess
from dataclasses import dataclass
def find_dotnet_cli():
if os.name == "nt":
for hint_dir in os.environ["PATH"].split(os.pathsep):
hint_dir = hint_dir.strip('"')
hint_path = os.path.join(hint_dir, "dotnet")
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
return hint_path
if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK):
return hint_path + ".exe"
else:
for hint_dir in os.environ["PATH"].split(os.pathsep):
hint_dir = hint_dir.strip('"')
hint_path = os.path.join(hint_dir, "dotnet")
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
return hint_path
def find_msbuild_standalone_windows():
msbuild_tools_path = find_msbuild_tools_path_reg()
if msbuild_tools_path:
return os.path.join(msbuild_tools_path, "MSBuild.exe")
return None
def find_msbuild_mono_windows(mono_prefix):
assert mono_prefix is not None
mono_bin_dir = os.path.join(mono_prefix, "bin")
msbuild_mono = os.path.join(mono_bin_dir, "msbuild.bat")
if os.path.isfile(msbuild_mono):
return msbuild_mono
return None
def find_msbuild_mono_unix():
import sys
hint_dirs = []
if sys.platform == "darwin":
hint_dirs[:0] = [
"/Library/Frameworks/Mono.framework/Versions/Current/bin",
"/usr/local/var/homebrew/linked/mono/bin",
]
for hint_dir in hint_dirs:
hint_path = os.path.join(hint_dir, "msbuild")
if os.path.isfile(hint_path):
return hint_path
elif os.path.isfile(hint_path + ".exe"):
return hint_path + ".exe"
for hint_dir in os.environ["PATH"].split(os.pathsep):
hint_dir = hint_dir.strip('"')
hint_path = os.path.join(hint_dir, "msbuild")
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
return hint_path
if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK):
return hint_path + ".exe"
return None
def find_msbuild_tools_path_reg():
import subprocess
program_files = os.getenv("PROGRAMFILES(X86)")
if not program_files:
program_files = os.getenv("PROGRAMFILES")
vswhere = os.path.join(program_files, "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: " + str(e))
except OSError:
pass # Fine, vswhere not found
except (subprocess.CalledProcessError, OSError):
pass
@dataclass
class ToolsLocation:
dotnet_cli: str = ""
msbuild_standalone: str = ""
msbuild_mono: str = ""
mono_bin_dir: str = ""
def find_any_msbuild_tool(mono_prefix):
# Preference order: dotnet CLI > Standalone MSBuild > Mono's MSBuild
# Find dotnet CLI
dotnet_cli = find_dotnet_cli()
if dotnet_cli:
return ToolsLocation(dotnet_cli=dotnet_cli)
# Find standalone MSBuild
if os.name == "nt":
msbuild_standalone = find_msbuild_standalone_windows()
if msbuild_standalone:
return ToolsLocation(msbuild_standalone=msbuild_standalone)
if mono_prefix:
# Find Mono's MSBuild
if os.name == "nt":
msbuild_mono = find_msbuild_mono_windows(mono_prefix)
if msbuild_mono:
return ToolsLocation(msbuild_mono=msbuild_mono)
else:
msbuild_mono = find_msbuild_mono_unix()
if msbuild_mono:
return ToolsLocation(msbuild_mono=msbuild_mono)
return None
def run_msbuild(tools: ToolsLocation, sln: str, msbuild_args: [str] = None):
if msbuild_args is None:
msbuild_args = []
using_msbuild_mono = False
# Preference order: dotnet CLI > Standalone MSBuild > Mono's MSBuild
if tools.dotnet_cli:
args = [tools.dotnet_cli, "msbuild"]
elif tools.msbuild_standalone:
args = [tools.msbuild_standalone]
elif tools.msbuild_mono:
args = [tools.msbuild_mono]
using_msbuild_mono = True
else:
raise RuntimeError("Path to MSBuild or dotnet CLI not provided.")
args += [sln]
if len(msbuild_args) > 0:
args += msbuild_args
print("Running MSBuild: ", " ".join(shlex.quote(arg) for arg in args), flush=True)
msbuild_env = os.environ.copy()
# Needed when running from Developer Command Prompt for VS
if "PLATFORM" in msbuild_env:
del msbuild_env["PLATFORM"]
if using_msbuild_mono:
# The (Csc/Vbc/Fsc)ToolExe environment variables are required when
# building with Mono's MSBuild. They must point to the batch files
# in Mono's bin directory to make sure they are executed with Mono.
msbuild_env.update(
{
"CscToolExe": os.path.join(tools.mono_bin_dir, "csc.bat"),
"VbcToolExe": os.path.join(tools.mono_bin_dir, "vbc.bat"),
"FscToolExe": os.path.join(tools.mono_bin_dir, "fsharpc.bat"),
}
)
return subprocess.call(args, env=msbuild_env)
def build_godot_api(msbuild_tool, module_dir, output_dir):
target_filenames = [
"GodotSharp.dll",
"GodotSharp.pdb",
"GodotSharp.xml",
"GodotSharpEditor.dll",
"GodotSharpEditor.pdb",
"GodotSharpEditor.xml",
"GodotPlugins.dll",
"GodotPlugins.pdb",
"GodotPlugins.runtimeconfig.json",
]
for build_config in ["Debug", "Release"]:
editor_api_dir = os.path.join(output_dir, "GodotSharp", "Api", build_config)
targets = [os.path.join(editor_api_dir, filename) for filename in target_filenames]
sln = os.path.join(module_dir, "glue/GodotSharp/GodotSharp.sln")
exit_code = run_msbuild(
msbuild_tool,
sln=sln,
msbuild_args=["/restore", "/t:Build", "/p:Configuration=" + build_config, "/p:NoWarn=1591"],
)
if exit_code != 0:
return exit_code
# Copy targets
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)
os.makedirs(editor_api_dir)
def copy_target(target_path):
from shutil import copy
filename = os.path.basename(target_path)
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)
for scons_target in targets:
copy_target(scons_target)
return 0
def build_all(msbuild_tool, module_dir, output_dir, godot_target, godot_platform):
# Godot API
exit_code = build_godot_api(msbuild_tool, module_dir, output_dir)
if exit_code != 0:
return exit_code
# GodotTools
sln = os.path.join(module_dir, "editor/GodotTools/GodotTools.sln")
args = ["/restore", "/t:Build", "/p:Configuration=" + ("Debug" if godot_target == "debug" else "Release")] + (
["/p:GodotPlatform=" + godot_platform] if godot_platform else []
)
exit_code = run_msbuild(msbuild_tool, sln=sln, msbuild_args=args)
if exit_code != 0:
return exit_code
# Godot.NET.Sdk
sln = os.path.join(module_dir, "editor/Godot.NET.Sdk/Godot.NET.Sdk.sln")
exit_code = run_msbuild(msbuild_tool, sln=sln, msbuild_args=["/restore", "/t:Build", "/p:Configuration=Release"])
if exit_code != 0:
return exit_code
return 0
def main():
import argparse
import sys
parser = argparse.ArgumentParser(description="Builds all Godot .NET solutions")
parser.add_argument("--godot-output-dir", type=str, required=True)
parser.add_argument("--godot-target", choices=["debug", "release_debug", "release"], type=str, required=True)
parser.add_argument("--godot-platform", type=str, default="")
parser.add_argument("--mono-prefix", type=str, default="")
args = parser.parse_args()
this_script_dir = os.path.dirname(os.path.realpath(__file__))
module_dir = os.path.abspath(os.path.join(this_script_dir, os.pardir))
output_dir = os.path.abspath(args.godot_output_dir)
msbuild_tool = find_any_msbuild_tool(args.mono_prefix)
if msbuild_tool is None:
print("Unable to find MSBuild")
sys.exit(1)
exit_code = build_all(msbuild_tool, module_dir, output_dir, args.godot_target, args.godot_platform)
sys.exit(exit_code)
if __name__ == "__main__":
main()

View File

@ -1,20 +0,0 @@
def generate_header(solution_dir, version_header_dst):
import os
latest_mtime = 0
for root, dirs, files in os.walk(solution_dir, topdown=True):
dirs[:] = [d for d in dirs if d not in ["Generated"]] # Ignored generated files
files = [f for f in files if f.endswith(".cs")]
for file in files:
filepath = os.path.join(root, file)
mtime = os.path.getmtime(filepath)
latest_mtime = mtime if mtime > latest_mtime else latest_mtime
glue_version = int(latest_mtime) # The latest modified time will do for now
with open(version_header_dst, "w") as version_header:
version_header.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
version_header.write("#ifndef CS_GLUE_VERSION_H\n")
version_header.write("#define CS_GLUE_VERSION_H\n\n")
version_header.write("#define CS_GLUE_VERSION UINT32_C(" + str(glue_version) + ")\n")
version_header.write("\n#endif // CS_GLUE_VERSION_H\n")

View File

@ -1,55 +0,0 @@
# Build Godot.NET.Sdk solution
import os
from SCons.Script import Dir
def build_godot_net_sdk(source, target, env):
# source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
module_dir = env["module_dir"]
solution_path = os.path.join(module_dir, "editor/Godot.NET.Sdk/Godot.NET.Sdk.sln")
build_config = "Release"
from .solution_builder import build_solution
extra_msbuild_args = ["/p:GodotPlatform=" + env["platform"]]
build_solution(env, solution_path, build_config, extra_msbuild_args)
# No need to copy targets. The Godot.NET.Sdk csproj takes care of copying them.
def get_nupkgs_versions(props_file):
import xml.etree.ElementTree as ET
tree = ET.parse(props_file)
root = tree.getroot()
return {
"Godot.NET.Sdk": root.find("./PropertyGroup/PackageVersion_Godot_NET_Sdk").text.strip(),
"Godot.SourceGenerators": root.find("./PropertyGroup/PackageVersion_Godot_SourceGenerators").text.strip(),
}
def build(env_mono):
assert env_mono["tools"]
output_dir = Dir("#bin").abspath
editor_tools_dir = os.path.join(output_dir, "GodotSharp", "Tools")
nupkgs_dir = os.path.join(editor_tools_dir, "nupkgs")
module_dir = os.getcwd()
nupkgs_versions = get_nupkgs_versions(os.path.join(module_dir, "SdkPackageVersions.props"))
target_filenames = [
"Godot.NET.Sdk.%s.nupkg" % nupkgs_versions["Godot.NET.Sdk"],
"Godot.SourceGenerators.%s.nupkg" % nupkgs_versions["Godot.SourceGenerators"],
]
targets = [os.path.join(nupkgs_dir, filename) for filename in target_filenames]
cmd = env_mono.CommandNoCache(targets, [], build_godot_net_sdk, module_dir=module_dir)
env_mono.AlwaysBuild(cmd)

View File

@ -1,38 +0,0 @@
# Build GodotTools solution
import os
from SCons.Script import Dir
def build_godot_tools(source, target, env):
# source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
module_dir = env["module_dir"]
solution_path = os.path.join(module_dir, "editor/GodotTools/GodotTools.sln")
build_config = "Debug" if env["target"] == "debug" else "Release"
from .solution_builder import build_solution
extra_msbuild_args = ["/p:GodotPlatform=" + env["platform"]]
build_solution(env, solution_path, build_config, extra_msbuild_args)
# No need to copy targets. The GodotTools csproj takes care of copying them.
def build(env_mono, api_sln_cmd):
assert env_mono["tools"]
output_dir = Dir("#bin").abspath
editor_tools_dir = os.path.join(output_dir, "GodotSharp", "Tools")
target_filenames = ["GodotTools.dll"]
if env_mono["target"] == "debug":
target_filenames += ["GodotTools.pdb"]
targets = [os.path.join(editor_tools_dir, filename) for filename in target_filenames]
cmd = env_mono.CommandNoCache(targets, api_sln_cmd, build_godot_tools, module_dir=os.getcwd())
env_mono.AlwaysBuild(cmd)

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,145 +0,0 @@
import os
verbose = False
def find_dotnet_cli():
import os.path
if os.name == "nt":
for hint_dir in os.environ["PATH"].split(os.pathsep):
hint_dir = hint_dir.strip('"')
hint_path = os.path.join(hint_dir, "dotnet")
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
return hint_path
if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK):
return hint_path + ".exe"
else:
for hint_dir in os.environ["PATH"].split(os.pathsep):
hint_dir = hint_dir.strip('"')
hint_path = os.path.join(hint_dir, "dotnet")
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
return hint_path
def find_msbuild_unix():
import os.path
import sys
hint_dirs = []
if sys.platform == "darwin":
hint_dirs[:0] = [
"/Library/Frameworks/Mono.framework/Versions/Current/bin",
"/usr/local/var/homebrew/linked/mono/bin",
]
for hint_dir in hint_dirs:
hint_path = os.path.join(hint_dir, "msbuild")
if os.path.isfile(hint_path):
return hint_path
elif os.path.isfile(hint_path + ".exe"):
return hint_path + ".exe"
for hint_dir in os.environ["PATH"].split(os.pathsep):
hint_dir = hint_dir.strip('"')
hint_path = os.path.join(hint_dir, "msbuild")
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
return hint_path
if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK):
return hint_path + ".exe"
return None
def find_msbuild_windows(env):
from .mono_reg_utils import find_mono_root_dir, find_msbuild_tools_path_reg
mono_root = env["mono_prefix"] or find_mono_root_dir(env["bits"])
if not mono_root:
raise RuntimeError("Cannot find mono root directory")
mono_bin_dir = os.path.join(mono_root, "bin")
msbuild_mono = os.path.join(mono_bin_dir, "msbuild.bat")
msbuild_tools_path = find_msbuild_tools_path_reg()
if msbuild_tools_path:
return (os.path.join(msbuild_tools_path, "MSBuild.exe"), {})
if os.path.isfile(msbuild_mono):
# The (Csc/Vbc/Fsc)ToolExe environment variables are required when
# building with Mono's MSBuild. They must point to the batch files
# in Mono's bin directory to make sure they are executed with Mono.
mono_msbuild_env = {
"CscToolExe": os.path.join(mono_bin_dir, "csc.bat"),
"VbcToolExe": os.path.join(mono_bin_dir, "vbc.bat"),
"FscToolExe": os.path.join(mono_bin_dir, "fsharpc.bat"),
}
return (msbuild_mono, mono_msbuild_env)
return None
def run_command(command, args, env_override=None, name=None):
def cmd_args_to_str(cmd_args):
return " ".join([arg if not " " in arg else '"%s"' % arg for arg in cmd_args])
args = [command] + args
if name is None:
name = os.path.basename(command)
if verbose:
print("Running '%s': %s" % (name, cmd_args_to_str(args)))
import subprocess
try:
if env_override is None:
subprocess.check_call(args)
else:
subprocess.check_call(args, env=env_override)
except subprocess.CalledProcessError as e:
raise RuntimeError("'%s' exited with error code: %s" % (name, e.returncode))
def build_solution(env, solution_path, build_config, extra_msbuild_args=[]):
global verbose
verbose = env["verbose"]
msbuild_env = os.environ.copy()
# Needed when running from Developer Command Prompt for VS
if "PLATFORM" in msbuild_env:
del msbuild_env["PLATFORM"]
msbuild_args = []
dotnet_cli = find_dotnet_cli()
if dotnet_cli:
msbuild_path = dotnet_cli
msbuild_args += ["msbuild"] # `dotnet msbuild` command
else:
# Find MSBuild
if os.name == "nt":
msbuild_info = find_msbuild_windows(env)
if msbuild_info is None:
raise RuntimeError("Cannot find MSBuild executable")
msbuild_path = msbuild_info[0]
msbuild_env.update(msbuild_info[1])
else:
msbuild_path = find_msbuild_unix()
if msbuild_path is None:
raise RuntimeError("Cannot find MSBuild executable")
print("MSBuild path: " + msbuild_path)
# Build solution
msbuild_args += [solution_path, "/restore", "/t:Build", "/p:Configuration=" + build_config]
msbuild_args += extra_msbuild_args
run_command(msbuild_path, msbuild_args, env_override=msbuild_env, name="msbuild")

View File

@ -1,8 +1,10 @@
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):
return True
return env["module_gdnative_enabled"]
def configure(env):
@ -13,52 +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("mono_glue", "Build with the Mono glue sources", True))
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,33 +325,22 @@ 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;
String _debug_error;
friend class GDMono;
void _on_scripts_domain_unloaded();
void _on_scripts_domain_about_to_unload();
#ifdef TOOLS_ENABLED
EditorPlugin *godotsharp_editor = nullptr;
@ -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)

File diff suppressed because it is too large Load Diff

View File

@ -218,6 +218,11 @@ class BindingsGenerator {
bool is_singleton = false;
bool is_ref_counted = false;
/**
* Determines whether the
*/
bool c_type_is_disposable_struct = false;
/**
* Used only by Object-derived types.
* Determines if this type is not abstract (incomplete).
@ -232,32 +237,35 @@ class BindingsGenerator {
*/
bool memory_own = false;
/**
* This must be set to true for any struct bigger than 32-bits. Those cannot be passed/returned by value
* with internal calls, so we must use pointers instead. Returns must be replace with out parameters.
* In this case, [c_out] and [cs_out] must have a different format, explained below.
* The Mono IL interpreter icall trampolines don't support passing structs bigger than 32-bits by value (at least not on WASM).
*/
bool ret_as_byref_arg = false;
// !! The comments of the following fields make reference to other fields via square brackets, e.g.: [field_name]
// !! When renaming those fields, make sure to rename their references in the comments
// --- C INTERFACE ---
static const char *DEFAULT_VARARG_C_IN;
/**
* One or more statements that manipulate the parameter before being passed as argument of a ptrcall.
* If the statement adds a local that must be passed as the argument instead of the parameter,
* the name of that local must be specified with [c_arg_in].
* For variadic methods, this field is required and, if empty, [DEFAULT_VARARG_C_IN] is used instead.
* Formatting elements:
* %0: [c_type] of the parameter
* %1: name of the parameter
* %2-4: reserved
* %5: indentation text
*/
String c_in;
/**
* One or more statements that manipulate the parameter before being passed as argument of a vararg call.
* If the statement adds a local that must be passed as the argument instead of the parameter,
* the name of that local must be specified with [c_arg_in].
* Formatting elements:
* %0: [c_type] of the parameter
* %1: name of the parameter
* %2-4: reserved
* %5: indentation text
*/
String c_in_vararg;
/**
* Determines the expression that will be passed as argument to ptrcall.
* By default the value equals the name of the parameter,
@ -281,7 +289,8 @@ class BindingsGenerator {
* %0: [c_type_out] of the return type
* %1: name of the variable to be returned
* %2: [name] of the return type
* %3: name of the parameter that must be assigned the return value
* %3-4: reserved
* %5: indentation text
*/
String c_out;
@ -320,6 +329,7 @@ class BindingsGenerator {
* %0 or %s: name of the parameter
*/
String cs_in;
bool cs_in_is_unsafe = false;
/**
* One or more statements that determine how a variable of this type is returned from a method.
@ -328,7 +338,9 @@ class BindingsGenerator {
* %0: internal method name
* %1: internal method call arguments without surrounding parenthesis
* %2: [cs_type] of the return type
* %3: [im_type_out] of the return type
* %3: [c_type_out] of the return type
* %4: reserved
* %5: indentation text
*/
String cs_out;
@ -338,16 +350,6 @@ class BindingsGenerator {
*/
String cs_type;
/**
* Type used for parameters of internal call methods.
*/
String im_type_in;
/**
* Type used for the return type of internal call methods.
*/
String im_type_out;
const DocData::ClassDoc *class_doc = nullptr;
List<ConstantInterface> constants;
@ -356,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) {
@ -402,8 +406,8 @@ class BindingsGenerator {
itype.c_type = itype.name;
itype.cs_type = itype.proxy_name;
itype.im_type_in = "ref " + itype.proxy_name;
itype.im_type_out = itype.proxy_name;
itype.c_type_in = itype.proxy_name + "*";
itype.c_type_out = itype.proxy_name;
itype.class_doc = &EditorHelp::get_doc_data()->class_list[itype.proxy_name];
}
@ -437,65 +441,26 @@ class BindingsGenerator {
return itype;
}
static void create_placeholder_type(TypeInterface &r_itype, const StringName &p_cname) {
r_itype.name = p_cname;
r_itype.cname = p_cname;
r_itype.proxy_name = r_itype.name;
r_itype.c_type = r_itype.name;
r_itype.c_type_in = "MonoObject*";
r_itype.c_type_out = "MonoObject*";
r_itype.cs_type = r_itype.proxy_name;
r_itype.im_type_in = r_itype.proxy_name;
r_itype.im_type_out = r_itype.proxy_name;
}
static void postsetup_enum_type(TypeInterface &r_enum_itype) {
// C interface for enums is the same as that of 'uint32_t'. Remember to apply
// any of the changes done here to the 'uint32_t' type interface as well.
r_enum_itype.c_arg_in = "&%s_in";
{
// The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'.
r_enum_itype.c_in = "\t%0 %1_in = (%0)%1;\n";
r_enum_itype.c_out = "\treturn (%0)%1;\n";
r_enum_itype.c_type = "int64_t";
}
r_enum_itype.c_type_in = "int32_t";
r_enum_itype.c_type_out = r_enum_itype.c_type_in;
r_enum_itype.cs_type = r_enum_itype.proxy_name;
r_enum_itype.cs_in = "(int)%s";
r_enum_itype.cs_out = "return (%2)%0(%1);";
r_enum_itype.im_type_in = "int";
r_enum_itype.im_type_out = "int";
r_enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[r_enum_itype.proxy_name];
}
static void postsetup_enum_type(TypeInterface &r_enum_itype);
TypeInterface() {}
};
struct InternalCall {
String name;
String im_type_out; // Return type for the C# method declaration. Also used as companion of [unique_siq]
String im_sig; // Signature for the C# method declaration
String unique_sig; // Unique signature to avoid duplicates in containers
bool editor_only = false;
bool is_vararg;
TypeReference return_type;
List<TypeReference> argument_types;
_FORCE_INLINE_ int get_arguments_count() const { return argument_types.size(); }
InternalCall() {}
InternalCall(const String &p_name, const String &p_im_type_out, const String &p_im_sig = String(), const String &p_unique_sig = String()) {
InternalCall(ClassDB::APIType api_type, const String &p_name, const String &p_unique_sig = String()) {
name = p_name;
im_type_out = p_im_type_out;
im_sig = p_im_sig;
unique_sig = p_unique_sig;
editor_only = false;
}
InternalCall(ClassDB::APIType api_type, const String &p_name, const String &p_im_type_out, const String &p_im_sig = String(), const String &p_unique_sig = String()) {
name = p_name;
im_type_out = p_im_type_out;
im_sig = p_im_sig;
unique_sig = p_unique_sig;
editor_only = api_type == ClassDB::API_EDITOR;
}
@ -510,7 +475,6 @@ class BindingsGenerator {
OrderedHashMap<StringName, TypeInterface> obj_types;
Map<StringName, TypeInterface> placeholder_types;
Map<StringName, TypeInterface> builtin_types;
Map<StringName, TypeInterface> enum_types;
@ -518,13 +482,9 @@ class BindingsGenerator {
List<ConstantInterface> global_constants;
List<InternalCall> method_icalls;
/// Stores the unique internal calls from [method_icalls] that are assigned to each method.
Map<const MethodInterface *, const InternalCall *> method_icalls_map;
List<const InternalCall *> generated_icall_funcs;
List<InternalCall> core_custom_icalls;
List<InternalCall> editor_custom_icalls;
Map<StringName, List<StringName>> blacklisted_methods;
void _initialize_blacklisted_methods();
@ -536,6 +496,8 @@ class BindingsGenerator {
StringName type_Object = StaticCString::create("Object");
StringName type_RefCounted = StaticCString::create("RefCounted");
StringName type_RID = StaticCString::create("RID");
StringName type_Callable = StaticCString::create("Callable");
StringName type_Signal = StaticCString::create("Signal");
StringName type_String = StaticCString::create("String");
StringName type_StringName = StaticCString::create("StringName");
StringName type_NodePath = StaticCString::create("NodePath");
@ -601,17 +563,6 @@ class BindingsGenerator {
NameCache name_cache;
const List<InternalCall>::Element *find_icall_by_name(const String &p_name, const List<InternalCall> &p_list) {
const List<InternalCall>::Element *it = p_list.front();
while (it) {
if (it->get().name == p_name) {
return it;
}
it = it->next();
}
return nullptr;
}
const ConstantInterface *find_constant_by_name(const String &p_name, const List<ConstantInterface> &p_constants) const {
for (const ConstantInterface &E : p_constants) {
if (E.name == p_name) {
@ -622,10 +573,9 @@ class BindingsGenerator {
return nullptr;
}
inline String get_unique_sig(const TypeInterface &p_type) {
if (p_type.is_ref_counted) {
return "Ref";
} else if (p_type.is_object_type) {
inline String get_arg_unique_sig(const TypeInterface &p_type) {
// For parameters, we treat reference and non-reference derived types the same.
if (p_type.is_object_type) {
return "Obj";
} else if (p_type.is_enum) {
return "int";
@ -634,15 +584,27 @@ class BindingsGenerator {
return p_type.name;
}
inline String get_ret_unique_sig(const TypeInterface *p_type) {
// Reference derived return types are treated differently.
if (p_type->is_ref_counted) {
return "Ref";
} else if (p_type->is_object_type) {
return "Obj";
} else if (p_type->is_enum) {
return "int";
}
return p_type->name;
}
String bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype);
int _determine_enum_prefix(const EnumInterface &p_ienum);
void _apply_prefix_to_enum_constants(EnumInterface &p_ienum, int p_prefix_length);
void _generate_method_icalls(const TypeInterface &p_itype);
Error _populate_method_icalls_table(const TypeInterface &p_itype);
const TypeInterface *_get_type_or_null(const TypeReference &p_typeref);
const TypeInterface *_get_type_or_placeholder(const TypeReference &p_typeref);
StringName _get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta);
StringName _get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta);
@ -661,11 +623,11 @@ class BindingsGenerator {
Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output);
Error _generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output);
Error _generate_cs_native_calls(const InternalCall &p_icall, StringBuilder &r_output);
void _generate_array_extensions(StringBuilder &p_output);
void _generate_global_constants(StringBuilder &p_output);
Error _generate_glue_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, StringBuilder &p_output);
Error _save_file(const String &p_path, const StringBuilder &p_content);
void _log(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
@ -676,15 +638,12 @@ public:
Error generate_cs_core_project(const String &p_proj_dir);
Error generate_cs_editor_project(const String &p_proj_dir);
Error generate_cs_api(const String &p_output_dir);
Error generate_glue(const String &p_output_dir);
_FORCE_INLINE_ bool is_log_print_enabled() { return log_print_enabled; }
_FORCE_INLINE_ void set_log_print_enabled(bool p_enabled) { log_print_enabled = p_enabled; }
_FORCE_INLINE_ bool is_initialized() { return initialized; }
static uint32_t get_version();
static void handle_cmdline_args(const List<String> &p_cmdline_args);
BindingsGenerator() {

View File

@ -43,179 +43,120 @@
#include "main/main.h"
#include "../csharp_script.h"
#include "../glue/cs_glue_version.gen.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
}
@ -223,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
@ -249,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
}
@ -282,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

@ -0,0 +1,7 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GC/@EntryIndexedValue">GC</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=gdnative/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=godotsharp/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=icall/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=quat/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=vcall/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -1,47 +1,27 @@
using System;
using System.Collections.Generic;
using System.Collections;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;
using Godot.NativeInterop;
namespace Godot.Collections
{
class ArraySafeHandle : SafeHandle
{
public ArraySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
{
this.handle = handle;
}
public override bool IsInvalid
{
get { return handle == IntPtr.Zero; }
}
protected override bool ReleaseHandle()
{
Array.godot_icall_Array_Dtor(handle);
return true;
}
}
/// <summary>
/// Wrapper around Godot's Array class, an array of Variant
/// typed elements allocated in the engine in C++. Useful when
/// interfacing with the engine. Otherwise prefer .NET collections
/// such as <see cref="System.Array"/> or <see cref="List{T}"/>.
/// </summary>
public class Array : IList, IDisposable
public sealed class Array : IList, IDisposable
{
ArraySafeHandle safeHandle;
bool disposed = false;
public godot_array NativeValue;
/// <summary>
/// Constructs a new empty <see cref="Array"/>.
/// </summary>
public Array()
{
safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor());
NativeValue = NativeFuncs.godotsharp_array_new();
}
/// <summary>
@ -58,6 +38,7 @@ namespace Godot.Collections
Add(element);
}
// TODO: This must be removed. Lots of silent mistakes as it takes pretty much anything.
/// <summary>
/// Constructs a new <see cref="Array"/> from the given objects.
/// </summary>
@ -66,28 +47,44 @@ namespace Godot.Collections
public Array(params object[] array) : this()
{
if (array == null)
{
throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'");
}
safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor_MonoArray(array));
NativeValue = NativeFuncs.godotsharp_array_new();
int length = array.Length;
Resize(length);
for (int i = 0; i < length; i++)
this[i] = array[i];
}
internal Array(ArraySafeHandle handle)
private Array(godot_array nativeValueToOwn)
{
safeHandle = handle;
NativeValue = nativeValueToOwn;
}
internal Array(IntPtr handle)
// Explicit name to make it very clear
internal static Array CreateTakingOwnershipOfDisposableValue(godot_array nativeValueToOwn)
=> new Array(nativeValueToOwn);
~Array()
{
safeHandle = new ArraySafeHandle(handle);
Dispose(false);
}
internal IntPtr GetPtr()
/// <summary>
/// Disposes of this <see cref="Array"/>.
/// </summary>
public void Dispose()
{
if (disposed)
throw new ObjectDisposedException(GetType().FullName);
Dispose(true);
GC.SuppressFinalize(this);
}
return safeHandle.DangerousGetHandle();
public void Dispose(bool disposing)
{
// Always dispose `NativeValue` even if disposing is true
NativeValue.Dispose();
}
/// <summary>
@ -97,7 +94,9 @@ namespace Godot.Collections
/// <returns>A new Godot Array.</returns>
public Array Duplicate(bool deep = false)
{
return new Array(godot_icall_Array_Duplicate(GetPtr(), deep));
godot_array newArray;
NativeFuncs.godotsharp_array_duplicate(ref NativeValue, deep.ToGodotBool(), out newArray);
return CreateTakingOwnershipOfDisposableValue(newArray);
}
/// <summary>
@ -105,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(GetPtr(), 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(GetPtr());
}
public void Shuffle() => NativeFuncs.godotsharp_array_shuffle(ref NativeValue);
/// <summary>
/// Concatenates these two <see cref="Array"/>s.
@ -126,26 +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)
{
return new Array(godot_icall_Array_Concatenate(left.GetPtr(), right.GetPtr()));
}
int leftCount = left.Count;
int rightCount = right.Count;
// IDisposable
Array newArray = left.Duplicate(deep: false);
newArray.Resize(leftCount + rightCount);
/// <summary>
/// Disposes of this <see cref="Array"/>.
/// </summary>
public void Dispose()
{
if (disposed)
return;
for (int i = 0; i < rightCount; i++)
newArray[i + leftCount] = right[i];
if (safeHandle != null)
{
safeHandle.Dispose();
safeHandle = null;
}
disposed = true;
return newArray;
}
// IList
@ -158,10 +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(GetPtr(), index);
set => godot_icall_Array_SetAt(GetPtr(), index, value);
get
{
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);
}
}
/// <summary>
@ -170,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(GetPtr(), 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(GetPtr(), 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(GetPtr());
public void Clear() => Resize(0);
/// <summary>
/// Searches this <see cref="Array"/> for an object
@ -190,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(GetPtr(), 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.
@ -200,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(GetPtr(), 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(GetPtr(), 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(GetPtr(), index);
public void RemoveAt(int index)
{
if (index < 0 || index > Count)
throw new IndexOutOfRangeException();
NativeFuncs.godotsharp_array_remove_at(ref NativeValue, index);
}
// ICollection
@ -222,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(GetPtr());
public int Count => NativeValue.Size;
object ICollection.SyncRoot => this;
@ -233,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(GetPtr(), 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
@ -266,75 +303,39 @@ 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(GetPtr());
using godot_string str = default;
NativeFuncs.godotsharp_array_to_string(ref NativeValue, &str);
return Marshaling.mono_string_from_godot(str);
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static IntPtr godot_icall_Array_Ctor();
/// <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 extern static IntPtr godot_icall_Array_Ctor_MonoArray(System.Array array);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Array_Dtor(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static object godot_icall_Array_At(IntPtr ptr, int index);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static object godot_icall_Array_At_Generic(IntPtr ptr, int index, int elemTypeEncoding, IntPtr elemTypeClass);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Array_SetAt(IntPtr ptr, int index, object value);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static int godot_icall_Array_Count(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static int godot_icall_Array_Add(IntPtr ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Array_Clear(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static IntPtr godot_icall_Array_Concatenate(IntPtr left, IntPtr right);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, System.Array array, int arrayIndex);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static IntPtr godot_icall_Array_Duplicate(IntPtr ptr, bool deep);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Array_Insert(IntPtr ptr, int index, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static bool godot_icall_Array_Remove(IntPtr ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Array_RemoveAt(IntPtr ptr, int index);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static Error godot_icall_Array_Resize(IntPtr ptr, int newSize);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static Error godot_icall_Array_Shuffle(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static string godot_icall_Array_ToString(IntPtr 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
{
Array UnderlyingArray { get; }
Type TypeOfElements { get; }
}
// TODO: Now we should be able to avoid boxing
/// <summary>
/// Typed wrapper around Godot's Array class, an array of Variant
/// typed elements allocated in the engine in C++. Useful when
@ -342,24 +343,27 @@ namespace Godot.Collections
/// such as arrays or <see cref="List{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the array.</typeparam>
public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T>
[SuppressMessage("ReSharper", "RedundantExtendsListEntry")]
public sealed class Array<T> : IList<T>, ICollection<T>, IEnumerable<T>, IGenericGodotArray
{
Array objectArray;
private readonly Array _underlyingArray;
internal static int elemTypeEncoding;
internal static IntPtr elemTypeClass;
// ReSharper disable StaticMemberInGenericType
// Warning is about unique static fields being created for each generic type combination:
// https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html
// In our case this is exactly what we want.
private static readonly Type TypeOfElements = typeof(T);
// ReSharper restore StaticMemberInGenericType
static Array()
{
Array.godot_icall_Array_Generic_GetElementTypeInfo(typeof(T), out elemTypeEncoding, out elemTypeClass);
}
Array IGenericGodotArray.UnderlyingArray => _underlyingArray;
Type IGenericGodotArray.TypeOfElements => TypeOfElements;
/// <summary>
/// Constructs a new empty <see cref="Array{T}"/>.
/// </summary>
public Array()
{
objectArray = new Array();
_underlyingArray = new Array();
}
/// <summary>
@ -372,7 +376,7 @@ namespace Godot.Collections
if (collection == null)
throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'");
objectArray = new Array(collection);
_underlyingArray = new Array(collection);
}
/// <summary>
@ -386,7 +390,8 @@ namespace Godot.Collections
{
throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'");
}
objectArray = new Array(array);
_underlyingArray = new Array(array);
}
/// <summary>
@ -395,23 +400,12 @@ namespace Godot.Collections
/// <param name="array">The untyped array to construct from.</param>
public Array(Array array)
{
objectArray = array;
_underlyingArray = array;
}
internal Array(IntPtr handle)
{
objectArray = new Array(handle);
}
internal Array(ArraySafeHandle handle)
{
objectArray = new Array(handle);
}
internal IntPtr GetPtr()
{
return objectArray.GetPtr();
}
// Explicit name to make it very clear
internal static Array<T> CreateTakingOwnershipOfDisposableValue(godot_array nativeValueToOwn)
=> new Array<T>(Array.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn));
/// <summary>
/// Converts this typed <see cref="Array{T}"/> to an untyped <see cref="Array"/>.
@ -419,7 +413,7 @@ namespace Godot.Collections
/// <param name="from">The typed array to convert.</param>
public static explicit operator Array(Array<T> from)
{
return from.objectArray;
return from._underlyingArray;
}
/// <summary>
@ -429,7 +423,7 @@ namespace Godot.Collections
/// <returns>A new Godot Array.</returns>
public Array<T> Duplicate(bool deep = false)
{
return new Array<T>(objectArray.Duplicate(deep));
return new Array<T>(_underlyingArray.Duplicate(deep));
}
/// <summary>
@ -439,7 +433,7 @@ namespace Godot.Collections
/// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns>
public Error Resize(int newSize)
{
return objectArray.Resize(newSize);
return _underlyingArray.Resize(newSize);
}
/// <summary>
@ -447,7 +441,7 @@ namespace Godot.Collections
/// </summary>
public void Shuffle()
{
objectArray.Shuffle();
_underlyingArray.Shuffle();
}
/// <summary>
@ -458,7 +452,7 @@ namespace Godot.Collections
/// <returns>A new Godot Array with the contents of both arrays.</returns>
public static Array<T> operator +(Array<T> left, Array<T> right)
{
return new Array<T>(left.objectArray + right.objectArray);
return new Array<T>(left._underlyingArray + right._underlyingArray);
}
// IList<T>
@ -469,8 +463,15 @@ namespace Godot.Collections
/// <value>The value at the given index.</value>
public T this[int index]
{
get { return (T)Array.godot_icall_Array_At_Generic(GetPtr(), index, elemTypeEncoding, elemTypeClass); }
set { objectArray[index] = value; }
get
{
_underlyingArray.GetVariantBorrowElementAt(index, out godot_variant borrowElem);
unsafe
{
return (T)Marshaling.variant_to_mono_object_of_type(&borrowElem, TypeOfElements);
}
}
set => _underlyingArray[index] = value;
}
/// <summary>
@ -481,7 +482,7 @@ namespace Godot.Collections
/// <returns>The index of the item, or -1 if not found.</returns>
public int IndexOf(T item)
{
return objectArray.IndexOf(item);
return _underlyingArray.IndexOf(item);
}
/// <summary>
@ -494,7 +495,7 @@ namespace Godot.Collections
/// <param name="item">The item to insert.</param>
public void Insert(int index, T item)
{
objectArray.Insert(index, item);
_underlyingArray.Insert(index, item);
}
/// <summary>
@ -503,7 +504,7 @@ namespace Godot.Collections
/// <param name="index">The index of the element to remove.</param>
public void RemoveAt(int index)
{
objectArray.RemoveAt(index);
_underlyingArray.RemoveAt(index);
}
// ICollection<T>
@ -513,10 +514,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
{
get { return objectArray.Count; }
}
public int Count => _underlyingArray.Count;
bool ICollection<T>.IsReadOnly => false;
@ -528,7 +526,7 @@ namespace Godot.Collections
/// <returns>The new size after adding the item.</returns>
public void Add(T item)
{
objectArray.Add(item);
_underlyingArray.Add(item);
}
/// <summary>
@ -536,7 +534,7 @@ namespace Godot.Collections
/// </summary>
public void Clear()
{
objectArray.Clear();
_underlyingArray.Clear();
}
/// <summary>
@ -546,7 +544,7 @@ namespace Godot.Collections
/// <returns>Whether or not this array contains the given item.</returns>
public bool Contains(T item)
{
return objectArray.Contains(item);
return _underlyingArray.Contains(item);
}
/// <summary>
@ -561,19 +559,18 @@ 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.");
// TODO This may be quite slow because every element access is an internal call.
// It could be moved entirely to an internal call if we find out how to do the cast there.
int count = objectArray.Count;
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++)
{
array[arrayIndex] = (T)this[i];
array[arrayIndex] = this[i];
arrayIndex++;
}
}
@ -586,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(GetPtr(), item);
int index = IndexOf(item);
if (index >= 0)
{
RemoveAt(index);
return true;
}
return false;
}
// IEnumerable<T>
@ -597,23 +601,20 @@ namespace Godot.Collections
/// <returns>An enumerator.</returns>
public IEnumerator<T> GetEnumerator()
{
int count = objectArray.Count;
int count = _underlyingArray.Count;
for (int i = 0; i < count; i++)
{
yield return (T)this[i];
yield return this[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>
/// Converts this <see cref="Array{T}"/> to a string.
/// </summary>
/// <returns>A string representation of this array.</returns>
public override string ToString() => objectArray.ToString();
public override string ToString() => _underlyingArray.ToString();
}
}

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

@ -727,7 +727,7 @@ namespace Godot
/// (in the YXZ convention: when *composing*, first Y, then X, and Z last),
/// given in the vector format as (X angle, Y angle, Z angle).
///
/// Consider using the <see cref="Basis(Quaternion)"/> constructor instead, which
/// Consider using the <see cref="Basis(Godot.Quaternion)"/> constructor instead, which
/// uses a <see cref="Godot.Quaternion"/> quaternion instead of Euler angles.
/// </summary>
/// <param name="eulerYXZ">The Euler angles to create the basis from.</param>

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

@ -4,11 +4,67 @@ using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Godot.NativeInterop;
namespace Godot
{
internal static class DelegateUtils
{
[UnmanagedCallersOnly]
internal static godot_bool DelegateEquals(IntPtr delegateGCHandleA, IntPtr delegateGCHandleB)
{
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();
}
}
[UnmanagedCallersOnly]
internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, godot_variant** args, uint argc,
godot_variant* outRet)
{
try
{
// 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)
{
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)
{
ExceptionUtils.DebugPrintUnhandledException(e);
*outRet = default;
}
}
// TODO: Check if we should be using BindingFlags.DeclaredOnly (would give better reflection performance).
private enum TargetKind : uint
{
Static,
@ -16,7 +72,10 @@ namespace Godot
CompilerGenerated
}
internal static bool TrySerializeDelegate(Delegate @delegate, Collections.Array serializedData)
internal static bool TrySerializeDelegateWithGCHandle(IntPtr delegateGCHandle, Collections.Array serializedData)
=> TrySerializeDelegate((Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target, serializedData);
private static bool TrySerializeDelegate(Delegate @delegate, Collections.Array serializedData)
{
if (@delegate is MulticastDelegate multicastDelegate)
{
@ -72,12 +131,14 @@ namespace Godot
return true;
}
}
// ReSharper disable once RedundantNameQualifier
case Godot.Object godotObject:
{
using (var stream = new MemoryStream())
using (var writer = new BinaryWriter(stream))
{
writer.Write((ulong)TargetKind.GodotObject);
// ReSharper disable once RedundantCast
writer.Write((ulong)godotObject.GetInstanceId());
SerializeType(writer, @delegate.GetType());
@ -93,7 +154,7 @@ namespace Godot
{
Type targetType = target.GetType();
if (targetType.GetCustomAttribute(typeof(CompilerGeneratedAttribute), true) != null)
if (targetType.IsDefined(typeof(CompilerGeneratedAttribute), true))
{
// Compiler generated. Probably a closure. Try to serialize it.
@ -213,6 +274,14 @@ namespace Godot
}
}
private static bool TryDeserializeDelegateWithGCHandle(Collections.Array serializedData,
out IntPtr delegateGCHandle)
{
bool res = TryDeserializeDelegate(serializedData, out Delegate @delegate);
delegateGCHandle = GCHandle.ToIntPtr(GCHandle.Alloc(@delegate));
return res;
}
private static bool TryDeserializeDelegate(Collections.Array serializedData, out Delegate @delegate)
{
if (serializedData.Count == 1)
@ -276,6 +345,7 @@ namespace Godot
case TargetKind.GodotObject:
{
ulong objectId = reader.ReadUInt64();
// ReSharper disable once RedundantNameQualifier
Godot.Object godotObject = GD.InstanceFromId(objectId);
if (godotObject == null)
return false;
@ -313,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,49 +1,28 @@
using System;
using System.Collections.Generic;
using System.Collections;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Godot.NativeInterop;
using System.Diagnostics.CodeAnalysis;
namespace Godot.Collections
{
class DictionarySafeHandle : SafeHandle
{
public DictionarySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
{
this.handle = handle;
}
public override bool IsInvalid
{
get { return handle == IntPtr.Zero; }
}
protected override bool ReleaseHandle()
{
Dictionary.godot_icall_Dictionary_Dtor(handle);
return true;
}
}
/// <summary>
/// Wrapper around Godot's Dictionary class, a dictionary of Variant
/// typed elements allocated in the engine in C++. Useful when
/// interfacing with the engine.
/// </summary>
public class Dictionary :
public sealed class Dictionary :
IDictionary,
IDisposable
{
DictionarySafeHandle safeHandle;
bool disposed = false;
public godot_dictionary NativeValue;
/// <summary>
/// Constructs a new empty <see cref="Dictionary"/>.
/// </summary>
public Dictionary()
{
safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor());
NativeValue = NativeFuncs.godotsharp_dictionary_new();
}
/// <summary>
@ -60,22 +39,18 @@ namespace Godot.Collections
Add(entry.Key, entry.Value);
}
internal Dictionary(DictionarySafeHandle handle)
private Dictionary(godot_dictionary nativeValueToOwn)
{
safeHandle = handle;
NativeValue = nativeValueToOwn;
}
internal Dictionary(IntPtr handle)
{
safeHandle = new DictionarySafeHandle(handle);
}
// Explicit name to make it very clear
internal static Dictionary CreateTakingOwnershipOfDisposableValue(godot_dictionary nativeValueToOwn)
=> new Dictionary(nativeValueToOwn);
internal IntPtr GetPtr()
~Dictionary()
{
if (disposed)
throw new ObjectDisposedException(GetType().FullName);
return safeHandle.DangerousGetHandle();
Dispose(false);
}
/// <summary>
@ -83,16 +58,14 @@ namespace Godot.Collections
/// </summary>
public void Dispose()
{
if (disposed)
return;
Dispose(true);
GC.SuppressFinalize(this);
}
if (safeHandle != null)
{
safeHandle.Dispose();
safeHandle = null;
}
disposed = true;
public void Dispose(bool disposing)
{
// Always dispose `NativeValue` even if disposing is true
NativeValue.Dispose();
}
/// <summary>
@ -102,7 +75,9 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary Duplicate(bool deep = false)
{
return new Dictionary(godot_icall_Dictionary_Duplicate(GetPtr(), deep));
godot_dictionary newDictionary;
NativeFuncs.godotsharp_dictionary_duplicate(ref NativeValue, deep.ToGodotBool(), out newDictionary);
return CreateTakingOwnershipOfDisposableValue(newDictionary);
}
// IDictionary
@ -114,8 +89,9 @@ namespace Godot.Collections
{
get
{
IntPtr handle = godot_icall_Dictionary_Keys(GetPtr());
return new Array(new ArraySafeHandle(handle));
godot_array keysArray;
NativeFuncs.godotsharp_dictionary_keys(ref NativeValue, out keysArray);
return Array.CreateTakingOwnershipOfDisposableValue(keysArray);
}
}
@ -126,16 +102,24 @@ namespace Godot.Collections
{
get
{
IntPtr handle = godot_icall_Dictionary_Values(GetPtr());
return new Array(new ArraySafeHandle(handle));
godot_array valuesArray;
NativeFuncs.godotsharp_dictionary_values(ref NativeValue, out valuesArray);
return Array.CreateTakingOwnershipOfDisposableValue(valuesArray);
}
}
private (Array keys, Array values, int count) GetKeyValuePairs()
{
int count = godot_icall_Dictionary_KeyValuePairs(GetPtr(), out IntPtr keysHandle, out IntPtr valuesHandle);
Array keys = new Array(new ArraySafeHandle(keysHandle));
Array values = new Array(new ArraySafeHandle(valuesHandle));
godot_array keysArray;
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);
}
@ -147,10 +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(GetPtr(), key);
set => godot_icall_Dictionary_SetValue(GetPtr(), key, value);
get
{
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);
}
}
/// <summary>
@ -159,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(GetPtr(), 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(GetPtr());
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(GetPtr(), 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"/>.
@ -183,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(GetPtr(), 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
@ -196,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(GetPtr());
public int Count => NativeFuncs.godotsharp_dictionary_count(ref NativeValue);
/// <summary>
/// Copies the elements of this <see cref="Dictionary"/> to the given
@ -210,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++)
{
@ -253,15 +274,23 @@ namespace Godot.Collections
{
UpdateEntry();
}
return entry;
}
}
private void UpdateEntry()
private unsafe void UpdateEntry()
{
dirty = false;
godot_icall_Dictionary_KeyValuePairAt(dictionary.GetPtr(), 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;
@ -286,75 +315,23 @@ 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(GetPtr());
using godot_string str = default;
NativeFuncs.godotsharp_dictionary_to_string(ref NativeValue, &str);
return Marshaling.mono_string_from_godot(str);
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static IntPtr godot_icall_Dictionary_Ctor();
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Dictionary_Dtor(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static object godot_icall_Dictionary_GetValue(IntPtr ptr, object key);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static object godot_icall_Dictionary_GetValue_Generic(IntPtr ptr, object key, int valTypeEncoding, IntPtr valTypeClass);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static IntPtr godot_icall_Dictionary_Keys(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static IntPtr godot_icall_Dictionary_Values(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static int godot_icall_Dictionary_Count(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static int godot_icall_Dictionary_KeyValuePairs(IntPtr ptr, out IntPtr keys, out IntPtr values);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Dictionary_KeyValuePairAt(IntPtr ptr, int index, out object key, out object value);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Dictionary_Clear(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static IntPtr godot_icall_Dictionary_Duplicate(IntPtr ptr, bool deep);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static bool godot_icall_Dictionary_TryGetValue_Generic(IntPtr ptr, object key, out object value, int valTypeEncoding, IntPtr valTypeClass);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Dictionary_Generic_GetValueTypeInfo(Type valueType, out int valTypeEncoding, out IntPtr valTypeClass);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static string godot_icall_Dictionary_ToString(IntPtr ptr);
}
internal interface IGenericGodotDictionary
{
Dictionary UnderlyingDictionary { get; }
Type TypeOfKeys { get; }
Type TypeOfValues { get; }
}
// TODO: Now we should be able to avoid boxing
/// <summary>
/// Typed wrapper around Godot's Dictionary class, a dictionary of Variant
/// typed elements allocated in the engine in C++. Useful when
@ -363,25 +340,30 @@ namespace Godot.Collections
/// </summary>
/// <typeparam name="TKey">The type of the dictionary's keys.</typeparam>
/// <typeparam name="TValue">The type of the dictionary's values.</typeparam>
public class Dictionary<TKey, TValue> :
IDictionary<TKey, TValue>
public sealed class Dictionary<TKey, TValue> :
IDictionary<TKey, TValue>, IGenericGodotDictionary
{
private readonly Dictionary objectDict;
private readonly Dictionary _underlyingDict;
internal static int valTypeEncoding;
internal static IntPtr valTypeClass;
// ReSharper disable StaticMemberInGenericType
// Warning is about unique static fields being created for each generic type combination:
// https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html
// In our case this is exactly what we want.
private static readonly Type TypeOfKeys = typeof(TKey);
static Dictionary()
{
Dictionary.godot_icall_Dictionary_Generic_GetValueTypeInfo(typeof(TValue), out valTypeEncoding, out valTypeClass);
}
private static readonly Type TypeOfValues = typeof(TValue);
// ReSharper restore StaticMemberInGenericType
Dictionary IGenericGodotDictionary.UnderlyingDictionary => _underlyingDict;
Type IGenericGodotDictionary.TypeOfKeys => TypeOfKeys;
Type IGenericGodotDictionary.TypeOfValues => TypeOfValues;
/// <summary>
/// Constructs a new empty <see cref="Dictionary{TKey, TValue}"/>.
/// </summary>
public Dictionary()
{
objectDict = new Dictionary();
_underlyingDict = new Dictionary();
}
/// <summary>
@ -391,19 +373,13 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary(IDictionary<TKey, TValue> dictionary)
{
objectDict = new Dictionary();
_underlyingDict = new Dictionary();
if (dictionary == null)
throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'");
// TODO: Can be optimized
IntPtr godotDictionaryPtr = GetPtr();
foreach (KeyValuePair<TKey, TValue> entry in dictionary)
{
Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value);
}
Add(entry.Key, entry.Value);
}
/// <summary>
@ -413,18 +389,13 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary(Dictionary dictionary)
{
objectDict = dictionary;
_underlyingDict = dictionary;
}
internal Dictionary(IntPtr handle)
{
objectDict = new Dictionary(handle);
}
internal Dictionary(DictionarySafeHandle handle)
{
objectDict = new Dictionary(handle);
}
// Explicit name to make it very clear
internal static Dictionary<TKey, TValue> CreateTakingOwnershipOfDisposableValue(
godot_dictionary nativeValueToOwn)
=> new Dictionary<TKey, TValue>(Dictionary.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn));
/// <summary>
/// Converts this typed <see cref="Dictionary{TKey, TValue}"/> to an untyped <see cref="Dictionary"/>.
@ -432,12 +403,7 @@ namespace Godot.Collections
/// <param name="from">The typed dictionary to convert.</param>
public static explicit operator Dictionary(Dictionary<TKey, TValue> from)
{
return from.objectDict;
}
internal IntPtr GetPtr()
{
return objectDict.GetPtr();
return from._underlyingDict;
}
/// <summary>
@ -447,7 +413,7 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary<TKey, TValue> Duplicate(bool deep = false)
{
return new Dictionary<TKey, TValue>(objectDict.Duplicate(deep));
return new Dictionary<TKey, TValue>(_underlyingDict.Duplicate(deep));
}
// IDictionary<TKey, TValue>
@ -458,8 +424,24 @@ namespace Godot.Collections
/// <value>The value at the given <paramref name="key"/>.</value>
public TValue this[TKey key]
{
get { return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); }
set { objectDict[key] = value; }
get
{
unsafe
{
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;
}
/// <summary>
@ -469,8 +451,9 @@ namespace Godot.Collections
{
get
{
IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(objectDict.GetPtr());
return new Array<TKey>(new ArraySafeHandle(handle));
godot_array keyArray;
NativeFuncs.godotsharp_dictionary_keys(ref _underlyingDict.NativeValue, out keyArray);
return Array<TKey>.CreateTakingOwnershipOfDisposableValue(keyArray);
}
}
@ -481,15 +464,23 @@ namespace Godot.Collections
{
get
{
IntPtr handle = Dictionary.godot_icall_Dictionary_Values(objectDict.GetPtr());
return new Array<TValue>(new ArraySafeHandle(handle));
godot_array 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(GetPtr(), 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,7 +491,7 @@ namespace Godot.Collections
/// <param name="value">The object to add.</param>
public void Add(TKey key, TValue value)
{
objectDict.Add(key, value);
_underlyingDict.Add(key, value);
}
/// <summary>
@ -510,16 +501,17 @@ namespace Godot.Collections
/// <returns>Whether or not this dictionary contains the given key.</returns>
public bool ContainsKey(TKey key)
{
return objectDict.Contains(key);
return _underlyingDict.Contains(key);
}
/// <summary>
/// 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(GetPtr(), key);
using godot_variant variantKey = Marshaling.mono_object_to_variant(key);
return NativeFuncs.godotsharp_dictionary_remove_key(ref _underlyingDict.NativeValue, &variantKey).ToBool();
}
/// <summary>
@ -528,10 +520,19 @@ 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_Generic(GetPtr(), key, out object retValue, valTypeEncoding, valTypeClass);
value = found ? (TValue)retValue : default;
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();
using (retValue)
{
value = found ?
(TValue)Marshaling.variant_to_mono_object_of_type(&retValue, TypeOfValues) :
default;
}
return found;
}
@ -542,16 +543,13 @@ 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
{
get { return objectDict.Count; }
}
public int Count => _underlyingDict.Count;
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false;
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
objectDict.Add(item.Key, item.Value);
_underlyingDict.Add(item.Key, item.Value);
}
/// <summary>
@ -559,12 +557,22 @@ namespace Godot.Collections
/// </summary>
public void Clear()
{
objectDict.Clear();
_underlyingDict.Clear();
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
unsafe bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
return objectDict.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>
@ -579,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++)
{
@ -593,10 +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(GetPtr(), 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>>
@ -613,15 +638,12 @@ namespace Godot.Collections
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>
/// Converts this <see cref="Dictionary{TKey, TValue}"/> to a string.
/// </summary>
/// <returns>A string representation of this dictionary.</returns>
public override string ToString() => objectDict.ToString();
public override string ToString() => _underlyingDict.ToString();
}
}

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,17 +1,64 @@
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using Godot.Collections;
using Godot.NativeInterop;
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
{
return new Array<T>(godot_icall_SceneTree_get_nodes_in_group_Generic(Object.GetPtr(this), StringName.GetPtr(group), typeof(T)));
}
var array = GetNodesInGroup(group);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static IntPtr godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, IntPtr group, Type elemType);
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,7 @@ 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.
@ -13,14 +13,20 @@ namespace Godot
{
public static partial class GD
{
public static object Bytes2Var(byte[] bytes, bool allowObjects = false)
public static unsafe object Bytes2Var(byte[] bytes, bool allowObjects = false)
{
return godot_icall_GD_bytes2var(bytes, allowObjects);
using var varBytes = Marshaling.mono_array_to_PackedByteArray(bytes);
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)
@ -28,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)
{
@ -38,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)
@ -63,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()
@ -83,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)
@ -165,109 +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);
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 bool TypeExists(StringName type)
public static unsafe byte[] Var2Bytes(object var, bool fullObjects = false)
{
return godot_icall_GD_type_exists(StringName.GetPtr(type));
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 byte[] Var2Bytes(object var, bool fullObjects = false)
public static unsafe string Var2Str(object var)
{
return godot_icall_GD_var2bytes(var, fullObjects);
}
public static 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 extern static object godot_icall_GD_bytes2var(byte[] bytes, bool allowObjects);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static object godot_icall_GD_convert(object what, Variant.Type type);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static int godot_icall_GD_hash(object var);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static Object godot_icall_GD_instance_from_id(ulong instanceId);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_GD_print(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_GD_printerr(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_GD_printraw(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_GD_prints(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_GD_printt(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static float godot_icall_GD_randf();
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static uint godot_icall_GD_randi();
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_GD_randomize();
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static double godot_icall_GD_randf_range(double from, double to);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static int godot_icall_GD_randi_range(int from, int to);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static uint godot_icall_GD_rand_seed(ulong seed, out ulong newSeed);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_GD_seed(ulong seed);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static string godot_icall_GD_str(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static object godot_icall_GD_str2var(string str);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static bool godot_icall_GD_type_exists(IntPtr type);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static byte[] godot_icall_GD_var2bytes(object what, bool fullObjects);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static string godot_icall_GD_var2str(object var);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_GD_pusherror(string type);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static 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

@ -1,62 +0,0 @@
using System;
using System.Collections.Generic;
namespace Godot
{
static class MarshalUtils
{
/// <summary>
/// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
/// is <see cref="Godot.Collections.Array{T}"/>; otherwise returns <see langword="false"/>.
/// </summary>
/// <exception cref="System.InvalidOperationException">
/// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false.
/// </exception>
static bool TypeIsGenericArray(Type type) =>
type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>);
/// <summary>
/// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
/// is <see cref="Godot.Collections.Dictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>.
/// </summary>
/// <exception cref="System.InvalidOperationException">
/// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false.
/// </exception>
static bool TypeIsGenericDictionary(Type type) =>
type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>);
static bool TypeIsSystemGenericList(Type type) =>
type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.List<>);
static bool TypeIsSystemGenericDictionary(Type type) =>
type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.Dictionary<,>);
static bool TypeIsGenericIEnumerable(Type type) => type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
static bool TypeIsGenericICollection(Type type) => type.GetGenericTypeDefinition() == typeof(ICollection<>);
static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>);
static void ArrayGetElementType(Type arrayType, out Type elementType)
{
elementType = arrayType.GetGenericArguments()[0];
}
static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType)
{
var genericArgs = dictionaryType.GetGenericArguments();
keyType = genericArgs[0];
valueType = genericArgs[1];
}
static Type MakeGenericArrayType(Type elemType)
{
return typeof(Godot.Collections.Array<>).MakeGenericType(elemType);
}
static Type MakeGenericDictionaryType(Type keyType, Type valueType)
{
return typeof(Godot.Collections.Dictionary<,>).MakeGenericType(keyType, valueType);
}
}
}

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

@ -0,0 +1,481 @@
#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
{
internal static class GodotBoolExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe godot_bool ToGodotBool(this bool @bool)
{
return *(godot_bool*)&@bool;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool ToBool(this godot_bool godotBool)
{
return *(bool*)&godotBool;
}
}
// 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
public struct godot_ref : IDisposable
{
internal IntPtr _reference;
public void Dispose()
{
if (_reference == IntPtr.Zero)
return;
NativeFuncs.godotsharp_ref_destroy(ref this);
_reference = IntPtr.Zero;
}
public bool IsNull => _reference == IntPtr.Zero;
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum godot_variant_call_error_error
{
GODOT_CALL_ERROR_CALL_OK = 0,
GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD,
GODOT_CALL_ERROR_CALL_ERROR_INVALID_ARGUMENT,
GODOT_CALL_ERROR_CALL_ERROR_TOO_MANY_ARGUMENTS,
GODOT_CALL_ERROR_CALL_ERROR_TOO_FEW_ARGUMENTS,
GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL,
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_variant_call_error
{
public godot_variant_call_error_error error;
public int argument;
public Godot.Variant.Type expected;
}
[StructLayout(LayoutKind.Explicit)]
// ReSharper disable once InconsistentNaming
public struct godot_variant : IDisposable
{
[FieldOffset(0)] public Godot.Variant.Type _type;
// There's padding here
[FieldOffset(8)] internal godot_variant_data _data;
[StructLayout(LayoutKind.Explicit)]
// ReSharper disable once InconsistentNaming
internal unsafe struct godot_variant_data
{
[FieldOffset(0)] public godot_bool _bool;
[FieldOffset(0)] public long _int;
[FieldOffset(0)] public double _float;
[FieldOffset(0)] public Transform2D* _transform2d;
[FieldOffset(0)] public AABB* _aabb;
[FieldOffset(0)] public Basis* _basis;
[FieldOffset(0)] public Transform3D* _transform3d;
[FieldOffset(0)] private godot_variant_data_mem _mem;
// The following fields are not in the C++ union, but this is how they're stored in _mem.
[FieldOffset(0)] public godot_string_name _m_string_name;
[FieldOffset(0)] public godot_string _m_string;
[FieldOffset(0)] public Vector3 _m_vector3;
[FieldOffset(0)] public Vector3i _m_vector3i;
[FieldOffset(0)] public Vector2 _m_vector2;
[FieldOffset(0)] public Vector2i _m_vector2i;
[FieldOffset(0)] public Rect2 _m_rect2;
[FieldOffset(0)] public Rect2i _m_rect2i;
[FieldOffset(0)] public Plane _m_plane;
[FieldOffset(0)] public Quaternion _m_quaternion;
[FieldOffset(0)] public Color _m_color;
[FieldOffset(0)] public godot_node_path _m_node_path;
[FieldOffset(0)] public RID _m_rid;
[FieldOffset(0)] public godot_variant_obj_data _m_obj_data;
[FieldOffset(0)] public godot_callable _m_callable;
[FieldOffset(0)] public godot_signal _m_signal;
[FieldOffset(0)] public godot_dictionary _m_dictionary;
[FieldOffset(0)] public godot_array _m_array;
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_variant_obj_data
{
public UInt64 id;
public IntPtr obj;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
private struct godot_variant_data_mem
{
#pragma warning disable 169
private real_t _mem0;
private real_t _mem1;
private real_t _mem2;
private real_t _mem3;
#pragma warning restore 169
}
}
public void Dispose()
{
switch (_type)
{
case Variant.Type.Nil:
case Variant.Type.Bool:
case Variant.Type.Int:
case Variant.Type.Float:
case Variant.Type.Vector2:
case Variant.Type.Vector2i:
case Variant.Type.Rect2:
case Variant.Type.Rect2i:
case Variant.Type.Vector3:
case Variant.Type.Vector3i:
case Variant.Type.Plane:
case Variant.Type.Quaternion:
case Variant.Type.Color:
case Variant.Type.Rid:
return;
}
NativeFuncs.godotsharp_variant_destroy(ref this);
_type = Variant.Type.Nil;
}
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_string : IDisposable
{
internal IntPtr _ptr;
public void Dispose()
{
if (_ptr == IntPtr.Zero)
return;
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
public struct godot_string_name : IDisposable
{
internal IntPtr _data;
public void Dispose()
{
if (_data == IntPtr.Zero)
return;
NativeFuncs.godotsharp_string_name_destroy(ref this);
_data = IntPtr.Zero;
}
// An static method because an instance method could result in a hidden copy if called on an `in` parameter.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsEmpty(in godot_string_name name) =>
// This is all that's needed to check if it's empty. Equivalent to `== StringName()` in C++.
name._data == IntPtr.Zero;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_node_path : IDisposable
{
internal IntPtr _data;
public void Dispose()
{
if (_data == IntPtr.Zero)
return;
NativeFuncs.godotsharp_node_path_destroy(ref this);
_data = IntPtr.Zero;
}
// An static method because an instance method could result in a hidden copy if called on an `in` parameter.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsEmpty(in godot_node_path nodePath) =>
// This is all that's needed to check if it's empty. It's what the `is_empty()` C++ method does.
nodePath._data == IntPtr.Zero;
}
[StructLayout(LayoutKind.Explicit)]
// ReSharper disable once InconsistentNaming
public struct godot_signal : IDisposable
{
[FieldOffset(0)] public godot_string_name _name;
// There's padding here on 32-bit
[FieldOffset(8)] public UInt64 _objectId;
public void Dispose()
{
if (_name._data == IntPtr.Zero)
return;
NativeFuncs.godotsharp_signal_destroy(ref this);
_name._data = IntPtr.Zero;
}
}
[StructLayout(LayoutKind.Explicit)]
// ReSharper disable once InconsistentNaming
public struct godot_callable : IDisposable
{
[FieldOffset(0)] public godot_string_name _method;
// There's padding here on 32-bit
[FieldOffset(8)] public UInt64 _objectId;
[FieldOffset(8)] public IntPtr _custom;
public void Dispose()
{
if (_method._data == IntPtr.Zero && _custom == IntPtr.Zero)
return;
NativeFuncs.godotsharp_callable_destroy(ref this);
_method._data = IntPtr.Zero;
_custom = IntPtr.Zero;
}
}
// A correctly constructed value needs to call the native default constructor to allocate `_p`.
// Don't pass a C# default constructed `godot_array` to native code, unless it's going to
// 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
public struct godot_array : IDisposable
{
internal unsafe ArrayPrivate* _p;
[StructLayout(LayoutKind.Sequential)]
internal struct ArrayPrivate
{
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 = null;
}
}
// IMPORTANT:
// A correctly constructed value needs to call the native default constructor to allocate `_p`.
// Don't pass a C# default constructed `godot_dictionary` to native code, unless it's going to
// 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
public struct godot_dictionary : IDisposable
{
internal IntPtr _p;
public void Dispose()
{
if (_p == IntPtr.Zero)
return;
NativeFuncs.godotsharp_dictionary_destroy(ref this);
_p = IntPtr.Zero;
}
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_packed_byte_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe byte* _ptr;
public unsafe void Dispose()
{
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_byte_array_destroy(ref this);
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_packed_int32_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe int* _ptr;
public unsafe void Dispose()
{
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_int32_array_destroy(ref this);
_ptr = null;
}
public unsafe int Size => _ptr != null ? *(_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_packed_int64_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe long* _ptr;
public unsafe void Dispose()
{
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_int64_array_destroy(ref this);
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_packed_float32_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe float* _ptr;
public unsafe void Dispose()
{
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_float32_array_destroy(ref this);
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_packed_float64_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe double* _ptr;
public unsafe void Dispose()
{
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_float64_array_destroy(ref this);
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_packed_string_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe godot_string* _ptr;
public unsafe void Dispose()
{
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_string_array_destroy(ref this);
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_packed_vector2_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe Vector2* _ptr;
public unsafe void Dispose()
{
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_vector2_array_destroy(ref this);
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_packed_vector3_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe Vector3* _ptr;
public unsafe void Dispose()
{
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_vector3_array_destroy(ref this);
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_packed_color_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe Color* _ptr;
public unsafe void Dispose()
{
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_color_array_destroy(ref this);
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
}

View File

@ -0,0 +1,93 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Godot.Bridge;
// ReSharper disable InconsistentNaming
namespace Godot.NativeInterop
{
internal static class InteropUtils
{
public static Object UnmanagedGetManaged(IntPtr 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;
}
public static void TieManagedToUnmanaged(Object managed, IntPtr unmanaged,
StringName nativeName, bool refCounted, Type type, Type nativeType)
{
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());
}
}
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)
{
using godot_string src = Marshaling.mono_string_to_godot(name);
return UnmanagedGetManaged(NativeFuncs.godotsharp_engine_get_singleton(&src));
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,608 @@
using System;
using System.Runtime.InteropServices;
// ReSharper disable InconsistentNaming
namespace Godot.NativeInterop
{
#if !NET
// This improves P/Invoke performance.
// The attribute is not available with .NET Core and it's not needed there.
[System.Security.SuppressUnmanagedCodeSecurity]
#endif
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);
[DllImport(GodotDllName)]
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);
[DllImport(GodotDllName)]
public static extern void godotsharp_string_name_new_from_string(godot_string_name* dest, godot_string* name);
[DllImport(GodotDllName)]
public static extern void godotsharp_node_path_new_from_string(godot_node_path* dest, godot_string* name);
[DllImport(GodotDllName)]
public static extern void godotsharp_string_name_as_string(godot_string* r_dest, godot_string_name* p_name);
[DllImport(GodotDllName)]
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);
[DllImport(GodotDllName)]
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);
[DllImport(GodotDllName)]
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);
[DllImport(GodotDllName)]
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);
[DllImport(GodotDllName)]
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);
[DllImport(GodotDllName)]
public static extern void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle,
godot_callable* r_callable);
[DllImport(GodotDllName)]
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);
[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);
// variant.h
[DllImport(GodotDllName)]
public static extern void godotsharp_variant_new_string_name(godot_variant* r_dest, godot_string_name* p_s);
[DllImport(GodotDllName)]
public static extern void godotsharp_variant_new_node_path(godot_variant* r_dest, godot_node_path* p_np);
[DllImport(GodotDllName)]
public static extern void godotsharp_variant_new_object(godot_variant* r_dest, IntPtr p_obj);
[DllImport(GodotDllName)]
public static extern void godotsharp_variant_new_transform2d(godot_variant* r_dest, Transform2D* p_t2d);
[DllImport(GodotDllName)]
public static extern void godotsharp_variant_new_basis(godot_variant* r_dest, Basis* p_basis);
[DllImport(GodotDllName)]
public static extern void godotsharp_variant_new_transform3d(godot_variant* r_dest, Transform3D* p_trans);
[DllImport(GodotDllName)]
public static extern void godotsharp_variant_new_aabb(godot_variant* r_dest, AABB* p_aabb);
[DllImport(GodotDllName)]
public static extern void godotsharp_variant_new_dictionary(godot_variant* r_dest, godot_dictionary* p_dict);
[DllImport(GodotDllName)]
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);
[DllImport(GodotDllName)]
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);
[DllImport(GodotDllName)]
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);
[DllImport(GodotDllName)]
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);
[DllImport(GodotDllName)]
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);
[DllImport(GodotDllName)]
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);
[DllImport(GodotDllName)]
public static extern double godotsharp_variant_as_float(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern godot_string godotsharp_variant_as_string(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern Vector2 godotsharp_variant_as_vector2(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern Vector2i godotsharp_variant_as_vector2i(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern Rect2 godotsharp_variant_as_rect2(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern Rect2i godotsharp_variant_as_rect2i(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern Vector3 godotsharp_variant_as_vector3(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern Vector3i godotsharp_variant_as_vector3i(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern Transform2D godotsharp_variant_as_transform2d(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern Plane godotsharp_variant_as_plane(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern Quaternion godotsharp_variant_as_quaternion(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern AABB godotsharp_variant_as_aabb(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern Basis godotsharp_variant_as_basis(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern Transform3D godotsharp_variant_as_transform3d(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern Color godotsharp_variant_as_color(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern godot_string_name godotsharp_variant_as_string_name(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern godot_node_path godotsharp_variant_as_node_path(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern RID godotsharp_variant_as_rid(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern godot_callable godotsharp_variant_as_callable(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern godot_signal godotsharp_variant_as_signal(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern godot_dictionary godotsharp_variant_as_dictionary(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern godot_array godotsharp_variant_as_array(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern godot_packed_byte_array godotsharp_variant_as_packed_byte_array(godot_variant* p_self);
[DllImport(GodotDllName)]
public static extern godot_packed_int32_array godotsharp_variant_as_packed_int32_array(godot_variant* p_self);
[DllImport(GodotDllName)]
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);
[DllImport(GodotDllName)]
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);
[DllImport(GodotDllName)]
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)]
public static extern void godotsharp_string_new_with_utf16_chars(godot_string* r_dest, char* p_contents);
// string_name.h
[DllImport(GodotDllName)]
public static extern void godotsharp_string_name_new_copy(godot_string_name* r_dest, godot_string_name* p_src);
// node_path.h
[DllImport(GodotDllName)]
public static extern void godotsharp_node_path_new_copy(godot_node_path* r_dest, godot_node_path* p_src);
// 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);
// destroy functions
[DllImport(GodotDllName)]
public static extern void godotsharp_packed_byte_array_destroy(ref godot_packed_byte_array p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_packed_int32_array_destroy(ref godot_packed_int32_array p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_packed_int64_array_destroy(ref godot_packed_int64_array p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_packed_float32_array_destroy(ref godot_packed_float32_array p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_packed_float64_array_destroy(ref godot_packed_float64_array p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_packed_string_array_destroy(ref godot_packed_string_array p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_packed_vector2_array_destroy(ref godot_packed_vector2_array p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_packed_vector3_array_destroy(ref godot_packed_vector3_array p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_packed_color_array_destroy(ref godot_packed_color_array p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_variant_destroy(ref godot_variant p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_string_destroy(ref godot_string p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_string_name_destroy(ref godot_string_name p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_node_path_destroy(ref godot_node_path p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_signal_destroy(ref godot_signal p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_callable_destroy(ref godot_callable p_self);
[DllImport(GodotDllName)]
public static extern void godotsharp_array_destroy(ref godot_array p_self);
[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

@ -0,0 +1,84 @@
using System;
using System.Runtime.CompilerServices;
// ReSharper disable InconsistentNaming
namespace Godot.NativeInterop
{
public static unsafe partial class NativeFuncs
{
public static godot_string_name godotsharp_string_name_new_copy(godot_string_name* src)
{
godot_string_name ret;
godotsharp_string_name_new_copy(&ret, src);
return ret;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_string_name godotsharp_string_name_new_copy(godot_string_name src)
=> godotsharp_string_name_new_copy(&src);
public static godot_node_path godotsharp_node_path_new_copy(godot_node_path* src)
{
godot_node_path ret;
godotsharp_node_path_new_copy(&ret, src);
return ret;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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;
godotsharp_array_new_copy(&ret, src);
return ret;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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;
godotsharp_dictionary_new_copy(&ret, src);
return ret;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_dictionary godotsharp_dictionary_new_copy(godot_dictionary src)
=> godotsharp_dictionary_new_copy(&src);
public static godot_string_name godotsharp_string_name_new_from_string(string name)
{
godot_string_name ret;
using godot_string src = Marshaling.mono_string_to_godot(name);
godotsharp_string_name_new_from_string(&ret, &src);
return ret;
}
public static godot_node_path godotsharp_node_path_new_from_string(string name)
{
godot_node_path ret;
using godot_string src = Marshaling.mono_string_to_godot(name);
godotsharp_node_path_new_from_string(&ret, &src);
return ret;
}
}
}

View File

@ -0,0 +1,33 @@
using System;
namespace Godot.NativeInterop
{
internal ref struct VariantSpanDisposer
{
private readonly Span<godot_variant> _variantSpan;
// IMPORTANT: The span element must be default initialized.
// Make sure call Clear() on the span if it was created with stackalloc.
public VariantSpanDisposer(Span<godot_variant> variantSpan)
{
_variantSpan = variantSpan;
}
public void Dispose()
{
for (int i = 0; i < _variantSpan.Length; i++)
_variantSpan[i].Dispose();
}
}
internal static class VariantSpanExtensions
{
// Used to make sure we always initialize the span values to the default,
// as we need that in order to safely dispose all elements after.
public static Span<godot_variant> Cleared(this Span<godot_variant> span)
{
span.Clear();
return span;
}
}
}

View File

@ -0,0 +1,355 @@
using System;
using System.Runtime.CompilerServices;
// ReSharper disable InconsistentNaming
namespace Godot.NativeInterop
{
public static class VariantUtils
{
public static godot_variant CreateFromRID(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.ToGodotBool() } };
public static godot_variant CreateFromInt(long 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 } };
public static godot_variant CreateFromFloat(double 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 } };
public static godot_variant CreateFromVector2i(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 } };
public static godot_variant CreateFromVector3i(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 } };
public static godot_variant CreateFromRect2i(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 } };
public static godot_variant CreateFromColor(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 } };
public static unsafe godot_variant CreateFromTransform2D(Transform2D from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_transform2d(&ret, &from);
return ret;
}
public static unsafe godot_variant CreateFromBasis(Basis from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_basis(&ret, &from);
return ret;
}
public static unsafe godot_variant CreateFromTransform3D(Transform3D from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_transform3d(&ret, &from);
return ret;
}
public static unsafe godot_variant CreateFromAABB(AABB from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_aabb(&ret, &from);
return ret;
}
// Explicit name to make it very clear
public static godot_variant CreateFromCallableTakingOwnershipOfDisposableValue(godot_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 } };
// Explicit name to make it very clear
public static godot_variant CreateFromStringTakingOwnershipOfDisposableValue(godot_string from)
=> new() { _type = Variant.Type.String, _data = { _m_string = from } };
public static unsafe godot_variant CreateFromPackedByteArray(godot_packed_byte_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_packed_byte_array(&ret, from);
return ret;
}
public static unsafe godot_variant CreateFromPackedInt32Array(godot_packed_int32_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_packed_int32_array(&ret, from);
return ret;
}
public static unsafe godot_variant CreateFromPackedInt64Array(godot_packed_int64_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_packed_int64_array(&ret, from);
return ret;
}
public static unsafe godot_variant CreateFromPackedFloat32Array(godot_packed_float32_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_packed_float32_array(&ret, from);
return ret;
}
public static unsafe godot_variant CreateFromPackedFloat64Array(godot_packed_float64_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_packed_float64_array(&ret, from);
return ret;
}
public static unsafe godot_variant CreateFromPackedStringArray(godot_packed_string_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_packed_string_array(&ret, from);
return ret;
}
public static unsafe godot_variant CreateFromPackedVector2Array(godot_packed_vector2_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_packed_vector2_array(&ret, from);
return ret;
}
public static unsafe godot_variant CreateFromPackedVector3Array(godot_packed_vector3_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_packed_vector3_array(&ret, from);
return ret;
}
public static unsafe godot_variant CreateFromPackedColorArray(godot_packed_color_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_packed_color_array(&ret, from);
return ret;
}
public static unsafe godot_variant CreateFromArray(godot_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_array(&ret, from);
return ret;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe godot_variant CreateFromArray(godot_array from)
=> CreateFromArray(&from);
public static unsafe godot_variant CreateFromDictionary(godot_dictionary* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_dictionary(&ret, from);
return ret;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe godot_variant CreateFromDictionary(godot_dictionary from)
=> CreateFromDictionary(&from);
public static unsafe godot_variant CreateFromStringName(ref godot_string_name arg1)
{
godot_variant ret;
godot_string_name src = arg1;
NativeFuncs.godotsharp_variant_new_string_name(&ret, &src);
return ret;
}
public static unsafe godot_variant CreateFromNodePath(ref godot_node_path arg1)
{
godot_variant ret;
godot_node_path src = arg1;
NativeFuncs.godotsharp_variant_new_node_path(&ret, &src);
return ret;
}
public static unsafe godot_variant CreateFromGodotObject(IntPtr from)
{
if (from == IntPtr.Zero)
return new godot_variant();
godot_variant ret;
NativeFuncs.godotsharp_variant_new_object(&ret, from);
return ret;
}
// 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.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));
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));
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));
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));
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));
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));
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));
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));
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));
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
public static unsafe godot_string_name ConvertToStringName(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.StringName ?
NativeFuncs.godotsharp_string_name_new_copy(&(*p_var)._data._m_string_name) :
NativeFuncs.godotsharp_variant_as_string_name(p_var);
public static unsafe godot_node_path ConvertToNodePath(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.NodePath ?
NativeFuncs.godotsharp_node_path_new_copy(&(*p_var)._data._m_node_path) :
NativeFuncs.godotsharp_variant_as_node_path(p_var);
public static unsafe godot_array ConvertToArray(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Array ?
NativeFuncs.godotsharp_array_new_copy(&(*p_var)._data._m_array) :
NativeFuncs.godotsharp_variant_as_array(p_var);
public static unsafe godot_dictionary ConvertToDictionary(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Dictionary ?
NativeFuncs.godotsharp_dictionary_new_copy(&(*p_var)._data._m_dictionary) :
NativeFuncs.godotsharp_variant_as_dictionary(p_var);
}
}

View File

@ -1,24 +1,12 @@
using System;
using System.Runtime.CompilerServices;
using Godot.NativeInterop;
namespace Godot
{
public sealed partial class NodePath : IDisposable
public sealed class NodePath : IDisposable
{
private bool disposed = false;
private IntPtr ptr;
internal static IntPtr GetPtr(NodePath instance)
{
if (instance == null)
throw new NullReferenceException($"The instance of type {nameof(NodePath)} is null.");
if (instance.disposed)
throw new ObjectDisposedException(instance.GetType().FullName);
return instance.ptr;
}
public godot_node_path NativeValue;
~NodePath()
{
@ -31,112 +19,90 @@ namespace Godot
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
public void Dispose(bool disposing)
{
if (disposed)
return;
if (ptr != IntPtr.Zero)
{
godot_icall_NodePath_Dtor(ptr);
ptr = IntPtr.Zero;
}
disposed = true;
// Always dispose `NativeValue` even if disposing is true
NativeValue.Dispose();
}
internal NodePath(IntPtr ptr)
private NodePath(godot_node_path nativeValueToOwn)
{
this.ptr = ptr;
NativeValue = nativeValueToOwn;
}
public NodePath() : this(string.Empty) {}
// Explicit name to make it very clear
internal static NodePath CreateTakingOwnershipOfDisposableValue(godot_node_path nativeValueToOwn)
=> new NodePath(nativeValueToOwn);
public NodePath()
{
}
public NodePath(string path)
{
ptr = godot_icall_NodePath_Ctor(path);
if (!string.IsNullOrEmpty(path))
NativeValue = NativeFuncs.godotsharp_node_path_new_from_string(path);
}
public static implicit operator NodePath(string from) => new NodePath(from);
public static implicit operator string(NodePath from) => from.ToString();
public override string ToString()
public override unsafe string ToString()
{
return godot_icall_NodePath_operator_String(GetPtr(this));
if (IsEmpty)
return string.Empty;
godot_string dest;
godot_node_path src = NativeValue;
NativeFuncs.godotsharp_node_path_as_string(&dest, &src);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
public NodePath GetAsPropertyPath()
{
return new NodePath(godot_icall_NodePath_get_as_property_path(GetPtr(this)));
godot_node_path propertyPath = default;
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(GetPtr(this));
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(GetPtr(this), 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(GetPtr(this));
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(GetPtr(this), 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()
public int GetSubNameCount()
{
return godot_icall_NodePath_get_subname_count(GetPtr(this));
return NativeFuncs.godotsharp_node_path_get_subname_count(ref NativeValue);
}
public bool IsAbsolute()
{
return godot_icall_NodePath_is_absolute(GetPtr(this));
return NativeFuncs.godotsharp_node_path_is_absolute(ref NativeValue).ToBool();
}
public bool IsEmpty()
{
return godot_icall_NodePath_is_empty(GetPtr(this));
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern IntPtr godot_icall_NodePath_Ctor(string path);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void godot_icall_NodePath_Dtor(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string godot_icall_NodePath_operator_String(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string godot_icall_NodePath_get_name(IntPtr ptr, int arg1);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern int godot_icall_NodePath_get_name_count(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string godot_icall_NodePath_get_subname(IntPtr ptr, int arg1);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern int godot_icall_NodePath_get_subname_count(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool godot_icall_NodePath_is_absolute(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool godot_icall_NodePath_is_empty(IntPtr ptr);
public bool IsEmpty => godot_node_path.IsEmpty(in NativeValue);
}
}

View File

@ -1,48 +1,80 @@
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 bool _disposed = false;
private Type _cachedType = typeof(Object);
private static StringName nativeName = "Object";
internal IntPtr ptr;
internal bool memoryOwn;
internal IntPtr NativePtr;
internal bool MemoryOwn;
public Object() : this(false)
{
if (ptr == IntPtr.Zero)
ptr = godot_icall_Object_Ctor(this);
if (NativePtr == IntPtr.Zero)
{
unsafe
{
NativePtr = NativeCtor();
}
InteropUtils.TieManagedToUnmanaged(this, NativePtr,
NativeName, refCounted: false, GetType(), _cachedType);
}
else
{
InteropUtils.TieManagedToUnmanagedWithPreSetup(this, NativePtr);
}
_InitializeGodotScriptInstanceInternals();
}
internal void _InitializeGodotScriptInstanceInternals()
{
godot_icall_Object_ConnectEventSignals(ptr);
// 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
{
get { return ptr; }
}
public IntPtr NativeInstance => NativePtr;
internal static IntPtr GetPtr(Object instance)
{
if (instance == null)
return IntPtr.Zero;
if (instance.disposed)
if (instance._disposed)
throw new ObjectDisposedException(instance.GetType().FullName);
return instance.ptr;
return instance.NativePtr;
}
~Object()
@ -58,30 +90,32 @@ namespace Godot
protected virtual void Dispose(bool disposing)
{
if (disposed)
if (_disposed)
return;
if (ptr != IntPtr.Zero)
if (NativePtr != IntPtr.Zero)
{
if (memoryOwn)
if (MemoryOwn)
{
memoryOwn = false;
godot_icall_RefCounted_Disposed(this, ptr, !disposing);
MemoryOwn = false;
NativeFuncs.godotsharp_internal_refcounted_disposed(NativePtr, (!disposing).ToGodotBool());
}
else
{
godot_icall_Object_Disposed(this, ptr);
NativeFuncs.godotsharp_internal_object_disposed(NativePtr);
}
this.ptr = IntPtr.Zero;
NativePtr = IntPtr.Zero;
}
disposed = true;
_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>
@ -112,33 +146,236 @@ 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 IntPtr __ClassDB_get_method(StringName type, string method)
internal static Type InternalGetClassNativeBase(Type t)
{
return godot_icall_Object_ClassDB_get_method(StringName.GetPtr(type), method);
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;
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern IntPtr godot_icall_Object_Ctor(Object obj);
internal static bool InternalIsClassNativeBase(Type t)
{
var assemblyName = t.Assembly.GetName();
return assemblyName.Name == "GodotSharp" || assemblyName.Name == "GodotSharpEditor";
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Object_Disposed(Object obj, IntPtr ptr);
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);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_RefCounted_Disposed(Object obj, IntPtr ptr, bool isFinalizer);
while (top != null && top != native)
{
var methodInfo = top.GetMethod(method,
BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Object_ConnectEventSignals(IntPtr obj);
if (methodInfo != null)
{
var parameters = methodInfo.GetParameters();
int paramCount = parameters.Length;
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern string godot_icall_Object_ToString(IntPtr ptr);
if (argCount == paramCount)
{
object[] invokeParams = new object[paramCount];
// Used by the generated API
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern IntPtr godot_icall_Object_ClassDB_get_method(IntPtr type, string method);
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 = NativeFuncs.godotsharp_method_bind_get_method(ref type.NativeValue, methodChars);
}
if (methodBind == IntPtr.Zero)
throw new NativeMethodBindNotFoundException(type + "." + method);
return methodBind;
}
internal static unsafe delegate* unmanaged<IntPtr> ClassDB_get_constructor(StringName type)
{
// for some reason the '??' operator doesn't support 'delegate*'
var nativeConstructor = NativeFuncs.godotsharp_get_class_constructor(ref type.NativeValue);
if (nativeConstructor == null)
throw new NativeConstructorNotFoundException(type);
return nativeConstructor;
}
}
}

View File

@ -0,0 +1,135 @@
using System;
#nullable enable
namespace Godot
{
public partial class Object
{
public class NativeMemberNotFoundException : Exception
{
public NativeMemberNotFoundException()
{
}
public NativeMemberNotFoundException(string? message) : base(message)
{
}
public NativeMemberNotFoundException(string? message, Exception? innerException)
: base(message, innerException)
{
}
}
public class NativeConstructorNotFoundException : NativeMemberNotFoundException
{
private readonly string? _nativeClassName;
// ReSharper disable once InconsistentNaming
private const string Arg_NativeConstructorNotFoundException = "Unable to find the native constructor.";
public NativeConstructorNotFoundException()
: base(Arg_NativeConstructorNotFoundException)
{
}
public NativeConstructorNotFoundException(string? nativeClassName)
: this(Arg_NativeConstructorNotFoundException, nativeClassName)
{
}
public NativeConstructorNotFoundException(string? message, Exception? innerException)
: base(message, innerException)
{
}
public NativeConstructorNotFoundException(string? message, string? nativeClassName)
: base(message)
{
_nativeClassName = nativeClassName;
}
public NativeConstructorNotFoundException(string? message, string? nativeClassName, Exception? innerException)
: base(message, innerException)
{
_nativeClassName = nativeClassName;
}
public override string Message
{
get
{
string s = base.Message;
if (string.IsNullOrEmpty(s))
{
s = Arg_NativeConstructorNotFoundException;
}
if (!string.IsNullOrEmpty(_nativeClassName))
{
s += " " + string.Format("(Class '{0}')", _nativeClassName);
}
return s;
}
}
}
public class NativeMethodBindNotFoundException : NativeMemberNotFoundException
{
private readonly string? _nativeMethodName;
// ReSharper disable once InconsistentNaming
private const string Arg_NativeMethodBindNotFoundException = "Unable to find the native method bind.";
public NativeMethodBindNotFoundException()
: base(Arg_NativeMethodBindNotFoundException)
{
}
public NativeMethodBindNotFoundException(string? nativeMethodName)
: this(Arg_NativeMethodBindNotFoundException, nativeMethodName)
{
}
public NativeMethodBindNotFoundException(string? message, Exception? innerException)
: base(message, innerException)
{
}
public NativeMethodBindNotFoundException(string? message, string? nativeMethodName)
: base(message)
{
_nativeMethodName = nativeMethodName;
}
public NativeMethodBindNotFoundException(string? message, string? nativeMethodName, Exception? innerException)
: base(message, innerException)
{
_nativeMethodName = nativeMethodName;
}
public override string Message
{
get
{
string s = base.Message;
if (string.IsNullOrEmpty(s))
{
s = Arg_NativeMethodBindNotFoundException;
}
if (!string.IsNullOrEmpty(_nativeMethodName))
{
s += " " + string.Format("(Method '{0}')", _nativeMethodName);
}
return s;
}
}
}
}
}

View File

@ -1,84 +1,25 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Godot.NativeInterop;
namespace Godot
{
public sealed partial class RID : IDisposable
[StructLayout(LayoutKind.Sequential)]
public struct RID
{
private bool disposed = false;
private ulong _id; // Default is 0
internal IntPtr ptr;
internal static IntPtr GetPtr(RID instance)
internal RID(ulong id)
{
if (instance == null)
throw new NullReferenceException($"The instance of type {nameof(RID)} is null.");
if (instance.disposed)
throw new ObjectDisposedException(instance.GetType().FullName);
return instance.ptr;
}
~RID()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (disposed)
return;
if (ptr != IntPtr.Zero)
{
godot_icall_RID_Dtor(ptr);
ptr = IntPtr.Zero;
}
disposed = true;
}
internal RID(IntPtr ptr)
{
this.ptr = ptr;
}
public IntPtr NativeInstance
{
get { return ptr; }
}
internal RID()
{
this.ptr = IntPtr.Zero;
_id = id;
}
public RID(Object from)
{
this.ptr = godot_icall_RID_Ctor(Object.GetPtr(from));
}
=> _id = from is Resource res ? res.GetRid()._id : default;
public int GetId()
{
return godot_icall_RID_get_id(RID.GetPtr(this));
}
public ulong Id => _id;
public override string ToString() => "[RID]";
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static IntPtr godot_icall_RID_Ctor(IntPtr from);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_RID_Dtor(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static int godot_icall_RID_get_id(IntPtr ptr);
public override string ToString() => $"RID({Id})";
}
}

View File

@ -1,53 +1,66 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Godot.NativeInterop;
namespace Godot
{
public class SignalAwaiter : IAwaiter<object[]>, IAwaitable<object[]>
{
private bool completed;
private object[] result;
private Action action;
private bool _completed;
private object[] _result;
private Action _action;
public SignalAwaiter(Object source, StringName signal, Object target)
{
godot_icall_SignalAwaiter_connect(Object.GetPtr(source), StringName.GetPtr(signal), 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 extern static Error godot_icall_SignalAwaiter_connect(IntPtr source, IntPtr signal, IntPtr target, SignalAwaiter awaiter);
public bool IsCompleted
{
get
{
return completed;
}
}
public bool IsCompleted => _completed;
public void OnCompleted(Action action)
{
this.action = action;
this._action = action;
}
public object[] GetResult()
{
return result;
}
public object[] GetResult() => _result;
public IAwaiter<object[]> GetAwaiter()
{
return this;
}
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;
@ -192,7 +194,7 @@ namespace Godot
/// </summary>
public static string CEscape(this string instance)
{
var sb = new StringBuilder(string.Copy(instance));
var sb = new StringBuilder(instance);
sb.Replace("\\", "\\\\");
sb.Replace("\a", "\\a");
@ -215,7 +217,7 @@ namespace Godot
/// </summary>
public static string CUnescape(this string instance)
{
var sb = new StringBuilder(string.Copy(instance));
var sb = new StringBuilder(instance);
sb.Replace("\\a", "\a");
sb.Replace("\\b", "\b");
@ -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;
@ -732,7 +738,7 @@ namespace Godot
/// </summary>
public static string JSONEscape(this string instance)
{
var sb = new StringBuilder(string.Copy(instance));
var sb = new StringBuilder(instance);
sb.Replace("\\", "\\\\");
sb.Replace("\b", "\\b");
@ -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

@ -1,22 +1,12 @@
using System;
using System.Runtime.CompilerServices;
using Godot.NativeInterop;
namespace Godot
{
public sealed partial class StringName : IDisposable
public sealed class StringName : IDisposable
{
private IntPtr ptr;
internal static IntPtr GetPtr(StringName instance)
{
if (instance == null)
throw new NullReferenceException($"The instance of type {nameof(StringName)} is null.");
if (instance.ptr == IntPtr.Zero)
throw new ObjectDisposedException(instance.GetType().FullName);
return instance.ptr;
}
public godot_string_name NativeValue;
~StringName()
{
@ -29,54 +19,47 @@ namespace Godot
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
public void Dispose(bool disposing)
{
if (ptr != IntPtr.Zero)
{
godot_icall_StringName_Dtor(ptr);
ptr = IntPtr.Zero;
}
// Always dispose `NativeValue` even if disposing is true
NativeValue.Dispose();
}
internal StringName(IntPtr ptr)
private StringName(godot_string_name nativeValueToOwn)
{
this.ptr = ptr;
NativeValue = nativeValueToOwn;
}
// Explicit name to make it very clear
internal static StringName CreateTakingOwnershipOfDisposableValue(godot_string_name nativeValueToOwn)
=> new StringName(nativeValueToOwn);
public StringName()
{
ptr = IntPtr.Zero;
}
public StringName(string path)
public StringName(string name)
{
ptr = path == null ? IntPtr.Zero : godot_icall_StringName_Ctor(path);
if (!string.IsNullOrEmpty(name))
NativeValue = NativeFuncs.godotsharp_string_name_new_from_string(name);
}
public static implicit operator StringName(string from) => new StringName(from);
public static implicit operator string(StringName from) => from.ToString();
public override string ToString()
public override unsafe string ToString()
{
return ptr == IntPtr.Zero ? string.Empty : godot_icall_StringName_operator_String(GetPtr(this));
if (IsEmpty)
return string.Empty;
godot_string dest;
godot_string_name src = NativeValue;
NativeFuncs.godotsharp_string_name_as_string(&dest, &src);
using (dest)
return Marshaling.mono_string_from_godot(dest);
}
public bool IsEmpty()
{
return ptr == IntPtr.Zero || godot_icall_StringName_is_empty(GetPtr(this));
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern IntPtr godot_icall_StringName_Ctor(string path);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void godot_icall_StringName_Dtor(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string godot_icall_StringName_operator_String(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool godot_icall_StringName_is_empty(IntPtr ptr);
public bool IsEmpty => godot_string_name.IsEmpty(in NativeValue);
}
}

View File

@ -4,13 +4,22 @@
<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>
<LangVersion>9</LangVersion>
<!-- Disabled temporarily as it pollutes the warnings, but we need to document public APIs. -->
<NoWarn>CS1591</NoWarn>
</PropertyGroup>
<PropertyGroup>
<DefineConstants>$(DefineConstants);GODOT</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ReflectionAnalyzers" Version="0.1.22-dev" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers" />
<!--PackageReference Include="IDisposableAnalyzers" Version="3.4.13" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers" /-->
</ItemGroup>
<ItemGroup>
<Compile Include="Core\AABB.cs" />
<Compile Include="Core\Array.cs" />
@ -23,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" />
@ -30,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" />
@ -44,16 +56,24 @@
<Compile Include="Core\Interfaces\IAwaitable.cs" />
<Compile Include="Core\Interfaces\IAwaiter.cs" />
<Compile Include="Core\Interfaces\ISerializationListener.cs" />
<Compile Include="Core\MarshalUtils.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" />
<Compile Include="Core\NativeInterop\VariantUtils.cs" />
<Compile Include="Core\NodePath.cs" />
<Compile Include="Core\Object.base.cs" />
<Compile Include="Core\Object.exceptions.cs" />
<Compile Include="Core\Plane.cs" />
<Compile Include="Core\Quaternion.cs" />
<Compile Include="Core\Rect2.cs" />
<Compile Include="Core\Rect2i.cs" />
<Compile Include="Core\RID.cs" />
<Compile Include="Core\NativeInterop\NativeFuncs.cs" />
<Compile Include="Core\NativeInterop\InteropStructs.cs" />
<Compile Include="Core\NativeInterop\Marshaling.cs" />
<Compile Include="Core\SignalInfo.cs" />
<Compile Include="Core\SignalAwaiter.cs" />
<Compile Include="Core\StringExtensions.cs" />

View File

@ -0,0 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=core/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated_005Cgodotobjects/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary>

View File

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

View File

@ -4,9 +4,11 @@
<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>
<LangVersion>9</LangVersion>
</PropertyGroup>
<PropertyGroup>
<DefineConstants>$(DefineConstants);GODOT</DefineConstants>

View File

@ -0,0 +1,4 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated_005Cgodotobjects/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary>

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,257 +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. */
/*************************************************************************/
#ifdef MONO_GLUE_ENABLED
#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"
Object *godot_icall_Object_Ctor(MonoObject *p_obj) {
Object *instance = memnew(Object);
GDMonoInternals::tie_managed_to_unmanaged(p_obj, instance);
return instance;
}
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();
}
}
MethodBind *godot_icall_Object_ClassDB_get_method(StringName *p_type, MonoString *p_method) {
StringName type = p_type ? *p_type : StringName();
StringName method(GDMonoMarshal::mono_string_to_godot(p_method));
return ClassDB::get_method(type, method);
}
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_Ctor", godot_icall_Object_Ctor);
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_ClassDB_get_method", godot_icall_Object_ClassDB_get_method);
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);
}
#endif // MONO_GLUE_ENABLED

View File

@ -1,369 +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. */
/*************************************************************************/
#ifdef MONO_GLUE_ENABLED
#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"
Array *godot_icall_Array_Ctor() {
return memnew(Array);
}
void godot_icall_Array_Dtor(Array *ptr) {
memdelete(ptr);
}
MonoObject *godot_icall_Array_At(Array *ptr, int32_t index) {
if (index < 0 || index >= ptr->size()) {
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
return nullptr;
}
return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index));
}
MonoObject *godot_icall_Array_At_Generic(Array *ptr, int32_t index, uint32_t type_encoding, GDMonoClass *type_class) {
if (index < 0 || index >= ptr->size()) {
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
return nullptr;
}
return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index), ManagedType(type_encoding, type_class));
}
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++;
}
}
Array *godot_icall_Array_Ctor_MonoArray(MonoArray *mono_array) {
Array *godot_array = memnew(Array);
unsigned int count = mono_array_length(mono_array);
godot_array->resize(count);
for (unsigned int i = 0; i < count; i++) {
MonoObject *item = mono_array_get(mono_array, MonoObject *, i);
godot_icall_Array_SetAt(godot_array, i, item);
}
return godot_array;
}
Array *godot_icall_Array_Duplicate(Array *ptr, MonoBoolean deep) {
return memnew(Array(ptr->duplicate(deep)));
}
Array *godot_icall_Array_Concatenate(Array *left, Array *right) {
int count = left->size() + right->size();
Array *new_array = memnew(Array(left->duplicate(false)));
new_array->resize(count);
for (unsigned int i = 0; i < (unsigned int)right->size(); i++) {
new_array->operator[](i + left->size()) = right->operator[](i);
}
return new_array;
}
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();
}
void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class) {
MonoType *elem_type = mono_reflection_type_get_type(refltype);
*type_encoding = mono_type_get_type(elem_type);
MonoClass *type_class_raw = mono_class_from_mono_type(elem_type);
*type_class = GDMono::get_singleton()->get_class(type_class_raw);
}
MonoString *godot_icall_Array_ToString(Array *ptr) {
return GDMonoMarshal::mono_string_from_godot(Variant(*ptr).operator String());
}
Dictionary *godot_icall_Dictionary_Ctor() {
return memnew(Dictionary);
}
void godot_icall_Dictionary_Dtor(Dictionary *ptr) {
memdelete(ptr);
}
MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key) {
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);
return nullptr;
}
return GDMonoMarshal::variant_to_mono_object(ret);
}
MonoObject *godot_icall_Dictionary_GetValue_Generic(Dictionary *ptr, MonoObject *key, uint32_t type_encoding, GDMonoClass *type_class) {
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);
return nullptr;
}
return GDMonoMarshal::variant_to_mono_object(ret, ManagedType(type_encoding, type_class));
}
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);
}
Array *godot_icall_Dictionary_Keys(Dictionary *ptr) {
return memnew(Array(ptr->keys()));
}
Array *godot_icall_Dictionary_Values(Dictionary *ptr) {
return memnew(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) {
*keys = godot_icall_Dictionary_Keys(ptr);
*values = godot_icall_Dictionary_Values(ptr);
return godot_icall_Dictionary_Count(ptr);
}
void godot_icall_Dictionary_KeyValuePairAt(Dictionary *ptr, int index, MonoObject **key, MonoObject **value) {
Array *keys = godot_icall_Dictionary_Keys(ptr);
Array *values = godot_icall_Dictionary_Values(ptr);
*key = GDMonoMarshal::variant_to_mono_object(keys->get(index));
*value = GDMonoMarshal::variant_to_mono_object(values->get(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));
}
Dictionary *godot_icall_Dictionary_Duplicate(Dictionary *ptr, MonoBoolean deep) {
return memnew(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, MonoObject **value) {
Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
if (ret == nullptr) {
*value = nullptr;
return false;
}
*value = GDMonoMarshal::variant_to_mono_object(ret);
return true;
}
MonoBoolean godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObject *key, MonoObject **value, uint32_t type_encoding, GDMonoClass *type_class) {
Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
if (ret == nullptr) {
*value = nullptr;
return false;
}
*value = GDMonoMarshal::variant_to_mono_object(ret, ManagedType(type_encoding, type_class));
return true;
}
void godot_icall_Dictionary_Generic_GetValueTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class) {
MonoType *value_type = mono_reflection_type_get_type(refltype);
*type_encoding = mono_type_get_type(value_type);
MonoClass *type_class_raw = mono_class_from_mono_type(value_type);
*type_class = GDMono::get_singleton()->get_class(type_class_raw);
}
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_Dtor", godot_icall_Array_Dtor);
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_At_Generic", godot_icall_Array_At_Generic);
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_Generic_GetElementTypeInfo", godot_icall_Array_Generic_GetElementTypeInfo);
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_Dtor", godot_icall_Dictionary_Dtor);
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_GetValue_Generic", godot_icall_Dictionary_GetValue_Generic);
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_TryGetValue_Generic", godot_icall_Dictionary_TryGetValue_Generic);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Generic_GetValueTypeInfo", godot_icall_Dictionary_Generic_GetValueTypeInfo);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ToString", godot_icall_Dictionary_ToString);
}
#endif // MONO_GLUE_ENABLED

View File

@ -1,321 +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. */
/*************************************************************************/
#ifdef MONO_GLUE_ENABLED
#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(MonoArray *p_bytes, MonoBoolean p_allow_objects) {
Variant ret;
PackedByteArray varr = GDMonoMarshal::mono_array_to_PackedByteArray(p_bytes);
Error err = decode_variant(ret, varr.ptr(), varr.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));
}
MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects) {
Variant var = GDMonoMarshal::mono_object_to_variant(p_var);
PackedByteArray barr;
int len;
Error err = encode_variant(var, nullptr, len, p_full_objects);
ERR_FAIL_COND_V_MSG(err != OK, nullptr, "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).");
barr.resize(len);
encode_variant(var, barr.ptrw(), len, p_full_objects);
return GDMonoMarshal::PackedByteArray_to_mono_array(barr);
}
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);
}
#endif // MONO_GLUE_ENABLED

View File

@ -1,84 +0,0 @@
/*************************************************************************/
/* glue_header.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. */
/*************************************************************************/
#ifdef MONO_GLUE_ENABLED
#include "../mono_gd/gd_mono_marshal.h"
void godot_register_collections_icalls();
void godot_register_gd_icalls();
void godot_register_string_name_icalls();
void godot_register_nodepath_icalls();
void godot_register_object_icalls();
void godot_register_rid_icalls();
void godot_register_string_icalls();
void godot_register_scene_tree_icalls();
/**
* Registers internal calls that were not generated. This function is called
* from the generated GodotSharpBindings::register_generated_icalls() function.
*/
void godot_register_glue_header_icalls() {
godot_register_collections_icalls();
godot_register_gd_icalls();
godot_register_string_name_icalls();
godot_register_nodepath_icalls();
godot_register_object_icalls();
godot_register_rid_icalls();
godot_register_string_icalls();
godot_register_scene_tree_icalls();
}
// Used by the generated glue
#include "core/config/engine.h"
#include "core/object/class_db.h"
#include "core/object/method_bind.h"
#include "core/object/ref_counted.h"
#include "core/string/node_path.h"
#include "core/string/ustring.h"
#include "core/typedefs.h"
#include "core/variant/array.h"
#include "core/variant/dictionary.h"
#include "../mono_gd/gd_mono_class.h"
#include "../mono_gd/gd_mono_internals.h"
#include "../mono_gd/gd_mono_utils.h"
#define GODOTSHARP_INSTANCE_OBJECT(m_instance, m_type) \
static ClassDB::ClassInfo *ci = nullptr; \
if (!ci) { \
ci = ClassDB::classes.getptr(m_type); \
} \
Object *m_instance = ci->creation_func();
#include "arguments_vector.h"
#endif // MONO_GLUE_ENABLED

View File

@ -1,97 +0,0 @@
/*************************************************************************/
/* nodepath_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. */
/*************************************************************************/
#ifdef MONO_GLUE_ENABLED
#include "core/string/node_path.h"
#include "core/string/ustring.h"
#include "../mono_gd/gd_mono_marshal.h"
NodePath *godot_icall_NodePath_Ctor(MonoString *p_path) {
return memnew(NodePath(GDMonoMarshal::mono_string_to_godot(p_path)));
}
void godot_icall_NodePath_Dtor(NodePath *p_ptr) {
ERR_FAIL_NULL(p_ptr);
memdelete(p_ptr);
}
MonoString *godot_icall_NodePath_operator_String(NodePath *p_np) {
return GDMonoMarshal::mono_string_from_godot(p_np->operator String());
}
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());
}
NodePath *godot_icall_NodePath_get_as_property_path(NodePath *p_ptr) {
return memnew(NodePath(p_ptr->get_as_property_path()));
}
MonoBoolean godot_icall_NodePath_is_empty(NodePath *p_ptr) {
return (MonoBoolean)p_ptr->is_empty();
}
void godot_register_nodepath_icalls() {
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_Ctor", godot_icall_NodePath_Ctor);
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_Dtor", godot_icall_NodePath_Dtor);
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_operator_String", godot_icall_NodePath_operator_String);
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);
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_is_empty", godot_icall_NodePath_is_empty);
}
#endif // MONO_GLUE_ENABLED

View File

@ -1,64 +0,0 @@
/*************************************************************************/
/* rid_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. */
/*************************************************************************/
#ifdef MONO_GLUE_ENABLED
#include "core/io/resource.h"
#include "core/object/class_db.h"
#include "core/templates/rid.h"
#include "../mono_gd/gd_mono_marshal.h"
RID *godot_icall_RID_Ctor(Object *p_from) {
Resource *res_from = Object::cast_to<Resource>(p_from);
if (res_from) {
return memnew(RID(res_from->get_rid()));
}
return memnew(RID);
}
void godot_icall_RID_Dtor(RID *p_ptr) {
ERR_FAIL_NULL(p_ptr);
memdelete(p_ptr);
}
uint32_t godot_icall_RID_get_id(RID *p_ptr) {
return p_ptr->get_id();
}
void godot_register_rid_icalls() {
GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_Ctor", godot_icall_RID_Ctor);
GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_Dtor", godot_icall_RID_Dtor);
GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_get_id", godot_icall_RID_get_id);
}
#endif // MONO_GLUE_ENABLED

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