embed plugin loader directly into the launcher

This commit is contained in:
zznty
2022-10-29 01:50:14 +07:00
parent 7204815c0c
commit 66d3dc2ead
53 changed files with 5689 additions and 10 deletions

255
PluginLoader/LoaderTools.cs Normal file
View File

@@ -0,0 +1,255 @@
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using Windows.UI.Popups;
using HarmonyLib;
using PluginLoader.SEPM;
using Sandbox;
using Sandbox.Game.World;
using Sandbox.Graphics.GUI;
using VRage.FileSystem;
using VRage.Input;
using VRage.Plugins;
namespace PluginLoader;
public static class LoaderTools
{
public static string PluginsDir => Path.GetFullPath(Path.Combine(MyFileSystem.ExePath, "Plugins"));
public static DialogResult ShowMessageBox(string message, string title, MessageBoxButtons buttons, MessageBoxIcon icon)
{
var dialog = new MessageDialog(message, title);
switch (buttons)
{
case MessageBoxButtons.OK:
dialog.Commands.Add(new UICommand("Ok"));
break;
case MessageBoxButtons.OKCancel:
dialog.Commands.Add(new UICommand("Ok"));
dialog.Commands.Add(new UICommand("Cancel"));
break;
case MessageBoxButtons.AbortRetryIgnore:
break;
case MessageBoxButtons.YesNoCancel:
dialog.Commands.Add(new UICommand("Yes"));
dialog.Commands.Add(new UICommand("No"));
dialog.Commands.Add(new UICommand("Cancel"));
break;
case MessageBoxButtons.YesNo:
dialog.Commands.Add(new UICommand("Yes"));
dialog.Commands.Add(new UICommand("No"));
break;
case MessageBoxButtons.RetryCancel:
dialog.Commands.Add(new UICommand("Retry"));
dialog.Commands.Add(new UICommand("Cancel"));
break;
case MessageBoxButtons.CancelTryContinue:
break;
default:
throw new ArgumentOutOfRangeException(nameof(buttons), buttons, null);
}
WinRT.Interop.InitializeWithWindow.Initialize(dialog, Process.GetCurrentProcess().MainWindowHandle);
var result = dialog.ShowAsync().AsTask().Result;
return buttons switch
{
MessageBoxButtons.OK => DialogResult.OK,
MessageBoxButtons.OKCancel => result.Label == "Ok" ? DialogResult.OK : DialogResult.Cancel,
MessageBoxButtons.AbortRetryIgnore => DialogResult.Ignore,
MessageBoxButtons.YesNoCancel => result.Label switch
{
"Yes" => DialogResult.Yes,
"No" => DialogResult.No,
_ => DialogResult.Cancel
},
MessageBoxButtons.YesNo => result.Label switch
{
"Yes" => DialogResult.Yes,
_ => DialogResult.No
},
MessageBoxButtons.RetryCancel => result.Label switch
{
"Retry" => DialogResult.Retry,
_ => DialogResult.Cancel
},
MessageBoxButtons.CancelTryContinue => DialogResult.Cancel,
_ => throw new ArgumentOutOfRangeException(nameof(buttons), buttons, null)
};
}
public static void UnloadAndRestart()
{
LogFile.Dispose();
MySessionLoader.Unload();
MySandboxGame.Config.ControllerDefaultOnStart = MyInput.Static.IsJoystickLastUsed;
MySandboxGame.Config.Save();
MyScreenManager.CloseAllScreensNowExcept(null);
MyPlugins.Unload();
Restart();
}
public static void Restart()
{
Application.Restart();
Process.GetCurrentProcess().Kill();
}
public static void ExecuteMain(SEPMPlugin plugin)
{
var name = plugin.GetType().ToString();
plugin.Main(new(name), new());
}
public static string GetHash1(string file)
{
using (var sha = new SHA1Managed())
{
return GetHash(file, sha);
}
}
public static string GetHash256(string file)
{
using (var sha = new SHA256CryptoServiceProvider())
{
return GetHash(file, sha);
}
}
public static string GetHash(string file, HashAlgorithm hash)
{
using (var fileStream = new FileStream(file, FileMode.Open))
{
using (var bufferedStream = new BufferedStream(fileStream))
{
var data = hash.ComputeHash(bufferedStream);
var sb = new StringBuilder(2 * data.Length);
foreach (var b in data)
sb.AppendFormat("{0:x2}", b);
return sb.ToString();
}
}
}
/// <summary>
/// This method attempts to disable JIT compiling for the assembly.
/// This method will force any member access exceptions by methods to be thrown now instead of later.
/// </summary>
public static void Precompile(Assembly a)
{
Type[] types;
try
{
types = a.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
var sb = new StringBuilder();
sb.AppendLine("LoaderExceptions: ");
foreach (var e2 in e.LoaderExceptions)
sb.Append(e2).AppendLine();
LogFile.WriteLine(sb.ToString());
throw;
}
foreach (var t in types)
{
// Static constructors allow for early code execution which can cause issues later in the game
if (HasStaticConstructor(t))
continue;
foreach (var m in t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public |
BindingFlags.Instance | BindingFlags.Static))
{
if (m.HasAttribute<HarmonyReversePatch>())
throw new("Harmony attribute 'HarmonyReversePatch' found on the method '" + m.Name +
"' is not compatible with Plugin Loader!");
Precompile(m);
}
}
}
private static void Precompile(MethodInfo m)
{
if (!m.IsAbstract && !m.ContainsGenericParameters)
RuntimeHelpers.PrepareMethod(m.MethodHandle);
}
private static bool HasStaticConstructor(Type t)
{
return t.GetConstructors(BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic |
BindingFlags.Instance).Any(c => c.IsStatic);
}
public static void OpenFileDialog(string title, string directory, string filter, Action<string> onOk)
{
var t = new Thread(() => OpenFileDialogThread(title, directory, filter, onOk));
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
private static void OpenFileDialogThread(string title, string directory, string filter, Action<string> onOk)
{
try
{
// Get the file path via prompt
using (var openFileDialog = new OpenFileDialog())
{
if (Directory.Exists(directory))
openFileDialog.InitialDirectory = directory;
openFileDialog.Title = title;
openFileDialog.Filter = filter;
openFileDialog.RestoreDirectory = true;
if (openFileDialog.ShowDialog() == DialogResult.OK)
// Move back to the main thread so that we can interact with keen code again
MySandboxGame.Static.Invoke(
() => onOk(openFileDialog.FileName),
"PluginLoader");
}
}
catch (Exception e)
{
LogFile.WriteGameLog("Error while opening file dialog: " + e);
}
}
public static void OpenFolderDialog(string title, string directory, Action<string> onOk)
{
var t = new Thread(() => OpenFolderDialogThread(title, directory, onOk));
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
private static void OpenFolderDialogThread(string title, string directory, Action<string> onOk)
{
try
{
// Get the file path via prompt
using (var openFileDialog = new FolderBrowserDialog())
{
if (Directory.Exists(directory))
openFileDialog.SelectedPath = directory;
openFileDialog.Description = title;
if (openFileDialog.ShowDialog() == DialogResult.OK)
// Move back to the main thread so that we can interact with keen code again
MySandboxGame.Static.Invoke(
() => onOk(openFileDialog.SelectedPath),
"PluginLoader");
}
}
catch (Exception e)
{
LogFile.WriteGameLog("Error while opening file dialog: " + e);
}
}
}