private static bool IsMatch(UrlDir.UrlConfig url, string type, string[] namePatterns, string constraints) { if (url.type != type) { return(false); } if (namePatterns != null) { if (url.name == url.type) { return(false); } bool match = false; foreach (string pattern in namePatterns) { if (MMPatchLoader.WildcardMatch(url.name, pattern)) { match = true; break; } } if (!match) { return(false); } } return(MMPatchLoader.CheckConstraints(url.config, constraints)); }
public void TestModifyNode__MultiplyValue() { ConfigNode c1 = new TestConfigNode("NODE") { { "foo", "3" }, { "foo", "5" }, }; UrlDir.UrlConfig c2u = UrlBuilder.CreateConfig("abc/def", new TestConfigNode("@NODE") { { "@foo *", "2" }, }, root); PatchContext context = new PatchContext(c2u, root, logger, progress); ConfigNode c3 = MMPatchLoader.ModifyNode(new NodeStack(c1), c2u.config, context); EnsureNoErrors(); AssertConfigNodesEqual(new TestConfigNode("NODE") { { "foo", "6" }, { "foo", "5" }, }, c3); }
public bool IsMatch(ConfigNode node) { if (node.name != type) { return(false); } if (namePatterns != null) { string name = node.GetValue("name"); if (name == null) { return(false); } bool match = false; foreach (string pattern in namePatterns) { if (MMPatchLoader.WildcardMatch(name, pattern)) { match = true; break; } } if (!match) { return(false); } } return(MMPatchLoader.CheckConstraints(node, constraints)); }
public void TestModifyNode__EditNode__SpecialCharacters() { ConfigNode c1 = new TestConfigNode("NODE") { new TestConfigNode("INNER_NODE") { { "weird_values", "some\r\n\tstuff" }, }, }; UrlDir.UrlConfig c2u = UrlBuilder.CreateConfig("abc/def", new TestConfigNode("@NODE") { new TestConfigNode("@INNER_NODE") { { "another_weird_value", "some\r\nmore\tstuff" }, }, }); PatchContext context = new PatchContext(c2u, Enumerable.Empty <IProtoUrlConfig>(), logger, progress); ConfigNode c3 = MMPatchLoader.ModifyNode(new NodeStack(c1), c2u.config, context); EnsureNoErrors(); AssertConfigNodesEqual(new TestConfigNode("NODE") { new TestConfigNode("INNER_NODE") { { "weird_values", "some\r\n\tstuff" }, { "another_weird_value", "some\r\nmore\tstuff" }, }, }, c3); }
public void TestModifyNode__IndexAllWithAssign() { ConfigNode c1 = new TestConfigNode("NODE") { { "foo", "bar1" }, { "foo", "bar2" }, }; UrlDir.UrlConfig c2u = UrlBuilder.CreateConfig("abc/def", new TestConfigNode("@NODE") { { "@foo,*", "bar3" }, }); PatchContext context = new PatchContext(c2u, Enumerable.Empty <IProtoUrlConfig>(), logger, progress); ConfigNode c3 = MMPatchLoader.ModifyNode(new NodeStack(c1), c2u.config, context); EnsureNoErrors(); AssertConfigNodesEqual(new TestConfigNode("NODE") { { "foo", "bar3" }, { "foo", "bar3" }, }, c3); }
public IEnumerator Run() { PostPatchLoader.Instance.databaseConfigs = null; // Wait for game database to be initialized for the 2nd time and wait for any plugins to initialize yield return(null); yield return(null); IEnumerable <ModListGenerator.ModAddedByAssembly> modsAddedByAssemblies = ModListGenerator.GetAdditionalModsFromStaticMethods(ModLogger.Instance); IEnumerable <IProtoUrlConfig> databaseConfigs = null; this.patchLoader = new MMPatchLoader(modsAddedByAssemblies, ModLogger.Instance, this.counter, this.timings); ITaskStatus patchingThreadStatus = BackgroundTask.Start(delegate { databaseConfigs = patchLoader.Run(); }); while (true) { yield return(null); Status = patchLoader.status; Errors = patchLoader.errors; if (!patchingThreadStatus.IsRunning) { break; } } if (patchingThreadStatus.IsExitedWithError) { kspLogger.Exception("The patching thread threw an exception", patchingThreadStatus.Exception); FatalErrorHandler.HandleFatalError("The patching thread threw an exception"); yield break; } 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; }
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 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); // 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]; try { if (!NodeMatcher.IsMatch(url.config)) { continue; } ConfigNode clone = MMPatchLoader.ModifyNode(new NodeStack(url.config), UrlConfig.config, context); if (url.config.HasValue("name") && url.config.GetValue("name") == clone.GetValue("name")) { progress.Error(UrlConfig, $"Error - when applying copy {UrlConfig.SafeUrl()} to {url.SafeUrl()} - the copy needs to have a different name than the parent (use @name = xxx)"); } else { progress.ApplyingCopy(url, UrlConfig); file.AddConfig(clone); } } catch (Exception ex) { progress.Exception(UrlConfig, $"Exception while applying copy {UrlConfig.SafeUrl()} to {url.SafeUrl()}", ex); } } }
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; } ConfigNode clone = MMPatchLoader.ModifyNode(new NodeStack(protoConfig.Node), UrlConfig.config, context); if (protoConfig.Node.GetValue("name") is string name && name == clone.GetValue("name")) { progress.Error(UrlConfig, $"Error - when applying copy {UrlConfig.SafeUrl()} to {protoConfig.FullUrl} - the copy needs to have a different name than the parent (use @name = xxx)"); } else { progress.ApplyingCopy(protoConfig, UrlConfig); listNode = databaseConfigs.AddAfter(listNode, new ProtoUrlConfig(protoConfig.UrlFile, clone)); } }
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); } } } }
internal void Awake() { totalTime.Start(); // Allow loading the background in the laoding screen Application.runInBackground = true; QualitySettings.vSyncCount = 0; Application.targetFrameRate = -1; // More cool loading screen. Less 4 stoke logo. for (int i = 0; i < LoadingScreen.Instance.Screens.Count; i++) { var state = LoadingScreen.Instance.Screens[i]; state.fadeInTime = i < 3 ? 0.1f : 1; state.displayTime = i < 3 ? 1 : 3; state.fadeOutTime = i < 3 ? 0.1f : 1; } TextMeshProUGUI[] texts = LoadingScreen.Instance.gameObject.GetComponentsInChildren <TextMeshProUGUI>(); foreach (var text in texts) { textPos = Mathf.Min(textPos, text.rectTransform.localPosition.y); } // Ensure that only one copy of the service is run per scene change. if (loadedInScene || !ElectionAndCheck()) { Assembly currentAssembly = Assembly.GetExecutingAssembly(); Log("Multiple copies of current version. Using the first copy. Version: " + currentAssembly.GetName().Version); Destroy(gameObject); return; } DontDestroyOnLoad(gameObject); Version v = Assembly.GetExecutingAssembly().GetName().Version; version = v.Major + "." + v.Minor + "." + v.Build; // Subscribe to the RnD center spawn/deSpawn events GameEvents.onGUIRnDComplexSpawn.Add(OnRnDCenterSpawn); GameEvents.onGUIRnDComplexDespawn.Add(OnRnDCenterDeSpawn); LoadingScreen screen = FindObjectOfType <LoadingScreen>(); if (screen == null) { Log("Can't find LoadingScreen type. Aborting ModuleManager execution"); return; } List <LoadingSystem> list = LoadingScreen.Instance.loaders; if (list != null) { // So you can insert a LoadingSystem object in this list at any point. // GameDatabase is first in the list, and PartLoader is second // We could insert ModuleManager after GameDatabase to get it to run there // and SaveGameFixer after PartLoader. GameObject aGameObject = new GameObject("ModuleManager"); MMPatchLoader loader = aGameObject.AddComponent <MMPatchLoader>(); Log(string.Format("Adding ModuleManager to the loading screen {0}", list.Count)); int gameDatabaseIndex = list.FindIndex(s => s is GameDatabase); list.Insert(gameDatabaseIndex + 1, loader); } bool foolsDay = (DateTime.Now.Month == 4 && DateTime.Now.Day == 1); bool catDay = (DateTime.Now.Month == 2 && DateTime.Now.Day == 22); nyan = foolsDay || Environment.GetCommandLineArgs().Contains("-nyan-nyan"); nCats = catDay || Environment.GetCommandLineArgs().Contains("-ncats"); dumpPostPatch = Environment.GetCommandLineArgs().Contains("-mm-dump"); loadedInScene = true; }
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); } } }