godot/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs
Ignacio Roldán Etcheverry 50b603c7dc C#: Begin move to .NET Core
We're targeting .NET 5 for now to make development easier while
.NET 6 is not yet released.

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

Assembly unloading is not implemented yet. As such, many Godot
resources are leaked at exit. This will be re-implemented later
together with assembly hot-reloading.
2021-09-22 08:27:12 +02:00

196 lines
6.7 KiB
C#

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