mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 13:41:03 +00:00 
			
		
		
		
	 4b90d16250
			
		
	
	
		4b90d16250
		
	
	
	
	
		
			
			This commit adds initial support for games exported as NativeAOT shared libraries. At this moment, the NativeAOT runtime is experimental. Additionally, Godot is not trim-safe as it still makes some use of reflection. For the time being, a rd.xml file is needed to prevent code triming: ``` <Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata"> <Application> <Assembly Name="GodotSharp" Dynamic="Required All" /> <Assembly Name="GAME_ASSEMBLY" Dynamic="Required All" /> </Application> </Directives> ``` These are the csproj changes for publishing: ``` <PropertyGroup> <NativeLib>Shared</NativeLib> </PropertyGroup> <ItemGroup> <RdXmlFile Include="rd.xml" /> <PackageReference Include="Microsoft.DotNet.ILCompiler" Version="7.0.0-*" /> </ItemGroup> ``` More info: - https://github.com/dotnet/runtimelab/blob/feature/NativeAOT/docs/using-nativeaot/compiling.md - https://github.com/dotnet/runtimelab/tree/feature/NativeAOT/samples/NativeLibrary - https://github.com/dotnet/runtimelab/blob/feature/NativeAOT/docs/using-nativeaot/rd-xml-format.md
		
			
				
	
	
		
			142 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			142 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Collections.Generic;
 | |
| using System.IO;
 | |
| using System.Reflection;
 | |
| using System.Runtime.InteropServices;
 | |
| using System.Runtime.Loader;
 | |
| using Godot.Bridge;
 | |
| 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 Assembly? _projectAssembly;
 | |
| 
 | |
|         private static readonly AssemblyLoadContext MainLoadContext =
 | |
|             AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()) ??
 | |
|             AssemblyLoadContext.Default;
 | |
| 
 | |
|         private static DllImportResolver? _dllImportResolver;
 | |
| 
 | |
|         // Right now we do it this way for simplicity as hot-reload is disabled. It will need to be changed later.
 | |
|         [UnmanagedCallersOnly]
 | |
|         // ReSharper disable once UnusedMember.Local
 | |
|         private static unsafe godot_bool InitializeFromEngine(IntPtr godotDllHandle, godot_bool editorHint,
 | |
|             PluginsCallbacks* pluginsCallbacks, ManagedCallbacks* managedCallbacks)
 | |
|         {
 | |
|             try
 | |
|             {
 | |
|                 _dllImportResolver = new GodotDllImportResolver(godotDllHandle).OnResolveDllImport;
 | |
| 
 | |
|                 SharedAssemblies.Add(CoreApiAssembly.GetName());
 | |
|                 NativeLibrary.SetDllImportResolver(CoreApiAssembly, _dllImportResolver);
 | |
| 
 | |
|                 if (editorHint.ToBool())
 | |
|                 {
 | |
|                     _editorApiAssembly = Assembly.Load("GodotSharpEditor");
 | |
|                     SharedAssemblies.Add(_editorApiAssembly.GetName());
 | |
|                     NativeLibrary.SetDllImportResolver(_editorApiAssembly, _dllImportResolver);
 | |
|                 }
 | |
| 
 | |
|                 *pluginsCallbacks = new()
 | |
|                 {
 | |
|                     LoadProjectAssemblyCallback = &LoadProjectAssembly,
 | |
|                     LoadToolsAssemblyCallback = &LoadToolsAssembly,
 | |
|                 };
 | |
| 
 | |
|                 *managedCallbacks = ManagedCallbacks.Create();
 | |
| 
 | |
|                 return godot_bool.True;
 | |
|             }
 | |
|             catch (Exception e)
 | |
|             {
 | |
|                 Console.Error.WriteLine(e);
 | |
|                 return false.ToGodotBool();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [StructLayout(LayoutKind.Sequential)]
 | |
|         private struct PluginsCallbacks
 | |
|         {
 | |
|             public unsafe delegate* unmanaged<char*, godot_bool> LoadProjectAssemblyCallback;
 | |
|             public unsafe delegate* unmanaged<char*, IntPtr> LoadToolsAssemblyCallback;
 | |
|         }
 | |
| 
 | |
|         [UnmanagedCallersOnly]
 | |
|         private static unsafe godot_bool LoadProjectAssembly(char* nAssemblyPath)
 | |
|         {
 | |
|             try
 | |
|             {
 | |
|                 if (_projectAssembly != null)
 | |
|                     return godot_bool.True; // Already loaded
 | |
| 
 | |
|                 string assemblyPath = new(nAssemblyPath);
 | |
| 
 | |
|                 _projectAssembly = LoadPlugin(assemblyPath);
 | |
| 
 | |
|                 ScriptManagerBridge.LookupScriptsInAssembly(_projectAssembly);
 | |
| 
 | |
|                 return godot_bool.True;
 | |
|             }
 | |
|             catch (Exception e)
 | |
|             {
 | |
|                 Console.Error.WriteLine(e);
 | |
|                 return false.ToGodotBool();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [UnmanagedCallersOnly]
 | |
|         private 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, _dllImportResolver!);
 | |
| 
 | |
|                 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));
 | |
|         }
 | |
|     }
 | |
| }
 |