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() ) ); }
/// <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); }
/// <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); }
/// <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); }
/// <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, 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.ChangeType) { case GUIModChangeType.None: break; 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.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 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 Module 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)); } //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; }