Exemplo n.º 1
0
        private static EverestModuleMetadata[] loadZip(string archive)
        {
            try {
                using (ZipFile zip = new ZipFile(archive)) {
                    foreach (ZipEntry entry in zip.Entries)
                    {
                        if (entry.FileName == "metadata.yaml")
                        {
                            using (MemoryStream stream = entry.ExtractStream())
                                using (StreamReader reader = new StreamReader(stream)) {
                                    EverestModuleMetadata meta = YamlHelper.Deserializer.Deserialize <EverestModuleMetadata>(reader);
                                    meta.PathArchive = archive;
                                    meta.PostParse();
                                    return(new EverestModuleMetadata[] { meta });
                                }
                        }

                        if (entry.FileName == "multimetadata.yaml" ||
                            entry.FileName == "everest.yaml" ||
                            entry.FileName == "everest.yml")
                        {
                            using (MemoryStream stream = entry.ExtractStream())
                                using (StreamReader reader = new StreamReader(stream)) {
                                    if (!reader.EndOfStream)
                                    {
                                        EverestModuleMetadata[] multimetas = YamlHelper.Deserializer.Deserialize <EverestModuleMetadata[]>(reader);
                                        foreach (EverestModuleMetadata multimeta in multimetas)
                                        {
                                            multimeta.PathArchive = archive;
                                            multimeta.PostParse();
                                        }
                                        return(multimetas);
                                    }
                                }
                        }
                    }
                }
            } catch (Exception e) {
                Logger.Log(LogLevel.Warn, "loader", $"Failed loading everest.yaml in archive {archive}: {e}");
            }

            return(null);
        }
        private static bool tryUnblacklist(EverestModuleMetadata dependency, Dictionary <EverestModuleMetadata, string> allModsInformation, HashSet <string> modsToUnblacklist)
        {
            KeyValuePair <EverestModuleMetadata, string> match = default;

            // let's find the most recent installed mod that has the required name.
            foreach (KeyValuePair <EverestModuleMetadata, string> candidate in allModsInformation)
            {
                if (dependency.Name == candidate.Key.Name && (match.Key == null || match.Key.Version < candidate.Key.Version))
                {
                    match = candidate;
                }
            }

            if (match.Key == null || !Everest.Loader.Blacklist.Contains(match.Value))
            {
                // no result for this dependency (it isn't actually installed) or the mod isn't blacklisted (so it failed loading for some other reason).
                return(false);
            }

            if (modsToUnblacklist.Contains(match.Value))
            {
                // STOP RIGHT HERE! we already are planning to unblacklist this dependency. No need to go further!
                return(true);
            }

            // this dependency will have to be unblacklisted.
            modsToUnblacklist.Add(match.Value);

            // unblacklist all dependencies for this dependency. if one of them isn't unblacklistable, that doesn't matter: it will fail to load
            // after restarting the game and it will be handled then (this case should be extremely rare anyway).
            foreach (EverestModuleMetadata dependencyDependency in match.Key.Dependencies)
            {
                if (!Everest.Loader.DependencyLoaded(dependencyDependency))
                {
                    tryUnblacklist(dependencyDependency, allModsInformation, modsToUnblacklist);
                }
            }

            // and we are done!
            return(true);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Installs a mod update in the Mods directory once it has been downloaded.
        /// This method will replace the installed mod zip with the one that was just downloaded.
        /// </summary>
        /// <param name="update">The update info coming from the update server</param>
        /// <param name="mod">The mod metadata from Everest for the installed mod</param>
        /// <param name="zipPath">The path to the zip the update has been downloaded to</param>
        public static void InstallModUpdate(ModUpdateInfo update, EverestModuleMetadata mod, string zipPath)
        {
            // let's close the zip, as we will replace it now.
            foreach (ModContent content in Everest.Content.Mods)
            {
                if (content.GetType() == typeof(ZipModContent) && (content as ZipModContent).Path == mod.PathArchive)
                {
                    ZipModContent modZip = content as ZipModContent;

                    Logger.Log("ModUpdaterHelper", $"Closing mod .zip: {modZip.Path}");
                    modZip.Dispose();
                }
            }

            // delete the old zip, and move the new one.
            Logger.Log("ModUpdaterHelper", $"Deleting mod .zip: {mod.PathArchive}");
            File.Delete(mod.PathArchive);

            Logger.Log("ModUpdaterHelper", $"Moving {zipPath} to {mod.PathArchive}");
            File.Move(zipPath, mod.PathArchive);
        }
Exemplo n.º 4
0
        /// <summary>
        /// List all mods needing an update, by comparing the installed mods' hashes with the ones in the update checker database.
        /// </summary>
        /// <param name="updateCatalog">The update checker database (must not be null!)</param>
        /// <returns>A map listing all the updates: info from the update checker database => info from the installed mod</returns>
        public static SortedDictionary <ModUpdateInfo, EverestModuleMetadata> ListAvailableUpdates(Dictionary <string, ModUpdateInfo> updateCatalog)
        {
            SortedDictionary <ModUpdateInfo, EverestModuleMetadata> availableUpdatesCatalog = new SortedDictionary <ModUpdateInfo, EverestModuleMetadata>(new MostRecentUpdatedFirst());

            Logger.Log("ModUpdaterHelper", "Checking for updates");

            foreach (EverestModule module in Everest.Modules)
            {
                EverestModuleMetadata metadata = module.Metadata;
                if (metadata.PathArchive != null && updateCatalog.ContainsKey(metadata.Name))
                {
                    string xxHashStringInstalled = BitConverter.ToString(metadata.Hash).Replace("-", "").ToLowerInvariant();
                    Logger.Log("ModUpdaterHelper", $"Mod {metadata.Name}: installed hash {xxHashStringInstalled}, latest hash(es) {string.Join(", ", updateCatalog[metadata.Name].xxHash)}");
                    if (!updateCatalog[metadata.Name].xxHash.Contains(xxHashStringInstalled))
                    {
                        availableUpdatesCatalog.Add(updateCatalog[metadata.Name], metadata);
                    }
                }
            }

            Logger.Log("ModUpdaterHelper", $"{availableUpdatesCatalog.Count} update(s) available");
            return(availableUpdatesCatalog);
        }
        private static void Load()
        {
            EverestModuleMetadata meta = CelesteTasModule.Instance.Metadata;

            try {
                watcher = new FileSystemWatcher {
                    Path         = Path.GetDirectoryName(meta.DLL),
                    NotifyFilter = NotifyFilters.LastWrite,
                };

                watcher.Changed += (s, e) => {
                    if (e.FullPath == meta.DLL && Manager.Running)
                    {
                        Manager.DisableRun();
                    }
                };

                watcher.EnableRaisingEvents = true;
            } catch (Exception e) {
                e.LogException($"Failed watching folder: {Path.GetDirectoryName(meta.DLL)}");
                Unload();
            }
        }
Exemplo n.º 6
0
        public override void Update()
        {
            if (menu != null && task != null && task.IsCompleted)
            {
                // there is no download or install task in progress

                if (fetchingButton != null)
                {
                    // This means fetching the updates just finished. We have to remove the "Checking for updates" button
                    // and put the actual update list instead.

                    Logger.Log("OuiModUpdateList", "Rendering updates");

                    menu.Remove(fetchingButton);
                    fetchingButton = null;

                    if (updateCatalog == null)
                    {
                        // display an error message
                        TextMenu.Button button = new TextMenu.Button(Dialog.Clean("MODUPDATECHECKER_ERROR"));
                        button.Disabled = true;
                        menu.Add(button);
                    }
                    else if (availableUpdatesCatalog.Count == 0)
                    {
                        // display a dummy "no update available" button
                        TextMenu.Button button = new TextMenu.Button(Dialog.Clean("MODUPDATECHECKER_NOUPDATE"));
                        button.Disabled = true;
                        menu.Add(button);
                    }
                    else
                    {
                        // display one button per update
                        foreach (ModUpdateInfo update in availableUpdatesCatalog.Keys)
                        {
                            EverestModuleMetadata metadata = availableUpdatesCatalog[update];

                            string versionUpdate = metadata.VersionString;
                            if (metadata.VersionString != update.Version)
                            {
                                versionUpdate = $"{metadata.VersionString} > {update.Version}";
                            }

                            TextMenu.Button button = new TextMenu.Button($"{metadata.Name.SpacedPascalCase()} | v. {versionUpdate} ({new DateTime(1970, 1, 1, 0, 0, 0, 0).AddSeconds(update.LastUpdate):yyyy-MM-dd})");
                            button.Pressed(() => {
                                // make the menu non-interactive
                                menu.Focused    = false;
                                button.Disabled = true;

                                // trigger the update download
                                downloadModUpdate(update, metadata, button);
                            });

                            // if there is more than one hash, it means there is multiple downloads for this mod. Thus, we can't update it manually.
                            if (update.xxHash.Count > 1)
                            {
                                button.Disabled = true;
                            }

                            menu.Add(button);
                        }
                    }
                }

                if (menu.Focused && Selected && Input.MenuCancel.Pressed)
                {
                    if (shouldRestart)
                    {
                        Everest.QuickFullRestart();
                    }
                    else
                    {
                        // go back to mod options instead
                        Audio.Play(SFX.ui_main_button_back);
                        Overworld.Goto <OuiModOptions>();
                    }
                }
            }

            base.Update();
        }
        private void ProcessGetModInfo()
        {
            if (Engine.Scene is Level level)
            {
                string MetaToString(EverestModuleMetadata metadata, int Indentation = 0, bool comment = true)
                {
                    return((comment ? "# " : string.Empty) + string.Empty.PadLeft(Indentation) + $"{metadata.Name} {metadata.VersionString}\n");
                }

                List <EverestModuleMetadata> metas = Everest.Modules
                                                     .Where(module => module.Metadata.Name != "UpdateChecker" && module.Metadata.Name != "DialogCutscene")
                                                     .Select(module => module.Metadata).ToList();
                metas.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase));

                AreaData areaData             = AreaData.Get(level);
                string   moduleName           = string.Empty;
                EverestModuleMetadata mapMeta = null;
                if (Everest.Content.TryGet <AssetTypeMap>("Maps/" + areaData.SID, out ModAsset mapModAsset) && mapModAsset.Source != null)
                {
                    moduleName = mapModAsset.Source.Name;
                    mapMeta    = metas.FirstOrDefault(meta => meta.Name == moduleName);
                }

                string command = "";

                EverestModuleMetadata celesteMeta = metas.First(metadata => metadata.Name == "Celeste");
                EverestModuleMetadata everestMeta = metas.First(metadata => metadata.Name == "Everest");
                EverestModuleMetadata tasMeta     = metas.First(metadata => metadata.Name == "CelesteTAS");
                command += MetaToString(celesteMeta);
                command += MetaToString(everestMeta);
                command += MetaToString(tasMeta);
                metas.Remove(celesteMeta);
                metas.Remove(everestMeta);
                metas.Remove(tasMeta);

                EverestModuleMetadata speedrunToolMeta = metas.FirstOrDefault(metadata => metadata.Name == "SpeedrunTool");
                if (speedrunToolMeta != null)
                {
                    command += MetaToString(speedrunToolMeta);
                }

                command += "\n# Map:\n";
                if (mapMeta != null)
                {
                    command += MetaToString(mapMeta, 2);
                }

                string mode = level.Session.Area.Mode == AreaMode.Normal ? "ASide" : level.Session.Area.Mode.ToString();
                command += $"#   {areaData.SID} {mode}\n";

                if (!string.IsNullOrEmpty(moduleName) && mapMeta != null)
                {
                    List <EverestModuleMetadata> dependencies = mapMeta.Dependencies.Where(metadata =>
                                                                                           metadata.Name != "Celeste" && metadata.Name != "Everest" && metadata.Name != "UpdateChecker" &&
                                                                                           metadata.Name != "DialogCutscene" && metadata.Name != "CelesteTAS").ToList();
                    dependencies.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase));
                    if (dependencies.Count > 0)
                    {
                        command += "\n# Dependencies:\n";
                        command += string.Join(string.Empty,
                                               dependencies.Select(meta => metas.First(metadata => metadata.Name == meta.Name)).Select(meta => MetaToString(meta, 2)));
                    }

                    command += "\n# Other Installed Mods:\n";
                    command += string.Join(string.Empty,
                                           metas.Where(meta => meta.Name != moduleName && dependencies.All(metadata => metadata.Name != meta.Name))
                                           .Select(meta => MetaToString(meta, 2)));
                }
                else
                {
                    command += "\n# Other Installed Mods:\n";
                    command += string.Join(string.Empty, metas.Select(meta => MetaToString(meta, 2)));
                }

                byte[] commandBytes = Encoding.Default.GetBytes(command);
                WriteMessageGuaranteed(new Message(MessageIDs.ReturnModInfo, commandBytes));
            }
        }
