1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-26 15:46:23 +00:00

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.
This commit is contained in:
Ignacio Roldán Etcheverry
2021-09-12 20:23:05 +02:00
parent 513ee857a9
commit f9a67ee9da
96 changed files with 2475 additions and 5615 deletions

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,197 @@
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(IntPtr.Zero);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return Linux.dlopen(IntPtr.Zero, Linux.RTLD_LAZY);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return MacOS.dlopen(IntPtr.Zero, 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(IntPtr path, int mode);
}
private static class Linux
{
// libdl.so was resulting in DllNotFoundException, for some reason...
// libcoreclr.so should work with both CoreCLR and the .NET Core version of Mono.
private const string SystemLibrary = "libcoreclr.so";
public const int RTLD_LAZY = 1;
[DllImport(SystemLibrary)]
public static extern IntPtr dlopen(IntPtr path, int mode);
}
private static class Win32
{
private const string SystemLibrary = "Kernel32.dll";
[DllImport(SystemLibrary)]
public static extern IntPtr GetModuleHandle(IntPtr 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

@@ -95,7 +95,7 @@ namespace Godot.Collections
public Array Duplicate(bool deep = false)
{
godot_array newArray;
NativeFuncs.godotsharp_array_duplicate(ref NativeValue, deep, out newArray);
NativeFuncs.godotsharp_array_duplicate(ref NativeValue, deep.ToGodotBool(), out newArray);
return CreateTakingOwnershipOfDisposableValue(newArray);
}

View File

@@ -6,118 +6,167 @@ namespace Godot.Bridge
{
internal static class CSharpInstanceBridge
{
private static unsafe void Call(IntPtr godotObjectGCHandle, godot_string_name* method,
godot_variant** args, int argCount, godot_variant_call_error* ref_callError, godot_variant* r_ret)
[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)
{
// Performance is not critical here as this will be replaced with source generators.
var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
if (godotObject == null)
try
{
*r_ret = default;
(*ref_callError).error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL;
return;
// 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);
bool methodInvoked = godotObject.InternalGodotScriptCall(methodStr, args, argCount, out godot_variant retValue);
if (!methodInvoked)
{
*ret = default;
// This is important, as it tells Object::call that no method was called.
// Otherwise, it would prevent Object::call from calling native methods.
(*refCallError).error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD;
return false.ToGodotBool();
}
*ret = retValue;
return true.ToGodotBool();
}
using godot_string dest = default;
NativeFuncs.godotsharp_string_name_as_string(&dest, method);
string methodStr = Marshaling.mono_string_from_godot(dest);
bool methodInvoked = godotObject.InternalGodotScriptCall(methodStr, args, argCount, out godot_variant outRet);
if (!methodInvoked)
catch (Exception e)
{
*r_ret = default;
// This is important, as it tells Object::call that no method was called.
// Otherwise, it would prevent Object::call from calling native methods.
(*ref_callError).error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD;
return;
ExceptionUtils.DebugPrintUnhandledException(e);
*ret = default;
return false.ToGodotBool();
}
*r_ret = outRet;
}
private static unsafe bool Set(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* value)
[UnmanagedCallersOnly]
internal static unsafe godot_bool Set(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* value)
{
// Performance is not critical here as this will be replaced with source generators.
var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
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();
if (godotObject == null)
throw new InvalidOperationException();
var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_string_name_new_copy(name));
var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_string_name_new_copy(name));
if (godotObject.InternalGodotScriptSetFieldOrPropViaReflection(nameManaged.ToString(), value))
return true;
if (godotObject.InternalGodotScriptSetFieldOrPropViaReflection(nameManaged.ToString(), value))
return true.ToGodotBool();
object valueManaged = Marshaling.variant_to_mono_object(value);
object valueManaged = Marshaling.variant_to_mono_object(value);
return godotObject._Set(nameManaged, valueManaged);
return godotObject._Set(nameManaged, valueManaged).ToGodotBool();
}
catch (Exception e)
{
ExceptionUtils.DebugPrintUnhandledException(e);
return false.ToGodotBool();
}
}
private static unsafe bool Get(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* r_retValue)
[UnmanagedCallersOnly]
internal static unsafe godot_bool Get(IntPtr godotObjectGCHandle, godot_string_name* name,
godot_variant* outRet)
{
// 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 outRet))
try
{
*r_retValue = outRet;
return true;
// 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();
}
object ret = godotObject._Get(nameManaged);
if (ret == null)
catch (Exception e)
{
*r_retValue = default;
return false;
ExceptionUtils.DebugPrintUnhandledException(e);
*outRet = default;
return false.ToGodotBool();
}
*r_retValue = Marshaling.mono_object_to_variant(ret);
return true;
}
private static void CallDispose(IntPtr godotObjectGCHandle, bool okIfNull)
[UnmanagedCallersOnly]
internal static void CallDispose(IntPtr godotObjectGCHandle, godot_bool okIfNull)
{
var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
try
{
var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
if (okIfNull)
godotObject?.Dispose();
else
godotObject!.Dispose();
if (okIfNull.ToBool())
godotObject?.Dispose();
else
godotObject!.Dispose();
}
catch (Exception e)
{
ExceptionUtils.DebugPrintUnhandledException(e);
}
}
private static unsafe void CallToString(IntPtr godotObjectGCHandle, godot_string* r_res, bool* r_valid)
[UnmanagedCallersOnly]
internal static unsafe void CallToString(IntPtr godotObjectGCHandle, godot_string* outRes, godot_bool* outValid)
{
var self = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
if (self == null)
try
{
*r_res = default;
*r_valid = false;
return;
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();
}
var resultStr = self.ToString();
if (resultStr == null)
catch (Exception e)
{
*r_res = default;
*r_valid = false;
return;
ExceptionUtils.DebugPrintUnhandledException(e);
*outRes = default;
*outValid = false.ToGodotBool();
}
*r_res = Marshaling.mono_string_to_godot(resultStr);
*r_valid = true;
}
}
}

