/// <summary> /// Removes a mock service info from our collections. /// </summary> /// <param name="serviceInfo">Mock service info to remove</param> internal void RemoveService(ServiceInfo serviceInfo) { // If we delete a service : Unbind linked plugins and services. // Unbind generalized services foreach (ServiceInfo s in ServiceInfos.Where(si => si.Generalization == serviceInfo).ToList()) { s.Generalization = null; } // Unbind implementations foreach (PluginInfo p in PluginInfos.Where(pi => pi.Service == serviceInfo).ToList()) { p.Service = null; } // Delete all existing service references foreach (PluginInfo p in PluginInfos) { foreach (MockServiceReferenceInfo reference in p.InternalServiceReferences.Where(r => r.Reference == serviceInfo).ToList()) { p.InternalServiceReferences.Remove(reference); } } _labServiceInfos.Remove(serviceInfo); _serviceInfos.Remove(serviceInfo); }
/// <summary> /// Set an existing plugin's dependency to an existing service. /// </summary> /// <param name="plugin">Plugin</param> /// <param name="service">Service the plugin depends on</param> /// <param name="runningRequirement">How the plugin depends on the service</param> internal void SetPluginDependency(PluginInfo plugin, ServiceInfo service, DependencyRequirement runningRequirement) { Debug.Assert(plugin != null); Debug.Assert(service != null); Debug.Assert(ServiceInfos.Contains(service)); Debug.Assert(PluginInfos.Contains(plugin)); MockServiceReferenceInfo reference = new MockServiceReferenceInfo(plugin, service, runningRequirement); plugin.InternalServiceReferences.Add(reference); }
/// <summary> /// Removes an existing plugin dependency. /// </summary> /// <param name="plugin">Plugin owner.</param> /// <param name="service">Service reference</param> internal void RemovePluginDependency(PluginInfo plugin, ServiceInfo service) { Debug.Assert(plugin != null); Debug.Assert(service != null); Debug.Assert(ServiceInfos.Contains(service)); Debug.Assert(PluginInfos.Contains(plugin)); MockServiceReferenceInfo reference = plugin.InternalServiceReferences.First(x => x.Reference == service); if (reference != null) { plugin.InternalServiceReferences.Remove(reference); } }
/// <summary> /// 加载插件 /// </summary> /// <returns></returns> public virtual int LoadPlugins() { Plugins.Clear(); PluginInfos.Clear(); if (!Directory.Exists(Environment.CurrentDirectory + @"\Plugins")) { return(0); } string[] _files = Directory.GetFiles(Environment.CurrentDirectory + @"\Plugins"); PluginsAttribute _info = new PluginsAttribute(); foreach (var item in _files) { string _ext = Path.GetExtension(item); if (!_ext.Equals(".dll")) { continue; } try { Assembly _asm = DllAssembly = Assembly.UnsafeLoadFrom(item); Type[] _types = _asm.GetTypes(); foreach (var t in _types) { if (t.GetInterface(m_infaceType.Name) != null) { T _plug = (T)_asm.CreateInstance(t.FullName); Plug = t; Plugins.Add(_plug); // 获得插件信息 _info = t.GetCustomAttribute(typeof(PluginsAttribute), false) as PluginsAttribute; PluginInfos.Add(_info); LoadPluginsInfo(); } } } catch { return(0); } } return(PluginInfos.Count); }
/// <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 (ConsoleManager.ConsoleActive) { ConsoleManager.SetConsoleTitle($"{CurrentAssemblyName} {CurrentAssemblyVersion} - {productNameProp?.GetValue(null, null) ?? Paths.ProcessName}"); } Logger.LogMessage("Chainloader started"); ManagerObject = new GameObject("BepInEx_Manager"); UnityEngine.Object.DontDestroyOnLoad(ManagerObject); var pluginsToLoad = TypeLoader.FindPluginTypes(Paths.PluginPath, ToPluginInfo, HasBepinPlugins, "chainloader"); foreach (var keyValuePair in pluginsToLoad) { foreach (var pluginInfo in keyValuePair.Value) { pluginInfo.Location = keyValuePair.Key; } } var pluginInfos = pluginsToLoad.SelectMany(p => p.Value).ToList(); var loadedAssemblies = new Dictionary <string, Assembly>(); Logger.LogInfo($"{pluginInfos.Count} plugins to load"); // We use a sorted dictionary to ensure consistent load order var dependencyDict = new SortedDictionary <string, IEnumerable <string> >(StringComparer.InvariantCultureIgnoreCase); var pluginsByGUID = new Dictionary <string, PluginInfo>(); foreach (var pluginInfoGroup in pluginInfos.GroupBy(info => info.Metadata.GUID)) { PluginInfo loadedVersion = null; foreach (var pluginInfo in pluginInfoGroup.OrderByDescending(x => x.Metadata.Version)) { if (loadedVersion != null) { Logger.LogWarning($"Skipping [{pluginInfo}] because a newer version exists ({loadedVersion})"); continue; } loadedVersion = pluginInfo; // Perform checks that will prevent loading plugins in this run var filters = pluginInfo.Processes.ToList(); bool invalidProcessName = filters.Count != 0 && filters.All(x => !string.Equals(x.ProcessName.Replace(".exe", ""), Paths.ProcessName, StringComparison.InvariantCultureIgnoreCase)); if (invalidProcessName) { Logger.LogWarning($"Skipping [{pluginInfo}] because of process filters ({string.Join(", ", pluginInfo.Processes.Select(p => p.ProcessName).ToArray())})"); continue; } dependencyDict[pluginInfo.Metadata.GUID] = pluginInfo.Dependencies.Select(d => d.DependencyGUID); pluginsByGUID[pluginInfo.Metadata.GUID] = pluginInfo; } } foreach (var pluginInfo in pluginsByGUID.Values.ToList()) { if (pluginInfo.Incompatibilities.Any(incompatibility => pluginsByGUID.ContainsKey(incompatibility.IncompatibilityGUID))) { pluginsByGUID.Remove(pluginInfo.Metadata.GUID); dependencyDict.Remove(pluginInfo.Metadata.GUID); var incompatiblePlugins = pluginInfo.Incompatibilities.Select(x => x.IncompatibilityGUID).Where(x => pluginsByGUID.ContainsKey(x)).ToArray(); string message = $@"Could not load [{pluginInfo}] because it is incompatible with: {string.Join(", ", incompatiblePlugins)}"; DependencyErrors.Add(message); Logger.LogError(message); } else if (PluginTargetsWrongBepin(pluginInfo)) { string message = $@"Plugin [{pluginInfo}] targets a wrong version of BepInEx ({pluginInfo.TargettedBepInExVersion}) and might not work until you update"; DependencyErrors.Add(message); Logger.LogWarning(message); } } 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 Dictionary <string, Version>(); foreach (var pluginGUID in sortedPlugins) { // If the plugin is missing, don't process it if (!pluginsByGUID.TryGetValue(pluginGUID, out var pluginInfo)) { continue; } var dependsOnInvalidPlugin = false; var missingDependencies = new List <BepInDependency>(); foreach (var dependency in pluginInfo.Dependencies) { bool IsHardDependency(BepInDependency dep) => (dep.Flags & BepInDependency.DependencyFlags.HardDependency) != 0; // If the dependency wasn't already processed, it's missing altogether bool dependencyExists = processedPlugins.TryGetValue(dependency.DependencyGUID, out var pluginVersion); if (!dependencyExists || pluginVersion < dependency.MinimumVersion) { // If the dependency is hard, collect it into a list to show if (IsHardDependency(dependency)) { missingDependencies.Add(dependency); } continue; } // If the dependency is invalid (e.g. has missing dependencies) and hard, report that to the user if (invalidPlugins.Contains(dependency.DependencyGUID) && IsHardDependency(dependency)) { dependsOnInvalidPlugin = true; break; } } processedPlugins.Add(pluginGUID, pluginInfo.Metadata.Version); if (dependsOnInvalidPlugin) { string message = $"Skipping [{pluginInfo}] because it has a dependency that was not loaded. See previous errors for details."; DependencyErrors.Add(message); Logger.LogWarning(message); continue; } if (missingDependencies.Count != 0) { bool IsEmptyVersion(Version v) => v.Major == 0 && v.Minor == 0 && v.Build <= 0 && v.Revision <= 0; string message = $@"Could not load [{pluginInfo}] because it has missing dependencies: { string.Join(", ", missingDependencies.Select(s => IsEmptyVersion(s.MinimumVersion) ? s.DependencyGUID : $"{s.DependencyGUID} (v{s.MinimumVersion} or newer)").ToArray()) }" ; DependencyErrors.Add(message); Logger.LogError(message); invalidPlugins.Add(pluginGUID); continue; } try { Logger.LogInfo($"Loading [{pluginInfo}]"); if (!loadedAssemblies.TryGetValue(pluginInfo.Location, out var ass)) { loadedAssemblies[pluginInfo.Location] = ass = Assembly.LoadFile(pluginInfo.Location); } PluginInfos[pluginGUID] = pluginInfo; pluginInfo.Instance = (BaseUnityPlugin)ManagerObject.AddComponent(ass.GetType(pluginInfo.TypeName)); _plugins.Add(pluginInfo.Instance); } catch (Exception ex) { invalidPlugins.Add(pluginGUID); PluginInfos.Remove(pluginGUID); Logger.LogError($"Error loading [{pluginInfo}] : {ex.Message}"); if (ex is ReflectionTypeLoadException re) { Logger.LogDebug(TypeLoader.TypeLoadExceptionToString(re)); } else { Logger.LogDebug(ex); } } } }
//private Assembly AssemblyResolving(AssemblyLoadContext arg1, AssemblyName arg2) //{ // if (arg2.FullName == CurrentAssembly.FullName) // { // return CurrentAssembly; // } // var deps = DependencyContext.Default; // if (deps.CompileLibraries.Any(d => d.Name == arg2.Name)) // { // return Assembly.Load(arg2); // } // foreach (var item in DependencyAssemblies) // { // if (item.FullName == arg2.FullName) // { // return item; // } // } // return null; //} private void ResolveDenpendency(Assembly assembly) { string currentName = assembly.GetName().Name; if (CompileLibraries == null) { CompileLibraries = new HashSet <string>(); foreach (var item in DependencyContext.Default.CompileLibraries) { if (!CompileLibraries.Contains(item.Name)) { CompileLibraries.Add(item.Name); } } } List <CompilationLibrary> dependencyCompilationLibrary = DependencyContext.Load(assembly) .CompileLibraries.Where(de => PluginInfos.All(m => m.Name != de.Name) && de.Name != currentName && !CompileLibraries.Contains(de.Name)) .ToList(); if (LoadedAssemblies == null) { LoadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToDictionary(m => m.GetName().Name); } dependencyCompilationLibrary.Each(libaray => { foreach (var item in libaray.ResolveReferencePaths(new DependencyAssemblyResolver(Path.GetDirectoryName(assembly.Location)))) { string assemblyName = AssemblyName.GetAssemblyName(item).Name; if (!LoadedAssemblies.ContainsKey(assemblyName)) { Assembly assemblyDep = AssemblyLoadContext.Default.LoadFromAssemblyPath(item); DependencyAssemblies.Add(assemblyDep); LoadedAssemblies.Add(assemblyName, assemblyDep); } } }); PluginDescriptor plugin = null; foreach (var typeInfo in assembly.DefinedTypes) { if (typeInfo.IsAbstract || typeInfo.IsInterface) { continue; } if (PluginTypeInfo.IsAssignableFrom(typeInfo)) { plugin = new PluginDescriptor { PluginType = typeInfo.AsType(), Assembly = assembly, Dependency = dependencyCompilationLibrary, CurrentPluginPath = CurrentPath }; } } if (plugin != null) { PluginActivtor.LoadedPlugins.Add(plugin); } }
private string GetWrappedObjConstructorParamStr() { return(PluginInfos.StrConcat((pluginInfo) => $"{pluginInfo.ConcretePluginNamedTypeSymbol.GetFullTypeString()} {pluginInfo.PluginImplementationClassName.FirstCharToLowerCase(true)}")); }
GetWrappedMemberInfos(ISymbol wrapperSymbol) { return (PluginInfos .SelectMany(plugin => plugin.GetPluginMemberInfo(wrapperSymbol).ToCollection().ToList()).ToList()); }
/// <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); var pluginsToLoad = TypeLoader.FindPluginTypes(Paths.PluginPath, ToPluginInfo, HasBepinPlugins, "chainloader"); foreach (var keyValuePair in pluginsToLoad) { foreach (var pluginInfo in keyValuePair.Value) { pluginInfo.Location = keyValuePair.Key; } } var pluginInfos = pluginsToLoad.SelectMany(p => p.Value).ToList(); var loadedAssemblies = new Dictionary <string, Assembly>(); Logger.LogInfo($"{pluginInfos.Count} plugins to load"); var dependencyDict = new Dictionary <string, IEnumerable <string> >(); var pluginsByGUID = new Dictionary <string, PluginInfo>(); foreach (var pluginInfo in pluginInfos) { // Perform checks that will prevent loading plugins in this run var filters = pluginInfo.Processes.ToList(); bool invalidProcessName = filters.Count != 0 && filters.All(x => !string.Equals(x.ProcessName.Replace(".exe", ""), Paths.ProcessName, StringComparison.InvariantCultureIgnoreCase)); if (invalidProcessName) { Logger.LogWarning($"Skipping over plugin [{pluginInfo.Metadata.GUID}] due to process filter"); continue; } if (dependencyDict.ContainsKey(pluginInfo.Metadata.GUID)) { Logger.LogWarning($"Skipping [{pluginInfo.Metadata.Name}] because its GUID ({pluginInfo.Metadata.GUID}) is already used by another plugin."); continue; } dependencyDict[pluginInfo.Metadata.GUID] = pluginInfo.Dependencies.Select(d => d.DependencyGUID).Concat(pluginInfo.Incompatibilities.Select(i => i.IncompatibilityGUID)); pluginsByGUID[pluginInfo.Metadata.GUID] = pluginInfo; } 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 Dictionary <string, Version>(); foreach (var pluginGUID in sortedPlugins) { // If the plugin is missing, don't process it if (!pluginsByGUID.TryGetValue(pluginGUID, out var pluginInfo)) { continue; } var dependsOnInvalidPlugin = false; var missingDependencies = new List <BepInDependency>(); foreach (var dependency in pluginInfo.Dependencies) { // If the depenency wasn't already processed, it's missing altogether bool depenencyExists = processedPlugins.TryGetValue(dependency.DependencyGUID, out var pluginVersion); if (!depenencyExists || pluginVersion < dependency.MinimumVersion) { // If the dependency is hard, collect it into a list to show if ((dependency.Flags & BepInDependency.DependencyFlags.HardDependency) != 0) { missingDependencies.Add(dependency); } continue; } // If the dependency is invalid (e.g. has missing depedencies), report that to the user if (invalidPlugins.Contains(dependency.DependencyGUID)) { dependsOnInvalidPlugin = true; break; } } var incompatibilities = new List <BepInIncompatibility>(); foreach (var incompatibility in pluginInfo.Incompatibilities) { if (processedPlugins.ContainsKey(incompatibility.IncompatibilityGUID)) { incompatibilities.Add(incompatibility); } } processedPlugins.Add(pluginGUID, pluginInfo.Metadata.Version); if (dependsOnInvalidPlugin) { string message = $"Skipping [{pluginInfo.Metadata.Name}] because it has a dependency that was not loaded. See above errors for details."; DependencyErrors.Add(message); Logger.LogWarning(message); continue; } if (missingDependencies.Count != 0) { string ToMissingString(BepInDependency s) { bool emptyVersion = s.MinimumVersion.Major == 0 && s.MinimumVersion.Minor == 0 && s.MinimumVersion.Build == 0 && s.MinimumVersion.Revision == 0; if (emptyVersion) { return("- " + s.DependencyGUID); } return($"- {s.DependencyGUID} (at least v{s.MinimumVersion})"); } string message = $@"Could not load [{pluginInfo.Metadata.Name}] because it has missing dependencies: {string.Join(", ", missingDependencies.Select(ToMissingString).ToArray())}"; DependencyErrors.Add(message); Logger.LogError(message); invalidPlugins.Add(pluginGUID); continue; } if (incompatibilities.Count != 0) { string message = $@"Could not load [{pluginInfo.Metadata.Name}] because it is incompatible with: {string.Join(", ", incompatibilities.Select(i => i.IncompatibilityGUID).ToArray())}"; DependencyErrors.Add(message); Logger.LogError(message); invalidPlugins.Add(pluginGUID); continue; } try { Logger.LogInfo($"Loading [{pluginInfo.Metadata.Name} {pluginInfo.Metadata.Version}]"); if (!loadedAssemblies.TryGetValue(pluginInfo.Location, out var ass)) { loadedAssemblies[pluginInfo.Location] = ass = Assembly.LoadFile(pluginInfo.Location); } PluginInfos[pluginGUID] = pluginInfo; pluginInfo.Instance = (BaseUnityPlugin)ManagerObject.AddComponent(ass.GetType(pluginInfo.TypeName)); Plugins.Add(pluginInfo.Instance); } catch (Exception ex) { invalidPlugins.Add(pluginGUID); PluginInfos.Remove(pluginGUID); Logger.LogError($"Error loading [{pluginInfo.Metadata.Name}] : {ex.Message}"); if (ex is ReflectionTypeLoadException re) { Logger.LogDebug(TypeLoader.TypeLoadExceptionToString(re)); } else { 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; }