public UserInterventionHandlers(MainWindowVM mvm) { MainWindow = mvm; }
public MainWindow() { try { // Wire any unhandled crashing exceptions to log before exiting AppDomain.CurrentDomain.UnhandledException += (sender, e) => { // Don't do any special logging side effects Utils.LogStraightToFile("Error."); Utils.LogStraightToFile(((Exception)e.ExceptionObject).ToString()); Environment.Exit(-1); }; Utils.Log($"Wabbajack Build - {ThisAssembly.Git.Sha}"); Utils.Log($"Running in {AbsolutePath.EntryPoint}"); var p = SystemParametersConstructor.Create(); Utils.Log($"Detected Windows Version: {p.WindowsVersion}"); if (!(p.WindowsVersion.Major >= 10 && p.WindowsVersion.Minor >= 0)) { Utils.Log( $"You are not running a recent version of Windows (version 10 or greater), Wabbajack is not supported on OS versions older than Windows 10."); } Utils.Log( $"System settings - ({p.SystemMemorySize.ToFileSizeString()} RAM) ({p.SystemPageSize.ToFileSizeString()} Page), Display: {p.ScreenWidth} x {p.ScreenHeight} ({p.VideoMemorySize.ToFileSizeString()} VRAM - VideoMemorySizeMb={p.EnbLEVRAMSize})"); if (p.SystemPageSize == 0) { Utils.Log("Pagefile is disabled! Consider increasing to 20000MB. A disabled pagefile can cause crashes and poor in-game performance."); } else if (p.SystemPageSize < 2e+10) { Utils.Log("Pagefile below recommended! Consider increasing to 20000MB. A suboptimal pagefile can cause crashes and poor in-game performance."); } Warmup(); var _ = LauncherUpdater.Run(); var(settings, loadedSettings) = MainSettings.TryLoadTypicalSettings().AsTask().Result; // Load settings if (CLIArguments.NoSettings || !loadedSettings) { _settings = new MainSettings { Version = Consts.SettingsVersion }; RunWhenLoaded(DefaultSettings); } else { _settings = settings; RunWhenLoaded(LoadSettings); } // Set datacontext _mwvm = new MainWindowVM(this, _settings); DataContext = _mwvm; // Bring window to the front if it isn't already this.Initialized += (s, e) => { this.Activate(); this.Topmost = true; this.Focus(); }; this.ContentRendered += (s, e) => { this.Topmost = false; }; } catch (Exception ex) { Utils.LogStraightToFile("Error"); Utils.LogStraightToFile(ex.ToString()); Environment.Exit(-1); } }
public ModListGalleryVM(MainWindowVM mainWindowVM) : base(mainWindowVM) { MWVM = mainWindowVM; // load persistent filter settings if (settings.IsPersistent) { GameType = !string.IsNullOrEmpty(settings.Game) ? settings.Game : ALL_GAME_TYPE; ShowNSFW = settings.ShowNSFW; ShowUtilityLists = settings.ShowUtilityLists; OnlyInstalled = settings.OnlyInstalled; Search = settings.Search; } else { GameType = ALL_GAME_TYPE; } // subscribe to save signal MWVM.Settings.SaveSignal .Subscribe(_ => UpdateFiltersSettings()) .DisposeWith(this.CompositeDisposable); ClearFiltersCommand = ReactiveCommand.Create( () => { OnlyInstalled = false; ShowNSFW = false; ShowUtilityLists = false; Search = string.Empty; GameType = ALL_GAME_TYPE; }); this.WhenAny(x => x.OnlyInstalled) .Subscribe(val => { if (val) { GameType = ALL_GAME_TYPE; } }) .DisposeWith(CompositeDisposable); var sourceList = Observable.Return(Unit.Default) .ObserveOn(RxApp.TaskpoolScheduler) .SelectTask(async _ => { try { Error = null; var list = await ModlistMetadata.LoadFromGithub(); Error = ErrorResponse.Success; return(list .AsObservableChangeSet(x => x.DownloadMetadata?.Hash ?? Hash.Empty)); } catch (Exception ex) { Utils.Error(ex); Error = ErrorResponse.Fail(ex); return(Observable.Empty <IChangeSet <ModlistMetadata, Hash> >()); } }) // Unsubscribe and release when not active .FlowSwitch( this.WhenAny(x => x.IsActive), valueWhenOff: Observable.Return(ChangeSet <ModlistMetadata, Hash> .Empty)) .Switch() .RefCount(); _Loaded = sourceList.CollectionCount() .Select(c => c > 0) .ToProperty(this, nameof(Loaded)); // Convert to VM and bind to resulting list sourceList .ObserveOnGuiThread() .Transform(m => new ModListMetadataVM(this, m)) .DisposeMany() // Filter only installed .Filter(this.WhenAny(x => x.OnlyInstalled) .Select <bool, Func <ModListMetadataVM, bool> >(onlyInstalled => (vm) => { if (!onlyInstalled) { return(true); } if (!GameRegistry.Games.TryGetValue(vm.Metadata.Game, out var gameMeta)) { return(false); } return(gameMeta.IsInstalled); })) // Filter on search box .Filter(this.WhenAny(x => x.Search) .Debounce(TimeSpan.FromMilliseconds(150), RxApp.MainThreadScheduler) .Select <string, Func <ModListMetadataVM, bool> >(search => (vm) => { if (string.IsNullOrWhiteSpace(search)) { return(true); } return(vm.Metadata.Title.ContainsCaseInsensitive(search)); })) .Filter(this.WhenAny(x => x.ShowNSFW) .Select <bool, Func <ModListMetadataVM, bool> >(showNSFW => vm => { if (!vm.Metadata.NSFW) { return(true); } return(vm.Metadata.NSFW && showNSFW); })) .Filter(this.WhenAny(x => x.ShowUtilityLists) .Select <bool, Func <ModListMetadataVM, bool> >(showUtilityLists => vm => showUtilityLists ? vm.Metadata.UtilityList : !vm.Metadata.UtilityList)) // Filter by Game .Filter(this.WhenAny(x => x.GameType) .Debounce(TimeSpan.FromMilliseconds(150), RxApp.MainThreadScheduler) .Select <string, Func <ModListMetadataVM, bool> >(GameType => (vm) => { if (GameType == ALL_GAME_TYPE) { return(true); } if (string.IsNullOrEmpty(GameType)) { return(false); } return(GameType == vm.Metadata.Game.GetDescription <Game>().ToString()); })) .Bind(ModLists) .Subscribe() .DisposeWith(CompositeDisposable); // Extra GC when navigating away, just to immediately clean up modlist metadata this.WhenAny(x => x.IsActive) .Where(x => !x) .Skip(1) .Delay(TimeSpan.FromMilliseconds(50), RxApp.MainThreadScheduler) .Subscribe(_ => { GC.Collect(); }) .DisposeWith(CompositeDisposable); }
public void Init(MainWindowVM vm, MainSettings settings) { DataContext = vm; _mwvm = vm; _settings = settings; }
public CompilerVM(MainWindowVM mainWindowVM) { MWVM = mainWindowVM; OutputLocation = new FilePickerVM() { ExistCheckOption = FilePickerVM.CheckOptions.IfPathNotEmpty, PathType = FilePickerVM.PathTypeOptions.Folder, PromptTitle = "Select the folder to place the resulting modlist.wabbajack file", }; // Load settings CompilerSettings settings = MWVM.Settings.Compiler; SelectedCompilerType = settings.LastCompiledModManager; OutputLocation.TargetPath = settings.OutputLocation; MWVM.Settings.SaveSignal .Subscribe(_ => { settings.LastCompiledModManager = SelectedCompilerType; settings.OutputLocation = OutputLocation.TargetPath; }) .DisposeWith(CompositeDisposable); // Swap to proper sub VM based on selected type _compiler = this.WhenAny(x => x.SelectedCompilerType) // Delay so the initial VM swap comes in immediately, subVM comes right after .DelayInitial(TimeSpan.FromMilliseconds(50), RxApp.MainThreadScheduler) .Select <ModManager, ISubCompilerVM>(type => { switch (type) { case ModManager.MO2: return(new MO2CompilerVM(this)); case ModManager.Vortex: return(new VortexCompilerVM(this)); default: return(null); } }) // Unload old VM .Pairwise() .Do(pair => { pair.Previous?.Unload(); }) .Select(p => p.Current) .ToProperty(this, nameof(Compiler)); // Let sub VM determine what settings we're displaying and when _currentModlistSettings = this.WhenAny(x => x.Compiler.ModlistSettings) .ToProperty(this, nameof(CurrentModlistSettings)); _image = this.WhenAny(x => x.CurrentModlistSettings.ImagePath.TargetPath) // Throttle so that it only loads image after any sets of swaps have completed .Throttle(TimeSpan.FromMilliseconds(50), RxApp.MainThreadScheduler) .DistinctUntilChanged() .Select(path => { if (string.IsNullOrWhiteSpace(path)) { return(UIUtils.BitmapImageFromResource("Resources/Wabba_Mouth_No_Text.png")); } if (UIUtils.TryGetBitmapImageFromFile(path, out var image)) { return(image); } return(null); }) .ToProperty(this, nameof(Image)); _compiling = this.WhenAny(x => x.Compiler.ActiveCompilation) .Select(compilation => compilation != null) .ObserveOnGuiThread() .ToProperty(this, nameof(Compiling)); BackCommand = ReactiveCommand.Create( execute: () => { mainWindowVM.ActivePane = mainWindowVM.ModeSelectionVM; StartedCompilation = false; Completed = null; }, canExecute: this.WhenAny(x => x.Compiling) .Select(x => !x)); // Compile progress updates and populate ObservableCollection Dictionary <int, CPUDisplayVM> cpuDisplays = new Dictionary <int, CPUDisplayVM>(); this.WhenAny(x => x.Compiler.ActiveCompilation) .SelectMany(c => c?.QueueStatus ?? Observable.Empty <CPUStatus>()) .ObserveOn(RxApp.TaskpoolScheduler) // Attach start times to incoming CPU items .Scan( new CPUDisplayVM(), (_, cpu) => { var ret = cpuDisplays.TryCreate(cpu.ID); ret.AbsorbStatus(cpu); return(ret); }) .ToObservableChangeSet(x => x.Status.ID) .Batch(TimeSpan.FromMilliseconds(250), RxApp.TaskpoolScheduler) .EnsureUniqueChanges() .Filter(i => i.Status.IsWorking && i.Status.ID != WorkQueue.UnassignedCpuId) .ObserveOn(RxApp.MainThreadScheduler) .Sort(SortExpressionComparer <CPUDisplayVM> .Ascending(s => s.StartTime)) .Bind(StatusList) .Subscribe() .DisposeWith(CompositeDisposable); _percentCompleted = this.WhenAny(x => x.Compiler.ActiveCompilation) .StartWith(default(ACompiler)) .CombineLatest( this.WhenAny(x => x.Completed), (compiler, completed) => { if (compiler == null) { return(Observable.Return <float>(completed != null ? 1f : 0f)); } return(compiler.PercentCompleted.StartWith(0)); }) .Switch() .Debounce(TimeSpan.FromMilliseconds(25)) .ToProperty(this, nameof(PercentCompleted)); BeginCommand = ReactiveCommand.CreateFromTask( canExecute: this.WhenAny(x => x.Compiler.CanCompile) .Switch(), execute: async() => { try { await this.Compiler.Compile(); Completed = ErrorResponse.Success; } catch (Exception ex) { Completed = ErrorResponse.Fail(ex); while (ex.InnerException != null) { ex = ex.InnerException; } Utils.Error(ex, $"Compiler error"); } }); // When sub compiler begins a compile, mark state variable BeginCommand.StartingExecution() .Subscribe(_ => { StartedCompilation = true; }) .DisposeWith(CompositeDisposable); // Listen for user interventions, and compile a dynamic list of all unhandled ones var activeInterventions = this.WhenAny(x => x.Compiler.ActiveCompilation) .SelectMany(c => c?.LogMessages ?? Observable.Empty <IStatusMessage>()) .WhereCastable <IStatusMessage, IUserIntervention>() .ToObservableChangeSet() .AutoRefresh(i => i.Handled) .Filter(i => !i.Handled) .AsObservableList(); // Find the top intervention /w no CPU ID to be marked as "global" _ActiveGlobalUserIntervention = activeInterventions.Connect() .Filter(x => x.CpuID == WorkQueue.UnassignedCpuId) .QueryWhenChanged(query => query.FirstOrDefault()) .ObserveOnGuiThread() .ToProperty(this, nameof(ActiveGlobalUserIntervention)); CloseWhenCompleteCommand = ReactiveCommand.Create( canExecute: this.WhenAny(x => x.Completed) .Select(x => x != null), execute: () => { MWVM.ShutdownApplication(); }); GoToModlistCommand = ReactiveCommand.Create( canExecute: this.WhenAny(x => x.Completed) .Select(x => x != null), execute: () => { if (string.IsNullOrWhiteSpace(OutputLocation.TargetPath)) { Process.Start("explorer.exe", Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location)); } else { Process.Start("explorer.exe", OutputLocation.TargetPath); } }); }