View File

@@ -1,11 +1,22 @@
using System;
using System.Runtime.InteropServices;
using Godot.NativeInterop;
namespace Godot.Bridge
{
internal static class GCHandleBridge
{
private static void FreeGCHandle(IntPtr gcHandlePtr)
=> GCHandle.FromIntPtr(gcHandlePtr).Free();
[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

@@ -27,99 +27,150 @@ namespace Godot.Bridge
}
};
[UnmanagedCallersOnly]
internal static void FrameCallback()
{
Dispatcher.DefaultGodotTaskScheduler?.Activate();
try
{
Dispatcher.DefaultGodotTaskScheduler?.Activate();
}
catch (Exception e)
{
ExceptionUtils.DebugUnhandledException(e);
}
}
[UnmanagedCallersOnly]
internal static unsafe IntPtr CreateManagedForGodotObjectBinding(godot_string_name* nativeTypeName,
IntPtr godotObject)
{
Type nativeType = TypeGetProxyClass(nativeTypeName);
var obj = (Object)FormatterServices.GetUninitializedObject(nativeType);
try
{
Type nativeType = TypeGetProxyClass(nativeTypeName);
var obj = (Object)FormatterServices.GetUninitializedObject(nativeType);
obj.NativePtr = godotObject;
obj.NativePtr = godotObject;
var ctor = nativeType.GetConstructor(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
null, Type.EmptyTypes, null);
_ = ctor!.Invoke(obj, null);
var ctor = nativeType.GetConstructor(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
null, Type.EmptyTypes, null);
_ = ctor!.Invoke(obj, null);
return GCHandle.ToIntPtr(GCHandle.Alloc(obj));
return GCHandle.ToIntPtr(GCHandle.Alloc(obj));
}
catch (Exception e)
{
ExceptionUtils.DebugPrintUnhandledException(e);
return IntPtr.Zero;
}
}
internal static unsafe void CreateManagedForGodotObjectScriptInstance(IntPtr scriptPtr, IntPtr godotObject,
[UnmanagedCallersOnly]
internal static unsafe godot_bool CreateManagedForGodotObjectScriptInstance(IntPtr scriptPtr,
IntPtr godotObject,
godot_variant** args, int argCount)
{
// 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)
try
{
if (argCount == 0)
// 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)
{
throw new MissingMemberException(
$"Cannot create script instance. The class '{scriptType.FullName}' does not define a parameterless constructor.");
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.");
}
}
else
var parameters = ctor.GetParameters();
int paramCount = parameters.Length;
object[] invokeParams = new object[paramCount];
for (int i = 0; i < paramCount; i++)
{
throw new MissingMemberException(
$"The class '{scriptType.FullName}' does not define a constructor that takes x parameters.");
invokeParams[i] = Marshaling.variant_to_mono_object_of_type(
args[i], parameters[i].ParameterType);
}
ctor.Invoke(obj, invokeParams);
return true.ToGodotBool();
}
var parameters = ctor.GetParameters();
int paramCount = parameters.Length;
object[] invokeParams = new object[paramCount];
for (int i = 0; i < paramCount; i++)
catch (Exception e)
{
invokeParams[i] = Marshaling.variant_to_mono_object_of_type(
args[i], parameters[i].ParameterType);
ExceptionUtils.DebugPrintUnhandledException(e);
return false.ToGodotBool();
}
ctor.Invoke(obj, invokeParams);
}
private static unsafe void GetScriptNativeName(IntPtr scriptPtr, godot_string_name* r_res)
[UnmanagedCallersOnly]
internal static unsafe void GetScriptNativeName(IntPtr scriptPtr, godot_string_name* outRes)
{
// Performance is not critical here as this will be replaced with source generators.
if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType))
try
{
*r_res = default;
return;
// 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);
}
var native = Object.InternalGetClassNativeBase(scriptType);
var field = native?.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static |
BindingFlags.Public | BindingFlags.NonPublic);
if (field == null)
catch (Exception e)
{
*r_res = default;
return;
ExceptionUtils.DebugUnhandledException(e);
*outRes = default;
}
var nativeName = (StringName)field.GetValue(null);
*r_res = NativeFuncs.godotsharp_string_name_new_copy(nativeName.NativeValue);
}
private static void SetGodotObjectPtr(IntPtr gcHandlePtr, IntPtr newPtr)
[UnmanagedCallersOnly]
internal static void SetGodotObjectPtr(IntPtr gcHandlePtr, IntPtr newPtr)
{
var target = (Object)GCHandle.FromIntPtr(gcHandlePtr).Target;
if (target != null)
target.NativePtr = 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)
@@ -152,7 +203,9 @@ namespace Godot.Bridge
return wrapperType;
}
internal static void LookupScriptsInAssembly(Assembly assembly)
// Called from GodotPlugins
// ReSharper disable once UnusedMember.Local
private static void LookupScriptsInAssembly(Assembly assembly)
{
static void LookupScriptForClass(Type type)
{
@@ -208,294 +261,398 @@ namespace Godot.Bridge
}
}
[UnmanagedCallersOnly]
internal static unsafe void RaiseEventSignal(IntPtr ownerGCHandlePtr,
godot_string_name* eventSignalName, godot_variant** args, int argCount, bool* r_ownerIsNull)
godot_string_name* eventSignalName, godot_variant** args, int argCount, godot_bool* outOwnerIsNull)
{
var owner = (Object)GCHandle.FromIntPtr(ownerGCHandlePtr).Target;
if (owner == null)
try
{
*r_ownerIsNull = true;
return;
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();
}
*r_ownerIsNull = false;
owner.InternalRaiseEventSignal(eventSignalName, args, argCount);
}
internal static unsafe void GetScriptSignalList(IntPtr scriptPtr, godot_dictionary* r_retSignals)
[UnmanagedCallersOnly]
internal static unsafe void GetScriptSignalList(IntPtr scriptPtr, godot_dictionary* outRetSignals)
{
// 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)
try
{
// Legacy signals
// Performance is not critical here as this will be replaced with source generators.
using var signals = new Dictionary();
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;
}
*r_retSignals = NativeFuncs.godotsharp_dictionary_new_copy(signals.NativeValue);
}
internal static unsafe bool HasScriptSignal(IntPtr scriptPtr, godot_string* signalName)
{
// 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;
}
// 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;
}
top = top.BaseType;
}
return false;
}
internal static unsafe bool HasMethodUnknownParams(IntPtr scriptPtr, godot_string* method, bool deep)
{
// Performance is not critical here as this will be replaced with source generators.
if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType))
return false;
string methodStr = Marshaling.mono_string_from_godot(*method);
if (deep)
{
Type top = scriptType;
Type native = Object.InternalGetClassNativeBase(scriptType);
Type top = _scriptBridgeMap[scriptPtr];
Type native = Object.InternalGetClassNativeBase(top);
while (top != null && top != native)
{
var methodInfo = top.GetMethod(methodStr,
// 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);
if (methodInfo != null)
return true;
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;
}
return false;
*outRetSignals = NativeFuncs.godotsharp_dictionary_new_copy(signals.NativeValue);
}
else
catch (Exception e)
{
var methodInfo = scriptType.GetMethod(methodStr, BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
return methodInfo != null;
ExceptionUtils.DebugUnhandledException(e);
*outRetSignals = NativeFuncs.godotsharp_dictionary_new();
}
}
internal static bool ScriptIsOrInherits(IntPtr scriptPtr, IntPtr scriptPtrMaybeBase)
[UnmanagedCallersOnly]
internal static unsafe godot_bool HasScriptSignal(IntPtr scriptPtr, godot_string* signalName)
{
if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType))
return false;
try
{
// Performance is not critical here as this will be replaced with source generators.
using var signals = new Dictionary();
if (!_scriptBridgeMap.TryGetValue(scriptPtrMaybeBase, out var maybeBaseType))
return false;
string signalNameStr = Marshaling.mono_string_from_godot(*signalName);
return scriptType == maybeBaseType || maybeBaseType.IsAssignableFrom(scriptType);
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();
}
}
internal static unsafe bool AddScriptBridge(IntPtr scriptPtr, godot_string* scriptPath)
[UnmanagedCallersOnly]
internal static unsafe godot_bool HasMethodUnknownParams(IntPtr scriptPtr, godot_string* method,
godot_bool deep)
{
string scriptPathStr = Marshaling.mono_string_from_godot(*scriptPath);
try
{
// Performance is not critical here as this will be replaced with source generators.
if (!_scriptBridgeMap.TryGetValue(scriptPtr, out var scriptType))
return false.ToGodotBool();
if (!_scriptLookupMap.TryGetValue(scriptPathStr, out var lookupInfo))
return false;
string methodStr = Marshaling.mono_string_from_godot(*method);
_scriptBridgeMap.Add(scriptPtr, lookupInfo.ScriptType);
if (deep.ToBool())
{
Type top = scriptType;
Type native = Object.InternalGetClassNativeBase(scriptType);
return true;
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)
=> _scriptBridgeMap.Remove(scriptPtr);
internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, bool* r_tool,
godot_dictionary* r_rpcFunctionsDest)
{
// Performance is not critical here as this will be replaced with source generators.
var scriptType = _scriptBridgeMap[scriptPtr];
*r_tool = scriptType.GetCustomAttributes(inherit: false)
.OfType<ToolAttribute>()
.Any();
if (!*r_tool && scriptType.IsNested)
try
{
*r_tool = scriptType.DeclaringType?.GetCustomAttributes(inherit: false)
.OfType<ToolAttribute>()
.Any() ?? false;
_scriptBridgeMap.Remove(scriptPtr);
}
if (!*r_tool && scriptType.Assembly.GetName().Name == "GodotTools")
*r_tool = true;
// RPC functions
Dictionary<string, Dictionary> rpcFunctions = new();
Type top = _scriptBridgeMap[scriptPtr];
Type native = Object.InternalGetClassNativeBase(top);
while (top != null && top != native)
catch (Exception e)
{
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 rpcAttr = method.GetCustomAttributes(inherit: false)
.OfType<RPCAttribute>().FirstOrDefault();
if (rpcAttr == null)
continue;
var rpcConfig = new Dictionary();
rpcConfig["rpc_mode"] = (long)rpcAttr.Mode;
rpcConfig["call_local"] = rpcAttr.CallLocal;
rpcConfig["transfer_mode"] = (long)rpcAttr.TransferMode;
rpcConfig["channel"] = rpcAttr.TransferChannel;
rpcFunctions.Add(methodName, rpcConfig);
}
top = top.BaseType;
ExceptionUtils.DebugUnhandledException(e);
}
*r_rpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new_copy(((Dictionary)rpcFunctions).NativeValue);
}
internal static unsafe bool SwapGCHandleForType(IntPtr oldGCHandlePtr, IntPtr* r_newGCHandlePtr,
bool createWeak)
[UnmanagedCallersOnly]
internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_bool* outTool,
godot_dictionary* outRpcFunctionsDest)
{
var oldGCHandle = GCHandle.FromIntPtr(oldGCHandlePtr);
object target = oldGCHandle.Target;
if (target == null)
try
{
oldGCHandle.Free();
*r_newGCHandlePtr = IntPtr.Zero;
return false; // Called after the managed side was collected, so nothing to do here
// 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
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 rpcAttr = method.GetCustomAttributes(inherit: false)
.OfType<RPCAttribute>().FirstOrDefault();
if (rpcAttr == null)
continue;
var rpcConfig = new Dictionary();
rpcConfig["rpc_mode"] = (long)rpcAttr.Mode;
rpcConfig["call_local"] = rpcAttr.CallLocal;
rpcConfig["transfer_mode"] = (long)rpcAttr.TransferMode;
rpcConfig["channel"] = rpcAttr.TransferChannel;
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();
}
}
// Release the current weak handle and replace it with a strong handle.
var newGCHandle = GCHandle.Alloc(target, createWeak ? GCHandleType.Weak : GCHandleType.Normal);
[UnmanagedCallersOnly]
internal static unsafe godot_bool SwapGCHandleForType(IntPtr oldGCHandlePtr, IntPtr* outNewGCHandlePtr,
godot_bool createWeak)
{
try
{
var oldGCHandle = GCHandle.FromIntPtr(oldGCHandlePtr);
oldGCHandle.Free();
*r_newGCHandlePtr = GCHandle.ToIntPtr(newGCHandle);
return true;
object target = oldGCHandle.Target;
if (target == null)
{
oldGCHandle.Free();
*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);
oldGCHandle.Free();
*outNewGCHandlePtr = GCHandle.ToIntPtr(newGCHandle);
return true.ToGodotBool();
}
catch (Exception e)
{
ExceptionUtils.DebugUnhandledException(e);
*outNewGCHandlePtr = IntPtr.Zero;
return false.ToGodotBool();
}
}
}
}

