Example #1
0
        private static void LogStatus(List <QMod> mods, ModStatus statusToReport, string summary, Logger.Level logLevel)
        {
            List <QMod> specificMods = mods.FindAll(mod => mod.Status == statusToReport);

            if (specificMods.Count == 0)
            {
                return;
            }

            Logger.Log(logLevel, summary);
            foreach (QMod mod in specificMods)
            {
                Console.WriteLine($"- {mod.DisplayName} ({mod.Id})");
            }
        }
Example #2
0
        internal static bool LoadMod(QMod mod)
        {
            if (mod == null || mod.Loaded)
            {
                return(false);
            }

            try
            {
                string[] entryMethodSig = mod.EntryMethod.Split('.');
                string   entryType      = string.Join(".", entryMethodSig.Take(entryMethodSig.Length - 1).ToArray());
                string   entryMethod    = entryMethodSig[entryMethodSig.Length - 1];

                MethodInfo patchMethod = mod.LoadedAssembly.GetType(entryType).GetMethod(entryMethod);
                patchMethod.Invoke(mod.LoadedAssembly, new object[] { });
            }
            catch (ArgumentNullException e)
            {
                Logger.Error($"Could not parse entry method \"{mod.AssemblyName}\" for mod \"{mod.Id}\"");
                Logger.Exception(e);
                erroredMods.Add(mod);

                return(false);
            }
            catch (TargetInvocationException e)
            {
                Logger.Error($"Invoking the specified entry method \"{mod.EntryMethod}\" failed for mod \"{mod.Id}\"");
                Logger.Exception(e);
                return(false);
            }
            catch (Exception e)
            {
                Logger.Error($"An unexpected error occurred whilst trying to load mod \"{mod.Id}\"");
                Logger.Exception(e);
                return(false);
            }

            if (QModAPI.ErroredMods.Contains(mod?.LoadedAssembly))
            {
                Logger.Error($"Mod \"{mod.Id}\" could not be loaded.");
                QModAPI.ErroredMods.Remove(mod?.LoadedAssembly);
                return(false);
            }
            mod.Loaded = true;
            Logger.Info($"Loaded mod \"{mod.Id}\"");

            return(true);
        }
Example #3
0
        private static void CheckOldHarmony(IEnumerable <QMod> mods)
        {
            var modsThatUseOldHarmony = new List <QMod>();

            foreach (QMod mod in mods)
            {
                if (mod.IsLoaded && mod.HarmonyOutdated)
                {
                    modsThatUseOldHarmony.Add(mod);
                }
            }

            if (modsThatUseOldHarmony.Count > 0)
            {
                Logger.Warn($"Some mods are using an old version of harmony! This will NOT cause any problems, but it's not recommended:");
                foreach (QMod mod in modsThatUseOldHarmony)
                {
                    Console.WriteLine($"- {mod.DisplayName} ({mod.Id})");
                }
            }
        }
Example #4
0
        /// <inheritdoc />
        public override GameObject GetGameObject()
        {
            switch (_type)
            {
            case CompactType.Ingot:
                // ReSharper disable once AccessToStaticMemberViaDerivedType
                var prefab = GameObject.Instantiate(CraftData.GetPrefabForTechType(TechType.TitaniumIngot));
                Logger.Log(Logger.Level.Debug, $"Game Object instantiated for custom ingot ({ClassID})");
                //try to use a custom material
                if (ModAssets.Materials.TryGetValue(_baseType, out var mat))
                {
                    //material exists
                    var renderer = prefab.GetComponentInChildren <Renderer>();
                    renderer.material = mat;
                }

                return(prefab);

            default:
                throw new NotImplementedException($"Compact type {_type.ToString()} does not exist yet.");
            }
        }
