public void PassStarted(IPass pass) { if (pass == null) { throw new ArgumentNullException(nameof(pass)); } logger.Info(pass.Name + " pass"); OnPassStarted.Fire(pass); }
public void RunTestCases(UrlDir gameDatabaseRoot) { if (gameDatabaseRoot == null) { throw new ArgumentNullException(nameof(gameDatabaseRoot)); } logger.Info("Running tests..."); foreach (UrlDir.UrlConfig expect in gameDatabaseRoot.GetConfigs("MMTEST_EXPECT")) { // So for each of the expects, we expect all the configs before that node to match exactly. UrlDir.UrlFile parent = expect.parent; if (parent.configs.Count != expect.config.CountNodes + 1) { logger.Error("Test " + parent.name + " failed as expected number of nodes differs expected: " + expect.config.CountNodes + " found: " + (parent.configs.Count - 1)); for (int i = 0; i < parent.configs.Count; ++i) { logger.Info(parent.configs[i].config.ToString()); } continue; } for (int i = 0; i < expect.config.CountNodes; ++i) { ConfigNode gotNode = parent.configs[i].config; ConfigNode expectNode = expect.config.nodes[i]; if (!CompareRecursive(expectNode, gotNode)) { logger.Error("Test " + parent.name + "[" + i + "] failed as expected output and actual output differ.\nexpected:\n" + expectNode + "\nActually got:\n" + gotNode); } } // Purge the tests parent.configs.Clear(); } logger.Info("tests complete."); }
public void Apply(LinkedList <IProtoUrlConfig> databaseConfigs, IPatchProgress progress, IBasicLogger logger) { if (databaseConfigs == null) { throw new ArgumentNullException(nameof(databaseConfigs)); } if (progress == null) { throw new ArgumentNullException(nameof(progress)); } if (logger == null) { throw new ArgumentNullException(nameof(logger)); } PatchContext context = new PatchContext(UrlConfig, databaseConfigs, logger, progress); for (LinkedListNode <IProtoUrlConfig> listNode = databaseConfigs.First; listNode != null; listNode = listNode.Next) { IProtoUrlConfig protoConfig = listNode.Value; try { if (!NodeMatcher.IsMatch(protoConfig.Node)) { continue; } if (loop) { logger.Info($"Looping on {UrlConfig.SafeUrl()} to {protoConfig.FullUrl}"); } do { progress.ApplyingUpdate(protoConfig, UrlConfig); listNode.Value = protoConfig = new ProtoUrlConfig(protoConfig.UrlFile, MMPatchLoader.ModifyNode(new NodeStack(protoConfig.Node), UrlConfig.config, context)); } while (loop && NodeMatcher.IsMatch(protoConfig.Node)); if (loop) { protoConfig.Node.RemoveNodes("MM_PATCH_LOOP"); } } catch (Exception ex) { progress.Exception(UrlConfig, $"Exception while applying update {UrlConfig.SafeUrl()} to {protoConfig.FullUrl}", ex); } } }
public void Apply(UrlDir.UrlFile file, IPatchProgress progress, IBasicLogger logger) { if (file == null) { throw new ArgumentNullException(nameof(file)); } if (progress == null) { throw new ArgumentNullException(nameof(progress)); } if (logger == null) { throw new ArgumentNullException(nameof(logger)); } PatchContext context = new PatchContext(UrlConfig, file.root, logger, progress); for (int i = 0; i < file.configs.Count; i++) { UrlDir.UrlConfig urlConfig = file.configs[i]; try { if (!NodeMatcher.IsMatch(urlConfig.config)) { continue; } if (loop) { logger.Info($"Looping on {UrlConfig.SafeUrl()} to {urlConfig.SafeUrl()}"); } do { progress.ApplyingUpdate(urlConfig, UrlConfig); file.configs[i] = urlConfig = new UrlDir.UrlConfig(file, MMPatchLoader.ModifyNode(new NodeStack(urlConfig.config), UrlConfig.config, context)); } while (loop && NodeMatcher.IsMatch(urlConfig.config)); if (loop) { file.configs[i].config.RemoveNodes("MM_PATCH_LOOP"); } } catch (Exception ex) { progress.Exception(UrlConfig, $"Exception while applying update {UrlConfig.SafeUrl()} to {urlConfig.SafeUrl()}", ex); } } }
public IEnumerator Run() { PostPatchLoader.Instance.databaseConfigs = null; if (!Directory.Exists(logsDirPath)) { Directory.CreateDirectory(logsDirPath); } kspLogger.Info("Patching started on a new thread, all output will be directed to " + logPath); MessageQueue <ILogMessage> mmLogQueue = new MessageQueue <ILogMessage>(); QueueLogRunner logRunner = new QueueLogRunner(mmLogQueue); ITaskStatus loggingThreadStatus = BackgroundTask.Start(delegate { using StreamLogger streamLogger = new StreamLogger(new FileStream(logPath, FileMode.Create)); streamLogger.Info("Log started at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); logRunner.Run(streamLogger); streamLogger.Info("Done!"); });
private void ApplyPatches(IEnumerable <UrlDir.UrlFile> configFiles, IPass pass) { logger.Info(pass.Name + " pass"); Activity = "ModuleManager " + pass.Name; foreach (IPatch patch in pass) { try { foreach (UrlDir.UrlFile file in configFiles) { patch.Apply(file, progress, logger); } progress.PatchApplied(); } catch (Exception e) { progress.Exception(patch.UrlConfig, "Exception while processing node : " + patch.UrlConfig.SafeUrl(), e); logger.Error("Processed node was\n" + patch.UrlConfig.PrettyPrint()); } } }
public IEnumerator Run() { PostPatchLoader.Instance.databaseConfigs = null; if (!Directory.Exists(logsDirPath)) { Directory.CreateDirectory(logsDirPath); } kspLogger.Info("Patching started on a new thread, all output will be directed to " + logPath); MessageQueue <ILogMessage> mmLogQueue = new MessageQueue <ILogMessage>(); QueueLogRunner logRunner = new QueueLogRunner(mmLogQueue); ITaskStatus loggingThreadStatus = BackgroundTask.Start(delegate { using (StreamLogger streamLogger = new StreamLogger(new FileStream(logPath, FileMode.Create))) { logRunner.Run(streamLogger); streamLogger.Info("Done!"); } }); // Wait for game database to be initialized for the 2nd time and wait for any plugins to initialize yield return(null); yield return(null); IBasicLogger mmLogger = new QueueLogger(mmLogQueue); IEnumerable <ModListGenerator.ModAddedByAssembly> modsAddedByAssemblies = ModListGenerator.GetAdditionalModsFromStaticMethods(mmLogger); IEnumerable <IProtoUrlConfig> databaseConfigs = null; MMPatchLoader patchLoader = new MMPatchLoader(modsAddedByAssemblies, mmLogger); ITaskStatus patchingThreadStatus = BackgroundTask.Start(delegate { databaseConfigs = patchLoader.Run(); }); while (true) { yield return(null); if (!patchingThreadStatus.IsRunning) { logRunner.RequestStop(); } Status = patchLoader.status; Errors = patchLoader.errors; if (!patchingThreadStatus.IsRunning && !loggingThreadStatus.IsRunning) { break; } } if (patchingThreadStatus.IsExitedWithError) { kspLogger.Exception("The patching thread threw an exception", patchingThreadStatus.Exception); FatalErrorHandler.HandleFatalError("The patching thread threw an exception"); } if (loggingThreadStatus.IsExitedWithError) { kspLogger.Exception("The logging thread threw an exception", loggingThreadStatus.Exception); FatalErrorHandler.HandleFatalError("The logging thread threw an exception"); } if (databaseConfigs == null) { kspLogger.Error("The patcher returned a null collection of configs"); FatalErrorHandler.HandleFatalError("The patcher returned a null collection of configs"); yield break; } PostPatchLoader.Instance.databaseConfigs = databaseConfigs; }
private void ApplyPatches(string stage, IEnumerable <UrlDir.UrlConfig> patches) { logger.Info(stage + " pass"); Activity = "ModuleManager " + stage; foreach (UrlDir.UrlConfig mod in patches) { try { string name = mod.type.RemoveWS(); Command cmd = CommandParser.Parse(name, out string tmp); if (cmd == Command.Insert) { logger.Warning("Warning - Encountered insert node that should not exist at this stage: " + mod.SafeUrl()); continue; } else if (cmd != Command.Edit && cmd != Command.Copy && cmd != Command.Delete) { logger.Warning("Invalid command encountered on a patch: " + mod.SafeUrl()); continue; } string upperName = name.ToUpper(); PatchContext context = new PatchContext(mod, databaseRoot, logger, progress); char[] sep = { '[', ']' }; string condition = ""; if (upperName.Contains(":HAS[")) { int start = upperName.IndexOf(":HAS["); condition = name.Substring(start + 5, name.LastIndexOf(']') - start - 5); name = name.Substring(0, start); } string[] splits = name.Split(sep, 3); string[] patterns = splits.Length > 1 ? splits[1].Split(',', '|') : null; string type = splits[0].Substring(1); bool loop = mod.config.HasNode("MM_PATCH_LOOP"); foreach (UrlDir.UrlFile file in allConfigFiles) { if (cmd == Command.Edit) { foreach (UrlDir.UrlConfig url in file.configs) { if (!IsMatch(url, type, patterns, condition)) { continue; } if (loop) { logger.Info("Looping on " + mod.SafeUrl() + " to " + url.SafeUrl()); } do { progress.ApplyingUpdate(url, mod); url.config = MMPatchLoader.ModifyNode(new NodeStack(url.config), mod.config, context); } while (loop && IsMatch(url, type, patterns, condition)); if (loop) { url.config.RemoveNodes("MM_PATCH_LOOP"); } } } else if (cmd == Command.Copy) { // Avoid checking the new configs we are creating int count = file.configs.Count; for (int i = 0; i < count; i++) { UrlDir.UrlConfig url = file.configs[i]; if (!IsMatch(url, type, patterns, condition)) { continue; } ConfigNode clone = MMPatchLoader.ModifyNode(new NodeStack(url.config), mod.config, context); if (url.config.HasValue("name") && url.config.GetValue("name") == clone.GetValue("name")) { progress.Error(mod, $"Error - when applying copy {mod.SafeUrl()} to {url.SafeUrl()} - the copy needs to have a different name than the parent (use @name = xxx)"); } else { progress.ApplyingCopy(url, mod); file.AddConfig(clone); } } } else if (cmd == Command.Delete) { int i = 0; while (i < file.configs.Count) { UrlDir.UrlConfig url = file.configs[i]; if (IsMatch(url, type, patterns, condition)) { progress.ApplyingDelete(url, mod); file.configs.RemoveAt(i); } else { i++; } } } else { throw new NotImplementedException("This code should not be reachable"); } } progress.PatchApplied(); } catch (Exception e) { progress.Exception(mod, "Exception while processing node : " + mod.SafeUrl(), e); try { logger.Error("Processed node was\n" + mod.PrettyPrint()); } catch (Exception ex2) { logger.Exception("Exception while attempting to print a node", ex2); } } } }
private IEnumerator Run() { Stopwatch waitTimer = new Stopwatch(); waitTimer.Start(); progressTitle = "ModuleManager: Waiting for patching to finish"; while (databaseConfigs == null) { yield return(null); } waitTimer.Stop(); logger.Info("Waited " + ((float)waitTimer.ElapsedMilliseconds / 1000).ToString("F3") + "s for patching to finish"); Stopwatch postPatchTimer = new Stopwatch(); postPatchTimer.Start(); progressTitle = "ModuleManager: Applying patched game database"; logger.Info("Applying patched game database"); foreach (UrlDir.UrlFile file in GameDatabase.Instance.root.AllConfigFiles) { file.configs.Clear(); } foreach (IProtoUrlConfig protoConfig in databaseConfigs) { protoConfig.UrlFile.AddConfig(protoConfig.Node); } databaseConfigs = null; yield return(null); #if false // Using an dedicated external log is nice. Dumping it into KSP.log breaking the known formats is not. if (File.Exists(logPath)) { progressTitle = "ModuleManager: Dumping log to KSP log"; logger.Info("Dumping ModuleManager log to main log"); logger.Info("\n#### BEGIN MODULEMANAGER LOG ####\n\n\n" + File.ReadAllText(logPath) + "\n\n\n#### END MODULEMANAGER LOG ####"); } else { logger.Error("ModuleManager log does not exist: " + logPath); } #endif yield return(null); #if DEBUG InGameTestRunner testRunner = new InGameTestRunner(logger); testRunner.RunTestCases(GameDatabase.Instance.root); #endif yield return(null); progressTitle = "ModuleManager: Reloading things"; logger.Info("Reloading resources definitions"); PartResourceLibrary.Instance.LoadDefinitions(); logger.Info("Reloading Trait configs"); GameDatabase.Instance.ExperienceConfigs.LoadTraitConfigs(); logger.Info("Reloading Part Upgrades"); PartUpgradeManager.Handler.FillUpgrades(); yield return(null); progressTitle = "ModuleManager: Running post patch callbacks"; logger.Info("Running post patch callbacks"); foreach (ModuleManagerPostPatchCallback callback in postPatchCallbacks) { try { callback(); } catch (Exception e) { logger.Exception("Exception while running a post patch callback", e); } yield return(null); } yield return(null); // Call all "public static void ModuleManagerPostLoad()" on all class foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies()) { try { foreach (Type type in ass.GetTypes()) { MethodInfo method = type.GetMethod("ModuleManagerPostLoad", BindingFlags.Public | BindingFlags.Static); if (method != null && method.GetParameters().Length == 0) { try { logger.Info("Calling " + ass.GetName().Name + "." + type.Name + "." + method.Name + "()"); method.Invoke(null, null); } catch (Exception e) { logger.Exception("Exception while calling " + ass.GetName().Name + "." + type.Name + "." + method.Name + "()", e); } } } } catch (Exception e) { logger.Exception("Post run call threw an exception in loading " + ass.FullName, e); } } yield return(null); // Call "public void ModuleManagerPostLoad()" on all active MonoBehaviour instance foreach (MonoBehaviour obj in FindObjectsOfType <MonoBehaviour>()) { MethodInfo method = obj.GetType().GetMethod("ModuleManagerPostLoad", BindingFlags.Public | BindingFlags.Instance); if (method != null && method.GetParameters().Length == 0) { try { logger.Info("Calling " + obj.GetType().Name + "." + method.Name + "()"); method.Invoke(obj, null); } catch (Exception e) { logger.Exception("Exception while calling " + obj.GetType().Name + "." + method.Name + "() :", e); } } } yield return(null); if (ModuleManager.dumpPostPatch) { ModuleManager.OutputAllConfigs(); } postPatchTimer.Stop(); logger.Info("Post patch ran in " + ((float)postPatchTimer.ElapsedMilliseconds / 1000).ToString("F3") + "s"); ready = true; }
public static IEnumerable <ModAddedByAssembly> GetAdditionalModsFromStaticMethods(IBasicLogger logger) { List <ModAddedByAssembly> result = new List <ModAddedByAssembly>(); foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies()) { try { foreach (Type type in ass.GetTypes()) { MethodInfo method = type.GetMethod("ModuleManagerAddToModList", BindingFlags.Public | BindingFlags.Static); if (method != null && method.GetParameters().Length == 0 && typeof(IEnumerable <string>).IsAssignableFrom(method.ReturnType)) { string methodName = $"{ass.GetName().Name}.{type.Name}.{method.Name}()"; try { logger.Info("Calling " + methodName); IEnumerable <string> modsToAdd = (IEnumerable <string>)method.Invoke(null, null); if (modsToAdd == null) { logger.Error("ModuleManagerAddToModList returned null: " + methodName); continue; } foreach (string mod in modsToAdd) { result.Add(new ModAddedByAssembly(mod, ass.GetName().Name)); } } catch (Exception e) { logger.Exception("Exception while calling " + methodName, e); } } } } catch (Exception e) { logger.Exception("Add to mod list threw an exception in loading " + ass.FullName, e); } } foreach (MonoBehaviour obj in UnityEngine.Object.FindObjectsOfType <MonoBehaviour>()) { MethodInfo method = obj.GetType().GetMethod("ModuleManagerAddToModList", BindingFlags.Public | BindingFlags.Instance); if (method != null && method.GetParameters().Length == 0 && typeof(IEnumerable <string>).IsAssignableFrom(method.ReturnType)) { string methodName = $"{obj.GetType().Name}.{method.Name}()"; try { logger.Info("Calling " + methodName); IEnumerable <string> modsToAdd = (IEnumerable <string>)method.Invoke(obj, null); if (modsToAdd == null) { logger.Error("ModuleManagerAddToModList returned null: " + methodName); continue; } foreach (string mod in modsToAdd) { result.Add(new ModAddedByAssembly(mod, obj.GetType().Assembly.GetName().Name)); } } catch (Exception e) { logger.Exception("Exception while calling " + methodName, e); } } } return(result); }
public static IEnumerable <string> GenerateModList(IEnumerable <ModAddedByAssembly> modsAddedByAssemblies, IPatchProgress progress, IBasicLogger logger) { #region List of mods //string envInfo = "ModuleManager env info\n"; //envInfo += " " + Environment.OSVersion.Platform + " " + ModuleManager.intPtr.ToInt64().ToString("X16") + "\n"; //envInfo += " " + Convert.ToString(ModuleManager.intPtr.ToInt64(), 2) + " " + Convert.ToString(ModuleManager.intPtr.ToInt64() >> 63, 2) + "\n"; //string gamePath = Environment.GetCommandLineArgs()[0]; //envInfo += " Args: " + gamePath.Split(Path.DirectorySeparatorChar).Last() + " " + string.Join(" ", Environment.GetCommandLineArgs().Skip(1).ToArray()) + "\n"; //envInfo += " Executable SHA256 " + FileSHA(gamePath); // //log(envInfo); List <string> mods = new List <string>(); StringBuilder modListInfo = new StringBuilder(); modListInfo.Append("compiling list of loaded mods...\nMod DLLs found:\n"); string format = " {0,-40}{1,-25}{2,-25}{3,-25}{4}\n"; modListInfo.AppendFormat( format, "Name", "Assembly Version", "Assembly File Version", "KSPAssembly Version", "SHA256" ); modListInfo.Append('\n'); foreach (AssemblyLoader.LoadedAssembly mod in AssemblyLoader.loadedAssemblies) { if (string.IsNullOrEmpty(mod.assembly.Location)) //Diazo Edit for xEvilReeperx AssemblyReloader mod { continue; } FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(mod.assembly.Location); AssemblyName assemblyName = mod.assembly.GetName(); string kspAssemblyVersion; if (mod.versionMajor == 0 && mod.versionMinor == 0) { kspAssemblyVersion = ""; } else { kspAssemblyVersion = mod.versionMajor + "." + mod.versionMinor; } string fileSha = ""; try { fileSha = FileUtils.FileSHA(mod.assembly.Location); } catch (Exception e) { progress.Exception("Exception while generating SHA for assembly " + assemblyName.Name, e); } modListInfo.AppendFormat( format, assemblyName.Name, assemblyName.Version, fileVersionInfo.FileVersion, kspAssemblyVersion, fileSha ); // modlist += String.Format(" {0,-50} SHA256 {1}\n", modInfo, FileSHA(mod.assembly.Location)); if (!mods.Contains(assemblyName.Name, StringComparer.OrdinalIgnoreCase)) { mods.Add(assemblyName.Name); } } modListInfo.Append("Non-DLL mods added (:FOR[xxx]):\n"); foreach (UrlDir.UrlConfig cfgmod in GameDatabase.Instance.root.AllConfigs) { if (CommandParser.Parse(cfgmod.type, out string name) != Command.Insert) { if (name.Contains(":FOR[")) { name = name.RemoveWS(); // check for FOR[] blocks that don't match loaded DLLs and add them to the pass list try { string dependency = name.Substring(name.IndexOf(":FOR[") + 5); dependency = dependency.Substring(0, dependency.IndexOf(']')); if (!mods.Contains(dependency, StringComparer.OrdinalIgnoreCase)) { // found one, now add it to the list. mods.Add(dependency); modListInfo.AppendFormat(" {0}\n", dependency); } } catch (ArgumentOutOfRangeException) { progress.Error(cfgmod, "Skipping :FOR init for line " + name + ". The line most likely contains a space that should be removed"); } } } } modListInfo.Append("Mods by directory (sub directories of GameData):\n"); UrlDir gameData = GameDatabase.Instance.root.children.First(dir => dir.type == UrlDir.DirectoryType.GameData); foreach (UrlDir subDir in gameData.children) { string cleanName = subDir.name.RemoveWS(); if (!mods.Contains(cleanName, StringComparer.OrdinalIgnoreCase)) { mods.Add(cleanName); modListInfo.AppendFormat(" {0}\n", cleanName); } } modListInfo.Append("Mods added by assemblies:\n"); foreach (ModAddedByAssembly mod in modsAddedByAssemblies) { if (!mods.Contains(mod.modName, StringComparer.OrdinalIgnoreCase)) { mods.Add(mod.modName); modListInfo.AppendFormat(" {0}\n", mod); } } logger.Info(modListInfo.ToString()); mods.Sort(); #endregion List of mods return(mods); }
public void TestInfo() { logger.Info("well hi there"); logger.AssertInfo("well hi there"); }
public void TestInfo() { logger.Info("well hi there"); logger.Received().Log(LogType.Log, "well hi there"); }
public void ApplyingUpdate(UrlDir.UrlConfig original, UrlDir.UrlConfig patch) { logger.Info($"Applying update {patch.SafeUrl()} to {original.SafeUrl()}"); Counter.patchedNodes.Increment(); }