Skip to content

Commit

Permalink
Merge #3881 Caching and changeset fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
HebaruSan committed Aug 16, 2023
2 parents 9d0b75e + 8d478fb commit 1a884fe
Show file tree
Hide file tree
Showing 12 changed files with 236 additions and 207 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -19,6 +19,7 @@ All notable changes to this project will be documented in this file.
- [Multiple] Repository management fixes (#3876 by: HebaruSan; reviewed: techman83)
- [GUI] Restore window position without default instance (#3878 by: HebaruSan; reviewed: techman83)
- [CLI] Correctly print cmdline errors with braces (#3880 by: HebaruSan; reviewed: techman83)
- [Multiple] Caching and changeset fixes (#3881 by: HebaruSan; reviewed: techman83)

### Internal

Expand Down
33 changes: 1 addition & 32 deletions Core/Net/NetAsyncModulesDownloader.cs
Expand Up @@ -42,37 +42,6 @@ public NetAsyncModulesDownloader(IUser user, NetModuleCache cache)
this.cache = cache;
}

internal static List<HashSet<CkanModule>> GroupByDownloads(IEnumerable<CkanModule> modules)
{
// Each module is a vertex, each download URL is an edge
// We want to group the vertices by transitive connectedness
// We can go breadth first or depth first
// Once we encounter a mod, we never have to look at it again
var unsearched = modules.ToHashSet();
var groups = new List<HashSet<CkanModule>>();
while (unsearched.Count > 0)
{
// Find one group, remove it from unsearched, add it to groups
var searching = new List<CkanModule> { unsearched.First() };
unsearched.ExceptWith(searching);
var found = searching.ToHashSet();
// Breadth first search to find all modules any URLs in common, transitively
while (searching.Count > 0)
{
var origin = searching.First();
searching.Remove(origin);
var neighbors = origin.download
.SelectMany(dlUri => unsearched.Where(other => other.download.Contains(dlUri)))
.ToHashSet();
unsearched.ExceptWith(neighbors);
searching.AddRange(neighbors);
found.UnionWith(neighbors);
}
groups.Add(found);
}
return groups;
}

internal Net.DownloadTarget TargetFromModuleGroup(HashSet<CkanModule> group,
string[] preferredHosts)
=> TargetFromModuleGroup(group, group.OrderBy(m => m.identifier).First(), preferredHosts);
Expand Down Expand Up @@ -102,7 +71,7 @@ public void DownloadModules(IEnumerable<CkanModule> modules)
{
var activeURLs = this.modules.SelectMany(m => m.download)
.ToHashSet();
var moduleGroups = GroupByDownloads(modules);
var moduleGroups = CkanModule.GroupByDownloads(modules);
// Make sure we have enough space to download and cache
cache.CheckFreeSpace(moduleGroups.Select(grp => grp.First().download_size)
.Sum());
Expand Down
11 changes: 3 additions & 8 deletions Core/Registry/Registry.cs
Expand Up @@ -487,10 +487,7 @@ public void SetAllAvailable(IEnumerable<CkanModule> newAvail)
/// <returns>
/// True if we have at least one available mod, false otherwise.
/// </returns>
public bool HasAnyAvailable()
{
return available_modules.Count > 0;
}
public bool HasAnyAvailable() => available_modules.Count > 0;

/// <summary>
/// Mark a given module as available.
Expand Down Expand Up @@ -643,11 +640,9 @@ public string GetAvailableMetadata(string identifier)
/// </summary>
/// <param name="identifier">Name of mod to check</param>
public GameVersion LatestCompatibleKSP(string identifier)
{
return available_modules.ContainsKey(identifier)
? available_modules[identifier].LatestCompatibleKSP()
=> available_modules.TryGetValue(identifier, out AvailableModule availMod)
? availMod.LatestCompatibleKSP()
: null;
}

/// <summary>
/// Find the minimum and maximum mod versions and compatible game versions
Expand Down
41 changes: 41 additions & 0 deletions Core/Types/CkanModule.cs
Expand Up @@ -6,10 +6,13 @@
using System.Runtime.Serialization;
using System.Text;
using System.Text.RegularExpressions;

using Autofac;
using log4net;
using Newtonsoft.Json;

using CKAN.Versioning;
using CKAN.Extensions;
using CKAN.Games;

namespace CKAN
Expand Down Expand Up @@ -806,6 +809,44 @@ public static string FmtSize(long bytes)
: bytes < K*K*K*K ? $"{bytes /K/K/K :N1} GiB"
: $"{bytes /K/K/K/K :N1} TiB";

public HashSet<CkanModule> GetDownloadsGroup(IEnumerable<CkanModule> modules)
=> OneDownloadGroupingPass(modules.ToHashSet(), this);

public static List<HashSet<CkanModule>> GroupByDownloads(IEnumerable<CkanModule> modules)
{
// Each module is a vertex, each download URL is an edge
// We want to group the vertices by transitive connectedness
// We can go breadth first or depth first
// Once we encounter a mod, we never have to look at it again
var unsearched = modules.ToHashSet();
var groups = new List<HashSet<CkanModule>>();
while (unsearched.Count > 0)
{
groups.Add(OneDownloadGroupingPass(unsearched, unsearched.First()));
}
return groups;
}

private static HashSet<CkanModule> OneDownloadGroupingPass(HashSet<CkanModule> unsearched,
CkanModule firstModule)
{
var searching = new List<CkanModule> { firstModule };
unsearched.ExceptWith(searching);
var found = searching.ToHashSet();
// Breadth first search to find all modules with any URLs in common, transitively
while (searching.Count > 0)
{
var origin = searching.First();
searching.Remove(origin);
var neighbors = origin.download
.SelectMany(dlUri => unsearched.Where(other => other.download.Contains(dlUri)))
.ToHashSet();
unsearched.ExceptWith(neighbors);
searching.AddRange(neighbors);
found.UnionWith(neighbors);
}
return found;
}
}

public class InvalidModuleAttributesException : Exception
Expand Down
44 changes: 26 additions & 18 deletions GUI/Controls/Changeset.cs
Expand Up @@ -20,29 +20,19 @@ public Changeset()
List<ModuleLabel> AlertLabels,
Dictionary<CkanModule, string> conflicts)
{
changeset = changes;
alertLabels = AlertLabels;
ChangesListView.Items.Clear();
if (changes != null)
{
// Changeset sorting is handled upstream in the resolver
ChangesListView.Items.AddRange(changes
.Where(ch => ch.ChangeType != GUIModChangeType.None)
.Select(ch => makeItem(ch, conflicts))
.ToArray());
ChangesListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
ChangesListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
}
changeset = changes;
alertLabels = AlertLabels;
this.conflicts = conflicts;
ConfirmChangesButton.Enabled = conflicts == null || !conflicts.Any();
}

protected override void OnVisibleChanged(EventArgs e)
{
base.OnVisibleChanged(e);
if (Visible && Platform.IsMono)
if (Visible)
{
// Workaround: make sure the ListView headers are drawn
Util.Invoke(ChangesListView, () => ChangesListView.EndUpdate());
// Update list on each refresh in case caching changed
UpdateList();
}
}

Expand All @@ -54,6 +44,23 @@ public ListView.SelectedListViewItemCollection SelectedItems
public event Action<List<ModChange>> OnConfirmChanges;
public event Action<bool> OnCancelChanges;

private void UpdateList()
{
ChangesListView.BeginUpdate();
ChangesListView.Items.Clear();
if (changeset != null)
{
// Changeset sorting is handled upstream in the resolver
ChangesListView.Items.AddRange(changeset
.Where(ch => ch.ChangeType != GUIModChangeType.None)
.Select(ch => makeItem(ch, conflicts))
.ToArray());
ChangesListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
ChangesListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
}
ChangesListView.EndUpdate();
}

private void ChangesListView_SelectedIndexChanged(object sender, EventArgs e)
{
OnSelectedItemsChanged?.Invoke(ChangesListView.SelectedItems);
Expand Down Expand Up @@ -101,7 +108,8 @@ private ListViewItem makeItem(ModChange change, Dictionary<CkanModule, string> c
};
}

private List<ModChange> changeset;
private List<ModuleLabel> alertLabels;
private List<ModChange> changeset;
private List<ModuleLabel> alertLabels;
private Dictionary<CkanModule, string> conflicts;
}
}
44 changes: 30 additions & 14 deletions GUI/Controls/ManageMods.cs
Expand Up @@ -31,7 +31,9 @@ public ManageMods()
FilterNotInstalledButton.ToolTipText = Properties.Resources.FilterLinkToolTip;
FilterIncompatibleButton.ToolTipText = Properties.Resources.FilterLinkToolTip;

