fix imgui and plugin loading
This commit is contained in:
@@ -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>
|
||||||
|
|
||||||
|
34
CringeBootstrap/NLog.config
Normal file
34
CringeBootstrap/NLog.config
Normal 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>
|
@@ -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"));
|
||||||
|
|
||||||
|
@@ -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, )",
|
||||||
|
@@ -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()
|
||||||
|
@@ -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();
|
||||||
|
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -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,
|
||||||
|
108
CringeLauncher/Utils/ExceptionFormatter.cs
Normal file
108
CringeLauncher/Utils/ExceptionFormatter.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
49
CringePlugins/Loader/PluginsLifetime.cs
Normal file
49
CringePlugins/Loader/PluginsLifetime.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user