示例#1
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(
            List <string> modules,
            RelationshipResolverOptions options,
            NetAsyncDownloader downloader = null
            )
        {
            onReportProgress = onReportProgress ?? ((message, progress) => { });

            var resolver = new RelationshipResolver(modules, options, registry_manager.registry);
            List <CkanModule> modsToInstall = resolver.ModList();
            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.WriteLine("About to install...\n");

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

            bool ok = User.YesNo("\nContinue?", FrontEndType.CommandLine);

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

            User.WriteLine(""); // Just to look tidy.

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

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

            // We're about to install all our mods; so begin our transaction.
            var txoptions = new TransactionOptions();

            txoptions.Timeout = TransactionManager.MaximumTimeout;

            using (TransactionScope transaction = new TransactionScope(TransactionScopeOption.Required, txoptions))
            {
                for (int i = 0; i < modsToInstall.Count; i++)
                {
                    int percentComplete = (i * 100) / modsToInstall.Count;

                    onReportProgress(String.Format("Installing mod \"{0}\"", modsToInstall[i]),
                                     percentComplete);

                    Install(modsToInstall[i]);
                }

                onReportProgress("Updating registry", 70);

                registry_manager.Save();

                onReportProgress("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.)

            onReportProgress("Rescanning GameData", 90);

            ksp.ScanGameData();

            onReportProgress("Done!", 100);
        }
示例#2
0
        /// <summary>
        /// Downloads all the modules specified to the cache.
        /// Even if modules share download URLs, they will only be downloaded once.
        /// Blocks until the download is complete, cancelled, or errored.
        /// </summary>
        public void DownloadModules(
            NetFileCache cache,
            IEnumerable <CkanModule> modules,
            ModuleInstallerReportProgress progress = null
            )
        {
            var unique_downloads = new Dictionary <Uri, CkanModule>();

            // Walk through all our modules, but only keep the first of each
            // one that has a unique download path.
            foreach (CkanModule module in modules)
            {
                if (!unique_downloads.ContainsKey(module.download))
                {
                    unique_downloads[module.download] = module;
                }
            }

            // Attach our progress report, if requested.
            if (progress != null)
            {
                this.onProgressReport += (percent, bytesPerSecond, bytesLeft) =>
                                         progress(
                    String.Format("{0} kbps - downloading - {1} MiB left", bytesPerSecond / 1024, bytesLeft / 1024 / 1024),
                    percent
                    );
            }

            this.onCompleted = (_uris, paths, errors) => ModuleDownloadsComplete(cache, _uris, paths, unique_downloads.Values.ToArray(), errors);

            // Start the download!
            this.Download(unique_downloads.Keys);

            // The Monitor.Wait function releases a lock, and then waits until it can re-acquire it.
            // Elsewhere, our downloading callback pulses the lock, which causes us to wake up and
            // continue.
            lock (download_complete_lock)
            {
                log.Debug("Waiting for downloads to finish...");
                Monitor.Wait(download_complete_lock);
            }

            // If the user cancelled our progress, then signal that.
            if (downloadCanceled)
            {
                foreach (var download in downloads)
                {
                    download.agent.CancelAsync();
                }

                throw new CancelledActionKraken("Download cancelled by user");
            }

            // Check to see if we've had any errors. If so, then release the kraken!
            List <Exception> exceptions = downloads
                                          .Select(x => x.error)
                                          .Where(ex => ex != null)
                                          .ToList();

            // Let's check if any of these are certificate errors. If so,
            // we'll report that instead, as this is common (and user-fixable)
            // under Linux.
            foreach (Exception ex in exceptions)
            {
                if (ex is WebException && Regex.IsMatch(ex.Message, "authentication or decryption has failed"))
                {
                    throw new MissingCertificateKraken();
                }
            }

            if (exceptions.Count > 0)
            {
                throw new DownloadErrorsKraken(exceptions);
            }

            // Yay! Everything worked!
        }