View File

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

View File

@@ -11,38 +11,56 @@ namespace Godot
{
internal static class DelegateUtils
{
internal static bool DelegateEquals(IntPtr delegateGCHandleA, IntPtr delegateGCHandleB)
[UnmanagedCallersOnly]
internal static godot_bool DelegateEquals(IntPtr delegateGCHandleA, IntPtr delegateGCHandleB)
{
var @delegateA = (Delegate)GCHandle.FromIntPtr(delegateGCHandleA).Target;
var @delegateB = (Delegate)GCHandle.FromIntPtr(delegateGCHandleB).Target;
return @delegateA == @delegateB;
try
{
var @delegateA = (Delegate)GCHandle.FromIntPtr(delegateGCHandleA).Target;
var @delegateB = (Delegate)GCHandle.FromIntPtr(delegateGCHandleB).Target;
return (@delegateA == @delegateB).ToGodotBool();
}
catch (Exception e)
{
ExceptionUtils.DebugUnhandledException(e);
return false.ToGodotBool();
}
}
[UnmanagedCallersOnly]
internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, godot_variant** args, uint argc,
godot_variant* ret)
godot_variant* outRet)
{
// TODO: Optimize
var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target;
var managedArgs = new object[argc];
var parameterInfos = @delegate.Method.GetParameters();
var paramsLength = parameterInfos.Length;
if (argc != paramsLength)
try
{
throw new InvalidOperationException(
$"The delegate expects {paramsLength} arguments, but received {argc}.");
}
// TODO: Optimize
var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target;
var managedArgs = new object[argc];
for (uint i = 0; i < argc; i++)
var parameterInfos = @delegate!.Method.GetParameters();
var paramsLength = parameterInfos.Length;
if (argc != paramsLength)
{
throw new InvalidOperationException(
$"The delegate expects {paramsLength} arguments, but received {argc}.");
}
for (uint i = 0; i < argc; i++)
{
managedArgs[i] = Marshaling.variant_to_mono_object_of_type(
args[i], parameterInfos[i].ParameterType);
}
object invokeRet = @delegate.DynamicInvoke(managedArgs);
*outRet = Marshaling.mono_object_to_variant(invokeRet);
}
catch (Exception e)
{
managedArgs[i] = Marshaling.variant_to_mono_object_of_type(
args[i], parameterInfos[i].ParameterType);
ExceptionUtils.DebugPrintUnhandledException(e);
*outRet = default;
}
object invokeRet = @delegate.DynamicInvoke(managedArgs);
*ret = Marshaling.mono_object_to_variant(invokeRet);
}
// TODO: Check if we should be using BindingFlags.DeclaredOnly (would give better reflection performance).

