private void installFromckanToolStripMenuItem_Click(object sender, EventArgs e) { OpenFileDialog open_file_dialog = new OpenFileDialog { Filter = Resources.CKANFileFilter }; if (open_file_dialog.ShowDialog() == DialogResult.OK) { var path = open_file_dialog.FileName; CkanModule module; try { module = CkanModule.FromFile(path); } catch (Kraken kraken) { currentUser.RaiseError(kraken.InnerException == null ? kraken.Message : $"{kraken.Message}: {kraken.InnerException.Message}"); return; } catch (Exception ex) { currentUser.RaiseError(ex.Message); return; } // We'll need to make some registry changes to do this. RegistryManager registry_manager = RegistryManager.Instance(CurrentInstance); // Remove this version of the module in the registry, if it exists. registry_manager.registry.RemoveAvailable(module); // Sneakily add our version in... registry_manager.registry.AddAvailable(module); var changeset = new List <ModChange> { new ModChange( new GUIMod(module, registry_manager.registry, CurrentInstance.VersionCriteria()), GUIModChangeType.Install, null) }; menuStrip1.Enabled = false; RelationshipResolverOptions install_ops = RelationshipResolver.DefaultOpts(); install_ops.with_recommends = false; installWorker.RunWorkerAsync( new KeyValuePair <List <ModChange>, RelationshipResolverOptions>( changeset, install_ops)); changeSet = null; UpdateChangesDialog(null, installWorker); ShowWaitDialog(); } }
/// <summary> /// Resolves all relationships for a module. /// May recurse to ResolveStanza, which may add additional modules to be installed. /// </summary> private void Resolve(CkanModule module, RelationshipResolverOptions options, IEnumerable <RelationshipDescriptor> old_stanza = null) { // Even though we may resolve top-level suggests for our module, // we don't install suggestions all the down unless with_all_suggests // is true. var sub_options = (RelationshipResolverOptions)options.Clone(); sub_options.with_suggests = false; old_stanza = old_stanza?.Memoize(); log.DebugFormat("Resolving dependencies for {0}", module.identifier); ResolveStanza(module.depends, new SelectionReason.Depends(module), sub_options, false, old_stanza); if (options.with_recommends) { log.DebugFormat("Resolving recommends for {0}", module.identifier); ResolveStanza(module.recommends, new SelectionReason.Recommended(module), sub_options, true, old_stanza); } if (options.with_suggests || options.with_all_suggests) { log.DebugFormat("Resolving suggests for {0}", module.identifier); ResolveStanza(module.suggests, new SelectionReason.Suggested(module), sub_options, true, old_stanza); } }
/// <summary> /// Creates a new resolver that will find a way to install all the modules specified. /// </summary> public RelationshipResolver(ICollection <CkanModule> modules, RelationshipResolverOptions options, Registry registry, KSPVersion kspversion) { this.registry = registry; this.kspversion = kspversion; // Start by figuring out what versions we're installing, and then // adding them to the list. This *must* be pre-populated with all // user-specified modules, as they may be supplying things that provide // virtual packages. var user_requested_mods = new List <CkanModule>(); log.DebugFormat("Processing relationships for {0} modules", modules.Count); foreach (CkanModule module in modules) { log.DebugFormat("Preparing to resolve relationships for {0} {1}", module.identifier, module.version); var module1 = module; //Silence a warning re. closures over foreach var. foreach (CkanModule listed_mod in modlist.Values.Where(listed_mod => listed_mod.ConflictsWith(module1))) { if (options.procede_with_inconsistencies) { conflicts.Add(new KeyValuePair <Module, Module>(listed_mod, module)); conflicts.Add(new KeyValuePair <Module, Module>(module, listed_mod)); } else { throw new InconsistentKraken(string.Format("{0} conflicts with {1}, can't install both.", module, listed_mod)); } } user_requested_mods.Add(module); Add(module, new Relationship.UserRequested()); } // Now that we've already pre-populated modlist, we can resolve // the rest of our dependencies. foreach (CkanModule module in user_requested_mods) { log.InfoFormat("Resolving relationships for {0}", module.identifier); Resolve(module, options); } var final_modules = new List <Module>(modlist.Values); final_modules.AddRange(registry.InstalledModules.Select(x => x.Module)); if (!options.without_enforce_consistency) { // Finally, let's do a sanity check that our solution is actually sane. SanityChecker.EnforceConsistency( final_modules, registry.InstalledDlls ); } }
private void ConfirmChangesButton_Click(object sender, EventArgs e) { if (m_Changeset == null) { return; } menuStrip1.Enabled = false; RelationshipResolverOptions install_ops = RelationshipResolver.DefaultOpts(); install_ops.with_recommends = false; //Using the changeset passed in can cause issues with versions. // An example is Mechjeb for FAR at 25/06/2015 with a 1.0.2 install. // TODO Work out why this is. var user_change_set = mainModList.ComputeUserChangeSet().ToList(); m_InstallWorker.RunWorkerAsync( new KeyValuePair <List <KeyValuePair <GUIMod, GUIModChangeType> >, RelationshipResolverOptions>( user_change_set, install_ops)); m_Changeset = null; UpdateChangesDialog(null, m_InstallWorker); ShowWaitDialog(); }
public void Setup() { registry = CKAN.Registry.Empty(); options = RelationshipResolver.DefaultOpts(); generator = new RandomModuleGenerator(new Random(0451)); //Sanity checker means even incorrect RelationshipResolver logic was passing options.without_enforce_consistency = true; }
public static Dictionary <GUIMod, string> ComputeConflictsFromModList(IRegistryQuerier registry, IEnumerable <ModChange> change_set, KspVersionCriteria 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; 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.ToModule()), options, registry, ksp_version ); return(resolver.ConflictList.ToDictionary(item => new GUIMod(item.Key, registry, ksp_version), item => item.Value)); }
/// <summary> /// Attempts to convert the module_names to ckan modules via CkanModule.FromIDandVersion and then calls RelationshipResolver.ctor(IEnumerable{CkanModule}, Registry, KSPVersion)"/> /// </summary> /// <param name="module_names"></param> /// <param name="options"></param> /// <param name="registry"></param> /// <param name="kspversion"></param> public RelationshipResolver(IEnumerable <string> module_names, RelationshipResolverOptions options, IRegistryQuerier registry, KspVersionCriteria kspversion) : this(module_names.Select(name => TranslateModule(name, options, registry, kspversion)).ToList(), options, registry, kspversion) { // Does nothing, just calls the other overloaded constructor }
public void Constructor_WithoutModules_AlwaysReturns() { registry = CKAN.Registry.Empty(); options = RelationshipResolver.DefaultOpts(); Assert.DoesNotThrow(() => new RelationshipResolver(new List<string>(), options, registry, null)); }
public RelationshipResolver(ICollection <string> moduleNames, RelationshipResolverOptions options, Registry registry, KSPVersion kspversion) : this(moduleNames.Select(name => CkanModule.FromIDandVersion(registry, name, kspversion)).ToList(), options, registry, kspversion) { // Does nothing, just calles the other overloaded constructor }
/// <summary> /// Attempts to convert the module_names to ckan modules via CkanModule.FromIDandVersion and then calls RelationshipResolver.ctor(IEnumerable{CkanModule}, Registry, KSPVersion)"/> /// </summary> /// <param name="module_names"></param> /// <param name="options"></param> /// <param name="registry"></param> /// <param name="kspversion"></param> public RelationshipResolver(IEnumerable <string> module_names, RelationshipResolverOptions options, IRegistryQuerier registry, KspVersion kspversion) : this(module_names.Select(name => CkanModule.FromIDandVersion(registry, name, kspversion)).ToList(), options, registry, kspversion) { // Does nothing, just calls the other overloaded constructor }
/// <summary> /// Attempts to convert the module_names to ckan modules via CkanModule.FromIDandVersion and then calls RelationshipResolver.ctor(IEnumerable{CkanModule}, Registry, KSPVersion)"/> /// </summary> /// <param name="module_names"></param> /// <param name="options"></param> /// <param name="registry"></param> /// <param name="kspversion"></param> public RelationshipResolver(IEnumerable<string> module_names, RelationshipResolverOptions options, Registry registry, KSPVersion kspversion) : this(module_names.Select(name => CkanModule.FromIDandVersion(registry, name, kspversion)).ToList(), options, registry, kspversion) { // Does nothing, just calls the other overloaded constructor }
/// <summary> /// Creates a new resolver that will find a way to install all the modules specified. /// </summary> public RelationshipResolver(List <string> modules, RelationshipResolverOptions options, Registry registry) { this.registry = registry; // Start by figuring out what versions we're installing, and then // adding them to the list. This *must* be pre-populated with all // user-specified modules, as they may be supplying things that provide // virtual packages. var user_requested_mods = new List <CkanModule>(); log.DebugFormat("Processing relationships for {0} modules", modules.Count); foreach (string module in modules) { CkanModule mod = registry.LatestAvailable(module); if (mod == null) { throw new ModuleNotFoundKraken(module); } log.DebugFormat("Preparing to resolve relationships for {0} {1}", mod.identifier, mod.version); foreach (CkanModule listed_mod in this.modlist.Values) { if (listed_mod.ConflictsWith(mod)) { throw new InconsistentKraken(string.Format("{0} conflicts with {1}, can't install both.", mod, listed_mod)); } } user_requested_mods.Add(mod); this.Add(mod); } // Now that we've already pre-populated modlist, we can resolve // the rest of our dependencies. foreach (CkanModule module in user_requested_mods) { log.InfoFormat("Resolving relationships for {0}", module.identifier); Resolve(module, options); } var final_modules = new List <Module>(modlist.Values); final_modules.AddRange(registry.InstalledModules.Select(x => x.Module)); if (!options.without_enforce_consistency) { // Finally, let's do a sanity check that our solution is actually sane. SanityChecker.EnforceConsistency( final_modules, registry.InstalledDlls ); } }
public static Dictionary <GUIMod, string> ComputeConflictsFromModList(IRegistryQuerier registry, IEnumerable <ModChange> change_set, GameVersionCriteria ksp_version) { var modules_to_install = new HashSet <CkanModule>(); var modules_to_remove = new HashSet <CkanModule>(); 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); break; case GUIModChangeType.Remove: modules_to_remove.Add(change.Mod); 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); modules_to_install.Add(repl.ReplaceWith); } break; default: throw new ArgumentOutOfRangeException(); } } var resolver = new RelationshipResolver( modules_to_install.Except(modules_to_remove), modules_to_remove, options, registry, ksp_version ); return(resolver.ConflictList.ToDictionary( item => new GUIMod(item.Key, registry, ksp_version), item => item.Value )); }
/// <summary> /// Returns the default options for relationship resolution. /// </summary> public static RelationshipResolverOptions DefaultOpts() { var opts = new RelationshipResolverOptions(); opts.with_recommends = true; opts.with_suggests = false; opts.with_all_suggests = false; return(opts); }
/// <summary> /// Initiate the GUI installer flow for one specific module /// </summary> /// <param name="registry">Reference to the registry</param> /// <param name="module">Module to install</param> public async void InstallModuleDriver(IRegistryQuerier registry, CkanModule module) { RelationshipResolverOptions install_ops = RelationshipResolver.DefaultOpts(); install_ops.with_recommends = false; try { var initialChangeSet = new HashSet <ModChange>(); // Install the selected mod initialChangeSet.Add(new ModChange( new GUIMod(module, registry, CurrentInstance.VersionCriteria()), GUIModChangeType.Install, null )); InstalledModule installed = registry.InstalledModule(module.identifier); if (installed != null) { // Already installed, remove it first initialChangeSet.Add(new ModChange( new GUIMod(installed.Module, registry, CurrentInstance.VersionCriteria()), GUIModChangeType.Remove, null )); } List <ModChange> fullChangeSet = new List <ModChange>( await mainModList.ComputeChangeSetFromModList( registry, initialChangeSet, ModuleInstaller.GetInstance(CurrentInstance, Manager.Cache, GUI.user), CurrentInstance.VersionCriteria() ) ); if (fullChangeSet != null && fullChangeSet.Count > 0) { // Resolve the provides relationships in the dependencies installWorker.RunWorkerAsync( new KeyValuePair <List <ModChange>, RelationshipResolverOptions>( fullChangeSet, install_ops ) ); } } catch { // If we failed, do the clean-up normally done by PostInstallMods. HideWaitDialog(false); menuStrip1.Enabled = true; } finally { changeSet = null; } }
public void InstallList( List <string> modules, RelationshipResolverOptions options, IDownloader downloader = null ) { var resolver = new RelationshipResolver(modules, options, registry_manager.registry, ksp.Version()); List <CkanModule> modsToInstall = resolver.ModList(); InstallList(modsToInstall, options, downloader); }
/// <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="current_instance"></param> public static IEnumerable <KeyValuePair <CkanModule, GUIModChangeType> > ComputeChangeSetFromModList( Registry registry, HashSet <KeyValuePair <CkanModule, GUIModChangeType> > changeSet, ModuleInstaller installer, KSPVersion version) { var modules_to_install = new HashSet <string>(); var modules_to_remove = new HashSet <string>(); var options = new RelationshipResolverOptions() { without_toomanyprovides_kraken = true, with_recommends = false }; foreach (var change in changeSet) { switch (change.Value) { case GUIModChangeType.None: break; case GUIModChangeType.Install: modules_to_install.Add(change.Key.identifier); break; case GUIModChangeType.Remove: modules_to_remove.Add(change.Key.identifier); break; case GUIModChangeType.Update: break; default: throw new ArgumentOutOfRangeException(); } } //May throw InconsistentKraken var resolver = new RelationshipResolver(modules_to_install.ToList(), options, registry, version); changeSet.UnionWith( resolver.ModList() .Select(mod => new KeyValuePair <CkanModule, GUIModChangeType>(mod, GUIModChangeType.Install))); foreach (var reverse_dependencies in modules_to_remove.Select(installer.FindReverseDependencies)) { //TODO This would be a good place to have a event that alters the row's graphics to show it will be removed //TODO This currently gets the latest version. This may cause the displayed version to wrong in the changset. var modules = reverse_dependencies.Select(rDep => registry.LatestAvailable(rDep, null)); changeSet.UnionWith( modules.Select(mod => new KeyValuePair <CkanModule, GUIModChangeType>(mod, GUIModChangeType.Remove))); } return(changeSet); }
/// <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, IDownloader 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); }
private bool InstallList(ModuleResolution modules, RelationshipResolverOptions options) { if (toInstall.Any()) { // actual magic happens here, we run the installer with our mod list var module_installer = ModuleInstaller.GetInstance(manager.CurrentInstance, GUI.user); module_installer.onReportModInstalled = OnModInstalled; return(WasSuccessful(() => module_installer.InstallList(modules, options))); } return(true); }
/// <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, IDownloader netAsyncDownloader) { 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.Version()); List <CkanModule> upgrades = resolver.ModList(); Upgrade(upgrades, netAsyncDownloader); }
/// <summary> /// Creates a new Relationship resolver. /// </summary> /// <param name="options"><see cref="RelationshipResolverOptions"/></param> /// <param name="registry">The registry to use</param> /// <param name="kspversion">The version of the install that the registry corresponds to</param> public RelationshipResolver(RelationshipResolverOptions options, Registry registry, KSPVersion kspversion) { this.registry = registry; this.kspversion = kspversion; this.options = options; installed_modules = new HashSet<Module>(registry.InstalledModules.Select(i_module => i_module.Module)); var installed_relationship = new SelectionReason.Installed(); foreach (var module in installed_modules) { reasons.Add(module, installed_relationship); } }
private bool InstallList(HashSet <string> toInstall, RelationshipResolverOptions options, IDownloader downloader) { if (toInstall.Any()) { // actual magic happens here, we run the installer with our mod list var module_installer = ModuleInstaller.GetInstance(manager.CurrentInstance, GUI.user); module_installer.onReportModInstalled = OnModInstalled; return(WasSuccessful( () => module_installer.InstallList(toInstall.ToList(), options, downloader))); } return(true); }
/// <summary> /// Creates a new Relationship resolver. /// </summary> /// <param name="options">Options for the RelationshipResolver</param> /// <param name="registry">CKAN registry object for current game instance</param> /// <param name="GameVersion">The current KSP version criteria to consider</param> public RelationshipResolver(RelationshipResolverOptions options, IRegistryQuerier registry, GameVersionCriteria GameVersion) { this.registry = registry; this.GameVersion = GameVersion; this.options = options; installed_modules = new HashSet <CkanModule>(registry.InstalledModules.Select(i_module => i_module.Module)); var installed_relationship = new SelectionReason.Installed(); foreach (var module in installed_modules) { reasons.Add(module, installed_relationship); } }
/// <summary> /// Initiate the GUI installer flow for one specific module /// </summary> /// <param name="registry">Reference to the registry</param> /// <param name="module">Module to install</param> public async void InstallModuleDriver(IRegistryQuerier registry, CkanModule module) { RelationshipResolverOptions install_ops = RelationshipResolver.DefaultOpts(); install_ops.with_recommends = false; try { // Resolve the provides relationships in the dependencies List <ModChange> fullChangeSet = new List <ModChange>( await mainModList.ComputeChangeSetFromModList( registry, new HashSet <ModChange>() { new ModChange( new GUIMod( module, registry, CurrentInstance.VersionCriteria() ), GUIModChangeType.Install, null ) }, ModuleInstaller.GetInstance(CurrentInstance, GUI.user), CurrentInstance.VersionCriteria() ) ); if (fullChangeSet != null && fullChangeSet.Count > 0) { installWorker.RunWorkerAsync( new KeyValuePair <List <ModChange>, RelationshipResolverOptions>( fullChangeSet, install_ops ) ); } } catch { // If we failed, do the clean-up normally done by PostInstallMods. HideWaitDialog(false); menuStrip1.Enabled = true; } finally { changeSet = null; } }
public static Dictionary <GUIMod, string> ComputeConflictsFromModList(IRegistryQuerier registry, IEnumerable <ModChange> change_set, FactorioVersion ksp_version) { var modules_to_install = new HashSet <string>(); var modules_to_remove = new HashSet <string>(); var options = new RelationshipResolverOptions { without_toomanyprovides_kraken = true, procede_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; default: throw new ArgumentOutOfRangeException(); } } var installed = registry.Installed() .Where(pair => pair.Value.CompareTo(new ProvidedVersion("", "0.0.0")) != 0) .Select(pair => pair.Key); //We wish to only check mods that would exist after the changes are made. var mods_to_check = installed.Union(modules_to_install).Except(modules_to_remove).Select(p => new CfanModuleIdAndVersion(p)); var resolver = new RelationshipResolver(mods_to_check.ToList(), options, registry, ksp_version); return(resolver.ConflictList.ToDictionary(item => new GUIMod(item.Key, registry, ksp_version), item => item.Value)); }
private void ConfirmChangesButton_Click(object sender, EventArgs e) { menuStrip1.Enabled = false; RelationshipResolverOptions install_ops = RelationshipResolver.DefaultOpts(); install_ops.with_recommends = false; m_InstallWorker.RunWorkerAsync( new KeyValuePair <List <KeyValuePair <CkanModule, GUIModChangeType> >, RelationshipResolverOptions>( m_Changeset, install_ops)); m_Changeset = null; UpdateChangesDialog(null, m_InstallWorker); ShowWaitDialog(); }
/// <summary> /// Tries to get every mod in the Dictionary, which can be installed /// It also transforms the Recommender list to a string /// </summary> /// <param name="mods"></param> /// <returns></returns> private Dictionary <CkanModule, string> GetShowableMods(Dictionary <string, List <string> > mods) { Dictionary <CkanModule, string> modules = new Dictionary <CkanModule, string>(); var opts = new RelationshipResolverOptions { with_all_suggests = false, with_recommends = false, with_suggests = false, without_enforce_consistency = false, without_toomanyprovides_kraken = true }; foreach (var pair in mods) { CkanModule module; try { var resolver = new RelationshipResolver(new List <string> { pair.Key }, opts, RegistryManager.Instance(manager.CurrentInstance).registry, CurrentInstance.VersionCriteria()); if (!resolver.ModList().Any()) { continue; } module = RegistryManager.Instance(manager.CurrentInstance) .registry.LatestAvailable(pair.Key, CurrentInstance.VersionCriteria()); } catch { continue; } if (module == null) { continue; } modules.Add(module, String.Join(",", pair.Value.ToArray())); } return(modules); }
private void InstallList(HashSet <string> toInstall, RelationshipResolverOptions options) { if (toInstall.Any()) { var downloader = new NetAsyncDownloader(); // actual magic happens here, we run the installer with our mod list ModuleInstaller.Instance.onReportModInstalled = OnModInstalled; cancelCallback = downloader.CancelDownload; try { ModuleInstaller.Instance.InstallList(toInstall.ToList(), options, downloader); } catch (CancelledActionKraken) { // User cancelled, no action needed. } catch (InconsistentKraken inconsistency) { string message = ""; bool first = true; foreach (var msg in inconsistency.inconsistencies) { if (!first) { message += ", "; } else { first = false; } message += msg; } User.Error("Inconsistency detected - {0}", message); } // TODO: Handle our other krakens here, we want the user to know // when things have gone wrong! } }
/// <summary> /// Translate mods from identifiers in its default or identifier=version format into CkanModules, /// optionally falling back to incompatible modules if no compatibles could be found. /// </summary> /// <param name="name">The identifier or identifier=version of the module</param> /// <param name="options">If options.allow_incompatible is set, fall back to searching incompatible modules if no compatible has been found</param> /// <param name="registry">CKAN registry object for current game instance</param> /// <param name="GameVersion">The current KSP version criteria to consider</param> /// <returns>A CkanModule</returns> private static CkanModule TranslateModule(string name, RelationshipResolverOptions options, IRegistryQuerier registry, GameVersionCriteria GameVersion) { if (options.allow_incompatible) { try { return(CkanModule.FromIDandVersion(registry, name, GameVersion)); } catch (ModuleNotFoundKraken) { // No versions found matching our game version, so // look for incompatible versions. return(CkanModule.FromIDandVersion(registry, name, null)); } } else { return(CkanModule.FromIDandVersion(registry, name, GameVersion)); } }
public void InstallList(ModuleResolution modules, RelationshipResolverOptions options) { // We're about to install all our mods; so begin our transaction. using (TransactionScope transaction = CkanTransaction.CreateTransactionScope()) { var enumeratedMods = modules.Select((m, i) => new { Idx = i, Module = m }); foreach (var item in enumeratedMods) { var percentComplete = (item.Idx * 100) / modules.Count; User.RaiseProgress(string.Format("Installing mod \"{0}\"", item.Module), percentComplete); Install(item.Module); } User.RaiseProgress("Updating registry", 70); registry_manager.Save(!options.without_enforce_consistency); User.RaiseProgress("Commiting filesystem changes", 80); transaction.Complete(); } }
/// <summary> /// Returns mods that we require to install the selected module. /// This returns null if we can't compute these without user input, /// or if the mods conflict. /// </summary> private List <CkanModule> GetInstallDependencies(CkanModule module, RelationshipResolverOptions options) { var tmp = new List <string>(); tmp.Add(module.identifier); RelationshipResolver resolver = null; try { resolver = new RelationshipResolver(tmp, options, RegistryManager.Instance(KSPManager.CurrentInstance).registry); } catch (Kraken kraken) { // TODO: Both of these krakens contain extra information; either a list of // mods the user can choose from, or a list of inconsistencies that are blocking // this selection. We *should* display those to the user. See GH #345. if (kraken is TooManyModsProvideKraken || kraken is InconsistentKraken) { // Expected krakens. return(null); } else if (kraken is ModuleNotFoundKraken) { var not_found = (ModuleNotFoundKraken)kraken; log.ErrorFormat( "Can't find {0}, but {1} depends on it", not_found.module, module ); return(null); } log.ErrorFormat("Unexpected Kraken in GetInstallDeps: {0}", kraken.GetType()); return(null); } return(resolver.ModList()); }
private void ConfirmChangesButton_Click(object sender, EventArgs e) { if (changeSet == null) { return; } menuStrip1.Enabled = false; RetryCurrentActionButton.Visible = false; RelationshipResolverOptions install_ops = RelationshipResolver.DefaultOpts(); install_ops.with_recommends = false; //Using the changeset passed in can cause issues with versions. // An example is Mechjeb for FAR at 25/06/2015 with a 1.0.2 install. // TODO Work out why this is. installWorker.RunWorkerAsync( new KeyValuePair <List <ModChange>, RelationshipResolverOptions>( mainModList.ComputeUserChangeSet().ToList(), install_ops ) ); }
/// <summary> /// Tries to get every mod in the Dictionary, which can be installed /// It also transforms the Recommender list to a string /// </summary> /// <param name="mods"></param> /// <returns></returns> private Dictionary <CkanModule, string> GetShowableMods(Dictionary <CkanModule, List <string> > mods) { Dictionary <CkanModule, string> modules = new Dictionary <CkanModule, string>(); var opts = new RelationshipResolverOptions { with_all_suggests = false, with_recommends = false, with_suggests = false, without_enforce_consistency = false, without_toomanyprovides_kraken = true }; foreach (var pair in mods) { try { RelationshipResolver resolver = new RelationshipResolver( new List <CkanModule> { pair.Key }, null, opts, RegistryManager.Instance(manager.CurrentInstance).registry, CurrentInstance.VersionCriteria() ); if (resolver.ModList().Any()) { // Resolver was able to find a way to install, so show it to the user modules.Add(pair.Key, String.Join(",", pair.Value.ToArray())); } } catch { } } return(modules); }
/// <summary> /// Resolves all relationships for a module. /// May recurse to ResolveStanza, which may add additional modules to be installed. /// </summary> private void Resolve(CkanModule module, RelationshipResolverOptions options) { // Even though we may resolve top-level suggests for our module, // we don't install suggestions all the down unless with_all_suggests // is true. var sub_options = (RelationshipResolverOptions)options.Clone(); sub_options.with_suggests = false; log.DebugFormat("Resolving dependencies for {0}", module.identifier); ResolveStanza(module.depends, sub_options); if (options.with_recommends) { log.DebugFormat("Resolving recommends for {0}", module.identifier); ResolveStanza(module.recommends, sub_options, true); } if (options.with_suggests || options.with_all_suggests) { log.DebugFormat("Resolving suggests for {0}", module.identifier); ResolveStanza(module.suggests, sub_options, true); } }
private void UpdateRecommendedDialog(Dictionary<string, List<string>> mods, bool suggested = false) { if (!suggested) { RecommendedDialogLabel.Text = "The following modules have been recommended by one or more of the chosen modules:"; RecommendedModsListView.Columns[1].Text = "Recommended by:"; RecommendedModsToggleCheckbox.Text = "(De-)select all recommended mods."; RecommendedModsToggleCheckbox.Checked=true; m_TabController.RenameTab("ChooseRecommendedModsTabPage", "Choose recommended mods"); } else { RecommendedDialogLabel.Text = "The following modules have been suggested by one or more of the chosen modules:"; RecommendedModsListView.Columns[1].Text = "Suggested by:"; RecommendedModsToggleCheckbox.Text = "(De-)select all suggested mods."; RecommendedModsToggleCheckbox.Checked=false; m_TabController.RenameTab("ChooseRecommendedModsTabPage", "Choose suggested mods"); } RecommendedModsListView.Items.Clear(); foreach (var pair in mods) { Module module; try { var opts = new RelationshipResolverOptions { with_all_suggests = false, with_recommends = false, with_suggests = false, without_enforce_consistency = false, without_toomanyprovides_kraken = true }; var resolver = new RelationshipResolver(new List<string> {pair.Key}, opts, RegistryManager.Instance(manager.CurrentInstance).registry, CurrentInstance.Version()); if (!resolver.ModList().Any()) { continue; } module = RegistryManager.Instance(manager.CurrentInstance) .registry.LatestAvailable(pair.Key, CurrentInstance.Version()); } catch { continue; } if (module == null) { continue; } ListViewItem item = new ListViewItem {Tag = module, Checked = !suggested, Text = pair.Key}; ListViewItem.ListViewSubItem recommendedBy = new ListViewItem.ListViewSubItem(); string recommendedByString = ""; bool first = true; foreach (string mod in pair.Value) { if (!first) { recommendedByString += ", "; } else { first = false; } recommendedByString += mod; } recommendedBy.Text = recommendedByString; item.SubItems.Add(recommendedBy); ListViewItem.ListViewSubItem description = new ListViewItem.ListViewSubItem {Text = module.@abstract}; item.SubItems.Add(description); RecommendedModsListView.Items.Add(item); } }
private bool InstallList(HashSet<string> toInstall, RelationshipResolverOptions options, IDownloader downloader) { if (toInstall.Any()) { // actual magic happens here, we run the installer with our mod list var module_installer = ModuleInstaller.GetInstance(manager.CurrentInstance, GUI.user); module_installer.onReportModInstalled = OnModInstalled; return WasSuccessful( () => module_installer.InstallList(toInstall.ToList(), options, downloader)); } return true; }
/// <summary> /// Creates a new resolver that will find a way to install all the modules specified. /// </summary> public RelationshipResolver(IEnumerable<CkanModule> modules, RelationshipResolverOptions options, Registry registry, KSPVersion kspversion) : this(options,registry,kspversion) { AddModulesToInstall(modules); }
void MigrateMods(object sender, DoWorkEventArgs e) { SetStatus("Starting mod migration process"); SetProgressMarquee(); var registry = Main.Instance.CurrentInstance.Registry; var identifiers = (List<string>)e.Argument; foreach (var identifier in identifiers) { CkanModule module = null; try { module = registry.LatestAvailable(identifier, Main.Instance.CurrentInstance.Version()); } catch (Exception ex) { e.Result = ex; return; } if (module == null) { e.Result = null; return; } var installer = ModuleInstaller.GetInstance(Main.Instance.CurrentInstance, Main.Instance.m_User); SetStatus(String.Format("Downloading mod - {0}", identifier)); string zip = installer.CachedOrDownload(module); var files = ModuleInstaller.FindInstallableFiles(module, new ZipFile(zip), Main.Instance.CurrentInstance); SetStatus(String.Format("Removing existing files for {0}", identifier)); foreach (var file in files) { if (File.Exists(file.destination)) { try { File.Delete(file.destination); log.InfoFormat("Deleted \"{0}\"", file); } catch (Exception ex) { log.WarnFormat("Failed to delete \"{0}\" - {1}", file, ex.Message); break; } } } SetStatus("Scanning GameData"); Main.Instance.CurrentInstance.ScanGameData(); SetStatus("Unregistering auto-detected module"); // registry.DeregisterModule(Main.Instance.CurrentInstance, identifier); var opts = new RelationshipResolverOptions() { with_all_suggests = false, with_recommends = true, with_suggests = false, without_toomanyprovides_kraken = true }; SetStatus(String.Format("Installing {0} using CKAN", identifier)); installer.User = new NullUser(); bool success = false; while (!success) { try { installer.InstallList(new List<string>() { identifier }, opts); success = true; } catch (FileExistsKraken ex) { File.Delete(Path.Combine(Main.Instance.CurrentInstance.GameDir(), ex.filename)); } catch (Exception ex) { e.Result = ex; return; } } } }
/// <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<KeyValuePair<GUIMod, GUIModChangeType>>> ComputeChangeSetFromModList( Registry registry, HashSet<KeyValuePair<GUIMod, GUIModChangeType>> changeSet, ModuleInstaller installer, KSPVersion version) { var modules_to_install = new HashSet<CkanModule>(); var modules_to_remove = new HashSet<Module>(); var options = new RelationshipResolverOptions { without_toomanyprovides_kraken = false, with_recommends = false }; foreach (var change in changeSet) { switch (change.Value) { case GUIModChangeType.None: break; case GUIModChangeType.Install: modules_to_install.Add(change.Key.ToCkanModule()); break; case GUIModChangeType.Remove: modules_to_remove.Add(change.Key); break; case GUIModChangeType.Update: break; default: throw new ArgumentOutOfRangeException(); } } var installed_modules = registry.InstalledModules.Select(imod => imod.Module).ToDictionary(mod => mod.identifier, mod => mod); bool handled_all_to_many_provides = false; while (!handled_all_to_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.ToList(), options, registry, version); handled_all_to_many_provides = true; continue; } catch (TooManyModsProvideKraken k) { kraken = k; } catch (ModuleNotFoundKraken k) { //We shouldn't need this. However the relationship provider will throw TMPs with incompatible mods. user.RaiseError("Module {0} has not been found. This may be because it is not compatible " + "with the currently installed version of KSP", k.module); return null; } //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; } } foreach (var dependency in modules_to_remove. Select(mod => installer.FindReverseDependencies(mod.identifier)). SelectMany(reverse_dependencies => reverse_dependencies)) { //TODO This would be a good place to have a event that alters the row's graphics to show it will be removed Module module_by_version = registry.GetModuleByVersion(installed_modules[dependency].identifier, installed_modules[dependency].version) ?? registry.InstalledModule(dependency).Module; changeSet.Add( new KeyValuePair<GUIMod, GUIModChangeType>( new GUIMod(module_by_version, registry, version), GUIModChangeType.Remove)); } //May throw InconsistentKraken var resolver = new RelationshipResolver(options, registry, version); resolver.RemoveModsFromInstalledList( changeSet.Where(change => change.Value.Equals(GUIModChangeType.Remove)).Select(m => m.Key.ToModule())); resolver.AddModulesToInstall(modules_to_install.ToList()); changeSet.UnionWith( resolver.ModList() .Select( mod => new KeyValuePair<GUIMod, GUIModChangeType>(new GUIMod(mod, registry, version), GUIModChangeType.Install))); return changeSet; }
/// <summary> /// Resolve a relationship stanza (a list of relationships). /// This will add modules to be installed, if required. /// May recurse back to Resolve for those new modules. /// /// If `soft_resolve` is true, we warn rather than throw exceptions on mods we cannot find. /// If `soft_resolve` is false (default), we throw a ModuleNotFoundKraken if we can't find a dependency. /// /// Throws a TooManyModsProvideKraken if we have too many choices and /// options.without_toomanyprovides_kraken is not set. /// /// See RelationshipResolverOptions for further adjustments that can be made. /// /// </summary> private void ResolveStanza(IEnumerable<RelationshipDescriptor> stanza, SelectionReason reason, RelationshipResolverOptions options, bool soft_resolve = false) { if (stanza == null) { return; } foreach (var descriptor in stanza) { string dep_name = descriptor.name; log.DebugFormat("Considering {0}", dep_name); // If we already have this dependency covered, skip. // If it's already installed, skip. if (modlist.ContainsKey(dep_name)) { if (descriptor.version_within_bounds(modlist[dep_name].version)) continue; //TODO Ideally we could check here if it can be replaced by the version we want. throw new InconsistentKraken( string.Format( "{0} requires a version {1}. However a incompatible version, {2}, is in the resolver", dep_name, descriptor.RequiredVersion, modlist[dep_name].version)); } if (registry.IsInstalled(dep_name)) { if(descriptor.version_within_bounds(registry.InstalledVersion(dep_name))) continue; //TODO Ideally we could check here if it can be replaced by the version we want. throw new InconsistentKraken( string.Format( "{0} requires a version {1}. However a incompatible version, {2}, is already installed", dep_name, descriptor.RequiredVersion, registry.InstalledVersion(dep_name))); } List<CkanModule> candidates = registry.LatestAvailableWithProvides(dep_name, kspversion, descriptor) .Where(mod=>MightBeInstallable(mod)).ToList(); if (candidates.Count == 0) { if (!soft_resolve) { log.ErrorFormat("Dependency on {0} found, but nothing provides it.", dep_name); throw new ModuleNotFoundKraken(dep_name); } log.InfoFormat("{0} is recommended/suggested, but nothing provides it.", dep_name); continue; } if (candidates.Count > 1) { // Oh no, too many to pick from! // TODO: It would be great if instead we picked the one with the // most recommendations. if (options.without_toomanyprovides_kraken) { continue; } throw new TooManyModsProvideKraken(dep_name, candidates); } CkanModule candidate = candidates[0]; // Finally, check our candidate against everything which might object // to it being installed; that's all the mods which are fixed in our // list thus far, as well as everything on the system. var fixed_mods = new HashSet<Module>(modlist.Values); fixed_mods.UnionWith(installed_modules); var conflicting_mod = fixed_mods.FirstOrDefault(mod => mod.ConflictsWith(candidate)); if (conflicting_mod == null) { // Okay, looks like we want this one. Adding. Add(candidate, reason); Resolve(candidate, options); } else if (soft_resolve) { log.InfoFormat("{0} would cause conflicts, excluding it from consideration", candidate); } else { if (options.procede_with_inconsistencies) { Add(candidate, reason); conflicts.Add(new KeyValuePair<Module, Module>(conflicting_mod, candidate)); conflicts.Add(new KeyValuePair<Module, Module>(candidate, conflicting_mod)); } else { throw new InconsistentKraken(string.Format("{0} conflicts with {1}, can't install both.", conflicting_mod, candidate)); } } } }
/// <summary> /// Resolves all relationships for a module. /// May recurse to ResolveStanza, which may add additional modules to be installed. /// </summary> private void Resolve(CkanModule module, RelationshipResolverOptions options) { // Even though we may resolve top-level suggests for our module, // we don't install suggestions all the down unless with_all_suggests // is true. var sub_options = (RelationshipResolverOptions) options.Clone(); sub_options.with_suggests = false; log.DebugFormat("Resolving dependencies for {0}", module.identifier); ResolveStanza(module.depends, new SelectionReason.Depends(module), sub_options); if (options.with_recommends) { log.DebugFormat("Resolving recommends for {0}", module.identifier); ResolveStanza(module.recommends, new SelectionReason.Recommended(module), sub_options, true); } if (options.with_suggests || options.with_all_suggests) { log.DebugFormat("Resolving suggests for {0}", module.identifier); ResolveStanza(module.suggests, new SelectionReason.Suggested(module), sub_options, true); } }
/// <summary> /// Returns the default options for relationship resolution. /// </summary> // TODO: This should just be able to return a new RelationshipResolverOptions // and the defaults in the class definition should do the right thing. public static RelationshipResolverOptions DefaultOpts() { var opts = new RelationshipResolverOptions { with_recommends = true, with_suggests = false, with_all_suggests = false }; return opts; }
public static Dictionary<GUIMod, string> ComputeConflictsFromModList(Registry registry, IEnumerable<KeyValuePair<GUIMod, GUIModChangeType>> change_set, KSPVersion ksp_version) { var modules_to_install = new HashSet<string>(); var modules_to_remove = new HashSet<string>(); var options = new RelationshipResolverOptions { without_toomanyprovides_kraken = true, procede_with_inconsistencies = true, without_enforce_consistency = true, with_recommends = false }; foreach (var change in change_set) { switch (change.Value) { case GUIModChangeType.None: break; case GUIModChangeType.Install: modules_to_install.Add(change.Key.Identifier); break; case GUIModChangeType.Remove: modules_to_remove.Add(change.Key.Identifier); break; case GUIModChangeType.Update: break; default: throw new ArgumentOutOfRangeException(); } } var installed = registry.Installed() .Where(pair => pair.Value.CompareTo(new ProvidesVersion("")) != 0) .Select(pair => pair.Key); //We wish to only check mods that would exist after the changes are made. var mods_to_check = installed.Union(modules_to_install).Except(modules_to_remove); var resolver = new RelationshipResolver(mods_to_check.ToList(), options, registry, ksp_version); return resolver.ConflictList.ToDictionary(item => new GUIMod(item.Key, registry, ksp_version), item => item.Value); }
/// <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); }
/// <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.Version()); 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.RaiseMessage("About to install...\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("\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 NetAsyncDownloader(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!\n", 100); }
/// <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, IDownloader netAsyncDownloader) { 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.Version()); List<CkanModule> upgrades = resolver.ModList(); Upgrade(upgrades, netAsyncDownloader); }
public void InstallList( List<string> modules, RelationshipResolverOptions options, IDownloader downloader = null ) { var resolver = new RelationshipResolver(modules, options, registry_manager.registry, ksp.Version()); List<CkanModule> modsToInstall = resolver.ModList(); InstallList(modsToInstall, options, downloader); }
/// <summary> /// Tries to get every mod in the Dictionary, which can be installed /// It also transforms the Recommender list to a string /// </summary> /// <param name="mods"></param> /// <returns></returns> private Dictionary<CkanModule, string> GetShowableMods(Dictionary<string, List<string>> mods) { Dictionary<CkanModule, string> modules = new Dictionary<CkanModule, string>(); var opts = new RelationshipResolverOptions { with_all_suggests = false, with_recommends = false, with_suggests = false, without_enforce_consistency = false, without_toomanyprovides_kraken = true }; foreach (var pair in mods) { CkanModule module; try { var resolver = new RelationshipResolver(new List<string> { pair.Key }, opts, RegistryManager.Instance(manager.CurrentInstance).registry, CurrentInstance.Version()); if (!resolver.ModList().Any()) { continue; } module = RegistryManager.Instance(manager.CurrentInstance) .registry.LatestAvailable(pair.Key, CurrentInstance.Version()); } catch { continue; } if (module == null) { continue; } modules.Add(module, String.Join(",", pair.Value.ToArray())); } return modules; }