/// <summary> /// The entry point for the BepInEx plugin system, called on the very first LoadScene() from UnityEngine. /// </summary> public static void Initialize() { if (_loaded) { return; } if (!Directory.Exists(Utility.PluginsDirectory)) { Directory.CreateDirectory(Utility.PluginsDirectory); } Preloader.AllocateConsole(); try { UnityLogWriter unityLogWriter = new UnityLogWriter(); if (Preloader.PreloaderLog != null) { unityLogWriter.WriteToLog($"{Preloader.PreloaderLog}\r\n"); } Logger.SetLogger(unityLogWriter); if (bool.Parse(Config.GetEntry("log_unity_messages", "false", "Global"))) { UnityLogWriter.ListenUnityLogs(); } string consoleTile = $"BepInEx {Assembly.GetExecutingAssembly().GetName().Version} - {Application.productName}"; ConsoleWindow.Title = consoleTile; Logger.Log(LogLevel.Message, "Chainloader started"); UnityEngine.Object.DontDestroyOnLoad(ManagerObject); string currentProcess = Process.GetCurrentProcess().ProcessName.ToLower(); var pluginTypes = TypeLoader.LoadTypes <BaseUnityPlugin>(Utility.PluginsDirectory) .Where(plugin => { //Perform a filter for currently running process var filters = MetadataHelper.GetAttributes <BepInProcess>(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 <Type, IEnumerable <Type> > dependencyDict = new Dictionary <Type, IEnumerable <Type> >(); foreach (Type t in pluginTypes) { try { IEnumerable <Type> 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) { UnityInjector.ConsoleUtil.ConsoleWindow.Attach(); Console.WriteLine("Error occurred starting the game"); Console.WriteLine(ex.ToString()); } _loaded = true; }
/// <summary> /// The entrypoint for the BepInEx plugin system. /// </summary> 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 <BaseUnityPlugin>(Paths.PluginPath) .Where(plugin => { //Perform a filter for currently running process var filters = MetadataHelper.GetAttributes <BepInProcess>(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 <Type, IEnumerable <Type> > dependencyDict = new Dictionary <Type, IEnumerable <Type> >(); foreach (Type t in pluginTypes) { try { IEnumerable <Type> 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; }
/// <summary> /// The entrypoint for the BepInEx plugin system. /// </summary> 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); } if (!Directory.Exists(Paths.PatcherPluginPath)) { Directory.CreateDirectory(Paths.PatcherPluginPath); } try { 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.LogMessage("Chainloader started"); ManagerObject = new GameObject("BepInEx_Manager"); UnityEngine.Object.DontDestroyOnLoad(ManagerObject); string currentProcess = Process.GetCurrentProcess().ProcessName.ToLower(); var globalPluginTypes = TypeLoader.LoadTypes <BaseUnityPlugin>(Paths.PluginPath).ToList(); var selectedPluginTypes = globalPluginTypes .Where(plugin => { //Ensure metadata exists var metadata = MetadataHelper.GetMetadata(plugin); if (metadata == null) { Logger.LogWarning($"Skipping over type [{plugin.Name}] as no metadata attribute is specified"); return(false); } //Perform a filter for currently running process var filters = MetadataHelper.GetAttributes <BepInProcess>(plugin); if (filters.Length == 0) //no filters means it loads everywhere { return(true); } var result = filters.Any(x => x.ProcessName.ToLower().Replace(".exe", "") == currentProcess); if (!result) { Logger.LogInfo($"Skipping over plugin [{metadata.GUID}] due to process filter"); } return(result); }) .ToList(); Logger.LogInfo($"{selectedPluginTypes.Count} / {globalPluginTypes.Count} plugins to load"); var dependencyDict = new Dictionary <string, IEnumerable <string> >(); var pluginsByGUID = new Dictionary <string, Type>(); foreach (Type t in selectedPluginTypes) { var dependencies = MetadataHelper.GetDependencies(t, selectedPluginTypes); var metadata = MetadataHelper.GetMetadata(t); if (metadata.GUID == null) { Logger.LogWarning($"Skipping [{metadata.Name}] because it does not have a valid GUID."); continue; } if (dependencyDict.ContainsKey(metadata.GUID)) { Logger.LogWarning($"Skipping [{metadata.Name}] because its GUID ({metadata.GUID}) is already used by another plugin."); continue; } dependencyDict[metadata.GUID] = dependencies.Select(d => d.DependencyGUID); pluginsByGUID[metadata.GUID] = t; } var emptyDependencies = new string[0]; // Sort plugins by their dependencies. // Give missing dependencies no dependencies of its own, which will cause missing plugins to be first in the resulting list. var sortedPlugins = Utility.TopologicalSort(dependencyDict.Keys, x => dependencyDict.TryGetValue(x, out var deps) ? deps : emptyDependencies).ToList(); var invalidPlugins = new HashSet <string>(); var processedPlugins = new HashSet <string>(); foreach (var pluginGUID in sortedPlugins) { // If the plugin is missing, don't process it if (!pluginsByGUID.TryGetValue(pluginGUID, out var pluginType)) { continue; } var metadata = MetadataHelper.GetMetadata(pluginType); var dependencies = MetadataHelper.GetDependencies(pluginType, selectedPluginTypes); var dependsOnInvalidPlugin = false; var missingDependencies = new List <string>(); foreach (var dependency in dependencies) { // If the depenency wasn't already processed, it's missing altogether if (!processedPlugins.Contains(dependency.DependencyGUID)) { // If the dependency is hard, collect it into a list to show if ((dependency.Flags & BepInDependency.DependencyFlags.HardDependency) != 0) { missingDependencies.Add(dependency.DependencyGUID); } continue; } // If the dependency is invalid (e.g. has missing depedencies), report that to the user if (invalidPlugins.Contains(dependency.DependencyGUID)) { dependsOnInvalidPlugin = true; break; } } processedPlugins.Add(pluginGUID); if (dependsOnInvalidPlugin) { Logger.LogWarning($"Skipping [{metadata.Name}] because it has a dependency that was not loaded. See above errors for details."); continue; } if (missingDependencies.Count != 0) { Logger.LogError($@"Missing the following dependencies for [{metadata.Name}]: {"\r\n"}{ string.Join("\r\n", missingDependencies.Select(s => $"- {s}").ToArray()) }{"\r\n"}Loading will be skipped; expect further errors and unstabilities." ); invalidPlugins.Add(pluginGUID); continue; } try { Logger.LogInfo($"Loading [{metadata.Name} {metadata.Version}]"); Plugins.Add((BaseUnityPlugin)ManagerObject.AddComponent(pluginType)); } catch (Exception ex) { invalidPlugins.Add(pluginGUID); Logger.LogError($"Error loading [{metadata.Name}] : {ex.Message}"); Logger.LogDebug(ex); } } } catch (Exception ex) { ConsoleWindow.Attach(); Console.WriteLine("Error occurred starting the game"); Console.WriteLine(ex.ToString()); } Logger.LogMessage("Chainloader startup complete"); _loaded = true; }
/// <summary> /// The entrypoint for the BepInEx plugin system. /// </summary> 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); } if (!Directory.Exists(Paths.PatcherPluginPath)) { Directory.CreateDirectory(Paths.PatcherPluginPath); } try { 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.LogMessage("Chainloader started"); ManagerObject = new GameObject("BepInEx_Manager"); UnityEngine.Object.DontDestroyOnLoad(ManagerObject); string currentProcess = Process.GetCurrentProcess().ProcessName.ToLower(); var globalPluginTypes = TypeLoader.LoadTypes <BaseUnityPlugin>(Paths.PluginPath).ToList(); var selectedPluginTypes = globalPluginTypes .Where(plugin => { //Ensure metadata exists var metadata = MetadataHelper.GetMetadata(plugin); if (metadata == null) { Logger.LogWarning($"Skipping over type [{plugin.Name}] as no metadata attribute is specified"); return(false); } //Perform a filter for currently running process var filters = MetadataHelper.GetAttributes <BepInProcess>(plugin); if (filters.Length == 0) //no filters means it loads everywhere { return(true); } var result = filters.Any(x => x.ProcessName.ToLower().Replace(".exe", "") == currentProcess); if (!result) { Logger.LogInfo($"Skipping over plugin [{metadata.GUID}] due to process filter"); } return(result); }) .ToList(); Logger.LogInfo($"{selectedPluginTypes.Count} / {globalPluginTypes.Count} plugins to load"); Dictionary <Type, IEnumerable <Type> > dependencyDict = new Dictionary <Type, IEnumerable <Type> >(); foreach (Type t in selectedPluginTypes) { try { IEnumerable <Type> dependencies = MetadataHelper.GetDependencies(t, selectedPluginTypes); dependencyDict[t] = dependencies; } catch (MissingDependencyException) { var metadata = MetadataHelper.GetMetadata(t); Logger.LogWarning($"Cannot load [{metadata.Name}] due to missing dependencies."); } } var sortedTypes = Utility.TopologicalSort(dependencyDict.Keys, x => dependencyDict[x]).ToList(); foreach (Type t in sortedTypes) { try { var metadata = MetadataHelper.GetMetadata(t); Logger.LogInfo($"Loading [{metadata.Name} {metadata.Version}]"); var plugin = (BaseUnityPlugin)ManagerObject.AddComponent(t); Plugins.Add(plugin); } catch (Exception ex) { Logger.LogError($"Error loading [{t.Name}] : {ex.Message}"); Logger.LogDebug(ex); } } } catch (Exception ex) { ConsoleWindow.Attach(); Console.WriteLine("Error occurred starting the game"); Console.WriteLine(ex.ToString()); } Logger.LogMessage("Chainloader startup complete"); _loaded = true; }