Example #5
0
 internal static void Parse(string versionStr)
 {
     try
     {
         Version currentVersion = Assembly.GetExecutingAssembly().GetName().Version;
         if (versionStr == null)
         {
             Logger.Error("There was an error retrieving the latest version from GitHub!");
             return;
         }
         Version latestVersion = new Version(versionStr);
         if (latestVersion == null)
         {
             Logger.Error("There was an error retrieving the latest version from GitHub!");
             return;
         }
         if (latestVersion > currentVersion)
         {
             Logger.Info($"Newer version found: {latestVersion.ToStringParsed()} (current version: {currentVersion.ToStringParsed()}");
             if (Patcher.erroredMods.Count <= 0)
             {
                 Dialog.Show(
                     $"There is a newer version of QModManager available: {latestVersion.ToStringParsed()} (current version: {currentVersion.ToStringParsed()})",
                     Dialog.Button.download, Dialog.Button.close, true);
             }
         }
         else
         {
             Logger.Info($"Recieved latest version from GitHub. We are up to date!");
         }
     }
     catch (Exception e)
     {
         Logger.Error("There was an error retrieving the latest version from GitHub!");
         Logger.Exception(e);
         return;
     }
 }
Example #6
0
        internal static void RemoveDuplicateModIDs()
        {
            List <QMod> duplicateModIDs = new List <QMod>();

            foreach (QMod mod in sortedMods)
            {
                List <QMod> matchingMods = sortedMods.Where((QMod qmod) => qmod.Id == mod.Id).ToList();
                if (matchingMods.Count > 1)
                {
                    foreach (QMod duplicateMod in matchingMods)
                    {
                        if (!duplicateModIDs.Contains(duplicateMod))
                        {
                            duplicateModIDs.Add(duplicateMod);
                        }
                        if (!erroredMods.Contains(duplicateMod))
                        {
                            erroredMods.Add(duplicateMod);
                        }
                    }
                }
            }

            if (duplicateModIDs.Count > 0)
            {
                string toWrite = $"Multiple mods with the same ID found:\n";
                foreach (QMod mod in duplicateModIDs)
                {
                    if (sortedMods.Contains(mod))
                    {
                        sortedMods.Remove(mod);
                    }
                    toWrite += $"- {mod.DisplayName} ({mod.Id})\n";
                }

                Logger.Error(toWrite);
            }
        }
Example #7
0
        internal static void Patch()
        {
            try
            {
                if (patched)
                {
                    Logger.Warn("Patch method was called multiple times!");
                    return;
                }
                patched = true;

                Logger.Info($"Loading QModManager v{Assembly.GetExecutingAssembly().GetName().Version.ToStringParsed()}...");

                if (QModBaseDir == null)
                {
                    Logger.Fatal("A fatal error has occurred.");
                    Logger.Fatal("There was an error with the QMods directory");
                    Logger.Fatal("Please make sure that you ran Subnautica from Steam/Epic/Discord, and not from the executable file!");
                    return;
                }

                try
                {
                    Logger.Info($"Folder structure:\n{IOUtilities.GetFolderStructureAsTree()}\n");
                }
                catch (Exception e)
                {
                    Logger.Error("There was an error while trying to display the folder structure.");
                    Logger.Exception(e);
                }

                QModHooks.Load();
#pragma warning disable CS0618 // Type or member is obsolete
                Hooks.Load();
#pragma warning restore CS0618 // Type or member is obsolete

                PirateCheck.IsPirate(Environment.CurrentDirectory);

                if (!DetectGame())
                {
                    return;
                }

                PatchHarmony();

                if (NitroxCheck.IsInstalled)
                {
                    Logger.Fatal($"Nitrox was detected!");
                    Dialog.Show("Both QModManager and Nitrox detected. QModManager is not compatible with Nitrox. Please uninstall one of them.", Dialog.Button.disabled, Dialog.Button.disabled, false);
                    return;
                }

                StartLoadingMods();
                ShowErroredMods();

                VersionCheck.Check();

                QModHooks.Start += PrefabDebugger.Main;

                QModHooks.OnLoadEnd?.Invoke();
#pragma warning disable CS0618 // Type or member is obsolete
                Hooks.OnLoadEnd?.Invoke();
#pragma warning restore CS0618 // Type or member is obsolete

                Logger.Info($"Finished loading QModManager. Loaded {loadedMods.Count} mods");
            }
            catch (Exception e)
            {
                Logger.Error("EXCEPTION CAUGHT!");
                Logger.Exception(e);
            }
        }
