Skip to content

Commit

Permalink
Merge #4152 Use latest compatible instead of latest available for Ins…
Browse files Browse the repository at this point in the history
…tall checkboxes
  • Loading branch information
HebaruSan committed Aug 8, 2024
2 parents d33f716 + 9ac52ad commit be75f8e
Show file tree
Hide file tree
Showing 14 changed files with 94 additions and 100 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ All notable changes to this project will be documented in this file.
- [Multiple] New Crowdin updates (#4019 by: Olympic1, vinix38; reviewed: HebaruSan)
- [Core] Support Windows KSP1 instances on Linux (#4044 by: HebaruSan)
- [GUI] I18n updates from Crowdin (#4050 by: HebaruSan)
- [Multiple] Better version specific relationships at install and upgrade (#4023 by: HebaruSan)
- [Multiple] Better version specific relationships at install and upgrade (#4023, #4152 by: HebaruSan)
- [GUI] Proportional, granular progress updates for installing (#4055 by: HebaruSan)
- [GUI] Modpack compatibility prompt, GameComparator clean-up (#4056 by: HebaruSan)
- [ConsoleUI] Add downloads column for ConsoleUI (#4063 by: HebaruSan)
Expand Down
14 changes: 7 additions & 7 deletions Core/ModuleInstaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1514,20 +1514,20 @@ public void ImportFiles(HashSet<FileInfo> files, IUser user, Action<CkanModule>
HashSet<CkanModule> installable = new HashSet<CkanModule>();
List<FileInfo> deletable = new List<FileInfo>();
// Get the mapping of known hashes to modules
Dictionary<string, List<CkanModule>> index = registry.GetSha1Index();
var index = registry.GetDownloadHashesIndex();
int i = 0;
foreach (FileInfo f in files)
{
int percent = i * 100 / files.Count;
user.RaiseProgress(string.Format(Properties.Resources.ModuleInstallerImporting, f.Name, percent), percent);
// Calc SHA-1 sum
string sha1 = Cache.GetFileHashSha1(f.FullName, new Progress<long>(bytes => {}));
// Find SHA-1 sum in registry (potentially multiple)
if (index.ContainsKey(sha1))
// Find SHA-256 or SHA-1 sum in registry (potentially multiple)
if (index.TryGetValue(Cache.GetFileHashSha256(f.FullName, new Progress<long>(bytes => {})),
out List<CkanModule> matches)
|| index.TryGetValue(Cache.GetFileHashSha1(f.FullName, new Progress<long>(bytes => {})),
out matches))
{
deletable.Add(f);
List<CkanModule> matches = index[sha1];
foreach (CkanModule mod in matches)
foreach (var mod in matches)
{
if (mod.IsCompatible(instance.VersionCriteria()))
{
Expand Down
2 changes: 1 addition & 1 deletion Core/Net/NetFileCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ public void EnforceSizeLimit(long bytes, Registry registry)
);

// This object lets us find the modules associated with a cached file
Dictionary<string, List<CkanModule>> hashMap = registry.GetDownloadHashIndex();
var hashMap = registry.GetDownloadUrlHashIndex();

// Prune the module lists to only those that are compatible
foreach (var kvp in hashMap)
Expand Down
2 changes: 2 additions & 0 deletions Core/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -323,4 +323,6 @@ Free up space on that device or change your settings to use another location.
<data name="NetModuleCacheModuleResuming" xml:space="preserve"><value>{0} {1} ({2}, {3} remaining)</value></data>
<data name="ModuleInstallerUpgradeUpgradingResuming" xml:space="preserve"><value> * Upgrade: {0} {1} to {2} ({3}, {4} remaining)</value></data>
<data name="ModpackName" xml:space="preserve"><value>installed-{0}</value></data>
<data name="InstalledModuleToString" xml:space="preserve"><value>{0} (installed {1})</value></data>
<data name="InstalledModuleToStringAutoInstalled" xml:space="preserve"><value>{0} (installed {1}, auto-installed)</value></data>
</root>
16 changes: 11 additions & 5 deletions Core/Registry/InstalledModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ public class InstalledModule
[JsonProperty]
private Dictionary<string, InstalledModuleFile> installed_files;

public IEnumerable<string> Files => installed_files.Keys;
public string identifier => source_module.identifier;
public CkanModule Module => source_module;
public DateTime InstallTime => install_time;
public IEnumerable<string> Files => installed_files.Keys;
public string identifier => source_module.identifier;
public CkanModule Module => source_module;
public DateTime InstallTime => install_time;

public bool AutoInstalled
{
Expand Down Expand Up @@ -125,7 +125,7 @@ public void Renormalise(GameInstance ksp)
// We need case insensitive path matching on Windows
var normalised_installed_files = new Dictionary<string, InstalledModuleFile>(Platform.PathComparer);

foreach (KeyValuePair<string, InstalledModuleFile> tuple in installed_files)
foreach (var tuple in installed_files)
{
string path = CKANPathUtils.NormalizePath(tuple.Key);

Expand All @@ -141,5 +141,11 @@ public void Renormalise(GameInstance ksp)
}

#endregion

public override string ToString()
=> string.Format(AutoInstalled ? Properties.Resources.InstalledModuleToStringAutoInstalled
: Properties.Resources.InstalledModuleToString,
Module,
InstallTime);
}
}
119 changes: 49 additions & 70 deletions Core/Registry/Registry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,12 @@ private void EnlistWithTransaction()
[JsonIgnore]
private HashSet<string> untagged;

[JsonIgnore]
private Dictionary<string, List<CkanModule>> downloadHashesIndex;

[JsonIgnore]
private Dictionary<string, List<CkanModule>> downloadUrlHashIndex;

// Index of which mods provide what, format:
// providers[provided] = { provider1, provider2, ... }
// Built by BuildProvidesIndex, makes LatestAvailableWithProvides much faster.
Expand All @@ -508,10 +514,12 @@ private void InvalidateAvailableModCaches()
// These member variables hold references to data from our repo data manager
// that reflects how the available modules look to this instance.
// Clear them when we have reason to believe the upstream available modules have changed.
providers = null;
sorter = null;
tags = null;
untagged = null;
providers = null;
sorter = null;
tags = null;
untagged = null;
downloadHashesIndex = null;
downloadUrlHashIndex = null;
}

private void InvalidateInstalledCaches()
Expand Down Expand Up @@ -1069,7 +1077,7 @@ internal Dictionary<string, ProvidesModuleVersion> ProvidedByInstalled()
/// <summary>
/// <see cref = "IRegistryQuerier.InstalledVersion" />
/// </summary>
public ModuleVersion InstalledVersion(string modIdentifier, bool with_provides=true)
public ModuleVersion InstalledVersion(string modIdentifier, bool with_provides = true)
{
// If it's genuinely installed, return the details we have.
// (Includes DLCs)
Expand Down Expand Up @@ -1100,27 +1108,17 @@ public ModuleVersion InstalledVersion(string modIdentifier, bool with_provides=t
/// <see cref = "IRegistryQuerier.GetInstalledVersion" />
/// </summary>
public CkanModule GetInstalledVersion(string mod_identifier)
=> installed_modules.TryGetValue(mod_identifier, out InstalledModule installedModule)
? installedModule.Module
: null;
=> InstalledModule(mod_identifier)?.Module;

/// <summary>
/// Returns the module which owns this file, or null if not known.
/// Throws a PathErrorKraken if an absolute path is provided.
/// </summary>
public string FileOwner(string file)
{
file = CKANPathUtils.NormalizePath(file);

if (Path.IsPathRooted(file))
{
throw new PathErrorKraken(
file,
"KSPUtils.FileOwner can only work with relative paths.");
}

return installed_files.TryGetValue(file, out string fileOwner) ? fileOwner : null;
}
public InstalledModule FileOwner(string file)
=> installed_files.TryGetValue(CKANPathUtils.NormalizePath(file),
out string fileOwner)
? InstalledModule(fileOwner)
: null;

/// <summary>
/// <see cref="IRegistryQuerier.CheckSanity"/>
Expand Down Expand Up @@ -1232,71 +1230,52 @@ public IEnumerable<string> FindReverseDependencies(
satisfiedFilter);

/// <summary>
/// Get a dictionary of all mod versions indexed by their downloads' SHA-1 hash.
/// Get a dictionary of all mod versions indexed by their downloads' SHA-256 and SHA-1 hashes.
/// Useful for finding the mods for a group of files without repeatedly searching the entire registry.
/// </summary>
/// <returns>
/// dictionary[sha1] = {mod1, mod2, mod3};
/// dictionary[sha256 or sha1] = {mod1, mod2, mod3};
/// </returns>
public Dictionary<string, List<CkanModule>> GetSha1Index()
public Dictionary<string, List<CkanModule>> GetDownloadHashesIndex()
=> downloadHashesIndex = downloadHashesIndex
?? repoDataMgr.GetAllAvailableModules(repositories.Values)
.SelectMany(availMod => availMod.module_version.Values)
.SelectMany(ModWithDownloadHashes)
.GroupBy(tuple => tuple.Item1,
tuple => tuple.Item2)
.ToDictionary(grp => grp.Key,
grp => grp.ToList());

private IEnumerable<Tuple<string, CkanModule>> ModWithDownloadHashes(CkanModule m)
{
var index = new Dictionary<string, List<CkanModule>>();
foreach (var am in repoDataMgr.GetAllAvailableModules(repositories.Values))
if (!string.IsNullOrEmpty(m.download_hash?.sha256))
{
foreach (var kvp2 in am.module_version)
{
CkanModule mod = kvp2.Value;
if (mod.download_hash != null)
{
if (index.ContainsKey(mod.download_hash.sha1))
{
index[mod.download_hash.sha1].Add(mod);
}
else
{
index.Add(mod.download_hash.sha1, new List<CkanModule>() {mod});
}
}
}
yield return new Tuple<string, CkanModule>(m.download_hash.sha256, m);
}
if (!string.IsNullOrEmpty(m.download_hash?.sha1))
{
yield return new Tuple<string, CkanModule>(m.download_hash.sha1, m);
}
return index;
}

/// <summary>
/// Get a dictionary of all mod versions indexed by their download URLs' hash.
/// Get a dictionary of all mod versions indexed by their download URLs' hashes.
/// Useful for finding the mods for a group of URLs without repeatedly searching the entire registry.
/// </summary>
/// <returns>
/// dictionary[urlHash] = {mod1, mod2, mod3};
/// </returns>
public Dictionary<string, List<CkanModule>> GetDownloadHashIndex()
{
var index = new Dictionary<string, List<CkanModule>>();
foreach (var am in repoDataMgr?.GetAllAvailableModules(repositories.Values)
public Dictionary<string, List<CkanModule>> GetDownloadUrlHashIndex()
=> downloadUrlHashIndex = downloadUrlHashIndex
?? (repoDataMgr?.GetAllAvailableModules(repositories.Values)
?? Enumerable.Empty<AvailableModule>())
{
foreach (var kvp2 in am.module_version)
{
CkanModule mod = kvp2.Value;
if (mod.download != null)
{
foreach (var dlUri in mod.download)
{
string hash = NetFileCache.CreateURLHash(dlUri);
if (index.ContainsKey(hash))
{
index[hash].Add(mod);
}
else
{
index.Add(hash, new List<CkanModule>() {mod});
}
}
}
}
}
return index;
}
.SelectMany(am => am.module_version.Values)
.Where(m => m.download != null && m.download.Count > 0)
.SelectMany(m => m.download.Select(url => new Tuple<Uri, CkanModule>(url, m)))
.GroupBy(tuple => tuple.Item1,
tuple => tuple.Item2)
.ToDictionary(grp => NetFileCache.CreateURLHash(grp.Key),
grp => grp.ToList());

/// <summary>
/// Return all hosts from latest versions of all available modules,
Expand Down
4 changes: 2 additions & 2 deletions Core/Types/Kraken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,8 @@ public class FileExistsKraken : Kraken

// These aren't set at construction time, but exist so that we can decorate the
// kraken as appropriate.
public CkanModule installingModule;
public string owningModule;
public CkanModule installingModule;
public InstalledModule owningModule;

public FileExistsKraken(string filename, string reason = null, Exception innerException = null)
: base(reason, innerException)
Expand Down
5 changes: 3 additions & 2 deletions Dockerfile.metadata
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
FROM ubuntu:22.04 as base

# Don't prompt for time zone
ENV DEBIAN_FRONTEND=noninteractive
ENV DEBIAN_FRONTEND noninteractive

# Put user-installed Python code in path
ENV PATH "$PATH:/root/.local/bin"
Expand Down Expand Up @@ -33,7 +33,8 @@ RUN apt-get install -y --no-install-recommends \
python3-pip python3-setuptools python3-dev

# Install the meta tester's Python code and its Infra dep
ENV PIP_ROOT_USER_ACTION=ignore
ENV PIP_ROOT_USER_ACTION ignore
ENV PIP_BREAK_SYSTEM_PACKAGES 1
RUN pip3 install --upgrade pip
RUN pip3 install 'git+https://github.com/KSP-CKAN/NetKAN-Infra#subdirectory=netkan'
RUN pip3 install 'git+https://github.com/KSP-CKAN/xKAN-meta_testing'
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.netkan
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM ubuntu:22.04

# Don't prompt for time zone
ENV DEBIAN_FRONTEND=noninteractive
ENV DEBIAN_FRONTEND noninteractive

# Set up Mono's APT repo
RUN apt-get update \
Expand Down
8 changes: 4 additions & 4 deletions GUI/Controls/ManageMods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ public void MarkAllUpdates()
{
if (!Main.Instance.LabelsHeld(gmod.Identifier))
{
gmod.SelectedMod = gmod.LatestAvailableMod;
gmod.SelectedMod = gmod.LatestCompatibleMod;
}
}
}
Expand Down Expand Up @@ -881,7 +881,7 @@ private void ModGrid_CellValueChanged(object sender, DataGridViewCellEventArgs e
case "Installed":
gmod.SelectedMod = nowChecked ? gmod.SelectedMod
?? gmod.InstalledMod?.Module
?? gmod.LatestAvailableMod
?? gmod.LatestCompatibleMod
: null;
break;
case "UpdateCol":
Expand All @@ -890,10 +890,10 @@ private void ModGrid_CellValueChanged(object sender, DataGridViewCellEventArgs e
&& (gmod.InstalledMod == null
|| gmod.InstalledMod.Module.version < gmod.SelectedMod.version)
? gmod.SelectedMod
: gmod.LatestAvailableMod
: gmod.LatestCompatibleMod
: gmod.InstalledMod?.Module;

if (nowChecked && gmod.SelectedMod == gmod.LatestAvailableMod)
if (nowChecked && gmod.SelectedMod == gmod.LatestCompatibleMod)
{
// Reinstall, force update without change
UpdateChangeSetAndConflicts(currentInstance,
Expand Down
4 changes: 3 additions & 1 deletion GUI/Controls/ModInfoTabs/Metadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ public void UpdateModInfo(GUIMod gui_module)
MetadataModuleReleaseStatusTextBox.Text = module.release_status.ToString();
}
var compatMod = gui_module.LatestCompatibleMod ?? gui_module.LatestAvailableMod ?? gui_module.ToModule();
var compatMod = gui_module.LatestCompatibleMod
?? gui_module.LatestAvailableMod
?? gui_module.ToModule();
MetadataModuleGameCompatibilityTextBox.Text = string.Format(
Properties.Resources.GUIModGameCompatibilityLong,
gui_module.GameCompatibility,
Expand Down
4 changes: 3 additions & 1 deletion GUI/Dialogs/AskUserForAutoUpdatesDialog.resx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="autoCheckLabel.Text" xml:space="preserve"><value>Do you wish for CKAN to automatically check for updates on start-up? (Can be changed later from Settings)</value></data>
<data name="autoCheckLabel.Text" xml:space="preserve"><value>Would you like CKAN to check for new versions of CKAN automatically at start-up?

You can check for new versions of CKAN manually or change this setting later in the Settings window.</value></data>
<data name="YesButton.Text" xml:space="preserve"><value>Yes, check for updates</value></data>
<data name="NoButton.Text" xml:space="preserve"><value>No</value></data>
<data name="$this.Text" xml:space="preserve"><value>Check for updates</value></data>
Expand Down
2 changes: 1 addition & 1 deletion GUI/Main/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,7 @@ private void InstallFromCkanFiles(string[] files)
rel.ExactMatch(registry_manager.registry, crit, installed, toInstall)
// Otherwise look for incompatible
?? rel.ExactMatch(registry_manager.registry, null, installed, toInstall))
.Where(mod => mod != null));
.OfType<CkanModule>());
}
toInstall.Add(module);
}
Expand Down
Loading

0 comments on commit be75f8e

Please sign in to comment.