Exemplo n.º 8
0
        public override IEnumerator Enter(Oui from)
        {
            menu = new TextMenu();

            // display the title and a dummy "Fetching" button
            menu.Add(new TextMenu.Header(Dialog.Clean("UPDATECHECKER_MENU_TITLE")));

            menu.Add(subHeader = new TextMenuExt.SubHeaderExt(Dialog.Clean("UPDATECHECKER_MENU_HEADER")));

            fetchingButton          = new TextMenu.Button(Dialog.Clean("UPDATECHECKER_FETCHING"));
            fetchingButton.Disabled = true;
            menu.Add(fetchingButton);

            Scene.Add(menu);

            menu.Visible = Visible = true;
            menu.Focused = false;

            for (float p = 0f; p < 1f; p += Engine.DeltaTime * 4f)
            {
                menu.X = offScreenX + -1920f * Ease.CubeOut(p);
                alpha  = Ease.CubeOut(p);
                yield return(null);
            }

            menu.Focused = true;

            task = new Task(() => {
                // 1. Download the updates list
                Logger.Log("UpdateChecker", "Downloading last versions list");
                try {
                    using (WebClient wc = new WebClient()) {
                        string yamlData = wc.DownloadString("https://max480-random-stuff.appspot.com/celeste/everest_update.yaml");
                        updateCatalog   = new Deserializer().Deserialize <Dictionary <string, ModUpdateInfo> >(yamlData);
                        foreach (string name in updateCatalog.Keys)
                        {
                            updateCatalog[name].Name = name;
                        }
                        Logger.Log("UpdateChecker", $"Downloaded {updateCatalog.Count} item(s)");
                    }
                }
                catch (Exception e) {
                    Logger.Log("UpdateChecker", $"Download failed! {e.ToString()}");
                }

                // 2. Find out what actually has been updated
                availableUpdatesCatalog.Clear();

                if (updateCatalog != null)
                {
                    Logger.Log("UpdateChecker", "Checking for updates");

                    foreach (EverestModule module in Everest.Modules)
                    {
                        EverestModuleMetadata metadata = module.Metadata;
                        if (metadata.PathArchive != null && updateCatalog.ContainsKey(metadata.Name))
                        {
                            string xxHashStringInstalled = BitConverter.ToString(metadata.Hash).Replace("-", "").ToLowerInvariant();
                            Logger.Log("UpdateChecker", $"Mod {metadata.Name}: installed hash {xxHashStringInstalled}, latest hash(es) {string.Join(", ", updateCatalog[metadata.Name].xxHash)}");
                            if (!updateCatalog[metadata.Name].xxHash.Contains(xxHashStringInstalled))
                            {
                                availableUpdatesCatalog.Add(updateCatalog[metadata.Name], metadata);
                            }
                        }
                    }

                    Logger.Log("UpdateChecker", $"{availableUpdatesCatalog.Count} update(s) available");
                }
            });

            task.Start();
        }
