Example #1
0
        // this probably needs to be refactored
        private void InstallMods(object sender, DoWorkEventArgs e)
        {
            installCanceled = false;
            ClearLog();

            var opts = (KeyValuePair <ModChanges, RelationshipResolverOptions>)e.Argument;

            IRegistryQuerier registry  = RegistryManager.Instance(manager.CurrentInstance).registry;
            ModuleInstaller  installer = ModuleInstaller.GetInstance(CurrentInstance, Manager.Cache, GUI.user);

            // Avoid accumulating multiple event handlers
            installer.onReportModInstalled -= OnModInstalled;
            installer.onReportModInstalled += OnModInstalled;
            // setup progress callback

            // this will be the final list of mods we want to install
            HashSet <CkanModule> toInstall = new HashSet <CkanModule>();
            var toUninstall = new HashSet <string>();
            var toUpgrade   = new HashSet <string>();

            // First compose sets of what the user wants installed, upgraded, and removed.
            foreach (ModChange change in opts.Key)
            {
                switch (change.ChangeType)
                {
                case GUIModChangeType.Remove:
                    toUninstall.Add(change.Mod.Identifier);
                    break;

                case GUIModChangeType.Update:
                    toUpgrade.Add(change.Mod.Identifier);
                    break;

                case GUIModChangeType.Install:
                    toInstall.Add(change.Mod.ToModule());
                    break;

                case GUIModChangeType.Replace:
                    ModuleReplacement repl = registry.GetReplacement(change.Mod.ToModule(), CurrentInstance.VersionCriteria());
                    if (repl != null)
                    {
                        toUninstall.Add(repl.ToReplace.identifier);
                        toInstall.Add(repl.ReplaceWith);
                    }
                    break;
                }
            }

            // Prompt for recommendations and suggestions, if any
            var recRows = getRecSugRows(
                opts.Key.Where(ch => ch.ChangeType == GUIModChangeType.Install)
                .Select(ch => ch.Mod.ToModule()),
                registry, toInstall
                );

            if (recRows.Any())
            {
                ShowRecSugDialog(recRows, toInstall);
            }

            tabController.HideTab("ChooseRecommendedModsTabPage");

            if (installCanceled)
            {
                tabController.ShowTab("ManageModsTabPage");
                e.Result = new KeyValuePair <bool, ModChanges>(false, opts.Key);
                return;
            }

            // Now let's make all our changes.
            tabController.RenameTab("WaitTabPage", "Status log");
            ShowWaitDialog();
            tabController.SetTabLock(true);

            IDownloader downloader = new NetAsyncModulesDownloader(GUI.user);

            cancelCallback = () =>
            {
                downloader.CancelDownload();
                installCanceled = true;
            };

            // checks if all actions were successfull
            bool processSuccessful       = false;
            bool resolvedAllProvidedMods = false;

            // uninstall/installs/upgrades until every list is empty
            // if the queue is NOT empty, resolvedAllProvidedMods is set to false until the action is done
            while (!resolvedAllProvidedMods)
            {
                try
                {
                    e.Result = new KeyValuePair <bool, ModChanges>(false, opts.Key);
                    if (toUninstall.Count > 0)
                    {
                        processSuccessful = false;
                        if (!installCanceled)
                        {
                            installer.UninstallList(toUninstall, false, toInstall.Select(m => m.identifier));
                            processSuccessful = true;
                        }
                    }
                    if (toUpgrade.Count > 0)
                    {
                        processSuccessful = false;
                        if (!installCanceled)
                        {
                            installer.Upgrade(toUpgrade, downloader);
                            processSuccessful = true;
                        }
                    }
                    if (toInstall.Count > 0)
                    {
                        processSuccessful = false;
                        if (!installCanceled)
                        {
                            installer.InstallList(toInstall, opts.Value, downloader, false);
                            processSuccessful = true;
                        }
                    }
                    e.Result = new KeyValuePair <bool, ModChanges>(processSuccessful, opts.Key);
                    if (installCanceled)
                    {
                        return;
                    }
                    resolvedAllProvidedMods = true;
                }
                catch (TooManyModsProvideKraken k)
                {
                    // Prompt user to choose which mod to use
                    CkanModule chosen = TooManyModsProvideCore(k).Result;
                    // Close the selection prompt
                    Util.Invoke(this, () =>
                    {
                        tabController.ShowTab("WaitTabPage");
                        tabController.HideTab("ChooseProvidedModsTabPage");
                    });
                    if (chosen != null)
                    {
                        // User picked a mod, queue it up for installation
                        toInstall.Add(chosen);
                        // DON'T return so we can loop around and try the above InstallList call again
                    }
                    else
                    {
                        // User cancelled, get out
                        tabController.ShowTab("ManageModsTabPage");
                        e.Result = new KeyValuePair <bool, ModChanges>(false, opts.Key);
                        return;
                    }
                }
                catch (DependencyNotSatisfiedKraken ex)
                {
                    GUI.user.RaiseMessage(
                        "{0} requires {1} but it is not listed in the index, or not available for your version of KSP.",
                        ex.parent, ex.module);
                    return;
                }
                catch (ModuleNotFoundKraken ex)
                {
                    GUI.user.RaiseMessage(
                        "Module {0} required but it is not listed in the index, or not available for your version of KSP.",
                        ex.module);
                    return;
                }
                catch (BadMetadataKraken ex)
                {
                    GUI.user.RaiseMessage("Bad metadata detected for module {0}: {1}", ex.module, ex.Message);
                    return;
                }
                catch (FileExistsKraken ex)
                {
                    if (ex.owningModule != null)
                    {
                        GUI.user.RaiseMessage(
                            "\r\nOh no! We tried to overwrite a file owned by another mod!\r\n" +
                            "Please try a `ckan update` and try again.\r\n\r\n" +
                            "If this problem re-occurs, then it maybe a packaging bug.\r\n" +
                            "Please report it at:\r\n\r\n" +
                            "https://github.com/KSP-CKAN/NetKAN/issues/new\r\n\r\n" +
                            "Please including the following information in your report:\r\n\r\n" +
                            "File           : {0}\r\n" +
                            "Installing Mod : {1}\r\n" +
                            "Owning Mod     : {2}\r\n" +
                            "CKAN Version   : {3}\r\n",
                            ex.filename, ex.installingModule, ex.owningModule,
                            Meta.GetVersion()
                            );
                    }
                    else
                    {
                        GUI.user.RaiseMessage(
                            "\r\n\r\nOh no!\r\n\r\n" +
                            "It looks like you're trying to install a mod which is already installed,\r\n" +
                            "or which conflicts with another mod which is already installed.\r\n\r\n" +
                            "As a safety feature, the CKAN will *never* overwrite or alter a file\r\n" +
                            "that it did not install itself.\r\n\r\n" +
                            "If you wish to install {0} via the CKAN,\r\n" +
                            "then please manually uninstall the mod which owns:\r\n\r\n" +
                            "{1}\r\n\r\n" + "and try again.\r\n",
                            ex.installingModule, ex.filename
                            );
                    }

                    GUI.user.RaiseMessage("Your GameData has been returned to its original state.\r\n");
                    return;
                }
                catch (InconsistentKraken ex)
                {
                    // The prettiest Kraken formats itself for us.
                    GUI.user.RaiseMessage(ex.InconsistenciesPretty);
                    return;
                }
                catch (CancelledActionKraken)
                {
                    return;
                }
                catch (MissingCertificateKraken kraken)
                {
                    // Another very pretty kraken.
                    GUI.user.RaiseMessage(kraken.ToString());
                    return;
                }
                catch (DownloadThrottledKraken kraken)
                {
                    string msg = kraken.ToString();
                    GUI.user.RaiseMessage(msg);
                    if (YesNoDialog($"{msg}\r\n\r\nOpen settings now?", "Open Settings", "No"))
                    {
                        // Launch the URL describing this host's throttling practices, if any
                        if (kraken.infoUrl != null)
                        {
                            Process.Start(new ProcessStartInfo()
                            {
                                UseShellExecute = true,
                                FileName        = kraken.infoUrl.ToString()
                            });
                        }
                        // Now pretend they clicked the menu option for the settings
                        Enabled = false;
                        settingsDialog.ShowDialog();
                        Enabled = true;
                    }
                    return;
                }
                catch (ModuleDownloadErrorsKraken kraken)
                {
                    GUI.user.RaiseMessage(kraken.ToString());
                    GUI.user.RaiseError(kraken.ToString());
                    return;
                }
                catch (DirectoryNotFoundKraken kraken)
                {
                    GUI.user.RaiseMessage("\r\n{0}", kraken.Message);
                    return;
                }
                catch (DllNotFoundException)
                {
                    if (GUI.user.RaiseYesNoDialog("libcurl installation not found. Open wiki page for help?"))
                    {
                        Process.Start(new ProcessStartInfo()
                        {
                            UseShellExecute = true,
                            FileName        = "https://github.com/KSP-CKAN/CKAN/wiki/libcurl"
                        });
                    }
                    throw;
                }
            }
        }
