Compare commits

..
8 Commits
19 changed files with 514 additions and 33 deletions
@@ -6,6 +6,8 @@ namespace Daybreak.Configuration
{
[JsonProperty("GamePath")]
public string GamePath { get; set; }
[JsonProperty("ToolboxPath")]
public string ToolboxPath { get; set; }
[JsonProperty("CharacterName")]
public string CharacterName { get; set; }
[JsonProperty("LeftBrowserDefault")]
@@ -5,6 +5,7 @@ using Daybreak.Services.Configuration;
using Daybreak.Services.Credentials;
using Daybreak.Services.Logging;
using Daybreak.Services.Screenshots;
using Daybreak.Services.Updater;
using Daybreak.Services.ViewManagement;
using Daybreak.Views;
using Microsoft.Web.WebView2.Core;
@@ -29,6 +30,7 @@ namespace Daybreak.Configuration
serviceProducer.RegisterSingleton<IScreenshotProvider, ScreenshotProvider>();
serviceProducer.RegisterSingleton<IConfigurationManager, ConfigurationManager>();
serviceProducer.RegisterSingleton<IBloogumClient, BloogumClient>();
serviceProducer.RegisterSingleton<IApplicationUpdater, ApplicationUpdater>();
serviceProducer.RegisterSingleton<CoreWebView2Environment, CoreWebView2Environment>((sp) => TaskExtensions.RunSync(() => CoreWebView2Environment.CreateAsync(null, "BrowserData", null)));
}
public static void RegisterLifetimeServices(IApplicationLifetimeProducer applicationLifetimeProducer)
@@ -42,8 +44,10 @@ namespace Daybreak.Configuration
{
viewProducer.ThrowIfNull(nameof(viewProducer));
viewProducer.RegisterView<StartupView>();
viewProducer.RegisterView<MainView>();
viewProducer.RegisterView<SettingsView>();
viewProducer.RegisterView<AskUpdateView>();
viewProducer.RegisterView<UpdateView>();
}
}
}
+1 -1
View File
@@ -9,7 +9,7 @@
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
<LangVersion>preview</LangVersion>
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
<Version>0.1.2</Version>
<Version>0.1.4</Version>
</PropertyGroup>
<ItemGroup>
+19 -2
View File
@@ -1,5 +1,6 @@
using Daybreak.Services.Bloogum;
using Daybreak.Services.Screenshots;
using Daybreak.Services.Updater;
using Daybreak.Services.ViewManagement;
using Daybreak.Views;
using Pepa.Wpf.Utilities;
@@ -26,6 +27,7 @@ namespace Daybreak.Launch
private readonly IViewManager viewManager;
private readonly IScreenshotProvider screenshotProvider;
private readonly IBloogumClient bloogumClient;
private readonly IApplicationUpdater applicationUpdater;
private readonly CancellationTokenSource cancellationToken = new();
public string CreditText
@@ -37,18 +39,20 @@ namespace Daybreak.Launch
public MainWindow(
IViewManager viewManager,
IScreenshotProvider screenshotProvider,
IBloogumClient bloogumClient)
IBloogumClient bloogumClient,
IApplicationUpdater applicationUpdater)
{
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
this.screenshotProvider = screenshotProvider.ThrowIfNull(nameof(screenshotProvider));
this.bloogumClient = bloogumClient.ThrowIfNull(nameof(bloogumClient));
this.applicationUpdater = applicationUpdater.ThrowIfNull(nameof(applicationUpdater));
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.viewManager.ShowView<StartupView>();
this.SetupImageCycle();
this.CheckForUpdates();
}
private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
@@ -145,6 +149,19 @@ namespace Daybreak.Launch
}
}
private async void CheckForUpdates()
{
var updateAvailable = await this.applicationUpdater.UpdateAvailable().ConfigureAwait(true);
if (updateAvailable)
{
this.viewManager.ShowView<AskUpdateView>();
}
else
{
this.viewManager.ShowView<MainView>();
}
}
private static Color GetAverageColor(BitmapSource bitmap)
{
var format = bitmap.Format;
+44
View File
@@ -0,0 +1,44 @@
using System.ComponentModel;
namespace Daybreak.Models
{
public sealed class UpdateStatus : INotifyPropertyChanged
{
public static readonly UpdateStep StartingStep = new("Starting");
public static readonly UpdateStep CheckingLatestVersion = new("Checking latest version");
public static UpdateStep Downloading(double progress) => new DownloadUpdateStep("Downloading", progress);
public static readonly UpdateStep DownloadFinished = new("Download finished. Application will restart in order to apply the update.");
private UpdateStep currentStep = StartingStep;
public event PropertyChangedEventHandler PropertyChanged;
public UpdateStep CurrentStep
{
get => this.currentStep;
set
{
this.currentStep = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentStep)));
}
}
public class UpdateStep
{
public string Name { get; }
internal UpdateStep(string name)
{
this.Name = name;
}
}
public class DownloadUpdateStep : UpdateStep
{
internal DownloadUpdateStep(string name, double progress) : base(name)
{
this.Progress = progress;
}
public double Progress { get; }
}
}
}
@@ -11,12 +11,14 @@ namespace Daybreak.Services.ApplicationDetection
{
public class ApplicationDetector : IApplicationDetector
{
private const string ToolboxProcessName = "GWToolbox";
private const string ProcessName = "gw";
private readonly IConfigurationManager configurationManager;
private readonly ICredentialManager credentialManager;
public bool IsGuildwarsRunning => GuildwarsProcessDetected();
public bool IsToolboxRunning => GuildwarsToolboxProcessDetected();
public ApplicationDetector(
IConfigurationManager configurationManager,
@@ -46,7 +48,7 @@ namespace Daybreak.Services.ApplicationDetection
{
if (Process.Start(executable, new List<string> { "-email", credentials.Username, "-password", credentials.Password, "-character", configuration.CharacterName }) is null)
{
throw new InvalidOperationException($"Unable to launch executable");
throw new InvalidOperationException($"Unable to launch {executable}");
}
},
onNone: () =>
@@ -55,10 +57,29 @@ namespace Daybreak.Services.ApplicationDetection
});
}
public void LaunchGuildwarsToolbox()
{
var configuration = this.configurationManager.GetConfiguration();
var executable = configuration.ToolboxPath;
if (File.Exists(executable) is false)
{
throw new InvalidOperationException($"Guildwars executable doesn't exist at {executable}");
}
if (Process.Start(executable) is null)
{
throw new InvalidOperationException($"Unable to launch {executable}");
}
}
private static bool GuildwarsProcessDetected()
{
var process = Process.GetProcessesByName(ProcessName).FirstOrDefault();
return process is not null;
return Process.GetProcessesByName(ProcessName).FirstOrDefault() is not null;
}
private static bool GuildwarsToolboxProcessDetected()
{
return Process.GetProcessesByName(ToolboxProcessName).FirstOrDefault() is not null;
}
}
}
@@ -3,6 +3,8 @@
public interface IApplicationDetector
{
bool IsGuildwarsRunning { get; }
bool IsToolboxRunning { get; }
void LaunchGuildwars();
void LaunchGuildwarsToolbox();
}
}
@@ -1,7 +1,9 @@
using Daybreak.Configuration;
using Daybreak.Exceptions;
using Daybreak.Services.Logging;
using Daybreak.Utils;
using System;
using System.Extensions;
using System.IO;
namespace Daybreak.Services.Configuration
@@ -11,9 +13,11 @@ namespace Daybreak.Services.Configuration
private const string ConfigName = "Daybreak.config.json";
private ApplicationConfiguration applicationConfiguration;
private readonly ILogger logger;
public ConfigurationManager()
public ConfigurationManager(ILogger logger)
{
this.logger = logger.ThrowIfNull(nameof(logger));
try
{
var serializedConfig = File.ReadAllText(ConfigName);
@@ -21,7 +25,8 @@ namespace Daybreak.Services.Configuration
}
catch(Exception e)
{
throw new FatalException("Failed to load application configuration. See inner exception for details", e);
this.logger.LogWarning($"No configuration detected. Loading default configuration. Details: {e}");
this.applicationConfiguration = new ApplicationConfiguration();
}
}
@@ -0,0 +1,143 @@
using Daybreak.Models;
using Daybreak.Services.Logging;
using Daybreak.Utils;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Extensions;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
namespace Daybreak.Services.Updater
{
public sealed class ApplicationUpdater : IApplicationUpdater
{
private const string ExtractAndRunPs1 = "ExtractAndRun.ps1";
private const string TempFile = "tempfile.zip";
private const string VersionTag = "{VERSION}";
private const string InputFileTag = "{INPUTFILE}";
private const string OutputPathTag = "{OUTPUTPATh}";
private const string Url = "https://github.com/AlexMacocian/Daybreak/releases/latest";
private const string DownloadUrl = $"https://github.com/AlexMacocian/Daybreak/releases/download/v{VersionTag}/Daybreakv{VersionTag}.zip";
private const string DelayCommand = "Start-Sleep -m 3000";
private const string ExtractCommandTemplate = $"Expand-Archive -Path '{InputFileTag}' -DestinationPath '{OutputPathTag}' -Force";
private const string RunClientCommand = @".\Daybreak.exe";
private const string RemoveTempFile = $"Remove-item {TempFile}";
private const string RemovePs1 = $"Remove-item {ExtractAndRunPs1}";
private readonly ILogger logger;
private readonly HttpClient httpClient = new();
public string CurrentVersion => Assembly.GetExecutingAssembly().GetName().Version.ToString();
public ApplicationUpdater(ILogger logger)
{
this.logger = logger.ThrowIfNull(nameof(logger));
}
public async Task<bool> DownloadUpdate(UpdateStatus updateStatus)
{
updateStatus.CurrentStep = UpdateStatus.CheckingLatestVersion;
var latestVersion = (await this.GetLatestVersion()).ExtractValue();
if (latestVersion is null)
{
this.logger.LogWarning("Failed to retrieve latest version. Aborting update");
return false;
}
using var downloadLatestResponse = await this.httpClient.GetAsync(
DownloadUrl.Replace(VersionTag, latestVersion));
if (downloadLatestResponse.IsSuccessStatusCode is false)
{
this.logger.LogWarning("Failed to download latest version. Aborting udpate");
return false;
}
this.logger.LogInformation("Beginning update download");
var downloadStream = await downloadLatestResponse.Content.ReadAsStreamAsync();
var fileStream = File.OpenWrite(TempFile);
var downloadSize = (double)downloadStream.Length;
var buffer = new byte[1024];
var length = 0;
double downloaded = 0;
var tickTime = DateTime.Now;
while (downloadStream.CanRead && (length = await downloadStream.ReadAsync(buffer)) > 0)
{
downloaded += length;
await fileStream.WriteAsync(buffer, 0, length);
if ((DateTime.Now - tickTime).TotalMilliseconds > 50)
{
tickTime = DateTime.Now;
updateStatus.CurrentStep = UpdateStatus.Downloading(downloaded / downloadSize);
}
}
updateStatus.CurrentStep = UpdateStatus.Downloading(1);
updateStatus.CurrentStep = UpdateStatus.DownloadFinished;
fileStream.Close();
this.logger.LogInformation("Downloaded update");
return true;
}
public async Task<bool> UpdateAvailable()
{
var version = string.Join('.', this.CurrentVersion.Split('.').Take(3));
var maybeLatestVersion = await this.GetLatestVersion();
return maybeLatestVersion.Switch(
onSome: latestVersion => string.Compare(version, latestVersion, true) < 0,
onNone: () =>
{
this.logger.LogWarning("Failed to retrieve latest version");
return false;
}).ExtractValue();
}
public void FinalizeUpdate()
{
File.WriteAllLines(ExtractAndRunPs1, new List<string>()
{
DelayCommand,
ExtractCommandTemplate
.Replace(InputFileTag, Path.GetFullPath(TempFile))
.Replace(OutputPathTag, Directory.GetCurrentDirectory()),
RemoveTempFile,
RemovePs1,
RunClientCommand
});
var process = new Process()
{
StartInfo = new ProcessStartInfo
{
FileName = "powershell.exe",
Arguments = $@"{Directory.GetCurrentDirectory()}\{ExtractAndRunPs1}",
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
WindowStyle = ProcessWindowStyle.Maximized,
WorkingDirectory = Directory.GetCurrentDirectory()
},
};
if (process.Start() is false)
{
throw new InvalidOperationException("Failed to create and start powershell script");
}
}
private async Task<Optional<string>> GetLatestVersion()
{
using var response = await this.httpClient.GetAsync(Url);
if (response.IsSuccessStatusCode)
{
var versionTag = response.RequestMessage.RequestUri.ToString().Split('/').Last().TrimStart('v');
return versionTag;
}
return Optional.None<string>();
}
}
}
@@ -0,0 +1,13 @@
using Daybreak.Models;
using System.Threading.Tasks;
namespace Daybreak.Services.Updater
{
public interface IApplicationUpdater
{
string CurrentVersion { get; }
void FinalizeUpdate();
Task<bool> UpdateAvailable();
Task<bool> DownloadUpdate(UpdateStatus updateStatus);
}
}
+21
View File
@@ -0,0 +1,21 @@
<UserControl x:Class="Daybreak.Views.AskUpdateView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Daybreak.Views"
mc:Ignorable="d"
xmlns:controls="clr-namespace:Daybreak.Controls"
d:DesignHeight="450" d:DesignWidth="800">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Width="400" Height="200"
Background="White">
<TextBlock Text="An update has been detected. Do you wish to update?" FontSize="20" TextWrapping="Wrap"
Foreground="Black"></TextBlock>
<controls:OpaqueButton Text="No" Foreground="Black" TransparentBackground="Gray" BackgroundOpacity="0.2" Width="50" Height="30"
FontSize="16" HorizontalAlignment="Center" Margin="0, 0, 80, 0"
Clicked="NoButton_Clicked"></controls:OpaqueButton>
<controls:OpaqueButton Text="Yes" Foreground="Black" TransparentBackground="Gray" BackgroundOpacity="0.2" Width="50" Height="30"
FontSize="16" HorizontalAlignment="Center" Margin="80, 0, 0, 0"
Clicked="YesButton_Clicked"></controls:OpaqueButton>
</Grid>
</UserControl>
+42
View File
@@ -0,0 +1,42 @@
using Daybreak.Services.Logging;
using Daybreak.Services.Updater;
using Daybreak.Services.ViewManagement;
using Daybreak.Utils;
using System.Extensions;
using System.Windows.Controls;
namespace Daybreak.Views
{
/// <summary>
/// Interaction logic for AskUpdateView.xaml
/// </summary>
public partial class AskUpdateView : UserControl
{
private readonly IApplicationUpdater applicationUpdater;
private readonly ILogger logger;
private readonly IViewManager viewManager;
public AskUpdateView(
IApplicationUpdater applicationUpdater,
ILogger logger,
IViewManager viewManager)
{
this.applicationUpdater = applicationUpdater.ThrowIfNull(nameof(applicationUpdater));
this.logger = logger.ThrowIfNull(nameof(logger));
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
this.InitializeComponent();
}
private void NoButton_Clicked(object sender, System.EventArgs e)
{
this.logger.LogInformation("User declined update");
this.viewManager.ShowView<MainView>();
}
private void YesButton_Clicked(object sender, System.EventArgs e)
{
this.logger.LogInformation("User accepted update");
this.viewManager.ShowView<UpdateView>();
}
}
}
@@ -1,4 +1,4 @@
<UserControl x:Class="Daybreak.Views.StartupView"
<UserControl x:Class="Daybreak.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@@ -25,9 +25,13 @@
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<controls:OpaqueButton Text="Launch game" Highlight="White" TransparentBackground="Black" HighlightOpacity="0.3" BackgroundOpacity="0.2"
Foreground="White" FontSize="36" Width="250" Height="60" VerticalAlignment="Bottom" Margin="0, 0, 0, 30"
Foreground="White" FontSize="36" Width="250" Height="60" VerticalAlignment="Bottom"
IsEnabled="{Binding ElementName=_this, Path=LaunchButtonEnabled, Mode=OneWay}"
Clicked="OpaqueButton_Clicked" Grid.Row="1" Grid.ColumnSpan="3"></controls:OpaqueButton>
Clicked="LaunchButton_Clicked" Grid.Row="0" Grid.ColumnSpan="3"></controls:OpaqueButton>
<controls:OpaqueButton Text="Launch toolbox" Highlight="White" TransparentBackground="Black" HighlightOpacity="0.3" BackgroundOpacity="0.2"
Foreground="White" FontSize="24" Width="200" Height="40" Margin="0, 10, 0, 10"
IsEnabled="{Binding ElementName=_this, Path=LaunchToolboxButtonEnabled, Mode=OneWay}"
Clicked="LaunchToolboxButton_Clicked" Grid.Row="1" Grid.ColumnSpan="3"></controls:OpaqueButton>
<Grid x:Name="RightContainer" Grid.Column="2" Margin="10">
<controls:ChromiumBrowserWrapper Address="{Binding ElementName=_this, Path=RightBrowserAddress, Mode=OneWay}"
Foreground="White" FavoriteUriChanged="RightBrowser_FavoriteUriChanged"
@@ -14,18 +14,20 @@ namespace Daybreak.Views
/// <summary>
/// Interaction logic for StartupView.xaml
/// </summary>
public partial class StartupView : UserControl
public partial class MainView : UserControl
{
public static readonly DependencyProperty LaunchButtonEnabledProperty =
DependencyPropertyExtensions.Register<StartupView, bool>(nameof(LaunchButtonEnabled));
DependencyPropertyExtensions.Register<MainView, bool>(nameof(LaunchButtonEnabled));
public static readonly DependencyProperty LaunchToolboxButtonEnabledProperty =
DependencyPropertyExtensions.Register<MainView, bool>(nameof(LaunchToolboxButtonEnabled));
public static readonly DependencyProperty RightBrowserAddressProperty =
DependencyPropertyExtensions.Register<StartupView, string>(nameof(RightBrowserAddress));
DependencyPropertyExtensions.Register<MainView, string>(nameof(RightBrowserAddress));
public static readonly DependencyProperty LeftBrowserAddressProperty =
DependencyPropertyExtensions.Register<StartupView, string>(nameof(LeftBrowserAddress));
DependencyPropertyExtensions.Register<MainView, string>(nameof(LeftBrowserAddress));
public static readonly DependencyProperty RightBrowserFavoriteAddressProperty =
DependencyPropertyExtensions.Register<StartupView, string>(nameof(RightBrowserFavoriteAddress));
DependencyPropertyExtensions.Register<MainView, string>(nameof(RightBrowserFavoriteAddress));
public static readonly DependencyProperty LeftBrowserFavoriteAddressProperty =
DependencyPropertyExtensions.Register<StartupView, string>(nameof(LeftBrowserFavoriteAddress));
DependencyPropertyExtensions.Register<MainView, string>(nameof(LeftBrowserFavoriteAddress));
private readonly IApplicationDetector applicationDetector;
private readonly IViewManager viewManager;
@@ -60,8 +62,13 @@ namespace Daybreak.Views
get => this.GetTypedValue<bool>(LaunchButtonEnabledProperty);
set => this.SetTypedValue(LaunchButtonEnabledProperty, value);
}
public bool LaunchToolboxButtonEnabled
{
get => this.GetTypedValue<bool>(LaunchToolboxButtonEnabledProperty);
set => this.SetTypedValue(LaunchToolboxButtonEnabledProperty, value);
}
public StartupView(
public MainView(
IApplicationDetector applicationDetector,
IViewManager viewManager,
IConfigurationManager configurationManager)
@@ -90,14 +97,8 @@ namespace Daybreak.Views
private void CheckGameState()
{
if (applicationDetector.IsGuildwarsRunning)
{
this.LaunchButtonEnabled = false;
}
else
{
this.LaunchButtonEnabled = true;
}
this.LaunchButtonEnabled = applicationDetector.IsGuildwarsRunning is false;
this.LaunchToolboxButtonEnabled = applicationDetector.IsToolboxRunning is false;
}
private void StartupView_Loaded(object sender, RoutedEventArgs e)
@@ -109,7 +110,7 @@ namespace Daybreak.Views
this.cancellationTokenSource.Cancel();
}
private void OpaqueButton_Clicked(object sender, EventArgs e)
private void LaunchButton_Clicked(object sender, EventArgs e)
{
try
{
@@ -121,6 +122,18 @@ namespace Daybreak.Views
}
}
private void LaunchToolboxButton_Clicked(object sender, EventArgs e)
{
try
{
this.applicationDetector.LaunchGuildwarsToolbox();
}
catch
{
this.viewManager.ShowView<SettingsView>();
}
}
private void LeftBrowser_FavoriteUriChanged(object sender, string e)
{
var config = this.configurationManager.GetConfiguration();
+9 -2
View File
@@ -16,6 +16,7 @@
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
@@ -39,9 +40,15 @@
x:Name="GamePathTextbox" FontSize="22" Margin="0, 0, 30, 0" Background="Transparent" Foreground="White"></TextBox>
<controls:FilePickerGlyph Grid.Row="4" Grid.Column="1" Height="30" Width="30" HorizontalAlignment="Right"
Foreground="White" BorderBrush="Gray" BorderThickness="1"
Clicked="FilePickerGlyph_Clicked"></controls:FilePickerGlyph>
<TextBlock Text="Address bar readonly: " FontSize="22" Foreground="White" Grid.Row="5"/>
Clicked="GameFilePickerGlyph_Clicked"></controls:FilePickerGlyph>
<TextBlock Text="Toolbox path: " FontSize="22" Foreground="White" Grid.Row="5"></TextBlock>
<TextBox Grid.Column="1" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Grid.Row="5"
x:Name="ToolboxPathTextbox" FontSize="22" Margin="0, 0, 30, 0" Background="Transparent" Foreground="White"></TextBox>
<controls:FilePickerGlyph Grid.Row="5" Grid.Column="1" Height="30" Width="30" HorizontalAlignment="Right"
Foreground="White" BorderBrush="Gray" BorderThickness="1"
Clicked="ToolboxFilePickerGlyph_Clicked"></controls:FilePickerGlyph>
<TextBlock Text="Address bar readonly: " FontSize="22" Foreground="White" Grid.Row="6"/>
<TextBox Grid.Column="1" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Grid.Row="6"
x:Name="AddressBarReadonlyTextbox" FontSize="22" Background="Transparent" Foreground="White"></TextBox>
</Grid>
</UserControl>
+20 -3
View File
@@ -43,6 +43,7 @@ namespace Daybreak.Views
this.AddressBarReadonlyTextbox.Text = config.AddressBarReadonly.ToString();
this.CharacterTextbox.Text = config.CharacterName;
this.GamePathTextbox.Text = config.GamePath;
this.ToolboxPathTextbox.Text = config.ToolboxPath;
}
private void SaveButton_Clicked(object sender, System.EventArgs e)
@@ -50,6 +51,7 @@ namespace Daybreak.Views
var currentConfig = this.configurationManager.GetConfiguration();
currentConfig.CharacterName = this.CharacterTextbox.Text;
currentConfig.GamePath = this.GamePathTextbox.Text;
currentConfig.ToolboxPath = this.ToolboxPathTextbox.Text;
if (bool.TryParse(this.AddressBarReadonlyTextbox.Text, out var addressBarReadonly))
{
currentConfig.AddressBarReadonly = addressBarReadonly;
@@ -57,10 +59,10 @@ namespace Daybreak.Views
this.configurationManager.SaveConfiguration(currentConfig);
this.credentialManager.StoreCredentials(new LoginCredentials { Username = this.UsernameTextbox.Text, Password = this.PasswordBox.Password });
this.viewManager.ShowView<StartupView>();
this.viewManager.ShowView<MainView>();
}
private void FilePickerGlyph_Clicked(object sender, System.EventArgs e)
private void GameFilePickerGlyph_Clicked(object sender, System.EventArgs e)
{
var filePicker = new OpenFileDialog()
{
@@ -75,9 +77,24 @@ namespace Daybreak.Views
}
}
private void ToolboxFilePickerGlyph_Clicked(object sender, System.EventArgs e)
{
var filePicker = new OpenFileDialog()
{
CheckFileExists = true,
CheckPathExists = true,
DefaultExt = "exe",
Multiselect = false
};
if (filePicker.ShowDialog() is true)
{
this.ToolboxPathTextbox.Text = filePicker.FileName;
}
}
private void BackButton_Clicked(object sender, System.EventArgs e)
{
this.viewManager.ShowView<StartupView>();
this.viewManager.ShowView<MainView>();
}
}
}
+22
View File
@@ -0,0 +1,22 @@
<UserControl x:Class="Daybreak.Views.UpdateView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Daybreak.Views"
mc:Ignorable="d"
x:Name="_this"
xmlns:controls="clr-namespace:Daybreak.Controls"
Loaded="UpdateView_Loaded"
d:DesignHeight="450" d:DesignWidth="800">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Background="White" MinHeight="200" MinWidth="400">
<StackPanel VerticalAlignment="Center">
<TextBlock Text="{Binding ElementName=_this, Path=Description, Mode=OneWay}" Margin="10, 0, 10, 10" Foreground="Black"
TextWrapping="Wrap"></TextBlock>
<ProgressBar Minimum="0" Maximum="100" Value="{Binding ElementName=_this, Path=ProgressValue, Mode=OneWay}" Width="300" Height="20"></ProgressBar>
<controls:OpaqueButton Text="Ok" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="30" Height="20"
IsEnabled="{Binding ElementName=_this, Path=ContinueButtonEnabled, Mode=OneWay}"
Clicked="OpaqueButton_Clicked" Foreground="Black"></controls:OpaqueButton>
</StackPanel>
</Grid>
</UserControl>
+102
View File
@@ -0,0 +1,102 @@
using Daybreak.Models;
using Daybreak.Services.Logging;
using Daybreak.Services.Updater;
using Daybreak.Services.ViewManagement;
using Daybreak.Utils;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Extensions;
namespace Daybreak.Views
{
/// <summary>
/// Interaction logic for UpdateView.xaml
/// </summary>
public partial class UpdateView : UserControl
{
public readonly static DependencyProperty DescriptionProperty =
DependencyPropertyExtensions.Register<UpdateView, string>(nameof(Description));
public readonly static DependencyProperty ProgressValueProperty =
DependencyPropertyExtensions.Register<UpdateView, double>(nameof(ProgressValue));
public readonly static DependencyProperty ContinueButtonEnabledProperty =
DependencyPropertyExtensions.Register<UpdateView, bool>(nameof(ContinueButtonEnabled));
private readonly ILogger logger;
private readonly IViewManager viewManager;
private readonly IApplicationUpdater applicationUpdater;
private readonly UpdateStatus updateStatus = new();
private bool success = false;
public string Description
{
get => this.GetTypedValue<string>(DescriptionProperty);
set => this.SetValue(DescriptionProperty, value);
}
public double ProgressValue
{
get => this.GetTypedValue<double>(ProgressValueProperty);
set => this.SetValue(ProgressValueProperty, value);
}
public bool ContinueButtonEnabled
{
get => this.GetTypedValue<bool>(ContinueButtonEnabledProperty);
set => this.SetValue(ContinueButtonEnabledProperty, value);
}
public UpdateView(
IApplicationUpdater applicationUpdater,
ILogger logger,
IViewManager viewManager)
{
this.applicationUpdater = applicationUpdater;
this.logger = logger;
this.viewManager = viewManager;
this.updateStatus.PropertyChanged += UpdateStatus_PropertyChanged;
this.InitializeComponent();
}
private void UpdateStatus_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
this.Dispatcher.Invoke(() =>
{
if (this.updateStatus.CurrentStep is UpdateStatus.DownloadUpdateStep downloadUpdateStep)
{
this.ProgressValue = downloadUpdateStep.Progress * 100;
}
this.Description = this.updateStatus.CurrentStep.Name;
});
}
private async void UpdateView_Loaded(object sender, RoutedEventArgs e)
{
this.logger.LogInformation("Starting update procedure");
var success = await applicationUpdater.DownloadUpdate(updateStatus).ConfigureAwait(true);
if (success is false)
{
this.logger.LogError("Update procedure failed");
}
else
{
this.success= true;
this.logger.LogInformation("Downloaded update");
}
this.ContinueButtonEnabled = true;
}
private void OpaqueButton_Clicked(object sender, System.EventArgs e)
{
if (this.success)
{
this.applicationUpdater.FinalizeUpdate();
Application.Current.Shutdown();
}
else
{
this.viewManager.ShowView<MainView>();
}
}
}
}
+2
View File
@@ -3,6 +3,8 @@ Custom client for Guildwars.
Requires standalone version https://developer.microsoft.com/microsoft-edge/webview2.
![Alt Text](https://media1.giphy.com/media/Z32o0OZ5pZHDOIodzD/giphy.gif)
![Alt Text](https://media0.giphy.com/media/aQ8Wl7lsuhT0AblCPI/giphy.gif)
![Alt Text](https://media2.giphy.com/media/s06PtxgeAAZtoJhTx6/giphy.gif)
## Features
Automatically detect if guildwars is running or not. Includes the ability to launch guildwars from the client.