From 1288299dcc7df9eae8028c19c2afbdf09138530b Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Thu, 29 Aug 2024 11:20:37 +0100 Subject: [PATCH 1/5] Use queue for processing RPC message backlog --- Bloxstrap/App.xaml.cs | 3 -- Bloxstrap/Integrations/DiscordRichPresence.cs | 35 +++++++------------ 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/Bloxstrap/App.xaml.cs b/Bloxstrap/App.xaml.cs index 651806d..695a0f1 100644 --- a/Bloxstrap/App.xaml.cs +++ b/Bloxstrap/App.xaml.cs @@ -6,9 +6,6 @@ using System.Windows.Threading; using Microsoft.Win32; using Bloxstrap.Models.SettingTasks.Base; -using Bloxstrap.UI.Elements.About.Pages; -using Bloxstrap.UI.Elements.About; -using System; namespace Bloxstrap { diff --git a/Bloxstrap/Integrations/DiscordRichPresence.cs b/Bloxstrap/Integrations/DiscordRichPresence.cs index c79fe04..7f30aab 100644 --- a/Bloxstrap/Integrations/DiscordRichPresence.cs +++ b/Bloxstrap/Integrations/DiscordRichPresence.cs @@ -9,7 +9,7 @@ namespace Bloxstrap.Integrations private DiscordRPC.RichPresence? _currentPresence; private DiscordRPC.RichPresence? _currentPresenceCopy; - private Message? _stashedRPCMessage; + private Queue _messageQueue = new(); private bool _visible = true; private long _currentUniverseId; @@ -17,7 +17,7 @@ namespace Bloxstrap.Integrations public DiscordRichPresence(ActivityWatcher activityWatcher) { - const string LOG_IDENT = "DiscordRichPresence::DiscordRichPresence"; + const string LOG_IDENT = "DiscordRichPresence"; _activityWatcher = activityWatcher; @@ -47,7 +47,7 @@ namespace Bloxstrap.Integrations _rpcClient.Initialize(); } - public void ProcessRPCMessage(Message message) + public void ProcessRPCMessage(Message message, bool implicitUpdate = true) { const string LOG_IDENT = "DiscordRichPresence::ProcessRPCMessage"; @@ -56,14 +56,8 @@ namespace Bloxstrap.Integrations if (_currentPresence is null || _currentPresenceCopy is null) { - if (_activityWatcher.ActivityInGame) - { - App.Logger.WriteLine(LOG_IDENT, "Presence is not yet set, but is currently in game, stashing presence set request"); - _stashedRPCMessage = message; - return; - } - - App.Logger.WriteLine(LOG_IDENT, "Presence is not set, aborting"); + App.Logger.WriteLine(LOG_IDENT, "Presence is not set, enqueuing message"); + _messageQueue.Enqueue(message); return; } @@ -159,7 +153,8 @@ namespace Bloxstrap.Integrations } } - UpdatePresence(); + if (implicitUpdate) + UpdatePresence(); } public void SetVisibility(bool visible) @@ -183,7 +178,7 @@ namespace Bloxstrap.Integrations App.Logger.WriteLine(LOG_IDENT, "Not in game, clearing presence"); _currentPresence = _currentPresenceCopy = null; - _stashedRPCMessage = null; + _messageQueue.Clear(); UpdatePresence(); return true; @@ -284,17 +279,13 @@ namespace Bloxstrap.Integrations // this is used for configuration from BloxstrapRPC _currentPresenceCopy = _currentPresence.Clone(); - // TODO: use queue for stashing messages - if (_stashedRPCMessage is not null) + if (_messageQueue.Any()) { - App.Logger.WriteLine(LOG_IDENT, "Found stashed RPC message, invoking presence set command now"); - ProcessRPCMessage(_stashedRPCMessage); - _stashedRPCMessage = null; - } - else - { - UpdatePresence(); + App.Logger.WriteLine(LOG_IDENT, "Processing queued messages"); + ProcessRPCMessage(_messageQueue.Dequeue(), false); } + + UpdatePresence(); return true; } From 9fd4c367fb0b5a7501697a2e453c63535b977fcd Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Fri, 30 Aug 2024 00:25:19 +0100 Subject: [PATCH 2/5] Fix start event handler + detecting launch failure --- Bloxstrap/Bootstrapper.cs | 50 +++++++++++++------------ Bloxstrap/Resources/Strings.Designer.cs | 27 +++++++++++++ Bloxstrap/Resources/Strings.resx | 9 +++++ Bloxstrap/UI/Frontend.cs | 14 +++++++ Bloxstrap/Utility/SystemEvent.cs | 43 --------------------- 5 files changed, 76 insertions(+), 67 deletions(-) delete mode 100644 Bloxstrap/Utility/SystemEvent.cs diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs index 0fc6171..1844cf5 100644 --- a/Bloxstrap/Bootstrapper.cs +++ b/Bloxstrap/Bootstrapper.cs @@ -212,7 +212,9 @@ namespace Bloxstrap await mutex.ReleaseAsync(); if (!App.LaunchSettings.NoLaunchFlag.Active && !_cancelFired) - await StartRoblox(); + StartRoblox(); + + Dialog?.CloseBootstrapper(); } private async Task CheckLatestVersion() @@ -273,7 +275,7 @@ namespace Bloxstrap _versionPackageManifest = await PackageManifest.Get(_latestVersionGuid); } - private async Task StartRoblox() + private void StartRoblox() { const string LOG_IDENT = "Bootstrapper::StartRoblox"; @@ -287,6 +289,12 @@ namespace Bloxstrap _launchCommandLine = _launchCommandLine.Replace("robloxLocale:en_us", $"robloxLocale:{match.Groups[1].Value}", StringComparison.InvariantCultureIgnoreCase); } + // needed for the start event to fire + if (!String.IsNullOrEmpty(_launchCommandLine)) + _launchCommandLine += " "; + + _launchCommandLine += "-isInstallerLaunch"; + var startInfo = new ProcessStartInfo() { FileName = _playerLocation, @@ -297,10 +305,11 @@ namespace Bloxstrap if (_launchMode == LaunchMode.StudioAuth) { Process.Start(startInfo); - Dialog?.CloseBootstrapper(); return; } + using var startEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "www.roblox.com/robloxStartedEvent"); + // v2.2.0 - byfron will trip if we keep a process handle open for over a minute, so we're doing this now int gameClientPid; using (var gameClient = Process.Start(startInfo)!) @@ -308,20 +317,16 @@ namespace Bloxstrap gameClientPid = gameClient.Id; } - App.Logger.WriteLine(LOG_IDENT, $"Started Roblox (PID {gameClientPid})"); + App.Logger.WriteLine(LOG_IDENT, $"Started Roblox (PID {gameClientPid}), waiting for start event"); - using (var startEvent = new SystemEvent(AppData.StartEvent)) + if (!startEvent.WaitOne(TimeSpan.FromSeconds(10))) { - // TODO: get rid of this - bool startEventFired = await startEvent.WaitForEvent(); - - startEvent.Close(); - - // TODO: this cannot silently exit like this - if (!startEventFired) - return; + Frontend.ShowPlayerErrorDialog(); + return; } + App.Logger.WriteLine(LOG_IDENT, "Start event signalled"); + var autoclosePids = new List(); // launch custom integrations now @@ -352,20 +357,17 @@ namespace Bloxstrap autoclosePids.Add(pid); } - using (var proclock = new InterProcessLock("Watcher")) + + string args = gameClientPid.ToString(); + + if (autoclosePids.Any()) + args += $";{String.Join(',', autoclosePids)}"; + + using (var ipl = new InterProcessLock("Watcher")) { - string args = gameClientPid.ToString(); - - if (autoclosePids.Any()) - args += $";{String.Join(',', autoclosePids)}"; - - if (proclock.IsAcquired) + if (ipl.IsAcquired) Process.Start(Paths.Process, $"-watcher \"{args}\""); } - - // event fired, wait for 3 seconds then close - await Task.Delay(3000); - Dialog?.CloseBootstrapper(); } public void CancelInstall() diff --git a/Bloxstrap/Resources/Strings.Designer.cs b/Bloxstrap/Resources/Strings.Designer.cs index 1ce1035..b70ee99 100644 --- a/Bloxstrap/Resources/Strings.Designer.cs +++ b/Bloxstrap/Resources/Strings.Designer.cs @@ -886,6 +886,33 @@ namespace Bloxstrap.Resources { } } + /// + /// Looks up a localized string similar to Roblox has crashed.. + /// + public static string Dialog_PlayerError_Crash { + get { + return ResourceManager.GetString("Dialog.PlayerError.Crash", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Roblox failed to launch.. + /// + public static string Dialog_PlayerError_FailedLaunch { + get { + return ResourceManager.GetString("Dialog.PlayerError.FailedLaunch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please read the following help information, which will open in your web browser when you close this dialog.. + /// + public static string Dialog_PlayerError_HelpInformation { + get { + return ResourceManager.GetString("Dialog.PlayerError.HelpInformation", resourceCulture); + } + } + /// /// Looks up a localized string similar to Early 2015. /// diff --git a/Bloxstrap/Resources/Strings.resx b/Bloxstrap/Resources/Strings.resx index 52c7f52..301c5c9 100644 --- a/Bloxstrap/Resources/Strings.resx +++ b/Bloxstrap/Resources/Strings.resx @@ -1144,4 +1144,13 @@ If not, then please report this exception to the maintainers of this fork. Do NO Issues may occur and your settings may be altered. A reinstall is recommended. Are you sure you want to continue? + + Roblox failed to launch. + + + Roblox has crashed. + + + Please read the following help information, which will open in your web browser when you close this dialog. + \ No newline at end of file diff --git a/Bloxstrap/UI/Frontend.cs b/Bloxstrap/UI/Frontend.cs index af21245..1699b6b 100644 --- a/Bloxstrap/UI/Frontend.cs +++ b/Bloxstrap/UI/Frontend.cs @@ -33,6 +33,20 @@ namespace Bloxstrap.UI } } + public static void ShowPlayerErrorDialog(bool crash = false) + { + if (App.LaunchSettings.QuietFlag.Active) + return; + + string topLine = Strings.Dialog_PlayerError_FailedLaunch; + + if (crash) + topLine = Strings.Dialog_PlayerError_Crash; + + ShowMessageBox($"{topLine}\n\n{Strings.Dialog_PlayerError_HelpInformation}", MessageBoxImage.Error); + Utilities.ShellExecute($"https://github.com/{App.ProjectRepository}/wiki/Roblox-crashes-or-does-not-launch"); + } + public static void ShowExceptionDialog(Exception exception) { Application.Current.Dispatcher.Invoke(() => diff --git a/Bloxstrap/Utility/SystemEvent.cs b/Bloxstrap/Utility/SystemEvent.cs deleted file mode 100644 index f4b4746..0000000 --- a/Bloxstrap/Utility/SystemEvent.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Roblox Studio Mod Manager (ProjectSrc/Utility/SystemEvent.cs) - * MIT License - * Copyright (c) 2015-present MaximumADHD -*/ - -namespace Bloxstrap.Utility -{ - public class SystemEvent : EventWaitHandle - { - public string Name { get; private set; } - - public SystemEvent(string name, bool init = false, EventResetMode mode = EventResetMode.AutoReset) : base(init, mode, name) - { - if (init) - Reset(); - else - Set(); - - Name = name; - } - - public override string ToString() - { - return Name; - } - - public Task WaitForEvent() - { - return Task.Run(WaitOne); - } - - public Task WaitForEvent(TimeSpan timeout, bool exitContext = false) - { - return Task.Run(() => WaitOne(timeout, exitContext)); - } - - public Task WaitForEvent(int millisecondsTimeout, bool exitContext = false) - { - return Task.Run(() => WaitOne(millisecondsTimeout, exitContext)); - } - } -} From 719fbb898e03c1e89a5cd9159053a26ba7b01b42 Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Fri, 30 Aug 2024 00:51:58 +0100 Subject: [PATCH 3/5] Fix custom integration autoclosing not working --- Bloxstrap/Resources/Strings.Designer.cs | 11 +--- Bloxstrap/Resources/Strings.resx | 5 +- .../Settings/Pages/IntegrationsPage.xaml | 4 +- Bloxstrap/Watcher.cs | 51 ++++++++----------- 4 files changed, 26 insertions(+), 45 deletions(-) diff --git a/Bloxstrap/Resources/Strings.Designer.cs b/Bloxstrap/Resources/Strings.Designer.cs index b70ee99..28c8fc2 100644 --- a/Bloxstrap/Resources/Strings.Designer.cs +++ b/Bloxstrap/Resources/Strings.Designer.cs @@ -2528,15 +2528,6 @@ namespace Bloxstrap.Resources { } } - /// - /// Looks up a localized string similar to e.g. C:\Windows\System32\cmd.exe. - /// - public static string Menu_Integrations_Custom_AppLocation_Placeholder { - get { - return ResourceManager.GetString("Menu.Integrations.Custom.AppLocation.Placeholder", resourceCulture); - } - } - /// /// Looks up a localized string similar to Auto close when Roblox closes. /// @@ -2565,7 +2556,7 @@ namespace Bloxstrap.Resources { } /// - /// Looks up a localized string similar to e.g. /k echo Roblox is running!. + /// Looks up a localized string similar to Roblox is running!. /// public static string Menu_Integrations_Custom_LaunchArgs_Placeholder { get { diff --git a/Bloxstrap/Resources/Strings.resx b/Bloxstrap/Resources/Strings.resx index 301c5c9..6f57c25 100644 --- a/Bloxstrap/Resources/Strings.resx +++ b/Bloxstrap/Resources/Strings.resx @@ -727,9 +727,6 @@ Selecting 'No' will ignore this warning and continue installation. Application Location - - e.g. C:\Windows\System32\cmd.exe - Auto close when Roblox closes @@ -740,7 +737,7 @@ Selecting 'No' will ignore this warning and continue installation. Launch Arguments - e.g. /k echo Roblox is running! + Roblox is running! New Integration diff --git a/Bloxstrap/UI/Elements/Settings/Pages/IntegrationsPage.xaml b/Bloxstrap/UI/Elements/Settings/Pages/IntegrationsPage.xaml index ef3c0f0..bf8a959 100644 --- a/Bloxstrap/UI/Elements/Settings/Pages/IntegrationsPage.xaml +++ b/Bloxstrap/UI/Elements/Settings/Pages/IntegrationsPage.xaml @@ -95,11 +95,11 @@ - + - + diff --git a/Bloxstrap/Watcher.cs b/Bloxstrap/Watcher.cs index 652675e..9b14f88 100644 --- a/Bloxstrap/Watcher.cs +++ b/Bloxstrap/Watcher.cs @@ -1,6 +1,4 @@ using Bloxstrap.Integrations; -using System.CodeDom; -using System.Security.Permissions; namespace Bloxstrap { @@ -54,7 +52,7 @@ namespace Bloxstrap if (split.Length >= 2) { - foreach (string strPid in split[0].Split(';')) + foreach (string strPid in split[1].Split(',')) { if (int.TryParse(strPid, out int pid) && pid != 0) _autoclosePids.Add(pid); @@ -86,38 +84,33 @@ namespace Bloxstrap _notifyIcon = new(this); } - public void KillRobloxProcess() => KillProcess(_gameClientPid); + public void KillRobloxProcess() => CloseProcess(_gameClientPid, true); - public void KillProcess(int pid) + public void CloseProcess(int pid, bool force = false) { - using var process = Process.GetProcessById(pid); - - App.Logger.WriteLine("Watcher::KillProcess", $"Killing process '{process.ProcessName}' (PID {process.Id})"); - - if (process.HasExited) + const string LOG_IDENT = "Watcher::CloseProcess"; + try { - App.Logger.WriteLine("Watcher::KillProcess", $"PID {process.Id} has already exited"); - return; + using var process = Process.GetProcessById(pid); + + App.Logger.WriteLine(LOG_IDENT, $"Killing process '{process.ProcessName}' (pid={pid}, force={force})"); + + if (process.HasExited) + { + App.Logger.WriteLine(LOG_IDENT, $"PID {pid} has already exited"); + return; + } + + if (force) + process.Kill(); + else + process.CloseMainWindow(); } - - process.Kill(); - process.Close(); - } - - public void CloseProcess(int pid) - { - using var process = Process.GetProcessById(pid); - - App.Logger.WriteLine("Watcher::CloseProcess", $"Closing process '{process.ProcessName}' (PID {process.Id})"); - - if (process.HasExited) + catch (Exception ex) { - App.Logger.WriteLine("Watcher::CloseProcess", $"PID {process.Id} has already exited"); - return; + App.Logger.WriteLine(LOG_IDENT, $"PID {pid} could not be closed"); + App.Logger.WriteException(LOG_IDENT, ex); } - - process.CloseMainWindow(); - process.Close(); } public async Task Run() From f747f40ca50bcefa20f487e23bc25cdcc733f1f5 Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Fri, 30 Aug 2024 01:40:32 +0100 Subject: [PATCH 4/5] Add setting deeplink launch data from BloxstrapRPC plus a bunch of tweaks to the bootstrapper --- Bloxstrap/Bootstrapper.cs | 25 +-- Bloxstrap/Integrations/ActivityWatcher.cs | 45 ++++- Bloxstrap/Integrations/DiscordRichPresence.cs | 160 ++++++++++-------- .../ContextMenu/MenuContainer.xaml.cs | 2 +- 4 files changed, 144 insertions(+), 88 deletions(-) diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs index 1844cf5..969966b 100644 --- a/Bloxstrap/Bootstrapper.cs +++ b/Bloxstrap/Bootstrapper.cs @@ -281,20 +281,22 @@ namespace Bloxstrap SetStatus(Strings.Bootstrapper_Status_Starting); - if (App.Settings.Prop.ForceRobloxLanguage) + if (_launchMode == LaunchMode.Player) { - var match = Regex.Match(_launchCommandLine, "gameLocale:([a-z_]+)", RegexOptions.CultureInvariant); + if (App.Settings.Prop.ForceRobloxLanguage) + { + var match = Regex.Match(_launchCommandLine, "gameLocale:([a-z_]+)", RegexOptions.CultureInvariant); - if (match.Groups.Count == 2) - _launchCommandLine = _launchCommandLine.Replace("robloxLocale:en_us", $"robloxLocale:{match.Groups[1].Value}", StringComparison.InvariantCultureIgnoreCase); + if (match.Groups.Count == 2) + _launchCommandLine = _launchCommandLine.Replace("robloxLocale:en_us", $"robloxLocale:{match.Groups[1].Value}", StringComparison.InvariantCultureIgnoreCase); + } + + if (!String.IsNullOrEmpty(_launchCommandLine)) + _launchCommandLine += " "; + + _launchCommandLine += "-isInstallerLaunch"; } - // needed for the start event to fire - if (!String.IsNullOrEmpty(_launchCommandLine)) - _launchCommandLine += " "; - - _launchCommandLine += "-isInstallerLaunch"; - var startInfo = new ProcessStartInfo() { FileName = _playerLocation, @@ -308,7 +310,7 @@ namespace Bloxstrap return; } - using var startEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "www.roblox.com/robloxStartedEvent"); + using var startEvent = new EventWaitHandle(false, EventResetMode.ManualReset, AppData.StartEvent); // v2.2.0 - byfron will trip if we keep a process handle open for over a minute, so we're doing this now int gameClientPid; @@ -357,7 +359,6 @@ namespace Bloxstrap autoclosePids.Add(pid); } - string args = gameClientPid.ToString(); if (autoclosePids.Any()) diff --git a/Bloxstrap/Integrations/ActivityWatcher.cs b/Bloxstrap/Integrations/ActivityWatcher.cs index fbca8d1..f1ab955 100644 --- a/Bloxstrap/Integrations/ActivityWatcher.cs +++ b/Bloxstrap/Integrations/ActivityWatcher.cs @@ -1,4 +1,6 @@ -namespace Bloxstrap.Integrations +using System.Web; + +namespace Bloxstrap.Integrations { public class ActivityWatcher : IDisposable { @@ -43,6 +45,7 @@ public string ActivityMachineAddress = ""; public bool ActivityMachineUDMUX = false; public bool ActivityIsTeleport = false; + public string ActivityLaunchData = ""; public ServerType ActivityServerType = ServerType.Public; public bool IsDisposed = false; @@ -119,6 +122,16 @@ } } + public string GetActivityDeeplink() + { + string deeplink = $"roblox://experiences/start?placeId={ActivityPlaceId}&gameInstanceId={ActivityJobId}"; + + if (!String.IsNullOrEmpty(ActivityLaunchData)) + deeplink += "&launchData=" + HttpUtility.UrlEncode(ActivityLaunchData); + + return deeplink; + } + private void ReadLogEntry(string entry) { const string LOG_IDENT = "ActivityWatcher::ReadLogEntry"; @@ -222,6 +235,7 @@ ActivityMachineAddress = ""; ActivityMachineUDMUX = false; ActivityIsTeleport = false; + ActivityLaunchData = ""; ActivityServerType = ServerType.Public; OnGameLeave?.Invoke(this, new EventArgs()); @@ -280,6 +294,35 @@ return; } + if (message.Command == "SetLaunchData") + { + string? data; + + try + { + data = message.Data.Deserialize(); + } + catch (Exception) + { + App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization threw an exception)"); + return; + } + + if (data is null) + { + App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization returned null)"); + return; + } + + if (data.Length > 200) + { + App.Logger.WriteLine(LOG_IDENT, "Data cannot be longer than 200 characters"); + return; + } + + ActivityLaunchData = data; + } + OnRPCMessage?.Invoke(this, message); LastRPCRequest = DateTime.Now; diff --git a/Bloxstrap/Integrations/DiscordRichPresence.cs b/Bloxstrap/Integrations/DiscordRichPresence.cs index 7f30aab..9dfc624 100644 --- a/Bloxstrap/Integrations/DiscordRichPresence.cs +++ b/Bloxstrap/Integrations/DiscordRichPresence.cs @@ -51,7 +51,7 @@ namespace Bloxstrap.Integrations { const string LOG_IDENT = "DiscordRichPresence::ProcessRPCMessage"; - if (message.Command != "SetRichPresence") + if (message.Command != "SetRichPresence" && message.Command != "SetLaunchData") return; if (_currentPresence is null || _currentPresenceCopy is null) @@ -61,95 +61,107 @@ namespace Bloxstrap.Integrations return; } - Models.BloxstrapRPC.RichPresence? presenceData; - // a lot of repeated code here, could this somehow be cleaned up? - try + if (message.Command == "SetLaunchData") { - presenceData = message.Data.Deserialize(); + var buttonQuery = _currentPresence.Buttons.Where(x => x.Label == "Join server"); + + if (!buttonQuery.Any()) + return; + + buttonQuery.First().Url = _activityWatcher.GetActivityDeeplink(); } - catch (Exception) + else if (message.Command == "SetRichPresence") { - App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization threw an exception)"); - return; - } + Models.BloxstrapRPC.RichPresence? presenceData; - if (presenceData is null) - { - App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization returned null)"); - return; - } - - if (presenceData.Details is not null) - { - if (presenceData.Details.Length > 128) - App.Logger.WriteLine(LOG_IDENT, $"Details cannot be longer than 128 characters"); - else if (presenceData.Details == "") - _currentPresence.Details = _currentPresenceCopy.Details; - else - _currentPresence.Details = presenceData.Details; - } - - if (presenceData.State is not null) - { - if (presenceData.State.Length > 128) - App.Logger.WriteLine(LOG_IDENT, $"State cannot be longer than 128 characters"); - else if (presenceData.State == "") - _currentPresence.State = _currentPresenceCopy.State; - else - _currentPresence.State = presenceData.State; - } - - if (presenceData.TimestampStart == 0) - _currentPresence.Timestamps.Start = null; - else if (presenceData.TimestampStart is not null) - _currentPresence.Timestamps.StartUnixMilliseconds = presenceData.TimestampStart * 1000; - - if (presenceData.TimestampEnd == 0) - _currentPresence.Timestamps.End = null; - else if (presenceData.TimestampEnd is not null) - _currentPresence.Timestamps.EndUnixMilliseconds = presenceData.TimestampEnd * 1000; - - if (presenceData.SmallImage is not null) - { - if (presenceData.SmallImage.Clear) + try { - _currentPresence.Assets.SmallImageKey = ""; + presenceData = message.Data.Deserialize(); } - else if (presenceData.SmallImage.Reset) + catch (Exception) { - _currentPresence.Assets.SmallImageText = _currentPresenceCopy.Assets.SmallImageText; - _currentPresence.Assets.SmallImageKey = _currentPresenceCopy.Assets.SmallImageKey; + App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization threw an exception)"); + return; } - else - { - if (presenceData.SmallImage.AssetId is not null) - _currentPresence.Assets.SmallImageKey = $"https://assetdelivery.roblox.com/v1/asset/?id={presenceData.SmallImage.AssetId}"; - if (presenceData.SmallImage.HoverText is not null) - _currentPresence.Assets.SmallImageText = presenceData.SmallImage.HoverText; + if (presenceData is null) + { + App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization returned null)"); + return; } - } - if (presenceData.LargeImage is not null) - { - if (presenceData.LargeImage.Clear) + if (presenceData.Details is not null) { - _currentPresence.Assets.LargeImageKey = ""; + if (presenceData.Details.Length > 128) + App.Logger.WriteLine(LOG_IDENT, $"Details cannot be longer than 128 characters"); + else if (presenceData.Details == "") + _currentPresence.Details = _currentPresenceCopy.Details; + else + _currentPresence.Details = presenceData.Details; } - else if (presenceData.LargeImage.Reset) - { - _currentPresence.Assets.LargeImageText = _currentPresenceCopy.Assets.LargeImageText; - _currentPresence.Assets.LargeImageKey = _currentPresenceCopy.Assets.LargeImageKey; - } - else - { - if (presenceData.LargeImage.AssetId is not null) - _currentPresence.Assets.LargeImageKey = $"https://assetdelivery.roblox.com/v1/asset/?id={presenceData.LargeImage.AssetId}"; - if (presenceData.LargeImage.HoverText is not null) - _currentPresence.Assets.LargeImageText = presenceData.LargeImage.HoverText; + if (presenceData.State is not null) + { + if (presenceData.State.Length > 128) + App.Logger.WriteLine(LOG_IDENT, $"State cannot be longer than 128 characters"); + else if (presenceData.State == "") + _currentPresence.State = _currentPresenceCopy.State; + else + _currentPresence.State = presenceData.State; + } + + if (presenceData.TimestampStart == 0) + _currentPresence.Timestamps.Start = null; + else if (presenceData.TimestampStart is not null) + _currentPresence.Timestamps.StartUnixMilliseconds = presenceData.TimestampStart * 1000; + + if (presenceData.TimestampEnd == 0) + _currentPresence.Timestamps.End = null; + else if (presenceData.TimestampEnd is not null) + _currentPresence.Timestamps.EndUnixMilliseconds = presenceData.TimestampEnd * 1000; + + if (presenceData.SmallImage is not null) + { + if (presenceData.SmallImage.Clear) + { + _currentPresence.Assets.SmallImageKey = ""; + } + else if (presenceData.SmallImage.Reset) + { + _currentPresence.Assets.SmallImageText = _currentPresenceCopy.Assets.SmallImageText; + _currentPresence.Assets.SmallImageKey = _currentPresenceCopy.Assets.SmallImageKey; + } + else + { + if (presenceData.SmallImage.AssetId is not null) + _currentPresence.Assets.SmallImageKey = $"https://assetdelivery.roblox.com/v1/asset/?id={presenceData.SmallImage.AssetId}"; + + if (presenceData.SmallImage.HoverText is not null) + _currentPresence.Assets.SmallImageText = presenceData.SmallImage.HoverText; + } + } + + if (presenceData.LargeImage is not null) + { + if (presenceData.LargeImage.Clear) + { + _currentPresence.Assets.LargeImageKey = ""; + } + else if (presenceData.LargeImage.Reset) + { + _currentPresence.Assets.LargeImageText = _currentPresenceCopy.Assets.LargeImageText; + _currentPresence.Assets.LargeImageKey = _currentPresenceCopy.Assets.LargeImageKey; + } + else + { + if (presenceData.LargeImage.AssetId is not null) + _currentPresence.Assets.LargeImageKey = $"https://assetdelivery.roblox.com/v1/asset/?id={presenceData.LargeImage.AssetId}"; + + if (presenceData.LargeImage.HoverText is not null) + _currentPresence.Assets.LargeImageText = presenceData.LargeImage.HoverText; + } } } diff --git a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs index c4412ba..bd90844 100644 --- a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs +++ b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs @@ -104,7 +104,7 @@ namespace Bloxstrap.UI.Elements.ContextMenu private void RichPresenceMenuItem_Click(object sender, RoutedEventArgs e) => _watcher.RichPresence?.SetVisibility(((MenuItem)sender).IsChecked); - private void InviteDeeplinkMenuItem_Click(object sender, RoutedEventArgs e) => Clipboard.SetDataObject($"roblox://experiences/start?placeId={_activityWatcher?.ActivityPlaceId}&gameInstanceId={_activityWatcher?.ActivityJobId}"); + private void InviteDeeplinkMenuItem_Click(object sender, RoutedEventArgs e) => Clipboard.SetDataObject(_activityWatcher?.GetActivityDeeplink()); private void ServerDetailsMenuItem_Click(object sender, RoutedEventArgs e) => ShowServerInformationWindow(); From 2791cb0b2ed5187b898d7c95cb887acbb329d80a Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Fri, 30 Aug 2024 13:29:51 +0100 Subject: [PATCH 5/5] Refactor automatic updater + fix install details + fix launch flag parser + fix temp directory Automatic updater now relies on the -upgrade flag specifically being set and uses a mutex for coordinating the process Temp directory is now obtained appropriately (should fix exceptions relating to it?) Installation details are now reconfigured on every upgrade Specifying a nonexistant flag would insta-crash the app Also, the message box was making the wrong sound for the warning icon --- Bloxstrap/App.xaml.cs | 27 ++++- Bloxstrap/Bootstrapper.cs | 112 ++++++++++++------ Bloxstrap/Installer.cs | 60 +++++----- Bloxstrap/LaunchSettings.cs | 6 +- Bloxstrap/Paths.cs | 4 + Bloxstrap/Resources/Strings.Designer.cs | 2 +- Bloxstrap/Resources/Strings.resx | 2 +- .../Elements/Dialogs/FluentMessageBox.xaml.cs | 2 +- .../ViewModels/Dialogs/LaunchMenuViewModel.cs | 1 - .../ViewModels/Installer/WelcomeViewModel.cs | 27 +---- 10 files changed, 144 insertions(+), 99 deletions(-) diff --git a/Bloxstrap/App.xaml.cs b/Bloxstrap/App.xaml.cs index 695a0f1..3a02b2e 100644 --- a/Bloxstrap/App.xaml.cs +++ b/Bloxstrap/App.xaml.cs @@ -15,7 +15,11 @@ namespace Bloxstrap public partial class App : Application { public const string ProjectName = "Bloxstrap"; + public const string ProjectOwner = "pizzaboxer"; public const string ProjectRepository = "pizzaboxer/bloxstrap"; + public const string ProjectDownloadLink = "https://bloxstrap.pizzaboxer.xyz"; + public const string ProjectHelpLink = "https://github.com/pizzaboxer/bloxstrap/wiki"; + public const string ProjectSupportLink = "https://github.com/pizzaboxer/bloxstrap/issues/new"; public const string RobloxPlayerAppName = "RobloxPlayerBeta"; public const string RobloxStudioAppName = "RobloxStudioBeta"; @@ -103,6 +107,27 @@ namespace Bloxstrap Terminate(ErrorCode.ERROR_INSTALL_FAILURE); } + public static async Task GetLatestRelease() + { + const string LOG_IDENT = "App::GetLatestRelease"; + + GithubRelease? releaseInfo = null; + + try + { + releaseInfo = await Http.GetJson($"https://api.github.com/repos/{ProjectRepository}/releases/latest"); + + if (releaseInfo is null || releaseInfo.Assets is null) + Logger.WriteLine(LOG_IDENT, "Encountered invalid data"); + } + catch (Exception ex) + { + Logger.WriteException(LOG_IDENT, ex); + } + + return releaseInfo; + } + protected override void OnStartup(StartupEventArgs e) { const string LOG_IDENT = "App::OnStartup"; @@ -221,7 +246,7 @@ namespace Bloxstrap Locale.Set(Settings.Prop.Locale); #if !DEBUG - if (!LaunchSettings.UninstallFlag.Active) + if (!LaunchSettings.BypassUpdateCheck) Installer.HandleUpgrade(); #endif diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs index 969966b..16c7c84 100644 --- a/Bloxstrap/Bootstrapper.cs +++ b/Bloxstrap/Bootstrapper.cs @@ -1,4 +1,17 @@ -using System.Windows; +// To debug the automatic updater: +// - Uncomment the definition below +// - Publish the executable +// - Launch the executable (click no when it asks you to upgrade) +// - Launch Roblox (for testing web launches, run it from the command prompt) +// - To re-test the same executable, delete it from the installation folder + +// #define DEBUG_UPDATER + +#if DEBUG_UPDATER +#warning "Automatic updater debugging is enabled" +#endif + +using System.Windows; using System.Windows.Forms; using Microsoft.Win32; @@ -152,9 +165,14 @@ namespace Bloxstrap await RobloxDeployment.GetInfo(RobloxDeployment.DefaultChannel); -#if !DEBUG - if (App.Settings.Prop.CheckForUpdates) - await CheckForUpdates(); +#if !DEBUG || DEBUG_UPDATER + if (App.Settings.Prop.CheckForUpdates && !App.LaunchSettings.UpgradeFlag.Active) + { + bool updatePresent = await CheckForUpdates(); + + if (updatePresent) + return; + } #endif // ensure only one instance of the bootstrapper is running at the time @@ -405,7 +423,7 @@ namespace Bloxstrap App.Terminate(ErrorCode.ERROR_CANCELLED); } - #endregion +#endregion #region App Install public void RegisterProgramSize() @@ -449,53 +467,56 @@ namespace Bloxstrap #endif } - private async Task CheckForUpdates() + private async Task CheckForUpdates() { const string LOG_IDENT = "Bootstrapper::CheckForUpdates"; // don't update if there's another instance running (likely running in the background) - if (Process.GetProcessesByName(App.ProjectName).Count() > 1) + // i don't like this, but there isn't much better way of doing it /shrug + if (Process.GetProcessesByName(App.ProjectName).Length > 1) { App.Logger.WriteLine(LOG_IDENT, $"More than one Bloxstrap instance running, aborting update check"); - return; + return false; } - App.Logger.WriteLine(LOG_IDENT, $"Checking for updates..."); + App.Logger.WriteLine(LOG_IDENT, "Checking for updates..."); - GithubRelease? releaseInfo; +#if !DEBUG_UPDATER + var releaseInfo = await App.GetLatestRelease(); - try - { - releaseInfo = await Http.GetJson($"https://api.github.com/repos/{App.ProjectRepository}/releases/latest"); - } - catch (Exception ex) - { - App.Logger.WriteLine(LOG_IDENT, $"Failed to fetch releases: {ex}"); - return; - } - - if (releaseInfo is null || releaseInfo.Assets is null) - { - App.Logger.WriteLine(LOG_IDENT, $"No updates found"); - return; - } + if (releaseInfo is null) + return false; var versionComparison = Utilities.CompareVersions(App.Version, releaseInfo.TagName); // check if we aren't using a deployed build, so we can update to one if a new version comes out - if (versionComparison == VersionComparison.Equal && App.IsProductionBuild || versionComparison == VersionComparison.GreaterThan) + if (App.IsProductionBuild && versionComparison == VersionComparison.Equal || versionComparison == VersionComparison.GreaterThan) { - App.Logger.WriteLine(LOG_IDENT, $"No updates found"); - return; + App.Logger.WriteLine(LOG_IDENT, "No updates found"); + return false; } + string version = releaseInfo.TagName; +#else + string version = App.Version; +#endif + SetStatus(Strings.Bootstrapper_Status_UpgradingBloxstrap); - + try { - // 64-bit is always the first option - GithubReleaseAsset asset = releaseInfo.Assets[0]; - string downloadLocation = Path.Combine(Paths.LocalAppData, "Temp", asset.Name); +#if DEBUG_UPDATER + string downloadLocation = Path.Combine(Paths.TempUpdates, "Bloxstrap.exe"); + + Directory.CreateDirectory(Paths.TempUpdates); + + File.Copy(Paths.Process, downloadLocation, true); +#else + var asset = releaseInfo.Assets![0]; + + string downloadLocation = Path.Combine(Paths.TempUpdates, asset.Name); + + Directory.CreateDirectory(Paths.TempUpdates); App.Logger.WriteLine(LOG_IDENT, $"Downloading {releaseInfo.TagName}..."); @@ -503,25 +524,35 @@ namespace Bloxstrap { var response = await App.HttpClient.GetAsync(asset.BrowserDownloadUrl); - await using var fileStream = new FileStream(downloadLocation, FileMode.CreateNew); + await using var fileStream = new FileStream(downloadLocation, FileMode.OpenOrCreate, FileAccess.Write); await response.Content.CopyToAsync(fileStream); } +#endif - App.Logger.WriteLine(LOG_IDENT, $"Starting {releaseInfo.TagName}..."); + App.Logger.WriteLine(LOG_IDENT, $"Starting {version}..."); ProcessStartInfo startInfo = new() { FileName = downloadLocation, }; + startInfo.ArgumentList.Add("-upgrade"); + foreach (string arg in App.LaunchSettings.Args) startInfo.ArgumentList.Add(arg); - + + if (_launchMode == LaunchMode.Player && !startInfo.ArgumentList.Contains("-player")) + startInfo.ArgumentList.Add("-player"); + else if (_launchMode == LaunchMode.Studio && !startInfo.ArgumentList.Contains("-studio")) + startInfo.ArgumentList.Add("-studio"); + App.Settings.Save(); + + new InterProcessLock("AutoUpdater"); Process.Start(startInfo); - App.Terminate(); + return true; } catch (Exception ex) { @@ -529,10 +560,14 @@ namespace Bloxstrap App.Logger.WriteException(LOG_IDENT, ex); Frontend.ShowMessageBox( - string.Format(Strings.Bootstrapper_AutoUpdateFailed, releaseInfo.TagName), + string.Format(Strings.Bootstrapper_AutoUpdateFailed, version), MessageBoxImage.Information ); + + Utilities.ShellExecute(App.ProjectDownloadLink); } + + return false; } #endregion @@ -851,6 +886,7 @@ namespace Bloxstrap foreach (FontFace fontFace in fontFamilyData.Faces) fontFace.AssetId = "rbxasset://fonts/CustomFont.ttf"; + // TODO: writing on every launch is not necessary File.WriteAllText(modFilepath, JsonSerializer.Serialize(fontFamilyData, new JsonSerializerOptions { WriteIndented = true })); } @@ -902,6 +938,8 @@ namespace Bloxstrap // the manifest is primarily here to keep track of what files have been // deleted from the modifications folder, so that we know when to restore the original files from the downloaded packages // now check for files that have been deleted from the mod folder according to the manifest + + // TODO: this needs to extract the files from packages in bulk, this is way too slow foreach (string fileLocation in App.State.Prop.ModManifest) { if (modFolderFiles.Contains(fileLocation)) diff --git a/Bloxstrap/Installer.cs b/Bloxstrap/Installer.cs index 849a834..2e841fd 100644 --- a/Bloxstrap/Installer.cs +++ b/Bloxstrap/Installer.cs @@ -1,9 +1,4 @@ -using System.DirectoryServices; -using System.Reflection; -using System.Reflection.Metadata.Ecma335; -using System.Windows; -using System.Windows.Media.Animation; -using Bloxstrap.Resources; +using System.Windows; using Microsoft.Win32; namespace Bloxstrap @@ -50,12 +45,13 @@ namespace Bloxstrap uninstallKey.SetValue("InstallLocation", Paths.Base); uninstallKey.SetValue("NoRepair", 1); - uninstallKey.SetValue("Publisher", "pizzaboxer"); + uninstallKey.SetValue("Publisher", App.ProjectOwner); uninstallKey.SetValue("ModifyPath", $"\"{Paths.Application}\" -settings"); uninstallKey.SetValue("QuietUninstallString", $"\"{Paths.Application}\" -uninstall -quiet"); uninstallKey.SetValue("UninstallString", $"\"{Paths.Application}\" -uninstall"); - uninstallKey.SetValue("URLInfoAbout", $"https://github.com/{App.ProjectRepository}"); - uninstallKey.SetValue("URLUpdateInfo", $"https://github.com/{App.ProjectRepository}/releases/latest"); + uninstallKey.SetValue("HelpLink", App.ProjectHelpLink); + uninstallKey.SetValue("URLInfoAbout", App.ProjectSupportLink); + uninstallKey.SetValue("URLUpdateInfo", App.ProjectDownloadLink); } // only register player, for the scenario where the user installs bloxstrap, closes it, @@ -331,8 +327,9 @@ namespace Bloxstrap return; // 2.0.0 downloads updates to /Updates so lol - // TODO: 2.8.0 will download them to /Bloxstrap/Updates - bool isAutoUpgrade = Paths.Process.StartsWith(Path.Combine(Paths.Base, "Updates")) || Paths.Process.StartsWith(Path.Combine(Paths.LocalAppData, "Temp")); + bool isAutoUpgrade = App.LaunchSettings.UpgradeFlag.Active + || Paths.Process.StartsWith(Path.Combine(Paths.Base, "Updates")) + || Paths.Process.StartsWith(Paths.Temp); var existingVer = FileVersionInfo.GetVersionInfo(Paths.Application).ProductVersion; var currentVer = FileVersionInfo.GetVersionInfo(Paths.Process).ProductVersion; @@ -353,7 +350,7 @@ namespace Bloxstrap } // silently upgrade version if the command line flag is set or if we're launching from an auto update - if (!App.LaunchSettings.UpgradeFlag.Active && !isAutoUpgrade) + if (!isAutoUpgrade) { var result = Frontend.ShowMessageBox( Strings.InstallChecker_VersionDifferentThanInstalled, @@ -365,41 +362,38 @@ namespace Bloxstrap return; } + App.Logger.WriteLine(LOG_IDENT, "Doing upgrade"); + Filesystem.AssertReadOnly(Paths.Application); - // TODO: make this use a mutex somehow - // yes, this is EXTREMELY hacky, but the updater process that launched the - // new version may still be open and so we have to wait for it to close - int attempts = 0; - while (attempts < 10) + using (var ipl = new InterProcessLock("AutoUpdater", TimeSpan.FromSeconds(5))) { - attempts++; - - try + if (!ipl.IsAcquired) { - File.Delete(Paths.Application); - break; - } - catch (Exception) - { - if (attempts == 1) - App.Logger.WriteLine(LOG_IDENT, "Waiting for write permissions to update version"); - - Thread.Sleep(500); + App.Logger.WriteLine(LOG_IDENT, "Failed to update! (Could not obtain singleton mutex)"); + return; } } - if (attempts == 10) + try { - App.Logger.WriteLine(LOG_IDENT, "Failed to update! (Could not get write permissions after 5 seconds)"); + File.Copy(Paths.Process, Paths.Application, true); + } + catch (Exception ex) + { + App.Logger.WriteLine(LOG_IDENT, "Failed to update! (Could not replace executable)"); + App.Logger.WriteException(LOG_IDENT, ex); return; } - File.Copy(Paths.Process, Paths.Application); - using (var uninstallKey = Registry.CurrentUser.CreateSubKey(App.UninstallKey)) { uninstallKey.SetValue("DisplayVersion", App.Version); + + uninstallKey.SetValue("Publisher", App.ProjectOwner); + uninstallKey.SetValue("HelpLink", App.ProjectHelpLink); + uninstallKey.SetValue("URLInfoAbout", App.ProjectSupportLink); + uninstallKey.SetValue("URLUpdateInfo", App.ProjectDownloadLink); } // update migrations diff --git a/Bloxstrap/LaunchSettings.cs b/Bloxstrap/LaunchSettings.cs index 046fd9a..25a24fa 100644 --- a/Bloxstrap/LaunchSettings.cs +++ b/Bloxstrap/LaunchSettings.cs @@ -28,6 +28,8 @@ namespace Bloxstrap public LaunchFlag StudioFlag { get; } = new("studio"); + public bool BypassUpdateCheck => UninstallFlag.Active || WatcherFlag.Active; + public LaunchMode RobloxLaunchMode { get; set; } = LaunchMode.None; public string RobloxLaunchArgs { get; private set; } = ""; @@ -37,7 +39,7 @@ namespace Bloxstrap /// public string[] Args { get; private set; } - private Dictionary _flagMap = new(); + private readonly Dictionary _flagMap = new(); public LaunchSettings(string[] args) { @@ -68,7 +70,7 @@ namespace Bloxstrap string identifier = arg[1..]; - if (_flagMap[identifier] is not LaunchFlag flag) + if (!_flagMap.TryGetValue(identifier, out LaunchFlag? flag) || flag is null) continue; flag.Active = true; diff --git a/Bloxstrap/Paths.cs b/Bloxstrap/Paths.cs index cd04cc8..43d4a1c 100644 --- a/Bloxstrap/Paths.cs +++ b/Bloxstrap/Paths.cs @@ -4,6 +4,7 @@ { // note that these are directories that aren't tethered to the basedirectory // so these can safely be called before initialization + public static string Temp => Path.Combine(Path.GetTempPath(), App.ProjectName); public static string UserProfile => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); public static string LocalAppData => Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); public static string Desktop => Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory); @@ -12,6 +13,9 @@ public static string Process => Environment.ProcessPath!; + public static string TempUpdates => Path.Combine(Temp, "Updates"); + public static string TempLogs => Path.Combine(Temp, "Logs"); + public static string Base { get; private set; } = ""; public static string Downloads { get; private set; } = ""; public static string Logs { get; private set; } = ""; diff --git a/Bloxstrap/Resources/Strings.Designer.cs b/Bloxstrap/Resources/Strings.Designer.cs index 28c8fc2..0b754ca 100644 --- a/Bloxstrap/Resources/Strings.Designer.cs +++ b/Bloxstrap/Resources/Strings.Designer.cs @@ -106,7 +106,7 @@ namespace Bloxstrap.Resources { } /// - /// Looks up a localized string similar to Bloxstrap was unable to auto-update to {0}. Please update it manually by downloading and running the latest release from the GitHub page.. + /// Looks up a localized string similar to Bloxstrap was unable to automatically update to version {0}. Please update it manually by downloading and running it from the website.. /// public static string Bootstrapper_AutoUpdateFailed { get { diff --git a/Bloxstrap/Resources/Strings.resx b/Bloxstrap/Resources/Strings.resx index 6f57c25..8f12701 100644 --- a/Bloxstrap/Resources/Strings.resx +++ b/Bloxstrap/Resources/Strings.resx @@ -124,7 +124,7 @@ lookup failed - Bloxstrap was unable to auto-update to {0}. Please update it manually by downloading and running the latest release from the GitHub page. + Bloxstrap was unable to automatically update to version {0}. Please update it manually by downloading and running it from the website. Roblox is currently running, and launching another instance will close it. Are you sure you want to continue launching? diff --git a/Bloxstrap/UI/Elements/Dialogs/FluentMessageBox.xaml.cs b/Bloxstrap/UI/Elements/Dialogs/FluentMessageBox.xaml.cs index 30d5dfc..47a4c39 100644 --- a/Bloxstrap/UI/Elements/Dialogs/FluentMessageBox.xaml.cs +++ b/Bloxstrap/UI/Elements/Dialogs/FluentMessageBox.xaml.cs @@ -41,7 +41,7 @@ namespace Bloxstrap.UI.Elements.Dialogs case MessageBoxImage.Warning: iconFilename = "Warning"; - sound = SystemSounds.Asterisk; + sound = SystemSounds.Exclamation; break; case MessageBoxImage.Information: diff --git a/Bloxstrap/UI/ViewModels/Dialogs/LaunchMenuViewModel.cs b/Bloxstrap/UI/ViewModels/Dialogs/LaunchMenuViewModel.cs index 0680998..25962a9 100644 --- a/Bloxstrap/UI/ViewModels/Dialogs/LaunchMenuViewModel.cs +++ b/Bloxstrap/UI/ViewModels/Dialogs/LaunchMenuViewModel.cs @@ -5,7 +5,6 @@ using Bloxstrap.UI.Elements.About; namespace Bloxstrap.UI.ViewModels.Installer { - // TODO: have it so it shows "Launch Roblox"/"Install and Launch Roblox" depending on state of /App/ folder public class LaunchMenuViewModel { public string Version => string.Format(Strings.Menu_About_Version, App.Version); diff --git a/Bloxstrap/UI/ViewModels/Installer/WelcomeViewModel.cs b/Bloxstrap/UI/ViewModels/Installer/WelcomeViewModel.cs index 5ba85f3..a903659 100644 --- a/Bloxstrap/UI/ViewModels/Installer/WelcomeViewModel.cs +++ b/Bloxstrap/UI/ViewModels/Installer/WelcomeViewModel.cs @@ -19,32 +19,15 @@ // called by codebehind on page load public async void DoChecks() { - const string LOG_IDENT = "WelcomeViewModel::DoChecks"; + var releaseInfo = await App.GetLatestRelease(); - // TODO: move into unified function that bootstrapper can use too - GithubRelease? releaseInfo = null; - - try + if (releaseInfo is not null) { - releaseInfo = await Http.GetJson($"https://api.github.com/repos/{App.ProjectRepository}/releases/latest"); - - if (releaseInfo is null || releaseInfo.Assets is null) + if (Utilities.CompareVersions(App.Version, releaseInfo.TagName) == VersionComparison.LessThan) { - App.Logger.WriteLine(LOG_IDENT, $"Encountered invalid data when fetching GitHub releases"); + VersionNotice = String.Format(Strings.Installer_Welcome_UpdateNotice, App.Version, releaseInfo.TagName.Replace("v", "")); + OnPropertyChanged(nameof(VersionNotice)); } - else - { - if (Utilities.CompareVersions(App.Version, releaseInfo.TagName) == VersionComparison.LessThan) - { - VersionNotice = String.Format(Resources.Strings.Installer_Welcome_UpdateNotice, App.Version, releaseInfo.TagName.Replace("v", "")); - OnPropertyChanged(nameof(VersionNotice)); - } - } - } - catch (Exception ex) - { - App.Logger.WriteLine(LOG_IDENT, $"Error occurred when fetching GitHub releases"); - App.Logger.WriteException(LOG_IDENT, ex); } CanContinue = true;