private DependencyGraph(IList <T> mods, Func <T, string> modNameGetter) { int size = mods.Count; vertices = new Vertex[size]; IDictionary <string, Vertex> nameLookup = new Dictionary <string, Vertex>(size); // Create a vertex in the dependency graph for each mod to load for (int i = 0; i < size; ++i) { Assembly modAssembly = mods[i].Assembly; string modName = modNameGetter(mods[i]); Vertex modVertex = new Vertex(i, mods[i], modName); vertices[i] = modVertex; nameLookup[modAssembly.GetName().Name] = modVertex; } // Add an edge for each dependency between mods IDictionary <string, IList <AssemblyName> > modsWithMissingDeps = new SortedDictionary <string, IList <AssemblyName> >(); List <AssemblyName> missingDependencies = new List <AssemblyName>(); HashSet <string> optionalDependencies = new HashSet <string>(); foreach (Vertex modVertex in vertices) { Assembly modAssembly = modVertex.mod.Assembly; missingDependencies.Clear(); optionalDependencies.Clear(); MelonOptionalDependenciesAttribute optionals = (MelonOptionalDependenciesAttribute)Attribute.GetCustomAttribute(modAssembly, typeof(MelonOptionalDependenciesAttribute)); if (optionals != null && optionals.AssemblyNames != null) { optionalDependencies.UnionWith(optionals.AssemblyNames); } foreach (AssemblyName dependency in modAssembly.GetReferencedAssemblies()) { if (nameLookup.TryGetValue(dependency.Name, out Vertex dependencyVertex)) { modVertex.dependencies.Add(dependencyVertex); dependencyVertex.dependents.Add(modVertex); } else if (!TryLoad(dependency) && !optionalDependencies.Contains(dependency.Name)) { missingDependencies.Add(dependency); } } if (missingDependencies.Count > 0) { // modVertex.skipLoading = true; modsWithMissingDeps.Add(modNameGetter(modVertex.mod), missingDependencies.ToArray()); } } if (modsWithMissingDeps.Count > 0) { // Some mods are missing dependencies. Don't load these mods and show an error message MelonLogger.LogWarning(BuildMissingDependencyMessage(modsWithMissingDeps)); } }