Exemplo n.º 9
0
        private void downloadModUpdate(ModUpdateInfo update, EverestModuleMetadata mod, TextMenu.Button button)
        {
            task = new Task(() => {
                // we will download the mod to Celeste_Directory/mod-update.zip at first.
                string zipPath = Path.Combine(Everest.PathGame, "mod-update.zip");

                try {
                    // download it...
                    button.Label = $"{update.Name} ({Dialog.Clean("UPDATECHECKER_DOWNLOADING")})";
                    downloadMod(update, button, zipPath);

                    // verify its checksum
                    string actualHash   = BitConverter.ToString(Everest.GetChecksum("mod-update.zip")).Replace("-", "").ToLowerInvariant();
                    string expectedHash = update.xxHash[0];
                    Logger.Log("UpdateChecker", $"Verifying checksum: actual hash is {actualHash}, expected hash is {expectedHash}");
                    if (expectedHash != actualHash)
                    {
                        throw new IOException($"Checksum error: expected {expectedHash}, got {actualHash}");
                    }

                    // mark restarting as required, as we will do weird stuff like closing zips afterwards.
                    if (!shouldRestart)
                    {
                        shouldRestart       = true;
                        subHeader.TextColor = Color.OrangeRed;
                        subHeader.Title     = $"{Dialog.Clean("UPDATECHECKER_MENU_HEADER")} ({Dialog.Clean("UPDATECHECKER_WILLRESTART")})";
                    }

                    // install it
                    button.Label = $"{update.Name} ({Dialog.Clean("UPDATECHECKER_INSTALLING")})";
                    installMod(update, mod, zipPath);

                    // done!
                    button.Label = $"{update.Name} ({Dialog.Clean("UPDATECHECKER_UPDATED")})";

                    // select another enabled option: the next one, or the last one if there is no next one.
                    if (menu.Selection + 1 > menu.LastPossibleSelection)
                    {
                        menu.Selection = menu.LastPossibleSelection;
                    }
                    else
                    {
                        menu.Selection++;
                    }
                } catch (Exception e) {
                    // update failed
                    button.Label = $"{update.Name} ({Dialog.Clean("UPDATECHECKER_FAILED")})";
                    Logger.Log("UpdateChecker", $"Updating {update.Name} failed");
                    Logger.LogDetailed(e);
                    button.Disabled = false;

                    // try to delete mod-update.zip if it still exists.
                    if (File.Exists(zipPath))
                    {
                        try {
                            Logger.Log("UpdateChecker", $"Deleting temp file {zipPath}");
                            File.Delete(zipPath);
                        } catch (Exception) {
                            Logger.Log("UpdateChecker", $"Removing {zipPath} failed");
                        }
                    }
                }

                // give the menu control back to the player
                menu.Focused = true;
            });
            task.Start();
        }