View File

@@ -76,7 +76,7 @@ namespace Godot.Collections
public Dictionary Duplicate(bool deep = false)
{
godot_dictionary newDictionary;
NativeFuncs.godotsharp_dictionary_duplicate(ref NativeValue, deep, out newDictionary);
NativeFuncs.godotsharp_dictionary_duplicate(ref NativeValue, deep.ToGodotBool(), out newDictionary);
return CreateTakingOwnershipOfDisposableValue(newDictionary);
}
@@ -137,7 +137,7 @@ namespace Godot.Collections
{
using godot_variant variantKey = Marshaling.mono_object_to_variant(key);
if (NativeFuncs.godotsharp_dictionary_try_get_value(ref NativeValue, &variantKey,
out godot_variant value))
out godot_variant value).ToBool())
{
using (value)
return Marshaling.variant_to_mono_object(&value);
@@ -165,7 +165,7 @@ namespace Godot.Collections
{
using godot_variant variantKey = Marshaling.mono_object_to_variant(key);
if (NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey))
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);
@@ -185,7 +185,7 @@ namespace Godot.Collections
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);
return NativeFuncs.godotsharp_dictionary_contains_key(ref NativeValue, &variantKey).ToBool();
}
/// <summary>
@@ -432,7 +432,7 @@ namespace Godot.Collections
{
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))
&variantKey, out godot_variant value).ToBool())
{
using (value)
return (TValue)Marshaling.variant_to_mono_object_of_type(&value, TypeOfValues);
@@ -513,7 +513,7 @@ namespace Godot.Collections
public unsafe bool Remove(TKey key)
{
using godot_variant variantKey = Marshaling.mono_object_to_variant(key);
return NativeFuncs.godotsharp_dictionary_remove_key(ref _underlyingDict.NativeValue, &variantKey);
return NativeFuncs.godotsharp_dictionary_remove_key(ref _underlyingDict.NativeValue, &variantKey).ToBool();
}
/// <summary>
@@ -526,7 +526,7 @@ namespace Godot.Collections
{
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);
&variantKey, out godot_variant retValue).ToBool();
using (retValue)
{
@@ -566,7 +566,7 @@ namespace Godot.Collections
{
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);
&variantKey, out godot_variant retValue).ToBool();
using (retValue)
{
@@ -574,7 +574,7 @@ namespace Godot.Collections
return false;
using godot_variant variantValue = Marshaling.mono_object_to_variant(item.Value);
return NativeFuncs.godotsharp_variant_equals(&variantValue, &retValue);
return NativeFuncs.godotsharp_variant_equals(&variantValue, &retValue).ToBool();
}
}
@@ -610,7 +610,7 @@ namespace Godot.Collections
{
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);
&variantKey, out godot_variant retValue).ToBool();
using (retValue)
{
@@ -618,8 +618,11 @@ namespace Godot.Collections
return false;
using godot_variant variantValue = Marshaling.mono_object_to_variant(item.Value);
if (NativeFuncs.godotsharp_variant_equals(&variantValue, &retValue))
return NativeFuncs.godotsharp_dictionary_remove_key(ref _underlyingDict.NativeValue, &variantKey);
if (NativeFuncs.godotsharp_variant_equals(&variantValue, &retValue).ToBool())
{
return NativeFuncs.godotsharp_dictionary_remove_key(
ref _underlyingDict.NativeValue, &variantKey).ToBool();
}
return false;
}

