コード例 #1
0
 private static void LogModLoadTime(QMod mod)
 {
     if (elapsedTimes.TryGetValue(mod, out string elapsed))
     {
         AddLog($"- {mod.DisplayName} ({mod.Id}) - {elapsed}");
     }
     else
     {
         AddLog($"- {mod.DisplayName} ({mod.Id}) - Unknown load time");
     }
 }
コード例 #2
0
ファイル: QMod.cs プロジェクト: nesrak1/QModManager
        //// <summary>
        //// The <see cref="MessageReceiver"/>s and <see cref="GlobalMessageReceiver"/>s defined in this mod
        //// </summary>
        //[JsonIgnore] public Dictionary<IQMod, List<MethodInfo>> MessageReceivers { get; set; }

        internal static QMod FromJsonFile(string file)
        {
            try
            {
                JsonSerializerSettings settings = new JsonSerializerSettings
                {
                    MissingMemberHandling = MissingMemberHandling.Ignore,
                };

                string json = File.ReadAllText(file);
                QMod   mod  = JsonConvert.DeserializeObject <QMod>(json);

                if (mod == null)
                {
                    return(null);
                }

                if (mod.Game == "BelowZero")
                {
                    mod.ParsedGame = Patcher.Game.BelowZero;
                }
                else if (mod.Game == "Both")
                {
                    mod.ParsedGame = Patcher.Game.Both;
                }
                else
                {
                    mod.ParsedGame = Patcher.Game.Subnautica;
                }

                try
                {
                    mod.ParsedVersion = new Version(mod.Version);
                }
                catch (Exception e)
                {
                    Logger.Error($"There was an error parsing version \"{mod.Version}\" for mod \"{mod.DisplayName}\"");
                    Logger.Exception(e);
                    mod.ParsedVersion = null;
                }

                return(mod);
            }
            catch (Exception e)
            {
                Logger.Error($"\"mod.json\" deserialization failed for file \"{file}\"!");
                Logger.Exception(e);

                return(null);
            }
        }
コード例 #3
0
ファイル: Patcher.cs プロジェクト: nesrak1/QModManager
        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);
        }
コード例 #4
0
            public string[] LoadBeforeOtherMods = new string[] { }; // Might not be needed

            //public QMod() { }

            public static QMod FromJsonFile(string file)
            {
                try
                {
                    JsonSerializerSettings settings = new JsonSerializerSettings
                    {
                        MissingMemberHandling = MissingMemberHandling.Ignore
                    };

                    string json = File.ReadAllText(file);
                    QMod   mod  = JsonConvert.DeserializeObject <QMod>(json);

                    return(mod);
                }
                catch (Exception e)
                {
                    AddLog("ERROR! mod.json deserialization failed!");
                    AddLog(e.Message);
                    AddLog(e.StackTrace);

                    return(null);
                }
            }
コード例 #5
0
        /// <summary>
        /// Uses reflection to load a mod
        /// </summary>
        /// <param name="mod">The mod to load</param>
        /// <returns>The loaded mod</returns>
        internal static QMod LoadMod(QMod mod)
        {
            if (mod == null)
            {
                return(null);
            }

            if (string.IsNullOrEmpty(mod.EntryMethod))
            {
                AddLog($"ERROR! No EntryMethod specified for mod {mod.DisplayName}");
            }
            else
            {
                try
                {
                    if (sw.IsRunning)
                    {
                        sw.Stop();
                    }
                    sw.Reset();
                    sw.Start();

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

                    MethodInfo qPatchMethod = mod.LoadedAssembly.GetType(entryType).GetMethod(entryMethod);
                    qPatchMethod.Invoke(mod.LoadedAssembly, new object[] { });

                    sw.Stop();

                    string elapsedTime = ParseTime(sw);

                    elapsedTimes.Add(mod, elapsedTime);
                }
                catch (ArgumentNullException e)
                {
                    AddLog($"ERROR! Could not parse entry method {mod.AssemblyName} for mod {mod.DisplayName}");
                    if (e.InnerException != null)
                    {
                        AddLog(e.InnerException.Message);
                        AddLog(e.InnerException.StackTrace);
                    }
                    return(null);
                }
                catch (TargetInvocationException e)
                {
                    AddLog($"ERROR! Invoking the specified entry method {mod.EntryMethod} failed for mod {mod.Id}");
                    if (e.InnerException != null)
                    {
                        AddLog(e.InnerException.Message);
                        AddLog(e.InnerException.StackTrace);
                    }
                    return(null);
                }
                catch (Exception e)
                {
                    AddLog("ERROR! An unexpected error occurred!");
                    AddLog(e.Message);
                    AddLog(e.StackTrace);
                    if (e.InnerException != null)
                    {
                        AddLog(e.InnerException.Message);
                        AddLog(e.InnerException.StackTrace);
                    }
                    return(null);
                }
            }

            return(mod);
        }
