using BepInEx.Common; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using UnityEngine; namespace BepInEx { /// /// The manager and loader for all plugins, and the entry point for BepInEx. /// public class Chainloader { /// /// The loaded and initialized list of plugins. /// public static List Plugins { get; protected set; } = new List(); /// /// The GameObject that all plugins are attached to as components. /// public static GameObject ManagerObject { get; protected set; } = new GameObject("BepInEx_Manager"); static bool loaded = false; /// /// The entry point for BepInEx, called on the very first LoadScene() from UnityEngine. /// public static void Initialize() { if (loaded) return; if (!Directory.Exists(Common.Utility.PluginsDirectory)) Directory.CreateDirectory(Utility.PluginsDirectory); if (bool.Parse(Config.GetEntry("console", "false")) || bool.Parse(Config.GetEntry("console-shiftjis", "false"))) { try { UnityInjector.ConsoleUtil.ConsoleWindow.Attach(); if (bool.Parse(Config.GetEntry("console-shiftjis", "false"))) UnityInjector.ConsoleUtil.ConsoleEncoding.ConsoleCodePage = 932; } catch { BepInLogger.Log("Failed to allocate console!", true); } } try { BepInLogger.Log($"BepInEx {Assembly.GetExecutingAssembly().GetName().Version}"); BepInLogger.Log("Chainloader started"); UnityEngine.Object.DontDestroyOnLoad(ManagerObject); string currentProcess = Process.GetCurrentProcess().ProcessName.ToLower(); var pluginTypes = TypeLoader.LoadTypes(Utility.PluginsDirectory) .Where(plugin => { //Perform a filter for currently running process var filters = TypeLoader.GetAttributes(plugin); if (!filters.Any()) return true; return filters.All(x => x.ProcessName.ToLower().Replace(".exe", "") == currentProcess); }) .ToList(); BepInLogger.Log($"{pluginTypes.Count} plugins selected"); Dictionary> dependencyDict = new Dictionary>(); foreach (Type t in pluginTypes) { try { IEnumerable dependencies = TypeLoader.GetDependencies(t, pluginTypes); dependencyDict[t] = dependencies; } catch (MissingDependencyException) { var metadata = TypeLoader.GetMetadata(t); BepInLogger.Log($"Cannot load [{metadata.Name}] due to missing dependencies."); } } pluginTypes = TopologicalSort(dependencyDict.Keys, x => dependencyDict[x]).ToList(); foreach (Type t in pluginTypes) { try { var metadata = TypeLoader.GetMetadata(t); var plugin = (BaseUnityPlugin) ManagerObject.AddComponent(t); Plugins.Add(plugin); BepInLogger.Log($"Loaded [{metadata.Name} {metadata.Version}]"); } catch (Exception ex) { BepInLogger.Log($"Error loading [{t.Name}] : {ex.Message}"); } } } catch (Exception ex) { UnityInjector.ConsoleUtil.ConsoleWindow.Attach(); Console.WriteLine("Error occurred starting the game"); Console.WriteLine(ex.ToString()); } loaded = true; } protected static IEnumerable TopologicalSort( IEnumerable nodes, Func> dependencySelector) { List sorted_list = new List(); HashSet visited = new HashSet(); HashSet sorted = new HashSet(); foreach (TNode input in nodes) Visit(input); return sorted_list; void Visit(TNode node) { if (visited.Contains(node)) { if (!sorted.Contains(node)) throw new Exception("Cyclic Dependency"); } else { visited.Add(node); foreach (var dep in dependencySelector(node)) Visit(dep); sorted.Add(node); sorted_list.Add(node); } } } } }