feat: launcher auto-updating
This commit is contained in:
parent
813537af75
commit
e500ef0d26
|
|
@ -1,10 +1,13 @@
|
||||||
namespace Kiseki.Launcher.Windows;
|
namespace Kiseki.Launcher.Windows;
|
||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Net;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
using Microsoft.Win32;
|
using Kiseki.Launcher.Helpers;
|
||||||
|
using Kiseki.Launcher.Models;
|
||||||
|
|
||||||
|
using Microsoft.Win32;
|
||||||
using Syroot.Windows.IO;
|
using Syroot.Windows.IO;
|
||||||
|
|
||||||
public class Bootstrapper : Interfaces.IBootstrapper
|
public class Bootstrapper : Interfaces.IBootstrapper
|
||||||
|
|
@ -15,7 +18,7 @@ public class Bootstrapper : Interfaces.IBootstrapper
|
||||||
private readonly Dictionary<string, string> Arguments = new();
|
private readonly Dictionary<string, string> Arguments = new();
|
||||||
|
|
||||||
public event EventHandler<string>? OnHeadingChange;
|
public event EventHandler<string>? OnHeadingChange;
|
||||||
public event EventHandler<int>? OnProgressBarAdd;
|
public event EventHandler<int>? OnProgressBarSet;
|
||||||
public event EventHandler<Enums.ProgressBarState>? OnProgressBarStateChange;
|
public event EventHandler<Enums.ProgressBarState>? OnProgressBarStateChange;
|
||||||
public event EventHandler<string[]>? OnError;
|
public event EventHandler<string[]>? OnError;
|
||||||
|
|
||||||
|
|
@ -46,9 +49,66 @@ public class Bootstrapper : Interfaces.IBootstrapper
|
||||||
return true;
|
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()
|
public void Abort()
|
||||||
|
|
@ -63,9 +123,9 @@ public class Bootstrapper : Interfaces.IBootstrapper
|
||||||
OnHeadingChange!.Invoke(this, heading);
|
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)
|
protected virtual void ProgressBarStateChange(Enums.ProgressBarState state)
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ public class MainWindow : Form
|
||||||
{
|
{
|
||||||
Bootstrapper = new Bootstrapper(payload);
|
Bootstrapper = new Bootstrapper(payload);
|
||||||
Bootstrapper.OnHeadingChange += Bootstrapper_HeadingChanged;
|
Bootstrapper.OnHeadingChange += Bootstrapper_HeadingChanged;
|
||||||
Bootstrapper.OnProgressBarAdd += Bootstrapper_ProgressBarAdded;
|
Bootstrapper.OnProgressBarSet += Bootstrapper_ProgressBarSet;
|
||||||
Bootstrapper.OnProgressBarStateChange += Bootstrapper_ProgressBarStateChanged;
|
Bootstrapper.OnProgressBarStateChange += Bootstrapper_ProgressBarStateChanged;
|
||||||
Bootstrapper.OnError += Bootstrapper_Errored;
|
Bootstrapper.OnError += Bootstrapper_Errored;
|
||||||
|
|
||||||
|
|
@ -72,9 +72,9 @@ public class MainWindow : Form
|
||||||
Page.Heading = heading;
|
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)
|
private void Bootstrapper_ProgressBarStateChanged(object? sender, Enums.ProgressBarState state)
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ namespace Kiseki.Launcher.Windows;
|
||||||
|
|
||||||
public static class Win32
|
public static class Win32
|
||||||
{
|
{
|
||||||
// Ref: https://learn.microsoft.com/en-us/windows/win32/msi/error-codes
|
// REF: https://learn.microsoft.com/en-us/windows/win32/msi/error-codes
|
||||||
// Ref: https://i-logic.com/serial/errorcodes.htm
|
// REF: https://i-logic.com/serial/errorcodes.htm
|
||||||
public enum ErrorCode
|
public enum ErrorCode
|
||||||
{
|
{
|
||||||
ERROR_SUCCESS = 0,
|
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
|
public static class Base64
|
||||||
{
|
{
|
||||||
// Source: https://stackoverflow.com/a/54143400
|
// REF: https://stackoverflow.com/a/54143400
|
||||||
public static bool IsBase64String(string base64)
|
public static bool IsBase64String(string base64)
|
||||||
{
|
{
|
||||||
Span<byte> buffer = new(new byte[base64.Length]);
|
Span<byte> buffer = new(new byte[base64.Length]);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ public interface IBootstrapper
|
||||||
{
|
{
|
||||||
// These connect to MainWindow
|
// These connect to MainWindow
|
||||||
event EventHandler<string>? OnHeadingChange;
|
event EventHandler<string>? OnHeadingChange;
|
||||||
event EventHandler<int>? OnProgressBarAdd;
|
event EventHandler<int>? OnProgressBarSet;
|
||||||
event EventHandler<Enums.ProgressBarState>? OnProgressBarStateChange;
|
event EventHandler<Enums.ProgressBarState>? OnProgressBarStateChange;
|
||||||
event EventHandler<string[]>? OnError;
|
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