Skip to content

Commit

Permalink
Merge #4067 Treat mods with missing files as upgradeable/reinstallable
Browse files Browse the repository at this point in the history
  • Loading branch information
HebaruSan committed Mar 25, 2024
2 parents 69224fa + bd76c47 commit 76318a5
Show file tree
Hide file tree
Showing 19 changed files with 185 additions and 130 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ All notable changes to this project will be documented in this file.
- [ConsoleUI] Add downloads column for ConsoleUI (#4063 by: HebaruSan)
- [ConsoleUI] Play game option for ConsoleUI (#4064 by: HebaruSan)
- [ConsoleUI] ConsoleUI prompt to delete non-empty folders after uninstall (#4066 by: HebaruSan)
- [Multiple] Treat mods with missing files as upgradeable/reinstallable (#4067 by: HebaruSan)

### Bugfixes

Expand Down
3 changes: 1 addition & 2 deletions Cmdline/Action/List.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;

Expand Down Expand Up @@ -55,7 +54,7 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
{
var installed = new SortedDictionary<string, ModuleVersion>(registry.Installed());
var upgradeable = registry
.CheckUpgradeable(instance.VersionCriteria(), new HashSet<string>())
.CheckUpgradeable(instance, new HashSet<string>())
[true]
.Select(m => m.identifier)
.ToHashSet();
Expand Down
4 changes: 2 additions & 2 deletions Cmdline/Action/Upgrade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
if (options.upgrade_all)
{
var to_upgrade = registry
.CheckUpgradeable(instance.VersionCriteria(), new HashSet<string>())
.CheckUpgradeable(instance, new HashSet<string>())
[true];
if (to_upgrade.Count == 0)
{
Expand Down Expand Up @@ -225,7 +225,7 @@ private void UpgradeModules(GameInstanceManager manager,
.ToList();
// Modules allowed by THOSE modules' relationships
var upgradeable = registry
.CheckUpgradeable(crit, heldIdents, limiters)
.CheckUpgradeable(instance, heldIdents, limiters)
[true]
.ToDictionary(m => m.identifier,
m => m);
Expand Down
2 changes: 1 addition & 1 deletion ConsoleUI/InstallScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public override void Run(ConsoleTheme theme, Action<ConsoleTheme> process = null
}
if (plan.Upgrade.Count > 0) {
var upgGroups = registry
.CheckUpgradeable(manager.CurrentInstance.VersionCriteria(),
.CheckUpgradeable(manager.CurrentInstance,
// Hold identifiers not chosen for upgrading
registry.Installed(false)
.Keys
Expand Down
2 changes: 1 addition & 1 deletion ConsoleUI/ModInfoScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ private int addDependencies(int top = 8)
int nameW = midL - 2 - lblW - 2 - 1;
int depsH = (h - 2) * numDeps / (numDeps + numConfs);
var upgradeableGroups = registry
.CheckUpgradeable(manager.CurrentInstance.VersionCriteria(),
.CheckUpgradeable(manager.CurrentInstance,
new HashSet<string>());

AddObject(new ConsoleFrame(
Expand Down
2 changes: 1 addition & 1 deletion ConsoleUI/ModListScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ private List<CkanModule> GetAllMods(ConsoleTheme theme, bool force = false)
}
}
upgradeableGroups = registry
.CheckUpgradeable(crit, new HashSet<string>());
.CheckUpgradeable(manager.CurrentInstance, new HashSet<string>());
}
return allMods;
}
Expand Down
25 changes: 17 additions & 8 deletions Core/Registry/IRegistryQuerier.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
Expand Down Expand Up @@ -172,7 +173,7 @@ public static bool IsAutodetected(this IRegistryQuerier querier, string identifi
/// </summary>
public static bool HasUpdate(this IRegistryQuerier querier,
string identifier,
GameVersionCriteria versionCrit,
GameInstance instance,
out CkanModule latestMod,
ICollection<CkanModule> installed = null)
{
Expand All @@ -186,7 +187,7 @@ public static bool HasUpdate(this IRegistryQuerier querier,
// Check if it's available
try
{
latestMod = querier.LatestAvailable(identifier, versionCrit, null, installed);
latestMod = querier.LatestAvailable(identifier, instance.VersionCriteria(), null, installed);
}
catch
{
Expand All @@ -199,7 +200,15 @@ public static bool HasUpdate(this IRegistryQuerier querier,
// Check if the installed module is up to date
var comp = latestMod.version.CompareTo(instVer);
if (comp == -1
|| (comp == 0 && !querier.MetadataChanged(identifier)))
|| (comp == 0 && !querier.MetadataChanged(identifier)
// Check if any of the files or directories are missing
&& (instance == null
|| (querier.InstalledModule(identifier)
?.Files
.Select(instance.ToAbsoluteGameDir)
.All(p => Directory.Exists(p) || File.Exists(p))
// Manually installed, consider up to date
?? true))))
{
latestMod = null;
return false;
Expand All @@ -212,26 +221,26 @@ public static bool HasUpdate(this IRegistryQuerier querier,
}

public static Dictionary<bool, List<CkanModule>> CheckUpgradeable(this IRegistryQuerier querier,
GameVersionCriteria versionCrit,
GameInstance instance,
HashSet<string> heldIdents)
{
// Get the absolute latest versions ignoring restrictions,
// to break out of mutual version-depending deadlocks
var unlimited = querier.Installed(false)
.Keys
.Select(ident => !heldIdents.Contains(ident)
&& querier.HasUpdate(ident, versionCrit,
&& querier.HasUpdate(ident, instance,
out CkanModule latest)
&& !latest.IsDLC
? latest
: querier.GetInstalledVersion(ident))
.Where(m => m != null)
.ToList();
return querier.CheckUpgradeable(versionCrit, heldIdents, unlimited);
return querier.CheckUpgradeable(instance, heldIdents, unlimited);
}

public static Dictionary<bool, List<CkanModule>> CheckUpgradeable(this IRegistryQuerier querier,
GameVersionCriteria versionCrit,
GameInstance instance,
HashSet<string> heldIdents,
List<CkanModule> initial)
{
Expand All @@ -241,7 +250,7 @@ public static Dictionary<bool, List<CkanModule>> CheckUpgradeable(this IRegistry
foreach (var ident in initial.Select(module => module.identifier))
{
if (!heldIdents.Contains(ident)
&& querier.HasUpdate(ident, versionCrit,
&& querier.HasUpdate(ident, instance,
out CkanModule latest, initial)
&& !latest.IsDLC)
{
Expand Down
47 changes: 2 additions & 45 deletions Core/Registry/InstalledModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.ComponentModel;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Runtime.Serialization;

using Newtonsoft.Json;
Expand All @@ -12,51 +11,9 @@ namespace CKAN
[JsonObject(MemberSerialization.OptIn)]
public class InstalledModuleFile
{

// TODO: This class should also record file paths as well.
// It's just sha1 now for registry compatibility.

[JsonProperty("sha1_sum", NullValueHandling = NullValueHandling.Ignore)]
private readonly string sha1_sum;

public string Sha1 => sha1_sum;

public InstalledModuleFile(string path, GameInstance ksp)
{
string absolute_path = ksp.ToAbsoluteGameDir(path);
// TODO: What is the net performance cost of calculating this? Big files are not quick to hash!
sha1_sum = Sha1Sum(absolute_path);
}

// We need this because otherwise JSON.net tries to pass in
// our sha1's as paths, and things go wrong.
[JsonConstructor]
private InstalledModuleFile()
{
}

/// <summary>
/// Returns the sha1 sum of the given filename.
/// Returns null if passed a directory.
/// Throws an exception on failure to access the file.
/// </summary>
private static string Sha1Sum(string path)
public InstalledModuleFile()
{
if (Directory.Exists(path))
{
return null;
}

SHA1 hasher = SHA1.Create();

// Even if we throw an exception, the using block here makes sure
// we close our file.
using (var fh = File.OpenRead(path))
{
string sha1 = BitConverter.ToString(hasher.ComputeHash(fh));
fh.Close();
return sha1;
}
}
}

Expand Down Expand Up @@ -127,7 +84,7 @@ public InstalledModule(GameInstance ksp, CkanModule module, IEnumerable<string>
}

// IMF needs a KSP object so it can compute the SHA1.
installed_files[file] = new InstalledModuleFile(file, ksp);
installed_files[file] = new InstalledModuleFile();
}
}
}
Expand Down
9 changes: 7 additions & 2 deletions GUI/Controls/Changeset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ public void LoadChangeset(List<ModChange> changes,
changes?.Select(ch => new ChangesetRow(ch, AlertLabels, conflicts))
.ToList()
?? new List<ChangesetRow>());
ChangesGrid.AutoResizeColumns();
}

public CkanModule SelectedItem => SelectedRow?.Change.Mod;
Expand Down Expand Up @@ -70,6 +69,7 @@ private void ChangesGrid_DataBindingComplete(object sender, DataGridViewBindingC
}
}
}
ChangesGrid.AutoResizeColumns();
}

private void ChangesGrid_CellClick(object sender, DataGridViewCellEventArgs e)
Expand Down Expand Up @@ -101,8 +101,13 @@ private void BackButton_Click(object sender, EventArgs e)

private void ChangesGrid_SelectionChanged(object sender, EventArgs e)
{
if (ChangesGrid.SelectedRows.Count > 0 && !Visible)
{
// Suppress selection while inactive
ChangesGrid.ClearSelection();
}
// Don't pop up mod info when they click the X icons
if (ChangesGrid.CurrentCell?.OwningColumn is DataGridViewTextBoxColumn)
else if (ChangesGrid.CurrentCell?.OwningColumn is DataGridViewTextBoxColumn)
{
OnSelectedItemsChanged?.Invoke(SelectedRow?.Change.Mod);
}
Expand Down
1 change: 0 additions & 1 deletion GUI/Controls/ChooseRecommendedMods.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 76318a5

Please sign in to comment.