Exemplo n.º 10
0
        private void downloadAllDependencies()
        {
            // 1. Compute the list of dependencies we must download.
            LogLine(Dialog.Clean("DEPENDENCYDOWNLOADER_DOWNLOADING_DATABASE"));
            Dictionary <string, ModUpdateInfo> availableDownloads = ModUpdaterHelper.DownloadModUpdateList();

            if (availableDownloads == null)
            {
                shouldAutoRestart = false;
                shouldRestart     = false;

                LogLine(Dialog.Clean("DEPENDENCYDOWNLOADER_DOWNLOAD_DATABASE_FAILED"));
            }
            else
            {
                Logger.Log("OuiDependencyDownloader", "Computing dependencies to download...");

                // these mods are not installed currently, we will install them
                Dictionary <string, ModUpdateInfo> modsToInstall = new Dictionary <string, ModUpdateInfo>();

                // these mods are already installed, but need an update to satisfy the dependency
                Dictionary <string, ModUpdateInfo>         modsToUpdate = new Dictionary <string, ModUpdateInfo>();
                Dictionary <string, EverestModuleMetadata> modsToUpdateCurrentVersions = new Dictionary <string, EverestModuleMetadata>();

                // Everest should be updated to satisfy a dependency on Everest
                bool shouldUpdateEverest = false;

                // these mods are absent from the database
                HashSet <string> modsNotFound = new HashSet <string>();

                // these mods have multiple downloads, and as such, should be installed manually
                HashSet <string> modsNotInstallableAutomatically = new HashSet <string>();

                // these mods are blacklisted, and should be removed from the blacklist instead of being re-installed
                HashSet <string> modsBlacklisted = new HashSet <string>();

                // these mods are in the database, but the version found in there won't satisfy the dependency
                Dictionary <string, HashSet <Version> > modsWithIncompatibleVersionInDatabase = new Dictionary <string, HashSet <Version> >();
                Dictionary <string, string>             modsDatabaseVersions = new Dictionary <string, string>();

                foreach (EverestModuleMetadata dependency in MissingDependencies)
                {
                    if (Everest.Loader.Delayed.Any(delayedMod => dependency.Name == delayedMod.Item1.Name))
                    {
                        Logger.Log("OuiDependencyDownloader", $"{dependency.Name} is installed but failed to load, skipping");
                    }
                    else if (dependency.Name == "Everest")
                    {
                        Logger.Log("OuiDependencyDownloader", $"Everest should be updated");
                        shouldUpdateEverest = true;
                        shouldAutoRestart   = false;

                        // TODO: maybe check more precisely for blacklisted mods? We're only basing ourselves on the name here.
                    }
                    else if (Everest.Loader.Blacklist.Contains($"{dependency.Name}.zip"))
                    {
                        Logger.Log("OuiDependencyDownloader", $"{dependency.Name} is blacklisted, and should be unblacklisted instead");
                        modsBlacklisted.Add(dependency.Name);
                        shouldAutoRestart = false;
                    }
                    else if (!availableDownloads.ContainsKey(dependency.Name))
                    {
                        Logger.Log("OuiDependencyDownloader", $"{dependency.Name} was not found in the database");
                        modsNotFound.Add(dependency.Name);
                        shouldAutoRestart = false;
                    }
                    else if (availableDownloads[dependency.Name].xxHash.Count > 1)
                    {
                        Logger.Log("OuiDependencyDownloader", $"{dependency.Name} has multiple versions and cannot be installed automatically");
                        modsNotInstallableAutomatically.Add(dependency.Name);
                        shouldAutoRestart = false;
                    }
                    else if (!isVersionCompatible(dependency.Version, availableDownloads[dependency.Name].Version))
                    {
                        Logger.Log("OuiDependencyDownloader", $"{dependency.Name} has a version in database ({availableDownloads[dependency.Name].Version}) that would not satisfy dependency ({dependency.Version})");

                        // add the required version to the list of versions for this mod
                        HashSet <Version> requiredVersions = modsWithIncompatibleVersionInDatabase.TryGetValue(dependency.Name, out HashSet <Version> result) ? result : new HashSet <Version>();
                        requiredVersions.Add(dependency.Version);
                        modsWithIncompatibleVersionInDatabase[dependency.Name] = requiredVersions;
                        modsDatabaseVersions[dependency.Name] = availableDownloads[dependency.Name].Version;
                        shouldAutoRestart = false;
                    }
                    else
                    {
                        EverestModuleMetadata installedVersion = null;
                        foreach (EverestModule module in Everest.Modules)
                        {
                            // note: if the mod is installed, but not as a zip, this will be treated as a fresh install rather than an update.
                            // this is fine since zips take the priority over directories, and we cannot update directory mods anyway.
                            if (module.Metadata.PathArchive != null && module.Metadata.Name == dependency.Name)
                            {
                                installedVersion = module.Metadata;
                                break;
                            }
                        }

                        if (installedVersion != null)
                        {
                            Logger.Log("OuiDependencyDownloader", $"{dependency.Name} is already installed and will be updated");
                            modsToUpdate[dependency.Name] = availableDownloads[dependency.Name];
                            modsToUpdateCurrentVersions[dependency.Name] = installedVersion;
                        }
                        else
                        {
                            Logger.Log("OuiDependencyDownloader", $"{dependency.Name} will be installed");
                            modsToInstall[dependency.Name] = availableDownloads[dependency.Name];
                        }
                    }
                }

                // actually install the mods now
                foreach (ModUpdateInfo modToInstall in modsToInstall.Values)
                {
                    downloadDependency(modToInstall, null);
                }

                foreach (ModUpdateInfo modToUpdate in modsToUpdate.Values)
                {
                    downloadDependency(modToUpdate, modsToUpdateCurrentVersions[modToUpdate.Name]);
                }

                // display all mods that couldn't be accounted for
                if (shouldUpdateEverest)
                {
                    LogLine(Dialog.Clean("DEPENDENCYDOWNLOADER_MUST_UPDATE_EVEREST"));
                }

                foreach (string mod in modsNotFound)
                {
                    LogLine(string.Format(Dialog.Get("DEPENDENCYDOWNLOADER_MOD_NOT_FOUND"), mod));
                }

                foreach (string mod in modsNotInstallableAutomatically)
                {
                    LogLine(string.Format(Dialog.Get("DEPENDENCYDOWNLOADER_MOD_NOT_AUTO_INSTALLABLE"), mod));
                }

                foreach (string mod in modsBlacklisted)
                {
                    LogLine(string.Format(Dialog.Get("DEPENDENCYDOWNLOADER_MOD_BLACKLISTED"), mod));
                }

                foreach (string mod in modsWithIncompatibleVersionInDatabase.Keys)
                {
                    LogLine(string.Format(Dialog.Get("DEPENDENCYDOWNLOADER_MOD_WRONG_VERSION"), mod,
                                          string.Join(", ", modsWithIncompatibleVersionInDatabase[mod]), modsDatabaseVersions[mod]));
                }
            }

            Progress    = 1;
            ProgressMax = 1;

            if (shouldAutoRestart)
            {
                // there are no errors to display: restart automatically
                LogLine(Dialog.Clean("DEPENDENCYDOWNLOADER_RESTARTING"));
                for (int i = 3; i > 0; --i)
                {
                    Lines[Lines.Count - 1] = string.Format(Dialog.Get("DEPENDENCYDOWNLOADER_RESTARTING_IN"), i);
                    Thread.Sleep(1000);
                }
                Lines[Lines.Count - 1] = Dialog.Clean("DEPENDENCYDOWNLOADER_RESTARTING");

                Everest.QuickFullRestart();
            }
            else if (shouldRestart)
            {
                LogLine("\n" + Dialog.Clean("DEPENDENCYDOWNLOADER_PRESS_BACK_TO_RESTART"));
            }
            else
            {
                LogLine("\n" + Dialog.Clean("DEPENDENCYDOWNLOADER_PRESS_BACK_TO_GO_BACK"));
            }
        }