mainModList = new ModList(source => UpdateFilters());
mainModList = new ModList();
mainModList.ModFiltersUpdated += UpdateFilters;
UpdateFilters();
FilterToolButton.MouseHover += (sender, args) => FilterToolButton.ShowDropDown();
launchGameToolStripMenuItem.MouseHover += (sender, args) => launchGameToolStripMenuItem.ShowDropDown();
ApplyToolButton.MouseHover += (sender, args) => ApplyToolButton.ShowDropDown();
Expand Down Expand Up @@ -929,8 +931,9 @@ public void FocusMod(string key, bool exactMatch, bool showAsFirst = false)
});

ModGrid.ClearSelection();
var rows = ModGrid.Rows.Cast<DataGridViewRow>().Where(row => row.Visible);
DataGridViewRow match = rows.FirstOrDefault(does_name_begin_with_key);
DataGridViewRow match = ModGrid.Rows.Cast<DataGridViewRow>()
.Where(row => row.Visible)
.FirstOrDefault(does_name_begin_with_key);
if (match == null && first_match != null)
{
// If there were no matches after the first match, cycle over to the beginning.
Expand Down Expand Up @@ -1031,6 +1034,13 @@ private void reinstallToolStripMenuItem_Click(object sender, EventArgs e)
.ToList());
}

