mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
More Installer stuff and modularisation.
This commit is contained in:
parent
365970bef9
commit
2ada4621b8
@ -4,6 +4,9 @@ using Fluxor;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Wabbajack.App.Blazor.Models;
|
||||
using Wabbajack.App.Blazor.Utility;
|
||||
using Wabbajack.DTOs;
|
||||
using Wabbajack.Services.OSIntegrated;
|
||||
|
||||
namespace Wabbajack.App.Blazor
|
||||
@ -12,7 +15,7 @@ namespace Wabbajack.App.Blazor
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IHost _host;
|
||||
|
||||
|
||||
public App()
|
||||
{
|
||||
_host = Host.CreateDefaultBuilder(Array.Empty<string>())
|
||||
@ -28,10 +31,12 @@ namespace Wabbajack.App.Blazor
|
||||
services.AddOSIntegrated();
|
||||
services.AddFluxor(o => o.ScanAssemblies(typeof(App).Assembly));
|
||||
services.AddBlazorWebView();
|
||||
services.AddAllSingleton<ILoggerProvider, LoggerProvider>();
|
||||
services.AddTransient<MainWindow>();
|
||||
services.AddSingleton<SystemParametersConstructor>();
|
||||
return services;
|
||||
}
|
||||
|
||||
|
||||
private void OnStartup(object sender, StartupEventArgs e)
|
||||
{
|
||||
MainWindow mainWindow = _serviceProvider.GetRequiredService<MainWindow>();
|
||||
|
22
Wabbajack.App.Blazor/Components/InfoBlock.razor
Normal file
22
Wabbajack.App.Blazor/Components/InfoBlock.razor
Normal file
@ -0,0 +1,22 @@
|
||||
<div id="info-block">
|
||||
<div class="title">@Title</div>
|
||||
<div class="subtitle">@Subtitle</div>
|
||||
<div class="comment">@Comment</div>
|
||||
<div class="description">@Description</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public string Title { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Subtitle { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Comment { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Description { get; set; }
|
||||
|
||||
}
|
27
Wabbajack.App.Blazor/Components/InfoBlock.razor.scss
Normal file
27
Wabbajack.App.Blazor/Components/InfoBlock.razor.scss
Normal file
@ -0,0 +1,27 @@
|
||||
#info-block {
|
||||
width: 50%;
|
||||
height: fit-content;
|
||||
margin: 1rem;
|
||||
|
||||
.title {
|
||||
font-size: 5rem;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin-left: 10px;
|
||||
font-size: 2rem;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
.comment {
|
||||
margin-left: 20px;
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-left: 20px;
|
||||
margin-top: 20px;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
}
|
5
Wabbajack.App.Blazor/Components/Logger.razor
Normal file
5
Wabbajack.App.Blazor/Components/Logger.razor
Normal file
@ -0,0 +1,5 @@
|
||||
<h3>Logger</h3>
|
||||
|
||||
@code {
|
||||
|
||||
}
|
@ -45,7 +45,7 @@ namespace Wabbajack.App.Blazor.Components
|
||||
dispose.Dispose();
|
||||
|
||||
AbsolutePath path = KnownFolders.EntryPoint.Combine("downloaded_mod_lists", Metadata.Links.MachineURL).WithExtension(Ext.Wabbajack);
|
||||
_dispatcher.Dispatch(new UpdateInstallState(InstallState.InstallStateEnum.Configuration, null, path));
|
||||
_dispatcher.Dispatch(new UpdateInstallState(InstallState.InstallStateEnum.Configuration, null, path, null, null));
|
||||
NavigationManager.NavigateTo("/configure");
|
||||
|
||||
}
|
||||
|
@ -1,21 +1,59 @@
|
||||
using System;
|
||||
using Fluxor;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Wabbajack.App.Blazor.Models;
|
||||
using Wabbajack.App.Blazor.Utility;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Installer;
|
||||
using Wabbajack.Paths.IO;
|
||||
|
||||
namespace Wabbajack.App.Blazor
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private readonly ILogger<MainWindow> _logger;
|
||||
private readonly IStore _store;
|
||||
private readonly ILogger<MainWindow> _logger;
|
||||
private readonly LoggerProvider _loggerProvider;
|
||||
private readonly IStore _store;
|
||||
private readonly SystemParametersConstructor _systemParams;
|
||||
|
||||
public MainWindow(ILogger<MainWindow> logger, IStore store, IServiceProvider serviceProvider)
|
||||
public MainWindow(ILogger<MainWindow> logger, IStore store, IServiceProvider serviceProvider, LoggerProvider loggerProvider,
|
||||
SystemParametersConstructor systemParams)
|
||||
{
|
||||
_logger = logger;
|
||||
_store = store;
|
||||
_logger = logger;
|
||||
_store = store;
|
||||
_loggerProvider = loggerProvider;
|
||||
_systemParams = systemParams;
|
||||
_store.InitializeAsync().Wait();
|
||||
Resources.Add("services", serviceProvider);
|
||||
InitializeComponent();
|
||||
|
||||
try
|
||||
{
|
||||
// TODO: Not sure how to set this up.
|
||||
//_logger.LogInformation("Wabbajack Build - {Sha}", ThisAssembly.Git.Sha);
|
||||
_logger.LogInformation("Running in {EntryPoint}", KnownFolders.EntryPoint);
|
||||
|
||||
SystemParameters p = _systemParams.Create();
|
||||
|
||||
_logger.LogInformation("Detected Windows Version: {Version}", Environment.OSVersion.VersionString);
|
||||
|
||||
_logger.LogInformation(
|
||||
"System settings - ({MemorySize} RAM) ({PageSize} Page), Display: {ScreenWidth} x {ScreenHeight} ({Vram} VRAM - VideoMemorySizeMb={ENBVRam})",
|
||||
p.SystemMemorySize.ToFileSizeString(), p.SystemPageSize.ToFileSizeString(), p.ScreenWidth, p.ScreenHeight,
|
||||
p.VideoMemorySize.ToFileSizeString(), p.EnbLEVRAMSize);
|
||||
|
||||
if (p.SystemPageSize == 0)
|
||||
_logger.LogInformation(
|
||||
"Page file is disabled! Consider increasing to 20000MB. A disabled page file can cause crashes and poor in-game performance");
|
||||
else if (p.SystemPageSize < 2e+10)
|
||||
_logger.LogInformation(
|
||||
"Page file below recommended! Consider increasing to 20000MB. A suboptimal page file can cause crashes and poor in-game performance");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error during Main Window startup.");
|
||||
Environment.Exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
6
Wabbajack.App.Blazor/Models/Install.cs
Normal file
6
Wabbajack.App.Blazor/Models/Install.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace Wabbajack.App.Blazor.Models;
|
||||
|
||||
public class Install
|
||||
{
|
||||
|
||||
}
|
149
Wabbajack.App.Blazor/Models/LoggerProvider.cs
Normal file
149
Wabbajack.App.Blazor/Models/LoggerProvider.cs
Normal file
@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using DynamicData;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Wabbajack.Paths;
|
||||
using Wabbajack.Paths.IO;
|
||||
using Wabbajack.Services.OSIntegrated;
|
||||
|
||||
namespace Wabbajack.App.Blazor.Models;
|
||||
|
||||
public class LoggerProvider : ILoggerProvider
|
||||
{
|
||||
private readonly RelativePath _appName;
|
||||
private readonly Configuration _configuration;
|
||||
private readonly CompositeDisposable _disposables;
|
||||
private readonly Stream _logFile;
|
||||
private readonly StreamWriter _logStream;
|
||||
|
||||
public readonly ReadOnlyObservableCollection<ILogMessage> _messagesFiltered;
|
||||
private readonly DateTime _startupTime;
|
||||
|
||||
private long _messageId;
|
||||
private readonly SourceCache<ILogMessage, long> _messageLog = new(m => m.MessageId);
|
||||
private readonly Subject<ILogMessage> _messages = new();
|
||||
|
||||
public LoggerProvider(Configuration configuration)
|
||||
{
|
||||
_startupTime = DateTime.UtcNow;
|
||||
_configuration = configuration;
|
||||
_configuration.LogLocation.CreateDirectory();
|
||||
|
||||
_disposables = new CompositeDisposable();
|
||||
|
||||
// Messages
|
||||
// .ObserveOnGuiThread()
|
||||
// .Subscribe(m => _messageLog.AddOrUpdate(m));
|
||||
|
||||
Messages.Subscribe(LogToFile);
|
||||
|
||||
_messageLog.Connect()
|
||||
.Bind(out _messagesFiltered)
|
||||
.Subscribe();
|
||||
|
||||
_appName = typeof(LoggerProvider).Assembly.Location.ToAbsolutePath().FileName;
|
||||
LogPath = _configuration.LogLocation.Combine($"{_appName}.current.log");
|
||||
_logFile = LogPath.Open(FileMode.Append, FileAccess.Write);
|
||||
|
||||
_logStream = new StreamWriter(_logFile, Encoding.UTF8);
|
||||
}
|
||||
|
||||
public IObservable<ILogMessage> Messages => _messages;
|
||||
public AbsolutePath LogPath { get; }
|
||||
public ReadOnlyObservableCollection<ILogMessage> MessageLog => _messagesFiltered;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_disposables.Dispose();
|
||||
}
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
return new Logger(this, categoryName);
|
||||
}
|
||||
|
||||
private void LogToFile(ILogMessage logMessage)
|
||||
{
|
||||
var line = $"[{logMessage.TimeStamp - _startupTime}] {logMessage.LongMessage}";
|
||||
lock (_logStream)
|
||||
{
|
||||
_logStream.Write(line);
|
||||
_logStream.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
private long NextMessageId()
|
||||
{
|
||||
return Interlocked.Increment(ref _messageId);
|
||||
}
|
||||
|
||||
public class Logger : ILogger
|
||||
{
|
||||
private readonly string _categoryName;
|
||||
private readonly LoggerProvider _provider;
|
||||
private ImmutableList<object> Scopes = ImmutableList<object>.Empty;
|
||||
|
||||
public Logger(LoggerProvider provider, string categoryName)
|
||||
{
|
||||
_categoryName = categoryName;
|
||||
_provider = provider;
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception,
|
||||
Func<TState, Exception?, string> formatter)
|
||||
{
|
||||
Debug.WriteLine($"{logLevel} - {formatter(state, exception)}");
|
||||
_provider._messages.OnNext(new LogMessage<TState>(DateTime.UtcNow, _provider.NextMessageId(), logLevel,
|
||||
eventId, state, exception, formatter));
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
Scopes = Scopes.Add(state);
|
||||
return Disposable.Create(() => Scopes = Scopes.Remove(state));
|
||||
}
|
||||
}
|
||||
|
||||
public interface ILogMessage
|
||||
{
|
||||
long MessageId { get; }
|
||||
|
||||
string ShortMessage { get; }
|
||||
DateTime TimeStamp { get; }
|
||||
string LongMessage { get; }
|
||||
}
|
||||
|
||||
private record LogMessage<TState>(DateTime TimeStamp, long MessageId, LogLevel LogLevel, EventId EventId,
|
||||
TState State, Exception? Exception, Func<TState, Exception?, string> Formatter) : ILogMessage
|
||||
{
|
||||
public string ShortMessage => Formatter(State, Exception);
|
||||
|
||||
public string LongMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine(ShortMessage);
|
||||
if (Exception != null)
|
||||
{
|
||||
sb.Append("Exception: ");
|
||||
sb.Append(Exception);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,12 +4,10 @@
|
||||
<div id="content">
|
||||
<div class="image" style="background-image: url('@Image');"></div>
|
||||
<div class="list">
|
||||
<div class="info">
|
||||
<div class="name">@Name</div>
|
||||
<div class="author">@Author</div>
|
||||
<div class="version">@Version</div>
|
||||
<div class="description">@Description</div>
|
||||
</div>
|
||||
@if (Name != string.Empty)
|
||||
{
|
||||
<InfoBlock Title="@Name" Subtitle="@Author" Comment="@Version.ToString()" Description="@Description"/>
|
||||
}
|
||||
<div class="modlist-image" style="background-image: url('@Image');"></div>
|
||||
</div>
|
||||
<div class="settings">
|
||||
@ -20,9 +18,9 @@
|
||||
<span>Download Location</span>
|
||||
</div>
|
||||
<div class="paths">
|
||||
<span class="modlist-file">D:\Programs\Wabbajack\downloaded_mod_lists\magnum_opus.wabbajack</span>
|
||||
<span class="install-location"></span>
|
||||
<span class="download-location"></span>
|
||||
<span class="modlist-file">@ModListPath</span>
|
||||
<span class="install-location" @onclick="SelectInstallFolder">@InstallPath</span>
|
||||
<span class="download-location" @onclick="SelectDownloadFolder">@DownloadPath</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@ -52,4 +50,5 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="install" @onclick="NavigateInstall">Install</div>
|
||||
</div>
|
@ -1,13 +1,19 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using Fluxor;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
using Wabbajack.App.Blazor.Store;
|
||||
using Wabbajack.DTOs;
|
||||
using Wabbajack.DTOs.JsonConverters;
|
||||
using Wabbajack.Installer;
|
||||
using Wabbajack.Paths;
|
||||
using Ookii.Dialogs.Wpf;
|
||||
using Wabbajack.App.Blazor.Utility;
|
||||
|
||||
namespace Wabbajack.App.Blazor.Pages
|
||||
{
|
||||
@ -18,14 +24,20 @@ namespace Wabbajack.App.Blazor.Pages
|
||||
[Inject] private DTOSerializer _dtos { get; set; }
|
||||
[Inject] private IDispatcher _dispatcher { get; set; }
|
||||
|
||||
private string Name { get; set; }
|
||||
private string Author { get; set; }
|
||||
private string Description { get; set; }
|
||||
private Version Version { get; set; }
|
||||
private string Image { get; set; }
|
||||
private string Name { get; set; } = "";
|
||||
private string Author { get; set; } = "";
|
||||
private string Description { get; set; } = "";
|
||||
private Version Version { get; set; } = Version.Parse("0.0.0");
|
||||
private string Image { get; set; } = "";
|
||||
private ModList ModList { get; set; }
|
||||
private AbsolutePath ModListPath { get; set; }
|
||||
private AbsolutePath InstallPath { get; set; }
|
||||
private AbsolutePath DownloadPath { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
// var Location = KnownFolders.EntryPoint.Combine("downloaded_mod_lists", machineURL).WithExtension(Ext.Wabbajack);
|
||||
|
||||
await CheckValidInstallPath();
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
@ -33,23 +45,56 @@ namespace Wabbajack.App.Blazor.Pages
|
||||
|
||||
private async Task CheckValidInstallPath()
|
||||
{
|
||||
if (_installState.Value.CurrentPath == null) return;
|
||||
if (_installState.Value.CurrentModListPath == null) return;
|
||||
|
||||
var listPath = (AbsolutePath)_installState.Value.CurrentPath;
|
||||
ModList modList = await StandardInstaller.LoadFromFile(_dtos, listPath);
|
||||
_dispatcher.Dispatch(new UpdateInstallState(InstallState.InstallStateEnum.Configuration, modList, listPath));
|
||||
ModListPath = (AbsolutePath)_installState.Value.CurrentModListPath;
|
||||
ModList = await StandardInstaller.LoadFromFile(_dtos, ModListPath);
|
||||
_dispatcher.Dispatch(new UpdateInstallState(InstallState.InstallStateEnum.Configuration, ModList, ModListPath, null, null));
|
||||
|
||||
Name = _installState.Value.CurrentModList.Name;
|
||||
Author = _installState.Value.CurrentModList.Author;
|
||||
Description = _installState.Value.CurrentModList.Description;
|
||||
Version = _installState.Value.CurrentModList.Version;
|
||||
ModListPath = (AbsolutePath)_installState.Value.CurrentModListPath;
|
||||
|
||||
|
||||
Name = _installState.Value.CurrentModlist.Name;
|
||||
Author = _installState.Value.CurrentModlist.Author;
|
||||
Description = _installState.Value.CurrentModlist.Description;
|
||||
Version = _installState.Value.CurrentModlist.Version;
|
||||
|
||||
var imagepath = (AbsolutePath)_installState.Value.CurrentPath;
|
||||
Stream image = await StandardInstaller.ModListImageStream(imagepath);
|
||||
Stream image = await StandardInstaller.ModListImageStream(ModListPath);
|
||||
await using var reader = new MemoryStream();
|
||||
await image.CopyToAsync(reader);
|
||||
Image = $"data:image/png;base64,{Convert.ToBase64String(reader.ToArray())}";
|
||||
}
|
||||
|
||||
private async void SelectInstallFolder()
|
||||
{
|
||||
try
|
||||
{
|
||||
AbsolutePath? thing = await Dialog.ShowDialogNonBlocking(true);
|
||||
if (thing != null) InstallPath = (AbsolutePath)thing;
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Print(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async void SelectDownloadFolder()
|
||||
{
|
||||
try
|
||||
{
|
||||
AbsolutePath? thing = await Dialog.ShowDialogNonBlocking(true);
|
||||
if (thing != null) DownloadPath = (AbsolutePath)thing;
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Print(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void NavigateInstall()
|
||||
{
|
||||
_dispatcher.Dispatch(new UpdateInstallState(InstallState.InstallStateEnum.Configuration, ModList, ModListPath, InstallPath, DownloadPath));
|
||||
NavigationManager.NavigateTo("installing");
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ $checkbox-background-checked: purple;
|
||||
padding: 0.25rem;
|
||||
margin: 0.25rem;
|
||||
white-space: pre;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#content {
|
||||
@ -40,34 +41,6 @@ $checkbox-background-checked: purple;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
|
||||
.info {
|
||||
width: 50%;
|
||||
height: fit-content;
|
||||
margin: 1rem;
|
||||
|
||||
.name {
|
||||
font-size: 5rem;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
.author {
|
||||
margin-left: 10px;
|
||||
font-size: 2rem;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
.version {
|
||||
margin-left: 20px;
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-left: 20px;
|
||||
margin-top: 20px;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.modlist-image {
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
@ -79,6 +52,7 @@ $checkbox-background-checked: purple;
|
||||
}
|
||||
|
||||
.settings {
|
||||
font-size: 0.9rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
@ -167,4 +141,11 @@ $checkbox-background-checked: purple;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.install {
|
||||
width: 300px;
|
||||
height: 50px;
|
||||
background-color: green;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
@ -2,6 +2,9 @@
|
||||
@namespace Wabbajack.App.Blazor.Pages
|
||||
@using Wabbajack.Networking.WabbajackClientApi;
|
||||
@using Wabbajack.DTOs
|
||||
@using System.Diagnostics
|
||||
@using Microsoft.Extensions.Logging
|
||||
@using Wabbajack.App.Blazor.Models
|
||||
|
||||
@inject Client _client
|
||||
|
||||
@ -14,24 +17,31 @@
|
||||
|
||||
@code {
|
||||
|
||||
List<ModlistMetadata> _listItems = new();
|
||||
[Inject]
|
||||
private ILogger<Gallery> _logger { get; set; }
|
||||
// [Inject]
|
||||
// private LoggerProvider _loggerProvider { get; set; }
|
||||
[Inject]
|
||||
private Services.OSIntegrated.Configuration _configuration { get; set; }
|
||||
|
||||
protected override async Task<Task> OnAfterRenderAsync(bool firstRender)
|
||||
private List<ModlistMetadata> _listItems { get; set; } = new() { };
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (!firstRender) return base.OnAfterRenderAsync(firstRender);
|
||||
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Getting modlists...");
|
||||
ModlistMetadata[] modLists = await _client.LoadLists();
|
||||
_listItems = modLists.ToList();
|
||||
_listItems.AddRange(modLists.ToList());
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
//TODO: Figure out why an exception is thrown on first navigation.
|
||||
Debug.Print(ex.Message);
|
||||
_logger.LogError(ex, "Error while loading lists");
|
||||
}
|
||||
|
||||
return base.OnAfterRenderAsync(firstRender);
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
}
|
@ -1,14 +1,11 @@
|
||||
@page "/installing"
|
||||
@namespace Wabbajack.App.Blazor.Pages
|
||||
@using System.Diagnostics
|
||||
@using Wabbajack.Common
|
||||
@using Wabbajack.Paths.IO
|
||||
@inherits Fluxor.Blazor.Web.Components.FluxorComponent
|
||||
|
||||
<div id="content">
|
||||
<div class="image"></div>
|
||||
<div class="info">
|
||||
<div class="step">Downloading</div>
|
||||
<div class="step">@StatusText</div>
|
||||
<div class="description">Downloading missing archives...</div>
|
||||
<div class="number">16 of 228 archives downloaded</div>
|
||||
<div class="number">128.27 GB remaining</div>
|
||||
@ -27,21 +24,4 @@
|
||||
<div class="mod-author">Simon Magus and colinswrath</div>
|
||||
<div class="mod-info">Manbeast is a complete overhaul of Skyrim’s Werewolf system designed to balance existing Werewolf mechanics and add powerful new lycanthropic abilities to the game.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public string machineURL { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
var Location = KnownFolders.EntryPoint.Combine("downloaded_mod_lists", machineURL).WithExtension(Ext.Wabbajack);
|
||||
if (Location.FileExists())
|
||||
{
|
||||
Debug.Print("IT EXISTS I GUESS");
|
||||
}
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
}
|
||||
</div>
|
82
Wabbajack.App.Blazor/Pages/Installing.razor.cs
Normal file
82
Wabbajack.App.Blazor/Pages/Installing.razor.cs
Normal file
@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Shell;
|
||||
using Fluxor;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Wabbajack.App.Blazor.Store;
|
||||
using Wabbajack.App.Blazor.Utility;
|
||||
using Wabbajack.Downloaders.GameFile;
|
||||
using Wabbajack.DTOs;
|
||||
using Wabbajack.DTOs.JsonConverters;
|
||||
using Wabbajack.Installer;
|
||||
using Wabbajack.Paths;
|
||||
|
||||
namespace Wabbajack.App.Blazor.Pages
|
||||
{
|
||||
public partial class Installing
|
||||
{
|
||||
[Inject] private NavigationManager NavigationManager { get; set; }
|
||||
[Inject] private IState<InstallState> _installState { get; set; }
|
||||
[Inject] private DTOSerializer _dtos { get; set; }
|
||||
[Inject] private IDispatcher _dispatcher { get; set; }
|
||||
[Inject] private IServiceProvider _serviceProvider { get; set; }
|
||||
[Inject] private SystemParametersConstructor _parametersConstructor { get; set; }
|
||||
[Inject] private IGameLocator _gameLocator { get; set; }
|
||||
|
||||
private ModList modlist { get; set; }
|
||||
|
||||
private string StatusText { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
modlist = _installState.Value.CurrentModList;
|
||||
|
||||
Task.Run(BeginInstall);
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
private async Task BeginInstall()
|
||||
{
|
||||
// var postfix = (await ModListLocation.TargetPath.ToString().Hash()).ToHex();
|
||||
// await _settingsManager.Save(InstallSettingsPrefix + postfix, new SavedInstallSettings
|
||||
// {
|
||||
// ModListLocation = ModListLocation.TargetPath,
|
||||
// InstallLocation = Installer.Location.TargetPath,
|
||||
// DownloadLoadction = Installer.DownloadLocation.TargetPath,
|
||||
// Metadata = ModlistMetadata
|
||||
// });
|
||||
|
||||
try
|
||||
{
|
||||
var installer = StandardInstaller.Create(_serviceProvider, new InstallerConfiguration
|
||||
{
|
||||
Game = modlist.GameType,
|
||||
Downloads = (AbsolutePath)_installState.Value.CurrentDownloadPath,
|
||||
Install = (AbsolutePath)_installState.Value.CurrentInstallPath,
|
||||
ModList = modlist,
|
||||
ModlistArchive = (AbsolutePath)_installState.Value.CurrentModListPath,
|
||||
SystemParameters = _parametersConstructor.Create(),
|
||||
GameFolder = _gameLocator.GameLocation(modlist.GameType)
|
||||
});
|
||||
|
||||
|
||||
installer.OnStatusUpdate = update =>
|
||||
{
|
||||
if (StatusText != update.StatusText)
|
||||
{
|
||||
StatusText = update.StatusText;
|
||||
InvokeAsync(StateHasChanged);
|
||||
}
|
||||
};
|
||||
await installer.Begin(CancellationToken.None);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Print(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,13 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using DynamicData;
|
||||
using Fluxor;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Win32;
|
||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
using Wabbajack.App.Blazor.Store;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.DTOs;
|
||||
using Wabbajack.DTOs.JsonConverters;
|
||||
using Wabbajack.Installer;
|
||||
@ -17,33 +21,47 @@ namespace Wabbajack.App.Blazor.Pages
|
||||
[Inject] private IState<InstallState> _installState { get; set; }
|
||||
[Inject] private IDispatcher _dispatcher { get; set; }
|
||||
|
||||
private AbsolutePath _modListPath { get; set; }
|
||||
|
||||
private void SelectFile()
|
||||
{
|
||||
var file = new OpenFileDialog
|
||||
using (var dialog = new CommonOpenFileDialog())
|
||||
{
|
||||
Filter = "Wabbajack (*.wabbajack)|*.wabbajack",
|
||||
FilterIndex = 1,
|
||||
Multiselect = false,
|
||||
Title = "Wabbajack file for install"
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
if (file.ShowDialog() != true) return;
|
||||
var path = file.FileName.ToAbsolutePath();
|
||||
VerifyFile(path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Print(ex.Message);
|
||||
dialog.Multiselect = false;
|
||||
dialog.Filters.Add(new CommonFileDialogFilter("Wabbajack File", "*" + Ext.Wabbajack));
|
||||
if (dialog.ShowDialog() != CommonFileDialogResult.Ok) return;
|
||||
_modListPath = dialog.FileName.ToAbsolutePath();
|
||||
}
|
||||
VerifyFile(_modListPath);
|
||||
}
|
||||
|
||||
public async void VerifyFile(AbsolutePath path)
|
||||
// private void SelectFile()
|
||||
// {
|
||||
// var file = new OpenFileDialog
|
||||
// {
|
||||
// Filter = "Wabbajack (*.wabbajack)|*.wabbajack",
|
||||
// FilterIndex = 1,
|
||||
// Multiselect = false,
|
||||
// Title = "Wabbajack file for install"
|
||||
// };
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// if (file.ShowDialog() != true) return;
|
||||
// var path = file.FileName.ToAbsolutePath();
|
||||
// VerifyFile(path);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// Debug.Print(ex.Message);
|
||||
// }
|
||||
// }
|
||||
|
||||
private void VerifyFile(AbsolutePath path)
|
||||
{
|
||||
try
|
||||
{
|
||||
_dispatcher.Dispatch(new UpdateInstallState(InstallState.InstallStateEnum.Configuration, null, path));
|
||||
_dispatcher.Dispatch(new UpdateInstallState(InstallState.InstallStateEnum.Configuration, null, path, null, null));
|
||||
NavigationManager.NavigateTo("/configure");
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -8,17 +8,21 @@ namespace Wabbajack.App.Blazor.Store;
|
||||
public class InstallState
|
||||
{
|
||||
public InstallStateEnum CurrentInstallState { get; }
|
||||
public ModList? CurrentModlist { get; }
|
||||
public AbsolutePath? CurrentPath { get; }
|
||||
public ModList? CurrentModList { get; }
|
||||
public AbsolutePath? CurrentModListPath { get; }
|
||||
public AbsolutePath? CurrentInstallPath { get; }
|
||||
public AbsolutePath? CurrentDownloadPath { get; }
|
||||
|
||||
// Required for initial state.
|
||||
private InstallState() { }
|
||||
|
||||
public InstallState(InstallStateEnum newState, ModList? newModList, AbsolutePath? newPath)
|
||||
public InstallState(InstallStateEnum newState, ModList? newModList, AbsolutePath? newModListPath, AbsolutePath? newInstallPath, AbsolutePath? newDownloadPath)
|
||||
{
|
||||
CurrentInstallState = newState;
|
||||
CurrentModlist = newModList ?? CurrentModlist;
|
||||
CurrentPath = newPath ?? CurrentPath;
|
||||
CurrentModList = newModList ?? CurrentModList;
|
||||
CurrentModListPath = newModListPath ?? CurrentModListPath;
|
||||
CurrentInstallPath = newInstallPath ?? CurrentInstallPath;
|
||||
CurrentDownloadPath = newDownloadPath ?? CurrentDownloadPath;
|
||||
}
|
||||
|
||||
public enum InstallStateEnum
|
||||
@ -33,20 +37,24 @@ public class InstallState
|
||||
|
||||
public class UpdateInstallState
|
||||
{
|
||||
public InstallState.InstallStateEnum State { get; }
|
||||
public ModList? Modlist { get; }
|
||||
public AbsolutePath? Path { get; }
|
||||
public InstallState.InstallStateEnum State { get; }
|
||||
public ModList? ModList { get; }
|
||||
public AbsolutePath? ModListPath { get; }
|
||||
public AbsolutePath? InstallPath { get; }
|
||||
public AbsolutePath? DownloadPath { get; }
|
||||
|
||||
public UpdateInstallState(InstallState.InstallStateEnum state, ModList? modlist, AbsolutePath? path)
|
||||
public UpdateInstallState(InstallState.InstallStateEnum state, ModList? modlist, AbsolutePath? modlistPath, AbsolutePath? installPath, AbsolutePath? downloadPath)
|
||||
{
|
||||
State = state;
|
||||
Modlist = modlist;
|
||||
Path = path;
|
||||
State = state;
|
||||
ModList = modlist;
|
||||
ModListPath = modlistPath;
|
||||
InstallPath = installPath;
|
||||
DownloadPath = downloadPath;
|
||||
}
|
||||
}
|
||||
|
||||
public static class InstallStateReducers
|
||||
{
|
||||
[ReducerMethod]
|
||||
public static InstallState ReduceChangeInstallState(InstallState state, UpdateInstallState action) => new(action.State, action.Modlist, action.Path);
|
||||
public static InstallState ReduceChangeInstallState(InstallState state, UpdateInstallState action) => new(action.State, action.ModList, action.ModListPath, action.InstallPath, action.DownloadPath);
|
||||
}
|
25
Wabbajack.App.Blazor/Utility/Dialog.cs
Normal file
25
Wabbajack.App.Blazor/Utility/Dialog.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
using Wabbajack.Paths;
|
||||
|
||||
namespace Wabbajack.App.Blazor.Utility;
|
||||
|
||||
public static class Dialog
|
||||
{
|
||||
public static async Task<AbsolutePath?> ShowDialogNonBlocking(bool isFolderPicker = false)
|
||||
{
|
||||
return await Task.Factory.StartNew(() =>
|
||||
{
|
||||
Window newWindow = new();
|
||||
var dialog = new CommonOpenFileDialog();
|
||||
dialog.IsFolderPicker = isFolderPicker;
|
||||
dialog.Multiselect = false;
|
||||
CommonFileDialogResult result = dialog.ShowDialog(newWindow);
|
||||
return result == CommonFileDialogResult.Ok ? dialog.FileName : null;
|
||||
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext())
|
||||
.ContinueWith(result => result.Result?.ToAbsolutePath())
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
156
Wabbajack.App.Blazor/Utility/SystemParametersConstructor.cs
Normal file
156
Wabbajack.App.Blazor/Utility/SystemParametersConstructor.cs
Normal file
@ -0,0 +1,156 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PInvoke;
|
||||
using Silk.NET.Core.Native;
|
||||
using Silk.NET.DXGI;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Installer;
|
||||
using Wabbajack;
|
||||
using static PInvoke.User32;
|
||||
using UnmanagedType = System.Runtime.InteropServices.UnmanagedType;
|
||||
|
||||
namespace Wabbajack.App.Blazor.Utility
|
||||
{
|
||||
// Much of the GDI code here is taken from : https://github.com/ModOrganizer2/modorganizer/blob/master/src/envmetrics.cpp
|
||||
// Thanks to MO2 for being good citizens and supporting OSS code
|
||||
public class SystemParametersConstructor
|
||||
{
|
||||
private readonly ILogger<SystemParametersConstructor> _logger;
|
||||
|
||||
public SystemParametersConstructor(ILogger<SystemParametersConstructor> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private IEnumerable<(int Width, int Height, bool IsPrimary)> GetDisplays()
|
||||
{
|
||||
// Needed to make sure we get the right values from this call
|
||||
SetProcessDPIAware();
|
||||
unsafe
|
||||
{
|
||||
var col = new List<(int Width, int Height, bool IsPrimary)>();
|
||||
|
||||
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero,
|
||||
delegate(IntPtr hMonitor, IntPtr hdcMonitor, RECT* lprcMonitor, void* dwData)
|
||||
{
|
||||
MONITORINFOEX mi = new MONITORINFOEX();
|
||||
mi.cbSize = Marshal.SizeOf(mi);
|
||||
bool success = GetMonitorInfo(hMonitor, (MONITORINFO*)&mi);
|
||||
if (success)
|
||||
{
|
||||
col.Add(((mi.Monitor.right - mi.Monitor.left), (mi.Monitor.bottom - mi.Monitor.top),
|
||||
mi.Flags == MONITORINFO_Flags.MONITORINFOF_PRIMARY));
|
||||
}
|
||||
|
||||
return true;
|
||||
}, IntPtr.Zero);
|
||||
return col;
|
||||
}
|
||||
}
|
||||
|
||||
public SystemParameters Create()
|
||||
{
|
||||
var (width, height, _) = GetDisplays().First(d => d.IsPrimary);
|
||||
|
||||
/*using var f = new SharpDX.DXGI.Factory1();
|
||||
var video_memory = f.Adapters1.Select(a =>
|
||||
Math.Max(a.Description.DedicatedSystemMemory, (long)a.Description.DedicatedVideoMemory)).Max();*/
|
||||
|
||||
var dxgiMemory = 0UL;
|
||||
|
||||
unsafe
|
||||
{
|
||||
using var api = DXGI.GetApi();
|
||||
|
||||
IDXGIFactory1* factory1 = default;
|
||||
|
||||
try
|
||||
{
|
||||
//https://docs.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-createdxgifactory1
|
||||
SilkMarshal.ThrowHResult(api.CreateDXGIFactory1(SilkMarshal.GuidPtrOf<IDXGIFactory1>(), (void**)&factory1));
|
||||
|
||||
uint i = 0u;
|
||||
while (true)
|
||||
{
|
||||
IDXGIAdapter1* adapter1 = default;
|
||||
|
||||
//https://docs.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgifactory1-enumadapters1
|
||||
var res = factory1->EnumAdapters1(i, &adapter1);
|
||||
|
||||
var exception = Marshal.GetExceptionForHR(res);
|
||||
if (exception != null) break;
|
||||
|
||||
AdapterDesc1 adapterDesc = default;
|
||||
|
||||
//https://docs.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgiadapter1-getdesc1
|
||||
SilkMarshal.ThrowHResult(adapter1->GetDesc1(&adapterDesc));
|
||||
|
||||
var systemMemory = (ulong)adapterDesc.DedicatedSystemMemory;
|
||||
var videoMemory = (ulong)adapterDesc.DedicatedVideoMemory;
|
||||
|
||||
var maxMemory = Math.Max(systemMemory, videoMemory);
|
||||
if (maxMemory > dxgiMemory)
|
||||
dxgiMemory = maxMemory;
|
||||
|
||||
adapter1->Release();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "While getting SystemParameters");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (factory1->LpVtbl != (void**)IntPtr.Zero)
|
||||
factory1->Release();
|
||||
}
|
||||
}
|
||||
|
||||
var memory = GetMemoryStatus();
|
||||
return new SystemParameters
|
||||
{
|
||||
ScreenWidth = width,
|
||||
ScreenHeight = height,
|
||||
VideoMemorySize = (long)dxgiMemory,
|
||||
SystemMemorySize = (long)memory.ullTotalPhys,
|
||||
SystemPageSize = (long)memory.ullTotalPageFile - (long)memory.ullTotalPhys
|
||||
};
|
||||
}
|
||||
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
static extern bool GlobalMemoryStatusEx([In, Out] MEMORYSTATUSEX lpBuffer);
|
||||
|
||||
public static MEMORYSTATUSEX GetMemoryStatus()
|
||||
{
|
||||
var mstat = new MEMORYSTATUSEX();
|
||||
GlobalMemoryStatusEx(mstat);
|
||||
return mstat;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
||||
public class MEMORYSTATUSEX
|
||||
{
|
||||
public uint dwLength;
|
||||
public uint dwMemoryLoad;
|
||||
public ulong ullTotalPhys;
|
||||
public ulong ullAvailPhys;
|
||||
public ulong ullTotalPageFile;
|
||||
public ulong ullAvailPageFile;
|
||||
public ulong ullTotalVirtual;
|
||||
public ulong ullAvailVirtual;
|
||||
public ulong ullAvailExtendedVirtual;
|
||||
|
||||
public MEMORYSTATUSEX()
|
||||
{
|
||||
dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWPF>true</UseWPF>
|
||||
<PublishSingleFile>True</PublishSingleFile>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -14,11 +15,15 @@
|
||||
|
||||
<ItemGroup>
|
||||
<!-- <PackageReference Include="LibSassBuilder" Version="2.0.1" />-->
|
||||
<PackageReference Include="DynamicData" Version="7.4.9" />
|
||||
<PackageReference Include="Fluxor" Version="4.2.2-Alpha" />
|
||||
<PackageReference Include="Fluxor.Blazor.Web" Version="4.2.2-Alpha" />
|
||||
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Wpf" Version="6.0.101-preview.11.2349" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.1" />
|
||||
<PackageReference Include="PInvoke.User32" Version="0.7.104" />
|
||||
<PackageReference Include="Silk.NET.DXGI" Version="2.12.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
Loading…
Reference in New Issue
Block a user