View File

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

View File

@@ -24,14 +24,16 @@ namespace Godot
if (nativeBase)
{
// Native type
var field = typeOfT.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static |
BindingFlags.Public | BindingFlags.NonPublic);
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;
godotsharp_array_filter_godot_objects_by_native(&nativeNameAux, &inputAux, &filteredArray);
NativeFuncs.godotsharp_array_filter_godot_objects_by_native(
&nativeNameAux, &inputAux, &filteredArray);
return Array<T>.CreateTakingOwnershipOfDisposableValue(filteredArray);
}
else
@@ -39,7 +41,7 @@ namespace Godot
// Custom derived type
godot_array inputAux = array.NativeValue;
godot_array filteredArray;
godotsharp_array_filter_godot_objects_by_non_native(&inputAux, &filteredArray);
NativeFuncs.godotsharp_array_filter_godot_objects_by_non_native(&inputAux, &filteredArray);
var filteredArrayWrapped = Array.CreateTakingOwnershipOfDisposableValue(filteredArray);
@@ -62,13 +64,5 @@ namespace Godot
return resWrapped;
}
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern unsafe void godotsharp_array_filter_godot_objects_by_native(godot_string_name* p_native_name,
godot_array* p_input, godot_array* r_output);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern unsafe void godotsharp_array_filter_godot_objects_by_non_native(godot_array* p_input,
godot_array* r_output);
}
}

View File

@@ -30,7 +30,7 @@ namespace Godot
{
using var varBytes = Marshaling.mono_array_to_PackedByteArray(bytes);
using godot_variant ret = default;
NativeFuncs.godotsharp_bytes2var(&varBytes, allowObjects, &ret);
NativeFuncs.godotsharp_bytes2var(&varBytes, allowObjects.ToGodotBool(), &ret);
return Marshaling.variant_to_mono_object(&ret);
}
@@ -561,7 +561,7 @@ namespace Godot
{
using var variant = Marshaling.mono_object_to_variant(var);
using godot_packed_byte_array varBytes = default;
NativeFuncs.godotsharp_var2bytes(&variant, fullObjects, &varBytes);
NativeFuncs.godotsharp_var2bytes(&variant, fullObjects.ToGodotBool(), &varBytes);
using (varBytes)
return Marshaling.PackedByteArray_to_mono_array(&varBytes);
}

View File

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

View File

