Skip to content

Commit

Permalink
Merge #4232 Refactor relationship resolver to capture full resolved tree
Browse files Browse the repository at this point in the history
  • Loading branch information
HebaruSan committed Oct 17, 2024
2 parents 4b90893 + 7fb9708 commit 653d085
Show file tree
Hide file tree
Showing 41 changed files with 1,489 additions and 829 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.
### Bugfixes

- [GUI] Set focus better on Ctrl+F (#4230 by: HebaruSan)
- [Multiple] Refactor relationship resolver to capture full resolved tree (#4232 by: HebaruSan)

### Internal

Expand Down
2 changes: 1 addition & 1 deletion Cmdline/Action/Install.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
user.RaiseMessage("");
done = true;
}
catch (DependencyNotSatisfiedKraken ex)
catch (DependenciesNotSatisfiedKraken ex)
{
user.RaiseError("{0}", ex.Message);
user.RaiseMessage(Properties.Resources.InstallTryAgain);
Expand Down
5 changes: 2 additions & 3 deletions Cmdline/Action/Replace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,9 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
regMgr);
user.RaiseMessage("");
}
catch (DependencyNotSatisfiedKraken ex)
catch (DependenciesNotSatisfiedKraken ex)
{
user.RaiseMessage(Properties.Resources.ReplaceDependencyNotSatisfied,
ex.parent, ex.module, ex.version ?? "", instance.game.ShortName);
user.RaiseMessage("{0}", ex.Message);
}
}
else
Expand Down
1 change: 0 additions & 1 deletion Cmdline/Properties/Resources.fr-FR.resx
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,6 @@ Soyez sûr d'avoir saisi au moins les valeurs de version majeure et mineure au f
</data>
<data name="InstallTryAgain" xml:space="preserve">
<value>Si vous êtes chanceux, vous pouvez taper `ckan update` et réessayer.
Essayez `ckan install --no-recommends` pour ignorer l'installation des modules recommandés.
Ou `ckan install --allow-incompatible` pour ignorer la compatibilité des modules.</value>
</data>
<data name="InstallUnversionedDependencyNotSatisfied" xml:space="preserve">
Expand Down
1 change: 0 additions & 1 deletion Cmdline/Properties/Resources.it-IT.resx
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,6 @@ Assicurati di inserire almeno i valori di versione maggiore e minore nella forma
</data>
<data name="InstallTryAgain" xml:space="preserve">
<value>Se sei fortunato, puoi fare un `ckan update` e riprovare.
Prova `ckan install --no-recommends` per saltare l'installazione dei moduli consigliati.
Oppure `ckan install --allow-incompatible` per ignorare la compatibilità dei moduli.</value>
</data>
<data name="InstallUnversionedDependencyNotSatisfied" xml:space="preserve">
Expand Down
1 change: 0 additions & 1 deletion Cmdline/Properties/Resources.pl-PL.resx
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,6 @@ Upewnij się, że wprowadziłeś wersję w postaci Maj.Min - np. 1.5</value>
</data>
<data name="InstallTryAgain" xml:space="preserve">
<value>Jeśli masz szczęście, możesz wykonać `ckan update` i spróbować ponownie.
Spróbuj `ckan install --no-recommends` aby pominąć instalację zalecanych modułów.
Lub `ckan install --allow-niecompatible` aby zignorować kompatybilność modułów.</value>
</data>
<data name="InstallUnversionedDependencyNotSatisfied" xml:space="preserve">
Expand Down
7 changes: 4 additions & 3 deletions Cmdline/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,10 @@ Make sure to enter at least the version major and minor values in the form Maj.M
<data name="ImportError" xml:space="preserve"><value>Import error: {0}</value></data>
<data name="ImportNotFound" xml:space="preserve"><value>File not found: {0}</value></data>
<data name="InstallNotFound" xml:space="preserve"><value>File not found, exiting: {0}</value></data>
<data name="InstallTryAgain" xml:space="preserve"><value>If you're lucky, you can do a `ckan update` and try again.
Try `ckan install --no-recommends` to skip installation of recommended modules.
Or `ckan install --allow-incompatible` to ignore module compatibility.</value></data>
<data name="InstallTryAgain" xml:space="preserve"><value>
You can use the `ckan compat` commands to change which game versions are treated as compatible.
If a newly compatible version was just released, you can do a `ckan update` and try again.
Or `ckan install --allow-incompatible` to ignore compatibility of the modules on the command line (but not dependencies).</value></data>
<data name="InstallUnversionedDependencyNotSatisfied" xml:space="preserve"><value>Module {0} required but it is not listed in the index, or not available for your version of {1}</value></data>
<data name="InstallVersionedDependencyNotSatisfied" xml:space="preserve"><value>Module {0} {1} required but it is not listed in the index, or not available for your version of {2}</value></data>
<data name="InstallBadMetadata" xml:space="preserve"><value>Bad metadata detected for module {0}: {1}</value></data>
Expand Down
1 change: 0 additions & 1 deletion Cmdline/Properties/Resources.ru-RU.resx
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,6 @@
<data name="ImportNotFound" xml:space="preserve"><value>Файл не найден: {0}</value></data>
<data name="InstallNotFound" xml:space="preserve"><value>Файл не найден, выход: {0}</value></data>
<data name="InstallTryAgain" xml:space="preserve"><value>Попробуйте совершить `ckan update` и попытайтесь заново.
Используйте `ckan install --no-recommends`, чтобы пропустить установку рекомендуемых модулей.
Либо используйте `ckan install --allow-incompatible`, чтобы игнорировать проблемы совместимости.</value></data>
<data name="InstallUnversionedDependencyNotSatisfied" xml:space="preserve"><value>Необходим модуль {0}, но он отсутствует в указателе либо недоступен для вашей версии {1}</value></data>
<data name="InstallVersionedDependencyNotSatisfied" xml:space="preserve"><value>Необходим модуль {0} {1}, но он отсутствует в указателе либо недоступен для вашей версии {2}</value></data>
Expand Down
16 changes: 9 additions & 7 deletions ConsoleUI/DependencyScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,13 @@ public DependencyScreen(ConsoleTheme theme,

private void generateList(HashSet<CkanModule> inst)
{
if (ModuleInstaller.FindRecommendations(
manager.CurrentInstance,
inst, new List<CkanModule>(inst), registry,
out Dictionary<CkanModule, Tuple<bool, List<string>>> recommendations,
out Dictionary<CkanModule, List<string>> suggestions,
out Dictionary<CkanModule, HashSet<string>> supporters
if (manager.CurrentInstance is GameInstance instance
&& ModuleInstaller.FindRecommendations(
instance,
inst, new List<CkanModule>(inst), registry,
out Dictionary<CkanModule, Tuple<bool, List<string>>> recommendations,
out Dictionary<CkanModule, List<string>> suggestions,
out Dictionary<CkanModule, HashSet<string>> supporters
)) {
foreach ((CkanModule mod, Tuple<bool, List<string>> checkedAndDependents) in recommendations) {
dependencies.Add(mod, new Dependency(
Expand Down Expand Up @@ -215,11 +216,12 @@ private bool HasConflicts(IEnumerable<CkanModule> toAdd,
plan.Remove.Select(ident => registry.InstalledModule(ident)?.Module)
.OfType<CkanModule>(),
RelationshipResolverOptions.ConflictsOpts(), registry,
manager.CurrentInstance.game,
manager.CurrentInstance.VersionCriteria());
descriptions = resolver.ConflictDescriptions.ToList();
return descriptions.Count > 0;
}
catch (DependencyNotSatisfiedKraken k)
catch (DependenciesNotSatisfiedKraken k)
{
descriptions = new List<string>() { k.Message };
return true;
Expand Down
4 changes: 2 additions & 2 deletions ConsoleUI/InstallScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,8 @@ public override void Run(Action? process = null)

} catch (BadMetadataKraken ex) {
RaiseError(Properties.Resources.InstallBadMetadata, ex.module?.ToString() ?? "", ex.Message);
} catch (DependencyNotSatisfiedKraken ex) {
RaiseError(Properties.Resources.InstallUnsatisfiedDependency, ex.parent, ex.module, ex.Message);
} catch (DependenciesNotSatisfiedKraken ex) {
RaiseError("{0}", ex.Message);
} catch (ModuleNotFoundKraken ex) {
RaiseError(Properties.Resources.InstallModuleNotFound, ex.module, ex.Message);
} catch (ModNotInstalledKraken ex) {
Expand Down
1 change: 1 addition & 0 deletions Core/CKAN-core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
<PackageReference Include="Nullable" Version="1.3.1" PrivateAssets="all" />
<PackageReference Include="IndexRange" Version="1.0.3" />
<PackageReference Include="StringSyntaxAttribute" Version="1.0.0" />
<PackageReference Include="Microsoft.Bcl.HashCode" Version="1.1.1" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\_build\meta\GlobalAssemblyVersionInfo.cs">
Expand Down
14 changes: 14 additions & 0 deletions Core/Extensions/EnumerableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,20 @@ public static IEnumerable<Match> WithMatches(this IEnumerable<string> source, Re
=> source.Select(item => Utilities.DefaultIfThrows(() => func(item),
exc => onThrow(item, exc)));

/// <summary>
/// Get a hash code for a sequence with a variable number of elements
/// </summary>
/// <typeparam name="T">Type of the elements in the sequence</typeparam>
/// <param name="source">The sequence</param>
/// <returns></returns>
public static int ToSequenceHashCode<T>(this IEnumerable<T> source)
=> source.Aggregate(new HashCode(),
(hc, item) =>
{
hc.Add(item);
return hc;
},
hc => hc.ToHashCode());
}

/// <summary>
Expand Down
26 changes: 16 additions & 10 deletions Core/ModuleInstaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ public void InstallList(ICollection<CkanModule> modules,
}
var resolver = new RelationshipResolver(modules, null, options,
registry_manager.registry,
instance.game,
instance.VersionCriteria());
var modsToInstall = resolver.ModList().ToList();
var downloads = new List<CkanModule>();
Expand Down Expand Up @@ -774,6 +775,7 @@ public void UninstallList(IEnumerable<string> mods,
.Where(im => !revdep.Contains(im.identifier))
.Concat(installing?.Select(m => new InstalledModule(null, m, Array.Empty<string>(), false)) ?? Array.Empty<InstalledModule>())
.ToList(),
instance.game,
instance.VersionCriteria())
.Select(im => im.identifier))
.ToList();
Expand Down Expand Up @@ -1185,6 +1187,7 @@ public void Upgrade(ICollection<CkanModule> modules,
.OfType<CkanModule>(),
RelationshipResolverOptions.DependsOnlyOpts(),
registry,
instance.game,
instance.VersionCriteria());
modules = resolver.ModList().ToArray();
autoInstalled = modules.ToDictionary(m => m, resolver.IsAutoInstalled);
Expand Down Expand Up @@ -1285,6 +1288,7 @@ public void Upgrade(ICollection<CkanModule> modules,
.Where(im => !removingIdents.Contains(im.identifier))
.Concat(modules.Select(m => new InstalledModule(null, m, Array.Empty<string>(), false)))
.ToList(),
instance.game,
instance.VersionCriteria())
.ToList();
if (autoRemoving.Count > 0)
Expand Down Expand Up @@ -1318,7 +1322,7 @@ public void Upgrade(ICollection<CkanModule> modules,
/// Enacts listed Module Replacements to the specified versions for the user's KSP.
/// Will *re-install* or *downgrade* (with a warning) as well as upgrade.
/// </summary>
/// <exception cref="DependencyNotSatisfiedKraken">Thrown if a dependency for a replacing module couldn't be satisfied.</exception>
/// <exception cref="DependenciesNotSatisfiedKraken">Thrown if a dependency for a replacing module couldn't be satisfied.</exception>
/// <exception cref="ModuleNotFoundKraken">Thrown if a module that should be replaced is not installed.</exception>
public void Replace(IEnumerable<ModuleReplacement> replacements,
RelationshipResolverOptions options,
Expand Down Expand Up @@ -1401,7 +1405,8 @@ public void Replace(IEnumerable<ModuleReplacement> replacements,
}
}
}
var resolver = new RelationshipResolver(modsToInstall, null, options, registry_manager.registry, instance.VersionCriteria());
var resolver = new RelationshipResolver(modsToInstall, null, options, registry_manager.registry,
instance.game, instance.VersionCriteria());
var resolvedModsToInstall = resolver.ModList().ToList();
AddRemove(ref possibleConfigOnlyDirs,
registry_manager,
Expand Down Expand Up @@ -1433,19 +1438,19 @@ public static IEnumerable<string> PrioritizedHosts(IEnumerable<Uri>? urls)
/// <returns>
/// true if anything found, false otherwise
/// </returns>
public static bool FindRecommendations(GameInstance? instance,
public static bool FindRecommendations(GameInstance instance,
HashSet<CkanModule> sourceModules,
List<CkanModule> toInstall,
Registry registry,
out Dictionary<CkanModule, Tuple<bool, List<string>>> recommendations,
out Dictionary<CkanModule, List<string>> suggestions,
out Dictionary<CkanModule, HashSet<string>> supporters)
{
var crit = instance?.VersionCriteria();
var crit = instance.VersionCriteria();
var resolver = new RelationshipResolver(sourceModules.Where(m => !m.IsDLC),
null,
RelationshipResolverOptions.KitchenSinkOpts(),
registry, crit);
registry, instance.game, crit);
var recommenders = resolver.Dependencies().ToHashSet();

var checkedRecs = resolver.Recommendations(recommenders)
Expand All @@ -1454,7 +1459,7 @@ public static bool FindRecommendations(GameInstance?
.ToHashSet();
var conflicting = new RelationshipResolver(toInstall.Concat(checkedRecs), null,
RelationshipResolverOptions.ConflictsOpts(),
registry, crit)
registry, instance.game, crit)
.ConflictList.Keys;
// Don't check recommendations that conflict with installed or installing mods
checkedRecs.ExceptWith(conflicting);
Expand Down Expand Up @@ -1486,7 +1491,7 @@ public static bool FindRecommendations(GameInstance?
toInstall.Concat(recommendations.Keys)
.Concat(suggestions.Keys))
.Where(kvp => CanInstall(toInstall.Append(kvp.Key).ToList(),
opts, registry, crit))
opts, registry, instance.game, crit))
.ToDictionary();

return recommendations.Count > 0
Expand All @@ -1507,14 +1512,15 @@ public static bool FindRecommendations(GameInstance?
public static bool CanInstall(List<CkanModule> toInstall,
RelationshipResolverOptions opts,
IRegistryQuerier registry,
GameVersionCriteria? crit)
IGame game,
GameVersionCriteria crit)
{
string request = string.Join(", ", toInstall.Select(m => m.identifier));
try
{
var installed = toInstall.Select(m => registry.InstalledModule(m.identifier)?.Module)
.OfType<CkanModule>();
var resolver = new RelationshipResolver(toInstall, installed, opts, registry, crit);
var resolver = new RelationshipResolver(toInstall, installed, opts, registry, game, crit);

var resolverModList = resolver.ModList(false).ToList();
if (resolverModList.Count >= toInstall.Count(m => !m.IsMetapackage))
Expand All @@ -1536,7 +1542,7 @@ public static bool CanInstall(List<CkanModule> toInstall,
foreach (var mod in k.modules)
{
// Try each option recursively to see if any are successful
if (CanInstall(toInstall.Append(mod).ToList(), opts, registry, crit))
if (CanInstall(toInstall.Append(mod).ToList(), opts, registry, game, crit))
{
// Child call will emit debug output, so we don't need to here
return true;
Expand Down
3 changes: 2 additions & 1 deletion Core/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,8 @@ Which {0} provider would you like to install?</value></data>
{1}

If the game is still running, close it and try again. Otherwise check the permissions.</value></data>
<data name="KrakenMissingDependency" xml:space="preserve"><value>{0} missing dependency {1}</value></data>
<data name="KrakenMissingDependency" xml:space="preserve"><value>Unsatisfied dependency {1} needed for: {0}</value></data>
<data name="KrakenMissingDependencyNeededFor" xml:space="preserve"><value>needed for {0}</value></data>
<data name="KrakenConflictsWith" xml:space="preserve"><value>{0} conflicts with {1}</value></data>
<data name="KrakenDownloadErrorsHeader" xml:space="preserve"><value>Uh oh, the following things went wrong when downloading...</value></data>
<data name="KrakenModuleDownloadErrorsHeader" xml:space="preserve"><value>One or more downloads were unsuccessful:</value></data>
Expand Down
6 changes: 3 additions & 3 deletions Core/Registry/CompatibilitySorter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public CompatibilitySorter(GameVersionCriteria crit
IEnumerable<Dictionary<string, AvailableModule>> available,
Dictionary<string, HashSet<AvailableModule>> providers,
Dictionary<string, InstalledModule> installed,
HashSet<string> dlls,
ICollection<string> dlls,
IDictionary<string, ModuleVersion> dlc)
{
CompatibleVersions = crit;
Expand Down Expand Up @@ -104,8 +104,8 @@ public ICollection<CkanModule> LatestIncompatible
}

private readonly Dictionary<string, InstalledModule> installed;
private readonly HashSet<string> dlls;
private readonly IDictionary<string, ModuleVersion> dlc;
private readonly ICollection<string> dlls;
private readonly IDictionary<string, ModuleVersion> dlc;

private List<CkanModule>? latestCompatible;
private List<CkanModule>? latestIncompatible;
Expand Down
Loading

0 comments on commit 653d085

Please sign in to comment.