public Dictionary<string, GUIMod> AllGUIMods()
=> ModGrid.Rows.Cast<DataGridViewRow>()
.Select(row => row.Tag as GUIMod)
.Where(guiMod => guiMod != null)
.ToDictionary(guiMod => guiMod.Identifier,
guiMod => guiMod);

private void purgeContentsToolStripMenuItem_Click(object sender, EventArgs e)
{
// Purge other versions as well since the user is likely to want that
Expand All @@ -1044,7 +1054,18 @@ private void purgeContentsToolStripMenuItem_Click(object sender, EventArgs e)
{
Main.Instance.Manager.Cache.Purge(mod);
}
selected.UpdateIsCached();

// Update all mods that share the same ZIP
var allGuiMods = AllGUIMods();
foreach (var otherMod in selected.ToModule().GetDownloadsGroup(
allGuiMods.Values.Select(guiMod => guiMod.ToModule())))
{
allGuiMods[otherMod.identifier].UpdateIsCached();
}

// Reapply searches in case is:cached or not:cached is active
UpdateFilters();

Main.Instance.RefreshModContentsTree();
}
}
Expand All @@ -1067,7 +1088,7 @@ private void EditModSearches_SurrenderFocus()
Util.Invoke(this, () => ModGrid.Focus());
}

private void UpdateFilters()
public void UpdateFilters()
{
Util.Invoke(this, _UpdateFilters);
}
Expand Down Expand Up @@ -1503,15 +1524,10 @@ public void ResetFilterAndSelectModOnList(string key)
FocusMod(key, true);
}

public GUIMod SelectedModule
{
get
{
return ModGrid.SelectedRows.Count == 0
? null
: ModGrid.SelectedRows[0]?.Tag as GUIMod;
}
}
public GUIMod SelectedModule =>
ModGrid.SelectedRows.Count == 0
? null
: ModGrid.SelectedRows[0]?.Tag as GUIMod;

#region Navigation History

Expand Down
26 changes: 22 additions & 4 deletions GUI/Main/MainDownload.cs
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
Expand Down Expand Up @@ -26,7 +27,7 @@ public void StartDownload(GUIMod module)
{
// Just pass to the existing worker
downloader.DownloadModules(new List<CkanModule> { module.ToCkanModule() });
module.UpdateIsCached();
UpdateCachedByDownloads(module);
});
}
else
Expand Down Expand Up @@ -82,13 +83,30 @@ public void PostModCaching(object sender, RunWorkerCompletedEventArgs e)
}
}

private void UpdateCachedByDownloads(GUIMod module)
{
// Update all mods that share the same ZIP
var allGuiMods = ManageMods.AllGUIMods();
foreach (var otherMod in module.ToModule().GetDownloadsGroup(
allGuiMods.Values.Select(guiMod => guiMod.ToModule())))
{
allGuiMods[otherMod.identifier].UpdateIsCached();
}
}

private void _PostModCaching(GUIMod module)
{
module.UpdateIsCached();
// Update mod list in case is:cached or not:cached filters are active
RefreshModList();
UpdateCachedByDownloads(module);

// Reapply searches in case is:cached or not:cached is active
ManageMods.UpdateFilters();

// User might have selected another row. Show current in tree.
RefreshModContentsTree();

// Close progress tab and switch back to mod list
HideWaitDialog();
EnableMainWindow();
}
}
}

0 comments on commit 1a884fe

Please sign in to comment.