1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-12 13:20:55 +00:00

C#/netcore: Add base desktop game export implementation

This base implementation is still very barebones but it defines the path
for how exporting will work (at least when embedding the .NET runtime).

Many manual steps are still needed, which should be automatized in the
future. For example, in addition to the API assemblies, now you also
need to copy the GodotPlugins assembly to each game project.
This commit is contained in:
Ignacio Roldán Etcheverry
2021-12-28 23:25:16 +01:00
parent f88d8902cf
commit 88e367a406
51 changed files with 1568 additions and 1224 deletions

View File

@@ -4,6 +4,7 @@ using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Loader;
using Godot.Bridge;
using Godot.NativeInterop;
namespace GodotPlugins
@@ -13,6 +14,7 @@ namespace GodotPlugins
private static readonly List<AssemblyName> SharedAssemblies = new();
private static readonly Assembly CoreApiAssembly = typeof(Godot.Object).Assembly;
private static Assembly? _editorApiAssembly;
private static Assembly? _projectAssembly;
private static readonly AssemblyLoadContext MainLoadContext =
AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()) ??
@@ -20,67 +22,59 @@ namespace GodotPlugins
// 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)
// ReSharper disable once UnusedMember.Local
private static unsafe godot_bool InitializeFromEngine(godot_bool editorHint,
PluginsCallbacks* pluginsCallbacks, ManagedCallbacks* managedCallbacks)
{
try
{
SharedAssemblies.Add(CoreApiAssembly.GetName());
NativeLibrary.SetDllImportResolver(CoreApiAssembly, GodotDllImportResolver.OnResolveDllImport);
if (editorHint.ToBool())
{
_editorApiAssembly = Assembly.Load("GodotSharpEditor");
SharedAssemblies.Add(_editorApiAssembly.GetName());
NativeLibrary.SetDllImportResolver(_editorApiAssembly, GodotDllImportResolver.OnResolveDllImport);
}
NativeLibrary.SetDllImportResolver(CoreApiAssembly, OnResolveDllImport);
*pluginsCallbacks = new()
{
LoadProjectAssemblyCallback = &LoadProjectAssembly,
LoadToolsAssemblyCallback = &LoadToolsAssembly,
};
*managedCallbacks = Godot.Bridge.ManagedCallbacks.Create();
*managedCallbacks = 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
private 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)
private static unsafe godot_bool LoadProjectAssembly(char* nAssemblyPath)
{
try
{
if (_projectAssembly != null)
return godot_bool.True; // Already loaded
string assemblyPath = new(nAssemblyPath);
var assembly = LoadPlugin(assemblyPath);
_projectAssembly = 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 });
ScriptManagerBridge.LookupScriptsInAssembly(_projectAssembly);
return godot_bool.True;
}
@@ -92,7 +86,7 @@ namespace GodotPlugins
}
[UnmanagedCallersOnly]
internal static unsafe IntPtr LoadToolsAssembly(char* nAssemblyPath)
private static unsafe IntPtr LoadToolsAssembly(char* nAssemblyPath)
{
try
{
@@ -103,7 +97,7 @@ namespace GodotPlugins
var assembly = LoadPlugin(assemblyPath);
NativeLibrary.SetDllImportResolver(assembly, OnResolveDllImport);
NativeLibrary.SetDllImportResolver(assembly, GodotDllImportResolver.OnResolveDllImport);
var method = assembly.GetType("GodotTools.GodotSharpEditor")?
.GetMethod("InternalCreateInstance",
@@ -140,58 +134,5 @@ namespace GodotPlugins
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
}
}