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)); } }
private static void LogException(Exception ex) => MelonLogger.Warning($"Patch Shield Exception: {ex}");
private static bool Initialize() { string GeneratorProcessPath = Path.Combine(Path.Combine(Path.Combine(Path.Combine(Imports.GetGameDirectory(), "MelonLoader"), "Dependencies"), "AssemblyGenerator"), "MelonLoader.AssemblyGenerator.exe"); if (!File.Exists(GeneratorProcessPath)) { MelonLogger.LogError("MelonLoader.AssemblyGenerator.exe does not Exist!"); return(false); } var generatorProcessInfo = new ProcessStartInfo(GeneratorProcessPath); generatorProcessInfo.Arguments = $"\"{MelonLoaderBase.UnityVersion}\" {"\"" + Regex.Replace(Imports.GetGameDirectory(), @"(\\+)$", @"$1$1") + "\""} {"\"" + Regex.Replace(Imports.GetGameDataDirectory(), @"(\\+)$", @"$1$1") + "\""} {(Force_Regenerate() ? "true" : "false")} {(string.IsNullOrEmpty(Force_Version_Unhollower()) ? "" : Force_Version_Unhollower())}"; generatorProcessInfo.UseShellExecute = false; generatorProcessInfo.RedirectStandardOutput = true; generatorProcessInfo.CreateNoWindow = true; Process process = null; try { process = Process.Start(generatorProcessInfo); } catch (Exception e) { MelonLogger.LogError(e.ToString()); MelonLogger.LogError("Unable to Start Assembly Generator!"); return(false); } var stdout = process.StandardOutput; while (!stdout.EndOfStream) { MelonLogger.Log(stdout.ReadLine()); } while (!process.HasExited) { Thread.Sleep(100); } if (process.ExitCode != 0) { MelonLogger.Native_ThrowInternalError($"Assembly Generator exited with code {process.ExitCode}"); return(false); } if (Imports.IsDebugMode()) { MelonLogger.Log($"Assembly Generator ran Successfully!"); } return(true); }
private void TopologicalSortInto(IList <T> loadedMods) { int[] unloadedDependencies = new int[vertices.Length]; SortedList <string, Vertex> loadableMods = new SortedList <string, Vertex>(); int skippedMods = 0; // Find all sinks in the dependency graph, i.e. mods without any dependencies on other mods for (int i = 0; i < vertices.Length; ++i) { Vertex vertex = vertices[i]; int dependencyCount = vertex.dependencies.Count; unloadedDependencies[i] = dependencyCount; if (dependencyCount == 0) { loadableMods.Add(vertex.name, vertex); } } // Perform the (reverse) topological sorting while (loadableMods.Count > 0) { Vertex mod = loadableMods.Values[0]; loadableMods.RemoveAt(0); if (!mod.skipLoading) { loadedMods.Add(mod.mod); } else { ++skippedMods; } foreach (Vertex dependent in mod.dependents) { unloadedDependencies[dependent.index] -= 1; dependent.skipLoading |= mod.skipLoading; if (unloadedDependencies[dependent.index] == 0) { loadableMods.Add(dependent.name, dependent); } } } // Check if all mods were either loaded or skipped. If this is not the case, there is a cycle in the dependency graph if (loadedMods.Count + skippedMods < vertices.Length) { StringBuilder errorMessage = new StringBuilder("Some mods could not be loaded due to a cyclic dependency:\n"); for (int i = 0; i < vertices.Length; ++i) { if (unloadedDependencies[i] > 0) { errorMessage.Append($"- '{vertices[i].name}'\n"); } } errorMessage.Length -= 1; // Remove trailing newline MelonLogger.LogError(errorMessage.ToString()); } }
public override void OnApplicationStart() { Instance = this; base.OnApplicationStart(); ClassInjector.RegisterTypeInIl2Cpp <Grenade>(); ClassInjector.RegisterTypeInIl2Cpp <PinScript>(); ClassInjector.RegisterTypeInIl2Cpp <HandleScript>(); var folder = new DirectoryInfo(Path.Combine(Path.GetDirectoryName(Application.dataPath), "UserData", "Grenades")); if (!folder.Exists) { folder.Create(); } else { foreach (var file in folder.EnumerateFiles("*.grenade")) { try { var bundle = AssetBundle.LoadFromFile(file.FullName); bundle.hideFlags = HideFlags.DontUnloadUnusedAsset; TextAsset text = bundle.LoadAsset <TextAsset>("Grenades.xml"); var xml = XDocument.Parse(text.text); foreach (var grenadeXml in xml.Root.Elements("Grenade")) { var prefab = bundle.LoadAsset <GameObject>((string)grenadeXml.Attribute("prefab")); prefab.hideFlags = HideFlags.DontUnloadUnusedAsset; Shaders.ReplaceDummyShaders(prefab); var guid = Guid.NewGuid(); prefab.name = guidPrefix + guid.ToString(); this.definitions[guid] = grenadeXml; var g = prefab.AddComponent <Grenade>(); SpawnMenu.AddItem(prefab, (string)grenadeXml.Attribute("name") ?? "[Grenade]", (int?)grenadeXml.Attribute("pool") ?? 4, (CategoryFilters)Enum.Parse(typeof(CategoryFilters), (string)grenadeXml.Attribute("category") ?? "GADGETS", true)); } } catch (Exception e) { Log.LogError($"Failed when loading grenade bundle: {file.Name}\n{e.Message}\n{e.StackTrace}"); } } } CustomMapIntegration.Init(); if (Environment.GetCommandLineArgs().Contains("--grenades.debug")) { void ExportXml() { foreach (var def in Instance.definitions.Values) { var file = Path.Combine(folder.FullName, $"exported_{(string)def.Attribute("name") ?? "noname"}.xml"); def.Save(file); } } var i = Interfaces.AddNewInterface("Grenades Debug", Color.green); i.CreateFunctionElement("Output all xml", Color.green, null, null, ExportXml); } }
private DependencyGraph(IList <T> melons) { int size = melons.Count; vertices = new Vertex[size]; Dictionary <string, Vertex> nameLookup = new Dictionary <string, Vertex>(size); // Create a vertex in the dependency graph for each Melon to load for (int i = 0; i < size; ++i) { Assembly melonAssembly = melons[i].Assembly; string melonName = melons[i].Info.Name; Vertex melonVertex = new Vertex(i, melons[i], melonName); vertices[i] = melonVertex; nameLookup[melonAssembly.GetName().Name] = melonVertex; } // Add an edge for each dependency between Melons SortedDictionary <string, IList <AssemblyName> > melonsWithMissingDeps = new SortedDictionary <string, IList <AssemblyName> >(); SortedDictionary <string, IList <AssemblyName> > melonsWithIncompatibilities = new SortedDictionary <string, IList <AssemblyName> >(); List <AssemblyName> missingDependencies = new List <AssemblyName>(); List <AssemblyName> incompatibilities = new List <AssemblyName>(); HashSet <string> optionalDependencies = new HashSet <string>(); HashSet <string> additionalDependencies = new HashSet <string>(); foreach (Vertex melonVertex in vertices) { Assembly melonAssembly = melonVertex.melon.Assembly; missingDependencies.Clear(); optionalDependencies.Clear(); incompatibilities.Clear(); additionalDependencies.Clear(); MelonOptionalDependenciesAttribute optionals = (MelonOptionalDependenciesAttribute)Attribute.GetCustomAttribute(melonAssembly, typeof(MelonOptionalDependenciesAttribute)); if ((optionals != null) && (optionals.AssemblyNames != null)) { optionalDependencies.UnionWith(optionals.AssemblyNames); } MelonAdditionalDependenciesAttribute additionals = (MelonAdditionalDependenciesAttribute)Attribute.GetCustomAttribute(melonAssembly, typeof(MelonAdditionalDependenciesAttribute)); if ((additionals != null) && (additionals.AssemblyNames != null)) { additionalDependencies.UnionWith(additionals.AssemblyNames); } MelonIncompatibleAssembliesAttribute incompatibleAssemblies = (MelonIncompatibleAssembliesAttribute)Attribute.GetCustomAttribute(melonAssembly, typeof(MelonIncompatibleAssembliesAttribute)); if ((incompatibleAssemblies != null) && (incompatibleAssemblies.AssemblyNames != null)) { foreach (string name in incompatibleAssemblies.AssemblyNames) { foreach (Vertex v in vertices) { AssemblyName assemblyName = v.melon.Assembly.GetName(); if ((v != melonVertex) && (assemblyName.Name == name)) { incompatibilities.Add(assemblyName); v.skipLoading = true; } } } } foreach (AssemblyName dependency in melonAssembly.GetReferencedAssemblies()) { if (nameLookup.TryGetValue(dependency.Name, out Vertex dependencyVertex)) { melonVertex.dependencies.Add(dependencyVertex); dependencyVertex.dependents.Add(melonVertex); } else if (!TryLoad(dependency) && !optionalDependencies.Contains(dependency.Name)) { missingDependencies.Add(dependency); } } foreach (string dependencyName in additionalDependencies) { AssemblyName dependency = new AssemblyName(dependencyName); if (nameLookup.TryGetValue(dependencyName, out Vertex dependencyVertex)) { melonVertex.dependencies.Add(dependencyVertex); dependencyVertex.dependents.Add(melonVertex); } else if (!TryLoad(dependency)) { missingDependencies.Add(dependency); } } if (missingDependencies.Count > 0) { // melonVertex.skipLoading = true; melonsWithMissingDeps.Add(melonVertex.melon.Info.Name, missingDependencies.ToArray()); } if (incompatibilities.Count > 0) { melonsWithIncompatibilities.Add(melonVertex.melon.Info.Name, incompatibilities.ToArray()); } } // Some Melons are missing dependencies. Don't load these Melons and show an error message if (melonsWithMissingDeps.Count > 0) { MelonLogger.Warning(BuildMissingDependencyMessage(melonsWithMissingDeps)); } if (melonsWithIncompatibilities.Count > 0) { MelonLogger.Warning(BuildIncompatibleAssembliesMessage(melonsWithIncompatibilities)); } }
private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e) => MelonLogger.Error((e.ExceptionObject as Exception).ToString());
static Core() { try { MelonUtils.Setup(); } catch (Exception ex) { MelonLogger.Error("MelonUtils.Setup Exception: " + ex.ToString()); throw ex; } Harmony.HarmonyInstance harmonyInstance = Harmony.HarmonyInstance.Create("MelonLoader"); try { harmonyInstance.Patch(typeof(Thread).GetProperty("CurrentCulture", BindingFlags.Public | BindingFlags.Instance).GetGetMethod(), new Harmony.HarmonyMethod(typeof(Core).GetMethod("GetCurrentCulturePrefix", BindingFlags.NonPublic | BindingFlags.Static))); } catch (Exception ex) { MelonLogger.Warning("Thread.CurrentCulture Exception: " + ex.ToString()); } try { harmonyInstance.Patch(typeof(Thread).GetProperty("CurrentUICulture", BindingFlags.Public | BindingFlags.Instance).GetGetMethod(), new Harmony.HarmonyMethod(typeof(Core).GetMethod("GetCurrentCulturePrefix", BindingFlags.NonPublic | BindingFlags.Static))); } catch (Exception ex) { MelonLogger.Warning("Thread.CurrentUICulture Exception: " + ex.ToString()); } try { ((AppDomainSetup)typeof(AppDomain).GetProperty("SetupInformationNoCopy", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(AppDomain.CurrentDomain, new object[0])).ApplicationBase = MelonUtils.GameDirectory; } catch (Exception ex) { MelonLogger.Warning("AppDomainSetup.ApplicationBase Exception: " + ex.ToString()); } Directory.SetCurrentDirectory(MelonUtils.GameDirectory); AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler; AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolveHandler; AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += AssemblyResolveHandler; try { MelonPreferences.LegacyCheck(); } catch (Exception ex) { MelonLogger.Error("MelonPreferences.LegacyCheck Exception: " + ex.ToString()); MelonPreferences.WasError = true; } try { MelonPreferences.Load_Internal(); } catch (Exception ex) { MelonLogger.Error("MelonPreferences.Load_Internal Exception: " + ex.ToString()); MelonPreferences.WasError = true; } if (MelonPreferences.WasLegacyLoaded) { try { MelonPreferences.Save_Internal(); } catch (Exception ex) { MelonLogger.Error("MelonPreferences.Save_Internal Exception: " + ex.ToString()); MelonPreferences.WasError = true; } } MelonPreferences.SaveAfterEntryCreation = true; try { bHaptics_NativeLibrary.Load(); } catch (Exception ex) { MelonLogger.Error("bHaptics_NativeLibrary.Load Exception: " + ex.ToString()); bHaptics.WasError = true; } }
private static void SendError(string txt) => MelonLogger.ManualMelonError(MelonUtils.GetMelonFromStackTrace(), txt ?? "null");