Exemplo n.º 11
0
        private void downloadDependency(ModUpdateInfo mod, EverestModuleMetadata installedVersion)
        {
            string downloadDestination = Path.Combine(Everest.PathGame, $"dependency-download.zip");

            try {
                // 1. Download
                LogLine(string.Format(Dialog.Get("DEPENDENCYDOWNLOADER_DOWNLOADING"), mod.Name, mod.URL));
                LogLine("", false);

                Everest.Updater.DownloadFileWithProgress(mod.URL, downloadDestination, (position, length, speed) => {
                    if (length > 0)
                    {
                        Lines[Lines.Count - 1] = $"{((int) Math.Floor(100D * (position / (double) length)))}% @ {speed} KiB/s";
                        Progress    = position;
                        ProgressMax = (int)length;
                    }
                    else
                    {
                        Lines[Lines.Count - 1] = $"{((int) Math.Floor(position / 1000D))}KiB @ {speed} KiB/s";
                        ProgressMax            = 0;
                    }
                });

                ProgressMax            = 0;
                Lines[Lines.Count - 1] = Dialog.Clean("DEPENDENCYDOWNLOADER_DOWNLOAD_FINISHED");

                // 2. Verify checksum
                LogLine(Dialog.Clean("DEPENDENCYDOWNLOADER_VERIFYING_CHECKSUM"));
                ModUpdaterHelper.VerifyChecksum(mod, downloadDestination);

                // 3. Install mod
                shouldRestart = true;
                if (installedVersion != null)
                {
                    LogLine(string.Format(Dialog.Get("DEPENDENCYDOWNLOADER_UPDATING"), mod.Name, installedVersion.Version, mod.Version, installedVersion.PathArchive));
                    ModUpdaterHelper.InstallModUpdate(mod, installedVersion, downloadDestination);
                }
                else
                {
                    string installDestination = Path.Combine(Everest.Loader.PathMods, $"{mod.Name}.zip");
                    LogLine(string.Format(Dialog.Get("DEPENDENCYDOWNLOADER_INSTALLING"), mod.Name, mod.Version, installDestination));
                    File.Move(downloadDestination, installDestination);
                }
            } catch (Exception e) {
                // install failed
                LogLine(string.Format(Dialog.Get("DEPENDENCYDOWNLOADER_INSTALL_FAILED"), mod.Name));
                Logger.LogDetailed(e);
                shouldAutoRestart = false;

                // try to delete the file if it still exists.
                if (File.Exists(downloadDestination))
                {
                    try {
                        Logger.Log("OuiDependencyDownloader", $"Deleting temp file {downloadDestination}");
                        File.Delete(downloadDestination);
                    } catch (Exception) {
                        Logger.Log("OuiDependencyDownloader", $"Removing {downloadDestination} failed");
                    }
                }
            }
        }
