Skip to content

Commit

Permalink
Handle recommendations with virtual dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
HebaruSan committed Nov 18, 2018
1 parent 9bd2b82 commit 07ad596
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 31 deletions.
22 changes: 14 additions & 8 deletions GUI/MainDialogs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,29 @@ public bool YesNoDialog(string text)
return yesNoDialog.ShowYesNoDialog(text) == DialogResult.Yes;
}

//Ugly Hack. Possible fix is to alter the relationship provider so we can use a loop
//over reason for to find a user requested mod. Or, you know, pass in a handler to it.
// Ugly Hack. Possible fix is to alter the relationship provider so we can use a loop
// over reason for to find a user requested mod. Or, you know, pass in a handler to it.
private readonly ConcurrentStack<GUIMod> last_mod_to_have_install_toggled = new ConcurrentStack<GUIMod>();
public async Task<CkanModule> TooManyModsProvide(TooManyModsProvideKraken kraken)
{
//We want LMtHIT to be the last user selection. If we alter this handling a too many provides
// it needs to be reset so a potential second too many provides doesn't use the wrong mod.
GUIMod mod;

private async Task<CkanModule> TooManyModsProvideCore(TooManyModsProvideKraken kraken)
{
TaskCompletionSource<CkanModule> task = new TaskCompletionSource<CkanModule>();
Util.Invoke(this, () =>
{
UpdateProvidedModsDialog(kraken, task);
tabController.ShowTab("ChooseProvidedModsTabPage", 3);
tabController.SetTabLock(true);
});
var module = await task.Task;
return await task.Task;
}

public async Task<CkanModule> TooManyModsProvide(TooManyModsProvideKraken kraken)
{
// We want LMtHIT to be the last user selection. If we alter this handling a too many provides
// it needs to be reset so a potential second too many provides doesn't use the wrong mod.
GUIMod mod;

var module = await TooManyModsProvideCore(kraken);

if (module == null
&& last_mod_to_have_install_toggled.TryPeek(out mod))
Expand Down
24 changes: 24 additions & 0 deletions GUI/MainInstall.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,30 @@ private void InstallMods(object sender, DoWorkEventArgs e) // this probably need
}
resolvedAllProvidedMods = true;
}
catch (TooManyModsProvideKraken k)
{
// Prompt user to choose which mod to use
CkanModule chosen = TooManyModsProvideCore(k).Result;
// Close the selection prompt
Util.Invoke(this, () =>
{
tabController.ShowTab("WaitTabPage");
tabController.HideTab("ChooseProvidedModsTabPage");
});
if (chosen != null)
{
// User picked a mod, queue it up for installation
toInstall.Add(chosen);
// DON'T return so we can loop around and try the above InstallList call again
}
else
{
// User cancelled, get out
tabController.ShowTab("ManageModsTabPage");
e.Result = new KeyValuePair<bool, ModChanges>(false, opts.Key);
return;
}
}
catch (DependencyNotSatisfiedKraken ex)
{
GUI.user.RaiseMessage(
Expand Down
98 changes: 75 additions & 23 deletions GUI/MainRecommendations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,38 +112,90 @@ private Dictionary<CkanModule, string> GetShowableMods(
HashSet<CkanModule> toInstall
)
{
Dictionary<CkanModule, string> modules = new Dictionary<CkanModule, string>();
return mods.Where(kvp => CanInstall(
registry, versionCriteria,
new RelationshipResolverOptions()
{
with_all_suggests = false,
with_recommends = false,
with_suggests = false,
without_enforce_consistency = false,
without_toomanyprovides_kraken = false
},
toInstall.ToList().Concat(new List<CkanModule>() { kvp.Key }).ToList()
)).ToDictionary(
kvp => kvp.Key,
kvp => string.Join(", ", kvp.Value.ToArray())
);
}

var opts = new RelationshipResolverOptions
/// <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
{
with_all_suggests = false,
with_recommends = false,
with_suggests = false,
without_enforce_consistency = false,
without_toomanyprovides_kraken = true
};
RelationshipResolver resolver = new RelationshipResolver(
toInstall,
null,
opts, registry, versionCriteria
);

foreach (var pair in mods)
if (resolver.ModList().Count() >= toInstall.Count)
{
// 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)
{
try
// One of the dependencies is virtual
foreach (CkanModule mod in k.modules)
{
List<CkanModule> instPlusOne = toInstall.ToList();
instPlusOne.Add(pair.Key);
RelationshipResolver resolver = new RelationshipResolver(
instPlusOne,
null,
opts, registry, versionCriteria
);

if (resolver.ModList().Any())
// Try each option recursively to see if any are successful
if (CanInstall(registry, versionCriteria, opts, toInstall.Concat(new List<CkanModule>() { mod }).ToList()))
{
// Resolver was able to find a way to install, so show it to the user
modules.Add(pair.Key, String.Join(",", pair.Value.ToArray()));
// Child call will emit debug output, so we don't need to here
return true;
}
}
catch { }
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 modules;
return false;
}

private void UpdateRecommendedDialog(Dictionary<CkanModule, string> mods, bool suggested = false)
Expand Down

0 comments on commit 07ad596

Please sign in to comment.