1725 lines
77 KiB
C#
1725 lines
77 KiB
C#
using System.Diagnostics;
|
||||
|
|
using System.IO;
|
|||
|
|
using System.IO.Compression;
|
|||
|
|
using System.Text;
|
|||
|
|
using System.Text.RegularExpressions;
|
|||
|
|
using PCL.Core.App;
|
|||
|
|
using PCL.Core.App.Localization;
|
|||
|
|
using PCL.Core.UI;
|
|||
|
|
using PCL.Core.Utils.Validate;
|
|||
|
|
using PCL.Network;
|
|||
|
|
using PCL.Network.Loaders;
|
|||
|
|
using static PCL.ModLoader;
|
|||
|
|
using PCL.Core.Utils;
|
|||
|
|
|
|||
|
|
namespace PCL;
|
|||
|
|
|
|||
|
|
public static class ModModpack
|
|||
|
|
{
|
|||
|
|
// 触发整合包安装的外部接口
|
|||
|
|
/// <summary>
|
|||
|
|
/// 弹窗要求选择一个整合包文件并进行安装。
|
|||
|
|
/// </summary>
|
|||
|
|
public static void ModpackInstall()
|
|||
|
|
{
|
|||
|
|
var file = SystemDialogs.SelectFile(Lang.Text("Minecraft.Download.Modpack.FileDialog.Filter"),
|
|||
|
|
Lang.Text("Minecraft.Download.Modpack.FileDialog.Title")); // 选择整合包文件
|
|||
|
|
if (string.IsNullOrEmpty(file))
|
|||
|
|
return;
|
|||
|
|
ModBase.RunInThread(() =>
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
ModpackInstall(file);
|
|||
|
|
}
|
|||
|
|
catch (ModBase.CancelledException ex)
|
|||
|
|
{
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
ModBase.Log(
|
|||
|
|
ex,
|
|||
|
|
"手动安装整合包失败",
|
|||
|
|
ModBase.LogLevel.Msgbox,
|
|||
|
|
userSummary: Lang.Text("Minecraft.Download.Modpack.Error.OperationFailed"));
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 构建并启动安装给定的整合包文件的加载器,并返回该加载器。若失败则抛出异常。
|
|||
|
|
/// 必须在工作线程执行。
|
|||
|
|
/// </summary>
|
|||
|
|
/// <exception cref="ModBase.CancelledException" />
|
|||
|
|
public static LoaderCombo<string> ModpackInstall(string file, string instanceName = null, string logo = null,
|
|||
|
|
string resourceId = null, bool isOnlineInstall = false)
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[ModPack] 整合包安装请求:" + (file ?? "null"));
|
|||
|
|
ZipArchive archive = null;
|
|||
|
|
var archiveBaseFolder = "";
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 字符校验
|
|||
|
|
var targetFolder = $@"{ModFolder.mcFolderSelected}versions\{instanceName}\";
|
|||
|
|
if (targetFolder.Contains("!") || targetFolder.Contains(";"))
|
|||
|
|
{
|
|||
|
|
HintService.Hint(Lang.Text("Minecraft.Download.Modpack.InvalidGamePathChars", targetFolder),
|
|||
|
|
HintType.Error);
|
|||
|
|
throw new ModBase.CancelledException();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取整合包种类与关键 Json
|
|||
|
|
var packType = -1;
|
|||
|
|
do
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
archive = new ZipArchive(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read));
|
|||
|
|
if (archive.Entries.Any(e => e.IsEncrypted))
|
|||
|
|
throw new Exception(Lang.Text("Minecraft.Download.Modpack.EncryptedArchiveUnsupported"));
|
|||
|
|
// 从根目录判断整合包类型
|
|||
|
|
if (archive.GetEntry("mcbbs.packmeta") is not null)
|
|||
|
|
{
|
|||
|
|
packType = 3;
|
|||
|
|
break;
|
|||
|
|
} // MCBBS 整合包(优先于 manifest.json 判断)
|
|||
|
|
|
|||
|
|
if (archive.GetEntry("mmc-pack.json") is not null)
|
|||
|
|
{
|
|||
|
|
packType = 2;
|
|||
|
|
break;
|
|||
|
|
} // MMC 整合包(优先于 manifest.json 判断,#4194)
|
|||
|
|
|
|||
|
|
if (archive.GetEntry("modrinth.index.json") is not null)
|
|||
|
|
{
|
|||
|
|
packType = 4;
|
|||
|
|
break;
|
|||
|
|
} // Modrinth 整合包
|
|||
|
|
|
|||
|
|
if (archive.GetEntry("manifest.json") is not null)
|
|||
|
|
{
|
|||
|
|
var json = (JsonObject)ModBase.GetJson(ModBase.ReadFile(archive.GetEntry("manifest.json").Open(),
|
|||
|
|
Encoding.UTF8));
|
|||
|
|
if (json["addons"] is null)
|
|||
|
|
{
|
|||
|
|
packType = 0;
|
|||
|
|
break; // CurseForge 整合包
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
packType = 3;
|
|||
|
|
break;
|
|||
|
|
// MCBBS 整合包
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (archive.GetEntry("modpack.json") is not null)
|
|||
|
|
{
|
|||
|
|
packType = 1;
|
|||
|
|
break;
|
|||
|
|
} // HMCL 整合包
|
|||
|
|
|
|||
|
|
if (archive.GetEntry("modpack.zip") is not null || archive.GetEntry("modpack.mrpack") is not null)
|
|||
|
|
{
|
|||
|
|
packType = 9;
|
|||
|
|
break;
|
|||
|
|
} // 带启动器的压缩包
|
|||
|
|
|
|||
|
|
// 从一级目录判断整合包类型
|
|||
|
|
var exitTry = false;
|
|||
|
|
foreach (var Entry in archive.Entries)
|
|||
|
|
{
|
|||
|
|
var fullNames = Entry.FullName.Split("/");
|
|||
|
|
archiveBaseFolder = fullNames[0] + "/";
|
|||
|
|
// 确定为一级目录下
|
|||
|
|
if (fullNames.Count() != 2)
|
|||
|
|
continue;
|
|||
|
|
// 判断是否为关键文件
|
|||
|
|
if (fullNames[1] == "mcbbs.packmeta")
|
|||
|
|
{
|
|||
|
|
packType = 3;
|
|||
|
|
exitTry = true;
|
|||
|
|
break;
|
|||
|
|
} // MCBBS 整合包(优先于 manifest.json 判断)
|
|||
|
|
|
|||
|
|
if (fullNames[1] == "mmc-pack.json")
|
|||
|
|
{
|
|||
|
|
packType = 2;
|
|||
|
|
exitTry = true;
|
|||
|
|
break;
|
|||
|
|
} // MMC 整合包(优先于 manifest.json 判断,#4194)
|
|||
|
|
|
|||
|
|
if (fullNames[1] == "modrinth.index.json")
|
|||
|
|
{
|
|||
|
|
packType = 4;
|
|||
|
|
exitTry = true;
|
|||
|
|
break;
|
|||
|
|
} // Modrinth 整合包
|
|||
|
|
|
|||
|
|
if (fullNames[1] == "manifest.json")
|
|||
|
|
{
|
|||
|
|
var json = (JsonObject)ModBase.GetJson(ModBase.ReadFile(Entry.Open(), Encoding.UTF8));
|
|||
|
|
if (json["addons"] is null)
|
|||
|
|
{
|
|||
|
|
packType = 0;
|
|||
|
|
exitTry = true;
|
|||
|
|
break; // CurseForge 整合包
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
packType = 3;
|
|||
|
|
archiveBaseFolder = "overrides/";
|
|||
|
|
exitTry = true;
|
|||
|
|
break;
|
|||
|
|
// MCBBS 整合包
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (fullNames[1] == "modpack.json")
|
|||
|
|
{
|
|||
|
|
packType = 1;
|
|||
|
|
exitTry = true;
|
|||
|
|
break;
|
|||
|
|
} // HMCL 整合包
|
|||
|
|
|
|||
|
|
if (fullNames[1] == "modpack.zip" || fullNames[1] == "modpack.mrpack")
|
|||
|
|
{
|
|||
|
|
packType = 9;
|
|||
|
|
exitTry = true;
|
|||
|
|
break;
|
|||
|
|
} // 带启动器的压缩包
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (exitTry) break;
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
if (ex.Message.Contains("Error.WinIOError"))
|
|||
|
|
throw new Exception(Lang.Text("Minecraft.Download.Modpack.OpenFailed"), ex);
|
|||
|
|
else if (file.EndsWithF(".rar", true))
|
|||
|
|
throw new Exception(Lang.Text("Minecraft.Download.Modpack.RarUnsupported"), ex);
|
|||
|
|
else
|
|||
|
|
throw new Exception(Lang.Text("Minecraft.Download.Modpack.UnsupportedArchive"), ex);
|
|||
|
|
}
|
|||
|
|
} while (false);
|
|||
|
|
|
|||
|
|
// 执行对应的安装方法
|
|||
|
|
switch (packType)
|
|||
|
|
{
|
|||
|
|
case 0:
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[ModPack] 整合包种类:CurseForge");
|
|||
|
|
return InstallPackCurseForge(file, archive, archiveBaseFolder, instanceName, logo, resourceId,
|
|||
|
|
isOnlineInstall);
|
|||
|
|
}
|
|||
|
|
case 1:
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[ModPack] 整合包种类:HMCL");
|
|||
|
|
return InstallPackHMCL(file, archive, archiveBaseFolder);
|
|||
|
|
}
|
|||
|
|
case 2:
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[ModPack] 整合包种类:MMC");
|
|||
|
|
return InstallPackMMC(file, archive, archiveBaseFolder);
|
|||
|
|
}
|
|||
|
|
case 3:
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[ModPack] 整合包种类:MCBBS");
|
|||
|
|
return InstallPackMCBBS(file, archive, archiveBaseFolder, instanceName);
|
|||
|
|
}
|
|||
|
|
case 4:
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[ModPack] 整合包种类:Modrinth");
|
|||
|
|
return InstallPackModrinth(file, archive, archiveBaseFolder, instanceName, logo, resourceId,
|
|||
|
|
isOnlineInstall);
|
|||
|
|
}
|
|||
|
|
case 9:
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[ModPack] 整合包种类:带启动器的压缩包");
|
|||
|
|
return InstallPackLauncherPack(file, archive, archiveBaseFolder);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
default:
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[ModPack] 整合包种类:未能识别,假定为压缩包");
|
|||
|
|
return InstallPackCompress(file, archive);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
finally
|
|||
|
|
{
|
|||
|
|
if (archive is not null)
|
|||
|
|
archive.Dispose();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void ExtractModpackFiles(string installTemp, string fileAddress, LoaderBase loader,
|
|||
|
|
double progressIncrement)
|
|||
|
|
{
|
|||
|
|
// 解压文件
|
|||
|
|
var retryCount = 1;
|
|||
|
|
var encode = Encoding.GetEncoding("GB18030");
|
|||
|
|
var initialProgress = loader.Progress;
|
|||
|
|
|
|||
|
|
while (retryCount <= 5)
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
loader.Progress = initialProgress;
|
|||
|
|
|
|||
|
|
// 删除旧目录
|
|||
|
|
ModBase.DeleteDirectory(installTemp);
|
|||
|
|
|
|||
|
|
// 解压文件,ProgressIncrementHandler 通过 Lambda 更新进度
|
|||
|
|
ModBase.ExtractFile(fileAddress, installTemp, encode,
|
|||
|
|
delta => loader.Progress += delta * progressIncrement);
|
|||
|
|
|
|||
|
|
// 解压成功,更新进度并退出循环
|
|||
|
|
loader.Progress = initialProgress + progressIncrement;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
ModBase.Log(ex, $"第 {retryCount} 次解压尝试失败");
|
|||
|
|
|
|||
|
|
if (ex is ArgumentException || ex is IOException)
|
|||
|
|
{
|
|||
|
|
encode = Encoding.UTF8;
|
|||
|
|
ModBase.Log("[ModPack] 已切换压缩包解压编码为 UTF8");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查加载器状态,决定是否中止
|
|||
|
|
if (loader is not null && loader.LoadingState != MyLoading.MyLoadingState.Run)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
// 增加重试次数
|
|||
|
|
retryCount++;
|
|||
|
|
|
|||
|
|
if (retryCount <= 5)
|
|||
|
|
// 等待一段时间再重试
|
|||
|
|
Thread.Sleep((retryCount - 1) * 2000);
|
|||
|
|
else
|
|||
|
|
throw new Exception("解压整合包文件失败", ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 从整合包的 override 目录复制文件,同时设置 PCL 的配置文件与版本隔离。
|
|||
|
|
/// 对路径末尾是否为 \ 没有要求。
|
|||
|
|
/// </summary>
|
|||
|
|
private static void CopyOverrideDirectory(string overridesFolder, string versionFolder, LoaderBase loader,
|
|||
|
|
double progressIncrement)
|
|||
|
|
{
|
|||
|
|
if (!overridesFolder.EndsWithF(@"\"))
|
|||
|
|
overridesFolder += @"\";
|
|||
|
|
if (!versionFolder.EndsWithF(@"\"))
|
|||
|
|
versionFolder += @"\";
|
|||
|
|
// 复制文件
|
|||
|
|
if (Directory.Exists(overridesFolder))
|
|||
|
|
{
|
|||
|
|
ModBase.Log($"[ModPack] 处理整合包覆写文件夹:{overridesFolder} → {versionFolder}");
|
|||
|
|
ModBase.CopyDirectory(overridesFolder, versionFolder,
|
|||
|
|
delta => loader.Progress += delta * progressIncrement);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
ModBase.Log($"[ModPack] 整合包中没有覆写文件夹:{overridesFolder}");
|
|||
|
|
loader.Progress += progressIncrement;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置 ini
|
|||
|
|
var overridesIni = $@"{overridesFolder}PCL\Setup.ini";
|
|||
|
|
var versionIni = $@"{versionFolder}PCL\Setup.ini";
|
|||
|
|
if (File.Exists(overridesIni))
|
|||
|
|
{
|
|||
|
|
ModBase.WriteIni(overridesIni, "VersionArgumentIndie", 1.ToString()); // 开启版本隔离
|
|||
|
|
ModBase.WriteIni(overridesIni, "VersionArgumentIndieV2", true.ToString());
|
|||
|
|
ModBase.CopyFile(overridesIni, versionIni); // 覆写已有的 ini
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
ModBase.WriteIni(versionIni, "VersionArgumentIndie", 1.ToString()); // 开启版本隔离
|
|||
|
|
ModBase.WriteIni(versionIni, "VersionArgumentIndieV2", true.ToString());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ModBase.IniClearCache(versionIni); // 重置缓存,避免被安装过程中写入的 ini 覆盖
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#region CurseForge
|
|||
|
|
|
|||
|
|
private static LoaderCombo<string> InstallPackCurseForge(string fileAddress, ZipArchive archive,
|
|||
|
|
string archiveBaseFolder, string instanceName = null, string logo = null, string resourceId = null,
|
|||
|
|
bool isOnlineInstall = false)
|
|||
|
|
{
|
|||
|
|
// 读取 Json 文件
|
|||
|
|
JsonObject json;
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
json = (JsonObject)ModBase.GetJson(
|
|||
|
|
ModBase.ReadFile(archive.GetEntry(archiveBaseFolder + "manifest.json").Open()));
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
throw new Exception("CurseForge 整合包安装信息存在问题", ex);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (json["minecraft"] is null || json["minecraft"]["version"] is null)
|
|||
|
|
throw new Exception("CurseForge 整合包未提供 Minecraft 版本信息");
|
|||
|
|
|
|||
|
|
// 获取实例名
|
|||
|
|
if (instanceName is null)
|
|||
|
|
{
|
|||
|
|
instanceName = (string)(json["name"] ?? "");
|
|||
|
|
var validate = new FolderNameValidator(Path.Combine(ModFolder.mcFolderSelected, "versions"));
|
|||
|
|
if (!validate.Validate(instanceName).IsValid)
|
|||
|
|
instanceName = "";
|
|||
|
|
if (string.IsNullOrEmpty(instanceName))
|
|||
|
|
instanceName = ModMain.MyMsgBoxInput(Lang.Text("Minecraft.Download.Modpack.InputInstanceName"), "", "",
|
|||
|
|
[validate]);
|
|||
|
|
if (string.IsNullOrEmpty(instanceName))
|
|||
|
|
throw new ModBase.CancelledException();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取 Mod API 版本信息
|
|||
|
|
string forgeVersion = null;
|
|||
|
|
string neoForgeVersion = null;
|
|||
|
|
string fabricVersion = null;
|
|||
|
|
var modLoader = ModComp.CompLoaderType.Any;
|
|||
|
|
foreach (var Entry in (dynamic)json["minecraft"]["modLoaders"] ?? Array.Empty<JsonNode>())
|
|||
|
|
{
|
|||
|
|
string id = (Entry["id"] ?? "").ToString().ToLower();
|
|||
|
|
if (id.StartsWithF("forge-"))
|
|||
|
|
{
|
|||
|
|
// Forge 指定
|
|||
|
|
if (id.Contains("recommended"))
|
|||
|
|
throw new Exception(Lang.Text("Minecraft.Download.Modpack.TooOldUnsupported"));
|
|||
|
|
ModBase.Log("[ModPack] 整合包 Forge 版本:" + id);
|
|||
|
|
forgeVersion = id.Replace("forge-", "");
|
|||
|
|
modLoader = ModComp.CompLoaderType.Forge;
|
|||
|
|
}
|
|||
|
|
else if (id.StartsWithF("neoforge-"))
|
|||
|
|
{
|
|||
|
|
// NeoForge 指定
|
|||
|
|
ModBase.Log("[ModPack] 整合包 NeoForge 版本:" + id);
|
|||
|
|
neoForgeVersion = id.Replace("neoforge-", "");
|
|||
|
|
modLoader = ModComp.CompLoaderType.NeoForge;
|
|||
|
|
}
|
|||
|
|
else if (id.StartsWithF("fabric-"))
|
|||
|
|
{
|
|||
|
|
// Fabric 指定
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[ModPack] 整合包 Fabric 版本:" + id);
|
|||
|
|
fabricVersion = id.Replace("fabric-", "");
|
|||
|
|
modLoader = ModComp.CompLoaderType.Fabric;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
ModBase.Log(ex, "读取整合包 Fabric 版本失败:" + id);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else if (id.StartsWithF("quilt-"))
|
|||
|
|
{
|
|||
|
|
throw new Exception(Lang.Text("Minecraft.Download.Modpack.QuiltUnsupported"));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 解压
|
|||
|
|
var installTemp = ModMain.RequestTaskTempFolder();
|
|||
|
|
var installLoaders = new List<LoaderBase>();
|
|||
|
|
var overrideHome = (string)(json["overrides"] ?? "");
|
|||
|
|
if (!string.IsNullOrEmpty(overrideHome))
|
|||
|
|
installLoaders.Add(new LoaderTask<string, int>(Lang.Text("Minecraft.Download.Modpack.Stage.ExtractModpack"),
|
|||
|
|
task =>
|
|||
|
|
{
|
|||
|
|
ExtractModpackFiles(installTemp, fileAddress, task, 0.6d);
|
|||
|
|
CopyOverrideDirectory(
|
|||
|
|
Path.Combine(installTemp, archiveBaseFolder, overrideHome == "." || overrideHome == "./" ? "" : overrideHome),
|
|||
|
|
$@"{ModFolder.mcFolderSelected}versions\{instanceName}", task, 0.4d);
|
|||
|
|
})
|
|||
|
|
{
|
|||
|
|
ProgressWeight = new FileInfo(fileAddress).Length / 1024d / 1024d / 6d,
|
|||
|
|
block = false
|
|||
|
|
}); // 每 6M 需要 1s
|
|||
|
|
// 获取 Mod 列表
|
|||
|
|
var modList = new List<int>();
|
|||
|
|
var modOptionalList = new List<int>();
|
|||
|
|
foreach (var ModEntry in (dynamic)json["files"] ?? Array.Empty<JsonNode>())
|
|||
|
|
{
|
|||
|
|
if (ModEntry["projectID"] is null || ModEntry["fileID"] is null)
|
|||
|
|
{
|
|||
|
|
HintService.Hint(Lang.Text("Minecraft.Download.Modpack.ModMissingRequiredInfoSkipped", ModEntry));
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
modList.Add((int)ModEntry["fileID"]);
|
|||
|
|
if (ModEntry["required"] is JsonNode requiredNode && !requiredNode.ToObject<bool>())
|
|||
|
|
modOptionalList.Add((int)ModEntry["fileID"]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (modList.Any())
|
|||
|
|
{
|
|||
|
|
var modDownloadLoaders = new List<LoaderBase>();
|
|||
|
|
// 获取 Mod 下载信息
|
|||
|
|
modDownloadLoaders.Add(new LoaderTask<int, JsonArray>(
|
|||
|
|
Lang.Text("Minecraft.Download.Modpack.Stage.PrepareModsDownloadInfo"), task =>
|
|||
|
|
{
|
|||
|
|
var allowMirror = true;
|
|||
|
|
JsonArray ret;
|
|||
|
|
var tryCount = 0;
|
|||
|
|
do
|
|||
|
|
{
|
|||
|
|
tryCount += 1;
|
|||
|
|
ret = (JsonArray)((JsonObject)ModBase.GetJson(ModDownload.DlModRequest(
|
|||
|
|
"https://api.curseforge.com/v1/mods/files",
|
|||
|
|
"POST", "{\"fileIds\": [" + modList.Join(",") + "]}", "application/json",
|
|||
|
|
allowMirror)))["data"];
|
|||
|
|
if (modList.Count <= ret.Count)
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[Modpack] 已获取到的模组数量足够,开始进行下一步");
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
allowMirror = false;
|
|||
|
|
ModBase.Log($"[Modpack] 获取模组数量不达标,设置镜像源允许状态为: {allowMirror}");
|
|||
|
|
if (tryCount > 3) throw new Exception(Lang.Text("Minecraft.Download.Modpack.SomeModsDeleted"));
|
|||
|
|
} while (true);
|
|||
|
|
|
|||
|
|
task.output = ret;
|
|||
|
|
})
|
|||
|
|
{
|
|||
|
|
ProgressWeight = modList.Count / 10d
|
|||
|
|
}); // 每 10 Mod 需要 1s
|
|||
|
|
// 构造 NetFile
|
|||
|
|
modDownloadLoaders.Add(new LoaderTask<JsonArray, List<DownloadFile>>(
|
|||
|
|
Lang.Text("Minecraft.Download.Modpack.Stage.BuildModsDownloadInfo"), task =>
|
|||
|
|
{
|
|||
|
|
var fileList = new Dictionary<int, DownloadFile>();
|
|||
|
|
foreach (var ModJson in task.input)
|
|||
|
|
{
|
|||
|
|
var id = ModJson["id"].ToObject<int>();
|
|||
|
|
// 跳过重复的 Mod(疑似 CurseForge Bug)
|
|||
|
|
if (fileList.ContainsKey(id))
|
|||
|
|
continue;
|
|||
|
|
// 可选 Mod 提示
|
|||
|
|
if (modOptionalList.Contains(id))
|
|||
|
|
if (ModMain.MyMsgBox(
|
|||
|
|
Lang.Text("Minecraft.Download.Modpack.OptionalFile.Message", ModJson["displayName"]),
|
|||
|
|
Lang.Text("Minecraft.Download.Modpack.OptionalFile.Title"),
|
|||
|
|
Lang.Text("Minecraft.Download.Modpack.OptionalFile.Download"),
|
|||
|
|
Lang.Text("Minecraft.Download.Modpack.OptionalFile.Skip")
|
|||
|
|
) == 2)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
// 根据 modules 和文件名后缀判断资源类型
|
|||
|
|
string targetFolder;
|
|||
|
|
ModComp.CompType type;
|
|||
|
|
if (ModJson["modules"].AsArray().Any()) // modules 可能返回 null(#1006)
|
|||
|
|
{
|
|||
|
|
var moduleNames = ((JsonArray)ModJson["modules"]).Select(l => l["name"].ToString()).ToList();
|
|||
|
|
if (moduleNames.Contains("META-INF") || moduleNames.Contains("mcmod.info") ||
|
|||
|
|
(ModJson?["FileName"]?.ToString()?.EndsWithF(".jar", true)).GetValueOrDefault())
|
|||
|
|
{
|
|||
|
|
targetFolder = "mods";
|
|||
|
|
type = ModComp.CompType.Mod;
|
|||
|
|
}
|
|||
|
|
else if (moduleNames.Contains("pack.mcmeta"))
|
|||
|
|
{
|
|||
|
|
targetFolder = "resourcepacks";
|
|||
|
|
type = ModComp.CompType.ResourcePack;
|
|||
|
|
}
|
|||
|
|
else if (moduleNames.Contains("level.dat"))
|
|||
|
|
{
|
|||
|
|
targetFolder = "saves";
|
|||
|
|
type = ModComp.CompType.World;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
targetFolder = "shaderpacks";
|
|||
|
|
type = ModComp.CompType.Shader;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
targetFolder = "mods";
|
|||
|
|
type = ModComp.CompType.Mod;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 建立 CompFile
|
|||
|
|
var file = new ModComp.CompFile((JsonObject)ModJson, type);
|
|||
|
|
if (!file.Available)
|
|||
|
|
continue;
|
|||
|
|
// 实际的添加
|
|||
|
|
fileList.Add(id,
|
|||
|
|
file.ToNetFile($@"{ModFolder.mcFolderSelected}versions\{instanceName}\{targetFolder}\",
|
|||
|
|
ModComp.DownloadReason.ModPack, json["minecraft"]!["version"]!.ToString(), modLoader));
|
|||
|
|
task.Progress += 1d / (1 + modList.Count);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
task.output = fileList.Values.ToList();
|
|||
|
|
})
|
|||
|
|
{
|
|||
|
|
ProgressWeight = modList.Count / 200d,
|
|||
|
|
show = false
|
|||
|
|
}); // 每 200 Mod 需要 1s
|
|||
|
|
// 下载 Mod 文件
|
|||
|
|
modDownloadLoaders.Add(new LoaderDownload(Lang.Text("Minecraft.Download.Modpack.Stage.DownloadMods"), [])
|
|||
|
|
{ ProgressWeight = modList.Count * 1.5d }); // 每个 Mod 需要 1.5s
|
|||
|
|
// 构造加载器
|
|||
|
|
installLoaders.Add(
|
|||
|
|
new LoaderCombo<int>(Lang.Text("Minecraft.Download.Modpack.Stage.DownloadMods.MainLoader"),
|
|||
|
|
modDownloadLoaders)
|
|||
|
|
{ show = false, ProgressWeight = modDownloadLoaders.Sum(l => l.ProgressWeight) });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 构造加载器
|
|||
|
|
var request = new ModDownloadLib.McInstallRequest
|
|||
|
|
{
|
|||
|
|
targetInstanceName = instanceName,
|
|||
|
|
targetInstanceFolder = $@"{ModFolder.mcFolderSelected}versions\{instanceName}\",
|
|||
|
|
minecraftName = json["minecraft"]["version"].ToString(),
|
|||
|
|
forgeVersion = forgeVersion,
|
|||
|
|
neoForgeVersion = neoForgeVersion,
|
|||
|
|
fabricVersion = fabricVersion,
|
|||
|
|
};
|
|||
|
|
var mergeLoaders = ModDownloadLib.McInstallLoader(request);
|
|||
|
|
// 构造总加载器
|
|||
|
|
var loaders = new List<LoaderBase>();
|
|||
|
|
loaders.Add(new LoaderCombo<string>(Lang.Text("Minecraft.Download.Modpack.Stage.ModpackInstall"),
|
|||
|
|
installLoaders)
|
|||
|
|
{ show = false, block = false, ProgressWeight = installLoaders.Sum(l => l.ProgressWeight) });
|
|||
|
|
loaders.Add(new LoaderCombo<string>(Lang.Text("Minecraft.Download.Modpack.Stage.GameInstall"), mergeLoaders)
|
|||
|
|
{ show = false, ProgressWeight = mergeLoaders.Sum(l => l.ProgressWeight) });
|
|||
|
|
loaders.Add(new LoaderTask<string, string>(Lang.Text("Minecraft.Download.Modpack.Stage.FinalizeFiles"), task =>
|
|||
|
|
{
|
|||
|
|
// 设置图标
|
|||
|
|
var versionFolder = $@"{ModFolder.mcFolderSelected}versions\{instanceName}\";
|
|||
|
|
if (logo is not null && File.Exists(logo))
|
|||
|
|
{
|
|||
|
|
File.Copy(logo, Path.Combine(versionFolder, "PCL", "Logo.png"), true);
|
|||
|
|
States.Instance.LogoPath[versionFolder] = @"PCL\Logo.png";
|
|||
|
|
States.Instance.IsLogoCustom[versionFolder] = true;
|
|||
|
|
ModBase.Log("[ModPack] 已设置整合包 Logo:" + logo);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 删除原始整合包文件
|
|||
|
|
foreach (var Target in new[] { Path.Combine(versionFolder, "原始整合包.zip"), Path.Combine(versionFolder, "原始整合包.mrpack") })
|
|||
|
|
if (File.Exists(Target))
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[ModPack] 删除原始整合包文件:" + Target);
|
|||
|
|
File.Delete(Target);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (File.Exists(fileAddress) && ModBase.GetFileNameWithoutExtentionFromPath(fileAddress) == "modpack")
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[ModPack] 删除安装整合包文件:" + fileAddress);
|
|||
|
|
File.Delete(fileAddress);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 整合包版本
|
|||
|
|
if (json["version"] is not null) States.Instance.ModpackVersion[versionFolder] = json["version"].ToString();
|
|||
|
|
States.Instance.ModpackSource[versionFolder] = "CurseForge";
|
|||
|
|
States.Instance.ModpackId[versionFolder] = resourceId;
|
|||
|
|
do
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var projects = ModComp.CompRequest.GetCompProjectsByIds([resourceId]);
|
|||
|
|
if (projects.Count == 0)
|
|||
|
|
break;
|
|||
|
|
States.Instance.CustomInfo[versionFolder] = projects.First().Description;
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
ModBase.Log(ex, "[ModPack] 获取整合包描述文本失败");
|
|||
|
|
}
|
|||
|
|
} while (false);
|
|||
|
|
})
|
|||
|
|
{
|
|||
|
|
ProgressWeight = 0.1d,
|
|||
|
|
show = false
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 重复任务检查
|
|||
|
|
var loaderName = Lang.Text("Minecraft.Download.Modpack.Task.CurseForgeInstall", instanceName);
|
|||
|
|
if (loaderTaskbar.Any(l => (l.name ?? "") == (loaderName ?? "")))
|
|||
|
|
{
|
|||
|
|
HintService.Hint(Lang.Text("Minecraft.Download.Modpack.Installing"), HintType.Error);
|
|||
|
|
throw new ModBase.CancelledException();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 启动
|
|||
|
|
var loader = new LoaderCombo<string>(loaderName, loaders) { OnStateChanged = ModDownloadLib.McInstallState };
|
|||
|
|
loader.Start(request.targetInstanceFolder);
|
|||
|
|
LoaderTaskbarAdd(loader);
|
|||
|
|
ModMain.frmMain.BtnExtraDownload.ShowRefresh();
|
|||
|
|
if (!isOnlineInstall)
|
|||
|
|
ModBase.RunInUi(() => ModMain.frmMain.PageChange(FormMain.PageType.TaskManager));
|
|||
|
|
return loader;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region Modrinth
|
|||
|
|
|
|||
|
|
private static LoaderCombo<string> InstallPackModrinth(string fileAddress, ZipArchive archive,
|
|||
|
|
string archiveBaseFolder, string instanceName = null, string logo = null, string resourceId = null,
|
|||
|
|
bool isOnlineInstall = false)
|
|||
|
|
{
|
|||
|
|
// 读取 Json 文件
|
|||
|
|
JsonObject json;
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
json = (JsonObject)ModBase.GetJson(
|
|||
|
|
ModBase.ReadFile(archive.GetEntry(archiveBaseFolder + "modrinth.index.json").Open()));
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
throw new Exception("Modrinth 整合包安装信息存在问题", ex);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (json["dependencies"] is null || json["dependencies"]["minecraft"] is null)
|
|||
|
|
throw new Exception("Modrinth 整合包未提供 Minecraft 版本信息");
|
|||
|
|
// 获取 Mod API 版本信息
|
|||
|
|
string minecraftVersion = null;
|
|||
|
|
string forgeVersion = null;
|
|||
|
|
string neoForgeVersion = null;
|
|||
|
|
string fabricVersion = null;
|
|||
|
|
var modLoader = ModComp.CompLoaderType.Any;
|
|||
|
|
foreach (var Entry in json["dependencies"]?.AsObject() ?? new JsonObject())
|
|||
|
|
switch (Entry.Key.ToLower() ?? "")
|
|||
|
|
{
|
|||
|
|
case "minecraft":
|
|||
|
|
{
|
|||
|
|
minecraftVersion = Entry.Value?.ToObject<string>();
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
case "forge": // eg. 14.23.5.2859 / 1.19-41.1.0
|
|||
|
|
{
|
|||
|
|
forgeVersion = Entry.Value?.ToObject<string>();
|
|||
|
|
modLoader = ModComp.CompLoaderType.Forge;
|
|||
|
|
ModBase.Log("[ModPack] 整合包 Forge 版本:" + forgeVersion);
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
case "neoforge":
|
|||
|
|
case "neo-forge": // eg. 20.6.98-beta
|
|||
|
|
{
|
|||
|
|
neoForgeVersion = Entry.Value?.ToObject<string>();
|
|||
|
|
modLoader = ModComp.CompLoaderType.NeoForge;
|
|||
|
|
ModBase.Log("[ModPack] 整合包 NeoForge 版本:" + neoForgeVersion);
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
case "fabric-loader": // eg. 0.14.14
|
|||
|
|
{
|
|||
|
|
fabricVersion = Entry.Value?.ToObject<string>();
|
|||
|
|
modLoader = ModComp.CompLoaderType.Fabric;
|
|||
|
|
ModBase.Log("[ModPack] 整合包 Fabric 版本:" + fabricVersion);
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
case "quilt-loader": // eg. 0.26.0
|
|||
|
|
{
|
|||
|
|
throw new Exception(Lang.Text("Minecraft.Download.Modpack.QuiltUnsupported"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
default:
|
|||
|
|
{
|
|||
|
|
HintService.Hint(Lang.Text("Minecraft.Download.Modpack.UnknownLoader", Entry.Key, Entry.Value),
|
|||
|
|
HintType.Error);
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取实例名
|
|||
|
|
if (instanceName is null)
|
|||
|
|
{
|
|||
|
|
instanceName = (string)(json["name"] ?? "");
|
|||
|
|
var validate = new FolderNameValidator(Path.Combine(ModFolder.mcFolderSelected, "versions"));
|
|||
|
|
if (!validate.Validate(instanceName).IsValid)
|
|||
|
|
instanceName = "";
|
|||
|
|
if (string.IsNullOrEmpty(instanceName))
|
|||
|
|
instanceName = ModMain.MyMsgBoxInput(Lang.Text("Minecraft.Download.Modpack.InputInstanceName"), "", "",
|
|||
|
|
[validate]);
|
|||
|
|
if (string.IsNullOrEmpty(instanceName))
|
|||
|
|
throw new ModBase.CancelledException();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 解压
|
|||
|
|
var installTemp = ModMain.RequestTaskTempFolder();
|
|||
|
|
var installLoaders = new List<LoaderBase>();
|
|||
|
|
installLoaders.Add(new LoaderTask<string, int>(Lang.Text("Minecraft.Download.Modpack.Stage.ExtractModpack"),
|
|||
|
|
task =>
|
|||
|
|
{
|
|||
|
|
ExtractModpackFiles(installTemp, fileAddress, task, 0.5d);
|
|||
|
|
CopyOverrideDirectory(Path.Combine(installTemp, archiveBaseFolder, "overrides"),
|
|||
|
|
Path.Combine(ModFolder.mcFolderSelected, "versions", instanceName), task, 0.4d);
|
|||
|
|
CopyOverrideDirectory(Path.Combine(installTemp, archiveBaseFolder, "client-overrides"),
|
|||
|
|
Path.Combine(ModFolder.mcFolderSelected, "versions", instanceName), task, 0.1d);
|
|||
|
|
})
|
|||
|
|
{
|
|||
|
|
ProgressWeight = new FileInfo(fileAddress).Length / 1024d / 1024d / 6d,
|
|||
|
|
block = false
|
|||
|
|
}); // 每 6M 需要 1s
|
|||
|
|
// 获取下载文件列表
|
|||
|
|
var fileList = new List<DownloadFile>();
|
|||
|
|
foreach (var File in (dynamic)json["files"] ?? Array.Empty<JsonNode>())
|
|||
|
|
{
|
|||
|
|
// 检查是否需要该文件
|
|||
|
|
if (File["env"] is not null)
|
|||
|
|
switch (File["env"]["client"].ToString() ?? "")
|
|||
|
|
{
|
|||
|
|
case "optional":
|
|||
|
|
{
|
|||
|
|
if (ModMain.MyMsgBox(
|
|||
|
|
Lang.Text("Minecraft.Download.Modpack.OptionalFile.Message",
|
|||
|
|
ModBase.GetFileNameFromPath(File["path"].ToString())),
|
|||
|
|
Lang.Text("Minecraft.Download.Modpack.OptionalFile.Title"),
|
|||
|
|
Lang.Text("Minecraft.Download.Modpack.OptionalFile.Download"),
|
|||
|
|
Lang.Text("Minecraft.Download.Modpack.OptionalFile.Skip")
|
|||
|
|
) == 2) continue;
|
|||
|
|
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
case "unsupported":
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加下载文件
|
|||
|
|
var urls = ((JsonArray)File["downloads"])
|
|||
|
|
.OfType<JsonNode>()
|
|||
|
|
.Select(x => ModComp.CompFile.HandleCurseForgeDownloadUrls(x.ToString()))
|
|||
|
|
.ToList();
|
|||
|
|
// 镜像源
|
|||
|
|
urls = urls.SelectMany(x => ModDownload.DlSourceModDownloadGet(x)).ToList();
|
|||
|
|
var targetPath = $@"{ModFolder.mcFolderSelected}versions\{instanceName}\{File["path"]}";
|
|||
|
|
if (!Path.GetFullPath(targetPath)
|
|||
|
|
.StartsWithF($@"{ModFolder.mcFolderSelected}versions\{instanceName}\", true))
|
|||
|
|
{
|
|||
|
|
ModMain.MyMsgBox(Lang.Text("Minecraft.Download.Modpack.PathOutsideInstance.Message", targetPath),
|
|||
|
|
Lang.Text("Minecraft.Download.Modpack.PathOutsideInstance.Title"), isWarn: true);
|
|||
|
|
throw new ModBase.CancelledException();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fileList.Add(new DownloadFile(
|
|||
|
|
ModComp.CompFile.HandleModrinthDownloadUrls(urls, ModComp.DownloadReason.ModPack, minecraftVersion,
|
|||
|
|
modLoader), targetPath,
|
|||
|
|
new ModBase.FileChecker(actualSize: ((JsonNode)File["fileSize"]).ToObject<long>(),
|
|||
|
|
hash: File["hashes"]["sha1"].ToString()), true));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (fileList.Any())
|
|||
|
|
installLoaders.Add(
|
|||
|
|
new LoaderDownload(Lang.Text("Minecraft.Download.Modpack.Stage.DownloadAdditions"), fileList)
|
|||
|
|
{ ProgressWeight = fileList.Count * 1.5d }); // 每个 Mod 需要 1.5s
|
|||
|
|
|
|||
|
|
// 构造加载器
|
|||
|
|
var request = new ModDownloadLib.McInstallRequest
|
|||
|
|
{
|
|||
|
|
targetInstanceName = instanceName,
|
|||
|
|
targetInstanceFolder = $@"{ModFolder.mcFolderSelected}versions\{instanceName}\",
|
|||
|
|
minecraftName = minecraftVersion,
|
|||
|
|
forgeVersion = forgeVersion,
|
|||
|
|
neoForgeVersion = neoForgeVersion,
|
|||
|
|
fabricVersion = fabricVersion,
|
|||
|
|
};
|
|||
|
|
var mergeLoaders = ModDownloadLib.McInstallLoader(request);
|
|||
|
|
// 构造总加载器
|
|||
|
|
var loaders = new List<LoaderBase>();
|
|||
|
|
loaders.Add(new LoaderCombo<string>(Lang.Text("Minecraft.Download.Modpack.Stage.ModpackInstall"),
|
|||
|
|
installLoaders)
|
|||
|
|
{ show = false, block = false, ProgressWeight = installLoaders.Sum(l => l.ProgressWeight) });
|
|||
|
|
loaders.Add(new LoaderCombo<string>(Lang.Text("Minecraft.Download.Modpack.Stage.GameInstall"), mergeLoaders)
|
|||
|
|
{ show = false, ProgressWeight = mergeLoaders.Sum(l => l.ProgressWeight) });
|
|||
|
|
loaders.Add(new LoaderTask<string, string>(Lang.Text("Minecraft.Download.Modpack.Stage.FinalizeFiles"), task =>
|
|||
|
|
{
|
|||
|
|
// 设置图标
|
|||
|
|
var versionFolder = $@"{ModFolder.mcFolderSelected}versions\{instanceName}\";
|
|||
|
|
if (logo is not null && File.Exists(logo))
|
|||
|
|
{
|
|||
|
|
File.Copy(logo, Path.Combine(versionFolder, "PCL", "Logo.png"), true);
|
|||
|
|
States.Instance.LogoPath[versionFolder] = @"PCL\Logo.png";
|
|||
|
|
States.Instance.IsLogoCustom[versionFolder] = true;
|
|||
|
|
ModBase.Log("[ModPack] 已设置整合包 Logo:" + logo);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 删除原始整合包文件
|
|||
|
|
foreach (var Target in new[] { Path.Combine(versionFolder, "原始整合包.zip"), Path.Combine(versionFolder, "原始整合包.mrpack") })
|
|||
|
|
if (File.Exists(Target))
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[ModPack] 删除原始整合包文件:" + Target);
|
|||
|
|
File.Delete(Target);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (File.Exists(fileAddress) && ModBase.GetFileNameWithoutExtentionFromPath(fileAddress) == "modpack")
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[ModPack] 删除安装整合包文件:" + fileAddress);
|
|||
|
|
File.Delete(fileAddress);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 整合包版本
|
|||
|
|
if (json["versionId"] is not null)
|
|||
|
|
States.Instance.ModpackVersion[versionFolder] = json["versionId"].ToString();
|
|||
|
|
States.Instance.ModpackSource[versionFolder] = "Modrinth";
|
|||
|
|
States.Instance.ModpackId[versionFolder] = resourceId;
|
|||
|
|
do
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var projects = ModComp.CompRequest.GetCompProjectsByIds([resourceId]);
|
|||
|
|
if (projects.Count == 0)
|
|||
|
|
break;
|
|||
|
|
States.Instance.CustomInfo[versionFolder] = projects.First().Description;
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
ModBase.Log(ex, "[ModPack] 获取整合包描述文本失败");
|
|||
|
|
}
|
|||
|
|
} while (false);
|
|||
|
|
})
|
|||
|
|
{
|
|||
|
|
ProgressWeight = 0.1d,
|
|||
|
|
show = false
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 重复任务检查
|
|||
|
|
var loaderName = Lang.Text("Minecraft.Download.Modpack.Task.ModrinthInstall", instanceName);
|
|||
|
|
if (loaderTaskbar.Any(l => (l.name ?? "") == (loaderName ?? "")))
|
|||
|
|
{
|
|||
|
|
HintService.Hint(Lang.Text("Minecraft.Download.Modpack.Installing"), HintType.Error);
|
|||
|
|
throw new ModBase.CancelledException();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 启动
|
|||
|
|
var loader = new LoaderCombo<string>(loaderName, loaders) { OnStateChanged = ModDownloadLib.McInstallState };
|
|||
|
|
loader.Start(request.targetInstanceFolder);
|
|||
|
|
LoaderTaskbarAdd(loader);
|
|||
|
|
ModMain.frmMain.BtnExtraDownload.ShowRefresh();
|
|||
|
|
if (!isOnlineInstall)
|
|||
|
|
ModBase.RunInUi(() => ModMain.frmMain.PageChange(FormMain.PageType.TaskManager));
|
|||
|
|
return loader;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region HMCL
|
|||
|
|
|
|||
|
|
private static LoaderCombo<string> InstallPackHMCL(string fileAddress, ZipArchive archive, string archiveBaseFolder)
|
|||
|
|
{
|
|||
|
|
// 读取 Json 文件
|
|||
|
|
JsonObject json;
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
json = (JsonObject)ModBase.GetJson(
|
|||
|
|
ModBase.ReadFile(archive.GetEntry(archiveBaseFolder + "modpack.json").Open(), Encoding.UTF8));
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
throw new Exception("HMCL 整合包安装信息存在问题", ex);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取实例名
|
|||
|
|
var instanceName = (string)(json["name"] ?? "");
|
|||
|
|
var validate = new FolderNameValidator(Path.Combine(ModFolder.mcFolderSelected, "versions"));
|
|||
|
|
if (!validate.Validate(instanceName).IsValid)
|
|||
|
|
instanceName = "";
|
|||
|
|
if (string.IsNullOrEmpty(instanceName))
|
|||
|
|
instanceName = ModMain.MyMsgBoxInput(Lang.Text("Minecraft.Download.Modpack.InputInstanceName"), "", "",
|
|||
|
|
[validate]);
|
|||
|
|
if (string.IsNullOrEmpty(instanceName))
|
|||
|
|
throw new ModBase.CancelledException();
|
|||
|
|
// 解压
|
|||
|
|
var installTemp = ModMain.RequestTaskTempFolder();
|
|||
|
|
var installLoaders = new List<LoaderBase>();
|
|||
|
|
installLoaders.Add(new LoaderTask<string, int>(Lang.Text("Minecraft.Download.Modpack.Stage.ExtractModpack"),
|
|||
|
|
task =>
|
|||
|
|
{
|
|||
|
|
ExtractModpackFiles(installTemp, fileAddress, task, 0.6d);
|
|||
|
|
CopyOverrideDirectory(Path.Combine(installTemp, archiveBaseFolder, "minecraft"),
|
|||
|
|
Path.Combine(ModFolder.mcFolderSelected, "versions", instanceName), task, 0.4d);
|
|||
|
|
})
|
|||
|
|
{
|
|||
|
|
ProgressWeight = new FileInfo(fileAddress).Length / 1024d / 1024d / 6d,
|
|||
|
|
block = false
|
|||
|
|
}); // 每 6M 需要 1s
|
|||
|
|
// 构造游戏本体安装加载器
|
|||
|
|
if (json["gameVersion"] is null)
|
|||
|
|
throw new Exception(Lang.Text("Minecraft.Download.Modpack.MissingGameVersion.Hmcl"));
|
|||
|
|
var request = new ModDownloadLib.McInstallRequest
|
|||
|
|
{
|
|||
|
|
targetInstanceName = instanceName,
|
|||
|
|
targetInstanceFolder = $@"{ModFolder.mcFolderSelected}versions\{instanceName}\",
|
|||
|
|
minecraftName = json["gameVersion"].ToString()
|
|||
|
|
};
|
|||
|
|
var mergeLoaders = ModDownloadLib.McInstallLoader(request);
|
|||
|
|
// 构造总加载器
|
|||
|
|
var loaders = new List<LoaderBase>
|
|||
|
|
{
|
|||
|
|
new LoaderCombo<string>(Lang.Text("Minecraft.Download.Modpack.Stage.ModpackInstall"), installLoaders)
|
|||
|
|
{ show = false, block = false, ProgressWeight = installLoaders.Sum(l => l.ProgressWeight) },
|
|||
|
|
new LoaderCombo<string>(Lang.Text("Minecraft.Download.Modpack.Stage.GameInstall"), mergeLoaders)
|
|||
|
|
{ show = false, ProgressWeight = mergeLoaders.Sum(l => l.ProgressWeight) }
|
|||
|
|
};
|
|||
|
|
// 重复任务检查
|
|||
|
|
var loaderName = Lang.Text("Minecraft.Download.Modpack.Task.HmclInstall", instanceName);
|
|||
|
|
if (loaderTaskbar.Any(l => (l.name ?? "") == (loaderName ?? "")))
|
|||
|
|
{
|
|||
|
|
HintService.Hint(Lang.Text("Minecraft.Download.Modpack.Installing"), HintType.Error);
|
|||
|
|
throw new ModBase.CancelledException();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 启动
|
|||
|
|
var loader = new LoaderCombo<string>(loaderName, loaders) { OnStateChanged = ModDownloadLib.McInstallState };
|
|||
|
|
loader.Start(request.targetInstanceFolder);
|
|||
|
|
LoaderTaskbarAdd(loader);
|
|||
|
|
ModMain.frmMain.BtnExtraDownload.ShowRefresh();
|
|||
|
|
ModBase.RunInUi(() => ModMain.frmMain.PageChange(FormMain.PageType.TaskManager));
|
|||
|
|
return loader;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region MCBBS
|
|||
|
|
|
|||
|
|
private static LoaderCombo<string> InstallPackMCBBS(string fileAddress, ZipArchive archive,
|
|||
|
|
string archiveBaseFolder, string instanceName = null)
|
|||
|
|
{
|
|||
|
|
// 读取 Json 文件
|
|||
|
|
JsonObject json;
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// VB 的 If(a, b) 在 C# 中如果是 null 合并则用 ??,如果是三元运算则用 ?:
|
|||
|
|
var entry = archive.GetEntry(archiveBaseFolder + "mcbbs.packmeta") ??
|
|||
|
|
archive.GetEntry(archiveBaseFolder + "manifest.json");
|
|||
|
|
using (var stream = entry.Open())
|
|||
|
|
{
|
|||
|
|
json = (JsonObject)ModBase.GetJson(ModBase.ReadFile(stream, Encoding.UTF8));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
throw new Exception("MCBBS 整合包安装信息存在问题", ex);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取实例名
|
|||
|
|
if (instanceName is null)
|
|||
|
|
{
|
|||
|
|
instanceName = json["name"]?.ToString() ?? "";
|
|||
|
|
var validate = new FolderNameValidator(Path.Combine(ModFolder.mcFolderSelected, "versions"));
|
|||
|
|
|
|||
|
|
if (!validate.Validate(instanceName).IsValid) instanceName = "";
|
|||
|
|
|
|||
|
|
if (string.IsNullOrEmpty(instanceName))
|
|||
|
|
instanceName = ModMain.MyMsgBoxInput(Lang.Text("Minecraft.Download.Modpack.InputInstanceName"), "", "",
|
|||
|
|
[validate]);
|
|||
|
|
|
|||
|
|
if (string.IsNullOrEmpty(instanceName)) throw new ModBase.CancelledException();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 解压与路径准备
|
|||
|
|
var installTemp = ModMain.RequestTaskTempFolder();
|
|||
|
|
var versionFolder = $"{ModFolder.mcFolderSelected}versions\\{instanceName}";
|
|||
|
|
var installLoaders = new List<LoaderBase>();
|
|||
|
|
|
|||
|
|
// 解压整合包文件任务
|
|||
|
|
var unzipTask = new LoaderTask<string, int>(Lang.Text("Minecraft.Download.Modpack.Stage.ExtractModpack"),
|
|||
|
|
task =>
|
|||
|
|
{
|
|||
|
|
ExtractModpackFiles(installTemp, fileAddress, task, 0.6);
|
|||
|
|
CopyOverrideDirectory(
|
|||
|
|
Path.Combine(installTemp, archiveBaseFolder, "overrides"),
|
|||
|
|
Path.Combine(ModFolder.mcFolderSelected, "versions", instanceName),
|
|||
|
|
task, 0.4);
|
|||
|
|
|
|||
|
|
// JVM 参数处理
|
|||
|
|
if (json["launchInfo"] is not null)
|
|||
|
|
{
|
|||
|
|
var launchInfo = (JsonObject)json["launchInfo"];
|
|||
|
|
Config.Instance.JvmArgs[versionFolder] = string.Join(" ", launchInfo["javaArgument"]);
|
|||
|
|
Config.Instance.GameArgs[versionFolder] = string.Join(" ", launchInfo["launchArgument"]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 整合包版本
|
|||
|
|
if (json["version"] is not null) States.Instance.ModpackVersion[versionFolder] = json["version"].ToString();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
unzipTask.ProgressWeight = new FileInfo(fileAddress).Length / 1024.0 / 1024.0 / 6.0; // 每 6M 需要 1s
|
|||
|
|
unzipTask.block = false;
|
|||
|
|
installLoaders.Add(unzipTask);
|
|||
|
|
|
|||
|
|
// 构造加载器
|
|||
|
|
if (json["addons"] is null)
|
|||
|
|
throw new Exception(Lang.Text("Minecraft.Download.Modpack.MissingGameVersion.McbbsAddons"));
|
|||
|
|
|
|||
|
|
var addons = new Dictionary<string, string>();
|
|||
|
|
foreach (var EntryNode in json["addons"].AsArray()) { var entry = EntryNode.AsObject(); addons.Add(entry["id"].ToString(), entry["version"].ToString()); }
|
|||
|
|
|
|||
|
|
if (!addons.ContainsKey("game"))
|
|||
|
|
{
|
|||
|
|
HintService.Hint(Lang.Text("Minecraft.Download.Modpack.MissingGameVersion.Generic"), HintType.Error);
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (addons.ContainsKey("quilt"))
|
|||
|
|
throw new Exception(Lang.Text("Minecraft.Download.Modpack.QuiltUnsupported"));
|
|||
|
|
|
|||
|
|
// 构造安装请求
|
|||
|
|
var request = new ModDownloadLib.McInstallRequest
|
|||
|
|
{
|
|||
|
|
targetInstanceName = instanceName,
|
|||
|
|
targetInstanceFolder = $"{ModFolder.mcFolderSelected}versions\\{instanceName}\\",
|
|||
|
|
minecraftName = addons["game"],
|
|||
|
|
optiFineVersion = addons.ContainsKey("optifine") ? addons["optifine"] : null,
|
|||
|
|
forgeVersion = addons.ContainsKey("forge") ? addons["forge"] : null,
|
|||
|
|
neoForgeVersion = addons.ContainsKey("neoforge") ? addons["neoforge"] : null,
|
|||
|
|
fabricVersion = addons.ContainsKey("fabric") ? addons["fabric"] : null,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
var mergeLoaders = ModDownloadLib.McInstallLoader(request);
|
|||
|
|
|
|||
|
|
// 构造总加载器
|
|||
|
|
var loaders = new List<LoaderBase>();
|
|||
|
|
loaders.Add(new LoaderCombo<string>(Lang.Text("Minecraft.Download.Modpack.Stage.ModpackInstall"),
|
|||
|
|
installLoaders)
|
|||
|
|
{
|
|||
|
|
show = false,
|
|||
|
|
block = false,
|
|||
|
|
ProgressWeight = installLoaders.Sum(l => l.ProgressWeight)
|
|||
|
|
});
|
|||
|
|
loaders.Add(new LoaderCombo<string>(Lang.Text("Minecraft.Download.Modpack.Stage.GameInstall"), mergeLoaders)
|
|||
|
|
{
|
|||
|
|
show = false,
|
|||
|
|
ProgressWeight = mergeLoaders.Sum(l => l.ProgressWeight)
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 重复任务检查
|
|||
|
|
var loaderName = Lang.Text("Minecraft.Download.Modpack.Task.McbbsInstall", instanceName);
|
|||
|
|
if (loaderTaskbar.Any(l => l.name == loaderName))
|
|||
|
|
{
|
|||
|
|
HintService.Hint(Lang.Text("Minecraft.Download.Modpack.Installing"), HintType.Error);
|
|||
|
|
throw new ModBase.CancelledException();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 启动任务
|
|||
|
|
var loader = new LoaderCombo<string>(loaderName, loaders);
|
|||
|
|
loader.OnStateChanged = ModDownloadLib.McInstallState;
|
|||
|
|
|
|||
|
|
loader.Start(request.targetInstanceFolder);
|
|||
|
|
LoaderTaskbarAdd(loader);
|
|||
|
|
|
|||
|
|
ModMain.frmMain.BtnExtraDownload.ShowRefresh();
|
|||
|
|
ModBase.RunInUi(() => ModMain.frmMain.PageChange(FormMain.PageType.TaskManager));
|
|||
|
|
|
|||
|
|
return loader;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 带启动器的压缩包
|
|||
|
|
|
|||
|
|
private static LoaderCombo<string> InstallPackLauncherPack(string fileAddress, ZipArchive archive,
|
|||
|
|
string archiveBaseFolder)
|
|||
|
|
{
|
|||
|
|
// 获取解压路径
|
|||
|
|
ModMain.MyMsgBox(Lang.Text("Minecraft.Download.Modpack.SelectEmptyFolder.Message"),
|
|||
|
|
Lang.Text("Common.Action.Install"), Lang.Text("Common.Action.Continue"), forceWait: true);
|
|||
|
|
var targetFolder = SystemDialogs.SelectFolder(Lang.Text("Minecraft.Download.Modpack.SelectTargetFolder.Title"));
|
|||
|
|
if (string.IsNullOrEmpty(targetFolder))
|
|||
|
|
throw new ModBase.CancelledException();
|
|||
|
|
if (Directory.GetFileSystemEntries(targetFolder).Length > 0)
|
|||
|
|
{
|
|||
|
|
HintService.Hint(Lang.Text("Minecraft.Download.Modpack.TargetFolderMustBeEmpty"), HintType.Error);
|
|||
|
|
throw new ModBase.CancelledException();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 解压
|
|||
|
|
var loader = new LoaderCombo<string>(Lang.Text("Minecraft.Download.Modpack.Stage.ExtractArchive"), new[]
|
|||
|
|
{
|
|||
|
|
new LoaderTask<string, int>(Lang.Text("Minecraft.Download.Modpack.Stage.ExtractArchive"), task =>
|
|||
|
|
{
|
|||
|
|
ExtractModpackFiles(targetFolder, fileAddress, task, 0.9d);
|
|||
|
|
Thread.Sleep(400); // 避免文件争用
|
|||
|
|
// 查找解压后的 exe 文件
|
|||
|
|
string launcher = null;
|
|||
|
|
foreach (var ExeFile in Directory.GetFiles(targetFolder, "*.exe", SearchOption.TopDirectoryOnly))
|
|||
|
|
{
|
|||
|
|
var info = FileVersionInfo.GetVersionInfo(ExeFile);
|
|||
|
|
ModBase.Log($"[Modpack] 文件 {ExeFile} 的产品名标识为 {info.ProductName}");
|
|||
|
|
if (info.ProductName == "Plain Craft Launcher")
|
|||
|
|
{
|
|||
|
|
launcher = ExeFile;
|
|||
|
|
ModBase.Log($"[Modpack] 发现整合包附带的 PCL 启动器:{ExeFile}");
|
|||
|
|
}
|
|||
|
|
else if ((info.ProductName.ContainsF("Launcher", true) || info.ProductName.ContainsF("启动", true)) &&
|
|||
|
|
!(info.ProductName == "Plain Craft Launcher Admin Manager"))
|
|||
|
|
{
|
|||
|
|
if (launcher is null)
|
|||
|
|
{
|
|||
|
|
launcher = ExeFile;
|
|||
|
|
ModBase.Log($"[Modpack] 发现整合包附带的疑似第三方启动器:{ExeFile}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
task.Progress = 0.95d;
|
|||
|
|
// 尝试使用附带的启动器打开
|
|||
|
|
if (launcher is not null)
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[Modpack] 找到压缩包中附带的启动器:" + launcher);
|
|||
|
|
if (ModMain.MyMsgBox(Lang.Text("Minecraft.Download.Modpack.BundledLauncher.Message", launcher),
|
|||
|
|
Lang.Text("Minecraft.Download.Modpack.BundledLauncher.Title"),
|
|||
|
|
Lang.Text("Minecraft.Download.Modpack.BundledLauncher.UseBundled"),
|
|||
|
|
Lang.Text("Minecraft.Download.Modpack.BundledLauncher.DoNotUse")
|
|||
|
|
) == 1)
|
|||
|
|
{
|
|||
|
|
ModBase.OpenExplorer(targetFolder);
|
|||
|
|
ModBase.ShellOnly(launcher, "--wait"); // 要求等待已有的 PCL 退出
|
|||
|
|
ModBase.Log("[Modpack] 为换用整合包中的启动器启动,强制结束程序");
|
|||
|
|
ModMain.frmMain.EndProgram(false);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[Modpack] 未找到压缩包中附带的启动器");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ModBase.OpenExplorer(targetFolder);
|
|||
|
|
// 加入文件夹列表
|
|||
|
|
var instanceName = ModBase.GetFolderNameFromPath(targetFolder);
|
|||
|
|
Directory.CreateDirectory(Path.Combine(targetFolder, ".minecraft"));
|
|||
|
|
PageSelectLeft.AddFolder(
|
|||
|
|
Path.Combine(targetFolder, ".minecraft", archiveBaseFolder.Replace("/", @"\").TrimStart('\\')), instanceName,
|
|||
|
|
false); // 格式例如:包裹文件夹\.minecraft\(最短为空字符串)
|
|||
|
|
// 调用 modpack 文件进行安装
|
|||
|
|
var modpackFile = Directory.GetFiles(targetFolder, "modpack.*", SearchOption.AllDirectories).First();
|
|||
|
|
ModBase.Log("[Modpack] 调用 modpack 文件继续安装:" + modpackFile);
|
|||
|
|
ModpackInstall(modpackFile);
|
|||
|
|
})
|
|||
|
|
});
|
|||
|
|
loader.Start(targetFolder);
|
|||
|
|
LoaderTaskbarAdd(loader);
|
|||
|
|
ModMain.frmMain.BtnExtraDownload.ShowRefresh();
|
|||
|
|
ModMain.frmMain.BtnExtraDownload.Ribble();
|
|||
|
|
return loader;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 普通压缩包
|
|||
|
|
|
|||
|
|
private static LoaderCombo<string> InstallPackCompress(string fileAddress, ZipArchive archive)
|
|||
|
|
{
|
|||
|
|
// 尝试定位 .minecraft 文件夹:寻找形如 “/versions/XXX/XXX.json” 的路径
|
|||
|
|
Match match = null;
|
|||
|
|
var regex = new Regex(@"^.*\/(?=versions\/(?<ver>[^\/]+)\/(\k<ver>)\.json$)", RegexOptions.IgnoreCase);
|
|||
|
|
foreach (var Entry in archive.Entries)
|
|||
|
|
{
|
|||
|
|
var entryMatch = regex.Match("/" + Entry.FullName);
|
|||
|
|
if (entryMatch.Success)
|
|||
|
|
{
|
|||
|
|
match = entryMatch;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (match is null)
|
|||
|
|
throw new Exception(Lang.Text("Minecraft.Download.Modpack.UnknownArchiveStructure")); // 没有匹配
|
|||
|
|
var archiveBaseFolder = match.Value.Replace("/", @"\").TrimStart('\\'); // 格式例如:包裹文件夹\.minecraft\(最短为空字符串)
|
|||
|
|
var instanceName = match.Groups[1].Value;
|
|||
|
|
ModBase.Log("[ModPack] 检测到压缩包的 .minecraft 根目录:" + archiveBaseFolder + ",命中的实例名:" + instanceName);
|
|||
|
|
// 获取解压路径
|
|||
|
|
ModMain.MyMsgBox(Lang.Text("Minecraft.Download.Modpack.SelectEmptyFolder.Message"),
|
|||
|
|
Lang.Text("Common.Action.Install"), Lang.Text("Common.Action.Continue"), forceWait: true);
|
|||
|
|
var targetFolder = SystemDialogs.SelectFolder(Lang.Text("Minecraft.Download.Modpack.SelectTargetFolder.Title"));
|
|||
|
|
if (string.IsNullOrEmpty(targetFolder))
|
|||
|
|
throw new ModBase.CancelledException();
|
|||
|
|
if (targetFolder.Contains("!") || targetFolder.Contains(";"))
|
|||
|
|
{
|
|||
|
|
HintService.Hint(Lang.Text("Minecraft.Download.Modpack.InvalidGamePathChars", targetFolder),
|
|||
|
|
HintType.Error);
|
|||
|
|
throw new ModBase.CancelledException();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (Directory.GetFileSystemEntries(targetFolder).Length > 0)
|
|||
|
|
{
|
|||
|
|
HintService.Hint(Lang.Text("Minecraft.Download.Modpack.TargetFolderMustBeEmpty"), HintType.Error);
|
|||
|
|
throw new ModBase.CancelledException();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 解压
|
|||
|
|
var loader = new LoaderCombo<string>(Lang.Text("Minecraft.Download.Modpack.Stage.ExtractArchive"), new[]
|
|||
|
|
{
|
|||
|
|
new LoaderTask<string, int>(Lang.Text("Minecraft.Download.Modpack.Stage.ExtractArchive"), task =>
|
|||
|
|
{
|
|||
|
|
ExtractModpackFiles(targetFolder, fileAddress, task, 0.95d);
|
|||
|
|
// 加入文件夹列表
|
|||
|
|
PageSelectLeft.AddFolder(Path.Combine(targetFolder, archiveBaseFolder), ModBase.GetFolderNameFromPath(targetFolder),
|
|||
|
|
false);
|
|||
|
|
Thread.Sleep(400); // 避免文件争用
|
|||
|
|
ModBase.RunInUi(() => ModMain.frmMain.PageChange(FormMain.PageType.InstanceSelect));
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
{
|
|||
|
|
OnStateChanged = ModDownloadLib.McInstallState
|
|||
|
|
};
|
|||
|
|
loader.Start(targetFolder);
|
|||
|
|
LoaderTaskbarAdd(loader);
|
|||
|
|
ModMain.frmMain.BtnExtraDownload.ShowRefresh();
|
|||
|
|
ModMain.frmMain.BtnExtraDownload.Ribble();
|
|||
|
|
return loader;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region MultiMC
|
|||
|
|
|
|||
|
|
public class MMCPackInfo
|
|||
|
|
{
|
|||
|
|
public JsonObject additionalJson = new();
|
|||
|
|
public bool isCleanroomOverrided;
|
|||
|
|
public bool isFabricOverrided;
|
|||
|
|
public bool isForgeOverrided;
|
|||
|
|
public bool isMcArgsEdited;
|
|||
|
|
public bool isMinecraftOverrided;
|
|||
|
|
public bool isNeoForgeOverrided;
|
|||
|
|
public JsonArray jvmArgs = new();
|
|||
|
|
public JsonArray libraries = new();
|
|||
|
|
public JsonObject overridedJson = new();
|
|||
|
|
public string tweakers = null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static LoaderCombo<string> InstallPackMMC(string fileAddress, ZipArchive archive, string archiveBaseFolder)
|
|||
|
|
{
|
|||
|
|
// 读取 Json 文件
|
|||
|
|
JsonObject packJson;
|
|||
|
|
string packInstance;
|
|||
|
|
MMCPackInfo packInfo = null;
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
packJson = (JsonObject)ModBase.GetJson(
|
|||
|
|
ModBase.ReadFile(archive.GetEntry(archiveBaseFolder + "mmc-pack.json").Open(), Encoding.UTF8));
|
|||
|
|
packInstance = ModBase.ReadFile(archive.GetEntry(archiveBaseFolder + "instance.cfg").Open(), Encoding.UTF8);
|
|||
|
|
|
|||
|
|
#region JSON Patches
|
|||
|
|
|
|||
|
|
// 参考 https://github.com/MultiMC/Launcher/wiki/JSON-Patches
|
|||
|
|
do
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
if (!archive.Entries.Any(e =>
|
|||
|
|
e.FullName.Equals(archiveBaseFolder + "patches/", StringComparison.OrdinalIgnoreCase)))
|
|||
|
|
break;
|
|||
|
|
ModBase.Log("[ModPack] 安装的 MultiMC 整合包存在 JSON Patches");
|
|||
|
|
// 排序预处理
|
|||
|
|
var patches = new List<KeyValuePair<JsonObject, int>>();
|
|||
|
|
foreach (var entry in archive.Entries)
|
|||
|
|
if (!entry.FullName.EndsWith("/") && entry.FullName.StartsWith(archiveBaseFolder + "patches/"))
|
|||
|
|
{
|
|||
|
|
var patch = (JsonObject)ModBase.GetJson(ModBase.ReadFile(
|
|||
|
|
archive.GetEntry(entry.FullName).Open(), Encoding.UTF8));
|
|||
|
|
patches.Add(new KeyValuePair<JsonObject, int>(patch,
|
|||
|
|
(int)(patch["order"] is not null ? patch["order"] : 0)));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var components = (JsonArray)packJson["components"];
|
|||
|
|
var componentUids = components
|
|||
|
|
.Select(c => c["uid"]?.ToString())
|
|||
|
|
.ToHashSet();
|
|||
|
|
|
|||
|
|
patches = patches
|
|||
|
|
.Where(p => componentUids.Contains(p.Key["uid"]?.ToString()))
|
|||
|
|
.OrderBy(p => p.Value)
|
|||
|
|
.ToList();
|
|||
|
|
// 应用 Patches
|
|||
|
|
packInfo = new MMCPackInfo();
|
|||
|
|
|
|||
|
|
string tweakers = null;
|
|||
|
|
JsonObject assetIndex = null;
|
|||
|
|
JsonObject javaVerJson = null;
|
|||
|
|
string mainClass = null;
|
|||
|
|
var gameArguments = new JsonArray();
|
|||
|
|
var jvmArguments = new JsonArray();
|
|||
|
|
var libJson = new JsonArray();
|
|||
|
|
var addLibJson = new JsonArray();
|
|||
|
|
foreach (var Patch in patches)
|
|||
|
|
{
|
|||
|
|
var patchJson = Patch.Key;
|
|||
|
|
if ((string)patchJson["uid"] == "net.minecraft")
|
|||
|
|
{
|
|||
|
|
packInfo.isMinecraftOverrided = true;
|
|||
|
|
}
|
|||
|
|
else if ((string)patchJson["uid"] == "net.minecraftforge")
|
|||
|
|
{
|
|||
|
|
if (patchJson["version"].ToString().StartsWithF("0."))
|
|||
|
|
packInfo.isCleanroomOverrided = true;
|
|||
|
|
else
|
|||
|
|
packInfo.isForgeOverrided = true;
|
|||
|
|
}
|
|||
|
|
else if ((string)patchJson["uid"] == "net.neoforged")
|
|||
|
|
{
|
|||
|
|
packInfo.isNeoForgeOverrided = true;
|
|||
|
|
}
|
|||
|
|
else if ((string)patchJson["uid"] == "net.fabricmc.fabric-loader")
|
|||
|
|
{
|
|||
|
|
packInfo.isFabricOverrided = true;
|
|||
|
|
}
|
|||
|
|
else if ((string)patchJson["uid"] == "org.quiltmc.quilt-loader")
|
|||
|
|
{
|
|||
|
|
throw new Exception(Lang.Text("Minecraft.Download.Modpack.QuiltUnsupported"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// JVM 参数
|
|||
|
|
if (patchJson["+jvmArgs"] is not null)
|
|||
|
|
{
|
|||
|
|
jvmArguments.Merge(patchJson["+jvmArgs"]);
|
|||
|
|
ModBase.Log($"[ModPack] 已应用 JSON-Patch {patchJson["uid"]} 的 JVM 参数");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Libraries
|
|||
|
|
if (patchJson["libraries"] is not null || patchJson["+libraries"] is not null)
|
|||
|
|
{
|
|||
|
|
var libs = new JsonArray();
|
|||
|
|
if (patchJson["libraries"] is not null)
|
|||
|
|
foreach (var Library in patchJson["libraries"].AsArray())
|
|||
|
|
{
|
|||
|
|
if (Library is not JsonObject LibraryObj) continue;
|
|||
|
|
var libJobj = LibraryObj.DeepClone().AsObject();
|
|||
|
|
if (libJobj["MMC-hint"] is not null)
|
|||
|
|
{
|
|||
|
|
libJobj.Add("hint", libJobj["MMC-hint"]?.DeepClone());
|
|||
|
|
libJobj.Remove("MMC-hint");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
libs.Add(libJobj);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (patchJson["+libraries"] is not null)
|
|||
|
|
foreach (var Library in patchJson["+libraries"].AsArray()) // TODO: 此处处理不严谨,但也能用吧
|
|||
|
|
{
|
|||
|
|
if (Library is not JsonObject LibraryObj) continue;
|
|||
|
|
var libJobj = LibraryObj.DeepClone().AsObject();
|
|||
|
|
if (libJobj["MMC-hint"] is not null)
|
|||
|
|
{
|
|||
|
|
libJobj.Add("hint", libJobj["MMC-hint"]?.DeepClone());
|
|||
|
|
libJobj.Remove("MMC-hint");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
libs.Add(libJobj);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
libJson.Merge(libs);
|
|||
|
|
ModBase.Log($"[ModPack] 已应用 JSON-Patch {patchJson["uid"]} 的 Libraries");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Tweakers
|
|||
|
|
if (patchJson["+tweakers"] is not null)
|
|||
|
|
{
|
|||
|
|
tweakers = (string)patchJson["+tweakers"][0];
|
|||
|
|
ModBase.Log($"[ModPack] 已应用 JSON-Patch {patchJson["uid"]} 的 Tweakers");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// AssetIndex
|
|||
|
|
if (patchJson["assetIndex"] is not null)
|
|||
|
|
{
|
|||
|
|
assetIndex = patchJson["assetIndex"]?.DeepClone().AsObject();
|
|||
|
|
ModBase.Log($"[ModPack] 已应用 JSON-Patch {patchJson["uid"]} 的 AssetIndex");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// minecraftArguments -> arguments.game
|
|||
|
|
if (patchJson["minecraftArguments"] is not null)
|
|||
|
|
{
|
|||
|
|
foreach (var Arg in patchJson["minecraftArguments"].ToString().Split(" "))
|
|||
|
|
gameArguments.Add(Arg);
|
|||
|
|
packInfo.isMcArgsEdited = true;
|
|||
|
|
ModBase.Log(
|
|||
|
|
$"[ModPack] 已应用 JSON-Patch {patchJson["uid"]} 的 minecraftArguments 至 arguments.game");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// mainClass
|
|||
|
|
if (patchJson["mainClass"] is not null)
|
|||
|
|
{
|
|||
|
|
mainClass = (string)patchJson["mainClass"];
|
|||
|
|
ModBase.Log($"[ModPack] 已应用 JSON-Patch {patchJson["uid"]} 的 mainClass");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Java 版本要求
|
|||
|
|
if (patchJson["compatibleJavaMajors"] is not null)
|
|||
|
|
{
|
|||
|
|
var javaVersion = 0;
|
|||
|
|
string javaComponent = null;
|
|||
|
|
var javaMajors = (JsonArray)patchJson["compatibleJavaMajors"];
|
|||
|
|
foreach (var Java in javaMajors)
|
|||
|
|
{
|
|||
|
|
if (javaVersion > ModBase.Val(Java))
|
|||
|
|
continue;
|
|||
|
|
// 优先选择主要的版本
|
|||
|
|
if (ModBase.Val(Java) == 21d)
|
|||
|
|
{
|
|||
|
|
javaVersion = 21;
|
|||
|
|
javaComponent = "java-runtime-delta";
|
|||
|
|
}
|
|||
|
|
else if (ModBase.Val(Java) == 17d)
|
|||
|
|
{
|
|||
|
|
javaVersion = 17;
|
|||
|
|
javaComponent = "java-runtime-gamma";
|
|||
|
|
}
|
|||
|
|
else if (ModBase.Val(Java) == 11d)
|
|||
|
|
{
|
|||
|
|
javaVersion = 11;
|
|||
|
|
javaComponent = null;
|
|||
|
|
}
|
|||
|
|
else if (ModBase.Val(Java) == 8d)
|
|||
|
|
{
|
|||
|
|
javaVersion = 8;
|
|||
|
|
javaComponent = "jre-legacy";
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (javaVersion == 0)
|
|||
|
|
{
|
|||
|
|
javaVersion = (int)javaMajors[0];
|
|||
|
|
javaComponent = null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
javaVerJson = new JsonObject { { "majorVersion", javaVersion } };
|
|||
|
|
if (javaComponent is not null) javaVerJson.Add("component", javaComponent);
|
|||
|
|
ModBase.Log($"[ModPack] JSON-Patch {patchJson["uid"]} 要求 Java 版本: " + javaVersion);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
JsonObject jsonArguments = null;
|
|||
|
|
if (!string.IsNullOrWhiteSpace(tweakers))
|
|||
|
|
{
|
|||
|
|
gameArguments.Add("--tweakClass");
|
|||
|
|
gameArguments.Add(tweakers);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (gameArguments is not null || jvmArguments is not null)
|
|||
|
|
{
|
|||
|
|
jvmArguments.Insert(0, "-Djava.library.path=${natives_directory}");
|
|||
|
|
jvmArguments.Insert(1, "-Dminecraft.launcher.brand=${launcher_name}");
|
|||
|
|
jvmArguments.Insert(2, "-Dminecraft.launcher.version=${launcher_version}");
|
|||
|
|
jvmArguments.Insert(3, "-cp");
|
|||
|
|
jvmArguments.Insert(4, "${classpath}");
|
|||
|
|
jsonArguments = new JsonObject { { "game", gameArguments }, { "jvm", jvmArguments } };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
packInfo.overridedJson = new JsonObject();
|
|||
|
|
if (jsonArguments is not null)
|
|||
|
|
packInfo.overridedJson.Add("arguments", jsonArguments);
|
|||
|
|
if (mainClass is not null)
|
|||
|
|
packInfo.overridedJson.Add("mainClass", mainClass);
|
|||
|
|
if (assetIndex is not null)
|
|||
|
|
packInfo.overridedJson.Add("assetIndex", assetIndex);
|
|||
|
|
if (javaVerJson is not null)
|
|||
|
|
packInfo.overridedJson.Add("javaVersion", javaVerJson);
|
|||
|
|
if (libJson is not null)
|
|||
|
|
packInfo.overridedJson.Add("libraries", libJson);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
ModBase.Log(ex, "应用 MMC JSON-Patches 失败");
|
|||
|
|
}
|
|||
|
|
} while (false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
throw new Exception("MMC 整合包安装信息存在问题", ex);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取实例名
|
|||
|
|
var instanceName = packInstance.RegexSeek(@"(?<=\nname\=)[^\n]+") ?? "";
|
|||
|
|
var validate = new FolderNameValidator(Path.Combine(ModFolder.mcFolderSelected, "versions"));
|
|||
|
|
if (!validate.Validate(instanceName).IsValid)
|
|||
|
|
instanceName = "";
|
|||
|
|
if (string.IsNullOrEmpty(instanceName))
|
|||
|
|
instanceName = ModMain.MyMsgBoxInput(Lang.Text("Minecraft.Download.Modpack.InputInstanceName"), "", "",
|
|||
|
|
[validate]);
|
|||
|
|
if (string.IsNullOrEmpty(instanceName))
|
|||
|
|
throw new ModBase.CancelledException();
|
|||
|
|
// 解压
|
|||
|
|
var installTemp = ModMain.RequestTaskTempFolder();
|
|||
|
|
var versionFolder = $@"{ModFolder.mcFolderSelected}versions\{instanceName}";
|
|||
|
|
var installLoaders = new List<LoaderBase>();
|
|||
|
|
installLoaders.Add(new LoaderTask<string, int>(Lang.Text("Minecraft.Download.Modpack.Stage.ExtractModpack"),
|
|||
|
|
task =>
|
|||
|
|
{
|
|||
|
|
ExtractModpackFiles(installTemp, fileAddress, task, 0.55d);
|
|||
|
|
CopyOverrideDirectory(Path.Combine(installTemp, archiveBaseFolder, "libraries"),
|
|||
|
|
Path.Combine(ModFolder.mcFolderSelected, "versions", instanceName, "libraries"), task, 0.2d);
|
|||
|
|
CopyOverrideDirectory(Path.Combine(installTemp, archiveBaseFolder, ".minecraft"),
|
|||
|
|
Path.Combine(ModFolder.mcFolderSelected, "versions", instanceName), task, 0.2d);
|
|||
|
|
|
|||
|
|
#region instance.cfg
|
|||
|
|
|
|||
|
|
// 读取 MMC 设置文件(#2655)
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var mMCSetupFile = Path.Combine(installTemp, archiveBaseFolder, "instance.cfg");
|
|||
|
|
// 将其中的等号替换为冒号,以符合 ini 文件格式
|
|||
|
|
if (File.Exists(mMCSetupFile))
|
|||
|
|
{
|
|||
|
|
List<string> lines = [];
|
|||
|
|
foreach (var Line in ModBase.ReadFile(mMCSetupFile).Split(new[] { "\r", "\n" },
|
|||
|
|
StringSplitOptions.RemoveEmptyEntries))
|
|||
|
|
{
|
|||
|
|
if (!Line.Contains("="))
|
|||
|
|
continue;
|
|||
|
|
lines.Add(Line.BeforeFirst("=") + ":" + Line.AfterFirst("="));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ModBase.WriteFile(mMCSetupFile, lines.Join("\r\n"));
|
|||
|
|
// 读取文件
|
|||
|
|
if (Convert.ToBoolean(ModBase.ReadIni(mMCSetupFile, "OverrideCommands",
|
|||
|
|
false.ToString())))
|
|||
|
|
{
|
|||
|
|
var preLaunchCommand = ModBase.ReadIni(mMCSetupFile, "PreLaunchCommand");
|
|||
|
|
if (!string.IsNullOrEmpty(preLaunchCommand))
|
|||
|
|
{
|
|||
|
|
preLaunchCommand = preLaunchCommand.Replace(@"\""", "\"")
|
|||
|
|
.Replace("$INST_JAVA", "{java}java.exe").Replace(@"$INST_MC_DIR\", "{minecraft}")
|
|||
|
|
.Replace("$INST_MC_DIR", "{minecraft}").Replace(@"$INST_DIR\", "{verpath}")
|
|||
|
|
.Replace("$INST_DIR", "{verpath}").Replace("$INST_ID", "{name}")
|
|||
|
|
.Replace("$INST_NAME", "{name}");
|
|||
|
|
Config.Instance.PreLaunchCommand[versionFolder] = preLaunchCommand;
|
|||
|
|
ModBase.Log("[ModPack] 迁移 MultiMC 实例独立设置:启动前执行命令:" + preLaunchCommand);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (Convert.ToBoolean(ModBase.ReadIni(mMCSetupFile, "JoinServerOnLaunch",
|
|||
|
|
false.ToString())))
|
|||
|
|
{
|
|||
|
|
var serverAddress = ModBase.ReadIni(mMCSetupFile, "JoinServerOnLaunchAddress")
|
|||
|
|
.Replace(@"\""", "\"");
|
|||
|
|
Config.Instance.ServerToEnter[versionFolder] = serverAddress;
|
|||
|
|
ModBase.Log("[ModPack] 迁移 MultiMC 实例独立设置:自动进入服务器:" + serverAddress);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (Convert.ToBoolean(ModBase.ReadIni(mMCSetupFile, "IgnoreJavaCompatibility",
|
|||
|
|
false.ToString())))
|
|||
|
|
{
|
|||
|
|
Config.Instance.IgnoreJavaCompatibility[versionFolder] = true;
|
|||
|
|
ModBase.Log("[ModPack] 迁移 MultiMC 实例独立设置:忽略 Java 兼容性警告");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var logo = Path.GetFileName(ModBase.ReadIni(mMCSetupFile, "iconKey"));
|
|||
|
|
if (!string.IsNullOrEmpty(logo) && File.Exists($"{installTemp}{archiveBaseFolder}{logo}.png"))
|
|||
|
|
{
|
|||
|
|
States.Instance.IsLogoCustom[versionFolder] = true;
|
|||
|
|
States.Instance.LogoPath[versionFolder] = @"PCL\Logo.png";
|
|||
|
|
ModBase.CopyFile($"{installTemp}{archiveBaseFolder}{logo}.png",
|
|||
|
|
$@"{ModFolder.mcFolderSelected}versions\{instanceName}\PCL\Logo.png");
|
|||
|
|
ModBase.Log($"[ModPack] 迁移 MultiMC 实例独立设置:实例图标({logo}.png)");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// JVM 参数
|
|||
|
|
var jvmArgs = ModBase.ReadIni(mMCSetupFile, "JvmArgs");
|
|||
|
|
if (!string.IsNullOrEmpty(jvmArgs))
|
|||
|
|
{
|
|||
|
|
if (Convert.ToBoolean(ModBase.ReadIni(mMCSetupFile, "OverrideJavaArgs",
|
|||
|
|
false.ToString())))
|
|||
|
|
{
|
|||
|
|
Config.Instance.JvmArgs[versionFolder] = jvmArgs;
|
|||
|
|
ModBase.Log("[ModPack] 迁移 MultiMC 实例独立设置:JVM 参数(覆盖):" + jvmArgs);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
jvmArgs = jvmArgs +
|
|||
|
|
" " +
|
|||
|
|
Config.Launch.JvmArgs;
|
|||
|
|
Config.Instance.JvmArgs[versionFolder] = jvmArgs;
|
|||
|
|
ModBase.Log("[ModPack] 迁移 MultiMC 实例独立设置:JVM 参数(追加):" + jvmArgs);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
ModBase.Log(ex, $"读取 MMC 配置文件失败({installTemp}{archiveBaseFolder}instance.cfg)");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
})
|
|||
|
|
{
|
|||
|
|
ProgressWeight = new FileInfo(fileAddress).Length / 1024d / 1024d / 6d,
|
|||
|
|
block = false
|
|||
|
|
}); // 每 6M 需要 1s
|
|||
|
|
// 构造实例安装请求
|
|||
|
|
if (packJson["components"] is null)
|
|||
|
|
throw new Exception(Lang.Text("Minecraft.Download.Modpack.MissingGameVersion.Generic"));
|
|||
|
|
var request = new ModDownloadLib.McInstallRequest
|
|||
|
|
{
|
|||
|
|
targetInstanceName = instanceName,
|
|||
|
|
targetInstanceFolder = $@"{ModFolder.mcFolderSelected}versions\{instanceName}\"
|
|||
|
|
};
|
|||
|
|
foreach (var Component in packJson["components"].AsArray())
|
|||
|
|
switch ((Component["uid"] ?? "").ToString() ?? "")
|
|||
|
|
{
|
|||
|
|
case "org.lwjgl":
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[ModPack] 已跳过 LWJGL 项");
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
case "net.minecraft":
|
|||
|
|
{
|
|||
|
|
request.minecraftName = (string)Component["version"];
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
case "net.minecraftforge":
|
|||
|
|
{
|
|||
|
|
if (Component["version"].ToString().StartsWithF("0."))
|
|||
|
|
request.cleanroomVersion = (string)Component["version"];
|
|||
|
|
else
|
|||
|
|
request.forgeVersion = (string)Component["version"];
|
|||
|
|
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
case "net.neoforged":
|
|||
|
|
{
|
|||
|
|
request.neoForgeVersion = (string)Component["version"];
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
case "net.fabricmc.fabric-loader":
|
|||
|
|
{
|
|||
|
|
request.fabricVersion = (string)Component["version"];
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
case "org.quiltmc.quilt-loader":
|
|||
|
|
{
|
|||
|
|
throw new Exception(Lang.Text("Minecraft.Download.Modpack.QuiltUnsupported"));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (packInfo is not null)
|
|||
|
|
request.mmcPackInfo = packInfo;
|
|||
|
|
// 构造加载器
|
|||
|
|
var mergeLoaders = ModDownloadLib.McInstallLoader(request);
|
|||
|
|
// 构造总加载器
|
|||
|
|
var loaders = new List<LoaderBase>();
|
|||
|
|
loaders.Add(new LoaderCombo<string>(Lang.Text("Minecraft.Download.Modpack.Stage.ModpackInstall"),
|
|||
|
|
installLoaders)
|
|||
|
|
{ show = false, block = false, ProgressWeight = installLoaders.Sum(l => l.ProgressWeight) });
|
|||
|
|
loaders.Add(new LoaderCombo<string>(Lang.Text("Minecraft.Download.Modpack.Stage.GameInstall"), mergeLoaders)
|
|||
|
|
{ show = false, ProgressWeight = mergeLoaders.Sum(l => l.ProgressWeight) });
|
|||
|
|
|
|||
|
|
// 重复任务检查
|
|||
|
|
var loaderName = Lang.Text("Minecraft.Download.Modpack.Task.MmcInstall", instanceName);
|
|||
|
|
if (loaderTaskbar.Any(l => (l.name ?? "") == (loaderName ?? "")))
|
|||
|
|
{
|
|||
|
|
HintService.Hint(Lang.Text("Minecraft.Download.Modpack.Installing"), HintType.Error);
|
|||
|
|
throw new ModBase.CancelledException();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 启动
|
|||
|
|
var loader = new LoaderCombo<string>(loaderName, loaders) { OnStateChanged = ModDownloadLib.McInstallState };
|
|||
|
|
loader.Start(request.targetInstanceFolder);
|
|||
|
|
LoaderTaskbarAdd(loader);
|
|||
|
|
ModMain.frmMain.BtnExtraDownload.ShowRefresh();
|
|||
|
|
ModBase.RunInUi(() => ModMain.frmMain.PageChange(FormMain.PageType.TaskManager));
|
|||
|
|
return loader;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
}
|