using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using BepInEx.Logging;
using UnityEngine;
using UnityInjector.ConsoleUtil;
using UnityLogWriter = BepInEx.Logging.UnityLogWriter;
namespace BepInEx.Bootstrap
{
///
/// The manager and loader for all plugins, and the entry point for BepInEx plugin system.
///
public static class Chainloader
{
///
/// The loaded and initialized list of plugins.
///
public static List Plugins { get; private set; } = new List();
///
/// The GameObject that all plugins are attached to as components.
///
public static GameObject ManagerObject { get; private set; } = new GameObject("BepInEx_Manager");
private static bool _loaded = false;
private static bool _initialized = false;
///
/// Initializes BepInEx to be able to start the chainloader.
///
public static void Initialize(string containerExePath, bool startConsole = true)
{
if (_initialized)
return;
//Set vitals
Paths.ExecutablePath = containerExePath;
//Start logging
if (startConsole)
{
ConsoleWindow.Attach();
ConsoleEncoding.ConsoleCodePage = (uint)Encoding.UTF8.CodePage;
Console.OutputEncoding = Encoding.UTF8;
}
UnityLogWriter unityLogWriter = new UnityLogWriter();
if (Logger.CurrentLogger != null && Logger.CurrentLogger is PreloaderLogWriter preloaderLogger)
unityLogWriter.WriteToLog($"{preloaderLogger}\r\n");
Logger.SetLogger(unityLogWriter);
_initialized = true;
}
///
/// The entrypoint for the BepInEx plugin system.
///
public static void Start()
{
if (_loaded)
return;
if (!_initialized)
throw new InvalidOperationException("BepInEx has not been initialized. Please call Chainloader.Initialize prior to starting the chainloader instance.");
if (!Directory.Exists(Paths.PluginPath))
Directory.CreateDirectory(Paths.PluginPath);
try
{
if (bool.Parse(Config.GetEntry("chainloader-log-unity-messages", "false", "BepInEx")))
UnityLogWriter.ListenUnityLogs();
var productNameProp = typeof(Application).GetProperty("productName", BindingFlags.Public | BindingFlags.Static);
if (productNameProp != null)
ConsoleWindow.Title =
$"BepInEx {Assembly.GetExecutingAssembly().GetName().Version} - {productNameProp.GetValue(null, null)}";
Logger.Log(LogLevel.Message, "Chainloader started");
UnityEngine.Object.DontDestroyOnLoad(ManagerObject);
string currentProcess = Process.GetCurrentProcess().ProcessName.ToLower();
var pluginTypes = TypeLoader.LoadTypes(Paths.PluginPath)
.Where(plugin =>
{
//Perform a filter for currently running process
var filters = MetadataHelper.GetAttributes(plugin);
if (!filters.Any())
return true;
return filters.Any(x => x.ProcessName.ToLower().Replace(".exe", "") == currentProcess);
})
.ToList();
Logger.Log(LogLevel.Info, $"{pluginTypes.Count} plugins selected");
Dictionary> dependencyDict = new Dictionary>();
foreach (Type t in pluginTypes)
{
try
{
IEnumerable dependencies = MetadataHelper.GetDependencies(t, pluginTypes);
dependencyDict[t] = dependencies;
}
catch (MissingDependencyException)
{
var metadata = MetadataHelper.GetMetadata(t);
Logger.Log(LogLevel.Info, $"Cannot load [{metadata.Name}] due to missing dependencies.");
}
}
pluginTypes = Utility.TopologicalSort(dependencyDict.Keys, x => dependencyDict[x]).ToList();
foreach (Type t in pluginTypes)
{
try
{
var metadata = MetadataHelper.GetMetadata(t);
var plugin = (BaseUnityPlugin) ManagerObject.AddComponent(t);
Plugins.Add(plugin);
Logger.Log(LogLevel.Info, $"Loaded [{metadata.Name} {metadata.Version}]");
}
catch (Exception ex)
{
Logger.Log(LogLevel.Info, $"Error loading [{t.Name}] : {ex.Message}");
}
}
}
catch (Exception ex)
{
ConsoleWindow.Attach();
Console.WriteLine("Error occurred starting the game");
Console.WriteLine(ex.ToString());
}
_loaded = true;
}
}
}