CI / Go Backend (push) Canceled after 0s
初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器 HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块 Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚 AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
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
|
||
}
|