488 lines
20 KiB
C#
488 lines
20 KiB
C#
using System.IO;
|
||||
|
|
using System.Text.Json;
|
|||
|
|
using PCL.Core.App;
|
|||
|
|
using PCL.Core.IO;
|
|||
|
|
using PCL.Core.Minecraft;
|
|||
|
|
using PCL.Core.Minecraft.Java.UserPreference;
|
|||
|
|
using PCL.Network;
|
|||
|
|
using PCL.Network.Loaders;
|
|||
|
|
using PCL.Core.App.Localization;
|
|||
|
|
using PCL.Core.Utils.OS;
|
|||
|
|
using PCL.Core.Utils;
|
|||
|
|
|
|||
|
|
namespace PCL;
|
|||
|
|
|
|||
|
|
public static class ModJava
|
|||
|
|
{
|
|||
|
|
public static int javaListCacheVersion = 7;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 防止多个需要 Java 的部分同时要求下载 Java(#3797)。
|
|||
|
|
/// </summary>
|
|||
|
|
public static object javaLock = new();
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 目前所有可用的 Java。
|
|||
|
|
/// </summary>
|
|||
|
|
public static JavaManager Javas => JavaService.JavaManager;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 根据要求返回最适合的 Java,若找不到则返回 Nothing。
|
|||
|
|
/// 最小与最大版本在与输入相同时也会通过。
|
|||
|
|
/// 必须在工作线程调用,且必须包括 SyncLock JavaLock。
|
|||
|
|
/// </summary>
|
|||
|
|
public static JavaEntry JavaSelect(string cancelException, Version minVersion = null, Version maxVersion = null,
|
|||
|
|
McInstance relatedInstance = null, bool enforceVersionRange = false)
|
|||
|
|
{
|
|||
|
|
ModBase.Log(
|
|||
|
|
$"[Java] 要求选择合适 Java,要求最低版本 {(minVersion is not null ? minVersion.ToString() : "未指定")},要求选择的最高版本 {(maxVersion is not null ? maxVersion.ToString() : "未指定")},关联实例 {(relatedInstance is not null ? relatedInstance.Name : "未指定")}");
|
|||
|
|
|
|||
|
|
// 版本范围验证函数(安全处理 null 边界)
|
|||
|
|
bool IsVersionSuitable(Version ver)
|
|||
|
|
{
|
|||
|
|
return (minVersion is null || ver >= minVersion) && (maxVersion is null || ver <= maxVersion);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===== 优先级 1:实例专属 Java 偏好 =====
|
|||
|
|
if (relatedInstance is not null && relatedInstance.PathInstance is not null)
|
|||
|
|
{
|
|||
|
|
var rawPreference = Config.Instance.SelectedJava[relatedInstance.PathInstance];
|
|||
|
|
|
|||
|
|
if (!string.IsNullOrWhiteSpace(rawPreference))
|
|||
|
|
{
|
|||
|
|
var preference = GetInstanceJavaPreference(relatedInstance);
|
|||
|
|
|
|||
|
|
// 处理解析成功的偏好
|
|||
|
|
if (preference is not null)
|
|||
|
|
switch (true)
|
|||
|
|
{
|
|||
|
|
case object _ when preference is ExistingJava: // "exist"
|
|||
|
|
{
|
|||
|
|
var existPref = (ExistingJava)preference;
|
|||
|
|
var candidate = Javas.AddOrGet(existPref.JavaExePath);
|
|||
|
|
|
|||
|
|
if (candidate is not null && candidate.IsEnabled)
|
|||
|
|
{
|
|||
|
|
if (!IsVersionSuitable(candidate.Installation.Version))
|
|||
|
|
HintService.Hint(_GetJavaRangeWarning(
|
|||
|
|
"Minecraft.Launch.Java.Compatibility.InstanceSelectedOutOfRange",
|
|||
|
|
candidate.Installation.Version,
|
|||
|
|
minVersion,
|
|||
|
|
maxVersion));
|
|||
|
|
ModBase.Log($"[Java] 返回实例 '{relatedInstance.Name}' 指定的 Java: {candidate}");
|
|||
|
|
return candidate;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ModBase.Log($"[Java] 警告:实例指定的 Java 路径无效或不可用: {existPref.JavaExePath}");
|
|||
|
|
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
case object _ when preference is UseRelativePath: // "relative"
|
|||
|
|
{
|
|||
|
|
var relPref = (UseRelativePath)preference;
|
|||
|
|
var absPath =
|
|||
|
|
Path.GetFullPath(Path.Combine(Basics.ExecutableDirectory, relPref.RelativePath));
|
|||
|
|
|
|||
|
|
if (Files.IsPathWithinDirectory(absPath, Basics.ExecutableDirectory))
|
|||
|
|
{
|
|||
|
|
var candidate = Javas.Get(absPath);
|
|||
|
|
if (candidate is not null && candidate.IsEnabled)
|
|||
|
|
{
|
|||
|
|
if (!IsVersionSuitable(candidate.Installation.Version))
|
|||
|
|
HintService.Hint(_GetJavaRangeWarning(
|
|||
|
|
"Minecraft.Launch.Java.Compatibility.RelativePathSelectedOutOfRange",
|
|||
|
|
candidate.Installation.Version,
|
|||
|
|
minVersion,
|
|||
|
|
maxVersion),
|
|||
|
|
HintType.Error);
|
|||
|
|
ModBase.Log(
|
|||
|
|
$"[Java] 返回实例 '{relatedInstance.Name}' 相对路径指定的 Java ({relPref.RelativePath}): {candidate}");
|
|||
|
|
return candidate;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
ModBase.Log($"[Java] 警告:实例相对路径指定的 Java 无效: {absPath}");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
case object _ when preference is UseGlobalPreference: // "global"
|
|||
|
|
{
|
|||
|
|
// 不返回,继续到全局设置检查
|
|||
|
|
ModBase.Log($"[Java] 实例 '{relatedInstance.Name}' 配置为使用全局 Java 设置,继续检查全局配置");
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
default:
|
|||
|
|
{
|
|||
|
|
ModBase.Log($"[Java] 警告:未知的 Java 偏好类型 '{preference}',跳过处理");
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
ModBase.Log($"[Java] 实例 '{relatedInstance.Name}' 未指定 Java 偏好(空值),使用自动选择策略");
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
ModBase.Log($"[Java] 实例 '{relatedInstance.Name}' 无 Java 偏好配置,使用自动选择策略");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===== 优先级 2:全局指定的 Java =====
|
|||
|
|
var globalJavaPath = Config.Launch.SelectedJava;
|
|||
|
|
if (!string.IsNullOrWhiteSpace(globalJavaPath))
|
|||
|
|
{
|
|||
|
|
globalJavaPath = globalJavaPath.Trim();
|
|||
|
|
var candidate = Javas.AddOrGet(globalJavaPath);
|
|||
|
|
|
|||
|
|
if (candidate is not null && candidate.IsEnabled)
|
|||
|
|
{
|
|||
|
|
var versionSuitable = IsVersionSuitable(candidate.Installation.Version);
|
|||
|
|
if (enforceVersionRange && !versionSuitable)
|
|||
|
|
{
|
|||
|
|
ModBase.Log($"[Java] 全局指定的 Java 版本不满足强制范围要求,忽略并继续自动搜索: {candidate}");
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
if (!versionSuitable)
|
|||
|
|
HintService.Hint(_GetJavaRangeWarning(
|
|||
|
|
"Minecraft.Launch.Java.Compatibility.GlobalSelectedOutOfRange",
|
|||
|
|
candidate.Installation.Version,
|
|||
|
|
minVersion,
|
|||
|
|
maxVersion));
|
|||
|
|
ModBase.Log($"[Java] 返回全局指定的 Java: {candidate}");
|
|||
|
|
return candidate;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
ModBase.Log($"[Java] 警告:全局指定的 Java 路径无效或不可用: {globalJavaPath}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[Java] 无全局 Java 配置,使用自动选择策略");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===== 优先级 3:自动搜索合适版本 =====
|
|||
|
|
ModBase.Log("[Java] 开始自动搜索符合版本要求的 Java 运行时");
|
|||
|
|
Javas.CheckAllAvailability();
|
|||
|
|
|
|||
|
|
var reqMin = minVersion ?? new Version(1, 0, 0);
|
|||
|
|
var reqMax = maxVersion ?? new Version(999, 999, 999);
|
|||
|
|
|
|||
|
|
var candidates = Javas.SelectSuitableJavaAsync(reqMin, reqMax).GetAwaiter().GetResult();
|
|||
|
|
var ret = candidates.FirstOrDefault();
|
|||
|
|
|
|||
|
|
if (ret is null && candidates.Length == 0)
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[Java] 未找到符合版本要求的 Java,触发全盘重新扫描");
|
|||
|
|
Javas.ScanJavaAsync().GetAwaiter().GetResult();
|
|||
|
|
candidates = Javas.SelectSuitableJavaAsync(reqMin, reqMax).GetAwaiter().GetResult();
|
|||
|
|
ret = candidates.FirstOrDefault();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (ret is not null)
|
|||
|
|
ModBase.Log($"[Java] 返回自动选择的 Java: {ret}");
|
|||
|
|
else
|
|||
|
|
ModBase.Log("[Java] 最终未能确定可用的 Java 运行时");
|
|||
|
|
|
|||
|
|
return ret;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static string _GetJavaRangeWarning(
|
|||
|
|
string key,
|
|||
|
|
Version selectedVersion,
|
|||
|
|
Version? minVersion,
|
|||
|
|
Version? maxVersion)
|
|||
|
|
{
|
|||
|
|
return Lang.Text(
|
|||
|
|
key,
|
|||
|
|
selectedVersion,
|
|||
|
|
minVersion?.ToString() ?? Lang.Text("Minecraft.Launch.Java.Compatibility.NoMinimum"),
|
|||
|
|
maxVersion?.ToString() ?? Lang.Text("Minecraft.Launch.Java.Compatibility.NoMaximum"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static JavaPreference GetInstanceJavaPreference(McInstance instance)
|
|||
|
|
{
|
|||
|
|
var rawPreference = Config.Instance.SelectedJava[instance.PathInstance];
|
|||
|
|
|
|||
|
|
JavaPreference preference = default;
|
|||
|
|
|
|||
|
|
// 尝试读取 JSON 配置
|
|||
|
|
if (!string.IsNullOrEmpty(rawPreference))
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
preference = JsonSerializer.Deserialize<JavaPreference>(rawPreference, JsonCompat.SerializerOptions);
|
|||
|
|
}
|
|||
|
|
catch (JsonException)
|
|||
|
|
{
|
|||
|
|
// ignored
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 以旧方式读取配置
|
|||
|
|
if (preference is null)
|
|||
|
|
{
|
|||
|
|
var trimmed = rawPreference?.Trim();
|
|||
|
|
if (string.IsNullOrEmpty(trimmed))
|
|||
|
|
{
|
|||
|
|
preference = new AutoSelect();
|
|||
|
|
}
|
|||
|
|
else if (trimmed == "使用全局设置")
|
|||
|
|
{
|
|||
|
|
preference = new UseGlobalPreference();
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
preference = new ExistingJava(trimmed);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
switch (true)
|
|||
|
|
{
|
|||
|
|
case object _ when preference is ExistingJava:
|
|||
|
|
{
|
|||
|
|
var m = (ExistingJava)preference;
|
|||
|
|
if (!Path.IsPathRooted(m.JavaExePath)) preference = new UseGlobalPreference();
|
|||
|
|
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
case object _ when preference is UseRelativePath:
|
|||
|
|
{
|
|||
|
|
var m = (UseRelativePath)preference;
|
|||
|
|
if (!Files.IsPathWithinDirectory(m.RelativePath, Basics.ExecutableDirectory))
|
|||
|
|
preference = new UseGlobalPreference();
|
|||
|
|
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return preference;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 是否强制指定了 64 位 Java。如果没有强制指定,返回是否安装了 64 位 Java。
|
|||
|
|
/// </summary>
|
|||
|
|
public static bool IsGameSet64BitJava(McInstance relatedVersion = null)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 检查强制指定
|
|||
|
|
var userSetup = Config.Launch.SelectedJava;
|
|||
|
|
if (userSetup.StartsWith("{")) // 旧版本 Json 格式
|
|||
|
|
{
|
|||
|
|
var js = ModBase.GetJson(userSetup);
|
|||
|
|
userSetup = $"{js["Path"]}java.exe";
|
|||
|
|
Config.Launch.SelectedJava = userSetup;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (relatedVersion is not null)
|
|||
|
|
{
|
|||
|
|
var instancePreference = GetInstanceJavaPreference(relatedVersion);
|
|||
|
|
switch (true)
|
|||
|
|
{
|
|||
|
|
case object _ when instancePreference is AutoSelect:
|
|||
|
|
{
|
|||
|
|
return Javas.Existing64BitJava();
|
|||
|
|
}
|
|||
|
|
case object _ when instancePreference is ExistingJava:
|
|||
|
|
{
|
|||
|
|
var m = (ExistingJava)instancePreference;
|
|||
|
|
var java = Javas.AddOrGet(m.JavaExePath);
|
|||
|
|
return java is not null && java.Installation.Is64Bit;
|
|||
|
|
}
|
|||
|
|
case object _ when instancePreference is UseRelativePath:
|
|||
|
|
{
|
|||
|
|
var m = (UseRelativePath)instancePreference;
|
|||
|
|
var javaExePath = Path.GetFullPath(m.RelativePath);
|
|||
|
|
if (Files.IsPathWithinDirectory(javaExePath, Basics.ExecutableDirectory))
|
|||
|
|
{
|
|||
|
|
var java = Javas.Get(javaExePath);
|
|||
|
|
return java is not null && java.Installation.Is64Bit;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!string.IsNullOrEmpty(userSetup) && !File.Exists(userSetup))
|
|||
|
|
{
|
|||
|
|
Config.Launch.SelectedJava = "";
|
|||
|
|
userSetup = string.Empty;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (string.IsNullOrEmpty(userSetup)) return Javas.Existing64BitJava();
|
|||
|
|
var j = Javas.AddOrGet(userSetup);
|
|||
|
|
return j is not null && j.Installation.Is64Bit;
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
ModBase.Log(
|
|||
|
|
ex,
|
|||
|
|
"检查 Java 类别时出错",
|
|||
|
|
ModBase.LogLevel.Feedback,
|
|||
|
|
userSummary: Lang.Text("Minecraft.Launch.Java.Compatibility.CheckFailed"));
|
|||
|
|
if (relatedVersion is not null)
|
|||
|
|
Config.Instance.SelectedJava[relatedVersion.PathInstance] = "使用全局设置";
|
|||
|
|
Config.Launch.SelectedJava = "";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#region 下载
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 提示 Java 缺失,并弹窗确认是否自动下载。返回玩家选择是否下载。
|
|||
|
|
/// </summary>
|
|||
|
|
public static bool JavaDownloadConfirm(string versionDescription, bool forcedManualDownload = false)
|
|||
|
|
{
|
|||
|
|
if (!forcedManualDownload)
|
|||
|
|
return ModMain.MyMsgBox(
|
|||
|
|
Lang.Text("Minecraft.Launch.Java.AutoDownload.Message", versionDescription),
|
|||
|
|
Lang.Text("Minecraft.Launch.Java.AutoDownload.Title"),
|
|||
|
|
Lang.Text("Minecraft.Launch.Java.AutoDownload.Action"),
|
|||
|
|
Lang.Text("Common.Action.Cancel")) == 1;
|
|||
|
|
|
|||
|
|
ModMain.MyMsgBox(
|
|||
|
|
Lang.Text("Minecraft.Launch.Java.NotFound.Manual.Message", versionDescription),
|
|||
|
|
Lang.Text("Minecraft.Launch.Java.NotFound.Title"));
|
|||
|
|
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 获取下载 Java 的加载器。需要开启 IsForceRestart 以正常刷新 Java 列表。
|
|||
|
|
/// </summary>
|
|||
|
|
public static ModLoader.LoaderCombo<string> GetJavaDownloadLoader()
|
|||
|
|
{
|
|||
|
|
var javaDownloadLoader = new LoaderDownload(
|
|||
|
|
Lang.Text("Minecraft.Launch.Java.Task.DownloadFiles"),
|
|||
|
|
[])
|
|||
|
|
{
|
|||
|
|
ProgressWeight = 10d
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
var loader = new ModLoader.LoaderCombo<string>(
|
|||
|
|
Lang.Text("Minecraft.Launch.Java.Task.Download"),
|
|||
|
|
[
|
|||
|
|
new ModLoader.LoaderTask<string, List<DownloadFile>>(
|
|||
|
|
Lang.Text("Minecraft.Launch.Java.Task.GetDownloadInfo"),
|
|||
|
|
JavaFileList)
|
|||
|
|
{
|
|||
|
|
ProgressWeight = 2d
|
|||
|
|
},
|
|||
|
|
javaDownloadLoader
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
javaDownloadLoader.OnStateChangedThread += (_, newState, _) =>
|
|||
|
|
{
|
|||
|
|
switch (newState)
|
|||
|
|
{
|
|||
|
|
case ModBase.LoadState.Failed or ModBase.LoadState.Aborted
|
|||
|
|
when lastJavaBaseDir is not null:
|
|||
|
|
ModBase.Log(
|
|||
|
|
$"[Java] 由于下载未完成,清理未下载完成的 Java 文件:{lastJavaBaseDir}",
|
|||
|
|
ModBase.LogLevel.Debug);
|
|||
|
|
|
|||
|
|
ModBase.DeleteDirectory(lastJavaBaseDir);
|
|||
|
|
break;
|
|||
|
|
case ModBase.LoadState.Finished:
|
|||
|
|
Javas.ScanJavaAsync().GetAwaiter().GetResult();
|
|||
|
|
lastJavaBaseDir = null;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
javaDownloadLoader.hasOnStateChangedThread = true;
|
|||
|
|
return loader;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static string lastJavaBaseDir; // 用于在下载中断或失败时删除未完成下载的 Java 文件夹,防止残留只下了一半但 -version 能跑的 Java
|
|||
|
|
|
|||
|
|
private static readonly HashSet<string> ignoreHash = new[]
|
|||
|
|
{
|
|||
|
|
"12976a6c2b227cbac58969c1455444596c894656", "c80e4bab46e34d02826eab226a4441d0970f2aba",
|
|||
|
|
"84d2102ad171863db04e7ee22a259d1f6c5de4a5"
|
|||
|
|
}.ToHashSet();
|
|||
|
|
|
|||
|
|
private static void JavaFileList(ModLoader.LoaderTask<string, List<DownloadFile>> loader)
|
|||
|
|
{
|
|||
|
|
ModBase.Log("[Java] 开始获取 Java 下载信息");
|
|||
|
|
var indexFileStr = ModNet.NetGetCodeByLoader(
|
|||
|
|
ModDownload.DlVersionListOrder(
|
|||
|
|
new[]
|
|||
|
|
{
|
|||
|
|
"https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"
|
|||
|
|
},
|
|||
|
|
new[]
|
|||
|
|
{
|
|||
|
|
"https://bmclapi2.bangbang93.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"
|
|||
|
|
}), isJson: true);
|
|||
|
|
// 查找要下载的目标 Java
|
|||
|
|
string? targetName = null;
|
|||
|
|
JsonNode? targetValue = null;
|
|||
|
|
var components =
|
|||
|
|
(JsonObject)((JsonObject)ModBase.GetJson(indexFileStr))[$"windows-x{(SystemInfo.Is32BitSystem ? "86" : "64")}"];
|
|||
|
|
if (components.ContainsKey(loader.input)) // 精确匹配
|
|||
|
|
{
|
|||
|
|
targetName = loader.input;
|
|||
|
|
targetValue = components[loader.input];
|
|||
|
|
}
|
|||
|
|
else // 模糊匹配
|
|||
|
|
{
|
|||
|
|
var match = components.FirstOrDefault(c =>
|
|||
|
|
c.Value?.AsArray().FirstOrDefault()?["version"]?["name"]?.ToString().StartsWithF(loader.input) ?? false);
|
|||
|
|
targetName = match.Key;
|
|||
|
|
targetValue = match.Value;
|
|||
|
|
if (targetName is null)
|
|||
|
|
throw new Exception($"未能找到所需的 Java {loader.input}");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var targetComponent = targetValue?.AsArray().FirstOrDefault();
|
|||
|
|
if (targetComponent is null)
|
|||
|
|
throw new Exception($"Mojang 未提供所需的 Java {loader.input}");
|
|||
|
|
// 获取文件列表
|
|||
|
|
var address = (string)targetComponent["manifest"]["url"];
|
|||
|
|
ModLaunch.McLaunchLog($"准备下载 Java {targetComponent["version"]["name"]}({targetName}):{address}");
|
|||
|
|
var listFileStr = (JsonObject)Requester.FetchJson(
|
|||
|
|
ModDownload.DlSourceOrder(new[] { address },
|
|||
|
|
new[] { address.Replace("piston-meta.mojang.com", "bmclapi2.bangbang93.com") }).First(), RequestParam.WithRetry);
|
|||
|
|
lastJavaBaseDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
|||
|
|
".minecraft", "runtime", targetName);
|
|||
|
|
var results = new List<DownloadFile>(listFileStr["files"].AsObject().Count);
|
|||
|
|
foreach (var File in listFileStr["files"].AsObject())
|
|||
|
|
{
|
|||
|
|
if (File.Value?.AsObject()?["downloads"]?["raw"] is null)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
var info = File.Value["downloads"]["raw"].AsObject();
|
|||
|
|
var checkHash = info["sha1"];
|
|||
|
|
if (ignoreHash.Contains((string)checkHash))
|
|||
|
|
continue; // 跳过 3 个无意义大量重复文件(#3827)
|
|||
|
|
|
|||
|
|
var checker = new ModBase.FileChecker(actualSize: (long)info["size"], hash: (string)info["sha1"]);
|
|||
|
|
var filePath = Path.GetFullPath(Path.Combine(lastJavaBaseDir, File.Key));
|
|||
|
|
if (!Files.IsPathWithinDirectory(filePath, lastJavaBaseDir))
|
|||
|
|
throw new Exception($"{filePath} 不在 {lastJavaBaseDir} 中");
|
|||
|
|
|
|||
|
|
if (checker.Check(filePath) is null)
|
|||
|
|
continue; // 跳过已存在的文件
|
|||
|
|
var url = (string)info["url"];
|
|||
|
|
results.Add(new DownloadFile(
|
|||
|
|
ModDownload.DlSourceOrder(new[] { url },
|
|||
|
|
new[] { url.Replace("piston-data.mojang.com", "bmclapi2.bangbang93.com") }), filePath, checker));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
loader.output = results;
|
|||
|
|
ModBase.Log($"[Java] 需要下载 {results.Count} 个文件,目标文件夹:{lastJavaBaseDir}");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
}
|