Exemplo n.º 1
0
        private void Download()
        {
            ProgressScreen            ps   = new ProgressScreen($"Downloading {mod.identifier}");
            NetAsyncModulesDownloader dl   = new NetAsyncModulesDownloader(ps);
            ModuleInstaller           inst = ModuleInstaller.GetInstance(manager.CurrentInstance, manager.Cache, ps);

            LaunchSubScreen(
                ps,
                () => {
                try {
                    dl.DownloadModules(manager.Cache, new List <CkanModule> {
                        mod
                    });
                    if (!manager.Cache.IsMaybeCachedZip(mod))
                    {
                        ps.RaiseError("Download failed, file is corrupted");
                    }
                } catch (Exception ex) {
                    ps.RaiseError($"Download failed: {ex}");
                }
            }
                );
            // Don't let the installer re-use old screen references
            inst.User = null;
        }
Exemplo n.º 2
0
        private void CacheMod(object sender, DoWorkEventArgs e)
        {
            ResetProgress();
            ClearLog();

            NetAsyncModulesDownloader dowloader = new NetAsyncModulesDownloader(currentUser);

            dowloader.DownloadModules(CurrentInstance.Cache, new List<CkanModule> { (CkanModule)e.Argument });
            e.Result = e.Argument;
        }
Exemplo n.º 3
0
 /// <summary>
 /// The core of the module upgrading logic, with callbacks to
 /// support different input formats managed by the calling code.
 /// Handles transactions, creating commonly required objects,
 /// looping logic, prompting for TooManyModsProvideKraken resolution.
 /// </summary>
 /// <param name="manager">Game instance manager to use</param>
 /// <param name="user">IUser object for output</param>
 /// <param name="ksp">Game instance to use</param>
 /// <param name="attemptUpgradeCallback">Function to call to try to perform the actual upgrade, may throw TooManyModsProvideKraken</param>
 /// <param name="addUserChoiceCallback">Function to call when the user has requested a new module added to the change set in response to TooManyModsProvideKraken</param>
 private static void UpgradeModules(
     GameInstanceManager manager, IUser user, CKAN.GameInstance ksp,
     AttemptUpgradeAction attemptUpgradeCallback,
     System.Action <CkanModule> addUserChoiceCallback)
 {
     using (TransactionScope transact = CkanTransaction.CreateTransactionScope()) {
         var installer  = new ModuleInstaller(ksp, manager.Cache, user);
         var downloader = new NetAsyncModulesDownloader(user, manager.Cache);
         var regMgr     = RegistryManager.Instance(ksp);
         HashSet <string> possibleConfigOnlyDirs = null;
         bool             done = false;
         while (!done)
         {
             try
             {
                 attemptUpgradeCallback?.Invoke(installer, downloader, regMgr, ref possibleConfigOnlyDirs);
                 transact.Complete();
                 done = true;
             }
             catch (TooManyModsProvideKraken k)
             {
                 int choice = user.RaiseSelectionDialog(
                     k.Message,
                     k.modules.Select(m => $"{m.identifier} ({m.name})").ToArray());
                 if (choice < 0)
                 {
                     return;
                 }
                 else
                 {
                     addUserChoiceCallback?.Invoke(k.modules[choice]);
                 }
             }
         }
     }
 }