Example #2
0
        // this probably needs to be refactored
        private void InstallMods(object sender, DoWorkEventArgs e)
        {
            installCanceled = false;
            Wait.ClearLog();

            var opts = (KeyValuePair <ModChanges, RelationshipResolverOptions>)e.Argument;

            RegistryManager registry_manager = RegistryManager.Instance(manager.CurrentInstance);
            Registry        registry         = registry_manager.registry;
            ModuleInstaller installer        = ModuleInstaller.GetInstance(CurrentInstance, Manager.Cache, currentUser);

            // Avoid accumulating multiple event handlers
            installer.onReportModInstalled -= OnModInstalled;
            installer.onReportModInstalled += OnModInstalled;
            // setup progress callback

            // this will be the final list of mods we want to install
            HashSet <CkanModule> toInstall = new HashSet <CkanModule>();
            var toUninstall = new HashSet <string>();
            var toUpgrade   = new HashSet <string>();

            // First compose sets of what the user wants installed, upgraded, and removed.
            foreach (ModChange change in opts.Key)
            {
                switch (change.ChangeType)
                {
                case GUIModChangeType.Remove:
                    toUninstall.Add(change.Mod.identifier);
                    break;

                case GUIModChangeType.Update:
                    toUpgrade.Add(change.Mod.identifier);
                    break;

                case GUIModChangeType.Install:
                    toInstall.Add(change.Mod);
                    break;

                case GUIModChangeType.Replace:
                    ModuleReplacement repl = registry.GetReplacement(change.Mod, CurrentInstance.VersionCriteria());
                    if (repl != null)
                    {
                        toUninstall.Add(repl.ToReplace.identifier);
                        toInstall.Add(repl.ReplaceWith);
                    }
                    break;
                }
            }

            // Prompt for recommendations and suggestions, if any
            if (installer.FindRecommendations(
                    opts.Key.Where(ch => ch.ChangeType == GUIModChangeType.Install)
                    .Select(ch => ch.Mod)
                    .ToHashSet(),
                    toInstall,
                    registry,
                    out Dictionary <CkanModule, Tuple <bool, List <string> > > recommendations,
                    out Dictionary <CkanModule, List <string> > suggestions,
                    out Dictionary <CkanModule, HashSet <string> > supporters
                    ))
            {
                tabController.ShowTab("ChooseRecommendedModsTabPage", 3);
                ChooseRecommendedMods.LoadRecommendations(
                    registry, CurrentInstance.VersionCriteria(),
                    Manager.Cache, recommendations, suggestions, supporters);
                tabController.SetTabLock(true);
                var result = ChooseRecommendedMods.Wait();
                if (result == null)
                {
                    installCanceled = true;
                }
                else
                {
                    toInstall.UnionWith(result);
                }
                tabController.SetTabLock(false);
                tabController.HideTab("ChooseRecommendedModsTabPage");
            }

            if (installCanceled)
            {
                tabController.ShowTab("ManageModsTabPage");
                e.Result = new KeyValuePair <bool, ModChanges>(false, opts.Key);
                return;
            }

            // Now let's make all our changes.
            Util.Invoke(this, () =>
            {
                // Need to be on the GUI thread to get the translated string
                tabController.RenameTab("WaitTabPage", Properties.Resources.MainInstallWaitTitle);
            });
            ShowWaitDialog();
            tabController.SetTabLock(true);

            IDownloader downloader = new NetAsyncModulesDownloader(currentUser, Manager.Cache);

            downloader.Progress    += Wait.SetModuleProgress;
            downloader.AllComplete += Wait.DownloadsComplete;
            cancelCallback          = () =>
            {
                downloader.CancelDownload();
                installCanceled = true;
            };

            HashSet <string> possibleConfigOnlyDirs = null;

            // checks if all actions were successfull
            bool processSuccessful       = false;
            bool resolvedAllProvidedMods = false;

            // uninstall/installs/upgrades until every list is empty
            // if the queue is NOT empty, resolvedAllProvidedMods is set to false until the action is done
            while (!resolvedAllProvidedMods)
            {
                try
                {
                    e.Result = new KeyValuePair <bool, ModChanges>(false, opts.Key);
                    if (toUninstall.Count > 0)
                    {
                        processSuccessful = false;
                        if (!installCanceled)
                        {
                            installer.UninstallList(toUninstall, ref possibleConfigOnlyDirs, registry_manager, false, toInstall);
                            processSuccessful = true;
                        }
                    }
                    if (toUpgrade.Count > 0)
                    {
                        processSuccessful = false;
                        if (!installCanceled)
                        {
                            installer.Upgrade(toUpgrade, downloader, ref possibleConfigOnlyDirs, registry_manager);
                            processSuccessful = true;
                        }
                    }
                    if (toInstall.Count > 0)
                    {
                        processSuccessful = false;
                        if (!installCanceled)
                        {
                            installer.InstallList(toInstall, opts.Value, registry_manager, ref possibleConfigOnlyDirs, downloader, false);
                            processSuccessful = true;
                        }
                    }

                    HandlePossibleConfigOnlyDirs(registry, possibleConfigOnlyDirs);

                    e.Result = new KeyValuePair <bool, ModChanges>(processSuccessful, opts.Key);
                    if (installCanceled)
                    {
                        return;
                    }
                    resolvedAllProvidedMods = true;
                }
                catch (TooManyModsProvideKraken k)
                {
                    // Prompt user to choose which mod to use
                    tabController.ShowTab("ChooseProvidedModsTabPage", 3);
                    ChooseProvidedMods.LoadProviders(k.requested, k.modules, Manager.Cache);
                    tabController.SetTabLock(true);
                    CkanModule chosen = ChooseProvidedMods.Wait();
                    // Close the selection prompt
                    tabController.SetTabLock(false);
                    tabController.HideTab("ChooseProvidedModsTabPage");
                    if (chosen != null)
                    {
                        // User picked a mod, queue it up for installation
                        toInstall.Add(chosen);
                        // DON'T return so we can loop around and try the above InstallList call again
                        tabController.ShowTab("WaitTabPage");
                    }
                    else
                    {
                        // User cancelled, get out
                        tabController.ShowTab("ManageModsTabPage");
                        e.Result = new KeyValuePair <bool, ModChanges>(false, opts.Key);
                        return;
                    }
                }
            }
        }
