public void WatchesForFileExistence(
            Utility.Return r,
            DirectoryPath folder,
            [Frozen] IFileSystem fs,
            [Frozen] IProfileIdentifier ident,
            [Frozen] IGameDirectoryLookup lookup,
            Func <ProfileDataFolderVm> sutF)
        {
            lookup.TryGet(ident.Release, out Arg.Any <DirectoryPath>())
            .Returns(x =>
            {
                x[1] = folder;
                return(true);
            });
            var dataFolder = Path.Combine(folder.Path, "Data");

            fs.Directory.Exists(dataFolder).Returns(r);
            var sut = sutF();

            sut.DataFolderResult.Succeeded.Should().Be(r == Utility.Return.True);
            if (r == Utility.Return.True)
            {
                sut.DataFolderResult.Value.Path.Should().Be(dataFolder);
                sut.Path.Path.Should().NotBeNullOrWhiteSpace();
            }
            else
            {
                sut.Path.Path.Should().BeNullOrWhiteSpace();
            }
        }
 public ProfileDirectories(
     IWorkingDirectoryProvider paths,
     IWorkingDirectorySubPaths workingDirectorySubPaths,
     IProfileIdentifier ident)
 {
     Paths = paths;
     WorkingDirectorySubPaths = workingDirectorySubPaths;
     Ident = ident;
 }
Example #3
0
        public ProfileLoadOrder(
            ILogger logger,
            ILiveLoadOrderProvider liveLoadOrderProvider,
            IPluginListingsPathProvider listingsPathProvider,
            IProfileIdentifier ident,
            IProfileDataFolderVm dataFolder)
        {
            var loadOrderResult = dataFolder.WhenAnyValue(x => x.DataFolderResult)
                                  .ObserveOn(RxApp.TaskpoolScheduler)
                                  .Select(x =>
            {
                if (x.Failed)
                {
                    return(Results: Observable.Empty <IChangeSet <ReadOnlyModListingVM> >(), State: Observable.Return(ErrorResponse.Fail("Data folder not set")));
                }
                logger.Error("Getting live load order for {Release}. DataDirectory: {DataDirectory}, Plugin File Path: {PluginFilePath}", ident.Release, x.Value, listingsPathProvider.Path);
                var liveLo = liveLoadOrderProvider.Get(out var errors)
                             .Transform(listing => new ReadOnlyModListingVM(listing, x.Value))
                             .DisposeMany();
                return(Results: liveLo, State: errors);
            })
                                  .StartWith((Results: Observable.Empty <IChangeSet <ReadOnlyModListingVM> >(), State: Observable.Return(ErrorResponse.Fail("Load order uninitialized"))))
                                  .Replay(1)
                                  .RefCount();

            loadOrderResult.Select(lo => lo.State)
            .Switch()
            .Subscribe(loErr =>
            {
                if (loErr.Succeeded)
                {
                    logger.Information("Load order location successful");
                }
                else
                {
                    logger.Information("Load order location error: {Reason}", loErr.Reason);
                }
            })
            .DisposeWith(this);

            LoadOrder = loadOrderResult
                        .Select(x => x.Results)
                        .Switch()
                        .ObserveOnGui()
                        .AsObservableList();

            _state = loadOrderResult
                     .Select(x => x.State)
                     .Switch()
                     .ToGuiProperty(this, nameof(State), ErrorResponse.Success, deferSubscription: true);
        }