Example #8
0
        internal static void LoadAllMods()
        {
            string toWrite = "Loaded mods:\n";

            List <QMod> loadingErrorMods = new List <QMod>();
            QMod        smlHelper        = null;

            foreach (QMod mod in sortedMods)
            {
                if (mod != null && !mod.Loaded)
                {
                    if (mod.Id != "SMLHelper")
                    {
                        if (!LoadMod(mod))
                        {
                            if (!erroredMods.Contains(mod))
                            {
                                erroredMods.Add(mod);
                            }

                            if (!loadingErrorMods.Contains(mod))
                            {
                                loadingErrorMods.Add(mod);
                            }

                            continue;
                        }
                        else
                        {
                            toWrite += $"- {mod.DisplayName} ({mod.Id})\n";
                            loadedMods.Add(mod);
                        }
                    }
                    else
                    {
                        smlHelper = mod;
                    }
                }
            }
            if (smlHelper != null)
            {
                if (!LoadMod(smlHelper))
                {
                    if (!erroredMods.Contains(smlHelper))
                    {
                        erroredMods.Add(smlHelper);
                    }

                    if (!loadingErrorMods.Contains(smlHelper))
                    {
                        loadingErrorMods.Add(smlHelper);
                    }
                }
                else
                {
                    toWrite += $"- {smlHelper.DisplayName} ({smlHelper.Id})\n";
                    loadedMods.Add(smlHelper);
                }
            }

            if (loadingErrorMods.Count != 0)
            {
                string write = "The following mods could not be loaded:\n";

                foreach (QMod mod in loadingErrorMods)
                {
                    write += $"- {mod.DisplayName} ({mod.Id})\n";
                }

                Logger.Error(write);
            }

            Logger.Info(toWrite);

            CheckOldHarmony();
        }
