Chainloader.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Text;
  8. using BepInEx.Logging;
  9. using UnityEngine;
  10. using UnityInjector.ConsoleUtil;
  11. using UnityLogWriter = BepInEx.Logging.UnityLogWriter;
  12. namespace BepInEx.Bootstrap
  13. {
  14. /// <summary>
  15. /// The manager and loader for all plugins, and the entry point for BepInEx plugin system.
  16. /// </summary>
  17. public static class Chainloader
  18. {
  19. /// <summary>
  20. /// The loaded and initialized list of plugins.
  21. /// </summary>
  22. public static List<BaseUnityPlugin> Plugins { get; private set; } = new List<BaseUnityPlugin>();
  23. /// <summary>
  24. /// The GameObject that all plugins are attached to as components.
  25. /// </summary>
  26. public static GameObject ManagerObject { get; private set; } = new GameObject("BepInEx_Manager");
  27. private static bool _loaded = false;
  28. private static bool _initialized = false;
  29. /// <summary>
  30. /// Initializes BepInEx to be able to start the chainloader.
  31. /// </summary>
  32. public static void Initialize(string containerExePath, bool startConsole = true)
  33. {
  34. if (_initialized)
  35. return;
  36. //Set vitals
  37. Paths.ExecutablePath = containerExePath;
  38. //Start logging
  39. if (startConsole)
  40. {
  41. ConsoleWindow.Attach();
  42. ConsoleEncoding.ConsoleCodePage = (uint)Encoding.UTF8.CodePage;
  43. Console.OutputEncoding = Encoding.UTF8;
  44. }
  45. UnityLogWriter unityLogWriter = new UnityLogWriter();
  46. if (Logger.CurrentLogger != null && Logger.CurrentLogger is PreloaderLogWriter preloaderLogger)
  47. unityLogWriter.WriteToLog($"{preloaderLogger}\r\n");
  48. Logger.SetLogger(unityLogWriter);
  49. _initialized = true;
  50. }
  51. /// <summary>
  52. /// The entrypoint for the BepInEx plugin system.
  53. /// </summary>
  54. public static void Start()
  55. {
  56. if (_loaded)
  57. return;
  58. if (!_initialized)
  59. throw new InvalidOperationException("BepInEx has not been initialized. Please call Chainloader.Initialize prior to starting the chainloader instance.");
  60. if (!Directory.Exists(Paths.PluginPath))
  61. Directory.CreateDirectory(Paths.PluginPath);
  62. try
  63. {
  64. if (bool.Parse(Config.GetEntry("chainloader-log-unity-messages", "false", "BepInEx")))
  65. UnityLogWriter.ListenUnityLogs();
  66. var productNameProp = typeof(Application).GetProperty("productName", BindingFlags.Public | BindingFlags.Static);
  67. if (productNameProp != null)
  68. ConsoleWindow.Title =
  69. $"BepInEx {Assembly.GetExecutingAssembly().GetName().Version} - {productNameProp.GetValue(null, null)}";
  70. Logger.Log(LogLevel.Message, "Chainloader started");
  71. UnityEngine.Object.DontDestroyOnLoad(ManagerObject);
  72. string currentProcess = Process.GetCurrentProcess().ProcessName.ToLower();
  73. var pluginTypes = TypeLoader.LoadTypes<BaseUnityPlugin>(Paths.PluginPath)
  74. .Where(plugin =>
  75. {
  76. //Perform a filter for currently running process
  77. var filters = MetadataHelper.GetAttributes<BepInProcess>(plugin);
  78. if (!filters.Any())
  79. return true;
  80. return filters.Any(x => x.ProcessName.ToLower().Replace(".exe", "") == currentProcess);
  81. })
  82. .ToList();
  83. Logger.Log(LogLevel.Info, $"{pluginTypes.Count} plugins selected");
  84. Dictionary<Type, IEnumerable<Type>> dependencyDict = new Dictionary<Type, IEnumerable<Type>>();
  85. foreach (Type t in pluginTypes)
  86. {
  87. try
  88. {
  89. IEnumerable<Type> dependencies = MetadataHelper.GetDependencies(t, pluginTypes);
  90. dependencyDict[t] = dependencies;
  91. }
  92. catch (MissingDependencyException)
  93. {
  94. var metadata = MetadataHelper.GetMetadata(t);
  95. Logger.Log(LogLevel.Info, $"Cannot load [{metadata.Name}] due to missing dependencies.");
  96. }
  97. }
  98. pluginTypes = Utility.TopologicalSort(dependencyDict.Keys, x => dependencyDict[x]).ToList();
  99. foreach (Type t in pluginTypes)
  100. {
  101. try
  102. {
  103. var metadata = MetadataHelper.GetMetadata(t);
  104. var plugin = (BaseUnityPlugin)ManagerObject.AddComponent(t);
  105. Plugins.Add(plugin);
  106. Logger.Log(LogLevel.Info, $"Loaded [{metadata.Name} {metadata.Version}]");
  107. }
  108. catch (Exception ex)
  109. {
  110. Logger.Log(LogLevel.Info, $"Error loading [{t.Name}] : {ex.Message}");
  111. }
  112. }
  113. }
  114. catch (Exception ex)
  115. {
  116. ConsoleWindow.Attach();
  117. Console.WriteLine("Error occurred starting the game");
  118. Console.WriteLine(ex.ToString());
  119. }
  120. _loaded = true;
  121. }
  122. }
  123. }