Example #3
0
        public static Dictionary <GUIMod, string> ComputeConflictsFromModList(IRegistryQuerier registry,
                                                                              IEnumerable <ModChange> change_set, GameVersionCriteria ksp_version)
        {
            var modules_to_install = new HashSet <string>();
            var modules_to_remove  = new HashSet <string>();
            var options            = new RelationshipResolverOptions
            {
                without_toomanyprovides_kraken = true,
                proceed_with_inconsistencies   = true,
                without_enforce_consistency    = true,
                with_recommends = false
            };

            foreach (var change in change_set)
            {
                switch (change.ChangeType)
                {
                case GUIModChangeType.None:
                    break;

                case GUIModChangeType.Install:
                    modules_to_install.Add(change.Mod.identifier);
                    break;

                case GUIModChangeType.Remove:
                    modules_to_remove.Add(change.Mod.identifier);
                    break;

                case GUIModChangeType.Update:
                    break;

                case GUIModChangeType.Replace:
                    ModuleReplacement repl = registry.GetReplacement(change.Mod, ksp_version);
                    if (repl != null)
                    {
                        modules_to_remove.Add(repl.ToReplace.identifier);
                        modules_to_install.Add(repl.ReplaceWith.identifier);
                    }
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }
            }

            // Only check mods that would exist after the changes are made.
            IEnumerable <CkanModule> installed = registry.InstalledModules.Where(
                im => !modules_to_remove.Contains(im.Module.identifier)
                ).Select(im => im.Module);

            // Convert ONLY modules_to_install with CkanModule.FromIDandVersion,
            // because it may not find already-installed modules.
            IEnumerable <CkanModule> mods_to_check = installed.Union(
                modules_to_install.Except(modules_to_remove).Select(
                    name => CkanModule.FromIDandVersion(registry, name, ksp_version)
                    )
                );
            var resolver = new RelationshipResolver(
                mods_to_check,
                change_set.Where(ch => ch.ChangeType == GUIModChangeType.Remove)
                .Select(ch => ch.Mod),
                options, registry, ksp_version
                );

            return(resolver.ConflictList.ToDictionary(item => new GUIMod(item.Key, registry, ksp_version),
                                                      item => item.Value));
        }