Exemplo n.º 12
0
        public override void Update()
        {
            // check if the "press Back to restart" message has to be toggled
            if (menu != null && subHeader != null && (shouldRestart && menu.Focused) != willRestartMessageShown)
            {
                willRestartMessageShown = !willRestartMessageShown;
                if (willRestartMessageShown)
                {
                    subHeader.TextColor = Color.OrangeRed;
                    subHeader.Title     = $"{Dialog.Clean("MODUPDATECHECKER_MENU_HEADER")} ({Dialog.Clean("MODUPDATECHECKER_WILLRESTART")})";
                }
                else
                {
                    subHeader.TextColor = Color.Gray;
                    subHeader.Title     = Dialog.Clean("MODUPDATECHECKER_MENU_HEADER");
                }
            }

            if (menu != null && task != null && task.IsCompleted)
            {
                // there is no download or install task in progress

                if (fetchingButton != null)
                {
                    // This means fetching the updates just finished. We have to remove the "Checking for updates" button
                    // and put the actual update list instead.

                    Logger.Log("OuiModUpdateList", "Rendering updates");

                    menu.Remove(fetchingButton);
                    fetchingButton = null;

                    if (updateCatalog == null)
                    {
                        // display an error message
                        TextMenu.Button button = new TextMenu.Button(Dialog.Clean("MODUPDATECHECKER_ERROR"));
                        button.Disabled = true;
                        menu.Add(button);
                    }
                    else if (availableUpdatesCatalog.Count == 0)
                    {
                        // display a dummy "no update available" button
                        TextMenu.Button button = new TextMenu.Button(Dialog.Clean("MODUPDATECHECKER_NOUPDATE"));
                        button.Disabled = true;
                        menu.Add(button);
                    }
                    else
                    {
                        // if there are multiple updates...
                        if (availableUpdatesCatalog.Count > 1)
                        {
                            // display an "update all" button at the top of the list
                            updateAllButton = new TextMenu.Button(Dialog.Clean("MODUPDATECHECKER_UPDATE_ALL"));
                            updateAllButton.Pressed(() => downloadAllMods());

                            menu.Add(updateAllButton);
                        }

                        // then, display one button per update
                        foreach (ModUpdateInfo update in availableUpdatesCatalog.Keys)
                        {
                            EverestModuleMetadata metadata = availableUpdatesCatalog[update];

                            string versionUpdate = metadata.VersionString;
                            if (metadata.VersionString != update.Version)
                            {
                                versionUpdate = $"{metadata.VersionString} > {update.Version}";
                            }

                            TextMenu.Button button = new TextMenu.Button($"{metadata.Name.SpacedPascalCase()} | v. {versionUpdate} ({new DateTime(1970, 1, 1, 0, 0, 0, 0).AddSeconds(update.LastUpdate):yyyy-MM-dd})");
                            button.Pressed(() => {
                                // make the menu non-interactive
                                menu.Focused    = false;
                                button.Disabled = true;

                                // trigger the update download
                                downloadModUpdate(update, metadata, button);
                            });

                            // if there is more than one hash, it means there is multiple downloads for this mod. Thus, we can't update it manually.
                            // if there isnt, add it to the list of mods that can be updated via "update all"
                            if (update.xxHash.Count > 1)
                            {
                                button.Disabled = true;
                            }
                            else
                            {
                                updatableMods.Add(new ModUpdateHolder()
                                {
                                    update = update, metadata = metadata, button = button
                                });
                            }

                            menu.Add(button);
                        }
                    }
                }

                if (menu.Focused && Selected && Input.MenuCancel.Pressed)
                {
                    if (shouldRestart)
                    {
                        Everest.QuickFullRestart();
                    }
                    else
                    {
                        // go back to mod options instead
                        Audio.Play(SFX.ui_main_button_back);
                        Overworld.Goto <OuiModOptions>();
                    }
                }
            }

            base.Update();
        }