Example #9
0
        internal static void StartLoadingMods()
        {
            Logger.Info("Started loading mods");

            AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
            {
                FileInfo[] allDlls = new DirectoryInfo(QModBaseDir).GetFiles("*.dll", SearchOption.AllDirectories);
                foreach (FileInfo dll in allDlls)
                {
                    if (args.Name.Contains(Path.GetFileNameWithoutExtension(dll.Name)))
                    {
                        return(Assembly.LoadFrom(dll.FullName));
                    }
                }

                return(null);
            };

            Logger.Debug("Added AssemblyResolve event");

            if (!Directory.Exists(QModBaseDir))
            {
                Logger.Info("QMods directory was not found! Creating...");

                return;
            }

            string[] subDirs = Directory.GetDirectories(QModBaseDir);

            foreach (string subDir in subDirs)
            {
                if (Directory.GetFiles(subDir, "*.dll", SearchOption.TopDirectoryOnly).Length < 1)
                {
                    continue;
                }

                string folderName = new DirectoryInfo(subDir).Name;
                string jsonFile   = Path.Combine(subDir, "mod.json");

                if (!File.Exists(jsonFile))
                {
                    Logger.Error($"No \"mod.json\" file found for mod located in folder \"{subDir}\". A template file will be created");
                    File.WriteAllText(jsonFile, JsonConvert.SerializeObject(new QMod()));
                    erroredMods.Add(QMod.CreateFakeQMod(folderName));
                    continue;
                }

                QMod mod = QMod.FromJsonFile(Path.Combine(subDir, "mod.json"));

                if (!QMod.QModValid(mod, folderName))
                {
                    erroredMods.Add(QMod.CreateFakeQMod(folderName));

                    continue;
                }

                if (mod.Enable == false)
                {
                    Logger.Info($"Mod \"{mod.DisplayName}\" is disabled via config, skipping...");

                    continue;
                }

                string modAssemblyPath = Path.Combine(subDir, mod.AssemblyName);

                if (!File.Exists(modAssemblyPath))
                {
                    Logger.Error($"No matching dll found at \"{modAssemblyPath}\" for mod \"{mod.DisplayName}\"");
                    erroredMods.Add(mod);

                    continue;
                }

                mod.LoadedAssembly  = Assembly.LoadFrom(modAssemblyPath);
                mod.ModAssemblyPath = modAssemblyPath;
                //mod.MessageReceivers = GetMessageRecievers(mod.LoadedAssembly);

                foundMods.Add(mod);
            }

            // Add the found mods into the sortedMods list
            sortedMods.AddRange(foundMods);

            // Disable mods that are not for the detected game
            // (Disable Subnautica mods if Below Zero is detected and disable Below Zero mods if Subnautica is detected)
            DisableNonApplicableMods();

            // Remove mods with duplicate mod ids if any are found
            RemoveDuplicateModIDs();

            // Sort the mods based on their LoadBefore and LoadAfter properties
            // If any mods break (i.e., a loop is found), they are removed from the list so that they aren't loaded
            // And are outputted into the log.
            SortMods();

            // Check if all the mods' dependencies are present
            // If a mod's dependecies aren't present, that mods isn't loaded and it is outputted in the log.
            CheckForDependencies();

            // Finally, load all the mods after sorting and checking for dependencies.
            // If anything goes wrong during loading, it is outputted in the log.
            LoadAllMods();
        }
Example #10
0
 internal static void PatchHarmony()
 {
     HarmonyInstance.Create("qmodmanager").PatchAll();
     Logger.Debug("Patched!");
 }
Example #11
0
        internal static bool QModValid(QMod mod, string folderName)
        {
            bool success = true;

            if (mod == null)
            {
                Logger.Error($"Skipped a null mod found in folder \"{folderName}\"");

                return(false);
            }

            if (string.IsNullOrEmpty(mod.DisplayName))
            {
                Logger.Error($"Mod found in folder \"{folderName}\" is missing a display name!");

                success = false;
            }

            if (string.IsNullOrEmpty(mod.Id))
            {
                Logger.Error($"Mod found in folder \"{folderName}\" is missing an ID!");

                success = false;
            }
            else if (mod.Id != Regex.Replace(mod.Id, Patcher.IDRegex, "", RegexOptions.IgnoreCase))
            {
                Logger.Warn($"Mod found in folder \"{folderName}\" has an invalid ID! All invalid characters have been removed. (This can cause issues!)");
                mod.Id = Regex.Replace(mod.Id, Patcher.IDRegex, "", RegexOptions.IgnoreCase);
            }

            if (string.IsNullOrEmpty(mod.Author))
            {
                Logger.Error($"Mod found in folder \"{folderName}\" is missing an author!");

                success = false;
            }

            if (string.IsNullOrEmpty(mod.Version))
            {
                Logger.Error($"Mod found in folder \"{folderName}\" is missing a version!");

                success = false;
            }

            if (mod.ParsedVersion == null)
            {
                Logger.Warn($"Mod found in folder \"{folderName}\" has an invalid version!");
            }

            if (string.IsNullOrEmpty(mod.AssemblyName))
            {
                Logger.Error($"Mod found in folder \"{folderName}\" is missing an assembly name!");

                success = false;
            }
            else if (!mod.AssemblyName.EndsWith(".dll"))
            {
                Logger.Error($"Mod found in folder \"{folderName}\" is has an invalid assembly name!");

                success = false;
            }

            if (string.IsNullOrEmpty(mod.EntryMethod))
            {
                Logger.Error($"Mod found in folder \"{folderName}\" is missing an entry point!");

                success = false;
            }
            else if (mod.EntryMethod?.Count(c => c == '.') < 2)
            {
                Logger.Error($"Mod found in folder \"{folderName}\" has an invalid entry point!");

                success = false;
            }

            for (int i = 0; i < mod.LoadAfter.Length; i++)
            {
                string good = Regex.Replace(mod.LoadAfter[i], Patcher.IDRegex, "", RegexOptions.IgnoreCase);
                if (mod.LoadAfter[i] != good)
                {
                    mod.LoadAfter[i] = good;
                }
            }

            for (int i = 0; i < mod.LoadBefore.Length; i++)
            {
                string good = Regex.Replace(mod.LoadBefore[i], Patcher.IDRegex, "", RegexOptions.IgnoreCase);
                if (mod.LoadBefore[i] != good)
                {
                    mod.LoadBefore[i] = good;
                }
            }

            Dictionary <string, string> versionDependenciesLoop = new Dictionary <string, string>(mod.VersionDependencies);

            foreach (KeyValuePair <string, string> kvp in versionDependenciesLoop)
            {
                string good = Regex.Replace(kvp.Key, Patcher.IDRegex, "", RegexOptions.IgnoreCase);
                if (kvp.Key != good)
                {
                    mod.VersionDependencies.Remove(kvp.Key);
                    mod.VersionDependencies.Add(good, kvp.Value);
                }
            }

            return(success);
        }
