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));
            }
        }
Beispiel #2
0
 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());
            }
        }
Beispiel #5
0
        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));
            }
        }
Beispiel #7
0
 private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e) => MelonLogger.Error((e.ExceptionObject as Exception).ToString());
Beispiel #8
0
 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; }
 }
Beispiel #9
0
 private static void SendError(string txt) => MelonLogger.ManualMelonError(MelonUtils.GetMelonFromStackTrace(), txt ?? "null");