public void Constructor_ContainsSugestedOfSugested_When_With_all_suggests() { options.with_all_suggests = true; var list = new List<string>(); var sugested2 = generator.GeneratorRandomModule(); var sugested = generator.GeneratorRandomModule(sugests: new List<RelationshipDescriptor> { new RelationshipDescriptor {name = sugested2.identifier} }); var sugester = generator.GeneratorRandomModule(sugests: new List<RelationshipDescriptor> { new RelationshipDescriptor {name = sugested.identifier} }); list.Add(sugester.identifier); AddToRegistry(sugester, sugested, sugested2); var relationship_resolver = new RelationshipResolver(list, options, registry, null); CollectionAssert.Contains(relationship_resolver.ModList(), sugested2); options.with_all_suggests = false; relationship_resolver = new RelationshipResolver(list, options, registry, null); CollectionAssert.DoesNotContain(relationship_resolver.ModList(), sugested2); }
public void Constructor_WithConflictingModules() { var list = new List<string>(); var mod_a = generator.GeneratorRandomModule(); var mod_b = generator.GeneratorRandomModule(conflicts: new List<RelationshipDescriptor> { new RelationshipDescriptor {name=mod_a.identifier} }); list.Add(mod_a.identifier); list.Add(mod_b.identifier); AddToRegistry(mod_a, mod_b); Assert.Throws<InconsistentKraken>(() => new RelationshipResolver( list, options, registry, null)); options.procede_with_inconsistencies = true; var resolver = new RelationshipResolver(list, options, registry, null); Assert.That(resolver.ConflictList.Any(s => Equals(s.Key, mod_a))); Assert.That(resolver.ConflictList.Any(s => Equals(s.Key, mod_b))); Assert.That(resolver.ConflictList, Has.Count.EqualTo(2)); }
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 = null; try { module = CkanModule.FromFile(path); } catch (Kraken kraken) { m_User.RaiseError(kraken.Message + ": " + kraken.InnerException.Message); return; } catch (Exception ex) { m_User.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 <KeyValuePair <CkanModule, GUIModChangeType> >(); changeset.Add(new KeyValuePair <CkanModule, GUIModChangeType>(module, GUIModChangeType.Install)); menuStrip1.Enabled = false; RelationshipResolverOptions install_ops = RelationshipResolver.DefaultOpts(); install_ops.with_recommends = false; m_InstallWorker.RunWorkerAsync( new KeyValuePair <List <KeyValuePair <CkanModule, GUIModChangeType> >, RelationshipResolverOptions>( changeset, install_ops)); m_Changeset = null; UpdateChangesDialog(null, m_InstallWorker); ShowWaitDialog(); } }
/// <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); }
private void reinstallToolStripMenuItem_Click(object sender, EventArgs e) { GUIMod module = ModInfoTabControl.SelectedModule; if (module == null || !module.IsCKAN) { return; } YesNoDialog reinstallDialog = new YesNoDialog(); string confirmationText = $"Do you want to reinstall {module.Name}?"; if (reinstallDialog.ShowYesNoDialog(confirmationText) == DialogResult.No) { return; } IRegistryQuerier registry = RegistryManager.Instance(CurrentInstance).registry; // Build the list of changes, first the mod to remove: List <ModChange> toReinstall = new List <ModChange>() { new ModChange(module, GUIModChangeType.Remove, null) }; // Then everything we need to re-install: var revdep = registry.FindReverseDependencies(new List <string>() { module.Identifier }); var goners = revdep.Union( registry.FindRemovableAutoInstalled( registry.InstalledModules.Where(im => !revdep.Contains(im.identifier)) ).Select(im => im.Module.identifier) ); foreach (string id in goners) { toReinstall.Add(new ModChange( mainModList.full_list_of_mod_rows[id]?.Tag as GUIMod, GUIModChangeType.Install, null )); } // Hand off to centralized [un]installer code installWorker.RunWorkerAsync( new KeyValuePair <List <ModChange>, RelationshipResolverOptions>( toReinstall, RelationshipResolver.DependsOnlyOpts() ) ); }
private void Changeset_OnConfirmChanges() { menuStrip1.Enabled = 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(RegistryManager.Instance(Main.Instance.CurrentInstance).registry).ToList(), RelationshipResolver.DependsOnlyOpts() ) ); }
/// <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, Manager.Cache, 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(); }
private void minimizeNotifyIcon_BalloonTipClicked(object sender, EventArgs e) { // Unminimize OpenWindow(); // Check all the upgrade checkboxes ManageMods.MarkAllUpdates(); // Install installWorker.RunWorkerAsync( new KeyValuePair <List <ModChange>, RelationshipResolverOptions>( ManageMods.mainModList.ComputeUserChangeSet(RegistryManager.Instance(Main.Instance.CurrentInstance).registry).ToList(), RelationshipResolver.DependsOnlyOpts() ) ); }
/// <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); }
/// <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">Map from recommendations to lists of recommenders</param> /// <param name="registry">Registry of current game instance</param> /// <param name="versionCriteria">Versions compatible with current instance</param> /// <param name="toInstall">Modules planned to be installed</param> /// <returns>Map from installable recommendations to string describing recommenders</returns> private Dictionary <CkanModule, string> GetShowableMods( Dictionary <CkanModule, List <string> > mods, IRegistryQuerier registry, KspVersionCriteria versionCriteria, HashSet <CkanModule> toInstall ) { return(mods.Where(kvp => CanInstall( registry, versionCriteria, RelationshipResolver.DependsOnlyOpts(), toInstall.ToList().Concat(new List <CkanModule>() { kvp.Key }).ToList() )).ToDictionary( kvp => kvp.Key, kvp => string.Join(", ", kvp.Value.ToArray()) )); }
/// <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) { try { var userChangeSet = new List <ModChange>(); InstalledModule installed = registry.InstalledModule(module.identifier); if (installed != null) { // Already installed, remove it first userChangeSet.Add(new ModChange( new GUIMod(installed.Module, registry, CurrentInstance.VersionCriteria()), GUIModChangeType.Remove, null )); } // Install the selected mod userChangeSet.Add(new ModChange( new GUIMod(module, registry, CurrentInstance.VersionCriteria()), GUIModChangeType.Install, null )); if (userChangeSet.Count > 0) { // Resolve the provides relationships in the dependencies installWorker.RunWorkerAsync( new KeyValuePair <List <ModChange>, RelationshipResolverOptions>( userChangeSet, RelationshipResolver.DependsOnlyOpts() ) ); } } catch { // If we failed, do the clean-up normally done by PostInstallMods. HideWaitDialog(false); menuStrip1.Enabled = true; } finally { changeSet = null; } }
private void ConfirmChangesButton_Click(object sender, EventArgs e) { if (changeSet == null) { return; } menuStrip1.Enabled = false; RetryCurrentActionButton.Visible = 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(), RelationshipResolver.DependsOnlyOpts() ) ); }
/// <summary> /// Is the mod installed and does it have a newer version compatible with version /// We can't update AD mods /// </summary> public static bool HasUpdate(this IRegistryQuerier querier, string identifier, GameVersionCriteria version) { CkanModule newest_version; try { newest_version = querier.LatestAvailable(identifier, version); } catch (Exception) { return(false); } if (newest_version == null || !querier.IsInstalled(identifier, false) || !newest_version.version.IsGreaterThan(querier.InstalledVersion(identifier))) { return(false); } // All quick checks pass. Now check the relationships. try { var instMod = querier.InstalledModule(identifier); RelationshipResolver resolver = new RelationshipResolver( new CkanModule[] { newest_version }, // Remove the old module when installing the new one instMod == null ? null : new CkanModule[] { instMod.Module }, new RelationshipResolverOptions() { with_recommends = false, without_toomanyprovides_kraken = true, }, querier, version ); } catch (Exception) { return(false); } return(true); }
private void AuditRecommendations(IRegistryQuerier registry, KspVersionCriteria versionCriteria) { var recommended = new Dictionary <CkanModule, List <string> >(); var suggested = new Dictionary <CkanModule, List <string> >(); var toInstall = new HashSet <CkanModule>(); // Find recommendations and suggestions foreach (var mod in registry.InstalledModules.Select(im => im.Module)) { AddMod(mod.recommends, recommended, mod.identifier, registry, toInstall); AddMod(mod.suggests, suggested, mod.identifier, registry, toInstall); } if (!recommended.Any() && !suggested.Any()) { GUI.user.RaiseError("No recommendations or suggestions found."); return; } // Prompt user to choose installCanceled = false; ShowSelection(recommended, toInstall); ShowSelection(suggested, toInstall, true); tabController.HideTab("ChooseRecommendedModsTabPage"); // Install if (!installCanceled && toInstall.Any()) { installWorker.RunWorkerAsync( new KeyValuePair <List <ModChange>, RelationshipResolverOptions>( toInstall.Select(mod => new ModChange( new GUIMod(mod, registry, versionCriteria), GUIModChangeType.Install, null )).ToList(), RelationshipResolver.DefaultOpts() ) ); } }
/// <summary> /// Is the mod installed and does it have a newer version compatible with version /// We can't update AD mods /// </summary> public static bool HasUpdate(this IRegistryQuerier querier, string identifier, KspVersionCriteria version) { CkanModule newest_version; try { newest_version = querier.LatestAvailable(identifier, version); } catch (Exception) { return(false); } if (newest_version == null || !querier.IsInstalled(identifier, false) || querier.InstalledDlls.Contains(identifier) || !newest_version.version.IsGreaterThan(querier.InstalledVersion(identifier))) { return(false); } // All quick checks pass. Now check the relationships. try { RelationshipResolver resolver = new RelationshipResolver( new CkanModule[] { newest_version }, null, new RelationshipResolverOptions() { with_recommends = false, without_toomanyprovides_kraken = true, }, querier, version ); } catch (Exception) { return(false); } return(true); }
private void AuditRecommendations(IRegistryQuerier registry, GameVersionCriteria versionCriteria) { var installer = ModuleInstaller.GetInstance(CurrentInstance, Manager.Cache, currentUser); if (installer.FindRecommendations( registry.InstalledModules.Select(im => im.Module).ToHashSet(), new HashSet <CkanModule>(), registry as 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, new HashSet <CkanModule>(), new HashSet <CkanModule>(), versionCriteria, Manager.Cache, recommendations, suggestions, supporters); var result = ChooseRecommendedMods.Wait(); tabController.HideTab("ChooseRecommendedModsTabPage"); if (result != null && result.Any()) { installWorker.RunWorkerAsync( new KeyValuePair <List <ModChange>, RelationshipResolverOptions>( result.Select(mod => new ModChange( mod, GUIModChangeType.Install, null )).ToList(), RelationshipResolver.DependsOnlyOpts() ) ); } } else { currentUser.RaiseError(Properties.Resources.MainRecommendationsNoneFound); } }
public void Constructor_ProvidesSatisfyDependencies() { var list = new List<string>(); var mod_a = generator.GeneratorRandomModule(); var mod_b = generator.GeneratorRandomModule(provides: new List<string> { mod_a.identifier }); var depender = generator.GeneratorRandomModule(depends: new List<RelationshipDescriptor> { new RelationshipDescriptor {name = mod_a.identifier} }); list.Add(depender.identifier); AddToRegistry(mod_b, depender); var relationship_resolver = new RelationshipResolver(list, options, registry, null); CollectionAssert.AreEquivalent(relationship_resolver.ModList(), new List<CkanModule> { mod_b, depender }); }
/// <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()); }
/// <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); }
private void Changeset_OnConfirmChanges() { menuStrip1.Enabled = 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. try { installWorker.RunWorkerAsync( new KeyValuePair <List <ModChange>, RelationshipResolverOptions>( ManageMods.mainModList .ComputeUserChangeSet(RegistryManager.Instance(Main.Instance.CurrentInstance).registry) .ToList(), RelationshipResolver.DependsOnlyOpts() ) ); } catch (InvalidOperationException) { // Thrown if it's already busy, can happen if the user fouble-clicks the button. Ignore it. // More thread-safe than checking installWorker.IsBusy beforehand. } }
/// <summary> /// This function returns a changeset based on the selections of the user. /// Currently returns null if a conflict is detected. /// </summary> /// <param name="registry"></param> /// <param name="changeSet"></param> /// <param name="installer">A module installer for the current KSP install</param> /// <param name="version">The version of the current KSP install</param> public async Task <IEnumerable <ModChange> > ComputeChangeSetFromModList( IRegistryQuerier registry, HashSet <ModChange> changeSet, ModuleInstaller installer, FactorioVersion version) { var modules_to_install = new HashSet <CfanModule>(); var modules_to_remove = new HashSet <CfanModule>(); var options = new RelationshipResolverOptions { without_toomanyprovides_kraken = false, with_recommends = false }; foreach (var change in changeSet) { switch (change.ChangeType) { case GUIModChangeType.None: break; case GUIModChangeType.Update: case GUIModChangeType.Install: //TODO: Fix //This will give us a mod with a wrong version! modules_to_install.Add(change.Mod.ToCkanModule()); break; case GUIModChangeType.Remove: modules_to_remove.Add(change.Mod); break; 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 registry.FindReverseDependencies(modules_to_remove.Select(mod => mod.identifier))) { //TODO This would be a good place to have a event that alters the row's graphics to show it will be removed CfanModule module_by_version = registry.GetModuleByVersion(installed_modules[dependency].identifier, installed_modules[dependency].modVersion) ?? registry.InstalledModule(dependency).Module; changeSet.Add(new ModChange(new GUIMod(module_by_version, registry, version), GUIModChangeType.Remove, null)); } //May throw InconsistentKraken var resolver = new RelationshipResolver(options, registry, version); resolver.RemoveModsFromInstalledList( changeSet.Where(change => change.ChangeType.Equals(GUIModChangeType.Remove)).Select(m => m.Mod.ToModule())); resolver.AddModulesToInstall(modules_to_install.ToList()); changeSet.UnionWith( resolver.ModList() .Select(m => new ModChange(new GUIMod(m, registry, version), GUIModChangeType.Install, resolver.ReasonFor(m)))); return(changeSet); }
public void InstallList(List <string> modules, RelationshipResolverOptions options, IDownloader downloader = null) { var resolver = new RelationshipResolver(modules, options, registry_manager.registry, ksp.VersionCriteria()); InstallList(resolver.ModList().ToList(), 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="changeSet"></param> /// <param name="installer">A module installer for the current KSP install</param> /// <param name="version">The version of the current KSP install</param> public async Task <IEnumerable <ModChange> > ComputeChangeSetFromModList( IRegistryQuerier registry, HashSet <ModChange> changeSet, ModuleInstaller installer, KspVersionCriteria version) { var modules_to_install = new HashSet <CkanModule>(); var modules_to_remove = new HashSet <CkanModule>(); foreach (var change in changeSet) { switch (change.ChangeType) { case GUIModChangeType.None: break; case GUIModChangeType.Update: case GUIModChangeType.Install: //TODO: Fix //This will give us a mod with a wrong version! modules_to_install.Add(change.Mod.ToCkanModule()); break; case GUIModChangeType.Remove: modules_to_remove.Add(change.Mod); break; case GUIModChangeType.Replace: ModuleReplacement repl = registry.GetReplacement(change.Mod.ToModule(), version); if (repl != null) { modules_to_remove.Add(repl.ToReplace); modules_to_install.Add(repl.ReplaceWith); } break; default: throw new ArgumentOutOfRangeException(); } } var installed_modules = registry.InstalledModules.Select(imod => imod.Module).ToDictionary(mod => mod.identifier, mod => mod); foreach (var dependency in registry.FindReverseDependencies( modules_to_remove .Select(mod => mod.identifier) .Except(modules_to_install.Select(m => m.identifier)) )) { //TODO This would be a good place to have a event that alters the row's graphics to show it will be removed CkanModule module_by_version = registry.GetModuleByVersion(installed_modules[dependency].identifier, installed_modules[dependency].version) ?? registry.InstalledModule(dependency).Module; changeSet.Add(new ModChange(new GUIMod(module_by_version, registry, version), GUIModChangeType.Remove, null)); modules_to_remove.Add(module_by_version); } foreach (var im in registry.FindRemovableAutoInstalled( registry.InstalledModules.Where(im => !modules_to_remove.Any(m => m.identifier == im.identifier)) )) { changeSet.Add(new ModChange(new GUIMod(im.Module, registry, version), GUIModChangeType.Remove, new SelectionReason.NoLongerUsed())); modules_to_remove.Add(im.Module); } bool handled_all_too_many_provides = false; while (!handled_all_too_many_provides) { //Can't await in catch clause - doesn't seem to work in mono. Hence this flag TooManyModsProvideKraken kraken; try { new RelationshipResolver( modules_to_install, modules_to_remove, RelationshipResolver.DependsOnlyOpts(), registry, version); handled_all_too_many_provides = true; continue; } catch (TooManyModsProvideKraken k) { kraken = k; } //Shouldn't get here unless there is a kraken. var mod = await too_many_provides(kraken); if (mod != null) { modules_to_install.Add(mod); } else { //TODO Is could be a new type of Kraken. throw kraken; } } var resolver = new RelationshipResolver( modules_to_install, modules_to_remove, RelationshipResolver.DependsOnlyOpts(), registry, version); changeSet.UnionWith( resolver.ModList() .Select(m => new ModChange(new GUIMod(m, registry, version), GUIModChangeType.Install, resolver.ReasonFor(m)))); return(changeSet); }
private async 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); menuStrip1.Enabled = false; 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_manager.registry, new HashSet <ModChange>() { new ModChange( new GUIMod( module, registry_manager.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 ) ); ShowWaitDialog(); } } catch { // If we failed, do the clean-up normally done by PostInstallMods. HideWaitDialog(false); menuStrip1.Enabled = true; } finally { changeSet = null; } } }
/// <summary> /// Determine whether there is any way to install the given set of mods. /// Handles virtual dependencies, including recursively. /// </summary> /// <param name="registry">Registry of instance into which we want to install</param> /// <param name="versionCriteria">Compatible versions of instance</param> /// <param name="opts">Installer options</param> /// <param name="toInstall">Mods we want to install</param> /// <returns> /// True if it's possible to install these mods, false otherwise /// </returns> private bool CanInstall( IRegistryQuerier registry, KspVersionCriteria versionCriteria, RelationshipResolverOptions opts, List <CkanModule> toInstall ) { string request = toInstall.Select(m => m.identifier).Aggregate((a, b) => $"{a}, {b}"); try { RelationshipResolver resolver = new RelationshipResolver( toInstall, null, opts, registry, versionCriteria ); if (resolver.ModList().Count() >= toInstall.Count(m => !m.IsMetapackage)) { // We can install with no further dependencies string recipe = resolver.ModList() .Select(m => m.identifier) .Aggregate((a, b) => $"{a}, {b}"); log.Debug($"Installable: {request}: {recipe}"); return(true); } else { string problems = resolver.ConflictList.Values .Aggregate((a, b) => $"{a}, {b}"); log.Debug($"Can't install {request}: {problems}"); return(false); } } catch (TooManyModsProvideKraken k) { // One of the dependencies is virtual foreach (CkanModule mod in k.modules) { // Try each option recursively to see if any are successful if (CanInstall(registry, versionCriteria, opts, toInstall.Concat(new List <CkanModule>() { mod }).ToList())) { // Child call will emit debug output, so we don't need to here return(true); } } log.Debug($"Can't install {request}: Can't install provider of {k.requested}"); } catch (InconsistentKraken k) { log.Debug($"Can't install {request}: {k.ShortDescription}"); } catch (Exception ex) { log.Debug($"Can't install {request}: {ex.Message}"); } return(false); }
/// <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; }
public static Dictionary <GUIMod, string> ComputeConflictsFromModList(IRegistryQuerier registry, IEnumerable <ModChange> change_set, GameVersionCriteria ksp_version) { var modules_to_install = new HashSet <string>(); var modules_to_remove = new HashSet <string>(); var options = new RelationshipResolverOptions { without_toomanyprovides_kraken = true, proceed_with_inconsistencies = true, without_enforce_consistency = true, with_recommends = false }; foreach (var change in change_set) { switch (change.ChangeType) { case GUIModChangeType.None: break; case GUIModChangeType.Install: modules_to_install.Add(change.Mod.identifier); break; case GUIModChangeType.Remove: modules_to_remove.Add(change.Mod.identifier); break; case GUIModChangeType.Update: break; case GUIModChangeType.Replace: ModuleReplacement repl = registry.GetReplacement(change.Mod, ksp_version); if (repl != null) { modules_to_remove.Add(repl.ToReplace.identifier); modules_to_install.Add(repl.ReplaceWith.identifier); } break; default: throw new ArgumentOutOfRangeException(); } } // Only check mods that would exist after the changes are made. IEnumerable <CkanModule> installed = registry.InstalledModules.Where( im => !modules_to_remove.Contains(im.Module.identifier) ).Select(im => im.Module); // Convert ONLY modules_to_install with CkanModule.FromIDandVersion, // because it may not find already-installed modules. IEnumerable <CkanModule> mods_to_check = installed.Union( modules_to_install.Except(modules_to_remove).Select( name => CkanModule.FromIDandVersion(registry, name, ksp_version) ) ); var resolver = new RelationshipResolver( mods_to_check, change_set.Where(ch => ch.ChangeType == GUIModChangeType.Remove) .Select(ch => ch.Mod), options, registry, ksp_version ); return(resolver.ConflictList.ToDictionary(item => new GUIMod(item.Key, registry, ksp_version), item => item.Value)); }
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); }
public void ModList_WithInstalledModulesSugested_DoesNotContainThem() { options.with_all_suggests = true; var list = new List<string>(); var sugested = generator.GeneratorRandomModule(); var sugester = generator.GeneratorRandomModule(sugests: new List<RelationshipDescriptor> { new RelationshipDescriptor {name = sugested.identifier} }); list.Add(sugester.identifier); AddToRegistry(sugester, sugested); registry.Installed().Add(sugested.identifier, sugested.version); var relationship_resolver = new RelationshipResolver(list, options, registry, null); CollectionAssert.Contains(relationship_resolver.ModList(), sugested); }
/// <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 Constructor_WithDependantVersion_ChooseCorrectly(string ver, string dep, string other) { var list = new List<string>(); var dependant = generator.GeneratorRandomModule(version: new Version(ver)); var other_dependant = generator.GeneratorRandomModule(identifier: dependant.identifier, version: new Version(other)); var depender = generator.GeneratorRandomModule(depends: new List<RelationshipDescriptor> { new RelationshipDescriptor {name = dependant.identifier, version = new Version(dep)} }); list.Add(depender.identifier); AddToRegistry(depender, dependant, other_dependant); var relationship_resolver = new RelationshipResolver(list, options, registry, null); CollectionAssert.AreEquivalent(relationship_resolver.ModList(), new List<CkanModule> { dependant, depender }); }
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> /// 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); }
/// <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; }
public void ModList_WithInstalledModules_DoesNotContainThem() { var list = new List<string>(); var mod_a = generator.GeneratorRandomModule(); list.Add(mod_a.identifier); AddToRegistry(mod_a); registry.Installed().Add(mod_a.identifier, mod_a.version); var relationship_resolver = new RelationshipResolver(list, options, registry, null); CollectionAssert.IsEmpty(relationship_resolver.ModList()); }
/// <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}", module); downloads.Add(module); } else { User.RaiseMessage(" * {0} (cached)", module); } } 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); }
public void ModList_WithSugestedModulesThatWouldConflict_DoesNotContainThem() { options.with_all_suggests = true; var list = new List<string>(); var sugested = generator.GeneratorRandomModule(); var mod = generator.GeneratorRandomModule(conflicts: new List<RelationshipDescriptor> { new RelationshipDescriptor {name = sugested.identifier} }); var sugester = generator.GeneratorRandomModule(sugests: new List<RelationshipDescriptor> { new RelationshipDescriptor {name = sugested.identifier} }); list.Add(sugester.identifier); list.Add(mod.identifier); AddToRegistry(sugester, sugested, mod); var relationship_resolver = new RelationshipResolver(list, options, registry, null); CollectionAssert.DoesNotContain(relationship_resolver.ModList(), sugested); }
/// <summary> /// This function returns a changeset based on the selections of the user. /// Currently returns null if a conflict is detected. /// </summary> /// <param name="registry"></param> /// <param name="changeSet"></param> /// <param name="installer">A module installer for the current game instance</param> /// <param name="version">The version of the current game instance</param> public IEnumerable <ModChange> ComputeChangeSetFromModList( IRegistryQuerier registry, HashSet <ModChange> changeSet, ModuleInstaller installer, GameVersionCriteria version) { var modules_to_install = new HashSet <CkanModule>(); var modules_to_remove = new HashSet <CkanModule>(); foreach (var change in changeSet) { switch (change.ChangeType) { case GUIModChangeType.None: break; case GUIModChangeType.Update: case GUIModChangeType.Install: modules_to_install.Add(change.Mod); break; case GUIModChangeType.Remove: modules_to_remove.Add(change.Mod); break; case GUIModChangeType.Replace: ModuleReplacement repl = registry.GetReplacement(change.Mod, version); if (repl != null) { modules_to_remove.Add(repl.ToReplace); modules_to_install.Add(repl.ReplaceWith); } break; default: throw new ArgumentOutOfRangeException(); } } var installed_modules = registry.InstalledModules.Select(imod => imod.Module).ToDictionary(mod => mod.identifier, mod => mod); foreach (var dependent in registry.FindReverseDependencies( modules_to_remove .Select(mod => mod.identifier) .Except(modules_to_install.Select(m => m.identifier)), modules_to_install )) { //TODO This would be a good place to have an event that alters the row's graphics to show it will be removed CkanModule depMod; if (installed_modules.TryGetValue(dependent, out depMod)) { CkanModule module_by_version = registry.GetModuleByVersion(depMod.identifier, depMod.version) ?? registry.InstalledModule(dependent).Module; changeSet.Add(new ModChange(module_by_version, GUIModChangeType.Remove, null)); modules_to_remove.Add(module_by_version); } } foreach (var im in registry.FindRemovableAutoInstalled( registry.InstalledModules.Where(im => !modules_to_remove.Any(m => m.identifier == im.identifier) || modules_to_install.Any(m => m.identifier == im.identifier)) )) { changeSet.Add(new ModChange(im.Module, GUIModChangeType.Remove, new SelectionReason.NoLongerUsed())); modules_to_remove.Add(im.Module); } // Get as many dependencies as we can, but leave decisions and prompts for installation time RelationshipResolverOptions opts = RelationshipResolver.DependsOnlyOpts(); opts.without_toomanyprovides_kraken = true; opts.without_enforce_consistency = true; var resolver = new RelationshipResolver( modules_to_install, modules_to_remove, opts, registry, version); changeSet.UnionWith( resolver.ModList() .Select(m => new ModChange(m, GUIModChangeType.Install, resolver.ReasonFor(m)))); return(changeSet); }
public void ReasonFor_WithModsNotInList_ThrowsArgumentException() { var list = new List<string>(); var mod = generator.GeneratorRandomModule(); list.Add(mod.identifier); registry.AddAvailable(mod); AddToRegistry(mod); var relationship_resolver = new RelationshipResolver(list, options, registry, null); var mod_not_in_resolver_list = generator.GeneratorRandomModule(); CollectionAssert.DoesNotContain(relationship_resolver.ModList(),mod_not_in_resolver_list); Assert.Throws<ArgumentException>(() => relationship_resolver.ReasonFor(mod_not_in_resolver_list)); }
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); } }
public void ReasonFor_WithTreeOfMods_GivesCorrectParents() { var list = new List<string>(); var sugested = generator.GeneratorRandomModule(); var recommendedA = generator.GeneratorRandomModule(); var recommendedB = generator.GeneratorRandomModule(); var mod = generator.GeneratorRandomModule(sugests: new List<RelationshipDescriptor> { new RelationshipDescriptor { name = sugested.identifier}}); list.Add(mod.identifier); sugested.recommends = new List<RelationshipDescriptor> { new RelationshipDescriptor {name=recommendedA.identifier}, new RelationshipDescriptor { name = recommendedB.identifier}}; AddToRegistry(mod, sugested,recommendedA,recommendedB); options.with_all_suggests = true; options.with_recommends = true; var relationship_resolver = new RelationshipResolver(list, options, registry, null); var reason = relationship_resolver.ReasonFor(recommendedA); Assert.That(reason, Is.AssignableTo<SelectionReason.Recommended>()); Assert.That(reason.Parent, Is.EqualTo(sugested)); reason = relationship_resolver.ReasonFor(recommendedB); Assert.That(reason, Is.AssignableTo<SelectionReason.Recommended>()); Assert.That(reason.Parent, Is.EqualTo(sugested)); }
/// <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); }
public void ReasonFor_WithUserAddedMods_GivesReasonUserAdded() { var list = new List<string>(); var mod = generator.GeneratorRandomModule(); list.Add(mod.identifier); registry.AddAvailable(mod); AddToRegistry(mod); var relationship_resolver = new RelationshipResolver(list, options, registry, null); var reason = relationship_resolver.ReasonFor(mod); Assert.That(reason, Is.AssignableTo<SelectionReason.UserRequested>()); }
private IEnumerable <ListViewItem> getRecSugRows( IEnumerable <CkanModule> sourceModules, IRegistryQuerier registry, HashSet <CkanModule> toInstall ) { Dictionary <CkanModule, List <string> > dependersIndex = getDependersIndex(sourceModules, registry, toInstall); foreach (CkanModule mod in sourceModules.Where(m => m.recommends != null)) { foreach (RelationshipDescriptor rel in mod.recommends) { List <CkanModule> providers = rel.LatestAvailableWithProvides( registry, CurrentInstance.VersionCriteria() ); foreach (CkanModule provider in providers) { List <string> dependers; if (!registry.IsInstalled(provider.identifier) && !toInstall.Any(m => m.identifier == provider.identifier) && dependersIndex.TryGetValue(provider, out dependers) && CanInstall(registry, CurrentInstance.VersionCriteria(), RelationshipResolver.DependsOnlyOpts(), toInstall.ToList().Concat(new List <CkanModule>() { provider }).ToList())) { dependersIndex.Remove(provider); yield return(getRecSugItem( provider, string.Join(", ", dependers), RecommendationsGroup, providers.Count <= 1 || provider.identifier == (rel as ModuleRelationshipDescriptor)?.name )); } } } } foreach (CkanModule mod in sourceModules.Where(m => m.suggests != null)) { foreach (RelationshipDescriptor rel in mod.suggests) { List <CkanModule> providers = rel.LatestAvailableWithProvides( registry, CurrentInstance.VersionCriteria() ); foreach (CkanModule provider in providers) { List <string> dependers; if (!registry.IsInstalled(provider.identifier) && !toInstall.Any(m => m.identifier == provider.identifier) && dependersIndex.TryGetValue(provider, out dependers) && CanInstall(registry, CurrentInstance.VersionCriteria(), RelationshipResolver.DependsOnlyOpts(), toInstall.ToList().Concat(new List <CkanModule>() { provider }).ToList())) { dependersIndex.Remove(provider); yield return(getRecSugItem( provider, string.Join(", ", dependers), SuggestionsGroup, false )); } } } } }
// this functions computes a changeset from the user's choices in the GUI private List <KeyValuePair <CkanModule, GUIModChangeType> > ComputeChangeSetFromModList() // this probably needs to be refactored { var changeset = new HashSet <KeyValuePair <CkanModule, GUIModChangeType> >(); // these are the lists var modulesToInstall = new HashSet <string>(); var modulesToRemove = new HashSet <string>(); Registry registry = RegistryManager.Instance(KSPManager.CurrentInstance).registry; foreach (DataGridViewRow row in ModList.Rows) { var mod = (CkanModule)row.Tag; if (mod == null) { continue; } bool isInstalled = registry.IsInstalled(mod.identifier); var isInstalledCell = row.Cells[0] as DataGridViewCheckBoxCell; var isInstalledChecked = (bool)isInstalledCell.Value; if (!isInstalled && isInstalledChecked) { modulesToInstall.Add(mod.identifier); } else if (isInstalled && !isInstalledChecked) { modulesToRemove.Add(mod.identifier); } } RelationshipResolverOptions options = RelationshipResolver.DefaultOpts(); options.with_recommends = false; options.without_toomanyprovides_kraken = true; options.without_enforce_consistency = true; RelationshipResolver resolver = null; try { resolver = new RelationshipResolver(modulesToInstall.ToList(), options, registry); } catch (Exception e) { return(null); } foreach (CkanModule mod in resolver.ModList()) { changeset.Add(new KeyValuePair <CkanModule, GUIModChangeType>(mod, GUIModChangeType.Install)); } ModuleInstaller installer = ModuleInstaller.Instance; foreach (string moduleName in modulesToRemove) { var reverseDependencies = installer.FindReverseDependencies(moduleName); foreach (string reverseDependency in reverseDependencies) { CkanModule mod = registry.LatestAvailable(reverseDependency); changeset.Add(new KeyValuePair <CkanModule, GUIModChangeType>(mod, GUIModChangeType.Remove)); } } foreach (DataGridViewRow row in ModList.Rows) { var mod = (CkanModule)row.Tag; if (mod == null) { continue; } bool isInstalled = registry.IsInstalled(mod.identifier); var isInstalledCell = row.Cells[0] as DataGridViewCheckBoxCell; var isInstalledChecked = (bool)isInstalledCell.Value; DataGridViewCell shouldBeUpdatedCell = row.Cells[1]; bool shouldBeUpdated = false; if (shouldBeUpdatedCell is DataGridViewCheckBoxCell && shouldBeUpdatedCell.Value != null) { shouldBeUpdated = (bool)shouldBeUpdatedCell.Value; } if (isInstalled && !isInstalledChecked) { changeset.Add(new KeyValuePair <CkanModule, GUIModChangeType>(mod, GUIModChangeType.Remove)); } else if (isInstalled && isInstalledChecked && mod.version.IsGreaterThan(registry.InstalledVersion(mod.identifier)) && shouldBeUpdated) { changeset.Add(new KeyValuePair <CkanModule, GUIModChangeType>(mod, GUIModChangeType.Update)); } } return(changeset.ToList()); }