diff --git a/ModManager.sln b/ModManager.sln index b9a331dd..7c92df8e 100644 --- a/ModManager.sln +++ b/ModManager.sln @@ -32,7 +32,6 @@ Global {64ACF7A5-C0DB-4F65-9AF3-F5F549FE3D38}.Release|Any CPU.ActiveCfg = Release|Any CPU {64ACF7A5-C0DB-4F65-9AF3-F5F549FE3D38}.Release|Any CPU.Build.0 = Release|Any CPU {24688521-C739-4686-8CB4-C2A73BBC44D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {24688521-C739-4686-8CB4-C2A73BBC44D6}.Debug|Any CPU.Build.0 = Debug|Any CPU {24688521-C739-4686-8CB4-C2A73BBC44D6}.Release|Any CPU.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution diff --git a/ModManager/App.xaml b/ModManager/App.xaml index 3bf3b6ac..8420b1d0 100644 --- a/ModManager/App.xaml +++ b/ModManager/App.xaml @@ -25,11 +25,9 @@ - + - - + \ No newline at end of file diff --git a/ModManager/Utils/CredentialAccessor.cs b/ModManager/Utils/CredentialAccessor.cs new file mode 100644 index 00000000..5c355016 --- /dev/null +++ b/ModManager/Utils/CredentialAccessor.cs @@ -0,0 +1,53 @@ +using CredentialManagement; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Imya.UI.Utils +{ + public abstract class CredentialAccessor + { + protected abstract String _credentialID { get; } + + public bool IsTokenStored() + { + using (var cred = new Credential() { Target = _credentialID }) + { + return cred.Exists(); + } + } + + protected void SaveToken(String token) + { + using (var cred = new Credential() { Target = _credentialID }) + { + cred.Password = token; + cred.Type = CredentialType.Generic; + cred.PersistanceType = PersistanceType.LocalComputer; + cred.Save(); + } + } + + protected bool TryGetSavedToken(out String token) + { + using (var cred = new Credential() { Target = _credentialID }) + { + token = ""; + if (!cred.Exists()) return false; + cred.Load(); + token = cred.Password; + return true; + } + } + + protected void CleanSavedToken() + { + using (var cred = new Credential() { Target = _credentialID }) + { + cred.Delete(); + } + } + } +} diff --git a/ModManager/Utils/DeviceFlowAuthenticator.cs b/ModManager/Utils/DeviceFlowAuthenticator.cs index 92731868..3ca6b40f 100644 --- a/ModManager/Utils/DeviceFlowAuthenticator.cs +++ b/ModManager/Utils/DeviceFlowAuthenticator.cs @@ -12,10 +12,10 @@ namespace Imya.UI.Utils { - public class DeviceFlowAuthenticator : IAuthenticator + public class DeviceFlowAuthenticator : CredentialAccessor, IAuthenticator { private String ClientID = "37a11ee844d5eea41346"; - private const String tokenCredentialId = "IMYA_OAUTH_TOKEN"; + protected override String _credentialID { get; } = "IMYA_OAUTH_TOKEN"; public event IAuthenticator.PopupRequestedEventHandler UserCodeReceived = delegate { }; public event IAuthenticator.AuthenticatedEventHandler AuthenticationSuccess = delegate { }; @@ -36,14 +36,14 @@ public async Task StartAuthentication() String? access_token = null; //use saved token. if it doesn't exist, create a token request and save the result. - if (!TryGetSavedOauthToken(out access_token)) + if (!TryGetSavedToken(out access_token)) { var token = await GetAuthToken(); if (!(token is OauthToken valid_token)) return; access_token = valid_token.AccessToken; - SaveOauthToken(access_token); + SaveToken(access_token); } var credentials = new Credentials(access_token); _client.Connection.Credentials = credentials; @@ -79,48 +79,7 @@ private void OpenInBrowser(String LoginUrl) } } - public bool HasStoredLoginInfo() - { - using (var cred = new Credential() { Target = tokenCredentialId }) - { - return cred.Exists(); - } - } - - private void SaveOauthToken(String token) - { - using (var cred = new Credential() { Target = tokenCredentialId }) - { - cred.Password = token; - cred.Type = CredentialType.Generic; - cred.PersistanceType = PersistanceType.LocalComputer; - cred.Save(); - } - } - - private bool TryGetSavedOauthToken(out String token) - { - using (var cred = new Credential() { Target = tokenCredentialId }) - { - token = ""; - if (!cred.Exists()) return false; - cred.Load(); - token = cred.Password; - return true; - } - } - - private void CleanSavedOauthToken() - { - using (var cred = new Credential() { Target = tokenCredentialId }) - { - cred.Delete(); - } - } - - public void RemoveAuthentication() - { - CleanSavedOauthToken(); - } + public bool HasStoredLoginInfo() => IsTokenStored(); + public void RemoveAuthentication() => CleanSavedToken(); } } diff --git a/ModManager/Utils/ModioAuthenticator.cs b/ModManager/Utils/ModioAuthenticator.cs new file mode 100644 index 00000000..e788ccde --- /dev/null +++ b/ModManager/Utils/ModioAuthenticator.cs @@ -0,0 +1,32 @@ +using Anno.EasyMod.ModioWrapper; +using Modio; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using CredentialManagement; + +namespace Imya.UI.Utils +{ + class ModioAuthenticator : CredentialAccessor, IModioAuthenticator + { + protected override String _credentialID { get; } = "IMYA_MODIO_TOKEN"; + + public async Task CreateAuthenticatedAsync() + { + if (TryGetSavedToken(out string token)) + { + return new Client(new Credentials("", token)); + } + + return await RunAuthFlow(); + } + + private async Task RunAuthFlow() + { + throw new NotImplementedException(); + } + } +} diff --git a/ModManager/Utils/PopupCreator.cs b/ModManager/Utils/PopupCreator.cs index 07db0e4f..58bd6e99 100644 --- a/ModManager/Utils/PopupCreator.cs +++ b/ModManager/Utils/PopupCreator.cs @@ -1,5 +1,4 @@ -using Imya.Enums; -using Imya.GithubIntegration.Download; +using Imya.GithubIntegration.Download; using Imya.Models; using Imya.Services; using Imya.Services.Interfaces; @@ -9,6 +8,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using Anno.EasyMod.Metadata; namespace Imya.UI.Utils { diff --git a/ModManager/ValueConverters/AttributeConverters.cs b/ModManager/ValueConverters/AttributeConverters.cs index 1eca07a3..282ac886 100644 --- a/ModManager/ValueConverters/AttributeConverters.cs +++ b/ModManager/ValueConverters/AttributeConverters.cs @@ -1,4 +1,5 @@ -using Imya.Models.Attributes; +using Anno.EasyMod.Attributes; +using Imya.Models.Attributes; using Imya.Models.Attributes.Factories; using System; using System.Globalization; @@ -10,23 +11,23 @@ namespace Imya.UI.ValueConverters { static class AttributeIcons { - public static (string, SolidColorBrush) AttributeToIcon(AttributeType type, ModStatus? status) + public static (string, SolidColorBrush) AttributeToIcon(string type, ModStatus? status) { return type switch { - AttributeType.ModStatus when status == ModStatus.Updated => ("Update", FindResourceBrush("HighlightColorBrush")), - AttributeType.ModStatus when status == ModStatus.New => ("Download", FindResourceBrush("HighlightColorBrush")), - AttributeType.ModStatus when status == ModStatus.Obsolete => ("RemoveCircleOutline", FindResourceBrush("ErrorColorBrush")), - AttributeType.ModCompabilityIssue => ("AlertBox", FindResourceBrush("ErrorColorBrush")), - AttributeType.ModReplacedByIssue => ("RemoveCircleOutline", FindResourceBrush("ErrorColorBrush")), - AttributeType.UnresolvedDependencyIssue => ("FileTree", FindResourceBrush("ErrorColorBrush")), - AttributeType.TweakedMod => ("Tools", FindResourceBrush("InformationColorBrush")), - AttributeType.MissingModinfo => ("HelpBox", FindResourceBrush("InformationColorBrush")), - AttributeType.ModContentInSubfolder => ("AlertBox", FindResourceBrush("ErrorColorBrush")), - AttributeType.IssueModRemoved => ("TrashCanOutline", FindResourceBrush("ErrorColorBrush")), - AttributeType.IssueModAccess => ("FolderAlertOutline", FindResourceBrush("ErrorColorBrush")), - AttributeType.CyclicDependency => ("CircleArrows", FindResourceBrush("ErrorColorBrush")), - AttributeType.DlcNotOwned => ("Ubisoft", FindResourceBrush("InformationColorBrush")), + AttributeTypes.ModStatus when status == ModStatus.Updated => ("Update", FindResourceBrush("HighlightColorBrush")), + AttributeTypes.ModStatus when status == ModStatus.New => ("Download", FindResourceBrush("HighlightColorBrush")), + AttributeTypes.ModStatus when status == ModStatus.Obsolete => ("RemoveCircleOutline", FindResourceBrush("ErrorColorBrush")), + AttributeTypes.ModCompabilityIssue => ("AlertBox", FindResourceBrush("ErrorColorBrush")), + AttributeTypes.ModReplacedByIssue => ("RemoveCircleOutline", FindResourceBrush("ErrorColorBrush")), + AttributeTypes.UnresolvedDependencyIssue => ("FileTree", FindResourceBrush("ErrorColorBrush")), + AttributeTypes.TweakedMod => ("Tools", FindResourceBrush("InformationColorBrush")), + AttributeTypes.MissingModinfo => ("HelpBox", FindResourceBrush("InformationColorBrush")), + AttributeTypes.ModContentInSubfolder => ("AlertBox", FindResourceBrush("ErrorColorBrush")), + AttributeTypes.IssueModRemoved => ("TrashCanOutline", FindResourceBrush("ErrorColorBrush")), + AttributeTypes.IssueModAccess => ("FolderAlertOutline", FindResourceBrush("ErrorColorBrush")), + AttributeTypes.CyclicDependency => ("CircleArrows", FindResourceBrush("ErrorColorBrush")), + AttributeTypes.DlcNotOwned => ("Ubisoft", FindResourceBrush("InformationColorBrush")), _ => ("InformationOutline", FindResourceBrush("TextColorBrush")), }; } @@ -39,12 +40,12 @@ private static SolidColorBrush FindResourceBrush(String ResourceName) private static readonly SolidColorBrush Fallback = new(Colors.Black); } - [ValueConversion(typeof(IAttribute), typeof(SolidColorBrush))] + [ValueConversion(typeof(IModAttribute), typeof(SolidColorBrush))] public class AttributeColorConverter : IValueConverter { public object Convert(object value, Type TargetType, object parameter, CultureInfo Culture) { - if (value is not IAttribute attrib) + if (value is not IModAttribute attrib) return string.Empty; return AttributeIcons.AttributeToIcon(attrib.AttributeType, (attrib as ModStatusAttribute)?.Status).Item2; @@ -56,12 +57,12 @@ public object ConvertBack(object value, Type TargetType, object parameter, Cultu } } - [ValueConversion(typeof(IAttribute), typeof(string))] + [ValueConversion(typeof(IModAttribute), typeof(string))] public class AttributeIconConverter : IValueConverter { public object Convert(object value, Type TargetType, object parameter, CultureInfo Culture) { - if (value is not IAttribute attrib) return + if (value is not IModAttribute attrib) return string.Empty; return AttributeIcons.AttributeToIcon(attrib.AttributeType, (attrib as ModStatusAttribute)?.Status).Item1; diff --git a/ModManager/ValueConverters/DlcIconConverter.cs b/ModManager/ValueConverters/DlcIconConverter.cs index 76a0a7e8..a580b060 100644 --- a/ModManager/ValueConverters/DlcIconConverter.cs +++ b/ModManager/ValueConverters/DlcIconConverter.cs @@ -1,10 +1,8 @@ -using Imya.Enums; -using Imya.Services.Interfaces; +using Anno.EasyMod.Metadata; using System; using System.Collections.Generic; using System.Drawing; using System.Globalization; -using System.IO; using System.Windows.Data; namespace Imya.UI.ValueConverters diff --git a/ModManager/ValueConverters/DlcTextConverter.cs b/ModManager/ValueConverters/DlcTextConverter.cs index a7b7f884..511a3fbb 100644 --- a/ModManager/ValueConverters/DlcTextConverter.cs +++ b/ModManager/ValueConverters/DlcTextConverter.cs @@ -1,5 +1,4 @@ -using Imya.Enums; -using Imya.Models; +using Imya.Models; using Imya.Texts; using Imya.Utils; using Microsoft.Extensions.DependencyInjection; @@ -10,6 +9,8 @@ using System.Windows.Data; using System.Windows.Markup; +using Anno.EasyMod.Metadata; + namespace Imya.UI.ValueConverters { /// diff --git a/ModManager/ValueConverters/FallbackImageConverter.cs b/ModManager/ValueConverters/FallbackImageConverter.cs index 39e2c253..63654dc2 100644 --- a/ModManager/ValueConverters/FallbackImageConverter.cs +++ b/ModManager/ValueConverters/FallbackImageConverter.cs @@ -14,12 +14,21 @@ namespace Imya.UI.ValueConverters [ValueConversion(typeof(Uri), typeof(Uri))] public class FallbackImageConverter : IValueConverter { - static Uri fallback = new Uri(Path.Combine(Environment.CurrentDirectory, "resources", "modicon_placeholder.png")); + static Uri fallback = new Uri(Path.Combine(Environment.CurrentDirectory, "resources", "modbanner_placeholder.png")); + + static readonly string[] filenames = { + "banner.png", + "banner.jpg" + }; public object Convert(object value, Type TargetType, object parameter, CultureInfo Culture) { - var str = value as string; - return str is null ? fallback : new Uri(str); + var str = value as Uri; + if (str is null) + return fallback.AbsoluteUri; + if(str.IsFile && !File.Exists(str.LocalPath)) + return fallback.AbsoluteUri; + return str.AbsoluteUri; } public object ConvertBack(object value, Type TargetType, object parameter, CultureInfo Culture) diff --git a/ModManager/ValueConverters/SizeConverters.cs b/ModManager/ValueConverters/SizeConverters.cs index 7c2d40f7..bb7fc6b0 100644 --- a/ModManager/ValueConverters/SizeConverters.cs +++ b/ModManager/ValueConverters/SizeConverters.cs @@ -1,4 +1,5 @@ using Imya.Models; +using Imya.Texts; using System; using System.Collections.Generic; using System.Globalization; @@ -12,12 +13,18 @@ namespace Imya.UI.ValueConverters [ValueConversion(typeof(double), typeof(String))] internal sealed class SpeedConverter : IValueConverter { + private ITextManager _textManager; + public SpeedConverter(ITextManager textManager) + { + _textManager = textManager; + } + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is double dvalue) - return Math.Round(dvalue / (1024 * 1024), 2) + " MB/s"; + return Math.Round(dvalue / (1024 * 1024), 2) + _textManager.GetText("HEADER_FOLDER_SIZE_MB").Text + "/s"; if (value is long lvalue) - return Math.Round((float)lvalue / (1024 * 1024), 2) + " MB/s"; + return Math.Round((float)lvalue / (1024 * 1024), 2) + _textManager.GetText("HEADER_FOLDER_SIZE_MB").Text +"/s"; return String.Empty; } @@ -30,12 +37,17 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu [ValueConversion(typeof(long), typeof(String))] internal sealed class ByteSizeConverter : IValueConverter { + private ITextManager _textManager; + public ByteSizeConverter(ITextManager textManager) + { + _textManager = textManager; + } public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is double dvalue) - return Math.Round(dvalue / (1024 * 1024), 2) + " MB"; + return Math.Round(dvalue / (1024 * 1024), 2) + _textManager.GetText("HEADER_FOLDER_SIZE_MB").Text; if (value is long lvalue) - return Math.Round((float)lvalue / (1024 * 1024), 2) + " MB"; + return Math.Round((float)lvalue / (1024 * 1024), 2) + _textManager.GetText("HEADER_FOLDER_SIZE_MB").Text; return String.Empty; } diff --git a/ModManager/Views/Components/AttributeStaticHelp.xaml.cs b/ModManager/Views/Components/AttributeStaticHelp.xaml.cs index 39e50be2..bb52b8d0 100644 --- a/ModManager/Views/Components/AttributeStaticHelp.xaml.cs +++ b/ModManager/Views/Components/AttributeStaticHelp.xaml.cs @@ -1,4 +1,5 @@ -using Imya.Models; +using Anno.EasyMod.Attributes; +using Imya.Models; using Imya.Models.Attributes; using Imya.Texts; using Imya.Utils; @@ -25,7 +26,7 @@ namespace Imya.UI.Components public class AttributeText { - public IAttribute Attribute { get; init; } + public IModAttribute Attribute { get; init; } public IText Text { get; init; } } diff --git a/ModManager/Views/Components/ConsoleLog.xaml b/ModManager/Views/Components/ConsoleLog.xaml deleted file mode 100644 index b13f633c..00000000 --- a/ModManager/Views/Components/ConsoleLog.xaml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - diff --git a/ModManager/Views/Components/ConsoleLog.xaml.cs b/ModManager/Views/Components/ConsoleLog.xaml.cs deleted file mode 100644 index f2deb36b..00000000 --- a/ModManager/Views/Components/ConsoleLog.xaml.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Windows.Controls; - -namespace Imya.UI.Components -{ - /// - /// Interaktionslogik für Console.xaml - /// - public partial class ConsoleLog : UserControl - { - public TextBox Console { get => ConsoleOut; } - public ConsoleLog() - { - InitializeComponent(); - } - - private void ConsoleLog_TextChanged(object sender, TextChangedEventArgs e) - { - ScrollPane.ScrollToBottom(); - } - } -} diff --git a/ModManager/Views/Components/Dashboard.xaml b/ModManager/Views/Components/Dashboard.xaml index 588c84de..443a72ef 100644 --- a/ModManager/Views/Components/Dashboard.xaml +++ b/ModManager/Views/Components/Dashboard.xaml @@ -15,7 +15,8 @@ - + + @@ -214,7 +215,7 @@ @@ -288,5 +289,35 @@ + + + + + + + + + + + + + + + + + + diff --git a/ModManager/Views/Components/Dashboard.xaml.cs b/ModManager/Views/Components/Dashboard.xaml.cs index a7f1512e..2355ad19 100644 --- a/ModManager/Views/Components/Dashboard.xaml.cs +++ b/ModManager/Views/Components/Dashboard.xaml.cs @@ -17,7 +17,8 @@ using Imya.Services; using Imya.Services.Interfaces; using Imya.Texts; -using Imya.Models.Mods; +using Anno.EasyMod.Mods; +using System.Threading.Tasks; namespace Imya.UI.Components { @@ -37,7 +38,8 @@ public partial class Dashboard : UserControl, INotifyPropertyChanged public IAuthenticator Authenticator { get; } private ITweakService _tweakService; private IGameLauncherFactory _launcherFactory; - private ModCollection _globalMods; + private IModCollection _globalMods; + private Modio.Client? _modioClient; private PopupCreator _popupCreator; @@ -50,7 +52,18 @@ private set { } private bool _canStartGame; - public Dashboard( + public Modio.Models.User? ModioUser + { + get => _modioUser; + private set + { + _modioUser = value; + OnPropertyChanged(nameof(ModioUser)); + } + } + private Modio.Models.User? _modioUser; + + public Dashboard( IAuthenticator authenticator, ITweakService tweakService, IAppSettings appSettings, @@ -60,7 +73,8 @@ public Dashboard( ITextManager textManager, PopupCreator popupCreator, IAuthenticationController authController, - IImyaSetupService imyaSetupService) + IImyaSetupService imyaSetupService, + Modio.Client? modioClient = null) { Authenticator = authenticator; AppSettings = appSettings; @@ -79,6 +93,15 @@ public Dashboard( MainViewController.ViewChanged += UpdateSelection; CanStartGame = CheckCanStartGame(); + + _modioClient = modioClient; + if(_modioClient is not null) + { + Task.Run(async () => { + var user = await _modioClient.User.GetCurrentUser(); + ModioUser = user; + }); + } } private bool CheckCanStartGame() @@ -102,8 +125,8 @@ private bool CheckCanStartGame() public void StartGameClick(object sender, RoutedEventArgs e) { - var withUnresolved = _globalMods.WithAttribute(AttributeType.UnresolvedDependencyIssue); - var withIncompatibleIssue = _globalMods.WithAttribute(AttributeType.ModCompabilityIssue); + var withUnresolved = _globalMods.Where(x => x.Attributes.HasAttributeOfType(AttributeTypes.UnresolvedDependencyIssue)); + var withIncompatibleIssue = _globalMods.Where(x => x.Attributes.HasAttributeOfType(AttributeTypes.ModCompabilityIssue)); if (withUnresolved?.Count() > 0 || withIncompatibleIssue?.Count() > 0) { diff --git a/ModManager/Views/Components/ModDescriptionDisplay.xaml b/ModManager/Views/Components/ModDescriptionDisplay.xaml index 96e232e0..a8936bba 100644 --- a/ModManager/Views/Components/ModDescriptionDisplay.xaml +++ b/ModManager/Views/Components/ModDescriptionDisplay.xaml @@ -19,7 +19,7 @@ - + @@ -44,11 +44,11 @@ Orientation="Horizontal" VerticalAlignment="Center"> - - @@ -66,7 +66,7 @@ BorderBrush="{DynamicResource InteractiveComponentColorBrush_Light}" Visibility="{Binding ShowImage, Converter={StaticResource BoolToVis}}" Width="{Binding DescriptionTextWidth, UpdateSourceTrigger=PropertyChanged}"> - @@ -251,7 +251,7 @@ Text="{Binding TextManager[HEADER_FOLDER_SIZE].Text}" Style="{StaticResource IMYA_TEXT}" /> + Text="{Binding Mod.Size, UpdateSourceTrigger=PropertyChanged}" /> @@ -320,9 +320,9 @@ - - diff --git a/ModManager/Views/Components/ModDescriptionDisplay.xaml.cs b/ModManager/Views/Components/ModDescriptionDisplay.xaml.cs index 24a9d6da..635089b2 100644 --- a/ModManager/Views/Components/ModDescriptionDisplay.xaml.cs +++ b/ModManager/Views/Components/ModDescriptionDisplay.xaml.cs @@ -5,10 +5,9 @@ using System.Runtime.CompilerServices; using System.Windows; using System.Windows.Controls; -using Imya.Enums; +using Anno.EasyMod.Mods; +using Anno.EasyMod.Metadata; using Imya.Models; -using Imya.Models.ModMetadata; -using Imya.Models.Mods; using Imya.Texts; using Imya.Utils; @@ -22,19 +21,19 @@ namespace Imya.UI.Components public partial class ModDescriptionDisplay : UserControl, INotifyPropertyChanged { #region Mod properties - public Mod? Mod + public IMod? Mod { get => _mod; private set => SetProperty(ref _mod, value); } - private Mod? _mod; + private IMod? _mod; - public Mod? ParentMod + public IMod? ParentMod { get => _parentMod; private set => SetProperty(ref _parentMod, value); } - private Mod? _parentMod; + private IMod? _parentMod; // Needs retrigger of OnPropertyChanged on language change public DlcId[] DlcIds @@ -127,6 +126,13 @@ public String? MarkdownDescription } private String? _markdownDescription; + public Uri? ImagePath + { + get => _imagePath; + set => SetProperty(ref _imagePath, value); + } + private Uri? _imagePath; + public ITextManager TextManager { get; init; } public double WindowWidth { get; private set; } @@ -150,28 +156,32 @@ public ModDescriptionDisplay(ITextManager textManager) TextManager.LanguageChanged += OnLanguageChanged; } - private void SetDescription(Mod? mod) + private void SetDescription(IMod? mod) { // TODO is it really necessary to trigger all invidiual fields? - ShowKnownIssues = mod?.HasKnownIssues ?? false; - ShowDescription = mod?.HasDescription ?? false; + ShowKnownIssues = mod?.Modinfo.KnownIssues is not null; + ShowDescription = mod?.Modinfo.Description is not null; ShowExtraInfo = mod is not null; - ShowVersion = (mod?.HasVersion ?? false) || (mod?.HasCreator ?? false); - ShowDlcDeps = mod?.HasDlcDependencies ?? false; - ShowModID = Properties.Settings.Default.ModCreatorMode && (mod?.HasModID ?? false); + ShowVersion = (mod?.Version is not null) || (mod?.Modinfo.CreatorName is not null); + ShowDlcDeps = mod?.Modinfo.DLCDependencies is not null; + ShowModID = Properties.Settings.Default.ModCreatorMode; DlcIds = Mod?.Modinfo.DLCDependencies?.Where(x => x?.DLC != null).Select(x => (DlcId)x.DLC!).OrderBy(x => x).ToArray() ?? Array.Empty(); // the default behavior for images is different: // If the mod does not have an image, it will show a placeholder. // Only hide the image in case there is no displayed mod. - ShowImage = mod is not null; + + ImagePath = mod is not null + ? mod.Image + : null; + ShowImage = true; AdjustDocumentWidth(); UpdateDescription(); } - public void SetDisplayedMod(Mod? mod) + public void SetDisplayedMod(IMod? mod) { Mod = mod; ParentMod = mod; @@ -220,7 +230,7 @@ private void UpdateDescription() UseMarkdownDescription = false; MarkdownDescription = null; - if (Mod is null || !Mod.HasDescription || Mod.Modinfo.Description?.Text is not String description) + if (Mod is null || Mod.Modinfo.Description?.ToString() is not String description) return; if (description.StartsWith("file::")) diff --git a/ModManager/Views/Components/ModList.xaml b/ModManager/Views/Components/ModList.xaml index 9cf8189e..bc1e3786 100644 --- a/ModManager/Views/Components/ModList.xaml +++ b/ModManager/Views/Components/ModList.xaml @@ -132,9 +132,9 @@ - - + - - /// Either the only or the first mod in the current selection /// - public Mod? CurrentlySelectedMod { get; private set; } = null; - public IEnumerable? CurrentlySelectedMods { get; private set; } = null; + public IMod? CurrentlySelectedMod { get; private set; } = null; + public IEnumerable? CurrentlySelectedMods { get; private set; } = null; public BindableModCollection Mods { get; init; } @@ -88,7 +88,7 @@ public async void DeactivateSelection() public async void DeleteSelection() { - await Mods.Model.DeleteAsync(ListBox_ModList.SelectedItems.OfType().Select(x => x.Model).ToArray()); + await Mods.Model.RemoveAsync(ListBox_ModList.SelectedItems.OfType().Select(x => x.Model).ToArray()); OnSelectionChanged(); } @@ -105,11 +105,11 @@ public void EnableExtendedSelection() private void OnSearchRequest(object sender, TextChangedEventArgs e) { string filterText = SearchTextBox.Text; - Mods.Filter = string.IsNullOrWhiteSpace(filterText) ? null : x => x.HasKeywords(filterText); + Mods.Filter = string.IsNullOrWhiteSpace(filterText) ? null : x => x.Name.Contains(filterText); } public event ModListSelectionChangedHandler? ModList_SelectionChanged; - public delegate void ModListSelectionChangedHandler(Mod? mod); + public delegate void ModListSelectionChangedHandler(IMod? mod); #region INotifyPropertyChangedMembers public event PropertyChangedEventHandler? PropertyChanged = delegate { }; diff --git a/ModManager/Views/Components/ModTweaker.xaml b/ModManager/Views/Components/ModTweaker.xaml index 7712c822..f7eeb74f 100644 --- a/ModManager/Views/Components/ModTweaker.xaml +++ b/ModManager/Views/Components/ModTweaker.xaml @@ -33,11 +33,11 @@ Orientation="Horizontal" VerticalAlignment="Center"> - - _currentMod; set @@ -47,7 +47,7 @@ public Mod? CurrentMod OnPropertyChanged(nameof(CurrentMod)); } } - private Mod? _currentMod; + private IMod? _currentMod; public ModTweaker( ITextManager textManager, @@ -75,7 +75,7 @@ private void OnVisibleChanged(object sender, DependencyPropertyChangedEventArgs } } - public void UpdateCurrentDisplay(Mod mod) + public void UpdateCurrentDisplay(IMod mod) { CurrentMod = mod; if (IsVisible) @@ -89,7 +89,7 @@ public void OnAppExit(object sender, ExitEventArgs e) TweakManager.Save(); } - private void LoadTweaks(Mod mod) + private void LoadTweaks(IMod mod) { if (TweakManager.HasUnsavedChanges) { @@ -125,7 +125,7 @@ private void SaveButtonClicked(object sender, RoutedEventArgs e) private void ResetButtonClicked(object sender, RoutedEventArgs e) { - if (CurrentMod is Mod) + if (CurrentMod is IMod) Reload(); } diff --git a/ModManager/Views/GithubBrowserView.xaml.cs b/ModManager/Views/GithubBrowserView.xaml.cs index 09f2ea1d..b73f7883 100644 --- a/ModManager/Views/GithubBrowserView.xaml.cs +++ b/ModManager/Views/GithubBrowserView.xaml.cs @@ -3,7 +3,6 @@ using Imya.GithubIntegration.JsonData; using Imya.Models.Installation; using Imya.Models.Installation.Interfaces; -using Imya.Models.Mods; using Imya.Services.Interfaces; using Imya.Texts; using Imya.UI.Models; diff --git a/ModManager/Views/InstallationView.xaml b/ModManager/Views/InstallationView.xaml index 95634334..7abb3f1a 100644 --- a/ModManager/Views/InstallationView.xaml +++ b/ModManager/Views/InstallationView.xaml @@ -18,8 +18,6 @@ TrueValue="Collapsed" FalseValue="Visible" /> - - @@ -129,7 +127,7 @@ FontWeight="Bold" TextAlignment="Right" Style="{StaticResource IMYA_TEXT}" - Text="{Binding InstallationManager.BytesPerSecondSpeed, Converter={StaticResource DownloadSpeed}}" + Text="{Binding InstallationManager.BytesPerSecondSpeed, Converter={StaticResource SpeedConverter}}" DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:InstallationView}}}" /> @@ -261,7 +259,7 @@ Orientation="Horizontal"> - diff --git a/ModManager/Views/ModActivationView.xaml b/ModManager/Views/ModActivationView.xaml index 2107b11c..e8daeea2 100644 --- a/ModManager/Views/ModActivationView.xaml +++ b/ModManager/Views/ModActivationView.xaml @@ -40,11 +40,13 @@ UpdateSourceTrigger="PropertyChanged" /> - - diff --git a/ModManager/Views/ModActivationView.xaml.cs b/ModManager/Views/ModActivationView.xaml.cs index e8d26f3d..7d0cc14a 100644 --- a/ModManager/Views/ModActivationView.xaml.cs +++ b/ModManager/Views/ModActivationView.xaml.cs @@ -1,5 +1,5 @@ -using Imya.Models; -using Imya.Models.Mods; +using Anno.EasyMod.Mods; +using Imya.Models; using Imya.Services; using Imya.Services.Interfaces; using Imya.Texts; @@ -22,7 +22,7 @@ namespace Imya.UI.Views public partial class ModActivationView : UserControl, INotifyPropertyChanged { public ITextManager TextManager { get; init; } - public ModCollection? Mods { get; private set; } + public IModCollection? Mods { get; private set; } public IGameSetupService GameSetupManager { get; } public ModList ModList { get; init; } @@ -128,7 +128,7 @@ private async void LoadProfileClick(object sender, RoutedEventArgs e) if (dialogResult is true && Dialog.SelectedProfile is not null) { - await Mods.LoadProfileAsync(Dialog.SelectedProfile); + //await Mods.LoadProfileAsync(Dialog.SelectedProfile); } } @@ -146,8 +146,8 @@ private void SaveProfileClick(object sender, RoutedEventArgs e) _profilesService.SaveProfile(profile, dialog.ProfileFilename); } - Mod? _previousSelection = null; - private void OnUpdateSelection(Mod? m) + IMod? _previousSelection = null; + private void OnUpdateSelection(IMod? m) { if (_previousSelection == m) return; diff --git a/ModManager/Views/ModinfoCreatorView.xaml.cs b/ModManager/Views/ModinfoCreatorView.xaml.cs index 235b25a5..529b8d5e 100644 --- a/ModManager/Views/ModinfoCreatorView.xaml.cs +++ b/ModManager/Views/ModinfoCreatorView.xaml.cs @@ -13,12 +13,11 @@ using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; -using Imya.Enums; -using Imya.Models.ModMetadata; using Imya.Texts; using Imya.UI.Popup; using Imya.UI.Utils; using Imya.Utils; +using Anno.EasyMod.Metadata; namespace Imya.UI.Views { @@ -83,15 +82,12 @@ public void OnDlcDeleteClick(object sender, RoutedEventArgs e) public void Load(String Filename) { - if (ModinfoLoader.TryLoadFromFile(Filename, out var _modinfo)) - { - ModinfoFactory = new ModinfoFactory(_modinfo!); - } + throw new NotImplementedException(); } public void Save(String Filename) { - ModinfoLoader.TrySaveToFile(Filename, ModinfoFactory.GetResult()); + throw new NotImplementedException(); } #region INotifyPropertyChangedMembers diff --git a/ModManager/Views/SettingsView.xaml b/ModManager/Views/SettingsView.xaml index 692c68b9..a20d66b1 100644 --- a/ModManager/Views/SettingsView.xaml +++ b/ModManager/Views/SettingsView.xaml @@ -22,7 +22,6 @@ - @@ -244,13 +243,13 @@ - - + + + lib\Anno.EasyMod.dll + + + diff --git a/ModManager_Classes/Models/Attributes/AttributeCollection.cs b/ModManager_Classes/Models/Attributes/AttributeCollection.cs index 4f49ba49..883bfd99 100644 --- a/ModManager_Classes/Models/Attributes/AttributeCollection.cs +++ b/ModManager_Classes/Models/Attributes/AttributeCollection.cs @@ -4,6 +4,7 @@ namespace Imya.Models.Attributes { +#if false public class AttributeCollection : ObservableCollection { private object _lock = new Object(); @@ -20,12 +21,12 @@ public void AddAttribute(IAttribute attrib) } } - public IAttribute? GetByType(AttributeType type) + public IAttribute? GetByType(AttributeTypes type) { return this.FirstOrDefault(x => x.AttributeType == type); } - public bool HasAttribute(AttributeType type) + public bool HasAttribute(AttributeTypes type) { return this.Any(x => x?.AttributeType == type); } @@ -35,11 +36,12 @@ public void RemoveAttribute(IAttribute attrib) Remove(attrib); } - public void RemoveAttributesByType(AttributeType type) + public void RemoveAttributesByType(AttributeTypes type) { var items = this.Where(x => x.AttributeType == type).ToArray(); foreach (var item in items) Remove(item); } } +#endif } diff --git a/ModManager_Classes/Models/Attributes/AttributeTypes.cs b/ModManager_Classes/Models/Attributes/AttributeTypes.cs new file mode 100644 index 00000000..206bc09e --- /dev/null +++ b/ModManager_Classes/Models/Attributes/AttributeTypes.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Imya.Models.Attributes +{ + public static class AttributeTypes + { + public const String ModStatus = "ModStatus"; + public const String ModCompabilityIssue = "ModCompabilityIssue"; + public const String UnresolvedDependencyIssue = "UnresolvedDependencyIssue"; + public const String TweakedMod = "TweakedMod"; + public const String MissingModinfo = "MissingModinfo"; + public const String ModContentInSubfolder = "ModContentInSubfolder"; + public const String IssueModRemoved = "IssueModRemoved"; + public const String IssueModAccess = "IssueModAccess"; + public const String IssueNoDelete = "IssueNoDelete"; + public const String ModReplacedByIssue = "ModReplacedByIssue"; + public const String CyclicDependency = "CyclicDependency"; + public const String DlcNotOwned = "DlcNotOwned"; + } +} diff --git a/ModManager_Classes/Models/Attributes/ConcreteAttributes/GenericAttribute.cs b/ModManager_Classes/Models/Attributes/ConcreteAttributes/GenericAttribute.cs index 6dbe0fee..91b47c2f 100644 --- a/ModManager_Classes/Models/Attributes/ConcreteAttributes/GenericAttribute.cs +++ b/ModManager_Classes/Models/Attributes/ConcreteAttributes/GenericAttribute.cs @@ -1,4 +1,5 @@ -using System; +using Anno.EasyMod.Attributes; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -6,11 +7,11 @@ namespace Imya.Models.Attributes { - public class GenericAttribute : IAttribute + public class GenericAttribute : IModAttribute { - public AttributeType AttributeType { get; init; } + public string AttributeType { get; init; } public IText Description { get; init; } - bool IAttribute.MultipleAllowed => false; + bool IModAttribute.MultipleAllowed => false; } } diff --git a/ModManager_Classes/Models/Attributes/ConcreteAttributes/GenericModContextAttribute.cs b/ModManager_Classes/Models/Attributes/ConcreteAttributes/GenericModContextAttribute.cs index bdb57fc2..2ec3ffd3 100644 --- a/ModManager_Classes/Models/Attributes/ConcreteAttributes/GenericModContextAttribute.cs +++ b/ModManager_Classes/Models/Attributes/ConcreteAttributes/GenericModContextAttribute.cs @@ -1,19 +1,20 @@ -using Imya.Models.Mods; +using Anno.EasyMod.Attributes; +using Anno.EasyMod.Mods; using Imya.Utils; namespace Imya.Models.Attributes { - public class GenericModContextAttribute : IAttribute + public class GenericModContextAttribute : IModAttribute { - public AttributeType AttributeType { get; init; } + public string AttributeType { get; init; } public IText Description { get; init; } - public IEnumerable Context { get; init; } + public IEnumerable Context { get; init; } - bool IAttribute.MultipleAllowed => true; + bool IModAttribute.MultipleAllowed => true; public GenericModContextAttribute() { - Context = Enumerable.Empty(); + Context = Enumerable.Empty(); } } } diff --git a/ModManager_Classes/Models/Attributes/ConcreteAttributes/ModDependencyIssueAttribute.cs b/ModManager_Classes/Models/Attributes/ConcreteAttributes/ModDependencyIssueAttribute.cs index aee2ca89..de5cabf8 100644 --- a/ModManager_Classes/Models/Attributes/ConcreteAttributes/ModDependencyIssueAttribute.cs +++ b/ModManager_Classes/Models/Attributes/ConcreteAttributes/ModDependencyIssueAttribute.cs @@ -1,18 +1,13 @@ -using Imya.Utils; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Anno.EasyMod.Attributes; namespace Imya.Models.Attributes { - public class ModDependencyIssueAttribute : IAttribute + public class ModDependencyIssueAttribute : IModAttribute { - public AttributeType AttributeType { get; init; } = AttributeType.UnresolvedDependencyIssue; + public string AttributeType { get; init; } = AttributeTypes.UnresolvedDependencyIssue; public IText Description { get; init; } - bool IAttribute.MultipleAllowed => true; + bool IModAttribute.MultipleAllowed => true; public IEnumerable UnresolvedDependencies { get; init; } diff --git a/ModManager_Classes/Models/Attributes/ConcreteAttributes/ModReplacedByIssueAttribute.cs b/ModManager_Classes/Models/Attributes/ConcreteAttributes/ModReplacedByIssueAttribute.cs index 16335a59..4aab11d1 100644 --- a/ModManager_Classes/Models/Attributes/ConcreteAttributes/ModReplacedByIssueAttribute.cs +++ b/ModManager_Classes/Models/Attributes/ConcreteAttributes/ModReplacedByIssueAttribute.cs @@ -1,18 +1,19 @@ -using Imya.Models.Mods; -using Imya.Utils; + +using Anno.EasyMod.Attributes; +using Anno.EasyMod.Mods; namespace Imya.Models.Attributes { - public class ModReplacedByIssue : IAttribute + public class ModReplacedByIssue : IModAttribute { - public AttributeType AttributeType { get; } = AttributeType.ModReplacedByIssue; + public string AttributeType { get; } = AttributeTypes.ModReplacedByIssue; public IText Description { get; init; } - bool IAttribute.MultipleAllowed => true; + bool IModAttribute.MultipleAllowed => true; - readonly Mod _replacedBy; + readonly IMod _replacedBy; - public ModReplacedByIssue(Mod replacedBy) + public ModReplacedByIssue(IMod replacedBy) { _replacedBy = replacedBy; } diff --git a/ModManager_Classes/Models/Attributes/Factories/ContentInSubfolderAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Factories/ContentInSubfolderAttributeFactory.cs index cac47831..7924536d 100644 --- a/ModManager_Classes/Models/Attributes/Factories/ContentInSubfolderAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Factories/ContentInSubfolderAttributeFactory.cs @@ -1,10 +1,6 @@ -using Imya.Models.Attributes.Interfaces; +using Anno.EasyMod.Attributes; +using Imya.Models.Attributes.Interfaces; using Imya.Texts; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Imya.Models.Attributes.Factories { @@ -19,11 +15,11 @@ public ContentInSubfolderAttributeFactory(ITextManager textManager) Subfolder = new GenericAttribute() { - AttributeType = AttributeType.ModContentInSubfolder, + AttributeType = AttributeTypes.ModContentInSubfolder, Description = _textManager.GetText("ATTRIBUTE_MODCONTENTSUBFOLDER") }; } - public IAttribute Get() => Subfolder; + public IModAttribute Get() => Subfolder; } } diff --git a/ModManager_Classes/Models/Attributes/Factories/CyclicDependencyAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Factories/CyclicDependencyAttributeFactory.cs index eb11865f..32f79b28 100644 --- a/ModManager_Classes/Models/Attributes/Factories/CyclicDependencyAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Factories/CyclicDependencyAttributeFactory.cs @@ -1,5 +1,6 @@ -using Imya.Models.Attributes.Interfaces; -using Imya.Models.Mods; +using Anno.EasyMod.Attributes; +using Anno.EasyMod.Mods; +using Imya.Models.Attributes.Interfaces; using Imya.Texts; using Imya.Utils; using System; @@ -18,14 +19,14 @@ public CyclicDependencyAttributeFactory(ITextManager textManager) _textManager = textManager; } - public IAttribute Get(IEnumerable context) + public IModAttribute Get(IEnumerable context) { return new GenericModContextAttribute() { - AttributeType = AttributeType.CyclicDependency, + AttributeType = AttributeTypes.CyclicDependency, Description = new SimpleText( string.Format(_textManager.GetText("ATTRIBUTE_CYCLIC_DEPENDENCY").Text, - string.Join(',', context.Select(x => $"[{x.Category}] {x.Name}")))), + string.Join(',', context.Select(x => $"[{x.Modinfo.Category}] {x.Name}")))), Context = context }; } diff --git a/ModManager_Classes/Models/Attributes/Factories/MissingModinfoAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Factories/MissingModinfoAttributeFactory.cs index 64445775..f599013d 100644 --- a/ModManager_Classes/Models/Attributes/Factories/MissingModinfoAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Factories/MissingModinfoAttributeFactory.cs @@ -1,11 +1,6 @@ -using Imya.Models.Attributes.Interfaces; +using Anno.EasyMod.Attributes; +using Imya.Models.Attributes.Interfaces; using Imya.Texts; -using Imya.Utils; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Imya.Models.Attributes.Factories { @@ -19,11 +14,11 @@ public MissingModinfoAttributeFactory(ITextManager textManager) _textManager = textManager; } - public IAttribute Get() + public IModAttribute Get() { return new GenericAttribute() { - AttributeType = AttributeType.MissingModinfo, + AttributeType = AttributeTypes.MissingModinfo, Description = _textManager.GetText("ATTRIBUTE_NOMODINFO") }; } diff --git a/ModManager_Classes/Models/Attributes/Factories/ModAccessIssueAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Factories/ModAccessIssueAttributeFactory.cs index cd8a6743..21dadc5d 100644 --- a/ModManager_Classes/Models/Attributes/Factories/ModAccessIssueAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Factories/ModAccessIssueAttributeFactory.cs @@ -1,6 +1,6 @@ -using Imya.Models.Attributes.Interfaces; +using Anno.EasyMod.Attributes; +using Imya.Models.Attributes.Interfaces; using Imya.Texts; -using Imya.Utils; namespace Imya.Models.Attributes.Factories { @@ -15,7 +15,7 @@ public ModAccessIssueAttributeFactory(ITextManager textManager) GenericAttribute ModAccessIssueAttribute = new GenericAttribute() { - AttributeType = AttributeType.IssueModAccess, + AttributeType = AttributeTypes.IssueModAccess, //Description = TextManager.Instance.GetText("ATTRIBUTE_NOMODINFO") Description = new SimpleText("Access to the Folder is denied. Please close all programs accessing this folder and retry.") }; @@ -23,17 +23,17 @@ public ModAccessIssueAttributeFactory(ITextManager textManager) GenericAttribute ModAccessIssue_NoDeleteAttribute = new GenericAttribute() { - AttributeType = AttributeType.IssueModAccess, + AttributeType = AttributeTypes.IssueModAccess, //Description = TextManager.Instance.GetText("ATTRIBUTE_NOMODINFO") Description = new SimpleText("Could not delete this mod.") }; - public IAttribute Get() + public IModAttribute Get() { return ModAccessIssueAttribute; } - public IAttribute GetNoDelete() + public IModAttribute GetNoDelete() { return ModAccessIssue_NoDeleteAttribute; } diff --git a/ModManager_Classes/Models/Attributes/Factories/ModCompabilityAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Factories/ModCompabilityAttributeFactory.cs index 417b54c5..340478c4 100644 --- a/ModManager_Classes/Models/Attributes/Factories/ModCompabilityAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Factories/ModCompabilityAttributeFactory.cs @@ -1,5 +1,6 @@ -using Imya.Models.Attributes.Interfaces; -using Imya.Models.Mods; +using Anno.EasyMod.Attributes; +using Anno.EasyMod.Mods; +using Imya.Models.Attributes.Interfaces; using Imya.Texts; using Imya.Utils; using System; @@ -18,14 +19,14 @@ public ModCompabilityAttributeFactory(ITextManager textManager) _textManager = textManager; } - public IAttribute Get(IEnumerable context) + public IModAttribute Get(IEnumerable context) { return new GenericModContextAttribute() { - AttributeType = AttributeType.ModCompabilityIssue, + AttributeType = AttributeTypes.ModCompabilityIssue, Description = new SimpleText( string.Format(_textManager.GetText("ATTRIBUTE_COMPABILITYERROR").Text, - string.Join("\n - ", context.Select(x => $"[{x.Category}] {x.Name}")))), + string.Join("\n - ", context.Select(x => $"[{x.Modinfo.Category}] {x.Name}")))), Context = context }; } diff --git a/ModManager_Classes/Models/Attributes/Factories/ModDependencyIssueAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Factories/ModDependencyIssueAttributeFactory.cs index d6cdc549..5bcb2fae 100644 --- a/ModManager_Classes/Models/Attributes/Factories/ModDependencyIssueAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Factories/ModDependencyIssueAttributeFactory.cs @@ -1,5 +1,5 @@ -using Imya.Models.Attributes.Interfaces; -using Imya.Models.Mods; +using Anno.EasyMod.Attributes; +using Imya.Models.Attributes.Interfaces; using Imya.Texts; using System; using System.Collections.Generic; @@ -17,12 +17,12 @@ public ModDependencyIssueAttributeFactory(ITextManager textManager) _textManager = textManager; } - public IAttribute Get(IEnumerable context) + public IModAttribute Get(IEnumerable context) { String text = _textManager.GetText("ATTRIBUTE_MISSINGDEPENDENCY")?.Text ?? ""; return new ModDependencyIssueAttribute() { - AttributeType = AttributeType.UnresolvedDependencyIssue, + AttributeType = AttributeTypes.UnresolvedDependencyIssue, Description = new SimpleText(string.Format(text, string.Join("\n - ", context))), UnresolvedDependencies = context }; diff --git a/ModManager_Classes/Models/Attributes/Factories/ModReplacedByAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Factories/ModReplacedByAttributeFactory.cs index f160b58f..d9aaeb5f 100644 --- a/ModManager_Classes/Models/Attributes/Factories/ModReplacedByAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Factories/ModReplacedByAttributeFactory.cs @@ -1,5 +1,6 @@ -using Imya.Models.Attributes.Interfaces; -using Imya.Models.Mods; +using Anno.EasyMod.Attributes; +using Anno.EasyMod.Mods; +using Imya.Models.Attributes.Interfaces; using Imya.Texts; using System; using System.Collections.Generic; @@ -17,7 +18,7 @@ public ModReplacedByAttributeFactory(ITextManager textManager) _textManager = textManager; } - public IAttribute Get(Mod replacedBy) + public IModAttribute Get(IMod replacedBy) { return new ModReplacedByIssue(replacedBy) { diff --git a/ModManager_Classes/Models/Attributes/Factories/ModStatusAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Factories/ModStatusAttributeFactory.cs index 6625a89c..dddb0fb9 100644 --- a/ModManager_Classes/Models/Attributes/Factories/ModStatusAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Factories/ModStatusAttributeFactory.cs @@ -1,4 +1,5 @@ -using Imya.Models.Attributes.Interfaces; +using Anno.EasyMod.Attributes; +using Imya.Models.Attributes.Interfaces; using Imya.Texts; using Imya.Utils; using System; @@ -22,7 +23,7 @@ public enum ModStatus public class ModStatusAttributeFactory : IModStatusAttributeFactory { private readonly ITextManager _textManager; - private Dictionary> static_attribs; + private Dictionary> static_attribs; public ModStatusAttributeFactory(ITextManager textManager) { _textManager = textManager; @@ -33,7 +34,7 @@ public ModStatusAttributeFactory(ITextManager textManager) ModStatus.Default, () => new ModStatusAttribute() { - AttributeType = AttributeType.ModStatus, + AttributeType = AttributeTypes.ModStatus, Description = new SimpleText("Mod Status Attribute"), Status = ModStatus.Default } @@ -42,7 +43,7 @@ public ModStatusAttributeFactory(ITextManager textManager) ModStatus.New, () => new ModStatusAttribute() { - AttributeType = AttributeType.ModStatus, + AttributeType = AttributeTypes.ModStatus, Description = _textManager.GetText("ATTRIBUTE_STATUS_NEW"), Status = ModStatus.New } @@ -51,7 +52,7 @@ public ModStatusAttributeFactory(ITextManager textManager) ModStatus.Updated, () => new ModStatusAttribute() { - AttributeType = AttributeType.ModStatus, + AttributeType = AttributeTypes.ModStatus, Description = _textManager.GetText("ATTRIBUTE_STATUS_UPDATE"), Status = ModStatus.Updated } @@ -60,7 +61,7 @@ public ModStatusAttributeFactory(ITextManager textManager) ModStatus.Obsolete, () => new ModStatusAttribute() { - AttributeType = AttributeType.ModStatus, + AttributeType = AttributeTypes.ModStatus, Description = _textManager.GetText("ATTRIBUTE_STATUS_OBSOLETE"), Status = ModStatus.Obsolete } @@ -68,7 +69,7 @@ public ModStatusAttributeFactory(ITextManager textManager) }; } - public IAttribute Get(ModStatus status) + public IModAttribute Get(ModStatus status) { return static_attribs[status].Invoke(); } diff --git a/ModManager_Classes/Models/Attributes/Factories/RemovedFolderAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Factories/RemovedFolderAttributeFactory.cs index 056ce08f..cbda951f 100644 --- a/ModManager_Classes/Models/Attributes/Factories/RemovedFolderAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Factories/RemovedFolderAttributeFactory.cs @@ -1,4 +1,5 @@ -using Imya.Models.Attributes.Interfaces; +using Anno.EasyMod.Attributes; +using Imya.Models.Attributes.Interfaces; using Imya.Texts; using Imya.Utils; @@ -14,13 +15,13 @@ public RemovedFolderAttributeFactory(ITextManager textManager) RemovedFolderAttribute = new GenericAttribute() { - AttributeType = AttributeType.IssueModRemoved, + AttributeType = AttributeTypes.IssueModRemoved, //Description = TextManager.Instance.GetText("ATTRIBUTE_TWEAKED") Description = new SimpleText("This mod has been removed by another program") }; } - public IAttribute Get() + public IModAttribute Get() { return RemovedFolderAttribute; } diff --git a/ModManager_Classes/Models/Attributes/Factories/TweakedAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Factories/TweakedAttributeFactory.cs index fe65a78e..6564a53e 100644 --- a/ModManager_Classes/Models/Attributes/Factories/TweakedAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Factories/TweakedAttributeFactory.cs @@ -1,4 +1,5 @@ -using Imya.Models.Attributes.Interfaces; +using Anno.EasyMod.Attributes; +using Imya.Models.Attributes.Interfaces; using Imya.Texts; using Imya.Utils; @@ -15,12 +16,12 @@ public TweakedAttributeFactory(ITextManager textManager) TweakedAttribute = new GenericAttribute() { - AttributeType = AttributeType.TweakedMod, + AttributeType = AttributeTypes.TweakedMod, Description = _textManager.GetText("ATTRIBUTE_TWEAKED") }; } - public IAttribute Get() + public IModAttribute Get() { return TweakedAttribute; } diff --git a/ModManager_Classes/Models/Attributes/IAttribute.cs b/ModManager_Classes/Models/Attributes/IAttribute.cs deleted file mode 100644 index e40dcaf8..00000000 --- a/ModManager_Classes/Models/Attributes/IAttribute.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Imya.Models.Attributes -{ - public enum AttributeType - { - ModStatus, - ModCompabilityIssue, - UnresolvedDependencyIssue, - TweakedMod, - MissingModinfo, - ModContentInSubfolder, - IssueModRemoved, - IssueModAccess, - IssueNoDelete, - ModReplacedByIssue, - CyclicDependency, - DlcNotOwned - } - - public interface IAttribute - { - AttributeType AttributeType { get; } - IText Description { get; } - - bool MultipleAllowed { get; } - } -} diff --git a/ModManager_Classes/Models/Attributes/Interfaces/IContentInSubfolderAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Interfaces/IContentInSubfolderAttributeFactory.cs index 575b1c71..0bc96a1d 100644 --- a/ModManager_Classes/Models/Attributes/Interfaces/IContentInSubfolderAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Interfaces/IContentInSubfolderAttributeFactory.cs @@ -1,7 +1,8 @@ -namespace Imya.Models.Attributes.Interfaces +using Anno.EasyMod.Attributes; +namespace Imya.Models.Attributes.Interfaces { public interface IContentInSubfolderAttributeFactory { - IAttribute Get(); + IModAttribute Get(); } } \ No newline at end of file diff --git a/ModManager_Classes/Models/Attributes/Interfaces/ICyclicDependencyAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Interfaces/ICyclicDependencyAttributeFactory.cs index 8199a2f0..4d159ee5 100644 --- a/ModManager_Classes/Models/Attributes/Interfaces/ICyclicDependencyAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Interfaces/ICyclicDependencyAttributeFactory.cs @@ -1,9 +1,10 @@ -using Imya.Models.Mods; +using Anno.EasyMod.Attributes; +using Anno.EasyMod.Mods; namespace Imya.Models.Attributes.Interfaces { public interface ICyclicDependencyAttributeFactory { - IAttribute Get(IEnumerable context); + IModAttribute Get(IEnumerable context); } } \ No newline at end of file diff --git a/ModManager_Classes/Models/Attributes/Interfaces/IMissingModinfoAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Interfaces/IMissingModinfoAttributeFactory.cs index 8f7d9fcc..c38bfa79 100644 --- a/ModManager_Classes/Models/Attributes/Interfaces/IMissingModinfoAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Interfaces/IMissingModinfoAttributeFactory.cs @@ -1,7 +1,9 @@ -namespace Imya.Models.Attributes.Interfaces +using Anno.EasyMod.Attributes; + +namespace Imya.Models.Attributes.Interfaces { public interface IMissingModinfoAttributeFactory { - IAttribute Get(); + IModAttribute Get(); } } \ No newline at end of file diff --git a/ModManager_Classes/Models/Attributes/Interfaces/IModAccessIssueAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Interfaces/IModAccessIssueAttributeFactory.cs index d7de9023..5779f77a 100644 --- a/ModManager_Classes/Models/Attributes/Interfaces/IModAccessIssueAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Interfaces/IModAccessIssueAttributeFactory.cs @@ -1,8 +1,10 @@ -namespace Imya.Models.Attributes.Interfaces +using Anno.EasyMod.Attributes; + +namespace Imya.Models.Attributes.Interfaces { public interface IModAccessIssueAttributeFactory { - IAttribute Get(); - IAttribute GetNoDelete(); + IModAttribute Get(); + IModAttribute GetNoDelete(); } } \ No newline at end of file diff --git a/ModManager_Classes/Models/Attributes/Interfaces/IModCompabilityAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Interfaces/IModCompabilityAttributeFactory.cs index fc1e88c7..cec692d1 100644 --- a/ModManager_Classes/Models/Attributes/Interfaces/IModCompabilityAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Interfaces/IModCompabilityAttributeFactory.cs @@ -1,9 +1,10 @@ -using Imya.Models.Mods; +using Anno.EasyMod.Attributes; +using Anno.EasyMod.Mods; namespace Imya.Models.Attributes.Interfaces { public interface IModCompabilityAttributeFactory { - IAttribute Get(IEnumerable context); + IModAttribute Get(IEnumerable context); } } \ No newline at end of file diff --git a/ModManager_Classes/Models/Attributes/Interfaces/IModDependencyIssueAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Interfaces/IModDependencyIssueAttributeFactory.cs index b232cb2a..8c042267 100644 --- a/ModManager_Classes/Models/Attributes/Interfaces/IModDependencyIssueAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Interfaces/IModDependencyIssueAttributeFactory.cs @@ -1,9 +1,9 @@ -using Imya.Models.Mods; +using Anno.EasyMod.Attributes; namespace Imya.Models.Attributes.Interfaces { public interface IModDependencyIssueAttributeFactory { - IAttribute Get(IEnumerable context); + IModAttribute Get(IEnumerable context); } } \ No newline at end of file diff --git a/ModManager_Classes/Models/Attributes/Interfaces/IModReplacedByAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Interfaces/IModReplacedByAttributeFactory.cs index aa994ab6..715794d3 100644 --- a/ModManager_Classes/Models/Attributes/Interfaces/IModReplacedByAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Interfaces/IModReplacedByAttributeFactory.cs @@ -1,9 +1,10 @@ -using Imya.Models.Mods; +using Anno.EasyMod.Attributes; +using Anno.EasyMod.Mods; namespace Imya.Models.Attributes.Interfaces { public interface IModReplacedByAttributeFactory { - IAttribute Get(Mod replacedBy); + IModAttribute Get(IMod replacedBy); } } \ No newline at end of file diff --git a/ModManager_Classes/Models/Attributes/Interfaces/IModStatusAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Interfaces/IModStatusAttributeFactory.cs index 4d036811..0d63ed9f 100644 --- a/ModManager_Classes/Models/Attributes/Interfaces/IModStatusAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Interfaces/IModStatusAttributeFactory.cs @@ -1,9 +1,10 @@ -using Imya.Models.Attributes.Factories; +using Anno.EasyMod.Attributes; +using Imya.Models.Attributes.Factories; namespace Imya.Models.Attributes.Interfaces { public interface IModStatusAttributeFactory { - IAttribute Get(ModStatus status); + IModAttribute Get(ModStatus status); } } \ No newline at end of file diff --git a/ModManager_Classes/Models/Attributes/Interfaces/IRemovedFolderAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Interfaces/IRemovedFolderAttributeFactory.cs index 27498a3b..3bba8752 100644 --- a/ModManager_Classes/Models/Attributes/Interfaces/IRemovedFolderAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Interfaces/IRemovedFolderAttributeFactory.cs @@ -1,7 +1,9 @@ -namespace Imya.Models.Attributes.Interfaces +using Anno.EasyMod.Attributes; + +namespace Imya.Models.Attributes.Interfaces { public interface IRemovedFolderAttributeFactory { - IAttribute Get(); + IModAttribute Get(); } } \ No newline at end of file diff --git a/ModManager_Classes/Models/Attributes/Interfaces/ITweakedAttributeFactory.cs b/ModManager_Classes/Models/Attributes/Interfaces/ITweakedAttributeFactory.cs index d9fb3f2c..119e7009 100644 --- a/ModManager_Classes/Models/Attributes/Interfaces/ITweakedAttributeFactory.cs +++ b/ModManager_Classes/Models/Attributes/Interfaces/ITweakedAttributeFactory.cs @@ -1,7 +1,9 @@ -namespace Imya.Models.Attributes.Interfaces +using Anno.EasyMod.Attributes; + +namespace Imya.Models.Attributes.Interfaces { public interface ITweakedAttributeFactory { - IAttribute Get(); + IModAttribute Get(); } } \ No newline at end of file diff --git a/ModManager_Classes/Models/ModActivationProfile.cs b/ModManager_Classes/Models/ModActivationProfile.cs index de5ba2ef..090ca318 100644 --- a/ModManager_Classes/Models/ModActivationProfile.cs +++ b/ModManager_Classes/Models/ModActivationProfile.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using Imya.Models.Mods; namespace Imya.Models { diff --git a/ModManager_Classes/Models/ModComparer.cs b/ModManager_Classes/Models/ModComparer.cs index f523fb7e..d8c5b40e 100644 --- a/ModManager_Classes/Models/ModComparer.cs +++ b/ModManager_Classes/Models/ModComparer.cs @@ -3,15 +3,15 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using Imya.Models.Mods; +using Anno.EasyMod.Mods; namespace Imya.Models { - public class CompareByActiveCategoryName : IComparer + public class CompareByActiveCategoryName : IComparer { public readonly static CompareByActiveCategoryName Default = new(); - public int Compare(Mod? x, Mod? y) + public int Compare(IMod? x, IMod? y) { if (y is null && x is null) return 0; if (x is null) return -1; @@ -25,11 +25,11 @@ public int Compare(Mod? x, Mod? y) } } - public class CompareByActive : IComparer + public class CompareByActive : IComparer { public readonly static CompareByActive Default = new(); - public int Compare(Mod? x, Mod? y) + public int Compare(IMod? x, IMod? y) { if (y is null && x is null) return 0; if (x is null) return -1; @@ -39,20 +39,20 @@ public int Compare(Mod? x, Mod? y) } } - public class CompareByCategoryName : IComparer + public class CompareByCategoryName : IComparer { public readonly static CompareByCategoryName Default = new(); - public int Compare(Mod? x, Mod? y) + public int Compare(IMod? x, IMod? y) { if (y is null && x is null) return 0; if (x is null) return -1; if (y is null) return 1; - int category = string.Compare(x.Modinfo?.Category.Text, y.Modinfo?.Category.Text); + int category = string.Compare(x.Modinfo?.Category?.ToString(), y.Modinfo?.Category?.ToString()); if (category != 0) return category; - int name = string.Compare(x.Modinfo?.ModName.Text, y.Modinfo?.ModName.Text); + int name = string.Compare(x.Modinfo?.ModName?.ToString(), y.Modinfo?.ModName?.ToString()); if (name != 0) return name; @@ -60,11 +60,11 @@ public int Compare(Mod? x, Mod? y) } } - public class CompareByFolder : IComparer + public class CompareByFolder : IComparer { public readonly static CompareByFolder Default = new(); - public int Compare(Mod? x, Mod? y) + public int Compare(IMod? x, IMod? y) { if (y is null && x is null) return 0; if (x is null) return -1; @@ -74,11 +74,11 @@ public int Compare(Mod? x, Mod? y) } } - public class ComparebyLoadOrder : IComparer + public class ComparebyLoadOrder : IComparer { public readonly static ComparebyLoadOrder Default = new(); - public int Compare(Mod? x, Mod? y) + public int Compare(IMod? x, IMod? y) { //ignore inactive var byActive = CompareByActive.Default.Compare(x, y); @@ -107,7 +107,8 @@ public int Compare(Mod? x, Mod? y) } private enum Category { LoadAfterNoWildcard, NoLoadAfter, Wildcard } - private Category GetCategory(Mod x) + + private Category GetCategory(IMod x) { if (x.Modinfo.LoadAfterIds is null) return Category.NoLoadAfter; @@ -116,18 +117,18 @@ private Category GetCategory(Mod x) else return Category.LoadAfterNoWildcard; } - private bool IsWildcardDependant(Mod x) + private bool IsWildcardDependant(IMod x) { return x.Modinfo?.LoadAfterIds?.Contains("*") ?? false; } } - public class CompareByLoadAfterID : IComparer + public class CompareByLoadAfterID : IComparer { public readonly static CompareByLoadAfterID Default = new(); - public int Compare(Mod? x, Mod? y) + public int Compare(IMod? x, IMod? y) { if (y is null && x is null) return 0; if (x is null) return -1; diff --git a/ModManager_Classes/Models/ModMetadata/LocalizedModinfo.cs b/ModManager_Classes/Models/ModMetadata/LocalizedModinfo.cs deleted file mode 100644 index 2afb4c48..00000000 --- a/ModManager_Classes/Models/ModMetadata/LocalizedModinfo.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Imya.Models.ModMetadata.ModinfoModel; -using Imya.Texts; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using static System.Net.Mime.MediaTypeNames; - -namespace Imya.Models.ModMetadata -{ - public class LocalizedModinfo : Modinfo - { - public LocalizedModinfo() - { } - - /// - /// Localized mod name or folder name as default. - /// - public new IText ModName { get; init; } - - /// - /// Localized category or "NoCategory" as default. - /// - public new IText Category { get; init; } - public new IText? Description { get; init; } - public new IText[]? KnownIssues { get; init; } - } -} diff --git a/ModManager_Classes/Models/ModMetadata/LocalizedModinfoFactory.cs b/ModManager_Classes/Models/ModMetadata/LocalizedModinfoFactory.cs deleted file mode 100644 index 61ed8942..00000000 --- a/ModManager_Classes/Models/ModMetadata/LocalizedModinfoFactory.cs +++ /dev/null @@ -1,71 +0,0 @@ -using Imya.Models.ModMetadata.ModinfoModel; -using Imya.Texts; -using Octokit; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using static System.Net.Mime.MediaTypeNames; - -namespace Imya.Models.ModMetadata -{ - public class LocalizedModinfoFactory - { - private readonly ITextManager _textManager; - - public LocalizedModinfoFactory( - ITextManager textManager) - { - _textManager = textManager; - } - - public LocalizedModinfo GetLocalizedModinfo(Modinfo m, string folderName) - { - var localized = new LocalizedModinfo - { - Category = _textManager.CreateLocalizedText(m.Category, ""), - ModName = _textManager.CreateLocalizedText(m.ModName, folderName), - Description = _textManager.CreateLocalizedText(m.Description, null), - KnownIssues = m.KnownIssues?.Select(x => _textManager.CreateLocalizedText(x, null)).OfType().ToArray(), - Version = m.Version, - ModID = m.ModID, - IncompatibleIds = m.IncompatibleIds, - DeprecateIds = m.DeprecateIds, - ModDependencies = m.ModDependencies, - DLCDependencies = m.DLCDependencies, - CreatorName = m.CreatorName, - CreatorContact = m.CreatorContact, - Image = m.Image, - LoadAfterIds = m.LoadAfterIds - }; - - return localized; - } - - public LocalizedModinfo GetDummyModinfo(string foldername) - { - bool matches = MatchNameCategory(foldername, out var category, out var name); - var modName = new SimpleText(matches ? name : foldername); - var modCategory = matches ? new SimpleText(category) : _textManager.GetText("MODLIST_NOCATEGORY"); - - return new LocalizedModinfo() - { - Category = modCategory, - ModName = modName - }; - } - - private bool MatchNameCategory(string folderName, out string category, out string name) - { - string CategoryPattern = @"[[][a-z]+[]]"; - category = Regex.Match(folderName, CategoryPattern, RegexOptions.IgnoreCase).Value.TrimStart('[').TrimEnd(']'); - - string NamePattern = @"[^]]*"; - name = Regex.Match(folderName, NamePattern, RegexOptions.RightToLeft).Value.TrimStart(' '); - - return !name.Equals("") && !category.Equals(""); - } - } -} diff --git a/ModManager_Classes/Models/ModMetadata/ModinfoModel/DLC.cs b/ModManager_Classes/Models/ModMetadata/ModinfoModel/DLC.cs deleted file mode 100644 index 1eaa9767..00000000 --- a/ModManager_Classes/Models/ModMetadata/ModinfoModel/DLC.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Imya.Enums; - -namespace Imya.Models.ModMetadata.ModinfoModel -{ - public class Dlc - { - public Dlc() { } - - [JsonConverter(typeof(StringEnumConverter))] - public DlcId? DLC { get; set; } - - [JsonConverter(typeof(StringEnumConverter))] - public DlcRequirement? Dependant { get; set; } - } -} diff --git a/ModManager_Classes/Models/ModMetadata/ModinfoModel/Localized.cs b/ModManager_Classes/Models/ModMetadata/ModinfoModel/Localized.cs deleted file mode 100644 index e23dede9..00000000 --- a/ModManager_Classes/Models/ModMetadata/ModinfoModel/Localized.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Imya.Utils; - -namespace Imya.Models.ModMetadata.ModinfoModel -{ - public class Localized - { - public string? Chinese { get; set; } - public string? English { get; set; } - public string? French { get; set; } - public string? German { get; set; } - public string? Italian { get; set; } - public string? Japanese { get; set; } - public string? Korean { get; set; } - public string? Polish { get; set; } - public string? Russian { get; set; } - public string? Spanish { get; set; } - public string? Taiwanese { get; set; } - - public Localized() { } - - // keep most common languages on top - public bool HasAny() => - English is not null || German is not null || - French is not null || Italian is not null || Polish is not null || Russian is not null || Spanish is not null || - Japanese is not null || Korean is not null || Taiwanese is not null; - } - - public class FakeLocalized : Localized - { - public FakeLocalized(string text) - { - Chinese = text; - English = text; - French = text; - German = text; - Italian = text; - Japanese = text; - Korean = text; - Polish = text; - Russian = text; - Spanish = text; - Taiwanese = text; - } - } -} diff --git a/ModManager_Classes/Models/ModMetadata/ModinfoModel/ModIdActiveTouple.cs b/ModManager_Classes/Models/ModMetadata/ModinfoModel/ModIdActiveTouple.cs deleted file mode 100644 index 58b22573..00000000 --- a/ModManager_Classes/Models/ModMetadata/ModinfoModel/ModIdActiveTouple.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Imya.Models.ModMetadata.ModinfoModel -{ - public class ModIdActiveTouple - { - public ModIdActiveTouple() { } - public string? ModID { get; set; } - public bool? Active { get; set; } - public string? Version { get; set; } - } -} diff --git a/ModManager_Classes/Models/ModMetadata/ModinfoModel/Modinfo.cs b/ModManager_Classes/Models/ModMetadata/ModinfoModel/Modinfo.cs deleted file mode 100644 index dc33a0a0..00000000 --- a/ModManager_Classes/Models/ModMetadata/ModinfoModel/Modinfo.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Imya.Utils; -using Newtonsoft.Json; - -namespace Imya.Models.ModMetadata.ModinfoModel -{ - public class Modinfo - { - public Modinfo() { } - public string? Version { get; set; } - public string? ModID { get; set; } - public string[]? IncompatibleIds { get; set; } - public string[]? DeprecateIds { get; set; } - public string[]? ModDependencies { get; set; } - public Localized? Category { get; set; } - public Localized? ModName { get; set; } - public Localized? Description { get; set; } - public Localized[]? KnownIssues { get; set; } - public Dlc[]? DLCDependencies { get; set; } - public string? CreatorName { get; set; } - public string? CreatorContact { get; set; } - public string? Image { get; set; } - public string[]? LoadAfterIds { get; set; } - } - - /// - /// Localized version of Modinfo. Localized properties are readonly. - /// -} diff --git a/ModManager_Classes/Models/ModTweaker/DataModel/Tweaking/ModTweaks.cs b/ModManager_Classes/Models/ModTweaker/DataModel/Tweaking/ModTweaks.cs index 35f1ac1c..b5798d63 100644 --- a/ModManager_Classes/Models/ModTweaker/DataModel/Tweaking/ModTweaks.cs +++ b/ModManager_Classes/Models/ModTweaker/DataModel/Tweaking/ModTweaks.cs @@ -1,6 +1,6 @@ -using Imya.Models.Attributes; +using Anno.EasyMod.Mods; +using Imya.Models.Attributes; using Imya.Models.Attributes.Factories; -using Imya.Models.Mods; using Imya.Models.NotifyPropertyChanged; using Imya.Services; using System.Collections.ObjectModel; @@ -26,12 +26,12 @@ public IEnumerable? TweakerFiles } private IEnumerable? _tweakerFiles = null; - private Mod? _mod = null; + private IMod? _mod = null; public string ModBaseName { get => _mod.FolderName; } public string ModBasePath { get => _mod.FullModPath; } - public ModTweaks(Mod mod, IEnumerable tweakerFiles) + public ModTweaks(IMod mod, IEnumerable tweakerFiles) { _mod = mod; TweakerFiles = new ObservableCollection(tweakerFiles); diff --git a/ModManager_Classes/Models/ModTweaker/IO/ModTweaksExporter.cs b/ModManager_Classes/Models/ModTweaker/IO/ModTweaksExporter.cs index 65bd9a88..3c13f915 100644 --- a/ModManager_Classes/Models/ModTweaker/IO/ModTweaksExporter.cs +++ b/ModManager_Classes/Models/ModTweaker/IO/ModTweaksExporter.cs @@ -1,5 +1,4 @@ -using Imya.Models.Mods; -using Imya.Models.ModTweaker.DataModel.Storage; +using Imya.Models.ModTweaker.DataModel.Storage; using Imya.Models.ModTweaker.DataModel.Tweaking; using System; using System.Collections.Generic; diff --git a/ModManager_Classes/Models/ModTweaker/IO/ModTweaksLoader.cs b/ModManager_Classes/Models/ModTweaker/IO/ModTweaksLoader.cs index bb3730e2..41072d24 100644 --- a/ModManager_Classes/Models/ModTweaker/IO/ModTweaksLoader.cs +++ b/ModManager_Classes/Models/ModTweaker/IO/ModTweaksLoader.cs @@ -1,25 +1,37 @@ -using Imya.Models.Mods; +using Anno.EasyMod.Mods; using Imya.Models.ModTweaker.DataModel.Storage; using Imya.Models.ModTweaker.DataModel.Tweaking; +using Microsoft.Extensions.Logging; namespace Imya.Models.ModTweaker.IO { public class ModTweaksLoader { private readonly ITweakRepository _tweakRepository; + private readonly ILogger _logger; - public ModTweaksLoader(ITweakRepository tweakRepository) + public ModTweaksLoader(ITweakRepository tweakRepository, + ILogger logger) { _tweakRepository = tweakRepository; + _logger = logger; } - public ModTweaks? Load(Mod mod) + public ModTweaks? Load(IMod mod) { + string[]? files = Array.Empty(); + try + { + var test = mod?.GetFiles(); + files = mod?.EnumerateFiles("*.xml")? + .Where(x => !x.EndsWith("imyatweak.include.xml")) + .Select(x => Path.GetRelativePath(mod.FullModPath, x)) + .ToArray(); - var files = mod.GetFilesWithExtension("xml") - .Where(x => !x.EndsWith("imyatweak.include.xml")) - .Select(x => Path.GetRelativePath(mod.FullModPath, x)) - .ToArray(); + } catch (Exception ex) + { + _logger.LogError("Error during File enumeration!", ex); + } var list = new List(); foreach (string file in files) diff --git a/ModManager_Classes/Models/Mods/IModCollectionFactory.cs b/ModManager_Classes/Models/Mods/IModCollectionFactory.cs deleted file mode 100644 index 84a5757a..00000000 --- a/ModManager_Classes/Models/Mods/IModCollectionFactory.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Imya.Models.Mods -{ - public interface IModCollectionFactory - { - ModCollection Get(string Filepath, - bool normalize = false, - bool loadImages = false, - bool autofixSubfolder = false); - } -} diff --git a/ModManager_Classes/Models/Mods/IModFactory.cs b/ModManager_Classes/Models/Mods/IModFactory.cs deleted file mode 100644 index 45b4bdd1..00000000 --- a/ModManager_Classes/Models/Mods/IModFactory.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Imya.Models.Mods -{ - public interface IModFactory - { - Mod? GetFromFolder(string modFolderPath, bool loadImages = false); - } -} diff --git a/ModManager_Classes/Models/Mods/Mod.cs b/ModManager_Classes/Models/Mods/Mod.cs deleted file mode 100644 index b0e9c5c3..00000000 --- a/ModManager_Classes/Models/Mods/Mod.cs +++ /dev/null @@ -1,307 +0,0 @@ -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Text.RegularExpressions; -using Imya.Utils; -using Imya.Models.NotifyPropertyChanged; -using Imya.Models.Attributes; -using Imya.Models.Attributes.Interfaces; -using Imya.Models.Attributes.Factories; -using Imya.Models.ModMetadata.ModinfoModel; -using Imya.Models.ModMetadata; - -namespace Imya.Models.Mods -{ - public class Mod : PropertyChangedNotifier - { - #region ModLoader info - /// - /// Folder name including activation "-". - /// - public string FullFolderName => (IsActive ? "" : "-") + FolderName; - - /// - /// Folder name excluding activation "-". - /// - public string FolderName { get; private set; } - - /// - /// "-" activation. - /// - public bool IsActive - { - get => _isActive; - set => SetProperty(ref _isActive, value); - } - private bool _isActive; - - /// - /// "-" activation and valid. - /// - public bool IsActiveAndValid => _isActive && !IsRemoved; - - /// - /// Mod disappeared. - /// - public bool IsRemoved - { - get => _isRemoved; - set => SetProperty(ref _isRemoved, value); - } - private bool _isRemoved; - - public bool IsObsolete - { - get => _isObsolete; - set - { - if (_isObsolete == value) - return; - _isObsolete = value; - OnPropertyChanged(nameof(IsObsolete)); - } - } - private bool _isObsolete; - - /// - /// Full path to mod folder. - /// - public string FullModPath => Path.Combine(BasePath, FullFolderName); - public string BasePath { get; private set; } // TODO use ModDirectory as parent and retrieve it from there as soon as it's not a global manager anymore - #endregion - - #region Mandatory Mod Manager info (with defaults) - /// - /// Name without category. - /// - public IText Name => Modinfo.ModName; - - /// - /// Category with default "NoCategory". - /// - public IText Category => Modinfo.Category; - - public string ModID => Modinfo.ModID ?? FolderName; - #endregion - - #region Optional Mod Manager info - public LocalizedModinfo Modinfo { get; private init; } - public ImyaImageSource? Image { get; private set; } - - public Version? Version { get; private init; } - - public bool HasVersion { get => Modinfo.Version is not null; } - public bool HasDescription { get => Modinfo.Description is not null; } - public bool HasKnownIssues { get => Modinfo.KnownIssues is not null && Modinfo.KnownIssues.Length > 0; } - public bool HasDlcDependencies { get => Modinfo.DLCDependencies is not null && Modinfo.DLCDependencies.Length > 0; } - public bool HasCreator { get => Modinfo.CreatorName is not null; } - public bool HasImage { get => Image is not null; } - - public bool HasModID { get => Modinfo.ModID is not null; } - #endregion - - public float SizeInMB { get; private set; } - - public Attributes.AttributeCollection Attributes { get; } = new(); - - /// i.e. "[Gameplay] AI Shipyard" - /// absolute path without folderName - public Mod( - bool isActive, - string folderName, - LocalizedModinfo modinfo, - string basePath) - { - IsActive = isActive; - FolderName = folderName; - BasePath = basePath; - Modinfo = modinfo; - - SubMods = new List(); - - // Just construct as base64 for now. - // TODO move to separate async function - if (Modinfo.Image is not null) - { - Image = new ImyaImageSource(); - Image.ConstructAsBase64Image(Modinfo.Image); - } - - if (VersionEx.TryParse(Modinfo.Version, out var version)) - Version = version; - - // Just get the size - // TODO move to separate async? - var info = new DirectoryInfo(FullModPath); - SizeInMB = (float)Math.Round((decimal)info.EnumerateFiles("*", SearchOption.AllDirectories).Sum(x => x.Length) / 1024 / 1024, 1); - } - - public void InitImageAsFilepath(string ImagePath) - { - Image = new ImyaImageSource(); - Image.ConstructAsFilepathImage(ImagePath); - } - - #region modifying actions - /// - /// Remove duplicate "-" from folder name. - /// Will overwrite any existing folder. - /// Note: Modinfo will not be updated. - /// - /// - public async Task NormalizeAsync() - { - if (!FolderName.StartsWith("-")) return; - - var trimAllDash = FolderName; - while (trimAllDash.StartsWith("-")) - trimAllDash = trimAllDash[1..]; - - await Task.Run(() => - { - string sourcePath = Path.Combine(BasePath, FullFolderName); - string targetPath = Path.Combine(BasePath, IsActive ? "" : "-" + trimAllDash); - try - { - DirectoryEx.CleanMove(sourcePath, targetPath); - Console.WriteLine($"Removed duplicate '-' from {FullFolderName}"); - } - catch (Exception e) - { - Console.WriteLine($"Failed to remove duplicate '-' from {FullFolderName}. Cause: {e.Message}"); - } - }); - - FolderName = trimAllDash; - } - - /// - /// Change activation by renaming the mod folder. - /// Note: target folder will be overwritten if both active and inactive states are available. - /// - internal void AdaptToActiveStatus(bool active) - { - string sourcePath = Path.Combine(BasePath, FullFolderName); - string targetPath = Path.Combine(BasePath, (active ? "" : "-") + FolderName); - - try - { - DirectoryEx.CleanMove(sourcePath, targetPath); - IsActive = active; - } - catch (DirectoryNotFoundException) - { - IsRemoved = true; - throw new InvalidOperationException($"Failed to access mod: {FolderName}. Cause: The mod has been removed"); - } - catch (Exception e) - { - throw new InvalidOperationException($"Failed to access mod: {FolderName}. Cause: {e.Message}"); - } - } - - /// - /// Deactivate and give it a special flag as deletion candidate. - /// - #endregion - - #region readonly actions - /// - /// Check if mod name, category and creator fields contain all keywords. - /// - public bool HasKeywords(string spaceSeparatedKeywords) - { - var keywords = spaceSeparatedKeywords.Split(" "); - return keywords.Aggregate(true, (isMatch, keyword) => isMatch &= HasKeyword(keyword)); - } - - private bool HasKeyword(string keyword) - { - var k = keyword.ToLower(); - return Modinfo.ModName.Text.ToLower().Contains(k) || - (Modinfo.Category?.Text.ToLower().Contains(k) ?? false) || - (Modinfo.CreatorName?.ToLower().Contains(k) ?? false); - } - - /// - /// Return all files with a specific extension. - /// - public IEnumerable GetFilesWithExtension(string extension) - { - return Directory.EnumerateFiles(FullModPath, $"*.{extension}", SearchOption.AllDirectories); - } - - #endregion - - #region comparisons - /// - /// Check if mod has the same version and content as the target mod. - /// - public bool HasSameContentAs(Mod? target) - { - if (target is null) return false; - - if (Modinfo.Version != target.Modinfo.Version) - return false; - - var dirA = new DirectoryInfo(FullModPath); - var dirB = new DirectoryInfo(target.FullModPath); - var listA = dirA.GetFiles("*", SearchOption.AllDirectories); - var listB = dirB.GetFiles("*", SearchOption.AllDirectories); - - // TODO filter tweaking files! - - // first compare path only to avoid costly md5 checks - var pathComparer = new FilePathComparer(prefixPathA: dirA.FullName, prefixPathB: dirB.FullName); - bool areIdentical = listA.SequenceEqual(listB, pathComparer); - if (!areIdentical) - return false; - - // no path (or file length) difference, now go for file content - return listA.SequenceEqual(listB, new FileMd5Comparer()); - } - - /// - /// Check if mod is an update of the target mod. - /// Must have same ModID. - /// Go by modinfo.json version first and fallback to checksum if versions are equal. - /// Consider null targets as updatable. - /// - public bool IsUpdateOf(Mod? target) - { - if (target is null || target.IsRemoved) - return true; - if (target.Modinfo.ModID != Modinfo.ModID) - return false; - - // compare content when unversioned - if (target.Version is null && Version is null) - return !HasSameContentAs(target); // consider same as outdated - - // prefer versioned mods - if (target.Version is null) - return true; - if (Version is null) - return false; - - if (Version == target.Version) - return !HasSameContentAs(target); // consider same as outdated - - return Version > target.Version; - } - #endregion - - #region Sub mods - public List SubMods { get; private set; } - public IEnumerable DistinctSubMods { get => SubMods.DistinctBy(x => (x.ModID, x.Version)); } - - public bool HasSubmods { get => SubMods.Count() > 0; } - - public IEnumerable DistinctSubModsIncludingSelf { get => DistinctSubMods.Prepend(this); } - #endregion - - public ModStatus GetStatus() - { - return (Attributes.GetByType(AttributeType.ModStatus) as ModStatusAttribute)?.Status ?? ModStatus.Default; - } - } -} diff --git a/ModManager_Classes/Models/Mods/ModCollection.cs b/ModManager_Classes/Models/Mods/ModCollection.cs deleted file mode 100644 index 950d2d0e..00000000 --- a/ModManager_Classes/Models/Mods/ModCollection.cs +++ /dev/null @@ -1,439 +0,0 @@ -using Imya.Models.Attributes; -using Imya.Models.Attributes.Factories; -using Imya.Models.Attributes.Interfaces; -using Imya.Models.NotifyPropertyChanged; -using Imya.Services; -using Imya.Services.Interfaces; -using Imya.Utils; -using Imya.Validation; -using Microsoft.Extensions.DependencyInjection; -using System.Collections; -using System.Collections.Specialized; - -namespace Imya.Models.Mods -{ - public class ModCollection : PropertyChangedNotifier, IReadOnlyCollection, INotifyCollectionChanged - { - #region UI related - public int ActiveMods - { - get => _activeMods; - set => SetProperty(ref _activeMods, value); - } - private int _activeMods; - - public int ActiveSizeInMBs - { - get => _activeSizeInMBs; - set => SetProperty(ref _activeSizeInMBs, value); - } - private int _activeSizeInMBs = 0; - - public int InstalledSizeInMBs - { - get => _installedSizeInMBs; - set => SetProperty(ref _installedSizeInMBs, value); - } - private int _installedSizeInMBs = 0; - #endregion - - /// - /// Triggers after any change of the collection. - /// Mods are fully loaded at this point. - /// - public event NotifyCollectionChangedEventHandler? CollectionChanged; - - public string ModsPath { get; set; } - - public IReadOnlyList Mods => _mods; - private List _mods = new(); - - public IEnumerable ModIDs { get => _modids; } - private List _modids = new(); - - public bool Normalize { get; init; } - public bool LoadImages { get; init; } - public bool AutofixSubfolder { get; init; } - - private IGameSetupService _gameSetupService; - private IModFactory _modFactory; - private IModStatusAttributeFactory _modStatusAttributeFactory; - private IModAccessIssueAttributeFactory _modAccessIssueAttributeFactory; - private IRemovedFolderAttributeFactory _removedFolderAttributeFactory; - - /// - /// This constructor is internal. - /// To create a ModCollection, use - /// - /// Path to mods. - /// Remove duplicate "-" - /// Load image files into memory - /// find data/ in subfolder and move up - internal ModCollection( - IGameSetupService gameSetupService, - IModFactory modFactory, - IModStatusAttributeFactory modStatusAttributeFactory, - IModAccessIssueAttributeFactory modAccessIssueAttributeFactory, - IRemovedFolderAttributeFactory removedFolderAttributeFactory) - { - _gameSetupService = gameSetupService; - _modFactory = modFactory; - _modStatusAttributeFactory = modStatusAttributeFactory; - _modAccessIssueAttributeFactory = modAccessIssueAttributeFactory; - _removedFolderAttributeFactory = removedFolderAttributeFactory; - - ModsPath = ""; - Normalize = false; - LoadImages = false; - AutofixSubfolder = false; - } - - /// - /// Load all mods. - /// - /// TODO: All the loading should be done inside - /// - public async Task LoadModsAsync() - { - if (Directory.Exists(ModsPath)) - { - if (AutofixSubfolder) - AutofixSubfolders(ModsPath); - - _mods = await LoadModsAsync(Directory.EnumerateDirectories(ModsPath) - .Where(x => !Path.GetFileName(x).StartsWith("."))); - - int i = 0; - } - else - { - _mods = new(); - } - - OnActivationChanged(null); - } - - /// - /// If there's no data/ top-level, move all data/ folders up - no matter how deep they are. - /// Obviously ignore data/ under data/ like in data/abc/data/ situations - /// - private static void AutofixSubfolders(string modsPath) - { - foreach (var folder in Directory.EnumerateDirectories(modsPath)) - { - if (Directory.Exists(Path.Combine(folder, "data")) || File.Exists(Path.Combine(folder, "modinfo.json"))) - continue; - - var potentialMods = DirectoryEx.FindFolder(folder, "data").Select(x => Path.GetDirectoryName(x)!); - foreach (var potentialMod in potentialMods) - { - try - { - DirectoryEx.CleanMove(potentialMod, Path.Combine(modsPath, Path.GetFileName(potentialMod))); - } - catch - { - // TODO should we say something? - } - } - - // only remove the parent folder if all content has been moved - if (!Directory.EnumerateFiles(folder, "*", SearchOption.AllDirectories).Any()) - { - try - { - Directory.Delete(folder, true); - } - catch - { - // tough luck, but not harmful - } - } - } - } - - private async Task> LoadModsAsync(IEnumerable folders, bool invokeEvents = true) - { - var mods = folders.SelectNoNull(x => _modFactory.GetFromFolder(x, loadImages: true)).ToList(); - if (Normalize) - { - foreach (var mod in mods) - await mod.NormalizeAsync(); - } - - if (invokeEvents) - { - CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, mods)); - } - - return mods; - } - - #region MemberFunctions - public async void OnModPathChanged(string _) - { - ModsPath = _gameSetupService.GetModDirectory(); - - await LoadModsAsync(); - } - - - private void OnActivationChanged(Mod? sender) - { - // remove mods with IssueModRemoved attribute - int removedModCount = _mods.Count(x => x.IsRemoved); - - int newActiveCount = _mods.Count(x => x.IsActive); - if (removedModCount > 0 || ActiveMods != newActiveCount) - { - ActiveMods = newActiveCount; - ActiveSizeInMBs = (int)Math.Round(_mods.Sum(x => x.IsActive ? x.SizeInMB : 0)); - InstalledSizeInMBs = (int)Math.Round(_mods.Sum(x => x.SizeInMB)); - Console.WriteLine($"{ActiveMods} active mods. {_mods.Count} total found."); - - // trigger changed events for activation/deactivation - if (sender is null) - CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - else - CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, sender, sender)); - } - } - - #region Add, remove mods - /// - /// Moves mods from collection to mod folder. - /// Source collection folder will be deleted afterwards. - /// Existing mods will be overwriten, old names with same mod id deactivated. - /// - public async Task MoveIntoAsync(ModCollection source, bool allowOldToOverwrite = false, CancellationToken ct = default) - { - Directory.CreateDirectory(ModsPath); - - try - { - foreach (var sourceMod in source.Mods) - { - await Task.Run( - async () => await MoveSingleModIntoAsync(sourceMod, source.ModsPath, allowOldToOverwrite), - ct - ); - } - } - catch (Exception e) - { - Console.WriteLine($"Move Error: {e.Message}"); - } - finally - { - Directory.Delete(source.ModsPath, true); - } - - CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, Mods.ToList())); - } - - private async Task MoveSingleModIntoAsync(Mod sourceMod, string sourceModsPath, bool allowOldToOverwrite) - { - var (targetMod, targetModPath) = SelectTargetMod(sourceMod); - - if (!allowOldToOverwrite && !sourceMod.IsUpdateOf(targetMod)) - { - Console.WriteLine($"Skip update of {sourceMod.FolderName}. Source version: {sourceMod.Modinfo.Version}, target version: {targetMod?.Modinfo.Version}"); - return; - } - - // do it! - var status = Directory.Exists(targetModPath) ? ModStatus.Updated : ModStatus.New; - sourceMod.Attributes.AddAttribute(_modStatusAttributeFactory.Get(status)); - - DirectoryEx.CleanMove(Path.Combine(sourceModsPath, sourceMod.FullFolderName), targetModPath); - Console.WriteLine($"{sourceMod.Attributes.GetByType(AttributeType.ModStatus)}: {sourceMod.FolderName}"); - - // mark all duplicate id mods as obsolete - if (sourceMod.Modinfo.ModID != null) - { - var sameModIDs = WhereByModID(sourceMod.Modinfo.ModID).Where(x => x != targetMod); - foreach (var mod in sameModIDs) - await MakeObsoleteAsync(mod, ModsPath); - // mark mod as updated, since there was the same modid already there - if (sameModIDs.Any()) - status = ModStatus.Updated; - } - - // mark deprecated ids as obsolete - if (sourceMod.Modinfo.DeprecateIds != null) - { - var deprecateIDs = sourceMod.Modinfo.DeprecateIds.SelectMany(x => WhereByModID(x)); - foreach (var mod in deprecateIDs) - await MakeObsoleteAsync(mod, ModsPath); - } - - // update mod list, only remove in case of same folder - if (targetMod is not null) - { - _mods.Remove(targetMod); - } - var reparsed = (await LoadModsAsync(new string[] { targetModPath }, false)).First(); - reparsed.Attributes.AddAttribute(_modStatusAttributeFactory.Get(status)); - _mods.Add(reparsed); - } - - private (Mod?, string) SelectTargetMod(Mod sourceMod) - { - // select target mod - var targetMod = FirstByFolderName(sourceMod.FolderName); - string targetModPath = Path.Combine(ModsPath, targetMod?.FullFolderName ?? sourceMod.FullFolderName); - - // re-select target mod when modids are different (safeguard after 9 tries) - var iteration = 1; - while (iteration < 10 && - sourceMod.Modinfo.ModID is not null && - targetMod?.Modinfo.ModID is not null && - sourceMod.Modinfo.ModID != targetMod.Modinfo.ModID) - { - targetMod = FirstByFolderName($"{sourceMod.FolderName}-{iteration}"); - targetModPath = Path.Combine(ModsPath, targetMod?.FullFolderName ?? $"{sourceMod.FullFolderName}-{iteration}"); - iteration++; - } - - return (targetMod, targetModPath); - } - - public async Task ChangeActivationAsync(IEnumerable mods, bool activation_status) - { - if (mods.Any(x => !_mods.Contains(x))) - { - throw new InvalidOperationException("Collection cannot change mods that are not in it."); - } - var tasks = mods.Select(x => Task.Run(async () => await ChangeActivationNoEventsAsync(x, activation_status))).ToList(); - await Task.WhenAll(tasks); - - OnActivationChanged(null); - } - - public async Task ChangeActivationAsync(Mod mod, bool active) - { - await ChangeActivationNoEventsAsync(mod, active); - OnActivationChanged(mod); - } - - private async Task ChangeActivationNoEventsAsync(Mod mod, bool active) - { - if (mod.IsActive == active || mod.IsRemoved) - return; - - var verb = active ? "activate" : "deactivate"; - - await Task.Run(() => - { - try - { - mod.AdaptToActiveStatus(active); - Console.WriteLine($"{verb} {mod.FolderName}. Folder renamed to {mod.FullFolderName}"); - } - catch (InvalidOperationException e) - { - Console.WriteLine(e.Message); - mod.IsRemoved = true; - } - }); - } - - /// - /// Permanently delete all mods from collection. - /// - public async Task DeleteAsync(IEnumerable mods) - { - if (mods.Any(x => !_mods.Contains(x))) - { - throw new InvalidOperationException("Collection cannot change mods that are not in it."); - } - - var deleted = mods.ToList(); - - foreach (var mod in mods) - await DeleteAsync(mod); - - CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, deleted)); - } - - /// - /// Permanently delete mod from collection. - /// - /// - private async Task DeleteAsync(Mod mod) - { - await Task.Run(() => - { - try - { - Directory.Delete(mod.FullModPath, true); - - // remove from the mod lists to prevent access. - _mods.Remove(mod); - } - catch (DirectoryNotFoundException) - { - // remove from the mod lists to prevent access. - _mods.Remove(mod); - } - catch (Exception e) - { - mod.Attributes.AddAttribute(_modAccessIssueAttributeFactory.GetNoDelete()); - Console.WriteLine($"Failed to delete Mod: {mod.Category} {mod.Name}. Cause: {e.Message}"); - } - }); - } - - - public async Task MakeObsoleteAsync(Mod mod, string path) - { - await ChangeActivationNoEventsAsync(mod, false); - mod.Attributes.AddAttribute(_modStatusAttributeFactory.Get(ModStatus.Obsolete)); - Console.WriteLine($"{ModStatus.Obsolete}: {mod.FolderName}"); - } - - #endregion - - /// - /// Returns mod with given folder name. Activated mods come first. - /// - private Mod? FirstByFolderName(string folderName, bool ignoreActivation = true) - { - var match = Mods.Where(x => (ignoreActivation ? x.FolderName : x.FullFolderName) == folderName).ToArray(); - - // prefer activated one in case of two - if (ignoreActivation && match.Length == 2) - return match[0].IsActive ? match[0] : match[1]; - - return match.FirstOrDefault(); - } - - private IEnumerable WhereByModID(string modID) - { - return Mods.Where(x => x.Modinfo.ModID == modID); - } - - public async Task LoadProfileAsync(ModActivationProfile profile) - { - var activationSet = new HashSet(profile); - - foreach (var mod in Mods) - { - bool active = activationSet.Contains(mod.FolderName); - if (active != mod.IsActive) - await ChangeActivationNoEventsAsync(mod, active); - } - - CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } - #endregion - - public IEnumerable WithAttribute(AttributeType attributeType) => Mods.Where(x => x.Attributes.HasAttribute(attributeType)); - - #region IReadOnlyCollection - public int Count => _mods.Count; - public IEnumerator GetEnumerator() => _mods.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => _mods.GetEnumerator(); - #endregion - } -} diff --git a/ModManager_Classes/Models/Mods/ModCollectionFactory.cs b/ModManager_Classes/Models/Mods/ModCollectionFactory.cs deleted file mode 100644 index 2e8a0d2d..00000000 --- a/ModManager_Classes/Models/Mods/ModCollectionFactory.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.IO.Compression; -using Imya.Models.Attributes.Interfaces; -using Imya.Models.Installation; -using Imya.Services.Interfaces; -using Imya.Validation; -using Microsoft.Extensions.DependencyInjection; - -namespace Imya.Models.Mods -{ - /// - /// Install mods from zip file - might depracate this honestly - /// - public class ModCollectionFactory : IModCollectionFactory - { - private readonly IServiceProvider _serviceProvider; - public ModCollectionFactory(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public ModCollection Get( - string Filepath, - bool normalize = false, - bool loadImages = false, - bool autofixSubfolder = false) - { - var collection = new ModCollection( - _serviceProvider.GetRequiredService(), - _serviceProvider.GetRequiredService(), - _serviceProvider.GetRequiredService(), - _serviceProvider.GetRequiredService(), - _serviceProvider.GetRequiredService()) - { - ModsPath = Filepath, - Normalize = normalize, - LoadImages = loadImages, - AutofixSubfolder = autofixSubfolder - }; - - return collection; - } - } -} diff --git a/ModManager_Classes/Models/Mods/ModFactory.cs b/ModManager_Classes/Models/Mods/ModFactory.cs deleted file mode 100644 index 42bcad92..00000000 --- a/ModManager_Classes/Models/Mods/ModFactory.cs +++ /dev/null @@ -1,88 +0,0 @@ -using Imya.Models.Attributes; -using Imya.Models.Attributes.Factories; -using Imya.Models.Attributes.Interfaces; -using Imya.Models.ModMetadata; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Imya.Models.Mods -{ - public class ModFactory : IModFactory - { - private readonly IMissingModinfoAttributeFactory _missingModinfoAttributeFactory; - private readonly LocalizedModinfoFactory _localizedModinfoFactory; - - public ModFactory( - IMissingModinfoAttributeFactory missingModinfoAttributeFactory, - LocalizedModinfoFactory localizedModinfoFactory) - { - _missingModinfoAttributeFactory = missingModinfoAttributeFactory; - _localizedModinfoFactory = localizedModinfoFactory; - } - - public Mod? GetFromFolder(string modFolderPath, bool loadImages = false) - { - var basePath = Path.GetDirectoryName(modFolderPath); - if (basePath is null || !Directory.Exists(modFolderPath)) - return null; - - var folder = Path.GetFileName(modFolderPath); - var isActive = !folder.StartsWith("-"); - var folderName = isActive ? folder : folder[1..]; - - bool hasModinfo = ModinfoLoader.TryLoadFromFile(Path.Combine(modFolderPath, "modinfo.json"), out var modinfo); - - var localizedModinfo = hasModinfo ? - _localizedModinfoFactory.GetLocalizedModinfo(modinfo!, folderName) - : _localizedModinfoFactory.GetDummyModinfo(folderName); - - var mod = new Mod( - isActive, - folderName, - localizedModinfo, - basePath); - - if (!hasModinfo) - mod.Attributes.AddAttribute(_missingModinfoAttributeFactory.Get()); - - string[] modinfos = Directory.GetFiles(Path.Combine(basePath, folder), "modinfo.json", SearchOption.AllDirectories); - - if (loadImages) - { - var imagepath = Path.Combine(mod.FullModPath, "banner.jpg"); - if (File.Exists(imagepath)) - mod.InitImageAsFilepath(Path.Combine(imagepath)); - else - { - imagepath = Path.Combine(mod.FullModPath, "banner.png"); - if (File.Exists(imagepath)) - mod.InitImageAsFilepath(Path.Combine(imagepath)); - } - } - - if (modinfos.Length > 1) - { - foreach (var submodinfo in modinfos) - { - if (submodinfo.ToLower() == Path.Combine(basePath, folder, "modinfo.json").ToLower()) - { - continue; - } - - Mod? submod = GetFromFolder(Path.GetDirectoryName(submodinfo) ?? "", loadImages: true); - if (submod is not null) - { - mod.SubMods.Add(submod); - } - } - } - - - return mod; - } - } -} diff --git a/ModManager_Classes/Services/ImyaSetupService.cs b/ModManager_Classes/Services/ImyaSetupService.cs index a7186e1b..bab3b8bd 100644 --- a/ModManager_Classes/Services/ImyaSetupService.cs +++ b/ModManager_Classes/Services/ImyaSetupService.cs @@ -1,13 +1,6 @@ -using Imya.Models.Mods; +using Anno.EasyMod.Mods; using Imya.Models.NotifyPropertyChanged; using Imya.Services.Interfaces; -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; namespace Imya.Services { @@ -28,7 +21,7 @@ public class ImyaSetupService : PropertyChangedNotifier, IImyaSetupService public string TweakDirectoryPath { get => Path.Combine(_gameSetup.GameRootPath, WorkingDirectory, TweakDirectory); } public string UnpackDirectoryPath { get => Path.Combine(_gameSetup.GameRootPath, WorkingDirectory, UnpackDirectory); } - public ModCollection GlobalModCollection + public IModCollection GlobalModCollection { get => _globalModCollection; set @@ -38,20 +31,24 @@ public ModCollection GlobalModCollection if (_globalModCollection == value) return; + /* if (_globalModCollection is not null) { _gameSetup.GameRootPathChanged -= _globalModCollection.OnModPathChanged; _gameSetup.ModDirectoryNameChanged -= _globalModCollection.OnModPathChanged; } + */ _globalModCollection = value; + /* _gameSetup.GameRootPathChanged += value.OnModPathChanged; _gameSetup.ModDirectoryNameChanged += value.OnModPathChanged; + */ } } - private ModCollection _globalModCollection; + private IModCollection _globalModCollection; public ImyaSetupService(IGameSetupService gameSetup) { diff --git a/ModManager_Classes/Services/InstallationService.cs b/ModManager_Classes/Services/InstallationService.cs index 70b984c1..67128101 100644 --- a/ModManager_Classes/Services/InstallationService.cs +++ b/ModManager_Classes/Services/InstallationService.cs @@ -7,7 +7,7 @@ using Imya.Services.Interfaces; using Imya.Utils; using Imya.Models.Installation.Interfaces; -using Imya.Models.Mods; +using Anno.EasyMod.Mods.LocalMods; namespace Imya.Services { @@ -102,11 +102,11 @@ public double CurrentDownloadSpeedPerSecond private float max_progress = 1; #endregion - private readonly IModCollectionFactory _modCollectionFactory; + private readonly ILocalModCollectionFactory _modCollectionFactory; private readonly IImyaSetupService _imyaSetupService; public InstallationService( - IModCollectionFactory factory, + ILocalModCollectionFactory factory, IImyaSetupService imyaSetupService) { _modCollectionFactory = factory; @@ -268,13 +268,12 @@ private async Task MoveModsAsync(IUnpackableInstallation unpackable) if (unpackable.CancellationToken.IsCancellationRequested) return; unpackable.Status = InstallationStatus.MovingFiles; - var newCollection = _modCollectionFactory.Get(unpackable.UnpackTargetPath, autofixSubfolder : true); - await newCollection.LoadModsAsync(); + var newCollection = await _modCollectionFactory.GetAsync(unpackable.UnpackTargetPath); //async waiting await Task.Run(() => _moveIntoSem.WaitOne(), unpackable.CancellationToken); if (!unpackable.CancellationToken.IsCancellationRequested) { - await _imyaSetupService.GlobalModCollection.MoveIntoAsync(newCollection); + await _imyaSetupService.GlobalModCollection.AddAsync(newCollection.Mods); } _moveIntoSem.Release(); Unpacks.Remove(unpackable); @@ -304,7 +303,7 @@ private async Task DownloadAsync(IDownloadableInstallation downloadable) private void CleanUpUnpackable(IUnpackable unpackable) { if (Directory.Exists(unpackable.UnpackTargetPath)) - Directory.Delete(unpackable.UnpackTargetPath); + Directory.Delete(unpackable.UnpackTargetPath, true); } private void CleanUpDownloadable(IDownloadable downloadable) diff --git a/ModManager_Classes/Services/Interfaces/IImyaSetupService.cs b/ModManager_Classes/Services/Interfaces/IImyaSetupService.cs index ab840335..194f59dd 100644 --- a/ModManager_Classes/Services/Interfaces/IImyaSetupService.cs +++ b/ModManager_Classes/Services/Interfaces/IImyaSetupService.cs @@ -1,9 +1,4 @@ -using Imya.Models.Mods; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Anno.EasyMod.Mods; namespace Imya.Services.Interfaces { @@ -15,7 +10,7 @@ public interface IImyaSetupService public String TweakDirectoryPath { get; } public String UnpackDirectoryPath { get; } - public ModCollection GlobalModCollection { get; set; } + public IModCollection GlobalModCollection { get; set; } void Init(); } diff --git a/ModManager_Classes/Services/Interfaces/IProfilesService.cs b/ModManager_Classes/Services/Interfaces/IProfilesService.cs index 9fe766ba..e7d3edc6 100644 --- a/ModManager_Classes/Services/Interfaces/IProfilesService.cs +++ b/ModManager_Classes/Services/Interfaces/IProfilesService.cs @@ -1,5 +1,5 @@ -using Imya.Models; -using Imya.Models.Mods; +using Anno.EasyMod.Mods; +using Imya.Models; using System; using System.Collections.Generic; using System.Linq; @@ -11,7 +11,7 @@ namespace Imya.Services.Interfaces public interface IProfilesService { IEnumerable GetSavedKeys(); - ModActivationProfile CreateFromModCollection(ModCollection collection); + ModActivationProfile CreateFromModCollection(IModCollection collection); ModActivationProfile? LoadProfile(String key); void DeleteActivationProfile(String key); void SaveProfile(ModActivationProfile profile, String key); diff --git a/ModManager_Classes/Services/Interfaces/ITweakService.cs b/ModManager_Classes/Services/Interfaces/ITweakService.cs index b14d6fa4..7b5374d7 100644 --- a/ModManager_Classes/Services/Interfaces/ITweakService.cs +++ b/ModManager_Classes/Services/Interfaces/ITweakService.cs @@ -1,4 +1,4 @@ -using Imya.Models.Mods; +using Anno.EasyMod.Mods; using Imya.Models.ModTweaker.DataModel.Tweaking; using System; using System.Collections.Generic; @@ -18,6 +18,6 @@ public interface ITweakService void Save(); Task SaveAsync(); void Unload(); - void Load(Mod mod, bool ClearCurrentWhileLoading = true); + void Load(IMod mod, bool ClearCurrentWhileLoading = true); } } diff --git a/ModManager_Classes/Services/ProfilesService.cs b/ModManager_Classes/Services/ProfilesService.cs index 832f5ac2..b0c27b94 100644 --- a/ModManager_Classes/Services/ProfilesService.cs +++ b/ModManager_Classes/Services/ProfilesService.cs @@ -1,6 +1,6 @@ -using Imya.Models; +using Anno.EasyMod.Mods; +using Imya.Models; using Imya.Models.Attributes.Factories; -using Imya.Models.Mods; using Imya.Services.Interfaces; using System; using System.Collections.Generic; @@ -33,7 +33,7 @@ public IEnumerable GetSavedKeys() return filepaths.Select(x => Path.GetFileNameWithoutExtension(x)).ToArray(); } - public ModActivationProfile CreateFromModCollection(ModCollection collection) + public ModActivationProfile CreateFromModCollection(IModCollection collection) { var foldernames = collection.Mods .Where(x => x.IsActive) diff --git a/ModManager_Classes/Services/TweakService.cs b/ModManager_Classes/Services/TweakService.cs index 62dc00df..3cbc42de 100644 --- a/ModManager_Classes/Services/TweakService.cs +++ b/ModManager_Classes/Services/TweakService.cs @@ -1,4 +1,4 @@ -using Imya.Models.Mods; +using Anno.EasyMod.Mods; using Imya.Models.ModTweaker.DataModel.Tweaking; using Imya.Models.ModTweaker.IO; using Imya.Models.NotifyPropertyChanged; @@ -87,7 +87,7 @@ public async Task SaveAsync() IsSaving = false; } - public void Load(Mod mod, bool ClearCurrentWhileLoading = true) + public void Load(IMod mod, bool ClearCurrentWhileLoading = true) { IsLoading = true; HasUnsavedChanges = false; diff --git a/ModManager_Classes/Texts/ITextManager.cs b/ModManager_Classes/Texts/ITextManager.cs index 704ab07c..4599dfe4 100644 --- a/ModManager_Classes/Texts/ITextManager.cs +++ b/ModManager_Classes/Texts/ITextManager.cs @@ -1,6 +1,5 @@ -using Imya.Models; -using Imya.Models.ModMetadata; -using Imya.Models.ModMetadata.ModinfoModel; +using Anno.EasyMod.Metadata; +using Imya.Models; using System; using System.Collections.Generic; using System.Linq; diff --git a/ModManager_Classes/Texts/LocalizedText.cs b/ModManager_Classes/Texts/LocalizedText.cs index 6227dbe3..d13b1a55 100644 --- a/ModManager_Classes/Texts/LocalizedText.cs +++ b/ModManager_Classes/Texts/LocalizedText.cs @@ -1,5 +1,4 @@ -using Imya.Models.ModMetadata; -using Imya.Utils; +using Imya.Utils; using Newtonsoft.Json; using System.Runtime.Serialization; using Imya.Enums; diff --git a/ModManager_Classes/Texts/TextManager.cs b/ModManager_Classes/Texts/TextManager.cs index a6689b08..6c03cdb2 100644 --- a/ModManager_Classes/Texts/TextManager.cs +++ b/ModManager_Classes/Texts/TextManager.cs @@ -1,5 +1,5 @@ -using Imya.Models; -using Imya.Models.ModMetadata.ModinfoModel; +using Anno.EasyMod.Metadata; +using Imya.Models; using Newtonsoft.Json; // TODO move all text/language related code under Imya.Text or Imya.Language diff --git a/ModManager_Classes/Utils/ModinfoCreationManager.cs b/ModManager_Classes/Utils/ModinfoCreationManager.cs index a661237a..35edf862 100644 --- a/ModManager_Classes/Utils/ModinfoCreationManager.cs +++ b/ModManager_Classes/Utils/ModinfoCreationManager.cs @@ -1,12 +1,11 @@ -using Imya.Enums; -using Imya.Models.ModMetadata.ModinfoModel; -using Imya.Models.NotifyPropertyChanged; +using Anno.EasyMod.Metadata; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; +using Imya.Models.NotifyPropertyChanged; namespace Imya.Utils { diff --git a/ModManager_Classes/Utils/ModinfoLoader.cs b/ModManager_Classes/Utils/ModinfoLoader.cs deleted file mode 100644 index aa89074e..00000000 --- a/ModManager_Classes/Utils/ModinfoLoader.cs +++ /dev/null @@ -1,114 +0,0 @@ -using Imya.Models.ModMetadata.ModinfoModel; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace Imya.Models.ModMetadata -{ - public class ModinfoLoader - { - public static bool TryLoadFromFile(string filePath, out Modinfo? metadata) - { - var shortPath = Path.Combine(Path.GetFileName(Path.GetDirectoryName(filePath)??""), Path.GetFileName(filePath)); - - if (AutofixModinfoArrays(filePath)) - { - Console.WriteLine($"Fixed modloader relevant arrays in modinfo from {shortPath}"); - } - - try - { - var settings = new JsonSerializerSettings(); - // Every field is optional, thus be kind and forgive errors. - settings.Error += (obj, args) => { - args.ErrorContext.Handled = true; - Console.WriteLine($"Warning: {args.ErrorContext.Path} in {shortPath} is invalid"); - }; - metadata = JsonConvert.DeserializeObject(File.ReadAllText(filePath), settings)??new Modinfo(); - Console.WriteLine($"Loaded Modinfo file from {shortPath}"); - return true; - } - catch (JsonSerializationException) - { - metadata = null; - Console.WriteLine("Json Serialization failed: {0}", shortPath); - } - catch (IOException) - { - metadata = null; - Console.WriteLine("File not found: {0}", shortPath); - } - return false; - } - - public static bool TrySaveToFile(String Filename, Modinfo m) - { - try - { - using (StreamWriter writer = new StreamWriter(File.Create(Filename))) - { - writer.Write(JsonConvert.SerializeObject(m, Formatting.Indented)); - } - Console.WriteLine($"Saved Modinfo file to {Filename}"); - return true; - } - catch (JsonSerializationException) - { - Console.WriteLine("Json Serialization failed: {0}", Filename); - } - catch (IOException) - { - Console.WriteLine("File not found: {0}", Filename); - } - return false; - } - - /// - /// Automatically fix ModDependencies and other mod loader relevant arrays to avoid crashes. - /// - /// true if file was changed - public static bool AutofixModinfoArrays(string filePath) - { - if (!File.Exists(filePath)) return false; - - var fixToken = (JToken root, string name) => - { - JToken? token = root.SelectToken(name); - if (token is not null && token.Type != JTokenType.Array && token.Type != JTokenType.Null) - { - var newToken = JToken.FromObject(new string[] { token.ToString() }); - root[name] = newToken; - return true; - } - - return false; - }; - - try - { - var modinfo = JObject.Parse(File.ReadAllText(filePath)); - bool fix = false; - fix |= fixToken(modinfo.Root, "ModDependencies"); - fix |= fixToken(modinfo.Root, "IncompatibleIds"); - fix |= fixToken(modinfo.Root, "LoadAfterIds"); - fix |= fixToken(modinfo.Root, "DeprecateIds"); - - if (fix) - { - // backup, just in case - var backupFile = filePath + ".bak"; - if (File.Exists(backupFile)) File.Delete(backupFile); - File.Move(filePath, backupFile); - - File.WriteAllText(filePath, modinfo.ToString(Formatting.Indented)); - } - } - catch (Exception) - { - // There's nothing we can or should do if it's completely invalid. - return false; - } - - return false; - } - } -} diff --git a/ModManager_Classes/Validation/CyclicDependencyValidator.cs b/ModManager_Classes/Validation/CyclicDependencyValidator.cs index f6b26d40..356c95c1 100644 --- a/ModManager_Classes/Validation/CyclicDependencyValidator.cs +++ b/ModManager_Classes/Validation/CyclicDependencyValidator.cs @@ -1,6 +1,6 @@ -using Imya.Models.Attributes; +using Anno.EasyMod.Mods; +using Imya.Models.Attributes; using Imya.Models.Attributes.Interfaces; -using Imya.Models.Mods; using System; using System.Collections.Generic; using System.Collections.Specialized; @@ -20,24 +20,24 @@ public CyclicDependencyValidator(ICyclicDependencyAttributeFactory attributeFact _attributeFactory = attributeFactory; } - public void Validate(IEnumerable changed, IReadOnlyCollection all, NotifyCollectionChangedAction changedAction) + public void Validate(IEnumerable changed, IReadOnlyCollection all, NotifyCollectionChangedAction changedAction) { - foreach (Mod x in all) - x.Attributes.RemoveAttributesByType(AttributeType.CyclicDependency); - foreach (Mod x in changed) + foreach (IMod x in all) + x.Attributes.RemoveByType(AttributeTypes.CyclicDependency); + foreach (IMod x in changed) { var cyclics = CyclicDependencies(x, all); if (cyclics.Count() > 0) { - x.Attributes.AddAttribute(_attributeFactory.Get(cyclics)); + x.Attributes.Add(_attributeFactory.Get(cyclics)); } } } - private IEnumerable CyclicDependencies(Mod x, IReadOnlyCollection others) + private IEnumerable CyclicDependencies(IMod x, IReadOnlyCollection others) { if (!x.IsActive) - return Enumerable.Empty(); + return Enumerable.Empty(); return others.Where(y => y.IsActive && (y.Modinfo?.LoadAfterIds?.Contains(x.ModID) ?? false) diff --git a/ModManager_Classes/Validation/IModValidator.cs b/ModManager_Classes/Validation/IModValidator.cs index 4913a69f..b553453d 100644 --- a/ModManager_Classes/Validation/IModValidator.cs +++ b/ModManager_Classes/Validation/IModValidator.cs @@ -1,10 +1,10 @@ -using Imya.Models.Mods; +using Anno.EasyMod.Mods; using System.Collections.Specialized; namespace Imya.Validation { public interface IModValidator { - void Validate(IEnumerable changed, IReadOnlyCollection all, NotifyCollectionChangedAction changedAction); + void Validate(IEnumerable changed, IReadOnlyCollection all, NotifyCollectionChangedAction changedAction); } } diff --git a/ModManager_Classes/Validation/ModCollectionHooks.cs b/ModManager_Classes/Validation/ModCollectionHooks.cs index 6e3f311e..62accbed 100644 --- a/ModManager_Classes/Validation/ModCollectionHooks.cs +++ b/ModManager_Classes/Validation/ModCollectionHooks.cs @@ -1,5 +1,5 @@ -using Imya.Models.Attributes; -using Imya.Models.Mods; +using Anno.EasyMod.Mods; +using Imya.Models.Attributes; using Imya.Models.ModTweaker; using Imya.Models.ModTweaker.DataModel.Storage; using Imya.Models.ModTweaker.DataModel.Tweaking; @@ -13,14 +13,14 @@ namespace Imya.Validation public class ModCollectionHooks { private List validators = new(); - private ModCollection _mods; + private IModCollection _mods; public ModCollectionHooks() { } - public void HookTo(ModCollection mods) + public void HookTo(IModCollection mods) { mods.CollectionChanged += ValidateOnChange; _mods = mods; @@ -38,10 +38,10 @@ public void AddHook(IModValidator validator) private void ValidateOnChange(object? sender, NotifyCollectionChangedEventArgs e) { - if (sender is not ModCollection collection) + if (sender is not IModCollection collection) return; - IEnumerable changed = e.NewItems?.OfType() ?? collection.Mods; + IEnumerable changed = e.NewItems?.OfType() ?? collection.Mods; foreach (var validator in validators) validator.Validate(changed, collection.Mods, e.Action); } @@ -50,7 +50,7 @@ private void ValidateOnDlcChange() { foreach (var validator in validators) { - validator.Validate(Enumerable.Empty(), _mods.Mods, NotifyCollectionChangedAction.Move); + validator.Validate(Enumerable.Empty(), _mods.Mods, NotifyCollectionChangedAction.Move); } } diff --git a/ModManager_Classes/Validation/ModCompatibilityValidator.cs b/ModManager_Classes/Validation/ModCompatibilityValidator.cs index b863df2b..99e12d95 100644 --- a/ModManager_Classes/Validation/ModCompatibilityValidator.cs +++ b/ModManager_Classes/Validation/ModCompatibilityValidator.cs @@ -1,7 +1,7 @@ -using Imya.Models.Attributes; +using Anno.EasyMod.Mods; +using Anno.EasyMod.Metadata; +using Imya.Models.Attributes; using Imya.Models.Attributes.Interfaces; -using Imya.Models.ModMetadata.ModinfoModel; -using Imya.Models.Mods; using System.Collections.Immutable; using System.Collections.Specialized; @@ -16,15 +16,15 @@ public ModCompatibilityValidator(IModCompabilityAttributeFactory factory) _compabilityAttributeFactory = factory; } - public void Validate(IEnumerable changed, IReadOnlyCollection all, NotifyCollectionChangedAction changedAction) + public void Validate(IEnumerable changed, IReadOnlyCollection all, NotifyCollectionChangedAction changedAction) { foreach (var mod in all) ValidateSingle(mod, all); } - private void ValidateSingle(Mod mod, IReadOnlyCollection collection) + private void ValidateSingle(IMod mod, IReadOnlyCollection collection) { - mod.Attributes.RemoveAttributesByType(AttributeType.ModCompabilityIssue); + mod.Attributes.RemoveByType(AttributeTypes.ModCompabilityIssue); // skip dependency check if inactive or standalone if (!mod.IsActiveAndValid || collection is null) @@ -32,11 +32,11 @@ private void ValidateSingle(Mod mod, IReadOnlyCollection collection) var incompatibles = GetIncompatibleMods(mod.Modinfo, collection); if (incompatibles.Any()) - mod.Attributes.AddAttribute(_compabilityAttributeFactory.Get(incompatibles)); + mod.Attributes.Add(_compabilityAttributeFactory.Get(incompatibles)); } - private static IEnumerable GetIncompatibleMods(Modinfo modinfo, IReadOnlyCollection collection) + private static IEnumerable GetIncompatibleMods(Modinfo modinfo, IReadOnlyCollection collection) { if (collection is null || modinfo.IncompatibleIds is null || modinfo.ModID is null) yield break; diff --git a/ModManager_Classes/Validation/ModContentValidator.cs b/ModManager_Classes/Validation/ModContentValidator.cs index 8f934972..15495ac1 100644 --- a/ModManager_Classes/Validation/ModContentValidator.cs +++ b/ModManager_Classes/Validation/ModContentValidator.cs @@ -1,6 +1,6 @@ -using Imya.Models.Attributes; +using Anno.EasyMod.Mods; +using Imya.Models.Attributes; using Imya.Models.Attributes.Interfaces; -using Imya.Models.Mods; using Imya.Utils; using System.Collections.Specialized; @@ -18,17 +18,16 @@ public ModContentValidator(IContentInSubfolderAttributeFactory factory) _factory = factory; } - public void Validate(IEnumerable changed, IReadOnlyCollection all, NotifyCollectionChangedAction changedAction) + public void Validate(IEnumerable changed, IReadOnlyCollection all, NotifyCollectionChangedAction changedAction) { foreach (var mod in changed) ValidateSingle(mod); } - private void ValidateSingle(Mod mod) + private void ValidateSingle(IMod mod) { if (mod.IsRemoved || !Directory.Exists(mod.FullModPath)) { - mod.IsRemoved = true; return; } @@ -40,7 +39,7 @@ private void ValidateSingle(Mod mod) if (foundFolders.Length > 0) { // there's a data/ somewhere deeper, probably a mistake then - mod.Attributes.AddAttribute(_factory.Get()); + mod.Attributes.Add(_factory.Get()); } } } diff --git a/ModManager_Classes/Validation/ModDependencyValidator.cs b/ModManager_Classes/Validation/ModDependencyValidator.cs index 5a5cf82b..81273d80 100644 --- a/ModManager_Classes/Validation/ModDependencyValidator.cs +++ b/ModManager_Classes/Validation/ModDependencyValidator.cs @@ -1,13 +1,13 @@ using Imya.Models.Attributes; +using Anno.EasyMod.Metadata; using Imya.Models.Attributes.Interfaces; -using Imya.Models.ModMetadata.ModinfoModel; -using Imya.Models.Mods; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Text; using System.Threading.Tasks; +using Anno.EasyMod.Mods; namespace Imya.Validation { @@ -20,25 +20,25 @@ public ModDependencyValidator(IModDependencyIssueAttributeFactory factory) _dependencyAttributeFactory = factory; } - public void Validate(IEnumerable changed, IReadOnlyCollection all, NotifyCollectionChangedAction changedAction) + public void Validate(IEnumerable changed, IReadOnlyCollection all, NotifyCollectionChangedAction changedAction) { foreach (var mod in all) ValidateSingle(mod, all); } - private void ValidateSingle(Mod mod, IReadOnlyCollection collection) + private void ValidateSingle(IMod mod, IReadOnlyCollection collection) { - mod.Attributes.RemoveAttributesByType(AttributeType.UnresolvedDependencyIssue); + mod.Attributes.RemoveByType(AttributeTypes.UnresolvedDependencyIssue); // skip dependency check if inactive or standalone if (!mod.IsActiveAndValid || collection is null) return; var unresolvedDeps = GetUnresolvedDependencies(mod.Modinfo, collection).ToArray(); if (unresolvedDeps.Any()) - mod.Attributes.AddAttribute(_dependencyAttributeFactory.Get(unresolvedDeps)); + mod.Attributes.Add(_dependencyAttributeFactory.Get(unresolvedDeps)); } - private IEnumerable GetUnresolvedDependencies(Modinfo modinfo, IReadOnlyCollection collection) + private IEnumerable GetUnresolvedDependencies(Modinfo modinfo, IReadOnlyCollection collection) { if (modinfo.ModDependencies is null) yield break; @@ -46,7 +46,7 @@ private IEnumerable GetUnresolvedDependencies(Modinfo modinfo, IReadOnly foreach (var dep in modinfo.ModDependencies) { if (!collection.Any(x => x.Modinfo.ModID is not null - && (x.Modinfo.ModID.Equals(dep) || x.SubMods?.Find(submod => submod.ModID.Equals(dep)) is not null) + && (x.Modinfo.ModID.Equals(dep) || x.SubMods?.Where(submod => submod.ModID.Equals(dep)) is not null) && x.IsActiveAndValid)) yield return dep; } diff --git a/ModManager_Classes/Validation/ModReplacementValidator.cs b/ModManager_Classes/Validation/ModReplacementValidator.cs index 0e2ef2d6..c7396ced 100644 --- a/ModManager_Classes/Validation/ModReplacementValidator.cs +++ b/ModManager_Classes/Validation/ModReplacementValidator.cs @@ -1,13 +1,13 @@ using Imya.Models.Attributes.Interfaces; using Imya.Models.Attributes; -using Imya.Models.Mods; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using Imya.Models.ModMetadata.ModinfoModel; using System.Collections.Specialized; +using Anno.EasyMod.Mods; +using Anno.EasyMod.Metadata; namespace Imya.Validation { @@ -20,26 +20,26 @@ public ModReplacementValidator(IModReplacedByAttributeFactory factory) _modReplacedByAttributeFactory = factory; } - public void Validate(IEnumerable changed, IReadOnlyCollection all, NotifyCollectionChangedAction changedAction) + public void Validate(IEnumerable changed, IReadOnlyCollection all, NotifyCollectionChangedAction changedAction) { foreach (var mod in all) ValidateSingle(mod, all); } - private void ValidateSingle(Mod mod, IReadOnlyCollection collection) + private void ValidateSingle(IMod mod, IReadOnlyCollection collection) { - mod.Attributes.RemoveAttributesByType(AttributeType.ModReplacedByIssue); + mod.Attributes.RemoveByType(AttributeTypes.ModReplacedByIssue); // skip dependency check if inactive or standalone if (!mod.IsActiveAndValid || collection is null) return; - Mod? newReplacementMod = HasBeenDeprecated(mod.Modinfo, collection) ?? IsNewestOfID(mod, collection); + IMod? newReplacementMod = HasBeenDeprecated(mod.Modinfo, collection) ?? IsNewestOfID(mod, collection); if (newReplacementMod is not null && newReplacementMod != mod) - mod.Attributes.AddAttribute(_modReplacedByAttributeFactory.Get(newReplacementMod)); + mod.Attributes.Add(_modReplacedByAttributeFactory.Get(newReplacementMod)); } - private static Mod? HasBeenDeprecated(Modinfo modinfo, IReadOnlyCollection collection) + private static IMod? HasBeenDeprecated(Modinfo modinfo, IReadOnlyCollection collection) { if (collection is null || modinfo.ModID is null) return null; @@ -47,7 +47,7 @@ private void ValidateSingle(Mod mod, IReadOnlyCollection collection) return collection.FirstOrDefault(x => x.Modinfo?.DeprecateIds?.Contains(modinfo.ModID) ?? false); } - private static Mod? IsNewestOfID(Mod mod, IReadOnlyCollection collection) + private static IMod? IsNewestOfID(IMod mod, IReadOnlyCollection collection) { if (collection is null || mod.Modinfo.ModID is null) return null; diff --git a/ModManager_Classes/Validation/RemovedModValidator.cs b/ModManager_Classes/Validation/RemovedModValidator.cs index 25895342..f8386a91 100644 --- a/ModManager_Classes/Validation/RemovedModValidator.cs +++ b/ModManager_Classes/Validation/RemovedModValidator.cs @@ -1,6 +1,6 @@ -using Imya.Models.Attributes; +using Anno.EasyMod.Mods; +using Imya.Models.Attributes; using Imya.Models.Attributes.Interfaces; -using Imya.Models.Mods; using Imya.Texts; using System; using System.Collections.Generic; @@ -20,22 +20,22 @@ public RemovedModValidator(IRemovedFolderAttributeFactory attributeFactory) _attributeFactory = attributeFactory; } - public void Validate(IEnumerable changed, IReadOnlyCollection all, NotifyCollectionChangedAction changedAction) + public void Validate(IEnumerable changed, IReadOnlyCollection all, NotifyCollectionChangedAction changedAction) { foreach (var mod in changed) ValidateSingle(mod); } - private void ValidateSingle(Mod mod) + private void ValidateSingle(IMod mod) { if (!mod.IsRemoved) { - mod.Attributes.RemoveAttributesByType(AttributeType.IssueModRemoved); + mod.Attributes.RemoveByType(AttributeTypes.IssueModRemoved); return; } mod.Attributes.Clear(); - mod.Attributes.AddAttribute(_attributeFactory.Get()); + mod.Attributes.Add(_attributeFactory.Get()); } } } diff --git a/ModManager_Classes/Validation/TweakValidator.cs b/ModManager_Classes/Validation/TweakValidator.cs index 913ebc7c..35170329 100644 --- a/ModManager_Classes/Validation/TweakValidator.cs +++ b/ModManager_Classes/Validation/TweakValidator.cs @@ -1,6 +1,6 @@ -using Imya.Models.Attributes; +using Anno.EasyMod.Mods; +using Imya.Models.Attributes; using Imya.Models.Attributes.Interfaces; -using Imya.Models.Mods; using Imya.Models.ModTweaker.DataModel.Storage; using Imya.Models.ModTweaker.IO; using System; @@ -33,7 +33,7 @@ public TweakValidator( _tweakedAttributeFactory = tweakedAttributeFactory; } - public void Validate(IEnumerable changed, IReadOnlyCollection all, NotifyCollectionChangedAction changedAction) + public void Validate(IEnumerable changed, IReadOnlyCollection all, NotifyCollectionChangedAction changedAction) { if (changedAction == NotifyCollectionChangedAction.Reset @@ -47,9 +47,9 @@ public void Validate(IEnumerable changed, IReadOnlyCollection all, Not } - private void UpdateWithTweak(Mod mod) + private void UpdateWithTweak(IMod mod) { - mod.Attributes.RemoveAttributesByType(AttributeType.TweakedMod); + mod.Attributes.RemoveByType(AttributeTypes.TweakedMod); //if (!_tweakRepository.IsStored(mod.FolderName)) //return; @@ -61,7 +61,7 @@ private void UpdateWithTweak(Mod mod) var tweaks = _tweaksLoader.Load(mod); if (tweaks is not null && !tweaks.IsEmpty) { - mod.Attributes.AddAttribute(_tweakedAttributeFactory.Get()); + mod.Attributes.Add(_tweakedAttributeFactory.Get()); _tweaksave_sem.Wait(); _tweaksExporter.Save(tweaks); _tweaksave_sem.Release(); diff --git a/ModManager_Classes/lib/Anno.EasyMod.dll b/ModManager_Classes/lib/Anno.EasyMod.dll new file mode 100644 index 00000000..56206474 Binary files /dev/null and b/ModManager_Classes/lib/Anno.EasyMod.dll differ diff --git a/tests/Imya.UnitTests/AttributeTests.cs b/tests/Imya.UnitTests/AttributeTests.cs index 67e4048b..991574ef 100644 --- a/tests/Imya.UnitTests/AttributeTests.cs +++ b/tests/Imya.UnitTests/AttributeTests.cs @@ -1,4 +1,4 @@ -using Imya.Models.Attributes; +using Anno.EasyMod; using Xunit; namespace Imya.UnitTests diff --git a/tests/Imya.UnitTests/ExternalAccessTests.cs b/tests/Imya.UnitTests/ExternalAccessTests.cs index d52da9d8..9604d482 100644 --- a/tests/Imya.UnitTests/ExternalAccessTests.cs +++ b/tests/Imya.UnitTests/ExternalAccessTests.cs @@ -1,8 +1,10 @@ -using Imya.Models; +using Anno.EasyMod.DI; +using Anno.EasyMod.Mods; +using Anno.EasyMod.Mods.LocalMods; +using Anno.EasyMod.Utils; +using Imya.Models; using Imya.Models.Attributes.Factories; using Imya.Models.Attributes.Interfaces; -using Imya.Models.ModMetadata; -using Imya.Models.Mods; using Imya.Services.Interfaces; using Imya.Texts; using Imya.Utils; @@ -20,8 +22,6 @@ namespace Imya.UnitTests { public class ExternalAccessTests { - IModCollectionFactory _collectionFactory; - IServiceProvider serviceProvider; public ExternalAccessTests() @@ -34,7 +34,7 @@ public ExternalAccessTests() services.AddSingleton(x => new ModCollectionHooks()); services.AddSingleton(Mock.Of()); services.AddSingleton(); - services.AddSingleton(); + services.ConfigureEasyMod(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -42,11 +42,8 @@ public ExternalAccessTests() services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); }).Build() .Services; - - _collectionFactory = serviceProvider.GetRequiredService(); } [Fact] @@ -58,8 +55,12 @@ public async Task ChangeActivationOnDeletedMod() // prepare a mod const string folder = $@"{test}\mods\[a] mod1"; Directory.CreateDirectory(folder); - var mods = _collectionFactory.Get($@"{test}\mods"); - await mods.LoadModsAsync(); + + var mods = await serviceProvider + .GetRequiredService() + .AddFromLocalSource($@"{test}\mods") + .BuildAsync(); + var hooks = new ModCollectionHooks(); hooks.HookTo(mods); hooks.AddHook(serviceProvider.GetRequiredService()); @@ -77,7 +78,7 @@ public async Task ChangeActivationOnDeletedMod() Assert.Single(mods); Assert.True(mods.Mods[0].IsRemoved); //todo these should be seperate tests - Assert.True(mods.Mods[0].Attributes.HasAttribute(Models.Attributes.AttributeType.IssueModRemoved)); + Assert.True(mods.Mods[0].Attributes.HasAttributeOfType(Models.Attributes.AttributeTypes.IssueModRemoved)); Assert.Single(mods.Mods[0].Attributes); // activation state should be unchanged @@ -98,31 +99,34 @@ public async Task ValidateOnDeletedMod() Directory.CreateDirectory(folderB); File.WriteAllText($@"{folderB}\modinfo.json", @"{""ModID"": ""modB"", ""ModDependencies"": [ ""modA"" ]}"); const string folderC = $@"{test}\mods\[a] mod C"; - Directory.CreateDirectory(folderC); - var mods = _collectionFactory.Get($@"{test}\mods"); - await mods.LoadModsAsync(); + Directory.CreateDirectory(folderC); + + var mods = await serviceProvider + .GetRequiredService() + .AddFromLocalSource($@"{test}\mods") + .BuildAsync(); // check - Assert.Equal(3, mods.Count); + Assert.Equal(3, mods.Count()); // delete mod DirectoryEx.EnsureDeleted(folderA); // deactivate deleted mod to trigger IsRemoved=true - Mod modA = mods.Mods.Where(x => x.ModID == "modA").First(); + IMod modA = mods.Mods.Where(x => x.ModID == "modA").First(); modA.IsRemoved = true; // trigger validation by deleting mod3 var hooks = new ModCollectionHooks(); hooks.HookTo(mods); hooks.AddHook(serviceProvider.GetRequiredService()); - await mods.DeleteAsync(mods.Mods.Where(x => x.ModID == "[a] mod C").ToArray()); + await mods.RemoveAsync(mods.Mods.Where(x => x.ModID == "[a] mod C").ToArray()); - Assert.Equal(2, mods.Count); + Assert.Equal(2, mods.Count()); // mod B should have dependency issue - Mod modB = mods.Mods.Where(x => x.ModID == "modB").First(); - Assert.True(modB.Attributes.HasAttribute(Models.Attributes.AttributeType.UnresolvedDependencyIssue)); + IMod modB = mods.Mods.Where(x => x.ModID == "modB").First(); + Assert.True(modB.Attributes.HasAttributeOfType(Models.Attributes.AttributeTypes.UnresolvedDependencyIssue)); } [Fact] @@ -134,14 +138,18 @@ public async Task InstallDeletedMod() // prepare a mod const string folderA = $@"{test}\mods\[a] mod1"; Directory.CreateDirectory(folderA); - var mods = _collectionFactory.Get($@"{test}\mods"); - await mods.LoadModsAsync(); + var mods = await serviceProvider + .GetRequiredService() + .AddFromLocalSource($@"{test}\mods") + .BuildAsync(); // prepare reinstall mod const string folderB = $@"{test}\mods2\[a] mod1"; Directory.CreateDirectory(folderB); - var modsInstall = _collectionFactory.Get($@"{test}\mods2"); - await modsInstall.LoadModsAsync(); + var mods = await serviceProvider + .GetRequiredService() + .AddFromLocalSource($@"{test}\mods") + .BuildAsync(); // check Assert.Single(mods); @@ -156,10 +164,10 @@ public async Task InstallDeletedMod() // should be active mod now Assert.Single(mods); Assert.False(mods.Mods[0].IsRemoved); - Assert.False(mods.Mods[0].Attributes.HasAttribute(Models.Attributes.AttributeType.IssueModRemoved)); + Assert.False(mods.Mods[0].Attributes.HasAttribute(Models.Attributes.AttributeTypes.IssueModRemoved)); Assert.True(mods.Mods[0].IsActive); // check reevaluation of things like modinfo.json - Assert.True(mods.Mods[0].Attributes.HasAttribute(Models.Attributes.AttributeType.MissingModinfo)); + Assert.True(mods.Mods[0].Attributes.HasAttribute(Models.Attributes.AttributeTypes.MissingModinfo)); } [Fact] diff --git a/tests/Imya.UnitTests/Imya.UnitTests.csproj b/tests/Imya.UnitTests/Imya.UnitTests.csproj index 99c30ffc..d21a4f22 100644 --- a/tests/Imya.UnitTests/Imya.UnitTests.csproj +++ b/tests/Imya.UnitTests/Imya.UnitTests.csproj @@ -27,4 +27,10 @@ + + + ..\..\ModManager_Classes\lib\Anno.EasyMod.dll + + + diff --git a/tests/Imya.UnitTests/ModCollectionTests.cs b/tests/Imya.UnitTests/ModCollectionTests.cs index 26b406dc..2fe00bed 100644 --- a/tests/Imya.UnitTests/ModCollectionTests.cs +++ b/tests/Imya.UnitTests/ModCollectionTests.cs @@ -21,9 +21,7 @@ namespace Imya.UnitTests { // Note: don't use await, debug doesn't work well with it public class ModCollectionTests - { - ModCollectionFactory _collectionFactory; - + { public ModCollectionTests() { var builder = Host.CreateDefaultBuilder(); @@ -35,15 +33,11 @@ public ModCollectionTests() services.AddSingleton(Mock.Of()); services.AddSingleton(Mock.Of()); services.AddSingleton(); - services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(Mock.Of()); - services.AddSingleton(); }).Build() .Services; - - _collectionFactory = services.GetRequiredService(); } [Fact]