Example #4
0
        /// <summary>
        /// This function returns a changeset based on the selections of the user.
        /// Currently returns null if a conflict is detected.
        /// </summary>
        /// <param name="registry"></param>
        /// <param name="changeSet"></param>
        /// <param name="installer">A module installer for the current game instance</param>
        /// <param name="version">The version of the current game instance</param>
        public IEnumerable <ModChange> ComputeChangeSetFromModList(
            IRegistryQuerier registry, HashSet <ModChange> changeSet, ModuleInstaller installer,
            GameVersionCriteria version)
        {
            var modules_to_install = new HashSet <CkanModule>();
            var modules_to_remove  = new HashSet <CkanModule>();

            foreach (var change in changeSet)
            {
                switch (change.ChangeType)
                {
                case GUIModChangeType.None:
                    break;

                case GUIModChangeType.Update:
                case GUIModChangeType.Install:
                    modules_to_install.Add(change.Mod);
                    break;

                case GUIModChangeType.Remove:
                    modules_to_remove.Add(change.Mod);
                    break;

                case GUIModChangeType.Replace:
                    ModuleReplacement repl = registry.GetReplacement(change.Mod, version);
                    if (repl != null)
                    {
                        modules_to_remove.Add(repl.ToReplace);
                        modules_to_install.Add(repl.ReplaceWith);
                    }
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }
            }

            var installed_modules =
                registry.InstalledModules.Select(imod => imod.Module).ToDictionary(mod => mod.identifier, mod => mod);

            foreach (var dependent in registry.FindReverseDependencies(
                         modules_to_remove
                         .Select(mod => mod.identifier)
                         .Except(modules_to_install.Select(m => m.identifier)),
                         modules_to_install
                         ))
            {
                //TODO This would be a good place to have an event that alters the row's graphics to show it will be removed
                CkanModule depMod;
                if (installed_modules.TryGetValue(dependent, out depMod))
                {
                    CkanModule module_by_version = registry.GetModuleByVersion(depMod.identifier,
                                                                               depMod.version)
                                                   ?? registry.InstalledModule(dependent).Module;
                    changeSet.Add(new ModChange(module_by_version, GUIModChangeType.Remove, null));
                    modules_to_remove.Add(module_by_version);
                }
            }
            foreach (var im in registry.FindRemovableAutoInstalled(
                         registry.InstalledModules.Where(im => !modules_to_remove.Any(m => m.identifier == im.identifier) || modules_to_install.Any(m => m.identifier == im.identifier))
                         ))
            {
                changeSet.Add(new ModChange(im.Module, GUIModChangeType.Remove, new SelectionReason.NoLongerUsed()));
                modules_to_remove.Add(im.Module);
            }

            // Get as many dependencies as we can, but leave decisions and prompts for installation time
            RelationshipResolverOptions opts = RelationshipResolver.DependsOnlyOpts();

            opts.without_toomanyprovides_kraken = true;
            opts.without_enforce_consistency    = true;

            var resolver = new RelationshipResolver(
                modules_to_install,
                modules_to_remove,
                opts, registry, version);

            changeSet.UnionWith(
                resolver.ModList()
                .Select(m => new ModChange(m, GUIModChangeType.Install, resolver.ReasonFor(m))));

            return(changeSet);
        }
