Chainloader.cs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. using BepInEx.Common;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Reflection;
  8. using UnityEngine;
  9. namespace BepInEx
  10. {
  11. /// <summary>
  12. /// The manager and loader for all plugins, and the entry point for BepInEx.
  13. /// </summary>
  14. public class Chainloader
  15. {
  16. /// <summary>
  17. /// The loaded and initialized list of plugins.
  18. /// </summary>
  19. public static List<BaseUnityPlugin> Plugins { get; protected set; } = new List<BaseUnityPlugin>();
  20. /// <summary>
  21. /// The GameObject that all plugins are attached to as components.
  22. /// </summary>
  23. public static GameObject ManagerObject { get; protected set; } = new GameObject("BepInEx_Manager");
  24. static bool loaded = false;
  25. /// <summary>
  26. /// The entry point for BepInEx, called on the very first LoadScene() from UnityEngine.
  27. /// </summary>
  28. public static void Initialize()
  29. {
  30. if (loaded)
  31. return;
  32. if (!Directory.Exists(Common.Utility.PluginsDirectory))
  33. Directory.CreateDirectory(Utility.PluginsDirectory);
  34. if (bool.Parse(Config.GetEntry("console", "false")) || bool.Parse(Config.GetEntry("console-shiftjis", "false")))
  35. {
  36. try
  37. {
  38. UnityInjector.ConsoleUtil.ConsoleWindow.Attach();
  39. if (bool.Parse(Config.GetEntry("console-shiftjis", "false")))
  40. UnityInjector.ConsoleUtil.ConsoleEncoding.ConsoleCodePage = 932;
  41. }
  42. catch
  43. {
  44. BepInLogger.Log("Failed to allocate console!", true);
  45. }
  46. }
  47. try
  48. {
  49. BepInLogger.Log($"BepInEx {Assembly.GetExecutingAssembly().GetName().Version}");
  50. BepInLogger.Log("Chainloader started");
  51. UnityEngine.Object.DontDestroyOnLoad(ManagerObject);
  52. string currentProcess = Process.GetCurrentProcess().ProcessName.ToLower();
  53. var pluginTypes = TypeLoader.LoadTypes<BaseUnityPlugin>(Utility.PluginsDirectory)
  54. .Where(plugin =>
  55. {
  56. //Perform a filter for currently running process
  57. var filters = TypeLoader.GetAttributes<BepInProcess>(plugin);
  58. if (!filters.Any())
  59. return true;
  60. return filters.All(x => x.ProcessName.ToLower().Replace(".exe", "") == currentProcess);
  61. })
  62. .ToList();
  63. BepInLogger.Log($"{pluginTypes.Count} plugins selected");
  64. Dictionary<Type, IEnumerable<Type>> dependencyDict = new Dictionary<Type, IEnumerable<Type>>();
  65. foreach (Type t in pluginTypes)
  66. {
  67. try
  68. {
  69. IEnumerable<Type> dependencies = TypeLoader.GetDependencies(t, pluginTypes);
  70. dependencyDict[t] = dependencies;
  71. }
  72. catch (MissingDependencyException)
  73. {
  74. var metadata = TypeLoader.GetMetadata(t);
  75. BepInLogger.Log($"Cannot load [{metadata.Name}] due to missing dependencies.");
  76. }
  77. }
  78. pluginTypes = TopologicalSort(dependencyDict.Keys, x => dependencyDict[x]).ToList();
  79. foreach (Type t in pluginTypes)
  80. {
  81. try
  82. {
  83. var metadata = TypeLoader.GetMetadata(t);
  84. var plugin = (BaseUnityPlugin) ManagerObject.AddComponent(t);
  85. Plugins.Add(plugin);
  86. BepInLogger.Log($"Loaded [{metadata.Name} {metadata.Version}]");
  87. }
  88. catch (Exception ex)
  89. {
  90. BepInLogger.Log($"Error loading [{t.Name}] : {ex.Message}");
  91. }
  92. }
  93. }
  94. catch (Exception ex)
  95. {
  96. UnityInjector.ConsoleUtil.ConsoleWindow.Attach();
  97. Console.WriteLine("Error occurred starting the game");
  98. Console.WriteLine(ex.ToString());
  99. }
  100. loaded = true;
  101. }
  102. protected static IEnumerable<TNode> TopologicalSort<TNode>(
  103. IEnumerable<TNode> nodes,
  104. Func<TNode, IEnumerable<TNode>> dependencySelector)
  105. {
  106. List<TNode> sorted_list = new List<TNode>();
  107. HashSet<TNode> visited = new HashSet<TNode>();
  108. HashSet<TNode> sorted = new HashSet<TNode>();
  109. foreach (TNode input in nodes)
  110. Visit(input);
  111. return sorted_list;
  112. void Visit(TNode node)
  113. {
  114. if (visited.Contains(node))
  115. {
  116. if (!sorted.Contains(node))
  117. throw new Exception("Cyclic Dependency");
  118. }
  119. else
  120. {
  121. visited.Add(node);
  122. foreach (var dep in dependencySelector(node))
  123. Visit(dep);
  124. sorted.Add(node);
  125. sorted_list.Add(node);
  126. }
  127. }
  128. }
  129. }
  130. }