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