Exemplo n.º 13
0
        private void downloadAllDependencies()
        {
            // 1. Compute the list of dependencies we must download.
            LogLine(Dialog.Clean("DEPENDENCYDOWNLOADER_DOWNLOADING_DATABASE"));

            Everest.Updater.Entry everestVersionToInstall = null;

            Dictionary <string, ModUpdateInfo> availableDownloads = ModUpdaterHelper.DownloadModUpdateList();

            if (availableDownloads == null)
            {
                shouldAutoExit = false;
                shouldRestart  = false;

                LogLine(Dialog.Clean("DEPENDENCYDOWNLOADER_DOWNLOAD_DATABASE_FAILED"));
            }
            else
            {
                // load information on all installed mods, so that we can spot blacklisted ones easily.
                LogLine(Dialog.Clean("DEPENDENCYDOWNLOADER_LOADING_INSTALLED_MODS"));

                Progress    = 0;
                ProgressMax = 100;
                Dictionary <string, EverestModuleMetadata[]> allModsInformationFlipped =
                    OuiModToggler.LoadAllModYamls(progress => {
                    Lines[Lines.Count - 1] = $"{Dialog.Clean("DEPENDENCYDOWNLOADER_LOADING_INSTALLED_MODS")} ({(int) (progress * 100)}%)";
                    Progress = (int)(progress * 100);
                });
                ProgressMax = 0;

                // but flip around the mapping for convenience.
                Dictionary <EverestModuleMetadata, string> allModsInformation = new Dictionary <EverestModuleMetadata, string>();
                foreach (KeyValuePair <string, EverestModuleMetadata[]> mods in allModsInformationFlipped)
                {
                    foreach (EverestModuleMetadata mod in mods.Value)
                    {
                        allModsInformation[mod] = mods.Key;
                    }
                }
                Lines[Lines.Count - 1] = $"{Dialog.Clean("DEPENDENCYDOWNLOADER_LOADING_INSTALLED_MODS")} {Dialog.Clean("DEPENDENCYDOWNLOADER_DONE")}";

                Logger.Log("OuiDependencyDownloader", "Computing dependencies to download...");

                // these mods are not installed currently, we will install them
                Dictionary <string, ModUpdateInfo> modsToInstall = new Dictionary <string, ModUpdateInfo>();

                // these mods are already installed, but need an update to satisfy the dependency
                Dictionary <string, ModUpdateInfo>         modsToUpdate = new Dictionary <string, ModUpdateInfo>();
                Dictionary <string, EverestModuleMetadata> modsToUpdateCurrentVersions = new Dictionary <string, EverestModuleMetadata>();

                // Everest should be updated to satisfy a dependency on Everest
                bool shouldUpdateEverestManually = false;

                // these mods are absent from the database
                HashSet <string> modsNotFound = new HashSet <string>();

                // these mods have multiple downloads, and as such, should be installed manually
                HashSet <string> modsNotInstallableAutomatically = new HashSet <string>();

                // these mods should be unblacklisted.
                HashSet <string> modFilenamesToUnblacklist = new HashSet <string>();

                // these mods are in the database, but the version found in there won't satisfy the dependency
                Dictionary <string, HashSet <Version> > modsWithIncompatibleVersionInDatabase = new Dictionary <string, HashSet <Version> >();
                Dictionary <string, string>             modsDatabaseVersions = new Dictionary <string, string>();

                foreach (EverestModuleMetadata dependency in MissingDependencies)
                {
                    if (Everest.Loader.Delayed.Any(delayedMod => dependency.Name == delayedMod.Item1.Name))
                    {
                        Logger.Log("OuiDependencyDownloader", $"{dependency.Name} is installed but failed to load, skipping");
                    }
                    else if (dependency.Name == "Everest")
                    {
                        Logger.Log("OuiDependencyDownloader", $"Everest should be updated");
                        shouldAutoExit = false;

                        if (dependency.Version.Major != 1 || dependency.Version.Build > 0 || dependency.Version.Revision > 0)
                        {
                            // the Everest version is not 1.XXX.0.0: Everest should be updated manually because this shouldn't happen.
                            shouldUpdateEverestManually = true;
                        }
                        else if (!shouldUpdateEverestManually && (everestVersionToInstall == null || everestVersionToInstall.Build < dependency.Version.Minor))
                        {
                            everestVersionToInstall = findEverestVersionToInstall(dependency.Version.Minor);
                            if (everestVersionToInstall == null)
                            {
                                // a suitable version was not found! so, it should be installed manually.
                                shouldUpdateEverestManually = true;
                            }
                        }
                    }
                    else if (tryUnblacklist(dependency, allModsInformation, modFilenamesToUnblacklist))
                    {
                        Logger.Log("OuiDependencyDownloader", $"{dependency.Name} is blacklisted, and should be unblacklisted instead");
                    }
                    else if (!availableDownloads.ContainsKey(dependency.Name))
                    {
                        Logger.Log("OuiDependencyDownloader", $"{dependency.Name} was not found in the database");
                        modsNotFound.Add(dependency.Name);
                        shouldAutoExit = false;
                    }
                    else if (availableDownloads[dependency.Name].xxHash.Count > 1)
                    {
                        Logger.Log("OuiDependencyDownloader", $"{dependency.Name} has multiple versions and cannot be installed automatically");
                        modsNotInstallableAutomatically.Add(dependency.Name);
                        shouldAutoExit = false;
                    }
                    else if (!isVersionCompatible(dependency.Version, availableDownloads[dependency.Name].Version))
                    {
                        Logger.Log("OuiDependencyDownloader", $"{dependency.Name} has a version in database ({availableDownloads[dependency.Name].Version}) that would not satisfy dependency ({dependency.Version})");

                        // add the required version to the list of versions for this mod
                        HashSet <Version> requiredVersions = modsWithIncompatibleVersionInDatabase.TryGetValue(dependency.Name, out HashSet <Version> result) ? result : new HashSet <Version>();
                        requiredVersions.Add(dependency.Version);
                        modsWithIncompatibleVersionInDatabase[dependency.Name] = requiredVersions;
                        modsDatabaseVersions[dependency.Name] = availableDownloads[dependency.Name].Version;
                        shouldAutoExit = false;
                    }
                    else
                    {
                        EverestModuleMetadata installedVersion = null;
                        foreach (EverestModule module in Everest.Modules)
                        {
                            // note: if the mod is installed, but not as a zip, this will be treated as a fresh install rather than an update.
                            // this is fine since zips take the priority over directories, and we cannot update directory mods anyway.
                            if (module.Metadata.PathArchive != null && module.Metadata.Name == dependency.Name)
                            {
                                installedVersion = module.Metadata;
                                break;
                            }
                        }

                        if (installedVersion != null)
                        {
                            Logger.Log("OuiDependencyDownloader", $"{dependency.Name} is already installed and will be updated");
                            modsToUpdate[dependency.Name] = availableDownloads[dependency.Name];
                            modsToUpdateCurrentVersions[dependency.Name] = installedVersion;
                        }
                        else
                        {
                            Logger.Log("OuiDependencyDownloader", $"{dependency.Name} will be installed");
                            modsToInstall[dependency.Name] = availableDownloads[dependency.Name];
                        }
                    }
                }

                // actually install the mods now
                foreach (ModUpdateInfo modToInstall in modsToInstall.Values)
                {
                    downloadDependency(modToInstall, null);
                }

                foreach (ModUpdateInfo modToUpdate in modsToUpdate.Values)
                {
                    downloadDependency(modToUpdate, modsToUpdateCurrentVersions[modToUpdate.Name]);
                }

                // unblacklist mods if this is needed
                if (modFilenamesToUnblacklist.Count > 0)
                {
                    // remove the mods from blacklist.txt
                    if (!unblacklistMods(modFilenamesToUnblacklist))
                    {
                        // something bad happened
                        shouldAutoExit = false;
                        shouldRestart  = true;
                    }

                    foreach (string modFilename in modFilenamesToUnblacklist)
                    {
                        try {
                            LogLine(string.Format(Dialog.Get("DEPENDENCYDOWNLOADER_MOD_UNBLACKLIST"), modFilename));

                            // remove the mod from the loaded blacklist
                            while (Everest.Loader._Blacklist.Contains(modFilename))
                            {
                                Everest.Loader._Blacklist.Remove(modFilename);
                            }

                            // hot load the mod
                            if (modFilename.EndsWith(".zip"))
                            {
                                Everest.Loader.LoadZip(Path.Combine(Everest.Loader.PathMods, modFilename));
                            }
                            else
                            {
                                Everest.Loader.LoadDir(Path.Combine(Everest.Loader.PathMods, modFilename));
                            }
                        } catch (Exception e) {
                            // something bad happened during the mod hot loading, log it and prompt to restart the game to load the mod.
                            LogLine(Dialog.Clean("DEPENDENCYDOWNLOADER_UNBLACKLIST_FAILED"));
                            Logger.LogDetailed(e);
                            shouldAutoExit = false;
                            shouldRestart  = true;
                            break;
                        }
                    }
                }

                // display all mods that couldn't be accounted for
                if (shouldUpdateEverestManually)
                {
                    LogLine(Dialog.Clean("DEPENDENCYDOWNLOADER_MUST_UPDATE_EVEREST"));
                }

                foreach (string mod in modsNotFound)
                {
                    LogLine(string.Format(Dialog.Get("DEPENDENCYDOWNLOADER_MOD_NOT_FOUND"), mod));
                }

                foreach (string mod in modsNotInstallableAutomatically)
                {
                    LogLine(string.Format(Dialog.Get("DEPENDENCYDOWNLOADER_MOD_NOT_AUTO_INSTALLABLE"), mod));
                }

                foreach (string mod in modsWithIncompatibleVersionInDatabase.Keys)
                {
                    LogLine(string.Format(Dialog.Get("DEPENDENCYDOWNLOADER_MOD_WRONG_VERSION"), mod,
                                          string.Join(", ", modsWithIncompatibleVersionInDatabase[mod]), modsDatabaseVersions[mod]));
                }
            }

            Progress    = 1;
            ProgressMax = 1;

            if (shouldAutoExit)
            {
                // there are no errors to display: restart automatically
                if (shouldRestart)
                {
                    LogLine(Dialog.Clean("DEPENDENCYDOWNLOADER_RESTARTING"));
                    for (int i = 3; i > 0; --i)
                    {
                        Lines[Lines.Count - 1] = string.Format(Dialog.Get("DEPENDENCYDOWNLOADER_RESTARTING_IN"), i);
                        Thread.Sleep(1000);
                    }
                    Lines[Lines.Count - 1] = Dialog.Clean("DEPENDENCYDOWNLOADER_RESTARTING");

                    Everest.QuickFullRestart();
                }
                else
                {
                    Exit();
                }
            }
            else if (everestVersionToInstall != null)
            {
                LogLine("\n" + string.Format(Dialog.Get("DEPENDENCYDOWNLOADER_EVEREST_UPDATE"), everestVersionToInstall.Build));
                this.everestVersionToInstall = everestVersionToInstall;
            }
            else if (shouldRestart)
            {
                LogLine("\n" + Dialog.Clean("DEPENDENCYDOWNLOADER_PRESS_BACK_TO_RESTART"));
            }
            else
            {
                LogLine("\n" + Dialog.Clean("DEPENDENCYDOWNLOADER_PRESS_BACK_TO_GO_BACK"));
            }
        }