From d659cc452ac277dd0580f4faecfb8b3d8c46c546 Mon Sep 17 00:00:00 2001 From: fgsfds <4870330+fgsfds@users.noreply.github.com> Date: Fri, 31 May 2024 10:02:36 +0500 Subject: [PATCH] added support for multiple versions --- .../Core/Controls/DownloadsControl.axaml | 2 +- src/Avalonia/Core/ViewModels/ModsViewModel.cs | 4 +- src/ClientCommon/Config/ConfigProvider.cs | 7 +- src/ClientCommon/Config/ConfigProviderFake.cs | 5 +- src/ClientCommon/Config/IConfigProvider.cs | 5 +- src/Common/AddonVersion.cs | 28 +++ src/Common/Interfaces/IGame.cs | 6 +- .../Interfaces/IInstalledAddonsProvider.cs | 6 +- src/Games/Games/BaseGame.cs | 11 +- src/Games/Games/BloodGame.cs | 9 +- src/Games/Games/DukeGame.cs | 17 +- src/Games/Games/FuryGame.cs | 7 +- src/Games/Games/RedneckGame.cs | 11 +- src/Games/Games/SlaveGame.cs | 7 +- src/Games/Games/WangGame.cs | 11 +- .../Providers/DownloadableAddonsProvider.cs | 221 ++++++++++-------- src/Mods/Providers/InstalledAddonsProvider.cs | 30 +-- src/Ports/Ports/BasePort.cs | 7 +- 18 files changed, 231 insertions(+), 163 deletions(-) create mode 100644 src/Common/AddonVersion.cs diff --git a/src/Avalonia/Core/Controls/DownloadsControl.axaml b/src/Avalonia/Core/Controls/DownloadsControl.axaml index a1230ad9..9cb84766 100644 --- a/src/Avalonia/Core/Controls/DownloadsControl.axaml +++ b/src/Avalonia/Core/Controls/DownloadsControl.axaml @@ -59,8 +59,8 @@ - + diff --git a/src/Avalonia/Core/ViewModels/ModsViewModel.cs b/src/Avalonia/Core/ViewModels/ModsViewModel.cs index ef63a821..e76c2bb2 100644 --- a/src/Avalonia/Core/ViewModels/ModsViewModel.cs +++ b/src/Avalonia/Core/ViewModels/ModsViewModel.cs @@ -139,11 +139,11 @@ private void ModCheckboxPressed(object? obj) if (!mod.IsEnabled) { - Game.InstalledAddonsProvider.DisableAddon(mod.Id); + Game.InstalledAddonsProvider.DisableAddon(new(mod.Id, mod.Version)); } else if (mod.IsEnabled) { - Game.InstalledAddonsProvider.EnableAddon(mod.Id); + Game.InstalledAddonsProvider.EnableAddon(new(mod.Id, mod.Version)); } } diff --git a/src/ClientCommon/Config/ConfigProvider.cs b/src/ClientCommon/Config/ConfigProvider.cs index b29e34f0..b22927c3 100644 --- a/src/ClientCommon/Config/ConfigProvider.cs +++ b/src/ClientCommon/Config/ConfigProvider.cs @@ -1,4 +1,5 @@ using ClientCommon.Helpers; +using Common; using Common.Enums; using Common.Helpers; using System.Runtime.CompilerServices; @@ -106,9 +107,9 @@ public HashSet DisabledAutoloadMods get => [.. _dbContext.DisabledAddons.Select(x => x.AddonId)]; } - public void ChangeModState(string addonId, bool isEnabled) + public void ChangeModState(AddonVersion addonId, bool isEnabled) { - var existing = _dbContext.DisabledAddons.Find([addonId]); + var existing = _dbContext.DisabledAddons.Find([addonId.Id]); if (existing is null) { @@ -118,7 +119,7 @@ public void ChangeModState(string addonId, bool isEnabled) } else { - _dbContext.DisabledAddons.Add(new() { AddonId = addonId }); + _dbContext.DisabledAddons.Add(new() { AddonId = addonId.Id }); } } else diff --git a/src/ClientCommon/Config/ConfigProviderFake.cs b/src/ClientCommon/Config/ConfigProviderFake.cs index 318255c2..7b0867e4 100644 --- a/src/ClientCommon/Config/ConfigProviderFake.cs +++ b/src/ClientCommon/Config/ConfigProviderFake.cs @@ -1,4 +1,5 @@ -using Common.Enums; +using Common; +using Common.Enums; namespace ClientCommon.Config; @@ -30,5 +31,5 @@ public sealed class ConfigProviderFake : IConfigProvider public void AddScore(string addonId, bool isUpvote) => throw new NotImplementedException(); - public void ChangeModState(string addonId, bool isEnabled) => throw new NotImplementedException(); + public void ChangeModState(AddonVersion addonId, bool isEnabled) => throw new NotImplementedException(); } diff --git a/src/ClientCommon/Config/IConfigProvider.cs b/src/ClientCommon/Config/IConfigProvider.cs index 05afe0e4..b037491f 100644 --- a/src/ClientCommon/Config/IConfigProvider.cs +++ b/src/ClientCommon/Config/IConfigProvider.cs @@ -1,4 +1,5 @@ -using Common.Enums; +using Common; +using Common.Enums; using static ClientCommon.Config.ConfigProvider; namespace ClientCommon.Config; @@ -26,5 +27,5 @@ public interface IConfigProvider void AddPlaytime(string addonId, TimeSpan playTime); void AddScore(string addonId, bool isUpvote); - void ChangeModState(string addonId, bool isEnabled); + void ChangeModState(AddonVersion addonId, bool isEnabled); } \ No newline at end of file diff --git a/src/Common/AddonVersion.cs b/src/Common/AddonVersion.cs new file mode 100644 index 00000000..204aef0c --- /dev/null +++ b/src/Common/AddonVersion.cs @@ -0,0 +1,28 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Common; + +public readonly struct AddonVersion +{ + public required readonly string Id { get; init; } + public required readonly string? Version{ get; init; } + + [SetsRequiredMembers] + public AddonVersion( + string title, + string? version + ) + { + Id = title; + Version = version; + } + + [SetsRequiredMembers] + public AddonVersion( + string title + ) + { + Id = title; + Version = null; + } +} diff --git a/src/Common/Interfaces/IGame.cs b/src/Common/Interfaces/IGame.cs index 10377f32..4e9a67f3 100644 --- a/src/Common/Interfaces/IGame.cs +++ b/src/Common/Interfaces/IGame.cs @@ -61,16 +61,16 @@ public interface IGame /// /// Get list of official addons and custom campaigns /// - Dictionary GetCampaigns(); + Dictionary GetCampaigns(); /// /// Get list of custom maps /// - Dictionary GetSingleMaps(); + Dictionary GetSingleMaps(); /// /// Get list of autoload mods /// - Dictionary GetAutoloadMods(bool enabledOnly); + Dictionary GetAutoloadMods(bool enabledOnly); } } \ No newline at end of file diff --git a/src/Common/Interfaces/IInstalledAddonsProvider.cs b/src/Common/Interfaces/IInstalledAddonsProvider.cs index 18c43e7a..8e1ae085 100644 --- a/src/Common/Interfaces/IInstalledAddonsProvider.cs +++ b/src/Common/Interfaces/IInstalledAddonsProvider.cs @@ -24,7 +24,7 @@ public interface IInstalledAddonsProvider /// Get installed addons /// /// Addon type - Dictionary GetInstalledAddons(AddonTypeEnum addonType); + Dictionary GetInstalledAddons(AddonTypeEnum addonType); /// /// Create cache of installed addons @@ -36,12 +36,12 @@ public interface IInstalledAddonsProvider /// Disable addon /// /// Addon id - void DisableAddon(string id); + void DisableAddon(AddonVersion id); /// /// Enable addon /// /// Addon id - void EnableAddon(string id); + void EnableAddon(AddonVersion id); } } \ No newline at end of file diff --git a/src/Games/Games/BaseGame.cs b/src/Games/Games/BaseGame.cs index 0d322173..5e98e67e 100644 --- a/src/Games/Games/BaseGame.cs +++ b/src/Games/Games/BaseGame.cs @@ -1,5 +1,6 @@ using ClientCommon.Helpers; using ClientCommon.Providers; +using Common; using Common.Enums; using Common.Helpers; using Common.Interfaces; @@ -88,7 +89,7 @@ PlaytimeProvider playtimeProvider /// - public virtual Dictionary GetCampaigns() + public virtual Dictionary GetCampaigns() { var originalCampaigns = GetOriginalCampaigns(); @@ -112,7 +113,7 @@ public virtual Dictionary GetCampaigns() /// - public virtual Dictionary GetSingleMaps() + public virtual Dictionary GetSingleMaps() { var maps = InstalledAddonsProvider.GetInstalledAddons(AddonTypeEnum.Map); @@ -121,13 +122,13 @@ public virtual Dictionary GetSingleMaps() /// - public virtual Dictionary GetAutoloadMods(bool enabledOnly) + public virtual Dictionary GetAutoloadMods(bool enabledOnly) { var mods = InstalledAddonsProvider.GetInstalledAddons(AddonTypeEnum.Mod); if (enabledOnly) { - Dictionary enabled = new(StringComparer.OrdinalIgnoreCase); + Dictionary enabled = new(); foreach (var mod in mods) { @@ -166,7 +167,7 @@ private void Cleanup() /// Get list of original campaigns /// /// - protected abstract Dictionary GetOriginalCampaigns(); + protected abstract Dictionary GetOriginalCampaigns(); /// diff --git a/src/Games/Games/BloodGame.cs b/src/Games/Games/BloodGame.cs index cf54afca..d74b0fd5 100644 --- a/src/Games/Games/BloodGame.cs +++ b/src/Games/Games/BloodGame.cs @@ -1,4 +1,5 @@ using ClientCommon.Providers; +using Common; using Common.Enums; using Common.Enums.Addons; using Common.Helpers; @@ -39,12 +40,12 @@ PlaytimeProvider playtimeProvider /// - protected override Dictionary GetOriginalCampaigns() + protected override Dictionary GetOriginalCampaigns() { - Dictionary campaigns = new(2, StringComparer.OrdinalIgnoreCase); + Dictionary campaigns = new(2); var bloodId = nameof(GameEnum.Blood).ToLower(); - campaigns.Add(bloodId, new BloodCampaign() + campaigns.Add(new(bloodId), new BloodCampaign() { Id = bloodId, Type = AddonTypeEnum.Official, @@ -82,7 +83,7 @@ protected override Dictionary GetOriginalCampaigns() { var bloodCpId = nameof(BloodAddonEnum.BloodCP).ToLower(); - campaigns.Add(bloodCpId, new BloodCampaign() + campaigns.Add(new(bloodCpId), new BloodCampaign() { Id = bloodCpId, Type = AddonTypeEnum.Official, diff --git a/src/Games/Games/DukeGame.cs b/src/Games/Games/DukeGame.cs index 0e5d0f8d..c7cdab2b 100644 --- a/src/Games/Games/DukeGame.cs +++ b/src/Games/Games/DukeGame.cs @@ -1,4 +1,5 @@ using ClientCommon.Providers; +using Common; using Common.Enums; using Common.Enums.Addons; using Common.Enums.Versions; @@ -74,15 +75,15 @@ PlaytimeProvider playtimeProvider /// - protected override Dictionary GetOriginalCampaigns() + protected override Dictionary GetOriginalCampaigns() { - Dictionary campaigns = new(6, StringComparer.OrdinalIgnoreCase); + Dictionary campaigns = new(6); if (IsBaseGameInstalled && GameInstallFolder != DukeWTInstallPath) { var dukeId = nameof(GameEnum.Duke3D).ToLower(); - campaigns.Add(dukeId, new DukeCampaign() + campaigns.Add(new(dukeId), new DukeCampaign() { Id = dukeId, Type = AddonTypeEnum.Official, @@ -115,7 +116,7 @@ Duke Nukem 3D is a first-person shooter developed and published by **3D Realms** if (IsWorldTourInstalled) { var dukeWtId = nameof(DukeVersionEnum.Duke3D_WT).ToLower(); - campaigns.Add(dukeWtId, new DukeCampaign() + campaigns.Add(new(dukeWtId), new DukeCampaign() { Id = dukeWtId, Type = AddonTypeEnum.Official, @@ -150,7 +151,7 @@ Duke Nukem 3D is a first-person shooter developed and published by **3D Realms** if (IsCaribbeanInstalled) { var dukeVacaId = nameof(DukeAddonEnum.DukeVaca).ToLower(); - campaigns.Add(dukeVacaId, new DukeCampaign() + campaigns.Add(new(dukeVacaId), new DukeCampaign() { Id = dukeVacaId, Type = AddonTypeEnum.Official, @@ -183,7 +184,7 @@ Duke Nukem 3D is a first-person shooter developed and published by **3D Realms** if (IsNuclearWinterInstalled) { var dukeNwId = nameof(DukeAddonEnum.DukeNW).ToLower(); - campaigns.Add(dukeNwId, new DukeCampaign() + campaigns.Add(new(dukeNwId), new DukeCampaign() { Id = dukeNwId, Type = AddonTypeEnum.Official, @@ -215,7 +216,7 @@ Duke Nukem must travel to the North Pole in order to stop the brainwashed Santa if (IsDukeDCInstalled) { var dukeDcId = nameof(DukeAddonEnum.DukeDC).ToLower(); - campaigns.Add(dukeDcId, new DukeCampaign() + campaigns.Add(new(dukeDcId), new DukeCampaign() { Id = dukeDcId, Type = AddonTypeEnum.Official, @@ -250,7 +251,7 @@ Duke Nukem must travel to the North Pole in order to stop the brainwashed Santa if (IsDuke64Installed) { var duke64Id = nameof(GameEnum.Duke64).ToLower(); - campaigns.Add(duke64Id, new DukeCampaign() + campaigns.Add(new(duke64Id), new DukeCampaign() { Id = duke64Id, Type = AddonTypeEnum.Official, diff --git a/src/Games/Games/FuryGame.cs b/src/Games/Games/FuryGame.cs index 4898607f..c43956fb 100644 --- a/src/Games/Games/FuryGame.cs +++ b/src/Games/Games/FuryGame.cs @@ -1,4 +1,5 @@ using ClientCommon.Providers; +using Common; using Common.Enums; using Common.Helpers; using Common.Interfaces; @@ -28,14 +29,14 @@ PlaytimeProvider playtimeProvider /// - protected override Dictionary GetOriginalCampaigns() + protected override Dictionary GetOriginalCampaigns() { - Dictionary campaigns = new(1, StringComparer.OrdinalIgnoreCase); + Dictionary campaigns = new(1); if (IsBaseGameInstalled) { var furyId = nameof(GameEnum.Fury).ToLower(); - campaigns.Add(furyId, new FuryCampaign() + campaigns.Add(new(furyId), new FuryCampaign() { Id = furyId, Type = AddonTypeEnum.Official, diff --git a/src/Games/Games/RedneckGame.cs b/src/Games/Games/RedneckGame.cs index 73222696..aa2bdbe4 100644 --- a/src/Games/Games/RedneckGame.cs +++ b/src/Games/Games/RedneckGame.cs @@ -1,4 +1,5 @@ using ClientCommon.Providers; +using Common; using Common.Enums; using Common.Enums.Addons; using Common.Enums.Versions; @@ -45,14 +46,14 @@ PlaytimeProvider playtimeProvider /// - protected override Dictionary GetOriginalCampaigns() + protected override Dictionary GetOriginalCampaigns() { - Dictionary campaigns = new(3, StringComparer.OrdinalIgnoreCase); + Dictionary campaigns = new(3); if (IsBaseGameInstalled) { var redneckId = nameof(GameEnum.Redneck).ToLower(); - campaigns.Add(redneckId, new RedneckCampaign() + campaigns.Add(new(redneckId), new RedneckCampaign() { Id = redneckId, Type = AddonTypeEnum.Official, @@ -86,7 +87,7 @@ protected override Dictionary GetOriginalCampaigns() if (IsRoute66Installed) { var redneckR66Id = nameof(RedneckAddonEnum.Route66).ToLower(); - campaigns.Add(redneckR66Id, new RedneckCampaign() + campaigns.Add(new(redneckR66Id), new RedneckCampaign() { Id = redneckR66Id, Type = AddonTypeEnum.Official, @@ -117,7 +118,7 @@ protected override Dictionary GetOriginalCampaigns() if (IsAgainInstalled) { var redneckRaId = nameof(GameEnum.RidesAgain).ToLower(); - campaigns.Add(redneckRaId, new RedneckCampaign() + campaigns.Add(new(redneckRaId), new RedneckCampaign() { Id = redneckRaId, Type = AddonTypeEnum.Official, diff --git a/src/Games/Games/SlaveGame.cs b/src/Games/Games/SlaveGame.cs index 8925e0a7..1bb50608 100644 --- a/src/Games/Games/SlaveGame.cs +++ b/src/Games/Games/SlaveGame.cs @@ -1,4 +1,5 @@ using ClientCommon.Providers; +using Common; using Common.Enums; using Common.Helpers; using Common.Interfaces; @@ -28,14 +29,14 @@ PlaytimeProvider playtimeProvider /// - protected override Dictionary GetOriginalCampaigns() + protected override Dictionary GetOriginalCampaigns() { - Dictionary campaigns = new(1, StringComparer.OrdinalIgnoreCase); + Dictionary campaigns = new(1); if (IsBaseGameInstalled) { var slaveId = nameof(GameEnum.Exhumed).ToLower(); - campaigns.Add(slaveId, new SlaveCampaign() + campaigns.Add(new(slaveId), new SlaveCampaign() { Id = slaveId, Type = AddonTypeEnum.Official, diff --git a/src/Games/Games/WangGame.cs b/src/Games/Games/WangGame.cs index 42e321ae..6eee1da5 100644 --- a/src/Games/Games/WangGame.cs +++ b/src/Games/Games/WangGame.cs @@ -1,4 +1,5 @@ using ClientCommon.Providers; +using Common; using Common.Enums; using Common.Enums.Addons; using Common.Helpers; @@ -41,14 +42,14 @@ PlaytimeProvider playtimeProvider /// /// Get list of original campaigns /// - protected override Dictionary GetOriginalCampaigns() + protected override Dictionary GetOriginalCampaigns() { - Dictionary campaigns = new(3, StringComparer.OrdinalIgnoreCase); + Dictionary campaigns = new(3); if (IsBaseGameInstalled) { var wangId = nameof(GameEnum.ShadowWarrior).ToLower(); - campaigns.Add(wangId, new WangCampaign() + campaigns.Add(new(wangId), new WangCampaign() { Id = wangId, Type = AddonTypeEnum.Official, @@ -77,7 +78,7 @@ protected override Dictionary GetOriginalCampaigns() if (IsWantonInstalled) { var wangWdId = nameof(WangAddonEnum.Wanton).ToLower(); - campaigns.Add(wangWdId, new WangCampaign() + campaigns.Add(new(wangWdId), new WangCampaign() { Id = wangWdId, Type = AddonTypeEnum.Official, @@ -109,7 +110,7 @@ protected override Dictionary GetOriginalCampaigns() if (IsTwinDragonInstalled) { var wangTdId = nameof(WangAddonEnum.TwinDragon).ToLower(); - campaigns.Add(wangTdId, new WangCampaign() + campaigns.Add(new(wangTdId), new WangCampaign() { Id = wangTdId, Type = AddonTypeEnum.Official, diff --git a/src/Mods/Providers/DownloadableAddonsProvider.cs b/src/Mods/Providers/DownloadableAddonsProvider.cs index a1d7d28e..37d9fb45 100644 --- a/src/Mods/Providers/DownloadableAddonsProvider.cs +++ b/src/Mods/Providers/DownloadableAddonsProvider.cs @@ -1,154 +1,181 @@ using ClientCommon.API; using ClientCommon.Helpers; +using Common; using Common.Enums; using Common.Helpers; using Common.Interfaces; using Common.Tools; using System.Collections.Immutable; -namespace Mods.Providers +namespace Mods.Providers; + +/// +/// Class that provides lists of addons available to download +/// +public sealed class DownloadableAddonsProvider : IDownloadableAddonsProvider { - /// - /// Class that provides lists of addons available to download - /// - public sealed class DownloadableAddonsProvider : IDownloadableAddonsProvider + private readonly IGame _game; + private readonly ArchiveTools _archiveTools; + private readonly ApiInterface _apiInterface; + + private Dictionary>? _cache; + private readonly SemaphoreSlim _semaphore = new(1); + + public event AddonChanged AddonDownloadedEvent; + + /// + public Progress Progress { get; private set; } + + [Obsolete($"Don't create directly. Use {nameof(DownloadableAddonsProviderFactory)}.")] + public DownloadableAddonsProvider( + IGame game, + ArchiveTools archiveTools, + ApiInterface apiInterface + ) { - private readonly IGame _game; - private readonly ArchiveTools _archiveTools; - private readonly ApiInterface _apiInterface; + _game = game; + _archiveTools = archiveTools; + _apiInterface = apiInterface; - private Dictionary>? _cache; - private readonly SemaphoreSlim _semaphore = new(1); + Progress = _archiveTools.Progress; + } - public event AddonChanged AddonDownloadedEvent; - /// - public Progress Progress { get; private set; } + /// + public async Task CreateCacheAsync() + { + await _semaphore.WaitAsync(); - [Obsolete($"Don't create directly. Use {nameof(DownloadableAddonsProviderFactory)}.")] - public DownloadableAddonsProvider( - IGame game, - ArchiveTools archiveTools, - ApiInterface apiInterface - ) + if (_cache is not null) { - _game = game; - _archiveTools = archiveTools; - _apiInterface = apiInterface; - - Progress = _archiveTools.Progress; + _semaphore.Release(); + return; } + var addons = await _apiInterface.GetAddonsAsync(_game.GameEnum).ConfigureAwait(false); - /// - public async Task CreateCacheAsync() + if (addons is null || addons.Count == 0) { - await _semaphore.WaitAsync(); + _semaphore.Release(); + return; + } - if (_cache is not null) - { - _semaphore.Release(); - return; - } + _cache = []; - var addons = await _apiInterface.GetAddonsAsync(_game.GameEnum).ConfigureAwait(false); + addons = [.. addons.OrderBy(a => a.Title).OrderBy(a => a.Version)]; - if (addons is null || addons.Count == 0) - { - _semaphore.Release(); - return; - } + foreach (var addon in addons) + { + _cache.TryAdd(addon.AddonType, []); + _cache[addon.AddonType].TryAdd(new(addon.Id, addon.Version), addon); + } - _cache = []; + _semaphore.Release(); + } - foreach (var addon in addons) - { - _cache.TryAdd(addon.AddonType, []); - _cache[addon.AddonType].TryAdd(addon.Id, addon); - } - _semaphore.Release(); + /// + public ImmutableList GetDownloadableAddons(AddonTypeEnum addonType) + { + if (_cache is null) + { + return []; + } + + if (!_cache.TryGetValue(addonType, out var addonTypeCache)) + { + return []; } + var installedAddons = _game.InstalledAddonsProvider.GetInstalledAddons(addonType); - /// - public ImmutableList GetDownloadableAddons(AddonTypeEnum addonType) + foreach (var downloadableAddon in addonTypeCache) { - if (_cache is null) - { - return []; - } + var existinsAddons = installedAddons.Where(x => x.Key.Id == downloadableAddon.Key.Id).Select(x => x.Key); - if (!_cache.TryGetValue(addonType, out var addonTypeCache)) + downloadableAddon.Value.IsInstalled = true; + + if (!existinsAddons.Any()) { - return []; + downloadableAddon.Value.IsInstalled = false; + continue; } - var installedAddons = _game.InstalledAddonsProvider.GetInstalledAddons(addonType); - - foreach (var downloadableAddon in addonTypeCache) + //Death Wish hack + if (downloadableAddon.Key.Id.Contains("death-wish", StringComparison.InvariantCultureIgnoreCase) && + downloadableAddon.Key.Version!.StartsWith('1')) { - if (installedAddons.TryGetValue(downloadableAddon.Key, out var installedAddon)) + if (existinsAddons.Contains(downloadableAddon.Key)) { downloadableAddon.Value.IsInstalled = true; - - if (VersionComparer.Compare(downloadableAddon.Value.Version, installedAddon.Version, ">")) - { - downloadableAddon.Value.HasNewerVersion = true; - } } else { downloadableAddon.Value.IsInstalled = false; } + + continue; } + else + { + foreach (var version in existinsAddons.Select(x => x.Version).Where(x => x is not null)) + { + downloadableAddon.Value.HasNewerVersion = true; - return [.. addonTypeCache.Values]; + if (VersionComparer.Compare(downloadableAddon.Value.Version, version!, "==")) + { + downloadableAddon.Value.HasNewerVersion = false; + break; + } + } + } } + return [.. addonTypeCache.Values]; + } + - /// - public async Task DownloadAddonAsync(IDownloadableAddon addon) + /// + public async Task DownloadAddonAsync(IDownloadableAddon addon) + { + var url = addon.DownloadUrl; + var file = Path.GetFileName(url.ToString()); + string path; + + if (addon.AddonType is AddonTypeEnum.TC) + { + path = _game.CampaignsFolderPath; + } + else if (addon.AddonType is AddonTypeEnum.Map) + { + path = _game.MapsFolderPath; + } + else if (addon.AddonType is AddonTypeEnum.Mod) + { + path = _game.ModsFolderPath; + } + else { - var url = addon.DownloadUrl; - var file = Path.GetFileName(url.ToString()); - string path; + ThrowHelper.NotImplementedException(addon.AddonType.ToString()); + return; + } - if (addon.AddonType is AddonTypeEnum.TC) - { - path = _game.CampaignsFolderPath; - } - else if (addon.AddonType is AddonTypeEnum.Map) - { - path = _game.MapsFolderPath; - } - else if (addon.AddonType is AddonTypeEnum.Mod) - { - path = _game.ModsFolderPath; - } - else - { - ThrowHelper.NotImplementedException(addon.AddonType.ToString()); - return; - } + var pathToFile = Path.Combine(path, file); - var pathToFile = Path.Combine(path, file); + await _archiveTools.DownloadFileAsync(url, pathToFile).ConfigureAwait(false); - await _archiveTools.DownloadFileAsync(url, pathToFile).ConfigureAwait(false); + _game.InstalledAddonsProvider.AddAddon(addon.AddonType, pathToFile); - _game.InstalledAddonsProvider.AddAddon(addon.AddonType, pathToFile); + if (!ClientProperties.IsDevMode) + { + var result = await _apiInterface.IncreaseNumberOfInstallsAsync(addon.Id); - if (!ClientProperties.IsDevMode) + if (result) { - var result = await _apiInterface.IncreaseNumberOfInstallsAsync(addon.Id); - - if (result) - { - addon.Installs++; - } + addon.Installs++; } - - AddonDownloadedEvent?.Invoke(_game, addon.AddonType); } + + AddonDownloadedEvent?.Invoke(_game, addon.AddonType); } } diff --git a/src/Mods/Providers/InstalledAddonsProvider.cs b/src/Mods/Providers/InstalledAddonsProvider.cs index d95719b3..0735bf5e 100644 --- a/src/Mods/Providers/InstalledAddonsProvider.cs +++ b/src/Mods/Providers/InstalledAddonsProvider.cs @@ -1,5 +1,6 @@ using ClientCommon.Config; using ClientCommon.Providers; +using Common; using Common.Enums; using Common.Enums.Versions; using Common.Helpers; @@ -20,7 +21,7 @@ public sealed class InstalledAddonsProvider : IInstalledAddonsProvider private readonly IConfigProvider _config; private readonly PlaytimeProvider _playtimeProvider; - private readonly Dictionary> _cache; + private readonly Dictionary> _cache; private static readonly SemaphoreSlim _semaphore = new(1); private bool _isCacheUpdating = false; @@ -95,13 +96,13 @@ public void AddAddon(AddonTypeEnum addonType, string pathToFile) var dict = _cache[addon.Type]; - if (dict.TryGetValue(addon.Id, out _)) + if (dict.TryGetValue(new(addon.Id, addon.Version), out _)) { - dict[addon.Id] = addon; + dict[new(addon.Id, addon.Version)] = addon; } else { - dict.Add(addon.Id, addon); + dict.Add(new(addon.Id, addon.Version), addon); } } @@ -113,13 +114,13 @@ public void DeleteAddon(IAddon addon) File.Delete(addon.PathToFile); - _cache[addon.Type].Remove(addon.Id); + _cache[addon.Type].Remove(new(addon.Id, addon.Version)); AddonsChangedEvent?.Invoke(_game, addon.Type); } /// - public void EnableAddon(string addonId) + public void EnableAddon(AddonVersion addonId) { ((AutoloadMod)_cache[AddonTypeEnum.Mod][addonId]).IsEnabled = true; @@ -127,7 +128,7 @@ public void EnableAddon(string addonId) } /// - public void DisableAddon(string addonId) + public void DisableAddon(AddonVersion addonId) { ((AutoloadMod)_cache[AddonTypeEnum.Mod][addonId]).IsEnabled = false; @@ -135,7 +136,7 @@ public void DisableAddon(string addonId) } /// - public Dictionary GetInstalledAddons(AddonTypeEnum addonType) + public Dictionary GetInstalledAddons(AddonTypeEnum addonType) { if (_isCacheUpdating) { @@ -161,9 +162,9 @@ public Dictionary GetInstalledAddons(AddonTypeEnum addonType) /// /// Addon type /// Paths to addon files - private Dictionary GetAddonsFromFiles(AddonTypeEnum addonType, IEnumerable files) + private Dictionary GetAddonsFromFiles(AddonTypeEnum addonType, IEnumerable files) { - Dictionary addedAddons = new(files.Count(), StringComparer.OrdinalIgnoreCase); + Dictionary addedAddons = new(files.Count()); foreach (var file in files) { @@ -176,25 +177,26 @@ private Dictionary GetAddonsFromFiles(AddonTypeEnum addonType, I continue; } - if (addedAddons.TryGetValue(newAddon.Id, out var existingMod)) + if (newAddon is AutoloadMod && + addedAddons.TryGetValue(new(newAddon.Id, newAddon.Version), out var existingMod)) { if (existingMod.Version is null && newAddon.Version is not null) { //replacing with addon that have version - addedAddons[newAddon.Id] = newAddon; + addedAddons[new(newAddon.Id, newAddon.Version)] = newAddon; } else if (existingMod.Version is not null && newAddon.Version is not null && VersionComparer.Compare(newAddon.Version, existingMod.Version, ">")) { //replacing with addon that have higher version - addedAddons[newAddon.Id] = newAddon; + addedAddons[new(newAddon.Id, newAddon.Version)] = newAddon; } } else { - addedAddons.Add(newAddon.Id, newAddon); + addedAddons.Add(new(newAddon.Id, newAddon.Version), newAddon); } } catch (Exception) diff --git a/src/Ports/Ports/BasePort.cs b/src/Ports/Ports/BasePort.cs index 26ed4f31..fa735035 100644 --- a/src/Ports/Ports/BasePort.cs +++ b/src/Ports/Ports/BasePort.cs @@ -1,4 +1,5 @@ using ClientCommon.Helpers; +using Common; using Common.Enums; using Common.Enums.Addons; using Common.Helpers; @@ -168,7 +169,7 @@ protected void GetMapArgs(StringBuilder sb, IGame game, IAddon camp) /// /// Autoload mod /// Campaign - protected bool ValidateAutoloadMod(AutoloadMod autoloadMod, IAddon campaign, Dictionary addons) + protected bool ValidateAutoloadMod(AutoloadMod autoloadMod, IAddon campaign, Dictionary addons) { if (!autoloadMod.IsEnabled) { @@ -199,7 +200,7 @@ protected bool ValidateAutoloadMod(AutoloadMod autoloadMod, IAddon campaign, Dic { foreach (var dep in autoloadMod.DependentAddons) { - if (!addons.ContainsKey(dep.Key) && + if (!addons.ContainsKey(new(dep.Key, dep.Value)) && !campaign.Id.Equals(dep.Key, StringComparison.OrdinalIgnoreCase)) { //skipping mod that doesn't have every dependency @@ -212,7 +213,7 @@ protected bool ValidateAutoloadMod(AutoloadMod autoloadMod, IAddon campaign, Dic { foreach (var dep in autoloadMod.IncompatibleAddons) { - if (addons.ContainsKey(dep.Key) || + if (addons.ContainsKey(new(dep.Key, dep.Value)) || campaign.Id.Equals(dep.Key, StringComparison.OrdinalIgnoreCase)) { //skipping incompatible mods