Example #12
0
        public void OnHandHover(GUIHand hand)
        {
            if (!enabled)
            {
                return;
            }

            Light light = this.gameObject.GetComponentInChildren <Light>();

            Renderer[] renderers = this.gameObject.GetComponentsInChildren <Renderer>();
            if (light == null)
            {
                Logger.Log(Logger.Level.Error, "[ColorizableSpotlight] Light Component is missing!");
                return;
            }

            var reticle = HandReticle.main;

            reticle.SetIcon(HandReticle.IconType.Hand, 1f);

            if (Input.GetKeyDown(KeyCode.R))
            {
                if (light.color.r >= 1.0f)
                {
                    light.color = new Color(0f, light.color.g, light.color.b);
                }
                else
                {
                    light.color = new Color(light.color.r + 0.1f, light.color.g, light.color.b);
                }

                savedColor.r = light.color.r;
                ErrorMessage.AddDebug($"Spotlight: Red levels updated ({light.color.r:0.00}/1)");
            }
            else if (Input.GetKeyDown(KeyCode.G))
            {
                if (light.color.g >= 1.0f)
                {
                    light.color = new Color(light.color.r, 0f, light.color.b);
                }
                else
                {
                    light.color = new Color(light.color.r, light.color.g + 0.1f, light.color.b);
                }

                savedColor.g = light.color.g;
                ErrorMessage.AddDebug($"Spotlight: Green levels updated ({light.color.g:0.00}/1)");
            }
            else if (Input.GetKeyDown(KeyCode.B))
            {
                if (light.color.b >= 1.0f)
                {
                    light.color = new Color(light.color.r, light.color.g, 0f);
                }
                else
                {
                    light.color = new Color(light.color.r, light.color.g, light.color.b + 0.1f);
                }

                savedColor.b = light.color.b;
                ErrorMessage.AddDebug($"Spotlight: Blue levels updated ({light.color.b:0.00}/1)");
            }
            foreach (var renderer in renderers)
            {
                foreach (var material in renderer.materials)
                {
                    material.SetColor(ShaderPropertyID._GlowColor, new Color(light.color.r, light.color.g, light.color.b));
                }
            }
            reticle.SetInteractText($"Press \"R\" to change the Red Levels, current red Level: {light.color.r:0.00}\nPress \"G\" to change the Green Levels, current Green Level: {light.color.g:0.00}\nPress \"B\" to change the Blue Levels, current Blue level: {light.color.b:0.00}");
        }