Beispiel #1
0
        public override void Begin()
        {
            base.Begin();

            // add on-screen elements like GameLoader/OverworldLoader
            Add(new HudRenderer());
            Add(snow);
            RendererList.UpdateLists();

            // register the routine
            Entity entity = new Entity();

            entity.Add(new Coroutine(Routine()));
            Add(entity);

            // run the update check task asynchronously
            new Task(() => {
                // display "checking for updates" message, in case the async task is not done yet.
                modUpdatingMessage = Dialog.Clean("AUTOUPDATECHECKER_CHECKING");

                SortedDictionary <ModUpdateInfo, EverestModuleMetadata> updateList = ModUpdaterHelper.GetAsyncLoadedModUpdates();
                if (updateList == null || updateList.Count == 0)
                {
                    // no mod update, clear message and continue right away.
                    modUpdatingMessage = null;
                    shouldContinue     = true;
                }
                else
                {
                    // install mod updates
                    autoUpdate(updateList);
                }
            }).Start();
        }
Beispiel #2
0
        public MainMenuModOptionsButton(string labelName, string iconName, Oui oui, Vector2 targetPosition, Vector2 tweenFrom, Action onConfirm)
            : base(labelName, iconName, oui, targetPosition, tweenFrom, onConfirm)
        {
            int delayedModCount = Everest.Loader.Delayed.Count;

            // if the update check failed or isn't done yet, assume there are no updates (no message in main menu).
            int modUpdatesAvailable = ModUpdaterHelper.IsAsyncUpdateCheckingDone() ? (ModUpdaterHelper.GetAsyncLoadedModUpdates()?.Count ?? 0) : 0;

            if (delayedModCount > 1)
            {
                subText = string.Format(Dialog.Get("MENU_MODOPTIONS_MULTIPLE_MODS_FAILEDTOLOAD"), delayedModCount);
            }
            else if (delayedModCount == 1)
            {
                subText = Dialog.Clean("MENU_MODOPTIONS_ONE_MOD_FAILEDTOLOAD");
            }
            else if (Everest.Updater.HasUpdate)
            {
                subText = Dialog.Clean("MENU_MODOPTIONS_UPDATE_AVAILABLE");
            }
            else if (modUpdatesAvailable > 1)
            {
                subText = string.Format(Dialog.Get("MENU_MODOPTIONS_MOD_UPDATES_AVAILABLE"), modUpdatesAvailable);
            }
            else if (modUpdatesAvailable == 1)
            {
                subText = Dialog.Clean("MENU_MODOPTIONS_MOD_UPDATE_AVAILABLE");
            }
            else
            {
                subText = null;
            }
        }
Beispiel #3
0
        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));
        }
Beispiel #4
0
        private static Scene _GetNextScene(Overworld.StartMode startMode, HiresSnow snow)
        {
            bool transitionToModUpdater = false;

            if (CoreModule.Settings.AutoUpdateModsOnStartup)
            {
                if (!ModUpdaterHelper.IsAsyncUpdateCheckingDone())
                {
                    // update checking is not done yet.
                    // transition to mod updater screen to display the "checking for updates" message.
                    transitionToModUpdater = true;
                }
                else
                {
                    SortedDictionary <ModUpdateInfo, EverestModuleMetadata> modUpdates = ModUpdaterHelper.GetAsyncLoadedModUpdates();
                    if (modUpdates != null && modUpdates.Count != 0)
                    {
                        // update checking is done, and updates are available.
                        // transition to mod updater screen in order to install the updates
                        transitionToModUpdater = true;
                    }
                }
            }

            if (transitionToModUpdater)
            {
                return(new AutoModUpdater(snow));
            }
            else
            {
                return(new OverworldLoader(startMode, snow));
            }
        }
Beispiel #5
0
        /// <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);
            }
        }
Beispiel #6
0
        /// <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);
            }
        }