Example #5
0
        /// <summary>
        /// This function returns a changeset based on the selections of the user.
        /// Currently returns null if a conflict is detected.
        /// </summary>
        /// <param name="registry"></param>
        /// <param name="changeSet"></param>
        /// <param name="installer">A module installer for the current KSP install</param>
        /// <param name="version">The version of the current KSP install</param>
        public async Task <IEnumerable <ModChange> > ComputeChangeSetFromModList(
            IRegistryQuerier registry, HashSet <ModChange> changeSet, ModuleInstaller installer,
            KspVersionCriteria version)
        {
            var modules_to_install = new HashSet <CkanModule>();
            var modules_to_remove  = new HashSet <CkanModule>();

            foreach (var change in changeSet)
            {
                switch (change.ChangeType)
                {
                case GUIModChangeType.None:
                    break;

                case GUIModChangeType.Update:
                case GUIModChangeType.Install:
                    //TODO: Fix
                    //This will give us a mod with a wrong version!
                    modules_to_install.Add(change.Mod.ToCkanModule());
                    break;

                case GUIModChangeType.Remove:
                    modules_to_remove.Add(change.Mod);
                    break;

                case GUIModChangeType.Replace:
                    ModuleReplacement repl = registry.GetReplacement(change.Mod.ToModule(), version);
                    if (repl != null)
                    {
                        modules_to_remove.Add(repl.ToReplace);
                        modules_to_install.Add(repl.ReplaceWith);
                    }
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }
            }

            var installed_modules =
                registry.InstalledModules.Select(imod => imod.Module).ToDictionary(mod => mod.identifier, mod => mod);

            foreach (var dependency in registry.FindReverseDependencies(
                         modules_to_remove
                         .Select(mod => mod.identifier)
                         .Except(modules_to_install.Select(m => m.identifier))
                         ))
            {
                //TODO This would be a good place to have a event that alters the row's graphics to show it will be removed
                CkanModule module_by_version = registry.GetModuleByVersion(installed_modules[dependency].identifier,
                                                                           installed_modules[dependency].version) ?? registry.InstalledModule(dependency).Module;
                changeSet.Add(new ModChange(new GUIMod(module_by_version, registry, version), GUIModChangeType.Remove, null));
                modules_to_remove.Add(module_by_version);
            }
            foreach (var im in registry.FindRemovableAutoInstalled(
                         registry.InstalledModules.Where(im => !modules_to_remove.Any(m => m.identifier == im.identifier))
                         ))
            {
                changeSet.Add(new ModChange(new GUIMod(im.Module, registry, version), GUIModChangeType.Remove, new SelectionReason.NoLongerUsed()));
                modules_to_remove.Add(im.Module);
            }

            bool handled_all_too_many_provides = false;

            while (!handled_all_too_many_provides)
            {
                //Can't await in catch clause - doesn't seem to work in mono. Hence this flag
                TooManyModsProvideKraken kraken;
                try
                {
                    new RelationshipResolver(
                        modules_to_install,
                        modules_to_remove,
                        RelationshipResolver.DependsOnlyOpts(),
                        registry, version);
                    handled_all_too_many_provides = true;
                    continue;
                }
                catch (TooManyModsProvideKraken k)
                {
                    kraken = k;
                }
                //Shouldn't get here unless there is a kraken.
                var mod = await too_many_provides(kraken);

                if (mod != null)
                {
                    modules_to_install.Add(mod);
                }
                else
                {
                    //TODO Is could be a new type of Kraken.
                    throw kraken;
                }
            }

            var resolver = new RelationshipResolver(
                modules_to_install,
                modules_to_remove,
                RelationshipResolver.DependsOnlyOpts(), registry, version);

            changeSet.UnionWith(
                resolver.ModList()
                .Select(m => new ModChange(new GUIMod(m, registry, version), GUIModChangeType.Install, resolver.ReasonFor(m))));

            return(changeSet);
        }
