diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..013007bb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "dotnet.preferCSharpExtension": true +} \ No newline at end of file diff --git a/PCL.Core.csproj b/PCL.Core.csproj index b130a4e5..6b49118b 100644 --- a/PCL.Core.csproj +++ b/PCL.Core.csproj @@ -171,6 +171,10 @@ + + + + diff --git a/PCL.Core.sln b/PCL.Core.sln new file mode 100644 index 00000000..16a96477 --- /dev/null +++ b/PCL.Core.sln @@ -0,0 +1,24 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCL.Core", "PCL.Core.csproj", "{F46883D6-36B8-D972-8661-D0CA2DB6695B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F46883D6-36B8-D972-8661-D0CA2DB6695B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F46883D6-36B8-D972-8661-D0CA2DB6695B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F46883D6-36B8-D972-8661-D0CA2DB6695B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F46883D6-36B8-D972-8661-D0CA2DB6695B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E1C68EDF-2735-4241-A0AE-513B3C1AC497} + EndGlobalSection +EndGlobal diff --git a/Utils/Mod/CurseForge.cs b/Utils/Mod/CurseForge.cs new file mode 100644 index 00000000..a04de201 --- /dev/null +++ b/Utils/Mod/CurseForge.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Threading.Tasks; + +namespace PCL.Core.Utils.Mod; + +public class CurseForgeMod +{ + private static string CurseForgeBaseAPI = "https://api.curseforge.com/v1"; + + public static async Task GetModInfomationByHash(List modHash) + { + HttpResponseMessage result = await Network.GetResponse( + CurseForgeBaseAPI + "/fingerprints/432", + HttpMethod.Post, + new Dictionary() + { + ["Content-Type"] = "application/json" + }, + new JsonObject() + { + ["fingerprints"] = JsonSerializer.Serialize(modHash) + }.ToJsonString()); + + + + return JsonNode.Parse(await result.Content.ReadAsStringAsync()); + } + +} \ No newline at end of file diff --git a/Utils/Modpack/CurseForge.cs b/Utils/Modpack/CurseForge.cs new file mode 100644 index 00000000..acb51e1b --- /dev/null +++ b/Utils/Modpack/CurseForge.cs @@ -0,0 +1,152 @@ + + +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Threading.Tasks; +using PCL.Core.Utils.Mod; + +namespace PCL.Core.Utils.Modpack; + + +public class CurseForge +{ + public string? Author; + public string? Name; + public string? Version; + public string? InputFolder; + public string? OutputFile; + public List Modloaders = new(); + private List ModFiles = new(); + private JsonNode? Manifest; + private List AddToFile = new(); + private dynamic? Entry; + private void OnFinsh() + { + Task.Run(() => + { + this.Export(); + }); + } + + private void Start() + { + List tasks = [ + this.PrepareMods(), + this.PrepareOverride() + ]; + Task.WaitAll(tasks.ToArray()); + this.PrepareJson().GetAwaiter().GetResult(); + this.OnFinsh(); + } + + private void Export() + { + if(this.OutputFile is null) throw new ArgumentNullException("参数无效:OutputFile"); + using (FileStream fs = new(this.OutputFile, FileMode.Create)) + { + using (ZipArchive Zipfile = new(fs)) + { + using(var Entry = Zipfile.CreateEntry("manifest.json").Open()) + { + using (MemoryStream memoryStream = new(this.Manifest!.ToJsonString().GetBytes())) + { + memoryStream.CopyTo(Entry); + } + } + + + + } + } + } + + public async Task PrepareOverride() + { + foreach (var (file,Entry) in this.ModFiles.Zip(this.Entry, (f,e) => (f,e))) + { + if(file.FileId == Entry.FileId) continue; + } + foreach (var file in Directory.GetFiles(this.InputFolder)) + { + + } + return true; + } + public async Task PrepareJson() + { + this.Manifest = new JsonObject() + { + ["minecraft"] = new JsonObject() + { + ["version"] = this.Version, + ["modloaders"] = JsonSerializer.Serialize(this.Modloaders) + }, + ["manifestType"] = "minecraftModpack", + ["overrides"] = "overrides", + ["manifestVersion"] = 1, + ["version"] = this.Version, + ["author"] = this.Author, + ["name"] = this.Name, + ["files"] = JsonSerializer.Serialize(this.ModFiles) + }; + } + public async Task PrepareMods(dynamic? modHashes = null) + { + if (modHashes is null) goto ModCompareFinished; + this.Entry = modHashes; + modHashes["modloaders"] = JsonSerializer.Serialize(this.Modloaders); + string modsFolder = InputFolder?.TrimEnd('/') + "/mods"; + if (Directory.Exists(modsFolder)) + { + List modHash = new(); + JsonNode? result = await CurseForgeMod.GetModInfomationByHash(modHash); + if (result is null) goto ModCompareFinished; + JsonArray? data = result["data"]?["exactMatches"]?.AsObject().AsArray(); + if (data is null) goto ModCompareFinished; + foreach (JsonNode? modInfo in data) + { + try + { + ModFiles.Add(new CfModFile() + { + ProjectId = int.Parse(data["id"]?.ToString()), + FileId = int.Parse(data["file"]?["id"]?.ToString()), + Required = true, + }); + } + catch + { + continue; + } + } + } + ModCompareFinished: + return true; + } +} + + +public class CfPackVersion +{ + public string Version; + public List Modloaders; +} + +public class CfModloaders +{ + public string? Id; + public bool Primary; +} + +public class CfModFile +{ + public int ProjectId; + public int FileId; + public bool Required; +} \ No newline at end of file diff --git a/Utils/Network.cs b/Utils/Network.cs new file mode 100644 index 00000000..3987b758 --- /dev/null +++ b/Utils/Network.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Runtime.Remoting.Messaging; +using System.Threading; +using System.Threading.Tasks; +using PCL.Core.Utils; +namespace PCL.Core.Utils; + +public class Network +{ + private static readonly HttpClientHandler Handler = new() + { + AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip | DecompressionMethods.None + }; + private static readonly HttpClient Client = new(Handler); + + public static async Task GetResponse( + string requestUrl, + HttpMethod method, + Dictionary? headers = null, + object? data = null, + int timeout = 25000 + ) + { + int retry = 3; + retry: + using (HttpRequestMessage request = new(method,requestUrl)) + { + if (data is not null) + request.Content = new ByteArrayContent((data is string)? (data as string).GetBytes():data as byte[]); + if (headers is not null) foreach (var header in headers) + { + if (header.Key.StartsWith("Content", StringComparison.OrdinalIgnoreCase) && request.Content is not null) + { + request.Content.Headers.TryAddWithoutValidation(header.Key, header.Value); + continue; + } + + request.Headers.TryAddWithoutValidation(header.Key, header.Value); + } + + using (CancellationTokenSource cts = new(timeout)) + { + HttpResponseMessage response = + await Client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cts.Token); + try + { + response.EnsureSuccessStatusCode(); + return response; + } + catch (HttpRequestException ex) + { + if (retry > 0) + { + retry--; + goto retry; + } + // 尝试读取远程服务器返回的数据数据 + try + { + if (response.Content.Headers.ContentLength <= 1024 * 1024) + ex.Data["ServerResponse"] = (await response.Content.ReadAsByteArrayAsync()).GetString(); + } + catch + { + + } + throw; + } + } + } + } +} \ No newline at end of file diff --git a/Utils/Override.cs b/Utils/Override.cs new file mode 100644 index 00000000..93c22806 --- /dev/null +++ b/Utils/Override.cs @@ -0,0 +1,14 @@ +namespace PCL.Core.Utils; +using System.Text; + +public static class Override +{ + public static byte[] GetBytes(this string content,Encoding? encode = null) + { + return (encode ?? Encoding.UTF8).GetBytes(content); + } + public static string GetString(this byte[] content,Encoding? encode = null) + { + return (encode ?? Encoding.UTF8).GetString(content); + } +} \ No newline at end of file