forked from Mirror/Ryujinx
Ava UI: Fixes and cleanup Updater (#4269)
* ava: Fixes and cleanup Updater * _updateSuccessful
This commit is contained in:
parent
bb89e36fd8
commit
eb2cc159fa
4 changed files with 284 additions and 253 deletions
|
@ -7,9 +7,7 @@ using ICSharpCode.SharpZipLib.Zip;
|
|||
using Newtonsoft.Json.Linq;
|
||||
using Ryujinx.Ava;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
|
@ -32,7 +30,6 @@ namespace Ryujinx.Modules
|
|||
internal static class Updater
|
||||
{
|
||||
private const string GitHubApiURL = "https://api.github.com";
|
||||
internal static bool Running;
|
||||
|
||||
private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory;
|
||||
private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
|
||||
|
@ -43,20 +40,19 @@ namespace Ryujinx.Modules
|
|||
private static string _platformExt;
|
||||
private static string _buildUrl;
|
||||
private static long _buildSize;
|
||||
private static bool _updateSuccessful;
|
||||
private static bool _running;
|
||||
|
||||
private static readonly string[] WindowsDependencyDirs = Array.Empty<string>();
|
||||
|
||||
public static bool UpdateSuccessful { get; private set; }
|
||||
|
||||
public static async Task BeginParse(MainWindow mainWindow, bool showVersionUpToDate)
|
||||
public static async Task BeginParse(Window mainWindow, bool showVersionUpToDate)
|
||||
{
|
||||
if (Running)
|
||||
if (_running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Running = true;
|
||||
mainWindow.ViewModel.CanUpdate = false;
|
||||
_running = true;
|
||||
|
||||
// Detect current platform
|
||||
if (OperatingSystem.IsMacOS())
|
||||
|
@ -82,21 +78,25 @@ namespace Ryujinx.Modules
|
|||
catch
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!");
|
||||
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage], LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
||||
});
|
||||
|
||||
_running = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Get latest version number from GitHub API
|
||||
try
|
||||
{
|
||||
using (HttpClient jsonClient = ConstructHttpClient())
|
||||
{
|
||||
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
|
||||
using HttpClient jsonClient = ConstructHttpClient();
|
||||
|
||||
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
|
||||
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
|
||||
JObject jsonRoot = JObject.Parse(fetchedJson);
|
||||
JToken assets = jsonRoot["assets"];
|
||||
|
@ -123,6 +123,8 @@ namespace Ryujinx.Modules
|
|||
});
|
||||
}
|
||||
|
||||
_running = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -141,18 +143,22 @@ namespace Ryujinx.Modules
|
|||
});
|
||||
}
|
||||
|
||||
_running = false;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, exception.Message);
|
||||
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterFailedToGetVersionMessage]);
|
||||
});
|
||||
|
||||
_running = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -163,11 +169,16 @@ namespace Ryujinx.Modules
|
|||
catch
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from Github!");
|
||||
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage], LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
||||
});
|
||||
|
||||
_running = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -181,8 +192,7 @@ namespace Ryujinx.Modules
|
|||
});
|
||||
}
|
||||
|
||||
Running = false;
|
||||
mainWindow.ViewModel.CanUpdate = true;
|
||||
_running = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -210,7 +220,8 @@ namespace Ryujinx.Modules
|
|||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
// Show a message asking the user if they want to update
|
||||
var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
|
||||
var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(
|
||||
LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
|
||||
LocaleManager.Instance[LocaleKeys.RyujinxUpdaterMessage],
|
||||
$"{Program.Version} -> {newVersion}");
|
||||
|
||||
|
@ -218,12 +229,16 @@ namespace Ryujinx.Modules
|
|||
{
|
||||
UpdateRyujinx(mainWindow, _buildUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
_running = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static HttpClient ConstructHttpClient()
|
||||
{
|
||||
HttpClient result = new HttpClient();
|
||||
HttpClient result = new();
|
||||
|
||||
// Required by GitHub to interract with APIs.
|
||||
result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0");
|
||||
|
@ -233,7 +248,7 @@ namespace Ryujinx.Modules
|
|||
|
||||
public static async void UpdateRyujinx(Window parent, string downloadUrl)
|
||||
{
|
||||
UpdateSuccessful = false;
|
||||
_updateSuccessful = false;
|
||||
|
||||
// Empty update dir, although it shouldn't ever have anything inside it
|
||||
if (Directory.Exists(UpdateDir))
|
||||
|
@ -245,17 +260,16 @@ namespace Ryujinx.Modules
|
|||
|
||||
string updateFile = Path.Combine(UpdateDir, "update.bin");
|
||||
|
||||
var taskDialog = new TaskDialog()
|
||||
TaskDialog taskDialog = new()
|
||||
{
|
||||
Header = LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
|
||||
SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading],
|
||||
IconSource = new SymbolIconSource { Symbol = Symbol.Download },
|
||||
Buttons = { },
|
||||
ShowProgressBar = true
|
||||
ShowProgressBar = true,
|
||||
XamlRoot = parent
|
||||
};
|
||||
|
||||
taskDialog.XamlRoot = parent;
|
||||
|
||||
taskDialog.Opened += (s, e) =>
|
||||
{
|
||||
if (_buildSize >= 0)
|
||||
|
@ -270,7 +284,7 @@ namespace Ryujinx.Modules
|
|||
|
||||
await taskDialog.ShowAsync(true);
|
||||
|
||||
if (UpdateSuccessful)
|
||||
if (_updateSuccessful)
|
||||
{
|
||||
var shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterCompleteMessage],
|
||||
|
@ -305,8 +319,8 @@ namespace Ryujinx.Modules
|
|||
int totalProgressPercentage = 0;
|
||||
int[] progressPercentage = new int[ConnectionCount];
|
||||
|
||||
List<byte[]> list = new List<byte[]>(ConnectionCount);
|
||||
List<WebClient> webClients = new List<WebClient>(ConnectionCount);
|
||||
List<byte[]> list = new(ConnectionCount);
|
||||
List<WebClient> webClients = new(ConnectionCount);
|
||||
|
||||
for (int i = 0; i < ConnectionCount; i++)
|
||||
{
|
||||
|
@ -317,9 +331,9 @@ namespace Ryujinx.Modules
|
|||
{
|
||||
#pragma warning disable SYSLIB0014
|
||||
// TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient.
|
||||
using (WebClient client = new WebClient())
|
||||
using WebClient client = new();
|
||||
#pragma warning restore SYSLIB0014
|
||||
{
|
||||
|
||||
webClients.Add(client);
|
||||
|
||||
if (i == ConnectionCount - 1)
|
||||
|
@ -405,20 +419,18 @@ namespace Ryujinx.Modules
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void DoUpdateWithSingleThreadWorker(TaskDialog taskDialog, string downloadUrl, string updateFile)
|
||||
{
|
||||
using (HttpClient client = new HttpClient())
|
||||
{
|
||||
using HttpClient client = new();
|
||||
// We do not want to timeout while downloading
|
||||
client.Timeout = TimeSpan.FromDays(1);
|
||||
|
||||
using (HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result)
|
||||
using (Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result)
|
||||
{
|
||||
using (Stream updateFileStream = File.Open(updateFile, FileMode.Create))
|
||||
{
|
||||
using Stream updateFileStream = File.Open(updateFile, FileMode.Create);
|
||||
|
||||
long totalBytes = response.Content.Headers.ContentLength.Value;
|
||||
long byteWritten = 0;
|
||||
|
||||
|
@ -440,11 +452,9 @@ namespace Ryujinx.Modules
|
|||
updateFileStream.Write(buffer, 0, readSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InstallUpdate(taskDialog, updateFile);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static double GetPercentage(double value, double max)
|
||||
|
@ -454,8 +464,11 @@ namespace Ryujinx.Modules
|
|||
|
||||
private static void DoUpdateWithSingleThread(TaskDialog taskDialog, string downloadUrl, string updateFile)
|
||||
{
|
||||
Thread worker = new Thread(() => DoUpdateWithSingleThreadWorker(taskDialog, downloadUrl, updateFile));
|
||||
worker.Name = "Updater.SingleThreadWorker";
|
||||
Thread worker = new(() => DoUpdateWithSingleThreadWorker(taskDialog, downloadUrl, updateFile))
|
||||
{
|
||||
Name = "Updater.SingleThreadWorker"
|
||||
};
|
||||
|
||||
worker.Start();
|
||||
}
|
||||
|
||||
|
@ -483,10 +496,10 @@ namespace Ryujinx.Modules
|
|||
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
using (Stream inStream = File.OpenRead(updateFile))
|
||||
using (Stream gzipStream = new GZipInputStream(inStream))
|
||||
using (TarInputStream tarStream = new TarInputStream(gzipStream, Encoding.ASCII))
|
||||
{
|
||||
using Stream inStream = File.OpenRead(updateFile);
|
||||
using GZipInputStream gzipStream = new(inStream);
|
||||
using TarInputStream tarStream = new(gzipStream, Encoding.ASCII);
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
TarEntry tarEntry;
|
||||
|
@ -516,12 +529,11 @@ namespace Ryujinx.Modules
|
|||
|
||||
taskDialog.SetProgressBarState(100, TaskDialogProgressState.Normal);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Stream inStream = File.OpenRead(updateFile))
|
||||
using (ZipFile zipFile = new ZipFile(inStream))
|
||||
{
|
||||
using Stream inStream = File.OpenRead(updateFile);
|
||||
using ZipFile zipFile = new(inStream);
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
double count = 0;
|
||||
|
@ -549,7 +561,6 @@ namespace Ryujinx.Modules
|
|||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Delete downloaded zip
|
||||
File.Delete(updateFile);
|
||||
|
@ -594,21 +605,24 @@ namespace Ryujinx.Modules
|
|||
|
||||
SetFileExecutable(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx"));
|
||||
|
||||
UpdateSuccessful = true;
|
||||
_updateSuccessful = true;
|
||||
|
||||
taskDialog.Hide();
|
||||
}
|
||||
|
||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
public static bool CanUpdate(bool showWarnings, StyleableWindow parent)
|
||||
public static bool CanUpdate(bool showWarnings)
|
||||
{
|
||||
#if !DISABLE_UPDATER
|
||||
if (RuntimeInformation.OSArchitecture != Architecture.X64)
|
||||
{
|
||||
if (showWarnings)
|
||||
{
|
||||
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedMessage],
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedMessage],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage]);
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -618,8 +632,12 @@ namespace Ryujinx.Modules
|
|||
{
|
||||
if (showWarnings)
|
||||
{
|
||||
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetMessage],
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetMessage],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetSubMessage]);
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -629,8 +647,12 @@ namespace Ryujinx.Modules
|
|||
{
|
||||
if (showWarnings)
|
||||
{
|
||||
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildMessage],
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildMessage],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]);
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -642,18 +664,27 @@ namespace Ryujinx.Modules
|
|||
{
|
||||
if (ReleaseInformation.IsFlatHubBuild())
|
||||
{
|
||||
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage]);
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage]);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]);
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
|
||||
// NOTE: This method should always reflect the latest build layout.s
|
||||
private static IEnumerable<string> EnumerateFilesToDelete()
|
||||
|
@ -677,7 +708,7 @@ namespace Ryujinx.Modules
|
|||
|
||||
private static void MoveAllFilesOver(string root, string dest, TaskDialog taskDialog)
|
||||
{
|
||||
var total = Directory.GetFiles(root, "*", SearchOption.AllDirectories).Length;
|
||||
int total = Directory.GetFiles(root, "*", SearchOption.AllDirectories).Length;
|
||||
foreach (string directory in Directory.GetDirectories(root))
|
||||
{
|
||||
string dirName = Path.GetFileName(directory);
|
||||
|
@ -694,6 +725,7 @@ namespace Ryujinx.Modules
|
|||
foreach (string file in Directory.GetFiles(root))
|
||||
{
|
||||
count++;
|
||||
|
||||
File.Move(file, Path.Combine(dest, Path.GetFileName(file)), true);
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
|
|
|
@ -88,7 +88,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
private float _volume;
|
||||
private string _backendText;
|
||||
|
||||
private bool _canUpdate;
|
||||
private bool _canUpdate = true;
|
||||
private Cursor _cursor;
|
||||
private string _title;
|
||||
private string _currentEmulatedGamePath;
|
||||
|
@ -177,11 +177,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
|
||||
public bool CanUpdate
|
||||
{
|
||||
get => _canUpdate;
|
||||
get => _canUpdate && EnableNonGameRunningControls && Modules.Updater.CanUpdate(false);
|
||||
set
|
||||
{
|
||||
_canUpdate = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -165,7 +165,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||
|
||||
public async void CheckForUpdates(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (Updater.CanUpdate(true, Window))
|
||||
if (Updater.CanUpdate(true))
|
||||
{
|
||||
await Updater.BeginParse(Window, true);
|
||||
}
|
||||
|
|
|
@ -271,7 +271,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||
ViewModel.LoadApplication(_launchPath, _startFullscreen);
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false, this))
|
||||
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))
|
||||
{
|
||||
Updater.BeginParse(this, false).ContinueWith(task =>
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue