From ed0335a03d616dda1d0cbb40352aa82b20d72ab1 Mon Sep 17 00:00:00 2001 From: WhiteCAT <64885812+whitecat346@users.noreply.github.com> Date: Thu, 6 Feb 2025 20:50:12 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=AE=8C=E6=88=90=E4=B8=BB=E8=A6=81?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E9=80=BB=E8=BE=91=EF=BC=88=E6=9A=82=E6=97=B6?= =?UTF-8?q?=E4=B8=8D=E8=83=BD=E8=BF=90=E8=A1=8C=EF=BC=89=20=E4=B8=8B?= =?UTF-8?q?=E4=B8=80=E6=AD=A5=E8=AE=A1=E5=88=92=E6=98=AF=E5=AF=B9=E4=B8=8D?= =?UTF-8?q?=E5=90=8C=E7=9A=84=E6=B8=B8=E6=88=8F=E7=89=88=E6=9C=AC=E5=81=9A?= =?UTF-8?q?=E5=87=BA=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PCL2.Neo.sln | 6 + PCL2.Neo.sln.DotSettings.user | 19 + PCL2.Neo/Models/Audio/AudioData.cs | 6 +- PCL2.Neo/Models/Minecraft/JavaData.cs | 4 +- .../Models/Minecraft/McVersion/Downloader.cs | 42 ++ .../Models/Minecraft/McVersion/VersionData.cs | 446 ++++++++++++++++++ PCL2.Neo/Utils/Logger.cs | 3 +- PCL2.Neo/Utils/MathUtils.cs | 13 +- .../Minecraft/McVersion/VersionTests.cs | 19 + PCL2.NeoTests/PCL2.NeoTests.csproj | 23 + 10 files changed, 572 insertions(+), 9 deletions(-) create mode 100644 PCL2.Neo/Models/Minecraft/McVersion/Downloader.cs create mode 100644 PCL2.Neo/Models/Minecraft/McVersion/VersionData.cs create mode 100644 PCL2.NeoTests/Models/Minecraft/McVersion/VersionTests.cs create mode 100644 PCL2.NeoTests/PCL2.NeoTests.csproj diff --git a/PCL2.Neo.sln b/PCL2.Neo.sln index 1c667b7..d53bf28 100644 --- a/PCL2.Neo.sln +++ b/PCL2.Neo.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 17.12.35527.113 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PCL2.Neo", "PCL2.Neo\PCL2.Neo.csproj", "{AF527853-D92E-44CE-A3EB-1E308C4FBFE2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCL2.NeoTests", "PCL2.NeoTests\PCL2.NeoTests.csproj", "{41B478FE-8F3D-4611-8C5F-5396FF8DF6AF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {AF527853-D92E-44CE-A3EB-1E308C4FBFE2}.Debug|Any CPU.Build.0 = Debug|Any CPU {AF527853-D92E-44CE-A3EB-1E308C4FBFE2}.Release|Any CPU.ActiveCfg = Release|Any CPU {AF527853-D92E-44CE-A3EB-1E308C4FBFE2}.Release|Any CPU.Build.0 = Release|Any CPU + {41B478FE-8F3D-4611-8C5F-5396FF8DF6AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {41B478FE-8F3D-4611-8C5F-5396FF8DF6AF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {41B478FE-8F3D-4611-8C5F-5396FF8DF6AF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {41B478FE-8F3D-4611-8C5F-5396FF8DF6AF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PCL2.Neo.sln.DotSettings.user b/PCL2.Neo.sln.DotSettings.user index 59c8d1d..696da71 100644 --- a/PCL2.Neo.sln.DotSettings.user +++ b/PCL2.Neo.sln.DotSettings.user @@ -15,4 +15,23 @@ ForceIncluded ForceIncluded ForceIncluded + <SessionState ContinuousTestingMode="0" Name="RebuildJsonTest" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <TestAncestor> + <TestId>MSTest::41B478FE-8F3D-4611-8C5F-5396FF8DF6AF::net9.0::PCL2.Neo.Models.Minecraft.McVersion.VersionData.Tests.VersionTests.RebuildJsonTest</TestId> + <TestId>MSTest::41B478FE-8F3D-4611-8C5F-5396FF8DF6AF::net9.0::PCL2.Neo.Models.Minecraft.McVersion.VersionData.Tests.VersionTests.LibrariesParserTest</TestId> + </TestAncestor> +</SessionState> + <SessionState ContinuousTestingMode="0" IsActive="True" Name="LibrariesParserTest" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <TestAncestor> + <TestId>MSTest::41B478FE-8F3D-4611-8C5F-5396FF8DF6AF::net9.0::PCL2.Neo.Models.Minecraft.McVersion.VersionData.Tests.VersionTests.LibrariesParserTest</TestId> + <TestId>MSTest::41B478FE-8F3D-4611-8C5F-5396FF8DF6AF::net9.0::PCL2.Neo.Models.Minecraft.McVersion.VersionData.Tests.VersionTests.RebuildJsonTest</TestId> + <TestId>MSTest::41B478FE-8F3D-4611-8C5F-5396FF8DF6AF::net9.0::PCL2.Neo.Models.Minecraft.McVersion.VersionData.Tests.VersionTests.ParserTest</TestId> + </TestAncestor> +</SessionState> + <SessionState ContinuousTestingMode="0" Name="RebuildJsonTest #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <TestAncestor> + <TestId>MSTest::41B478FE-8F3D-4611-8C5F-5396FF8DF6AF::net9.0::PCL2.Neo.Models.Minecraft.McVersion.VersionData.Tests.VersionTests.RebuildJsonTest</TestId> + </TestAncestor> +</SessionState> + <data><HostParameters type="LocalHostParameters" /><Argument type="StandaloneArgument"><Arguments IsNull="False"></Arguments><FileName IsNull="False"></FileName><WorkingDirectory IsNull="False"></WorkingDirectory><Scope><ProcessFilters /></Scope></Argument><Info type="TimelineInfo"><LoadPdb>True</LoadPdb></Info><CoreOptions type="CoreOptions"><CoreTempPath IsNull="False"></CoreTempPath><RemoteEndPoint IsNull="False"></RemoteEndPoint><AdditionalEnvironmentVariables><Item><Key>DOTNET_ENVIRONMENT</Key><Value>Development</Value></Item><Item><Key>COMPLUS_InstallRoot</Key><Value IsNull="False"></Value></Item><Item><Key>RESHARPER_HOST_VERSION</Key><Value>243.0.20241111.161023</Value></Item><Item><Key>COMPLUS_Version</Key><Value IsNull="False"></Value></Item><Item><Key>RESHARPER_TESTRUNNER</Key><Value>Profile</Value></Item><Item><Key>RESHARPER_HOST</Key><Value>ReSharperPlatformVs17</Value></Item></AdditionalEnvironmentVariables></CoreOptions><HostOptions type="HostOptions"><HostTempPath IsNull="False"></HostTempPath><ReprofileDisableReason>LaunchedByReSharperUnitTestRunner</ReprofileDisableReason></HostOptions></data> \ No newline at end of file diff --git a/PCL2.Neo/Models/Audio/AudioData.cs b/PCL2.Neo/Models/Audio/AudioData.cs index fe916fe..40cf6cf 100644 --- a/PCL2.Neo/Models/Audio/AudioData.cs +++ b/PCL2.Neo/Models/Audio/AudioData.cs @@ -18,8 +18,8 @@ public enum FileType public class AudioData { - public required string Name { get; set; } - public required string Path { get; set; } - public required FileType Type { get; set; } + public string Name { get; set; } + public string Path { get; set; } + public FileType Type { get; set; } } } diff --git a/PCL2.Neo/Models/Minecraft/JavaData.cs b/PCL2.Neo/Models/Minecraft/JavaData.cs index 5d08584..db2dfe1 100644 --- a/PCL2.Neo/Models/Minecraft/JavaData.cs +++ b/PCL2.Neo/Models/Minecraft/JavaData.cs @@ -7,9 +7,9 @@ namespace PCL2.Neo.Models.Minecraft { internal record JavaExist { - public required bool IsExist { get; set; } + public bool IsExist { get; set; } - public required string Path { get; set; } + public string Path { get; set; } } public class JavaEntity(string path) { diff --git a/PCL2.Neo/Models/Minecraft/McVersion/Downloader.cs b/PCL2.Neo/Models/Minecraft/McVersion/Downloader.cs new file mode 100644 index 0000000..c6c8787 --- /dev/null +++ b/PCL2.Neo/Models/Minecraft/McVersion/Downloader.cs @@ -0,0 +1,42 @@ +using PCL2.Neo.Models.Minecraft.McVersion.VersionManifest; +using System; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Tasks; + +namespace PCL2.Neo.Models.Minecraft.McVersion +{ + public class Downloader + { + public static async Task GetVersionManifest() + { + try + { + using var client = new HttpClient(); + var response = await client + .GetStringAsync("https://launchermeta.mojang.com/mc/game/version_manifest.json"); + return JsonSerializer.Deserialize(response); + } + catch (HttpRequestException e) + { + // TODO: Handle Exception + throw; + } + } + + public static async Task GetVersionInfo(VersionInfo info) + { + try + { + using var client = new HttpClient(); + return await client + .GetStringAsync(info.Url); + } + catch (HttpRequestException e) + { + // TODO: Handle Exception + throw; + } + } + } +} diff --git a/PCL2.Neo/Models/Minecraft/McVersion/VersionData.cs b/PCL2.Neo/Models/Minecraft/McVersion/VersionData.cs new file mode 100644 index 0000000..9154e90 --- /dev/null +++ b/PCL2.Neo/Models/Minecraft/McVersion/VersionData.cs @@ -0,0 +1,446 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.Json; +using System.Text.RegularExpressions; + +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 + +namespace PCL2.Neo.Models.Minecraft.McVersion +{ + namespace VersionManifest + { + public record Latest + { + public string Release { get; init; } + public string Snapshot { get; init; } + } + + public record VersionInfo + { + public string Id { get; init; } + public string Time { get; init; } + public string Type { get; init; } + public string Url { get; init; } + public string ReleaseTime { get; init; } + } + + public record VersionManifestData + { + public Latest Latest { get; init; } + public List Versions { get; init; } + } + } + + namespace VersionData + { + public record RulesFeaturesElements + { + public string Name { get; init; } + public bool Value { get; init; } + } + + public record GameRulesElements + { + public string Action { get; init; } + public RulesFeaturesElements RulesFeature { get; init; } + } + + public record GameRulesList + { + public List Rules { get; init; } + public List Value { get; init; } + } + + public record Game + { + public List GameRules { get; init; } + public List Value { get; init; } + } + + public record OsElements + { + public string Name { get; init; } + public string Value { get; init; } + } + + public record JvmRulesElements + { + public string? FirstAction { get; init; } + public string Action { get; init; } + public List Os { get; init; } + } + + public record JvmRulesList + { + public List Rules { get; init; } + public List Value { get; init; } + } + + public record Jvm + { + public List Rules { get; init; } + public List Value { get; init; } + } + + public record Arguments + { + public Game Game { get; init; } + public Jvm Jvm { get; init; } + } + + public record AssestIndex + { + public string Id { get; init; } + public string Sha1 { get; init; } + public string Size { get; init; } + public int TotalSize { get; init; } + public string Url { get; init; } + } + + public record DownloadsEneemltValue + { + public string Sha1 { get; init; } + public int Size { get; init; } + public string Url { get; init; } + } + + public record Downloads + { + public DownloadsEneemltValue Client { get; init; } + public DownloadsEneemltValue ClientMappings { get; init; } + public DownloadsEneemltValue Server { get; init; } + public DownloadsEneemltValue ServerMappings { get; init; } + } + + public record JavaVersion + { + public string Component { get; init; } + public int MajorVersion { get; init; } + } + + public record Artifact + { + public string Path { get; init; } + public string Sha1 { get; init; } + public int Size { get; init; } + public string Url { get; init; } + } + + public record LibrariesDownloads + { + public Artifact Artifact { get; set; } + public List<(string, Artifact)>? Classifiers { get; set; } + public string Name { get; set; } + public List<(string, string)>? Natives { get; set; } + public JvmRulesElements? Rules { get; set; } + } + + public record LibrariesNameUrl + { + public string Name { get; set; } + public string Url { get; set; } + } + + public record Libraries + { + public List Downloads { get; set; } + public List NameUrls { get; set; } + } + + public record File + { + public string Id { get; init; } + public string Sha1 { get; init; } + public int Size { get; init; } + public string Url { get; init; } + } + + public record Client + { + public string Argument { get; init; } + public File File { get; init; } + public string Type { get; init; } + } + + public record Logging + { + public Client Client { get; init; } + } + + public record Temp + { + public AssestIndex AssestIndex { get; init; } + public string Assets { get; init; } + public byte ComplianceLevel { get; init; } + public Downloads Downloads { get; init; } + public string Id { get; init; } + public JavaVersion JavaVersion { get; init; } + public Logging Logging { get; init; } + public string MainClass { get; init; } + public byte MinimunLauncherVersion { get; init; } + public string ReleaseTime { get; init; } + public string Time { get; init; } + public string Type { get; init; } + } + + public record VersionData + { + public Arguments Arguments { get; init; } + public AssestIndex AssestIndex { get; init; } + public string Assets { get; init; } + public byte ComplianceLevel { get; init; } + public Downloads Downloads { get; init; } + public string Id { get; init; } + public JavaVersion JavaVersion { get; init; } + public Libraries Libraries { get; init; } + public Logging Logging { get; init; } + public string MainClass { get; init; } + public byte MinimunLauncherVersion { get; init; } + public string ReleaseTime { get; init; } + public string Time { get; init; } + public string Type { get; init; } + } + + public partial class Version + { + private static GameRulesList GameRulesListParser(JsonElement element) + { + var rulesList = new GameRulesList { Rules = [], Value = [] }; + + var rules = element.GetProperty("rules").EnumerateArray().ElementAt(0); + //var values = element.GetProperty("value").EnumerateArray(); + var values = element.GetProperty("value"); + + var action = rules.GetProperty("action").GetString()!; + var features = rules.GetProperty("features"); + + var ruleElement = new GameRulesElements + { + Action = action, + RulesFeature = new RulesFeaturesElements + { + Name = RulesNameRegex().Match(features.GetRawText()).Groups[1].Value, + Value = features.GetRawText().Contains("true") + } + }; + + // add values + switch (values.ValueKind) + { + case JsonValueKind.Array: + { + foreach (var item in values.EnumerateArray()) rulesList.Value.Add(item.GetString()!); + break; + } + case JsonValueKind.String: + rulesList.Value.Add(values.GetString()!); + break; + default: + throw new JsonException("Unknown JsonValueKind"); + } + + rulesList.Rules.Add(ruleElement); + + return rulesList; + } + + private static Game GameParser(JsonElement element) + { + var enummerable = element.EnumerateArray(); + var game = new Game { GameRules = [], Value = [] }; + + foreach (var item in enummerable) + { + switch (item.ValueKind) + { + case JsonValueKind.String: + game.Value.Add(item.GetString()!); + break; + case JsonValueKind.Object: + game.GameRules.Add(GameRulesListParser(item)); + break; + default: + throw new JsonException("Unknown JsonValueKind"); + } + } + + return game; + } + + private static JvmRulesElements JvmRulesElementsParser(JsonElement element) + { + var os = element.GetProperty("os").GetRawText(); + + return new JvmRulesElements + { + Action = element.GetProperty("action").GetString()!, + Os = + [ + new OsElements() + { + Name = RulesNameRegex().Match(os).Groups[1].Value, + Value = JvmOsValueRegex().Match(os).Groups[1].Value + } + ] + }; + } + + private static JvmRulesList JvmRulesListParser(JsonElement element) + { + var result = new JvmRulesList { Rules = [], Value = [] }; + + var rules = element.GetProperty("rules").EnumerateArray().ElementAt(0); + var values = element.GetProperty("value"); + + result.Rules.Add(JvmRulesElementsParser(rules)); + + switch (values.ValueKind) + { + case JsonValueKind.Array: + { + var valArray = values.EnumerateArray(); + foreach (var item in valArray) result.Value.Add(item.GetString()!); + break; + } + case JsonValueKind.String: + result.Value.Add(values.GetString()!); + break; + default: + throw new JsonException("Unknown JsonValueKind"); + } + + return result; + } + + private static Jvm JvmParser(JsonElement element) + { + var enummerable = element.EnumerateArray(); + var jvm = new Jvm { Rules = [], Value = [] }; + + foreach (var item in enummerable) + { + switch (item.ValueKind) + { + case JsonValueKind.Object: + jvm.Rules.Add(JvmRulesListParser(item)); + break; + case JsonValueKind.String: + jvm.Value.Add(item.GetString()!); + break; + default: + throw new JsonException("Unknown JsonValueKind"); + } + } + + return jvm; + } + + private static Arguments ArgumentsParser(JsonElement element) + { + var game = GameParser(element.GetProperty("game")); + var jvm = JvmParser(element.GetProperty("jvm")); + return new Arguments { Game = game, Jvm = jvm }; + } + + private static LibrariesDownloads DowloadParser(JsonElement element) + { + var artifact = element.GetProperty("downloads").GetProperty("artifact").GetRawText(); + var name = element.GetProperty("name").GetString()!; + + if (element.TryGetProperty("rules", out var rules)) + { + //return new LibrariesDownloads + //{ + // Artifact = JsonSerializer.Deserialize(artifact)!, + // Name = name, + // Rules = JvmRulesElementsParser(rules.EnumerateArray().ElementAt(0)) + //}; + // TODO: add code + } + else + { + //return new LibrariesDownloads + //{ + // Artifact = JsonSerializer.Deserialize(artifact)!, Name = name, Rules = null + //}; + // TODO: add code + } + } + + private static Libraries LibrariesParser(JsonElement element) + { + var array = element.EnumerateArray(); + var result = new Libraries { Downloads = [], NameUrls = [] }; + + foreach (var item in array) + { + if (item.TryGetProperty("downloads", out _)) + { + result.Downloads.Add(DowloadParser(item)); + } + else + { + result.NameUrls.Add(JsonSerializer.Deserialize(item.GetRawText())!); + } + } + + return result; + } + + private static Temp RebuildJson(JsonElement element) + { + var root = element.EnumerateObject(); + var result = new StringBuilder("{"); + foreach (var pro in root.Where(pro => pro.Name != "arguments" && pro.Name != "libraries")) + { + result.Append($"{pro},"); + } + + result.Remove(result.Length - 1, 1).Append("}"); + + var str = result.ToString(); + //Console.WriteLine(str); + + return JsonSerializer.Deserialize(str)!; + } + + public static VersionData Parser(string input) + { + var root = JsonDocument.Parse(input).RootElement; + var arguments = root.GetProperty("arguments"); + + var argumentsJson = ArgumentsParser(arguments); + var rebuild = RebuildJson(root); + var libraries = LibrariesParser(root.GetProperty("libraries")); + + return new VersionData + { + Arguments = argumentsJson, + AssestIndex = rebuild.AssestIndex, + Assets = rebuild.Assets, + ComplianceLevel = rebuild.ComplianceLevel, + Downloads = rebuild.Downloads, + Id = rebuild.Id, + JavaVersion = rebuild.JavaVersion, + Libraries = libraries, + Logging = rebuild.Logging, + MainClass = rebuild.MainClass, + MinimunLauncherVersion = rebuild.MinimunLauncherVersion, + ReleaseTime = rebuild.ReleaseTime, + Time = rebuild.Time, + Type = rebuild.Type + }; + } + + [GeneratedRegex(@"""([^""]+)""\s*:\s*true")] + private static partial Regex RulesNameRegex(); + + [GeneratedRegex(@"(?<=""name"":\s*)""([^""]+)""")] + private static partial Regex JvmOsValueRegex(); + } + } +} diff --git a/PCL2.Neo/Utils/Logger.cs b/PCL2.Neo/Utils/Logger.cs index 0918aa9..2c2664b 100644 --- a/PCL2.Neo/Utils/Logger.cs +++ b/PCL2.Neo/Utils/Logger.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.IO; using System.Text; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using static PCL2.Neo.Const; @@ -127,7 +128,7 @@ public void Log(string text, LogLevel level = LogLevel.Normal, string title = " #if DEBUG Debug.Write(logText); #endif - string msg = StringUtils.RegexReplace(text, "", @"\[[^\]]+?\] "); + string msg = Regex.Replace(text, @"\[[^\]]+?\] ", string.Empty); switch (level) { #if DEBUG diff --git a/PCL2.Neo/Utils/MathUtils.cs b/PCL2.Neo/Utils/MathUtils.cs index 3a5bb98..bdd66d5 100644 --- a/PCL2.Neo/Utils/MathUtils.cs +++ b/PCL2.Neo/Utils/MathUtils.cs @@ -16,7 +16,7 @@ public static string RadixConvert(string input, int fromRadix, int toRadix) var isNegative = input.StartsWith("-"); if (isNegative) input = input.TrimStart('-'); long realNum = 0, scale = 1; - foreach (var digit in input.Reverse().Select(l => digits.IndexOfF(l.ToString()))) + foreach (var digit in input.Reverse().Select(l => digits.IndexOf(l.ToString(), StringComparison.Ordinal))) { realNum += digit * scale; scale *= fromRadix; @@ -38,8 +38,15 @@ public static string RadixConvert(string input, int fromRadix, int toRadix) /// public static double MathBezier(double x, double x1, double y1, double x2, double y2, double acc = 0.01) { - if (x <= 0 || double.IsNaN(x)) return 0; - if (x >= 1) return 1; + switch (x) + { + case <= 0: + case double.NaN: + return 0; + case >= 1: + return 1; + } + var a = x; double b; do diff --git a/PCL2.NeoTests/Models/Minecraft/McVersion/VersionTests.cs b/PCL2.NeoTests/Models/Minecraft/McVersion/VersionTests.cs new file mode 100644 index 0000000..cdd83de --- /dev/null +++ b/PCL2.NeoTests/Models/Minecraft/McVersion/VersionTests.cs @@ -0,0 +1,19 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Text.Json; +using System.IO; +using PCL2.Neo.Models.Minecraft.McVersion.VersionData; + +namespace PCL2.Neo.Models.Minecraft.McVersion.VersionData.Tests +{ + [TestClass()] + public class VersionTests + { + [TestMethod()] + public void ParserTest() + { + var fileInfo = System.IO.File.ReadAllText( + @"C:\Users\WhiteCAT\Desktop\Games\PCL2\.minecraft\versions\1.19.2-Fabric 0.14.10\1.19.2-Fabric 0.14.10.json"); + Console.WriteLine(Version.Parser(fileInfo)); + } + } +} \ No newline at end of file diff --git a/PCL2.NeoTests/PCL2.NeoTests.csproj b/PCL2.NeoTests/PCL2.NeoTests.csproj new file mode 100644 index 0000000..23710a9 --- /dev/null +++ b/PCL2.NeoTests/PCL2.NeoTests.csproj @@ -0,0 +1,23 @@ + + + + net9.0 + latest + enable + enable + + + + + + + + + + + + + + + + From dfda226f5f90e66abe70530ca159ec39248a6d29 Mon Sep 17 00:00:00 2001 From: WhiteCAT <64885812+whitecat346@users.noreply.github.com> Date: Fri, 7 Feb 2025 19:27:39 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=AE=8C=E6=88=90version.json=E7=9A=84?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=EF=BC=8C=E5=AF=B9=E4=BA=8E=E4=B8=8D=E5=90=8C?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E4=B9=9F=E6=9C=89=E5=85=BC=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PCL2.Neo.sln.DotSettings.user | 15 +- PCL2.Neo/.editorconfig | 23 +- .../Models/Minecraft/McVersion/VersionData.cs | 369 +++++----------- .../Minecraft/McVersion/VersionFileParser.cs | 393 ++++++++++++++++++ .../Minecraft/McVersion/VersionTests.cs | 73 +++- 5 files changed, 585 insertions(+), 288 deletions(-) create mode 100644 PCL2.Neo/Models/Minecraft/McVersion/VersionFileParser.cs diff --git a/PCL2.Neo.sln.DotSettings.user b/PCL2.Neo.sln.DotSettings.user index 696da71..c73ba06 100644 --- a/PCL2.Neo.sln.DotSettings.user +++ b/PCL2.Neo.sln.DotSettings.user @@ -15,23 +15,20 @@ ForceIncluded ForceIncluded ForceIncluded - <SessionState ContinuousTestingMode="0" Name="RebuildJsonTest" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + + <SessionState ContinuousTestingMode="0" IsActive="True" Name="TirmTest" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <TestAncestor> - <TestId>MSTest::41B478FE-8F3D-4611-8C5F-5396FF8DF6AF::net9.0::PCL2.Neo.Models.Minecraft.McVersion.VersionData.Tests.VersionTests.RebuildJsonTest</TestId> - <TestId>MSTest::41B478FE-8F3D-4611-8C5F-5396FF8DF6AF::net9.0::PCL2.Neo.Models.Minecraft.McVersion.VersionData.Tests.VersionTests.LibrariesParserTest</TestId> + <TestId>MSTest::41B478FE-8F3D-4611-8C5F-5396FF8DF6AF::net9.0::PCL2.Neo.Models.Minecraft.McVersion.VersionData.Tests.MethodTest.TirmTest</TestId> + <TestId>MSTest::41B478FE-8F3D-4611-8C5F-5396FF8DF6AF::net9.0::PCL2.Neo.Models.Minecraft.McVersion.VersionData.Tests.VersionTests.ParserTest</TestId> </TestAncestor> </SessionState> - <SessionState ContinuousTestingMode="0" IsActive="True" Name="LibrariesParserTest" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <SessionState ContinuousTestingMode="0" Name="LibrariesParserTest" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <TestAncestor> <TestId>MSTest::41B478FE-8F3D-4611-8C5F-5396FF8DF6AF::net9.0::PCL2.Neo.Models.Minecraft.McVersion.VersionData.Tests.VersionTests.LibrariesParserTest</TestId> <TestId>MSTest::41B478FE-8F3D-4611-8C5F-5396FF8DF6AF::net9.0::PCL2.Neo.Models.Minecraft.McVersion.VersionData.Tests.VersionTests.RebuildJsonTest</TestId> <TestId>MSTest::41B478FE-8F3D-4611-8C5F-5396FF8DF6AF::net9.0::PCL2.Neo.Models.Minecraft.McVersion.VersionData.Tests.VersionTests.ParserTest</TestId> </TestAncestor> </SessionState> - <SessionState ContinuousTestingMode="0" Name="RebuildJsonTest #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <TestAncestor> - <TestId>MSTest::41B478FE-8F3D-4611-8C5F-5396FF8DF6AF::net9.0::PCL2.Neo.Models.Minecraft.McVersion.VersionData.Tests.VersionTests.RebuildJsonTest</TestId> - </TestAncestor> -</SessionState> + <data><HostParameters type="LocalHostParameters" /><Argument type="StandaloneArgument"><Arguments IsNull="False"></Arguments><FileName IsNull="False"></FileName><WorkingDirectory IsNull="False"></WorkingDirectory><Scope><ProcessFilters /></Scope></Argument><Info type="TimelineInfo"><LoadPdb>True</LoadPdb></Info><CoreOptions type="CoreOptions"><CoreTempPath IsNull="False"></CoreTempPath><RemoteEndPoint IsNull="False"></RemoteEndPoint><AdditionalEnvironmentVariables><Item><Key>DOTNET_ENVIRONMENT</Key><Value>Development</Value></Item><Item><Key>COMPLUS_InstallRoot</Key><Value IsNull="False"></Value></Item><Item><Key>RESHARPER_HOST_VERSION</Key><Value>243.0.20241111.161023</Value></Item><Item><Key>COMPLUS_Version</Key><Value IsNull="False"></Value></Item><Item><Key>RESHARPER_TESTRUNNER</Key><Value>Profile</Value></Item><Item><Key>RESHARPER_HOST</Key><Value>ReSharperPlatformVs17</Value></Item></AdditionalEnvironmentVariables></CoreOptions><HostOptions type="HostOptions"><HostTempPath IsNull="False"></HostTempPath><ReprofileDisableReason>LaunchedByReSharperUnitTestRunner</ReprofileDisableReason></HostOptions></data> \ No newline at end of file diff --git a/PCL2.Neo/.editorconfig b/PCL2.Neo/.editorconfig index ba5847a..759c875 100644 --- a/PCL2.Neo/.editorconfig +++ b/PCL2.Neo/.editorconfig @@ -27,7 +27,7 @@ dotnet_search_reference_assemblies = true # 组织 Using dotnet_separate_import_directive_groups = false dotnet_sort_system_directives_first = false -file_header_template = unset +file_header_template = # this. 和 Me. 首选项 dotnet_style_qualification_for_event = false @@ -219,28 +219,33 @@ dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case dotnet_naming_symbols.interface.applicable_kinds = interface dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.interface.required_modifiers = +dotnet_naming_symbols.interface.required_modifiers = dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.types.required_modifiers = +dotnet_naming_symbols.types.required_modifiers = dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.non_field_members.required_modifiers = +dotnet_naming_symbols.non_field_members.required_modifiers = # 命名样式 -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = dotnet_naming_style.pascal_case.capitalization = pascal_case dotnet_naming_style.begins_with_i.required_prefix = I -dotnet_naming_style.begins_with_i.required_suffix = -dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = dotnet_naming_style.begins_with_i.capitalization = pascal_case +# ReSharper properties +resharper_int_align_fields = true +resharper_int_align_variables = true +resharper_place_accessorholder_attribute_on_same_line = false + [*.{cs,vb}] end_of_line = crlf dotnet_style_qualification_for_field = false:silent diff --git a/PCL2.Neo/Models/Minecraft/McVersion/VersionData.cs b/PCL2.Neo/Models/Minecraft/McVersion/VersionData.cs index 9154e90..05f8431 100644 --- a/PCL2.Neo/Models/Minecraft/McVersion/VersionData.cs +++ b/PCL2.Neo/Models/Minecraft/McVersion/VersionData.cs @@ -1,11 +1,5 @@ -using System; using System.Collections.Generic; -using System.Data; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Text.Json; -using System.Text.RegularExpressions; +using System.Text.Json.Serialization; #pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 @@ -69,9 +63,9 @@ public record OsElements public record JvmRulesElements { - public string? FirstAction { get; init; } - public string Action { get; init; } - public List Os { get; init; } + public string? FirstAction { get; set; } + public string Action { get; set; } + public List Os { get; set; } } public record JvmRulesList @@ -92,11 +86,11 @@ public record Arguments public Jvm Jvm { get; init; } } - public record AssestIndex + public record AssetIndex { public string Id { get; init; } public string Sha1 { get; init; } - public string Size { get; init; } + public int Size { get; init; } public int TotalSize { get; init; } public string Url { get; init; } } @@ -111,8 +105,13 @@ public record DownloadsEneemltValue public record Downloads { public DownloadsEneemltValue Client { get; init; } + + [JsonPropertyName("client_mappings")] public DownloadsEneemltValue ClientMappings { get; init; } + public DownloadsEneemltValue Server { get; init; } + + [JsonPropertyName("server_mappings")] public DownloadsEneemltValue ServerMappings { get; init; } } @@ -124,25 +123,45 @@ public record JavaVersion public record Artifact { + [JsonPropertyName("path")] public string Path { get; init; } + + [JsonPropertyName("sha1")] public string Sha1 { get; init; } + + [JsonPropertyName("size")] public int Size { get; init; } + + [JsonPropertyName("url")] public string Url { get; init; } } public record LibrariesDownloads { - public Artifact Artifact { get; set; } + public Artifact? Artifact { get; set; } public List<(string, Artifact)>? Classifiers { get; set; } public string Name { get; set; } public List<(string, string)>? Natives { get; set; } public JvmRulesElements? Rules { get; set; } + public List Exclude { get; set; } } public record LibrariesNameUrl { + [JsonPropertyName("name")] public string Name { get; set; } - public string Url { get; set; } + + [JsonPropertyName("url")] + public string? Url { get; set; } + + [JsonPropertyName("checksums")] + public List CheckSums { get; set; } + + [JsonPropertyName("serverreq")] + public bool? ServerReq { get; set; } + + [JsonPropertyName("clientreq")] + public bool? ClientReq { get; set; } } public record Libraries @@ -173,274 +192,92 @@ public record Logging public record Temp { - public AssestIndex AssestIndex { get; init; } + [JsonPropertyName("assetIndex")] + public AssetIndex AssetIndex { get; init; } + + [JsonPropertyName("assets")] public string Assets { get; init; } + + [JsonPropertyName("complianceLevel")] public byte ComplianceLevel { get; init; } + + [JsonPropertyName("downloads")] public Downloads Downloads { get; init; } + + [JsonPropertyName("id")] public string Id { get; init; } + + [JsonPropertyName("javaVersion")] public JavaVersion JavaVersion { get; init; } + + [JsonPropertyName("logging")] public Logging Logging { get; init; } + + [JsonPropertyName("mainClass")] public string MainClass { get; init; } + + [JsonPropertyName("minimumLauncherVersion")] public byte MinimunLauncherVersion { get; init; } + + [JsonPropertyName("releaseTime")] public string ReleaseTime { get; init; } + + [JsonPropertyName("time")] public string Time { get; init; } + + [JsonPropertyName("type")] public string Type { get; init; } + + [JsonPropertyName("clientVersion")] + public string? ClientVersion { get; init; } } public record VersionData { - public Arguments Arguments { get; init; } - public AssestIndex AssestIndex { get; init; } - public string Assets { get; init; } - public byte ComplianceLevel { get; init; } - public Downloads Downloads { get; init; } - public string Id { get; init; } - public JavaVersion JavaVersion { get; init; } - public Libraries Libraries { get; init; } - public Logging Logging { get; init; } - public string MainClass { get; init; } - public byte MinimunLauncherVersion { get; init; } - public string ReleaseTime { get; init; } - public string Time { get; init; } - public string Type { get; init; } - } + [JsonPropertyName("arguments")] + public Arguments? Arguments { get; set; } - public partial class Version - { - private static GameRulesList GameRulesListParser(JsonElement element) - { - var rulesList = new GameRulesList { Rules = [], Value = [] }; - - var rules = element.GetProperty("rules").EnumerateArray().ElementAt(0); - //var values = element.GetProperty("value").EnumerateArray(); - var values = element.GetProperty("value"); - - var action = rules.GetProperty("action").GetString()!; - var features = rules.GetProperty("features"); - - var ruleElement = new GameRulesElements - { - Action = action, - RulesFeature = new RulesFeaturesElements - { - Name = RulesNameRegex().Match(features.GetRawText()).Groups[1].Value, - Value = features.GetRawText().Contains("true") - } - }; - - // add values - switch (values.ValueKind) - { - case JsonValueKind.Array: - { - foreach (var item in values.EnumerateArray()) rulesList.Value.Add(item.GetString()!); - break; - } - case JsonValueKind.String: - rulesList.Value.Add(values.GetString()!); - break; - default: - throw new JsonException("Unknown JsonValueKind"); - } - - rulesList.Rules.Add(ruleElement); - - return rulesList; - } - - private static Game GameParser(JsonElement element) - { - var enummerable = element.EnumerateArray(); - var game = new Game { GameRules = [], Value = [] }; - - foreach (var item in enummerable) - { - switch (item.ValueKind) - { - case JsonValueKind.String: - game.Value.Add(item.GetString()!); - break; - case JsonValueKind.Object: - game.GameRules.Add(GameRulesListParser(item)); - break; - default: - throw new JsonException("Unknown JsonValueKind"); - } - } - - return game; - } - - private static JvmRulesElements JvmRulesElementsParser(JsonElement element) - { - var os = element.GetProperty("os").GetRawText(); - - return new JvmRulesElements - { - Action = element.GetProperty("action").GetString()!, - Os = - [ - new OsElements() - { - Name = RulesNameRegex().Match(os).Groups[1].Value, - Value = JvmOsValueRegex().Match(os).Groups[1].Value - } - ] - }; - } - - private static JvmRulesList JvmRulesListParser(JsonElement element) - { - var result = new JvmRulesList { Rules = [], Value = [] }; - - var rules = element.GetProperty("rules").EnumerateArray().ElementAt(0); - var values = element.GetProperty("value"); - - result.Rules.Add(JvmRulesElementsParser(rules)); - - switch (values.ValueKind) - { - case JsonValueKind.Array: - { - var valArray = values.EnumerateArray(); - foreach (var item in valArray) result.Value.Add(item.GetString()!); - break; - } - case JsonValueKind.String: - result.Value.Add(values.GetString()!); - break; - default: - throw new JsonException("Unknown JsonValueKind"); - } - - return result; - } - - private static Jvm JvmParser(JsonElement element) - { - var enummerable = element.EnumerateArray(); - var jvm = new Jvm { Rules = [], Value = [] }; - - foreach (var item in enummerable) - { - switch (item.ValueKind) - { - case JsonValueKind.Object: - jvm.Rules.Add(JvmRulesListParser(item)); - break; - case JsonValueKind.String: - jvm.Value.Add(item.GetString()!); - break; - default: - throw new JsonException("Unknown JsonValueKind"); - } - } - - return jvm; - } - - private static Arguments ArgumentsParser(JsonElement element) - { - var game = GameParser(element.GetProperty("game")); - var jvm = JvmParser(element.GetProperty("jvm")); - return new Arguments { Game = game, Jvm = jvm }; - } - - private static LibrariesDownloads DowloadParser(JsonElement element) - { - var artifact = element.GetProperty("downloads").GetProperty("artifact").GetRawText(); - var name = element.GetProperty("name").GetString()!; - - if (element.TryGetProperty("rules", out var rules)) - { - //return new LibrariesDownloads - //{ - // Artifact = JsonSerializer.Deserialize(artifact)!, - // Name = name, - // Rules = JvmRulesElementsParser(rules.EnumerateArray().ElementAt(0)) - //}; - // TODO: add code - } - else - { - //return new LibrariesDownloads - //{ - // Artifact = JsonSerializer.Deserialize(artifact)!, Name = name, Rules = null - //}; - // TODO: add code - } - } - - private static Libraries LibrariesParser(JsonElement element) - { - var array = element.EnumerateArray(); - var result = new Libraries { Downloads = [], NameUrls = [] }; - - foreach (var item in array) - { - if (item.TryGetProperty("downloads", out _)) - { - result.Downloads.Add(DowloadParser(item)); - } - else - { - result.NameUrls.Add(JsonSerializer.Deserialize(item.GetRawText())!); - } - } - - return result; - } - - private static Temp RebuildJson(JsonElement element) - { - var root = element.EnumerateObject(); - var result = new StringBuilder("{"); - foreach (var pro in root.Where(pro => pro.Name != "arguments" && pro.Name != "libraries")) - { - result.Append($"{pro},"); - } - - result.Remove(result.Length - 1, 1).Append("}"); - - var str = result.ToString(); - //Console.WriteLine(str); - - return JsonSerializer.Deserialize(str)!; - } - - public static VersionData Parser(string input) - { - var root = JsonDocument.Parse(input).RootElement; - var arguments = root.GetProperty("arguments"); - - var argumentsJson = ArgumentsParser(arguments); - var rebuild = RebuildJson(root); - var libraries = LibrariesParser(root.GetProperty("libraries")); - - return new VersionData - { - Arguments = argumentsJson, - AssestIndex = rebuild.AssestIndex, - Assets = rebuild.Assets, - ComplianceLevel = rebuild.ComplianceLevel, - Downloads = rebuild.Downloads, - Id = rebuild.Id, - JavaVersion = rebuild.JavaVersion, - Libraries = libraries, - Logging = rebuild.Logging, - MainClass = rebuild.MainClass, - MinimunLauncherVersion = rebuild.MinimunLauncherVersion, - ReleaseTime = rebuild.ReleaseTime, - Time = rebuild.Time, - Type = rebuild.Type - }; - } - - [GeneratedRegex(@"""([^""]+)""\s*:\s*true")] - private static partial Regex RulesNameRegex(); - - [GeneratedRegex(@"(?<=""name"":\s*)""([^""]+)""")] - private static partial Regex JvmOsValueRegex(); + [JsonPropertyName("assetIndex")] + public AssetIndex AssetIndex { get; set; } + + [JsonPropertyName("assets")] + public string Assets { get; set; } + + [JsonPropertyName("complianceLevel")] + public byte ComplianceLevel { get; set; } + + [JsonPropertyName("downloads")] + public Downloads Downloads { get; set; } + + [JsonPropertyName("id")] + public string Id { get; set; } + + [JsonPropertyName("javaVersion")] + public JavaVersion JavaVersion { get; set; } + + [JsonPropertyName("libraries")] + public Libraries Libraries { get; set; } + + [JsonPropertyName("logging")] + public Logging Logging { get; set; } + + [JsonPropertyName("mainClass")] + public string MainClass { get; set; } + + [JsonPropertyName("minimumLauncherVersion")] + public byte MinimunLauncherVersion { get; set; } + + [JsonPropertyName("releaseTime")] + public string ReleaseTime { get; set; } + + [JsonPropertyName("time")] + public string Time { get; set; } + + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("clientVersion")] + public string? ClientVersion { get; set; } } } } diff --git a/PCL2.Neo/Models/Minecraft/McVersion/VersionFileParser.cs b/PCL2.Neo/Models/Minecraft/McVersion/VersionFileParser.cs new file mode 100644 index 0000000..f25a3a3 --- /dev/null +++ b/PCL2.Neo/Models/Minecraft/McVersion/VersionFileParser.cs @@ -0,0 +1,393 @@ +using PCL2.Neo.Models.Minecraft.McVersion.VersionData; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Text.RegularExpressions; + +namespace PCL2.Neo.Models.Minecraft.McVersion +{ + public partial class VersionFileParser + { + private static GameRulesList GameRulesListParser(JsonElement element) + { + var rulesList = new GameRulesList { Rules = [], Value = [] }; + + var rules = element.GetProperty("rules").EnumerateArray().ElementAt(0); + //var values = element.GetProperty("value").EnumerateArray(); + var values = element.GetProperty("value"); + + var action = rules.GetProperty("action").GetString()!; + var features = rules.GetProperty("features"); + + var ruleElement = new GameRulesElements + { + Action = action, + RulesFeature = new RulesFeaturesElements + { + Name = RulesNameRegex().Match(features.GetRawText()).Groups[1].Value, + Value = features.GetRawText().Contains("true") + } + }; + + // add values + switch (values.ValueKind) + { + case JsonValueKind.Array: + { + foreach (var item in values.EnumerateArray()) rulesList.Value.Add(item.GetString()!); + break; + } + case JsonValueKind.String: + rulesList.Value.Add(values.GetString()!); + break; + default: + throw new JsonException("Unknown JsonValueKind"); + } + + rulesList.Rules.Add(ruleElement); + + return rulesList; + } + + private static Game GameParser(JsonElement element) + { + var enummerable = element.EnumerateArray(); + var game = new Game { GameRules = [], Value = [] }; + + foreach (var item in enummerable) + { + switch (item.ValueKind) + { + case JsonValueKind.String: + game.Value.Add(item.GetString()!); + break; + case JsonValueKind.Object: + game.GameRules.Add(GameRulesListParser(item)); + break; + default: + throw new JsonException("Unknown JsonValueKind"); + } + } + + return game; + } + + private static JvmRulesElements JvmRulesElementsParser(JsonElement element) + { + var os = element.GetProperty("os").GetRawText(); + + return new JvmRulesElements + { + Action = element.GetProperty("action").GetString()!, + Os = + [ + new OsElements() + { + Name = RulesNameRegex().Match(os).Groups[1].Value, + Value = JvmOsValueRegex().Match(os).Groups[1].Value + } + ] + }; + } + + private static JvmRulesList JvmRulesListParser(JsonElement element) + { + var result = new JvmRulesList { Rules = [], Value = [] }; + + var rules = element.GetProperty("rules").EnumerateArray().ElementAt(0); + var values = element.GetProperty("value"); + + result.Rules.Add(JvmRulesElementsParser(rules)); + + switch (values.ValueKind) + { + case JsonValueKind.Array: + { + var valArray = values.EnumerateArray(); + foreach (var item in valArray) result.Value.Add(item.GetString()!); + break; + } + case JsonValueKind.String: + result.Value.Add(values.GetString()!); + break; + default: + throw new JsonException("Unknown JsonValueKind"); + } + + return result; + } + + private static Jvm JvmParser(JsonElement element) + { + var enummerable = element.EnumerateArray(); + var jvm = new Jvm { Rules = [], Value = [] }; + + foreach (var item in enummerable) + { + switch (item.ValueKind) + { + case JsonValueKind.Object: + jvm.Rules.Add(JvmRulesListParser(item)); + break; + case JsonValueKind.String: + jvm.Value.Add(item.GetString()!); + break; + default: + throw new JsonException("Unknown JsonValueKind"); + } + } + + return jvm; + } + + private static Arguments ArgumentsParser(JsonElement element) + { + var game = GameParser(element.GetProperty("game")); + var jvm = JvmParser(element.GetProperty("jvm")); + return new Arguments { Game = game, Jvm = jvm }; + } + + private static JvmRulesElements ActionParser(JsonElement element) + { + var result = new JvmRulesElements { Os = [], FirstAction = null }; + foreach (var item in element.EnumerateArray()) + { + if (item.TryGetProperty("os", out _)) + { + var action = JvmRulesElementsParser(item); + result.Action = action.Action; + result.Os.Add(action.Os[0]); + } + else + { + var action = item.GetProperty("action").GetString(); + result.FirstAction = action; + } + } + + return result; + } + + private static (string, string) GetNameValueTuple(string input) + { + var match = GetNameValueRegex().Match(input); + return (match.Groups[1].Value, match.Groups[2].Value); + } + + private static (string, Artifact) GetClassifiersValueTuple(string element) + { + var match = GetDoubleQuateValue().Match(element); + var content = ClassifiersContentRegex().Match(element).Value; + return (match.Groups[1].Value, JsonSerializer.Deserialize(content, + SerializerOptions)!); + } + + private static readonly JsonSerializerOptions SerializerOptions = + new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + + private static List GetNativesList(JsonElement element) + { + var str = element.GetRawText().Trim(['{', '}']).Split(','); + return str.Select(s => s.Trim()).ToList(); + } + + private static List GetCLassifiersList(JsonElement element) + { + string input = element.GetRawText(); + + // 去除首尾的大括号 + string trimmedInput = input.Trim(); + trimmedInput = trimmedInput.Substring(1, trimmedInput.Length - 2).Trim(); + + // 解析字符串,保留第二层大括号 + List resultList = []; + int braceCount = 0; + int startIndex = 0; + + for (int i = 0; i < trimmedInput.Length; i++) + { + switch (trimmedInput[i]) + { + case '{': + braceCount++; + break; + case '}': + braceCount--; + break; + case ',' when braceCount == 0: + { + // 当 braceCount 为 0 时,表示当前逗号是第一层的分隔符 + string item = trimmedInput.Substring(startIndex, i - startIndex).Trim(); + resultList.Add(item); + startIndex = i + 1; + break; + } + } + } + + // 添加最后一个元素 + if (startIndex < trimmedInput.Length) + { + string lastItem = trimmedInput.Substring(startIndex).Trim(); + resultList.Add(lastItem); + } + + return resultList; + } + + private static LibrariesDownloads DowloadParser(JsonElement element) + { + var downloads = element.GetProperty("downloads"); + var name = element.GetProperty("name").GetString()!; + + var result = new LibrariesDownloads + { + Classifiers = [], + Natives = [], + Rules = null, + Artifact = null, + Exclude = [] + }; + + // try get artifact + if (downloads.TryGetProperty("artifact", out var artifact)) + { + var artifactContent = artifact.GetRawText(); + result.Artifact = JsonSerializer.Deserialize(artifactContent, SerializerOptions)!; + } + + // try get rules + if (element.TryGetProperty("rules", out var rules)) + { + result.Rules = ActionParser(rules); + } + + // try get natives + if (element.TryGetProperty("natives", out var natives)) + { + foreach (var item in GetNativesList(element)) + { + result.Natives.Add(GetNameValueTuple(item)); + } + } + + // try get classifiers + if (downloads.TryGetProperty("classifiers", out JsonElement value)) + { + foreach (var item in GetCLassifiersList(value)) + { + result.Classifiers.Add(GetClassifiersValueTuple(item)); + } + } + + // try get exclude + if (element.TryGetProperty("extract", out var extract)) + { + var exclude = extract.GetProperty("exclude").EnumerateArray(); + foreach (var item in exclude) + { + result.Exclude.Add(item.GetString()!); + } + } + + // set name + result.Name = name; + + return result; + } + + private static Libraries LibrariesParser(JsonElement element) + { + var array = element.EnumerateArray(); + var result = new Libraries { Downloads = [], NameUrls = [] }; + + foreach (var item in array) + { + if (item.TryGetProperty("downloads", out _)) + { + result.Downloads.Add(DowloadParser(item)); + } + else + { + result.NameUrls.Add( + JsonSerializer.Deserialize(item.GetRawText(), SerializerOptions)!); + } + } + + return result; + } + + private static Temp RebuildJson(JsonElement element) + { + var root = element.EnumerateObject(); + var result = new StringBuilder("{"); + foreach (var pro in root.Where(pro => pro.Name != "arguments" && pro.Name != "libraries")) + { + result.Append($"{pro},"); + } + + result.Remove(result.Length - 1, 1).Append("}"); + + var str = result.ToString(); + //Console.WriteLine(str); + + return JsonSerializer.Deserialize(str, SerializerOptions)!; + } + + public static VersionData.VersionData Parser(string input) + { + var result = new VersionData.VersionData(); + + var root = JsonDocument.Parse(input).RootElement; + + //var arguments = root.GetProperty("arguments") + // try get arguments + if (root.TryGetProperty("arguments", out var arguments)) + { + var argumentsJson = ArgumentsParser(arguments); + result.Arguments = argumentsJson; + } + else + { + result.Arguments = null; + } + + var rebuild = RebuildJson(root); + var libraries = LibrariesParser(root.GetProperty("libraries")); + + return result with + { + AssetIndex = rebuild.AssetIndex, + Assets = rebuild.Assets, + ComplianceLevel = rebuild.ComplianceLevel, + Downloads = rebuild.Downloads, + Id = rebuild.Id, + JavaVersion = rebuild.JavaVersion, + Libraries = libraries, + Logging = rebuild.Logging, + MainClass = rebuild.MainClass, + MinimunLauncherVersion = rebuild.MinimunLauncherVersion, + ReleaseTime = rebuild.ReleaseTime, + Time = rebuild.Time, + Type = rebuild.Type, + ClientVersion = rebuild.ClientVersion + }; + } + + [GeneratedRegex("\"([^\"]+)\"")] + private static partial Regex RulesNameRegex(); + + [GeneratedRegex(@"(?<=""name"":\s*)""([^""]+)""")] + private static partial Regex JvmOsValueRegex(); + + [GeneratedRegex("\"(.*?)\":\\s*\"(.*?)\"")] + private static partial Regex GetNameValueRegex(); + + [GeneratedRegex("\"([^\"]+)\":\\s*\\{")] + private static partial Regex GetDoubleQuateValue(); + + [GeneratedRegex(@"\{[^{}]*\}")] + private static partial Regex ClassifiersContentRegex(); + } +} diff --git a/PCL2.NeoTests/Models/Minecraft/McVersion/VersionTests.cs b/PCL2.NeoTests/Models/Minecraft/McVersion/VersionTests.cs index cdd83de..9296963 100644 --- a/PCL2.NeoTests/Models/Minecraft/McVersion/VersionTests.cs +++ b/PCL2.NeoTests/Models/Minecraft/McVersion/VersionTests.cs @@ -7,13 +7,78 @@ namespace PCL2.Neo.Models.Minecraft.McVersion.VersionData.Tests { [TestClass()] public class VersionTests + { + } + + [TestClass()] + public class MethodTest { [TestMethod()] - public void ParserTest() + public void TirmTest() + { + string input = @" { - var fileInfo = System.IO.File.ReadAllText( - @"C:\Users\WhiteCAT\Desktop\Games\PCL2\.minecraft\versions\1.19.2-Fabric 0.14.10\1.19.2-Fabric 0.14.10.json"); - Console.WriteLine(Version.Parser(fileInfo)); + ""natives-linux"": { + ""path"": ""org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-linux.jar"", + ""sha1"": ""d898a33b5d0a6ef3fed3a4ead506566dce6720a5"", + ""size"": 578539, + ""url"": ""https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-linux.jar"" + }, + ""natives-osx"": { + ""path"": ""org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-osx.jar"", + ""sha1"": ""79f5ce2fea02e77fe47a3c745219167a542121d7"", + ""size"": 468116, + ""url"": ""https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-osx.jar"" + }, + ""natives-windows"": { + ""path"": ""org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-windows.jar"", + ""sha1"": ""78b2a55ce4dc29c6b3ec4df8ca165eba05f9b341"", + ""size"": 613680, + ""url"": ""https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-windows.jar"" + } + }"; + + // 去除首尾的大括号 + string trimmedInput = input.Trim(); + trimmedInput = trimmedInput.Substring(1, trimmedInput.Length - 2).Trim(); + + // 解析字符串,保留第二层大括号 + List resultList = new List(); + int braceCount = 0; + int startIndex = 0; + + for (int i = 0; i < trimmedInput.Length; i++) + { + if (trimmedInput[i] == '{') + { + braceCount++; + } + else if (trimmedInput[i] == '}') + { + braceCount--; + } + else if (trimmedInput[i] == ',' && braceCount == 0) + { + // 当 braceCount 为 0 时,表示当前逗号是第一层的分隔符 + string item = trimmedInput.Substring(startIndex, i - startIndex).Trim(); + resultList.Add(item); + startIndex = i + 1; + } + } + + // 添加最后一个元素 + if (startIndex < trimmedInput.Length) + { + string lastItem = trimmedInput.Substring(startIndex).Trim(); + resultList.Add(lastItem); + } + + // 输出结果 + foreach (var item in resultList) + { + Console.WriteLine(item); + Console.WriteLine(); // 分隔每个结果 + } } } } \ No newline at end of file