/// <summary> /// Does the actual downloading of the mod. This is it's own function, to avoid double code /// </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="button">The button for that mod shown on the interface</param> /// <returns>Bool wether the update failed or not</returns> private bool doDownloadModUpdate(ModUpdateInfo update, EverestModuleMetadata mod, TextMenu.Button button) { // we will download the mod to Celeste_Directory/[update.GetHashCode()].zip at first. string zipPath = Path.Combine(Everest.PathGame, $"modupdate-{update.GetHashCode()}.zip"); try { // download it... button.Label = $"{ModUpdaterHelper.FormatModName(update.Name)} ({Dialog.Clean("MODUPDATECHECKER_DOWNLOADING")})"; downloadMod(update, button, zipPath); // verify its checksum ModUpdaterHelper.VerifyChecksum(update, zipPath); // mark restarting as required, as we will do weird stuff like closing zips afterwards. shouldRestart = true; // install it button.Label = $"{ModUpdaterHelper.FormatModName(update.Name)} ({Dialog.Clean("MODUPDATECHECKER_INSTALLING")})"; ModUpdaterHelper.InstallModUpdate(update, mod, zipPath); // done! button.Label = $"{ModUpdaterHelper.FormatModName(update.Name)} ({Dialog.Clean("MODUPDATECHECKER_UPDATED")})"; return(true); } catch (Exception e) { // update failed button.Label = $"{ModUpdaterHelper.FormatModName(update.Name)} ({Dialog.Clean("MODUPDATECHECKER_FAILED")})"; Logger.Log("OuiModUpdateList", $"Updating {update.Name} failed"); Logger.LogDetailed(e); // try to delete mod-update.zip if it still exists. ModUpdaterHelper.TryDelete(zipPath); return(false); } }
protected virtual void CreateModMenuSectionHeader(TextMenu menu, bool inGame, EventInstance snapshot) { Type type = SettingsType; EverestModuleSettings settings = _Settings; if (type == null || settings == null) { return; } string typeName = type.Name.ToLowerInvariant(); if (typeName.EndsWith("settings")) { typeName = typeName.Substring(0, typeName.Length - 8); } string nameDefaultPrefix = $"modoptions_{typeName}_"; string name; // We lazily reuse this field for the props later on. name = type.GetCustomAttribute <SettingNameAttribute>()?.Name ?? $"{nameDefaultPrefix}title"; name = name.DialogCleanOrNull() ?? ModUpdaterHelper.FormatModName(Metadata.Name); menu.Add(new patch_TextMenu.patch_SubHeader(name + " | v." + Metadata.VersionString)); }
/// <summary> /// Downloads a mod update. /// </summary> /// <param name="update">The update info coming from the update server</param> /// <param name="button">The button for that mod shown on the interface</param> /// <param name="zipPath">The path to the zip the update will be downloaded to</param> private static void downloadMod(ModUpdateInfo update, TextMenu.Button button, string zipPath) { Logger.Log("OuiModUpdateList", $"Downloading {update.URL} to {zipPath}"); Func <int, long, int, bool> progressCallback = (position, length, speed) => { if (ongoingUpdateCancelled) { return(false); } if (length > 0) { button.Label = $"{ModUpdaterHelper.FormatModName(update.Name)} ({((int) Math.Floor(100D * (position / (double) length)))}% @ {speed} KiB/s)"; } else { button.Label = $"{ModUpdaterHelper.FormatModName(update.Name)} ({((int) Math.Floor(position / 1000D))}KiB @ {speed} KiB/s)"; } return(true); }; try { Everest.Updater.DownloadFileWithProgress(update.URL, zipPath, progressCallback); } catch (WebException e) { Logger.Log(LogLevel.Warn, "OuiModUpdateList", $"Download failed, trying mirror {update.MirrorURL}"); Logger.LogDetailed(e); Everest.Updater.DownloadFileWithProgress(update.MirrorURL, zipPath, progressCallback); } }
/// <summary> /// Downloads a mod update. /// </summary> /// <param name="update">The update info coming from the update server</param> /// <param name="button">The button for that mod shown on the interface</param> /// <param name="zipPath">The path to the zip the update will be downloaded to</param> private static void downloadMod(ModUpdateInfo update, TextMenu.Button button, string zipPath) { Logger.Log("OuiModUpdateList", $"Downloading {update.URL} to {zipPath}"); Everest.Updater.DownloadFileWithProgress(update.URL, zipPath, (position, length, speed) => { if (length > 0) { button.Label = $"{ModUpdaterHelper.FormatModName(update.Name)} ({((int) Math.Floor(100D * (position / (double) length)))}% @ {speed} KiB/s)"; } else { button.Label = $"{ModUpdaterHelper.FormatModName(update.Name)} ({((int) Math.Floor(position / 1000D))}KiB @ {speed} KiB/s)"; } }); }
private void autoUpdate(SortedDictionary <ModUpdateInfo, EverestModuleMetadata> updateList) { int currentlyUpdatedModIndex = 1; // this is common to all mods. string zipPath = Path.Combine(Everest.PathGame, "mod-update.zip"); bool restartRequired = false; bool failuresOccured = false; // iterate through all mods to update now. foreach (ModUpdateInfo update in updateList.Keys) { // common beginning for all messages: f.e. [1/3] Auto-updating Polygon Dreams string progressString = $"[{currentlyUpdatedModIndex}/{updateList.Count}] {Dialog.Clean("AUTOUPDATECHECKER_UPDATING")} {ModUpdaterHelper.FormatModName(update.Name)}:"; try { // show the cancel button for downloading showCancel = true; // download it... modUpdatingMessage = $"{progressString} {Dialog.Clean("AUTOUPDATECHECKER_DOWNLOADING")}"; Logger.Log("AutoModUpdater", $"Downloading {update.URL} to {zipPath}"); Func <int, long, int, bool> progressCallback = (position, length, speed) => { if (skipUpdate) { return(false); } if (length > 0) { modUpdatingMessage = $"{progressString} {Dialog.Clean("AUTOUPDATECHECKER_DOWNLOADING")} " + $"({((int) Math.Floor(100D * (position / (double) length)))}% @ {speed} KiB/s)"; } else { modUpdatingMessage = $"{progressString} {Dialog.Clean("AUTOUPDATECHECKER_DOWNLOADING")} " + $"({((int) Math.Floor(position / 1000D))}KiB @ {speed} KiB/s)"; } return(true); }; try { Everest.Updater.DownloadFileWithProgress(update.URL, zipPath, progressCallback); } catch (WebException e) { Logger.Log(LogLevel.Warn, "AutoModUpdater", $"Download failed, trying mirror {update.MirrorURL}"); Logger.LogDetailed(e); Everest.Updater.DownloadFileWithProgress(update.MirrorURL, zipPath, progressCallback); } // hide the cancel button for downloading, download is done showCancel = false; if (skipUpdate) { Logger.Log("AutoModUpdater", "Update was skipped"); // try to delete mod-update.zip if it still exists. ModUpdaterHelper.TryDelete(zipPath); if (restartRequired) { // stop trying to update mods; restart right away break; } else { // proceed to the game right away shouldContinue = true; return; } } // verify its checksum modUpdatingMessage = $"{progressString} {Dialog.Clean("AUTOUPDATECHECKER_VERIFYING")}"; ModUpdaterHelper.VerifyChecksum(update, zipPath); // install it restartRequired = true; modUpdatingMessage = $"{progressString} {Dialog.Clean("AUTOUPDATECHECKER_INSTALLING")}"; ModUpdaterHelper.InstallModUpdate(update, updateList[update], zipPath); } catch (Exception e) { // update failed Logger.Log("AutoModUpdater", $"Updating {update.Name} failed"); Logger.LogDetailed(e); failuresOccured = true; // try to delete mod-update.zip if it still exists. ModUpdaterHelper.TryDelete(zipPath); // stop trying to update further mods. break; } currentlyUpdatedModIndex++; } // don't show "cancel" anymore, install ended. showCancel = false; if (!failuresOccured) { // restart when everything is done modUpdatingMessage = Dialog.Clean("DEPENDENCYDOWNLOADER_RESTARTING"); Thread.Sleep(1000); Everest.QuickFullRestart(); } else { modUpdatingMessage = Dialog.Clean("AUTOUPDATECHECKER_FAILED"); // stop the cogwheel cogwheelSpinning = false; cogwheelStopTime = RawTimeActive; if (restartRequired) { // failures occured, restart is required modUpdatingSubMessage = Dialog.Clean("AUTOUPDATECHECKER_REBOOT"); confirmToRestart = true; } else { // failures occured, restart is not required modUpdatingSubMessage = Dialog.Clean("AUTOUPDATECHECKER_CONTINUE"); confirmToContinue = true; } } }
public override void Update() { // check if the "press Back to restart" message has to be toggled if (menu != null && subHeader != null) { if (menu.Focused && shouldRestart) { subHeader.TextColor = Color.OrangeRed; subHeader.Title = $"{Dialog.Clean("MODUPDATECHECKER_MENU_HEADER")} ({Dialog.Clean("MODUPDATECHECKER_WILLRESTART")})"; } else if (!menu.Focused && ongoingUpdateCancelled && menuOnScreen) { subHeader.TextColor = Color.Gray; subHeader.Title = $"{Dialog.Clean("MODUPDATECHECKER_MENU_HEADER")} ({Dialog.Clean("MODUPDATECHECKER_CANCELLING")})"; } 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($"{ModUpdaterHelper.FormatModName(metadata.Name)} | 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 isn't, 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>(); } } } if (Input.MenuCancel.Pressed) { // cancel any ongoing download (this has no effect if no download is ongoing anyway). ongoingUpdateCancelled = true; } base.Update(); }