@@ -1,26 +1,36 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Godot.NativeInterop
{
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_bool
internal static class GodotBoolExtensions
{
public byte _value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe godot_bool ToGodotBool(this bool @bool)
{
return *(godot_bool*)&@bool;
}
public unsafe godot_bool(bool value) => _value = *(byte*)&value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool ToBool(this godot_bool godotBool)
{
return *(bool*)&godotBool;
}
}
public static unsafe implicit operator bool(godot_bool godotBool) => *(bool*)&godotBool._value;
public static implicit operator godot_bool(bool @bool) => new godot_bool(@bool);
// Apparently a struct with a byte is not blittable? It crashes when calling a UnmanagedCallersOnly function ptr.
// ReSharper disable once InconsistentNaming
public enum godot_bool : byte
{
True = 1,
False = 0
}
[StructLayout(LayoutKind.Sequential)]

View File

@@ -16,13 +16,14 @@ namespace Godot.NativeInterop
return null;
IntPtr gcHandlePtr;
bool has_cs_script_instance = false;
godot_bool has_cs_script_instance = false.ToGodotBool();
// First try to get the tied managed instance from a CSharpInstance script instance
unsafe
{
gcHandlePtr = unmanaged_get_script_instance_managed(unmanaged, &has_cs_script_instance);
gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_get_script_instance_managed(
unmanaged, &has_cs_script_instance);
}
if (gcHandlePtr != IntPtr.Zero)
@@ -30,12 +31,12 @@ namespace Godot.NativeInterop
// Otherwise, if the object has a CSharpInstance script instance, return null
if (has_cs_script_instance)
if (has_cs_script_instance.ToBool())
return null;
// If it doesn't have a CSharpInstance script instance, try with native instance bindings
gcHandlePtr = unmanaged_get_instance_binding_managed(unmanaged);
gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_get_instance_binding_managed(unmanaged);
object target = gcHandlePtr != IntPtr.Zero ? GCHandle.FromIntPtr(gcHandlePtr).Target : null;
@@ -44,22 +45,12 @@ namespace Godot.NativeInterop
// If the native instance binding GC handle target was collected, create a new one
gcHandlePtr = unmanaged_instance_binding_create_managed(unmanaged, gcHandlePtr);
gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_instance_binding_create_managed(
unmanaged, gcHandlePtr);
return gcHandlePtr != IntPtr.Zero ? (Object)GCHandle.FromIntPtr(gcHandlePtr).Target : null;
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern unsafe IntPtr unmanaged_get_script_instance_managed(IntPtr p_unmanaged,
bool* r_has_cs_script_instance);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern IntPtr unmanaged_get_instance_binding_managed(IntPtr p_unmanaged);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern IntPtr unmanaged_instance_binding_create_managed(IntPtr p_unmanaged,
IntPtr oldGCHandlePtr);
public static void TieManagedToUnmanaged(Object managed, IntPtr unmanaged,
StringName nativeName, bool refCounted, Type type, Type nativeType)
{
@@ -70,30 +61,22 @@ namespace Godot.NativeInterop
unsafe
{
godot_string_name nativeNameAux = nativeName.NativeValue;
internal_tie_native_managed_to_unmanaged(GCHandle.ToIntPtr(gcHandle), unmanaged,
&nativeNameAux, refCounted);
NativeFuncs.godotsharp_internal_tie_native_managed_to_unmanaged(
GCHandle.ToIntPtr(gcHandle), unmanaged, &nativeNameAux, refCounted.ToGodotBool());
}
}
else
{
IntPtr scriptPtr = internal_new_csharp_script();
IntPtr scriptPtr = NativeFuncs.godotsharp_internal_new_csharp_script();
ScriptManagerBridge.AddScriptBridgeWithType(scriptPtr, type);
// IMPORTANT: This must be called after AddScriptWithTypeBridge
internal_tie_user_managed_to_unmanaged(GCHandle.ToIntPtr(gcHandle), unmanaged,
scriptPtr, refCounted);
NativeFuncs.godotsharp_internal_tie_user_managed_to_unmanaged(
GCHandle.ToIntPtr(gcHandle), unmanaged, scriptPtr, refCounted.ToGodotBool());
}
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern unsafe void internal_tie_native_managed_to_unmanaged(IntPtr gcHandleIntPtr,
IntPtr unmanaged, godot_string_name* nativeName, bool refCounted);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_tie_user_managed_to_unmanaged(IntPtr gcHandleIntPtr,
IntPtr unmanaged, IntPtr scriptPtr, bool refCounted);
public static void TieManagedToUnmanagedWithPreSetup(Object managed, IntPtr unmanaged,
Type type, Type nativeType)
{
@@ -101,16 +84,10 @@ namespace Godot.NativeInterop
return;
var strongGCHandle = GCHandle.Alloc(managed, GCHandleType.Normal);
internal_tie_managed_to_unmanaged_with_pre_setup(GCHandle.ToIntPtr(strongGCHandle), unmanaged);
NativeFuncs.godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup(
GCHandle.ToIntPtr(strongGCHandle), unmanaged);
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_tie_managed_to_unmanaged_with_pre_setup(
IntPtr gcHandleIntPtr, IntPtr unmanaged);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern IntPtr internal_new_csharp_script();
public static unsafe Object EngineGetSingleton(string name)
{
using godot_string src = Marshaling.mono_string_to_godot(name);

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
@@ -420,44 +421,18 @@ namespace Godot.NativeInterop
if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>))
{
// TODO: Validate key and value types are compatible with Variant
#if NET
Collections.IGenericGodotDictionary genericGodotDictionary =
IDictionaryToGenericGodotDictionary((dynamic)p_obj);
#else
var genericArguments = type.GetGenericArguments();
var godotDict = new Collections.Dictionary();
// With .NET Standard we need a package reference for Microsoft.CSharp in order to
// use dynamic, so we have this workaround for now until we switch to .NET 5/6.
var method = typeof(Marshaling).GetMethod(nameof(IDictionaryToGenericGodotDictionary),
BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly)!
.MakeGenericMethod(genericArguments[0], genericArguments[1]);
foreach (KeyValuePair<object, object> entry in (IDictionary)p_obj)
godotDict.Add(entry.Key, entry.Value);
var genericGodotDictionary = (Collections.IGenericGodotDictionary)method
.Invoke(null, new[] { p_obj });
#endif
var godotDict = genericGodotDictionary.UnderlyingDictionary;
if (godotDict == null)
return new godot_variant();
return VariantUtils.CreateFromDictionary(godotDict.NativeValue);
}
if (genericTypeDefinition == typeof(System.Collections.Generic.List<>))
{
// TODO: Validate element type is compatible with Variant
#if NET
var nativeGodotArray =
(godot_array)mono_array_to_Array(System.Runtime.InteropServices.CollectionsMarshal.AsSpan((dynamic)p_obj));
#else
// With .NET Standard we need a package reference for Microsoft.CSharp in order to
// use dynamic, so we have this workaround for now until we switch to .NET 5/6.
// Also CollectionsMarshal.AsSpan is not available with .NET Standard.
var collection = (System.Collections.ICollection)p_obj;
var array = new object[collection.Count];
collection.CopyTo(array, 0);
var nativeGodotArray = mono_array_to_Array(array);
#endif
var nativeGodotArray = mono_array_to_Array((IList)p_obj);
return VariantUtils.CreateFromArray(&nativeGodotArray);
}
}
@@ -478,9 +453,6 @@ namespace Godot.NativeInterop
}
}
private static Collections.Dictionary<TKey, TValue> IDictionaryToGenericGodotDictionary<TKey, TValue>
(IDictionary<TKey, TValue> dictionary) => new(dictionary);
public static unsafe string variant_to_mono_string(godot_variant* p_var)
{
switch ((*p_var)._type)
@@ -855,7 +827,7 @@ namespace Godot.NativeInterop
switch ((*p_var)._type)
{
case Variant.Type.Bool:
return (bool)(*p_var)._data._bool;
return (*p_var)._data._bool.ToBool();
case Variant.Type.Int:
return (*p_var)._data._int;
case Variant.Type.Float:
@@ -1058,7 +1030,7 @@ namespace Godot.NativeInterop
godot_string_name name;
if (NativeFuncs.godotsharp_callable_get_data_for_marshalling(
p_callable, &delegateGCHandle, &godotObject, &name))
p_callable, &delegateGCHandle, &godotObject, &name).ToBool())
{
if (delegateGCHandle != IntPtr.Zero)
{
@@ -1141,15 +1113,37 @@ namespace Godot.NativeInterop
return ret;
}
public static godot_array mono_array_to_Array(Span<object> p_array)
public static godot_array mono_array_to_Array(object[] p_array)
{
if (p_array.IsEmpty)
int length = p_array.Length;
if (length == 0)
return NativeFuncs.godotsharp_array_new();
using var array = new Collections.Array();
array.Resize(p_array.Length);
array.Resize(length);
for (int i = 0; i < p_array.Length; i++)
for (int i = 0; i < length; i++)
array[i] = p_array[i];
godot_array src = array.NativeValue;
unsafe
{
return NativeFuncs.godotsharp_array_new_copy(&src);
}
}
public static godot_array mono_array_to_Array(IList p_array)
{
int length = p_array.Count;
if (length == 0)
return NativeFuncs.godotsharp_array_new();
using var array = new Collections.Array();
array.Resize(length);
for (int i = 0; i < length; i++)
array[i] = p_array[i];
godot_array src = array.NativeValue;

View File

@@ -20,21 +20,62 @@ namespace Godot.NativeInterop
public static extern IntPtr godotsharp_method_bind_get_method(ref godot_string_name p_classname,
char* p_methodname);
#if NET
[DllImport(GodotDllName)]
public static extern delegate* unmanaged<IntPtr> godotsharp_get_class_constructor(ref godot_string_name p_classname);
#else
// Workaround until we switch to .NET 5/6
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_get_class_constructor(ref godot_string_name p_classname);
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_invoke_class_constructor(IntPtr p_creation_func);
#endif
public static extern delegate* unmanaged<IntPtr> godotsharp_get_class_constructor(
ref godot_string_name p_classname);
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_engine_get_singleton(godot_string* p_name);
[DllImport(GodotDllName)]
internal static extern void godotsharp_internal_object_disposed(IntPtr ptr);
[DllImport(GodotDllName)]
internal static extern void godotsharp_internal_refcounted_disposed(IntPtr ptr, godot_bool isFinalizer);
[DllImport(GodotDllName)]
internal static extern void godotsharp_internal_object_connect_event_signal(IntPtr obj,
godot_string_name* eventSignal);
[DllImport(GodotDllName)]
internal static extern Error godotsharp_internal_signal_awaiter_connect(IntPtr source,
ref godot_string_name signal,
IntPtr target, IntPtr awaiterHandlePtr);
[DllImport(GodotDllName)]
public static extern void godotsharp_internal_tie_native_managed_to_unmanaged(IntPtr gcHandleIntPtr,
IntPtr unmanaged, godot_string_name* nativeName, godot_bool refCounted);
[DllImport(GodotDllName)]
public static extern void godotsharp_internal_tie_user_managed_to_unmanaged(IntPtr gcHandleIntPtr,
IntPtr unmanaged, IntPtr scriptPtr, godot_bool refCounted);
[DllImport(GodotDllName)]
public static extern void godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup(
IntPtr gcHandleIntPtr, IntPtr unmanaged);
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_internal_unmanaged_get_script_instance_managed(IntPtr p_unmanaged,
godot_bool* r_has_cs_script_instance);
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_internal_unmanaged_get_instance_binding_managed(IntPtr p_unmanaged);
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_internal_unmanaged_instance_binding_create_managed(IntPtr p_unmanaged,
IntPtr oldGCHandlePtr);
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_internal_new_csharp_script();
[DllImport(GodotDllName)]
public static extern void godotsharp_array_filter_godot_objects_by_native(godot_string_name* p_native_name,
godot_array* p_input, godot_array* r_output);
[DllImport(GodotDllName)]
public static extern void godotsharp_array_filter_godot_objects_by_non_native(godot_array* p_input,
godot_array* r_output);
[DllImport(GodotDllName)]
public static extern void godotsharp_ref_destroy(ref godot_ref p_instance);
@@ -509,12 +550,12 @@ namespace Godot.NativeInterop
public static extern int godotsharp_node_path_get_subname_count(ref godot_node_path p_self);
[DllImport(GodotDllName)]
public static extern bool godotsharp_node_path_is_absolute(ref godot_node_path p_self);
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, bool p_allow_objects,
public static extern void godotsharp_bytes2var(godot_packed_byte_array* p_bytes, godot_bool p_allow_objects,
godot_variant* r_ret);
[DllImport(GodotDllName)]
@@ -578,7 +619,7 @@ namespace Godot.NativeInterop
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, bool fullObjects,
public static extern void godotsharp_var2bytes(godot_variant* what, godot_bool fullObjects,
godot_packed_byte_array* bytes);
[DllImport(GodotDllName)]

View File

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

View File

@@ -263,7 +263,7 @@ namespace Godot
/// <returns>If the <see cref="NodePath"/> is an absolute path.</returns>
public bool IsAbsolute()
{
return NativeFuncs.godotsharp_node_path_is_absolute(ref NativeValue);
return NativeFuncs.godotsharp_node_path_is_absolute(ref NativeValue).ToBool();
}
/// <summary>

View File

@@ -21,14 +21,11 @@ namespace Godot
{
if (NativePtr == IntPtr.Zero)
{
#if NET
unsafe
{
NativePtr = NativeCtor();
}
#else
NativePtr = _gd__invoke_class_constructor(NativeCtor);
#endif
InteropUtils.TieManagedToUnmanaged(this, NativePtr,
NativeName, refCounted: false, GetType(), _cachedType);
}
@@ -58,7 +55,7 @@ namespace Godot
{
using var eventSignalName = new StringName(eventSignal.Name);
godot_string_name eventSignalNameAux = eventSignalName.NativeValue;
godot_icall_Object_ConnectEventSignal(NativePtr, &eventSignalNameAux);
NativeFuncs.godotsharp_internal_object_connect_event_signal(NativePtr, &eventSignalNameAux);
}
}
@@ -114,14 +111,14 @@ namespace Godot
if (MemoryOwn)
{
MemoryOwn = false;
godot_icall_RefCounted_Disposed(NativePtr, !disposing);
NativeFuncs.godotsharp_internal_refcounted_disposed(NativePtr, (!disposing).ToGodotBool());
}
else
{
godot_icall_Object_Disposed(NativePtr);
NativeFuncs.godotsharp_internal_object_disposed(NativePtr);
}
this.NativePtr = IntPtr.Zero;
NativePtr = IntPtr.Zero;
}
_disposed = true;
@@ -391,7 +388,6 @@ namespace Godot
return methodBind;
}
#if NET
internal static unsafe delegate* unmanaged<IntPtr> ClassDB_get_constructor(StringName type)
{
// for some reason the '??' operator doesn't support 'delegate*'
@@ -402,30 +398,5 @@ namespace Godot
return nativeConstructor;
}
#else
internal static 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 == IntPtr.Zero)
throw new NativeConstructorNotFoundException(type);
return nativeConstructor;
}
internal static IntPtr _gd__invoke_class_constructor(IntPtr ctorFuncPtr)
=> NativeFuncs.godotsharp_invoke_class_constructor(ctorFuncPtr);
#endif
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Object_Disposed(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_RefCounted_Disposed(IntPtr ptr, bool isFinalizer);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern unsafe void godot_icall_Object_ConnectEventSignal(IntPtr obj,
godot_string_name* eventSignal);
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Godot.NativeInterop;
@@ -13,14 +12,10 @@ namespace Godot
public SignalAwaiter(Object source, StringName signal, Object target)
{
godot_icall_SignalAwaiter_connect(Object.GetPtr(source), ref signal.NativeValue,
NativeFuncs.godotsharp_internal_signal_awaiter_connect(Object.GetPtr(source), ref signal.NativeValue,
Object.GetPtr(target), GCHandle.ToIntPtr(GCHandle.Alloc(this)));
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, ref godot_string_name signal,
IntPtr target, IntPtr awaiterHandlePtr);
public bool IsCompleted => _completed;
public void OnCompleted(Action action)
@@ -32,30 +27,38 @@ namespace Godot
public IAwaiter<object[]> GetAwaiter() => this;
internal static unsafe void SignalCallback(IntPtr awaiterGCHandlePtr,
godot_variant** args, int argCount,
bool* r_awaiterIsNull)
[UnmanagedCallersOnly]
internal static unsafe void SignalCallback(IntPtr awaiterGCHandlePtr, godot_variant** args, int argCount,
godot_bool* outAwaiterIsNull)
{
var awaiter = (SignalAwaiter)GCHandle.FromIntPtr(awaiterGCHandlePtr).Target;
if (awaiter == null)
try
{
*r_awaiterIsNull = true;
return;
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;
awaiter._action?.Invoke();
}
catch (Exception e)
{
ExceptionUtils.DebugPrintUnhandledException(e);
*outAwaiterIsNull = false.ToGodotBool();
}
*r_awaiterIsNull = false;
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;
awaiter._action?.Invoke();
}
}
}

View File

@@ -4,7 +4,7 @@
<OutputPath>bin/$(Configuration)</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace>Godot</RootNamespace>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile>
<EnableDefaultItems>false</EnableDefaultItems>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -34,6 +34,7 @@
<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" />
@@ -58,6 +59,7 @@
<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" />

View File

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

View File

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