Main.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Reflection;
  5. using System.Runtime.InteropServices;
  6. using System.Runtime.Loader;
  7. using Godot.NativeInterop;
  8. namespace GodotPlugins
  9. {
  10. public static class Main
  11. {
  12. private static readonly List<AssemblyName> SharedAssemblies = new();
  13. private static readonly Assembly CoreApiAssembly = typeof(Godot.Object).Assembly;
  14. private static Assembly? _editorApiAssembly;
  15. private static readonly AssemblyLoadContext MainLoadContext =
  16. AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()) ??
  17. AssemblyLoadContext.Default;
  18. // Right now we do it this way for simplicity as hot-reload is disabled. It will need to be changed later.
  19. [UnmanagedCallersOnly]
  20. internal static unsafe godot_bool Initialize(godot_bool editorHint,
  21. PluginsCallbacks* pluginsCallbacks, Godot.Bridge.ManagedCallbacks* managedCallbacks)
  22. {
  23. try
  24. {
  25. SharedAssemblies.Add(CoreApiAssembly.GetName());
  26. if (editorHint.ToBool())
  27. {
  28. _editorApiAssembly = Assembly.Load("GodotSharpEditor");
  29. SharedAssemblies.Add(_editorApiAssembly.GetName());
  30. }
  31. NativeLibrary.SetDllImportResolver(CoreApiAssembly, OnResolveDllImport);
  32. *pluginsCallbacks = new()
  33. {
  34. LoadProjectAssemblyCallback = &LoadProjectAssembly,
  35. LoadToolsAssemblyCallback = &LoadToolsAssembly,
  36. };
  37. *managedCallbacks = Godot.Bridge.ManagedCallbacks.Create();
  38. return godot_bool.True;
  39. }
  40. catch (Exception e)
  41. {
  42. Console.Error.WriteLine(e);
  43. *pluginsCallbacks = default;
  44. *managedCallbacks = default;
  45. return false.ToGodotBool();
  46. }
  47. }
  48. [StructLayout(LayoutKind.Sequential)]
  49. internal struct PluginsCallbacks
  50. {
  51. public unsafe delegate* unmanaged<char*, godot_bool> LoadProjectAssemblyCallback;
  52. public unsafe delegate* unmanaged<char*, IntPtr> LoadToolsAssemblyCallback;
  53. }
  54. [UnmanagedCallersOnly]
  55. internal static unsafe godot_bool LoadProjectAssembly(char* nAssemblyPath)
  56. {
  57. try
  58. {
  59. string assemblyPath = new(nAssemblyPath);
  60. var assembly = LoadPlugin(assemblyPath);
  61. var method = CoreApiAssembly.GetType("Godot.Bridge.ScriptManagerBridge")?
  62. .GetMethod("LookupScriptsInAssembly",
  63. BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
  64. if (method == null)
  65. {
  66. throw new MissingMethodException("Godot.Bridge.ScriptManagerBridge",
  67. "LookupScriptsInAssembly");
  68. }
  69. method.Invoke(null, new object[] { assembly });
  70. return godot_bool.True;
  71. }
  72. catch (Exception e)
  73. {
  74. Console.Error.WriteLine(e);
  75. return false.ToGodotBool();
  76. }
  77. }
  78. [UnmanagedCallersOnly]
  79. internal static unsafe IntPtr LoadToolsAssembly(char* nAssemblyPath)
  80. {
  81. try
  82. {
  83. string assemblyPath = new(nAssemblyPath);
  84. if (_editorApiAssembly == null)
  85. throw new InvalidOperationException("The Godot editor API assembly is not loaded");
  86. var assembly = LoadPlugin(assemblyPath);
  87. NativeLibrary.SetDllImportResolver(assembly, OnResolveDllImport);
  88. var method = assembly.GetType("GodotTools.GodotSharpEditor")?
  89. .GetMethod("InternalCreateInstance",
  90. BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
  91. if (method == null)
  92. {
  93. throw new MissingMethodException("GodotTools.GodotSharpEditor",
  94. "InternalCreateInstance");
  95. }
  96. return (IntPtr?)method.Invoke(null, null) ?? IntPtr.Zero;
  97. }
  98. catch (Exception e)
  99. {
  100. Console.Error.WriteLine(e);
  101. return IntPtr.Zero;
  102. }
  103. }
  104. private static Assembly LoadPlugin(string assemblyPath)
  105. {
  106. string assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
  107. var sharedAssemblies = new List<string>();
  108. foreach (var sharedAssembly in SharedAssemblies)
  109. {
  110. string? sharedAssemblyName = sharedAssembly.Name;
  111. if (sharedAssemblyName != null)
  112. sharedAssemblies.Add(sharedAssemblyName);
  113. }
  114. var loadContext = new PluginLoadContext(assemblyPath, sharedAssemblies, MainLoadContext);
  115. return loadContext.LoadFromAssemblyName(new AssemblyName(assemblyName));
  116. }
  117. public static IntPtr OnResolveDllImport(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
  118. {
  119. if (libraryName == "__Internal")
  120. {
  121. if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
  122. {
  123. return Win32.GetModuleHandle(IntPtr.Zero);
  124. }
  125. else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
  126. {
  127. return Linux.dlopen(IntPtr.Zero, Linux.RTLD_LAZY);
  128. }
  129. else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
  130. {
  131. return MacOS.dlopen(IntPtr.Zero, MacOS.RTLD_LAZY);
  132. }
  133. }
  134. return IntPtr.Zero;
  135. }
  136. // ReSharper disable InconsistentNaming
  137. private static class MacOS
  138. {
  139. private const string SystemLibrary = "/usr/lib/libSystem.dylib";
  140. public const int RTLD_LAZY = 1;
  141. [DllImport(SystemLibrary)]
  142. public static extern IntPtr dlopen(IntPtr path, int mode);
  143. }
  144. private static class Linux
  145. {
  146. // libdl.so was resulting in DllNotFoundException, for some reason...
  147. // libcoreclr.so should work with both CoreCLR and the .NET Core version of Mono.
  148. private const string SystemLibrary = "libcoreclr.so";
  149. public const int RTLD_LAZY = 1;
  150. [DllImport(SystemLibrary)]
  151. public static extern IntPtr dlopen(IntPtr path, int mode);
  152. }
  153. private static class Win32
  154. {
  155. private const string SystemLibrary = "Kernel32.dll";
  156. [DllImport(SystemLibrary)]
  157. public static extern IntPtr GetModuleHandle(IntPtr lpModuleName);
  158. }
  159. // ReSharper restore InconsistentNaming
  160. }
  161. }