Beispiel #1
0
        public ProfileVM(ConfigurationVM parent, GameRelease?release = null, string?id = null)
        {
            ID      = id ?? Guid.NewGuid().ToString();
            Config  = parent;
            Release = release ?? GameRelease.Oblivion;
            AddGitPatcherCommand      = ReactiveCommand.Create(() => SetInitializer(new GitPatcherInitVM(this)));
            AddSolutionPatcherCommand = ReactiveCommand.Create(() => SetInitializer(new SolutionPatcherInitVM(this)));
            AddCliPatcherCommand      = ReactiveCommand.Create(() => SetInitializer(new CliPatcherInitVM(this)));
            AddSnippetPatcherCommand  = ReactiveCommand.Create(() => SetPatcherForInitialConfiguration(new CodeSnippetPatcherVM(this)));

            ProfileDirectory = Path.Combine(Execution.Paths.WorkingDirectory, ID);
            WorkingDirectory = Execution.Paths.ProfileWorkingDirectory(ID);

            var dataFolderResult = this.WhenAnyValue(x => x.DataPathOverride)
                                   .Select(path =>
            {
                if (path != null)
                {
                    return(Observable.Return(GetResponse <string> .Succeed(path)));
                }
                Log.Logger.Information("Starting to locate data folder");
                return(this.WhenAnyValue(x => x.Release)
                       .ObserveOn(RxApp.TaskpoolScheduler)
                       .Select(x =>
                {
                    try
                    {
                        if (!GameLocations.TryGetGameFolder(x, out var gameFolder))
                        {
                            return GetResponse <string> .Fail("Could not automatically locate Data folder.  Run Steam/GoG/etc once to properly register things.");
                        }
                        return GetResponse <string> .Succeed(Path.Combine(gameFolder, "Data"));
                    }
                    catch (Exception ex)
                    {
                        return GetResponse <string> .Fail(string.Empty, ex);
                    }
                }));
            })
                                   .Switch()
                                   // Watch folder for existance
                                   .Select(x =>
            {
                if (x.Failed)
                {
                    return(Observable.Return(x));
                }
                return(Noggog.ObservableExt.WatchFile(x.Value)
                       .StartWith(Unit.Default)
                       .Select(_ =>
                {
                    if (Directory.Exists(x.Value))
                    {
                        return x;
                    }
                    return GetResponse <string> .Fail($"Data folder did not exist: {x.Value}");
                }));
            })
                                   .Switch()
                                   .StartWith(GetResponse <string> .Fail("Data folder uninitialized"))
                                   .Replay(1)
                                   .RefCount();

            _DataFolder = dataFolderResult
                          .Select(x => x.Value)
                          .ToGuiProperty <string>(this, nameof(DataFolder), string.Empty);

            dataFolderResult
            .Subscribe(d =>
            {
                if (d.Failed)
                {
                    Log.Logger.Error($"Could not locate data folder: {d.Reason}");
                }
                else
                {
                    Log.Logger.Information($"Data Folder: {d.Value}");
                }
            })
            .DisposeWith(this);

            var loadOrderResult = Observable.CombineLatest(
                this.WhenAnyValue(x => x.Release),
                dataFolderResult,
                (release, dataFolder) => (release, dataFolder))
                                  .ObserveOn(RxApp.TaskpoolScheduler)
                                  .Select(x =>
            {
                if (x.dataFolder.Failed)
                {
                    return(Results: Observable.Empty <IChangeSet <LoadOrderEntryVM> >(), State: Observable.Return(ErrorResponse.Fail("Data folder not set")));
                }
                Log.Logger.Error($"Getting live load order for {x.release} -> {x.dataFolder.Value}");
                var liveLo = Mutagen.Bethesda.LoadOrder.GetLiveLoadOrder(x.release, x.dataFolder.Value, out var errors)
                             .Transform(listing => new LoadOrderEntryVM(listing, x.dataFolder.Value))
                             .DisposeMany();
                return(Results: liveLo, State: errors);
            })
                                  .StartWith((Results: Observable.Empty <IChangeSet <LoadOrderEntryVM> >(), State: Observable.Return(ErrorResponse.Fail("Load order uninitialized"))))
                                  .Replay(1)
                                  .RefCount();

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

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

            _LargeOverallError = Observable.CombineLatest(
                dataFolderResult,
                loadOrderResult
                .Select(x => x.State)
                .Switch(),
                Patchers.Connect()
                .ObserveOnGui()
                .FilterOnObservable(p => p.WhenAnyValue(x => x.IsOn), scheduler: RxApp.MainThreadScheduler)
                .QueryWhenChanged(q => q)
                .StartWith(Noggog.ListExt.Empty <PatcherVM>()),
                Patchers.Connect()
                .ObserveOnGui()
                .FilterOnObservable(p => Observable.CombineLatest(
                                        p.WhenAnyValue(x => x.IsOn),
                                        p.WhenAnyValue(x => x.State.IsHaltingError),
                                        (on, halting) => on && halting),
                                    scheduler: RxApp.MainThreadScheduler)
                .QueryWhenChanged(q => q)
                .StartWith(Noggog.ListExt.Empty <PatcherVM>()),
                LoadOrder.Connect()
                .ObserveOnGui()
                .FilterOnObservable(
                    x => x.WhenAnyValue(y => y.Exists)
                    .DistinctUntilChanged()
                    .Select(x => !x),
                    scheduler: RxApp.MainThreadScheduler)
                .QueryWhenChanged(q => q)
                .StartWith(Noggog.ListExt.Empty <LoadOrderEntryVM>())
                .Throttle(TimeSpan.FromMilliseconds(200), RxApp.MainThreadScheduler),
                (dataFolder, loadOrder, enabledPatchers, erroredEnabledPatchers, missingMods) =>
            {
                if (enabledPatchers.Count == 0)
                {
                    return(GetResponse <PatcherVM> .Fail("There are no enabled patchers to run."));
                }
                if (!dataFolder.Succeeded)
                {
                    return(dataFolder.BubbleFailure <PatcherVM>());
                }
                if (!loadOrder.Succeeded)
                {
                    return(loadOrder.BubbleFailure <PatcherVM>());
                }
                if (missingMods.Count > 0)
                {
                    return(GetResponse <PatcherVM> .Fail($"Load order had mods that were missing:{Environment.NewLine}{string.Join(Environment.NewLine, missingMods.Select(x => x.Listing.ModKey))}"));
                }
                if (erroredEnabledPatchers.Count > 0)
                {
                    var errPatcher = erroredEnabledPatchers.First();
                    return(GetResponse <PatcherVM> .Fail(errPatcher, $"\"{errPatcher.DisplayName}\" has a blocking error: {errPatcher.State.RunnableState.Reason}"));
                }
                return(GetResponse <PatcherVM> .Succeed(null !));
            })
Beispiel #2
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 !));
            })