Refactor, make safer, add some logging
This commit is contained in:
Connor Drahoss 2024-02-21 07:34:48 +01:00
parent b6181e174b
commit 4663bbca87
19 changed files with 67 additions and 150 deletions

View File

@ -2,7 +2,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="VaultSmpInstaller.App" x:Class="VaultSmpInstaller.App"
x:DataType="viewModels:ThemeViewModel" x:DataType="viewModels:ThemeViewModel"
xmlns:local="using:VaultSmpInstaller"
xmlns:viewModels="clr-namespace:VaultSmpInstaller.ViewModels" xmlns:viewModels="clr-namespace:VaultSmpInstaller.ViewModels"
RequestedThemeVariant="Dark"> RequestedThemeVariant="Dark">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. --> <!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
@ -17,7 +16,7 @@
<Setter Property="Button.HorizontalAlignment" Value="Center" /> <Setter Property="Button.HorizontalAlignment" Value="Center" />
</Style> </Style>
<Style Selector="Button"> <Style Selector="Button">
<Setter Property="Button.Background" Value="{Binding ButtonBackground}"/> <Setter Property="Background" Value="{Binding ButtonBackground}"/>
</Style> </Style>
</Application.Styles> </Application.Styles>

View File

@ -6,7 +6,7 @@ using VaultSmpInstaller.Views;
namespace VaultSmpInstaller; namespace VaultSmpInstaller;
public partial class App : Application public class App : Application
{ {
public override void Initialize() public override void Initialize()
{ {

View File

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
namespace VaultSmpInstaller.Data; namespace VaultSmpInstaller.Data;

View File

@ -10,7 +10,4 @@ namespace VaultSmpInstaller.Data;
[JsonSerializable(typeof(bool))] [JsonSerializable(typeof(bool))]
[JsonSerializable(typeof(Dictionary<string, string>))] [JsonSerializable(typeof(Dictionary<string, string>))]
[JsonSerializable(typeof(Dictionary<string, ModProfile>))] [JsonSerializable(typeof(Dictionary<string, ModProfile>))]
public partial class JsonContext: JsonSerializerContext public partial class JsonContext: JsonSerializerContext;
{
}

View File

@ -1,11 +1,9 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
namespace VaultSmpInstaller.Data; namespace VaultSmpInstaller.Data;
[JsonSerializable(typeof(ModProfile))]
public class ModProfile public class ModProfile
{ {
[JsonPropertyName("required")] [JsonPropertyName("required")]

View File

@ -1,11 +0,0 @@
using Avalonia.Media;
namespace VaultSmpInstaller.ViewModels;
public class DownloadingWindowViewModel : ViewModelBase
{
public static Brush Background => SolidColorBrush.Parse("#282A36");
public static Brush SecondaryBackground => SolidColorBrush.Parse("#44475A");
public static Brush ButtonBackground => SolidColorBrush.Parse("#6272A4");
public static Brush TextColor => SolidColorBrush.Parse("#F8F8F2");
}

View File

@ -1,15 +1,8 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO; using System.IO;
using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
using Avalonia.Media; using Avalonia.Media;
using ReactiveUI; using ReactiveUI;
@ -17,7 +10,7 @@ using VaultSmpInstaller.Data;
namespace VaultSmpInstaller.ViewModels; namespace VaultSmpInstaller.ViewModels;
public class MainWindowViewModel : ViewModelBase public class MainWindowViewModel : ReactiveObject
{ {
public static Brush Background => SolidColorBrush.Parse("#282A36"); public static Brush Background => SolidColorBrush.Parse("#282A36");
public static Brush SecondaryBackground => SolidColorBrush.Parse("#44475A"); public static Brush SecondaryBackground => SolidColorBrush.Parse("#44475A");
@ -46,7 +39,7 @@ public class MainWindowViewModel : ViewModelBase
if (File.Exists(Path.Combine(SelectedInstance.InstancePath, "installedInstance.json"))) if (File.Exists(Path.Combine(SelectedInstance.InstancePath, "installedInstance.json")))
{ {
await using FileStream fs = new FileStream(Path.Combine(SelectedInstance.InstancePath, "installedInstance.json"), FileMode.Open); await using FileStream fs = new FileStream(Path.Combine(SelectedInstance.InstancePath, "installedInstance.json"), FileMode.Open);
InstalledInstanceConfig = await JsonSerializer.DeserializeAsync<InstanceConfig>(fs, JsonContext.Default.InstanceConfig); InstalledInstanceConfig = await JsonSerializer.DeserializeAsync(fs, JsonContext.Default.InstanceConfig);
if (InstalledInstanceConfig != null) if (InstalledInstanceConfig != null)
{ {
InstalledInstanceConfig.InstancePath = SelectedInstance.InstancePath; InstalledInstanceConfig.InstancePath = SelectedInstance.InstancePath;
@ -66,8 +59,8 @@ public class MainWindowViewModel : ViewModelBase
}); });
} }
public InstanceConfig? InstalledInstanceConfig { get; set; } = null; public InstanceConfig? InstalledInstanceConfig { get; set; }
public InstanceConfig? LatestInstanceConfig { get; set; } = null; public InstanceConfig? LatestInstanceConfig { get; set; }
public Dictionary<string, ModProfile> EnabledModProfiles => LatestInstanceConfig?.ModProfiles.Where(pair => pair.Value.IsEnabled).ToDictionary() ?? new Dictionary<string, ModProfile>(); public Dictionary<string, ModProfile> EnabledModProfiles => LatestInstanceConfig?.ModProfiles.Where(pair => pair.Value.IsEnabled).ToDictionary() ?? new Dictionary<string, ModProfile>();
public List<string> EnabledModProfileNames => EnabledModProfiles.Keys.ToList(); public List<string> EnabledModProfileNames => EnabledModProfiles.Keys.ToList();
@ -75,7 +68,7 @@ public class MainWindowViewModel : ViewModelBase
public List<string> DisabledModProfileNames => DisabledModProfiles.Keys.ToList(); public List<string> DisabledModProfileNames => DisabledModProfiles.Keys.ToList();
public Dictionary<string, ModProfile> InstalledModProfiles => InstalledInstanceConfig?.ModProfiles.Where(pair => pair.Value.IsEnabled).ToDictionary() ?? new Dictionary<string, ModProfile>(); public Dictionary<string, ModProfile> InstalledModProfiles => InstalledInstanceConfig?.ModProfiles.Where(pair => pair.Value.IsEnabled).ToDictionary() ?? new Dictionary<string, ModProfile>();
public List<string> InstalledModProfileNames => InstalledModProfiles.Keys.ToList(); public List<string> InstalledModProfileNames => InstalledModProfiles.Keys.ToList();
public string SelectedInstanceName => SelectedInstance == null ? "No profile selected" : $"Selected Profile: {SelectedInstance.InstanceName}"; public string SelectedInstanceName => SelectedInstance == null ? "No profile selected" : $"Selected Profile: {SelectedInstance.InstanceName}";
public ProfileWindow2ViewModel.InstanceInfo? SelectedInstance { get; set; } public ProfileWindow2ViewModel.InstanceInfo? SelectedInstance { get; set; }

View File

@ -9,7 +9,7 @@ using ReactiveUI;
namespace VaultSmpInstaller.ViewModels; namespace VaultSmpInstaller.ViewModels;
public class ProfileWindow1ViewModel : ViewModelBase public class ProfileWindow1ViewModel : ReactiveObject
{ {
public Interaction<ProfileWindow2ViewModel, ProfileWindow2ViewModel.InstanceInfo?> ShowProfileSelectionDialog { get; } public Interaction<ProfileWindow2ViewModel, ProfileWindow2ViewModel.InstanceInfo?> ShowProfileSelectionDialog { get; }
@ -25,12 +25,15 @@ public class ProfileWindow1ViewModel : ViewModelBase
UseCurseforgeCommand = ReactiveCommand.CreateFromTask(async () => UseCurseforgeCommand = ReactiveCommand.CreateFromTask(async () =>
{ {
if (curseforgeDir == null) return null;
var profileWindowModel = new ProfileWindow2ViewModel(ProfileWindow2ViewModel.InstanceType.Curseforge, curseforgeDir); var profileWindowModel = new ProfileWindow2ViewModel(ProfileWindow2ViewModel.InstanceType.Curseforge, curseforgeDir);
return await ShowProfileSelectionDialog.Handle(profileWindowModel); return await ShowProfileSelectionDialog.Handle(profileWindowModel);
}); });
UsePrismCommand = ReactiveCommand.CreateFromTask(async () => UsePrismCommand = ReactiveCommand.CreateFromTask(async () =>
{ {
if (prismDir == null) return null;
var profileWindowModel = new ProfileWindow2ViewModel(ProfileWindow2ViewModel.InstanceType.Prism, prismDir); var profileWindowModel = new ProfileWindow2ViewModel(ProfileWindow2ViewModel.InstanceType.Prism, prismDir);
return await ShowProfileSelectionDialog.Handle(profileWindowModel); return await ShowProfileSelectionDialog.Handle(profileWindowModel);
}); });
@ -41,8 +44,8 @@ public class ProfileWindow1ViewModel : ViewModelBase
public static Brush ButtonBackground => SolidColorBrush.Parse("#6272A4"); public static Brush ButtonBackground => SolidColorBrush.Parse("#6272A4");
public static Brush TextColor => SolidColorBrush.Parse("#F8F8F2"); public static Brush TextColor => SolidColorBrush.Parse("#F8F8F2");
private string? _curseforgeInstanceDir = null; private string? _curseforgeInstanceDir;
public bool IsCurseforgeInstalled { get; set; } = false; public bool IsCurseforgeInstalled { get; set; }
public string CurseforgeButtonText => IsCurseforgeInstalled ? "Curseforge" : "Curseforge Not Detected"; public string CurseforgeButtonText => IsCurseforgeInstalled ? "Curseforge" : "Curseforge Not Detected";
@ -61,9 +64,9 @@ public class ProfileWindow1ViewModel : ViewModelBase
} }
} }
public bool IsPrismInstalled { get; set; } = false; public bool IsPrismInstalled { get; set; }
private string? _prismInstanceDir = null; private string? _prismInstanceDir;
public string PrismButtonText => IsPrismInstalled ? "Prism Launcher" : "Prism Launcher Not Detected"; public string PrismButtonText => IsPrismInstalled ? "Prism Launcher" : "Prism Launcher Not Detected";
public string? PrismInstanceDir public string? PrismInstanceDir
@ -81,7 +84,7 @@ public class ProfileWindow1ViewModel : ViewModelBase
} }
} }
public bool IsOverwolfInstalled { get; set; } = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Overwolf", "UninstallString", null) != null; public bool IsOverwolfInstalled { get; } = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Overwolf", "UninstallString", null) != null;
public bool TryGetCurseforgeMinecraftRoot(out string? minecraftRoot) public bool TryGetCurseforgeMinecraftRoot(out string? minecraftRoot)
{ {
@ -91,7 +94,12 @@ public class ProfileWindow1ViewModel : ViewModelBase
if (!File.Exists(Path.Combine(appData, "Curseforge", "storage.json"))) return false; if (!File.Exists(Path.Combine(appData, "Curseforge", "storage.json"))) return false;
var curseforgeConfig = JsonNode.Parse(File.ReadAllText(Path.Combine(appData, "Curseforge", "storage.json")))!.AsObject(); var curseforgeConfig = JsonNode.Parse(File.ReadAllText(Path.Combine(appData, "Curseforge", "storage.json")))?.AsObject();
if (curseforgeConfig == null)
{
File.AppendAllText(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "smpinstaller.log"), "Failed to read curseforge config!\n");
return false;
}
if (!curseforgeConfig.TryGetPropertyValue("minecraft-settings", out var minecraftSettingsNode)) if (!curseforgeConfig.TryGetPropertyValue("minecraft-settings", out var minecraftSettingsNode))
{ {
minecraftRoot = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "curseforge", "minecraft", "Instances"); minecraftRoot = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "curseforge", "minecraft", "Instances");
@ -132,61 +140,4 @@ public class ProfileWindow1ViewModel : ViewModelBase
} }
return false; return false;
} }
// public bool TryGetOverwolfMinecraftRoot(out string? minecraftRoot)
// {
// minecraftRoot = null;
//
// var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
// var overwolfDbDirectory = Path.Combine(localAppData, "Overwolf", "BrowserCache", "Local Storage", "leveldb");
//
// if (!Directory.Exists(overwolfDbDirectory)) return false;
//
// var overwolfDb = new Database(new(overwolfDbDirectory), false, new Options() { ReadOnly = true});
// byte[] databaseKey = new byte[]
// {
// 0x5F, 0x6F,
// 0x76, 0x65, 0x72, 0x77, 0x6F, 0x6C, 0x66, 0x2D,
// 0x65, 0x78, 0x74, 0x65, 0x6E, 0x73, 0x69, 0x6F,
// 0x6E, 0x3A, 0x2F, 0x2F, 0x63, 0x63, 0x68, 0x68,
// 0x63, 0x61, 0x69, 0x61, 0x70, 0x65, 0x69, 0x6B,
// 0x6A, 0x62, 0x64, 0x62, 0x70, 0x66, 0x70, 0x6C,
// 0x67, 0x6D, 0x70, 0x6F, 0x62, 0x62, 0x63, 0x64,
// 0x6B, 0x64, 0x61, 0x70, 0x68, 0x63, 0x6C, 0x62,
// 0x6D, 0x6B, 0x62, 0x6A, 0x00, 0x01, 0x6D, 0x69,
// 0x6E, 0x65, 0x63, 0x72, 0x61, 0x66, 0x74, 0x2D,
// 0x73, 0x65, 0x74, 0x74, 0x69, 0x6E, 0x67, 0x73
// };
// String? curseforgeMinecraftSettings = "";
// try
// {
// overwolfDb.Open();
// curseforgeMinecraftSettings = Encoding.ASCII.GetString(overwolfDb.Get(databaseKey))[1..];
// }
// catch (NullReferenceException e)
// {
// minecraftRoot = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "curseforge", "minecraft", "Instances");
// return true;
// }
// finally
// {
// overwolfDb.Close();
// overwolfDb.Dispose();
// }
// if(String.IsNullOrEmpty(curseforgeMinecraftSettings))
// {
// minecraftRoot = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "curseforge", "minecraft", "Instances");
// return true;
// }
//
// var minecraftSettings = JsonNode.Parse(curseforgeMinecraftSettings);
// if (!minecraftSettings!.AsObject().TryGetPropertyValue("minecraftRoot", out var minecraftRootNode)) {
// minecraftRoot = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "curseforge", "minecraft", "Instances");
// return true;
// }
// if (!minecraftRootNode!.AsValue().TryGetValue(out minecraftRoot)) return false;
//
// minecraftRoot = Path.Combine(minecraftRoot, "Instances");
// return true;
// }
} }

