Files
se-launcher/PluginLoader/Main.cs
zznty 9fb29d2011
All checks were successful
Build / Build Launcher (push) Successful in 2m31s
update logging and add pl splash as the main one
2024-05-31 17:12:08 +07:00

254 lines
7.1 KiB
C#

using System.Diagnostics;
using System.Net;
using System.Reflection;
using HarmonyLib;
using PluginLoader.Compiler;
using PluginLoader.Data;
using PluginLoader.GUI;
using PluginLoader.Stats;
using Sandbox.Game.World;
using VRage.Plugins;
using SEPMPlugin = PluginLoader.SEPM.SEPMPlugin;
namespace PluginLoader;
public class Main : IHandleInputPlugin
{
public static Main Instance;
private readonly List<PluginInstance> plugins = new();
private bool init;
public Main()
{
var sw = Stopwatch.StartNew();
RunSplash();
Instance = this;
var temp = Cursor.Current;
Cursor.Current = Cursors.AppStarting;
var pluginsDir = LoaderTools.PluginsDir;
LogFile.Init(Directory.CreateDirectory(pluginsDir).FullName);
LogFile.Log.Debug("Starting - v{Version}", Assembly.GetExecutingAssembly().GetName().Version.ToString(3));
// Fix tls 1.2 not supported on Windows 7 - github.com is tls 1.2 only
try
{
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
}
catch (NotSupportedException e)
{
LogFile.Log.Warn(e, "An error occurred while setting up networking, web requests will probably fail");
}
Splash?.SetText("Finding references...");
RoslynReferences.GenerateAssemblyList();
AppDomain.CurrentDomain.AssemblyResolve += ResolveDependencies;
Config = PluginConfig.Load(pluginsDir);
List = new(pluginsDir, Config);
Config.Init(List);
StatsClient.OverrideBaseUrl(Config.StatsServerBaseUrl);
Splash?.SetText("Patching...");
LogFile.Log.Debug("Patching");
new Harmony("avaness.PluginLoader").PatchAll(Assembly.GetExecutingAssembly());
Splash?.SetText("Instantiating plugins...");
LogFile.Log.Debug("Instantiating plugins");
foreach (var id in Config)
{
var data = List[id];
if (data is GitHubPlugin github)
github.Init(pluginsDir);
if (PluginInstance.TryGet(data, out var p))
{
plugins.Add(p);
if (data.IsLocal)
HasLocal = true;
}
}
sw.Stop();
// FIXME: It can potentially run in the background speeding up the game's startup
//ReportEnabledPlugins();
LogFile.Log.Debug("Finished startup. Took {Time}ms", sw.ElapsedMilliseconds);
Cursor.Current = temp;
}
public PluginList List { get; }
public PluginConfig Config { get; }
public SplashScreen? Splash { get; set; }
/// <summary>
/// True if a local plugin was loaded
/// </summary>
public bool HasLocal { get; }
// Skip local plugins, keep only enabled ones
public string[] TrackablePluginIds => Config.EnabledPlugins.Where(id => !List[id].IsLocal).ToArray();
public void Init(object gameInstance)
{
LogFile.Log.Debug("Initializing {PluginsCount} plugins", plugins.Count);
for (var i = plugins.Count - 1; i >= 0; i--)
{
var p = plugins[i];
if (!p.Init(gameInstance))
plugins.RemoveAtFast(i);
}
init = true;
}
public void Update()
{
if (init)
for (var i = plugins.Count - 1; i >= 0; i--)
{
var p = plugins[i];
if (!p.Update())
plugins.RemoveAtFast(i);
}
}
public void HandleInput()
{
if (init)
for (var i = plugins.Count - 1; i >= 0; i--)
{
var p = plugins[i];
if (!p.HandleInput())
plugins.RemoveAtFast(i);
}
}
public void Dispose()
{
foreach (var p in plugins)
p.Dispose();
plugins.Clear();
AppDomain.CurrentDomain.AssemblyResolve -= ResolveDependencies;
Instance = null;
}
public bool TryGetPluginInstance(string id, out PluginInstance instance)
{
instance = null;
if (!init)
return false;
foreach (var p in plugins)
if (p.Id == id)
{
instance = p;
return true;
}
return false;
}
private void RunSplash()
{
var resetEvent = new ManualResetEventSlim();
var thread = new Thread(() =>
{
Application.EnableVisualStyles();
Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
Splash = new();
resetEvent.Set();
Task.Run(() =>
{
Sandbox.MySandboxGame.m_windowCreatedEvent.WaitOne();
Splash.Invoke(() => Splash.Delete());
});
Application.Run(Splash);
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
resetEvent.Wait();
}
private void ReportEnabledPlugins()
{
if (!PlayerConsent.ConsentGiven)
return;
Splash?.SetText("Reporting plugin usage...");
LogFile.Log.Debug("Reporting plugin usage");
// Config has already been validated at this point so all enabled plugins will have list items
// FIXME: Move into a background thread
if (StatsClient.Track(TrackablePluginIds))
LogFile.Log.Debug("List of enabled plugins has been sent to the statistics server");
else
LogFile.Log.Debug("Failed to send the list of enabled plugins to the statistics server");
}
public void RegisterComponents()
{
LogFile.Log.Debug("Registering {PluginsCount} components", plugins.Count);
foreach (var plugin in plugins)
plugin.RegisterSession(MySession.Static);
}
public void DisablePlugins()
{
Config.Disable();
plugins.Clear();
LogFile.Log.Debug("Disabled all plugins");
}
public void InstantiatePlugins()
{
LogFile.Log.Debug($"Loading {plugins.Count} plugins");
for (var i = plugins.Count - 1; i >= 0; i--)
{
var p = plugins[i];
if (!p.Instantiate())
plugins.RemoveAtFast(i);
}
}
private Assembly? ResolveDependencies(object? sender, ResolveEventArgs args)
{
var assembly = args.RequestingAssembly?.GetName().ToString();
var requestedName = new AssemblyName(args.Name);
switch (requestedName.Name)
{
case "0Harmony":
{
if (assembly != null)
LogFile.Log.Debug("Resolving 0Harmony for {AssemblyName}", assembly);
else
LogFile.Log.Debug("Resolving 0Harmony");
return typeof(Harmony).Assembly;
}
case "SEPluginManager":
{
if (assembly != null)
LogFile.Log.Debug("Resolving SEPluginManager for {AssemblyName}", assembly);
else
LogFile.Log.Debug("Resolving SEPluginManager");
return typeof(SEPMPlugin).Assembly;
}
default:
return null;
}
}
}