Exemplo n.º 4
0
        /// <summary>
        /// Run the screen
        /// </summary>
        /// <param name="process">Framework parameter not used by this object</param>
        public override void Run(Action process = null)
        {
            HashSet <string> rejected = new HashSet <string>();

            DrawBackground();
            using (TransactionScope trans = CkanTransaction.CreateTransactionScope()) {
                bool retry = false;
                do
                {
                    Draw();
                    try {
                        // Reset this so we stop unless an exception sets it to true
                        retry = false;

                        // GUI prompts user to choose recs/sugs,
                        // CmdLine assumes recs and ignores sugs
                        if (plan.Install.Count > 0)
                        {
                            // Track previously rejected optional dependencies and don't prompt for them again.
                            DependencyScreen ds = new DependencyScreen(manager, plan, rejected, debug);
                            if (ds.HaveOptions())
                            {
                                LaunchSubScreen(ds);
                            }
                        }

                        // FUTURE: BackgroundWorker

                        HashSet <string> possibleConfigOnlyDirs = null;

                        RegistryManager regMgr = RegistryManager.Instance(manager.CurrentInstance);
                        ModuleInstaller inst   = ModuleInstaller.GetInstance(manager.CurrentInstance, manager.Cache, this);
                        inst.onReportModInstalled = OnModInstalled;
                        if (plan.Remove.Count > 0)
                        {
                            inst.UninstallList(plan.Remove, ref possibleConfigOnlyDirs, regMgr, true, plan.Install);
                            plan.Remove.Clear();
                        }
                        NetAsyncModulesDownloader dl = new NetAsyncModulesDownloader(this, manager.Cache);
                        if (plan.Upgrade.Count > 0)
                        {
                            inst.Upgrade(plan.Upgrade, dl, ref possibleConfigOnlyDirs, regMgr);
                            plan.Upgrade.Clear();
                        }
                        if (plan.Install.Count > 0)
                        {
                            List <CkanModule> iList = new List <CkanModule>(plan.Install);
                            inst.InstallList(iList, resolvOpts, regMgr, ref possibleConfigOnlyDirs, dl);
                            plan.Install.Clear();
                        }
                        if (plan.Replace.Count > 0)
                        {
                            inst.Replace(AllReplacements(plan.Replace), resolvOpts, dl, ref possibleConfigOnlyDirs, regMgr, true);
                        }

                        trans.Complete();
                        // Don't let the installer re-use old screen references
                        inst.User = null;
                        inst.onReportModInstalled = null;
                    } catch (CancelledActionKraken) {
                        // Don't need to tell the user they just cancelled out.
                    } catch (FileNotFoundKraken ex) {
                        // Possible file corruption
                        RaiseError(ex.Message);
                    } catch (DirectoryNotFoundKraken ex) {
                        RaiseError(ex.Message);
                    } catch (FileExistsKraken ex) {
                        if (ex.owningModule != null)
                        {
                            RaiseMessage($"{ex.installingModule} tried to install {ex.filename}, but {ex.owningModule} has already installed it.");
                            RaiseMessage($"Please report this problem at https://github.com/KSP-CKAN/NetKAN/issues/new/choose");
                        }
                        else
                        {
                            RaiseMessage($"{ex.installingModule} tried to install {ex.filename}, but it is already installed.");
                            RaiseMessage($"Please manually uninstall the mod that owns this file to install {ex.installingModule}.");
                        }
                        RaiseError("Game files reverted.");
                    } catch (DownloadErrorsKraken ex) {
                        RaiseError(ex.ToString());
                    } catch (ModuleDownloadErrorsKraken ex) {
                        RaiseError(ex.ToString());
                    } catch (DownloadThrottledKraken ex) {
                        if (RaiseYesNoDialog($"{ex.ToString()}\n\nEdit authentication tokens now?"))
                        {
                            if (ex.infoUrl != null)
                            {
                                ModInfoScreen.LaunchURL(ex.infoUrl);
                            }
                            LaunchSubScreen(new AuthTokenScreen());
                        }
                    } catch (MissingCertificateKraken ex) {
                        RaiseError(ex.ToString());
                    } catch (InconsistentKraken ex) {
                        RaiseError(ex.InconsistenciesPretty);
                    } catch (TooManyModsProvideKraken ex) {
                        ConsoleChoiceDialog <CkanModule> ch = new ConsoleChoiceDialog <CkanModule>(
                            $"Module {ex.requested} is provided by multiple modules. Which would you like to install?",
                            "Name",
                            ex.modules,
                            (CkanModule mod) => mod.ToString()
                            );
                        CkanModule chosen = ch.Run();
                        DrawBackground();
                        if (chosen != null)
                        {
                            // Use chosen to continue installing
                            plan.Install.Add(chosen);
                            retry = true;
                        }
                    } catch (BadMetadataKraken ex) {
                        RaiseError($"Bad metadata detected for {ex.module}: {ex.Message}");
                    } catch (DependencyNotSatisfiedKraken ex) {
                        RaiseError($"{ex.parent} requires {ex.module}, but it is not listed in the index, or not available for your version of KSP.\r\n{ex.Message}");
                    } catch (ModuleNotFoundKraken ex) {
                        RaiseError($"Module {ex.module} required but it is not listed in the index, or not available for your version of KSP.\r\n{ex.Message}");
                    } catch (ModNotInstalledKraken ex) {
                        RaiseError($"{ex.mod} is not installed, can't remove");
                    }
                } while (retry);
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// Makes sure all the specified mods are downloaded.
        /// </summary>
        private void DownloadModules(IEnumerable<CkanModule> mods, NetAsyncModulesDownloader downloader)
        {
            List<CkanModule> downloads = mods.Where(module => !ksp.Cache.IsCachedZip(module.download)).ToList();

            if (downloads.Count > 0)
            {
                downloader.DownloadModules(ksp.Cache, downloads);
            }
        }
Exemplo n.º 6
0
        /// <summary>
        /// Upgrades or installs the mods listed to the specified versions for the user's KSP.
        /// Will *re-install* or *downgrade* (with a warning) as well as upgrade.
        /// Throws ModuleNotFoundKraken if a module is not installed.
        /// </summary>
        public void Upgrade(IEnumerable<CkanModule> modules, NetAsyncModulesDownloader netAsyncDownloader, bool enforceConsistency = true)
        {
            // Start by making sure we've downloaded everything.
            DownloadModules(modules, netAsyncDownloader);

            // Our upgrade involves removing everything that's currently installed, then
            // adding everything that needs installing (which may involve new mods to
            // satisfy dependencies). We always know the list passed in is what we need to
            // install, but we need to calculate what needs to be removed.
            var to_remove = new List<string>();

            // Let's discover what we need to do with each module!
            foreach (CkanModule module in modules)
            {
                string ident = module.identifier;
                InstalledModule installed_mod = registry_manager.registry.InstalledModule(ident);

                if (installed_mod == null)
                {
                    //Maybe ModuleNotInstalled ?
                    if (registry_manager.registry.IsAutodetected(ident))
                    {
                        throw new ModuleNotFoundKraken(ident, module.version.ToString(), String.Format("Can't upgrade {0} as it was not installed by CKAN. \r\n Please remove manually before trying to install it.", ident));
                    }

                    User.RaiseMessage("Installing previously uninstalled mod {0}", ident);
                }
                else
                {
                    // Module already installed. We'll need to remove it first.
                    to_remove.Add(module.identifier);

                    CkanModule installed = installed_mod.Module;
                    if (installed.version.IsEqualTo(module.version))
                    {
                        log.InfoFormat("{0} is already at the latest version, reinstalling", installed.identifier);
                    }
                    else if (installed.version.IsGreaterThan(module.version))
                    {
                        log.WarnFormat("Downgrading {0} from {1} to {2}", ident, installed.version, module.version);
                    }
                    else
                    {
                        log.InfoFormat("Upgrading {0} to {1}", ident, module.version);
                    }
                }
            }

            AddRemove(
                modules,
                to_remove,
                enforceConsistency
            );
        }
Exemplo n.º 7
0
        /// <summary>
        /// Upgrades the mods listed to the latest versions for the user's KSP.
        /// Will *re-install* with warning even if an upgrade is not available.
        /// Throws ModuleNotFoundKraken if module is not installed, or not available.
        /// </summary>
        public void Upgrade(IEnumerable<string> identifiers, NetAsyncModulesDownloader netAsyncDownloader, bool enforceConsistency = true)
        {
            var options = new RelationshipResolverOptions();

            // We do not wish to pull in any suggested or recommended mods.
            options.with_recommends = false;
            options.with_suggests = false;

            var resolver = new RelationshipResolver(identifiers.ToList(), options, registry_manager.registry, ksp.VersionCriteria());
            Upgrade(resolver.ModList(), netAsyncDownloader, enforceConsistency);
        }
Exemplo n.º 8
0
        /// <summary>
        ///     Installs all modules given a list of identifiers as a transaction. Resolves dependencies.
        ///     This *will* save the registry at the end of operation.
        ///
        /// Propagates a BadMetadataKraken if our install metadata is bad.
        /// Propagates a FileExistsKraken if we were going to overwrite a file.
        /// Propagates a CancelledActionKraken if the user cancelled the install.
        /// </summary>
        //
        // TODO: Break this up into smaller pieces! It's huge!
        public void InstallList(
            ICollection<CkanModule> modules,
            RelationshipResolverOptions options,
            IDownloader downloader = null
        )
        {
            var resolver = new RelationshipResolver(modules, options, registry_manager.registry, ksp.VersionCriteria());
            var modsToInstall = resolver.ModList().ToList();
            List<CkanModule> downloads = new List<CkanModule> ();

            // TODO: All this user-stuff should be happening in another method!
            // We should just be installing mods as a transaction.

            User.RaiseMessage("About to install...\r\n");

            foreach (CkanModule module in modsToInstall)
            {
                if (!ksp.Cache.IsCachedZip(module.download))
                {
                    User.RaiseMessage(" * {0} {1}", module.name, module.version);
                    downloads.Add(module);
                }
                else
                {
                    User.RaiseMessage(" * {0} {1}(cached)", module.name, module.version);
                }
            }

            bool ok = User.RaiseYesNoDialog("\r\nContinue?");

            if (!ok)
            {
                throw new CancelledActionKraken("User declined install list");
            }

            User.RaiseMessage(String.Empty); // Just to look tidy.

            if (downloads.Count > 0)
            {
                if (downloader == null)
                {
                    downloader = new NetAsyncModulesDownloader(User);
                }

                downloader.DownloadModules(ksp.Cache, downloads);
            }

            // We're about to install all our mods; so begin our transaction.
            using (TransactionScope transaction = CkanTransaction.CreateTransactionScope())
            {
                for (int i = 0; i < modsToInstall.Count; i++)
                {
                    int percent_complete = (i * 100) / modsToInstall.Count;

                    User.RaiseProgress(String.Format("Installing mod \"{0}\"", modsToInstall[i]),
                                         percent_complete);

                    Install(modsToInstall[i]);
                }

                User.RaiseProgress("Updating registry", 70);

                registry_manager.Save(!options.without_enforce_consistency);

                User.RaiseProgress("Commiting filesystem changes", 80);

                transaction.Complete();

            }

            // We can scan GameData as a separate transaction. Installing the mods
            // leaves everything consistent, and this is just gravy. (And ScanGameData
            // acts as a Tx, anyway, so we don't need to provide our own.)

            User.RaiseProgress("Rescanning GameData", 90);

            if (!options.without_enforce_consistency)
            {
                ksp.ScanGameData();
            }

            User.RaiseProgress("Done!\r\n", 100);
        }
Exemplo n.º 9
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, GUI.user);
            // setup progress callback

            toInstall = new HashSet<string>();
            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.Identifier);
                        break;
                }
            }

            // Now work on satisifying dependencies.

            var recommended = new Dictionary<string, List<string>>();
            var suggested = new Dictionary<string, List<string>>();

            foreach (var change in opts.Key)
            {
                if (change.ChangeType == GUIModChangeType.Install)
                {
                    AddMod(change.Mod.ToModule().recommends, recommended, change.Mod.Identifier, registry);
                    AddMod(change.Mod.ToModule().suggests, suggested, change.Mod.Identifier, registry);
                }
            }

            ShowSelection(recommended);
            ShowSelection(suggested, true);

            tabController.HideTab("ChooseRecommendedModsTabPage");

            if (installCanceled)
            {
                tabController.HideTab("WaitTabPage");
                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");
            tabController.ShowTab("WaitTabPage");
            tabController.SetTabLock(true);

            var downloader = new NetAsyncModulesDownloader(GUI.user);
            cancelCallback = () =>
            {
                downloader.CancelDownload();
                installCanceled = true;
            };

            //Transaction is needed here to revert changes when an installation is cancelled
            //TODO: Cancellation should be handelt in the ModuleInstaller
            using (var transaction = CkanTransaction.CreateTransactionScope())
            {

                //Set the result to false/failed in case we return
                e.Result = new KeyValuePair<bool, ModChanges>(false, opts.Key);
                SetDescription("Uninstalling selected mods");
                if (!WasSuccessful(() => installer.UninstallList(toUninstall)))
                    return;
                if (installCanceled) return;

                SetDescription("Updating selected mods");
                if (!WasSuccessful(() => installer.Upgrade(toUpgrade, downloader)))
                    return;
                if (installCanceled) return;

                // TODO: We should be able to resolve all our provisioning conflicts
                // before we start installing anything. CKAN.SanityChecker can be used to
                // pre-check if our changes are going to be consistent.

                bool resolvedAllProvidedMods = false;

                while (!resolvedAllProvidedMods)
                {
                    if (installCanceled)
                    {
                        e.Result = new KeyValuePair<bool, ModChanges>(false, opts.Key);
                        return;
                    }
                    var ret = InstallList(toInstall, opts.Value, downloader);
                    if (!ret)
                    {
                        // install failed for some reason, error message is already displayed to the user
                        e.Result = new KeyValuePair<bool, ModChanges>(false, opts.Key);
                        return;
                    }
                    resolvedAllProvidedMods = true;
                }

                if (!installCanceled)
                {
                    transaction.Complete();
                }
            }

            e.Result = new KeyValuePair<bool, ModChanges>(!installCanceled, opts.Key);
        }
