diff --git a/PCL.Core/Minecraft/Exceptions/ForgeInstallerException.cs b/PCL.Core/Minecraft/Exceptions/ForgeInstallerException.cs new file mode 100644 index 000000000..79b9b628d --- /dev/null +++ b/PCL.Core/Minecraft/Exceptions/ForgeInstallerException.cs @@ -0,0 +1,30 @@ +using System; +using System.Text; +using System.Collections.Generic; + +namespace PCL.Core.Minecraft.Exceptions; + +public class ForgeInstallerException : Exception +{ + public IEnumerable? Logs { get; init; } + + public ForgeInstallerException(){} + + public ForgeInstallerException(string message) : base(message){} + + public ForgeInstallerException(string message,Exception? inner):base(message,inner){} + + public ForgeInstallerException(IEnumerable logs):this("An error throws when execute install.") + { + Logs = logs; + } + + public override string ToString() + { + var details = base.ToString(); + var builder = new StringBuilder(details); + builder.Append($"{Environment.NewLine}--- Installer Logs --- {Environment.NewLine}Output:"); + builder.AppendJoin(Environment.NewLine, Logs ?? ["Nothing"]); + return builder.ToString(); + } +} \ No newline at end of file diff --git a/PCL.Core/Minecraft/Instance/Utils/ForgeInstaller.cs b/PCL.Core/Minecraft/Instance/Utils/ForgeInstaller.cs new file mode 100644 index 000000000..e544de97c --- /dev/null +++ b/PCL.Core/Minecraft/Instance/Utils/ForgeInstaller.cs @@ -0,0 +1,141 @@ +using System; +using System.Threading.Tasks; +using System.IO; +using System.Text.Json.Nodes; +using ICSharpCode.SharpZipLib.Zip; +using PCL.Core.Logging; +using PCL.Core.Minecraft.Exceptions; +using PCL.Core.IO; +using PCL.Core.Utils.Exts; +using System.Collections.Generic; + +namespace PCL.Core.Minecraft.Instance.Utils; + +public class ForgeInstaller +{ + public required string JarPath; + public required string JavaPath; + public required Version ForgeVersion; + + public required string InstancePath; + + public required string MinecraftBaseFolder; + + private ZipFile? _zipFile; + private JsonNode? _installerProfile; + + private async Task _GetInstallerProfileAsync() + { + if(_installerProfile is not null) return _installerProfile; + if(_zipFile is null) _zipFile = new ZipFile(File.OpenRead(JarPath)); + var profileEntry = _zipFile.GetEntry("installer_profile.json"); + if(profileEntry is null) throw new ForgeInstallerException("找不到安装配置文件"); + _installerProfile = await JsonNode.ParseAsync(_zipFile.GetInputStream(profileEntry)); + return _installerProfile; + } + + private ForgeInstallImplVersion _SelectImplVersion() + { + if(ForgeVersion.Major > 20) return ForgeInstallImplVersion.Latest; + if(_GetInstallerProfileAsync().GetAwaiter().GetResult()?["install"] is not null) + return ForgeInstallImplVersion.Version1; + return ForgeInstallImplVersion.Version2; + } + + /// + /// 开始 Forge 安装(自动选择安装方式) + /// + public async Task StartInstallAsync() + { + var installerProfile = await _GetInstallerProfileAsync(); + if (installerProfile is null) throw new ForgeInstallerException("未找到安装配置文件"); + await StartInstallAsync(_SelectImplVersion()); + } + /// + /// 用于指定 Forge 安装实现的重载方法(仅用于自动检测无法确定合适的安装方法时使用) + /// + /// 实现版本 + public async Task StartInstallAsync(ForgeInstallImplVersion version) + { + switch(version){ + case ForgeInstallImplVersion.Version1: + LogWrapper.Debug("Installer","选择的实现版本:Legacy"); + await _OldInstallMethod1Async(); + break; + case ForgeInstallImplVersion.Version2: + LogWrapper.Debug("Installer","选择的实现版本:Middle"); + await _OldInstallMethod2Async(); + break; + default: + LogWrapper.Debug("Installer","选择的实现版本:Latest"); + await _NewInstallAsync(); + break; + } + } + + #region "旧版本 Forge 安装实现(版本 1)" + + private async Task _OldInstallMethod1Async() + { + + } + + #endregion + + #region "旧版本 Forge 安装实现(版本 2)" + + private async Task _OldInstallMethod2Async() + { + var libraryPath = Path.Combine(MinecraftBaseFolder,"libraries"); + var profile = await _GetInstallerProfileAsync(); + var jsonPath = profile?["json"]?.ToString().TrimStart('/'); + using var jsonEntryStream = _zipFile!.GetInputStream(_zipFile.GetEntry(jsonPath)); + using var reader = new StreamReader(jsonEntryStream); + var json = JsonNode.Parse(await reader.ReadToEndAsync()); + var instanceName = Path.GetDirectoryName(InstancePath); + json?["id"] = instanceName; + File.WriteAllText(Path.Combine(InstancePath,$"{instanceName}.json"),json?.ToJsonString()); + var libraryCount = 0; + foreach(ZipEntry entry in _zipFile) + { + if(entry.Name.StartsWith("/maven")) + { + var targetPath = Path.Combine(libraryPath, entry.Name); + if(entry.IsDirectory) { + Directory.CreateDirectory(targetPath); + continue; + } + libraryCount++; + using var fs = File.OpenWrite(targetPath); + using var zipStream = _zipFile.GetInputStream(entry); + var task = zipStream?.CopyToAsync(fs) ?? throw new NullReferenceException(); + await task; + } + } + LogWrapper.Debug("Installer",$"已解压 {libraryCount} 个文件。"); + } + + #endregion + + #region "新版本 Forge 安装实现" + + private List _CollectProcessor() + { + return []; + } + + private async Task _NewInstallAsync() + { + + } + + #endregion + +} + +public enum ForgeInstallImplVersion +{ + Version1, + Version2, + Latest +} \ No newline at end of file