From c616eb6957a00ad332fa5607bdba4bb1246c9951 Mon Sep 17 00:00:00 2001 From: laolarou Date: Sat, 16 Nov 2024 12:09:18 +0800 Subject: [PATCH] bug fix and improvement improve DownloadHelper performance improve multi-thread dispatch prevent race condition in ResourceCompleter code cleanup --- .../ProjBobcat/Class/Helper/ArchiveHelper.cs | 2 - .../ProjBobcat/Class/Helper/CryptoHelper.cs | 14 - .../Class/Helper/CurseForgeAPIHelper.cs | 9 +- .../ProjBobcat/Class/Helper/DownloadHelper.cs | 453 +++++++++------- .../ProjBobcat/Class/Helper/FileHelper.cs | 4 +- .../ProjBobcat/Class/Helper/FileTypeHelper.cs | 6 +- .../ProjBobcat/Class/Helper/GamePathHelper.cs | 2 +- .../Helper/GameResourcesResolveHelper.cs | 21 +- .../Class/Helper/GameVersionHelper.cs | 6 +- .../ProjBobcat/Class/Helper/HttpHelper.cs | 11 +- .../ProjBobcat/Class/Helper/MavenHelper.cs | 5 +- .../Class/Helper/ModrinthAPIHelper.cs | 5 +- .../NativeReplace/NativeReplaceHelper.cs | 12 +- .../Class/Helper/ProcessorHelper.cs | 2 +- .../Class/Helper/SystemInfoHelper.cs | 17 +- .../Class/Helper/TOMLParser/TomlParser.cs | 498 +++++++++--------- .../Helper/TOMLParser/TommyExtensions.cs | 8 +- .../Class/LaunchArgumentParserBase.cs | 4 +- ProjBobcat/ProjBobcat/Class/LaunchWrapper.cs | 63 +-- .../ProjBobcat/Class/Model/AppxPackageInfo.cs | 2 +- .../Class/Model/AssetObjectModel.cs | 4 +- .../Class/Model/Auth/MicrosoftAuthResult.cs | 2 +- .../Class/Model/Auth/YggdrasilAuthResult.cs | 1 + .../CurseForge/API/FeaturedQueryOptions.cs | 7 +- .../Model/CurseForge/API/SearchOptions.cs | 30 +- .../Model/CurseForge/CurseForgeAddonInfo.cs | 4 +- .../CurseForgeFeaturedAddonModel.cs | 12 +- .../CurseForgeFuzzySearchResponseModel.cs | 15 +- .../CurseForge/CurseForgeLatestFileModel.cs | 3 +- .../CurseForgeSortableGameVersionModel.cs | 3 +- .../{ => Downloading}/DownloadCheckResult.cs | 2 +- .../Model/{ => Downloading}/DownloadFile.cs | 6 +- .../Model/{ => Downloading}/DownloadRange.cs | 2 +- .../{ => Downloading}/DownloadSettings.cs | 26 +- .../Model/Fabric/FabricLoaderArtifactModel.cs | 2 +- .../Class/Model/Forge/ForgeInstallProfile.cs | 4 +- .../Model/Forge/LegacyForgeInstallProfile.cs | 8 +- .../Class/Model/GameConfigurationManager.cs | 16 +- .../Model/GameResource/GameModInfoModel.cs | 4 +- .../GameResource/GameResourcePackModel.cs | 7 +- .../Model/JsonContexts/DictionaryContext.cs | 4 +- .../Class/Model/JsonContexts/StringContext.cs | 4 +- .../ProjBobcat/Class/Model/JvmRulesModel.cs | 14 +- .../ProjBobcat/Class/Model/LaunchErrorType.cs | 2 +- .../ProjBobcat/Class/Model/LaunchSettings.cs | 10 +- .../LauncherAccount/LauncherAccountModel.cs | 3 +- .../Model/LauncherProfile/ResolutionModel.cs | 4 +- .../LiteLoaderDownloadVersionModel.cs | 2 +- .../Microsoft/Graph/DeviceIdResponseModel.cs | 4 +- .../Microsoft/Graph/GraphAuthResultModel.cs | 4 +- .../Graph/GraphResponseErrorModel.cs | 4 +- .../MicrosoftAuth/AuthMojangResponseModel.cs | 4 +- .../MicrosoftAuth/AuthXBLRequestModel.cs | 4 +- .../Model/MicrosoftAuth/AuthXSTSErrorModel.cs | 4 +- .../MicrosoftAuth/AuthXSTSRequestModel.cs | 4 +- .../MicrosoftAuth/AuthXSTSResponseModel.cs | 4 +- .../MicrosoftAuth/MojangErrorResponseModel.cs | 4 +- .../MojangOwnershipResponseModel.cs | 4 +- .../MojangProfileResponseModel.cs | 8 +- .../Model/Modrinth/ModrinthSearchOptions.cs | 14 +- .../Model/Modrinth/ModrinthSearchResult.cs | 4 +- .../Class/Model/Mojang/UserProfile.cs | 2 +- .../Class/Model/Mojang/VersionManifest.cs | 4 +- .../Class/Model/NativeReplaceModel.cs | 18 +- .../Model/NeoForge/NeoForgeVersionsModel.cs | 2 +- .../Optifine/OptifineDownloadVersionModel.cs | 25 +- .../ProjBobcat/Class/Model/PlayerUUID.cs | 22 +- .../Class/Model/ProgressReportBase.cs | 2 +- .../Class/Model/Quilt/QuiltLoaderModel.cs | 2 +- .../Model/Quilt/QuiltSupportGameModel.cs | 2 +- .../ProjBobcat/Class/Model/RawVersionModel.cs | 4 +- .../Model/ResourceCompleterCheckResult.cs | 1 + .../Class/Model/ServerPing/PingPayload.cs | 4 +- .../ProjBobcat/Class/Model/ServerSettings.cs | 10 +- .../Class/Model/Version/ComparableVersion.cs | 20 +- .../Model/Version/Item/BigIntegerItem.cs | 12 +- .../Class/Model/Version/Item/IntItem.cs | 16 +- .../Class/Model/Version/Item/ListItem.cs | 12 +- .../Class/Model/Version/Item/LongItem.cs | 12 +- .../Class/Model/Version/Item/StringItem.cs | 15 +- .../YggdrasilAuth/AuthRefreshRequestModel.cs | 16 +- .../Model/YggdrasilAuth/AuthRequestModel.cs | 16 +- .../Model/YggdrasilAuth/AuthResponseModel.cs | 4 +- .../YggdrasilAuth/AuthTokenRequestModel.cs | 4 +- .../Class/Model/YggdrasilAuth/ErrorModel.cs | 4 +- .../YggdrasilAuth/SignOutRequestModel.cs | 4 +- .../ProjBobcat/Class/VersionLocatorBase.cs | 2 +- ProjBobcat/ProjBobcat/Constants.cs | 4 +- .../Authenticator/MicrosoftAuthenticator.cs | 28 +- .../Authenticator/OfflineAuthenticator.cs | 14 +- .../Authenticator/YggdrasilAuthenticator.cs | 74 +-- .../DefaultResourceCompleter.cs | 94 ++-- .../Installer/FabricInstaller.cs | 38 +- .../ForgeInstaller/ForgeInstallerFactory.cs | 6 +- .../HighVersionForgeInstaller.cs | 192 +++---- .../ForgeInstaller/LegacyForgeInstaller.cs | 29 +- .../Installer/LiteLoaderInstaller.cs | 58 +- .../ModPackInstaller/CurseForgeInstaller.cs | 180 +++---- .../ModPackInstaller/ModPackInstallerBase.cs | 10 +- .../ModPackInstaller/ModrinthInstaller.cs | 43 +- .../Installer/OptifineInstaller.cs | 67 +-- .../Installer/QuiltInstaller.cs | 27 +- .../Launch/DefaultLaunchArgumentParser.cs | 269 +++++----- .../Launch/DefaultLauncherAccountParser.cs | 112 ++-- .../Launch/DefaultLauncherProfileParser.cs | 124 +++-- .../Launch/DefaultVersionLocator.cs | 99 ++-- .../Launch/GameCore/DefaultGameCore.cs | 61 +-- .../Launch/GameCore/GameCoreBase.cs | 28 +- .../AnalysisReport/AnalysisReport.cs | 2 +- .../LogAnalysis/DefaultLogAnalyzer.cs | 113 ++-- .../Logging/DefaultGameLogResolver.cs | 52 +- .../ResourceInfoResolver/AssetInfoResolver.cs | 67 +-- .../GameLoggingInfoResolver.cs | 26 +- .../LibraryInfoResolver.cs | 56 +- .../ResourceInfoResolver/ResolverBase.cs | 16 +- .../VersionInfoResolver.cs | 16 +- .../Service/ServerPingService.cs | 92 ++-- .../Event/DownloadFileChangedEventArgs.cs | 3 +- .../CurseForgeModResolveException.cs | 22 +- .../Exceptions/UnknownGameNameException.cs | 3 +- .../ProjBobcat/Handler/RedirectHandler.cs | 8 +- ProjBobcat/ProjBobcat/Handler/RetryHandler.cs | 4 +- .../Interface/IResourceCompleter.cs | 2 +- .../DateTimeConverterUsingDateTimeParse.cs | 2 +- .../Platforms/Linux/FileHelper.Linux.cs | 2 +- .../Platforms/Linux/SystemInfoHelper.Linux.cs | 12 +- .../Platforms/MacOS/FileHelper.MacOS.cs | 2 +- .../Platforms/MacOS/SystemInfoHelper.MacOS.cs | 17 +- .../Windows/DefaultMinecraftUWPCore.cs | 8 +- .../Windows/SystemInfoHelper.Windows.cs | 64 ++- 130 files changed, 1888 insertions(+), 1904 deletions(-) delete mode 100644 ProjBobcat/ProjBobcat/Class/Helper/CryptoHelper.cs rename ProjBobcat/ProjBobcat/Class/Model/{ => Downloading}/DownloadCheckResult.cs (84%) rename ProjBobcat/ProjBobcat/Class/Model/{ => Downloading}/DownloadFile.cs (88%) rename ProjBobcat/ProjBobcat/Class/Model/{ => Downloading}/DownloadRange.cs (91%) rename ProjBobcat/ProjBobcat/Class/Model/{ => Downloading}/DownloadSettings.cs (55%) diff --git a/ProjBobcat/ProjBobcat/Class/Helper/ArchiveHelper.cs b/ProjBobcat/ProjBobcat/Class/Helper/ArchiveHelper.cs index aa522b52..1eed7cc1 100644 --- a/ProjBobcat/ProjBobcat/Class/Helper/ArchiveHelper.cs +++ b/ProjBobcat/ProjBobcat/Class/Helper/ArchiveHelper.cs @@ -5,8 +5,6 @@ namespace ProjBobcat.Class.Helper; -#nullable enable - public static class ArchiveHelper { public static bool TryOpen(string path, [MaybeNullWhen(false)] out IArchive archive) diff --git a/ProjBobcat/ProjBobcat/Class/Helper/CryptoHelper.cs b/ProjBobcat/ProjBobcat/Class/Helper/CryptoHelper.cs deleted file mode 100644 index b19c9a05..00000000 --- a/ProjBobcat/ProjBobcat/Class/Helper/CryptoHelper.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace ProjBobcat.Class.Helper; - -/// -/// 加密助手 -/// -public static class CryptoHelper -{ - public static string BytesToString(this byte[] bytes) - { - return BitConverter.ToString(bytes).Replace("-", string.Empty); - } -} \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Helper/CurseForgeAPIHelper.cs b/ProjBobcat/ProjBobcat/Class/Helper/CurseForgeAPIHelper.cs index 6a84acce..bd4d7fd2 100644 --- a/ProjBobcat/ProjBobcat/Class/Helper/CurseForgeAPIHelper.cs +++ b/ProjBobcat/ProjBobcat/Class/Helper/CurseForgeAPIHelper.cs @@ -15,7 +15,9 @@ namespace ProjBobcat.Class.Helper; #region Temp Models record AddonInfoReqModel(IEnumerable modIds); + record FileInfoReqModel(IEnumerable fileIds); + record FuzzyFingerPrintReqModel(IEnumerable fingerprints); [JsonSerializable(typeof(AddonInfoReqModel))] @@ -44,7 +46,7 @@ static HttpRequestMessage Req(HttpMethod method, string url) { if (string.IsNullOrEmpty(ApiKey)) throw new NullReferenceException("未设置 API KEY,请调用 SetApiKey(string apiKey) 来进行设置。"); - + var req = new HttpRequestMessage(method, url); req.Headers.Add("x-api-key", ApiKey); @@ -117,7 +119,7 @@ public static void SetApiKey(string apiKey) using var req = Req(HttpMethod.Post, reqUrl); req.Content = new StringContent(data, Encoding.UTF8, "application/json"); - + using var res = await Client.SendAsync(req); res.EnsureSuccessStatusCode(); @@ -176,7 +178,8 @@ public static void SetApiKey(string apiKey) return (await res.Content.ReadFromJsonAsync(CurseForgeModelContext.Default.DataModelString))?.Data; } - public static async Task TryFuzzySearchFile(long[] fingerprint, int gameId = 432) + public static async Task TryFuzzySearchFile(long[] fingerprint, + int gameId = 432) { var reqUrl = $"{BaseUrl}/fingerprints/{gameId}"; diff --git a/ProjBobcat/ProjBobcat/Class/Helper/DownloadHelper.cs b/ProjBobcat/ProjBobcat/Class/Helper/DownloadHelper.cs index 72cb2062..287e8c57 100644 --- a/ProjBobcat/ProjBobcat/Class/Helper/DownloadHelper.cs +++ b/ProjBobcat/ProjBobcat/Class/Helper/DownloadHelper.cs @@ -1,37 +1,67 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; +using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Dataflow; using ProjBobcat.Class.Model; +using ProjBobcat.Class.Model.Downloading; namespace ProjBobcat.Class.Helper; -/// -/// 下载帮助器。 -/// public static class DownloadHelper { /// - /// 下载线程 + /// Download thread count /// public static int DownloadThread { get; set; } = 8; - static HttpClient Head => HttpClientHelper.HeadClient; - static HttpClient Data => HttpClientHelper.DataClient; - static HttpClient MultiPart => HttpClientHelper.MultiPartClient; + private static HttpClient Head => HttpClientHelper.HeadClient; + private static HttpClient Data => HttpClientHelper.DataClient; + private static HttpClient MultiPart => HttpClientHelper.MultiPartClient; - #region 下载数据 + /// + /// Receive data from remote stream + /// + /// Average download speed + private static async Task ReceiveFromRemoteStreamAsync( + Stream remoteStream, + Stream destStream, + DownloadFile downloadFile, + long responseLength, + CancellationToken ct, + bool reportDownloadSpeed = false) + { + var startTime = Stopwatch.GetTimestamp(); + + await remoteStream.CopyToAsync(destStream, ct); + await destStream.FlushAsync(ct); + + var duration = Stopwatch.GetElapsedTime(startTime); + var elapsedTime = duration.TotalSeconds == 0 ? 1 : duration.TotalSeconds; + var speed = responseLength / elapsedTime; + + if (reportDownloadSpeed) + downloadFile.OnChanged( + speed, + 1, + responseLength, + responseLength); + + return speed; + } + + #region Download data /// - /// 下载文件(通过线程池) + /// Simple download data impl /// /// /// @@ -61,60 +91,44 @@ public static async Task DownloadData(DownloadFile downloadFile, DownloadSetting res.EnsureSuccessStatusCode(); - await using var stream = await res.Content.ReadAsStreamAsync(cts.Token); - await using var outputStream = File.Create(filePath); - var responseLength = res.Content.Headers.ContentLength ?? 0; - var downloadedBytesCount = 0L; - var sw = new Stopwatch(); + var hashCheckFile = downloadSettings.CheckFile && !string.IsNullOrEmpty(downloadFile.CheckSum); - var tSpeed = 0d; - var cSpeed = 0; - var lastWrotePos = 0L; - - while (true) - { - sw.Restart(); - await stream.CopyToAsync(outputStream, cts.Token); - var bytesRead = outputStream.Position - lastWrotePos; - lastWrotePos = outputStream.Position; - sw.Stop(); + using var hashProvider = downloadSettings.GetCryptoTransform(); - if (bytesRead == 0) break; + double averageSpeed; - downloadedBytesCount += bytesRead; + var outputStream = File.Create(filePath); + var cryptoStream = new CryptoStream(outputStream, hashProvider, CryptoStreamMode.Write, true); - var elapsedTime = sw.Elapsed.TotalSeconds == 0 ? 1 : sw.Elapsed.TotalSeconds; - var speed = bytesRead / elapsedTime; - - tSpeed += speed; - cSpeed++; - - downloadFile.OnChanged( - speed, - (double)downloadedBytesCount / responseLength, - downloadedBytesCount, - responseLength); + await using (var stream = await res.Content.ReadAsStreamAsync(cts.Token)) + await using (Stream destStream = hashCheckFile ? cryptoStream : outputStream) + { + averageSpeed = await ReceiveFromRemoteStreamAsync( + stream, + destStream, + downloadFile, + responseLength, + cts.Token, + true); + + if (hashCheckFile && destStream is CryptoStream cStream) + await cStream.FlushFinalBlockAsync(cts.Token); } - sw.Stop(); - - if (downloadSettings.CheckFile && !string.IsNullOrEmpty(downloadFile.CheckSum)) + if (hashCheckFile) { - await outputStream.FlushAsync(cts.Token); - outputStream.Seek(0, SeekOrigin.Begin); - - var checkSum = (await downloadSettings.HashDataAsync(outputStream, cts.Token)).BytesToString(); + var checkSum = Convert.ToHexString(hashProvider.Hash!.AsSpan()); if (!(checkSum?.Equals(downloadFile.CheckSum, StringComparison.OrdinalIgnoreCase) ?? false)) { downloadFile.RetryCount++; + DeleteFileWithRetry(filePath); continue; } } - var aSpeed = tSpeed / cSpeed; - downloadFile.OnCompleted(true, null, aSpeed); + downloadFile.OnCompleted(true, null, averageSpeed); return; } @@ -177,26 +191,25 @@ public static (double Speed, SizeUnit Unit) AutoFormatSpeed(double transferSpeed return (convertedSpeed, unit); } - #region 下载一个列表中的文件(自动确定是否使用分片下载) + #region Download a list of files /// - /// 下载文件方法(自动确定是否使用分片下载) + /// Advanced file download impl /// /// /// - public static Task AdvancedDownloadFile(DownloadFile df, DownloadSettings downloadSettings) + private static Task AdvancedDownloadFile(DownloadFile df, DownloadSettings downloadSettings) { if (!Directory.Exists(df.DownloadPath)) Directory.CreateDirectory(df.DownloadPath); - if (df.FileSize is >= 1048576 or 0) - return MultiPartDownloadTaskAsync(df, downloadSettings); - - return DownloadData(df, downloadSettings); + return df.FileSize is >= 1048576 or 0 + ? MultiPartDownloadTaskAsync(df, downloadSettings) + : DownloadData(df, downloadSettings); } /// - /// 下载文件方法(自动确定是否使用分片下载) + /// File download method (Auto detect download method) /// /// 文件列表 /// @@ -210,22 +223,124 @@ public static async Task AdvancedDownloadListFile( d => AdvancedDownloadFile(d, downloadSettings), new ExecutionDataflowBlockOptions { - BoundedCapacity = DownloadThread * 2, - MaxDegreeOfParallelism = DownloadThread + BoundedCapacity = DownloadThread, + MaxDegreeOfParallelism = DownloadThread, + EnsureOrdered = false }); - foreach (var downloadFile in fileEnumerable) - { - await actionBlock.SendAsync(downloadFile); - } + foreach (var downloadFile in fileEnumerable) await actionBlock.SendAsync(downloadFile); actionBlock.Complete(); await actionBlock.Completion; } + public static ActionBlock AdvancedDownloadListFileActionBlock(DownloadSettings downloadSettings) + { + ProcessorHelper.SetMaxThreads(); + + var actionBlock = new ActionBlock( + d => AdvancedDownloadFile(d, downloadSettings), + new ExecutionDataflowBlockOptions + { + BoundedCapacity = DownloadThread * 5, + MaxDegreeOfParallelism = DownloadThread, + EnsureOrdered = false + }); + + return actionBlock; + } + #endregion - #region 分片下载 + #region Partial download + + private static async Task<(long FileLength, bool CanPartialDownload)> CanUsePartialDownload( + string url, + DownloadSettings downloadSettings, + CancellationToken ct) + { + using var headReq = new HttpRequestMessage(HttpMethod.Head, url); + + if (downloadSettings.Authentication != null) + headReq.Headers.Authorization = downloadSettings.Authentication; + if (!string.IsNullOrEmpty(downloadSettings.Host)) + headReq.Headers.Host = downloadSettings.Host; + + try + { + using var headRes = await Head.SendAsync(headReq, ct); + + headRes.EnsureSuccessStatusCode(); + + var responseLength = headRes.Content.Headers.ContentLength ?? 0; + var hasAcceptRanges = headRes.Headers.AcceptRanges.Count != 0; + + using var rangeGetMessage = new HttpRequestMessage(HttpMethod.Get, url); + rangeGetMessage.Headers.Range = new RangeHeaderValue(0, 0); + + if (downloadSettings.Authentication != null) + rangeGetMessage.Headers.Authorization = downloadSettings.Authentication; + if (!string.IsNullOrEmpty(downloadSettings.Host)) + rangeGetMessage.Headers.Host = downloadSettings.Host; + + using var rangeGetRes = await Head.SendAsync(rangeGetMessage, ct); + + var parallelDownloadSupported = + responseLength != 0 && + hasAcceptRanges && + rangeGetRes.StatusCode == HttpStatusCode.PartialContent && + (rangeGetRes.Content.Headers.ContentRange?.HasRange ?? false) && + rangeGetRes.Content.Headers.ContentLength == 1; + + return (responseLength, parallelDownloadSupported); + } + catch (HttpRequestException) + { + return (0, false); + } + } + + private static IEnumerable CalculateDownloadRanges( + long fileLength, + DownloadSettings downloadSettings) + { + var partSize = fileLength / downloadSettings.DownloadParts; + var totalSize = fileLength; + + while (totalSize > 0) + { + //计算分片 + var to = totalSize; + var from = totalSize - partSize; + + if (from < 0) from = 0; + + totalSize -= partSize; + + yield return new DownloadRange + { + Start = from, + End = to, + TempFileName = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()) + }; + } + } + + private static void DeleteFileWithRetry(string filePath, int retryCount = 3) + { + ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(retryCount, 0); + + for (var i = 0; i < retryCount; i++) + try + { + File.Delete(filePath); + return; + } + catch + { + // ignored + } + } /// /// 分片下载方法(异步) @@ -249,48 +364,19 @@ public static async Task MultiPartDownloadTaskAsync( var timeout = downloadSettings.Timeout; var isLatestFileCheckSucceeded = true; - List? readRanges = null; + ImmutableList? readRanges = null; for (var r = 0; r <= downloadSettings.RetryCount; r++) { - using var cts = new CancellationTokenSource(timeout * Math.Max(1, r + 1)); + using var cts = new CancellationTokenSource(timeout * Math.Max(1, (r + 1) * 0.5)); try { #region Get file size - using var headReq = new HttpRequestMessage(HttpMethod.Head, downloadFile.DownloadUri); - - if (downloadSettings.Authentication != null) - headReq.Headers.Authorization = downloadSettings.Authentication; - if (!string.IsNullOrEmpty(downloadSettings.Host)) - headReq.Headers.Host = downloadSettings.Host; - - using var headRes = await Head.SendAsync(headReq, cts.Token); - - headRes.EnsureSuccessStatusCode(); - - var responseLength = headRes.Content.Headers.ContentLength ?? 0; - var hasAcceptRanges = headRes.Headers.AcceptRanges.Count != 0; + var urlInfo = await CanUsePartialDownload(downloadFile.DownloadUri, downloadSettings, cts.Token); - using var rangeGetMessage = new HttpRequestMessage(HttpMethod.Get, downloadFile.DownloadUri); - rangeGetMessage.Headers.Range = new RangeHeaderValue(0, 0); - - if (downloadSettings.Authentication != null) - rangeGetMessage.Headers.Authorization = downloadSettings.Authentication; - if (!string.IsNullOrEmpty(downloadSettings.Host)) - rangeGetMessage.Headers.Host = downloadSettings.Host; - - using var rangeGetRes = await Head.SendAsync(rangeGetMessage, cts.Token); - - var parallelDownloadSupported = - responseLength != 0 && - hasAcceptRanges && - rangeGetRes.StatusCode == HttpStatusCode.PartialContent && - (rangeGetRes.Content.Headers.ContentRange?.HasRange ?? false) && - rangeGetRes.Content.Headers.ContentLength == 1; - - if (!parallelDownloadSupported) + if (!urlInfo.CanPartialDownload) { await DownloadData(downloadFile, downloadSettings); return; @@ -303,38 +389,15 @@ public static async Task MultiPartDownloadTaskAsync( #region Calculate ranges - readRanges = []; - var partSize = responseLength / downloadSettings.DownloadParts; - var totalSize = responseLength; - - while (totalSize > 0) - { - //计算分片 - var to = totalSize; - var from = totalSize - partSize; - - if (from < 0) from = 0; - - totalSize -= partSize; - - readRanges.Add(new DownloadRange - { - Start = from, - End = to, - TempFileName = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()) - }); - } + readRanges = CalculateDownloadRanges(urlInfo.FileLength, downloadSettings).Reverse().ToImmutableList(); #endregion #region Parallel download - var downloadedBytesCount = 0L; var tasksDone = 0; - var doneRanges = new ConcurrentBag(); - var streamBlock = - new TransformBlock( + new TransformBlock( async p => { using var request = new HttpRequestMessage(HttpMethod.Get, downloadFile.DownloadUri); @@ -346,67 +409,64 @@ public static async Task MultiPartDownloadTaskAsync( request.Headers.Range = new RangeHeaderValue(p.Start, p.End); - var downloadTask = await MultiPart.SendAsync( - request, - HttpCompletionOption.ResponseHeadersRead, - cts.Token); - - return (downloadTask, p); + try + { + var downloadTask = await MultiPart.SendAsync( + request, + HttpCompletionOption.ResponseHeadersRead, + cts.Token); + + return (downloadTask, p); + } + catch (HttpRequestException e) + { + Console.WriteLine(e); + await cts.CancelAsync(); + } + + return null; }, new ExecutionDataflowBlockOptions { BoundedCapacity = downloadSettings.DownloadParts, + EnsureOrdered = false, MaxDegreeOfParallelism = downloadSettings.DownloadParts, CancellationToken = cts.Token }); - var tSpeed = 0D; - var cSpeed = 0; + var locker = new object(); + var aggregatedSpeed = 0D; + var aggregatedSpeedCount = 0; - var writeActionBlock = new ActionBlock<(HttpResponseMessage, DownloadRange)>(async t => + var writeActionBlock = new ActionBlock<(HttpResponseMessage, DownloadRange)?>(async t => { - using var res = t.Item1; + if (!t.HasValue) return; - await using var stream = await res.Content.ReadAsStreamAsync(cts.Token); - await using var fileToWriteTo = File.Create(t.Item2.TempFileName); - - var sw = new Stopwatch(); - var lastWrotePos = 0L; + var pair = t.Value; + using var res = pair.Item1; - while (true) + await using (var stream = await res.Content.ReadAsStreamAsync(cts.Token)) + await using (var fileToWriteTo = File.Create(pair.Item2.TempFileName)) { - sw.Restart(); - - await stream.CopyToAsync(fileToWriteTo, cts.Token); - var bytesRead = fileToWriteTo.Position - lastWrotePos; - lastWrotePos = fileToWriteTo.Position; - - sw.Stop(); - - if (bytesRead == 0) - break; - - Interlocked.Add(ref downloadedBytesCount, bytesRead); - - var elapsedTime = Math.Ceiling(sw.Elapsed.TotalSeconds); - var speed = bytesRead / elapsedTime; - - tSpeed += speed; - cSpeed++; - - downloadFile.OnChanged( - speed, - (double)downloadedBytesCount / responseLength, - downloadedBytesCount, - responseLength); + var averageSpeed = await ReceiveFromRemoteStreamAsync( + stream, + fileToWriteTo, + downloadFile, + urlInfo.FileLength, + cts.Token, + downloadSettings.ShowDownloadProgressForPartialDownload); + + lock (locker) + { + aggregatedSpeed += averageSpeed; + aggregatedSpeedCount++; + } } - sw.Stop(); - Interlocked.Add(ref tasksDone, 1); - doneRanges.Add(t.Item2); }, new ExecutionDataflowBlockOptions { BoundedCapacity = downloadSettings.DownloadParts, + EnsureOrdered = false, MaxDegreeOfParallelism = downloadSettings.DownloadParts, CancellationToken = cts.Token }); @@ -415,7 +475,7 @@ public static async Task MultiPartDownloadTaskAsync( var filesBlock = new TransformManyBlock, DownloadRange>(chunk => chunk, - new ExecutionDataflowBlockOptions()); + new ExecutionDataflowBlockOptions { EnsureOrdered = false }); filesBlock.LinkTo(streamBlock, linkOptions); streamBlock.LinkTo(writeActionBlock, linkOptions); @@ -425,9 +485,9 @@ public static async Task MultiPartDownloadTaskAsync( await writeActionBlock.Completion; - var aSpeed = tSpeed / cSpeed; + var aSpeed = aggregatedSpeed / aggregatedSpeedCount; - if (doneRanges.Count != readRanges.Count) + if (tasksDone != readRanges.Count) { downloadFile.RetryCount++; streamBlock.Complete(); @@ -435,32 +495,54 @@ public static async Task MultiPartDownloadTaskAsync( continue; } - await using (var outputStream = File.Create(filePath)) + var hashCheckFile = downloadSettings.CheckFile && !string.IsNullOrEmpty(downloadFile.CheckSum); + + using var hashProvider = downloadSettings.GetCryptoTransform(); + + var fileStream = File.Create(filePath); + var hashStream = new CryptoStream(fileStream, hashProvider, CryptoStreamMode.Write); + + await using (Stream destStream = hashCheckFile ? hashStream : fileStream) { + var index = 0; + foreach (var inputFilePath in readRanges) { await using var inputStream = File.OpenRead(inputFilePath.TempFileName); - outputStream.Seek(inputFilePath.Start, SeekOrigin.Begin); - await inputStream.CopyToAsync(outputStream, cts.Token); + // Because the feature of HTTP range response, + // the first byte of the first range is the last byte of the file. + // So we need to skip the first byte of the first range. + // (Expect the first part) + if (index != 0) + inputStream.Seek(1, SeekOrigin.Begin); + + await inputStream.CopyToAsync(destStream, cts.Token); + + index++; } - await outputStream.FlushAsync(cts.Token); - outputStream.Seek(0, SeekOrigin.Begin); + await destStream.FlushAsync(cts.Token); + + if (hashCheckFile && destStream is CryptoStream cStream) + await cStream.FlushFinalBlockAsync(cts.Token); + } - if (downloadSettings.CheckFile && !string.IsNullOrEmpty(downloadFile.CheckSum)) + if (hashCheckFile) + { + var checkSum = Convert.ToHexString(hashProvider.Hash.AsSpan()); + + if (!checkSum.Equals(downloadFile.CheckSum, StringComparison.OrdinalIgnoreCase)) { - var checkSum = (await downloadSettings.HashDataAsync(outputStream, cts.Token)).BytesToString(); + downloadFile.RetryCount++; + isLatestFileCheckSucceeded = false; - if (!checkSum.Equals(downloadFile.CheckSum, StringComparison.OrdinalIgnoreCase)) - { - downloadFile.RetryCount++; - isLatestFileCheckSucceeded = false; - continue; - } + DeleteFileWithRetry(filePath); - isLatestFileCheckSucceeded = true; + continue; } + + isLatestFileCheckSucceeded = true; } streamBlock.Complete(); @@ -475,14 +557,7 @@ public static async Task MultiPartDownloadTaskAsync( { if (readRanges != null) foreach (var piece in readRanges.Where(piece => File.Exists(piece.TempFileName))) - try - { - File.Delete(piece.TempFileName); - } - catch (Exception e) - { - Debug.WriteLine(e); - } + DeleteFileWithRetry(piece.TempFileName); downloadFile.RetryCount++; exceptions.Add(ex); diff --git a/ProjBobcat/ProjBobcat/Class/Helper/FileHelper.cs b/ProjBobcat/ProjBobcat/Class/Helper/FileHelper.cs index 188de765..04eccb8e 100644 --- a/ProjBobcat/ProjBobcat/Class/Helper/FileHelper.cs +++ b/ProjBobcat/ProjBobcat/Class/Helper/FileHelper.cs @@ -34,7 +34,6 @@ public static FileType GetFileType(string path) public static async Task OpenReadAsync(string path, CancellationToken token) { while (!token.IsCancellationRequested) - { try { return File.OpenRead(path); @@ -43,8 +42,7 @@ public static FileType GetFileType(string path) { await Task.Delay(1000, token); } - } - + return null; } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Helper/FileTypeHelper.cs b/ProjBobcat/ProjBobcat/Class/Helper/FileTypeHelper.cs index 0f7d593f..1986f26d 100644 --- a/ProjBobcat/ProjBobcat/Class/Helper/FileTypeHelper.cs +++ b/ProjBobcat/ProjBobcat/Class/Helper/FileTypeHelper.cs @@ -24,9 +24,11 @@ public static async Task TryDetectFileTypeAsync(string filePath) await using var fs = File.OpenRead(filePath); using var archive = ArchiveFactory.Open(fs); - if (archive.Entries.Any(e => e.Key?.Equals("manifest.json", StringComparison.OrdinalIgnoreCase) ?? false)) + if (archive.Entries.Any( + e => e.Key?.Equals("manifest.json", StringComparison.OrdinalIgnoreCase) ?? false)) return AssetFileType.CurseForgeModPack; - if (archive.Entries.Any(e => e.Key?.Equals("modrinth.index.json", StringComparison.OrdinalIgnoreCase) ?? false)) + if (archive.Entries.Any(e => + e.Key?.Equals("modrinth.index.json", StringComparison.OrdinalIgnoreCase) ?? false)) return AssetFileType.ModrinthModPack; break; } diff --git a/ProjBobcat/ProjBobcat/Class/Helper/GamePathHelper.cs b/ProjBobcat/ProjBobcat/Class/Helper/GamePathHelper.cs index f8dd4093..55b23901 100644 --- a/ProjBobcat/ProjBobcat/Class/Helper/GamePathHelper.cs +++ b/ProjBobcat/ProjBobcat/Class/Helper/GamePathHelper.cs @@ -42,7 +42,7 @@ public static string GetGamePath(string id) public static string GetGameJsonPath(string rootPath, string id) { var versions = Path.Combine(rootPath, "versions", id); - + if (!Directory.Exists(versions)) return Path.Combine(versions, $"{id}.json"); diff --git a/ProjBobcat/ProjBobcat/Class/Helper/GameResourcesResolveHelper.cs b/ProjBobcat/ProjBobcat/Class/Helper/GameResourcesResolveHelper.cs index 584d3ea9..9be9f985 100644 --- a/ProjBobcat/ProjBobcat/Class/Helper/GameResourcesResolveHelper.cs +++ b/ProjBobcat/ProjBobcat/Class/Helper/GameResourcesResolveHelper.cs @@ -32,23 +32,19 @@ static string ProcessJsonString(string json) { if (jsonSplit[i].Trim().EndsWith("{") || jsonSplit[i].Trim() == "\n" || string.IsNullOrWhiteSpace(jsonSplit[i].Trim())) - { continue; - } if (startIndex == 0) - { if ((!jsonSplit[i].Trim().EndsWith("\"") && !jsonSplit[i].Trim().EndsWith(",")) || - (jsonSplit[i].Replace(" ", "").EndsWith(":\""))) + jsonSplit[i].Replace(" ", "").EndsWith(":\"")) { startIndex = i; continue; } - } if (startIndex == 0) continue; if ((!jsonSplit[i].Trim().StartsWith("\"") || !jsonSplit[i + 1].Trim().StartsWith("}")) && - (!jsonSplit[i].Trim().StartsWith("\",")) && (!jsonSplit[i].Trim().EndsWith("\","))) continue; + !jsonSplit[i].Trim().StartsWith("\",") && !jsonSplit[i].Trim().EndsWith("\",")) continue; endIndex = i; break; @@ -92,7 +88,7 @@ static string ProcessJsonString(string json) var infoTable = arr.Children.First(); var title = infoTable.HasKey("modId") - ? (infoTable["modId"]?.AsString ?? "-") + ? infoTable["modId"]?.AsString ?? "-" : Path.GetFileName(file); var author = infoTable.HasKey("authors") ? infoTable["authors"]?.AsString @@ -200,7 +196,8 @@ static async Task GetFabricModInfo( "[!] 数据包 JSON 异常", e.Message }; - return new GameModResolvedInfo(null, file, errorList.ToImmutableList(), Path.GetFileName(file), null, "Fabric", isEnabled); + return new GameModResolvedInfo(null, file, errorList.ToImmutableList(), Path.GetFileName(file), null, + "Fabric", isEnabled); } } @@ -292,7 +289,7 @@ public static async IAsyncEnumerable ResolveModListAsync( null, "Unknown", isEnabled); - + ReturnResult: result = result! with { LoaderType = GetModLoaderType(archive) }; yield return result; @@ -332,7 +329,6 @@ public static async IAsyncEnumerable ResolveModListAsync( } if (packInfoEntry != null) - { try { await using var stream = packInfoEntry.OpenEntryStream(); @@ -347,7 +343,6 @@ public static async IAsyncEnumerable ResolveModListAsync( description = $"[!] 数据包 JSON 异常: {e.Message}"; version = -1; } - } return new GameResourcePackResolvedInfo(fileName, description, version, imageBytes); } @@ -367,7 +362,6 @@ public static async IAsyncEnumerable ResolveModListAsync( var version = -1; if (File.Exists(infoPath)) - { try { await using var contentStream = File.OpenRead(infoPath); @@ -382,7 +376,6 @@ public static async IAsyncEnumerable ResolveModListAsync( description = $"[!] 数据包 JSON 异常: {e.Message}"; version = -1; } - } return new GameResourcePackResolvedInfo(fileName, description, version, imageBytes); } @@ -420,7 +413,7 @@ public static async IAsyncEnumerable ResolveResour if (!archive.Entries.Any(e => Path.GetFileName(e.Key?.TrimEnd('/')) ?.Equals("shaders", StringComparison.OrdinalIgnoreCase) ?? false)) - return null; + return null; var model = new GameShaderPackResolvedInfo(Path.GetFileName(file), false); diff --git a/ProjBobcat/ProjBobcat/Class/Helper/GameVersionHelper.cs b/ProjBobcat/ProjBobcat/Class/Helper/GameVersionHelper.cs index d93cd9f4..09ddcfed 100644 --- a/ProjBobcat/ProjBobcat/Class/Helper/GameVersionHelper.cs +++ b/ProjBobcat/ProjBobcat/Class/Helper/GameVersionHelper.cs @@ -1,9 +1,9 @@ -using ProjBobcat.Class.Model; +using System; using System.Collections.Generic; -using System.Text.RegularExpressions; -using System; using System.Linq; using System.Text.Json; +using System.Text.RegularExpressions; +using ProjBobcat.Class.Model; namespace ProjBobcat.Class.Helper; diff --git a/ProjBobcat/ProjBobcat/Class/Helper/HttpHelper.cs b/ProjBobcat/ProjBobcat/Class/Helper/HttpHelper.cs index 1e4dc1ad..37409643 100644 --- a/ProjBobcat/ProjBobcat/Class/Helper/HttpHelper.cs +++ b/ProjBobcat/ProjBobcat/Class/Helper/HttpHelper.cs @@ -17,18 +17,21 @@ public static partial class HttpHelper { const string UriRegexStr = "((([A-Za-z]{3,9}:(?:\\/\\/)?)(?:[-;:&=\\+$,\\w]+@)?[A-Za-z0-9.-]+(:[0-9]+)?|(?:ww‌​w.|[-;:&=\\+$,\\w]+@)[A-Za-z0-9.-]+)((?:\\/[\\+~%\\/.\\w-_]*)?\\??(?:[-\\+=&;%@.\\w_]*)#?‌​(?:[\\w]*))?)"; - - [GeneratedRegex(UriRegexStr)] - private static partial Regex UriRegex(); static HttpClient Client => HttpClientHelper.DefaultClient; + [GeneratedRegex(UriRegexStr)] + private static partial Regex UriRegex(); + /// /// 正则匹配Uri /// /// 待处理Uri /// 匹配的Uri - public static string RegexMatchUri(string uri) => UriRegex().Match(uri).Value; + public static string RegexMatchUri(string uri) + { + return UriRegex().Match(uri).Value; + } /// /// Http Delete方法 diff --git a/ProjBobcat/ProjBobcat/Class/Helper/MavenHelper.cs b/ProjBobcat/ProjBobcat/Class/Helper/MavenHelper.cs index 2406ff99..39aa1d0a 100644 --- a/ProjBobcat/ProjBobcat/Class/Helper/MavenHelper.cs +++ b/ProjBobcat/ProjBobcat/Class/Helper/MavenHelper.cs @@ -65,7 +65,10 @@ public static partial class MavenHelper /// /// Maven Id /// 处理好的Group Path - public static string GetGroupPath(this string artifactId) => GroupPathRegex().Replace(artifactId, "/"); + public static string GetGroupPath(this string artifactId) + { + return GroupPathRegex().Replace(artifactId, "/"); + } /// /// 获取Maven全名 diff --git a/ProjBobcat/ProjBobcat/Class/Helper/ModrinthAPIHelper.cs b/ProjBobcat/ProjBobcat/Class/Helper/ModrinthAPIHelper.cs index a9be1e74..c51f6f24 100644 --- a/ProjBobcat/ProjBobcat/Class/Helper/ModrinthAPIHelper.cs +++ b/ProjBobcat/ProjBobcat/Class/Helper/ModrinthAPIHelper.cs @@ -2,12 +2,11 @@ using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Json; -using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; -using ProjBobcat.Class.Model; +using ProjBobcat.Class.Model.Downloading; using ProjBobcat.Class.Model.Modrinth; namespace ProjBobcat.Class.Helper; @@ -146,7 +145,7 @@ await res.Content.ReadFromJsonAsync(ModrinthProjectDependencyInfoContext.Default ModrinthModelContext.Default.FileMatchRequestModel); using var res = await HttpHelper.Post(reqUrl, data); - + if (!res.IsSuccessStatusCode) return null; diff --git a/ProjBobcat/ProjBobcat/Class/Helper/NativeReplace/NativeReplaceHelper.cs b/ProjBobcat/ProjBobcat/Class/Helper/NativeReplace/NativeReplaceHelper.cs index db1d761d..f414047f 100644 --- a/ProjBobcat/ProjBobcat/Class/Helper/NativeReplace/NativeReplaceHelper.cs +++ b/ProjBobcat/ProjBobcat/Class/Helper/NativeReplace/NativeReplaceHelper.cs @@ -45,7 +45,8 @@ _ when RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD) => "freebsd", return $"{platform}-{arch}"; } - public static List Replace(List versions, List libs, NativeReplacementPolicy policy) + public static List Replace(List versions, List libs, + NativeReplacementPolicy policy) { if (policy == NativeReplacementPolicy.Disabled) return libs; @@ -56,10 +57,7 @@ public static List Replace(List versions, List= 2 }) - { - minor = int.TryParse(versionsArr[1], out var outMinor) ? outMinor : -1; - } + if (versionsArr is { Length: >= 2 }) minor = int.TryParse(versionsArr[1], out var outMinor) ? outMinor : -1; if (minor is -1 or >= 19 && policy == NativeReplacementPolicy.LegacyOnly) return libs; @@ -116,14 +114,12 @@ public static List Replace(List versions, List - /// 尝试获取进程的退出码 + /// 尝试获取进程的退出码 /// /// /// diff --git a/ProjBobcat/ProjBobcat/Class/Helper/SystemInfoHelper.cs b/ProjBobcat/ProjBobcat/Class/Helper/SystemInfoHelper.cs index 16d3048e..d21736da 100644 --- a/ProjBobcat/ProjBobcat/Class/Helper/SystemInfoHelper.cs +++ b/ProjBobcat/ProjBobcat/Class/Helper/SystemInfoHelper.cs @@ -75,22 +75,27 @@ public static async IAsyncEnumerable FindJava(bool fullSearch = false) if (fullSearch) await foreach (var path in DeepJavaSearcher.DeepSearch()) - if (searched.Add(path)) yield return path; + if (searched.Add(path)) + yield return path; if (OperatingSystem.IsWindows()) foreach (var path in Platforms.Windows.SystemInfoHelper.FindJavaWindows()) - if (searched.Add(path)) yield return path; + if (searched.Add(path)) + yield return path; if (OperatingSystem.IsMacOS()) foreach (var path in Platforms.MacOS.SystemInfoHelper.FindJavaMacOS()) - if (searched.Add(path)) yield return path; + if (searched.Add(path)) + yield return path; if (OperatingSystem.IsLinux()) - foreach(var path in Platforms.Linux.SystemInfoHelper.FindJavaLinux()) - if (searched.Add(path)) yield return path; + foreach (var path in Platforms.Linux.SystemInfoHelper.FindJavaLinux()) + if (searched.Add(path)) + yield return path; foreach (var path in FindJavaInOfficialGamePath()) - if (searched.Add(path)) yield return path; + if (searched.Add(path)) + yield return path; var evJava = FindJavaUsingEnvironmentVariable(); diff --git a/ProjBobcat/ProjBobcat/Class/Helper/TOMLParser/TomlParser.cs b/ProjBobcat/ProjBobcat/Class/Helper/TOMLParser/TomlParser.cs index ea7d3873..401d7dff 100644 --- a/ProjBobcat/ProjBobcat/Class/Helper/TOMLParser/TomlParser.cs +++ b/ProjBobcat/ProjBobcat/Class/Helper/TOMLParser/TomlParser.cs @@ -60,7 +60,7 @@ public virtual IEnumerable Keys public IEnumerator GetEnumerator() { - return Children.GetEnumerator(); + return this.Children.GetEnumerator(); } public virtual bool TryGetNode(string key, out TomlNode? node) @@ -101,17 +101,17 @@ public virtual void Delete(int index) public virtual void AddRange(IEnumerable nodes) { - foreach (var tomlNode in nodes) Add(tomlNode); + foreach (var tomlNode in nodes) this.Add(tomlNode); } public virtual void WriteTo(TextWriter tw, string name = null) { - tw.WriteLine(ToInlineToml()); + tw.WriteLine(this.ToInlineToml()); } public virtual string ToInlineToml() { - return ToString(); + return this.ToString(); } #region Native type to TOML cast @@ -206,15 +206,16 @@ public class TomlString : TomlNode public override string ToString() { - return Value; + return this.Value; } public override string ToInlineToml() { - if (Value.IndexOf(TomlSyntax.LITERAL_STRING_SYMBOL) != -1 && PreferLiteral) PreferLiteral = false; - var quotes = new string(PreferLiteral ? TomlSyntax.LITERAL_STRING_SYMBOL : TomlSyntax.BASIC_STRING_SYMBOL, - IsMultiline ? 3 : 1); - var result = PreferLiteral ? Value : Value.Escape(!IsMultiline); + if (this.Value.IndexOf(TomlSyntax.LITERAL_STRING_SYMBOL) != -1 && this.PreferLiteral) + this.PreferLiteral = false; + var quotes = new string(this.PreferLiteral ? TomlSyntax.LITERAL_STRING_SYMBOL : TomlSyntax.BASIC_STRING_SYMBOL, + this.IsMultiline ? 3 : 1); + var result = this.PreferLiteral ? this.Value : this.Value.Escape(!this.IsMultiline); return $"{quotes}{result}{quotes}"; } } @@ -237,14 +238,14 @@ public enum Base public override string ToString() { - return Value.ToString(); + return this.Value.ToString(); } public override string ToInlineToml() { - return IntegerBase != Base.Decimal - ? $"0{TomlSyntax.BaseIdentifiers[(int)IntegerBase]}{Convert.ToString(Value, (int)IntegerBase)}" - : Value.ToString(CultureInfo.InvariantCulture); + return this.IntegerBase != Base.Decimal + ? $"0{TomlSyntax.BaseIdentifiers[(int)this.IntegerBase]}{Convert.ToString(this.Value, (int)this.IntegerBase)}" + : this.Value.ToString(CultureInfo.InvariantCulture); } } @@ -257,22 +258,22 @@ public class TomlFloat : TomlNode, IFormattable public string ToString(string? format, IFormatProvider? formatProvider) { - return Value.ToString(format, formatProvider); + return this.Value.ToString(format, formatProvider); } public override string ToString() { - return Value.ToString(CultureInfo.CurrentCulture); + return this.Value.ToString(CultureInfo.CurrentCulture); } public string ToString(IFormatProvider formatProvider) { - return Value.ToString(formatProvider); + return this.Value.ToString(formatProvider); } public override string ToInlineToml() { - return Value switch + return this.Value switch { var v when double.IsNaN(v) => TomlSyntax.NAN_VALUE, var v when double.IsPositiveInfinity(v) => TomlSyntax.INF_VALUE, @@ -291,12 +292,12 @@ public class TomlBoolean : TomlNode public override string ToString() { - return Value.ToString(); + return this.Value.ToString(); } public override string ToInlineToml() { - return Value ? TomlSyntax.TRUE_VALUE : TomlSyntax.FALSE_VALUE; + return this.Value ? TomlSyntax.TRUE_VALUE : TomlSyntax.FALSE_VALUE; } } @@ -312,28 +313,28 @@ public class TomlDateTime : TomlNode, IFormattable public string ToString(string? format, IFormatProvider? formatProvider) { - return Value.ToString(format, formatProvider); + return this.Value.ToString(format, formatProvider); } public override string ToString() { - return Value.ToString(CultureInfo.CurrentCulture); + return this.Value.ToString(CultureInfo.CurrentCulture); } public string ToString(IFormatProvider formatProvider) { - return Value.ToString(formatProvider); + return this.Value.ToString(formatProvider); } public override string ToInlineToml() { - return Value switch + return this.Value switch { - var v when OnlyDate => v.ToString(TomlSyntax.LocalDateFormat), - var v when OnlyTime => v.ToString(TomlSyntax.RFC3339LocalTimeFormats[SecondsPrecision]), + var v when this.OnlyDate => v.ToString(TomlSyntax.LocalDateFormat), + var v when this.OnlyTime => v.ToString(TomlSyntax.RFC3339LocalTimeFormats[this.SecondsPrecision]), var v when v.Kind is DateTimeKind.Local => - v.ToString(TomlSyntax.RFC3339LocalDateTimeFormats[SecondsPrecision]), - var v => v.ToString(TomlSyntax.RFC3339Formats[SecondsPrecision]) + v.ToString(TomlSyntax.RFC3339LocalDateTimeFormats[this.SecondsPrecision]), + var v => v.ToString(TomlSyntax.RFC3339Formats[this.SecondsPrecision]) }; } } @@ -345,48 +346,48 @@ public class TomlArray : TomlNode public override bool HasValue { get; } = true; public override bool IsArray { get; } = true; public bool IsTableArray { get; set; } - public List RawArray => values ??= new List(); + public List RawArray => this.values ??= new List(); public override TomlNode this[int index] { get { - if (index < RawArray.Count) return RawArray[index]; + if (index < this.RawArray.Count) return this.RawArray[index]; var lazy = new TomlLazy(this); this[index] = lazy; return lazy; } set { - if (index == RawArray.Count) - RawArray.Add(value); + if (index == this.RawArray.Count) + this.RawArray.Add(value); else - RawArray[index] = value; + this.RawArray[index] = value; } } - public override int ChildrenCount => RawArray.Count; + public override int ChildrenCount => this.RawArray.Count; - public override IEnumerable Children => RawArray.AsEnumerable(); + public override IEnumerable Children => this.RawArray.AsEnumerable(); public override void Add(TomlNode node) { - RawArray.Add(node); + this.RawArray.Add(node); } public override void AddRange(IEnumerable nodes) { - RawArray.AddRange(nodes); + this.RawArray.AddRange(nodes); } public override void Delete(TomlNode node) { - RawArray.Remove(node); + this.RawArray.Remove(node); } public override void Delete(int index) { - RawArray.RemoveAt(index); + this.RawArray.RemoveAt(index); } public override string ToString() @@ -394,9 +395,9 @@ public override string ToString() var sb = new StringBuilder(); sb.Append(TomlSyntax.ARRAY_START_SYMBOL); - if (ChildrenCount != 0) + if (this.ChildrenCount != 0) sb.Append(' ') - .Append($"{TomlSyntax.ITEM_SEPARATOR} ".Join(RawArray.Select(n => n.ToInlineToml()))) + .Append($"{TomlSyntax.ITEM_SEPARATOR} ".Join(this.RawArray.Select(n => n.ToInlineToml()))) .Append(' '); sb.Append(TomlSyntax.ARRAY_END_SYMBOL); @@ -406,14 +407,14 @@ public override string ToString() public override void WriteTo(TextWriter tw, string name = null) { // If it's a normal array, write it as usual - if (!IsTableArray) + if (!this.IsTableArray) { - tw.Write(ToInlineToml()); + tw.Write(this.ToInlineToml()); return; } tw.WriteLine(); - Comment?.AsComment(tw); + this.Comment?.AsComment(tw); tw.Write(TomlSyntax.ARRAY_START_SYMBOL); tw.Write(TomlSyntax.ARRAY_START_SYMBOL); tw.Write(name); @@ -423,7 +424,7 @@ public override void WriteTo(TextWriter tw, string name = null) var first = true; - foreach (var tomlNode in RawArray) + foreach (var tomlNode in this.RawArray) { if (tomlNode is not TomlTable tbl) throw new TomlFormatException("The array is marked as array table but contains non-table nodes!"); @@ -435,7 +436,7 @@ public override void WriteTo(TextWriter tw, string name = null) { tw.WriteLine(); - Comment?.AsComment(tw); + this.Comment?.AsComment(tw); tw.Write(TomlSyntax.ARRAY_START_SYMBOL); tw.Write(TomlSyntax.ARRAY_START_SYMBOL); tw.Write(name); @@ -461,47 +462,47 @@ public class TomlTable : TomlNode public override bool HasValue { get; } = false; public override bool IsTable { get; } = true; public bool IsInline { get; set; } - public Dictionary RawTable => children ??= new Dictionary(); + public Dictionary RawTable => this.children ??= new Dictionary(); public override TomlNode this[string key] { get { - if (RawTable.TryGetValue(key, out var result)) return result; + if (this.RawTable.TryGetValue(key, out var result)) return result; var lazy = new TomlLazy(this); - RawTable[key] = lazy; + this.RawTable[key] = lazy; return lazy; } - set => RawTable[key] = value; + set => this.RawTable[key] = value; } - public override int ChildrenCount => RawTable.Count; - public override IEnumerable Children => RawTable.Select(kv => kv.Value); - public override IEnumerable Keys => RawTable.Select(kv => kv.Key); + public override int ChildrenCount => this.RawTable.Count; + public override IEnumerable Children => this.RawTable.Select(kv => kv.Value); + public override IEnumerable Keys => this.RawTable.Select(kv => kv.Key); public override bool HasKey(string key) { - return RawTable.ContainsKey(key); + return this.RawTable.ContainsKey(key); } public override void Add(string key, TomlNode node) { - RawTable.Add(key, node); + this.RawTable.Add(key, node); } public override bool TryGetNode(string key, out TomlNode node) { - return RawTable.TryGetValue(key, out node); + return this.RawTable.TryGetValue(key, out node); } public override void Delete(TomlNode node) { - RawTable.Remove(RawTable.First(kv => kv.Value == node).Key); + this.RawTable.Remove(this.RawTable.First(kv => kv.Value == node).Key); } public override void Delete(string key) { - RawTable.Remove(key); + this.RawTable.Remove(key); } public override string ToString() @@ -509,12 +510,12 @@ public override string ToString() var sb = new StringBuilder(); sb.Append(TomlSyntax.INLINE_TABLE_START_SYMBOL); - if (ChildrenCount != 0) + if (this.ChildrenCount != 0) { - var collapsed = CollectCollapsedItems(out var nonCollapsible); + var collapsed = this.CollectCollapsedItems(out var nonCollapsible); sb.Append(' '); - sb.Append($"{TomlSyntax.ITEM_SEPARATOR} ".Join(RawTable.Where(n => nonCollapsible.Contains(n.Key)) + sb.Append($"{TomlSyntax.ITEM_SEPARATOR} ".Join(this.RawTable.Where(n => nonCollapsible.Contains(n.Key)) .Select(n => $"{n.Key.AsKey()} {TomlSyntax.KEY_VALUE_SEPARATOR} {n.Value.ToInlineToml()}"))); @@ -539,7 +540,7 @@ Dictionary CollectCollapsedItems(out HashSet nonCollap if (nodes == null) { nodes = new Dictionary(); - foreach (var keyValuePair in RawTable) + foreach (var keyValuePair in this.RawTable) { var node = keyValuePair.Value; var key = keyValuePair.Key.AsKey(); @@ -558,7 +559,7 @@ Dictionary CollectCollapsedItems(out HashSet nonCollap return nodes; } - foreach (var keyValuePair in RawTable) + foreach (var keyValuePair in this.RawTable) { var node = keyValuePair.Value; var key = keyValuePair.Key.AsKey(); @@ -585,20 +586,20 @@ Dictionary CollectCollapsedItems(out HashSet nonCollap public override void WriteTo(TextWriter tw, string name = null) { // The table is inline table - if (IsInline && name != null) + if (this.IsInline && name != null) { - tw.Write(ToInlineToml()); + tw.Write(this.ToInlineToml()); return; } - if (RawTable.All(n => n.Value.CollapseLevel != 0)) + if (this.RawTable.All(n => n.Value.CollapseLevel != 0)) return; - var hasRealValues = !RawTable.All(n => n.Value is TomlTable { IsInline: false }); + var hasRealValues = !this.RawTable.All(n => n.Value is TomlTable { IsInline: false }); - var collapsedItems = CollectCollapsedItems(out _); + var collapsedItems = this.CollectCollapsedItems(out _); - Comment?.AsComment(tw); + this.Comment?.AsComment(tw); if (name != null && (hasRealValues || collapsedItems.Count > 0)) { @@ -607,7 +608,7 @@ public override void WriteTo(TextWriter tw, string name = null) tw.Write(TomlSyntax.ARRAY_END_SYMBOL); tw.WriteLine(); } - else if (Comment != null) // Add some spacing between the first node and the comment + else if (this.Comment != null) // Add some spacing between the first node and the comment { tw.WriteLine(); } @@ -617,7 +618,7 @@ public override void WriteTo(TextWriter tw, string name = null) var sectionableItems = new Dictionary(); - foreach (var child in RawTable) + foreach (var child in this.RawTable) { // If value should be parsed as section, separate if from the bunch if (child.Value is TomlArray { IsTableArray: true } || child.Value is TomlTable { IsInline: false }) @@ -690,59 +691,60 @@ public TomlLazy(TomlNode parent) public override TomlNode this[int index] { - get => Set()[index]; - set => Set()[index] = value; + get => this.Set()[index]; + set => this.Set()[index] = value; } public override TomlNode this[string key] { - get => Set()[key]; - set => Set()[key] = value; + get => this.Set()[key]; + set => this.Set()[key] = value; } public override void Add(TomlNode node) { - Set().Add(node); + this.Set().Add(node); } public override void Add(string key, TomlNode node) { - Set().Add(key, node); + this.Set().Add(key, node); } public override void AddRange(IEnumerable nodes) { - Set().AddRange(nodes); + this.Set().AddRange(nodes); } TomlNode Set() where T : TomlNode, new() { - if (replacement != null) return replacement; + if (this.replacement != null) return this.replacement; var newNode = new T { - Comment = Comment + Comment = this.Comment }; - if (parent.IsTable) + if (this.parent.IsTable) { - var key = parent.Keys.FirstOrDefault(s => parent.TryGetNode(s, out var node) && node.Equals(this)); + var key = this.parent.Keys.FirstOrDefault(s => + this.parent.TryGetNode(s, out var node) && node.Equals(this)); if (key == null) return default(T); - parent[key] = newNode; + this.parent[key] = newNode; } - else if (parent.IsArray) + else if (this.parent.IsArray) { - var index = parent.Children.TakeWhile(child => child != this).Count(); - if (index == parent.ChildrenCount) return default(T); - parent[index] = newNode; + var index = this.parent.Children.TakeWhile(child => child != this).Count(); + if (index == this.parent.ChildrenCount) return default(T); + this.parent[index] = newNode; } else { return default(T); } - replacement = newNode; + this.replacement = newNode; return newNode; } } @@ -769,34 +771,34 @@ public enum ParseState public TOMLParser(TextReader reader) { this.reader = reader; - line = col = 0; + this.line = this.col = 0; } public bool ForceASCII { get; set; } public void Dispose() { - reader?.Dispose(); + this.reader?.Dispose(); } public TomlTable Parse() { - syntaxErrors = new List(); - line = col = 0; + this.syntaxErrors = new List(); + this.line = this.col = 0; var rootNode = new TomlTable(); var currentNode = rootNode; - currentState = ParseState.None; + this.currentState = ParseState.None; var keyParts = new List(); var arrayTable = false; var latestComment = new StringBuilder(); var firstComment = true; int currentChar; - while ((currentChar = reader.Peek()) >= 0) + while ((currentChar = this.reader.Peek()) >= 0) { var c = (char)currentChar; - if (currentState == ParseState.None) + if (this.currentState == ParseState.None) { // Skip white space if (TomlSyntax.IsWhiteSpace(c)) goto consume_character; @@ -811,8 +813,7 @@ public TomlTable Parse() firstComment = false; } - if (TomlSyntax.IsLineBreak(c)) - AdvanceLine(); + if (TomlSyntax.IsLineBreak(c)) this.AdvanceLine(); goto consume_character; } @@ -821,9 +822,9 @@ public TomlTable Parse() if (c == TomlSyntax.COMMENT_SYMBOL) { // Consume the comment symbol and buffer the whole comment line - reader.Read(); - latestComment.AppendLine(reader.ReadLine()?.Trim()); - AdvanceLine(0); + this.reader.Read(); + latestComment.AppendLine(this.reader.ReadLine()?.Trim()); + this.AdvanceLine(0); continue; } @@ -832,45 +833,43 @@ public TomlTable Parse() if (c == TomlSyntax.TABLE_START_SYMBOL) { - currentState = ParseState.Table; + this.currentState = ParseState.Table; goto consume_character; } if (TomlSyntax.IsBareKey(c) || TomlSyntax.IsQuoted(c)) { - currentState = ParseState.KeyValuePair; + this.currentState = ParseState.KeyValuePair; } else { - AddError($"Unexpected character \"{c}\""); + this.AddError($"Unexpected character \"{c}\""); continue; } } - if (currentState == ParseState.KeyValuePair) + if (this.currentState == ParseState.KeyValuePair) { - var keyValuePair = ReadKeyValuePair(keyParts); + var keyValuePair = this.ReadKeyValuePair(keyParts); if (keyValuePair == null) { latestComment.Length = 0; keyParts.Clear(); - if (currentState != ParseState.None) - AddError("Failed to parse key-value pair!"); + if (this.currentState != ParseState.None) this.AddError("Failed to parse key-value pair!"); continue; } keyValuePair.Comment = latestComment.ToString().TrimEnd(); - var inserted = InsertNode(keyValuePair, currentNode, keyParts); + var inserted = this.InsertNode(keyValuePair, currentNode, keyParts); latestComment.Length = 0; keyParts.Clear(); - if (inserted) - currentState = ParseState.SkipToNextLine; + if (inserted) this.currentState = ParseState.SkipToNextLine; continue; } - if (currentState == ParseState.Table) + if (this.currentState == ParseState.Table) { if (keyParts.Count == 0) { @@ -878,11 +877,11 @@ public TomlTable Parse() if (c == TomlSyntax.TABLE_START_SYMBOL) { // Consume the character - ConsumeChar(); + this.ConsumeChar(); arrayTable = true; } - if (!ReadKeyName(ref keyParts, TomlSyntax.TABLE_END_SYMBOL, true)) + if (!this.ReadKeyName(ref keyParts, TomlSyntax.TABLE_END_SYMBOL, true)) { keyParts.Clear(); continue; @@ -890,7 +889,7 @@ public TomlTable Parse() if (keyParts.Count == 0) { - AddError("Table name is emtpy."); + this.AddError("Table name is emtpy."); arrayTable = false; latestComment.Length = 0; keyParts.Clear(); @@ -904,11 +903,11 @@ public TomlTable Parse() if (arrayTable) { // Consume the ending bracket so we can peek the next character - ConsumeChar(); - var nextChar = reader.Peek(); + this.ConsumeChar(); + var nextChar = this.reader.Peek(); if (nextChar < 0 || (char)nextChar != TomlSyntax.TABLE_END_SYMBOL) { - AddError($"Array table {".".Join(keyParts)} has only one closing bracket."); + this.AddError($"Array table {".".Join(keyParts)} has only one closing bracket."); keyParts.Clear(); arrayTable = false; latestComment.Length = 0; @@ -916,7 +915,7 @@ public TomlTable Parse() } } - currentNode = CreateTable(rootNode, keyParts, arrayTable); + currentNode = this.CreateTable(rootNode, keyParts, arrayTable); if (currentNode != null) { currentNode.IsInline = false; @@ -929,81 +928,80 @@ public TomlTable Parse() if (currentNode == null) { - if (currentState != ParseState.None) - AddError("Error creating table array!"); + if (this.currentState != ParseState.None) this.AddError("Error creating table array!"); continue; } - currentState = ParseState.SkipToNextLine; + this.currentState = ParseState.SkipToNextLine; goto consume_character; } if (keyParts.Count != 0) { - AddError($"Unexpected character \"{c}\""); + this.AddError($"Unexpected character \"{c}\""); keyParts.Clear(); arrayTable = false; latestComment.Length = 0; } } - if (currentState == ParseState.SkipToNextLine) + if (this.currentState == ParseState.SkipToNextLine) { if (TomlSyntax.IsWhiteSpace(c) || c == TomlSyntax.NEWLINE_CARRIAGE_RETURN_CHARACTER) goto consume_character; if (c == TomlSyntax.COMMENT_SYMBOL || c == TomlSyntax.NEWLINE_CHARACTER) { - currentState = ParseState.None; - AdvanceLine(); + this.currentState = ParseState.None; + this.AdvanceLine(); if (c == TomlSyntax.COMMENT_SYMBOL) { - col++; - reader.ReadLine(); + this.col++; + this.reader.ReadLine(); continue; } goto consume_character; } - AddError($"Unexpected character \"{c}\" at the end of the line."); + this.AddError($"Unexpected character \"{c}\" at the end of the line."); } consume_character: - reader.Read(); - col++; + this.reader.Read(); + this.col++; } - if (currentState != ParseState.None && currentState != ParseState.SkipToNextLine) - AddError("Unexpected end of file!"); + if (this.currentState != ParseState.None && this.currentState != ParseState.SkipToNextLine) + this.AddError("Unexpected end of file!"); - if (syntaxErrors.Count > 0) - throw new TomlParseException(rootNode, syntaxErrors); + if (this.syntaxErrors.Count > 0) + throw new TomlParseException(rootNode, this.syntaxErrors); return rootNode; } bool AddError(string message) { - syntaxErrors.Add(new TomlSyntaxException(message, currentState, line, col)); + this.syntaxErrors.Add(new TomlSyntaxException(message, this.currentState, this.line, this.col)); // Skip the whole line in hope that it was only a single faulty value (and non-multiline one at that) - reader.ReadLine(); - AdvanceLine(0); - currentState = ParseState.None; + this.reader.ReadLine(); + this.AdvanceLine(0); + this.currentState = ParseState.None; return false; } void AdvanceLine(int startCol = -1) { - line++; - col = startCol; + this.line++; + this.col = startCol; } int ConsumeChar() { - col++; - return reader.Read(); + this.col++; + return this.reader.Read(); } #region Key-Value pair parsing @@ -1012,7 +1010,7 @@ int ConsumeChar() * Reads a single key-value pair. * Assumes the cursor is at the first character that belong to the pair (including possible whitespace). * Consumes all characters that belong to the key and the value (ignoring possible trailing whitespace at the end). - * + * * Example: * foo = "bar" ==> foo = "bar" * ^ ^ @@ -1020,7 +1018,7 @@ int ConsumeChar() TomlNode ReadKeyValuePair(List keyParts) { int cur; - while ((cur = reader.Peek()) >= 0) + while ((cur = this.reader.Peek()) >= 0) { var c = (char)cur; @@ -1028,11 +1026,11 @@ TomlNode ReadKeyValuePair(List keyParts) { if (keyParts.Count != 0) { - AddError("Encountered extra characters in key definition!"); + this.AddError("Encountered extra characters in key definition!"); return null; } - if (!ReadKeyName(ref keyParts, TomlSyntax.KEY_VALUE_SEPARATOR)) + if (!this.ReadKeyName(ref keyParts, TomlSyntax.KEY_VALUE_SEPARATOR)) return null; continue; @@ -1040,17 +1038,17 @@ TomlNode ReadKeyValuePair(List keyParts) if (TomlSyntax.IsWhiteSpace(c)) { - ConsumeChar(); + this.ConsumeChar(); continue; } if (c == TomlSyntax.KEY_VALUE_SEPARATOR) { - ConsumeChar(); - return ReadValue(); + this.ConsumeChar(); + return this.ReadValue(); } - AddError($"Unexpected character \"{c}\" in key name."); + this.AddError($"Unexpected character \"{c}\" in key name."); return null; } @@ -1061,7 +1059,7 @@ TomlNode ReadKeyValuePair(List keyParts) * Reads a single value. * Assumes the cursor is at the first character that belongs to the value (including possible starting whitespace). * Consumes all characters belonging to the value (ignoring possible trailing whitespace at the end). - * + * * Example: * "test" ==> "test" * ^ ^ @@ -1069,19 +1067,19 @@ TomlNode ReadKeyValuePair(List keyParts) TomlNode ReadValue(bool skipNewlines = false) { int cur; - while ((cur = reader.Peek()) >= 0) + while ((cur = this.reader.Peek()) >= 0) { var c = (char)cur; if (TomlSyntax.IsWhiteSpace(c)) { - ConsumeChar(); + this.ConsumeChar(); continue; } if (c == TomlSyntax.COMMENT_SYMBOL) { - AddError("No value found!"); + this.AddError("No value found!"); return null; } @@ -1089,26 +1087,26 @@ TomlNode ReadValue(bool skipNewlines = false) { if (skipNewlines) { - reader.Read(); - AdvanceLine(0); + this.reader.Read(); + this.AdvanceLine(0); continue; } - AddError("Encountered a newline when expecting a value!"); + this.AddError("Encountered a newline when expecting a value!"); return null; } if (TomlSyntax.IsQuoted(c)) { - var isMultiline = IsTripleQuote(c, out var excess); + var isMultiline = this.IsTripleQuote(c, out var excess); // Error occurred in triple quote parsing - if (currentState == ParseState.None) + if (this.currentState == ParseState.None) return null; var value = isMultiline - ? ReadQuotedValueMultiLine(c) - : ReadQuotedValueSingleLine(c, excess); + ? this.ReadQuotedValueMultiLine(c) + : this.ReadQuotedValueSingleLine(c, excess); return new TomlString { @@ -1120,9 +1118,9 @@ TomlNode ReadValue(bool skipNewlines = false) return c switch { - TomlSyntax.INLINE_TABLE_START_SYMBOL => ReadInlineTable(), - TomlSyntax.ARRAY_START_SYMBOL => ReadArray(), - _ => ReadTomlValue() + TomlSyntax.INLINE_TABLE_START_SYMBOL => this.ReadInlineTable(), + TomlSyntax.ARRAY_START_SYMBOL => this.ReadArray(), + _ => this.ReadTomlValue() }; } @@ -1133,11 +1131,11 @@ TomlNode ReadValue(bool skipNewlines = false) * Reads a single key name. * Assumes the cursor is at the first character belonging to the key (with possible trailing whitespace if `skipWhitespace = true`). * Consumes all the characters until the `until` character is met (but does not consume the character itself). - * + * * Example 1: * foo.bar ==> foo.bar (`skipWhitespace = false`, `until = ' '`) * ^ ^ - * + * * Example 2: * [ foo . bar ] ==> [ foo . bar ] (`skipWhitespace = true`, `until = ']'`) * ^ ^ @@ -1148,7 +1146,7 @@ bool ReadKeyName(ref List parts, char until, bool skipWhitespace = false var quoted = false; var prevWasSpace = false; int cur; - while ((cur = reader.Peek()) >= 0) + while ((cur = this.reader.Peek()) >= 0) { var c = (char)cur; @@ -1171,7 +1169,7 @@ bool ReadKeyName(ref List parts, char until, bool skipWhitespace = false if (c == TomlSyntax.SUBKEY_SEPARATOR) { if (buffer.Length == 0) - return AddError($"Found an extra subkey separator in {".".Join(parts)}..."); + return this.AddError($"Found an extra subkey separator in {".".Join(parts)}..."); parts.Add(buffer.ToString()); buffer.Length = 0; @@ -1181,20 +1179,20 @@ bool ReadKeyName(ref List parts, char until, bool skipWhitespace = false } if (prevWasSpace) - return AddError("Invalid spacing in key name"); + return this.AddError("Invalid spacing in key name"); if (TomlSyntax.IsQuoted(c)) { if (quoted) - return AddError("Expected a subkey separator but got extra data instead!"); + return this.AddError("Expected a subkey separator but got extra data instead!"); if (buffer.Length != 0) - return AddError("Encountered a quote in the middle of subkey name!"); + return this.AddError("Encountered a quote in the middle of subkey name!"); // Consume the quote character and read the key name - col++; - buffer.Append(ReadQuotedValueSingleLine((char)reader.Read())); + this.col++; + buffer.Append(this.ReadQuotedValueSingleLine((char)this.reader.Read())); quoted = true; continue; } @@ -1209,12 +1207,12 @@ bool ReadKeyName(ref List parts, char until, bool skipWhitespace = false break; consume_character: - reader.Read(); - col++; + this.reader.Read(); + this.col++; } if (buffer.Length == 0) - return AddError($"Found an extra subkey separator in {".".Join(parts)}..."); + return this.AddError($"Found an extra subkey separator in {".".Join(parts)}..."); parts.Add(buffer.ToString()); @@ -1229,7 +1227,7 @@ bool ReadKeyName(ref List parts, char until, bool skipWhitespace = false * Reads the whole raw value until the first non-value character is encountered. * Assumes the cursor start position at the first value character and consumes all characters that may be related to the value. * Example: - * + * * 1_0_0_0 ==> 1_0_0_0 * ^ ^ */ @@ -1237,12 +1235,12 @@ string ReadRawValue() { var result = new StringBuilder(); int cur; - while ((cur = reader.Peek()) >= 0) + while ((cur = this.reader.Peek()) >= 0) { var c = (char)cur; if (c == TomlSyntax.COMMENT_SYMBOL || TomlSyntax.IsNewLine(c) || TomlSyntax.IsValueSeparator(c)) break; result.Append(c); - ConsumeChar(); + this.ConsumeChar(); } // Replace trim with manual space counting? @@ -1253,7 +1251,7 @@ string ReadRawValue() * Reads and parses a non-string, non-composite TOML value. * Assumes the cursor at the first character that is related to the value (with possible spaces). * Consumes all the characters that are related to the value. - * + * * Example * 1_0_0_0 # This is a comment * @@ -1262,7 +1260,7 @@ string ReadRawValue() */ TomlNode ReadTomlValue() { - var value = ReadRawValue(); + var value = this.ReadRawValue(); TomlNode node = value switch { var v when TomlSyntax.IsBoolean(v) => bool.Parse(v), @@ -1329,14 +1327,14 @@ var v when TomlSyntax.IsFloat(v) => double.Parse(value.RemoveAll(TomlSyntax.INT_ SecondsPrecision = precision }; - AddError($"Value \"{value}\" is not a valid TOML 0.5.0 value!"); + this.AddError($"Value \"{value}\" is not a valid TOML 0.5.0 value!"); return null; } /** * Reads an array value. * Assumes the cursor is at the start of the array definition. Reads all character until the array closing bracket. - * + * * Example: * [1, 2, 3] ==> [1, 2, 3] * ^ ^ @@ -1344,32 +1342,31 @@ var v when TomlSyntax.IsFloat(v) => double.Parse(value.RemoveAll(TomlSyntax.INT_ TomlArray ReadArray() { // Consume the start of array character - ConsumeChar(); + this.ConsumeChar(); var result = new TomlArray(); TomlNode currentValue = null; int cur; - while ((cur = reader.Peek()) >= 0) + while ((cur = this.reader.Peek()) >= 0) { var c = (char)cur; if (c == TomlSyntax.ARRAY_END_SYMBOL) { - ConsumeChar(); + this.ConsumeChar(); break; } if (c == TomlSyntax.COMMENT_SYMBOL) { - reader.ReadLine(); - AdvanceLine(0); + this.reader.ReadLine(); + this.AdvanceLine(0); continue; } if (TomlSyntax.IsWhiteSpace(c) || TomlSyntax.IsNewLine(c)) { - if (TomlSyntax.IsLineBreak(c)) - AdvanceLine(); + if (TomlSyntax.IsLineBreak(c)) this.AdvanceLine(); goto consume_character; } @@ -1377,7 +1374,7 @@ TomlArray ReadArray() { if (currentValue == null) { - AddError("Encountered multiple value separators in an array!"); + this.AddError("Encountered multiple value separators in an array!"); return null; } @@ -1386,24 +1383,23 @@ TomlArray ReadArray() goto consume_character; } - currentValue = ReadValue(true); + currentValue = this.ReadValue(true); if (currentValue == null) { - if (currentState != ParseState.None) - AddError("Failed to determine and parse a value!"); + if (this.currentState != ParseState.None) this.AddError("Failed to determine and parse a value!"); return null; } if (result.ChildrenCount != 0 && result[0].GetType() != currentValue.GetType()) { - AddError( + this.AddError( $"Arrays cannot have mixed types! Inferred type: {result[0].GetType().FullName}. Element type: {currentValue.GetType().FullName}"); return null; } continue; consume_character: - ConsumeChar(); + this.ConsumeChar(); } if (currentValue != null) result.Add(currentValue); @@ -1413,37 +1409,37 @@ TomlArray ReadArray() /** * Reads an inline table. * Assumes the cursor is at the start of the table definition. Reads all character until the table closing bracket. - * + * * Example: * { test = "foo", value = 1 } ==> { test = "foo", value = 1 } * ^ ^ */ TomlNode ReadInlineTable() { - ConsumeChar(); + this.ConsumeChar(); var result = new TomlTable { IsInline = true }; TomlNode currentValue = null; var keyParts = new List(); int cur; - while ((cur = reader.Peek()) >= 0) + while ((cur = this.reader.Peek()) >= 0) { var c = (char)cur; if (c == TomlSyntax.INLINE_TABLE_END_SYMBOL) { - ConsumeChar(); + this.ConsumeChar(); break; } if (c == TomlSyntax.COMMENT_SYMBOL) { - AddError("Incomplete inline table definition!"); + this.AddError("Incomplete inline table definition!"); return null; } if (TomlSyntax.IsNewLine(c)) { - AddError("Inline tables are only allowed to be on single line"); + this.AddError("Inline tables are only allowed to be on single line"); return null; } @@ -1454,25 +1450,25 @@ TomlNode ReadInlineTable() { if (currentValue == null) { - AddError("Encountered multiple value separators in inline table!"); + this.AddError("Encountered multiple value separators in inline table!"); return null; } - if (!InsertNode(currentValue, result, keyParts)) + if (!this.InsertNode(currentValue, result, keyParts)) return null; keyParts.Clear(); currentValue = null; goto consume_character; } - currentValue = ReadKeyValuePair(keyParts); + currentValue = this.ReadKeyValuePair(keyParts); continue; consume_character: - ConsumeChar(); + this.ConsumeChar(); } - if (currentValue != null && !InsertNode(currentValue, result, keyParts)) + if (currentValue != null && !this.InsertNode(currentValue, result, keyParts)) return null; return result; @@ -1485,17 +1481,17 @@ TomlNode ReadInlineTable() /** * Checks if the string value a multiline string (i.e. a triple quoted string). * Assumes the cursor is at the first quote character. Consumes the least amount of characters needed to determine if the string is multiline. - * + * * If the result is false, returns the consumed character through the `excess` variable. - * + * * Example 1: * """test""" ==> """test""" * ^ ^ - * + * * Example 2: * "test" ==> "test" (doesn't return the first quote) * ^ ^ - * + * * Example 3: * "" ==> "" (returns the extra `"` through the `excess` variable) * ^ ^ @@ -1506,11 +1502,11 @@ bool IsTripleQuote(char quote, out char excess) int cur; // Consume the first quote - ConsumeChar(); - if ((cur = reader.Peek()) < 0) + this.ConsumeChar(); + if ((cur = this.reader.Peek()) < 0) { excess = '\0'; - return AddError("Unexpected end of file!"); + return this.AddError("Unexpected end of file!"); } if ((char)cur != quote) @@ -1520,11 +1516,11 @@ bool IsTripleQuote(char quote, out char excess) } // Consume the second quote - excess = (char)ConsumeChar(); - if ((cur = reader.Peek()) < 0 || (char)cur != quote) return false; + excess = (char)this.ConsumeChar(); + if ((cur = this.reader.Peek()) < 0 || (char)cur != quote) return false; // Consume the final quote - ConsumeChar(); + this.ConsumeChar(); excess = '\0'; return true; } @@ -1539,7 +1535,7 @@ bool ProcessQuotedValueCharacter(char quote, ref bool escaped) { if (TomlSyntax.ShouldBeEscaped(c)) - return AddError($"The character U+{c:X8} must be escaped in a string!"); + return this.AddError($"The character U+{c:X8} must be escaped in a string!"); if (escaped) { @@ -1552,7 +1548,7 @@ bool ProcessQuotedValueCharacter(char quote, if (isNonLiteral && c == TomlSyntax.ESCAPE_SYMBOL) escaped = true; if (c == TomlSyntax.NEWLINE_CHARACTER) - return AddError("Encountered newline in single line string!"); + return this.AddError("Encountered newline in single line string!"); sb.Append(c); return false; @@ -1562,7 +1558,7 @@ bool ProcessQuotedValueCharacter(char quote, * Reads a single-line string. * Assumes the cursor is at the first character that belongs to the string. * Consumes all characters that belong to the string (including the closing quote). - * + * * Example: * "test" ==> "test" * ^ ^ @@ -1575,21 +1571,20 @@ string ReadQuotedValueSingleLine(char quote, char initialData = '\0') if (initialData != '\0') { - var shouldReturn = - ProcessQuotedValueCharacter(quote, isNonLiteral, initialData, sb, ref escaped); - if (currentState == ParseState.None) return null; + var shouldReturn = this.ProcessQuotedValueCharacter(quote, isNonLiteral, initialData, sb, ref escaped); + if (this.currentState == ParseState.None) return null; if (shouldReturn) return isNonLiteral ? sb.ToString().Unescape() : sb.ToString(); } int cur; - while ((cur = reader.Read()) >= 0) + while ((cur = this.reader.Read()) >= 0) { // Consume the character - col++; + this.col++; var c = (char)cur; - if (ProcessQuotedValueCharacter(quote, isNonLiteral, c, sb, ref escaped)) + if (this.ProcessQuotedValueCharacter(quote, isNonLiteral, c, sb, ref escaped)) { - if (currentState == ParseState.None) return null; + if (this.currentState == ParseState.None) return null; break; } } @@ -1601,7 +1596,7 @@ string ReadQuotedValueSingleLine(char quote, char initialData = '\0') * Reads a multiline string. * Assumes the cursor is at the first character that belongs to the string. * Consumes all characters that belong to the string and the three closing quotes. - * + * * Example: * """test""" ==> """test""" * ^ ^ @@ -1615,7 +1610,7 @@ string ReadQuotedValueMultiLine(char quote) var quotesEncountered = 0; var first = true; int cur; - while ((cur = ConsumeChar()) >= 0) + while ((cur = this.ConsumeChar()) >= 0) { var c = (char)cur; if (TomlSyntax.ShouldBeEscaped(c)) @@ -1626,7 +1621,7 @@ string ReadQuotedValueMultiLine(char quote) if (TomlSyntax.IsLineBreak(c)) first = false; else - AdvanceLine(); + this.AdvanceLine(); continue; } @@ -1645,8 +1640,7 @@ string ReadQuotedValueMultiLine(char quote) { if (TomlSyntax.IsEmptySpace(c)) { - if (TomlSyntax.IsLineBreak(c)) - AdvanceLine(); + if (TomlSyntax.IsLineBreak(c)) this.AdvanceLine(); continue; } @@ -1656,7 +1650,7 @@ string ReadQuotedValueMultiLine(char quote) // If we encounter an escape sequence... if (isBasic && c == TomlSyntax.ESCAPE_SYMBOL) { - var next = reader.Peek(); + var next = this.reader.Peek(); if (next >= 0) { // ...and the next char is empty space, we must skip all whitespaces @@ -1702,7 +1696,7 @@ bool InsertNode(TomlNode node, TomlNode root, IList path) if (latestNode.TryGetNode(subkey, out var currentNode)) { if (currentNode.HasValue) - return AddError($"The key {".".Join(path)} already has a value assigned to it!"); + return this.AddError($"The key {".".Join(path)} already has a value assigned to it!"); } else { @@ -1714,7 +1708,7 @@ bool InsertNode(TomlNode node, TomlNode root, IList path) } if (latestNode.HasKey(path[^1])) - return AddError($"The key {".".Join(path)} is already defined!"); + return this.AddError($"The key {".".Join(path)} is already defined!"); latestNode[path[^1]] = node; node.CollapseLevel = path.Count - 1; return true; @@ -1736,7 +1730,7 @@ TomlTable CreateTable(TomlNode root, IList path, bool arrayTable) if (!arr.IsTableArray) { - AddError($"The array {".".Join(path)} cannot be redefined as an array table!"); + this.AddError($"The array {".".Join(path)} cannot be redefined as an array table!"); return null; } @@ -1755,7 +1749,7 @@ TomlTable CreateTable(TomlNode root, IList path, bool arrayTable) { if (node is not TomlArray { IsTableArray: true } array) { - AddError($"The key {".".Join(path)} has a value assigned to it!"); + this.AddError($"The key {".".Join(path)} has a value assigned to it!"); return null; } @@ -1767,13 +1761,13 @@ TomlTable CreateTable(TomlNode root, IList path, bool arrayTable) { if (arrayTable && !node.IsArray) { - AddError($"The table {".".Join(path)} cannot be redefined as an array table!"); + this.AddError($"The table {".".Join(path)} cannot be redefined as an array table!"); return null; } if (node is TomlTable { IsInline: false }) { - AddError($"The table {".".Join(path)} is defined multiple times!"); + this.AddError($"The table {".".Join(path)} is defined multiple times!"); return null; } } @@ -1837,8 +1831,8 @@ public class TomlParseException : Exception public TomlParseException(TomlTable parsed, IEnumerable exceptions) : base("TOML file contains format errors") { - ParsedTable = parsed; - SyntaxErrors = exceptions; + this.ParsedTable = parsed; + this.SyntaxErrors = exceptions; } public TomlTable ParsedTable { get; } @@ -1850,9 +1844,9 @@ public class TomlSyntaxException : Exception { public TomlSyntaxException(string message, TOMLParser.ParseState state, int line, int col) : base(message) { - ParseState = state; - Line = line; - Column = col; + this.ParseState = state; + this.Line = line; + this.Column = col; } public TOMLParser.ParseState ParseState { get; } @@ -1899,14 +1893,20 @@ public static bool IsNaN(string s) return s is NAN_VALUE or POS_NAN_VALUE or NEG_NAN_VALUE; } - public static bool IsInteger(string s) => IntegerPattern().IsMatch(s); + public static bool IsInteger(string s) + { + return IntegerPattern().IsMatch(s); + } - public static bool IsFloat(string s) => FloatPattern().IsMatch(s); + public static bool IsFloat(string s) + { + return FloatPattern().IsMatch(s); + } public static bool IsIntegerWithBase(string s, out int numberBase) { numberBase = 10; - + var match = BasedIntegerPattern().Match(s); if (!match.Success) return false; @@ -2033,11 +2033,13 @@ public static bool IsValueSeparator(char c) { return c is ITEM_SEPARATOR or ARRAY_END_SYMBOL or INLINE_TABLE_END_SYMBOL; } - + [GeneratedRegex("^(\\+|-)?(?!_)(0|(?!0)(_?\\d)*)$")] private static partial Regex IntegerPattern(); - [GeneratedRegex("^(\\+|-)?(?!_)(0|(?!0)(_?\\d)+)(((e(\\+|-)?(?!_)(_?\\d)+)?)|(\\.(?!_)(_?\\d)+(e(\\+|-)?(?!_)(_?\\d)+)?))$", RegexOptions.IgnoreCase)] + [GeneratedRegex( + "^(\\+|-)?(?!_)(0|(?!0)(_?\\d)+)(((e(\\+|-)?(?!_)(_?\\d)+)?)|(\\.(?!_)(_?\\d)+(e(\\+|-)?(?!_)(_?\\d)+)?))$", + RegexOptions.IgnoreCase)] private static partial Regex FloatPattern(); [GeneratedRegex("^(\\+|-)?0(?x|b|o)(?!_)(_?[0-9A-F])*$", RegexOptions.IgnoreCase)] diff --git a/ProjBobcat/ProjBobcat/Class/Helper/TOMLParser/TommyExtensions.cs b/ProjBobcat/ProjBobcat/Class/Helper/TOMLParser/TommyExtensions.cs index a2f8f58a..c07305d0 100644 --- a/ProjBobcat/ProjBobcat/Class/Helper/TOMLParser/TommyExtensions.cs +++ b/ProjBobcat/ProjBobcat/Class/Helper/TOMLParser/TommyExtensions.cs @@ -1,16 +1,16 @@ -using System; +#nullable disable + +using System; using System.Collections.Generic; using System.IO; using System.Text; -#nullable disable - namespace ProjBobcat.Class.Helper.TOMLParser; /// /// Class of various extension methods for Tommy /// -public static partial class TommyExtensions +public static class TommyExtensions { /// /// Tries to parse TOML file. diff --git a/ProjBobcat/ProjBobcat/Class/LaunchArgumentParserBase.cs b/ProjBobcat/ProjBobcat/Class/LaunchArgumentParserBase.cs index e879cf0e..02918697 100644 --- a/ProjBobcat/ProjBobcat/Class/LaunchArgumentParserBase.cs +++ b/ProjBobcat/ProjBobcat/Class/LaunchArgumentParserBase.cs @@ -47,12 +47,12 @@ public abstract class LaunchArgumentParserBase( /// Native 根目录 /// public virtual string NativeRoot => - Path.Combine(RootPath, GamePathHelper.GetNativeRoot(LaunchSettings.Version)); + Path.Combine(this.RootPath, GamePathHelper.GetNativeRoot(this.LaunchSettings.Version)); /// /// Asset 根目录 /// - public virtual string AssetRoot => Path.Combine(RootPath, GamePathHelper.GetAssetsRoot()); + public virtual string AssetRoot => Path.Combine(this.RootPath, GamePathHelper.GetAssetsRoot()); /// /// Class 路径 diff --git a/ProjBobcat/ProjBobcat/Class/LaunchWrapper.cs b/ProjBobcat/ProjBobcat/Class/LaunchWrapper.cs index 83c4edbc..0be04e3b 100644 --- a/ProjBobcat/ProjBobcat/Class/LaunchWrapper.cs +++ b/ProjBobcat/ProjBobcat/Class/LaunchWrapper.cs @@ -26,7 +26,7 @@ public class LaunchWrapper(AuthResultBase authResult, LaunchSettings launchSetti public AuthResultBase AuthResult { get; } = authResult; /// - /// 启动设置 + /// 启动设置 /// public LaunchSettings LaunchSettings { get; } = launchSettings; @@ -48,7 +48,7 @@ public class LaunchWrapper(AuthResultBase authResult, LaunchSettings launchSetti public void Dispose() { // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 - Dispose(true); + this.Dispose(true); GC.SuppressFinalize(this); } @@ -57,38 +57,39 @@ public void Dispose() /// public void Do() { - if (Process == null) return; - if (Process.ProcessName != "Minecraft.Windows") + if (this.Process == null) return; + if (this.Process.ProcessName != "Minecraft.Windows") { - Process.BeginOutputReadLine(); - Process.OutputDataReceived += ProcessOnOutputDataReceived; - Process.BeginErrorReadLine(); - Process.ErrorDataReceived += ProcessOnErrorDataReceived; + this.Process.BeginOutputReadLine(); + this.Process.OutputDataReceived += this.ProcessOnOutputDataReceived; + this.Process.BeginErrorReadLine(); + this.Process.ErrorDataReceived += this.ProcessOnErrorDataReceived; } - Process.Exited += ProcessOnExited; + + this.Process.Exited += this.ProcessOnExited; } void DisposeManaged() { - if (Process == null) return; + if (this.Process == null) return; - Process.OutputDataReceived -= ProcessOnOutputDataReceived; - Process.ErrorDataReceived -= ProcessOnErrorDataReceived; - Process.Exited -= ProcessOnExited; + this.Process.OutputDataReceived -= this.ProcessOnOutputDataReceived; + this.Process.ErrorDataReceived -= this.ProcessOnErrorDataReceived; + this.Process.Exited -= this.ProcessOnExited; } void ProcessOnExited(object? sender, EventArgs e) { - if (Process == null) return; + if (this.Process == null) return; - ExitCode = ProcessorHelper.TryGetProcessExitCode(Process, out var code) ? code : 0; + this.ExitCode = ProcessorHelper.TryGetProcessExitCode(this.Process, out var code) ? code : 0; } void ProcessOnErrorDataReceived(object sender, DataReceivedEventArgs e) { if (string.IsNullOrEmpty(e.Data)) return; - if (GameCore is GameCoreBase coreBase) + if (this.GameCore is GameCoreBase coreBase) coreBase.OnLogGameData(sender, new GameLogEventArgs { LogType = GameLogType.Unknown, @@ -99,19 +100,19 @@ void ProcessOnErrorDataReceived(object sender, DataReceivedEventArgs e) void ProcessOnOutputDataReceived(object sender, DataReceivedEventArgs e) { if (string.IsNullOrEmpty(e.Data)) return; - if (GameCore.GameLogResolver == null) return; + if (this.GameCore.GameLogResolver == null) return; - var totalPrefix = GameCore.GameLogResolver.ResolveTotalPrefix(e.Data); - var type = GameCore.GameLogResolver.ResolveLogType(string.IsNullOrEmpty(totalPrefix) + var totalPrefix = this.GameCore.GameLogResolver.ResolveTotalPrefix(e.Data); + var type = this.GameCore.GameLogResolver.ResolveLogType(string.IsNullOrEmpty(totalPrefix) ? e.Data : totalPrefix); if (type is GameLogType.ExceptionMessage or GameLogType.StackTrace) { - var exceptionMsg = GameCore.GameLogResolver.ResolveExceptionMsg(e.Data); - var stackTrace = GameCore.GameLogResolver.ResolveStackTrace(e.Data); + var exceptionMsg = this.GameCore.GameLogResolver.ResolveExceptionMsg(e.Data); + var stackTrace = this.GameCore.GameLogResolver.ResolveStackTrace(e.Data); - if (GameCore is GameCoreBase gameCoreBase) + if (this.GameCore is GameCoreBase gameCoreBase) gameCoreBase.OnLogGameData(sender, new GameLogEventArgs { LogType = type, @@ -123,14 +124,14 @@ void ProcessOnOutputDataReceived(object sender, DataReceivedEventArgs e) return; } - var time = GameCore.GameLogResolver.ResolveTime(totalPrefix); - var source = GameCore.GameLogResolver.ResolveSource(totalPrefix); + var time = this.GameCore.GameLogResolver.ResolveTime(totalPrefix); + var source = this.GameCore.GameLogResolver.ResolveSource(totalPrefix); - if (GameCore is GameCoreBase coreBase) + if (this.GameCore is GameCoreBase coreBase) coreBase.OnLogGameData(sender, new GameLogEventArgs { - GameId = LaunchSettings.Version, + GameId = this.LaunchSettings.Version, LogType = type, RawContent = e.Data, Content = e.Data[(totalPrefix?.Length ?? 0)..], @@ -141,17 +142,17 @@ void ProcessOnOutputDataReceived(object sender, DataReceivedEventArgs e) void Dispose(bool disposing) { - if (!_disposedValue) + if (!this._disposedValue) { if (disposing) { - DisposeManaged(); - Process?.Dispose(); + this.DisposeManaged(); + this.Process?.Dispose(); } // TODO: 释放未托管的资源(未托管的对象)并重写终结器 // TODO: 将大型字段设置为 null - _disposedValue = true; + this._disposedValue = true; } } -} +} \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/AppxPackageInfo.cs b/ProjBobcat/ProjBobcat/Class/Model/AppxPackageInfo.cs index 3574cc96..89368870 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/AppxPackageInfo.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/AppxPackageInfo.cs @@ -110,4 +110,4 @@ public record AppxPackageInfo /// 例如: Ok /// public string? Status { get; set; } -} +} \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/AssetObjectModel.cs b/ProjBobcat/ProjBobcat/Class/Model/AssetObjectModel.cs index 3ea27c83..2c44b89c 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/AssetObjectModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/AssetObjectModel.cs @@ -34,6 +34,4 @@ public class AssetObjectModel } [JsonSerializable(typeof(AssetObjectModel))] -public partial class AssetObjectModelContext : JsonSerializerContext -{ -} \ No newline at end of file +public partial class AssetObjectModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/Auth/MicrosoftAuthResult.cs b/ProjBobcat/ProjBobcat/Class/Model/Auth/MicrosoftAuthResult.cs index 312ac8df..73c05285 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Auth/MicrosoftAuthResult.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Auth/MicrosoftAuthResult.cs @@ -10,7 +10,7 @@ public class MicrosoftAuthResult : AuthResultBase public string? Skin { get; init; } /// - /// 披风 + /// 披风 /// public string? Cape { get; init; } diff --git a/ProjBobcat/ProjBobcat/Class/Model/Auth/YggdrasilAuthResult.cs b/ProjBobcat/ProjBobcat/Class/Model/Auth/YggdrasilAuthResult.cs index 2ebaad70..0f508114 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Auth/YggdrasilAuthResult.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Auth/YggdrasilAuthResult.cs @@ -11,6 +11,7 @@ public class YggdrasilAuthResult : AuthResultBase /// 可用的Profiles /// public ProfileInfoModel[]? Profiles { get; set; } + public string? LocalId { get; set; } public string? RemoteId { get; set; } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/CurseForge/API/FeaturedQueryOptions.cs b/ProjBobcat/ProjBobcat/Class/Model/CurseForge/API/FeaturedQueryOptions.cs index 8b241870..8d1b3764 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/CurseForge/API/FeaturedQueryOptions.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/CurseForge/API/FeaturedQueryOptions.cs @@ -1,5 +1,4 @@ -using System; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace ProjBobcat.Class.Model.CurseForge.API; @@ -26,6 +25,4 @@ public class FeaturedQueryOptions } [JsonSerializable(typeof(FeaturedQueryOptions))] -partial class FeaturedQueryOptionsContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class FeaturedQueryOptionsContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/CurseForge/API/SearchOptions.cs b/ProjBobcat/ProjBobcat/Class/Model/CurseForge/API/SearchOptions.cs index 87a07021..516d4f2d 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/CurseForge/API/SearchOptions.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/CurseForge/API/SearchOptions.cs @@ -17,22 +17,22 @@ public override string ToString() { var result = "?" + - $"gameId={GameId ?? 432}"; + $"gameId={this.GameId ?? 432}"; - if (Index != null) - result += $"&index={Index ?? 0}"; - if (Sort != null) - result += $"&sortOrder={Sort ?? 1}"; - if (PageSize != null) - result += $"&pageSize={PageSize}"; - if (!string.IsNullOrEmpty(GameVersion)) - result += $"&gameVersion={GameVersion}"; - if (!string.IsNullOrEmpty(SearchFilter)) - result += $"&searchFilter={SearchFilter}"; - if (ClassId != null && ParentCategoryId is null) - result += $"&classId={ClassId}"; - if (CategoryId != null) - result += $"&categoryId={CategoryId}"; + if (this.Index != null) + result += $"&index={this.Index ?? 0}"; + if (this.Sort != null) + result += $"&sortOrder={this.Sort ?? 1}"; + if (this.PageSize != null) + result += $"&pageSize={this.PageSize}"; + if (!string.IsNullOrEmpty(this.GameVersion)) + result += $"&gameVersion={this.GameVersion}"; + if (!string.IsNullOrEmpty(this.SearchFilter)) + result += $"&searchFilter={this.SearchFilter}"; + if (this.ClassId != null && this.ParentCategoryId is null) + result += $"&classId={this.ClassId}"; + if (this.CategoryId != null) + result += $"&categoryId={this.CategoryId}"; return result; } diff --git a/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeAddonInfo.cs b/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeAddonInfo.cs index 88074df7..764a788d 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeAddonInfo.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeAddonInfo.cs @@ -21,7 +21,9 @@ public class CurseForgeAddonInfo [JsonPropertyName("gameId")] public int GameId { get; set; } [JsonPropertyName("summary")] public string? Summary { get; set; } - [JsonPropertyName("links")] public IReadOnlyDictionary Links { get; set; } = ImmutableDictionary.Empty; + + [JsonPropertyName("links")] + public IReadOnlyDictionary Links { get; set; } = ImmutableDictionary.Empty; [JsonPropertyName("defaultFileId")] public int DefaultFileId { get; set; } [JsonPropertyName("releaseType")] public int ReleaseType { get; set; } diff --git a/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeFeaturedAddonModel.cs b/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeFeaturedAddonModel.cs index 2b25c9b8..b09160e0 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeFeaturedAddonModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeFeaturedAddonModel.cs @@ -1,16 +1,12 @@ -using System; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace ProjBobcat.Class.Model.CurseForge; public class CurseForgeFeaturedAddonModel { - [JsonPropertyName("featured")] - public CurseForgeAddonInfo[] Featured { get; set; } = []; + [JsonPropertyName("featured")] public CurseForgeAddonInfo[] Featured { get; set; } = []; - [JsonPropertyName("popular")] - public CurseForgeAddonInfo[] Popular { get; set; } = []; + [JsonPropertyName("popular")] public CurseForgeAddonInfo[] Popular { get; set; } = []; - [JsonPropertyName("recentlyUpdated")] - public CurseForgeAddonInfo[] RecentlyUpdated { get; set; } = []; + [JsonPropertyName("recentlyUpdated")] public CurseForgeAddonInfo[] RecentlyUpdated { get; set; } = []; } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeFuzzySearchResponseModel.cs b/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeFuzzySearchResponseModel.cs index d09cdd27..6f526af1 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeFuzzySearchResponseModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeFuzzySearchResponseModel.cs @@ -5,26 +5,21 @@ namespace ProjBobcat.Class.Model.CurseForge; public class CurseForgeFuzzySearchFileModel { - [JsonPropertyName("id")] - public long Id { get; set; } + [JsonPropertyName("id")] public long Id { get; set; } - [JsonPropertyName("file")] - public CurseForgeLatestFileModel? File { get; set; } + [JsonPropertyName("file")] public CurseForgeLatestFileModel? File { get; set; } } public class CurseForgeFuzzySearchResponseModel { - [JsonPropertyName("isCacheBuilt")] - public bool IsCacheBuilt { get; set; } + [JsonPropertyName("isCacheBuilt")] public bool IsCacheBuilt { get; set; } - [JsonPropertyName("exactMatches")] - public CurseForgeFuzzySearchFileModel[]? ExactMatches { get; set; } + [JsonPropertyName("exactMatches")] public CurseForgeFuzzySearchFileModel[]? ExactMatches { get; set; } [JsonPropertyName("exactFingerprints")] public long[]? ExactFingerprints { get; set; } - [JsonPropertyName("partialMatches")] - public CurseForgeFuzzySearchFileModel[]? PartialMatches { get; set; } + [JsonPropertyName("partialMatches")] public CurseForgeFuzzySearchFileModel[]? PartialMatches { get; set; } [JsonPropertyName("partialMatchFingerprints")] public JsonElement? PartialMatchFingerprints { get; set; } diff --git a/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeLatestFileModel.cs b/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeLatestFileModel.cs index 7c7d1868..4ff515bf 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeLatestFileModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeLatestFileModel.cs @@ -43,8 +43,7 @@ public class CurseForgeLatestFileModel [JsonPropertyName("packageFingerprint")] public long PackageFingerprint { get; set; } - [JsonPropertyName("fileFingerprint")] - public long FileFingerprint { get; set; } + [JsonPropertyName("fileFingerprint")] public long FileFingerprint { get; set; } [JsonPropertyName("gameVersions")] public required string[] GameVersions { get; init; } diff --git a/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeSortableGameVersionModel.cs b/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeSortableGameVersionModel.cs index 9dd9fd37..c10b7ba3 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeSortableGameVersionModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/CurseForge/CurseForgeSortableGameVersionModel.cs @@ -8,8 +8,7 @@ public class CurseForgeSortableGameVersionModel [JsonPropertyName("gameVersionPadded")] public string? GameVersionPadded { get; set; } - [JsonPropertyName("gameVersion")] - public string? GameVersion { get; set; } + [JsonPropertyName("gameVersion")] public string? GameVersion { get; set; } [JsonPropertyName("gameVersionReleaseDate")] public DateTime GameVersionReleaseDate { get; set; } diff --git a/ProjBobcat/ProjBobcat/Class/Model/DownloadCheckResult.cs b/ProjBobcat/ProjBobcat/Class/Model/Downloading/DownloadCheckResult.cs similarity index 84% rename from ProjBobcat/ProjBobcat/Class/Model/DownloadCheckResult.cs rename to ProjBobcat/ProjBobcat/Class/Model/Downloading/DownloadCheckResult.cs index 087778d0..e70b662a 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/DownloadCheckResult.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Downloading/DownloadCheckResult.cs @@ -1,6 +1,6 @@ using System; -namespace ProjBobcat.Class.Model; +namespace ProjBobcat.Class.Model.Downloading; public class DownloadCheckResult { diff --git a/ProjBobcat/ProjBobcat/Class/Model/DownloadFile.cs b/ProjBobcat/ProjBobcat/Class/Model/Downloading/DownloadFile.cs similarity index 88% rename from ProjBobcat/ProjBobcat/Class/Model/DownloadFile.cs rename to ProjBobcat/ProjBobcat/Class/Model/Downloading/DownloadFile.cs index 56b6f5f7..f1404704 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/DownloadFile.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Downloading/DownloadFile.cs @@ -1,7 +1,7 @@ using System; using ProjBobcat.Event; -namespace ProjBobcat.Class.Model; +namespace ProjBobcat.Class.Model.Downloading; /// /// 下载文件信息类 @@ -55,7 +55,7 @@ public class DownloadFile public void OnChanged(double speed, double progress, long bytesReceived, long totalBytes) { - Changed?.Invoke(this, new DownloadFileChangedEventArgs + this.Changed?.Invoke(this, new DownloadFileChangedEventArgs { Speed = speed, ProgressPercentage = progress, @@ -66,6 +66,6 @@ public void OnChanged(double speed, double progress, long bytesReceived, long to public void OnCompleted(bool? success, Exception? ex, double averageSpeed) { - Completed?.Invoke(this, new DownloadFileCompletedEventArgs(success, ex, averageSpeed)); + this.Completed?.Invoke(this, new DownloadFileCompletedEventArgs(success, ex, averageSpeed)); } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/DownloadRange.cs b/ProjBobcat/ProjBobcat/Class/Model/Downloading/DownloadRange.cs similarity index 91% rename from ProjBobcat/ProjBobcat/Class/Model/DownloadRange.cs rename to ProjBobcat/ProjBobcat/Class/Model/Downloading/DownloadRange.cs index f7e28e87..efe16866 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/DownloadRange.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Downloading/DownloadRange.cs @@ -1,6 +1,6 @@ using System.Diagnostics; -namespace ProjBobcat.Class.Model; +namespace ProjBobcat.Class.Model.Downloading; /// /// 下载范围类 diff --git a/ProjBobcat/ProjBobcat/Class/Model/DownloadSettings.cs b/ProjBobcat/ProjBobcat/Class/Model/Downloading/DownloadSettings.cs similarity index 55% rename from ProjBobcat/ProjBobcat/Class/Model/DownloadSettings.cs rename to ProjBobcat/ProjBobcat/Class/Model/Downloading/DownloadSettings.cs index 9c98e171..8e0316ef 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/DownloadSettings.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Downloading/DownloadSettings.cs @@ -1,11 +1,8 @@ using System; -using System.IO; using System.Net.Http.Headers; using System.Security.Cryptography; -using System.Threading; -using System.Threading.Tasks; -namespace ProjBobcat.Class.Model; +namespace ProjBobcat.Class.Model.Downloading; public enum HashType { @@ -31,28 +28,27 @@ public class DownloadSettings public TimeSpan Timeout { get; init; } public int DownloadParts { get; set; } public HashType HashType { get; init; } + public bool ShowDownloadProgressForPartialDownload { get; init; } /// - /// 认证 + /// 认证 /// public AuthenticationHeaderValue? Authentication { get; init; } /// - /// 请求源 + /// 请求源 /// public string? Host { get; init; } - public async Task HashDataAsync(Stream stream, CancellationToken? token) + internal HashAlgorithm GetCryptoTransform() { - token ??= CancellationToken.None; - - return HashType switch + return this.HashType switch { - HashType.MD5 => await MD5.HashDataAsync(stream, token.Value), - HashType.SHA1 => await SHA1.HashDataAsync(stream, token.Value), - HashType.SHA256 => await SHA256.HashDataAsync(stream, token.Value), - HashType.SHA384 => await SHA384.HashDataAsync(stream, token.Value), - HashType.SHA512 => await SHA512.HashDataAsync(stream, token.Value), + HashType.MD5 => MD5.Create(), + HashType.SHA1 => SHA1.Create(), + HashType.SHA256 => SHA256.Create(), + HashType.SHA384 => SHA384.Create(), + HashType.SHA512 => SHA512.Create(), _ => throw new NotSupportedException() }; } diff --git a/ProjBobcat/ProjBobcat/Class/Model/Fabric/FabricLoaderArtifactModel.cs b/ProjBobcat/ProjBobcat/Class/Model/Fabric/FabricLoaderArtifactModel.cs index 5f11433b..c4a9fb3b 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Fabric/FabricLoaderArtifactModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Fabric/FabricLoaderArtifactModel.cs @@ -10,4 +10,4 @@ public class FabricLoaderArtifactModel } [JsonSerializable(typeof(FabricLoaderArtifactModel))] -public partial class FabricLoaderArtifactModelContext : JsonSerializerContext {} \ No newline at end of file +public partial class FabricLoaderArtifactModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/Forge/ForgeInstallProfile.cs b/ProjBobcat/ProjBobcat/Class/Model/Forge/ForgeInstallProfile.cs index 8cea8b50..ed5fb3ca 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Forge/ForgeInstallProfile.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Forge/ForgeInstallProfile.cs @@ -56,6 +56,4 @@ public class ForgeInstallProfile } [JsonSerializable(typeof(ForgeInstallProfile))] -partial class ForgeInstallProfileContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class ForgeInstallProfileContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/Forge/LegacyForgeInstallProfile.cs b/ProjBobcat/ProjBobcat/Class/Model/Forge/LegacyForgeInstallProfile.cs index 480ab93d..9382d4b0 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Forge/LegacyForgeInstallProfile.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Forge/LegacyForgeInstallProfile.cs @@ -100,11 +100,7 @@ public class LegacyForgeInstallProfile } [JsonSerializable(typeof(VersionInfo))] -partial class LegacyForgeInstallVersionInfoContext : JsonSerializerContext -{ -} +partial class LegacyForgeInstallVersionInfoContext : JsonSerializerContext; [JsonSerializable(typeof(LegacyForgeInstallProfile))] -partial class LegacyForgeInstallProfileContext : JsonSerializerContext -{ -} +partial class LegacyForgeInstallProfileContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/GameConfigurationManager.cs b/ProjBobcat/ProjBobcat/Class/Model/GameConfigurationManager.cs index 236080a0..60b5a478 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/GameConfigurationManager.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/GameConfigurationManager.cs @@ -12,41 +12,41 @@ public class GameConfigurationManager : IEnumerable public GameConfigurationManager() { - _configuration = []; + this._configuration = []; } public GameConfigurationManager(string path) { var content = File.ReadAllLines(path); - _configuration = GetConfigurationDictionary(content); + this._configuration = GetConfigurationDictionary(content); } public GameConfigurationManager(IReadOnlyCollection list) { - _configuration = GetConfigurationDictionary(list); + this._configuration = GetConfigurationDictionary(list); } public string this[string key] { - get => _configuration[key]; - set => _configuration[key] = value; + get => this._configuration[key]; + set => this._configuration[key] = value; } public IEnumerator> GetEnumerator() { - return _configuration.GetEnumerator(); + return this._configuration.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { - return _configuration.GetEnumerator(); + return this._configuration.GetEnumerator(); } public async Task SaveAsync(string path) { var sb = new StringBuilder(); - foreach (var (key, value) in _configuration) sb.AppendLine($"{key}:{value}"); + foreach (var (key, value) in this._configuration) sb.AppendLine($"{key}:{value}"); await File.WriteAllTextAsync(path, sb.ToString()); } diff --git a/ProjBobcat/ProjBobcat/Class/Model/GameResource/GameModInfoModel.cs b/ProjBobcat/ProjBobcat/Class/Model/GameResource/GameModInfoModel.cs index 35424169..a4247842 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/GameResource/GameModInfoModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/GameResource/GameModInfoModel.cs @@ -32,6 +32,4 @@ public class GameModInfoModel [JsonSerializable(typeof(GameModInfoModel))] [JsonSerializable(typeof(List))] -partial class GameModInfoModelContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class GameModInfoModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/GameResource/GameResourcePackModel.cs b/ProjBobcat/ProjBobcat/Class/Model/GameResource/GameResourcePackModel.cs index c5d9bb2d..c753316a 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/GameResource/GameResourcePackModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/GameResource/GameResourcePackModel.cs @@ -1,5 +1,4 @@ -using System.Runtime.Serialization; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace ProjBobcat.Class.Model.GameResource; @@ -16,6 +15,4 @@ public class GameResourcePackModel } [JsonSerializable(typeof(GameResourcePackModel))] -partial class GameResourcePackModelContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class GameResourcePackModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/JsonContexts/DictionaryContext.cs b/ProjBobcat/ProjBobcat/Class/Model/JsonContexts/DictionaryContext.cs index e5e106d6..37b3f4f7 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/JsonContexts/DictionaryContext.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/JsonContexts/DictionaryContext.cs @@ -4,6 +4,4 @@ namespace ProjBobcat.Class.Model.JsonContexts; [JsonSerializable(typeof(Dictionary))] -partial class DictionaryContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class DictionaryContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/JsonContexts/StringContext.cs b/ProjBobcat/ProjBobcat/Class/Model/JsonContexts/StringContext.cs index e8d37e2c..2d958011 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/JsonContexts/StringContext.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/JsonContexts/StringContext.cs @@ -4,6 +4,4 @@ namespace ProjBobcat.Class.Model.JsonContexts; [JsonSerializable(typeof(string))] [JsonSerializable(typeof(string[]))] -partial class StringContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class StringContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/JvmRulesModel.cs b/ProjBobcat/ProjBobcat/Class/Model/JvmRulesModel.cs index e023cd5b..70fe9497 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/JvmRulesModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/JvmRulesModel.cs @@ -14,14 +14,14 @@ public class OperatingSystemRules public bool IsAllow() { - if (!string.IsNullOrEmpty(Name) && - !Name.Equals(Constants.OsSymbol, StringComparison.OrdinalIgnoreCase)) return false; - - if (!string.IsNullOrEmpty(Arch) && Arch != SystemInfoHelper.GetSystemArch()) return false; - + if (!string.IsNullOrEmpty(this.Name) && + !this.Name.Equals(Constants.OsSymbol, StringComparison.OrdinalIgnoreCase)) return false; + + if (!string.IsNullOrEmpty(this.Arch) && this.Arch != SystemInfoHelper.GetSystemArch()) return false; + if (OperatingSystem.IsWindows()) - if (!string.IsNullOrEmpty(Version) && - Version != $"^{Platforms.Windows.SystemInfoHelper.GetWindowsMajorVersion()}\\.") + if (!string.IsNullOrEmpty(this.Version) && + this.Version != $"^{Platforms.Windows.SystemInfoHelper.GetWindowsMajorVersion()}\\.") return false; return true; diff --git a/ProjBobcat/ProjBobcat/Class/Model/LaunchErrorType.cs b/ProjBobcat/ProjBobcat/Class/Model/LaunchErrorType.cs index 841c6cbf..83aaebc7 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/LaunchErrorType.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/LaunchErrorType.cs @@ -10,4 +10,4 @@ public enum LaunchErrorType IncompleteArguments, UnsupportedOperatingSystem, Unknown -} +} \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/LaunchSettings.cs b/ProjBobcat/ProjBobcat/Class/Model/LaunchSettings.cs index d57685e4..a8339aae 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/LaunchSettings.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/LaunchSettings.cs @@ -63,11 +63,11 @@ public override string ToString() sb .AppendLine() - .Append($"Game Name: {GameName}").AppendLine() - .Append($"Game Resource Path: {GameResourcePath}").AppendLine() - .Append($"Version: {Version}").AppendLine() - .Append($"Authenticator: {Authenticator?.GetType().Name ?? "-"}").AppendLine() - .Append($"Version Insulation: {VersionInsulation}").AppendLine() + .Append($"Game Name: {this.GameName}").AppendLine() + .Append($"Game Resource Path: {this.GameResourcePath}").AppendLine() + .Append($"Version: {this.Version}").AppendLine() + .Append($"Authenticator: {this.Authenticator?.GetType().Name ?? "-"}").AppendLine() + .Append($"Version Insulation: {this.VersionInsulation}").AppendLine() .AppendLine(); return sb.ToString(); diff --git a/ProjBobcat/ProjBobcat/Class/Model/LauncherAccount/LauncherAccountModel.cs b/ProjBobcat/ProjBobcat/Class/Model/LauncherAccount/LauncherAccountModel.cs index 30f739cb..1548ee6f 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/LauncherAccount/LauncherAccountModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/LauncherAccount/LauncherAccountModel.cs @@ -5,8 +5,7 @@ namespace ProjBobcat.Class.Model.LauncherAccount; public class LauncherAccountModel { - [JsonPropertyName("accounts")] - public Dictionary? Accounts { get; set; } + [JsonPropertyName("accounts")] public Dictionary? Accounts { get; set; } [JsonPropertyName("activeAccountLocalId")] public string? ActiveAccountLocalId { get; set; } diff --git a/ProjBobcat/ProjBobcat/Class/Model/LauncherProfile/ResolutionModel.cs b/ProjBobcat/ProjBobcat/Class/Model/LauncherProfile/ResolutionModel.cs index 679d7c10..86baac6b 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/LauncherProfile/ResolutionModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/LauncherProfile/ResolutionModel.cs @@ -13,11 +13,11 @@ public class ResolutionModel : IDefaultValueChecker public bool IsDefault() { - return Width == 0 && Height == 0; + return this.Width == 0 && this.Height == 0; } public override string ToString() { - return $"{Width} * {Height} {(FullScreen ? "[F]" : string.Empty)}"; + return $"{this.Width} * {this.Height} {(this.FullScreen ? "[F]" : string.Empty)}"; } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/LiteLoader/LiteLoaderDownloadVersionModel.cs b/ProjBobcat/ProjBobcat/Class/Model/LiteLoader/LiteLoaderDownloadVersionModel.cs index d2923dc5..620ebfd2 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/LiteLoader/LiteLoaderDownloadVersionModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/LiteLoader/LiteLoaderDownloadVersionModel.cs @@ -14,4 +14,4 @@ public class LiteLoaderDownloadVersionModel } [JsonSerializable(typeof(LiteLoaderDownloadVersionModel))] -public partial class LiteLoaderDownloadVersionModelContext : JsonSerializerContext {} \ No newline at end of file +public partial class LiteLoaderDownloadVersionModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/Microsoft/Graph/DeviceIdResponseModel.cs b/ProjBobcat/ProjBobcat/Class/Model/Microsoft/Graph/DeviceIdResponseModel.cs index ad95bbd6..f17709f1 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Microsoft/Graph/DeviceIdResponseModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Microsoft/Graph/DeviceIdResponseModel.cs @@ -18,6 +18,4 @@ public class DeviceIdResponseModel } [JsonSerializable(typeof(DeviceIdResponseModel))] -partial class DeviceIdResponseModelContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class DeviceIdResponseModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/Microsoft/Graph/GraphAuthResultModel.cs b/ProjBobcat/ProjBobcat/Class/Model/Microsoft/Graph/GraphAuthResultModel.cs index 64ad6b4f..c5b8843d 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Microsoft/Graph/GraphAuthResultModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Microsoft/Graph/GraphAuthResultModel.cs @@ -18,6 +18,4 @@ public class GraphAuthResultModel } [JsonSerializable(typeof(GraphAuthResultModel))] -public partial class GraphAuthResultModelContext : JsonSerializerContext -{ -} \ No newline at end of file +public partial class GraphAuthResultModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/Microsoft/Graph/GraphResponseErrorModel.cs b/ProjBobcat/ProjBobcat/Class/Model/Microsoft/Graph/GraphResponseErrorModel.cs index d6878415..404ec392 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Microsoft/Graph/GraphResponseErrorModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Microsoft/Graph/GraphResponseErrorModel.cs @@ -22,6 +22,4 @@ public class GraphResponseErrorModel } [JsonSerializable(typeof(GraphResponseErrorModel))] -partial class GraphResponseErrorModelContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class GraphResponseErrorModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthMojangResponseModel.cs b/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthMojangResponseModel.cs index cc1416b5..944a0920 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthMojangResponseModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthMojangResponseModel.cs @@ -17,6 +17,4 @@ public class AuthMojangResponseModel } [JsonSerializable(typeof(AuthMojangResponseModel))] -partial class AuthMojangResponseModelContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class AuthMojangResponseModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthXBLRequestModel.cs b/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthXBLRequestModel.cs index 9d91a357..c595e7f6 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthXBLRequestModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthXBLRequestModel.cs @@ -32,6 +32,4 @@ public static AuthXBLRequestModel Get(string accessToken) } [JsonSerializable(typeof(AuthXBLRequestModel))] -partial class AuthXBLRequestModelContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class AuthXBLRequestModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthXSTSErrorModel.cs b/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthXSTSErrorModel.cs index 378fdac9..f5ced59e 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthXSTSErrorModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthXSTSErrorModel.cs @@ -11,6 +11,4 @@ public class AuthXSTSErrorModel } [JsonSerializable(typeof(AuthXSTSErrorModel))] -partial class AuthXSTSErrorModelContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class AuthXSTSErrorModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthXSTSRequestModel.cs b/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthXSTSRequestModel.cs index c252887d..d3311240 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthXSTSRequestModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthXSTSRequestModel.cs @@ -30,6 +30,4 @@ public static AuthXSTSRequestModel Get(string token, string relyingParty = "rp:/ } [JsonSerializable(typeof(AuthXSTSRequestModel))] -partial class AuthXSTSRequestModelContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class AuthXSTSRequestModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthXSTSResponseModel.cs b/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthXSTSResponseModel.cs index 26f1be20..86a782cd 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthXSTSResponseModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/AuthXSTSResponseModel.cs @@ -13,6 +13,4 @@ public class AuthXSTSResponseModel } [JsonSerializable(typeof(AuthXSTSResponseModel))] -partial class AuthXSTSResponseModelContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class AuthXSTSResponseModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/MojangErrorResponseModel.cs b/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/MojangErrorResponseModel.cs index 06301bc3..b09b0df8 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/MojangErrorResponseModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/MojangErrorResponseModel.cs @@ -16,6 +16,4 @@ public class MojangErrorResponseModel } [JsonSerializable(typeof(MojangErrorResponseModel))] -partial class MojangErrorResponseModelContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class MojangErrorResponseModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/MojangOwnershipResponseModel.cs b/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/MojangOwnershipResponseModel.cs index 326be84e..ac86d0d4 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/MojangOwnershipResponseModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/MojangOwnershipResponseModel.cs @@ -19,6 +19,4 @@ public class MojangOwnershipResponseModel } [JsonSerializable(typeof(MojangOwnershipResponseModel))] -partial class MojangOwnershipResponseModelContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class MojangOwnershipResponseModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/MojangProfileResponseModel.cs b/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/MojangProfileResponseModel.cs index ea803a92..b61252e4 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/MojangProfileResponseModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/MicrosoftAuth/MojangProfileResponseModel.cs @@ -29,16 +29,14 @@ public class MojangProfileResponseModel public MojangSkinProfile? GetActiveSkin() { - return Skins?.FirstOrDefault(x => x.State?.Equals("ACTIVE", StringComparison.OrdinalIgnoreCase) ?? false); + return this.Skins?.FirstOrDefault(x => x.State?.Equals("ACTIVE", StringComparison.OrdinalIgnoreCase) ?? false); } public MojangSkinProfile? GetActiveCape() { - return Capes?.FirstOrDefault(x => x.State?.Equals("ACTIVE", StringComparison.OrdinalIgnoreCase) ?? false); + return this.Capes?.FirstOrDefault(x => x.State?.Equals("ACTIVE", StringComparison.OrdinalIgnoreCase) ?? false); } } [JsonSerializable(typeof(MojangProfileResponseModel))] -partial class MojangProfileResponseModelContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class MojangProfileResponseModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/Modrinth/ModrinthSearchOptions.cs b/ProjBobcat/ProjBobcat/Class/Model/Modrinth/ModrinthSearchOptions.cs index 99538d50..e58b9a9d 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Modrinth/ModrinthSearchOptions.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Modrinth/ModrinthSearchOptions.cs @@ -14,22 +14,22 @@ public class ModrinthSearchOptions public override string ToString() { - var sb = new StringBuilder($"?query={Name ?? "any"}&index={Index}"); + var sb = new StringBuilder($"?query={this.Name ?? "any"}&index={this.Index}"); var facets = new List(); - if (!string.IsNullOrEmpty(Category)) - facets.Add($"[\"categories:{Category}\"]"); - if (!string.IsNullOrEmpty(ProjectType)) - facets.Add($"[\"project_type:{ProjectType}\"]"); + if (!string.IsNullOrEmpty(this.Category)) + facets.Add($"[\"categories:{this.Category}\"]"); + if (!string.IsNullOrEmpty(this.ProjectType)) + facets.Add($"[\"project_type:{this.ProjectType}\"]"); if (facets.Count > 0) sb.Append("&facets=[") .AppendJoin(',', facets) .Append(']'); - if (Offset != null) sb.Append($"&offset={Offset}"); - if (Limit != null) sb.Append($"&limit={Limit}"); + if (this.Offset != null) sb.Append($"&offset={this.Offset}"); + if (this.Limit != null) sb.Append($"&limit={this.Limit}"); return sb.ToString(); } diff --git a/ProjBobcat/ProjBobcat/Class/Model/Modrinth/ModrinthSearchResult.cs b/ProjBobcat/ProjBobcat/Class/Model/Modrinth/ModrinthSearchResult.cs index ddb9a63f..b8729b8d 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Modrinth/ModrinthSearchResult.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Modrinth/ModrinthSearchResult.cs @@ -14,6 +14,4 @@ public class ModrinthSearchResult } [JsonSerializable(typeof(ModrinthSearchResult))] -partial class ModrinthSearchResultContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class ModrinthSearchResultContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/Mojang/UserProfile.cs b/ProjBobcat/ProjBobcat/Class/Model/Mojang/UserProfile.cs index e3d7260a..472793ed 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Mojang/UserProfile.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Mojang/UserProfile.cs @@ -19,4 +19,4 @@ public class UserProfile } [JsonSerializable(typeof(UserProfile))] -public partial class UserProfileContext : JsonSerializerContext{} \ No newline at end of file +public partial class UserProfileContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/Mojang/VersionManifest.cs b/ProjBobcat/ProjBobcat/Class/Model/Mojang/VersionManifest.cs index 8b7b2804..7842b1ec 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Mojang/VersionManifest.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Mojang/VersionManifest.cs @@ -26,6 +26,4 @@ public class VersionManifest } [JsonSerializable(typeof(VersionManifest))] -public partial class VersionManifestContext : JsonSerializerContext -{ -} \ No newline at end of file +public partial class VersionManifestContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/NativeReplaceModel.cs b/ProjBobcat/ProjBobcat/Class/Model/NativeReplaceModel.cs index af5fb672..09da064e 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/NativeReplaceModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/NativeReplaceModel.cs @@ -5,11 +5,9 @@ namespace ProjBobcat.Class.Model; public class NativeReplaceModel { - [JsonPropertyName("linux-arm64")] - public required IReadOnlyDictionary LinuxArm64 { get; init; } + [JsonPropertyName("linux-arm64")] public required IReadOnlyDictionary LinuxArm64 { get; init; } - [JsonPropertyName("linux-arm32")] - public required IReadOnlyDictionary LinuxArm86 { get; init; } + [JsonPropertyName("linux-arm32")] public required IReadOnlyDictionary LinuxArm86 { get; init; } [JsonPropertyName("linux-mips64el")] public required IReadOnlyDictionary LinuxMips64El { get; init; } @@ -23,18 +21,14 @@ public class NativeReplaceModel [JsonPropertyName("linux-riscv64")] public required IReadOnlyDictionary LinuxRiscV64 { get; init; } - [JsonPropertyName("windows-x86_64")] - public required IReadOnlyDictionary WindowsX64 { get; init; } + [JsonPropertyName("windows-x86_64")] public required IReadOnlyDictionary WindowsX64 { get; init; } - [JsonPropertyName("windows-x86")] - public required IReadOnlyDictionary WindowsX86 { get; init; } + [JsonPropertyName("windows-x86")] public required IReadOnlyDictionary WindowsX86 { get; init; } [JsonPropertyName("windows-arm64")] public required IReadOnlyDictionary WindowsArm64 { get; init; } - [JsonPropertyName("osx-arm64")] - public required IReadOnlyDictionary OsxArm64 { get; init; } + [JsonPropertyName("osx-arm64")] public required IReadOnlyDictionary OsxArm64 { get; init; } - [JsonPropertyName("freebsd-x86_64")] - public required IReadOnlyDictionary FreeBsdX64 { get; init; } + [JsonPropertyName("freebsd-x86_64")] public required IReadOnlyDictionary FreeBsdX64 { get; init; } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/NeoForge/NeoForgeVersionsModel.cs b/ProjBobcat/ProjBobcat/Class/Model/NeoForge/NeoForgeVersionsModel.cs index a38b54ec..1f8dedea 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/NeoForge/NeoForgeVersionsModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/NeoForge/NeoForgeVersionsModel.cs @@ -10,4 +10,4 @@ public class NeoForgeVersionModel } [JsonSerializable(typeof(NeoForgeVersionModel))] -public partial class NeoForgeVersionsModelContext : JsonSerializerContext { } \ No newline at end of file +public partial class NeoForgeVersionsModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/Optifine/OptifineDownloadVersionModel.cs b/ProjBobcat/ProjBobcat/Class/Model/Optifine/OptifineDownloadVersionModel.cs index ca25a51b..de5459d2 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Optifine/OptifineDownloadVersionModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Optifine/OptifineDownloadVersionModel.cs @@ -4,19 +4,18 @@ namespace ProjBobcat.Class.Model.Optifine; public class OptifineDownloadVersionModel { - [JsonPropertyName("_id")] - public required string Id { get; init; } - [JsonPropertyName("mcversion")] - public required string McVersion { get; init; } - [JsonPropertyName("patch")] - public required string Patch { get; init; } - [JsonPropertyName("type")] - public required string Type { get; init; } - [JsonPropertyName("__v")] - public required int VersionLocker { get; init; } - [JsonPropertyName("filename")] - public required string FileName { get; init; } + [JsonPropertyName("_id")] public required string Id { get; init; } + + [JsonPropertyName("mcversion")] public required string McVersion { get; init; } + + [JsonPropertyName("patch")] public required string Patch { get; init; } + + [JsonPropertyName("type")] public required string Type { get; init; } + + [JsonPropertyName("__v")] public required int VersionLocker { get; init; } + + [JsonPropertyName("filename")] public required string FileName { get; init; } } [JsonSerializable(typeof(OptifineDownloadVersionModel))] -public partial class OptifineDownloadVersionModelContext : JsonSerializerContext {} \ No newline at end of file +public partial class OptifineDownloadVersionModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/PlayerUUID.cs b/ProjBobcat/ProjBobcat/Class/Model/PlayerUUID.cs index 4fccb873..b2e587c4 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/PlayerUUID.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/PlayerUUID.cs @@ -43,44 +43,44 @@ public override bool CanConvertFrom(ITypeDescriptorContext? context, Type source public PlayerUUID(byte[] guidBytes) { - _guid = new Guid(guidBytes); + this._guid = new Guid(guidBytes); } public PlayerUUID(Guid guid) { - _guid = guid; + this._guid = guid; } public PlayerUUID(string guidString) { - _guid = new Guid(guidString); + this._guid = new Guid(guidString); } public int CompareTo(PlayerUUID other) { - return _guid.CompareTo(other._guid); + return this._guid.CompareTo(other._guid); } public override bool Equals(object? obj) { if (obj is PlayerUUID playerUuid) - return _guid.Equals(playerUuid._guid); + return this._guid.Equals(playerUuid._guid); return false; } public bool Equals(PlayerUUID other) { - return _guid.Equals(other._guid); + return this._guid.Equals(other._guid); } public override int GetHashCode() { - return _guid.GetHashCode(); + return this._guid.GetHashCode(); } public string ToString(string? format, IFormatProvider? formatProvider) { - return _guid.ToString(format, formatProvider); + return this._guid.ToString(format, formatProvider); } public static bool operator ==(PlayerUUID left, PlayerUUID right) @@ -95,7 +95,7 @@ public string ToString(string? format, IFormatProvider? formatProvider) public Guid ToGuid() { - return _guid; + return this._guid; } public static PlayerUUID FromOfflinePlayerName(string playerName, string prefix = "OfflinePlayer:") @@ -106,12 +106,12 @@ public static PlayerUUID FromOfflinePlayerName(string playerName, string prefix public string ToString(string format = "N") { - return _guid.ToString(format); + return this._guid.ToString(format); } public override string ToString() { - return _guid.ToString("N"); + return this._guid.ToString("N"); } public static PlayerUUID Random() diff --git a/ProjBobcat/ProjBobcat/Class/Model/ProgressReportBase.cs b/ProjBobcat/ProjBobcat/Class/Model/ProgressReportBase.cs index 3f735787..0e2860ac 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/ProgressReportBase.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/ProgressReportBase.cs @@ -10,7 +10,7 @@ public class ProgressReportBase : IProgressReport protected void InvokeStatusChangedEvent(string currentStage, double progress) { - StageChangedEventDelegate?.Invoke(this, new StageChangedEventArgs + this.StageChangedEventDelegate?.Invoke(this, new StageChangedEventArgs { CurrentStage = currentStage, Progress = progress diff --git a/ProjBobcat/ProjBobcat/Class/Model/Quilt/QuiltLoaderModel.cs b/ProjBobcat/ProjBobcat/Class/Model/Quilt/QuiltLoaderModel.cs index b436a435..6d59831e 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Quilt/QuiltLoaderModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Quilt/QuiltLoaderModel.cs @@ -14,4 +14,4 @@ public class QuiltLoaderModel } [JsonSerializable(typeof(QuiltLoaderModel))] -public partial class QuiltLoaderModelContext : JsonSerializerContext {} \ No newline at end of file +public partial class QuiltLoaderModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/Quilt/QuiltSupportGameModel.cs b/ProjBobcat/ProjBobcat/Class/Model/Quilt/QuiltSupportGameModel.cs index d2a97fee..8df41bdd 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Quilt/QuiltSupportGameModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Quilt/QuiltSupportGameModel.cs @@ -10,4 +10,4 @@ public class QuiltSupportGameModel } [JsonSerializable(typeof(QuiltSupportGameModel[]))] -public partial class QuiltSupportGameModelContext : JsonSerializerContext {} \ No newline at end of file +public partial class QuiltSupportGameModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/RawVersionModel.cs b/ProjBobcat/ProjBobcat/Class/Model/RawVersionModel.cs index 95f37813..6833a201 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/RawVersionModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/RawVersionModel.cs @@ -13,7 +13,7 @@ namespace ProjBobcat.Class.Model; public class FileInfo { [JsonIgnore] public string? Name { get; set; } - + [JsonPropertyName("path")] public string? Path { get; init; } [JsonPropertyName("sha1")] public string? Sha1 { get; set; } @@ -165,7 +165,7 @@ public class RawVersionModel public required string Id { get; set; } /// - /// ClientVersion(Reserved field for other third-party launchers) + /// ClientVersion(Reserved field for other third-party launchers) /// [JsonPropertyName("clientVersion")] public string? ClientVersion { get; set; } diff --git a/ProjBobcat/ProjBobcat/Class/Model/ResourceCompleterCheckResult.cs b/ProjBobcat/ProjBobcat/Class/Model/ResourceCompleterCheckResult.cs index a94286dc..c7538343 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/ResourceCompleterCheckResult.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/ResourceCompleterCheckResult.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using ProjBobcat.Class.Model.Downloading; namespace ProjBobcat.Class.Model; diff --git a/ProjBobcat/ProjBobcat/Class/Model/ServerPing/PingPayload.cs b/ProjBobcat/ProjBobcat/Class/Model/ServerPing/PingPayload.cs index 29a9d4cc..de4c09b2 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/ServerPing/PingPayload.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/ServerPing/PingPayload.cs @@ -17,6 +17,4 @@ public class PingPayload } [JsonSerializable(typeof(PingPayload))] -partial class PingPayloadContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class PingPayloadContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/ServerSettings.cs b/ProjBobcat/ProjBobcat/Class/Model/ServerSettings.cs index cddc4790..62c914b8 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/ServerSettings.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/ServerSettings.cs @@ -8,17 +8,17 @@ public class ServerSettings : IDefaultValueChecker public string? Address { get; set; } public ushort Port { get; set; } - [JsonIgnore] public bool IsDefaultValue => IsDefault(); - [JsonIgnore] public string DisplayStr => ToString(); + [JsonIgnore] public bool IsDefaultValue => this.IsDefault(); + [JsonIgnore] public string DisplayStr => this.ToString(); public bool IsDefault() { - return string.IsNullOrEmpty(Address) && Port == 0; + return string.IsNullOrEmpty(this.Address) && this.Port == 0; } public override string ToString() { - if (IsDefault()) return "[N/A]"; - return $"{Address}:{Port}"; + if (this.IsDefault()) return "[N/A]"; + return $"{this.Address}:{this.Port}"; } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/Version/ComparableVersion.cs b/ProjBobcat/ProjBobcat/Class/Model/Version/ComparableVersion.cs index 3cde9550..cb179bb1 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Version/ComparableVersion.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Version/ComparableVersion.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Linq; using ProjBobcat.Class.Model.Version.Item; namespace ProjBobcat.Class.Model.Version; @@ -58,26 +57,26 @@ public class ComparableVersion : IComparable public ComparableVersion(string version) { - ParseVersion(version); + this.ParseVersion(version); } public string Canonical { - get { return _canonical ??= _items.ToString(); } + get { return this._canonical ??= this._items.ToString(); } } public int CompareTo(ComparableVersion? other) { - return other == null ? 1 : _items.CompareTo(other._items); + return other == null ? 1 : this._items.CompareTo(other._items); } void ParseVersion(string version) { - _value = version; - _items.Clear(); + this._value = version; + this._items.Clear(); version = version.ToLower(CultureInfo.GetCultureInfo("en-US")); - var list = _items; + var list = this._items; var stack = new Stack(); @@ -164,22 +163,21 @@ static IItem ParseItem(bool isDigit, string buf) <= MaxLongItemLength => new LongItem(buf), _ => new BigIntegerItem(buf) }; - } public override string ToString() { - return _value; + return this._value; } public override bool Equals(object? obj) { - return obj is ComparableVersion version && _items.Equals(version._items); + return obj is ComparableVersion version && this._items.Equals(version._items); } public override int GetHashCode() { - return _items.GetHashCode(); + return this._items.GetHashCode(); } public static bool operator >(ComparableVersion a, ComparableVersion b) diff --git a/ProjBobcat/ProjBobcat/Class/Model/Version/Item/BigIntegerItem.cs b/ProjBobcat/ProjBobcat/Class/Model/Version/Item/BigIntegerItem.cs index 595e3167..4f9fb9b7 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Version/Item/BigIntegerItem.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Version/Item/BigIntegerItem.cs @@ -9,12 +9,12 @@ public class BigIntegerItem(string bigIntStr) : IItem public int CompareTo(object? obj) { - if (obj is not IItem item) return BigInteger.Zero.CompareTo(_value) == 0 ? 0 : 1; // 1.0 == 1, 1.1 > 1 + if (obj is not IItem item) return BigInteger.Zero.CompareTo(this._value) == 0 ? 0 : 1; // 1.0 == 1, 1.1 > 1 return item switch { IntItem or LongItem => 1, - BigIntegerItem biItem => _value.CompareTo(biItem._value), + BigIntegerItem biItem => this._value.CompareTo(biItem._value), StringItem => 1, // 1.1 > 1-sp ListItem => 1, // 1.1 > 1-1 _ => throw new ArgumentOutOfRangeException($"invalid item: {item.GetType()}") @@ -23,7 +23,7 @@ public int CompareTo(object? obj) public bool IsNull() { - return BigInteger.Zero.CompareTo(_value) == 0; + return BigInteger.Zero.CompareTo(this._value) == 0; } public override bool Equals(object? obj) @@ -31,16 +31,16 @@ public override bool Equals(object? obj) if (this == obj) return true; if (obj is not BigIntegerItem that) return false; - return _value.Equals(that._value); + return this._value.Equals(that._value); } public override int GetHashCode() { - return _value.GetHashCode(); + return this._value.GetHashCode(); } public override string ToString() { - return _value.ToString(); + return this._value.ToString(); } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/Version/Item/IntItem.cs b/ProjBobcat/ProjBobcat/Class/Model/Version/Item/IntItem.cs index c2a7d4eb..148a1d07 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Version/Item/IntItem.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Version/Item/IntItem.cs @@ -9,28 +9,28 @@ public class IntItem : IItem public IntItem() { - _value = 0; + this._value = 0; } public IntItem(string intStr) { - _value = int.TryParse(intStr, out var outInt) ? outInt : 0; + this._value = int.TryParse(intStr, out var outInt) ? outInt : 0; } public bool IsNull() { - return _value == 0; + return this._value == 0; } public int CompareTo(object? obj) { - if (obj is not IItem item) return _value == 0 ? 0 : 1; // 1.0 == 1, 1.1 > 1 + if (obj is not IItem item) return this._value == 0 ? 0 : 1; // 1.0 == 1, 1.1 > 1 switch (item) { case IntItem intItem: var itemValue = intItem._value; - return _value.CompareTo(itemValue); + return this._value.CompareTo(itemValue); case LongItem: case BigIntegerItem: return -1; @@ -48,16 +48,16 @@ public override bool Equals(object? obj) if (this == obj) return true; if (obj is not IntItem that) return false; - return _value == that._value; + return this._value == that._value; } public override int GetHashCode() { - return _value.GetHashCode(); + return this._value.GetHashCode(); } public override string ToString() { - return _value.ToString(); + return this._value.ToString(); } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/Version/Item/ListItem.cs b/ProjBobcat/ProjBobcat/Class/Model/Version/Item/ListItem.cs index f17d8600..77bd2116 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Version/Item/ListItem.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Version/Item/ListItem.cs @@ -10,13 +10,11 @@ public class ListItem : List, IItem public int CompareTo(object? obj) { if (obj is not IItem item) - { // 1-0 = 1- (normalize) = 1 // Compare the entire list of items with null - not just the first one, MNG-6964 - return Count == 0 + return this.Count == 0 ? 0 : this.Select(i => i.CompareTo(null)).FirstOrDefault(result => result != 0); - } switch (item) { @@ -27,7 +25,7 @@ public int CompareTo(object? obj) case StringItem: return 1; // 1-1 > 1-sp case ListItem listItem: - var left = GetEnumerator(); + var left = this.GetEnumerator(); var right = listItem.GetEnumerator(); var hasNextLeft = left.MoveNext(); @@ -58,18 +56,18 @@ public int CompareTo(object? obj) public bool IsNull() { - return Count == 0; + return this.Count == 0; } public void Normalize() { - for (var i = Count - 1; i >= 0; i--) + for (var i = this.Count - 1; i >= 0; i--) { var lastItem = this[i]; if (lastItem.IsNull()) // remove null trailing items: 0, "", empty list - RemoveAt(i); + this.RemoveAt(i); else if (lastItem is not ListItem) break; } diff --git a/ProjBobcat/ProjBobcat/Class/Model/Version/Item/LongItem.cs b/ProjBobcat/ProjBobcat/Class/Model/Version/Item/LongItem.cs index bb475cac..9033da12 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Version/Item/LongItem.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Version/Item/LongItem.cs @@ -8,7 +8,7 @@ public class LongItem(string longStr) : IItem public int CompareTo(object? obj) { - if (obj is not IItem item) return _value == 0 ? 0 : 1; // 1.0 == 1, 1.1 > 1 + if (obj is not IItem item) return this._value == 0 ? 0 : 1; // 1.0 == 1, 1.1 > 1 switch (item) { @@ -16,7 +16,7 @@ public int CompareTo(object? obj) return 1; case LongItem longItem: var itemValue = longItem._value; - return _value.CompareTo(itemValue); + return this._value.CompareTo(itemValue); case BigIntegerItem: return -1; case StringItem: @@ -30,7 +30,7 @@ public int CompareTo(object? obj) public bool IsNull() { - return _value == 0; + return this._value == 0; } public override bool Equals(object? obj) @@ -38,16 +38,16 @@ public override bool Equals(object? obj) if (this == obj) return true; if (obj is not LongItem that) return false; - return _value == that._value; + return this._value == that._value; } public override int GetHashCode() { - return _value.GetHashCode(); + return this._value.GetHashCode(); } public override string ToString() { - return _value.ToString(); + return this._value.ToString(); } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/Version/Item/StringItem.cs b/ProjBobcat/ProjBobcat/Class/Model/Version/Item/StringItem.cs index f74d546a..66af4222 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/Version/Item/StringItem.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/Version/Item/StringItem.cs @@ -41,19 +41,20 @@ public StringItem(string str, bool followedByDigit) _ => Empty }; - _value = Aliases.TryGetValue(str, out var outVal) ? outVal : str; + this._value = Aliases.TryGetValue(str, out var outVal) ? outVal : str; } public int CompareTo(object? obj) { if (obj is not IItem item) // 1-rc < 1, 1-ga > 1 - return Compare(ComparableQualifier(_value), ReleaseVersionIndex, StringComparison.Ordinal); + return Compare(ComparableQualifier(this._value), ReleaseVersionIndex, StringComparison.Ordinal); return item switch { IntItem or LongItem or BigIntegerItem => -1, // 1.any < 1.1 ? - StringItem strItem => Compare(ComparableQualifier(_value), ComparableQualifier(strItem._value), StringComparison.Ordinal), + StringItem strItem => Compare(ComparableQualifier(this._value), ComparableQualifier(strItem._value), + StringComparison.Ordinal), ListItem => -1, // 1.any < 1-1 _ => throw new ArgumentOutOfRangeException($"invalid item: {item.GetType()}") }; @@ -61,7 +62,7 @@ public int CompareTo(object? obj) public bool IsNull() { - return Compare(ComparableQualifier(_value), ReleaseVersionIndex, StringComparison.Ordinal) == 0; + return Compare(ComparableQualifier(this._value), ReleaseVersionIndex, StringComparison.Ordinal) == 0; } /** @@ -89,16 +90,16 @@ public override bool Equals(object? obj) if (this == obj) return true; if (obj is not StringItem that) return false; - return _value == that._value; + return this._value == that._value; } public override int GetHashCode() { - return _value.GetHashCode(); + return this._value.GetHashCode(); } public override string ToString() { - return _value; + return this._value; } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/AuthRefreshRequestModel.cs b/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/AuthRefreshRequestModel.cs index 7fee25c4..09061600 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/AuthRefreshRequestModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/AuthRefreshRequestModel.cs @@ -4,20 +4,14 @@ namespace ProjBobcat.Class.Model.YggdrasilAuth; public class AuthRefreshRequestModel { - [JsonPropertyName("accessToken")] - public required string AccessToken { get; init; } + [JsonPropertyName("accessToken")] public required string AccessToken { get; init; } - [JsonPropertyName("clientToken")] - public required string ClientToken { get; init; } + [JsonPropertyName("clientToken")] public required string ClientToken { get; init; } - [JsonPropertyName("requestUser")] - public required bool RequestUser { get; init; } + [JsonPropertyName("requestUser")] public required bool RequestUser { get; init; } - [JsonPropertyName("selectedProfile")] - public required ProfileInfoModel SelectedProfile { get; init; } + [JsonPropertyName("selectedProfile")] public required ProfileInfoModel SelectedProfile { get; init; } } [JsonSerializable(typeof(AuthRefreshRequestModel))] -partial class AuthRefreshRequestModelContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class AuthRefreshRequestModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/AuthRequestModel.cs b/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/AuthRequestModel.cs index 396ea10f..f032eda7 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/AuthRequestModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/AuthRequestModel.cs @@ -11,17 +11,13 @@ public class Agent public class AuthRequestModel { - [JsonPropertyName("username")] - public required string Username { get; init; } + [JsonPropertyName("username")] public required string Username { get; init; } - [JsonPropertyName("password")] - public required string Password { get; init; } + [JsonPropertyName("password")] public required string Password { get; init; } - [JsonPropertyName("clientToken")] - public required string ClientToken { get; init; } + [JsonPropertyName("clientToken")] public required string ClientToken { get; init; } - [JsonPropertyName("requestUser")] - public bool RequestUser { get; init; } + [JsonPropertyName("requestUser")] public bool RequestUser { get; init; } [JsonPropertyName("agent")] public Agent Agent { get; set; } = new() @@ -32,6 +28,4 @@ public class AuthRequestModel } [JsonSerializable(typeof(AuthRequestModel))] -partial class AuthRequestModelContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class AuthRequestModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/AuthResponseModel.cs b/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/AuthResponseModel.cs index c2307cfd..40d5c355 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/AuthResponseModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/AuthResponseModel.cs @@ -17,6 +17,4 @@ public class AuthResponseModel } [JsonSerializable(typeof(AuthResponseModel))] -partial class AuthResponseModelContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class AuthResponseModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/AuthTokenRequestModel.cs b/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/AuthTokenRequestModel.cs index c221b7f5..04948040 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/AuthTokenRequestModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/AuthTokenRequestModel.cs @@ -10,6 +10,4 @@ public class AuthTokenRequestModel } [JsonSerializable(typeof(AuthTokenRequestModel))] -partial class AuthTokenRequestModelContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class AuthTokenRequestModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/ErrorModel.cs b/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/ErrorModel.cs index 6b113c3d..22d28586 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/ErrorModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/ErrorModel.cs @@ -15,6 +15,4 @@ public class ErrorModel } [JsonSerializable(typeof(ErrorModel))] -partial class ErrorModelContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class ErrorModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/SignOutRequestModel.cs b/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/SignOutRequestModel.cs index bf519b58..ca38cfc7 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/SignOutRequestModel.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/YggdrasilAuth/SignOutRequestModel.cs @@ -10,6 +10,4 @@ public class SignOutRequestModel } [JsonSerializable(typeof(SignOutRequestModel))] -partial class SignOutRequestModelContext : JsonSerializerContext -{ -} \ No newline at end of file +partial class SignOutRequestModelContext : JsonSerializerContext; \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/VersionLocatorBase.cs b/ProjBobcat/ProjBobcat/Class/VersionLocatorBase.cs index f7368434..04d4039c 100644 --- a/ProjBobcat/ProjBobcat/Class/VersionLocatorBase.cs +++ b/ProjBobcat/ProjBobcat/Class/VersionLocatorBase.cs @@ -9,7 +9,7 @@ public abstract class VersionLocatorBase(string rootPath) : LauncherParserBase(r { public ILauncherProfileParser? LauncherProfileParser { get; init; } public ILauncherAccountParser? LauncherAccountParser { get; init; } - + public abstract VersionInfo? GetGame(string id); public abstract IEnumerable GetAllGames(); diff --git a/ProjBobcat/ProjBobcat/Constants.cs b/ProjBobcat/ProjBobcat/Constants.cs index 03313609..7d41de7c 100644 --- a/ProjBobcat/ProjBobcat/Constants.cs +++ b/ProjBobcat/ProjBobcat/Constants.cs @@ -8,7 +8,9 @@ public static class Constants public const string FallBackVersion = "0.0.0"; public static string WhereCommand => OperatingSystem.IsWindows() ? Windows.WhereCommand : UnixKind.WhereCommand; - public static string JavaExecutable => OperatingSystem.IsWindows() ? Windows.JavaExecutable : UnixKind.JavaExecutable; + + public static string JavaExecutable => + OperatingSystem.IsWindows() ? Windows.JavaExecutable : UnixKind.JavaExecutable; public static string JavaExecutableExtension => OperatingSystem.IsWindows() ? Windows.JavaExecutableExtension diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Authenticator/MicrosoftAuthenticator.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Authenticator/MicrosoftAuthenticator.cs index ae38c492..6b476ba8 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Authenticator/MicrosoftAuthenticator.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Authenticator/MicrosoftAuthenticator.cs @@ -52,10 +52,10 @@ public MicrosoftAuthenticator() public static string MSDeviceTokenRequestUrl => $"https://login.microsoftonline.com/{ApiSettings!.TenentId}/oauth2/v2.0/devicecode"; - public static string MSDeviceTokenStatusUrl => + public static string MSDeviceTokenStatusUrl => $"https://login.microsoftonline.com/{ApiSettings!.TenentId}/oauth2/v2.0/token"; - public static string MSRefreshTokenRequestUrl => + public static string MSRefreshTokenRequestUrl => $"https://login.microsoftonline.com/{ApiSettings!.TenentId}/oauth2/v2.0/token"; static HttpClient DefaultClient => HttpClientHelper.DefaultClient; @@ -66,12 +66,12 @@ public MicrosoftAuthenticator() public AuthResultBase Auth(bool userField = false) { - return AuthTaskAsync(userField).GetAwaiter().GetResult(); + return this.AuthTaskAsync(userField).GetAwaiter().GetResult(); } public async Task AuthTaskAsync(bool userField = false) { - if (CacheTokenProvider == null) + if (this.CacheTokenProvider == null) return new MicrosoftAuthResult { AuthStatus = AuthStatus.Failed, @@ -83,7 +83,7 @@ public async Task AuthTaskAsync(bool userField = false) } }; - var (isCredentialValid, cacheAuthResult) = await CacheTokenProvider(); + var (isCredentialValid, cacheAuthResult) = await this.CacheTokenProvider(); if (!isCredentialValid || cacheAuthResult == null) return new MicrosoftAuthResult @@ -143,14 +143,14 @@ await SendRequest(MSAuthXBLUrl, AuthXBLRequestModel.Get(accessToken), var errorMessage = errModel?.Message ?? "未知"; if (!string.IsNullOrEmpty(errModel?.Redirect)) errorMessage += $",相关链接:{errModel.Redirect}"; - + var err = new ErrorModel { Cause = reason, Error = $"XSTS 认证失败,原因:{reason}", ErrorMessage = errorMessage }; - + return new MicrosoftAuthResult { AuthStatus = AuthStatus.Failed, @@ -297,7 +297,7 @@ await profileResRes.Content.ReadFromJsonAsync(MojangErrorResponseModelContext.De Username = profileRes.Name }; - if (!LauncherAccountParser.AddNewAccount(uuid, accountModel, out var id)) + if (!this.LauncherAccountParser.AddNewAccount(uuid, accountModel, out var id)) return new MicrosoftAuthResult { AuthStatus = AuthStatus.Failed, @@ -322,7 +322,7 @@ await profileResRes.Content.ReadFromJsonAsync(MojangErrorResponseModelContext.De { var claims = JwtTokenHelper.GetTokenInfo(idToken); if (claims.TryGetValue("email", out var email)) - Email = email; + this.Email = email; else return new MicrosoftAuthResult { @@ -352,16 +352,16 @@ await profileResRes.Content.ReadFromJsonAsync(MojangErrorResponseModelContext.De UUID = sPUuid, UserName = profileRes.Name }, - Email = Email, + Email = this.Email, XBoxUid = xuid }; } public AuthResultBase? GetLastAuthResult() { - var (_, value) = LauncherAccountParser.LauncherAccount.Accounts! + var (_, value) = this.LauncherAccountParser.LauncherAccount.Accounts! .FirstOrDefault(x => - x.Value.Username.Equals(Email, StringComparison.OrdinalIgnoreCase) && + x.Value.Username.Equals(this.Email, StringComparison.OrdinalIgnoreCase) && x.Value.Type.Equals("XBox", StringComparison.OrdinalIgnoreCase)); if (value == default) @@ -369,8 +369,8 @@ await profileResRes.Content.ReadFromJsonAsync(MojangErrorResponseModelContext.De var sP = new ProfileInfoModel { - Name = value.MinecraftProfile?.Name ?? Email ?? string.Empty, - UUID = new PlayerUUID(value.MinecraftProfile?.Id ?? Email ?? string.Empty) + Name = value.MinecraftProfile?.Name ?? this.Email ?? string.Empty, + UUID = new PlayerUUID(value.MinecraftProfile?.Id ?? this.Email ?? string.Empty) }; return new MicrosoftAuthResult diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Authenticator/OfflineAuthenticator.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Authenticator/OfflineAuthenticator.cs index 1149db8d..ad3d2865 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Authenticator/OfflineAuthenticator.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Authenticator/OfflineAuthenticator.cs @@ -40,7 +40,7 @@ public AuthResultBase Auth(bool userField = false) Value = "zh-cn" }; - var uuid = PlayerUUID.FromOfflinePlayerName(Username); + var uuid = PlayerUUID.FromOfflinePlayerName(this.Username); var localUuid = GuidHelper.NewGuidString(); var accountModel = new AccountModel @@ -55,16 +55,16 @@ public AuthResultBase Auth(bool userField = false) MinecraftProfile = new AccountProfileModel { Id = uuid.ToString(), - Name = Username + Name = this.Username }, Persistent = true, RemoteId = GuidHelper.NewGuidString(), Type = "Mojang", UserProperites = [authProperty], - Username = Username + Username = this.Username }; - if (!LauncherAccountParser.AddNewAccount(localUuid, accountModel, out var id)) + if (!this.LauncherAccountParser.AddNewAccount(localUuid, accountModel, out var id)) return new AuthResultBase { AuthStatus = AuthStatus.Failed, @@ -83,7 +83,7 @@ public AuthResultBase Auth(bool userField = false) AuthStatus = AuthStatus.Succeeded, SelectedProfile = new ProfileInfoModel { - Name = Username, + Name = this.Username, UUID = uuid }, User = new UserInfoModel @@ -110,7 +110,7 @@ public AuthResultBase Auth(bool userField = false) /// public Task AuthTaskAsync(bool userField) { - return Task.FromResult(Auth()); + return Task.FromResult(this.Auth()); } /// @@ -120,6 +120,6 @@ public Task AuthTaskAsync(bool userField) [Obsolete("此方法已过时,请使用 Auth(bool) 代替。")] public AuthResultBase GetLastAuthResult() { - return Auth(); + return this.Auth(); } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Authenticator/YggdrasilAuthenticator.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Authenticator/YggdrasilAuthenticator.cs index 4310b274..2df51d2e 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Authenticator/YggdrasilAuthenticator.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Authenticator/YggdrasilAuthenticator.cs @@ -46,31 +46,31 @@ public class YggdrasilAuthenticator : IAuthenticator /// 获取登录Api地址。 /// string LoginAddress => - $"{AuthServer}{(string.IsNullOrEmpty(AuthServer) ? OfficialAuthServer : "/authserver")}/authenticate"; + $"{this.AuthServer}{(string.IsNullOrEmpty(this.AuthServer) ? OfficialAuthServer : "/authserver")}/authenticate"; /// /// 获取令牌刷新Api地址。 /// string RefreshAddress => - $"{AuthServer}{(string.IsNullOrEmpty(AuthServer) ? OfficialAuthServer : "/authserver")}/refresh"; + $"{this.AuthServer}{(string.IsNullOrEmpty(this.AuthServer) ? OfficialAuthServer : "/authserver")}/refresh"; /// /// 获取令牌验证Api地址。 /// string ValidateAddress => - $"{AuthServer}{(string.IsNullOrEmpty(AuthServer) ? OfficialAuthServer : "/authserver")}/validate"; + $"{this.AuthServer}{(string.IsNullOrEmpty(this.AuthServer) ? OfficialAuthServer : "/authserver")}/validate"; /// /// 获取令牌吊销Api地址。 /// string RevokeAddress => - $"{AuthServer}{(string.IsNullOrEmpty(AuthServer) ? OfficialAuthServer : "/authserver")}/invalidate"; + $"{this.AuthServer}{(string.IsNullOrEmpty(this.AuthServer) ? OfficialAuthServer : "/authserver")}/invalidate"; /// /// 获取登出Api地址。 /// string SignOutAddress => - $"{AuthServer}{(string.IsNullOrEmpty(AuthServer) ? OfficialAuthServer : "/authserver")}/signout"; + $"{this.AuthServer}{(string.IsNullOrEmpty(this.AuthServer) ? OfficialAuthServer : "/authserver")}/signout"; public required ILauncherAccountParser LauncherAccountParser { get; init; } @@ -81,7 +81,7 @@ public class YggdrasilAuthenticator : IAuthenticator /// public AuthResultBase Auth(bool userField = false) { - return AuthTaskAsync().GetAwaiter().GetResult(); + return this.AuthTaskAsync().GetAwaiter().GetResult(); } /// @@ -93,15 +93,15 @@ public async Task AuthTaskAsync(bool userField = false) { var requestModel = new AuthRequestModel { - ClientToken = LauncherAccountParser.LauncherAccount.MojangClientToken, + ClientToken = this.LauncherAccountParser.LauncherAccount.MojangClientToken, RequestUser = userField, - Username = Email, - Password = Password + Username = this.Email, + Password = this.Password }; var requestJson = JsonSerializer.Serialize(requestModel, typeof(AuthRequestModel), new AuthRequestModelContext(JsonHelper.CamelCasePropertyNamesSettings())); - using var resultJson = await HttpHelper.Post(LoginAddress, requestJson); + using var resultJson = await HttpHelper.Post(this.LoginAddress, requestJson); if (!resultJson.IsSuccessStatusCode) return new YggdrasilAuthResult @@ -133,7 +133,8 @@ public async Task AuthTaskAsync(bool userField = false) }; } - if (result.SelectedProfile == null && (result.AvailableProfiles == null || result.AvailableProfiles.Length == 0)) + if (result.SelectedProfile == null && + (result.AvailableProfiles == null || result.AvailableProfiles.Length == 0)) return new YggdrasilAuthResult { AuthStatus = AuthStatus.Failed, @@ -145,7 +146,7 @@ public async Task AuthTaskAsync(bool userField = false) } }; - if (string.IsNullOrEmpty(AuthServer) && result.SelectedProfile == null) + if (string.IsNullOrEmpty(this.AuthServer) && result.SelectedProfile == null) return new YggdrasilAuthResult { AuthStatus = AuthStatus.Failed, @@ -156,7 +157,7 @@ public async Task AuthTaskAsync(bool userField = false) Cause = "可能是因为您还没有购买正版游戏或是账户服务器出现了问题!" } }; - + if (result.AvailableProfiles == null || result.AvailableProfiles.Length == 0) return new YggdrasilAuthResult { @@ -172,19 +173,19 @@ public async Task AuthTaskAsync(bool userField = false) var profiles = result.AvailableProfiles .ToDictionary(profile => profile.UUID, - profile => new AuthProfileModel { DisplayName = profile.Name }) + profile => new AuthProfileModel { DisplayName = profile.Name }) .AsReadOnly(); foreach (var (playerUuid, authProfileModel) in profiles) { - var ids = LauncherAccountParser.LauncherAccount.Accounts!.Where(a => + var ids = this.LauncherAccountParser.LauncherAccount.Accounts!.Where(a => (a.Value.MinecraftProfile?.Name?.Equals(authProfileModel.DisplayName, StringComparison.OrdinalIgnoreCase) ?? false) && (a.Value.MinecraftProfile?.Id?.Equals(playerUuid.ToString(), StringComparison.OrdinalIgnoreCase) ?? false)) .Select(p => p.Value.Id); - foreach (var id in ids) LauncherAccountParser.RemoveAccount(id); + foreach (var id in ids) this.LauncherAccountParser.RemoveAccount(id); } var rUuid = GuidHelper.NewGuidString(); @@ -201,7 +202,7 @@ public async Task AuthTaskAsync(bool userField = false) RemoteId = result.User?.UUID.ToString() ?? new Guid().ToString(), Type = "Mojang", UserProperites = result.User?.Properties?.ToAuthProperties(profiles).ToArray() ?? [], - Username = Email + Username = this.Email }; if (result.SelectedProfile != null) @@ -233,7 +234,7 @@ public async Task AuthTaskAsync(bool userField = false) } */ - if (!LauncherAccountParser.AddNewAccount(rUuid, profile, out var accountId)) + if (!this.LauncherAccountParser.AddNewAccount(rUuid, profile, out var accountId)) return new YggdrasilAuthResult { AuthStatus = AuthStatus.Failed, @@ -264,9 +265,8 @@ public async Task AuthTaskAsync(bool userField = false) /// 验证状态。 public AuthResultBase GetLastAuthResult() { - var profile = - LauncherAccountParser.LauncherAccount.Accounts!.Values.FirstOrDefault(a => - a.Username.Equals(Email, StringComparison.OrdinalIgnoreCase)); + var profile = this.LauncherAccountParser.LauncherAccount.Accounts!.Values.FirstOrDefault(a => + a.Username.Equals(this.Email, StringComparison.OrdinalIgnoreCase)); if (profile is null) return new AuthResultBase @@ -300,7 +300,7 @@ public AuthResultBase GetLastAuthResult() ], SelectedProfile = new ProfileInfoModel { - Name = profile.MinecraftProfile?.Name ?? Email, + Name = profile.MinecraftProfile?.Name ?? this.Email, UUID = new PlayerUUID() } }; @@ -321,7 +321,7 @@ public async Task AuthRefreshTaskAsync(AuthResponseModel respons string.IsNullOrEmpty(response.ClientToken) || response.SelectedProfile == null) throw new ArgumentException(nameof(response)); - + var requestModel = new AuthRefreshRequestModel { AccessToken = response.AccessToken, @@ -332,7 +332,7 @@ public async Task AuthRefreshTaskAsync(AuthResponseModel respons var requestJson = JsonSerializer.Serialize(requestModel, typeof(AuthRefreshRequestModel), new AuthRefreshRequestModelContext(JsonHelper.CamelCasePropertyNamesSettings())); - using var resultJson = await HttpHelper.Post(RefreshAddress, requestJson); + using var resultJson = await HttpHelper.Post(this.RefreshAddress, requestJson); var resultJsonElement = await resultJson.Content.ReadFromJsonAsync(JsonElementContext.Default.JsonElement); object? result = resultJsonElement.TryGetProperty("cause", out _) ? resultJsonElement.Deserialize(ErrorModelContext.Default.ErrorModel) @@ -358,9 +358,9 @@ public async Task AuthRefreshTaskAsync(AuthResponseModel respons ErrorMessage = "用户字段缺少了部分重要数据,请联系开发者" } }; - + if (authResponse.SelectedProfile == null || - (authResponse.AvailableProfiles == null || authResponse.AvailableProfiles.Length == 0)) + authResponse.AvailableProfiles == null || authResponse.AvailableProfiles.Length == 0) return new AuthResultBase { AuthStatus = AuthStatus.Failed, @@ -380,12 +380,12 @@ public async Task AuthRefreshTaskAsync(AuthResponseModel respons .AsReadOnly(); var uuid = authResponse.User.UUID.ToString(); - var (_, value) = LauncherAccountParser.LauncherAccount.Accounts!.FirstOrDefault(a => + var (_, value) = this.LauncherAccountParser.LauncherAccount.Accounts!.FirstOrDefault(a => (a.Value.MinecraftProfile?.Name?.Equals(authResponse.User.UserName, StringComparison.OrdinalIgnoreCase) ?? false) && (a.Value.MinecraftProfile?.Id?.Equals(uuid, StringComparison.OrdinalIgnoreCase) ?? false)); - if (value != default) LauncherAccountParser.RemoveAccount(value.Id); + if (value != default) this.LauncherAccountParser.RemoveAccount(value.Id); var rUuid = GuidHelper.NewGuidString(); @@ -401,7 +401,7 @@ public async Task AuthRefreshTaskAsync(AuthResponseModel respons RemoteId = authResponse.User?.UUID.ToString() ?? new Guid().ToString(), Type = "Mojang", UserProperites = authResponse.User?.Properties?.ToAuthProperties(profiles).ToArray() ?? [], - Username = Email + Username = this.Email }; if (authResponse.SelectedProfile != null) @@ -414,7 +414,7 @@ public async Task AuthRefreshTaskAsync(AuthResponseModel respons }; } - if (!LauncherAccountParser.AddNewAccount(rUuid, profile, out var id)) + if (!this.LauncherAccountParser.AddNewAccount(rUuid, profile, out var id)) return new YggdrasilAuthResult { AuthStatus = AuthStatus.Failed, @@ -448,12 +448,12 @@ public async Task ValidateTokenTaskAsync(string accessToken) var requestModel = new AuthTokenRequestModel { AccessToken = accessToken, - ClientToken = LauncherAccountParser.LauncherAccount.MojangClientToken + ClientToken = this.LauncherAccountParser.LauncherAccount.MojangClientToken }; var requestJson = JsonSerializer.Serialize(requestModel, typeof(AuthTokenRequestModel), new AuthTokenRequestModelContext(JsonHelper.CamelCasePropertyNamesSettings())); - using var result = await HttpHelper.Post(ValidateAddress, requestJson); + using var result = await HttpHelper.Post(this.ValidateAddress, requestJson); return result.StatusCode.Equals(HttpStatusCode.NoContent); } @@ -462,12 +462,12 @@ public async Task TokenRevokeTaskAsync(string accessToken) var requestModel = new AuthTokenRequestModel { AccessToken = accessToken, - ClientToken = LauncherAccountParser.LauncherAccount.MojangClientToken + ClientToken = this.LauncherAccountParser.LauncherAccount.MojangClientToken }; var requestJson = JsonSerializer.Serialize(requestModel, typeof(AuthTokenRequestModel), new AuthTokenRequestModelContext(JsonHelper.CamelCasePropertyNamesSettings())); - using var x = await HttpHelper.Post(RevokeAddress, requestJson); + using var x = await HttpHelper.Post(this.RevokeAddress, requestJson); } /// @@ -479,13 +479,13 @@ public async Task SignOutTaskAsync() { var requestModel = new SignOutRequestModel { - Username = Email, - Password = Password + Username = this.Email, + Password = this.Password }; var requestJson = JsonSerializer.Serialize(requestModel, typeof(SignOutRequestModel), new SignOutRequestModelContext(JsonHelper.CamelCasePropertyNamesSettings())); - using var result = await HttpHelper.Post(SignOutAddress, requestJson); + using var result = await HttpHelper.Post(this.SignOutAddress, requestJson); return result.StatusCode.Equals(HttpStatusCode.NoContent); } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/DefaultResourceCompleter.cs b/ProjBobcat/ProjBobcat/DefaultComponent/DefaultResourceCompleter.cs index 51837ac3..9234bfcc 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/DefaultResourceCompleter.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/DefaultResourceCompleter.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks.Dataflow; using ProjBobcat.Class.Helper; using ProjBobcat.Class.Model; +using ProjBobcat.Class.Model.Downloading; using ProjBobcat.Event; using ProjBobcat.Interface; @@ -21,7 +22,7 @@ public class DefaultResourceCompleter : IResourceCompleter ulong _needToDownload, _totalDownloaded; - public TimeSpan TimeoutPerFile { get; set; } = TimeSpan.FromSeconds(8); + public TimeSpan TimeoutPerFile { get; set; } = TimeSpan.FromSeconds(3); public int DownloadParts { get; set; } = 16; public int DownloadThread { get; set; } = 16; public int MaxDegreeOfParallelism { get; set; } = 1; @@ -35,106 +36,98 @@ public class DefaultResourceCompleter : IResourceCompleter public TaskResult CheckAndDownload() { - return CheckAndDownloadTaskAsync().GetAwaiter().GetResult(); + return this.CheckAndDownloadTaskAsync().GetAwaiter().GetResult(); } public async Task> CheckAndDownloadTaskAsync() { - if ((ResourceInfoResolvers?.Count ?? 0) == 0) + if ((this.ResourceInfoResolvers?.Count ?? 0) == 0) return new TaskResult(TaskResultStatus.Success, value: null); - DownloadThread = DownloadThread <= 1 ? 16 : DownloadThread; + this.DownloadThread = this.DownloadThread <= 1 ? 16 : this.DownloadThread; - _needToDownload = 0; - _failedFiles.Clear(); + Interlocked.Exchange(ref this._needToDownload, 0); + this._failedFiles.Clear(); - var downloadBag = new ConcurrentBag(); var downloadSettings = new DownloadSettings { - CheckFile = CheckFile, - DownloadParts = DownloadParts, + CheckFile = this.CheckFile, + DownloadParts = this.DownloadParts, HashType = HashType.SHA1, - RetryCount = TotalRetry, - Timeout = TimeoutPerFile + RetryCount = this.TotalRetry, + Timeout = this.TimeoutPerFile }; - OnResolveComplete(this, new GameResourceInfoResolveEventArgs + this.OnResolveComplete(this, new GameResourceInfoResolveEventArgs { Progress = 0, Status = "正在进行资源检查" }); + var downloadActionBlock = DownloadHelper.AdvancedDownloadListFileActionBlock(downloadSettings); var checkAction = new ActionBlock(async resolver => { resolver.GameResourceInfoResolveEvent += FireResolveEvent; await Parallel.ForEachAsync( resolver.ResolveResourceAsync(), - new ParallelOptions { MaxDegreeOfParallelism = MaxDegreeOfParallelism }, + new ParallelOptions { MaxDegreeOfParallelism = this.MaxDegreeOfParallelism }, ReceiveGameResourceTask); return; void FireResolveEvent(object? sender, GameResourceInfoResolveEventArgs e) { - if (!downloadBag.IsEmpty) + if (Interlocked.Read(ref this._needToDownload) != 0) { resolver.GameResourceInfoResolveEvent -= FireResolveEvent; return; } - OnResolveComplete(sender, e); + this.OnResolveComplete(sender, e); } }, new ExecutionDataflowBlockOptions { - BoundedCapacity = MaxDegreeOfParallelism, - MaxDegreeOfParallelism = MaxDegreeOfParallelism + BoundedCapacity = this.MaxDegreeOfParallelism, + MaxDegreeOfParallelism = this.MaxDegreeOfParallelism }); - foreach(var r in ResourceInfoResolvers!) + foreach (var r in this.ResourceInfoResolvers!) await checkAction.SendAsync(r); checkAction.Complete(); await checkAction.Completion; - OnResolveComplete(this, new GameResourceInfoResolveEventArgs + downloadActionBlock.Complete(); + await downloadActionBlock.Completion; + + this.OnResolveComplete(this, new GameResourceInfoResolveEventArgs { Progress = 100, Status = "资源检查完成" }); - if (downloadBag.IsEmpty) - return new TaskResult( - TaskResultStatus.Success, - value: new ResourceCompleterCheckResult{FailedFiles = [], IsLibDownloadFailed = false}); - - var downloads = downloadBag.ToArray(); - - Random.Shared.Shuffle(downloads); - - await DownloadHelper.AdvancedDownloadListFile(downloads, downloadSettings); - - var isLibraryFailed = _failedFiles.Any(d => d.FileType == ResourceType.LibraryOrNative); - var result = _failedFiles switch + var isLibraryFailed = this._failedFiles.Any(d => d.FileType == ResourceType.LibraryOrNative); + var result = this._failedFiles switch { _ when isLibraryFailed => TaskResultStatus.Error, - _ when !_failedFiles.IsEmpty => TaskResultStatus.PartialSuccess, + _ when !this._failedFiles.IsEmpty => TaskResultStatus.PartialSuccess, _ => TaskResultStatus.Success }; var resultArgs = new ResourceCompleterCheckResult { IsLibDownloadFailed = isLibraryFailed, - FailedFiles = _failedFiles + FailedFiles = this._failedFiles }; return new TaskResult(result, value: resultArgs); - ValueTask ReceiveGameResourceTask(IGameResource element, CancellationToken ct) + async ValueTask ReceiveGameResourceTask(IGameResource element, CancellationToken ct) { - OnResolveComplete(this, new GameResourceInfoResolveEventArgs + this.OnResolveComplete(this, new GameResourceInfoResolveEventArgs { - Progress = 0, + Progress = -1, Status = $"发现未下载的 {element.FileName.CropStr()}({element.Type}),已加入下载队列" }); @@ -147,13 +140,11 @@ ValueTask ReceiveGameResourceTask(IGameResource element, CancellationToken ct) CheckSum = element.CheckSum, FileType = element.Type }; - dF.Completed += WhenCompleted; - - downloadBag.Add(dF); + dF.Completed += this.WhenCompleted; - Interlocked.Add(ref _needToDownload, 1); + await downloadActionBlock.SendAsync(dF, ct); - return default; + Interlocked.Add(ref this._needToDownload, 1); } } @@ -163,17 +154,17 @@ public void Dispose() void OnResolveComplete(object? sender, GameResourceInfoResolveEventArgs e) { - GameResourceInfoResolveStatus?.Invoke(sender, e); + this.GameResourceInfoResolveStatus?.Invoke(sender, e); } void OnCompleted(object? sender, DownloadFileCompletedEventArgs e) { - DownloadFileCompletedEvent?.Invoke(sender, e); + this.DownloadFileCompletedEvent?.Invoke(sender, e); } void OnChanged(double progress, double speed) { - DownloadFileChangedEvent?.Invoke(this, new DownloadFileChangedEventArgs + this.DownloadFileChangedEvent?.Invoke(this, new DownloadFileChangedEventArgs { ProgressPercentage = progress, Speed = speed @@ -183,13 +174,14 @@ void OnChanged(double progress, double speed) void WhenCompleted(object? sender, DownloadFileCompletedEventArgs e) { if (sender is not DownloadFile df) return; - if (!(e.Success ?? false)) _failedFiles.Add(df); + if (!(e.Success ?? false)) this._failedFiles.Add(df); + + df.Completed -= this.WhenCompleted; - df.Completed -= WhenCompleted; + var downloaded = Interlocked.Increment(ref this._totalDownloaded); + var needToDownload = Interlocked.Read(ref this._needToDownload); - var downloaded = Interlocked.Increment(ref _totalDownloaded); - - OnChanged((double)downloaded / _needToDownload, e.AverageSpeed); - OnCompleted(sender, e); + this.OnChanged((double)downloaded / needToDownload, e.AverageSpeed); + this.OnCompleted(sender, e); } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/FabricInstaller.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/FabricInstaller.cs index b950315e..6f0801b9 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/FabricInstaller.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/FabricInstaller.cs @@ -19,40 +19,40 @@ public class FabricInstaller : InstallerBase, IFabricInstaller public string Install() { - return InstallTaskAsync().GetAwaiter().GetResult(); + return this.InstallTaskAsync().GetAwaiter().GetResult(); } public async Task InstallTaskAsync() { - InvokeStatusChangedEvent("开始安装", 0); + this.InvokeStatusChangedEvent("开始安装", 0); - if (string.IsNullOrEmpty(RootPath)) + if (string.IsNullOrEmpty(this.RootPath)) throw new NullReferenceException("RootPath 字段为空"); - var mcVersion = LoaderArtifact.Intermediary.Version; - var fabricVersion = LoaderArtifact.Loader.Separator == "." - ? LoaderArtifact.Loader.Version - : LoaderArtifact.Loader.Version.Replace(LoaderArtifact.Loader.Separator ?? "+build.", ".build."); - var id = CustomId ?? $"{mcVersion}-fabric-{fabricVersion}"; + var mcVersion = this.LoaderArtifact.Intermediary.Version; + var fabricVersion = this.LoaderArtifact.Loader.Separator == "." + ? this.LoaderArtifact.Loader.Version + : this.LoaderArtifact.Loader.Version.Replace(this.LoaderArtifact.Loader.Separator ?? "+build.", ".build."); + var id = this.CustomId ?? $"{mcVersion}-fabric-{fabricVersion}"; var libraries = new List { new() { - Name = LoaderArtifact.Loader.Maven, + Name = this.LoaderArtifact.Loader.Maven, Url = "https://maven.fabricmc.net/" }, new() { - Name = LoaderArtifact.Intermediary.Maven, + Name = this.LoaderArtifact.Intermediary.Maven, Url = "https://maven.fabricmc.net/" } }; - libraries.AddRange(LoaderArtifact.LauncherMeta.Libraries.Common); - libraries.AddRange(LoaderArtifact.LauncherMeta.Libraries.Client); + libraries.AddRange(this.LoaderArtifact.LauncherMeta.Libraries.Common); + libraries.AddRange(this.LoaderArtifact.LauncherMeta.Libraries.Client); - var mainClassJObject = LoaderArtifact.LauncherMeta.MainClass; + var mainClassJObject = this.LoaderArtifact.LauncherMeta.MainClass; var mainClass = mainClassJObject.ValueKind switch { JsonValueKind.String => mainClassJObject.Deserialize(StringContext.Default.String), @@ -66,9 +66,9 @@ public async Task InstallTaskAsync() if (string.IsNullOrEmpty(mainClass)) throw new NullReferenceException("MainClass 字段为空"); - var inheritsFrom = string.IsNullOrEmpty(InheritsFrom) ? mcVersion : InheritsFrom; + var inheritsFrom = string.IsNullOrEmpty(this.InheritsFrom) ? mcVersion : this.InheritsFrom; - var installPath = Path.Combine(RootPath, GamePathHelper.GetGamePath(id)); + var installPath = Path.Combine(this.RootPath, GamePathHelper.GetGamePath(id)); var di = new DirectoryInfo(installPath); if (!di.Exists) @@ -76,7 +76,7 @@ public async Task InstallTaskAsync() else DirectoryHelper.CleanDirectory(di.FullName); - InvokeStatusChangedEvent("生成版本总成", 70); + this.InvokeStatusChangedEvent("生成版本总成", 70); var resultModel = new RawVersionModel { @@ -89,15 +89,15 @@ public async Task InstallTaskAsync() Time = DateTime.Now }; - var jsonPath = GamePathHelper.GetGameJsonPath(RootPath, id); + var jsonPath = GamePathHelper.GetGameJsonPath(this.RootPath, id); var jsonContent = JsonSerializer.Serialize(resultModel, typeof(RawVersionModel), new RawVersionModelContext(JsonHelper.CamelCasePropertyNamesSettings())); - InvokeStatusChangedEvent("将版本 Json 写入文件", 90); + this.InvokeStatusChangedEvent("将版本 Json 写入文件", 90); await File.WriteAllTextAsync(jsonPath, jsonContent); - InvokeStatusChangedEvent("安装完成", 100); + this.InvokeStatusChangedEvent("安装完成", 100); return id; } diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ForgeInstaller/ForgeInstallerFactory.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ForgeInstaller/ForgeInstallerFactory.cs index 2a54a867..e7a581ae 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ForgeInstaller/ForgeInstallerFactory.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ForgeInstaller/ForgeInstallerFactory.cs @@ -13,8 +13,8 @@ public static string GetForgeArtifactVersion(string mcVersion, string forgeVersi return (mcVer.Minor, mcVer.Build) switch { - ( 8, 8 or -1) => $"{mcVersion}-{forgeVersion}", //1.8.8, 1.8 - ( >= 7 and <= 8, _) => $"{mcVersion}-{forgeVersion}-{mcVersion}", //1.7 - 1.8, 1.8.9 + (8, 8 or -1) => $"{mcVersion}-{forgeVersion}", //1.8.8, 1.8 + (>= 7 and <= 8, _) => $"{mcVersion}-{forgeVersion}-{mcVersion}", //1.7 - 1.8, 1.8.9 _ => $"{mcVersion}-{forgeVersion}" //1.8.9+ }; } @@ -33,4 +33,4 @@ public static bool IsLegacyForgeInstaller(string forgeExecutable, string forgeVe return legacyUniversalJar && installProfileJson; } -} +} \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ForgeInstaller/HighVersionForgeInstaller.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ForgeInstaller/HighVersionForgeInstaller.cs index cd2046c5..5287a632 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ForgeInstaller/HighVersionForgeInstaller.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ForgeInstaller/HighVersionForgeInstaller.cs @@ -12,6 +12,7 @@ using ProjBobcat.Class; using ProjBobcat.Class.Helper; using ProjBobcat.Class.Model; +using ProjBobcat.Class.Model.Downloading; using ProjBobcat.Class.Model.Forge; using ProjBobcat.Class.Model.YggdrasilAuth; using ProjBobcat.Event; @@ -23,47 +24,27 @@ namespace ProjBobcat.DefaultComponent.Installer.ForgeInstaller; public partial class HighVersionForgeInstaller : InstallerBase, IForgeInstaller { - [GeneratedRegex(@"^\[.+\]$")] - private static partial Regex PathRegex(); - - [GeneratedRegex("^{.+}$")] - private static partial Regex VariableRegex(); - readonly ConcurrentBag _failedFiles = []; int _totalDownloaded, _needToDownload, _totalProcessed, _needToProcess; public required string JavaExecutablePath { get; init; } - public override required string RootPath { get; init; } public required string MineCraftVersionId { get; init; } public required string MineCraftVersion { get; init; } + + public FileInfo? CustomMojangClientMappings { get; init; } + public override required string RootPath { get; init; } public required string DownloadUrlRoot { get; init; } public required string ForgeExecutablePath { get; init; } public required VersionLocatorBase VersionLocator { get; init; } - public FileInfo? CustomMojangClientMappings { get; init; } - public ForgeInstallResult InstallForge() { - return InstallForgeTaskAsync().GetAwaiter().GetResult(); - } - - ForgeInstallResult GetCorruptedFileResult() - { - return new ForgeInstallResult - { - Succeeded = false, - Error = new ErrorModel - { - Cause = "损坏的 Forge 可执行文件", - Error = "安装前准备失败", - ErrorMessage = "损坏的 Forge 可执行文件,请确认您的路径是否正确" - } - }; + return this.InstallForgeTaskAsync().GetAwaiter().GetResult(); } public async Task InstallForgeTaskAsync() { - if (!File.Exists(JavaExecutablePath)) + if (!File.Exists(this.JavaExecutablePath)) return new ForgeInstallResult { Succeeded = false, @@ -75,7 +56,7 @@ public async Task InstallForgeTaskAsync() } }; - if (!File.Exists(ForgeExecutablePath)) + if (!File.Exists(this.ForgeExecutablePath)) return new ForgeInstallResult { Succeeded = false, @@ -87,33 +68,34 @@ public async Task InstallForgeTaskAsync() } }; - using var archive = ArchiveFactory.Open(Path.GetFullPath(ForgeExecutablePath)); + using var archive = ArchiveFactory.Open(Path.GetFullPath(this.ForgeExecutablePath)); #region 解析 Version.json - InvokeStatusChangedEvent("解析 Version.json", 0.1); + this.InvokeStatusChangedEvent("解析 Version.json", 0.1); var versionJsonEntry = - archive.Entries.FirstOrDefault(e => e.Key?.Equals("version.json", StringComparison.OrdinalIgnoreCase) ?? false); + archive.Entries.FirstOrDefault(e => + e.Key?.Equals("version.json", StringComparison.OrdinalIgnoreCase) ?? false); if (versionJsonEntry == default) - return GetCorruptedFileResult(); + return this.GetCorruptedFileResult(); await using var stream = versionJsonEntry.OpenEntryStream(); var versionJsonModel = await JsonSerializer.DeserializeAsync(stream, RawVersionModelContext.Default.RawVersionModel); - + if (versionJsonModel == default) - return GetCorruptedFileResult(); + return this.GetCorruptedFileResult(); var forgeVersion = versionJsonModel.Id.Replace("-forge-", "-"); - var id = string.IsNullOrEmpty(CustomId) ? versionJsonModel.Id : CustomId; + var id = string.IsNullOrEmpty(this.CustomId) ? versionJsonModel.Id : this.CustomId; versionJsonModel.Id = id; - if (!string.IsNullOrEmpty(InheritsFrom)) - versionJsonModel.InheritsFrom = InheritsFrom; + if (!string.IsNullOrEmpty(this.InheritsFrom)) + versionJsonModel.InheritsFrom = this.InheritsFrom; - var jsonPath = GamePathHelper.GetGameJsonPath(RootPath, id); + var jsonPath = GamePathHelper.GetGameJsonPath(this.RootPath, id); var jsonContent = JsonSerializer.Serialize(versionJsonModel, typeof(RawVersionModel), new RawVersionModelContext(JsonHelper.CamelCasePropertyNamesSettings())); @@ -123,14 +105,14 @@ public async Task InstallForgeTaskAsync() #region 解析 Install_profile.json - InvokeStatusChangedEvent("解析 Install_profile.json", 0.2); + this.InvokeStatusChangedEvent("解析 Install_profile.json", 0.2); var installProfileEntry = archive.Entries.FirstOrDefault(e => e.Key?.Equals("install_profile.json", StringComparison.OrdinalIgnoreCase) ?? false); - + if (installProfileEntry == default) - return GetCorruptedFileResult(); + return this.GetCorruptedFileResult(); await using var ipStream = installProfileEntry.OpenEntryStream(); var ipModel = @@ -142,7 +124,7 @@ public async Task InstallForgeTaskAsync() #region 解析 Lzma - InvokeStatusChangedEvent("解析 Lzma", 0.4); + this.InvokeStatusChangedEvent("解析 Lzma", 0.4); var serverLzma = archive.Entries.FirstOrDefault(e => e.Key?.Equals("data/server.lzma", StringComparison.OrdinalIgnoreCase) ?? false); @@ -156,7 +138,7 @@ public async Task InstallForgeTaskAsync() ipModel.Data["BINPATCH"].Server = $"[{serverMaven}]"; var serverBinMaven = serverMaven.ResolveMavenString()!; - var serverBinPath = Path.Combine(RootPath, + var serverBinPath = Path.Combine(this.RootPath, GamePathHelper.GetLibraryPath(serverBinMaven.Path)); var di = new DirectoryInfo(Path.GetDirectoryName(serverBinPath)!); @@ -176,7 +158,7 @@ public async Task InstallForgeTaskAsync() ipModel.Data["BINPATCH"].Client = $"[{clientMaven}]"; var clientBinMaven = clientMaven.ResolveMavenString()!; - var clientBinPath = Path.Combine(RootPath, + var clientBinPath = Path.Combine(this.RootPath, GamePathHelper.GetLibraryPath(clientBinMaven.Path)); var di = new DirectoryInfo(Path.GetDirectoryName(clientBinPath)!); @@ -192,7 +174,7 @@ public async Task InstallForgeTaskAsync() #region 解压 Forge Jar - InvokeStatusChangedEvent("解压 Forge Jar", 0.5); + this.InvokeStatusChangedEvent("解压 Forge Jar", 0.5); var forgeJar = archive.Entries.FirstOrDefault(e => e.Key?.Equals($"maven/net/minecraftforge/forge/{forgeVersion}/forge-{forgeVersion}.jar", @@ -206,7 +188,7 @@ public async Task InstallForgeTaskAsync() if (forgeUniversalJar != default) { var forgeUniversalSubPath = forgeUniversalJar.Key![(forgeUniversalJar.Key!.IndexOf('/') + 1)..]; - var forgeUniversalLibPath = Path.Combine(RootPath, + var forgeUniversalLibPath = Path.Combine(this.RootPath, GamePathHelper.GetLibraryPath(forgeUniversalSubPath)); if (string.IsNullOrEmpty(forgeUniversalSubPath) @@ -230,7 +212,7 @@ public async Task InstallForgeTaskAsync() var forgeSubPath = forgeJar.Key![(forgeJar.Key!.IndexOf('/') + 1)..]; var forgeLibPath = - Path.Combine(RootPath, GamePathHelper.GetLibraryPath(forgeSubPath)); + Path.Combine(this.RootPath, GamePathHelper.GetLibraryPath(forgeSubPath)); var forgeLibDir = Path.GetDirectoryName(forgeLibPath)!; if (!Directory.Exists(forgeLibDir)) @@ -251,9 +233,8 @@ public async Task InstallForgeTaskAsync() #region 预下载 Mojang Mappings if (ipModel.Data.TryGetValue("MOJMAPS", out var mapsVal) && - !string.IsNullOrEmpty(mapsVal.Client) && - CustomMojangClientMappings != null && - !string.IsNullOrEmpty(CustomMojangClientMappings.Url)) + !string.IsNullOrEmpty(mapsVal.Client) && this.CustomMojangClientMappings != null && + !string.IsNullOrEmpty(this.CustomMojangClientMappings.Url)) { var clientMavenStr = mapsVal.Client.TrimStart('[').TrimEnd(']'); var resolvedMappingMaven = clientMavenStr.ResolveMavenString()!; @@ -261,15 +242,15 @@ public async Task InstallForgeTaskAsync() var mappingFileName = Path.GetFileName(resolvedMappingMaven.Path); var mappingDf = new DownloadFile { - CheckSum = CustomMojangClientMappings.Sha1, + CheckSum = this.CustomMojangClientMappings.Sha1, DownloadPath = mappingPath!, - DownloadUri = CustomMojangClientMappings.Url, + DownloadUri = this.CustomMojangClientMappings.Url, FileName = mappingFileName }; mappingDf.Changed += (_, args) => { - InvokeStatusChangedEvent( + this.InvokeStatusChangedEvent( $"下载 - {mappingFileName} ( {args.ProgressPercentage} / 100 )", args.ProgressPercentage); }; @@ -291,7 +272,7 @@ public async Task InstallForgeTaskAsync() #region 解析 Processor - InvokeStatusChangedEvent("解析 Processor", 1); + this.InvokeStatusChangedEvent("解析 Processor", 1); string? ResolvePathRegex(string? val) { @@ -299,7 +280,7 @@ public async Task InstallForgeTaskAsync() var name = val[1..^1]; var maven = name.ResolveMavenString()!; - var path = Path.Combine(RootPath, + var path = Path.Combine(this.RootPath, GamePathHelper.GetLibraryPath(maven.Path)); return path; @@ -311,7 +292,7 @@ public async Task InstallForgeTaskAsync() "MINECRAFT_JAR", new ForgeInstallProfileData { - Client = GamePathHelper.GetVersionJar(RootPath, MineCraftVersionId) + Client = GamePathHelper.GetVersionJar(this.RootPath, this.MineCraftVersionId) } } }; @@ -320,10 +301,10 @@ public async Task InstallForgeTaskAsync() { var resolvedKey = ResolvePathRegex(v.Client); var resolvedValue = ResolvePathRegex(v.Server); - + if (string.IsNullOrEmpty(resolvedKey) || string.IsNullOrEmpty(resolvedValue)) continue; - + variables.TryAdd(k, new ForgeInstallProfileData { Client = resolvedKey, @@ -336,7 +317,7 @@ public async Task InstallForgeTaskAsync() if (string.IsNullOrEmpty(val) || string.IsNullOrEmpty(VariableRegex().Match(val).Value)) return val; var key = val[1..^1]; - + return variables[key].Client; } @@ -344,11 +325,11 @@ public async Task InstallForgeTaskAsync() var argsReplaceList = new Dictionary { { "{SIDE}", "client" }, - { "{MINECRAFT_JAR}", GamePathHelper.GetVersionJar(RootPath, MineCraftVersionId) }, - { "{MINECRAFT_VERSION}", MineCraftVersion }, - { "{ROOT}", RootPath }, - { "{INSTALLER}", ForgeExecutablePath }, - { "{LIBRARY_DIR}", Path.Combine(RootPath, GamePathHelper.GetLibraryRootPath()) } + { "{MINECRAFT_JAR}", GamePathHelper.GetVersionJar(this.RootPath, this.MineCraftVersionId) }, + { "{MINECRAFT_VERSION}", this.MineCraftVersion }, + { "{ROOT}", this.RootPath }, + { "{INSTALLER}", this.ForgeExecutablePath }, + { "{LIBRARY_DIR}", Path.Combine(this.RootPath, GamePathHelper.GetLibraryRootPath()) } }; foreach (var proc in ipModel.Processors) @@ -365,10 +346,10 @@ public async Task InstallForgeTaskAsync() { var resolvedKey = ResolveVariableRegex(k); var resolvedValue = ResolveVariableRegex(v); - + if (string.IsNullOrEmpty(resolvedKey) || string.IsNullOrEmpty(resolvedValue)) continue; - + outputs.TryAdd(resolvedKey, resolvedValue); } @@ -393,16 +374,16 @@ public async Task InstallForgeTaskAsync() #region 补全 Libraries - _failedFiles.Clear(); + this._failedFiles.Clear(); - var resolvedLibs = VersionLocator.GetNatives([.. ipModel.Libraries, .. versionJsonModel.Libraries]).Item2; + var resolvedLibs = this.VersionLocator.GetNatives([.. ipModel.Libraries, .. versionJsonModel.Libraries]).Item2; var libDownloadInfo = new List(); foreach (var lib in resolvedLibs) { if (string.IsNullOrEmpty(lib.Path) || string.IsNullOrEmpty(lib.Url)) continue; - + if ( (lib.Name?.StartsWith("net.minecraftforge:forge", StringComparison.OrdinalIgnoreCase) ?? false) && string.IsNullOrEmpty(lib.Url) @@ -411,9 +392,9 @@ public async Task InstallForgeTaskAsync() var symbolIndex = lib.Path.LastIndexOf('/'); var fileName = lib.Path[(symbolIndex + 1)..]; - var path = Path.Combine(RootPath, + var path = Path.Combine(this.RootPath, GamePathHelper.GetLibraryPath(lib.Path[..symbolIndex])); - + /* if (!string.IsNullOrEmpty(DownloadUrlRoot)) { @@ -428,16 +409,14 @@ public async Task InstallForgeTaskAsync() var fullFilePath = Path.Combine(path, fileName); if (File.Exists(fullFilePath)) - { if (!string.IsNullOrEmpty(lib.Sha1)) { await using var fs = File.OpenRead(fullFilePath); - var hashBytes = await SHA1.HashDataAsync(fs); + var hash = Convert.ToHexString(await SHA1.HashDataAsync(fs)); - if (hashBytes.BytesToString().Equals(lib.Sha1, StringComparison.OrdinalIgnoreCase)) + if (hash.Equals(lib.Sha1, StringComparison.OrdinalIgnoreCase)) continue; } - } var libDi = new DirectoryInfo(path); @@ -452,12 +431,12 @@ public async Task InstallForgeTaskAsync() DownloadUri = lib.Url, FileSize = lib.Size }; - df.Completed += WhenCompleted; + df.Completed += this.WhenCompleted; libDownloadInfo.Add(df); } - _needToDownload = libDownloadInfo.Count; + this._needToDownload = libDownloadInfo.Count; await DownloadHelper.AdvancedDownloadListFile(libDownloadInfo, new DownloadSettings { @@ -468,7 +447,7 @@ public async Task InstallForgeTaskAsync() Timeout = TimeSpan.FromSeconds(20) }); - if (!_failedFiles.IsEmpty) + if (!this._failedFiles.IsEmpty) return new ForgeInstallResult { Succeeded = false, @@ -484,11 +463,11 @@ public async Task InstallForgeTaskAsync() #region 启动 Process - _needToProcess = procList.Count; + this._needToProcess = procList.Count; foreach (var processor in procList) { var maven = processor.Processor.Jar.ResolveMavenString()!; - var libPath = Path.Combine(RootPath, GamePathHelper.GetLibraryPath(maven.Path)); + var libPath = Path.Combine(this.RootPath, GamePathHelper.GetLibraryPath(maven.Path)); using var libArchive = ArchiveFactory.Open(Path.GetFullPath(libPath)); var libEntry = @@ -496,7 +475,7 @@ public async Task InstallForgeTaskAsync() e.Key?.Equals("META-INF/MANIFEST.MF", StringComparison.OrdinalIgnoreCase) ?? false); if (libEntry == null) - return GetCorruptedFileResult(); + return this.GetCorruptedFileResult(); await using var libStream = libEntry.OpenEntryStream(); using var libSr = new StreamReader(libStream, Encoding.UTF8); @@ -513,7 +492,7 @@ where lineSp[0].Equals("Main-Class", StringComparison.OrdinalIgnoreCase) var cp = totalLibs .Select(MavenHelper.ResolveMavenString) - .Select(m => Path.Combine(RootPath, GamePathHelper.GetLibraryPath(m!.Path))); + .Select(m => Path.Combine(this.RootPath, GamePathHelper.GetLibraryPath(m!.Path))); var cpStr = string.Join(Path.PathSeparator, cp); var parameter = new List { @@ -524,11 +503,11 @@ where lineSp[0].Equals("Main-Class", StringComparison.OrdinalIgnoreCase) parameter.AddRange(processor.Arguments); - var pi = new ProcessStartInfo(JavaExecutablePath) + var pi = new ProcessStartInfo(this.JavaExecutablePath) { Arguments = string.Join(' ', parameter), UseShellExecute = false, - WorkingDirectory = Path.GetFullPath(RootPath), + WorkingDirectory = Path.GetFullPath(this.RootPath), RedirectStandardError = true, RedirectStandardOutput = true }; @@ -556,10 +535,11 @@ where lineSp[0].Equals("Main-Class", StringComparison.OrdinalIgnoreCase) logSb.AppendLine(args.Data); var data = args.Data; - var progress = (double)_totalProcessed / _needToProcess; + var progress = (double)this._totalProcessed / this._needToProcess; var dataStr = data.CropStr(40); - InvokeStatusChangedEvent($"{dataStr} <安装信息> ( {_totalProcessed} / {_needToProcess} )", progress); + this.InvokeStatusChangedEvent($"{dataStr} <安装信息> ( {this._totalProcessed} / {this._needToProcess} )", + progress); }; p.ErrorDataReceived += (_, args) => @@ -569,26 +549,28 @@ where lineSp[0].Equals("Main-Class", StringComparison.OrdinalIgnoreCase) errSb.AppendLine(args.Data); var data = args.Data ?? string.Empty; - var progress = (double)_totalProcessed / _needToProcess; + var progress = (double)this._totalProcessed / this._needToProcess; var dataStr = data.CropStr(40); - InvokeStatusChangedEvent($"{dataStr} <错误> ( {_totalProcessed} / {_needToProcess} )", progress); + this.InvokeStatusChangedEvent($"{dataStr} <错误> ( {this._totalProcessed} / {this._needToProcess} )", + progress); }; p.BeginOutputReadLine(); p.BeginErrorReadLine(); - _totalProcessed++; + this._totalProcessed++; await p.WaitForExitAsync(); - var installLogPath = Path.Combine(RootPath, GamePathHelper.GetGamePath(id)); + var installLogPath = Path.Combine(this.RootPath, GamePathHelper.GetGamePath(id)); if (logSb.Length != 0) - await File.WriteAllTextAsync(Path.Combine(installLogPath, $"PROCESSOR #{_totalProcessed}_Logs.log"), + await File.WriteAllTextAsync( + Path.Combine(installLogPath, $"PROCESSOR #{this._totalProcessed}_Logs.log"), logSb.ToString()); if (errSb.Length != 0) await File.WriteAllTextAsync( - Path.Combine(installLogPath, $"PROCESSOR #{_totalProcessed}_Errors.log"), errSb.ToString()); + Path.Combine(installLogPath, $"PROCESSOR #{this._totalProcessed}_Errors.log"), errSb.ToString()); if (errSb.Length != 0) return new ForgeInstallResult @@ -611,19 +593,39 @@ await File.WriteAllTextAsync( }; } + [GeneratedRegex(@"^\[.+\]$")] + private static partial Regex PathRegex(); + + [GeneratedRegex("^{.+}$")] + private static partial Regex VariableRegex(); + + ForgeInstallResult GetCorruptedFileResult() + { + return new ForgeInstallResult + { + Succeeded = false, + Error = new ErrorModel + { + Cause = "损坏的 Forge 可执行文件", + Error = "安装前准备失败", + ErrorMessage = "损坏的 Forge 可执行文件,请确认您的路径是否正确" + } + }; + } + void WhenCompleted(object? sender, DownloadFileCompletedEventArgs e) { if (sender is not DownloadFile file) return; - _totalDownloaded++; + this._totalDownloaded++; - var progress = (double)_totalDownloaded / _needToDownload; + var progress = (double)this._totalDownloaded / this._needToDownload; var retryStr = file.RetryCount > 0 ? $"[重试 - {file.RetryCount}] " : string.Empty; - InvokeStatusChangedEvent( - $"{retryStr}下载 - {file.FileName} ( {_totalDownloaded} / {_needToDownload} )", + this.InvokeStatusChangedEvent( + $"{retryStr}下载 - {file.FileName} ( {this._totalDownloaded} / {this._needToDownload} )", progress); - if (!(e.Success ?? false)) _failedFiles.Add(file); + if (!(e.Success ?? false)) this._failedFiles.Add(file); } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ForgeInstaller/LegacyForgeInstaller.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ForgeInstaller/LegacyForgeInstaller.cs index 4bf69214..cddbd5c5 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ForgeInstaller/LegacyForgeInstaller.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ForgeInstaller/LegacyForgeInstaller.cs @@ -34,22 +34,23 @@ public string DownloadUrlRoot public ForgeInstallResult InstallForge() { - return InstallForgeTaskAsync().GetAwaiter().GetResult(); + return this.InstallForgeTaskAsync().GetAwaiter().GetResult(); } public async Task InstallForgeTaskAsync() { try { - InvokeStatusChangedEvent("解压安装文件", 0.05); + this.InvokeStatusChangedEvent("解压安装文件", 0.05); - using var reader = ArchiveFactory.Open(ForgeExecutablePath); + using var reader = ArchiveFactory.Open(this.ForgeExecutablePath); var profileEntry = reader.Entries.FirstOrDefault(e => e.Key?.Equals("install_profile.json", StringComparison.Ordinal) ?? false); var legacyJarEntry = reader.Entries.FirstOrDefault(e => - e.Key?.Equals($"forge-{ForgeVersion}-universal.jar", StringComparison.OrdinalIgnoreCase) ?? false); + e.Key?.Equals($"forge-{this.ForgeVersion}-universal.jar", StringComparison.OrdinalIgnoreCase) ?? + false); if (profileEntry == default) return new ForgeInstallResult @@ -75,38 +76,38 @@ public async Task InstallForgeTaskAsync() Succeeded = false }; - InvokeStatusChangedEvent("解压完成", 0.1); + this.InvokeStatusChangedEvent("解压完成", 0.1); await using var stream = profileEntry.OpenEntryStream(); - InvokeStatusChangedEvent("解析安装文档", 0.35); + this.InvokeStatusChangedEvent("解析安装文档", 0.35); var profileModel = await JsonSerializer.DeserializeAsync(stream, LegacyForgeInstallProfileContext.Default.LegacyForgeInstallProfile); if (profileModel == null) throw new ArgumentNullException(nameof(profileModel)); - InvokeStatusChangedEvent("解析完成", 0.75); + this.InvokeStatusChangedEvent("解析完成", 0.75); - var id = string.IsNullOrEmpty(CustomId) ? profileModel.VersionInfo.Id : CustomId; + var id = string.IsNullOrEmpty(this.CustomId) ? profileModel.VersionInfo.Id : this.CustomId; - var installDir = Path.Combine(RootPath, GamePathHelper.GetGamePath(id)); - var jsonPath = GamePathHelper.GetGameJsonPath(RootPath, id); + var installDir = Path.Combine(this.RootPath, GamePathHelper.GetGamePath(id)); + var jsonPath = GamePathHelper.GetGameJsonPath(this.RootPath, id); var forgeDi = new DirectoryInfo(installDir); if (!forgeDi.Exists) forgeDi.Create(); profileModel.VersionInfo.Id = id; - if (!string.IsNullOrEmpty(InheritsFrom)) - profileModel.VersionInfo.InheritsFrom = InheritsFrom; + if (!string.IsNullOrEmpty(this.InheritsFrom)) + profileModel.VersionInfo.InheritsFrom = this.InheritsFrom; var forgeLibrary = profileModel.VersionInfo.Libraries.First(l => l.Name.StartsWith("net.minecraftforge:forge", StringComparison.OrdinalIgnoreCase)); var mavenInfo = forgeLibrary.Name.ResolveMavenString()!; var libSubPath = GamePathHelper.GetLibraryPath(mavenInfo.Path); - var forgeLibPath = Path.Combine(RootPath, libSubPath); + var forgeLibPath = Path.Combine(this.RootPath, libSubPath); var libDi = new DirectoryInfo(Path.GetDirectoryName(forgeLibPath)!); @@ -120,7 +121,7 @@ public async Task InstallForgeTaskAsync() new LegacyForgeInstallVersionInfoContext(JsonHelper.CamelCasePropertyNamesSettings())); await File.WriteAllTextAsync(jsonPath, versionJsonString); - InvokeStatusChangedEvent("文件写入完成", 1); + this.InvokeStatusChangedEvent("文件写入完成", 1); return new ForgeInstallResult { diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/LiteLoaderInstaller.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/LiteLoaderInstaller.cs index 91e69abd..d56d5bff 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/LiteLoaderInstaller.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/LiteLoaderInstaller.cs @@ -25,59 +25,59 @@ public class LiteLoaderInstaller : InstallerBase, ILiteLoaderInstaller public string Install() { - return InstallTaskAsync().GetAwaiter().GetResult(); + return this.InstallTaskAsync().GetAwaiter().GetResult(); } public async Task InstallTaskAsync() { - if (string.IsNullOrEmpty(RootPath)) + if (string.IsNullOrEmpty(this.RootPath)) throw new NullReferenceException("RootPath 不能为 null"); - if (InheritVersion == null) + if (this.InheritVersion == null) throw new NullReferenceException("InheritVersion 不能为 null"); - if (VersionModel == null) + if (this.VersionModel == null) throw new NullReferenceException("VersionModel 不能为 null"); - InvokeStatusChangedEvent("开始安装 LiteLoader", 0); + this.InvokeStatusChangedEvent("开始安装 LiteLoader", 0); - var vl = new DefaultVersionLocator(RootPath, Guid.Empty); - var rawVersion = vl.ParseRawVersion(VersionModel.McVersion); + var vl = new DefaultVersionLocator(this.RootPath, Guid.Empty); + var rawVersion = vl.ParseRawVersion(this.VersionModel.McVersion); - InvokeStatusChangedEvent("解析版本", 10); + this.InvokeStatusChangedEvent("解析版本", 10); if (rawVersion == null) - throw new UnknownGameNameException(VersionModel.McVersion); + throw new UnknownGameNameException(this.VersionModel.McVersion); - if (rawVersion.Id != VersionModel.McVersion) + if (rawVersion.Id != this.VersionModel.McVersion) throw new NotSupportedException("LiteLoader 并不支持这个 MineCraft 版本"); - var id = string.IsNullOrEmpty(CustomId) - ? $"{VersionModel.McVersion}-LiteLoader{VersionModel.McVersion}-{VersionModel.Version}" - : CustomId; + var id = string.IsNullOrEmpty(this.CustomId) + ? $"{this.VersionModel.McVersion}-LiteLoader{this.VersionModel.McVersion}-{this.VersionModel.Version}" + : this.CustomId; - var timeStamp = long.TryParse(VersionModel.Build.Timestamp, out var timeResult) ? timeResult : 0; + var timeStamp = long.TryParse(this.VersionModel.Build.Timestamp, out var timeResult) ? timeResult : 0; var time = TimeHelper.Unix11ToDateTime(timeStamp); - InvokeStatusChangedEvent("解析 Libraries", 30); + this.InvokeStatusChangedEvent("解析 Libraries", 30); var libraries = new List { new() { - Name = $"com.mumfrey:liteloader:{VersionModel.Version}", - Url = VersionModel.Type.Equals("SNAPSHOT", StringComparison.OrdinalIgnoreCase) + Name = $"com.mumfrey:liteloader:{this.VersionModel.Version}", + Url = this.VersionModel.Type.Equals("SNAPSHOT", StringComparison.OrdinalIgnoreCase) ? SnapshotRoot : ReleaseRoot } }; - foreach (var lib in VersionModel.Build.Libraries + foreach (var lib in this.VersionModel.Build.Libraries .Where(lib => !string.IsNullOrEmpty(lib.Name) && string.IsNullOrEmpty(lib.Url)).Where(lib => lib.Name.StartsWith("org.ow2.asm", StringComparison.OrdinalIgnoreCase))) lib.Url = "https://files.minecraftforge.net/maven/"; - libraries.AddRange(VersionModel.Build.Libraries); + libraries.AddRange(this.VersionModel.Build.Libraries); - InvokeStatusChangedEvent("Libraries 解析完成", 60); + this.InvokeStatusChangedEvent("Libraries 解析完成", 60); const string mainClass = "net.minecraft.launchwrapper.Launch"; var resultModel = new RawVersionModel @@ -87,25 +87,25 @@ public async Task InstallTaskAsync() ReleaseTime = time, Libraries = [.. libraries], MainClass = mainClass, - InheritsFrom = string.IsNullOrEmpty(InheritsFrom) ? VersionModel.McVersion : InheritsFrom, - BuildType = VersionModel.Type, - JarFile = InheritVersion.JarFile ?? InheritVersion.Id + InheritsFrom = string.IsNullOrEmpty(this.InheritsFrom) ? this.VersionModel.McVersion : this.InheritsFrom, + BuildType = this.VersionModel.Type, + JarFile = this.InheritVersion.JarFile ?? this.InheritVersion.Id }; - if (InheritVersion.Arguments != null) + if (this.InheritVersion.Arguments != null) resultModel.Arguments = new Arguments { Game = [ JsonSerializer.SerializeToElement("--tweakClass", StringContext.Default.String), - JsonSerializer.SerializeToElement(VersionModel.Build.TweakClass, StringContext.Default.String) + JsonSerializer.SerializeToElement(this.VersionModel.Build.TweakClass, StringContext.Default.String) ] }; else resultModel.MinecraftArguments = - $"{InheritVersion.MinecraftArguments} --tweakClass {VersionModel.Build.TweakClass}"; + $"{this.InheritVersion.MinecraftArguments} --tweakClass {this.VersionModel.Build.TweakClass}"; - var gamePath = Path.Combine(RootPath, GamePathHelper.GetGamePath(id)); + var gamePath = Path.Combine(this.RootPath, GamePathHelper.GetGamePath(id)); var di = new DirectoryInfo(gamePath); if (!di.Exists) @@ -113,13 +113,13 @@ public async Task InstallTaskAsync() else DirectoryHelper.CleanDirectory(di.FullName); - var jsonPath = GamePathHelper.GetGameJsonPath(RootPath, id); + var jsonPath = GamePathHelper.GetGameJsonPath(this.RootPath, id); var jsonContent = JsonSerializer.Serialize(resultModel, typeof(RawVersionModel), new RawVersionModelContext(JsonHelper.CamelCasePropertyNamesSettings())); await File.WriteAllTextAsync(jsonPath, jsonContent); - InvokeStatusChangedEvent("LiteLoader 安装完成", 100); + this.InvokeStatusChangedEvent("LiteLoader 安装完成", 100); return id; } diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ModPackInstaller/CurseForgeInstaller.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ModPackInstaller/CurseForgeInstaller.cs index a988f535..a8c1faa9 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ModPackInstaller/CurseForgeInstaller.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ModPackInstaller/CurseForgeInstaller.cs @@ -9,8 +9,8 @@ using System.Threading.Tasks; using System.Threading.Tasks.Dataflow; using ProjBobcat.Class.Helper; -using ProjBobcat.Class.Model; using ProjBobcat.Class.Model.CurseForge; +using ProjBobcat.Class.Model.Downloading; using ProjBobcat.Exceptions; using ProjBobcat.Interface; using SharpCompress.Archives; @@ -25,80 +25,21 @@ public sealed class CurseForgeInstaller : ModPackInstallerBase, ICurseForgeInsta public void Install() { - InstallTaskAsync().GetAwaiter().GetResult(); - } - - public static async Task<(string? FileName, string? Url)> TryGuessModDownloadLink(long fileId) - { - try - { - var files = await CurseForgeAPIHelper.GetFiles([fileId]); - - if (files == null || files.Length == 0) return default; - - var file = files.FirstOrDefault(f => f.Id == fileId); - - if (file == null || string.IsNullOrEmpty(file.FileName)) return default; - - var fileName = file.FileName; - var fileIdStr = fileId.ToString(); - var pendingCheckUrls = new[] - { - $"https://edge.forgecdn.net/files/{fileIdStr[..4]}/{fileIdStr[4..]}/{fileName}", - $"https://mediafiles.forgecdn.net/files/{fileIdStr[..4]}/{fileIdStr[4..]}/{fileName}" - }; - - var httpClient = HttpClientHelper.DefaultClient; - - foreach (var url in pendingCheckUrls) - { - using var checkReq = new HttpRequestMessage(HttpMethod.Head, url); - using var checkRes = await httpClient.SendAsync(checkReq); - - if (!checkRes.IsSuccessStatusCode) continue; - - return (fileName, url); - } - - return default; - } - catch (Exception e) - { - Debug.WriteLine(e); - return default; - } - } - - async Task<(bool, DownloadFile?)> TryGuessModDownloadLink(long fileId, string downloadPath) - { - var pair = await TryGuessModDownloadLink(fileId); - - if (string.IsNullOrEmpty(pair.FileName) || string.IsNullOrEmpty(pair.Url)) return (false, null); - - var df = new DownloadFile - { - DownloadPath = downloadPath, - DownloadUri = pair.Url, - FileName = pair.FileName - }; - - df.Completed += WhenCompleted; - - return (true, df); + this.InstallTaskAsync().GetAwaiter().GetResult(); } public async Task InstallTaskAsync() { - ArgumentException.ThrowIfNullOrEmpty(GameId); - ArgumentException.ThrowIfNullOrEmpty(RootPath); + ArgumentException.ThrowIfNullOrEmpty(this.GameId); + ArgumentException.ThrowIfNullOrEmpty(this.RootPath); - InvokeStatusChangedEvent("开始安装", 0); + this.InvokeStatusChangedEvent("开始安装", 0); - var manifest = await ReadManifestTask(); + var manifest = await this.ReadManifestTask(); ArgumentNullException.ThrowIfNull(manifest, "无法读取到 CurseForge 的 manifest 文件"); - var idPath = Path.Combine(RootPath, GamePathHelper.GetGamePath(GameId)); + var idPath = Path.Combine(this.RootPath, GamePathHelper.GetGamePath(this.GameId)); var downloadPath = Path.Combine(Path.GetFullPath(idPath), "mods"); var di = new DirectoryInfo(downloadPath); @@ -106,7 +47,7 @@ public async Task InstallTaskAsync() if (!di.Exists) di.Create(); - NeedToDownload = manifest.Files?.Length ?? 0; + this.NeedToDownload = manifest.Files?.Length ?? 0; var urlBlock = new TransformManyBlock, (long, long)>(urls => { @@ -133,24 +74,24 @@ public async Task InstallTaskAsync() DownloadUri = d, FileName = fn }; - downloadFile.Completed += WhenCompleted; + downloadFile.Completed += this.WhenCompleted; urlBags.Add(downloadFile); - TotalDownloaded++; - NeedToDownload++; + this.TotalDownloaded++; + this.NeedToDownload++; - var progress = (double)TotalDownloaded / NeedToDownload * 100; + var progress = (double)this.TotalDownloaded / this.NeedToDownload * 100; - InvokeStatusChangedEvent($"成功解析 MOD [{t.Item1}] 的下载地址", + this.InvokeStatusChangedEvent($"成功解析 MOD [{t.Item1}] 的下载地址", progress); } catch (CurseForgeModResolveException e) { - InvokeStatusChangedEvent($"MOD [{t.Item1}] 的下载地址解析失败,尝试手动拼接", + this.InvokeStatusChangedEvent($"MOD [{t.Item1}] 的下载地址解析失败,尝试手动拼接", 114514); - var (guessed, df) = await TryGuessModDownloadLink(t.Item2, di.FullName); + var (guessed, df) = await this.TryGuessModDownloadLink(t.Item2, di.FullName); if (!guessed || df == null) { @@ -162,7 +103,7 @@ public async Task InstallTaskAsync() var moreInfo = $""" 模组名称:{info.Name} - 模组链接:{((info.Links?.TryGetValue("websiteUrl", out var link) ?? false) ? link : "-")} + 模组链接:{(info.Links?.TryGetValue("websiteUrl", out var link) ?? false ? link : "-")} """; var ex = new CurseForgeModResolveException(t.Item1, t.Item2, moreInfo); @@ -178,12 +119,12 @@ public async Task InstallTaskAsync() urlBags.Add(df); - TotalDownloaded++; - NeedToDownload++; + this.TotalDownloaded++; + this.NeedToDownload++; - var progress = (double)TotalDownloaded / NeedToDownload * 100; + var progress = (double)this.TotalDownloaded / this.NeedToDownload * 100; - InvokeStatusChangedEvent($"成功解析 MOD [{t.Item1}] 的下载地址", + this.InvokeStatusChangedEvent($"成功解析 MOD [{t.Item1}] 的下载地址", progress); } }, new ExecutionDataflowBlockOptions @@ -202,7 +143,7 @@ public async Task InstallTaskAsync() if (!urlReqExceptions.IsEmpty) throw new AggregateException(urlReqExceptions); - TotalDownloaded = 0; + this.TotalDownloaded = 0; await DownloadHelper.AdvancedDownloadListFile(urlBags, new DownloadSettings { DownloadParts = 8, @@ -210,13 +151,13 @@ public async Task InstallTaskAsync() Timeout = TimeSpan.FromMinutes(1) }); - if (!FailedFiles.IsEmpty) + if (!this.FailedFiles.IsEmpty) throw new Exception("未能下载全部的 Mods"); - using var archive = ArchiveFactory.Open(Path.GetFullPath(ModPackPath)); + using var archive = ArchiveFactory.Open(Path.GetFullPath(this.ModPackPath)); - TotalDownloaded = 0; - NeedToDownload = archive.Entries.Count(); + this.TotalDownloaded = 0; + this.NeedToDownload = archive.Entries.Count(); foreach (var entry in archive.Entries) { @@ -244,22 +185,24 @@ public async Task InstallTaskAsync() ? $"...{subPath[(subPathLength - 15)..]}" : subPath; - InvokeStatusChangedEvent($"解压缩安装文件:{subPathName}", (double)TotalDownloaded / NeedToDownload * 100); + this.InvokeStatusChangedEvent($"解压缩安装文件:{subPathName}", + (double)this.TotalDownloaded / this.NeedToDownload * 100); await using var fs = File.OpenWrite(path); entry.WriteTo(fs); - TotalDownloaded++; + this.TotalDownloaded++; } - InvokeStatusChangedEvent("安装完成", 100); + this.InvokeStatusChangedEvent("安装完成", 100); } public async Task ReadManifestTask() { - using var archive = ArchiveFactory.Open(Path.GetFullPath(ModPackPath)); + using var archive = ArchiveFactory.Open(Path.GetFullPath(this.ModPackPath)); var manifestEntry = - archive.Entries.FirstOrDefault(x => x.Key?.Equals("manifest.json", StringComparison.OrdinalIgnoreCase) ?? false); + archive.Entries.FirstOrDefault(x => + x.Key?.Equals("manifest.json", StringComparison.OrdinalIgnoreCase) ?? false); if (manifestEntry == default) return default; @@ -272,4 +215,63 @@ await JsonSerializer.DeserializeAsync(stream, return manifestModel; } + + public static async Task<(string? FileName, string? Url)> TryGuessModDownloadLink(long fileId) + { + try + { + var files = await CurseForgeAPIHelper.GetFiles([fileId]); + + if (files == null || files.Length == 0) return default; + + var file = files.FirstOrDefault(f => f.Id == fileId); + + if (file == null || string.IsNullOrEmpty(file.FileName)) return default; + + var fileName = file.FileName; + var fileIdStr = fileId.ToString(); + var pendingCheckUrls = new[] + { + $"https://edge.forgecdn.net/files/{fileIdStr[..4]}/{fileIdStr[4..]}/{fileName}", + $"https://mediafiles.forgecdn.net/files/{fileIdStr[..4]}/{fileIdStr[4..]}/{fileName}" + }; + + var httpClient = HttpClientHelper.DefaultClient; + + foreach (var url in pendingCheckUrls) + { + using var checkReq = new HttpRequestMessage(HttpMethod.Head, url); + using var checkRes = await httpClient.SendAsync(checkReq); + + if (!checkRes.IsSuccessStatusCode) continue; + + return (fileName, url); + } + + return default; + } + catch (Exception e) + { + Debug.WriteLine(e); + return default; + } + } + + async Task<(bool, DownloadFile?)> TryGuessModDownloadLink(long fileId, string downloadPath) + { + var pair = await TryGuessModDownloadLink(fileId); + + if (string.IsNullOrEmpty(pair.FileName) || string.IsNullOrEmpty(pair.Url)) return (false, null); + + var df = new DownloadFile + { + DownloadPath = downloadPath, + DownloadUri = pair.Url, + FileName = pair.FileName + }; + + df.Completed += this.WhenCompleted; + + return (true, df); + } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ModPackInstaller/ModPackInstallerBase.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ModPackInstaller/ModPackInstallerBase.cs index 9591a802..3b0d738d 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ModPackInstaller/ModPackInstallerBase.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ModPackInstaller/ModPackInstallerBase.cs @@ -1,5 +1,6 @@ using System.Collections.Concurrent; using ProjBobcat.Class.Model; +using ProjBobcat.Class.Model.Downloading; using ProjBobcat.Event; namespace ProjBobcat.DefaultComponent.Installer.ModPackInstaller; @@ -13,17 +14,18 @@ protected void WhenCompleted(object? sender, DownloadFileCompletedEventArgs e) { if (sender is not DownloadFile file) return; - TotalDownloaded++; + this.TotalDownloaded++; - var progress = (double)TotalDownloaded / NeedToDownload * 100; + var progress = (double)this.TotalDownloaded / this.NeedToDownload * 100; var retryStr = file.RetryCount > 0 ? $"[重试 - {file.RetryCount}] " : string.Empty; var fileName = file.FileName.Length > 20 ? $"{file.FileName[..20]}..." : file.FileName; - InvokeStatusChangedEvent($"{retryStr}下载整合包中的 Mods - {fileName} ({TotalDownloaded} / {NeedToDownload})", + this.InvokeStatusChangedEvent( + $"{retryStr}下载整合包中的 Mods - {fileName} ({this.TotalDownloaded} / {this.NeedToDownload})", progress); - if (!(e.Success ?? false)) FailedFiles.Add(file); + if (!(e.Success ?? false)) this.FailedFiles.Add(file); } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ModPackInstaller/ModrinthInstaller.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ModPackInstaller/ModrinthInstaller.cs index 6748fc80..6fb18e16 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ModPackInstaller/ModrinthInstaller.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ModPackInstaller/ModrinthInstaller.cs @@ -5,7 +5,7 @@ using System.Text.Json; using System.Threading.Tasks; using ProjBobcat.Class.Helper; -using ProjBobcat.Class.Model; +using ProjBobcat.Class.Model.Downloading; using ProjBobcat.Class.Model.Modrinth; using ProjBobcat.Interface; using SharpCompress.Archives; @@ -20,7 +20,7 @@ public sealed class ModrinthInstaller : ModPackInstallerBase, IModrinthInstaller public async Task ReadIndexTask() { - using var archive = ArchiveFactory.Open(Path.GetFullPath(ModPackPath)); + using var archive = ArchiveFactory.Open(Path.GetFullPath(this.ModPackPath)); var manifestEntry = archive.Entries.FirstOrDefault(x => x.Key?.Equals("modrinth.index.json", StringComparison.OrdinalIgnoreCase) ?? false); @@ -38,24 +38,24 @@ public sealed class ModrinthInstaller : ModPackInstallerBase, IModrinthInstaller public void Install() { - InstallTaskAsync().GetAwaiter().GetResult(); + this.InstallTaskAsync().GetAwaiter().GetResult(); } public async Task InstallTaskAsync() { - if (string.IsNullOrEmpty(GameId)) - throw new ArgumentNullException(nameof(GameId)); - if (string.IsNullOrEmpty(RootPath)) - throw new ArgumentNullException(nameof(RootPath)); - - InvokeStatusChangedEvent("开始安装", 0); + if (string.IsNullOrEmpty(this.GameId)) + throw new ArgumentNullException(nameof(this.GameId)); + if (string.IsNullOrEmpty(this.RootPath)) + throw new ArgumentNullException(nameof(this.RootPath)); - var index = await ReadIndexTask(); + this.InvokeStatusChangedEvent("开始安装", 0); + + var index = await this.ReadIndexTask(); if (index == default) throw new Exception("无法读取到 Modrinth 的 manifest 文件"); - var idPath = Path.Combine(RootPath, GamePathHelper.GetGamePath(GameId)); + var idPath = Path.Combine(this.RootPath, GamePathHelper.GetGamePath(this.GameId)); var downloadPath = Path.Combine(Path.GetFullPath(idPath), "mods"); var di = new DirectoryInfo(downloadPath); @@ -83,13 +83,13 @@ public async Task InstallTaskAsync() FileName = fileName, FileSize = file.Size }; - df.Completed += WhenCompleted; + df.Completed += this.WhenCompleted; downloadFiles.Add(df); } - TotalDownloaded = 0; - NeedToDownload = downloadFiles.Count; + this.TotalDownloaded = 0; + this.NeedToDownload = downloadFiles.Count; await DownloadHelper.AdvancedDownloadListFile(downloadFiles, new DownloadSettings { DownloadParts = 8, @@ -99,13 +99,13 @@ public async Task InstallTaskAsync() HashType = HashType.SHA1 }); - if (!FailedFiles.IsEmpty) + if (!this.FailedFiles.IsEmpty) throw new NullReferenceException("未能下载全部的 Mods"); - using var archive = ArchiveFactory.Open(Path.GetFullPath(ModPackPath)); + using var archive = ArchiveFactory.Open(Path.GetFullPath(this.ModPackPath)); - TotalDownloaded = 0; - NeedToDownload = archive.Entries.Count(); + this.TotalDownloaded = 0; + this.NeedToDownload = archive.Entries.Count(); const string decompressPrefix = "overrides"; @@ -134,14 +134,15 @@ public async Task InstallTaskAsync() ? $"...{subPath[(subPathLength - 15)..]}" : subPath; - InvokeStatusChangedEvent($"解压缩安装文件:{subPathName}", (double)TotalDownloaded / NeedToDownload * 100); + this.InvokeStatusChangedEvent($"解压缩安装文件:{subPathName}", + (double)this.TotalDownloaded / this.NeedToDownload * 100); await using var fs = File.OpenWrite(path); entry.WriteTo(fs); - TotalDownloaded++; + this.TotalDownloaded++; } - InvokeStatusChangedEvent("安装完成", 100); + this.InvokeStatusChangedEvent("安装完成", 100); } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/OptifineInstaller.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/OptifineInstaller.cs index 8ead5013..fcdadee9 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/OptifineInstaller.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/OptifineInstaller.cs @@ -24,40 +24,41 @@ public class OptifineInstaller : InstallerBase, IOptifineInstaller public string Install() { - return InstallTaskAsync().GetAwaiter().GetResult(); + return this.InstallTaskAsync().GetAwaiter().GetResult(); } public async Task InstallTaskAsync() { - if (string.IsNullOrEmpty(JavaExecutablePath)) + if (string.IsNullOrEmpty(this.JavaExecutablePath)) throw new NullReferenceException("未指定 Java 运行时"); - if (string.IsNullOrEmpty(OptifineJarPath)) + if (string.IsNullOrEmpty(this.OptifineJarPath)) throw new NullReferenceException("未指定 Optifine 安装包路径"); - if (OptifineDownloadVersion == null) + if (this.OptifineDownloadVersion == null) throw new NullReferenceException("未指定 Optifine 下载信息"); - InvokeStatusChangedEvent("开始安装 Optifine", 0); - var mcVersion = OptifineDownloadVersion.McVersion; - var edition = OptifineDownloadVersion.Type; - var release = OptifineDownloadVersion.Patch; + this.InvokeStatusChangedEvent("开始安装 Optifine", 0); + var mcVersion = this.OptifineDownloadVersion.McVersion; + var edition = this.OptifineDownloadVersion.Type; + var release = this.OptifineDownloadVersion.Patch; var editionRelease = $"{edition}_{release}"; - var id = string.IsNullOrEmpty(CustomId) + var id = string.IsNullOrEmpty(this.CustomId) ? $"{mcVersion}-Optifine_{editionRelease}" - : CustomId; + : this.CustomId; - var versionPath = Path.Combine(RootPath, GamePathHelper.GetGamePath(id)); + var versionPath = Path.Combine(this.RootPath, GamePathHelper.GetGamePath(id)); var di = new DirectoryInfo(versionPath); if (!di.Exists) di.Create(); - InvokeStatusChangedEvent("读取 Optifine 数据", 20); - using var archive = ArchiveFactory.Open(OptifineJarPath); + this.InvokeStatusChangedEvent("读取 Optifine 数据", 20); + using var archive = ArchiveFactory.Open(this.OptifineJarPath); var entries = archive.Entries; var launchWrapperVersion = "1.12"; var launchWrapperOfEntry = - entries.FirstOrDefault(e => e.Key?.Equals("launchwrapper-of.txt", StringComparison.OrdinalIgnoreCase) ?? false); + entries.FirstOrDefault(e => + e.Key?.Equals("launchwrapper-of.txt", StringComparison.OrdinalIgnoreCase) ?? false); if (launchWrapperOfEntry != null) { @@ -69,12 +70,12 @@ public async Task InstallTaskAsync() var launchWrapperEntry = entries.FirstOrDefault(x => x.Key?.Equals($"launchwrapper-of-{launchWrapperVersion}.jar") ?? false); - InvokeStatusChangedEvent("生成版本总成", 40); + this.InvokeStatusChangedEvent("生成版本总成", 40); var versionModel = new RawVersionModel { Id = id, - InheritsFrom = InheritsFrom ?? mcVersion, + InheritsFrom = this.InheritsFrom ?? mcVersion, Arguments = new Arguments { Game = @@ -97,24 +98,24 @@ public async Task InstallTaskAsync() }, new Library { - Name = $"optifine:Optifine:{OptifineDownloadVersion.McVersion}_{editionRelease}" + Name = $"optifine:Optifine:{this.OptifineDownloadVersion.McVersion}_{editionRelease}" } ], MainClass = "net.minecraft.launchwrapper.Launch", MinimumLauncherVersion = 21 }; - var versionJsonPath = GamePathHelper.GetGameJsonPath(RootPath, id); + var versionJsonPath = GamePathHelper.GetGameJsonPath(this.RootPath, id); var jsonStr = JsonSerializer.Serialize(versionModel, typeof(RawVersionModel), new RawVersionModelContext(JsonHelper.CamelCasePropertyNamesSettings())); await File.WriteAllTextAsync(versionJsonPath, jsonStr); - var librariesPath = Path.Combine(RootPath, GamePathHelper.GetLibraryRootPath(), "optifine", + var librariesPath = Path.Combine(this.RootPath, GamePathHelper.GetLibraryRootPath(), "optifine", "launchwrapper-of", launchWrapperVersion); var libDi = new DirectoryInfo(librariesPath); - InvokeStatusChangedEvent("写入 Optifine 数据", 60); + this.InvokeStatusChangedEvent("写入 Optifine 数据", 60); if (!libDi.Exists) libDi.Create(); @@ -123,33 +124,33 @@ public async Task InstallTaskAsync() $"launchwrapper-of-{launchWrapperVersion}.jar"); if (!File.Exists(launchWrapperPath) && launchWrapperEntry != null) { - InvokeStatusChangedEvent($"解压 launcherwrapper-{launchWrapperVersion} 数据", 65); + this.InvokeStatusChangedEvent($"解压 launcherwrapper-{launchWrapperVersion} 数据", 65); await using var launchWrapperFs = File.OpenWrite(launchWrapperPath); launchWrapperEntry.WriteTo(launchWrapperFs); } - var gameJarPath = Path.Combine(RootPath, - GamePathHelper.GetGameExecutablePath(InheritsFrom ?? OptifineDownloadVersion.McVersion)); - var optifineLibPath = Path.Combine(RootPath, GamePathHelper.GetLibraryRootPath(), "optifine", "Optifine", - $"{OptifineDownloadVersion.McVersion}_{editionRelease}", - $"Optifine-{OptifineDownloadVersion.McVersion}_{editionRelease}.jar"); + var gameJarPath = Path.Combine(this.RootPath, + GamePathHelper.GetGameExecutablePath(this.InheritsFrom ?? this.OptifineDownloadVersion.McVersion)); + var optifineLibPath = Path.Combine(this.RootPath, GamePathHelper.GetLibraryRootPath(), "optifine", "Optifine", + $"{this.OptifineDownloadVersion.McVersion}_{editionRelease}", + $"Optifine-{this.OptifineDownloadVersion.McVersion}_{editionRelease}.jar"); var optifineLibPathDi = new DirectoryInfo(Path.GetDirectoryName(optifineLibPath)!); if (!optifineLibPathDi.Exists) optifineLibPathDi.Create(); - InvokeStatusChangedEvent("执行安装脚本", 80); + this.InvokeStatusChangedEvent("执行安装脚本", 80); - var ps = new ProcessStartInfo(JavaExecutablePath) + var ps = new ProcessStartInfo(this.JavaExecutablePath) { ArgumentList = { "-cp", - OptifineJarPath, + this.OptifineJarPath, "optifine.Patcher", Path.GetFullPath(gameJarPath), - OptifineJarPath, + this.OptifineJarPath, Path.GetFullPath(optifineLibPath) }, RedirectStandardError = true, @@ -164,7 +165,7 @@ public async Task InstallTaskAsync() void LogReceivedEvent(object sender, DataReceivedEventArgs args) { - InvokeStatusChangedEvent(args.Data ?? "loading...", 85); + this.InvokeStatusChangedEvent(args.Data ?? "loading...", 85); } p.OutputDataReceived += LogReceivedEvent; @@ -179,12 +180,12 @@ void LogReceivedEvent(object sender, DataReceivedEventArgs args) }; await p.WaitForExitAsync(); - InvokeStatusChangedEvent("安装即将完成", 90); + this.InvokeStatusChangedEvent("安装即将完成", 90); if (errList.Count > 0) throw new NullReferenceException(string.Join(Environment.NewLine, errList)); - InvokeStatusChangedEvent("Optifine 安装完成", 100); + this.InvokeStatusChangedEvent("Optifine 安装完成", 100); return id; } diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/QuiltInstaller.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/QuiltInstaller.cs index 58c2ae23..cf37fd96 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/QuiltInstaller.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/QuiltInstaller.cs @@ -18,21 +18,22 @@ public class QuiltInstaller : InstallerBase, IQuiltInstaller const string DefaultMetaUrl = "https://meta.quiltmc.org"; static HttpClient Client => HttpClientHelper.DefaultClient; + public required string MineCraftVersion { get; init; } public override required string RootPath { get; init; } public required QuiltLoaderModel LoaderArtifact { get; init; } - public required string MineCraftVersion { get; init; } public string Install() { - return InstallTaskAsync().GetAwaiter().GetResult(); + return this.InstallTaskAsync().GetAwaiter().GetResult(); } public async Task InstallTaskAsync() { - InvokeStatusChangedEvent("开始安装", 0); + this.InvokeStatusChangedEvent("开始安装", 0); - var url = $"{DefaultMetaUrl}/v3/versions/loader/{MineCraftVersion}/{LoaderArtifact.Version}/profile/json"; + var url = + $"{DefaultMetaUrl}/v3/versions/loader/{this.MineCraftVersion}/{this.LoaderArtifact.Version}/profile/json"; using var req = new HttpRequestMessage(HttpMethod.Get, url); using var res = await Client.SendAsync(req); @@ -45,7 +46,7 @@ public async Task InstallTaskAsync() }; var versionModel = await res.Content.ReadFromJsonAsync(new RawVersionModelContext(jsonOption).RawVersionModel); - InvokeStatusChangedEvent("生成版本总成", 70); + this.InvokeStatusChangedEvent("生成版本总成", 70); if (versionModel == null) throw new NullReferenceException(nameof(versionModel)); @@ -64,13 +65,13 @@ public async Task InstallTaskAsync() versionModel.Libraries[index] = hashed; } - if (!string.IsNullOrEmpty(CustomId)) - versionModel.Id = CustomId; - if(!string.IsNullOrEmpty(InheritsFrom)) - versionModel.InheritsFrom = InheritsFrom; + if (!string.IsNullOrEmpty(this.CustomId)) + versionModel.Id = this.CustomId; + if (!string.IsNullOrEmpty(this.InheritsFrom)) + versionModel.InheritsFrom = this.InheritsFrom; var id = versionModel.Id!; - var installPath = Path.Combine(RootPath, GamePathHelper.GetGamePath(id)); + var installPath = Path.Combine(this.RootPath, GamePathHelper.GetGamePath(id)); var di = new DirectoryInfo(installPath); if (!di.Exists) @@ -78,15 +79,15 @@ public async Task InstallTaskAsync() else DirectoryHelper.CleanDirectory(di.FullName); - var jsonPath = GamePathHelper.GetGameJsonPath(RootPath, id); + var jsonPath = GamePathHelper.GetGameJsonPath(this.RootPath, id); var jsonContent = JsonSerializer.Serialize(versionModel, typeof(RawVersionModel), new RawVersionModelContext(JsonHelper.CamelCasePropertyNamesSettings())); - InvokeStatusChangedEvent("将版本 Json 写入文件", 90); + this.InvokeStatusChangedEvent("将版本 Json 写入文件", 90); await File.WriteAllTextAsync(jsonPath, jsonContent); - InvokeStatusChangedEvent("安装完成", 100); + this.InvokeStatusChangedEvent("安装完成", 100); return id; } diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultLaunchArgumentParser.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultLaunchArgumentParser.cs index 18debe35..b7f6e02b 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultLaunchArgumentParser.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultLaunchArgumentParser.cs @@ -39,54 +39,56 @@ public DefaultLaunchArgumentParser( ArgumentNullException.ThrowIfNull(launchSettings); ArgumentNullException.ThrowIfNull(launcherProfileParser); - _launchSettings = launchSettings; - _rootVersion = rootVersion; - - AuthResult = authResult; - VersionLocator = versionLocator; - RootPath = rootPath; - LaunchSettings = launchSettings; - LauncherProfileParser = launcherProfileParser; - VersionInfo = LaunchSettings.VersionLocator.GetGame(LaunchSettings.Version) - ?? throw new UnknownGameNameException(LaunchSettings.Version); - GameProfile = LauncherProfileParser.GetGameProfile(LaunchSettings.GameName); + this._launchSettings = launchSettings; + this._rootVersion = rootVersion; + + this.AuthResult = authResult; + this.VersionLocator = versionLocator; + this.RootPath = rootPath; + this.LaunchSettings = launchSettings; + this.LauncherProfileParser = launcherProfileParser; + this.VersionInfo = this.LaunchSettings.VersionLocator.GetGame(this.LaunchSettings.Version) + ?? throw new UnknownGameNameException(this.LaunchSettings.Version); + this.GameProfile = this.LauncherProfileParser.GetGameProfile(this.LaunchSettings.GameName); var sb = new StringBuilder(); - foreach (var lib in VersionInfo.Libraries) - sb.Append($"{Path.Combine(RootPath, GamePathHelper.GetLibraryPath(lib.Path!))}{Path.PathSeparator}"); + foreach (var lib in this.VersionInfo.Libraries) + sb.Append($"{Path.Combine(this.RootPath, GamePathHelper.GetLibraryPath(lib.Path!))}{Path.PathSeparator}"); if (true) { var rootJarPath = string.IsNullOrEmpty(rootVersion) ? GamePathHelper.GetGameExecutablePath(launchSettings.Version) : GamePathHelper.GetGameExecutablePath(rootVersion); - var rootJarFullPath = Path.Combine(RootPath, rootJarPath); + var rootJarFullPath = Path.Combine(this.RootPath, rootJarPath); if (File.Exists(rootJarFullPath)) sb.Append(rootJarFullPath); } - ClassPath = sb.ToString(); - LastAuthResult = LaunchSettings.Authenticator.GetLastAuthResult(); + this.ClassPath = sb.ToString(); + this.LastAuthResult = this.LaunchSettings.Authenticator.GetLastAuthResult(); } public bool EnableXmlLoggingOutput { get; init; } + protected override string ClassPath { get; init; } + protected override VersionInfo VersionInfo { get; init; } + public IEnumerable ParseJvmHeadArguments() { - ArgumentNullException.ThrowIfNull(LaunchSettings); + ArgumentNullException.ThrowIfNull(this.LaunchSettings); - if (LaunchSettings.GameArguments == null && - LaunchSettings.FallBackGameArguments == null) + if (this.LaunchSettings.GameArguments == null && this.LaunchSettings.FallBackGameArguments == null) throw new ArgumentNullException("重要参数为 Null!"); - var gameArgs = LaunchSettings.GameArguments ?? LaunchSettings.FallBackGameArguments; + var gameArgs = this.LaunchSettings.GameArguments ?? this.LaunchSettings.FallBackGameArguments; if ((gameArgs!.AdditionalJvmArguments?.Count ?? 0) > 0) foreach (var jvmArg in gameArgs.AdditionalJvmArguments!) yield return jvmArg; - if (string.IsNullOrEmpty(GameProfile?.JavaArgs)) + if (string.IsNullOrEmpty(this.GameProfile?.JavaArgs)) { if (gameArgs.MaxMemory > 0) { @@ -123,7 +125,7 @@ public IEnumerable ParseJvmHeadArguments() } else { - yield return GameProfile.JavaArgs; + yield return this.GameProfile.JavaArgs; } /* @@ -135,18 +137,18 @@ public IEnumerable ParseJvmHeadArguments() public IEnumerable ParseJvmArguments() { - var versionNameFollowing = string.IsNullOrEmpty(_rootVersion) ? string.Empty : $",{_rootVersion}"; - var versionName = $"{LaunchSettings.Version}{versionNameFollowing}".Replace(' ', '_'); + var versionNameFollowing = string.IsNullOrEmpty(this._rootVersion) ? string.Empty : $",{this._rootVersion}"; + var versionName = $"{this.LaunchSettings.Version}{versionNameFollowing}".Replace(' ', '_'); var jvmArgumentsDic = new Dictionary { - { "${natives_directory}", $"\"{NativeRoot}\"" }, - { "${launcher_name}", $"\"{LaunchSettings.LauncherName}\"" }, + { "${natives_directory}", $"\"{this.NativeRoot}\"" }, + { "${launcher_name}", $"\"{this.LaunchSettings.LauncherName}\"" }, { "${launcher_version}", "32" }, - { "${classpath}", $"\"{ClassPath}\"" }, + { "${classpath}", $"\"{this.ClassPath}\"" }, { "${classpath_separator}", Path.PathSeparator.ToString() }, - { "${library_directory}", $"\"{Path.Combine(RootPath, GamePathHelper.GetLibraryRootPath())}\"" }, - { "${version_name}", versionName} + { "${library_directory}", $"\"{Path.Combine(this.RootPath, GamePathHelper.GetLibraryRootPath())}\"" }, + { "${version_name}", versionName } }; #region log4j 缓解措施 @@ -158,74 +160,74 @@ public IEnumerable ParseJvmArguments() yield return "-Dfml.ignoreInvalidMinecraftCertificates=true"; yield return "-Dfml.ignorePatchDiscrepancies=true"; - if (VersionInfo.JvmArguments?.Any() ?? false) + if (this.VersionInfo.JvmArguments?.Any() ?? false) { - foreach (var jvmArg in VersionInfo.JvmArguments) + foreach (var jvmArg in this.VersionInfo.JvmArguments) { var arg = jvmArg; // Patch for PCL2 - if (jvmArg?.Equals("-DFabricMcEmu= net.minecraft.client.main.Main ", StringComparison.OrdinalIgnoreCase) ?? false) + if (jvmArg?.Equals("-DFabricMcEmu= net.minecraft.client.main.Main ", + StringComparison.OrdinalIgnoreCase) ?? false) arg = "-DFabricMcEmu=net.minecraft.client.main.Main"; yield return StringHelper.ReplaceByDic(arg, jvmArgumentsDic); } - + yield break; } const string preset = """ - [ - { - "rules": [ - { - "action": "allow", - "os": { - "name": "osx" - } - } - ], - "value": [ - "-XstartOnFirstThread" - ] - }, - { - "rules": [ - { - "action": "allow", - "os": { - "name": "windows" - } - } - ], - "value": "-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump" - }, - { - "rules": [ - { - "action": "allow", - "os": { - "name": "windows", - "version": "^10\\\\." - } - } - ], - "value": [ - "-Dos.name=Windows 10", - "-Dos.version=10.0" - ] - }, - "-Djava.library.path=${natives_directory}", - "-Dminecraft.launcher.brand=${launcher_name}", - "-Dminecraft.launcher.version=${launcher_version}", - "-cp", - "${classpath}" - ] - """; - - var preJvmArguments = - VersionLocator.ParseJvmArguments(JsonSerializer.Deserialize(preset, - JsonElementContext.Default.JsonElementArray)!); + [ + { + "rules": [ + { + "action": "allow", + "os": { + "name": "osx" + } + } + ], + "value": [ + "-XstartOnFirstThread" + ] + }, + { + "rules": [ + { + "action": "allow", + "os": { + "name": "windows" + } + } + ], + "value": "-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump" + }, + { + "rules": [ + { + "action": "allow", + "os": { + "name": "windows", + "version": "^10\\\\." + } + } + ], + "value": [ + "-Dos.name=Windows 10", + "-Dos.version=10.0" + ] + }, + "-Djava.library.path=${natives_directory}", + "-Dminecraft.launcher.brand=${launcher_name}", + "-Dminecraft.launcher.version=${launcher_version}", + "-cp", + "${classpath}" + ] + """; + + var preJvmArguments = this.VersionLocator.ParseJvmArguments(JsonSerializer.Deserialize(preset, + JsonElementContext.Default.JsonElementArray)!); foreach (var preJvmArg in preJvmArguments) yield return StringHelper.ReplaceByDic(preJvmArg, jvmArgumentsDic); @@ -238,11 +240,11 @@ public IEnumerable ParseGameArguments(AuthResultBase authResult) authResult.SelectedProfile == null || string.IsNullOrEmpty(authResult.AccessToken)) throw new ArgumentNullException("无效的用户凭据,请检查登陆状态"); - - var gameDir = _launchSettings.VersionInsulation - ? Path.Combine(RootPath, GamePathHelper.GetGamePath(LaunchSettings.Version)) - : RootPath; - var clientIdUpper = (VersionLocator?.LauncherProfileParser?.LauncherProfile?.ClientToken ?? + + var gameDir = this._launchSettings.VersionInsulation + ? Path.Combine(this.RootPath, GamePathHelper.GetGamePath(this.LaunchSettings.Version)) + : this.RootPath; + var clientIdUpper = (this.VersionLocator?.LauncherProfileParser?.LauncherProfile?.ClientToken ?? Guid.Empty.ToString("D")) .Replace("-", string.Empty).ToUpper(); var clientIdBytes = Encoding.ASCII.GetBytes(clientIdUpper); @@ -259,10 +261,12 @@ public IEnumerable ParseGameArguments(AuthResultBase authResult) var mcArgumentsDic = new Dictionary { - { "${version_name}", $"\"{LaunchSettings.Version}\"" }, - { "${version_type}", $"\"{GameProfile?.Type ?? LaunchSettings.LauncherName}\"" }, - { "${assets_root}", $"\"{AssetRoot}\"" }, - { "${assets_index_name}", VersionInfo.AssetInfo?.Id ?? VersionInfo.Assets ?? VersionInfo.Id }, + { "${version_name}", $"\"{this.LaunchSettings.Version}\"" }, + { "${version_type}", $"\"{this.GameProfile?.Type ?? this.LaunchSettings.LauncherName}\"" }, + { "${assets_root}", $"\"{this.AssetRoot}\"" }, + { + "${assets_index_name}", this.VersionInfo.AssetInfo?.Id ?? this.VersionInfo.Assets ?? this.VersionInfo.Id + }, { "${game_directory}", $"\"{gameDir}\"" }, { "${auth_player_name}", authResult.SelectedProfile.Name }, { "${auth_uuid}", authResult.SelectedProfile.UUID.ToString() }, @@ -273,32 +277,32 @@ public IEnumerable ParseGameArguments(AuthResultBase authResult) { "${auth_xuid}", xuid } }; - foreach (var gameArg in VersionInfo.GameArguments) + foreach (var gameArg in this.VersionInfo.GameArguments) yield return StringHelper.ReplaceByDic(gameArg, mcArgumentsDic); } public List GenerateLaunchArguments() { - var javaPath = GameProfile?.JavaDir; + var javaPath = this.GameProfile?.JavaDir; if (string.IsNullOrEmpty(javaPath)) - javaPath = LaunchSettings.FallBackGameArguments?.JavaExecutable ?? - LaunchSettings.GameArguments.JavaExecutable; + javaPath = this.LaunchSettings.FallBackGameArguments?.JavaExecutable ?? + this.LaunchSettings.GameArguments.JavaExecutable; var arguments = new List { javaPath }; - arguments.AddRange(ParseJvmHeadArguments()); - arguments.AddRange(ParseJvmArguments()); + arguments.AddRange(this.ParseJvmHeadArguments()); + arguments.AddRange(this.ParseJvmArguments()); - if (EnableXmlLoggingOutput) - arguments.AddRange(ParseGameLoggingArguments()); + if (this.EnableXmlLoggingOutput) + arguments.AddRange(this.ParseGameLoggingArguments()); - arguments.Add(VersionInfo.MainClass); + arguments.Add(this.VersionInfo.MainClass); - arguments.AddRange(ParseGameArguments(AuthResult)); - arguments.AddRange(ParseAdditionalArguments()); + arguments.AddRange(this.ParseGameArguments(this.AuthResult)); + arguments.AddRange(this.ParseAdditionalArguments()); for (var i = 0; i < arguments.Count; i++) arguments[i] = arguments[i].Trim(); @@ -312,15 +316,15 @@ public List GenerateLaunchArguments() /// public IEnumerable ParseGameLoggingArguments() { - if (VersionInfo.Logging?.Client == null) yield break; - if (string.IsNullOrEmpty(VersionInfo.Logging.Client.File?.Url)) yield break; - if (string.IsNullOrEmpty(VersionInfo.Logging?.Client?.Argument)) yield break; + if (this.VersionInfo.Logging?.Client == null) yield break; + if (string.IsNullOrEmpty(this.VersionInfo.Logging.Client.File?.Url)) yield break; + if (string.IsNullOrEmpty(this.VersionInfo.Logging?.Client?.Argument)) yield break; - var fileName = Path.GetFileName(VersionInfo.Logging.Client.File?.Url); + var fileName = Path.GetFileName(this.VersionInfo.Logging.Client.File?.Url); if (string.IsNullOrEmpty(fileName)) yield break; - var filePath = Path.Combine(GamePathHelper.GetLoggingPath(RootPath), fileName); + var filePath = Path.Combine(GamePathHelper.GetLoggingPath(this.RootPath), fileName); if (!File.Exists(filePath)) yield break; @@ -329,7 +333,7 @@ public IEnumerable ParseGameLoggingArguments() { "${path}", $"\"{filePath}\"" } }; - yield return StringHelper.ReplaceByDic(VersionInfo.Logging.Client.Argument, argumentsDic); + yield return StringHelper.ReplaceByDic(this.VersionInfo.Logging.Client.Argument, argumentsDic); } /// @@ -338,39 +342,39 @@ public IEnumerable ParseGameLoggingArguments() /// public IEnumerable ParseAdditionalArguments() { - if ((VersionInfo.AvailableGameArguments?.Count ?? 0) == 0) yield break; - if (!VersionInfo.AvailableGameArguments!.ContainsKey("has_custom_resolution")) yield break; + if ((this.VersionInfo.AvailableGameArguments?.Count ?? 0) == 0) yield break; + if (!this.VersionInfo.AvailableGameArguments!.ContainsKey("has_custom_resolution")) yield break; - if (!(LaunchSettings.GameArguments.Resolution?.IsDefault() ?? true)) + if (!(this.LaunchSettings.GameArguments.Resolution?.IsDefault() ?? true)) { yield return "--width"; - yield return LaunchSettings.GameArguments.Resolution.Width.ToString(); + yield return this.LaunchSettings.GameArguments.Resolution.Width.ToString(); yield return "--height"; - yield return LaunchSettings.GameArguments.Resolution.Height.ToString(); + yield return this.LaunchSettings.GameArguments.Resolution.Height.ToString(); } - else if (!(LaunchSettings.FallBackGameArguments?.Resolution?.IsDefault() ?? true)) + else if (!(this.LaunchSettings.FallBackGameArguments?.Resolution?.IsDefault() ?? true)) { yield return "--width"; - yield return LaunchSettings.FallBackGameArguments.Resolution.Width.ToString(); + yield return this.LaunchSettings.FallBackGameArguments.Resolution.Width.ToString(); yield return "--height"; - yield return LaunchSettings.FallBackGameArguments.Resolution.Height.ToString(); + yield return this.LaunchSettings.FallBackGameArguments.Resolution.Height.ToString(); } - else if (!GameProfile!.Resolution!.IsDefault()) + else if (!this.GameProfile!.Resolution!.IsDefault()) { yield return "--width"; - yield return GameProfile.Resolution.Width.ToString(); + yield return this.GameProfile.Resolution.Width.ToString(); yield return "--height"; - yield return GameProfile.Resolution.Height.ToString(); + yield return this.GameProfile.Resolution.Height.ToString(); } - if (LaunchSettings.GameArguments.ServerSettings == null && - LaunchSettings.FallBackGameArguments?.ServerSettings == null) yield break; + if (this.LaunchSettings.GameArguments.ServerSettings == null && + this.LaunchSettings.FallBackGameArguments?.ServerSettings == null) yield break; - var serverSettings = LaunchSettings.GameArguments.ServerSettings ?? - LaunchSettings.FallBackGameArguments?.ServerSettings; + var serverSettings = this.LaunchSettings.GameArguments.ServerSettings ?? + this.LaunchSettings.FallBackGameArguments?.ServerSettings; if (serverSettings != null && !serverSettings.IsDefault()) { @@ -383,16 +387,9 @@ public IEnumerable ParseAdditionalArguments() yield return serverSettings.Port.ToString(); } - if (!string.IsNullOrEmpty(LaunchSettings.GameArguments.AdvanceArguments)) - { - yield return LaunchSettings.GameArguments.AdvanceArguments; - } - else if (!string.IsNullOrEmpty(LaunchSettings.FallBackGameArguments?.AdvanceArguments)) - { - yield return LaunchSettings.FallBackGameArguments.AdvanceArguments; - } + if (!string.IsNullOrEmpty(this.LaunchSettings.GameArguments.AdvanceArguments)) + yield return this.LaunchSettings.GameArguments.AdvanceArguments; + else if (!string.IsNullOrEmpty(this.LaunchSettings.FallBackGameArguments?.AdvanceArguments)) + yield return this.LaunchSettings.FallBackGameArguments.AdvanceArguments; } - - protected override string ClassPath { get; init; } - protected override VersionInfo VersionInfo { get; init; } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultLauncherAccountParser.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultLauncherAccountParser.cs index 0fc7b816..8267abcc 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultLauncherAccountParser.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultLauncherAccountParser.cs @@ -13,8 +13,8 @@ namespace ProjBobcat.DefaultComponent.Launch; public class DefaultLauncherAccountParser : LauncherParserBase, ILauncherAccountParser { - readonly object _lock = new(); readonly string _fullLauncherAccountPath; + readonly object _lock = new(); /// /// 构造函数 @@ -23,87 +23,67 @@ public class DefaultLauncherAccountParser : LauncherParserBase, ILauncherAccount /// public DefaultLauncherAccountParser(string rootPath, Guid clientToken) : base(rootPath) { - _fullLauncherAccountPath = Path.Combine(rootPath, GamePathHelper.GetLauncherAccountPath()); + this._fullLauncherAccountPath = Path.Combine(rootPath, GamePathHelper.GetLauncherAccountPath()); - if (!File.Exists(_fullLauncherAccountPath)) + if (!File.Exists(this._fullLauncherAccountPath)) { - LauncherAccount = GenerateLauncherAccountModel(clientToken); + this.LauncherAccount = this.GenerateLauncherAccountModel(clientToken); } else { var launcherProfileJson = - File.ReadAllText(_fullLauncherAccountPath, Encoding.UTF8); + File.ReadAllText(this._fullLauncherAccountPath, Encoding.UTF8); var val = JsonSerializer.Deserialize(launcherProfileJson, LauncherAccountModelContext.Default.LauncherAccountModel); if (val == null) { - LauncherAccount = GenerateLauncherAccountModel(clientToken); + this.LauncherAccount = this.GenerateLauncherAccountModel(clientToken); return; } - LauncherAccount = val; + this.LauncherAccount = val; } } - LauncherAccountModel GenerateLauncherAccountModel(Guid clientToken) - { - var launcherAccount = new LauncherAccountModel - { - Accounts = [], - MojangClientToken = clientToken.ToString("N") - }; - - var launcherProfileJson = - JsonSerializer.Serialize(launcherAccount, typeof(LauncherAccountModel), - new LauncherAccountModelContext(JsonHelper.CamelCasePropertyNamesSettings())); - - if (!Directory.Exists(RootPath)) - Directory.CreateDirectory(RootPath); - - File.WriteAllText(_fullLauncherAccountPath, launcherProfileJson); - - return launcherAccount; - } - public LauncherAccountModel LauncherAccount { get; set; } public bool ActivateAccount(string uuid) { - lock (_lock) + lock (this._lock) { - if (!(LauncherAccount?.Accounts?.ContainsKey(uuid) ?? false)) + if (!(this.LauncherAccount?.Accounts?.ContainsKey(uuid) ?? false)) return false; - LauncherAccount.ActiveAccountLocalId = uuid; - Save(); + this.LauncherAccount.ActiveAccountLocalId = uuid; + this.Save(); } - + return true; } public bool AddNewAccount(string uuid, AccountModel account, out Guid? id) { - if (LauncherAccount == null) + if (this.LauncherAccount == null) { id = null; return false; } - - LauncherAccount.Accounts ??= []; - lock (_lock) + this.LauncherAccount.Accounts ??= []; + + lock (this._lock) { - if (LauncherAccount.Accounts.ContainsKey(uuid)) + if (this.LauncherAccount.Accounts.ContainsKey(uuid)) { id = null; return false; } } - lock (_lock) + lock (this._lock) { - var oldRecord = LauncherAccount.Accounts + var oldRecord = this.LauncherAccount.Accounts .FirstOrDefault(a => a.Value.MinecraftProfile?.Id == account.MinecraftProfile?.Id).Value; if (oldRecord != null) { @@ -111,43 +91,43 @@ public bool AddNewAccount(string uuid, AccountModel account, out Guid? id) return true; } } - - lock (_lock) + + lock (this._lock) { var newId = Guid.NewGuid(); - var findResult = Find(account.Id); + var findResult = this.Find(account.Id); if (findResult is { Key: not null, Value: not null }) { newId = account.Id; - LauncherAccount.Accounts[findResult.Value.Key] = account; + this.LauncherAccount.Accounts[findResult.Value.Key] = account; } else { if (account.Id == default) account.Id = newId; - LauncherAccount.Accounts.Add(uuid, account); + this.LauncherAccount.Accounts.Add(uuid, account); } - Save(); + this.Save(); id = newId; } - + return true; } public KeyValuePair? Find(Guid id) { - lock (_lock) + lock (this._lock) { - return LauncherAccount?.Accounts?.FirstOrDefault(a => a.Value.Id == id); + return this.LauncherAccount?.Accounts?.FirstOrDefault(a => a.Value.Id == id); } } public bool RemoveAccount(Guid id) { - var result = Find(id); + var result = this.Find(id); if (!result.HasValue) return false; @@ -155,32 +135,50 @@ public bool RemoveAccount(Guid id) if (string.IsNullOrEmpty(key)) return false; - lock (_lock) + lock (this._lock) { - LauncherAccount?.Accounts?.Remove(key); - Save(); + this.LauncherAccount?.Accounts?.Remove(key); + this.Save(); } - + return true; } public void Save() { var launcherProfileJson = - JsonSerializer.Serialize(LauncherAccount, typeof(LauncherAccountModel), + JsonSerializer.Serialize(this.LauncherAccount, typeof(LauncherAccountModel), new LauncherAccountModelContext(JsonHelper.CamelCasePropertyNamesSettings())); for (var i = 0; i < 3; i++) - { try { - File.WriteAllText(_fullLauncherAccountPath, launcherProfileJson); + File.WriteAllText(this._fullLauncherAccountPath, launcherProfileJson); break; } catch (IOException) { if (i == 2) throw; } - } + } + + LauncherAccountModel GenerateLauncherAccountModel(Guid clientToken) + { + var launcherAccount = new LauncherAccountModel + { + Accounts = [], + MojangClientToken = clientToken.ToString("N") + }; + + var launcherProfileJson = + JsonSerializer.Serialize(launcherAccount, typeof(LauncherAccountModel), + new LauncherAccountModelContext(JsonHelper.CamelCasePropertyNamesSettings())); + + if (!Directory.Exists(this.RootPath)) + Directory.CreateDirectory(this.RootPath); + + File.WriteAllText(this._fullLauncherAccountPath, launcherProfileJson); + + return launcherAccount; } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultLauncherProfileParser.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultLauncherProfileParser.cs index 366aa938..380d606a 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultLauncherProfileParser.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultLauncherProfileParser.cs @@ -18,8 +18,8 @@ namespace ProjBobcat.DefaultComponent.Launch; /// public sealed class DefaultLauncherProfileParser : LauncherParserBase, ILauncherProfileParser { - readonly object _lock = new(); readonly string _fullLauncherProfilePath; + readonly object _lock = new(); /// /// 构造函数 @@ -28,57 +28,27 @@ public sealed class DefaultLauncherProfileParser : LauncherParserBase, ILauncher /// public DefaultLauncherProfileParser(string rootPath, Guid clientToken) : base(rootPath) { - _fullLauncherProfilePath = Path.Combine(rootPath, GamePathHelper.GetLauncherProfilePath()); - - if (!File.Exists(_fullLauncherProfilePath)) + this._fullLauncherProfilePath = Path.Combine(rootPath, GamePathHelper.GetLauncherProfilePath()); + + if (!File.Exists(this._fullLauncherProfilePath)) { - LauncherProfile = GenerateLauncherProfile(clientToken, rootPath); + this.LauncherProfile = this.GenerateLauncherProfile(clientToken, rootPath); } else { var launcherProfileJson = - File.ReadAllText(_fullLauncherProfilePath, Encoding.UTF8); + File.ReadAllText(this._fullLauncherProfilePath, Encoding.UTF8); var result = JsonSerializer.Deserialize(launcherProfileJson, LauncherProfileModelContext.Default.LauncherProfileModel); if (result == null) { - LauncherProfile = GenerateLauncherProfile(clientToken, rootPath); + this.LauncherProfile = this.GenerateLauncherProfile(clientToken, rootPath); return; } - - LauncherProfile = result; - } - } - - LauncherProfileModel GenerateLauncherProfile( - Guid clientToken, - string rootPath) - { - var launcherProfile = new LauncherProfileModel - { - ClientToken = clientToken.ToString("D"), - LauncherVersion = new LauncherVersionModel - { - Format = 1, - Name = string.Empty - }, - Profiles = [], - SelectedUser = new SelectedUserModel() - }; - LauncherProfile = launcherProfile; - - var launcherProfileJson = - JsonSerializer.Serialize(launcherProfile, typeof(LauncherProfileModel), - new LauncherProfileModelContext(JsonHelper.CamelCasePropertyNamesSettings())); - - if (!Directory.Exists(RootPath)) - Directory.CreateDirectory(rootPath); - - File.WriteAllText(_fullLauncherProfilePath, launcherProfileJson); - - return launcherProfile; + this.LauncherProfile = result; + } } public LauncherProfileModel LauncherProfile { get; set; } @@ -86,29 +56,29 @@ LauncherProfileModel GenerateLauncherProfile( public void AddNewGameProfile(GameProfileModel gameProfile) { if (string.IsNullOrEmpty(gameProfile.Name)) return; - if (IsGameProfileExist(gameProfile.Name)) return; + if (this.IsGameProfileExist(gameProfile.Name)) return; - lock (_lock) + lock (this._lock) { - LauncherProfile.Profiles!.Add(gameProfile.Name, gameProfile); - SaveProfile(); + this.LauncherProfile.Profiles!.Add(gameProfile.Name, gameProfile); + this.SaveProfile(); } } public void EmptyGameProfiles() { - lock (_lock) + lock (this._lock) { - LauncherProfile.Profiles?.Clear(); - SaveProfile(); + this.LauncherProfile.Profiles?.Clear(); + this.SaveProfile(); } } public GameProfileModel GetGameProfile(string name) { - lock (_lock) + lock (this._lock) { - var profile = LauncherProfile.Profiles!.FirstOrDefault( + var profile = this.LauncherProfile.Profiles!.FirstOrDefault( p => p.Value.Name?.Equals(name, StringComparison.Ordinal) ?? false).Value ?? throw new UnknownGameNameException(name); @@ -120,54 +90,82 @@ public GameProfileModel GetGameProfile(string name) public bool IsGameProfileExist(string name) { - lock (_lock) + lock (this._lock) { - return LauncherProfile.Profiles! + return this.LauncherProfile.Profiles! .Any(p => p.Value.Name?.Equals(name, StringComparison.Ordinal) ?? false); } } public void RemoveGameProfile(string name) { - lock (_lock) + lock (this._lock) { - LauncherProfile.Profiles!.Remove(name); + this.LauncherProfile.Profiles!.Remove(name); } } public void SaveProfile() { var launcherProfileJson = - JsonSerializer.Serialize(LauncherProfile, typeof(LauncherProfileModel), + JsonSerializer.Serialize(this.LauncherProfile, typeof(LauncherProfileModel), new LauncherProfileModelContext(JsonHelper.CamelCasePropertyNamesSettings())); for (var i = 0; i < 3; i++) - { try { - File.WriteAllText(_fullLauncherProfilePath, launcherProfileJson); + File.WriteAllText(this._fullLauncherProfilePath, launcherProfileJson); break; } catch (IOException) { if (i == 2) throw; } - } } public void SelectGameProfile(string name) { - if (!IsGameProfileExist(name)) throw new KeyNotFoundException(); + if (!this.IsGameProfileExist(name)) throw new KeyNotFoundException(); - LauncherProfile.SelectedUser ??= new SelectedUserModel(); - LauncherProfile.SelectedUser.Profile = name; - SaveProfile(); + this.LauncherProfile.SelectedUser ??= new SelectedUserModel(); + this.LauncherProfile.SelectedUser.Profile = name; + this.SaveProfile(); } public void SelectUser(PlayerUUID uuid) { - LauncherProfile.SelectedUser ??= new SelectedUserModel(); - LauncherProfile.SelectedUser.Account = uuid.ToString(); - SaveProfile(); + this.LauncherProfile.SelectedUser ??= new SelectedUserModel(); + this.LauncherProfile.SelectedUser.Account = uuid.ToString(); + this.SaveProfile(); + } + + LauncherProfileModel GenerateLauncherProfile( + Guid clientToken, + string rootPath) + { + var launcherProfile = new LauncherProfileModel + { + ClientToken = clientToken.ToString("D"), + LauncherVersion = new LauncherVersionModel + { + Format = 1, + Name = string.Empty + }, + Profiles = [], + SelectedUser = new SelectedUserModel() + }; + + this.LauncherProfile = launcherProfile; + + var launcherProfileJson = + JsonSerializer.Serialize(launcherProfile, typeof(LauncherProfileModel), + new LauncherProfileModelContext(JsonHelper.CamelCasePropertyNamesSettings())); + + if (!Directory.Exists(this.RootPath)) + Directory.CreateDirectory(rootPath); + + File.WriteAllText(this._fullLauncherProfilePath, launcherProfileJson); + + return launcherProfile; } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultVersionLocator.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultVersionLocator.cs index 82a6f282..2ad1d4a7 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultVersionLocator.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultVersionLocator.cs @@ -21,8 +21,6 @@ namespace ProjBobcat.DefaultComponent.Launch; /// public sealed class DefaultVersionLocator : VersionLocatorBase { - public NativeReplacementPolicy NativeReplacementPolicy { get; init; } = NativeReplacementPolicy.LegacyOnly; - readonly object _lock = new(); /// @@ -33,7 +31,7 @@ public sealed class DefaultVersionLocator : VersionLocatorBase /// public DefaultVersionLocator(string rootPath, Guid clientToken) : base(rootPath) { - LauncherProfileParser ??= new DefaultLauncherProfileParser(rootPath, clientToken); + this.LauncherProfileParser ??= new DefaultLauncherProfileParser(rootPath, clientToken); //防止给定路径不存在的时候Parser遍历文件夹爆炸。 //Prevents errors in the parser's folder traversal when the given path does not exist. @@ -41,15 +39,17 @@ public DefaultVersionLocator(string rootPath, Guid clientToken) : base(rootPath) Directory.CreateDirectory(GamePathHelper.GetVersionPath(rootPath)); } + public NativeReplacementPolicy NativeReplacementPolicy { get; init; } = NativeReplacementPolicy.LegacyOnly; + public override IEnumerable GetAllGames() { // 把每个DirectoryInfo类映射到VersionInfo类。 // Map each DirectoryInfo dir to VersionInfo class. - var di = new DirectoryInfo(GamePathHelper.GetVersionPath(RootPath)); + var di = new DirectoryInfo(GamePathHelper.GetVersionPath(this.RootPath)); foreach (var dir in di.EnumerateDirectories()) { - var version = ToVersion(dir.Name); + var version = this.ToVersion(dir.Name); if (version == null) continue; yield return version; } @@ -57,7 +57,7 @@ public override IEnumerable GetAllGames() public override VersionInfo? GetGame(string id) { - var version = ToVersion(id); + var version = this.ToVersion(id); return version; } @@ -283,7 +283,7 @@ public override (List, List) GetNatives(Library[] libr { if (string.IsNullOrEmpty(lib.Downloads.Artifact.Name)) lib.Downloads.Artifact.Name = lib.Name; - + if (!result.Item2.Any(l => l.Name!.Equals(lib.Name, StringComparison.OrdinalIgnoreCase))) result.Item2.Add(lib.Downloads.Artifact); } @@ -309,31 +309,31 @@ public override (List, List) GetNatives(Library[] libr /// public override RawVersionModel? ParseRawVersion(string id) { - var gamePath = Path.Combine(RootPath, GamePathHelper.GetGamePath(id)); + var gamePath = Path.Combine(this.RootPath, GamePathHelper.GetGamePath(id)); var possibleFiles = new List { - GamePathHelper.GetGameJsonPath(RootPath, id) + GamePathHelper.GetGameJsonPath(this.RootPath, id) }; - + // 预防 I/O 的错误。 // Prevents errors related to I/O. if (!Directory.Exists(gamePath)) return null; - if (!File.Exists(GamePathHelper.GetGameJsonPath(RootPath, id))) + if (!File.Exists(GamePathHelper.GetGameJsonPath(this.RootPath, id))) { var files = Directory .EnumerateFiles(gamePath, "*.json", SearchOption.TopDirectoryOnly) .ToArray(); if (files.Length == 0) return null; - + possibleFiles.AddRange(files); } foreach (var possibleFile in possibleFiles) { if (!File.Exists(possibleFile)) continue; - + try { using var fs = File.OpenRead(possibleFile); @@ -358,7 +358,6 @@ public override (List, List) GetNatives(Library[] libr } catch (JsonException) { - continue; } } @@ -375,7 +374,7 @@ public override (List, List) GetNatives(Library[] libr { // 反序列化。 // Deserialize. - var rawVersion = ParseRawVersion(id); + var rawVersion = this.ParseRawVersion(id); if (rawVersion == null) return null; @@ -399,7 +398,7 @@ public override (List, List) GetNatives(Library[] libr { inherits.Add(current); first = false; - current = ParseRawVersion(current.InheritsFrom); + current = this.ParseRawVersion(current.InheritsFrom); if (current == null) return null; @@ -407,12 +406,12 @@ public override (List, List) GetNatives(Library[] libr continue; } - var inheritVersion = ParseRawVersion(current.InheritsFrom); + var inheritVersion = this.ParseRawVersion(current.InheritsFrom); if (inheritVersion == null) return null; inherits.Add(inheritVersion); - current = ParseRawVersion(current.InheritsFrom); + current = this.ParseRawVersion(current.InheritsFrom); } } @@ -458,17 +457,17 @@ public override (List, List) GetNatives(Library[] libr if (flag) { var inheritsLibs = inherits[i]!.Libraries.ToList(); - inheritsLibs = NativeReplaceHelper.Replace([rawVersion, ..inherits!], inheritsLibs, NativeReplacementPolicy); - - var rootLibs = GetNatives([.. inheritsLibs]); + inheritsLibs = NativeReplaceHelper.Replace([rawVersion, ..inherits!], inheritsLibs, + this.NativeReplacementPolicy); + + var rootLibs = this.GetNatives([.. inheritsLibs]); result.Libraries = rootLibs.Item2; result.Natives = rootLibs.Item1; - jvmArgList.AddRange(ParseJvmArguments(inherits[i]!.Arguments?.Jvm)); + jvmArgList.AddRange(this.ParseJvmArguments(inherits[i]!.Arguments?.Jvm)); - var rootArgs = - ParseGameArguments( - (inherits[i]!.MinecraftArguments, inherits[i]!.Arguments?.Game)); + var rootArgs = this.ParseGameArguments( + (inherits[i]!.MinecraftArguments, inherits[i]!.Arguments?.Game)); gameArgList.AddRange(rootArgs.Item1); result.AvailableGameArguments = rootArgs.Item2; @@ -477,7 +476,7 @@ public override (List, List) GetNatives(Library[] libr continue; } - var middleLibs = GetNatives(inherits[i]!.Libraries); + var middleLibs = this.GetNatives(inherits[i]!.Libraries); // result.Libraries.AddRange(middleLibs.Item2); @@ -495,7 +494,8 @@ public override (List, List) GetNatives(Library[] libr var lMaven = result.Libraries[j].Name!.ResolveMavenString()!; - if (!lMaven.GetMavenFullName().Equals(mLMaven.GetMavenFullName(), StringComparison.OrdinalIgnoreCase)) + if (!lMaven.GetMavenFullName() + .Equals(mLMaven.GetMavenFullName(), StringComparison.OrdinalIgnoreCase)) continue; var v1 = new ComparableVersion(lMaven.Version); @@ -523,8 +523,8 @@ public override (List, List) GetNatives(Library[] libr .ToList(); result.Natives.AddRange(moreMiddleNatives); - var jvmArgs = ParseJvmArguments(inherits[i]!.Arguments?.Jvm); - var middleGameArgs = ParseGameArguments( + var jvmArgs = this.ParseJvmArguments(inherits[i]!.Arguments?.Jvm); + var middleGameArgs = this.ParseGameArguments( (inherits[i]!.MinecraftArguments, inherits[i]!.Arguments?.Game)); if (string.IsNullOrEmpty(inherits[i]!.MinecraftArguments)) @@ -559,7 +559,7 @@ public override (List, List) GetNatives(Library[] libr .SelectMany(a => a) .ToFrozenSet(); - ProcessProfile(result, id); + this.ProcessProfile(result, id); return result; } @@ -579,7 +579,8 @@ public override (List, List) GetNatives(Library[] libr var filteredLibs = duplicateLibs .Select(p => p.Value - .Where(lib => !lib.IsNewNativeLib() && (lib.Natives?.Count ?? 0) == 0 && lib.Downloads?.Classifiers == null) + .Where(lib => + !lib.IsNewNativeLib() && (lib.Natives?.Count ?? 0) == 0 && lib.Downloads?.Classifiers == null) .Where(lib => lib.Rules?.CheckAllow() ?? true) .ToList()) .Where(libs => libs.Count > 1); @@ -590,47 +591,43 @@ public override (List, List) GetNatives(Library[] libr .OrderByDescending(l => new ComparableVersion(l.Name.ResolveMavenString()?.Version ?? "0")) .ToList(); - for (var i = 1; i < sortedDuplicates.Count; i++) - { - rawLibs.Remove(sortedDuplicates[i]); - } + for (var i = 1; i < sortedDuplicates.Count; i++) rawLibs.Remove(sortedDuplicates[i]); } - rawLibs = NativeReplaceHelper.Replace([rawVersion, .. inherits ?? []], rawLibs, NativeReplacementPolicy); + rawLibs = NativeReplaceHelper.Replace([rawVersion, .. inherits ?? []], rawLibs, this.NativeReplacementPolicy); - var libs = GetNatives([.. rawLibs]); + var libs = this.GetNatives([.. rawLibs]); result.Libraries = libs.Item2; result.Natives = libs.Item1; - result.JvmArguments = ParseJvmArguments(rawVersion.Arguments?.Jvm).ToList(); + result.JvmArguments = this.ParseJvmArguments(rawVersion.Arguments?.Jvm).ToList(); - var gameArgs = - ParseGameArguments((rawVersion.MinecraftArguments, - rawVersion.Arguments?.Game)); + var gameArgs = this.ParseGameArguments((rawVersion.MinecraftArguments, + rawVersion.Arguments?.Game)); result.GameArguments = gameArgs.Item1; result.AvailableGameArguments = gameArgs.Item2; - ProcessProfile(result, id); + this.ProcessProfile(result, id); return result; } void ProcessProfile(VersionInfo result, string id) { - if (LauncherProfileParser == null) return; + if (this.LauncherProfileParser == null) return; var gameId = id.ToGuidHash().ToString("N"); - var gamePath = Path.Combine(RootPath, GamePathHelper.GetGamePath(id)); + var gamePath = Path.Combine(this.RootPath, GamePathHelper.GetGamePath(id)); - lock (_lock) + lock (this._lock) { - if (LauncherProfileParser.LauncherProfile.Profiles!.TryGetValue(gameId, out var oldProfileModel)) + if (this.LauncherProfileParser.LauncherProfile.Profiles!.TryGetValue(gameId, out var oldProfileModel)) { result.Name = oldProfileModel.Name!; oldProfileModel.GameDir = gamePath; oldProfileModel.LastVersionId = id; - LauncherProfileParser.LauncherProfile.Profiles![gameId] = oldProfileModel; - LauncherProfileParser.SaveProfile(); + this.LauncherProfileParser.LauncherProfile.Profiles![gameId] = oldProfileModel; + this.LauncherProfileParser.SaveProfile(); return; } @@ -643,8 +640,8 @@ void ProcessProfile(VersionInfo result, string id) Created = DateTime.Now }; - LauncherProfileParser.LauncherProfile.Profiles!.Add(gameId, gameProfile); - LauncherProfileParser.SaveProfile(); + this.LauncherProfileParser.LauncherProfile.Profiles!.Add(gameId, gameProfile); + this.LauncherProfileParser.SaveProfile(); } } -} +} \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Launch/GameCore/DefaultGameCore.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Launch/GameCore/DefaultGameCore.cs index 4cc7dc13..9034a0c2 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Launch/GameCore/DefaultGameCore.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Launch/GameCore/DefaultGameCore.cs @@ -2,8 +2,9 @@ using System.Diagnostics; using System.IO; using System.Linq; -using System.Text; using System.Threading.Tasks; +using Windows.Win32; +using Windows.Win32.Foundation; using Microsoft.Extensions.Configuration; using ProjBobcat.Class; using ProjBobcat.Class.Helper; @@ -38,13 +39,13 @@ public IArgumentParser LaunchArgumentParser /// public override required string RootPath { - get => _rootPath; + get => this._rootPath; init { if (string.IsNullOrEmpty(value)) - throw new ArgumentNullException(nameof(RootPath)); + throw new ArgumentNullException(nameof(this.RootPath)); - _rootPath = Path.GetFullPath(value.TrimEnd('/')); + this._rootPath = Path.GetFullPath(value.TrimEnd('/')); } } @@ -52,8 +53,8 @@ public override required string RootPath public override async Task LaunchTaskAsync(LaunchSettings settings) { - if (VersionLocator.LauncherProfileParser == null) - throw new ArgumentNullException(nameof(VersionLocator.LauncherProfileParser)); + if (this.VersionLocator.LauncherProfileParser == null) + throw new ArgumentNullException(nameof(this.VersionLocator.LauncherProfileParser)); try { @@ -65,11 +66,11 @@ public override async Task LaunchTaskAsync(LaunchSettings settings #region 解析游戏 Game Info Resolver - var version = VersionLocator.GetGame(settings.Version); + var version = this.VersionLocator.GetGame(settings.Version); //在以下方法中,我们存储前一个步骤的时间并且重置秒表,以此逐步测量启动时间。 //In the method InvokeLaunchLogThenStart(args), we storage the time span of the previous process and restart the watch in order that the time used in each step is recorded. - InvokeLaunchLogThenStart("解析游戏", ref prevSpan, ref stopwatch); + this.InvokeLaunchLogThenStart("解析游戏", ref prevSpan, ref stopwatch); //错误处理 //Error processor @@ -89,7 +90,7 @@ public override async Task LaunchTaskAsync(LaunchSettings settings #region 验证账户凭据 Legal Account Verifier - InvokeLaunchLogThenStart("正在验证账户凭据", ref prevSpan, ref stopwatch); + this.InvokeLaunchLogThenStart("正在验证账户凭据", ref prevSpan, ref stopwatch); //以下代码实现了账户模式从离线到在线的切换。 //The following code switches account mode between offline and yggdrasil. @@ -101,7 +102,7 @@ public override async Task LaunchTaskAsync(LaunchSettings settings _ => null }; - InvokeLaunchLogThenStart("账户凭据验证完成", ref prevSpan, ref stopwatch); + this.InvokeLaunchLogThenStart("账户凭据验证完成", ref prevSpan, ref stopwatch); //错误处理 //Error processor @@ -168,20 +169,17 @@ public override async Task LaunchTaskAsync(LaunchSettings settings }; var argumentParser = new DefaultLaunchArgumentParser( - settings, - VersionLocator.LauncherProfileParser, - VersionLocator, - authResult, - RootPath, + settings, this.VersionLocator.LauncherProfileParser, this.VersionLocator, + authResult, this.RootPath, version.RootVersion) { - EnableXmlLoggingOutput = EnableXmlLoggingOutput + EnableXmlLoggingOutput = this.EnableXmlLoggingOutput }; //以字符串数组形式生成启动参数。 //Generates launch cmd arguments in string[]. var arguments = argumentParser.GenerateLaunchArguments(); - InvokeLaunchLogThenStart("解析启动参数", ref prevSpan, ref stopwatch); + this.InvokeLaunchLogThenStart("解析启动参数", ref prevSpan, ref stopwatch); if (string.IsNullOrEmpty(arguments.First())) return new LaunchResult @@ -203,7 +201,7 @@ public override async Task LaunchTaskAsync(LaunchSettings settings //通过String Builder格式化参数。(转化成字符串) //Format the arguments using string builder.(Convert to string) // arguments.ForEach(arg => sb.Append(arg.Trim()).Append(' ')); - InvokeLaunchLogThenStart(string.Join(Environment.NewLine, arguments), ref prevSpan, ref stopwatch); + this.InvokeLaunchLogThenStart(string.Join(Environment.NewLine, arguments), ref prevSpan, ref stopwatch); #endregion @@ -211,7 +209,7 @@ public override async Task LaunchTaskAsync(LaunchSettings settings try { - var nativeRootPath = Path.Combine(RootPath, argumentParser.NativeRoot); + var nativeRootPath = Path.Combine(this.RootPath, argumentParser.NativeRoot); if (!Directory.Exists(nativeRootPath)) Directory.CreateDirectory(nativeRootPath); @@ -220,7 +218,7 @@ public override async Task LaunchTaskAsync(LaunchSettings settings foreach (var n in version.Natives) { var path = - Path.Combine(RootPath, GamePathHelper.GetLibraryPath(n.FileInfo.Path!)); + Path.Combine(this.RootPath, GamePathHelper.GetLibraryPath(n.FileInfo.Path!)); if (!File.Exists(path)) continue; @@ -242,7 +240,7 @@ public override async Task LaunchTaskAsync(LaunchSettings settings continue; } - InvokeLaunchLogThenStart($"[解压 Natives] - {entry.Key}", ref prevSpan, ref stopwatch); + this.InvokeLaunchLogThenStart($"[解压 Natives] - {entry.Key}", ref prevSpan, ref stopwatch); var fi = new FileInfo(extractPath); var di = fi.Directory ?? new DirectoryInfo(Path.GetDirectoryName(extractPath)!); @@ -274,8 +272,8 @@ public override async Task LaunchTaskAsync(LaunchSettings settings #region 启动游戏 Launch var rootPath = settings.VersionInsulation - ? Path.Combine(RootPath, GamePathHelper.GetGamePath(settings.Version)) - : RootPath; + ? Path.Combine(this.RootPath, GamePathHelper.GetGamePath(settings.Version)) + : this.RootPath; var psi = new ProcessStartInfo(executable, string.Join(' ', arguments)) { @@ -309,7 +307,7 @@ await Task.Run(() => } }); - InvokeLaunchLogThenStart("设置 log4j 缓解措施", ref prevSpan, ref stopwatch); + this.InvokeLaunchLogThenStart("设置 log4j 缓解措施", ref prevSpan, ref stopwatch); #endregion @@ -322,11 +320,11 @@ await Task.Run(() => }; launchWrapper.Do(); - InvokeLaunchLogThenStart("启动游戏", ref prevSpan, ref stopwatch); + this.InvokeLaunchLogThenStart("启动游戏", ref prevSpan, ref stopwatch); if (launchWrapper.Process == null) { - OnGameExit(launchWrapper, new GameExitEventArgs + this.OnGameExit(launchWrapper, new GameExitEventArgs { Exception = null, ExitCode = -1 @@ -345,7 +343,7 @@ await Task.Run(() => Task.Run(launchWrapper.Process.WaitForExit) .ContinueWith(task => { - OnGameExit(launchWrapper, new GameExitEventArgs + this.OnGameExit(launchWrapper, new GameExitEventArgs { Exception = task.Exception, ExitCode = launchWrapper.ExitCode == 0 @@ -360,12 +358,11 @@ await Task.Run(() => do { if (launchWrapper.Process == null) break; - + if (OperatingSystem.IsWindows() && OperatingSystem.IsWindowsVersionAtLeast(5)) - _ = Windows.Win32.PInvoke.SetWindowText( - new Windows.Win32.Foundation.HWND(launchWrapper.Process.MainWindowHandle), + _ = PInvoke.SetWindowText( + new HWND(launchWrapper.Process.MainWindowHandle), settings.WindowTitle); - } while (string.IsNullOrEmpty(launchWrapper.Process?.MainWindowTitle)); }); #pragma warning restore CS4014 // 由于此调用不会等待,因此在调用完成前将继续执行当前方法 @@ -405,7 +402,7 @@ await Task.Run(() => /// void InvokeLaunchLogThenStart(string item, ref TimeSpan time, ref Stopwatch sw) { - OnLogLaunchData(this, new LaunchLogEventArgs + this.OnLogLaunchData(this, new LaunchLogEventArgs { Item = item, ItemRunTime = sw.Elapsed - time diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Launch/GameCore/GameCoreBase.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Launch/GameCore/GameCoreBase.cs index 41895987..f72c8483 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Launch/GameCore/GameCoreBase.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Launch/GameCore/GameCoreBase.cs @@ -23,20 +23,20 @@ public abstract class GameCoreBase : IGameCore public event EventHandler GameExitEventDelegate { - add => ListEventDelegates.AddHandler(GameExitEventKey, value); - remove => ListEventDelegates.RemoveHandler(GameExitEventKey, value); + add => this.ListEventDelegates.AddHandler(GameExitEventKey, value); + remove => this.ListEventDelegates.RemoveHandler(GameExitEventKey, value); } public event EventHandler GameLogEventDelegate { - add => ListEventDelegates.AddHandler(GameLogEventKey, value); - remove => ListEventDelegates.RemoveHandler(GameLogEventKey, value); + add => this.ListEventDelegates.AddHandler(GameLogEventKey, value); + remove => this.ListEventDelegates.RemoveHandler(GameLogEventKey, value); } public event EventHandler LaunchLogEventDelegate { - add => ListEventDelegates.AddHandler(LaunchLogEventKey, value); - remove => ListEventDelegates.RemoveHandler(LaunchLogEventKey, value); + add => this.ListEventDelegates.AddHandler(LaunchLogEventKey, value); + remove => this.ListEventDelegates.RemoveHandler(LaunchLogEventKey, value); } /// @@ -46,7 +46,7 @@ public event EventHandler LaunchLogEventDelegate /// public virtual LaunchResult Launch(LaunchSettings settings) { - return LaunchTaskAsync(settings).GetAwaiter().GetResult(); + return this.LaunchTaskAsync(settings).GetAwaiter().GetResult(); } /// @@ -66,40 +66,40 @@ public virtual LaunchResult Launch(LaunchSettings settings) public void Dispose() { // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 - Dispose(true); + this.Dispose(true); GC.SuppressFinalize(this); } public virtual void OnGameExit(object sender, GameExitEventArgs e) { - var eventList = ListEventDelegates; + var eventList = this.ListEventDelegates; var @event = (EventHandler)eventList[GameExitEventKey]!; @event?.Invoke(sender, e); } public virtual void OnLogGameData(object sender, GameLogEventArgs e) { - var eventList = ListEventDelegates; + var eventList = this.ListEventDelegates; var @event = (EventHandler)eventList[GameLogEventKey]!; @event?.Invoke(sender, e); } public virtual void OnLogLaunchData(object sender, LaunchLogEventArgs e) { - var eventList = ListEventDelegates; + var eventList = this.ListEventDelegates; var @event = (EventHandler)eventList[LaunchLogEventKey]!; @event?.Invoke(sender, e); } protected virtual void Dispose(bool disposing) { - if (!_disposedValue) + if (!this._disposedValue) { - if (disposing) ListEventDelegates.Dispose(); + if (disposing) this.ListEventDelegates.Dispose(); // TODO: 释放未托管的资源(未托管的对象)并重写终结器 // TODO: 将大型字段设置为 null - _disposedValue = true; + this._disposedValue = true; } } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/LogAnalysis/AnalysisReport/AnalysisReport.cs b/ProjBobcat/ProjBobcat/DefaultComponent/LogAnalysis/AnalysisReport/AnalysisReport.cs index e95561fa..50f77548 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/LogAnalysis/AnalysisReport/AnalysisReport.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/LogAnalysis/AnalysisReport/AnalysisReport.cs @@ -7,5 +7,5 @@ public record AnalysisReport(CrashCauses Cause) : IAnalysisReport { public string? From { get; set; } public IReadOnlyCollection? Details { get; set; } - public bool HasDetails => Details is { Count: > 0 }; + public bool HasDetails => this.Details is { Count: > 0 }; } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/LogAnalysis/DefaultLogAnalyzer.cs b/ProjBobcat/ProjBobcat/DefaultComponent/LogAnalysis/DefaultLogAnalyzer.cs index ee7957da..f2f8589e 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/LogAnalysis/DefaultLogAnalyzer.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/LogAnalysis/DefaultLogAnalyzer.cs @@ -11,27 +11,6 @@ namespace ProjBobcat.DefaultComponent.LogAnalysis; public partial class DefaultLogAnalyzer : ILogAnalyzer { - [GeneratedRegex(@"(?<=\]: Warnings were found! ?[\n]+)[\w\W]+?(?=[\n]+\[)")] - private static partial Regex WarningsMatch(); - - [GeneratedRegex("(?<=Failed to create mod instance. ModID: )[^,]+")] - private static partial Regex ModInstanceMatch1(); - - [GeneratedRegex(@"(?<=Failed to create mod instance. ModId )[^\n]+(?= for )")] - private static partial Regex ModInstanceMatch2(); - - [GeneratedRegex(@"(?<=\tBlock: Block\{)[^\}]+")] - private static partial Regex BlockMatch(); - - [GeneratedRegex(@"(?<=\tBlock location: World: )\([^\)]+\)")] - private static partial Regex BlockLocationMatch(); - - [GeneratedRegex(@"(?<=\tEntity Type: )[^\n]+(?= \()")] - private static partial Regex EntityMatch(); - - [GeneratedRegex(@"(?<=\tEntity's Exact location: )[^\n]+")] - private static partial Regex EntityLocationMatch(); - /// /// 日志文件最后写入时间限制(分钟) /// @@ -44,14 +23,14 @@ public partial class DefaultLogAnalyzer : ILogAnalyzer public IEnumerable GenerateReport() { - if (string.IsNullOrEmpty(RootPath)) + if (string.IsNullOrEmpty(this.RootPath)) throw new NullReferenceException("未提供 RootPath 参数"); - if (string.IsNullOrEmpty(GameId)) + if (string.IsNullOrEmpty(this.GameId)) throw new NullReferenceException("未提供 GameId 参数"); var logs = new Dictionary>(); - foreach (var (logFileType, lines) in GetAllLogs()) + foreach (var (logFileType, lines) in this.GetAllLogs()) { if (!logs.ContainsKey(logFileType)) logs[logFileType] = []; @@ -78,6 +57,27 @@ public IEnumerable GenerateReport() yield return report; } + [GeneratedRegex(@"(?<=\]: Warnings were found! ?[\n]+)[\w\W]+?(?=[\n]+\[)")] + private static partial Regex WarningsMatch(); + + [GeneratedRegex("(?<=Failed to create mod instance. ModID: )[^,]+")] + private static partial Regex ModInstanceMatch1(); + + [GeneratedRegex(@"(?<=Failed to create mod instance. ModId )[^\n]+(?= for )")] + private static partial Regex ModInstanceMatch2(); + + [GeneratedRegex(@"(?<=\tBlock: Block\{)[^\}]+")] + private static partial Regex BlockMatch(); + + [GeneratedRegex(@"(?<=\tBlock location: World: )\([^\)]+\)")] + private static partial Regex BlockLocationMatch(); + + [GeneratedRegex(@"(?<=\tEntity Type: )[^\n]+(?= \()")] + private static partial Regex EntityMatch(); + + [GeneratedRegex(@"(?<=\tEntity's Exact location: )[^\n]+")] + private static partial Regex EntityLocationMatch(); + static LogFileType GetLogFileType(FileSystemInfo fi) { var fileName = Path.GetFileName(fi.FullName); @@ -104,32 +104,35 @@ static bool IsValidLogFile(FileInfo fi, double minutesAgo = 3) IEnumerable<(LogFileType, (string, string))> GetAllLogs() { - if (string.IsNullOrEmpty(RootPath)) + if (string.IsNullOrEmpty(this.RootPath)) throw new NullReferenceException("未提供 RootPath 参数"); - if (string.IsNullOrEmpty(GameId)) + if (string.IsNullOrEmpty(this.GameId)) throw new NullReferenceException("未提供 GameId 参数"); var logFiles = new List(); - var fullRootPath = Path.GetFullPath(RootPath); - var versionPath = Path.Combine(fullRootPath, GamePathHelper.GetGamePath(GameId)); + var fullRootPath = Path.GetFullPath(this.RootPath); + var versionPath = Path.Combine(fullRootPath, GamePathHelper.GetGamePath(this.GameId)); - var crashReportDi = new DirectoryInfo(Path.Combine(VersionIsolation ? versionPath : RootPath, "crash-reports")); + var crashReportDi = + new DirectoryInfo(Path.Combine(this.VersionIsolation ? versionPath : this.RootPath, "crash-reports")); if (crashReportDi.Exists) logFiles.AddRange(crashReportDi.GetFiles().Where(fi => fi.Extension is ".log" or ".txt")); - var versionDi = new DirectoryInfo(VersionIsolation ? RootPath : versionPath); + var versionDi = new DirectoryInfo(this.VersionIsolation ? this.RootPath : versionPath); if (versionDi.Exists) logFiles.AddRange(versionDi.GetFiles().Where(fi => fi.Extension == ".log")); - logFiles.Add(new FileInfo(Path.Combine(VersionIsolation ? versionPath : RootPath, "logs", "latest.log"))); - logFiles.Add(new FileInfo(Path.Combine(VersionIsolation ? versionPath : RootPath, "logs", "debug.log"))); + logFiles.Add(new FileInfo(Path.Combine(this.VersionIsolation ? versionPath : this.RootPath, "logs", + "latest.log"))); + logFiles.Add( + new FileInfo(Path.Combine(this.VersionIsolation ? versionPath : this.RootPath, "logs", "debug.log"))); - if (CustomLogFiles is { Count: > 0 }) - logFiles.AddRange(CustomLogFiles.Select(custom => new FileInfo(custom))); + if (this.CustomLogFiles is { Count: > 0 }) + logFiles.AddRange(this.CustomLogFiles.Select(custom => new FileInfo(custom))); foreach (var log in logFiles) { - if (!IsValidLogFile(log, LogFileLastWriteTimeLimit)) continue; + if (!IsValidLogFile(log, this.LogFileLastWriteTimeLimit)) continue; var logType = GetLogFileType(log); @@ -284,40 +287,40 @@ static IEnumerable AnalysisLogs2(IReadOnlyDictionary AnalysisLogs2(IReadOnlyDictionary AnalysisLogs2(IReadOnlyDictionary GameLogType.Fatal, @@ -66,7 +45,7 @@ public string ResolveStackTrace(string log) public string ResolveExceptionMsg(string log) { var exceptionMsg = ExceptionRegex().Match(log).Value; - + return exceptionMsg; } @@ -75,7 +54,7 @@ public string ResolveSource(string log) var content = SourceAndTypeRegex().Match(log).Value.Split('/').FirstOrDefault(); if (string.IsNullOrEmpty(content)) return string.Empty; - + var date = TimeFullRegex().Match(log).Value; var result = content.Replace($"{date} [", string.Empty); @@ -91,4 +70,25 @@ public string ResolveTotalPrefix(string log) { return TotalPrefixRegex().Match(log).Value; } + + [GeneratedRegex(LogSourceAndTypeRegex)] + private static partial Regex SourceAndTypeRegex(); + + [GeneratedRegex(LogTotalPrefixRegex)] + private static partial Regex TotalPrefixRegex(); + + [GeneratedRegex(LogTypeRegexStr)] + private static partial Regex TypeRegex(); + + [GeneratedRegex(LogTimeRegexStr)] + private static partial Regex TimeRegex(); + + [GeneratedRegex(LogDateRegex)] + private static partial Regex TimeFullRegex(); + + [GeneratedRegex(StackTraceAtStr)] + private static partial Regex StackTraceAtRegex(); + + [GeneratedRegex(ExceptionRegexStr)] + private static partial Regex ExceptionRegex(); } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/AssetInfoResolver.cs b/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/AssetInfoResolver.cs index e4174f99..ac0020f9 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/AssetInfoResolver.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/AssetInfoResolver.cs @@ -8,6 +8,7 @@ using System.Threading; using ProjBobcat.Class.Helper; using ProjBobcat.Class.Model; +using ProjBobcat.Class.Model.Downloading; using ProjBobcat.Class.Model.GameResource; using ProjBobcat.Class.Model.Mojang; using ProjBobcat.Interface; @@ -21,8 +22,8 @@ public sealed class AssetInfoResolver : ResolverBase public string? AssetIndexUriRoot { - get => _assetIndexUrlRoot; - init => _assetIndexUrlRoot = value?.TrimEnd('/'); + get => this._assetIndexUrlRoot; + init => this._assetIndexUrlRoot = value?.TrimEnd('/'); } public string AssetUriRoot { get; init; } = "https://resources.download.minecraft.net/"; @@ -31,16 +32,16 @@ public string? AssetIndexUriRoot public override async IAsyncEnumerable ResolveResourceAsync() { - if (!CheckLocalFiles) yield break; + if (!this.CheckLocalFiles) yield break; - OnResolve("开始进行游戏资源(Asset)检查"); + this.OnResolve("开始进行游戏资源(Asset)检查"); - if (VersionInfo?.AssetInfo == null) yield break; + if (this.VersionInfo?.AssetInfo == null) yield break; - var versions = Versions; - if ((Versions?.Count ?? 0) == 0) + var versions = this.Versions; + if ((this.Versions?.Count ?? 0) == 0) { - OnResolve("没有提供 Version Manifest, 开始下载"); + this.OnResolve("没有提供 Version Manifest, 开始下载"); using var vmJsonRes = await HttpHelper.Get(DefaultVersionManifestUrl); var vm = await vmJsonRes.Content.ReadFromJsonAsync(VersionManifestContext.Default.VersionManifest); @@ -51,27 +52,27 @@ public override async IAsyncEnumerable ResolveResourceAsync() if ((versions?.Count ?? 0) == 0) yield break; var isAssetInfoNotExists = - string.IsNullOrEmpty(VersionInfo.AssetInfo?.Url) && - string.IsNullOrEmpty(VersionInfo.AssetInfo?.Id); + string.IsNullOrEmpty(this.VersionInfo.AssetInfo?.Url) && + string.IsNullOrEmpty(this.VersionInfo.AssetInfo?.Id); if (isAssetInfoNotExists && - string.IsNullOrEmpty(VersionInfo.Assets)) + string.IsNullOrEmpty(this.VersionInfo.Assets)) yield break; var assetIndexesDi = - new DirectoryInfo(Path.Combine(BasePath, GamePathHelper.GetAssetsRoot(), "indexes")); + new DirectoryInfo(Path.Combine(this.BasePath, GamePathHelper.GetAssetsRoot(), "indexes")); var assetObjectsDi = - new DirectoryInfo(Path.Combine(BasePath, GamePathHelper.GetAssetsRoot(), "objects")); + new DirectoryInfo(Path.Combine(this.BasePath, GamePathHelper.GetAssetsRoot(), "objects")); if (!assetIndexesDi.Exists) assetIndexesDi.Create(); if (!assetObjectsDi.Exists) assetObjectsDi.Create(); - var id = VersionInfo.AssetInfo?.Id ?? VersionInfo.Assets; + var id = this.VersionInfo.AssetInfo?.Id ?? this.VersionInfo.Assets; var assetIndexesPath = Path.Combine(assetIndexesDi.FullName, $"{id}.json"); if (!File.Exists(assetIndexesPath)) { - OnResolve("没有发现 Asset Indexes 文件, 开始下载"); + this.OnResolve("没有发现 Asset Indexes 文件, 开始下载"); - var assetIndexDownloadUri = VersionInfo?.AssetInfo?.Url; + var assetIndexDownloadUri = this.VersionInfo?.AssetInfo?.Url; if (isAssetInfoNotExists) { @@ -91,11 +92,11 @@ public override async IAsyncEnumerable ResolveResourceAsync() if (string.IsNullOrEmpty(assetIndexDownloadUri)) yield break; - if (!string.IsNullOrEmpty(AssetIndexUriRoot)) + if (!string.IsNullOrEmpty(this.AssetIndexUriRoot)) { var assetIndexUriRoot = HttpHelper.RegexMatchUri(assetIndexDownloadUri); assetIndexDownloadUri = - $"{AssetIndexUriRoot}{assetIndexDownloadUri[assetIndexUriRoot.Length..]}"; + $"{this.AssetIndexUriRoot}{assetIndexDownloadUri[assetIndexUriRoot.Length..]}"; } var dp = new DownloadFile @@ -111,14 +112,14 @@ public override async IAsyncEnumerable ResolveResourceAsync() } catch (Exception e) { - OnResolve($"解析Asset Indexes 文件失败!原因:{e.Message}"); + this.OnResolve($"解析Asset Indexes 文件失败!原因:{e.Message}"); yield break; } - OnResolve("Asset Indexes 文件下载完成", 100); + this.OnResolve("Asset Indexes 文件下载完成", 100); } - OnResolve("开始解析Asset Indexes 文件..."); + this.OnResolve("开始解析Asset Indexes 文件..."); AssetObjectModel? assetObject; try @@ -129,26 +130,30 @@ public override async IAsyncEnumerable ResolveResourceAsync() } catch (Exception ex) { - OnResolve($"解析Asset Indexes 文件失败!原因:{ex.Message}"); + this.OnResolve($"解析Asset Indexes 文件失败!原因:{ex.Message}"); try { File.Delete(assetIndexesPath); } - catch (IOException) { } + catch (IOException) + { + } yield break; } if (assetObject == null) { - OnResolve("解析Asset Indexes 文件失败!原因:文件可能损坏或为空"); + this.OnResolve("解析Asset Indexes 文件失败!原因:文件可能损坏或为空"); try { File.Delete(assetIndexesPath); } - catch (IOException) { } + catch (IOException) + { + } yield break; } @@ -156,7 +161,7 @@ public override async IAsyncEnumerable ResolveResourceAsync() var checkedObject = 0; var objectCount = assetObject.Objects.Count; - OnResolve("检索并验证 Asset 资源"); + this.OnResolve("检索并验证 Asset 资源"); foreach (var (key, fi) in assetObject.Objects) { @@ -167,13 +172,13 @@ public override async IAsyncEnumerable ResolveResourceAsync() Interlocked.Increment(ref checkedObject); var progress = (double)checkedObject / objectCount * 100; - OnResolve(key.CropStr(20), progress); + this.OnResolve(key.CropStr(20), progress); if (File.Exists(filePath)) { await using var fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); - var computedHash = (await SHA1.HashDataAsync(fs)).BytesToString(); - + var computedHash = Convert.ToHexString(await SHA1.HashDataAsync(fs)); + if (computedHash.Equals(fi.Hash, StringComparison.OrdinalIgnoreCase)) continue; } @@ -182,13 +187,13 @@ public override async IAsyncEnumerable ResolveResourceAsync() Title = hash, Path = path, Type = ResourceType.Asset, - Url = $"{AssetUriRoot}{twoDigitsHash}/{fi.Hash}", + Url = $"{this.AssetUriRoot}{twoDigitsHash}/{fi.Hash}", FileSize = fi.Size, CheckSum = hash, FileName = hash }; } - OnResolve("Assets 解析完成", 100); + this.OnResolve("Assets 解析完成", 100); } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/GameLoggingInfoResolver.cs b/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/GameLoggingInfoResolver.cs index fe0b6e7a..ae1d8c66 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/GameLoggingInfoResolver.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/GameLoggingInfoResolver.cs @@ -13,40 +13,40 @@ public sealed class GameLoggingInfoResolver : ResolverBase { public override async IAsyncEnumerable ResolveResourceAsync() { - if (!CheckLocalFiles) yield break; - if (VersionInfo.Logging?.Client?.File == null) yield break; - if (string.IsNullOrEmpty(VersionInfo.Logging?.Client?.File.Url)) yield break; + if (!this.CheckLocalFiles) yield break; + if (this.VersionInfo.Logging?.Client?.File == null) yield break; + if (string.IsNullOrEmpty(this.VersionInfo.Logging?.Client?.File.Url)) yield break; - var fileName = Path.GetFileName(VersionInfo.Logging.Client.File?.Url); + var fileName = Path.GetFileName(this.VersionInfo.Logging.Client.File?.Url); if (string.IsNullOrEmpty(fileName)) yield break; - var loggingPath = GamePathHelper.GetLoggingPath(BasePath); + var loggingPath = GamePathHelper.GetLoggingPath(this.BasePath); var filePath = Path.Combine(loggingPath, fileName); if (File.Exists(filePath)) { - if (string.IsNullOrEmpty(VersionInfo.Logging?.Client?.File?.Sha1)) yield break; - + if (string.IsNullOrEmpty(this.VersionInfo.Logging?.Client?.File?.Sha1)) yield break; + await using var fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); - var computedHash = (await SHA1.HashDataAsync(fs)).BytesToString(); + var computedHash = Convert.ToHexString(await SHA1.HashDataAsync(fs)); - if (computedHash.Equals(VersionInfo.Logging?.Client?.File?.Sha1, StringComparison.OrdinalIgnoreCase)) + if (computedHash.Equals(this.VersionInfo.Logging?.Client?.File?.Sha1, StringComparison.OrdinalIgnoreCase)) yield break; } - if (string.IsNullOrEmpty(VersionInfo.Logging?.Client?.File?.Url)) + if (string.IsNullOrEmpty(this.VersionInfo.Logging?.Client?.File?.Url)) yield break; yield return new GameLoggingDownloadInfo { - CheckSum = VersionInfo.Logging?.Client?.File?.Sha1, + CheckSum = this.VersionInfo.Logging?.Client?.File?.Sha1, FileName = fileName, - FileSize = VersionInfo.Logging?.Client?.File?.Size ?? 0, + FileSize = this.VersionInfo.Logging?.Client?.File?.Size ?? 0, Path = loggingPath, Title = fileName, Type = ResourceType.Logging, - Url = VersionInfo.Logging!.Client!.File!.Url! + Url = this.VersionInfo.Logging!.Client!.File!.Url! }; } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/LibraryInfoResolver.cs b/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/LibraryInfoResolver.cs index 63d636e2..3210ca06 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/LibraryInfoResolver.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/LibraryInfoResolver.cs @@ -4,7 +4,6 @@ using System.Security.Cryptography; using System.Threading; using ProjBobcat.Class.Helper; -using ProjBobcat.Class.Helper.NativeReplace; using ProjBobcat.Class.Model; using ProjBobcat.Class.Model.GameResource; using ProjBobcat.Interface; @@ -23,57 +22,53 @@ public sealed class LibraryInfoResolver : ResolverBase public override async IAsyncEnumerable ResolveResourceAsync() { - if (!CheckLocalFiles) yield break; + if (!this.CheckLocalFiles) yield break; - OnResolve("开始进行游戏资源(Library)检查"); - if (VersionInfo.Natives.Count == 0 && - VersionInfo.Libraries.Count == 0) + this.OnResolve("开始进行游戏资源(Library)检查"); + if (this.VersionInfo.Natives.Count == 0 && this.VersionInfo.Libraries.Count == 0) yield break; - var libDi = new DirectoryInfo(Path.Combine(BasePath, GamePathHelper.GetLibraryRootPath())); + var libDi = new DirectoryInfo(Path.Combine(this.BasePath, GamePathHelper.GetLibraryRootPath())); if (!libDi.Exists) libDi.Create(); var checkedLib = 0; - var libCount = VersionInfo.Libraries?.Count ?? 0; + var libCount = this.VersionInfo.Libraries?.Count ?? 0; if (libCount > 0) - { - foreach (var lib in VersionInfo.Libraries!) + foreach (var lib in this.VersionInfo.Libraries!) { var libPath = GamePathHelper.GetLibraryPath(lib.Path!); - var filePath = Path.Combine(BasePath, libPath); + var filePath = Path.Combine(this.BasePath, libPath); Interlocked.Increment(ref checkedLib); var progress = (double)checkedLib / libCount * 100; - OnResolve(string.Empty, progress); + this.OnResolve(string.Empty, progress); if (File.Exists(filePath)) { if (string.IsNullOrEmpty(lib.Sha1)) continue; await using var fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); - var computedHash = (await SHA1.HashDataAsync(fs)).BytesToString(); + var computedHash = Convert.ToHexString(await SHA1.HashDataAsync(fs)); if (computedHash.Equals(lib.Sha1, StringComparison.OrdinalIgnoreCase)) continue; } - yield return GetDownloadFile(lib); + yield return this.GetDownloadFile(lib); } - } - OnResolve("检索并验证 Library"); + this.OnResolve("检索并验证 Library"); checkedLib = 0; - libCount = VersionInfo.Natives?.Count ?? 0; + libCount = this.VersionInfo.Natives?.Count ?? 0; if (libCount > 0) - { - foreach (var native in VersionInfo.Natives!) + foreach (var native in this.VersionInfo.Natives!) { var nativePath = GamePathHelper.GetLibraryPath(native.FileInfo.Path!); - var filePath = Path.Combine(BasePath, nativePath); + var filePath = Path.Combine(this.BasePath, nativePath); if (File.Exists(filePath)) { @@ -81,19 +76,18 @@ public override async IAsyncEnumerable ResolveResourceAsync() Interlocked.Increment(ref checkedLib); var progress = (double)checkedLib / libCount * 100; - OnResolve(string.Empty, progress); + this.OnResolve(string.Empty, progress); await using var fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); - var computedHash = (await SHA1.HashDataAsync(fs)).BytesToString(); + var computedHash = Convert.ToHexString(await SHA1.HashDataAsync(fs)); if (computedHash.Equals(native.FileInfo.Sha1, StringComparison.OrdinalIgnoreCase)) continue; } - yield return GetDownloadFile(native.FileInfo); + yield return this.GetDownloadFile(native.FileInfo); } - } - OnResolve("检查Library完成", 100); + this.OnResolve("检查Library完成", 100); } LibraryDownloadInfo GetDownloadFile(FileInfo lL) @@ -102,21 +96,21 @@ LibraryDownloadInfo GetDownloadFile(FileInfo lL) var uri = libType switch { LibraryType.Forge when lL.Url?.StartsWith("https://maven.minecraftforge.net", - StringComparison.OrdinalIgnoreCase) ?? false => $"{ForgeMavenUriRoot}{lL.Path}", + StringComparison.OrdinalIgnoreCase) ?? false => $"{this.ForgeMavenUriRoot}{lL.Path}", LibraryType.Forge when lL.Url?.StartsWith("https://files.minecraftforge.net/maven/", StringComparison.OrdinalIgnoreCase) ?? - false => $"{ForgeMavenOldUriRoot}{lL.Path}", - LibraryType.Forge => $"{ForgeUriRoot}{lL.Path}", - LibraryType.Fabric => $"{FabricMavenUriRoot}{lL.Path}", + false => $"{this.ForgeMavenOldUriRoot}{lL.Path}", + LibraryType.Forge => $"{this.ForgeUriRoot}{lL.Path}", + LibraryType.Fabric => $"{this.FabricMavenUriRoot}{lL.Path}", LibraryType.Quilt when !string.IsNullOrEmpty(lL.Url) => - $"{QuiltMavenUriRoot}{lL.Path}", - LibraryType.Other => $"{LibraryUriRoot}{lL.Path}", + $"{this.QuiltMavenUriRoot}{lL.Path}", + LibraryType.Other => $"{this.LibraryUriRoot}{lL.Path}", _ => string.Empty }; var symbolIndex = lL.Path!.LastIndexOf('/'); var fileName = lL.Path[(symbolIndex + 1)..]; - var path = Path.Combine(BasePath, + var path = Path.Combine(this.BasePath, GamePathHelper.GetLibraryPath(lL.Path[..symbolIndex])); return new LibraryDownloadInfo diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/ResolverBase.cs b/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/ResolverBase.cs index 55906bc8..83095dff 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/ResolverBase.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/ResolverBase.cs @@ -16,8 +16,8 @@ public abstract class ResolverBase : IResourceInfoResolver public event EventHandler GameResourceInfoResolveEvent { - add => listEventDelegates.AddHandler(ResolveEventKey, value); - remove => listEventDelegates.RemoveHandler(ResolveEventKey, value); + add => this.listEventDelegates.AddHandler(ResolveEventKey, value); + remove => this.listEventDelegates.RemoveHandler(ResolveEventKey, value); } public required string BasePath { get; init; } @@ -28,19 +28,19 @@ public event EventHandler GameResourceInfoReso public virtual IEnumerable ResolveResource() { - return ResolveResourceAsync().ToListAsync().GetAwaiter().GetResult(); + return this.ResolveResourceAsync().ToListAsync().GetAwaiter().GetResult(); } public void Dispose() { // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 - Dispose(true); + this.Dispose(true); GC.SuppressFinalize(this); } public virtual void OnResolve(string currentStatus, double progress = 0) { - var eventList = listEventDelegates; + var eventList = this.listEventDelegates; var @event = (EventHandler)eventList[ResolveEventKey]!; if (string.IsNullOrEmpty(currentStatus)) @@ -58,13 +58,13 @@ public virtual void OnResolve(string currentStatus, double progress = 0) protected virtual void Dispose(bool disposing) { - if (!disposedValue) + if (!this.disposedValue) { - if (disposing) listEventDelegates.Dispose(); + if (disposing) this.listEventDelegates.Dispose(); // TODO: 释放未托管的资源(未托管的对象)并重写终结器 // TODO: 将大型字段设置为 null - disposedValue = true; + this.disposedValue = true; } } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/VersionInfoResolver.cs b/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/VersionInfoResolver.cs index afaec598..219d39e0 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/VersionInfoResolver.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/VersionInfoResolver.cs @@ -14,10 +14,10 @@ public sealed class VersionInfoResolver : ResolverBase { public override async IAsyncEnumerable ResolveResourceAsync() { - if (!CheckLocalFiles) yield break; + if (!this.CheckLocalFiles) yield break; - var id = VersionInfo.RootVersion ?? VersionInfo.DirName; - var versionJson = GamePathHelper.GetGameJsonPath(BasePath, id); + var id = this.VersionInfo.RootVersion ?? this.VersionInfo.DirName; + var versionJson = GamePathHelper.GetGameJsonPath(this.BasePath, id); if (!File.Exists(versionJson)) yield break; @@ -27,20 +27,20 @@ public override async IAsyncEnumerable ResolveResourceAsync() if (rawVersionModel?.Downloads?.Client == null) yield break; var clientDownload = rawVersionModel.Downloads.Client; - var jarPath = GamePathHelper.GetVersionJar(BasePath, id); + var jarPath = GamePathHelper.GetVersionJar(this.BasePath, id); if (File.Exists(jarPath)) { if (string.IsNullOrEmpty(clientDownload.Sha1)) yield break; - + await using var jarFs = File.Open(jarPath, FileMode.Open, FileAccess.Read, FileShare.Read); - var computedHash = (await SHA1.HashDataAsync(jarFs)).BytesToString(); + var computedHash = Convert.ToHexString(await SHA1.HashDataAsync(jarFs)); if (computedHash.Equals(clientDownload.Sha1, StringComparison.OrdinalIgnoreCase)) yield break; } - + if (string.IsNullOrEmpty(clientDownload.Url)) yield break; @@ -49,7 +49,7 @@ public override async IAsyncEnumerable ResolveResourceAsync() CheckSum = clientDownload.Sha1 ?? string.Empty, FileName = $"{id}.jar", FileSize = clientDownload.Size, - Path = Path.Combine(BasePath, GamePathHelper.GetGamePath(id)), + Path = Path.Combine(this.BasePath, GamePathHelper.GetGamePath(id)), Title = $"{id}.jar", Type = ResourceType.GameJar, Url = clientDownload.Url diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Service/ServerPingService.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Service/ServerPingService.cs index ef60e78f..72335ed8 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Service/ServerPingService.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Service/ServerPingService.cs @@ -24,12 +24,12 @@ public class ServerPingService : ProgressReportBase public override string ToString() { - return $"{Address}:{Port}"; + return $"{this.Address}:{this.Port}"; } public ServerPingResult? Run() { - return RunAsync().Result; + return this.RunAsync().Result; } public async Task RunAsync() @@ -48,7 +48,7 @@ public override string ToString() try { - await client.ConnectAsync(Address, Port, cts.Token); + await client.ConnectAsync(this.Address, this.Port, cts.Token); } catch (TaskCanceledException) { @@ -57,37 +57,37 @@ public override string ToString() sw.Stop(); - InvokeStatusChangedEvent("正在连接到服务器...", 10); + this.InvokeStatusChangedEvent("正在连接到服务器...", 10); if (!client.Connected) { - InvokeStatusChangedEvent("无法连接到服务器", 10); + this.InvokeStatusChangedEvent("无法连接到服务器", 10); return null; } - _buffer = []; - _stream = client.GetStream(); + this._buffer = []; + this._stream = client.GetStream(); - InvokeStatusChangedEvent("发送请求...", 30); + this.InvokeStatusChangedEvent("发送请求...", 30); /* * Send a "Handshake" packet * http://wiki.vg/Server_List_Ping#Ping_Process */ - WriteVarInt(VersionId == 0 ? 47 : VersionId); - WriteString(Address); - WriteShort(Port); - WriteVarInt(1); - await Flush(0); + this.WriteVarInt(this.VersionId == 0 ? 47 : this.VersionId); + this.WriteString(this.Address); + this.WriteShort(this.Port); + this.WriteVarInt(1); + await this.Flush(0); /* * Send a "Status Request" packet * http://wiki.vg/Server_List_Ping#Ping_Process */ - await Flush(0); + await this.Flush(0); /* - * If you are using a modded server then use a larger buffer to account, + * If you are using a modded server then use a larger buffer to account, * see link for explanation and a motd to HTML snippet * https://gist.github.com/csh/2480d14fbbb33b4bbae3#gistcomment-2672658 */ @@ -100,12 +100,12 @@ public override string ToString() do { - var readLength = await _stream.ReadAsync(batch.AsMemory()); + var readLength = await this._stream.ReadAsync(batch.AsMemory()); await ms.WriteAsync(batch.AsMemory(0, readLength), cts.Token); if (!flag) { - var packetLength = ReadVarInt(ms.ToArray()); - remaining = packetLength - _offset; + var packetLength = this.ReadVarInt(ms.ToArray()); + remaining = packetLength - this._offset; flag = true; } @@ -116,14 +116,14 @@ public override string ToString() } while (remaining > 0); var buffer = ms.ToArray(); - _offset = 0; - var length = ReadVarInt(buffer); - var packet = ReadVarInt(buffer); - var jsonLength = ReadVarInt(buffer); + this._offset = 0; + var length = this.ReadVarInt(buffer); + var packet = this.ReadVarInt(buffer); + var jsonLength = this.ReadVarInt(buffer); - InvokeStatusChangedEvent($"收到包 0x{packet:X2} , 长度为 {length}", 80); + this.InvokeStatusChangedEvent($"收到包 0x{packet:X2} , 长度为 {length}", 80); - var json = ReadString(buffer, jsonLength); + var json = this.ReadString(buffer, jsonLength); var ping = JsonSerializer.Deserialize(json, PingPayloadContext.Default.PingPayload); if (ping == null) @@ -140,16 +140,16 @@ public override string ToString() byte ReadByte(IReadOnlyList buffer) { - var b = buffer[_offset]; - _offset += 1; + var b = buffer[this._offset]; + this._offset += 1; return b; } byte[] Read(byte[] buffer, int length) { var data = new byte[length]; - Array.Copy(buffer, _offset, data, 0, length); - _offset += length; + Array.Copy(buffer, this._offset, data, 0, length); + this._offset += length; return data; } @@ -158,7 +158,7 @@ int ReadVarInt(IReadOnlyList buffer) var value = 0; var size = 0; int b; - while (((b = ReadByte(buffer)) & 0x80) == 0x80) + while (((b = this.ReadByte(buffer)) & 0x80) == 0x80) { value |= (b & 0x7F) << (size++ * 7); if (size > 5) throw new IOException("This VarInt is an imposter!"); @@ -169,7 +169,7 @@ int ReadVarInt(IReadOnlyList buffer) string ReadString(byte[] buffer, int length) { - var data = Read(buffer, length); + var data = this.Read(buffer, length); return Encoding.UTF8.GetString(data); } @@ -177,47 +177,47 @@ void WriteVarInt(int value) { while ((value & 128) != 0) { - _buffer.Add((byte)((value & 127) | 128)); + this._buffer.Add((byte)((value & 127) | 128)); value = (int)(uint)value >> 7; } - _buffer.Add((byte)value); + this._buffer.Add((byte)value); } void WriteShort(ushort value) { - _buffer.AddRange(BitConverter.GetBytes(value)); + this._buffer.AddRange(BitConverter.GetBytes(value)); } void WriteString(string data) { var buffer = Encoding.UTF8.GetBytes(data); - WriteVarInt(buffer.Length); - _buffer.AddRange(buffer); + this.WriteVarInt(buffer.Length); + this._buffer.AddRange(buffer); } async Task Flush(int id = -1) { - var buffer = _buffer.ToArray(); - _buffer.Clear(); + var buffer = this._buffer.ToArray(); + this._buffer.Clear(); var add = 0; var packetData = new[] { (byte)0x00 }; if (id >= 0) { - WriteVarInt(id); - packetData = [.. _buffer]; + this.WriteVarInt(id); + packetData = [.. this._buffer]; add = packetData.Length; - _buffer.Clear(); + this._buffer.Clear(); } - WriteVarInt(buffer.Length + add); - var bufferLength = _buffer.ToArray(); - _buffer.Clear(); + this.WriteVarInt(buffer.Length + add); + var bufferLength = this._buffer.ToArray(); + this._buffer.Clear(); - await _stream.WriteAsync(bufferLength.AsMemory()); - await _stream.WriteAsync(packetData.AsMemory()); - await _stream.WriteAsync(buffer.AsMemory()); + await this._stream.WriteAsync(bufferLength.AsMemory()); + await this._stream.WriteAsync(packetData.AsMemory()); + await this._stream.WriteAsync(buffer.AsMemory()); } #endregion diff --git a/ProjBobcat/ProjBobcat/Event/DownloadFileChangedEventArgs.cs b/ProjBobcat/ProjBobcat/Event/DownloadFileChangedEventArgs.cs index 2050e31f..b694dfd0 100644 --- a/ProjBobcat/ProjBobcat/Event/DownloadFileChangedEventArgs.cs +++ b/ProjBobcat/ProjBobcat/Event/DownloadFileChangedEventArgs.cs @@ -5,9 +5,10 @@ namespace ProjBobcat.Event; public class DownloadFileChangedEventArgs : EventArgs { /// - /// 速度:字节 /秒 + /// 速度:字节 /秒 /// public double Speed { get; set; } + public double ProgressPercentage { get; set; } public long BytesReceived { get; set; } public long? TotalBytes { get; set; } diff --git a/ProjBobcat/ProjBobcat/Exceptions/CurseForgeModResolveException.cs b/ProjBobcat/ProjBobcat/Exceptions/CurseForgeModResolveException.cs index 7f778314..4d192488 100644 --- a/ProjBobcat/ProjBobcat/Exceptions/CurseForgeModResolveException.cs +++ b/ProjBobcat/ProjBobcat/Exceptions/CurseForgeModResolveException.cs @@ -9,24 +9,24 @@ public class CurseForgeModResolveException : Exception public CurseForgeModResolveException(long addonId, long fileId) { - _addonId = addonId; - _fileId = fileId; + this._addonId = addonId; + this._fileId = fileId; } - + public CurseForgeModResolveException(long addonId, long fileId, string moreInfo) { - _addonId = addonId; - _fileId = fileId; - _moreInfo = moreInfo; + this._addonId = addonId; + this._fileId = fileId; + this._moreInfo = moreInfo; } public override string ToString() { return $$""" - - 无法解析一个或多个 CurseForge 模组,可能的原因是因为该模组已近被作者删除或是该整合包所需的该模组的文件已经被删除。 - 模组文件下载链接:https://api.curseforge.com/v1/mods/{{_addonId}}/files/{{_fileId}}/download-url - {{_moreInfo}} - """; + + 无法解析一个或多个 CurseForge 模组,可能的原因是因为该模组已近被作者删除或是该整合包所需的该模组的文件已经被删除。 + 模组文件下载链接:https://api.curseforge.com/v1/mods/{{this._addonId}}/files/{{this._fileId}}/download-url + {{this._moreInfo}} + """; } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Exceptions/UnknownGameNameException.cs b/ProjBobcat/ProjBobcat/Exceptions/UnknownGameNameException.cs index cfb1e6db..882acb1f 100644 --- a/ProjBobcat/ProjBobcat/Exceptions/UnknownGameNameException.cs +++ b/ProjBobcat/ProjBobcat/Exceptions/UnknownGameNameException.cs @@ -12,7 +12,8 @@ namespace ProjBobcat.Exceptions; /// 导致当前异常的未知游戏名称。 /// 导致当前异常的异常。 [Serializable] -public class UnknownGameNameException(string message, string gameName, Exception? innerException) : Exception(message, innerException) +public class UnknownGameNameException(string message, string gameName, Exception? innerException) + : Exception(message, innerException) { /// /// 创建一个 的新实例。 diff --git a/ProjBobcat/ProjBobcat/Handler/RedirectHandler.cs b/ProjBobcat/ProjBobcat/Handler/RedirectHandler.cs index a8d68b85..f5c4737f 100644 --- a/ProjBobcat/ProjBobcat/Handler/RedirectHandler.cs +++ b/ProjBobcat/ProjBobcat/Handler/RedirectHandler.cs @@ -21,7 +21,7 @@ public RedirectHandler(HttpMessageHandler innerHandler) : base(innerHandler) public RedirectHandler(HttpMessageHandler innerHandler, int maxRetries) : base(innerHandler) { - _maxRetries = maxRetries; + this._maxRetries = maxRetries; } static async Task CloneHttpRequestMessageAsync( @@ -38,7 +38,7 @@ static async Task CloneHttpRequestMessageAsync( ms.Seek(0, SeekOrigin.Begin); clone.Content = new StreamContent(ms); - + foreach (var h in req.Content.Headers) clone.Content.Headers.Add(h.Key, h.Value); } @@ -80,7 +80,7 @@ protected override async Task SendAsync( var response = await base.SendAsync(request, cancellationToken); var statusCode = response.StatusCode; - while (currentRedirect < _maxRetries && + while (currentRedirect < this._maxRetries && statusCode is HttpStatusCode.MovedPermanently or HttpStatusCode.Found or @@ -88,7 +88,7 @@ HttpStatusCode.Found or { Debug.WriteLine($"第{currentRedirect}次重定向"); - var redirectedRes = await CreateRedirectResponse(request, response, cancellationToken); + var redirectedRes = await this.CreateRedirectResponse(request, response, cancellationToken); try { diff --git a/ProjBobcat/ProjBobcat/Handler/RetryHandler.cs b/ProjBobcat/ProjBobcat/Handler/RetryHandler.cs index acf353bc..5d6a9bc8 100644 --- a/ProjBobcat/ProjBobcat/Handler/RetryHandler.cs +++ b/ProjBobcat/ProjBobcat/Handler/RetryHandler.cs @@ -17,7 +17,7 @@ public RetryHandler(HttpMessageHandler innerHandler) : base(innerHandler) public RetryHandler(HttpMessageHandler innerHandler, int maxRetries) : base(innerHandler) { - _maxRetries = maxRetries; + this._maxRetries = maxRetries; } protected override async Task SendAsync( @@ -26,7 +26,7 @@ protected override async Task SendAsync( { HttpResponseMessage? response = null; - for (var i = 0; i < _maxRetries; i++) + for (var i = 0; i < this._maxRetries; i++) { try { diff --git a/ProjBobcat/ProjBobcat/Interface/IResourceCompleter.cs b/ProjBobcat/ProjBobcat/Interface/IResourceCompleter.cs index d8bcbbb1..ca4f56fa 100644 --- a/ProjBobcat/ProjBobcat/Interface/IResourceCompleter.cs +++ b/ProjBobcat/ProjBobcat/Interface/IResourceCompleter.cs @@ -21,7 +21,7 @@ public interface IResourceCompleter : IDisposable int DownloadParts { get; set; } /// - /// 最大并行下载数量 + /// 最大并行下载数量 /// int DownloadThread { get; set; } diff --git a/ProjBobcat/ProjBobcat/JsonConverter/DateTimeConverterUsingDateTimeParse.cs b/ProjBobcat/ProjBobcat/JsonConverter/DateTimeConverterUsingDateTimeParse.cs index b11c7abc..a4e54718 100644 --- a/ProjBobcat/ProjBobcat/JsonConverter/DateTimeConverterUsingDateTimeParse.cs +++ b/ProjBobcat/ProjBobcat/JsonConverter/DateTimeConverterUsingDateTimeParse.cs @@ -11,7 +11,7 @@ public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, Jso if (typeToConvert != typeof(DateTime)) throw new ArgumentException( $"{nameof(DateTimeConverterUsingDateTimeParse)} cannot deserialize " + - $"an object of {typeToConvert.Name}", + $"an object of {typeToConvert.Name}", nameof(typeToConvert)); return DateTime.Parse(reader.GetString() ?? string.Empty); diff --git a/ProjBobcat/ProjBobcat/Platforms/Linux/FileHelper.Linux.cs b/ProjBobcat/ProjBobcat/Platforms/Linux/FileHelper.Linux.cs index 3327df49..5d65fca3 100644 --- a/ProjBobcat/ProjBobcat/Platforms/Linux/FileHelper.Linux.cs +++ b/ProjBobcat/ProjBobcat/Platforms/Linux/FileHelper.Linux.cs @@ -23,7 +23,7 @@ public static bool Chmod(string filePath, string permissions) using var process = Process.Start(info); if (process == null) return false; - + var output = process.StandardError.ReadToEnd(); process.WaitForExit(); diff --git a/ProjBobcat/ProjBobcat/Platforms/Linux/SystemInfoHelper.Linux.cs b/ProjBobcat/ProjBobcat/Platforms/Linux/SystemInfoHelper.Linux.cs index a77cdbf9..c94f53c4 100644 --- a/ProjBobcat/ProjBobcat/Platforms/Linux/SystemInfoHelper.Linux.cs +++ b/ProjBobcat/ProjBobcat/Platforms/Linux/SystemInfoHelper.Linux.cs @@ -1,11 +1,11 @@ -using ProjBobcat.Class.Model; -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Runtime.Versioning; +using ProjBobcat.Class.Model; namespace ProjBobcat.Platforms.Linux; @@ -13,10 +13,12 @@ namespace ProjBobcat.Platforms.Linux; class SystemInfoHelper { /// - /// Get the system overall CPU usage percentage. + /// Get the system overall CPU usage percentage. /// - /// The percentange value with the '%' sign. e.g. if the usage is 30.1234 %, - /// then it will return 30.12. + /// + /// The percentange value with the '%' sign. e.g. if the usage is 30.1234 %, + /// then it will return 30.12. + /// public static CPUInfo GetLinuxCpuUsage() { var info = new ProcessStartInfo diff --git a/ProjBobcat/ProjBobcat/Platforms/MacOS/FileHelper.MacOS.cs b/ProjBobcat/ProjBobcat/Platforms/MacOS/FileHelper.MacOS.cs index 0d453e44..350468f1 100644 --- a/ProjBobcat/ProjBobcat/Platforms/MacOS/FileHelper.MacOS.cs +++ b/ProjBobcat/ProjBobcat/Platforms/MacOS/FileHelper.MacOS.cs @@ -23,7 +23,7 @@ public static bool Chmod(string filePath, string permissions) using var process = Process.Start(info); if (process == null) return false; - + var output = process.StandardError.ReadToEnd(); process.WaitForExit(); diff --git a/ProjBobcat/ProjBobcat/Platforms/MacOS/SystemInfoHelper.MacOS.cs b/ProjBobcat/ProjBobcat/Platforms/MacOS/SystemInfoHelper.MacOS.cs index e26dccec..6fff6245 100644 --- a/ProjBobcat/ProjBobcat/Platforms/MacOS/SystemInfoHelper.MacOS.cs +++ b/ProjBobcat/ProjBobcat/Platforms/MacOS/SystemInfoHelper.MacOS.cs @@ -1,5 +1,4 @@ -using ProjBobcat.Class.Model; -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -7,6 +6,7 @@ using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; +using ProjBobcat.Class.Model; namespace ProjBobcat.Platforms.MacOS; @@ -14,7 +14,8 @@ namespace ProjBobcat.Platforms.MacOS; static partial class SystemInfoHelper { [LibraryImport("libc", StringMarshalling = StringMarshalling.Utf8)] - private static partial int sysctlbyname(string name, out int int_val, ref IntPtr length, IntPtr newp, IntPtr newlen); + private static partial int sysctlbyname(string name, out int int_val, ref IntPtr length, IntPtr newp, + IntPtr newlen); public static IEnumerable FindJavaMacOS() { @@ -34,10 +35,12 @@ public static IEnumerable FindJavaMacOS() } /// - /// Get the system overall CPU usage percentage. + /// Get the system overall CPU usage percentage. /// - /// The percentange value with the '%' sign. e.g. if the usage is 30.1234 %, - /// then it will return 30.12. + /// + /// The percentange value with the '%' sign. e.g. if the usage is 30.1234 %, + /// then it will return 30.12. + /// public static CPUInfo GetOSXCpuUsage() { var info = new ProcessStartInfo @@ -126,7 +129,7 @@ public static MemoryInfo GetOsxMemoryStatus() .ToDictionary(pair => pair.Item1, pair2 => pair2.Item2); var pageSize = uint.TryParse(NumberMatchRegex().Match(split[0]).Value, out var pageSizeOut) ? pageSizeOut : 0; - var active = (infoDic.GetValueOrDefault("Pages active", 0)) * pageSize; + var active = infoDic.GetValueOrDefault("Pages active", 0) * pageSize; var used = active / Math.Pow(1024, 2); var total = GetTotalMemory() / Math.Pow(1024, 2); diff --git a/ProjBobcat/ProjBobcat/Platforms/Windows/DefaultMinecraftUWPCore.cs b/ProjBobcat/ProjBobcat/Platforms/Windows/DefaultMinecraftUWPCore.cs index 58be5bb5..e9c17792 100644 --- a/ProjBobcat/ProjBobcat/Platforms/Windows/DefaultMinecraftUWPCore.cs +++ b/ProjBobcat/ProjBobcat/Platforms/Windows/DefaultMinecraftUWPCore.cs @@ -54,12 +54,12 @@ public override LaunchResult Launch(LaunchSettings launchSettings) }; launchWrapper.Do(); - InvokeLaunchLogThenStart("启动游戏", ref prevSpan, ref stopwatch); + this.InvokeLaunchLogThenStart("启动游戏", ref prevSpan, ref stopwatch); Task.Run(launchWrapper.Process.WaitForExit) .ContinueWith(task => { - OnGameExit(launchWrapper, new GameExitEventArgs + this.OnGameExit(launchWrapper, new GameExitEventArgs { Exception = task.Exception, ExitCode = launchWrapper.ExitCode @@ -93,7 +93,7 @@ public override Task LaunchTaskAsync(LaunchSettings? settings) /// void InvokeLaunchLogThenStart(string item, ref TimeSpan time, ref Stopwatch sw) { - OnLogLaunchData(this, new LaunchLogEventArgs + this.OnLogLaunchData(this, new LaunchLogEventArgs { Item = item, ItemRunTime = sw.Elapsed - time @@ -103,4 +103,4 @@ void InvokeLaunchLogThenStart(string item, ref TimeSpan time, ref Stopwatch sw) } #endregion -} +} \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Platforms/Windows/SystemInfoHelper.Windows.cs b/ProjBobcat/ProjBobcat/Platforms/Windows/SystemInfoHelper.Windows.cs index 43b625db..cecdc093 100644 --- a/ProjBobcat/ProjBobcat/Platforms/Windows/SystemInfoHelper.Windows.cs +++ b/ProjBobcat/ProjBobcat/Platforms/Windows/SystemInfoHelper.Windows.cs @@ -2,16 +2,16 @@ using System.Collections.Frozen; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Runtime.Versioning; +using System.Security; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.System.SystemInformation; using Microsoft.Win32; using ProjBobcat.Class.Model; -using System.IO; -using System.Security; namespace ProjBobcat.Platforms.Windows; @@ -22,6 +22,20 @@ public static class SystemInfoHelper static readonly PerformanceCounter? MemUsagePercentageCounter; static readonly PerformanceCounter? CpuCounter; + static readonly string[] LineChArr = ["\r\n", "\r", "\n"]; + static readonly char[] SepArr = [':']; + + static readonly FrozenSet PossibleJavaDirs = new[] + { + "java", "jdk", "env", "环境", "run", "软件", "jre", "mc", "soft", "cache", "temp", "corretto", "roaming", + "users", "craft", "program", "世界", "net", "游戏", "oracle", "game", "file", "data", "jvm", "服务", "server", "客户", + "client", "整合", "应用", "运行", "前置", "mojang", "官启", "新建文件夹", "eclipse", "microsoft", "hotspot", "runtime", "x86", + "x64", "forge", "原版", "optifine", "官方", "启动", "hmcl", "mod", "高清", "download", "launch", "程序", "path", + "version", "baka", "pcl", "zulu", "local", "packages", "4297127d64ec6", "国服", "网易", "ext", "netease", "1.", + "启动", + "jdks" + }.ToFrozenSet(); + static SystemInfoHelper() { try @@ -35,7 +49,9 @@ static SystemInfoHelper() MemUsagePercentageCounter.NextValue(); CpuCounter.NextValue(); } - catch (Exception) { } + catch (Exception) + { + } } /// @@ -74,9 +90,6 @@ public static AppxPackageInfo GetAppxPackage(string appName) return SetAppxPackageInfoProperty(appxPackageInfo, values); } - static readonly string[] LineChArr = ["\r\n", "\r", "\n"]; - static readonly char[] SepArr = [':']; - /// /// 分析 PowerShell Get-AppxPackage 的输出。 /// @@ -155,18 +168,8 @@ static AppxPackageInfo SetAppxPackageInfoProperty(AppxPackageInfo appxPackageInf return appxPackageInfo; } - static readonly FrozenSet PossibleJavaDirs = new [] - { - "java", "jdk", "env", "环境", "run", "软件", "jre", "mc", "soft", "cache", "temp", "corretto", "roaming", - "users", "craft", "program", "世界", "net", "游戏", "oracle", "game", "file", "data", "jvm", "服务", "server", "客户", - "client", "整合", "应用", "运行", "前置", "mojang", "官启", "新建文件夹", "eclipse", "microsoft", "hotspot", "runtime", "x86", - "x64", "forge", "原版", "optifine", "官方", "启动", "hmcl", "mod", "高清", "download", "launch", "程序", "path", - "version", "baka", "pcl", "zulu", "local", "packages", "4297127d64ec6", "国服", "网易", "ext", "netease", "1.", "启动", - "jdks" - }.ToFrozenSet(); - /// - /// 查找可能的 javaw.exe 的路径。 + /// 查找可能的 javaw.exe 的路径。 /// /// 可能的 Java 路径构成的列表。 public static IEnumerable FindJavaWindows() @@ -188,8 +191,10 @@ public static IEnumerable FindJavaWindows() foreach (var drive in drives) FindJavaBlur(drive.RootDirectory, javas); - FindJavaBlur(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)), javas); - FindJavaBlur(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)), javas); + FindJavaBlur(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)), + javas); + FindJavaBlur(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)), + javas); FindJavaBlur(new DirectoryInfo(Environment.CurrentDirectory), javas); return javas; @@ -236,15 +241,20 @@ static void FindJavaBlur(DirectoryInfo di, HashSet javas, int maxDepth = continue; } - if (PossibleJavaDirs.Any(possibleName => dirName.Contains(possibleName, StringComparison.OrdinalIgnoreCase))) - { + if (PossibleJavaDirs.Any(possibleName => + dirName.Contains(possibleName, StringComparison.OrdinalIgnoreCase))) stack.Push((subDi, currentDepth + 1)); - } } } - catch(SecurityException) { } - catch (UnauthorizedAccessException) { } - catch (AccessViolationException) { } + catch (SecurityException) + { + } + catch (UnauthorizedAccessException) + { + } + catch (AccessViolationException) + { + } } } @@ -336,7 +346,7 @@ public static IEnumerable GetLogicalDrives() } /// - /// 获取 Windows 系统版本,7,8.1,10,11 + /// 获取 Windows 系统版本,7,8.1,10,11 /// /// public static string GetWindowsMajorVersion() @@ -353,7 +363,7 @@ public static string GetWindowsMajorVersion() } /// - /// 检查某个进程是否运行在 X86 模拟下 + /// 检查某个进程是否运行在 X86 模拟下 /// /// /// 待检查的进程,如果不传则检测当前进程