Example #4
0
 public ProfileSimpleLinkCacheVm(
     ILogger logger,
     IProfileLoadOrder loadOrder,
     IProfileDataFolderVm dataFolder,
     IProfileIdentifier ident)
 {
     _simpleLinkCache = Observable.CombineLatest(
         dataFolder.WhenAnyValue(x => x.Path),
         loadOrder.LoadOrder.Connect()
         .QueryWhenChanged()
         .Select(q => q.Where(x => x.Enabled).Select(x => x.ModKey).ToArray())
         .StartWithEmpty(),
         (dataFolder, loadOrder) => (dataFolder, loadOrder))
                        .Throttle(TimeSpan.FromMilliseconds(100), RxApp.TaskpoolScheduler)
                        .Select(x =>
     {
         return(Observable.Create <(ILinkCache?Cache, IDisposable Disposable)>(obs =>
         {
             try
             {
                 var loadOrder = Mutagen.Bethesda.Plugins.Order.LoadOrder.Import(
                     x.dataFolder,
                     x.loadOrder,
                     factory: (modPath) => ModInstantiator.Importer(modPath, ident.Release));
                 obs.OnNext(
                     (loadOrder.ToUntypedImmutableLinkCache(LinkCachePreferences.OnlyIdentifiers()),
                      loadOrder));
                 obs.OnCompleted();
                 // ToDo
                 // Figure out why returning this is disposing too early.
                 // Gets disposed undesirably, which makes formlink pickers fail
                 // return loadOrder;
             }
             catch (Exception ex)
             {
                 logger.Error(ex, "Error creating simple link cache for GUI lookups");
                 obs.OnNext((default, Disposable.Empty));
Example #5
0
        public ProfileVm(
            ILifetimeScope scope,
            IPatcherInitializationVm initVm,
            IProfileDataFolderVm dataFolder,
            IProfileIdentifier ident,
            IProfileNameVm nameProvider,
            IProfileLoadOrder loadOrder,
            IProfileDirectories dirs,
            IProfileVersioning versioning,
            IProfileDisplayControllerVm profileDisplay,
            ILockToCurrentVersioning lockSetting,
            ISelectedProfileControllerVm selProfile,
            IProfileExporter exporter,
            IProfileGroupsList groupsList,
            IEnvironmentErrorsVm environmentErrors,
            OverallErrorVm overallErrorVm,
            StartRun startRun,
            ILogger logger)
        {
            Scope              = scope;
            Init               = initVm;
            OverallErrorVm     = overallErrorVm;
            NameVm             = nameProvider;
            Groups             = groupsList.Groups;
            DataFolderOverride = dataFolder;
            Versioning         = versioning;
            LockSetting        = lockSetting;
            Exporter           = exporter;
            DisplayController  = profileDisplay;
            _startRun          = startRun;
            _logger            = logger;
            ID      = ident.ID;
            Release = ident.Release;

            GroupsDisplay = new SourceListUiFunnel <GroupVm>(Groups, this);

            ProfileDirectory = dirs.ProfileDirectory;
            WorkingDirectory = dirs.WorkingDirectory;

            EnvironmentErrors = environmentErrors;

            _dataFolder = dataFolder.WhenAnyValue(x => x.Path)
                          .ToGuiProperty <DirectoryPath>(this, nameof(DataFolder), string.Empty, deferSubscription: true);

            LoadOrder = loadOrder.LoadOrder;

            var enabledGroups = Groups.Connect()
                                .ObserveOnGui()
                                .FilterOnObservable(p => p.WhenAnyValue(x => x.IsOn), scheduler: RxApp.MainThreadScheduler)
                                .RefCount();

            var enabledGroupModKeys = enabledGroups
                                      .Transform(x => x.ModKey)
                                      .QueryWhenChanged(q => q.ToHashSet())
                                      .Replay(1).RefCount();

            _blockingError = Observable.CombineLatest(
                dataFolder.WhenAnyValue(x => x.DataFolderResult),
                loadOrder.WhenAnyValue(x => x.State),
                enabledGroups
                .QueryWhenChanged(q => q)
                .StartWith(Noggog.ListExt.Empty <GroupVm>()),
                enabledGroups
                .FilterOnObservable(g => g.WhenAnyValue(x => x.State).Select(x => x.IsHaltingError))
                .QueryWhenChanged(q => q)
                .StartWith(Noggog.ListExt.Empty <GroupVm>()),
                LoadOrder.Connect()
                .FilterOnObservable(
                    x =>
            {
                return(Observable.CombineLatest(
                           x.WhenAnyValue(y => y.Exists)
                           .DistinctUntilChanged(),
                           enabledGroupModKeys
                           .Select(groupModKeys => groupModKeys.Contains(x.ModKey)),
                           (exists, isEnabledGroupKey) => !exists && !isEnabledGroupKey));
            },
                    scheduler: RxApp.MainThreadScheduler)
                .QueryWhenChanged(q => q)
                .StartWith(Noggog.ListExt.Empty <ReadOnlyModListingVM>())
                .Throttle(TimeSpan.FromMilliseconds(200), RxApp.MainThreadScheduler),
                this.WhenAnyValue(x => x.IgnoreMissingMods),
                (dataFolder, loadOrder, enabledGroups, erroredEnabledGroups, missingMods, ignoreMissingMods) =>
            {
                if (enabledGroups.Count == 0)
                {
                    return(GetResponse <ViewModel> .Fail("There are no enabled groups to run."));
                }
                if (!dataFolder.Succeeded)
                {
                    return(dataFolder.BubbleFailure <ViewModel>());
                }
                if (!loadOrder.Succeeded)
                {
                    return(loadOrder.BubbleFailure <ViewModel>());
                }
                if (!ignoreMissingMods && missingMods.Count > 0)
                {
                    return(GetResponse <ViewModel> .Fail($"Load order had mods that were missing:{Environment.NewLine}{string.Join(Environment.NewLine, missingMods.Select(x => x.ModKey))}"));
                }
                if (erroredEnabledGroups.Count > 0)
                {
                    var errGroup = erroredEnabledGroups.First();
                    return(GetResponse <ViewModel> .Fail(errGroup, $"\"{errGroup.Name}\" has a blocking error: {errGroup.State}"));
                }
                return(GetResponse <ViewModel> .Succeed(null !));
            })
        public ProfileDataFolderVm(
            ILogger logger,
            ISchedulerProvider schedulerProvider,
            IWatchDirectory watchDirectory,
            IFileSystem fileSystem,
            IGameDirectoryLookup gameLocator,
            IProfileIdentifier ident)
        {
            FileSystem        = fileSystem;
            GameLocator       = gameLocator;
            _dataFolderResult = this.WhenAnyValue(x => x.DataPathOverride)
                                .Select(path =>
            {
                if (path != null)
                {
                    return(Observable.Return(GetResponse <DirectoryPath> .Succeed(path)));
                }
                logger.Information("Starting to locate data folder");
                return(Observable.Return(ident.Release)
                       .ObserveOn(schedulerProvider.TaskPool)
                       .Select(x =>
                {
                    try
                    {
                        if (!gameLocator.TryGet(x, out var gameFolder))
                        {
                            return GetResponse <DirectoryPath> .Fail(
                                "Could not automatically locate Data folder.  Run Steam/GoG/etc once to properly register things.");
                        }

                        return GetResponse <DirectoryPath> .Succeed(System.IO.Path.Combine(gameFolder, "Data"));
                    }
                    catch (Exception ex)
                    {
                        return GetResponse <DirectoryPath> .Fail(string.Empty, ex);
                    }
                }));
            })
                                .Switch()
                                // Watch folder for existence
                                .Select(x =>
            {
                if (x.Failed)
                {
                    return(Observable.Return(x));
                }
                return(watchDirectory.Watch(x.Value)
                       .StartWith(Unit.Default)
                       .Select(_ =>
                {
                    try
                    {
                        if (fileSystem.Directory.Exists(x.Value))
                        {
                            return x;
                        }
                        return GetResponse <DirectoryPath> .Fail($"Data folder did not exist: {x.Value}");
                    }
                    catch (Exception ex)
                    {
                        return GetResponse <DirectoryPath> .Fail(string.Empty, ex);
                    }
                }));
            })
                                .Switch()
                                .StartWith(GetResponse <DirectoryPath> .Fail("Data folder uninitialized"))
                                .Do(d =>
            {
                if (d.Failed)
                {
                    logger.Error("Could not locate data folder: {Reason}", d.Reason);
                }
                else
                {
                    logger.Information("Data Folder: {DataFolderPath}", d.Value);
                }
            })
                                .ToGuiProperty(this, nameof(DataFolderResult), deferSubscription: true);

            _path = this.WhenAnyValue(x => x.DataFolderResult)
                    .Select(x => x.Value)
                    .ToGuiProperty(this, nameof(Path), deferSubscription: true);
        }