コード例 #6
0
        /// <summary>
        /// Loads the mods
        /// </summary>
        internal static void LoadMods()
        {
            AppDomain.CurrentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs args)
            {
                var allDlls = new DirectoryInfo(QModBaseDir).GetFiles("*.dll", SearchOption.AllDirectories);
                foreach (var dll in allDlls)
                {
                    if (args.Name.Contains(Path.GetFileNameWithoutExtension(dll.Name)))
                    {
                        FileInfo[] modjson = dll.Directory.GetFiles("mod.json", SearchOption.TopDirectoryOnly);
                        if (modjson.Length != 0)
                        {
                            try
                            {
                                if (Newtonsoft.Json.Linq.JObject.Parse(File.ReadAllText(modjson[0].FullName))
                                    .TryGetValue("Enabled", out Newtonsoft.Json.Linq.JToken isEnabled) &&
                                    !(bool)isEnabled)
                                {
                                    Console.WriteLine("Cannot resolve Assembly " + dll.Name + " - Disabled by mod.json");
                                    continue;
                                }
                            }
                            catch { /*fail silently*/ }
                        }
                        Console.WriteLine();
                        return(Assembly.LoadFrom(dll.FullName));
                    }
                }

                Console.WriteLine("Could not find assembly " + args.Name);
                return(null);
            };

            if (patched)
            {
                return;
            }

            patched = true;

            if (!Directory.Exists(QModBaseDir))
            {
                AddLog("QMods directory was not found! Creating...");
                if (QModBaseDir == "ERR")
                {
                    AddLog("There was an error creating the QMods directory");
                    AddLog("Please make sure that you ran TerraTech from Steam");
                }
                try
                {
                    Directory.CreateDirectory(QModBaseDir);
                    AddLog("QMods directory created successfully!");
                }
                catch (Exception e)
                {
                    AddLog("EXCEPTION CAUGHT!");
                    AddLog(e.Message);
                    AddLog(e.StackTrace);
                    if (e.InnerException != null)
                    {
                        AddLog("INNER EXCEPTION:");
                        AddLog(e.InnerException.Message);
                        AddLog(e.InnerException.StackTrace);
                    }
                }
                Console.WriteLine(ParseLog());
                return;
            }

            var subDirs   = Directory.GetDirectories(QModBaseDir);
            var lastMods  = new List <QMod>();
            var firstMods = new List <QMod>();
            var otherMods = new List <QMod>();

            foreach (var subDir in subDirs)
            {
                try
                {
                    var jsonFile = Path.Combine(subDir, "mod.json");

                    if (!File.Exists(jsonFile))
                    {
                        AddLog($"ERROR! No \"mod.json\" file found in folder \"{subDir}\"");
                        File.WriteAllText(jsonFile, JsonConvert.SerializeObject(new QMod()));
                        AddLog("A template file was created");
                        continue;
                    }

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

                    if (mod == (null))
                    {
                        continue;
                    }

                    if (mod.Enable == false)
                    {
                        AddLog($"- {mod.DisplayName} is Disabled, skipping");
                        continue;
                    }

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

                    if (!File.Exists(modAssemblyPath))
                    {
                        AddLog($"ERROR! No matching dll found at \"{modAssemblyPath}\" for mod \"{mod.DisplayName}\"");
                        continue;
                    }

                    mod.LoadedAssembly  = Assembly.LoadFrom(modAssemblyPath);
                    mod.ModAssemblyPath = modAssemblyPath;

                    if (mod.Priority.Equals("Last"))
                    {
                        lastMods.Add(mod);
                        continue;
                    }
                    else if (mod.Priority.Equals("First"))
                    {
                        firstMods.Add(mod);
                        continue;
                    }
                    else
                    {
                        otherMods.Add(mod);
                        continue;
                    }
                }
                catch (Exception E)
                {
                    AddLog($"ERROR! Failed to read mod \"{subDir}\": {E.Message}\n{E.StackTrace}");
                }
            }

            // LoadBefore and LoadAfter stuff

            AddLog(" ");
            AddLog("Installed mods:");

            foreach (var mod in firstMods)
            {
                if (mod != null)
                {
                    loadedMods.Add(LoadMod(mod));
                    LogModLoadTime(mod);
                }
            }

            foreach (var mod in otherMods)
            {
                if (mod != null)
                {
                    loadedMods.Add(LoadMod(mod));
                    LogModLoadTime(mod);
                }
            }

            foreach (var mod in lastMods)
            {
                if (mod != null)
                {
                    loadedMods.Add(LoadMod(mod));
                    LogModLoadTime(mod);
                }
            }

            if (sw.IsRunning)
            {
                sw.Stop();
            }

            FlagGame();

            Console.WriteLine(ParseLog());
        }
コード例 #7
0
ファイル: Patcher.cs プロジェクト: nesrak1/QModManager
        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();
        }
コード例 #8
0
ファイル: Patcher.cs プロジェクト: nesrak1/QModManager
        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();
        }
コード例 #9
0
ファイル: QMod.cs プロジェクト: nesrak1/QModManager
        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);
        }