View File

@ -10,11 +10,10 @@ using ReactiveUI;
namespace VaultSmpInstaller.ViewModels; namespace VaultSmpInstaller.ViewModels;
public class ProfileWindow2ViewModel : ViewModelBase public class ProfileWindow2ViewModel : ReactiveObject
{ {
public Interaction<ProfileWindow2ViewModel, ProfileWindow2ViewModel.InstanceInfo?> ShowProfileSelectionDialog { get; } public ReactiveCommand<Unit, InstanceInfo?> SelectProfileCommand { get; }
public ReactiveCommand<Unit, ProfileWindow2ViewModel.InstanceInfo?> SelectProfileCommand { get; }
public static Brush Background => SolidColorBrush.Parse("#282A36"); public static Brush Background => SolidColorBrush.Parse("#282A36");
public static Brush SecondaryBackground => SolidColorBrush.Parse("#44475A"); public static Brush SecondaryBackground => SolidColorBrush.Parse("#44475A");
@ -37,6 +36,7 @@ public class ProfileWindow2ViewModel : ViewModelBase
foreach (var directory in Directory.EnumerateDirectories(instancesDir)) foreach (var directory in Directory.EnumerateDirectories(instancesDir))
{ {
if (!File.Exists(Path.Combine(directory, "instance.cfg"))) continue; if (!File.Exists(Path.Combine(directory, "instance.cfg"))) continue;
foreach(String line in File.ReadLines(Path.Combine(directory, "instance.cfg"))) foreach(String line in File.ReadLines(Path.Combine(directory, "instance.cfg")))
{ {
if (line.StartsWith("name")) if (line.StartsWith("name"))
@ -56,12 +56,17 @@ public class ProfileWindow2ViewModel : ViewModelBase
} }
} }
break; break;
case InstanceType.Curseforge: case InstanceType.Overwolf: case InstanceType.Curseforge:
foreach (var directory in Directory.EnumerateDirectories(instancesDir)) foreach (var directory in Directory.EnumerateDirectories(instancesDir))
{ {
if (!File.Exists(Path.Combine(directory, "minecraftinstance.json"))) continue; string? instanceConfigName = null;
if (File.Exists(Path.Combine(directory, "minecraftinstance.json"))) instanceConfigName = "minecraftinstance.json";
if (File.Exists(Path.Combine(directory, "minecraftInstance.json"))) instanceConfigName = "minecraftInstance.json";
if (instanceConfigName == null) continue;
var instanceConfig = JsonNode.Parse(File.ReadAllText(Path.Combine(directory, "minecraftinstance.json")))!.AsObject(); var instanceConfig = JsonNode.Parse(File.ReadAllText(Path.Combine(directory, instanceConfigName)))?.AsObject();
if(instanceConfig == null) continue;
if (!instanceConfig.TryGetPropertyValue("name", out var nameNode)) continue; if (!instanceConfig.TryGetPropertyValue("name", out var nameNode)) continue;
if (!nameNode!.AsValue().TryGetValue(out string? instanceName)) continue; if (!nameNode!.AsValue().TryGetValue(out string? instanceName)) continue;
@ -88,7 +93,7 @@ public class ProfileWindow2ViewModel : ViewModelBase
public enum InstanceType public enum InstanceType
{ {
Prism, Curseforge, Overwolf Prism, Curseforge
} }
public record InstanceInfo(String InstanceName, String InstancePath, String MinecraftPath, String ModsPath, String ConfigPath, String ScriptsPath); public record InstanceInfo(String InstanceName, String InstancePath, String MinecraftPath, String ModsPath, String ConfigPath, String ScriptsPath);

View File

@ -1,8 +1,9 @@
using Avalonia.Media; using Avalonia.Media;
using ReactiveUI;
namespace VaultSmpInstaller.ViewModels; namespace VaultSmpInstaller.ViewModels;
public class ThemeViewModel : ViewModelBase public class ThemeViewModel : ReactiveObject
{ {
public static Brush Background => SolidColorBrush.Parse("#282A36"); public static Brush Background => SolidColorBrush.Parse("#282A36");
public static Brush SecondaryBackground => SolidColorBrush.Parse("#44475A"); public static Brush SecondaryBackground => SolidColorBrush.Parse("#44475A");

View File

@ -1,7 +0,0 @@
using ReactiveUI;
namespace VaultSmpInstaller.ViewModels;
public class ViewModelBase : ReactiveObject
{
}

View File

@ -6,14 +6,14 @@
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia.ProgressRing" xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia.ProgressRing"
mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="160" mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="160"
x:Class="VaultSmpInstaller.Views.DownloadingWindow" x:Class="VaultSmpInstaller.Views.DownloadingWindow"
x:DataType="vm:DownloadingWindowViewModel" x:DataType="vm:ThemeViewModel"
Icon="/Assets/icon.ico" Icon="/Assets/icon.ico"
Title="Downloading Update" Title="Downloading Update"
Width="300" Height="160" Width="300" Height="160"
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
CanResize="False"> CanResize="False">
<Design.DataContext> <Design.DataContext>
<vm:DownloadingWindowViewModel/> <vm:ThemeViewModel/>
</Design.DataContext> </Design.DataContext>
<StackPanel Background="{Binding Background}"> <StackPanel Background="{Binding Background}">
<TextBlock FontSize="18" Margin="10, 10, 10, 0">Downloading Latest Release</TextBlock> <TextBlock FontSize="18" Margin="10, 10, 10, 0">Downloading Latest Release</TextBlock>

View File

@ -1,6 +1,4 @@
using Avalonia; using Avalonia.Controls;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace VaultSmpInstaller.Views; namespace VaultSmpInstaller.Views;

View File

@ -1,14 +1,14 @@
using Avalonia; using Avalonia.Controls;
using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Threading; using Avalonia.Threading;
namespace VaultSmpInstaller.Views; namespace VaultSmpInstaller.Views;
public partial class InstanceNotIntactWindow : Window public partial class InstanceNotIntactWindow : Window
{ {
private readonly MainWindow _mainWindow; public InstanceNotIntactWindow() { }
private readonly MainWindow? _mainWindow;
public InstanceNotIntactWindow(MainWindow mainWindow) public InstanceNotIntactWindow(MainWindow mainWindow)
{ {
InitializeComponent(); InitializeComponent();
@ -18,12 +18,12 @@ public partial class InstanceNotIntactWindow : Window
private void Continue(object? sender, RoutedEventArgs e) private void Continue(object? sender, RoutedEventArgs e)
{ {
Dispatcher.UIThread.Invoke(Close); Dispatcher.UIThread.Invoke(Close);
_mainWindow.ContinueInstalling.Set(); _mainWindow?.ContinueInstalling.Set();
} }
private void Cancel(object? sender, RoutedEventArgs e) private void Cancel(object? sender, RoutedEventArgs e)
{ {
Dispatcher.UIThread.Invoke(Close); Dispatcher.UIThread.Invoke(Close);
Dispatcher.UIThread.Invoke(_mainWindow.Close); if (_mainWindow != null) Dispatcher.UIThread.Invoke(_mainWindow.Close);
} }
} }

View File

@ -30,7 +30,7 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
private async Task ProcessInstance(String instancePath) private async Task ProcessInstance(String instancePath)
{ {
await using FileStream fs = new FileStream(Path.Combine(instancePath, "instance.json"), FileMode.Open); await using FileStream fs = new FileStream(Path.Combine(instancePath, "instance.json"), FileMode.Open);
ViewModel!.LatestInstanceConfig = await JsonSerializer.DeserializeAsync<InstanceConfig>(fs, JsonContext.Default.InstanceConfig); ViewModel!.LatestInstanceConfig = await JsonSerializer.DeserializeAsync(fs, JsonContext.Default.InstanceConfig);
if (ViewModel!.LatestInstanceConfig != null) if (ViewModel!.LatestInstanceConfig != null)
{ {
ViewModel!.LatestInstanceConfig.InstancePath = instancePath; ViewModel!.LatestInstanceConfig.InstancePath = instancePath;
@ -68,20 +68,19 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
private void StartDownload() private void StartDownload()
{ {
var downloadThread = new Thread(async (arg) => var downloadThread = new Thread((arg) =>
{ {
if (arg is CancellationTokenSource) if (arg is CancellationTokenSource tokenSource)
{ {
var tokenSource = (CancellationTokenSource)arg;
var archivePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".zip"); var archivePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".zip");
var extractPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); var extractPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
try try
{ {
HttpClient httpClient = new HttpClient(); HttpClient httpClient = new HttpClient();
using var response = await httpClient.GetAsync("https://holenode.cdnbcn.net/LatestProfile.zip", tokenSource.Token); using var response = httpClient.GetAsync("https://holenode.cdnbcn.net/LatestProfile.zip", tokenSource.Token).Result;
await using var fs = new FileStream(archivePath, FileMode.OpenOrCreate); using var fs = new FileStream(archivePath, FileMode.OpenOrCreate);
await response.Content.CopyToAsync(fs, tokenSource.Token); response.Content.CopyTo(fs, null, tokenSource.Token);
fs.Close(); fs.Close();
@ -91,16 +90,16 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
File.Delete(archivePath); File.Delete(archivePath);
await Dispatcher.UIThread.Invoke(() => ProcessInstance(extractPath)); Dispatcher.UIThread.Invoke(() => ProcessInstance(extractPath));
} }
catch (OperationCanceledException e) catch (OperationCanceledException)
{ {
try try
{ {
File.Delete(archivePath); File.Delete(archivePath);
Directory.Delete(extractPath, true); Directory.Delete(extractPath, true);
} }
catch (Exception ignored) catch (Exception)
{ {
// ignored // ignored
} }
@ -112,7 +111,7 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
CancellationTokenSource source = new CancellationTokenSource(); CancellationTokenSource source = new CancellationTokenSource();
downloadThread.Start(source); downloadThread.Start(source);
_downloadingWindow.DataContext = new DownloadingWindowViewModel(); _downloadingWindow.DataContext = new ThemeViewModel();
_downloadingWindow.Closed += (_, _) => source.Cancel(); _downloadingWindow.Closed += (_, _) => source.Cancel();
_downloadingWindow.ShowDialog(this); _downloadingWindow.ShowDialog(this);
} }

View File

@ -1,7 +1,5 @@
using System; using System;
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using ReactiveUI; using ReactiveUI;
using VaultSmpInstaller.ViewModels; using VaultSmpInstaller.ViewModels;
@ -17,9 +15,9 @@ public partial class ProfileWindow2 : ReactiveWindow<ProfileWindow2ViewModel>
this.WhenActivated(action => action(ViewModel!.SelectProfileCommand.Subscribe(Close))); this.WhenActivated(action => action(ViewModel!.SelectProfileCommand.Subscribe(Close)));
} }
private void SelectingItemsControl_OnSelectionChanged(object? sender, SelectionChangedEventArgs e) private void SelectingItemsControl_OnSelectionChanged(object? _, SelectionChangedEventArgs e)
{ {
ViewModel!.SelectedInstance = ViewModel.Instances[((string)e.AddedItems[0]!)!]; ViewModel!.SelectedInstance = ViewModel.Instances[(string)e.AddedItems[0]!];
ViewModel.RaisePropertyChanged(nameof(ViewModel.IsInstanceSelected)); ViewModel.RaisePropertyChanged(nameof(ViewModel.IsInstanceSelected));
ViewModel.RaisePropertyChanged(nameof(ViewModel.SelectedInstance)); ViewModel.RaisePropertyChanged(nameof(ViewModel.SelectedInstance));
} }

View File

@ -3,7 +3,6 @@
xmlns:vm="using:VaultSmpInstaller.ViewModels" xmlns:vm="using:VaultSmpInstaller.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="400" mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="400"
x:Class="VaultSmpInstaller.Views.RemoveOverwolfWindow" x:Class="VaultSmpInstaller.Views.RemoveOverwolfWindow"
x:DataType="vm:ThemeViewModel" x:DataType="vm:ThemeViewModel"

View File

@ -1,13 +1,9 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Threading; using Avalonia.Threading;
using FluentAvalonia.UI.Controls;
using Microsoft.Win32; using Microsoft.Win32;
namespace VaultSmpInstaller.Views; namespace VaultSmpInstaller.Views;

View File

@ -1,14 +1,14 @@
using Avalonia; using Avalonia.Controls;
using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Threading; using Avalonia.Threading;
namespace VaultSmpInstaller.Views; namespace VaultSmpInstaller.Views;
public partial class SuccessWindow : Window public partial class SuccessWindow : Window
{ {
private readonly MainWindow _mainWindow; public SuccessWindow() { }
private readonly MainWindow? _mainWindow;
public SuccessWindow(MainWindow mainWindow) public SuccessWindow(MainWindow mainWindow)
{ {
InitializeComponent(); InitializeComponent();
@ -17,7 +17,7 @@ public partial class SuccessWindow : Window
private void Ok(object? sender, RoutedEventArgs e) private void Ok(object? sender, RoutedEventArgs e)
{ {
_mainWindow.ContinueInstalling.Set(); _mainWindow?.ContinueInstalling.Set();
Dispatcher.UIThread.Invoke(Close); Dispatcher.UIThread.Invoke(Close);
} }
} }