fix imgui and plugin loading

This commit is contained in:
zznty
2024-10-11 12:11:53 +07:00
parent 0933915c64
commit 25981da0fe
10 changed files with 243 additions and 23 deletions

View File

@@ -14,6 +14,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="NLog.Schema" Version="5.3.4" />
<PackageReference Include="Velopack" Version="0.0.630-g9c52e40" /> <PackageReference Include="Velopack" Version="0.0.630-g9c52e40" />
</ItemGroup> </ItemGroup>

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true"
internalLogLevel="Info">
<!-- the targets to write to -->
<targets>
<default-wrapper xsi:type="AsyncWrapper" overflowAction="Block" />
<!-- write logs to file -->
<target xsi:type="File" name="allfile" archiveAboveSize="10485760" fileName="${specialfolder:folder=ApplicationData}/CringeLauncher/logs/log-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${message}" />
<target xsi:type="File" archiveAboveSize="10485760" name="error" fileName="${specialfolder:folder=ApplicationData}/CringeLauncher/logs/log-error-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${message} ${cringe-exception}" />
<target name="debugger" xsi:type="Debugger" layout="${message} ${cringe-exception}"/>
<target xsi:type="ColoredConsole" name="console" layout="[${longdate}] ${uppercase:${level}}: ${message} ${cringe-exception}" />
</targets>
<!-- rules to map from logger name to target -->
<rules>
<logger name="*" level="Info,Error,Fatal" writeTo="allfile,debugger,console" />
<logger name="*" level="Debug" writeTo="debugger" />
<logger name="*" level="Error" writeTo="error,debugger" />
</rules>
</nlog>
</configuration>

View File

@@ -12,17 +12,21 @@ while (!Debugger.IsAttached)
VelopackApp.Build().Run(); VelopackApp.Build().Run();
#if DEBUG
AssemblyLoadContext.Default.Resolving += (loadContext, name) => AssemblyLoadContext.Default.Resolving += (loadContext, name) =>
{ {
Debug.WriteLine($"resolving {name} in {loadContext}"); Debug.WriteLine($"resolving {name} in {loadContext}");
return null; return null;
}; };
#endif
var dir = Path.GetDirectoryName(args[0])!; var dir = Path.GetDirectoryName(args[0])!;
var context = new GameDirectoryAssemblyLoadContext(dir); var context = new GameDirectoryAssemblyLoadContext(dir);
// a list of assemblies which are not in the game binaries but reference them
context.AddDependencyOverride("CringeLauncher"); context.AddDependencyOverride("CringeLauncher");
context.AddDependencyOverride("PluginLoader"); context.AddDependencyOverride("PluginLoader");
context.AddDependencyOverride("CringePlugins");
var launcher = context.LoadFromAssemblyName(new AssemblyName("CringeLauncher")); var launcher = context.LoadFromAssemblyName(new AssemblyName("CringeLauncher"));

View File