Exemplo n.º 10
0
        public void InstallAndSortByCompat_WithAnyCompat_NoCrash()
        {
            /*
             * // An exception would be thrown at the bottom of this.
             * var main = new Main(null, new GUIUser(), false);
             * main.Manager = _manager;
             * // First sort by name
             * main.configuration.SortByColumnIndex = 2;
             * // Now sort by version
             * main.configuration.SortByColumnIndex = 6;
             * main.MarkModForInstall("kOS");
             *
             * // Make sure we have one requested change
             * var changeList = main.mainModList.ComputeUserChangeSet()
             *  .Select((change) => change.Mod.ToCkanModule()).ToList();
             *
             * // Do the install
             * new ModuleInstaller(_instance.KSP, main.currentUser).InstallList(
             *  changeList,
             *  new RelationshipResolverOptions(),
             *  new NetAsyncModulesDownloader(main.currentUser)
             * );
             */

            // Arrange

            DisposableKSP       instance        = new DisposableKSP();
            RegistryManager     registryManager = RegistryManager.Instance(instance.KSP);
            Registry            registry        = Registry.Empty();
            FakeConfiguration   config          = new FakeConfiguration(instance.KSP, instance.KSP.Name);
            GameInstanceManager manager         = new GameInstanceManager(new NullUser(), config);
            // A module with a ksp_version of "any" to repro our issue
            CkanModule   anyVersionModule = TestData.DogeCoinFlag_101_module();
            ModList      modList          = new ModList(null);
            DataGridView listGui          = new DataGridView();

            CKAN.ModuleInstaller      installer  = new CKAN.ModuleInstaller(instance.KSP, manager.Cache, manager.User);
            NetAsyncModulesDownloader downloader = new NetAsyncModulesDownloader(manager.User, manager.Cache);

            // Act

            // Install module and set it as pre-installed
            manager.Cache.Store(TestData.DogeCoinFlag_101_module(), TestData.DogeCoinFlagZip());
            registry.RegisterModule(anyVersionModule, new string[] { }, instance.KSP, false);
            registry.AddAvailable(anyVersionModule);

            HashSet <string> possibleConfigOnlyDirs = null;

            installer.InstallList(
                new List <CkanModule> {
                anyVersionModule
            },
                new RelationshipResolverOptions(),
                registryManager,
                ref possibleConfigOnlyDirs,
                downloader
                );

            // This module is not for "any" version,
            // to provide another to sort against
            registry.AddAvailable(TestData.kOS_014_module());

            // TODO: Refactor the column header code to allow mocking of the GUI without creating columns
            const int numCheckboxCols = 4;
            const int numTextCols     = 10;

            listGui.Columns.AddRange(
                Enumerable.Range(1, numCheckboxCols)
                .Select(i => (DataGridViewColumn) new DataGridViewCheckBoxColumn())
                .Concat(Enumerable.Range(1, numTextCols)
                        .Select(i => new DataGridViewTextBoxColumn()))
                .ToArray());

            // Assert (and Act a bit more)

            Assert.IsNotNull(instance.KSP);
            Assert.IsNotNull(manager);
            Assert.IsNotNull(modList);

            var modules = registry.available_modules
                          .Select(mod => new GUIMod(mod.Value.Latest(), registry, instance.KSP.VersionCriteria()))
                          .ToList();

            listGui.Rows.AddRange(modList.ConstructModList(modules, null).ToArray());
            // The header row adds one to the count
            Assert.AreEqual(modules.Count + 1, listGui.Rows.Count);

            // Sort by game compatibility, this is the fuse-lighting
            listGui.Sort(listGui.Columns[8], ListSortDirection.Descending);

            // Mark the mod for install, after completion we will get an exception
            var otherModule = modules.First(mod => mod.Identifier.Contains("kOS"));

            otherModule.IsInstallChecked = true;

            Assert.IsTrue(otherModule.IsInstallChecked);
            Assert.IsFalse(otherModule.IsInstalled);

            Assert.DoesNotThrow(() =>
            {
                // Install the "other" module
                installer.InstallList(
                    modList.ComputeUserChangeSet(null).Select(change => change.Mod).ToList(),
                    new RelationshipResolverOptions(),
                    registryManager,
                    ref possibleConfigOnlyDirs,
                    downloader
                    );

                // Now we need to sort
                // Make sure refreshing the GUI state does not throw a NullReferenceException
                listGui.Refresh();
            });

            instance.Dispose();
            manager.Dispose();
            config.Dispose();
        }