feat: launcher auto-updating
This commit is contained in:
parent
813537af75
commit
e500ef0d26
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
namespace Kiseki.Launcher.Enums;
|
||||
|
||||
public enum PackageType
|
||||
{
|
||||
Bootstrapper,
|
||||
Client,
|
||||
}
|
||||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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!;
|
||||
}
|
||||
Loading…
Reference in New Issue