/// <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; }
// 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()); }
/// <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 List <KeyValuePair <CkanModule, GUIModChangeType> > ComputeChangeSetFromModList(Registry registry, KSP current_instance) { var changeset = new HashSet <KeyValuePair <CkanModule, GUIModChangeType> >(); var modulesToInstall = new HashSet <string>(); var modulesToRemove = new HashSet <string>(); foreach (var mod in Modules.Where(mod => mod.IsInstallable())) { if (mod.IsInstalled) { if (!mod.IsInstallChecked) { modulesToRemove.Add(mod.Identifier); changeset.Add(new KeyValuePair <CkanModule, GUIModChangeType>(mod.ToCkanModule(), GUIModChangeType.Remove)); } else if (mod.IsInstallChecked && mod.HasUpdate && mod.IsUpgradeChecked) { changeset.Add(new KeyValuePair <CkanModule, GUIModChangeType>(mod.ToCkanModule(), GUIModChangeType.Update)); } } else if (mod.IsInstallChecked) { modulesToInstall.Add(mod.Identifier); } } RelationshipResolverOptions options = RelationshipResolver.DefaultOpts(); options.with_recommends = false; options.without_toomanyprovides_kraken = true; options.without_enforce_consistency = true; RelationshipResolver resolver; try { resolver = new RelationshipResolver(modulesToInstall.ToList(), options, registry); } catch (Exception) { //TODO FIX this so the UI reacts. return(null); } changeset.UnionWith( resolver.ModList() .Select(mod => new KeyValuePair <CkanModule, GUIModChangeType>(mod, GUIModChangeType.Install))); ModuleInstaller installer = ModuleInstaller.GetInstance(current_instance, GUI.user); foreach (var reverseDependencies in modulesToRemove.Select(mod => installer.FindReverseDependencies(mod))) { //TODO This would be a good place to have a event that alters the row's graphics to show it will be removed var modules = reverseDependencies.Select(rDep => registry.LatestAvailable(rDep)); changeset.UnionWith( modules.Select(mod => new KeyValuePair <CkanModule, GUIModChangeType>(mod, GUIModChangeType.Remove))); } return(changeset.ToList()); }