Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use latest compatible instead of latest available for Install checkboxes #4152

Merged
merged 5 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
10 changes: 6 additions & 4 deletions GUI/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,10 @@ https://github.com/KSP-CKAN/NetKAN/issues/new/choose

Please include the following information in your report:

File : {0}
Installing Mod : {1}
File: {0}
Installing Mod: {1}
Owning Mod: {2}
CKAN Version : {3}</value></data>
CKAN Version: {3}</value></data>
<data name="MainInstallUnownedFileExists" xml:space="preserve"><value>Oh no!

It looks like you're trying to install a mod which is already installed,
Expand Down Expand Up @@ -329,7 +329,9 @@ If you suspect a bug in the client: https://github.com/KSP-CKAN/CKAN/issues/new/
<data name="MainRepoFailed" xml:space="preserve"><value>Repository update failed!</value></data>
<data name="MainRepoSuccess" xml:space="preserve"><value>Repositories successfully updated.</value></data>
<data name="MainRepoOutdatedClient" xml:space="preserve"><value>Repositories updated, but found modules that require a new version of CKAN. Checking for updates...</value></data>
<data name="MainRepoAutoRefreshPrompt" xml:space="preserve"><value>Would you like CKAN to refresh the modlist every time it is loaded? (You can always manually refresh using the button up top.)</value></data>
<data name="MainRepoAutoRefreshPrompt" xml:space="preserve"><value>Would you like CKAN to refresh the mod list automatically every time it is loaded?

You can refresh the mod list manually with the Refresh button at the top, and you can change this setting later in the Settings. If you disable automatic refreshes, it's a good idea to refresh every few days.</value></data>
<data name="MainRepoBalloonTipDetails" xml:space="preserve"><value>{0} update(s) available</value></data>
<data name="MainRepoBalloonTipTooltip" xml:space="preserve"><value>Click to upgrade</value></data>
<data name="MainTrayIconResume" xml:space="preserve"><value>Resume</value></data>
Expand Down