private static bool IsConflicting(IModelRepositoryMod origin, IModelRepositoryMod otherMod, IModelStorageMod selected) { if (origin.State == LoadingState.Loading || otherMod.State == LoadingState.Loading || selected.GetState() == StorageModStateEnum.Loading) { return(false); } if (otherMod.GetVersionHash().IsMatch(origin.GetVersionHash())) { return(false); // that's fine, won't break anything } if (!otherMod.GetMatchHash().IsMatch(selected.GetMatchHash())) { return(false); // unrelated mod, we don't care } var actionType = GetModAction(origin, selected); if (actionType == ModActionEnum.Use) { return(false); // not our problem. only show conflict when we're trying to change something } return(true); }
private void AddRepositoryMod(IModelRepositoryMod mod) { _logger.Trace($"Added repository mod {mod.ParentRepository.Name}/{mod.Identifier}"); mod.StateChanged += _ => OnAnyChange(); mod.SelectionChanged += _ => OnAnyChange(); mod.DownloadIdentifierChanged += _ => OnAnyChange(); OnAnyChange(); }
internal static ModAction Create(ModSelection selection, IModelRepositoryMod parent) { return(selection switch { ModSelectionNone => new SelectNone(), ModSelectionLoading => new SelectLoading(), ModSelectionDisabled => new SelectDisabled(), ModSelectionDownload download => new SelectStorage(download.DownloadStorage), ModSelectionStorageMod actionStorageMod => new SelectMod(actionStorageMod.StorageMod, CoreCalculation.GetModAction(parent, actionStorageMod.StorageMod)), _ => throw new ArgumentException() });
private ModSelection GetSelection(IModelRepositoryMod mod) { _logger.Trace($"Checking auto-selection for mod {mod.Identifier}"); var storageMods = _model.GetStorageMods().ToList(); var previouslySelectedMod = storageMods.SingleOrDefault(m => m.GetStorageModIdentifiers().Equals(mod.GetPreviousSelection())); if (previouslySelectedMod != null) { return(new ModSelectionStorageMod(previouslySelectedMod)); } // TODO: check previously selected storage for download? // wait for everything to load. if (_model.GetStorages().Any(s => s.State == LoadingState.Loading)) { return(new ModSelectionLoading()); } if (_model.GetRepositories().Any(s => s.State == LoadingState.Loading)) { return(new ModSelectionLoading()); } if (_model.GetRepositoryMods().Any(s => s.State == LoadingState.Loading)) { return(new ModSelectionLoading()); } if (_model.GetStorageMods().Any(s => s.GetState() == StorageModStateEnum.Loading)) { return(new ModSelectionLoading()); } var selectedMod = CoreCalculation.AutoSelect(mod, storageMods, _model.GetRepositoryMods()); if (selectedMod != null) { return(new ModSelectionStorageMod(selectedMod)); } var storage = _model.GetStorages().FirstOrDefault(s => s.CanWrite && s.IsAvailable()); if (storage != null) { mod.DownloadIdentifier = CoreCalculation.GetAvailableDownloadIdentifier(storage, mod.Identifier); return(new ModSelectionDownload(storage)); } return(new ModSelectionNone()); }
public static string?GetErrorForSelection(IModelRepositoryMod mod, IEnumerable <IModelRepositoryMod> allRepositoryMods) { var selection = mod.GetCurrentSelection(); switch (selection) { case ModSelectionDisabled: return(null); case ModSelectionDownload when string.IsNullOrWhiteSpace(mod.DownloadIdentifier): return("Name must be a valid folder name"); case ModSelectionDownload when mod.DownloadIdentifier.IndexOfAny(Path.GetInvalidPathChars()) >= 0 || mod.DownloadIdentifier.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0: return("Invalid characters in name"); case ModSelectionDownload selectStorage: { if (selectStorage.DownloadStorage.State == LoadingState.Loading) { return(null); } var folderExists = selectStorage.DownloadStorage.HasMod(mod.DownloadIdentifier); return(folderExists ? "Name in use" : null); } case ModSelectionStorageMod selectMod when GetModAction(mod, selectMod.StorageMod) == ModActionEnum.AbortActiveAndUpdate: return("This mod is currently being updated"); case ModSelectionStorageMod selectMod: { var conflicts = GetConflictsUsingMod(mod, selectMod.StorageMod, allRepositoryMods); if (!conflicts.Any()) { return(null); } var conflictNames = conflicts.Select(c => $"{c}"); return("In conflict with: " + string.Join(", ", conflictNames)); } default: return(null); } }
internal static IModelStorageMod?AutoSelect(IModelRepositoryMod repoMod, IEnumerable <IModelStorageMod> storageMods, List <IModelRepositoryMod> allRepoMods) { var byActionType = new Dictionary <ModActionEnum, List <IModelStorageMod> >(); foreach (var storageMod in storageMods) { if (GetConflictsUsingMod(repoMod, storageMod, allRepoMods).Any()) { continue; } var action = GetModAction(repoMod, storageMod); byActionType.AddInBin(action, storageMod); } // Order of precedence var precedence = new[] { ModActionEnum.Use, ModActionEnum.Await, ModActionEnum.ContinueUpdate, ModActionEnum.Update }; foreach (var actionType in precedence) { if (!byActionType.TryGetValue(actionType, out var candidates)) { continue; } // no steam var foundMod = candidates.FirstOrDefault(mod => mod.CanWrite); if (foundMod != null) { return(foundMod); } // steam foundMod = candidates.FirstOrDefault(mod => !mod.CanWrite); if (foundMod != null) { return(foundMod); } } return(null); }
private static List <IModelRepositoryMod> GetConflictsUsingMod(IModelRepositoryMod repoMod, IModelStorageMod storageMod, IEnumerable <IModelRepositoryMod> allRepoMods) { var result = new List <IModelRepositoryMod>(); foreach (var mod in allRepoMods) { if (mod == repoMod) { continue; } if (mod.GetCurrentSelection() is not ModSelectionStorageMod otherMod || otherMod.StorageMod != storageMod) { continue; } if (IsConflicting(repoMod, mod, storageMod)) { result.Add(mod); } } return(result); }
private IModelStorageMod?AutoSelect(IModelRepositoryMod repoMod, IEnumerable <IModelRepositoryMod>?allRepoMods = null, params IModelStorageMod[] storageMods) { return(CoreCalculation.AutoSelect(repoMod, storageMods.ToList(), allRepoMods?.ToList() ?? new List <IModelRepositoryMod>())); }
// TODO: create more tests. especially for loading/error handling // TODO: split up. need to figure out how tho.. internal static ModActionEnum GetModAction(IModelRepositoryMod repoMod, IModelStorageMod storageMod) { if (repoMod.State == LoadingState.Loading || storageMod.GetState() == StorageModStateEnum.Loading) { return(ModActionEnum.Loading); } if (repoMod.State == LoadingState.Error || storageMod.GetState() == StorageModStateEnum.Error) { return(ModActionEnum.Unusable); } bool CheckMatch() => repoMod.GetMatchHash().IsMatch(storageMod.GetMatchHash()); bool CheckVersion() => repoMod.GetVersionHash().IsMatch(storageMod.GetVersionHash()); switch (storageMod.GetState()) { case StorageModStateEnum.CreatedWithUpdateTarget: { if (CheckVersion()) { return(ModActionEnum.ContinueUpdate); } if (CheckMatch()) { return(ModActionEnum.AbortAndUpdate); } return(ModActionEnum.Unusable); } case StorageModStateEnum.Created: { if (!CheckMatch()) { return(ModActionEnum.Unusable); } if (CheckVersion()) { return(ModActionEnum.Use); } return(storageMod.CanWrite ? ModActionEnum.Update : ModActionEnum.UnusableSteam); } case StorageModStateEnum.Updating: { if (CheckVersion()) { return(ModActionEnum.Await); } if (CheckMatch()) { return(ModActionEnum.AbortActiveAndUpdate); } return(ModActionEnum.Unusable); } default: throw new ArgumentOutOfRangeException(); } }