@@ -2,6 +2,12 @@
"version": 1, "version": 1,
"dependencies": { "dependencies": {
"net8.0-windows10.0.19041": { "net8.0-windows10.0.19041": {
"NLog.Schema": {
"type": "Direct",
"requested": "[5.3.4, )",
"resolved": "5.3.4",
"contentHash": "eMlf4Y0gx5KycV3cdptEocd9Y9h10fWhRVUKyuY9nF6a55eWJlpKHqCWQbgHjvbC/7cBfF8DsZG8dZbST2fpew=="
},
"Velopack": { "Velopack": {
"type": "Direct", "type": "Direct",
"requested": "[0.0.630-g9c52e40, )", "requested": "[0.0.630-g9c52e40, )",

View File

@@ -18,7 +18,7 @@ internal class ImGuiHandler : IDisposable
public static ImGuiHandler? Instance; public static ImGuiHandler? Instance;
public RenderTargetView? Rtv; public static RenderTargetView? Rtv;
private readonly RenderHandler _renderHandler; private readonly RenderHandler _renderHandler;
public ImGuiHandler(nint windowHandle, Device1 device, DeviceContext deviceContext) public ImGuiHandler(nint windowHandle, Device1 device, DeviceContext deviceContext)
@@ -29,6 +29,11 @@ internal class ImGuiHandler : IDisposable
CreateContext(); CreateContext();
var io = GetIO();
io.ConfigWindowsMoveFromTitleBarOnly = true;
io.ConfigFlags |= ImGuiConfigFlags.DockingEnable | ImGuiConfigFlags.ViewportsEnable;
ImGui_ImplWin32_Init(windowHandle); ImGui_ImplWin32_Init(windowHandle);
ImGui_ImplDX11_Init(device.NativePointer, deviceContext.NativePointer); ImGui_ImplDX11_Init(device.NativePointer, deviceContext.NativePointer);
@@ -40,11 +45,6 @@ internal class ImGuiHandler : IDisposable
PInvoke.SetWindowLongPtr((HWND)windowHandle, WINDOW_LONG_PTR_INDEX.GWL_WNDPROC, (nint)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() public void DoRender()

View File

@@ -3,7 +3,9 @@ using System.Reflection;
using System.Runtime.Loader; using System.Runtime.Loader;
using CringeBootstrap.Abstractions; using CringeBootstrap.Abstractions;
using CringeLauncher.Utils; using CringeLauncher.Utils;
using CringePlugins.Loader;
using HarmonyLib; using HarmonyLib;
using NLog;
using Sandbox; using Sandbox;
using Sandbox.Engine.Multiplayer; using Sandbox.Engine.Multiplayer;
using Sandbox.Engine.Networking; using Sandbox.Engine.Networking;
@@ -36,9 +38,23 @@ public class Launcher : ICorePlugin
private const uint AppId = 244850U; private const uint AppId = 244850U;
private SpaceEngineersGame? _game; private SpaceEngineersGame? _game;
private readonly Harmony _harmony = new("CringeBootstrap"); private readonly Harmony _harmony = new("CringeBootstrap");
private PluginsLifetime? _lifetime;
public void Initialize(string[] args) public void Initialize(string[] args)
{ {
LogManager.Setup()
.SetupExtensions(s =>
{
s.RegisterLayoutRenderer("cringe-exception", e =>
{
if (e.Exception is null) return string.Empty;
e.Exception.FormatStackTrace();
return e.Exception.ToString();
});
});
LogManager.ReconfigExistingLoggers();
//environment variable for viktor's plugins //environment variable for viktor's plugins
Environment.SetEnvironmentVariable("SE_PLUGIN_DISABLE_METHOD_VERIFICATION", "True"); Environment.SetEnvironmentVariable("SE_PLUGIN_DISABLE_METHOD_VERIFICATION", "True");
@@ -52,8 +68,12 @@ public class Launcher : ICorePlugin
CheckUpdates().GetAwaiter().GetResult(); CheckUpdates().GetAwaiter().GetResult();
#endif #endif
PluginLoader.Main.Instance.Splash?.SetText("Initializing game..."); PluginLoader.Main.Instance.Splash?.SetText("Initializing plugins...");
PluginLoader.Main.Instance.Splash?.SetBarValue(0); PluginLoader.Main.Instance.Splash?.SetBarValue(0);
_lifetime = new PluginsLifetime();
PluginLoader.Main.Instance.Splash?.SetText("Initializing game...");
InitTexts(); InitTexts();
SpaceEngineersGame.SetupBasicGameInfo(); SpaceEngineersGame.SetupBasicGameInfo();
MyFinalBuildConstants.APP_VERSION = MyPerGameSettings.BasicGameInfo.GameVersion.GetValueOrDefault(); MyFinalBuildConstants.APP_VERSION = MyPerGameSettings.BasicGameInfo.GameVersion.GetValueOrDefault();

View File

@@ -1,26 +1,17 @@
using System.Reflection; using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
using Windows.Win32;
using HarmonyLib; using HarmonyLib;
using VRage.Render11.Sprites; using VRage.Render11.Sprites;
using VRageRender; using VRageRender;
namespace CringeLauncher.Patches; namespace CringeLauncher.Patches;
[HarmonyPatch(typeof(MyRender11), nameof(MyRender11.FullDraw))] [HarmonyPatch(typeof(MyRender11), nameof(MyRender11.Present))]
public class RenderHookPatch public class RenderHookPatch
{ {
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) private static void Prefix()
{ {
var placedHook = false; ImGuiHandler.Instance?.DoRender();
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;
}
} }
} }

View File

@@ -10,9 +10,12 @@ namespace CringeLauncher.Patches;
[HarmonyPatch] [HarmonyPatch]
public static class SwapChainPatch public static class SwapChainPatch
{ {
internal static nint WindowHandle;
[HarmonyPrefix, HarmonyPatch(typeof(MyPlatformRender), nameof(MyPlatformRender.CreateSwapChain))] [HarmonyPrefix, HarmonyPatch(typeof(MyPlatformRender), nameof(MyPlatformRender.CreateSwapChain))]
private static bool SwapChainPrefix(nint windowHandle) private static bool SwapChainPrefix(nint windowHandle)
{ {
WindowHandle = windowHandle;
MyPlatformRender.DisposeSwapChain(); MyPlatformRender.DisposeSwapChain();
MyPlatformRender.Log.WriteLine("CreateDeviceInternal create swapchain"); MyPlatformRender.Log.WriteLine("CreateDeviceInternal create swapchain");
@@ -51,11 +54,15 @@ public static class SwapChainPatch
} }
factory.MakeWindowAssociation(windowHandle, WindowAssociationFlags.IgnoreAll); factory.MakeWindowAssociation(windowHandle, WindowAssociationFlags.IgnoreAll);
ImGuiHandler.Instance ??= new ImGuiHandler(windowHandle, MyRender11.DeviceInstance, MyRender11.RC.DeviceContext);
return false; return false;
} }
[HarmonyPostfix, HarmonyPatch(typeof(MyRender11), nameof(MyRender11.CreateDeviceInternal))]
private static void CreateDevicePostfix()
{
ImGuiHandler.Instance ??= new ImGuiHandler(WindowHandle, MyRender11.DeviceInstance, MyRender11.RC.DeviceContext);
}
[HarmonyPrefix, HarmonyPatch(typeof(MyBackbuffer), MethodType.Constructor, typeof(SharpDX.Direct3D11.Resource))] [HarmonyPrefix, HarmonyPatch(typeof(MyBackbuffer), MethodType.Constructor, typeof(SharpDX.Direct3D11.Resource))]
private static bool SwapChainBBPrefix(MyBackbuffer __instance, SharpDX.Direct3D11.Resource swapChainBB) private static bool SwapChainBBPrefix(MyBackbuffer __instance, SharpDX.Direct3D11.Resource swapChainBB)
{ {
@@ -67,7 +74,7 @@ public static class SwapChainPatch
}); });
__instance.m_srv = new ShaderResourceView(MyRender11.DeviceInstance, swapChainBB); __instance.m_srv = new ShaderResourceView(MyRender11.DeviceInstance, swapChainBB);
ImGuiHandler.Instance!.Rtv = new RenderTargetView(MyRender11.DeviceInstance, swapChainBB, new() ImGuiHandler.Rtv = new RenderTargetView(MyRender11.DeviceInstance, swapChainBB, new()
{ {
Format = Format.R8G8B8A8_UNorm, Format = Format.R8G8B8A8_UNorm,
Dimension = RenderTargetViewDimension.Texture2D, Dimension = RenderTargetViewDimension.Texture2D,

View File

@@ -0,0 +1,108 @@
using System.Diagnostics;
using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Runtime.Loader;
using System.Text;
using HarmonyLib;
namespace CringeLauncher.Utils;
public static class ExceptionFormatter
{
private static readonly AccessTools.FieldRef<Exception, string> StackTraceField = AccessTools.FieldRefAccess<Exception, string>("_remoteStackTraceString");
public static void FormatStackTrace(this Exception exception)
{
var stackTrace = new StackTrace(exception, true);
var sb = new StringBuilder();
var i = 0;
while (stackTrace.GetFrame(i++) is { } frame)
{
var method = frame.GetMethod();
if (method is null)
continue;
sb.Append("at ");
if (method.DeclaringType is { } declaringType &&
AssemblyLoadContext.GetLoadContext(declaringType.Assembly) is { } assemblyLoadContext)
sb.Append(assemblyLoadContext).Append("//");
if (method.IsStatic)
sb.Append("static ");
if (method is MethodInfo methodInfo)
sb.Append(methodInfo.ReturnType, false);
else
sb.Append("new");
sb.Append(' ');
if (method.DeclaringType is null)
sb.Append("<null>");
else
sb.Append(method.DeclaringType, true);
if (method is MethodInfo)
{
sb.Append('.');
sb.Append(method.Name);
}
if (method.ContainsGenericParameters)
sb.Append(method.GetGenericArguments(), false);
sb.Append('(');
var parameters = method.GetParameters();
for (var j = 0; j < parameters.Length; j++)
{
sb.Append(parameters[j].ParameterType, false);
if (!string.IsNullOrEmpty(parameters[j].Name))
sb.Append(' ').Append(parameters[j].Name);
if (j < parameters.Length - 1)
sb.Append(", ");
}
sb.Append(')');
if (frame.GetFileName() is { } fileName)
{
sb.Append(" in ").Append(fileName).Append('(').Append(frame.GetFileLineNumber()).Append(':').Append(frame.GetFileColumnNumber()).Append(')');
}
sb.AppendLine();
}
ref var stackTraceString = ref StackTraceField(exception);
stackTraceString = sb.ToString();
}
private static StringBuilder Append(this StringBuilder sb, Type type, bool fullName = false)
{
if (fullName && !string.IsNullOrEmpty(type.Namespace))
sb.Append(type.Namespace).Append('.');
sb.Append(type.Name);
if (type.ContainsGenericParameters)
sb.Append(type.GetGenericArguments(), fullName);
return sb;
}
private static StringBuilder Append(this StringBuilder sb, Type[] genericArguments, bool fullName)
{
sb.Append('<');
for (var i = 0; i < genericArguments.Length; i++)
{
sb.Append(genericArguments[i], fullName);
if (i < genericArguments.Length - 1)
sb.Append(", ");
}
sb.Append('>');
return sb;
}
}

View File

@@ -0,0 +1,49 @@
using System.Collections.Immutable;
using NLog;
namespace CringePlugins.Loader;
public class PluginsLifetime
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private readonly ImmutableArray<PluginInstance> _plugins;
public PluginsLifetime()
{
var dir = Directory.CreateDirectory(Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "CringeLauncher", "plugins"));
var plugins = ImmutableArray<PluginInstance>.Empty.ToBuilder();
foreach (var directory in dir.EnumerateDirectories())
{
var files = directory.GetFiles("*.dll");
if (files.Length != 1) continue;
try
{
plugins.Add(new PluginInstance(files[0].FullName));
}
catch (Exception e)
{
Log.Error(e, "Failed to load plugin {PluginPath}", files[0].FullName);
}
}
_plugins = plugins.ToImmutable();
foreach (var instance in _plugins)
{
try
{
instance.Instantiate();
instance.RegisterLifetime();
}
catch (Exception e)
{
Log.Error(e, "Failed to instantiate plugin {Plugin}", instance.Metadata);
}
}
}
}