Skip to content

Commit

Permalink
Ability to prioritize downloads by host
Browse files Browse the repository at this point in the history
  • Loading branch information
HebaruSan committed Aug 8, 2023
1 parent be91398 commit 2d405a8
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 16 deletions.
7 changes: 7 additions & 0 deletions Core/Configuration/IConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,12 @@ public interface IConfiguration
/// Paths that should be excluded from all installations
/// </summary>
string[] GlobalInstallFilters { get; set; }

/// <summary>
/// List of hosts in order of priority when there are multiple URLs to choose from.
/// The first null value represents where all other hosts should go.
/// </summary>
/// <value></value>
string[] PreferredHosts { get; set; }
}
}
34 changes: 26 additions & 8 deletions Core/Configuration/JsonConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;

using Newtonsoft.Json;

using CKAN.Games;

namespace CKAN.Configuration
Expand All @@ -23,20 +25,16 @@ private class Config
public IList<GameInstanceEntry> GameInstances { get; set; } = new List<GameInstanceEntry>();
public IDictionary<string, string> AuthTokens { get; set; } = new Dictionary<string, string>();
public string[] GlobalInstallFilters { get; set; } = new string[] { };
public string[] PreferredHosts { get; set; } = new string[] { };
}

public class ConfigConverter : JsonPropertyNamesChangedConverter
{
protected override Dictionary<string, string> mapping
{
get
=> new Dictionary<string, string>
{
return new Dictionary<string, string>
{
{ "KspInstances", "GameInstances" }
};
}
}
{ "KspInstances", "GameInstances" }
};
}

private class GameInstanceEntry
Expand Down Expand Up @@ -326,6 +324,26 @@ public string[] GlobalInstallFilters
}
}

public string[] PreferredHosts
{
get
{
lock (_lock)
{
return config.PreferredHosts;
}
}

set
{
lock (_lock)
{
config.PreferredHosts = value;
SaveConfig();
}
}
}

// <summary>
// Save the JSON configuration file.
// </summary>
Expand Down
5 changes: 5 additions & 0 deletions Core/Configuration/Win32RegistryConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ public void SetAuthToken(string host, string token)
/// </summary>
public string[] GlobalInstallFilters { get; set; }

/// <summary>
/// Not implemented because the Windows registry is deprecated
/// </summary>
public string[] PreferredHosts { get; set; }

