Skip to content

Commit

Permalink
Merge #4249 Start installing mods while downloads are still in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
HebaruSan committed Nov 6, 2024
2 parents 54bdf68 + 987df51 commit 49c79d2
Show file tree
Hide file tree
Showing 62 changed files with 1,511 additions and 746 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
- [GUI] Minor tray icon improvements (#4231 by: HebaruSan)
- [Multiple] Translation updates from Crowdin (#4233 by: vixnig38, ambition, tinygrox; reviewed: HebaruSan)
- [Multiple] Cache migration and other fixes (#4240 by: HebaruSan)
- [Multiple] Start installing mods while downloads are still in progress (#4249 by: HebaruSan)

### Bugfixes

Expand Down
29 changes: 26 additions & 3 deletions Cmdline/Action/Available.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

using CommandLine;

Expand All @@ -17,11 +19,25 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
AvailableOptions opts = (AvailableOptions)raw_options;
IRegistryQuerier registry = RegistryManager.Instance(instance, repoData).registry;

var compatible = registry
IEnumerable<CkanModule> compatible = registry
.CompatibleModules(instance.VersionCriteria())
.Where(m => !m.IsDLC)
.OrderBy(m => m.identifier);

if (opts.globs != null)
{
var regexes = opts.globs
.Select(str => new Regex("^"
+ str.Replace("?", ".")
.Replace("*", ".*")
+ "$",
RegexOptions.Compiled
| RegexOptions.IgnoreCase))
.ToArray();
compatible = compatible.Where(m => regexes.Any(re => re.IsMatch(m.identifier)
|| re.IsMatch(m.name)));
}

user.RaiseMessage(Properties.Resources.AvailableHeader,
instance.game.ShortName,
instance.Version()?.ToString() ?? "");
Expand All @@ -31,14 +47,18 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
{
foreach (CkanModule module in compatible)
{
user.RaiseMessage("* {0} ({1}) - {2} - {3}", module.identifier, module.version, module.name, module.@abstract);
user.RaiseMessage("* {0} ({1}) - {2} - {3}",
module.identifier, module.version,
module.name, module.@abstract);
}
}
else
{
foreach (CkanModule module in compatible)
{
user.RaiseMessage("* {0} ({1}) - {2}", module.identifier, module.version, module.name);
user.RaiseMessage("* {0} ({1}) - {2}",
module.identifier, module.version,
module.name);
}
}

Expand All @@ -53,6 +73,9 @@ internal class AvailableOptions : InstanceSpecificOptions
{
[Option("detail", HelpText = "Show short description of each module")]
public bool detail { get; set; }

[ValueList(typeof(List<string>))]
public List<string>? globs { get; set; }
}

}
2 changes: 1 addition & 1 deletion Cmdline/Action/Install.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
.Select(arg => new NetAsyncDownloader.DownloadTargetFile(getUri(arg)))
.ToArray();
log.DebugFormat("Urls: {0}", targets.SelectMany(t => t.urls));
new NetAsyncDownloader(new NullUser(), options.NetUserAgent).DownloadAndWait(targets);
new NetAsyncDownloader(new NullUser(), () => null, options.NetUserAgent).DownloadAndWait(targets);
log.DebugFormat("Files: {0}", targets.Select(t => t.filename));
modules = targets.Select(t => MainClass.LoadCkanFromFile(t.filename))
.ToList();
Expand Down
6 changes: 4 additions & 2 deletions Cmdline/Action/Update.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ private void UpdateRepositories(CKAN.GameInstance instance, string? userAgent, b
var registry = RegistryManager.Instance(instance, repoData).registry;
var result = repoData.Update(registry.Repositories.Values.ToArray(),
instance.game, force,
new NetAsyncDownloader(user, userAgent), user, userAgent);
new NetAsyncDownloader(user, () => null, userAgent), user, userAgent);
if (result == RepositoryDataManager.UpdateResult.Updated)
{
user.RaiseMessage(Properties.Resources.UpdateSummary,
Expand All @@ -197,7 +197,9 @@ private void UpdateRepositories(CKAN.GameInstance instance, string? userAgent, b
/// <param name="repos">Repositories to update</param>
private void UpdateRepositories(IGame game, Repository[] repos, string? userAgent, bool force = false)
{
var result = repoData.Update(repos, game, force, new NetAsyncDownloader(user, userAgent), user, userAgent);
var result = repoData.Update(repos, game, force,
new NetAsyncDownloader(user, () => null, userAgent),
user, userAgent);
if (result == RepositoryDataManager.UpdateResult.Updated)
{
user.RaiseMessage(Properties.Resources.UpdateSummary,
Expand Down
22 changes: 11 additions & 11 deletions Cmdline/Action/Upgrade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,18 +184,18 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
/// <param name="user">IUser object for output</param>
/// <param name="instance">Game instance to use</param>
/// <param name="modules">List of modules to upgrade</param>
private void UpgradeModules(NetModuleCache cache,
string? userAgent,
IUser user,
CKAN.GameInstance instance,
List<CkanModule> modules)
private void UpgradeModules(NetModuleCache cache,
string? userAgent,
IUser user,
CKAN.GameInstance instance,
List<CkanModule> modules)
{
UpgradeModules(
cache, userAgent, user, instance, repoData,
(ModuleInstaller installer, NetAsyncModulesDownloader downloader, RegistryManager regMgr, ref HashSet<string>? possibleConfigOnlyDirs) =>
installer.Upgrade(modules, downloader,
ref possibleConfigOnlyDirs,
regMgr, true, true, true),
regMgr, true, true),
modules.Add);
}

Expand All @@ -206,11 +206,11 @@ private void UpgradeModules(NetModuleCache cache,
/// <param name="user">IUser object for output</param>
/// <param name="instance">Game instance to use</param>
/// <param name="identsAndVersions">List of identifier[=version] to upgrade</param>
private void UpgradeModules(NetModuleCache cache,
string? userAgent,
IUser user,
CKAN.GameInstance instance,
List<string> identsAndVersions)
private void UpgradeModules(NetModuleCache cache,
string? userAgent,
IUser user,
CKAN.GameInstance instance,
List<string> identsAndVersions)
{
UpgradeModules(
cache, userAgent, user, instance, repoData,
Expand Down
22 changes: 7 additions & 15 deletions Cmdline/ConsoleUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -258,29 +258,21 @@ public void RaiseProgress(string message, int percent)
{
if (message != lastProgressMessage)
{
// The percent looks weird on non-download messages.
// The leading newline makes sure we don't end up with a mess from previous
// download messages.
GoToStartOfLine();
Console.Write("{0}", message);
// The percent looks weird on non-download messages
Console.WriteLine("{0}", message);
lastProgressMessage = message;
}

// This message leaves the cursor at the end of a line of text
atStartOfLine = false;
atStartOfLine = true;
}

public void RaiseProgress(int percent, long bytesPerSecond, long bytesLeft)
public void RaiseProgress(ByteRateCounter rateCounter)
{
if (!Headless || percent != previousPercent)
if (!Headless || rateCounter.Percent != previousPercent)
{
GoToStartOfLine();
var fullMsg = string.Format(CKAN.Properties.Resources.NetAsyncDownloaderProgress,
CkanModule.FmtSize(bytesPerSecond),
CkanModule.FmtSize(bytesLeft));
// The \r at the front here causes download messages to *overwrite* each other.
Console.Write("\r{0} - {1}% ", fullMsg, percent);
previousPercent = percent;
Console.Write("\r{0} ", rateCounter.Summary);
previousPercent = rateCounter.Percent;

// This message leaves the cursor at the end of a line of text
atStartOfLine = false;
Expand Down
4 changes: 3 additions & 1 deletion Cmdline/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,12 @@ public static IEnumerable<string> GetHelp(string verb)
case "import":
yield return $"{Properties.Resources.Usage}: ckan {verb} [{Properties.Resources.Options}] path [path2 ...]";
break;
case "available":
yield return $"{Properties.Resources.Usage}: ckan {verb} [{Properties.Resources.Options}] [glob ...]";
break;

// Commands with only --flag type options
case "gui":
case "available":
case "list":
case "update":
case "scan":
Expand Down
4 changes: 2 additions & 2 deletions ConsoleUI/InstallScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public override void Run(Action? process = null)
HashSet<string>? possibleConfigOnlyDirs = null;

ModuleInstaller inst = new ModuleInstaller(manager.CurrentInstance, manager.Cache, this, userAgent);
inst.onReportModInstalled += OnModInstalled;
inst.OneComplete += OnModInstalled;
if (plan.Remove.Count > 0) {
inst.UninstallList(plan.Remove, ref possibleConfigOnlyDirs, regMgr, true, new List<CkanModule>(plan.Install));
plan.Remove.Clear();
Expand Down Expand Up @@ -113,7 +113,7 @@ public override void Run(Action? process = null)
}

trans.Complete();
inst.onReportModInstalled -= OnModInstalled;
inst.OneComplete -= OnModInstalled;
// Don't let the installer re-use old screen references
inst.User = new NullUser();

Expand Down
2 changes: 1 addition & 1 deletion ConsoleUI/ModListScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ private bool UpdateRegistry(bool showNewModsPrompt = true)
repoData.Update(registry.Repositories.Values.ToArray(),
manager.CurrentInstance.game,
false,
new NetAsyncDownloader(ps, userAgent),
new NetAsyncDownloader(ps, () => null, userAgent),
ps,
userAgent);
} catch (Exception ex) {
Expand Down
2 changes: 1 addition & 1 deletion ConsoleUI/SplashScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public bool Run(ConsoleTheme theme)
if (ksp != null
&& !GameInstanceListScreen.TryGetInstance(theme, ksp, repoData,
(ConsoleTheme th) => Draw(th, false),
new Progress<int>(p => drawProgressBar(theme, 22, 20, p)))) {
new ProgressImmediate<int>(p => drawProgressBar(theme, 22, 20, p)))) {
Console.ResetColor();
Console.Clear();
Console.CursorVisible = true;
Expand Down
4 changes: 2 additions & 2 deletions ConsoleUI/Toolkit/ConsoleFileMultiSelectDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ public ConsoleFileMultiSelectDialog(ConsoleTheme theme,
return false;
});

AddTip("F10", Properties.Resources.Sort);
AddBinding(Keys.F10, (object sender) => {
AddTip(ConsoleScreen.MainMenuKeyTip, Properties.Resources.Sort);
AddBinding(ConsoleScreen.MainMenuKeys, (object sender) => {
fileList.SortMenu().Run(theme, right - 2, top + 2);
DrawBackground();
return true;
Expand Down
30 changes: 20 additions & 10 deletions ConsoleUI/Toolkit/ConsoleScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ protected ConsoleScreen(ConsoleTheme theme)
: base(theme)
{
AddTip(
"F10", MenuTip(),
MainMenuKeyTip, MenuTip(),
() => mainMenu != null
);
AddBinding(new ConsoleKeyInfo[] {Keys.F10, Keys.Apps}, (object sender) => {
AddBinding(MainMenuKeys, (object sender) => {
bool val = true;
if (mainMenu != null) {
DrawSelectedHamburger();
Expand All @@ -47,6 +47,21 @@ protected void LaunchSubScreen(ConsoleScreen cs, Action? newProc = null)
Draw();
}

/// <summary>
/// String that describes which key to press to open the main menu
/// MacOS has a conflicting default key bind for F10 (the CUA standard for opening the menu)
/// </summary>
public static readonly string MainMenuKeyTip = Platform.IsMac ? "Ctrl+T" : "F10";

/// <summary>
/// Keys that open the main menu
/// </summary>
public static readonly ConsoleKeyInfo[] MainMenuKeys = new ConsoleKeyInfo[] {
Keys.F10,
Keys.CtrlT,
Keys.Apps,
};

/// <summary>
/// Function returning text to be shown at the left edge of the top header bar
/// </summary>
Expand Down Expand Up @@ -208,15 +223,10 @@ public void RaiseProgress(string message, int percent)
/// <summary>
/// Update a user visible progress bar
/// </summary>
/// <param name="percent">Value 0-100 representing the progress</param>
/// <param name="bytesPerSecond">Current download rate</param>
/// <param name="bytesLeft">Bytes remaining in the downloads</param>
public void RaiseProgress(int percent, long bytesPerSecond, long bytesLeft)
/// <param name="rateCounter">Object with the progress info</param>
public void RaiseProgress(ByteRateCounter rateCounter)
{
var fullMsg = string.Format(CKAN.Properties.Resources.NetAsyncDownloaderProgress,
CkanModule.FmtSize(bytesPerSecond),
CkanModule.FmtSize(bytesLeft));
Progress(fullMsg, percent);
Progress(rateCounter.Summary, rateCounter.Percent);
Draw();
}

Expand Down
7 changes: 7 additions & 0 deletions ConsoleUI/Toolkit/Keys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,13 @@ public static class Keys {
(char)18, ConsoleKey.R, false, false, true
);

/// <summary>
/// Representation of Ctrl+R for key bindings
/// </summary>
public static readonly ConsoleKeyInfo CtrlT = new ConsoleKeyInfo(
(char)20, ConsoleKey.T, false, false, true
);

/// <summary>
/// Representation of Ctrl+U for key bindings
/// </summary>
Expand Down
9 changes: 4 additions & 5 deletions Core/CKANPathUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,15 @@ public static void CheckFreeSpace(DirectoryInfo where,
{
if (bytesToStore > 0)
{
var bytesFree = where.GetDrive()?.AvailableFreeSpace;
if (bytesFree.HasValue && bytesToStore > bytesFree.Value) {
var bytesFree = where.GetDrive().AvailableFreeSpace;
if (bytesToStore > bytesFree) {
throw new NotEnoughSpaceKraken(errorDescription, where,
bytesFree.Value, bytesToStore);
bytesFree, bytesToStore);
}
log.DebugFormat("Storing {0} to {1} ({2} free)...",
CkanModule.FmtSize(bytesToStore),
where.FullName,
bytesFree.HasValue ? CkanModule.FmtSize(bytesFree.Value)
: "unknown bytes");
CkanModule.FmtSize(bytesFree));
}
}

Expand Down
14 changes: 12 additions & 2 deletions Core/Extensions/CryptoExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ public static byte[] ComputeHash(this HashAlgorithm hashAlgo,
Stream stream,
IProgress<int>? progress,
CancellationToken? cancelToken = default)
{
PartialHash(hashAlgo, stream, progress, cancelToken);
var buffer = new byte[16];
hashAlgo.TransformFinalBlock(buffer, 0, 0);
return hashAlgo.Hash ?? Array.Empty<byte>();
}

public static void PartialHash(this HashAlgorithm hashAlgo,
Stream stream,
IProgress<int>? progress,
CancellationToken? cancelToken = default)
{
const int bufSize = 1024 * 1024;
var buffer = new byte[bufSize];
Expand All @@ -36,7 +47,7 @@ public static byte[] ComputeHash(this HashAlgorithm hashAlgo,
if (bytesRead < bufSize)
{
// Done!
hashAlgo.TransformFinalBlock(buffer, 0, bytesRead);
hashAlgo.TransformBlock(buffer, 0, bytesRead, buffer, 0);
progress?.Report(100);
break;
}
Expand All @@ -48,7 +59,6 @@ public static byte[] ComputeHash(this HashAlgorithm hashAlgo,
totalBytesRead += bytesRead;
progress?.Report((int)(100 * totalBytesRead / stream.Length));
}
return hashAlgo.Hash ?? Array.Empty<byte>();
}
}
}
9 changes: 9 additions & 0 deletions Core/Extensions/DictionaryExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Linq;
using System.Collections.Generic;

Expand All @@ -23,5 +24,13 @@ public static bool DictionaryEquals<K, V>(this IDictionary<K, V>? a,
return val;
}

public static IEnumerable<Tuple<K, V1, V2>> KeyZip<K, V1, V2>(this IDictionary<K, V1> source,
IDictionary<K, V2> other)
where K : notnull
=> source.Select(kvp => other.TryGetValue(kvp.Key, out V2? val2)
? Tuple.Create(kvp.Key, kvp.Value, val2)
: null)
.OfType<Tuple<K, V1, V2>>();

}
}
Loading

0 comments on commit 49c79d2

Please sign in to comment.