Beispiel #1
0
        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);
        }
Beispiel #3
0
        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));
        }
Beispiel #4
0
        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));
                    }
                }
Beispiel #10
0
        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;
        }
Beispiel #11
0
        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;
        }
Beispiel #13
0
        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);
                }
            }
        }