public static bool DoesRegistryConfigurationExist()
{
RegistryKey key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(CKAN_KEY_NO_PREFIX);
Expand Down
19 changes: 14 additions & 5 deletions Core/ModuleInstaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@ public static string Download(CkanModule module, string filename, NetModuleCache
{
log.Info("Downloading " + filename);

string tmp_file = Net.Download(module.download);
string tmp_file = Net.Download(module.download
.OrderBy(u => u,
new PreferredHostUriComparer(
ServiceLocator.Container.Resolve<IConfiguration>().PreferredHosts))
.First());

return cache.Store(module, tmp_file, new Progress<long>(bytes => {}), filename, true);
}
Expand Down Expand Up @@ -1051,14 +1055,14 @@ public void Upgrade(IEnumerable<CkanModule> modules, IDownloader netAsyncDownloa
{
User.RaiseMessage(Properties.Resources.ModuleInstallerUpgradeInstallingResuming,
module.name, module.version,
string.Join(", ", module.download.Select(dl => dl.Host).Distinct()),
string.Join(", ", PrioritizedHosts(module.download)),
CkanModule.FmtSize(module.download_size - inProgressFile.Length));
}
else
{
User.RaiseMessage(Properties.Resources.ModuleInstallerUpgradeInstallingUncached,
module.name, module.version,
string.Join(", ", module.download.Select(dl => dl.Host).Distinct()),
string.Join(", ", PrioritizedHosts(module.download)),
CkanModule.FmtSize(module.download_size));
}
}
Expand Down Expand Up @@ -1093,14 +1097,14 @@ public void Upgrade(IEnumerable<CkanModule> modules, IDownloader netAsyncDownloa
{
User.RaiseMessage(Properties.Resources.ModuleInstallerUpgradeUpgradingResuming,
module.name, installed.version, module.version,
string.Join(", ", module.download.Select(dl => dl.Host).Distinct()),
string.Join(", ", PrioritizedHosts(module.download)),
CkanModule.FmtSize(module.download_size - inProgressFile.Length));
}
else
{
User.RaiseMessage(Properties.Resources.ModuleInstallerUpgradeUpgradingUncached,
module.name, installed.version, module.version,
string.Join(", ", module.download.Select(dl => dl.Host).Distinct()),
string.Join(", ", PrioritizedHosts(module.download)),
CkanModule.FmtSize(module.download_size));
}
}
Expand Down Expand Up @@ -1229,6 +1233,11 @@ public void Replace(IEnumerable<ModuleReplacement> replacements, RelationshipRes

#endregion

public static IEnumerable<string> PrioritizedHosts(IEnumerable<Uri> urls)
=> urls.OrderBy(u => u, new PreferredHostUriComparer(ServiceLocator.Container.Resolve<IConfiguration>().PreferredHosts))
.Select(dl => dl.Host)
.Distinct();

/// <summary>
/// Makes sure all the specified mods are downloaded.
/// </summary>
Expand Down
2 changes: 2 additions & 0 deletions Core/Net/NetAsyncModulesDownloader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
using System.Threading;

using log4net;
using Autofac;

using CKAN.Configuration;
using CKAN.Extensions;

namespace CKAN
Expand Down
4 changes: 2 additions & 2 deletions Core/Net/NetModuleCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,11 @@ private static string DescribeUncachedAvailability(CkanModule m, FileInfo fi)
=> fi.Exists
? string.Format(Properties.Resources.NetModuleCacheModuleResuming,
m.name, m.version,
string.Join(", ", m.download.Select(dl => dl.Host).Distinct()),
string.Join(", ", ModuleInstaller.PrioritizedHosts(m.download)),
CkanModule.FmtSize(m.download_size - fi.Length))
: string.Format(Properties.Resources.NetModuleCacheModuleHostSize,
m.name, m.version,
string.Join(", ", m.download.Select(dl => dl.Host).Distinct()),
string.Join(", ", ModuleInstaller.PrioritizedHosts(m.download)),
CkanModule.FmtSize(m.download_size));

public string DescribeAvailability(CkanModule m)
Expand Down
29 changes: 29 additions & 0 deletions Core/Net/PreferredHostUriComparer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Linq;
using System.Collections.Generic;

namespace CKAN
{
public class PreferredHostUriComparer : IComparer<Uri>
{
public PreferredHostUriComparer(IEnumerable<string> hosts)
{
this.hosts = (hosts ?? Enumerable.Empty<string>()).ToList();
// null represents the position in the list for all other hosts
defaultPriority = this.hosts.IndexOf(null);
}

public int Compare(Uri a, Uri b)
=> GetPriority(a).CompareTo(GetPriority(b));

private int GetPriority(Uri u)
{
var index = hosts.IndexOf(u.Host);
return index == -1 ? defaultPriority
: index;
}

private readonly List<string> hosts;
private readonly int defaultPriority;
}
}
2 changes: 1 addition & 1 deletion Core/Registry/AvailableModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public void Remove(ModuleVersion version)
/// <param name="toInstall">Modules that are planned to be installed</param>
/// <returns></returns>
public CkanModule Latest(
GameVersionCriteria ksp_version = null,
GameVersionCriteria ksp_version = null,
RelationshipDescriptor relationship = null,
IEnumerable<CkanModule> installed = null,
IEnumerable<CkanModule> toInstall = null
Expand Down
2 changes: 2 additions & 0 deletions Tests/Core/Configuration/FakeConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ public string Language

public string[] GlobalInstallFilters { get; set; } = new string[] { };

public string[] PreferredHosts { get; set; } = new string[] { };

public void Dispose()
{
Directory.Delete(DownloadCacheDir, true);
Expand Down
94 changes: 94 additions & 0 deletions Tests/Core/Net/PreferredHostUriComparerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using System;
using System.Linq;

using NUnit.Framework;

using CKAN;

namespace Tests.Core.Net
{
[TestFixture]
public sealed class PreferredHostUriComparerTests
{
private static readonly Uri[] uris = new Uri[]
{
new Uri("https://taniwha.org/"),
new Uri("https://spacedock.info/"),
new Uri("https://github.com/"),
new Uri("https://archive.org/"),
};

// Reminder: null means "all other hosts"

[Test,
// Null settings
TestCase(null,
new string[]
{
"https://taniwha.org/",
"https://spacedock.info/",
"https://github.com/",
"https://archive.org/",
}),
// Empty settings
TestCase(new string[] { },
new string[]
{
"https://taniwha.org/",
"https://spacedock.info/",
"https://github.com/",
"https://archive.org/",
}),
// Irrelevant settings
TestCase(new string[] { "api.github.com", "curseforge.com", null, "www.dropbox.com", "drive.google.com" },
new string[]
{
"https://taniwha.org/",
"https://spacedock.info/",
"https://github.com/",
"https://archive.org/",
}),
// Prioritize one
TestCase(new string[] { "github.com", null },
new string[]
{
"https://github.com/",
"https://taniwha.org/",
"https://spacedock.info/",
"https://archive.org/",
}),
// De-prioritize one
TestCase(new string[] { null, "spacedock.info" },
new string[]
{
"https://taniwha.org/",
"https://github.com/",
"https://archive.org/",
"https://spacedock.info/",
}),
// Prioritize one, de-prioritize another
TestCase(new string[] { "github.com", null, "spacedock.info" },
new string[]
{
"https://github.com/",
"https://taniwha.org/",
"https://archive.org/",
"https://spacedock.info/",
}),
]
public void OrderBy_WithPreferences_SortsCorrectly(string[] preferredHosts,
string[] correctAnswer)
{
// Arrange
var comparer = new PreferredHostUriComparer(preferredHosts);

// Act
var result = uris.OrderBy(u => u, comparer)
.Select(u => u.ToString())
.ToArray();

// Assert
Assert.AreEqual(correctAnswer, result);
}
}
}

0 comments on commit 2d405a8

Please sign in to comment.