Beispiel #7
0
            public void Fetch()
            {
                // 1. Download the mod updates database
                updateCatalog = ModUpdaterHelper.DownloadModUpdateList();

                // 2. Find out what actually has been updated
                if (updateCatalog != null)
                {
                    availableUpdatesCatalog = ModUpdaterHelper.ListAvailableUpdates(updateCatalog, excludeBlacklist: false);
                }
            }
        /// <summary>
        /// Downloads and installs a mod update.
        /// </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>
        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.SpacedPascalCase()} ({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.
                    if (!shouldRestart)
                    {
                        shouldRestart       = true;
                        subHeader.TextColor = Color.OrangeRed;
                        subHeader.Title     = $"{Dialog.Clean("MODUPDATECHECKER_MENU_HEADER")} ({Dialog.Clean("MODUPDATECHECKER_WILLRESTART")})";
                    }

                    // install it
                    button.Label = $"{update.Name.SpacedPascalCase()} ({Dialog.Clean("MODUPDATECHECKER_INSTALLING")})";
                    ModUpdaterHelper.InstallModUpdate(update, mod, zipPath);

                    // done!
                    button.Label = $"{update.Name.SpacedPascalCase()} ({Dialog.Clean("MODUPDATECHECKER_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.MoveSelection(1);
                    }
                } catch (Exception e) {
                    // update failed
                    button.Label = $"{update.Name.SpacedPascalCase()} ({Dialog.Clean("MODUPDATECHECKER_FAILED")})";
                    Logger.Log("OuiModUpdateList", $"Updating {update.Name} failed");
                    Logger.LogDetailed(e);
                    button.Disabled = false;

                    // try to delete mod-update.zip if it still exists.
                    ModUpdaterHelper.TryDelete(zipPath);
                }

                // give the menu control back to the player
                menu.Focused = true;
            });
            task.Start();
        }
Beispiel #9
0
        /// <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)";
                }
            });
        }
