public void StartDownload(GUIMod module) { if (module == null || !module.IsCKAN) { return; } Main.Instance.ShowWaitDialog(false); if (cacheWorker.IsBusy) { Task.Factory.StartNew(() => { // Just pass to the existing worker downloader.DownloadModules(new List <CkanModule> { module.ToCkanModule() }); module.UpdateIsCached(); }); } else { // Start up a new worker downloader = new NetAsyncModulesDownloader(Main.Instance.currentUser, Main.Instance.Manager.Cache); cacheWorker.RunWorkerAsync(module); } }
/// <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); } }
/// <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 ); }
private void CacheMod(object sender, DoWorkEventArgs e) { Main.Instance.ResetProgress(); Main.Instance.ClearLog(); NetAsyncModulesDownloader downloader = new NetAsyncModulesDownloader(Main.Instance.currentUser); downloader.DownloadModules(Main.Instance.Manager.Cache, new List <CkanModule> { (CkanModule)e.Argument }); e.Result = e.Argument; }
/// <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); }
public void StartDownload(GUIMod module) { if (module == null || !module.IsCKAN) { return; } if (cacheWorker == null) { cacheWorker = new BackgroundWorker() { WorkerReportsProgress = true, WorkerSupportsCancellation = true, }; cacheWorker.DoWork += CacheMod; cacheWorker.RunWorkerCompleted += PostModCaching; } Main.Instance.ShowWaitDialog(false); if (cacheWorker.IsBusy) { Task.Factory.StartNew(() => { // Just pass to the existing worker downloader.DownloadModules(new List <CkanModule> { module.ToCkanModule() }); module.UpdateIsCached(); }); } else { // Start up a new worker downloader = new NetAsyncModulesDownloader(Main.Instance.currentUser, Main.Instance.Manager.Cache); cacheWorker.RunWorkerAsync(module); } }
private void CacheMod(object sender, DoWorkEventArgs e) { CfanModule module = (CfanModule)e.Argument; if (module.isMetapackage) { AddLogMessage($"Cannot download metapackage. {module.title}"); return; } if (module.download == null) { AddLogMessage($"No available download link for {module.title}."); return; } ResetProgress(); ClearLog(); NetAsyncModulesDownloader dowloader = new NetAsyncModulesDownloader(m_User, CurrentInstance.tryGetFactorioAuthData()); dowloader.DownloadModules(CurrentInstance.Cache, new List <CfanModule> { module }); e.Result = e.Argument; }
// 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; } } }
// 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; } } } }
// 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; } } }
/// <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> public void InstallList(ICollection <CkanModule> modules, RelationshipResolverOptions options, IDownloader downloader = null) { // TODO: Break this up into smaller pieces! It's huge! 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)) { User.RaiseMessage(" * {0} {1} ({2})", module.name, module.version, module.download.Host); 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); }
private void InstallMods(object sender, DoWorkEventArgs e) // this probably needs to be refactored { ShowWaitDialog(); installCanceled = false; ClearLog(); var opts = (KeyValuePair <ModChanges, RelationshipResolverOptions>)e.Argument; IRegistryQuerier registry = RegistryManager.Instance(manager.CurrentInstance).registry; ModuleInstaller installer = ModuleInstaller.GetInstance(CurrentInstance, GUI.user); // Avoid accumulating multiple event handlers installer.onReportModInstalled -= OnModInstalled; installer.onReportModInstalled += OnModInstalled; // setup progress callback 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; } } // Now work on satisifying dependencies. var recommended = new Dictionary <CkanModule, List <string> >(); var suggested = new Dictionary <CkanModule, 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); IDownloader downloader = new NetAsyncModulesDownloader(GUI.user); cancelCallback = () => { downloader.CancelDownload(); installCanceled = true; }; bool resolvedAllProvidedMods = false; while (!resolvedAllProvidedMods) { try { e.Result = new KeyValuePair <bool, ModChanges>(false, opts.Key); if (!installCanceled && toUninstall.Count > 0) { installer.UninstallList(toUninstall); } if (!installCanceled && toUpgrade.Count > 0) { installer.Upgrade(toUpgrade, downloader); } if (!installCanceled && toInstall.Count > 0) { installer.InstallList(toInstall, opts.Value, downloader); } e.Result = new KeyValuePair <bool, ModChanges>(!installCanceled, opts.Key); if (installCanceled) { return; } resolvedAllProvidedMods = true; } 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?")) { // 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; } } }
private void InstallMods(object sender, DoWorkEventArgs e) // this probably needs to be refactored { 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; }; GUI.user.RaiseMessage("About to install...\r\n"); // FIXME: here we should heat up the cache with any mods we're going to install, because // when this transaction types out it f***s up everything else. var resolvedMods = installer.ResolveModules(toInstall, opts.Value); foreach (var module in resolvedMods.CachedModules) { GUI.user.RaiseMessage(" * {0} {1}(cached)", module.name, module.version); } foreach (var module in resolvedMods.UncachedModules) { GUI.user.RaiseMessage(" * {0} {1}", module.name, module.version); } if (!GUI.user.RaiseYesNoDialog("\r\nContinue?")) { throw new CancelledActionKraken("User declined install list"); } GUI.user.RaiseMessage(String.Empty); // Just to look tidy. installer.EnsureCache(resolvedMods.UncachedModules); //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(resolvedMods, opts.Value); 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); }