From 606aadda9740861cc024138670ec760022ea863c Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Sat, 29 Apr 2023 23:55:43 +0100 Subject: [PATCH] Turn DeployManager from singleton to helper this really did not need to be oop lol --- Bloxstrap/App.xaml.cs | 5 - Bloxstrap/Bootstrapper.cs | 54 ++++- Bloxstrap/Deployment.cs | 139 +++++++++++++ Bloxstrap/Models/PackageManifest.cs | 2 +- Bloxstrap/Models/Settings.cs | 3 +- Bloxstrap/{ResourceHelper.cs => Resource.cs} | 2 +- Bloxstrap/Singletons/DeployManager.cs | 194 ------------------ Bloxstrap/ViewModels/GlobalViewModel.cs | 1 - Bloxstrap/ViewModels/InstallationViewModel.cs | 19 +- Bloxstrap/Views/Pages/InstallationPage.xaml | 2 +- 10 files changed, 203 insertions(+), 218 deletions(-) create mode 100644 Bloxstrap/Deployment.cs rename Bloxstrap/{ResourceHelper.cs => Resource.cs} (95%) delete mode 100644 Bloxstrap/Singletons/DeployManager.cs diff --git a/Bloxstrap/App.xaml.cs b/Bloxstrap/App.xaml.cs index 2397ab0..b9b31c2 100644 --- a/Bloxstrap/App.xaml.cs +++ b/Bloxstrap/App.xaml.cs @@ -47,7 +47,6 @@ namespace Bloxstrap // singletons public static readonly Logger Logger = new(); - public static readonly DeployManager DeployManager = new(); public static readonly JsonManager Settings = new(); public static readonly JsonManager State = new(); public static readonly FastFlagManager FastFlags = new(); @@ -259,11 +258,7 @@ namespace Bloxstrap { if (!IsFirstRun) ShouldSaveConfigs = true; - - DeployManager.Channel = Settings.Prop.Channel; - DeployManager.CheckReleaseChannel().Wait(); - // start bootstrapper and show the bootstrapper modal if we're not running silently Logger.WriteLine($"[App::OnStartup] Initializing bootstrapper"); Bootstrapper bootstrapper = new(commandLine); diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs index 7361a18..6f88bae 100644 --- a/Bloxstrap/Bootstrapper.cs +++ b/Bloxstrap/Bootstrapper.cs @@ -15,7 +15,6 @@ using Microsoft.Win32; using Bloxstrap.Dialogs; using Bloxstrap.Integrations; using Bloxstrap.Models; -using Bloxstrap.Singletons; using Bloxstrap.Tools; namespace Bloxstrap @@ -223,7 +222,52 @@ namespace Bloxstrap { SetStatus("Connecting to Roblox..."); - ClientVersion clientVersion = await App.DeployManager.GetLastDeploy(); + ClientVersion clientVersion = await Deployment.GetInfo(App.Settings.Prop.Channel); + + // briefly check if current channel is suitable to use + if (App.Settings.Prop.Channel.ToLower() != Deployment.DefaultChannel.ToLower()) + { + string? switchDefaultPrompt = null; + ClientVersion? defaultChannelInfo = null; + + App.Logger.WriteLine($"[Bootstrapper::CheckLatestVersion] Checking if current channel is suitable to use..."); + + if (App.Settings.Prop.UseReShade) + { + string manifest = await App.HttpClient.GetStringAsync(Deployment.GetLocation($"/{clientVersion.VersionGuid}-rbxManifest.txt")); + + if (manifest.Contains("RobloxPlayerBeta.dll")) + switchDefaultPrompt = $"You currently have ReShade enabled, however your current preferred channel ({App.Settings.Prop.Channel}) does not support ReShade. Would you like to switch to {Deployment.DefaultChannel}?"; + } + + if (String.IsNullOrEmpty(switchDefaultPrompt)) + { + // this SUCKS + defaultChannelInfo = await Deployment.GetInfo(Deployment.DefaultChannel); + int defaultChannelVersion = Int32.Parse(defaultChannelInfo.Version.Split('.')[1]); + int currentChannelVersion = Int32.Parse(clientVersion.Version.Split('.')[1]); + + if (currentChannelVersion < defaultChannelVersion) + switchDefaultPrompt = $"Your current preferred channel ({App.Settings.Prop.Channel}) appears to no longer be receiving updates. Would you like to switch to {Deployment.DefaultChannel}?"; + } + + if (!String.IsNullOrEmpty(switchDefaultPrompt)) + { + MessageBoxResult result = !App.Settings.Prop.PromptChannelChange ? MessageBoxResult.Yes : App.ShowMessageBox(switchDefaultPrompt, MessageBoxImage.Question, MessageBoxButton.YesNo); + + if (result == MessageBoxResult.Yes) + { + App.Settings.Prop.Channel = Deployment.DefaultChannel; + App.Logger.WriteLine($"[DeployManager::SwitchToDefault] Changed Roblox release channel from {App.Settings.Prop.Channel} to {Deployment.DefaultChannel}"); + + if (defaultChannelInfo is null) + defaultChannelInfo = await Deployment.GetInfo(Deployment.DefaultChannel); + + clientVersion = defaultChannelInfo; + } + } + } + _latestVersionGuid = clientVersion.VersionGuid; _versionFolder = Path.Combine(Directories.Versions, _latestVersionGuid); _versionPackageManifest = await PackageManifest.Get(_latestVersionGuid); @@ -242,7 +286,7 @@ namespace Bloxstrap _launchCommandLine = _launchCommandLine.Replace("LAUNCHTIMEPLACEHOLDER", DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString()); - if (App.Settings.Prop.Channel.ToLower() != DeployManager.DefaultChannel.ToLower()) + if (App.Settings.Prop.Channel.ToLower() != Deployment.DefaultChannel.ToLower()) _launchCommandLine += " -channel " + App.Settings.Prop.Channel.ToLower(); // whether we should wait for roblox to exit to handle stuff in the background or clean up after roblox closes @@ -936,7 +980,7 @@ namespace Bloxstrap private static async Task CheckModPreset(bool condition, string location, string name) { string modFolderLocation = Path.Combine(Directories.Modifications, location); - byte[] binaryData = string.IsNullOrEmpty(name) ? Array.Empty() : await ResourceHelper.Get(name); + byte[] binaryData = string.IsNullOrEmpty(name) ? Array.Empty() : await Resource.Get(name); if (condition) { @@ -963,7 +1007,7 @@ namespace Bloxstrap if (_cancelFired) return; - string packageUrl = $"{App.DeployManager.BaseUrl}/{_latestVersionGuid}-{package.Name}"; + string packageUrl = Deployment.GetLocation($"/{_latestVersionGuid}-{package.Name}"); string packageLocation = Path.Combine(Directories.Downloads, package.Signature); string robloxPackageLocation = Path.Combine(Directories.LocalAppData, "Roblox", "Downloads", package.Signature); diff --git a/Bloxstrap/Deployment.cs b/Bloxstrap/Deployment.cs new file mode 100644 index 0000000..c46d618 --- /dev/null +++ b/Bloxstrap/Deployment.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Tasks; + +using Bloxstrap.Models; + +namespace Bloxstrap +{ + public static class Deployment + { + #region Properties + public const string DefaultChannel = "LIVE"; + + // a list of roblox delpoyment locations that we check for, in case one of them don't work + private static List BaseUrls = new() + { + "https://setup.rbxcdn.com", + "https://setup-ak.rbxcdn.com", + "https://s3.amazonaws.com/setup.roblox.com" + }; + + private static string? _baseUrl = null; + + public static string BaseUrl + { + get + { + if (string.IsNullOrEmpty(_baseUrl)) + { + // check for a working accessible deployment domain + foreach (string attemptedUrl in BaseUrls) + { + App.Logger.WriteLine($"[DeployManager::DefaultBaseUrl.Set] Testing connection to '{attemptedUrl}'..."); + + try + { + App.HttpClient.GetAsync($"{attemptedUrl}/version").Wait(); + App.Logger.WriteLine($"[DeployManager::DefaultBaseUrl.Set] Connection successful!"); + _baseUrl = attemptedUrl; + break; + } + catch (Exception ex) + { + App.Logger.WriteLine($"[DeployManager::DefaultBaseUrl.Set] Connection failed!"); + App.Logger.WriteLine($"[DeployManager::DefaultBaseUrl.Set] {ex}"); + continue; + } + } + + if (string.IsNullOrEmpty(_baseUrl)) + throw new Exception("Unable to find an accessible Roblox deploy mirror!"); + } + + return _baseUrl; + } + } + + // most commonly used/interesting channels + public static readonly List SelectableChannels = new() + { + "LIVE", + "ZWinPlayer64", + "ZFlag", + "ZNext", + "ZCanary", + "ZIntegration", + "ZAvatarTeam", + "ZSocialTeam" + }; + #endregion + + public static string GetLocation(string resource, string? channel = null) + { + if (string.IsNullOrEmpty(channel)) + channel = App.Settings.Prop.Channel; + + string location = BaseUrl; + + if (channel.ToLower() != DefaultChannel.ToLower()) + location += $"/channel/{channel.ToLower()}"; + + location += resource; + + return location; + } + + public static async Task GetInfo(string channel, bool timestamp = false) + { + App.Logger.WriteLine($"[DeployManager::GetInfo] Getting deploy info for channel {channel} (timestamp={timestamp})"); + + HttpResponseMessage deployInfoResponse = await App.HttpClient.GetAsync($"https://clientsettingscdn.roblox.com/v2/client-version/WindowsPlayer/channel/{channel}"); + + string rawResponse = await deployInfoResponse.Content.ReadAsStringAsync(); + + if (!deployInfoResponse.IsSuccessStatusCode) + { + // 400 = Invalid binaryType. + // 404 = Could not find version details for binaryType. + // 500 = Error while fetching version information. + // either way, we throw + + App.Logger.WriteLine( + "[DeployManager::GetInfo] Failed to fetch deploy info!\r\n" + + $"\tStatus code: {deployInfoResponse.StatusCode}\r\n" + + $"\tResponse: {rawResponse}" + ); + + throw new Exception($"Could not get latest deploy for channel {channel}! (HTTP {deployInfoResponse.StatusCode})"); + } + + App.Logger.WriteLine($"[DeployManager::GetInfo] Got JSON: {rawResponse}"); + + ClientVersion clientVersion = JsonSerializer.Deserialize(rawResponse)!; + + // for preferences + if (timestamp) + { + App.Logger.WriteLine("[DeployManager::GetInfo] Getting timestamp..."); + + string manifestUrl = GetLocation($"/{clientVersion.VersionGuid}-rbxPkgManifest.txt", channel); + + // get an approximate deploy time from rbxpkgmanifest's last modified date + HttpResponseMessage pkgResponse = await App.HttpClient.GetAsync(manifestUrl); + + if (pkgResponse.Content.Headers.TryGetValues("last-modified", out var values)) + { + string lastModified = values.First(); + App.Logger.WriteLine($"[DeployManager::GetInfo] {manifestUrl} - Last-Modified: {lastModified}"); + clientVersion.Timestamp = DateTime.Parse(lastModified).ToLocalTime(); + } + } + + return clientVersion; + } + } +} diff --git a/Bloxstrap/Models/PackageManifest.cs b/Bloxstrap/Models/PackageManifest.cs index 6380277..4f1dde9 100644 --- a/Bloxstrap/Models/PackageManifest.cs +++ b/Bloxstrap/Models/PackageManifest.cs @@ -54,7 +54,7 @@ namespace Bloxstrap.Models public static async Task Get(string versionGuid) { - string pkgManifestUrl = $"{App.DeployManager.BaseUrl}/{versionGuid}-rbxPkgManifest.txt"; + string pkgManifestUrl = Deployment.GetLocation($"/{versionGuid}-rbxPkgManifest.txt"); var pkgManifestData = await App.HttpClient.GetStringAsync(pkgManifestUrl); return new PackageManifest(pkgManifestData); diff --git a/Bloxstrap/Models/Settings.cs b/Bloxstrap/Models/Settings.cs index 8d066bd..7d4b966 100644 --- a/Bloxstrap/Models/Settings.cs +++ b/Bloxstrap/Models/Settings.cs @@ -1,7 +1,6 @@ using System.Collections.ObjectModel; using Bloxstrap.Enums; -using Bloxstrap.Singletons; namespace Bloxstrap.Models { @@ -18,7 +17,7 @@ namespace Bloxstrap.Models public bool MultiInstanceLaunching { get; set; } = false; // channel configuration - public string Channel { get; set; } = DeployManager.DefaultChannel; + public string Channel { get; set; } = Deployment.DefaultChannel; public bool PromptChannelChange { get; set; } = false; // integration configuration diff --git a/Bloxstrap/ResourceHelper.cs b/Bloxstrap/Resource.cs similarity index 95% rename from Bloxstrap/ResourceHelper.cs rename to Bloxstrap/Resource.cs index 1cd5273..a5a89bb 100644 --- a/Bloxstrap/ResourceHelper.cs +++ b/Bloxstrap/Resource.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; namespace Bloxstrap { - static class ResourceHelper + static class Resource { static readonly Assembly assembly = Assembly.GetExecutingAssembly(); static readonly string[] resourceNames = assembly.GetManifestResourceNames(); diff --git a/Bloxstrap/Singletons/DeployManager.cs b/Bloxstrap/Singletons/DeployManager.cs deleted file mode 100644 index f42d4c9..0000000 --- a/Bloxstrap/Singletons/DeployManager.cs +++ /dev/null @@ -1,194 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Text.Json; -using System.Threading.Tasks; -using System.Windows; - -using Bloxstrap.Models; - -namespace Bloxstrap.Singletons -{ - // TODO - make this functional and into a helper instead of a singleton, this really doesn't need to be OOP - - public class DeployManager - { - #region Properties - public const string DefaultChannel = "LIVE"; - - public string Channel = DefaultChannel; - - // a list of roblox delpoyment locations that we check for, in case one of them don't work - private List BaseUrls = new() - { - "https://setup.rbxcdn.com", - "https://setup-ak.rbxcdn.com", - "https://s3.amazonaws.com/setup.roblox.com" - }; - - private string? _baseUrl = null; - - public string BaseUrl - { - get - { - if (string.IsNullOrEmpty(_baseUrl)) - { - // check for a working accessible deployment domain - foreach (string attemptedUrl in BaseUrls) - { - App.Logger.WriteLine($"[DeployManager::DefaultBaseUrl.Set] Testing connection to '{attemptedUrl}'..."); - - try - { - App.HttpClient.GetAsync($"{attemptedUrl}/version").Wait(); - App.Logger.WriteLine($"[DeployManager::DefaultBaseUrl.Set] Connection successful!"); - _baseUrl = attemptedUrl; - break; - } - catch (Exception ex) - { - App.Logger.WriteLine($"[DeployManager::DefaultBaseUrl.Set] Connection failed!"); - App.Logger.WriteLine($"[DeployManager::DefaultBaseUrl.Set] {ex}"); - continue; - } - } - - if (string.IsNullOrEmpty(_baseUrl)) - throw new Exception("Unable to find an accessible Roblox deploy mirror!"); - } - - if (Channel == DefaultChannel) - return _baseUrl; - else - return $"{_baseUrl}/channel/{Channel.ToLower()}"; - } - } - - // most commonly used/interesting channels - public static readonly List SelectableChannels = new() - { - "LIVE", - "ZWinPlayer64", - "ZFlag", - "ZNext", - "ZCanary", - "ZIntegration", - "ZAvatarTeam", - "ZSocialTeam" - }; - #endregion - - public async Task GetLastDeploy(bool timestamp = false) - { - App.Logger.WriteLine($"[DeployManager::GetLastDeploy] Getting deploy info for channel {Channel} (timestamp={timestamp})"); - - HttpResponseMessage deployInfoResponse = await App.HttpClient.GetAsync($"https://clientsettingscdn.roblox.com/v2/client-version/WindowsPlayer/channel/{Channel}").ConfigureAwait(false); - - string rawResponse = await deployInfoResponse.Content.ReadAsStringAsync(); - - if (!deployInfoResponse.IsSuccessStatusCode) - { - // 400 = Invalid binaryType. - // 404 = Could not find version details for binaryType. - // 500 = Error while fetching version information. - // either way, we throw - - App.Logger.WriteLine( - "[DeployManager::GetLastDeploy] Failed to fetch deploy info!\r\n" + - $"\tStatus code: {deployInfoResponse.StatusCode}\r\n" + - $"\tResponse: {rawResponse}" - ); - - throw new Exception($"Could not get latest deploy for channel {Channel}! (HTTP {deployInfoResponse.StatusCode})"); - } - - App.Logger.WriteLine($"[DeployManager::GetLastDeploy] Got JSON: {rawResponse}"); - - ClientVersion clientVersion = JsonSerializer.Deserialize(rawResponse)!; - - // for preferences - if (timestamp) - { - App.Logger.WriteLine("[DeployManager::GetLastDeploy] Getting timestamp..."); - - string manifestUrl = $"{BaseUrl}/{clientVersion.VersionGuid}-rbxPkgManifest.txt"; - - // get an approximate deploy time from rbxpkgmanifest's last modified date - HttpResponseMessage pkgResponse = await App.HttpClient.GetAsync(manifestUrl); - - if (pkgResponse.Content.Headers.TryGetValues("last-modified", out var values)) - { - string lastModified = values.First(); - App.Logger.WriteLine($"[DeployManager::GetLastDeploy] {manifestUrl} - Last-Modified: {lastModified}"); - clientVersion.Timestamp = DateTime.Parse(lastModified).ToLocalTime(); - } - } - - return clientVersion; - } - - public async Task CheckReleaseChannel() - { - App.Logger.WriteLine($"[DeployManager::CheckReleaseChannel] Checking current Roblox release channel ({App.Settings.Prop.Channel})..."); - - if (App.Settings.Prop.Channel.ToLower() == DefaultChannel.ToLower()) - { - App.Logger.WriteLine($"[DeployManager::CheckReleaseChannel] Channel is already {DefaultChannel}"); - return; - } - - ClientVersion versionInfo = await App.DeployManager.GetLastDeploy().ConfigureAwait(false); - - if (App.Settings.Prop.UseReShade) - { - string manifest = await App.HttpClient.GetStringAsync($"{App.DeployManager.BaseUrl}/{versionInfo.VersionGuid}-rbxManifest.txt"); - - if (manifest.Contains("RobloxPlayerBeta.dll")) - { - MessageBoxResult result = !App.Settings.Prop.PromptChannelChange ? MessageBoxResult.Yes : App.ShowMessageBox( - $"You currently have ReShade enabled, however your current preferred channel ({App.Settings.Prop.Channel}) does not support ReShade. Would you like to switch to {DefaultChannel}? ", - MessageBoxImage.Question, - MessageBoxButton.YesNo - ); - - if (result == MessageBoxResult.Yes) - { - SwitchToDefault(); - return; - } - } - } - - // this SUCKS - ClientVersion defaultChannelInfo = await new DeployManager().GetLastDeploy().ConfigureAwait(false); - int defaultChannelVersion = int.Parse(defaultChannelInfo.Version.Split('.')[1]); - int currentChannelVersion = int.Parse(versionInfo.Version.Split('.')[1]); - - if (currentChannelVersion < defaultChannelVersion) - { - MessageBoxResult result = !App.Settings.Prop.PromptChannelChange ? MessageBoxResult.Yes : App.ShowMessageBox( - $"Your current preferred channel ({App.Settings.Prop.Channel}) appears to no longer be receiving updates. Would you like to switch to {DefaultChannel}? ", - MessageBoxImage.Question, - MessageBoxButton.YesNo - ); - - if (result == MessageBoxResult.Yes) - { - SwitchToDefault(); - return; - } - } - } - - public static void SwitchToDefault() - { - if (App.Settings.Prop.Channel.ToLower() == DefaultChannel.ToLower()) - return; - - App.DeployManager.Channel = App.Settings.Prop.Channel = DefaultChannel; - App.Logger.WriteLine($"[DeployManager::CheckReleaseChannel] Changed Roblox release channel from {App.Settings.Prop.Channel} to {DefaultChannel}"); - } - } -} diff --git a/Bloxstrap/ViewModels/GlobalViewModel.cs b/Bloxstrap/ViewModels/GlobalViewModel.cs index 0c47ef2..14e11fc 100644 --- a/Bloxstrap/ViewModels/GlobalViewModel.cs +++ b/Bloxstrap/ViewModels/GlobalViewModel.cs @@ -1,5 +1,4 @@ using System.Windows.Input; - using CommunityToolkit.Mvvm.Input; namespace Bloxstrap.ViewModels diff --git a/Bloxstrap/ViewModels/InstallationViewModel.cs b/Bloxstrap/ViewModels/InstallationViewModel.cs index 9d31922..390d9de 100644 --- a/Bloxstrap/ViewModels/InstallationViewModel.cs +++ b/Bloxstrap/ViewModels/InstallationViewModel.cs @@ -7,9 +7,10 @@ using System.Threading.Tasks; using System.Windows; using System.Windows.Forms; using System.Windows.Input; + using CommunityToolkit.Mvvm.Input; + using Bloxstrap.Models; -using Bloxstrap.Singletons; namespace Bloxstrap.ViewModels { @@ -18,7 +19,7 @@ namespace Bloxstrap.ViewModels public event PropertyChangedEventHandler? PropertyChanged; public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - private bool _manualChannelEntry = !DeployManager.SelectableChannels.Contains(App.Settings.Prop.Channel); + private bool _manualChannelEntry = !Deployment.SelectableChannels.Contains(App.Settings.Prop.Channel); public ICommand BrowseInstallLocationCommand => new RelayCommand(BrowseInstallLocation); public ICommand OpenFolderCommand => new RelayCommand(OpenFolder); @@ -39,11 +40,9 @@ namespace Bloxstrap.ViewModels ChannelDeployInfo = null; OnPropertyChanged(nameof(ChannelDeployInfo)); - App.DeployManager.Channel = channel; - try { - ClientVersion info = await App.DeployManager.GetLastDeploy(true); + ClientVersion info = await Deployment.GetInfo(channel, true); ChannelDeployInfo = new DeployInfo { @@ -83,7 +82,7 @@ namespace Bloxstrap.ViewModels set => App.BaseDirectory = value; } - public IEnumerable Channels => DeployManager.SelectableChannels; + public IEnumerable Channels => Deployment.SelectableChannels; public string Channel { @@ -102,8 +101,12 @@ namespace Bloxstrap.ViewModels { _manualChannelEntry = value; - if (!value && !Channels.Contains(Channel)) - Channel = DeployManager.DefaultChannel; + if (!value) + { + // roblox typically sets channels in all lowercase, so here we find if a case insensitive match exists + string? matchingChannel = Channels.Where(x => x.ToLower() == Channel.ToLower()).FirstOrDefault(); + Channel = String.IsNullOrEmpty(matchingChannel) ? Deployment.DefaultChannel : matchingChannel; + } OnPropertyChanged(nameof(Channel)); OnPropertyChanged(nameof(ChannelComboBoxVisibility)); diff --git a/Bloxstrap/Views/Pages/InstallationPage.xaml b/Bloxstrap/Views/Pages/InstallationPage.xaml index a67d256..ca4dc89 100644 --- a/Bloxstrap/Views/Pages/InstallationPage.xaml +++ b/Bloxstrap/Views/Pages/InstallationPage.xaml @@ -59,7 +59,7 @@ - +