feat: launcher auto-updating

This commit is contained in:
rjindael 2023-08-02 16:50:17 -07:00
parent 813537af75
commit e500ef0d26
No known key found for this signature in database
GPG Key ID: D069369C906CCF31
7 changed files with 94 additions and 20 deletions

View File

@ -1,10 +1,13 @@
namespace Kiseki.Launcher.Windows;
using System.Diagnostics;
using System.Net;
using System.Reflection;
using Microsoft.Win32;
using Kiseki.Launcher.Helpers;
using Kiseki.Launcher.Models;
using Microsoft.Win32;
using Syroot.Windows.IO;
public class Bootstrapper : Interfaces.IBootstrapper
@ -15,7 +18,7 @@ public class Bootstrapper : Interfaces.IBootstrapper
private readonly Dictionary<string, string> Arguments = new();
public event EventHandler<string>? OnHeadingChange;
public event EventHandler<int>? OnProgressBarAdd;
public event EventHandler<int>? OnProgressBarSet;
public event EventHandler<Enums.ProgressBarState>? OnProgressBarStateChange;
public event EventHandler<string[]>? OnError;
@ -46,9 +49,66 @@ public class Bootstrapper : Interfaces.IBootstrapper
return true;
}
public void Run()
public async void Run()
{
//
// Check for updates
HeadingChange("Checking for updates...");
// Check for a new launcher release from GitHub
var release = await Http.GetJson<GitHubRelease>($"https://api.github.com/repos/{Constants.PROJECT_REPOSITORY}/releases/latest");
bool launcherUpToDate = true;
// TODO: We can remove this check once we do our first release.
if (release is not null && release.Assets is not null)
{
launcherUpToDate = Version == release.TagName[1..];
if (!launcherUpToDate)
{
// Update the launcher
HeadingChange("Getting the latest launcher...");
ProgressBarStateChange(Enums.ProgressBarState.Normal);
// TODO: This needs to be rewritten. It's a mess.
// REF: https://stackoverflow.com/a/9459441
Thread thread = new(() => {
using WebClient client = new();
client.DownloadProgressChanged += (_, e) => {
double bytesIn = double.Parse(e.BytesReceived.ToString());
double totalBytes = double.Parse(e.TotalBytesToReceive.ToString());
double percentage = bytesIn / totalBytes * 100;
ProgressBarSet(int.Parse(Math.Truncate(percentage).ToString()));
};
client.DownloadFileCompleted += (_, _) => {
HeadingChange("Installing the latest launcher...");
ProgressBarStateChange(Enums.ProgressBarState.Marquee);
// Rename Kiseki.Launcher.exe.new -> Kiseki.Launcher.exe, and launch it with our payload
string command = $"del /Q \"{Paths.Application}\" && move /Y \"{Paths.Application}.new\" \"{Paths.Application}\" && start \"\" \"{Paths.Application}\" {Payload}";
Process.Start(new ProcessStartInfo()
{
FileName = "cmd.exe",
Arguments = $"/c timeout 1 && {command}",
UseShellExecute = true,
WindowStyle = ProcessWindowStyle.Hidden
});
Environment.Exit((int)Win32.ErrorCode.ERROR_SUCCESS);
};
client.DownloadFileAsync(new Uri(release.Assets[0].BrowserDownloadUrl), $"{Paths.Application}.new");
});
thread.Start();
return;
}
}
}
public void Abort()
@ -63,9 +123,9 @@ public class Bootstrapper : Interfaces.IBootstrapper
OnHeadingChange!.Invoke(this, heading);
}
protected virtual void ProgressBarAdd(int value)
protected virtual void ProgressBarSet(int value)
{
OnProgressBarAdd!.Invoke(this, value);
OnProgressBarSet!.Invoke(this, value);
}
protected virtual void ProgressBarStateChange(Enums.ProgressBarState state)

View File

@ -13,7 +13,7 @@ public class MainWindow : Form
{
Bootstrapper = new Bootstrapper(payload);
Bootstrapper.OnHeadingChange += Bootstrapper_HeadingChanged;
Bootstrapper.OnProgressBarAdd += Bootstrapper_ProgressBarAdded;
Bootstrapper.OnProgressBarSet += Bootstrapper_ProgressBarSet;
Bootstrapper.OnProgressBarStateChange += Bootstrapper_ProgressBarStateChanged;
Bootstrapper.OnError += Bootstrapper_Errored;
@ -72,9 +72,9 @@ public class MainWindow : Form
Page.Heading = heading;
}
private void Bootstrapper_ProgressBarAdded(object? sender, int value)
private void Bootstrapper_ProgressBarSet(object? sender, int value)
{
Page.ProgressBar!.Value += value;
Page.ProgressBar!.Value = value;
}
private void Bootstrapper_ProgressBarStateChanged(object? sender, Enums.ProgressBarState state)

View File

@ -2,8 +2,8 @@ namespace Kiseki.Launcher.Windows;
public static class Win32
{
// Ref: https://learn.microsoft.com/en-us/windows/win32/msi/error-codes
// Ref: https://i-logic.com/serial/errorcodes.htm
// REF: https://learn.microsoft.com/en-us/windows/win32/msi/error-codes
// REF: https://i-logic.com/serial/errorcodes.htm
public enum ErrorCode
{
ERROR_SUCCESS = 0,

View File

@ -1,7 +0,0 @@
namespace Kiseki.Launcher.Enums;
public enum PackageType
{
Bootstrapper,
Client,
}

View File

@ -4,7 +4,7 @@ using System.Text;
public static class Base64
{
// Source: https://stackoverflow.com/a/54143400
// REF: https://stackoverflow.com/a/54143400
public static bool IsBase64String(string base64)
{
Span<byte> buffer = new(new byte[base64.Length]);

View File

@ -4,7 +4,7 @@ public interface IBootstrapper
{
// These connect to MainWindow
event EventHandler<string>? OnHeadingChange;
event EventHandler<int>? OnProgressBarAdd;
event EventHandler<int>? OnProgressBarSet;
event EventHandler<Enums.ProgressBarState>? OnProgressBarStateChange;
event EventHandler<string[]>? OnError;

View File

@ -0,0 +1,21 @@
namespace Kiseki.Launcher.Models;
using System.Text.Json.Serialization;
public class ClientRelease
{
[JsonPropertyName("checksums")]
public Dictionary<string, string> Checksums { get; set; } = null!;
[JsonPropertyName("asset")]
public ClientReleaseAsset Asset { get; set; } = null!;
}
public class ClientReleaseAsset
{
[JsonPropertyName("url")]
public string Url { get; set; } = null!;
[JsonPropertyName("checksum")]
public string Checksum { get; set; } = null!;
}