Example #6
0
        // this probably needs to be refactored
        private void InstallMods(object sender, DoWorkEventArgs e)
        {
            installCanceled = false;
            ClearLog();

            var opts = (KeyValuePair <ModChanges, RelationshipResolverOptions>)e.Argument;

            IRegistryQuerier registry  = RegistryManager.Instance(manager.CurrentInstance).registry;
            ModuleInstaller  installer = ModuleInstaller.GetInstance(CurrentInstance, Manager.Cache, GUI.user);

            // Avoid accumulating multiple event handlers
            installer.onReportModInstalled -= OnModInstalled;
            installer.onReportModInstalled += OnModInstalled;
            // setup progress callback

            // this will be the final list of mods we want to install
            HashSet <CkanModule> toInstall = new HashSet <CkanModule>();
            var toUninstall = new HashSet <string>();
            var toUpgrade   = new HashSet <string>();

            // First compose sets of what the user wants installed, upgraded, and removed.
            foreach (ModChange change in opts.Key)
            {
                switch (change.ChangeType)
                {
                case GUIModChangeType.Remove:
                    toUninstall.Add(change.Mod.Identifier);
                    break;

                case GUIModChangeType.Update:
                    toUpgrade.Add(change.Mod.Identifier);
                    break;

                case GUIModChangeType.Install:
                    toInstall.Add(change.Mod.ToModule());
                    break;

                case GUIModChangeType.Replace:
                    ModuleReplacement repl = registry.GetReplacement(change.Mod.ToModule(), CurrentInstance.VersionCriteria());
                    if (repl != null)
                    {
                        toUninstall.Add(repl.ToReplace.identifier);
                        toInstall.Add(repl.ReplaceWith);
                    }
                    break;
                }
            }

            // Prompt for recommendations and suggestions, if any
            var recRows = getRecSugRows(
                opts.Key.Where(ch => ch.ChangeType == GUIModChangeType.Install)
                .Select(ch => ch.Mod.ToModule()),
                registry, toInstall
                );

            if (recRows.Any())
            {
                ShowRecSugDialog(recRows, toInstall);
            }

            tabController.HideTab("ChooseRecommendedModsTabPage");

            if (installCanceled)
            {
                tabController.ShowTab("ManageModsTabPage");
                e.Result = new KeyValuePair <bool, ModChanges>(false, opts.Key);
                return;
            }

            // Now let's make all our changes.
            tabController.RenameTab("WaitTabPage", Properties.Resources.MainInstallWaitTitle);
            ShowWaitDialog();
            tabController.SetTabLock(true);

            IDownloader downloader = new NetAsyncModulesDownloader(GUI.user, Manager.Cache);

            cancelCallback = () =>
            {
                downloader.CancelDownload();
                installCanceled = true;
            };

            // checks if all actions were successfull
            bool processSuccessful       = false;
            bool resolvedAllProvidedMods = false;

            // uninstall/installs/upgrades until every list is empty
            // if the queue is NOT empty, resolvedAllProvidedMods is set to false until the action is done
            while (!resolvedAllProvidedMods)
            {
                try
                {
                    e.Result = new KeyValuePair <bool, ModChanges>(false, opts.Key);
                    if (toUninstall.Count > 0)
                    {
                        processSuccessful = false;
                        if (!installCanceled)
                        {
                            installer.UninstallList(toUninstall, false, toInstall.Select(m => m.identifier));
                            processSuccessful = true;
                        }
                    }
                    if (toUpgrade.Count > 0)
                    {
                        processSuccessful = false;
                        if (!installCanceled)
                        {
                            installer.Upgrade(toUpgrade, downloader);
                            processSuccessful = true;
                        }
                    }
                    if (toInstall.Count > 0)
                    {
                        processSuccessful = false;
                        if (!installCanceled)
                        {
                            installer.InstallList(toInstall, opts.Value, downloader, false);
                            processSuccessful = true;
                        }
                    }
                    e.Result = new KeyValuePair <bool, ModChanges>(processSuccessful, opts.Key);
                    if (installCanceled)
                    {
                        return;
                    }
                    resolvedAllProvidedMods = true;
                }
                catch (TooManyModsProvideKraken k)
                {
                    // Prompt user to choose which mod to use
                    CkanModule chosen = TooManyModsProvideCore(k).Result;
                    // Close the selection prompt
                    Util.Invoke(this, () =>
                    {
                        tabController.ShowTab("WaitTabPage");
                        tabController.HideTab("ChooseProvidedModsTabPage");
                    });
                    if (chosen != null)
                    {
                        // User picked a mod, queue it up for installation
                        toInstall.Add(chosen);
                        // DON'T return so we can loop around and try the above InstallList call again
                    }
                    else
                    {
                        // User cancelled, get out
                        tabController.ShowTab("ManageModsTabPage");
                        e.Result = new KeyValuePair <bool, ModChanges>(false, opts.Key);
                        return;
                    }
                }
                catch (DependencyNotSatisfiedKraken ex)
                {
                    GUI.user.RaiseMessage(Properties.Resources.MainInstallDepNotSatisfied, ex.parent, ex.module);
                    return;
                }
                catch (ModuleNotFoundKraken ex)
                {
                    GUI.user.RaiseMessage(Properties.Resources.MainInstallNotFound, ex.module);
                    return;
                }
                catch (BadMetadataKraken ex)
                {
                    GUI.user.RaiseMessage(Properties.Resources.MainInstallBadMetadata, ex.module, ex.Message);
                    return;
                }
                catch (FileExistsKraken ex)
                {
                    if (ex.owningModule != null)
                    {
                        GUI.user.RaiseMessage(
                            Properties.Resources.MainInstallFileExists,
                            ex.filename, ex.installingModule, ex.owningModule,
                            Meta.GetVersion()
                            );
                    }
                    else
                    {
                        GUI.user.RaiseMessage(
                            Properties.Resources.MainInstallUnownedFileExists,
                            ex.installingModule, ex.filename
                            );
                    }
                    GUI.user.RaiseMessage(Properties.Resources.MainInstallGameDataReverted);
                    return;
                }
                catch (InconsistentKraken ex)
                {
                    // The prettiest Kraken formats itself for us.
                    GUI.user.RaiseMessage(ex.InconsistenciesPretty);
                    return;
                }
                catch (CancelledActionKraken)
                {
                    return;
                }
                catch (MissingCertificateKraken kraken)
                {
                    // Another very pretty kraken.
                    GUI.user.RaiseMessage(kraken.ToString());
                    return;
                }
                catch (DownloadThrottledKraken kraken)
                {
                    string msg = kraken.ToString();
                    GUI.user.RaiseMessage(msg);
                    if (YesNoDialog(string.Format(Properties.Resources.MainInstallOpenSettingsPrompt, msg),
                                    Properties.Resources.MainInstallOpenSettings,
                                    Properties.Resources.MainInstallNo))
                    {
                        // Launch the URL describing this host's throttling practices, if any
                        if (kraken.infoUrl != null)
                        {
                            Process.Start(new ProcessStartInfo()
                            {
                                UseShellExecute = true,
                                FileName        = kraken.infoUrl.ToString()
                            });
                        }
                        // Now pretend they clicked the menu option for the settings
                        Enabled = false;
                        settingsDialog.ShowDialog();
                        Enabled = true;
                    }
                    return;
                }
                catch (ModuleDownloadErrorsKraken kraken)
                {
                    GUI.user.RaiseMessage(kraken.ToString());
                    GUI.user.RaiseError(kraken.ToString());
                    return;
                }
                catch (DirectoryNotFoundKraken kraken)
                {
                    GUI.user.RaiseMessage("\r\n{0}", kraken.Message);
                    return;
                }
                catch (DllNotFoundException)
                {
                    if (GUI.user.RaiseYesNoDialog(Properties.Resources.MainInstallLibCurlMissing))
                    {
                        Process.Start(new ProcessStartInfo()
                        {
                            UseShellExecute = true,
                            FileName        = "https://github.com/KSP-CKAN/CKAN/wiki/libcurl"
                        });
                    }
                    throw;
                }
            }
        }