diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs index 96c37982..5db507b8 100644 --- a/Bloxstrap/Bootstrapper.cs +++ b/Bloxstrap/Bootstrapper.cs @@ -10,6 +10,7 @@ using Bloxstrap.Helpers; using Bloxstrap.Helpers.Integrations; using Bloxstrap.Helpers.RSMM; +using System.Net; namespace Bloxstrap { @@ -66,8 +67,6 @@ public partial class Bootstrapper "By default, two mod presets are provided for restoring the old death\n" + "sound and the old mouse cursor.\n"; - private static readonly HttpClient Client = new(); - private string? LaunchCommandLine; private string VersionGuid = null!; @@ -87,7 +86,6 @@ public Bootstrapper(string? launchCommandLine = null) { LaunchCommandLine = launchCommandLine; FreshInstall = String.IsNullOrEmpty(Program.Settings.VersionGuid); - Client.Timeout = TimeSpan.FromMinutes(10); } // this is called from BootstrapperStyleForm.SetupDialog() @@ -133,7 +131,7 @@ private async Task CheckLatestVersion() { Dialog.Message = "Connecting to Roblox..."; - VersionGuid = await Client.GetStringAsync($"{DeployManager.BaseUrl}/version"); + VersionGuid = await Program.HttpClient.GetStringAsync($"{DeployManager.BaseUrl}/version"); VersionFolder = Path.Combine(Directories.Versions, VersionGuid); VersionPackageManifest = await PackageManifest.Get(VersionGuid); } @@ -611,7 +609,7 @@ private async void DownloadPackage(Package package) { Debug.WriteLine($"Downloading {package.Name}..."); - var response = await Client.GetAsync(packageUrl); + var response = await Program.HttpClient.GetAsync(packageUrl); if (CancelFired) return; diff --git a/Bloxstrap/Dialogs/Preferences.xaml b/Bloxstrap/Dialogs/Preferences.xaml index 09fdf822..c10019a7 100644 --- a/Bloxstrap/Dialogs/Preferences.xaml +++ b/Bloxstrap/Dialogs/Preferences.xaml @@ -34,7 +34,7 @@ - + diff --git a/Bloxstrap/Helpers/DeployManager.cs b/Bloxstrap/Helpers/DeployManager.cs index d794dbd2..aa5241fd 100644 --- a/Bloxstrap/Helpers/DeployManager.cs +++ b/Bloxstrap/Helpers/DeployManager.cs @@ -104,19 +104,16 @@ public static async Task GetLastDeploy(string channel) string baseUrl = BuildBaseUrl(channel); string lastDeploy = ""; - using (HttpClient client = new()) + string deployHistory = await Program.HttpClient.GetStringAsync($"{baseUrl}/DeployHistory.txt"); + + using (StringReader reader = new(deployHistory)) { - string deployHistory = await client.GetStringAsync($"{baseUrl}/DeployHistory.txt"); + string? line; - using (StringReader reader = new(deployHistory)) + while ((line = await reader.ReadLineAsync()) is not null) { - string? line; - - while ((line = await reader.ReadLineAsync()) is not null) - { - if (line.Contains("WindowsPlayer")) - lastDeploy = line; - } + if (line.Contains("WindowsPlayer")) + lastDeploy = line; } } @@ -132,10 +129,10 @@ public static async Task GetLastDeploy(string channel) lastDeploy = lastDeploy[18..]; // 'version-29fb7cdd06e84001 at 8/23/2022 2:07:27 PM, file version: 0, 542, 100, 5420251, git hash: b98d6b2bea36fa2161f48cca979fb620bb0c24fd ...' string versionGuid = lastDeploy[..lastDeploy.IndexOf(" at")]; // 'version-29fb7cdd06e84001' - + lastDeploy = lastDeploy[(versionGuid.Length + 4)..]; // '8/23/2022 2:07:27 PM, file version: 0, 542, 100, 5420251, git hash: b98d6b2bea36fa2161f48cca979fb620bb0c24fd ...' string strTimestamp = lastDeploy[..lastDeploy.IndexOf(", file")]; // '8/23/2022 2:07:27 PM' - + lastDeploy = lastDeploy[(strTimestamp.Length + 16)..]; // '0, 542, 100, 5420251, git hash: b98d6b2bea36fa2161f48cca979fb620bb0c24fd ...' string fileVersion = ""; @@ -157,11 +154,11 @@ public static async Task GetLastDeploy(string channel) // convert to traditional version format fileVersion = fileVersion.Replace(" ", "").Replace(',', '.'); - return new VersionDeploy - { - VersionGuid = versionGuid, - Timestamp = dtTimestamp, - FileVersion = fileVersion + return new VersionDeploy + { + VersionGuid = versionGuid, + Timestamp = dtTimestamp, + FileVersion = fileVersion }; } } diff --git a/Bloxstrap/Helpers/Integrations/RbxFpsUnlocker.cs b/Bloxstrap/Helpers/Integrations/RbxFpsUnlocker.cs index 2507dd59..2be56209 100644 --- a/Bloxstrap/Helpers/Integrations/RbxFpsUnlocker.cs +++ b/Bloxstrap/Helpers/Integrations/RbxFpsUnlocker.cs @@ -91,15 +91,12 @@ public static async Task CheckInstall() Debug.WriteLine("Installing/Updating rbxfpsunlocker..."); - using (HttpClient client = new()) - { - byte[] bytes = await client.GetByteArrayAsync(downloadUrl); + byte[] bytes = await Program.HttpClient.GetByteArrayAsync(downloadUrl); - using (MemoryStream zipStream = new(bytes)) - { - ZipArchive zip = new(zipStream); - zip.ExtractToDirectory(folderLocation, true); - } + using (MemoryStream zipStream = new(bytes)) + { + ZipArchive zip = new(zipStream); + zip.ExtractToDirectory(folderLocation, true); } if (!File.Exists(settingsLocation)) diff --git a/Bloxstrap/Helpers/RSMM/PackageManifest.cs b/Bloxstrap/Helpers/RSMM/PackageManifest.cs index bacfabdf..44048096 100644 --- a/Bloxstrap/Helpers/RSMM/PackageManifest.cs +++ b/Bloxstrap/Helpers/RSMM/PackageManifest.cs @@ -75,11 +75,8 @@ public static async Task Get(string versionGuid) string pkgManifestUrl = $"{DeployManager.BaseUrl}/{versionGuid}-rbxPkgManifest.txt"; string pkgManifestData; - using (HttpClient http = new()) - { - var getData = http.GetStringAsync(pkgManifestUrl); - pkgManifestData = await getData.ConfigureAwait(false); - } + var getData = Program.HttpClient.GetStringAsync(pkgManifestUrl); + pkgManifestData = await getData.ConfigureAwait(false); return new PackageManifest(pkgManifestData); } diff --git a/Bloxstrap/Helpers/ReverseLineReader.cs b/Bloxstrap/Helpers/ReverseLineReader.cs deleted file mode 100644 index 864e2c0b..00000000 --- a/Bloxstrap/Helpers/ReverseLineReader.cs +++ /dev/null @@ -1,288 +0,0 @@ -using System.Collections; -using System.IO; -using System.Text; - -// taken from MiscUtil: https://github.com/loory/MiscUtil -// the proper usage of MiscUtil nowadays is to *not* use the library and rather copy the code you need so lol - -namespace Bloxstrap.Helpers -{ - /// - /// Takes an encoding (defaulting to UTF-8) and a function which produces a seekable stream - /// (or a filename for convenience) and yields lines from the end of the stream backwards. - /// Only single byte encodings, and UTF-8 and Unicode, are supported. The stream - /// returned by the function must be seekable. - /// - public sealed class ReverseLineReader : IEnumerable - { - /// - /// Buffer size to use by default. Classes with internal access can specify - /// a different buffer size - this is useful for testing. - /// - private const int DefaultBufferSize = 4096; - - /// - /// Means of creating a Stream to read from. - /// - private readonly Func streamSource; - - /// - /// Encoding to use when converting bytes to text - /// - private readonly Encoding encoding; - - /// - /// Size of buffer (in bytes) to read each time we read from the - /// stream. This must be at least as big as the maximum number of - /// bytes for a single character. - /// - private readonly int bufferSize; - - /// - /// Function which, when given a position within a file and a byte, states whether - /// or not the byte represents the start of a character. - /// - private Func characterStartDetector; - - /// - /// Creates a LineReader from a stream source. The delegate is only - /// called when the enumerator is fetched. UTF-8 is used to decode - /// the stream into text. - /// - /// Data source - public ReverseLineReader(Func streamSource) - : this(streamSource, Encoding.UTF8) - { - } - - /// - /// Creates a LineReader from a filename. The file is only opened - /// (or even checked for existence) when the enumerator is fetched. - /// UTF8 is used to decode the file into text. - /// - /// File to read from - public ReverseLineReader(string filename) - : this(filename, Encoding.UTF8) - { - } - - /// - /// Creates a LineReader from a filename. The file is only opened - /// (or even checked for existence) when the enumerator is fetched. - /// - /// File to read from - /// Encoding to use to decode the file into text - public ReverseLineReader(string filename, Encoding encoding) - : this(() => File.OpenRead(filename), encoding) - { - } - - /// - /// Creates a LineReader from a stream source. The delegate is only - /// called when the enumerator is fetched. - /// - /// Data source - /// Encoding to use to decode the stream into text - public ReverseLineReader(Func streamSource, Encoding encoding) - : this(streamSource, encoding, DefaultBufferSize) - { - } - - internal ReverseLineReader(Func streamSource, Encoding encoding, int bufferSize) - { - this.streamSource = streamSource; - this.encoding = encoding; - this.bufferSize = bufferSize; - if (encoding.IsSingleByte) - { - // For a single byte encoding, every byte is the start (and end) of a character - characterStartDetector = (pos, data) => true; - } - else if (encoding is UnicodeEncoding) - { - // For UTF-16, even-numbered positions are the start of a character. - // TODO: This assumes no surrogate pairs. More work required - // to handle that. - characterStartDetector = (pos, data) => (pos & 1) == 0; - } - else if (encoding is UTF8Encoding) - { - // For UTF-8, bytes with the top bit clear or the second bit set are the start of a character - // See http://www.cl.cam.ac.uk/~mgk25/unicode.html - characterStartDetector = (pos, data) => (data & 0x80) == 0 || (data & 0x40) != 0; - } - else - { - throw new ArgumentException("Only single byte, UTF-8 and Unicode encodings are permitted"); - } - } - - /// - /// Returns the enumerator reading strings backwards. If this method discovers that - /// the returned stream is either unreadable or unseekable, a NotSupportedException is thrown. - /// - public IEnumerator GetEnumerator() - { - Stream stream = streamSource(); - if (!stream.CanSeek) - { - stream.Dispose(); - throw new NotSupportedException("Unable to seek within stream"); - } - if (!stream.CanRead) - { - stream.Dispose(); - throw new NotSupportedException("Unable to read within stream"); - } - return GetEnumeratorImpl(stream); - } - - private IEnumerator GetEnumeratorImpl(Stream stream) - { - try - { - long position = stream.Length; - - if (encoding is UnicodeEncoding && (position & 1) != 0) - { - throw new InvalidDataException("UTF-16 encoding provided, but stream has odd length."); - } - - // Allow up to two bytes for data from the start of the previous - // read which didn't quite make it as full characters - byte[] buffer = new byte[bufferSize + 2]; - char[] charBuffer = new char[encoding.GetMaxCharCount(buffer.Length)]; - int leftOverData = 0; - String? previousEnd = null; - // TextReader doesn't return an empty string if there's line break at the end - // of the data. Therefore we don't return an empty string if it's our *first* - // return. - bool firstYield = true; - - // A line-feed at the start of the previous buffer means we need to swallow - // the carriage-return at the end of this buffer - hence this needs declaring - // way up here! - bool swallowCarriageReturn = false; - - while (position > 0) - { - int bytesToRead = Math.Min(position > int.MaxValue ? bufferSize : (int)position, bufferSize); - - position -= bytesToRead; - stream.Position = position; - StreamUtil.ReadExactly(stream, buffer, bytesToRead); - // If we haven't read a full buffer, but we had bytes left - // over from before, copy them to the end of the buffer - if (leftOverData > 0 && bytesToRead != bufferSize) - { - // Buffer.BlockCopy doesn't document its behaviour with respect - // to overlapping data: we *might* just have read 7 bytes instead of - // 8, and have two bytes to copy... - Array.Copy(buffer, bufferSize, buffer, bytesToRead, leftOverData); - } - // We've now *effectively* read this much data. - bytesToRead += leftOverData; - - int firstCharPosition = 0; - while (!characterStartDetector(position + firstCharPosition, buffer[firstCharPosition])) - { - firstCharPosition++; - // Bad UTF-8 sequences could trigger this. For UTF-8 we should always - // see a valid character start in every 3 bytes, and if this is the start of the file - // so we've done a short read, we should have the character start - // somewhere in the usable buffer. - if (firstCharPosition == 3 || firstCharPosition == bytesToRead) - { - throw new InvalidDataException("Invalid UTF-8 data"); - } - } - leftOverData = firstCharPosition; - - int charsRead = encoding.GetChars(buffer, firstCharPosition, bytesToRead - firstCharPosition, charBuffer, 0); - int endExclusive = charsRead; - - for (int i = charsRead - 1; i >= 0; i--) - { - char lookingAt = charBuffer[i]; - if (swallowCarriageReturn) - { - swallowCarriageReturn = false; - if (lookingAt == '\r') - { - endExclusive--; - continue; - } - } - // Anything non-line-breaking, just keep looking backwards - if (lookingAt != '\n' && lookingAt != '\r') - { - continue; - } - // End of CRLF? Swallow the preceding CR - if (lookingAt == '\n') - { - swallowCarriageReturn = true; - } - int start = i + 1; - string bufferContents = new string(charBuffer, start, endExclusive - start); - endExclusive = i; - string stringToYield = previousEnd == null ? bufferContents : bufferContents + previousEnd; - if (!firstYield || stringToYield.Length != 0) - { - yield return stringToYield; - } - firstYield = false; - previousEnd = null; - } - - previousEnd = endExclusive == 0 ? null : (new string(charBuffer, 0, endExclusive) + previousEnd); - - // If we didn't decode the start of the array, put it at the end for next time - if (leftOverData != 0) - { - Buffer.BlockCopy(buffer, 0, buffer, bufferSize, leftOverData); - } - } - if (leftOverData != 0) - { - // At the start of the final buffer, we had the end of another character. - throw new InvalidDataException("Invalid UTF-8 data at start of stream"); - } - if (firstYield && string.IsNullOrEmpty(previousEnd)) - { - yield break; - } - yield return previousEnd ?? ""; - } - finally - { - stream.Dispose(); - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } - - public static class StreamUtil - { - public static void ReadExactly(Stream input, byte[] buffer, int bytesToRead) - { - int index = 0; - while (index < bytesToRead) - { - int read = input.Read(buffer, index, bytesToRead - index); - if (read == 0) - { - throw new EndOfStreamException - (String.Format("End of stream reached with {0} byte{1} left to read.", - bytesToRead - index, - bytesToRead - index == 1 ? "s" : "")); - } - index += read; - } - } - } -} \ No newline at end of file diff --git a/Bloxstrap/Helpers/Updater.cs b/Bloxstrap/Helpers/Updater.cs index 316a898e..942376a1 100644 --- a/Bloxstrap/Helpers/Updater.cs +++ b/Bloxstrap/Helpers/Updater.cs @@ -42,7 +42,7 @@ public static void CheckInstalledVersion() new Preferences().ShowDialog(); - Environment.Exit(0); + Program.Exit(); } } diff --git a/Bloxstrap/Helpers/Utilities.cs b/Bloxstrap/Helpers/Utilities.cs index 22a60d4a..a37d9adf 100644 --- a/Bloxstrap/Helpers/Utilities.cs +++ b/Bloxstrap/Helpers/Utilities.cs @@ -15,13 +15,8 @@ public static void OpenWebsite(string website) public static async Task GetJson(string url) { - using (HttpClient client = new()) - { - client.DefaultRequestHeaders.Add("User-Agent", Program.ProjectRepository); - - string json = await client.GetStringAsync(url); - return JsonSerializer.Deserialize(json); - } + string json = await Program.HttpClient.GetStringAsync(url); + return JsonSerializer.Deserialize(json); } public static string MD5File(string filename) diff --git a/Bloxstrap/Program.cs b/Bloxstrap/Program.cs index fea65247..980fc82f 100644 --- a/Bloxstrap/Program.cs +++ b/Bloxstrap/Program.cs @@ -8,6 +8,8 @@ using Bloxstrap.Helpers; using Bloxstrap.Models; using Bloxstrap.Dialogs; +using System.Net.Http; +using System.Net; namespace Bloxstrap { @@ -34,6 +36,7 @@ internal static class Program public static SettingsManager SettingsManager = new(); public static SettingsFormat Settings = SettingsManager.Settings; + public static readonly HttpClient HttpClient = new(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.All }); // shorthand public static DialogResult ShowMessageBox(string message, MessageBoxIcon icon = MessageBoxIcon.None, MessageBoxButtons buttons = MessageBoxButtons.OK) @@ -57,6 +60,9 @@ static void Main(string[] args) // see https://aka.ms/applicationconfiguration. ApplicationConfiguration.Initialize(); + HttpClient.Timeout = TimeSpan.FromMinutes(5); + HttpClient.DefaultRequestHeaders.Add("User-Agent", ProjectRepository); + LocalAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); StartMenu = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), "Programs", ProjectName);