Beispiel #10
0
        public override IEnumerator Enter(Oui from)
        {
            Everest.Loader.AutoLoadNewMods = false;

            menu = new TextMenu();

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

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

            fetchingButton          = new TextMenu.Button(Dialog.Clean("MODUPDATECHECKER_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;
            menuOnScreen = true;

            task = new Task(() => {
                // 1. Download the mod updates database
                updateCatalog = ModUpdaterHelper.DownloadModUpdateList();

                // 2. Find out what actually has been updated
                if (updateCatalog != null)
                {
                    availableUpdatesCatalog = ModUpdaterHelper.ListAvailableUpdates(updateCatalog);
                }
            });

            task.Start();
        }
Beispiel #11
0
        private IEnumerator Routine()
        {
            // display "checking for updates" message, in case the async task is not done yet.
            modUpdatingMessage = Dialog.Clean("AUTOUPDATECHECKER_CHECKING");

            // wait until the update check is over.
            showCancel = true;
            while (!ModUpdaterHelper.IsAsyncUpdateCheckingDone() && !skipUpdate)
            {
                yield return(null);
            }
            showCancel = false;

            if (!skipUpdate)
            {
                SortedDictionary <ModUpdateInfo, EverestModuleMetadata> updateList = ModUpdaterHelper.GetAsyncLoadedModUpdates();
                if (updateList == null || updateList.Count == 0)
                {
                    // no mod update, clear message and continue right away.
                    modUpdatingMessage = null;
                    shouldContinue     = true;
                }
                else
                {
                    // install mod updates
                    new Task(() => autoUpdate(updateList)).Start();
                }

                // wait until we can continue (async task finished, or player hit Confirm to continue)
                while (!shouldContinue)
                {
                    yield return(null);
                }
            }

            // proceed to the title screen, as GameLoader would do it normally.
            Engine.Scene = new OverworldLoader(Overworld.StartMode.Titlescreen, snow);
        }
Beispiel #12
0
        internal static void Boot()
        {
            Logger.Log(LogLevel.Info, "core", "Booting Everest");
            Logger.Log(LogLevel.Info, "core", $"AppDomain: {AppDomain.CurrentDomain.FriendlyName ?? "???"}");
            Logger.Log(LogLevel.Info, "core", $"VersionCelesteString: {VersionCelesteString}");

            if (Type.GetType("Mono.Runtime") != null)
            {
                // Mono hates HTTPS.
                ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => {
                    return(true);
                };
            }

            // enable TLS 1.2 to fix connecting to everestapi.github.io
            ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;

            PathGame = Path.GetDirectoryName(typeof(Celeste).Assembly.Location);

            // .NET hates it when strong-named dependencies get updated.
            AppDomain.CurrentDomain.AssemblyResolve += (asmSender, asmArgs) => {
                AssemblyName asmName = new AssemblyName(asmArgs.Name);
                if (!asmName.Name.StartsWith("Mono.Cecil") &&
                    !asmName.Name.StartsWith("YamlDotNet") &&
                    !asmName.Name.StartsWith("NLua"))
                {
                    return(null);
                }

                Assembly asm = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(other => other.GetName().Name == asmName.Name);
                if (asm != null)
                {
                    return(asm);
                }

                return(Assembly.LoadFrom(Path.Combine(PathGame, asmName.Name + ".dll")));
            };

            // .NET hates to acknowledge manually loaded assemblies.
            AppDomain.CurrentDomain.AssemblyResolve += (asmSender, asmArgs) => {
                AssemblyName asmName = new AssemblyName(asmArgs.Name);
                foreach (Assembly asm in _RelinkedAssemblies)
                {
                    if (asm.GetName().Name == asmName.Name)
                    {
                        return(asm);
                    }
                }

                return(null);
            };

            // Preload some basic dependencies.
            Assembly.Load("MonoMod.RuntimeDetour");
            Assembly.Load("MonoMod.Utils");
            Assembly.Load("Mono.Cecil");
            Assembly.Load("YamlDotNet");
            Assembly.Load("Newtonsoft.Json");
            Assembly.Load("Jdenticon");

            if (!File.Exists(Path.Combine(PathGame, "EverestXDGFlag")))
            {
                XDGPaths    = false;
                PathEverest = PathGame;
            }
            else
            {
                XDGPaths = true;
                string dataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
                Directory.CreateDirectory(PathEverest = Path.Combine(dataDir, "Everest"));
                Directory.CreateDirectory(Path.Combine(dataDir, "Everest", "Mods")); // Make sure it exists before content gets initialized
            }

            // Old versions of Everest have used a separate ModSettings folder.
            string modSettingsOld = Path.Combine(PathEverest, "ModSettings");
            string modSettingsRIP = Path.Combine(PathEverest, "ModSettings-OBSOLETE");

            if (Directory.Exists(modSettingsOld) || Directory.Exists(modSettingsRIP))
            {
                Logger.Log(LogLevel.Warn, "core", "THE ModSettings FOLDER IS OBSOLETE AND WILL NO LONGER BE USED!");
                if (Directory.Exists(modSettingsOld) && !Directory.Exists(modSettingsRIP))
                {
                    Directory.Move(modSettingsOld, modSettingsRIP);
                }
            }

            _DetourModManager           = new DetourModManager();
            _DetourModManager.OnILHook += (owner, from, to) => {
                _DetourOwners.Add(owner);
                object target = to.Target;
                _DetourLog.Add($"new ILHook by {owner.GetName().Name}: {from.GetID()} -> {to.Method?.GetID() ?? "???"}" + (target == null ? "" : $" (target: {target})"));
            };
            _DetourModManager.OnHook += (owner, from, to, target) => {
                _DetourOwners.Add(owner);
                _DetourLog.Add($"new Hook by {owner.GetName().Name}: {from.GetID()} -> {to.GetID()}" + (target == null ? "" : $" (target: {target})"));
            };
            _DetourModManager.OnDetour += (owner, from, to) => {
                _DetourOwners.Add(owner);
                _DetourLog.Add($"new Detour by {owner.GetName().Name}: {from.GetID()} -> {to.GetID()}");
            };
            _DetourModManager.OnNativeDetour += (owner, fromMethod, from, to) => {
                _DetourOwners.Add(owner);
                _DetourLog.Add($"new NativeDetour by {owner.GetName().Name}: {fromMethod?.ToString() ?? from.ToString("16X")} -> {to.ToString("16X")}");
            };
            HookEndpointManager.OnAdd += (from, to) => {
                Assembly owner = HookEndpointManager.GetOwner(to) as Assembly ?? typeof(Everest).Assembly;
                _DetourOwners.Add(owner);
                object target = to.Target;
                _DetourLog.Add($"new On.+= by {owner.GetName().Name}: {from.GetID()} -> {to.Method?.GetID() ?? "???"}" + (target == null ? "" : $" (target: {target})"));
                return(true);
            };
            HookEndpointManager.OnModify += (from, to) => {
                Assembly owner = HookEndpointManager.GetOwner(to) as Assembly ?? typeof(Everest).Assembly;
                _DetourOwners.Add(owner);
                object target = to.Target;
                _DetourLog.Add($"new IL.+= by {owner.GetName().Name}: {from.GetID()} -> {to.Method?.GetID() ?? "???"}" + (target == null ? "" : $" (target: {target})"));
                return(true);
            };

            // Before even initializing anything else, make sure to prepare any static flags.
            Flags.Initialize();

            // 0.1 parses into 1 in regions using ,
            // This also somehow sets the exception message language to English.
            CultureInfo.DefaultThreadCurrentCulture   = CultureInfo.InvariantCulture;
            CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture;

            if (!Flags.IsHeadless)
            {
                // Initialize the content helper.
                Content.Initialize();

                // Initialize all main managers before loading any mods.
                TouchInputManager.Instance = new TouchInputManager(Celeste.Instance);
                // Don't add it yet, though - add it in Initialize.
            }

            MainThreadHelper.Instance = new MainThreadHelper(Celeste.Instance);

            // Register our core module and load any other modules.
            new CoreModule().Register();

            // Note: Everest fulfills some mod dependencies by itself.
            new NullModule(new EverestModuleMetadata()
            {
                Name          = "Celeste",
                VersionString = $"{Celeste.Instance.Version.ToString()}-{(typeof(Game).Assembly.FullName.Contains("FNA") ? "fna" : "xna")}"
            }).Register();
            new NullModule(new EverestModuleMetadata()
            {
                Name          = "DialogCutscene",
                VersionString = "1.0.0"
            }).Register();
            new NullModule(new EverestModuleMetadata()
            {
                Name          = "UpdateChecker",
                VersionString = "1.0.2"
            }).Register();

            LuaLoader.Initialize();

            Loader.LoadAuto();

            if (!Flags.IsHeadless)
            {
                // Load stray .bins afterwards.
                Content.Crawl(new MapBinsInModsModContent(Path.Combine(PathEverest, "Mods")));
            }

            // Also let all mods parse the arguments.
            Queue <string> args = new Queue <string>(Args);

            while (args.Count > 0)
            {
                string arg = args.Dequeue();
                foreach (EverestModule mod in _Modules)
                {
                    if (mod.ParseArg(arg, args))
                    {
                        break;
                    }
                }
            }

            // Start requesting the version list ASAP.
            Updater.RequestAll();

            // Request the mod update list as well.
            ModUpdaterHelper.RunAsyncCheckForModUpdates(excludeBlacklist: true);
        }
        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"));
            }
        }
Beispiel #14
0
        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")} {update.Name.SpacedPascalCase()}:";

                try {
                    // download it...
                    modUpdatingMessage = $"{progressString} {Dialog.Clean("AUTOUPDATECHECKER_DOWNLOADING")}";

                    Logger.Log("AutoModUpdater", $"Downloading {update.URL} to {zipPath}");
                    Everest.Updater.DownloadFileWithProgress(update.URL, zipPath, (position, length, speed) => {
                        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)";
                        }
                    });

                    // 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);
                }

                currentlyUpdatedModIndex++;
            }

            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;
                }
            }
        }
Beispiel #15
0
        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;
                }
            }
        }
Beispiel #16
0
        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();
        }
Beispiel #17
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"));
            }
        }
Beispiel #18
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");
                    }
                }
            }
        }