From 0933915c64d03a48acff38df2119a40ea2a57919 Mon Sep 17 00:00:00 2001 From: zznty <94796179+zznty@users.noreply.github.com> Date: Thu, 10 Oct 2024 00:38:19 +0700 Subject: [PATCH] add bootstrap and plugins prototype --- .../CringeBootstrap.Abstractions.csproj | 10 + .../ICoreLoadContext.cs | 9 + CringeBootstrap.Abstractions/ICorePlugin.cs | 7 + .../packages.lock.json | 6 + CringeBootstrap/CringeBootstrap.csproj | 25 ++ .../GameDirectoryAssemblyLoadContext.cs | 67 +++ CringeBootstrap/Program.cs | 32 ++ CringeBootstrap/packages.lock.json | 409 ++++++++++++++++++ CringeLauncher.sln | 20 +- CringeLauncher/CringeLauncher.csproj | 39 +- CringeLauncher/ImGuiHandler.cs | 89 ++++ CringeLauncher/Launcher.cs | 48 +- CringeLauncher/NativeMethods.txt | 2 + CringeLauncher/Patches/EosInitPatch.cs | 3 +- .../Patches/IntrospectionPatches.cs | 159 +++++++ CringeLauncher/Patches/RenderHookPatch.cs | 26 ++ CringeLauncher/Patches/SwapChainPatch.cs | 78 ++++ CringeLauncher/Program.cs | 12 - CringeLauncher/packages.lock.json | 138 +++++- .../Abstractions/IRenderComponent.cs | 6 + CringePlugins/CringePlugins.csproj | 29 ++ CringePlugins/Globals.cs | 3 + .../Loader/PluginAssemblyLoadContext.cs | 38 ++ CringePlugins/Loader/PluginInstance.cs | 55 +++ CringePlugins/Loader/PluginMetadata.cs | 21 + CringePlugins/Render/RenderHandler.cs | 48 ++ CringePlugins/Utils/IntrospectionContext.cs | 49 +++ CringePlugins/packages.lock.json | 80 ++++ NuGet.config | 6 + PluginLoader/Compiler/RoslynReferences.cs | 3 +- PluginLoader/PluginLoader.csproj | 5 +- PluginLoader/packages.lock.json | 41 +- 32 files changed, 1487 insertions(+), 76 deletions(-) create mode 100644 CringeBootstrap.Abstractions/CringeBootstrap.Abstractions.csproj create mode 100644 CringeBootstrap.Abstractions/ICoreLoadContext.cs create mode 100644 CringeBootstrap.Abstractions/ICorePlugin.cs create mode 100644 CringeBootstrap.Abstractions/packages.lock.json create mode 100644 CringeBootstrap/CringeBootstrap.csproj create mode 100644 CringeBootstrap/GameDirectoryAssemblyLoadContext.cs create mode 100644 CringeBootstrap/Program.cs create mode 100644 CringeBootstrap/packages.lock.json create mode 100644 CringeLauncher/ImGuiHandler.cs create mode 100644 CringeLauncher/NativeMethods.txt create mode 100644 CringeLauncher/Patches/IntrospectionPatches.cs create mode 100644 CringeLauncher/Patches/RenderHookPatch.cs create mode 100644 CringeLauncher/Patches/SwapChainPatch.cs delete mode 100644 CringeLauncher/Program.cs create mode 100644 CringePlugins/Abstractions/IRenderComponent.cs create mode 100644 CringePlugins/CringePlugins.csproj create mode 100644 CringePlugins/Globals.cs create mode 100644 CringePlugins/Loader/PluginAssemblyLoadContext.cs create mode 100644 CringePlugins/Loader/PluginInstance.cs create mode 100644 CringePlugins/Loader/PluginMetadata.cs create mode 100644 CringePlugins/Render/RenderHandler.cs create mode 100644 CringePlugins/Utils/IntrospectionContext.cs create mode 100644 CringePlugins/packages.lock.json create mode 100644 NuGet.config diff --git a/CringeBootstrap.Abstractions/CringeBootstrap.Abstractions.csproj b/CringeBootstrap.Abstractions/CringeBootstrap.Abstractions.csproj new file mode 100644 index 0000000..0a6adc3 --- /dev/null +++ b/CringeBootstrap.Abstractions/CringeBootstrap.Abstractions.csproj @@ -0,0 +1,10 @@ + + + + net8.0 + enable + enable + true + + + diff --git a/CringeBootstrap.Abstractions/ICoreLoadContext.cs b/CringeBootstrap.Abstractions/ICoreLoadContext.cs new file mode 100644 index 0000000..d65fd94 --- /dev/null +++ b/CringeBootstrap.Abstractions/ICoreLoadContext.cs @@ -0,0 +1,9 @@ +using System.Reflection; + +namespace CringeBootstrap.Abstractions; + +public interface ICoreLoadContext +{ + Assembly? ResolveFromAssemblyName(AssemblyName assemblyName); + nint ResolveUnmanagedDll(string unmanagedDllName); +} \ No newline at end of file diff --git a/CringeBootstrap.Abstractions/ICorePlugin.cs b/CringeBootstrap.Abstractions/ICorePlugin.cs new file mode 100644 index 0000000..6083483 --- /dev/null +++ b/CringeBootstrap.Abstractions/ICorePlugin.cs @@ -0,0 +1,7 @@ +namespace CringeBootstrap.Abstractions; + +public interface ICorePlugin : IDisposable +{ + void Initialize(string[] args); + void Run(); +} \ No newline at end of file diff --git a/CringeBootstrap.Abstractions/packages.lock.json b/CringeBootstrap.Abstractions/packages.lock.json new file mode 100644 index 0000000..807ab82 --- /dev/null +++ b/CringeBootstrap.Abstractions/packages.lock.json @@ -0,0 +1,6 @@ +{ + "version": 1, + "dependencies": { + "net8.0": {} + } +} \ No newline at end of file diff --git a/CringeBootstrap/CringeBootstrap.csproj b/CringeBootstrap/CringeBootstrap.csproj new file mode 100644 index 0000000..2bbb0fd --- /dev/null +++ b/CringeBootstrap/CringeBootstrap.csproj @@ -0,0 +1,25 @@ + + + + Exe + net8.0-windows10.0.19041.0 + enable + enable + true + preview + true + true + true + win-x64 + + + + + + + + + + + + diff --git a/CringeBootstrap/GameDirectoryAssemblyLoadContext.cs b/CringeBootstrap/GameDirectoryAssemblyLoadContext.cs new file mode 100644 index 0000000..7a45136 --- /dev/null +++ b/CringeBootstrap/GameDirectoryAssemblyLoadContext.cs @@ -0,0 +1,67 @@ +using System.Collections.Immutable; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.Loader; +using CringeBootstrap.Abstractions; + +namespace CringeBootstrap; + +public class GameDirectoryAssemblyLoadContext : AssemblyLoadContext, ICoreLoadContext +{ + private static readonly ImmutableHashSet ReferenceAssemblies = ["netstandard"]; + private readonly Dictionary _assemblyNames = []; + + public GameDirectoryAssemblyLoadContext(string dir) : base("CringeBootstrap") + { + var files = Directory.GetFiles(dir, "*.dll"); + foreach (var file in files) + { + if (File.Exists(Path.Join(AppContext.BaseDirectory, Path.GetFileName(file)))) + continue; + + try + { + var name = AssemblyName.GetAssemblyName(file); + + AddOverride(name, file); + } + catch (BadImageFormatException) + { + // if we are trying to load native image + } + } + } + + public void AddOverride(AssemblyName name, string file) + { + var key = name.Name ?? name.FullName[..',']; + + if (key.StartsWith("System.") || ReferenceAssemblies.Contains(key)) + return; + + _assemblyNames.TryAdd(key, file); + } + + public void AddDependencyOverride(string name) + { + AddOverride(new(name), Path.Join(AppContext.BaseDirectory, name + ".dll")); + } + + protected override Assembly? Load(AssemblyName name) + { + var key = name.Name ?? name.FullName[..',']; + + try + { + return _assemblyNames.TryGetValue(key, out var value) ? LoadFromAssemblyPath(value) : null; + } + catch (BadImageFormatException e) + { + Debug.WriteLine(e); + return null; + } + } + + public Assembly? ResolveFromAssemblyName(AssemblyName assemblyName) => Load(assemblyName); + public nint ResolveUnmanagedDll(string unmanagedDllName) => LoadUnmanagedDll(unmanagedDllName); +} \ No newline at end of file diff --git a/CringeBootstrap/Program.cs b/CringeBootstrap/Program.cs new file mode 100644 index 0000000..ea856cd --- /dev/null +++ b/CringeBootstrap/Program.cs @@ -0,0 +1,32 @@ +using System.Diagnostics; +using System.Reflection; +using System.Runtime.Loader; +using CringeBootstrap; +using CringeBootstrap.Abstractions; +using Velopack; + +#if DEBUG +while (!Debugger.IsAttached) + Thread.Sleep(100); +#endif + +VelopackApp.Build().Run(); + +AssemblyLoadContext.Default.Resolving += (loadContext, name) => +{ + Debug.WriteLine($"resolving {name} in {loadContext}"); + return null; +}; + +var dir = Path.GetDirectoryName(args[0])!; +var context = new GameDirectoryAssemblyLoadContext(dir); + +context.AddDependencyOverride("CringeLauncher"); +context.AddDependencyOverride("PluginLoader"); + +var launcher = context.LoadFromAssemblyName(new AssemblyName("CringeLauncher")); + +using var corePlugin = (ICorePlugin) launcher.CreateInstance("CringeLauncher.Launcher")!; + +corePlugin.Initialize(args); +corePlugin.Run(); \ No newline at end of file diff --git a/CringeBootstrap/packages.lock.json b/CringeBootstrap/packages.lock.json new file mode 100644 index 0000000..befcb61 --- /dev/null +++ b/CringeBootstrap/packages.lock.json @@ -0,0 +1,409 @@ +{ + "version": 1, + "dependencies": { + "net8.0-windows10.0.19041": { + "Velopack": { + "type": "Direct", + "requested": "[0.0.630-g9c52e40, )", + "resolved": "0.0.630-g9c52e40", + "contentHash": "4xNKtV+vyZPdlaQMNeen7p3WqGoq7CkVVkneywod1kTM3zKphQQwxo6KESGxsolwImXM8cZk4E1Zu64SYbaJhQ==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "NuGet.Versioning": "6.10.1" + } + }, + "dnlib": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "cKHI720q+zfEEvzklWVGt6B0TH3AibAyJbpUJl4U6KvTP13tycfnqJpkGHRZ/oQ45BTIoIxIwltHIJVDN+iCqQ==" + }, + "ImGui.NET.DirectX": { + "type": "Transitive", + "resolved": "1.91.0.1", + "contentHash": "PpW1gQ9g97h6Hm/h/tkSBOmsBYgGwN8wKNmlJomcQFD/zRY1HPkJZz18XRSfRLHPmH2eeh4hhhZv1KHug7dF9g==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Lib.Harmony.Thin": { + "type": "Transitive", + "resolved": "2.3.3", + "contentHash": "jsaFv7XnWJnyfyvFbkgIkZtV6tWMteNUcDK3idq+3LwPqpTFNxsOv2eKmj4qqP8QR8UynG1Y9AUaC/+dVruMHg==", + "dependencies": { + "MonoMod.Core": "1.1.0", + "System.Text.Json": "8.0.1" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.3.4", + "contentHash": "AxkxcPR+rheX0SmvpLVIGLhOUXAKG56a64kV9VQZ4y9gR9ZmPXnqZvHJnmwLSwzrEP6junUF11vuc+aqo5r68g==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.11.0", + "contentHash": "djf8ujmqYImFgB04UGtcsEhHrzVqzHowS+EEl/Yunc5LdrYrZhGBWUTXoCF0NzYXJxtfuD+UVQarWpvrNc94Qg==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.3.4", + "System.Collections.Immutable": "8.0.0", + "System.Reflection.Metadata": "8.0.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.11.0", + "contentHash": "6XYi2EusI8JT4y2l/F3VVVS+ISoIX9nqHsZRaG6W5aFeJ5BEuBosHfT/ABb73FN0RZ1Z3cj2j7cL28SToJPXOw==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.3.4", + "Microsoft.CodeAnalysis.Common": "[4.11.0]", + "System.Collections.Immutable": "8.0.0", + "System.Reflection.Metadata": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==" + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.ObjectPool": { + "type": "Transitive", + "resolved": "5.0.10", + "contentHash": "pp9tbGqIhdEXL6Q1yJl+zevAJSq4BsxqhS1GXzBvEsEz9DDNu9GLNzgUy2xyFc4YjB4m4Ff2YEWTnvQvVYdkvQ==" + }, + "Mono.Cecil": { + "type": "Transitive", + "resolved": "0.11.5", + "contentHash": "fxfX+0JGTZ8YQeu1MYjbBiK2CYTSzDyEeIixt+yqKKTn7FW8rv7JMY70qevup4ZJfD7Kk/VG/jDzQQTpfch87g==" + }, + "MonoMod.Backports": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "GUAjCrCZEddqHKHFA7Lh61PgTzoKY7gfBShFe0hQe0p8iynHhBK3TWGyRi+QIw/PGfaRPwx6c33CPGFURBVM6g==", + "dependencies": { + "MonoMod.ILHelpers": "1.0.1" + } + }, + "MonoMod.Core": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "Ks8RntZGVcktr2QF/AovTEbuOkrgXz6omjrvT5LRveOIQJuy+IFuEQPBVWu+cSKVIoZD5XkpRFvlVrItgPIrXw==", + "dependencies": { + "Mono.Cecil": "0.11.5", + "MonoMod.Backports": "1.1.0", + "MonoMod.ILHelpers": "1.0.1", + "MonoMod.Utils": "25.0.4" + } + }, + "MonoMod.ILHelpers": { + "type": "Transitive", + "resolved": "1.0.1", + "contentHash": "6djj/Hz+/eTomo1H/sJEJNxBz2ZdhXjvH0MOmyU2xRtbjaIfBQuyVV0zNUbJhMY/8qoWrz7WXfskfFhdaY0afA==" + }, + "MonoMod.Utils": { + "type": "Transitive", + "resolved": "25.0.4", + "contentHash": "cB94MaZtFD9u4clYEFTwM4jGXnJnzXsxYF3yBpMZKHhXOas66tMF2frbdYte023i0MH4C5iRJbDjxHmA4x5VgA==", + "dependencies": { + "Mono.Cecil": "0.11.5", + "MonoMod.Backports": "1.1.0", + "MonoMod.ILHelpers": "1.0.1" + } + }, + "NLog": { + "type": "Transitive", + "resolved": "5.3.4", + "contentHash": "gLy7+O1hEYJXIlcTr1/VWjGXrZTQFZzYNO18IWasD64pNwz0BreV+nHLxWKXWZzERRzoKnsk2XYtwLkTVk7J1A==" + }, + "NuGet.Versioning": { + "type": "Transitive", + "resolved": "6.10.1", + "contentHash": "tovHZ3OlMVmsTdhv2z5nwnnhoA1ryhfJMyVQ9/+iv6d3h78fp230XaGy3K/iVcLwB50DdfNfIsitW97KSOWDFg==" + }, + "protobuf-net": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "kTGOK0E87473sOImOjgZOnz3kTC2aMLffoRWQLYNuBLJnwNNmjanF9IkevZ9Q7yYLeABQfcF3BpeepuMntMVNw==" + }, + "SharpDX": { + "type": "Transitive", + "resolved": "4.2.0-keen-cringe", + "contentHash": "LaJN3h1Gi1FWVdef2I5WtOH9gwzKCBniH0CragarbkN2QheYY6Lqm+91PcOfp1w/4wdVb+k8Kjv3sO393Tphtw==" + }, + "SixLabors.Core": { + "type": "Transitive", + "resolved": "1.0.0-beta0007", + "contentHash": "s9aPl6yxwcvoKRD0u0zjkCISZCCifbUi9/XVFjdvlx5Pt7vRYmGV0anq1EEftUjIEHbEu5aNBipbUSBIV2CE7w==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "SpaceEngineersDedicated.ReferenceAssemblies": { + "type": "Transitive", + "resolved": "1.204.18", + "contentHash": "GT7/9CBMx4jjor41zLOOl87YYM/JdJD8xp9ccXyuhP2oUaz25H3ZmCQuGeAuZNENKru1a/7hZrId4PwlMDGoew==", + "dependencies": { + "SharpDX": "4.2.0-keen-cringe", + "protobuf-net": "1.0.0" + } + }, + "Steamworks.NET": { + "type": "Transitive", + "resolved": "20.1.0", + "contentHash": "+GntwnyJ5tCNvUIaQxv2+ehDvZJzGUqlSB5xRBk1hTj1qqBJ6s4vK/OfGD/jae7aTmXiGSm8wpJORosNtQevJQ==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "WTlRjL6KWIMr/pAaq3rYqh0TJlzpouaQ/W1eelssHgtlwHAH25jXTkUphTYx9HaIIf7XA6qs/0+YhtLEQRkJ+Q==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==" + }, + "System.Configuration.ConfigurationManager": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "JlYi9XVvIREURRUlGMr1F6vOFLk7YSY4p1vHo4kX3tQ0AGrjqlRWHDi66ImHhy6qwXBG3BJ6Y1QlYQ+Qz6Xgww==", + "dependencies": { + "System.Diagnostics.EventLog": "8.0.0", + "System.Security.Cryptography.ProtectedData": "8.0.0" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "fdYxcRjQqTTacKId/2IECojlDSFvp7LP5N78+0z/xH7v/Tuw5ZAxu23Y6PTCRinqyu2ePx+Gn1098NC6jM6d+A==" + }, + "System.Diagnostics.PerformanceCounter": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "lX6DXxtJqVGWw7N/QmVoiCyVQ+Q/Xp+jVXPr3gLK1jJExSn1qmAjJQeb8gnOYeeBTG3E3PmG1nu92eYj/TEjpg==", + "dependencies": { + "System.Configuration.ConfigurationManager": "8.0.0" + } + }, + "System.Formats.Asn1": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "T6fD00dQ3NTbPDy31m4eQUwKW84s03z0N2C8HpOklyeaDgaJPa/TexP4/SkORMSOwc7WhKifnA6Ya33AkzmafA==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "jrK22i5LRzxZCfGb+tGmke2VH7oE0DvcDlJ1HAKYU8cPmD8XnpUT0bYn2Gy98GEhGjtfbR/sxKTVb+dE770pfA==", + "dependencies": { + "System.CodeDom": "8.0.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Private.ServiceModel": { + "type": "Transitive", + "resolved": "4.10.3", + "contentHash": "BcUV7OERlLqGxDXZuIyIMMmk1PbqBblLRbAoigmzIUx/M8A+8epvyPyXRpbgoucKH7QmfYdQIev04Phx2Co08A==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.Extensions.ObjectPool": "5.0.10", + "System.Numerics.Vectors": "4.5.0", + "System.Reflection.DispatchProxy": "4.7.1", + "System.Security.Cryptography.Xml": "6.0.1", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Reflection.DispatchProxy": { + "type": "Transitive", + "resolved": "4.7.1", + "contentHash": "C1sMLwIG6ILQ2bmOT4gh62V6oJlyF4BlHcVMrOoor49p0Ji2tA8QAoqyMcIhAdH6OHKJ8m7BU+r4LK2CUEOKqw==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==", + "dependencies": { + "System.Collections.Immutable": "8.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ==" + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "ynmbW2GjIGg9K1wXmVIRs4IlyDolf0JXNpzFQ8JCVgwM+myUC2JeUggl2PwQig2PNVMegKmN1aAx7WPQ8tI3vA==", + "dependencies": { + "System.Formats.Asn1": "6.0.0" + } + }, + "System.Security.Cryptography.ProtectedData": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg==" + }, + "System.Security.Cryptography.Xml": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "5e5bI28T0x73AwTsbuFP4qSRzthmU2C0Gqgg3AZ3KTxmSyA+Uhk31puA3srdaeWaacVnHhLdJywCzqOiEpbO/w==", + "dependencies": { + "System.Security.AccessControl": "6.0.0", + "System.Security.Cryptography.Pkcs": "6.0.1" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "7AWk2za1hSEJBppe/Lg+uDcam2TrDqwIKa9XcPssSwyjC2xa39EKEGul3CO5RWNF+hMuZG4zlBDrvhBdDTg4lg==", + "dependencies": { + "System.Text.Encodings.Web": "8.0.0" + } + }, + "Torch.SixLabors.ImageSharp": { + "type": "Transitive", + "resolved": "1.0.0-beta6", + "contentHash": "WJ7ocT79HgmuKi0+ltpvXTiMI80UcI3DeS8XSfYwJtTB1tcQws6zLPGuUwra6qe6qRrFfpABeDP3xvHV1rJgfg==", + "dependencies": { + "SixLabors.Core": "1.0.0-beta0007", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "cringebootstrap.abstractions": { + "type": "Project" + }, + "cringelauncher": { + "type": "Project", + "dependencies": { + "CringeBootstrap.Abstractions": "[1.0.0, )", + "CringePlugins": "[1.0.0, )", + "ImGui.NET.DirectX": "[1.91.0.1, )", + "PluginLoader": "[1.0.0, )", + "System.Diagnostics.PerformanceCounter": "[8.0.0, )", + "System.Management": "[8.0.0, )", + "System.Private.ServiceModel": "[4.10.3, )", + "Torch.SixLabors.ImageSharp": "[1.0.0-beta6, )", + "Velopack": "[0.0.630-g9c52e40, )", + "dnlib": "[4.4.0, )" + } + }, + "cringeplugins": { + "type": "Project", + "dependencies": { + "CringeBootstrap.Abstractions": "[1.0.0, )", + "NLog": "[5.3.4, )", + "SpaceEngineersDedicated.ReferenceAssemblies": "[1.*, )", + "dnlib": "[4.4.0, )" + } + }, + "pluginloader": { + "type": "Project", + "dependencies": { + "Lib.Harmony.Thin": "[2.3.3, )", + "Microsoft.CodeAnalysis.CSharp": "[4.11.0, )", + "NLog": "[5.3.4, )", + "SpaceEngineersDedicated.ReferenceAssemblies": "[1.*, )", + "Steamworks.NET": "[20.1.0, )" + } + } + }, + "net8.0-windows10.0.19041/win-x64": { + "ImGui.NET.DirectX": { + "type": "Transitive", + "resolved": "1.91.0.1", + "contentHash": "PpW1gQ9g97h6Hm/h/tkSBOmsBYgGwN8wKNmlJomcQFD/zRY1HPkJZz18XRSfRLHPmH2eeh4hhhZv1KHug7dF9g==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Steamworks.NET": { + "type": "Transitive", + "resolved": "20.1.0", + "contentHash": "+GntwnyJ5tCNvUIaQxv2+ehDvZJzGUqlSB5xRBk1hTj1qqBJ6s4vK/OfGD/jae7aTmXiGSm8wpJORosNtQevJQ==" + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "fdYxcRjQqTTacKId/2IECojlDSFvp7LP5N78+0z/xH7v/Tuw5ZAxu23Y6PTCRinqyu2ePx+Gn1098NC6jM6d+A==" + }, + "System.Diagnostics.PerformanceCounter": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "lX6DXxtJqVGWw7N/QmVoiCyVQ+Q/Xp+jVXPr3gLK1jJExSn1qmAjJQeb8gnOYeeBTG3E3PmG1nu92eYj/TEjpg==", + "dependencies": { + "System.Configuration.ConfigurationManager": "8.0.0" + } + }, + "System.Management": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "jrK22i5LRzxZCfGb+tGmke2VH7oE0DvcDlJ1HAKYU8cPmD8XnpUT0bYn2Gy98GEhGjtfbR/sxKTVb+dE770pfA==", + "dependencies": { + "System.CodeDom": "8.0.0" + } + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ==" + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "ynmbW2GjIGg9K1wXmVIRs4IlyDolf0JXNpzFQ8JCVgwM+myUC2JeUggl2PwQig2PNVMegKmN1aAx7WPQ8tI3vA==", + "dependencies": { + "System.Formats.Asn1": "6.0.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" + } + } + } +} \ No newline at end of file diff --git a/CringeLauncher.sln b/CringeLauncher.sln index 88beb5a..1d54ef4 100644 --- a/CringeLauncher.sln +++ b/CringeLauncher.sln @@ -1,9 +1,15 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CringeLauncher", "CringeLauncher\CringeLauncher.csproj", "{219C897E-452D-49B5-80C4-F3008718C16A}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CringeBootstrap", "CringeBootstrap\CringeBootstrap.csproj", "{219C897E-452D-49B5-80C4-F3008718C16A}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluginLoader", "PluginLoader\PluginLoader.csproj", "{A7C22A74-56EA-4DC2-89AA-A1134BFB8497}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CringeLauncher", "CringeLauncher\CringeLauncher.csproj", "{2A1B48E9-ED82-4EEB-A18A-E4148DFE3A19}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CringeBootstrap.Abstractions", "CringeBootstrap.Abstractions\CringeBootstrap.Abstractions.csproj", "{12AA2BBC-E795-4065-AF4A-9A44AFF69D92}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CringePlugins", "CringePlugins\CringePlugins.csproj", "{16F4D4BF-D363-4C71-8364-ACD0D63B0AE8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -18,5 +24,17 @@ Global {A7C22A74-56EA-4DC2-89AA-A1134BFB8497}.Debug|Any CPU.Build.0 = Debug|Any CPU {A7C22A74-56EA-4DC2-89AA-A1134BFB8497}.Release|Any CPU.ActiveCfg = Release|Any CPU {A7C22A74-56EA-4DC2-89AA-A1134BFB8497}.Release|Any CPU.Build.0 = Release|Any CPU + {2A1B48E9-ED82-4EEB-A18A-E4148DFE3A19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2A1B48E9-ED82-4EEB-A18A-E4148DFE3A19}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A1B48E9-ED82-4EEB-A18A-E4148DFE3A19}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2A1B48E9-ED82-4EEB-A18A-E4148DFE3A19}.Release|Any CPU.Build.0 = Release|Any CPU + {12AA2BBC-E795-4065-AF4A-9A44AFF69D92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {12AA2BBC-E795-4065-AF4A-9A44AFF69D92}.Debug|Any CPU.Build.0 = Debug|Any CPU + {12AA2BBC-E795-4065-AF4A-9A44AFF69D92}.Release|Any CPU.ActiveCfg = Release|Any CPU + {12AA2BBC-E795-4065-AF4A-9A44AFF69D92}.Release|Any CPU.Build.0 = Release|Any CPU + {16F4D4BF-D363-4C71-8364-ACD0D63B0AE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {16F4D4BF-D363-4C71-8364-ACD0D63B0AE8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {16F4D4BF-D363-4C71-8364-ACD0D63B0AE8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {16F4D4BF-D363-4C71-8364-ACD0D63B0AE8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/CringeLauncher/CringeLauncher.csproj b/CringeLauncher/CringeLauncher.csproj index b04d0e7..5a49894 100644 --- a/CringeLauncher/CringeLauncher.csproj +++ b/CringeLauncher/CringeLauncher.csproj @@ -1,40 +1,47 @@  - Exe net8.0-windows10.0.19041.0 + win-x64 enable enable true preview - true true - true - win-x64 - https://nuget.storage.yandexcloud.net/index.json + true - + + + + - all - runtime; build; native; contentfiles; analyzers; buildtransitive + all + runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + - - - - diff --git a/CringeLauncher/ImGuiHandler.cs b/CringeLauncher/ImGuiHandler.cs new file mode 100644 index 0000000..3c10b97 --- /dev/null +++ b/CringeLauncher/ImGuiHandler.cs @@ -0,0 +1,89 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.WindowsAndMessaging; +using CringePlugins.Render; +using ImGuiNET; +using SharpDX.Direct3D11; +using static ImGuiNET.ImGui; + +namespace CringeLauncher; + +internal class ImGuiHandler : IDisposable +{ + private readonly DeviceContext _deviceContext; + private static nint _wndproc; + + public static ImGuiHandler? Instance; + + public RenderTargetView? Rtv; + private readonly RenderHandler _renderHandler; + + public ImGuiHandler(nint windowHandle, Device1 device, DeviceContext deviceContext) + { + _deviceContext = deviceContext; + + _renderHandler = new RenderHandler(); + + CreateContext(); + + ImGui_ImplWin32_Init(windowHandle); + ImGui_ImplDX11_Init(device.NativePointer, deviceContext.NativePointer); + + _wndproc = PInvoke.GetWindowLongPtr((HWND)windowHandle, WINDOW_LONG_PTR_INDEX.GWL_WNDPROC); + + unsafe + { + delegate* unmanaged[Stdcall] wndProcHook = &WndProcHook; + + PInvoke.SetWindowLongPtr((HWND)windowHandle, WINDOW_LONG_PTR_INDEX.GWL_WNDPROC, (nint)wndProcHook); + } + + var io = GetIO(); + + io.ConfigWindowsMoveFromTitleBarOnly = true; + io.ConfigFlags |= ImGuiConfigFlags.DockingEnable | ImGuiConfigFlags.ViewportsEnable; + } + + public void DoRender() + { + if (Rtv is null) + throw new InvalidOperationException("Rtv is null"); + + ImGui_ImplDX11_NewFrame(); + ImGui_ImplWin32_NewFrame(); + NewFrame(); + + _renderHandler.OnFrame(); + + Render(); + + _deviceContext.ClearState(); + _deviceContext.OutputMerger.SetRenderTargets(Rtv); + + ImGui_ImplDX11_RenderDrawData(GetDrawData()); + + UpdatePlatformWindows(); + RenderPlatformWindowsDefault(); + } + + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])] + private static int WndProcHook(HWND hWnd, int msg, nint wParam, nint lParam) + { + var hookResult = ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam); + return hookResult == 0 ? CallWindowProc(_wndproc, hWnd, msg, wParam, lParam) : hookResult; + } + + [DllImport("USER32.dll", ExactSpelling = true, EntryPoint = "CallWindowProcW")] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] + [SupportedOSPlatform("windows5.0")] + private static extern int CallWindowProc(nint lpPrevWndFunc, HWND hWnd, int msg, nint wParam, nint lParam); + + public void Dispose() + { + _deviceContext.Dispose(); + _renderHandler.Dispose(); + } +} \ No newline at end of file diff --git a/CringeLauncher/Launcher.cs b/CringeLauncher/Launcher.cs index a84472e..c3873da 100644 --- a/CringeLauncher/Launcher.cs +++ b/CringeLauncher/Launcher.cs @@ -1,4 +1,8 @@ -using CringeLauncher.Utils; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.Loader; +using CringeBootstrap.Abstractions; +using CringeLauncher.Utils; using HarmonyLib; using Sandbox; using Sandbox.Engine.Multiplayer; @@ -10,6 +14,7 @@ using SpaceEngineers.Game; using SpaceEngineers.Game.Achievements; using SpaceEngineers.Game.GUI; using Steamworks; +using Velopack; using VRage; using VRage.Audio; using VRage.EOS; @@ -26,11 +31,11 @@ using VRageRender; namespace CringeLauncher; -public class Launcher : IDisposable +public class Launcher : ICorePlugin { private const uint AppId = 244850U; private SpaceEngineersGame? _game; - private readonly Harmony _harmony = new("CringeLauncher"); + private readonly Harmony _harmony = new("CringeBootstrap"); public void Initialize(string[] args) { @@ -42,6 +47,11 @@ public class Launcher : IDisposable _harmony.PatchAll(typeof(Launcher).Assembly); SteamAPI.Init(); MyPlugins.LoadPlugins([typeof(PluginLoader.Main).Assembly]); + + #if !DEBUG + CheckUpdates().GetAwaiter().GetResult(); + #endif + PluginLoader.Main.Instance.Splash?.SetText("Initializing game..."); PluginLoader.Main.Instance.Splash?.SetBarValue(0); InitTexts(); @@ -62,6 +72,9 @@ public class Launcher : IDisposable { MyPlatformGameSettings.SIMPLIFIED_SIMULATION_OVERRIDE = false; } + + MyFileSystem.ExePath = Path.GetDirectoryName(args.ElementAtOrDefault(0) ?? Assembly.GetExecutingAssembly().Location)!; + MyFileSystem.RootPath = new DirectoryInfo(MyFileSystem.ExePath).Parent!.FullName; MyInitializer.InvokeBeforeRun(AppId, MyPerGameSettings.BasicGameInfo.ApplicationName, MyVRage.Platform.System.GetRootPath(), MyVRage.Platform.System.GetAppDataPath(),true, 3, () => { @@ -88,6 +101,35 @@ public class Launcher : IDisposable public void Run() => _game?.Run(); + private async Task CheckUpdates() + { + void Report(string s, float p) + { + PluginLoader.Main.Instance.Splash?.Invoke(() => + { + PluginLoader.Main.Instance.Splash?.SetText(s); + PluginLoader.Main.Instance.Splash?.SetBarValue(p); + }); + } + + PluginLoader.Main.Instance.Splash?.SetText("Checking for updates..."); + + var mgr = new UpdateManager("https://the.place/you-host/updates"); + + // check for new version + var newVersion = await mgr.CheckForUpdatesAsync(); + if (newVersion == null) + return; // no update available + + // download new version + await mgr.DownloadUpdatesAsync(newVersion, i => Report("Downloading update...", i / 100f)); + + Report("Restarting for update", 1f); + + // install new version and restart app + mgr.ApplyUpdatesAndRestart(newVersion); + } + #region Keen shit private static void InitThreadPool() diff --git a/CringeLauncher/NativeMethods.txt b/CringeLauncher/NativeMethods.txt new file mode 100644 index 0000000..49c2ec8 --- /dev/null +++ b/CringeLauncher/NativeMethods.txt @@ -0,0 +1,2 @@ +GetWindowLongPtr +SetWindowLongPtr \ No newline at end of file diff --git a/CringeLauncher/Patches/EosInitPatch.cs b/CringeLauncher/Patches/EosInitPatch.cs index e1352d1..c68037b 100644 --- a/CringeLauncher/Patches/EosInitPatch.cs +++ b/CringeLauncher/Patches/EosInitPatch.cs @@ -4,7 +4,8 @@ using HarmonyLib; namespace CringeLauncher.Patches; -[HarmonyPatch] +// eos is disabled +// [HarmonyPatch] public class EosInitPatch { private static MethodInfo TargetMethod() => diff --git a/CringeLauncher/Patches/IntrospectionPatches.cs b/CringeLauncher/Patches/IntrospectionPatches.cs new file mode 100644 index 0000000..bea3b41 --- /dev/null +++ b/CringeLauncher/Patches/IntrospectionPatches.cs @@ -0,0 +1,159 @@ +using System.Diagnostics; +using System.Reflection; +using System.Runtime.Loader; +using CringeBootstrap.Abstractions; +using CringeLauncher.Utils; +using CringePlugins.Utils; +using HarmonyLib; +using VRage.FileSystem; +using VRage.Game; +using VRage.Game.Common; +using VRage.Game.Components; +using VRage.Game.Definitions; +using VRage.ObjectBuilders; +using VRage.ObjectBuilders.Private; +using VRage.Plugins; + +namespace CringeLauncher.Patches; + +[HarmonyPatch] +public static class IntrospectionPatches +{ + [HarmonyPrefix, HarmonyPatch(typeof(Assembly), nameof(Assembly.GetTypes))] + private static bool GetTypesPrefix(Assembly __instance, ref Type[] __result) + { + if (AssemblyLoadContext.GetLoadContext(__instance) is ICoreLoadContext || __instance.FullName?.StartsWith("System.") == true) + return true; + +#if DEBUG + Debugger.Break(); + + __result = []; + return false; +#else + throw new NotSupportedException($"Getting types from {__instance} is not supported"); +#endif + } + + [HarmonyPrefix, HarmonyPatch(typeof(MyPlugins), nameof(MyPlugins.LoadPlugins))] + private static bool LoadPluginsPrefix(List assemblies, List ___m_plugins, List ___m_handleInputPlugins) + { + foreach (var type in assemblies.SelectMany(b => IntrospectionContext.Global.CollectDerivedTypes(b.GetMainModule()))) + { + var instance = Activator.CreateInstance(type); + + if (instance is IPlugin plugin) + ___m_plugins.Add(plugin); + + if (instance is IHandleInputPlugin handleInputPlugin) + ___m_handleInputPlugins.Add(handleInputPlugin); + } + + return false; + } + + [HarmonyPrefix, HarmonyPatch(typeof(MyObjectBuilderSerializerKeen), nameof(MyObjectBuilderSerializerKeen.RegisterFromAssembly))] + private static bool RegisterXmlSerializersPrefix( + MyObjectFactory ___m_objectFactory, Assembly assembly) + { + Register(___m_objectFactory, assembly); + return false; + } + + [HarmonyPrefix, HarmonyPatch(typeof(MyComponentFactory), nameof(MyComponentFactory.RegisterFromAssembly))] + private static bool RegisterComponentsPrefix( + MyObjectFactory ___m_objectFactory, Assembly assembly) + { + Register(___m_objectFactory, assembly); + return false; + } + + [HarmonyTranspiler, + HarmonyPatch(typeof(MyDefinitionManagerBase), nameof(MyDefinitionManagerBase.RegisterTypesFromAssembly))] + private static IEnumerable RegisterDefinitionTypesTranspiler( + IEnumerable instructions) + { + var getTypesMethod = AccessTools.DeclaredMethod(typeof(Assembly), nameof(Assembly.GetTypes)); + var getDefinitionTypesMethod = AccessTools.DeclaredMethod(typeof(IntrospectionPatches), nameof(GetDefinitionTypes)); + return instructions.MethodReplacer(getTypesMethod, getDefinitionTypesMethod); + } + + private static Type[] GetDefinitionTypes(Assembly assembly) + { + return IntrospectionContext.Global.CollectAttributedTypes(assembly.GetMainModule()) + .ToArray(); + } + + [HarmonyTranspiler, + HarmonyPatch(typeof(MyObjectBuilderType), nameof(MyObjectBuilderType.RegisterFromAssembly))] + private static IEnumerable RegisterObjectBuilderTypesTranspiler( + IEnumerable instructions) + { + var getTypesMethod = AccessTools.DeclaredMethod(typeof(Assembly), nameof(Assembly.GetTypes)); + var getDefinitionTypesMethod = AccessTools.DeclaredMethod(typeof(IntrospectionPatches), nameof(GetObjectBuilderTypes)); + return instructions.MethodReplacer(getTypesMethod, getDefinitionTypesMethod); + } + + private static Type[] GetObjectBuilderTypes(Assembly assembly) + { + return IntrospectionContext.Global.CollectDerivedTypes(assembly.GetMainModule()) + .ToArray(); + } + + [HarmonyTranspiler, + HarmonyPatch(typeof(MyComponentTypeFactory), nameof(MyComponentTypeFactory.RegisterFromAssembly), typeof(Assembly))] + private static IEnumerable RegisterComponentTypesTranspiler(IEnumerable instructions) + { + var getTypesMethod = AccessTools.DeclaredMethod(typeof(Assembly), nameof(Assembly.GetTypes)); + var getDefinitionTypesMethod = AccessTools.DeclaredMethod(typeof(IntrospectionPatches), nameof(GetComponentTypes)); + return instructions.MethodReplacer(getTypesMethod, getDefinitionTypesMethod); + } + + private static Type[] GetComponentTypes(Assembly assembly) + { + return IntrospectionContext.Global.CollectDerivedTypes(assembly.GetMainModule()) + .ToArray(); + } + + private static void Register(MyObjectFactory factory, Assembly assembly) + where TAttribute : MyFactoryTagAttribute where TCreatedObjectBase : class + { + foreach (var type in IntrospectionContext.Global.CollectAttributedTypes(assembly.GetMainModule())) + { + foreach (var attribute in type.GetCustomAttributes()) + { + factory.RegisterDescriptor(attribute, type); + } + } + } + + [HarmonyPatch] + private static class GameAssembliesPatch + { + private static IEnumerable TargetMethods() + { + return AccessTools.GetDeclaredMethods(typeof(MyPlugins)) + .Where(b => b.Name.StartsWith("Register") && + b.GetParameters() is [var param] && param.ParameterType == typeof(string)); + } + + private static bool Prefix([HarmonyArgument(0)] string file) + { + var name = AssemblyName.GetAssemblyName(Path.Join(MyFileSystem.ExePath, file)); + + ref var staticAssembly = ref AccessTools.StaticFieldRefAccess(typeof(MyPlugins), name.Name switch + { + "Sandbox.Common" => "m_sandboxAssembly", + "Sandbox.Game" => "m_sandboxGameAssembly", + "SpaceEngineers.Game" => "m_gamePluginAssembly", + "SpaceEngineers.ObjectBuilders" => "m_gameObjBuildersPlugin", + _ => throw new ArgumentOutOfRangeException(null, $"Unknown assembly name: {name}") + }); + + staticAssembly = AssemblyLoadContext.GetLoadContext(typeof(GameAssembliesPatch).Assembly)! + .LoadFromAssemblyName(name); + + return false; + } + } +} \ No newline at end of file diff --git a/CringeLauncher/Patches/RenderHookPatch.cs b/CringeLauncher/Patches/RenderHookPatch.cs new file mode 100644 index 0000000..2fdad4b --- /dev/null +++ b/CringeLauncher/Patches/RenderHookPatch.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Reflection.Emit; +using HarmonyLib; +using VRage.Render11.Sprites; +using VRageRender; + +namespace CringeLauncher.Patches; + +[HarmonyPatch(typeof(MyRender11), nameof(MyRender11.FullDraw))] +public class RenderHookPatch +{ + private static IEnumerable Transpiler(IEnumerable instructions) + { + var placedHook = false; + var method = AccessTools.Method(typeof(MySpritesManager), nameof(MySpritesManager.FrameEnd)); + foreach (var instruction in instructions) + { + if (!placedHook && instruction.Calls(method)) + { + yield return CodeInstruction.CallClosure(() => ImGuiHandler.Instance?.DoRender()); + placedHook = true; + } + yield return instruction; + } + } +} \ No newline at end of file diff --git a/CringeLauncher/Patches/SwapChainPatch.cs b/CringeLauncher/Patches/SwapChainPatch.cs new file mode 100644 index 0000000..c5f704c --- /dev/null +++ b/CringeLauncher/Patches/SwapChainPatch.cs @@ -0,0 +1,78 @@ +using HarmonyLib; +using SharpDX.Direct3D11; +using SharpDX.DXGI; +using VRage.Platform.Windows.Render; +using VRage.Render11.Resources; +using VRageRender; + +namespace CringeLauncher.Patches; + +[HarmonyPatch] +public static class SwapChainPatch +{ + [HarmonyPrefix, HarmonyPatch(typeof(MyPlatformRender), nameof(MyPlatformRender.CreateSwapChain))] + private static bool SwapChainPrefix(nint windowHandle) + { + MyPlatformRender.DisposeSwapChain(); + MyPlatformRender.Log.WriteLine("CreateDeviceInternal create swapchain"); + + if (MyPlatformRender.m_swapchain != null) + return false; + + var chainDescription = new SwapChainDescription + { + BufferCount = 2, + Flags = SwapChainFlags.AllowModeSwitch, + IsWindowed = true, + ModeDescription = MyPlatformRender.GetCurrentModeDescriptor(MyPlatformRender.m_settings) with + { + Format = Format.R8G8B8A8_UNorm + }, + SampleDescription = { + Count = 1, + Quality = 0 + }, + OutputHandle = windowHandle, + Usage = Usage.ShaderInput | Usage.RenderTargetOutput, + SwapEffect = SwapEffect.Discard + }; + + var factory = MyPlatformRender.GetFactory(); + try + { + MyPlatformRender.m_swapchain = new SwapChain(factory, MyPlatformRender.DeviceInstance, chainDescription); + } + catch (Exception) + { + MyPlatformRender.Log.WriteLine("SwapChain factory = " + factory); + MyPlatformRender.Log.WriteLine("SwapChain Device = " + MyPlatformRender.DeviceInstance); + MyPlatformRender.PrintSwapChainDescriptionToLog(chainDescription); + throw; + } + factory.MakeWindowAssociation(windowHandle, WindowAssociationFlags.IgnoreAll); + + ImGuiHandler.Instance ??= new ImGuiHandler(windowHandle, MyRender11.DeviceInstance, MyRender11.RC.DeviceContext); + + return false; + } + + [HarmonyPrefix, HarmonyPatch(typeof(MyBackbuffer), MethodType.Constructor, typeof(SharpDX.Direct3D11.Resource))] + private static bool SwapChainBBPrefix(MyBackbuffer __instance, SharpDX.Direct3D11.Resource swapChainBB) + { + __instance.m_resource = swapChainBB; + __instance.m_rtv = new RenderTargetView(MyRender11.DeviceInstance, swapChainBB, new() + { + Format = Format.R8G8B8A8_UNorm_SRgb, + Dimension = RenderTargetViewDimension.Texture2D, + }); + __instance.m_srv = new ShaderResourceView(MyRender11.DeviceInstance, swapChainBB); + + ImGuiHandler.Instance!.Rtv = new RenderTargetView(MyRender11.DeviceInstance, swapChainBB, new() + { + Format = Format.R8G8B8A8_UNorm, + Dimension = RenderTargetViewDimension.Texture2D, + }); + + return false; + } +} \ No newline at end of file diff --git a/CringeLauncher/Program.cs b/CringeLauncher/Program.cs deleted file mode 100644 index 257b2c2..0000000 --- a/CringeLauncher/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Diagnostics; -using CringeLauncher; - -#if DEBUG -while (!Debugger.IsAttached) - Thread.Sleep(100); -#endif - -using var launcher = new Launcher(); - -launcher.Initialize(args); -launcher.Run(); \ No newline at end of file diff --git a/CringeLauncher/packages.lock.json b/CringeLauncher/packages.lock.json index 6b2776f..ac8a9e0 100644 --- a/CringeLauncher/packages.lock.json +++ b/CringeLauncher/packages.lock.json @@ -2,12 +2,40 @@ "version": 1, "dependencies": { "net8.0-windows10.0.19041": { + "dnlib": { + "type": "Direct", + "requested": "[4.4.0, )", + "resolved": "4.4.0", + "contentHash": "cKHI720q+zfEEvzklWVGt6B0TH3AibAyJbpUJl4U6KvTP13tycfnqJpkGHRZ/oQ45BTIoIxIwltHIJVDN+iCqQ==" + }, + "ImGui.NET.DirectX": { + "type": "Direct", + "requested": "[1.91.0.1, )", + "resolved": "1.91.0.1", + "contentHash": "PpW1gQ9g97h6Hm/h/tkSBOmsBYgGwN8wKNmlJomcQFD/zRY1HPkJZz18XRSfRLHPmH2eeh4hhhZv1KHug7dF9g==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "Krafs.Publicizer": { "type": "Direct", "requested": "[2.2.1, )", "resolved": "2.2.1", "contentHash": "QGI4nMGQbKsuFUUboixVHu4mv3lHB5RejIa7toIlzTmwLkuCYYEpUBJjmy3OpXYyj5dVSZAXVbr4oeMSloE67Q==" }, + "Microsoft.Windows.CsWin32": { + "type": "Direct", + "requested": "[0.3.106, )", + "resolved": "0.3.106", + "contentHash": "Mx5fK7uN6fwLR4wUghs6//HonAnwPBNmC2oonyJVhCUlHS/r6SUS3NkBc3+gaQiv+0/9bqdj1oSCKQFkNI+21Q==", + "dependencies": { + "Microsoft.Windows.SDK.Win32Docs": "0.1.42-alpha", + "Microsoft.Windows.SDK.Win32Metadata": "60.0.34-preview", + "Microsoft.Windows.WDK.Win32Metadata": "0.11.4-experimental" + } + }, "System.Diagnostics.PerformanceCounter": { "type": "Direct", "requested": "[8.0.0, )", @@ -50,6 +78,16 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Velopack": { + "type": "Direct", + "requested": "[0.0.630-g9c52e40, )", + "resolved": "0.0.630-g9c52e40", + "contentHash": "4xNKtV+vyZPdlaQMNeen7p3WqGoq7CkVVkneywod1kTM3zKphQQwxo6KESGxsolwImXM8cZk4E1Zu64SYbaJhQ==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "NuGet.Versioning": "6.10.1" + } + }, "Lib.Harmony.Thin": { "type": "Transitive", "resolved": "2.3.3", @@ -71,21 +109,36 @@ }, "Microsoft.CodeAnalysis.Common": { "type": "Transitive", - "resolved": "4.8.0", - "contentHash": "/jR+e/9aT+BApoQJABlVCKnnggGQbvGh7BKq2/wI1LamxC+LbzhcLj4Vj7gXCofl1n4E521YfF9w0WcASGg/KA==", + "resolved": "4.11.0", + "contentHash": "djf8ujmqYImFgB04UGtcsEhHrzVqzHowS+EEl/Yunc5LdrYrZhGBWUTXoCF0NzYXJxtfuD+UVQarWpvrNc94Qg==", "dependencies": { "Microsoft.CodeAnalysis.Analyzers": "3.3.4", - "System.Collections.Immutable": "7.0.0", - "System.Reflection.Metadata": "7.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.Collections.Immutable": "8.0.0", + "System.Reflection.Metadata": "8.0.0" } }, "Microsoft.CodeAnalysis.CSharp": { "type": "Transitive", - "resolved": "4.8.0", - "contentHash": "+3+qfdb/aaGD8PZRCrsdobbzGs1m9u119SkkJt8e/mk3xLJz/udLtS2T6nY27OTXxBBw10HzAbC8Z9w08VyP/g==", + "resolved": "4.11.0", + "contentHash": "6XYi2EusI8JT4y2l/F3VVVS+ISoIX9nqHsZRaG6W5aFeJ5BEuBosHfT/ABb73FN0RZ1Z3cj2j7cL28SToJPXOw==", "dependencies": { - "Microsoft.CodeAnalysis.Common": "[4.8.0]" + "Microsoft.CodeAnalysis.Analyzers": "3.3.4", + "Microsoft.CodeAnalysis.Common": "[4.11.0]", + "System.Collections.Immutable": "8.0.0", + "System.Reflection.Metadata": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==" + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" } }, "Microsoft.Extensions.ObjectPool": { @@ -93,6 +146,24 @@ "resolved": "5.0.10", "contentHash": "pp9tbGqIhdEXL6Q1yJl+zevAJSq4BsxqhS1GXzBvEsEz9DDNu9GLNzgUy2xyFc4YjB4m4Ff2YEWTnvQvVYdkvQ==" }, + "Microsoft.Windows.SDK.Win32Docs": { + "type": "Transitive", + "resolved": "0.1.42-alpha", + "contentHash": "Z/9po23gUA9aoukirh2ItMU2ZS9++Js9Gdds9fu5yuMojDrmArvY2y+tq9985tR3cxFxpZO1O35Wjfo0khj5HA==" + }, + "Microsoft.Windows.SDK.Win32Metadata": { + "type": "Transitive", + "resolved": "60.0.34-preview", + "contentHash": "TA3DUNi4CTeo+ItTXBnGZFt2159XOGSl0UOlG5vjDj4WHqZjhwYyyUnzOtrbCERiSaP2Hzg7otJNWwOSZgutyA==" + }, + "Microsoft.Windows.WDK.Win32Metadata": { + "type": "Transitive", + "resolved": "0.11.4-experimental", + "contentHash": "bf5MCmUyZf0gBlYQjx9UpRAZWBkRndyt9XicR+UNLvAUAFTZQbu6YaX/sNKZlR98Grn0gydfh/yT4I3vc0AIQA==", + "dependencies": { + "Microsoft.Windows.SDK.Win32Metadata": "60.0.34-preview" + } + }, "Mono.Cecil": { "type": "Transitive", "resolved": "0.11.5", @@ -134,8 +205,13 @@ }, "NLog": { "type": "Transitive", - "resolved": "5.2.8", - "contentHash": "jAIELkWBs1CXFPp986KSGpDFQZHCFccO+LMbKBTTNm42KifaI1mYzFMFQQfuGmGMTrCx0TFPhDjHDE4cLAZWiQ==" + "resolved": "5.3.4", + "contentHash": "gLy7+O1hEYJXIlcTr1/VWjGXrZTQFZzYNO18IWasD64pNwz0BreV+nHLxWKXWZzERRzoKnsk2XYtwLkTVk7J1A==" + }, + "NuGet.Versioning": { + "type": "Transitive", + "resolved": "6.10.1", + "contentHash": "tovHZ3OlMVmsTdhv2z5nwnnhoA1ryhfJMyVQ9/+iv6d3h78fp230XaGy3K/iVcLwB50DdfNfIsitW97KSOWDFg==" }, "protobuf-net": { "type": "Transitive", @@ -169,6 +245,11 @@ "resolved": "20.1.0", "contentHash": "+GntwnyJ5tCNvUIaQxv2+ehDvZJzGUqlSB5xRBk1hTj1qqBJ6s4vK/OfGD/jae7aTmXiGSm8wpJORosNtQevJQ==" }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, "System.CodeDom": { "type": "Transitive", "resolved": "8.0.0", @@ -176,8 +257,8 @@ }, "System.Collections.Immutable": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "dQPcs0U1IKnBdRDBkrCTi1FoajSTBzLcVTpjO4MBCMC7f4pDOIPzgBoX8JjG7X6uZRJ8EBxsi8+DR1JuwjnzOQ==" + "resolved": "8.0.0", + "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==" }, "System.Configuration.ConfigurationManager": { "type": "Transitive", @@ -210,10 +291,10 @@ }, "System.Reflection.Metadata": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "MclTG61lsD9sYdpNz9xsKBzjsmsfCtcMZYXz/IUr2zlhaTaABonlr1ESeompTgM+Xk+IwtGYU7/voh3YWB/fWw==", + "resolved": "8.0.0", + "contentHash": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==", "dependencies": { - "System.Collections.Immutable": "7.0.0" + "System.Collections.Immutable": "8.0.0" } }, "System.Runtime.CompilerServices.Unsafe": { @@ -266,18 +347,41 @@ "System.Text.Encodings.Web": "8.0.0" } }, + "cringebootstrap.abstractions": { + "type": "Project" + }, + "cringeplugins": { + "type": "Project", + "dependencies": { + "CringeBootstrap.Abstractions": "[1.0.0, )", + "NLog": "[5.3.4, )", + "SpaceEngineersDedicated.ReferenceAssemblies": "[1.*, )", + "dnlib": "[4.4.0, )" + } + }, "pluginloader": { "type": "Project", "dependencies": { "Lib.Harmony.Thin": "[2.3.3, )", - "Microsoft.CodeAnalysis.CSharp": "[4.8.0, )", - "NLog": "[5.2.8, )", + "Microsoft.CodeAnalysis.CSharp": "[4.11.0, )", + "NLog": "[5.3.4, )", "SpaceEngineersDedicated.ReferenceAssemblies": "[1.*, )", "Steamworks.NET": "[20.1.0, )" } } }, "net8.0-windows10.0.19041/win-x64": { + "ImGui.NET.DirectX": { + "type": "Direct", + "requested": "[1.91.0.1, )", + "resolved": "1.91.0.1", + "contentHash": "PpW1gQ9g97h6Hm/h/tkSBOmsBYgGwN8wKNmlJomcQFD/zRY1HPkJZz18XRSfRLHPmH2eeh4hhhZv1KHug7dF9g==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "System.Diagnostics.PerformanceCounter": { "type": "Direct", "requested": "[8.0.0, )", diff --git a/CringePlugins/Abstractions/IRenderComponent.cs b/CringePlugins/Abstractions/IRenderComponent.cs new file mode 100644 index 0000000..67f556b --- /dev/null +++ b/CringePlugins/Abstractions/IRenderComponent.cs @@ -0,0 +1,6 @@ +namespace CringePlugins.Abstractions; + +public interface IRenderComponent +{ + void OnFrame(); +} \ No newline at end of file diff --git a/CringePlugins/CringePlugins.csproj b/CringePlugins/CringePlugins.csproj new file mode 100644 index 0000000..e0e6f91 --- /dev/null +++ b/CringePlugins/CringePlugins.csproj @@ -0,0 +1,29 @@ + + + + net8.0 + enable + enable + true + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/CringePlugins/Globals.cs b/CringePlugins/Globals.cs new file mode 100644 index 0000000..ee489ac --- /dev/null +++ b/CringePlugins/Globals.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("CringeLauncher")] \ No newline at end of file diff --git a/CringePlugins/Loader/PluginAssemblyLoadContext.cs b/CringePlugins/Loader/PluginAssemblyLoadContext.cs new file mode 100644 index 0000000..c43264b --- /dev/null +++ b/CringePlugins/Loader/PluginAssemblyLoadContext.cs @@ -0,0 +1,38 @@ +using System.Reflection; +using System.Runtime.Loader; +using CringeBootstrap.Abstractions; + +namespace CringePlugins.Loader; + +internal class PluginAssemblyLoadContext : AssemblyLoadContext +{ + private readonly ICoreLoadContext _parentContext; + private readonly string _entrypointPath; + private readonly AssemblyDependencyResolver _dependencyResolver; + private Assembly? _assembly; + + internal PluginAssemblyLoadContext(ICoreLoadContext parentContext, string entrypointPath) : base($"Plugin Context {Path.GetFileNameWithoutExtension(entrypointPath)}", true) + { + _parentContext = parentContext; + _entrypointPath = entrypointPath; + _dependencyResolver = new(entrypointPath); + } + + public Assembly LoadEntrypoint() => _assembly ??= LoadFromAssemblyPath(_entrypointPath); + + protected override Assembly? Load(AssemblyName assemblyName) + { + if (_dependencyResolver.ResolveAssemblyToPath(assemblyName) is { } path) + return LoadFromAssemblyPath(path); + + return _parentContext.ResolveFromAssemblyName(assemblyName); + } + + protected override nint LoadUnmanagedDll(string unmanagedDllName) + { + if (_dependencyResolver.ResolveUnmanagedDllToPath(unmanagedDllName) is { } path) + return LoadUnmanagedDllFromPath(path); + + return _parentContext.ResolveUnmanagedDll(unmanagedDllName); + } +} \ No newline at end of file diff --git a/CringePlugins/Loader/PluginInstance.cs b/CringePlugins/Loader/PluginInstance.cs new file mode 100644 index 0000000..eb12577 --- /dev/null +++ b/CringePlugins/Loader/PluginInstance.cs @@ -0,0 +1,55 @@ +using System.Runtime.Loader; +using CringeBootstrap.Abstractions; +using CringePlugins.Utils; +using VRage.Plugins; + +namespace CringePlugins.Loader; + +internal sealed class PluginInstance +{ + private readonly string _entrypointPath; + private PluginAssemblyLoadContext? _context; + private IPlugin? _instance; + private IHandleInputPlugin? _handleInputInstance; + public PluginMetadata Metadata { get; } + + public PluginInstance(PluginMetadata metadata, string entrypointPath) + { + _entrypointPath = entrypointPath; + Metadata = metadata; + } + + public PluginInstance(string entrypointPath) : this(PluginMetadata.ReadFromEntrypoint(entrypointPath), entrypointPath) + { + } + + public void Instantiate() + { + if (AssemblyLoadContext.GetLoadContext(typeof(PluginInstance).Assembly) is not ICoreLoadContext parentContext) + throw new NotSupportedException("Plugin instantiation is not supported in this context"); + + _context = new PluginAssemblyLoadContext(parentContext, _entrypointPath); + + var entrypoint = _context.LoadEntrypoint(); + + var plugins = IntrospectionContext.Global.CollectDerivedTypes(entrypoint.GetMainModule()).ToArray(); + + if (plugins.Length == 0) + throw new InvalidOperationException("Entrypoint does not contain any plugins"); + if (plugins.Length > 1) + throw new InvalidOperationException("Entrypoint contains multiple plugins"); + + _instance = (IPlugin) Activator.CreateInstance(plugins[0])!; + _handleInputInstance = _instance as IHandleInputPlugin; + } + + public void RegisterLifetime() + { + if (_instance is null) + throw new InvalidOperationException("Must call Instantiate first"); + + MyPlugins.m_plugins.Add(_instance); + if (_handleInputInstance is not null) + MyPlugins.m_handleInputPlugins.Add(_handleInputInstance); + } +} \ No newline at end of file diff --git a/CringePlugins/Loader/PluginMetadata.cs b/CringePlugins/Loader/PluginMetadata.cs new file mode 100644 index 0000000..df9728f --- /dev/null +++ b/CringePlugins/Loader/PluginMetadata.cs @@ -0,0 +1,21 @@ +using System.Reflection; +using dnlib.DotNet; + +namespace CringePlugins.Loader; + +public record PluginMetadata(string Name, Version Version) +{ + public static PluginMetadata ReadFromEntrypoint(string entrypointPath) + { + var module = ModuleDefMD.Load(entrypointPath); + + var titleAttribute = module.CustomAttributes.Find(typeof(AssemblyTitleAttribute).FullName); + var versionAttribute = module.CustomAttributes.Find(typeof(AssemblyVersionAttribute).FullName); + + var name = titleAttribute?.ConstructorArguments[0].Value as string ?? module.FullName; + if (!Version.TryParse(versionAttribute?.ConstructorArguments[0].Value as string ?? "0.0.0.0", out var version)) + version = new(); + + return new(name, version); + } +} \ No newline at end of file diff --git a/CringePlugins/Render/RenderHandler.cs b/CringePlugins/Render/RenderHandler.cs new file mode 100644 index 0000000..1406144 --- /dev/null +++ b/CringePlugins/Render/RenderHandler.cs @@ -0,0 +1,48 @@ +using System.Collections.Concurrent; +using CringePlugins.Abstractions; +using NLog; + +namespace CringePlugins.Render; + +public sealed class RenderHandler : IDisposable +{ + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + + private static RenderHandler? _current; + public static RenderHandler Current => _current ?? throw new InvalidOperationException("Render is not yet initialized"); + + private readonly ConcurrentBag _components = []; + + internal RenderHandler() + { + _current = this; + } + + public void RegisterComponent(TComponent instance) where TComponent : IRenderComponent + { + _components.Add(new ComponentRegistration(typeof(TComponent), instance)); + } + + internal void OnFrame() + { + foreach (var (instanceType, renderComponent) in _components) + { + try + { + renderComponent.OnFrame(); + } + catch (Exception e) + { + Log.Error(e, "Component {TypeName} failed to render a new frame", instanceType); + } + } + } + + private record ComponentRegistration(Type InstanceType, IRenderComponent Instance); + + public void Dispose() + { + _current = null; + _components.Clear(); + } +} \ No newline at end of file diff --git a/CringePlugins/Utils/IntrospectionContext.cs b/CringePlugins/Utils/IntrospectionContext.cs new file mode 100644 index 0000000..d58158b --- /dev/null +++ b/CringePlugins/Utils/IntrospectionContext.cs @@ -0,0 +1,49 @@ +using System.Reflection; +using dnlib.DotNet; + +namespace CringePlugins.Utils; + +public class IntrospectionContext +{ + public static IntrospectionContext Global { get; } = new(); + + private readonly ModuleContext _context = ModuleDef.CreateModuleContext(); + + public IEnumerable CollectAttributedTypes(Module module, bool allowAbstract = false) where TAttribute : Attribute + { + var moduleDef = ModuleDefMD.Load(module, _context); + + return moduleDef.GetTypes() + .Where(b => b.CustomAttributes.IsDefined(typeof(TAttribute).FullName) && (allowAbstract || !b.IsAbstract)) + .Select(b => module.GetType(b.FullName, true, false)!); + } + + public IEnumerable CollectDerivedTypes(Module module, bool allowAbstract = false) + { + var moduleDef = ModuleDefMD.Load(module, _context); + + var token = moduleDef.ImportAsTypeSig(typeof(T)); + + return moduleDef.GetTypes() + .Where(b => (typeof(T).IsInterface + ? b.Interfaces.Any(i => i.Interface.FullName == token.FullName) + : MatchBaseType(b, token)) && (allowAbstract || !b.IsAbstract)) + .Select(b => module.GetType(b.FullName, true, false)!); + } + + private static bool MatchBaseType(ITypeDefOrRef? defOrRef, TypeSig token) + { + while ((defOrRef = defOrRef.GetBaseType()) != null) + { + if (defOrRef.FullName == token.FullName) + return true; + } + + return false; + } +} + +public static class AssemblyExtensions +{ + public static Module GetMainModule(this Assembly assembly) => assembly.GetModule(assembly.GetName().Name! + ".dll") ?? assembly.GetModules()[0]; +} \ No newline at end of file diff --git a/CringePlugins/packages.lock.json b/CringePlugins/packages.lock.json new file mode 100644 index 0000000..23ff5bf --- /dev/null +++ b/CringePlugins/packages.lock.json @@ -0,0 +1,80 @@ +{ + "version": 1, + "dependencies": { + "net8.0": { + "dnlib": { + "type": "Direct", + "requested": "[4.4.0, )", + "resolved": "4.4.0", + "contentHash": "cKHI720q+zfEEvzklWVGt6B0TH3AibAyJbpUJl4U6KvTP13tycfnqJpkGHRZ/oQ45BTIoIxIwltHIJVDN+iCqQ==" + }, + "Krafs.Publicizer": { + "type": "Direct", + "requested": "[2.2.1, )", + "resolved": "2.2.1", + "contentHash": "QGI4nMGQbKsuFUUboixVHu4mv3lHB5RejIa7toIlzTmwLkuCYYEpUBJjmy3OpXYyj5dVSZAXVbr4oeMSloE67Q==" + }, + "NLog": { + "type": "Direct", + "requested": "[5.3.4, )", + "resolved": "5.3.4", + "contentHash": "gLy7+O1hEYJXIlcTr1/VWjGXrZTQFZzYNO18IWasD64pNwz0BreV+nHLxWKXWZzERRzoKnsk2XYtwLkTVk7J1A==" + }, + "SpaceEngineersDedicated.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.*, )", + "resolved": "1.204.18", + "contentHash": "GT7/9CBMx4jjor41zLOOl87YYM/JdJD8xp9ccXyuhP2oUaz25H3ZmCQuGeAuZNENKru1a/7hZrId4PwlMDGoew==", + "dependencies": { + "SharpDX": "4.2.0-keen-cringe", + "protobuf-net": "1.0.0" + } + }, + "ImGui.NET.DirectX": { + "type": "Transitive", + "resolved": "1.91.0.1", + "contentHash": "PpW1gQ9g97h6Hm/h/tkSBOmsBYgGwN8wKNmlJomcQFD/zRY1HPkJZz18XRSfRLHPmH2eeh4hhhZv1KHug7dF9g==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "protobuf-net": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "kTGOK0E87473sOImOjgZOnz3kTC2aMLffoRWQLYNuBLJnwNNmjanF9IkevZ9Q7yYLeABQfcF3BpeepuMntMVNw==" + }, + "SharpDX": { + "type": "Transitive", + "resolved": "4.2.0-keen-cringe", + "contentHash": "LaJN3h1Gi1FWVdef2I5WtOH9gwzKCBniH0CragarbkN2QheYY6Lqm+91PcOfp1w/4wdVb+k8Kjv3sO393Tphtw==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "cringebootstrap.abstractions": { + "type": "Project" + }, + "cringerender": { + "type": "Project", + "dependencies": { + "ImGui.NET.DirectX": "[1.91.0.1, )", + "SpaceEngineersDedicated.ReferenceAssemblies": "[1.*, )" + } + } + } + } +} \ No newline at end of file diff --git a/NuGet.config b/NuGet.config new file mode 100644 index 0000000..3be5aa7 --- /dev/null +++ b/NuGet.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/PluginLoader/Compiler/RoslynReferences.cs b/PluginLoader/Compiler/RoslynReferences.cs index 0d8ebfa..b64515d 100644 --- a/PluginLoader/Compiler/RoslynReferences.cs +++ b/PluginLoader/Compiler/RoslynReferences.cs @@ -1,4 +1,5 @@ using System.Reflection; +using System.Runtime.Loader; using System.Text; using HarmonyLib; using Microsoft.CodeAnalysis; @@ -101,7 +102,7 @@ public static class RoslynReferences { try { - aRef = Assembly.Load(name); + aRef = AssemblyLoadContext.GetLoadContext(typeof(RoslynReferences).Assembly)!.LoadFromAssemblyName(name); return true; } catch (IOException) diff --git a/PluginLoader/PluginLoader.csproj b/PluginLoader/PluginLoader.csproj index 400dd00..77c4e3b 100644 --- a/PluginLoader/PluginLoader.csproj +++ b/PluginLoader/PluginLoader.csproj @@ -6,7 +6,6 @@ true true true - https://nuget.storage.yandexcloud.net/index.json @@ -18,9 +17,9 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + diff --git a/PluginLoader/packages.lock.json b/PluginLoader/packages.lock.json index a14d556..048b816 100644 --- a/PluginLoader/packages.lock.json +++ b/PluginLoader/packages.lock.json @@ -20,18 +20,21 @@ }, "Microsoft.CodeAnalysis.CSharp": { "type": "Direct", - "requested": "[4.8.0, )", - "resolved": "4.8.0", - "contentHash": "+3+qfdb/aaGD8PZRCrsdobbzGs1m9u119SkkJt8e/mk3xLJz/udLtS2T6nY27OTXxBBw10HzAbC8Z9w08VyP/g==", + "requested": "[4.11.0, )", + "resolved": "4.11.0", + "contentHash": "6XYi2EusI8JT4y2l/F3VVVS+ISoIX9nqHsZRaG6W5aFeJ5BEuBosHfT/ABb73FN0RZ1Z3cj2j7cL28SToJPXOw==", "dependencies": { - "Microsoft.CodeAnalysis.Common": "[4.8.0]" + "Microsoft.CodeAnalysis.Analyzers": "3.3.4", + "Microsoft.CodeAnalysis.Common": "[4.11.0]", + "System.Collections.Immutable": "8.0.0", + "System.Reflection.Metadata": "8.0.0" } }, "NLog": { "type": "Direct", - "requested": "[5.2.8, )", - "resolved": "5.2.8", - "contentHash": "jAIELkWBs1CXFPp986KSGpDFQZHCFccO+LMbKBTTNm42KifaI1mYzFMFQQfuGmGMTrCx0TFPhDjHDE4cLAZWiQ==" + "requested": "[5.3.4, )", + "resolved": "5.3.4", + "contentHash": "gLy7+O1hEYJXIlcTr1/VWjGXrZTQFZzYNO18IWasD64pNwz0BreV+nHLxWKXWZzERRzoKnsk2XYtwLkTVk7J1A==" }, "SpaceEngineersDedicated.ReferenceAssemblies": { "type": "Direct", @@ -56,13 +59,12 @@ }, "Microsoft.CodeAnalysis.Common": { "type": "Transitive", - "resolved": "4.8.0", - "contentHash": "/jR+e/9aT+BApoQJABlVCKnnggGQbvGh7BKq2/wI1LamxC+LbzhcLj4Vj7gXCofl1n4E521YfF9w0WcASGg/KA==", + "resolved": "4.11.0", + "contentHash": "djf8ujmqYImFgB04UGtcsEhHrzVqzHowS+EEl/Yunc5LdrYrZhGBWUTXoCF0NzYXJxtfuD+UVQarWpvrNc94Qg==", "dependencies": { "Microsoft.CodeAnalysis.Analyzers": "3.3.4", - "System.Collections.Immutable": "7.0.0", - "System.Reflection.Metadata": "7.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.Collections.Immutable": "8.0.0", + "System.Reflection.Metadata": "8.0.0" } }, "Mono.Cecil": { @@ -116,22 +118,17 @@ }, "System.Collections.Immutable": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "dQPcs0U1IKnBdRDBkrCTi1FoajSTBzLcVTpjO4MBCMC7f4pDOIPzgBoX8JjG7X6uZRJ8EBxsi8+DR1JuwjnzOQ==" + "resolved": "8.0.0", + "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==" }, "System.Reflection.Metadata": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "MclTG61lsD9sYdpNz9xsKBzjsmsfCtcMZYXz/IUr2zlhaTaABonlr1ESeompTgM+Xk+IwtGYU7/voh3YWB/fWw==", + "resolved": "8.0.0", + "contentHash": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==", "dependencies": { - "System.Collections.Immutable": "7.0.0" + "System.Collections.Immutable": "8.0.0" } }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, "System.Text.Encodings.Web": { "type": "Transitive", "resolved": "8.0.0",