Compare commits

..
16 Commits
Author SHA1 Message Date
Coldsmile_7 216b91f784 chore: initialize PCL-ISE 0.1.0
Build / Build (Debug) (push) Waiting to run
Build / Build (Release) (push) Waiting to run
2026-10-21 11:49:36 +08:00
LTCat 6b9a50df91 2.99.0.0 正式版 2026-10-20 14:51:42 +08:00
LTCat e4b0489a22 2.99.0.0 2026-10-20 14:33:23 +08:00
LTCat 5ef81e490a 2.14.0.0 正式版 2026-10-19 23:50:04 +08:00
LTCat ade6e04c6e 2.14.0.0 2026-10-17 03:39:13 +08:00
HerobrineXia 9dcb799fb0 Merge pull request #9452 from camileko/close-9450
fix: 修复没有 单个自定义事件 的情况下无法触发自定义事件列表
2026-10-14 21:51:02 +08:00
HerobrineXia 073546ba19 Merge pull request #9447 from camileko/close-9442
fix: 联网主页地址含有替换标记时主页无法显示
2026-10-14 19:51:45 +08:00
HerobrineXia f8584954b8 Merge pull request #9469 from RyogiMutsuki/fix/naudio/license
fix(license): 修复 NAudio 许可证问题
2026-10-13 02:25:52 +08:00
HerobrineXia a91d5e0079 Merge pull request #9445 from camileko/close-9254
fix: 修复窗口大小为最大化时启动高版本 Minecraft 会出现 Hidden Utility Window
2026-10-13 02:18:35 +08:00
HerobrineXia 5dcbd570a8 Merge pull request #9414 from camileko/close-9413
feat(cape): 添加 Aurora Cape 披风翻译
2026-10-13 00:34:31 +08:00
Ryogi Mutsuki 87a89e877d merge
fix(license): use hash ref to fixed license
2026-10-11 01:57:46 +08:00
Ryogi Mutsuki bc4ed0797f fix(license): use hash ref to fixed license 2026-10-11 01:56:11 +08:00
camileko b4a8193bae 修复没有单个自定义事件的情况下无法触发自定义事件列表 2026-10-06 00:09:53 +08:00
camileko 0a3258974a 联网主页地址含有替换标记时主页无法显示 2026-10-05 04:33:43 +08:00
unknown d69ef35547 把 Hidden Utility Window 加进窗口名黑名单 2026-10-05 00:18:13 +08:00
unknown 1ca8b78388 quick fix 2026-09-29 21:20:27 +08:00
87 changed files with 6939 additions and 2720 deletions
+34
View File
@@ -0,0 +1,34 @@
[CmdletBinding()]
param(
[ValidateSet('Debug', 'Release', 'Snapshot')]
[string]$Configuration = 'Debug'
)
$ErrorActionPreference = 'Stop'
$solution = Join-Path $PSScriptRoot '..\Plain Craft Launcher 2.sln'
$solution = [IO.Path]::GetFullPath($solution)
$msbuild = (Get-Command msbuild.exe -ErrorAction SilentlyContinue).Source
if (-not $msbuild) {
$vswhere = @(
(Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vswhere.exe'),
(Join-Path $env:ProgramFiles 'Microsoft Visual Studio\Installer\vswhere.exe')
) | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($vswhere) {
$installationPath = & $vswhere -latest -products * -requires Microsoft.Component.MSBuild -property installationPath
if ($installationPath) {
$candidate = Join-Path $installationPath 'MSBuild\Current\Bin\MSBuild.exe'
if (Test-Path $candidate) { $msbuild = $candidate }
}
}
}
if (-not $msbuild) {
throw 'MSBuild was not found. Install Visual Studio Build Tools with the .NET desktop build tools and .NET Framework 4.8 targeting pack.'
}
Write-Host "MSBuild: $msbuild"
Write-Host "Configuration: $Configuration"
& $msbuild $solution /t:Build "/p:Configuration=$Configuration" "/p:Platform=Any CPU" /m /v:minimal
exit $LASTEXITCODE
+6
View File
@@ -0,0 +1,6 @@
{
"recommendations": [
"ms-dotnettools.csharp",
"ms-dotnettools.csdevkit"
]
}
+3
View File
@@ -0,0 +1,3 @@
{
"dotnet.defaultSolution": "Plain Craft Launcher 2.sln"
}
+39
View File
@@ -0,0 +1,39 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Build PCL-ISE (Debug)",
"type": "shell",
"command": "powershell.exe",
"args": [
"-NoProfile",
"-ExecutionPolicy",
"Bypass",
"-File",
"${workspaceFolder}\\.vscode\\build.ps1",
"-Configuration",
"Debug"
],
"problemMatcher": "$msCompile",
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "Build PCL-ISE (Release)",
"type": "shell",
"command": "powershell.exe",
"args": [
"-NoProfile",
"-ExecutionPolicy",
"Bypass",
"-File",
"${workspaceFolder}\\.vscode\\build.ps1",
"-Configuration",
"Release"
],
"problemMatcher": "$msCompile"
}
]
}
+8
View File
@@ -1,6 +1,10 @@
namespace PCLCS; namespace PCLCS;
public static class Configs { public static class Configs {
public static readonly DynamicConfigEntry<McInstallManifest> McInstallManifest = new("MinecraftInstallManifest", null);
// Java
/// <summary> 当前 Java 相关配置的版本。0 代表尚未从老的 Settings 系统中迁移。</summary> /// <summary> 当前 Java 相关配置的版本。0 代表尚未从老的 Settings 系统中迁移。</summary>
public static readonly ConfigEntry<int> JavaConfigVersion = new("JavaConfigVersion", 0); public static readonly ConfigEntry<int> JavaConfigVersion = new("JavaConfigVersion", 0);
public static readonly ConfigEntry<ConcurrentList<Java>> JavaList = new("JavaList", []); public static readonly ConfigEntry<ConcurrentList<Java>> JavaList = new("JavaList", []);
@@ -8,6 +12,10 @@ public static class Configs {
public static readonly DynamicConfigEntry<bool> JavaMigrated = new("InstanceMigratedJava", false); public static readonly DynamicConfigEntry<bool> JavaMigrated = new("InstanceMigratedJava", false);
public static readonly DynamicConfigEntry<Java> JavaForced = new("InstanceForcedJava", null); public static readonly DynamicConfigEntry<Java> JavaForced = new("InstanceForcedJava", null);
// 提示
public static readonly ConfigEntry<bool> HintModifyInstanceWarning = new("HintModifyInstanceWarning", false);
static Configs() { static Configs() {
/// 当 JavaForced 获取到一个不在 JavaList 中的值时,将其重置为 null。 /// 当 JavaForced 获取到一个不在 JavaList 中的值时,将其重置为 null。
JavaForced.PreviewGet += (args, provider) => { JavaForced.PreviewGet += (args, provider) => {
+6 -2
View File
@@ -68,13 +68,17 @@ public static class Versions {
/// <summary> /// <summary>
/// 版本管理中 Mod 信息缓存的版本号。 /// 版本管理中 Mod 信息缓存的版本号。
/// </summary> /// </summary>
public const int LocalModCacheVersion = 19; public const int LocalModCacheVersion = 21;
/// <summary> /// <summary>
/// Minecraft 本地实例信息缓存的版本号。 /// Minecraft 本地实例信息缓存的版本号。
/// </summary> /// </summary>
public const int McInstanceCacheVersion = 38; public const int McInstanceCacheVersion = 39;
/// <summary> /// <summary>
/// Java 相关配置的版本号。 /// Java 相关配置的版本号。
/// </summary> /// </summary>
public const int JavaConfigVersion = 1; public const int JavaConfigVersion = 1;
/// <summary>
/// Minecraft 安装清单的版本号。
/// </summary>
public const int McInstallManifestVersion = 1;
} }
+3 -3
View File
@@ -87,9 +87,9 @@ namespace PCLCS;
// 相同检查 // 相同检查
public bool Equals(Java? other) public bool Equals(Java? other)
=> other is not null && string.Equals(Folder, other.Folder, StringComparison.OrdinalIgnoreCase); => other is not null && PathUtils.IsSame(Folder, other.Folder);
public override bool Equals(object? obj) => obj is Java other && Equals(other); public override bool Equals(object? obj) => obj is Java other && Equals(other);
public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Folder); public override int GetHashCode() => StringComparer.CurrentCultureIgnoreCase.GetHashCode(PathUtils.ForCompare(Folder));
/// <summary>用户友好的描述文本。</summary> /// <summary>用户友好的描述文本。</summary>
public override string ToString() public override string ToString()
@@ -211,7 +211,7 @@ public static class JavaUtils {
} }
// 结果 // 结果
var hasReparsePoint = HasReparsePoint(FileUtils.GetInfo(Path.Combine(targetFolder, "java.exe"))); var hasReparsePoint = HasReparsePoint(FileUtils.GetInfo(Path.Combine(targetFolder, "java.exe")));
var isSpecialPath = targetFolder.Contains("java8path_target_") || targetFolder.Contains("javapath_target_") || targetFolder.Contains("javatmp") || targetFolder.Contains("system32"); var isSpecialPath = targetFolder.ContainsIgnoreCase("java8path_target_") || targetFolder.ContainsIgnoreCase("javapath_target_") || targetFolder.ContainsIgnoreCase("javatmp") || targetFolder.ContainsIgnoreCase("system32");
results.Add((targetFolder, hasReparsePoint, isSpecialPath)); results.Add((targetFolder, hasReparsePoint, isSpecialPath));
} }
} catch (UnauthorizedAccessException) { } catch (UnauthorizedAccessException) {
+47
View File
@@ -0,0 +1,47 @@
using System.Globalization;
namespace PCLCS;
public static class McUtils {
/// <summary>
/// 比较两个版本名;左侧较新返回正数,相同返回 0,右侧较新返回负数。
/// 无法比较两个预发布版的大小。
/// 支持的格式:未知版本, 26.1-snapshot-1,1.13.2, 1.7.10-pre4, 1.8_pre, 1.14 Pre-Release 2, 1.14.4 C6
/// </summary>
public static int CompareVersion(string left, string right) {
if (left == right) return 0;
if (left == "未知版本" || right == "未知版本") return left == "未知版本" ? 1 : -1;
left = left.Lower();
right = right.Lower();
var leftParts = left.Replace("快照", "snapshot").Replace("预览版", "pre").RegexSearch("[a-z]+|[0-9]+").ToList();
var rightParts = right.Replace("快照", "snapshot").Replace("预览版", "pre").RegexSearch("[a-z]+|[0-9]+").ToList();
for (int i = 0; ; i++) {
if (i >= leftParts.Count && i >= rightParts.Count) return string.Compare(left, right, StringComparison.Ordinal); // 两边均缺失,感觉是一个东西
string leftValue = i < leftParts.Count ? leftParts[i] : "0";
string rightValue = i < rightParts.Count ? rightParts[i] : "0";
if (leftValue == rightValue) continue;
int leftNumber = leftValue switch {
"rc" => -1,
"pre" => -2,
"snapshot" => -3,
"experimental" => -4,
_ => int.TryParse(leftValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out int leftResult) ? leftResult : 0
};
int rightNumber = rightValue switch {
"rc" => -1,
"pre" => -2,
"snapshot" => -3,
"experimental" => -4,
_ => int.TryParse(rightValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out int rightResult) ? rightResult : 0
};
if (leftNumber == 0 && rightNumber == 0) return string.Compare(leftValue, rightValue, StringComparison.Ordinal);
if (leftNumber != rightNumber) return leftNumber.CompareTo(rightNumber);
}
}
/// <summary>
/// 比较两个版本名;左侧不旧于右侧返回 True。
/// </summary>
public static bool CompareVersionGE(string left, string right) => CompareVersion(left, right) >= 0;
}
+71
View File
@@ -0,0 +1,71 @@
namespace PCLCS;
/// <summary>
/// MC 版本安装清单及 PCL 管理文件清单。
/// </summary>
/// <param name="SchemaVersion"> 清单格式版本;无版本的旧清单为 0。</param>
/// <param name="Components"> 安装目标及受管理组件版本。</param>
/// <param name="LastModifiedUtc"> 最后修改时间(UTC)。</param>
/// <param name="ManagedFiles"> 受管理文件;没有文件时为空列表。</param>
public record McInstallManifest(int SchemaVersion, McComponents Components, DateTime LastModifiedUtc, IReadOnlyList<McInstallManifest.ManagedFile> ManagedFiles) {
/// <summary>
/// 清理以 Mod 形式安装的、已删除或修改的组件的记录,并更新清单。
/// </summary>
public McInstallManifest RefreshModComponents(string gameFolder) {
List<ManagedFile> invalidFiles = [];
foreach (ManagedFile entry in ManagedFiles) {
if (entry.Component is null) continue; // 不检查非组件文件
FileInfo file = new(Path.Combine(gameFolder, entry.RelativePath));
if (!file.Exists || file.LastWriteTimeUtc != entry.LastWriteTimeUtc.ToUniversalTime()) invalidFiles.Add(entry);
}
if (!invalidFiles.Any()) return this;
return this with {
Components = Components with {
OptiFineEntry = invalidFiles.Any(file => file.Component == "OptiFine") ? null : Components.OptiFineEntry,
FabricApiVersion = invalidFiles.Any(file => file.Component == "FabricApi") ? null : Components.FabricApiVersion,
OptiFabricVersion = invalidFiles.Any(file => file.Component == "OptiFabric") ? null : Components.OptiFabricVersion
},
ManagedFiles = ManagedFiles.Where(file => !invalidFiles.Contains(file)).ToList(),
LastModifiedUtc = DateTime.UtcNow
};
}
/// <summary>
/// 由 PCL 管理并记录的版本内文件。
/// </summary>
/// <param name="RelativePath"> 相对于游戏目录的文件路径。确保路径为全小写且可比较。</param>
/// <param name="Sha1"> 小写的 SHA1。</param>
/// <param name="LastWriteTimeUtc"> 目标文件的最后修改时间(UTC)。</param>
/// <param name="Component"> 安装器组件名;为空时表示文件由整合包管理。</param>
public record ManagedFile(string RelativePath, string Sha1, DateTime LastWriteTimeUtc,
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] string? Component = null);
}
/// <param name="MinecraftVersion"> 目标 Minecraft 版本。</param>
/// <param name="ModLoaderVersions"> 目标中各个 Mod Loader 的版本;原版为空字典。</param>
/// <param name="OptiFineEntry"> OptiFine 条目信息,不安装时为 <see langword="null"/>。</param>
/// <param name="FabricApiVersion"> 由 PCL 管理的 Fabric API 版本;不安装时为 <see langword="null"/>。</param>
/// <param name="OptiFabricVersion"> 由 PCL 管理的 OptiFabric 版本;不安装时为 <see langword="null"/>。</param>
[method: JsonConstructor]
public record McComponents(string MinecraftVersion, Dictionary<ModLoaders, string> ModLoaderVersions,
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] JObject? OptiFineEntry = null,
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] string? FabricApiVersion = null,
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] string? OptiFabricVersion = null) {
[JsonIgnore]
public string? OptiFineVersion => OptiFineEntry?["InstanceName"]?.Value<string>()?.AfterLast("-OptiFine_");
public McComponents(string minecraftVersion, JObject? optiFineEntry, string? forgeVersion,
string? neoForgeVersion, string? fabricVersion, string? fabricApiVersion, string? optiFabricVersion, string? liteLoaderVersion)
: this(minecraftVersion, [], optiFineEntry, fabricApiVersion, optiFabricVersion) {
if (forgeVersion is not null) ModLoaderVersions[ModLoaders.Forge] = forgeVersion;
if (neoForgeVersion is not null) ModLoaderVersions[ModLoaders.NeoForge] = neoForgeVersion;
if (fabricVersion is not null) ModLoaderVersions[ModLoaders.Fabric] = fabricVersion;
if (liteLoaderVersion is not null) ModLoaderVersions[ModLoaders.LiteLoader] = liteLoaderVersion;
}
public bool IsEquivalentTo(McComponents other) =>
MinecraftVersion == other.MinecraftVersion &&
OptiFineVersion == other.OptiFineVersion &&
FabricApiVersion == other.FabricApiVersion &&
OptiFabricVersion == other.OptiFabricVersion &&
ModLoaderVersions.Count == other.ModLoaderVersions.Count &&
ModLoaderVersions.All(entry => other.ModLoaderVersions.TryGetValue(entry.Key, out string? version) && version == entry.Value);
}
+295
View File
@@ -0,0 +1,295 @@
using System.Globalization;
namespace PCLCS;
/// <summary>
/// Minecraft 版本号和组件信息
/// </summary>
public sealed class McVersion {
/// <summary>
/// 原版版本名,如 26.1、26.1-snapshot-1、1.12.2、16w01a。
/// </summary>
public string VanillaName {
get => _vanillaName;
set {
_vanillaName = value ?? "Unknown";
Vanilla = NameToVersion(_vanillaName);
}
}
private string _vanillaName = "Unknown";
/// <summary>
/// 可比较的三段式原版版本号。
/// 旧格式会将次版本号移到主版本号,例如 1.20.1 会转换为 20.0.1,若没有版本号,例如旧快照,则为 9999.0.0。
/// </summary>
public Version Vanilla = new(9999, 0, 0);
/// <summary> 原版版本号是否可靠(不是通过猜测获取)。</summary>
public bool Reliable;
/// <summary> 原版版本号是否有效。</summary>
public bool Valid => Vanilla.Major < 1000;
/// <summary> 可供比较的原版 Drop 序数,例如 26.3.2 为 263,1.21.5 为 210;若没有版本号,例如旧快照,则直接指定为 209。</summary>
public int Drop => Valid ? Vanilla.Major * 10 + Vanilla.Minor : 209;
/// <summary> 已安装的 Mod Loader,可能有多个。未安装时为 <see cref="ModLoaders.None"/>。</summary>
public ModLoaders ModLoaders => ModLoaderVersions.Keys.Aggregate(ModLoaders.None, (result, loader) => result | loader);
/// <summary> 已安装的各个 Mod Loader 及其版本;无法确定精确版本时为“未知版本”。</summary>
public Dictionary<ModLoaders, string> ModLoaderVersions { get; } = [];
/// <summary> OptiFine 版本名,如 C8、C9_pre10;未识别到时为 null。</summary>
public string? OptiFine;
/// <summary> OptiFine 是否以 Mod 形式安装。</summary>
public bool OptiFineAsMod;
/// <summary> 版本的发布时间。</summary>
public DateTime ReleaseTime = new(1970, 1, 1, 15, 0, 0);
/// <summary> 是否为愚人节版本。</summary>
public bool IsFool;
/// <summary> 是否为快照、预发布版、发布候选或实验性版本。</summary>
public bool IsSnapshot;
/// <summary> 是否识别到已安装的 OptiFine。</summary>
public bool HasOptiFine => OptiFine is not null;
/// <summary>
/// 可供比较的 OptiFine 版本序数。
/// </summary>
public int OptiFineCode {
get {
if (OptiFine is null or "未知版本" || OptiFine.Length == 0) return 0;
// 字母编号,如 G2 中的 G(7)
int result = char.ToUpperInvariant(OptiFine[0]) - 'A' + 1;
// 末尾数字,如 C5 beta4 中的 5
result = result * 100 + (int.TryParse(OptiFine.Substring(1).RegexSeek(@"\d+"), out int mainVersion) ? mainVersion : 0);
// 测试标记(正式版为 99,Pre[x] 为 50+x,Beta[x] 为 x)
result *= 100;
string? preview = OptiFine.RegexSeek(@"(?i)(pre|beta)\d*");
if (preview is null) return result + 99;
int sequence = int.TryParse(preview.RegexSeek(@"\d+"), out int value) ? value : 1; // 为 pre 或 beta 结尾,视作 1
return result + (preview.StartsWithF("pre", true) ? 50 : 0) + sequence;
}
}
/// <summary>
/// 可供比较的类 Forge 版本序数。
/// </summary>
public int ForgelikeCode {
get {
if (!ModLoaderVersions.TryGetValue(ModLoaders.Forge, out string? version)) {
ModLoaderVersions.TryGetValue(ModLoaders.NeoForge, out version);
}
if (version is null or "未知版本") return 0;
// 提取所有数字段,以兼容 beta、alpha 和 snapshot 等非数字后缀。
int[] segments = version.RegexSearch(@"\d+")
.Select(segment => int.TryParse(segment, out int value) ? value : 0).ToArray();
return segments.Length switch {
>= 4 => segments[0] * 1000000 + segments[1] * 10000 + segments[3],
3 => segments[0] * 1000000 + segments[1] * 10000 + segments[2],
2 => segments[0] * 1000000 + segments[1] * 10000,
1 => segments[0] * 1000000,
_ => 0
};
}
}
/// <summary>
/// 创建默认 Minecraft 版本。
/// </summary>
/// <param name="vanillaName">原版版本名。</param>
/// <param name="reliable">版本名是否来自可靠依据。</param>
public McVersion(string vanillaName, bool reliable = true) {
Reliable = reliable;
VanillaName = vanillaName;
IsFool = IsFoolName(VanillaName);
IsSnapshot = IsSnapshotName(VanillaName);
}
/// <summary>
/// 通过 Components 创建 Minecraft 版本信息。
/// </summary>
public McVersion(McComponents components) : this(components.MinecraftVersion) {
OptiFine = components.OptiFineVersion?.AfterLast("HD_").AfterFirst("_");
foreach (var loader in components.ModLoaderVersions) ModLoaderVersions[loader.Key] = loader.Value;
}
/// <summary>
/// 通过 JSON 识别 Minecraft 版本和已安装组件。
/// </summary>
/// <param name="instanceName">实例文件夹名。</param>
/// <param name="json">展开的版本 JSON。</param>
/// <param name="jarVersionJson">版本 JAR 中的 version.json。</param>
public McVersion(string instanceName, JObject json, JObject? jarVersionJson = null) {
string jsonText = json.ToString(Formatting.None);
DateTime releaseTime = new(1970, 1, 1, 15, 0, 0); // 未知版本也可能显示为 1970 年
try {
if (json["releaseTime"] is JToken releaseTimeToken) releaseTime = releaseTimeToken.ToObject<DateTime>();
} catch {
}
bool reliable = true;
// 获取游戏版本
string vanillaName = (GetVanillaName(instanceName, json, jarVersionJson, releaseTime, ref reliable) ?? "Unknown").Replace("_unobfuscated", "").Replace(" Unobfuscated", "");
Reliable = reliable;
VanillaName = vanillaName;
ReleaseTime = releaseTime;
// 确定版本分类
string jsonType = json.Value<string>("type") ?? "";
// OptiFine
OptiFine = jsonText.ContainsIgnoreCase("optifine") ? jsonText.RegexSeek(@"(?<=HD_U_)[^\"":/]+") ?? "未知版本": null;
// 愚人节与快照版本
DateTime fixedReleaseTime = releaseTime.ToUniversalTime().AddHours(2);
IsFool = jsonType == "fool" || IsFoolName(vanillaName) || (fixedReleaseTime.Month == 4 && fixedReleaseTime.Day == 1 && jsonType == "snapshot");
IsSnapshot = IsSnapshotName(vanillaName) || instanceName.ContainsIgnoreCase("combat") || jsonType is "snapshot" or "pending" or "fool";
if (!Reliable) Logger.Info($"无法完全确认 MC 版本号的版本:{VanillaName}");
// LiteLoader
if (jsonText.ContainsIgnoreCase("liteloader")) {
ModLoaderVersions[ModLoaders.LiteLoader] = jsonText.RegexSeek(@"(?<=com\.mumfrey:liteloader:)[^\"":/]+") ?? "未知版本";
}
// Fabric、Forge、NeoForge
if (jsonText.ContainsIgnoreCase("net.fabricmc:fabric-loader") || jsonText.ContainsIgnoreCase("org.quiltmc:quilt-loader")) {
ModLoaderVersions[ModLoaders.Fabric] = jsonText.RegexSeek(@"(?<=(net\.fabricmc:fabric-loader:|org\.quiltmc:quilt-loader:))[0-9\.]+(\+build.[0-9]+)?")?
.Replace("+build", "") ?? "未知版本";
} else if (jsonText.ContainsIgnoreCase("net.neoforge")) {
// 1.20.1 JSON 范例:"--fml.forgeVersion", "47.1.99"
// 1.20.2+ JSON 范例:"--fml.neoForgeVersion", "20.6.119-beta"
ModLoaderVersions[ModLoaders.NeoForge] = FindArgumentValue(json, "--fml.neoForgeVersion") ?? FindArgumentValue(json, "--fml.forgeVersion") ?? "未知版本";
} else if (jsonText.ContainsIgnoreCase("minecraftforge")) {
ModLoaderVersions[ModLoaders.Forge] = jsonText.RegexSeek(@"(?<=forge:[0-9\.]+(?:_pre[0-9]*)?-)[0-9\.]+")
?? jsonText.RegexSeek(@"(?<=net\.minecraftforge:minecraftforge:)[0-9\.]+")
?? jsonText.RegexSeek(@"(?<=net\.minecraftforge:fmlloader:[0-9\.]+-)[0-9\.]+") ?? "未知版本";
}
}
private static string? GetVanillaName(string instanceName, JObject json, JObject? jarVersionJson, DateTime releaseTime, ref bool reliable) {
// 获取发布时间并判断是否为老版本
if (releaseTime.Year is > 2000 and < 2013) return "Old";
// 实验性快照
if (json.Value<string>("type") == "pending") return "pending";
// 从 PCL 下载的版本信息中获取版本号
if (json["clientVersion"] is JToken clientVersion) return clientVersion.ToString();
// 从 HMCL 下载的版本信息中获取版本号
if (json["patches"] is JArray patches) {
JToken? gameVersion = patches.OfType<JObject>().FirstOrDefault(patch => patch.Value<string>("id") == "game")?["version"];
if (gameVersion is not null) return gameVersion.ToString();
}
// 从 Forge / NeoForge Arguments 中获取版本号
if (FindArgumentValue(json, "--fml.mcVersion") is { Length: > 0 } fmlVersion) return fmlVersion;
// 从继承版本中获取版本号
string inherit = json["inheritsFrom"]?.ToString() ?? "";
if (inherit.Length > 0) {
string jar = json["jar"]?.ToString() ?? ""; // LiteLoader 优先使用 Jar
return jar.Length > 0 ? jar : inherit;
}
// 从下载地址中获取版本号
string? download = json["downloads"]?.ToString(Formatting.None).RegexSeek(@"(?<=launcher.mojang.com/mc/game/)[^/]*");
if (download is not null) return download;
// 从 Forge/OptiFine/Fabric 版本中获取版本号
string librariesText = json["libraries"]!.ToString();
string[] libraryPatterns = [
@"(?<=net.minecraftforge:forge:)[0-9]{1,2}.[0-9+.]+",
@"(?<=net.minecraftforge:fmlloader:)[0-9]{1,2}.[0-9+.]+",
@"(?<=optifine:OptiFine:)[0-9]{1,2}.[0-9+.]+",
@"(?<=((fabricmc)|(quiltmc)):intermediary:)[^\""]*"
];
foreach (string pattern in libraryPatterns) {
if (librariesText.RegexSeek(pattern) is string match) return match;
}
// 从 jar 项中获取版本号
if (json["jar"] is JToken jarToken) return jarToken.ToString();
// 从 jar 文件的 version.json 中获取版本号
if (jarVersionJson?["name"] is JToken jarNameToken && jarNameToken.ToString() is { Length: < 32 } jarName) { // 因为 wiki 说这玩意儿可能是个 hash,虽然我没发现
Logger.Info($"从版本 jar 中的 version.json 获取到版本号:{jarName}");
return jarName;
}
string VersionPattern = @"(([1-9][0-9]w[0-9]{2}[a-g])|((1|[2-9][0-9])\.[0-9]+(\.[0-9]+)?(-(pre|rc|snapshot-?)[1-9]*| Pre-Release( [1-9])?)?))(_unobfuscated)?";
// 从 JSON 的 ID 中获取
if (json["id"]!.ToString().RegexSeek(VersionPattern, RegexOptions.IgnoreCase) is string idMatch) return idMatch;
// 非准确的版本判断警告
Logger.Info($"无法完全确认 MC 版本号的版本:{instanceName}");
reliable = false;
// 从文件夹名中获取
if (instanceName.RegexSeek(VersionPattern, RegexOptions.IgnoreCase) is string folderMatch) return folderMatch;
// 从 JSON 出现的版本号中获取
JObject withoutLibraries = (JObject) json.DeepClone();
withoutLibraries.Remove("libraries");
return withoutLibraries.ToString(Formatting.None).RegexSeek(VersionPattern, RegexOptions.IgnoreCase);
}
private static bool IsFoolName(string name) {
name = name.Lower();
return name.StartsWithF("2.0") || name is "15w14a" or "1.rv-pre1" or
"3d shareware v1.34" or "20w14∞" or "22w13oneblockatatime" or "23w13a_or_b" or
"24w14potato" or "25w14craftmine" or "26w14a" || name.StartsWithF("20w14inf");
}
private static bool IsSnapshotName(string name) =>
new[] { "w", "snapshot", "rc", "pre", "experimental", "-" }.Any(name.ContainsIgnoreCase);
/// <summary>
/// 读取 arguments 中指定参数的字符串值。
/// </summary>
private static string? FindArgumentValue(JObject json, string name) {
JArray? arguments = json["arguments"]?["game"] as JArray;
if (arguments is null) return null;
for (int i = 0; i + 1 < arguments.Count; i++) {
if (arguments[i]?.ToString() == name) return arguments[i + 1]?.ToString();
}
return null;
}
/// <summary>
/// 判断版本字符串是否符合 Minecraft 原版格式,例如 1.x、26.x。
/// </summary>
public static bool IsStandardFormat(string? version) {
if (version is null) return false;
if (Regex.IsMatch(version, @"^1\.\d")) return true;
return double.TryParse(version.RegexSeek(@"^[2-9]\d\.\d+"), NumberStyles.Float,
CultureInfo.InvariantCulture, out double value) && value >= 26;
}
/// <summary>
/// 将版本名转换为 Drop 序数;无法转换时返回 209。
/// </summary>
public static int VersionToDrop(string? vanillaName) {
if (vanillaName is null) return 209;
Version version = NameToVersion(vanillaName);
return version.Major >= 1000 ? 209 : version.Major * 10 + version.Minor;
}
/// <summary>
/// 将 Drop 序数转换为版本字符串。
/// </summary>
public static string DropToVersion(int drop) {
if (drop >= 250) return $"{drop / 10}.{drop % 10}";
if (drop != 209) return $"1.{drop / 10}";
Logger.Warn(new Exception("尝试将旧快照版 Drop 序数转换为版本字符串,这不应该发生!"));
return "1.20";
}
/// <summary>
/// 将版本名转换为可比较的三段式原版版本号。
/// 对老版本格式,例如 1.20.3,会被转换为 20.0.3。
/// 对愚人节版本,返回其基于的正式版版本号。
/// 若没有版本号,例如旧快照,则为 9999.0.0。
/// </summary>
public static Version NameToVersion(string? name) {
if (name is null) return new Version(9999, 0, 0);
name = name.Lower().Replace("_unobfuscated", "").Replace(" unobfuscated", "");
// 愚人节版本
if (name.StartsWithF("2.0")) return NameToVersion("1.5.1");
if (name == "15w14a") return NameToVersion("1.8.3");
if (name.ContainsIgnoreCase(".rv-pre")) return NameToVersion("1.9.2");
if (name.ContainsIgnoreCase("shareware")) return NameToVersion("1.13.2");
if (name.StartsWithF("20w14") && name != "20w14a") return NameToVersion("1.15.2");
if (name.ContainsIgnoreCase("oneblockatatime")) return NameToVersion("1.18.2");
if (name.ContainsIgnoreCase("23w13a") && name != "20w13a") return NameToVersion("1.19.4");
if (name == "24w14potato") return NameToVersion("1.20.4");
if (name == "25w14craftmine") return NameToVersion("1.21.4");
if (name == "26w14a") return NameToVersion("26.1.1");
// 其余版本
static int ParseSegment(string[] segments, int index) {
if (index >= segments.Length) return 0;
// 只读取该段开头连续的数字,忽略其后的预览版标记。
return int.TryParse(segments[index].RegexSeek(@"^\d+"), out int value) ? value : 0;
}
string[] segments = name.Split([' ', '_', '-', '.'], StringSplitOptions.None);
if (name.StartsWithF("1.")) {
return new Version(ParseSegment(segments, 1), 0, ParseSegment(segments, 2));
} else if (name.RegexCheck(@"^[2-9][0-9]\.")) {
return new Version(ParseSegment(segments, 0), ParseSegment(segments, 1), ParseSegment(segments, 2));
} else {
return new Version(9999, 0, 0);
}
}
}
+423
View File
@@ -0,0 +1,423 @@
{
"f3a3c9d792bdb48595405d52c02cc7baf1db4ecb": "OptiFine_1.7.2_HD_U_E3.jar",
"657169c421ce280f80c73b05ab71ad582d2b37fd": "OptiFine_1.7.2_HD_U_E4.jar",
"99f20d449912cd860b409e4679c54dd8da43d55e": "OptiFine_1.7.2_HD_U_E7.jar",
"8253f6ed418a2a61a612cc9e69fd1fea1d11fa7d": "OptiFine_1.7.2_HD_U_E8.jar",
"379e52fc02cd75562d201b0c505269b6109b2136": "OptiFine_1.7.2_HD_U_F3.jar",
"fb04b91e47b5eaa172d2b76c843e488dba8f909b": "OptiFine_1.7.2_HD_U_F7.jar",
"b4b6c2c99f5734e4267030a47f2eaad2796eb2a7": "OptiFine_1.7.10_HD_U_D3.jar",
"a0b533bc97b24fe771f5b08536c6339d04f92bae": "OptiFine_1.7.10_HD_U_D4.jar",
"5e9327dd05a1e762c23c3c0227445d90f193922f": "OptiFine_1.7.10_HD_U_D6.jar",
"7a7d2adf21038d241dd375faa7316fb2c74d34d3": "OptiFine_1.7.10_HD_U_D7.jar",
"2345677be44dd56a0ae5b90e747a590c3cc00ff8": "OptiFine_1.7.10_HD_U_D8.jar",
"23f133173428aee712eb11f200b461e3114bde31": "OptiFine_1.7.10_HD_U_E3.jar",
"2130d7266224da6a7325051e37444f7e7c19277c": "OptiFine_1.7.10_HD_U_E7.jar",
"55c9c475ecd0f62cc1776ce52be04c5937112936": "OptiFine_1.8.0_HD_U_H5.jar",
"bb2c2997cdc66cfb3c311a3dc065752a4845c1a8": "OptiFine_1.8.0_HD_U_H6.jar",
"692b0b7e878c3416f21b4347fab4516f3cc86dfc": "OptiFine_1.8.0_HD_U_H7.jar",
"5464962b66b9b0dacb3d787e6a71d2365ae50bca": "OptiFine_1.8.0_HD_U_H8.jar",
"b7718b03111bec04036fd3352ce374eea273171c": "OptiFine_1.8.0_HD_U_I3.jar",
"0d1dcf64b4d104a71e8f65a1362a163ed81a9030": "OptiFine_1.8.0_HD_U_I7.jar",
"e5e5eb03748af0fa160b502a28359c0144e254d2": "OptiFine_1.8.8_HD_U_H5.jar",
"9294bc4562a143c35ece03a6b3d3a000109def5f": "OptiFine_1.8.8_HD_U_H6.jar",
"1c06c011904ab22952a96f73d4b575a5c265d207": "OptiFine_1.8.8_HD_U_H7.jar",
"3984e4dd222bb62cb7e28e4d3092029cc7534fd1": "OptiFine_1.8.8_HD_U_H8.jar",
"0dc33a9761ceac19ca0bbb1ea6813c0160d43aa1": "OptiFine_1.8.8_HD_U_I3.jar",
"30cbf4d61052cbfb736a78117da333df23c129a3": "OptiFine_1.8.8_HD_U_I7.jar",
"d8240cfa7387b994fdf2a64bdc8a8860857adc18": "OptiFine_1.8.9_HD_U_H5.jar",
"2636689b31810896cf80493f6fc4d8c1783048ea": "OptiFine_1.8.9_HD_U_H6.jar",
"e4544c9210bc056abffa1986a33c4e72623d4f31": "OptiFine_1.8.9_HD_U_H7.jar",
"4b73738913453bcbca969d3e77f05ae0cf5b8a7e": "OptiFine_1.8.9_HD_U_H8.jar",
"d5cd8b902077075bbf1d865cfd5b3362a6863ace": "OptiFine_1.8.9_HD_U_I3.jar",
"58bec8f8766918dcad2b8412f1fbb6f30cc9319f": "OptiFine_1.8.9_HD_U_I7.jar",
"dcab4849a24ccad5a6a3496c67bc5a88411d2a35": "OptiFine_1.8.9_HD_U_L5.jar",
"d362d58a28f5373b141b9e426e8e160638bfafcd": "OptiFine_1.8.9_HD_U_M5.jar",
"1f9dcc9735e94ebb5195c4014300ea462eb12878": "preview_OptiFine_1.8.9_HD_U_L6_pre1.jar",
"6e0b3b86e51c2647c98e277475ee7e68f32a0d0e": "preview_OptiFine_1.8.9_HD_U_M5_pre2.jar",
"7287fcd1a415ab87eca5e89457cc3507aa3fa579": "preview_OptiFine_1.8.9_HD_U_M6_pre1.jar",
"0bb792be16ec78d8b62586cc4856950befb3391b": "preview_OptiFine_1.8.9_HD_U_M6_pre2.jar",
"fd87c53197312614c34669feb7608f74a30da712": "OptiFine_1.9.0_HD_U_B1.jar",
"f99941037f3633926ffebb3eda67f296a701c019": "OptiFine_1.9.0_HD_U_B2.jar",
"34c47106cda1371ecf13e61aed4c1b53a3a53163": "OptiFine_1.9.0_HD_U_B3.jar",
"ebc7258e13344097588c73a0b48457083069e1df": "OptiFine_1.9.0_HD_U_B5.jar",
"999df92e8904f9268e549135edbec322d332ef62": "OptiFine_1.9.0_HD_U_D7.jar",
"0fe031e95f9c01481c9376d9a1df68f9e9857db8": "OptiFine_1.9.0_HD_U_D8.jar",
"f4bc2eb5e039e842eb07181ea564eba487513228": "OptiFine_1.9.0_HD_U_E3.jar",
"78870a94ec3c4db8aebc4c29d4d39bdda2bf05da": "OptiFine_1.9.0_HD_U_E7.jar",
"d3d55bb37e76d1932cb3ad9dde4b9127993f856d": "OptiFine_1.9.0_HD_U_H5.jar",
"b5faec3b84f42dd396512a3ce7c1b57c0b972b70": "OptiFine_1.9.0_HD_U_I5.jar",
"92742ecac143b1d28c215ca145bd9775a01c5085": "preview_OptiFine_1.9.0_HD_U_I5_pre1.jar",
"c87029938376017deba9108322bcb8424d64e92c": "OptiFine_1.9.2_HD_U_B1.jar",
"d3e5e1fa480e66603ca037d74966a95d25968d51": "OptiFine_1.9.2_HD_U_B2.jar",
"fbd545f921d098235a28f87cda55a0504e831954": "OptiFine_1.9.2_HD_U_B3.jar",
"1ae8e6daeeccb6ee48618e8ccdc0b6276d47944f": "OptiFine_1.9.2_HD_U_D7.jar",
"3350c829b29d170e9ac19809a3ae45cb099277b5": "OptiFine_1.9.2_HD_U_D8.jar",
"c032b2ae0b6087077efef8ae89b612e0d4ea81b7": "OptiFine_1.9.2_HD_U_E3.jar",
"3d8cbc19bcad9e9ecc98f607e10f65673a09b482": "OptiFine_1.9.4_HD_U_B4.jar",
"b6b9e66b34ddcda5294b016602fb17d741ff6f54": "OptiFine_1.9.4_HD_U_B5.jar",
"060ddd5a0b7b7ac5586de04ae53cf923ae5c7290": "OptiFine_1.9.4_HD_U_B6.jar",
"2c39611a89dc583cdb403f402c2efd2bd8c6da63": "OptiFine_1.9.4_HD_U_D7.jar",
"4b26926208de270225eb9d3af24a4ff3841238f5": "OptiFine_1.9.4_HD_U_D8.jar",
"012f82fce41aee5e6e56e551d450a07b2b361646": "OptiFine_1.9.4_HD_U_E3.jar",
"cf9c075fa71dab34cdf1c376a76bd08295c69ce6": "OptiFine_1.9.4_HD_U_E7.jar",
"b3a76b92e0e9594f6fa135f1156b9cc13712db20": "OptiFine_1.9.4_HD_U_H5.jar",
"68a206db0d425dd85f6eba33e52122b01e844c46": "OptiFine_1.9.4_HD_U_I5.jar",
"2e48d20605340cfad844a6587d3848e328fd4399": "preview_OptiFine_1.9.4_HD_U_I5_pre1.jar",
"d7a1e5f40e28fa3283b82bf3af7d3f433b91df08": "OptiFine_1.10_HD_U_B6.jar",
"717a02b21136e022cdb4d7d6ec6a4125f4ed2312": "OptiFine_1.10_HD_U_B7.jar",
"0b5d1719592a57cf414643c5b56f4bb3c23a5b94": "OptiFine_1.10_HD_U_C1.jar",
"236747126edcf0c97fc6a20fa7f6491accbf0108": "OptiFine_1.10_HD_U_D7.jar",
"c5fd78710e821f06f871994032e951065c15c40d": "OptiFine_1.10_HD_U_D8.jar",
"1bea8b0bae04ca4b25c6824a6f23a5442d5aa926": "OptiFine_1.10_HD_U_E3.jar",
"7a7195348ea583458b657573b5fcc72e4adc8308": "OptiFine_1.10_HD_U_E7.jar",
"3bcc46f718711cd6fe3db49de4cc810b75420de8": "OptiFine_1.10_HD_U_H5.jar",
"a0070afe9c1c0ced4bcfcda85359e07917d569bd": "OptiFine_1.10_HD_U_I5.jar",
"f9a36eb0246ad6ba303e670082be2c2515c50dcd": "preview_OptiFine_1.10_HD_U_I5_pre1.jar",
"3ea5dae651b6d64900562d1cde5070ebd9db2936": "OptiFine_1.10.2_HD_U_C1.jar",
"41968442e8d1e30e9a32e7a5594caca22312d9d6": "OptiFine_1.10.2_HD_U_C2.jar",
"9c8bc96d3125199e96f5dbfda685ea5dd92c253e": "OptiFine_1.10.2_HD_U_C3.jar",
"7ec4ccc213b482936baaffb9f1937f7147796893": "OptiFine_1.10.2_HD_U_D1.jar",
"3ee5aecc8e11567fb46de0b94e23cf1428f4c9fc": "OptiFine_1.10.2_HD_U_D2.jar",
"98059ed7e93d0fc058203569ccfe965b022fb18f": "OptiFine_1.10.2_HD_U_D3.jar",
"c3165d0a0207d72e18f4533e7aa5ca30e96819a9": "OptiFine_1.10.2_HD_U_D4.jar",
"347359aef422d64711b470f4cf0803cb072da0fa": "OptiFine_1.10.2_HD_U_D6.jar",
"4ddf7c45e00839031f5793451541bca002f9257f": "OptiFine_1.10.2_HD_U_D7.jar",
"5c3685e279c664a818c41ca213b2bc9bfc4340b0": "OptiFine_1.10.2_HD_U_D8.jar",
"129581af3d7d73b506b42d822cb2d3fce68c8e2e": "OptiFine_1.10.2_HD_U_E3.jar",
"f052d269109154d851cc7a8e766c78802a76e9b2": "OptiFine_1.10.2_HD_U_E7.jar",
"f285cfe6753c5d987e4cb0940da05e9513334d98": "OptiFine_1.10.2_HD_U_H5.jar",
"12fd8dd33150f9e4436f23bac08265305f949e20": "OptiFine_1.10.2_HD_U_I5.jar",
"adac69b25bb95242422a635965c7f17503c694be": "preview_OptiFine_1.10.2_HD_U_I5_pre1.jar",
"06031e4c828b66facefdfa32cd29c585909d9e45": "OptiFine_1.11_HD_U_B1.jar",
"449aef14cee17cf1c79d4bad73b69107202d3c1c": "OptiFine_1.11_HD_U_B2.jar",
"7663e1ee572c742e96a71a355f0775baf7cbc6ef": "OptiFine_1.11_HD_U_B3.jar",
"bde9d34f84a030734c7b9d09eda700c7c828eb96": "OptiFine_1.11_HD_U_B5.jar",
"579cfb75b86f3404c5f6e8f694c51b09b7c9b261": "OptiFine_1.11_HD_U_B6.jar",
"350af22b31dbc27d1b928e7144582fd361deb193": "OptiFine_1.11_HD_U_B7.jar",
"236161183cc5cd5faf84dd27824cc3eaa107ed5f": "OptiFine_1.11_HD_U_B8.jar",
"9ca32d42c7e5ad9de9665ffee9ddd41dddd33b66": "OptiFine_1.11_HD_U_C3.jar",
"2e45de9e9fb385f24c6eecb1a64537de78778105": "OptiFine_1.11_HD_U_C7.jar",
"32df58fec7148e75b599b18691f80e69b7f9cd1b": "OptiFine_1.11_HD_U_F5.jar",
"71057cb7a3d0971c3b56e1fbbc333304a72e1352": "OptiFine_1.11_HD_U_G5.jar",
"c2ee1e1019ab26bee56447f4df8a2639d2029925": "preview_OptiFine_1.11_HD_U_G5_pre1.jar",
"083a002215e2174c0d40af20d2f3844ea349a354": "OptiFine_1.11.2_HD_U_B5.jar",
"eb92ce00048faa95b184d121fa667074532fb9db": "OptiFine_1.11.2_HD_U_B6.jar",
"7449a2db44e86303d588b526dc73127c16b0e94f": "OptiFine_1.11.2_HD_U_B7.jar",
"3e1006c935fe27d83459e77d336460ac5b5c81b4": "OptiFine_1.11.2_HD_U_B8.jar",
"049e7e134e8c7364ab61810d84289394e17921de": "OptiFine_1.11.2_HD_U_B9.jar",
"4871f2edf7ded30c374aa770e9ea4adcc0b786d0": "OptiFine_1.11.2_HD_U_C1.jar",
"e7a54d9e203a1b951f8373ab6dcdedb348e536b1": "OptiFine_1.11.2_HD_U_C2.jar",
"8d7fd0facc2299f3dad9dc7c19cc13f72a780343": "OptiFine_1.11.2_HD_U_C3.jar",
"20db26eaafb1c4ebd99e1dc2fffe4a62456bf60e": "OptiFine_1.11.2_HD_U_C7.jar",
"e4efcf05086389fe18d6fe62eb8ab7512a07323c": "OptiFine_1.11.2_HD_U_F5.jar",
"65c425ab628d1748b489dee8619271402428509e": "OptiFine_1.11.2_HD_U_G5.jar",
"f253807a4689e44da030acd5c2187d2184885e87": "preview_OptiFine_1.11.2_HD_U_G5_pre1.jar",
"1fdacab6f3b0df1f46142acf9c427db2a653d680": "OptiFine_1.12_HD_U_C2.jar",
"a62a02c8437a0dccaf2ea89ab8392fd40dc92eb1": "OptiFine_1.12_HD_U_C3.jar",
"36d6f6f64048f68dd248a4d3c43a6f5e51fbb9c5": "OptiFine_1.12_HD_U_C4.jar",
"fd8e0e3ba43568e5ca0ce31c87373806d269b7e5": "OptiFine_1.12_HD_U_C5.jar",
"25ba634e5105e6d1e615dad0046905b68313cc9f": "OptiFine_1.12_HD_U_C7.jar",
"b9cd2775fc49bf009f82d3ce36235b1e702d46bb": "OptiFine_1.12_HD_U_F5.jar",
"3b8369d33c3257cb247c22b13a41b7db0924e948": "OptiFine_1.12_HD_U_G5.jar",
"aeee885ccf821c34d86537184389873d8c312ae1": "preview_OptiFine_1.12_HD_U_G5_pre1.jar",
"c754771bf6a7371a7084459505c244cbec61a7cf": "OptiFine_1.12.1_HD_U_C5.jar",
"4362a9d38542b5f4ba07782fc4d07949f4fede08": "OptiFine_1.12.1_HD_U_C6.jar",
"38785968beeb796cde55f91995c6cf8bfbc3b062": "OptiFine_1.12.1_HD_U_C7.jar",
"e6f9e6b573119c052062192695fed4ef388db9c1": "OptiFine_1.12.1_HD_U_F5.jar",
"3c9cd74810cad75de5091633a1e0c7d3d76ea09c": "OptiFine_1.12.1_HD_U_G5.jar",
"aaaaff3ae13a0cdffa2475632cf6303b9f86e0b0": "preview_OptiFine_1.12.1_HD_U_G5_pre1.jar",
"a9d21f084a485bc12fce8c9bea1660c6d4b08855": "OptiFine_1.12.2_HD_U_C5.jar",
"2159a0a754828d63446fca7b6f8fa57b710c6048": "OptiFine_1.12.2_HD_U_C6.jar",
"8ed8009fc82b43909190882927f335d5250a0e19": "OptiFine_1.12.2_HD_U_C7.jar",
"a55768a83db4ad0599f68d9bf3fa8041e2ae3852": "OptiFine_1.12.2_HD_U_C8.jar",
"e0236d21a2a164f2af1cfff8f9f325f53c9125f2": "OptiFine_1.12.2_HD_U_C9.jar",
"b91a0d82c24eef95be0af92993b14e94b842f166": "OptiFine_1.12.2_HD_U_D1.jar",
"04c28238506dc3561addb6efb87c9878d5b82ffe": "OptiFine_1.12.2_HD_U_D2.jar",
"b784000951d41f54d67685a0a275d9f61f810514": "OptiFine_1.12.2_HD_U_D3.jar",
"343f814410401ebdbb3314deb17c27cbab67bfac": "OptiFine_1.12.2_HD_U_E1.jar",
"5c87706b6d14061fed248506d8e3f95b7824ad18": "OptiFine_1.12.2_HD_U_E2.jar",
"e805d4be5c2a3343488c573145606e90bb13816d": "OptiFine_1.12.2_HD_U_E3.jar",
"0e8d45666e80829c9ab74527628dace9187fbfb5": "OptiFine_1.12.2_HD_U_F4.jar",
"84ada0f48bcfb16c37c49283627597bb74d935f6": "OptiFine_1.12.2_HD_U_F5.jar",
"ca3aea3a09ce215906c346fe190907fe0347b0c4": "OptiFine_1.12.2_HD_U_G5.jar",
"23a8be042f354ec97c99672906c3e61fdf54ca2c": "preview_OptiFine_1.12.2_HD_U_F6_pre1.jar",
"b71f993a364c7945081cc6cde9a20d29b4e43f1f": "preview_OptiFine_1.12.2_HD_U_G5_pre1.jar",
"ab2118b1ed23444d6decf081e0295e4c679af6c2": "preview_OptiFine_1.12.2_HD_U_G6_pre1.jar",
"25498de48dd9b7ed2ba5b83464950ad132e61c1f": "preview_OptiFine_1.14_HD_U_F1_pre6.jar",
"396ce7f0e22aae3543134baabd61ad547334c0aa": "OptiFine_1.14.2_HD_U_F1.jar",
"c8c6da3e2ae32d020d9001d4489aad702680f50f": "preview_OptiFine_1.14.2_HD_U_F1_pre15.jar",
"52420ef34128a8b15d7c8f5ed8f7ebc94b708efd": "OptiFine_1.14.3_HD_U_F1.jar",
"887d648f26f7ba17ea34c0c2dcff4096f7a226b1": "OptiFine_1.14.3_HD_U_F2.jar",
"ad8a8f60931f48e2873238d1460268bb4111f6c9": "OptiFine_1.14.4_HD_U_F2.jar",
"c9710081fb7012ddb56838ac54653ac2258a2404": "OptiFine_1.14.4_HD_U_F3.jar",
"c09f4d1ad66df8e3039334023422f1bc8cc211b8": "OptiFine_1.14.4_HD_U_F4.jar",
"3408258990309de773cc5a8ab71cb4137718a82b": "OptiFine_1.14.4_HD_U_F5.jar",
"117c01a5431244a8b6a3ac393374dac9624f9be6": "OptiFine_1.14.4_HD_U_G5.jar",
"ac602fe6a39d2fb4cadfe08e87af0b0daa2d8ac9": "preview_OptiFine_1.14.4_HD_U_G5_pre1.jar",
"630a32be9b711b5a479507159d5d2598189fed33": "OptiFine_1.15.2_HD_U_G6.jar",
"8339858b33056ec8975f6c0c9e1694a11f0d209a": "preview_OptiFine_1.15.2_HD_U_G1_pre1.jar",
"6cdf96d972b5f70c4575ff875a031c4931985686": "preview_OptiFine_1.15.2_HD_U_G1_pre10.jar",
"489d2284f2d50bd7c7cf4c216a766503d37e1f3c": "preview_OptiFine_1.15.2_HD_U_G1_pre12.jar",
"c66d412c4cb46c6f2c76c224dc45a62d30ee8b43": "preview_OptiFine_1.15.2_HD_U_G1_pre13.jar",
"582eb1d6470cc133db563bd1c2c8bc2068215806": "preview_OptiFine_1.15.2_HD_U_G1_pre18.jar",
"64d738862683c54a22e1856b2c13379599ca79ce": "preview_OptiFine_1.15.2_HD_U_G1_pre19.jar",
"3da9d466c847c50a73dc634dabc6a57f3b24e2f7": "preview_OptiFine_1.15.2_HD_U_G1_pre2.jar",
"e6583246a85dba26a478dd4925c298bb5b3f277a": "preview_OptiFine_1.15.2_HD_U_G1_pre20.jar",
"84c5d50e26aaa02733fa15a8c13ab845fbdea97f": "preview_OptiFine_1.15.2_HD_U_G1_pre21.jar",
"c55e449386eeca34df5b428d85927242fc841438": "preview_OptiFine_1.15.2_HD_U_G1_pre22.jar",
"459e17c77059671ef5daa9ab794400e8ae25095d": "preview_OptiFine_1.15.2_HD_U_G1_pre23.jar",
"2c6e4f61b1ceea92b76e0be40bc672e5b6d58efe": "preview_OptiFine_1.15.2_HD_U_G1_pre24.jar",
"4ea53e3d202239c5ffa2affa809e36998194f1bc": "preview_OptiFine_1.15.2_HD_U_G1_pre26.jar",
"29861ba50021b08200ef33f77c776f9f5b7135f7": "preview_OptiFine_1.15.2_HD_U_G1_pre27.jar",
"454dc0e03dab9d30a2ccce5823b7a0b309f5c859": "preview_OptiFine_1.15.2_HD_U_G1_pre28.jar",
"3c3623d75dc47722149edfb93fdca121c32e8987": "preview_OptiFine_1.15.2_HD_U_G1_pre29.jar",
"942e23fa0169c693fb1b645fae4db9217c6b5916": "preview_OptiFine_1.15.2_HD_U_G1_pre3.jar",
"bdbd92e5252c0afc0f22d6d5840f96762e5d01ea": "preview_OptiFine_1.15.2_HD_U_G1_pre30.jar",
"5daa9461c5762bdd647665f49d2e0ef0eb348910": "preview_OptiFine_1.15.2_HD_U_G1_pre4.jar",
"0029ab50050d19f4009f0a7d708982dde736f022": "preview_OptiFine_1.15.2_HD_U_G1_pre5.jar",
"646b279927cab6036e8f295efd5bda268acd85cf": "preview_OptiFine_1.15.2_HD_U_G1_pre7.jar",
"f11e79a7bc91a5fd985cb756445fc88e8c2dd31b": "preview_OptiFine_1.15.2_HD_U_G1_pre9.jar",
"1e9869b63a89a8ab81a58e1473dd2c2291fbd943": "preview_OptiFine_1.15.2_HD_U_G6_pre5.jar",
"87533b8ac0dfeb11241041f197f8f95c0ffa8c08": "OptiFine_1.16.1_HD_U_G2.jar",
"3e610c9bd89ae2a54cc06de6dfb1b00cd45e9895": "preview_OptiFine_1.16.1_HD_U_G2_pre1.jar",
"df817e1ab0e577be1dfb6c20a9b628d3d725c780": "preview_OptiFine_1.16.1_HD_U_G2_pre10.jar",
"dbca331e7abbe64d6aa360e6a93beec2935d24e3": "preview_OptiFine_1.16.1_HD_U_G2_pre11.jar",
"6fef880338cabe32443557762ca43fb36fe01f7c": "preview_OptiFine_1.16.1_HD_U_G2_pre12.jar",
"ea7bea6511f3b20aa672544d28dbc6d1a2ee8b41": "preview_OptiFine_1.16.1_HD_U_G2_pre13.jar",
"efdc788f59e1c1bcde3b64430322d8990ce7c7d6": "preview_OptiFine_1.16.1_HD_U_G2_pre14.jar",
"388100110b4dc8ef28eaa2f3a4ae2f33a8beb5ae": "preview_OptiFine_1.16.1_HD_U_G2_pre15.jar",
"63f5f097ce2e678d69767981b09b133ab8eac4f5": "preview_OptiFine_1.16.1_HD_U_G2_pre16.jar",
"c56031c3f6e5dcad536115e9f6a1fb5710b72391": "preview_OptiFine_1.16.1_HD_U_G2_pre2.jar",
"da269cdb109b6441ebed179a8d9f974590006cbb": "preview_OptiFine_1.16.1_HD_U_G2_pre3.jar",
"ad9b5d4e0bdd5697d4f6a6b9c30a64977bb8834e": "preview_OptiFine_1.16.1_HD_U_G2_pre4.jar",
"f6d5ea6cedff1f80bed298f9b456de2448b1f268": "preview_OptiFine_1.16.1_HD_U_G2_pre5.jar",
"f57a4269d404d4025de4d864b88510504e785e19": "preview_OptiFine_1.16.1_HD_U_G2_pre6.jar",
"28b46592e2f510a246a8bf62a2b12e808094167c": "preview_OptiFine_1.16.1_HD_U_G2_pre7.jar",
"26207daec6acc66250116e4f73295c9a031b491a": "preview_OptiFine_1.16.1_HD_U_G2_pre8.jar",
"6902e902866b765ff8e31716980c2067d491e004": "preview_OptiFine_1.16.1_HD_U_G2_pre9.jar",
"dde4274c696063496323731ad7b58e0f6d7265af": "OptiFine_1.16.2_HD_U_G3.jar",
"5b5627a814e8c95aa7ac27fa4cf2cc0b8bd4519b": "OptiFine_1.16.2_HD_U_G5.jar",
"5dafc10061020c519ba4abe4982a7dc80eae30f6": "preview_OptiFine_1.16.2_HD_U_G3_pre1.jar",
"0c0d9828984da34f9d5967027c244bb2c83a4396": "preview_OptiFine_1.16.2_HD_U_G3_pre2.jar",
"7c791e4348808132b8c7b59e6350ae2ee25a1918": "preview_OptiFine_1.16.2_HD_U_G3_pre3.jar",
"9a30a2cbc81095d38b4dc17e7d30a4b84e10e235": "preview_OptiFine_1.16.2_HD_U_G3_pre4.jar",
"ec1a4ce97e29c9ee2ac259d98e35bc386035a66d": "preview_OptiFine_1.16.2_HD_U_G3_pre5.jar",
"e03b0cdfbbcf01b1371b3e1090dee64e60a759d8": "preview_OptiFine_1.16.2_HD_U_G3_pre6.jar",
"40ae5fbb4e3724d6e5525d2688445c09ecfe5d39": "preview_OptiFine_1.16.2_HD_U_G3_pre7.jar",
"dc83d55f74722e1e6f503817e00e5ba2e6cb4abb": "OptiFine_1.16.3_HD_U_G3.jar",
"89d9472ee507946caa227e226524e5260ed944db": "OptiFine_1.16.3_HD_U_G4.jar",
"6a8f57ca84628da47b011e608ff449cde7ec13ed": "OptiFine_1.16.3_HD_U_G5.jar",
"f53bad69adaac9e523577dcb1c65b158802f3dee": "preview_OptiFine_1.16.3_HD_U_G3_pre2.jar",
"5b970eeae2ba07316b3e3a53da1d71de87c9f9f2": "preview_OptiFine_1.16.3_HD_U_G4_pre1.jar",
"599d9de6b656077b03fbebc9478924c548a6e9e0": "OptiFine_1.16.4_HD_U_G5.jar",
"7cce72db1f77c6178b549fa430e241eadb318f69": "OptiFine_1.16.4_HD_U_G6.jar",
"f49c042fa099907eafcbe22d7035d84f8b5715f1": "OptiFine_1.16.4_HD_U_G7.jar",
"f0b1eb59acdae57e10bb928046ee26012ef220ad": "preview_OptiFine_1.16.4_HD_U_G6_pre12.jar",
"ded7894eebda787b7bd5013aa8c66577f2ba03fc": "preview_OptiFine_1.16.4_HD_U_G7_pre1.jar",
"57cfcec3dd99b9f0950d0d6305bc95ecd8563212": "OptiFine_1.16.5_HD_U_G6.jar",
"706fb33784045d69e7ac5730426d813a39511dcd": "OptiFine_1.16.5_HD_U_G7.jar",
"137363842c5bfdebb72356a4079faeef3157eb3c": "OptiFine_1.16.5_HD_U_G8.jar",
"ad0f1577ce02a703185e46ee9e24fdeb65479fd5": "preview_OptiFine_1.16.5_HD_U_G7_pre10.jar",
"11770876367980da546c68bc95c4f794582907f2": "preview_OptiFine_1.16.5_HD_U_G7_pre11.jar",
"c5b32d2bad3bd54c31ddb29869a55fe44409cfe2": "preview_OptiFine_1.16.5_HD_U_G7_pre2.jar",
"9fc9cc09e72a3060d577f62f3535820056b0c2d4": "preview_OptiFine_1.16.5_HD_U_G7_pre4.jar",
"577fae32b60ac8d1b91e3fa645dc004d4a9e513e": "preview_OptiFine_1.16.5_HD_U_G7_pre5.jar",
"4bccf7f371ac83523796b4706569cc5077c46aea": "preview_OptiFine_1.16.5_HD_U_G7_pre6.jar",
"97b8d413a909354e2e0f42943cf292941dfe4c38": "preview_OptiFine_1.16.5_HD_U_G7_pre7.jar",
"9c48a804ee990e526000c7061bde9aa6f75d62a6": "preview_OptiFine_1.16.5_HD_U_G7_pre8.jar",
"9a9b69a52a4514f5d25c9f47127a822e93458fa3": "preview_OptiFine_1.16.5_HD_U_G7_pre9.jar",
"d16a450ec4205d02319480799008d8177ba05d69": "preview_OptiFine_1.16.5_HD_U_G8_pre1.jar",
"539c215aa0e8b8f696b325e89f3f9a3658ab32c8": "preview_OptiFine_1.16.5_HD_U_G8_pre10.jar",
"311ed81a27d24047b6ade33f46ac21c962897016": "preview_OptiFine_1.16.5_HD_U_G8_pre11.jar",
"4bc50f85809a299d30c3e3e73cfd87cad61cf7e6": "preview_OptiFine_1.16.5_HD_U_G8_pre12.jar",
"7de1b8e9f7cd6d7ca941d90121e50206aba80c68": "preview_OptiFine_1.16.5_HD_U_G8_pre13.jar",
"a4e021a75aab7ed1eb83ff7b45182d497aa930f2": "preview_OptiFine_1.16.5_HD_U_G8_pre14.jar",
"0ddcc4f47e10b2d671ebbf9147eb16cf8f1bb92a": "preview_OptiFine_1.16.5_HD_U_G8_pre2.jar",
"b8600be4fde6f8cc3c28173cc9bde6923956ff42": "preview_OptiFine_1.16.5_HD_U_G8_pre3.jar",
"d46b10098309929d9a6583323f059e65ba40a5e5": "preview_OptiFine_1.16.5_HD_U_G8_pre4.jar",
"e835424b36fdae3d5f46c6bf096852848127f1e7": "preview_OptiFine_1.16.5_HD_U_G8_pre5.jar",
"5a84983221331597f58e9636d41f1fb0c9e3de61": "preview_OptiFine_1.16.5_HD_U_G8_pre6.jar",
"d03ce2209d787539e5e51f80a7cbffc69a49ce9f": "preview_OptiFine_1.16.5_HD_U_G8_pre7.jar",
"1c646dc058aa32898917589858d900407883ba3f": "preview_OptiFine_1.16.5_HD_U_G8_pre8.jar",
"9bf369aa609e5612247473326dc298e6e6c9fecf": "preview_OptiFine_1.16.5_HD_U_G8_pre9.jar",
"b88b05278fbbf400cb5ec0cc26e0c5b7306e0c1e": "preview_OptiFine_1.17_HD_U_G9_pre16.jar",
"a6beac3091d17a4ba420116c75c89faeaee48539": "preview_OptiFine_1.17_HD_U_G9_pre17.jar",
"2839c5e74f64500561ee3e8605c51d9f06727c22": "preview_OptiFine_1.17_HD_U_G9_pre18.jar",
"455243ff55177189fff140ba7f4c4777cc977ef6": "preview_OptiFine_1.17_HD_U_G9_pre19.jar",
"bcc390e34bd529a86e6ae0e25d50417f46ba482f": "preview_OptiFine_1.17_HD_U_G9_pre20.jar",
"d30c23d745df54fd6ea2374e9c0b4392f0a78199": "preview_OptiFine_1.17_HD_U_G9_pre21.jar",
"075e7f8473b6785c3c6bbeeb41a3728f6ff3c3ba": "preview_OptiFine_1.17_HD_U_G9_pre22.jar",
"2ffaee36c717ae918cc93661df739740636900c9": "preview_OptiFine_1.17_HD_U_G9_pre23.jar",
"4385493e7ce4c6f4a3945f1d96246bce0b151c7e": "preview_OptiFine_1.17_HD_U_G9_pre24.jar",
"bcc33e67f134861eb30b1f0b9a829183c8afdebf": "preview_OptiFine_1.17_HD_U_G9_pre25.jar",
"6a8e34605cc828091027d9f011b804d916808af4": "preview_OptiFine_1.17_HD_U_G9_pre26.jar",
"4714bdceaefc15e0af9d5c72de8c05cf3051d760": "OptiFine_1.17.1_HD_U_G9.jar",
"cc64084e73d6423baf7ccbdd9f3760dd56f2fce5": "OptiFine_1.17.1_HD_U_H1.jar",
"1ee73bd882385a7abb9896b9469586741a6850a8": "preview_OptiFine_1.17.1_HD_U_G9_pre26.jar",
"537c1fbee9ebe25b49a8e6fb93db162d217f1c83": "preview_OptiFine_1.17.1_HD_U_G9_pre27.jar",
"68e1124f4b0376773d8d581da78f9bb6fc76486e": "preview_OptiFine_1.17.1_HD_U_G9_pre28.jar",
"3f80a562b39439831491c650c65ecb762e70d5f7": "preview_OptiFine_1.17.1_HD_U_G9_pre29.jar",
"9751c69568a61ce5b2a602b4dcf77c1cea57c709": "preview_OptiFine_1.17.1_HD_U_G9_pre30.jar",
"162fa1b4a0d5b89dad6030b5fd5903a3f24d39c7": "preview_OptiFine_1.17.1_HD_U_G9_pre31.jar",
"c4f3a33d892948927cecfaa60b284217325557c5": "preview_OptiFine_1.17.1_HD_U_G9_pre32.jar",
"12dbbc8ccd8dc02806768fb3fbe4a2d8c7c11301": "preview_OptiFine_1.17.1_HD_U_G9_pre33.jar",
"f74d1dfcb24c43e633076c63dd4492b29fca14b3": "preview_OptiFine_1.17.1_HD_U_G9_pre34.jar",
"42e55681fb106475ef13d5c5ef7113fb631392d4": "preview_OptiFine_1.17.1_HD_U_G9_pre35.jar",
"245ddb68349f5f81d067f4d9402d62db3d439d50": "preview_OptiFine_1.17.1_HD_U_H1_pre1.jar",
"0ac033f5ba4089311af55172281e73df143b9ac4": "preview_OptiFine_1.17.1_HD_U_H1_pre2.jar",
"68a3727bcdb0eac0986ff4e883d93fef406eb1c3": "preview_OptiFine_1.17.1_HD_U_H1_pre3.jar",
"a6534877558f0438bda604dc43093d61c6de88ae": "preview_OptiFine_1.17.1_HD_U_H1_pre4.jar",
"3e18db17b8695feb0a188ecdcb81b561d9d7030c": "preview_OptiFine_1.17.1_HD_U_H1_pre5.jar",
"c2e0c4391a671cd06931f7b6e965085c04c58762": "preview_OptiFine_1.17.1_HD_U_H1_pre6.jar",
"1ffb7325df5bf01b1d99bbbed8006a477b861394": "preview_OptiFine_1.17.1_HD_U_H1_pre7.jar",
"f2fa727e67b4795ddea3cc37cc5ced42e9c3b0c5": "preview_OptiFine_1.17.1_HD_U_H1_pre8.jar",
"aee167aff9937a068d514c85e3f8c766db99ed43": "preview_OptiFine_1.17.1_HD_U_H1_pre9.jar",
"59709e09632c80ab80ffcc4b5bf380d073bf467f": "preview_OptiFine_1.17.1_HD_U_H2_pre1.jar",
"52629bc5319ec203edd42de1776cb8d67a0e6b1b": "OptiFine_1.18_HD_U_H3.jar",
"e5077b4be03956118613162b5dbcf90406b27747": "preview_OptiFine_1.18_HD_U_H3_pre7.jar",
"326c160481cc849bccb821939992c101a8fe163b": "OptiFine_1.18.1_HD_U_H4.jar",
"1679654fdf49fd1216b090d51d3d8ec906094619": "OptiFine_1.18.1_HD_U_H5.jar",
"c86534ee821a78efc741c9cb407eec76625e1bab": "OptiFine_1.18.1_HD_U_H6.jar",
"34c98382131efa1394cff06528238dc44d255794": "preview_OptiFine_1.18.1_HD_U_H4_pre1.jar",
"d6cd0a2d0243ecad107220bff639eb46277762ea": "preview_OptiFine_1.18.1_HD_U_H5_pre1.jar",
"e24d766864f9238b94be5b3ad65608d7eb521fad": "preview_OptiFine_1.18.1_HD_U_H5_pre2.jar",
"bc1b87a6379acf5a5dca2a56196080ace49e4864": "preview_OptiFine_1.18.1_HD_U_H5_pre3.jar",
"555a6b8e420bd63a04d9f338d61f3333b4ccf5d9": "preview_OptiFine_1.18.1_HD_U_H5_pre4.jar",
"1d02694393f10992cbbd11a802568b0890b4dc81": "preview_OptiFine_1.18.1_HD_U_H5_pre5.jar",
"e3a22adc6931dcd7513f26afa1adecd62a35641b": "preview_OptiFine_1.18.1_HD_U_H5_pre6.jar",
"445a8cb8a1d565d8a1fabc0ba984c5936edbf561": "OptiFine_1.18.2_HD_U_H6.jar",
"8d83d4d5e325887e000a462029d2bf7a6e77eebc": "OptiFine_1.18.2_HD_U_H7.jar",
"2a461188167c1da6b1cfd520d85a656f45c74d70": "OptiFine_1.18.2_HD_U_H9.jar",
"e591777f272df533b62260a6dbe0e6cde62066f6": "preview_OptiFine_1.18.2_HD_U_H6_pre1.jar",
"f7c727c99e7f58ef9b86cc96fb894f628391016c": "preview_OptiFine_1.18.2_HD_U_H7_pre1.jar",
"cb51529f94e0eff5b9da92c8f70b1d498662a115": "preview_OptiFine_1.18.2_HD_U_H7_pre2.jar",
"ceea3c6612d2ec36c6ef1ca4a9355a141df9089f": "preview_OptiFine_1.18.2_HD_U_H7_pre3.jar",
"596c3a399889c3e2ef1d8fee994e8cf5a91e457e": "preview_OptiFine_1.18.2_HD_U_H8_pre1.jar",
"85f545aeb9d232ae970b0efa24e833f9c5d0fe33": "preview_OptiFine_1.18.2_HD_U_H9_pre1.jar",
"b9bbabb8f31f2b04ca1a1a8645df1be701f705bd": "preview_OptiFine_1.18.2_HD_U_H9_pre2.jar",
"c2bf213b4f60b6a29e402e3eaf219a4c85a5d1e6": "preview_OptiFine_1.18.2_HD_U_H9_pre3.jar",
"64c8c35313d25a64a8a8677cb851a2c83556ddc8": "OptiFine_1.19_HD_U_H8.jar",
"c668eaf9b6ce4e64f26a29fb27e830229ab4bef3": "OptiFine_1.19_HD_U_H9.jar",
"cda9130e0d36f0d6e06950ab393a8929b502a440": "preview_OptiFine_1.19_HD_U_H8_pre1.jar",
"cd1ae81f544672df776dc4426269771d2e82d0b2": "preview_OptiFine_1.19_HD_U_H8_pre10.jar",
"cb3ad2a9b4c23675ca569058f55dd174eed67a38": "preview_OptiFine_1.19_HD_U_H8_pre2.jar",
"2f7139ea0888ed18006e3a39b7475e88a3fd545d": "preview_OptiFine_1.19_HD_U_H8_pre3.jar",
"85cd70bb714e65935abf351811889a47a648bb96": "preview_OptiFine_1.19_HD_U_H8_pre4.jar",
"c65cbae4c1cfb5b9a5a573e4b83424df2d81b172": "preview_OptiFine_1.19_HD_U_H8_pre5.jar",
"10cd57890ebf449f5c258cff1c60d8dcefcc6cf6": "preview_OptiFine_1.19_HD_U_H8_pre6.jar",
"eb03e60c2fa9904a47eec1ef4a2c8804ee6cc0b8": "preview_OptiFine_1.19_HD_U_H8_pre7.jar",
"40d0b514168161fdefc91594f0d3f774f831d7de": "preview_OptiFine_1.19_HD_U_H8_pre8.jar",
"8e5bedd1c1a3641caad0743c4629db51c11f8feb": "preview_OptiFine_1.19_HD_U_H9_pre1.jar",
"7745216a810f0d617ed17a520fda44e12456492f": "preview_OptiFine_1.19_HD_U_H9_pre2.jar",
"6d6ed920eb99836e76549a7803af85881fcdee34": "preview_OptiFine_1.19_HD_U_H9_pre3.jar",
"7604e63ca74876b4e8ea9c3a36dbe80111c776b1": "preview_OptiFine_1.19_HD_U_H9_pre4.jar",
"b42dcce86c88ad4bcfa52a9d297afa800477aa78": "OptiFine_1.19.1_HD_U_H9.jar",
"648427a02940b3c53b3678cd5df1ff6997bd1e6c": "preview_OptiFine_1.19.1_HD_U_H9_pre1.jar",
"7cc221a6df6831b62c3b676b5f958e82a4487fd8": "OptiFine_1.19.2_HD_U_H9.jar",
"f304c56c7ab9119ce4269f2b019cd4668f48376d": "OptiFine_1.19.2_HD_U_I1.jar",
"708c1ecc148a0bd7d341e8f5e005e670903650f7": "OptiFine_1.19.2_HD_U_I2.jar",
"e0e924990bd12cc499fc89439f0b2c50b6e147fa": "preview_OptiFine_1.19.2_HD_U_I1_pre1.jar",
"465b229481c0a285195a168bf6581549a5cd7373": "preview_OptiFine_1.19.2_HD_U_I1_pre2.jar",
"af4398d0ce0628bcc0204e61c157e3c8951b9ad5": "preview_OptiFine_1.19.2_HD_U_I1_pre3.jar",
"35ef18f953bbea9a4cba6e14d0731d56f888114f": "preview_OptiFine_1.19.2_HD_U_I1_pre4.jar",
"b24d8725a8d06e1c190d1d2b3bc51c899f74b36d": "OptiFine_1.19.3_HD_U_I3.jar",
"f5ce615d9886e377079fd13cc4700a51d5a9c7b6": "preview_OptiFine_1.19.3_HD_U_I2_pre1.jar",
"10e6d97ea2c97cf0575a7c587fb11e1ddb73aa71": "preview_OptiFine_1.19.3_HD_U_I2_pre2.jar",
"89718cd608225d7c23f7e180d011daa4a47ce6bd": "preview_OptiFine_1.19.3_HD_U_I2_pre3.jar",
"204baf0fd59432054aebd2f83c83fe15fdd3e611": "preview_OptiFine_1.19.3_HD_U_I2_pre4.jar",
"90a52b3adc94a1b9a70f03f4c4ea14dfdf3424d8": "preview_OptiFine_1.19.3_HD_U_I2_pre5.jar",
"050d787936cc239ea8db0aa250d2ed72a44fa71d": "OptiFine_1.19.4_HD_U_I3.jar",
"253e9d29c6826c134fbdeea074d1d91c3410225c": "OptiFine_1.19.4_HD_U_I4.jar",
"068b998d6c5db5b3bb7ad6a0a7d0c6aefc4dc191": "preview_OptiFine_1.19.4_HD_U_I3_pre3.jar",
"a8b9156811c4745dbd612ebbb1df6f93d2fa734a": "preview_OptiFine_1.19.4_HD_U_I3_pre4.jar",
"6f2793e6236a27c06a74e8a1876ea6b29be038a7": "preview_OptiFine_1.19.4_HD_U_I3_pre5.jar",
"9ad82ef9f32fb5a0e7e7e0c7e8906c9f319a9705": "preview_OptiFine_1.19.4_HD_U_I3_pre6.jar",
"cf02ee8b5cf6922fbc28d6ffc8183949fff55d49": "preview_OptiFine_1.19.4_HD_U_I3_pre7.jar",
"702e0e9987209f66a5b4f825e7ac658fd49dd4b1": "preview_OptiFine_1.19.4_HD_U_I3_pre8.jar",
"04c8eaeadf24c42929500812a8f3f5fe634f0d1f": "preview_OptiFine_1.19.4_HD_U_I3_pre9.jar",
"968784e051c2c44f10a4ab448c979572977969c0": "preview_OptiFine_1.19.4_HD_U_I4_pre1.jar",
"abe4b1dbda7d95eb47953ef8aa11ed8ed1d8e238": "preview_OptiFine_1.20_HD_U_I5_pre3.jar",
"6612ce97d846018126f91cdbae23f0eff6bd2bab": "preview_OptiFine_1.20_HD_U_I5_pre4.jar",
"f37d62b6a071f6cd3d6bdfa378b56f7f5b9ec7c6": "preview_OptiFine_1.20_HD_U_I5_pre5.jar",
"c44bd276edd10fa910d929a4e7f6b8c7ffe6c23f": "OptiFine_1.20.1_HD_U_I5.jar",
"227914ab1037762eae44a2e66a568b87d7a02365": "OptiFine_1.20.1_HD_U_I6.jar",
"7484cf60bd8e019c40fa448daa294bd31f52962b": "preview_OptiFine_1.20.1_HD_U_I5_pre4.jar",
"953b1b4f15f56329bc4ade851846ef004ebbd5d0": "preview_OptiFine_1.20.1_HD_U_I5_pre5.jar",
"ebbdb3076f8291d7f8e10cbec3b1d3c49a0a701d": "preview_OptiFine_1.20.1_HD_U_I5_pre6.jar",
"b831d59eeb63122893f87a62ec3e5b3f1cc5586a": "preview_OptiFine_1.20.1_HD_U_I5_pre7.jar",
"dc93d991b07b2c5c6d2cd50a553599900c256264": "preview_OptiFine_1.20.1_HD_U_I5_pre8.jar",
"040a73427541170bccf162c04f4129b3909b8da1": "preview_OptiFine_1.20.1_HD_U_I5_pre9.jar",
"9f5fd75ede7770ad35383443b0a203f026cef5db": "preview_OptiFine_1.20.1_HD_U_I6_pre1.jar",
"85d5566b440036b926c5dd306108e48e7fb9b6e8": "preview_OptiFine_1.20.1_HD_U_I6_pre2.jar",
"d09c110358117336472414720bf961a581690dc5": "preview_OptiFine_1.20.1_HD_U_I6_pre3.jar",
"3365577f5161f9a1bbfefb15675bb5ece27600e4": "preview_OptiFine_1.20.1_HD_U_I6_pre4.jar",
"f317c701ae8c533fc34809dbce914bcced4e9a03": "preview_OptiFine_1.20.1_HD_U_I6_pre5.jar",
"445b32b14a2c2802d5a0832a708712d2fabafbc2": "preview_OptiFine_1.20.1_HD_U_I6_pre6.jar",
"9ea6b1389e88b81797f54b9d8d1e870d3fe8532c": "preview_OptiFine_1.20.2_HD_U_I7_pre1.jar",
"f2475e8d5746477d1d2972cd5aae62a5d9c04264": "OptiFine_1.20.4_HD_U_I7.jar",
"fec28a2fa152744bc2b47b3c5db3217974aa57ee": "preview_OptiFine_1.20.4_HD_U_I7_pre2.jar",
"4068365a57f4b154f4ebbc670894dc9038cf3819": "preview_OptiFine_1.20.4_HD_U_I7_pre3.jar",
"2481e0bed7610bfdfe26dea5a6bd8f01537c4ea8": "preview_OptiFine_1.20.4_HD_U_I7_pre4.jar",
"150990ba068cfb55bbf9e525c5ab84db0b8903e1": "preview_OptiFine_1.20.4_HD_U_I7_pre5.jar",
"fcf0f0f278d1901e4341d5ec8022bede03572430": "preview_OptiFine_1.20.4_HD_U_I7_pre6.jar",
"1c3395efa928758505b808291162a2514523b399": "preview_OptiFine_1.20.4_HD_U_I7_pre7.jar",
"4bcfec24e5d917ee96e0de6ef095bfc08782217b": "preview_OptiFine_1.20.4_HD_U_I8_pre1.jar",
"c931df21309800f1d1ece59b318e6b3e39c98176": "preview_OptiFine_1.20.4_HD_U_I8_pre2.jar",
"83f5ad147536405d86bf4ee47a7f7970e1fa8329": "preview_OptiFine_1.20.4_HD_U_I8_pre3.jar",
"7c6669319db18c9622d896da2a061479be88acf5": "preview_OptiFine_1.20.4_HD_U_I8_pre4.jar",
"bda7bbdd5e2d8c73148372e8c3b34d7c2485a4e8": "preview_OptiFine_1.20.6_HD_U_J1_pre18.jar",
"24a20f949a719480523669669a4e5e7339df6186": "OptiFine_1.21.1_HD_U_J1.jar",
"6bc74af8f733864539404a3f087aeb6f0cadc6b1": "preview_OptiFine_1.21.1_HD_U_J1_pre10.jar",
"4813a6cde6b0f5b20223d486ba7adf0816236a77": "preview_OptiFine_1.21.1_HD_U_J1_pre11.jar",
"c99ea98d8537f99b7470f1bcfa81c7ce405fd97e": "preview_OptiFine_1.21.1_HD_U_J1_pre12.jar",
"f2f55e6d61a4b90bdaa31eb520f5af78f8a1833b": "preview_OptiFine_1.21.1_HD_U_J1_pre13.jar",
"53a15b75ea9d20b644b0d3e9c7280ba172ade053": "preview_OptiFine_1.21.1_HD_U_J1_pre14.jar",
"da5575e1138fc2a4c28779cfc5e7d260253ccbdb": "preview_OptiFine_1.21.1_HD_U_J1_pre15.jar",
"0930876341beadf7014f57e9ebdd44be445dacbe": "OptiFine_1.21.3_HD_U_J2.jar",
"350f63ec3e59d21227f803d4078883884e482660": "preview_OptiFine_1.21.3_HD_U_J2_pre10.jar",
"2c48a4210acd25082c220f9601f17d1cc7769098": "preview_OptiFine_1.21.3_HD_U_J2_pre11.jar",
"8f0a097b1f7c9d065396bac9230eab62412a8b54": "preview_OptiFine_1.21.3_HD_U_J2_pre12.jar",
"f97df5638fcda7f4380d9284b329569d4e1554aa": "preview_OptiFine_1.21.3_HD_U_J2_pre8.jar",
"77df50552deea51687592b60c6d24561ee11aa74": "preview_OptiFine_1.21.3_HD_U_J2_pre9.jar",
"bcc6a7a140320566a985b1499a59fcb085255d14": "OptiFine_1.21.4_HD_U_J3.jar",
"e7bff4575b7311e11d7111506e855b4ad910481f": "preview_OptiFine_1.21.4_HD_U_J3_pre10.jar",
"52b2b6defdb8d1c189afb42125079f1b76ff72e2": "preview_OptiFine_1.21.4_HD_U_J3_pre11.jar",
"708f70aa5f70919a40d02ba75fc30d2d60c8e7cd": "preview_OptiFine_1.21.4_HD_U_J3_pre12.jar",
"86b796325d03d4571809a155c54cf6cba7c7482b": "preview_OptiFine_1.21.4_HD_U_J3_pre13.jar",
"618719a6a69eb674ea50c82cffe3d4e3c2f67abf": "preview_OptiFine_1.21.4_HD_U_J3_pre14.jar",
"98dd84b0581fcf7a27aa96238acb615b5b719825": "preview_OptiFine_1.21.4_HD_U_J3_pre15.jar",
"5c8573ae921082a069a9e7269500178f0c580c97": "preview_OptiFine_1.21.4_HD_U_J3_pre4.jar",
"17d51e1bfc94dde95368ab91aa0bb56e89c706bf": "preview_OptiFine_1.21.4_HD_U_J3_pre5.jar",
"1a4311606ac94d682800714793ea887c3276b18d": "preview_OptiFine_1.21.4_HD_U_J3_pre6.jar",
"12bf1a1b5607b5c62405a8c6ab4e1bd02bef2c8d": "preview_OptiFine_1.21.4_HD_U_J3_pre7.jar",
"f1f56403dc575fd975a000bfb133060285cacd6f": "preview_OptiFine_1.21.4_HD_U_J3_pre8.jar",
"9584de0e8204f5c855b60741d58af967dfc599a8": "preview_OptiFine_1.21.4_HD_U_J3_pre9.jar",
"0b97631e166ace9346744de33b7772df7bf5fbeb": "preview_OptiFine_1.21.4_HD_U_J4_pre1.jar",
"a776ec6c072529d4b731e0e6929a8aee56d04e76": "preview_OptiFine_1.21.4_HD_U_J4_pre2.jar",
"2a3fdfcbfc9c572b22c7d15c6129b8ea58ea1712": "preview_OptiFine_1.21.8_HD_U_J6_pre13.jar",
"ba7ca56c9b33d2fc7f7d22e20ce2765e4d257299": "preview_OptiFine_1.21.8_HD_U_J6_pre14.jar",
"097d3acd315b0682a4565ab9cbb4cdc1cc17d81f": "preview_OptiFine_1.21.8_HD_U_J6_pre15.jar",
"d7effca0de34c9442f06776848679a684f1f166a": "preview_OptiFine_1.21.8_HD_U_J6_pre16.jar",
"696ed52cdfd0ac2eafab8e98575d146b400de530": "preview_OptiFine_1.21.10_HD_U_J7_pre10.jar",
"be7bc81f0c9ce15316f76a059b0a690f62c03a5d": "preview_OptiFine_1.21.10_HD_U_J7_pre11.jar",
"731e698abe94bbd90cda628f80b4ce6ac5af8188": "preview_OptiFine_1.21.10_HD_U_J7_pre6.jar",
"63f189a4b7ff5b53e5bc56391f5274eab8555def": "preview_OptiFine_1.21.10_HD_U_J7_pre7.jar",
"7ccfc0085ccdef3f4ec21918a059ddd05171711a": "preview_OptiFine_1.21.10_HD_U_J7_pre8.jar",
"aad53ad3346944a12ed088ffbba98cf9eccc474c": "preview_OptiFine_1.21.10_HD_U_J7_pre9.jar",
"2800efeb56565f23494d372e7edac29333113e11": "OptiFine_1.21.11_HD_U_J8.jar",
"cee35b83c75da33dc07ffbb344386cae5cdc5c5b": "OptiFine_1.21.11_HD_U_J9.jar",
"687046282c370e690ec4b4aa6d90a6b42ada3ee1": "preview_OptiFine_1.21.11_HD_U_J8_pre10.jar",
"c09b05637610072bc33023d1c901e2726b7d16ce": "preview_OptiFine_1.21.11_HD_U_J8_pre11.jar",
"88682b40f08f462c4daae8e6b25ea99b1ed5cd14": "preview_OptiFine_1.21.11_HD_U_J8_pre12.jar",
"1c69de725c249d487df676470c71a7c05ece943c": "preview_OptiFine_1.21.11_HD_U_J8_pre3.jar",
"921b68e014539d75b77a47ca2117fc9e09ace4ed": "preview_OptiFine_1.21.11_HD_U_J8_pre4.jar",
"2fee496a35eeebf39fbb43017b12d752f5c37dee": "preview_OptiFine_1.21.11_HD_U_J8_pre5.jar",
"e5b5da810d43c02dccedfd99f83ae8ad63e49966": "preview_OptiFine_1.21.11_HD_U_J8_pre6.jar",
"8309638bab4d99258354b4d13c3cd82ff7c9645b": "preview_OptiFine_1.21.11_HD_U_J8_pre7.jar",
"b3d208ec7b5015842fcc77b3399b04b24e0b54cb": "preview_OptiFine_1.21.11_HD_U_J8_pre8.jar",
"c4ac7401bb5f668e4e92121501d1537cdbe7343c": "preview_OptiFine_1.21.11_HD_U_J8_pre9.jar",
"fd96031b5fc4c41ad71581eea53eb122022fff5b": "preview_OptiFine_1.21.11_HD_U_J9_pre1.jar",
"a159a217aba02cd7897394a51bd346c3b658b9f9": "preview_OptiFine_1.21.11_HD_U_J9_pre2.jar",
"2a784437076b539ea1a3c1a37ec98ff21bc3952c": "preview_OptiFine_1.21.11_HD_U_J9_pre3.jar",
"ee32ce4b406e290a0e99685afd09dc44cad03ddd": "preview_OptiFine_1.21.11_HD_U_J9_pre4.jar"
}
+2
View File
@@ -27,6 +27,7 @@
<Using Include="System.Collections.Generic" /> <Using Include="System.Collections.Generic" />
<Using Include="System.Collections.Concurrent" /> <Using Include="System.Collections.Concurrent" />
<Using Include="System.Text" /> <Using Include="System.Text" />
<Using Include="System.Text.RegularExpressions" />
<Using Include="System.Net" /> <Using Include="System.Net" />
<Using Include="System.Runtime.CompilerServices" /> <Using Include="System.Runtime.CompilerServices" />
<Using Include="System.Runtime.InteropServices" /> <Using Include="System.Runtime.InteropServices" />
@@ -58,6 +59,7 @@
<EmbeddedResource Include="Launch\JavaWrapper.jar" LogicalName="Launch\JavaWrapper.jar" /> <EmbeddedResource Include="Launch\JavaWrapper.jar" LogicalName="Launch\JavaWrapper.jar" />
<EmbeddedResource Include="Launch\LwjglUnsafeAgent.jar" LogicalName="Launch\LwjglUnsafeAgent.jar" /> <EmbeddedResource Include="Launch\LwjglUnsafeAgent.jar" LogicalName="Launch\LwjglUnsafeAgent.jar" />
<EmbeddedResource Include="Resource\WikiEntries.txt" LogicalName="Resource\WikiEntries.txt" /> <EmbeddedResource Include="Resource\WikiEntries.txt" LogicalName="Resource\WikiEntries.txt" />
<EmbeddedResource Include="Minecraft\OptiFineHashes.json" LogicalName="Minecraft\OptiFineHashes.json" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.VisualBasic" /> <Reference Include="Microsoft.VisualBasic" />
File diff suppressed because one or more lines are too long
+7 -6
View File
@@ -9,7 +9,10 @@ Public Class Application
'核心初始化 '核心初始化
MeloongCore.Main.Init("PCL") MeloongCore.Main.Init("PCL")
MeloongCore.Wpf.Main.Init() MeloongCore.Wpf.Main.Init()
If Not (e.Args.Contains("--update") OrElse e.Args.Contains("--gpu") OrElse e.Args.Contains("--memory")) Then '若是通过命令行调用,则不检查单例、不轮换日志(#9137)
WaitingMutex(e)
Logger.Instance = New PclLogger With {.logFolder = Paths.BaseThenName, .MinLevel = If(ModeDebug, LogLevel.Trace, LogLevel.Info)} Logger.Instance = New PclLogger With {.logFolder = Paths.BaseThenName, .MinLevel = If(ModeDebug, LogLevel.Trace, LogLevel.Info)}
End If
'提升主线程优先级 '提升主线程优先级
Thread.CurrentThread.Priority = ThreadPriority.Highest Thread.CurrentThread.Priority = ThreadPriority.Highest
'执行开发版测试 '执行开发版测试
@@ -47,7 +50,7 @@ Public Class Application
Catch ex As Exception Catch ex As Exception
Environment.Exit(ProcessReturnValues.Fail) Environment.Exit(ProcessReturnValues.Fail)
End Try End Try
ElseIf e.Args(0).StartsWithF("--memory") Then ElseIf e.Args(0) = "--memory" Then
'内存优化 '内存优化
Dim Ram = My.Computer.Info.AvailablePhysicalMemory Dim Ram = My.Computer.Info.AvailablePhysicalMemory
Try Try
@@ -70,7 +73,7 @@ Public Class Application
CheckPermissionWithException(Paths.Base & "PCL\") CheckPermissionWithException(Paths.Base & "PCL\")
Catch ex As Exception Catch ex As Exception
MsgBox($"PCL 没有对当前文件夹的权限({Paths.Base}PCL\),请尝试:" & vbCrLf & MsgBox($"PCL 没有对当前文件夹的权限({Paths.Base}PCL\),请尝试:" & vbCrLf &
"1. 将 PCL 移动到其他文件夹" & If(Paths.Base.StartsWithF("C:", True), ",例如 C 盘和桌面以外的其他位置。", "。") & vbCrLf & "1. 将 PCL 移动到其他文件夹" & If(PathUtils.GetDiskName(Paths.Base) = "C"c, ",例如 C 盘和桌面以外的其他位置。", "。") & vbCrLf &
"2. 删除当前目录中的 PCL 文件夹,然后再试。" & vbCrLf & "2. 删除当前目录中的 PCL 文件夹,然后再试。" & vbCrLf &
"3. 右键 PCL 选择属性,打开 兼容性 中的 以管理员身份运行此程序。", "3. 右键 PCL 选择属性,打开 兼容性 中的 以管理员身份运行此程序。",
MsgBoxStyle.Critical, "运行环境错误") MsgBoxStyle.Critical, "运行环境错误")
@@ -81,7 +84,7 @@ RetryCacheCheck:
DirectoryUtils.Create(PathTemp) DirectoryUtils.Create(PathTemp)
CheckPermissionWithException(PathTemp) CheckPermissionWithException(PathTemp)
Catch ex As Exception Catch ex As Exception
If PathTemp = Path.GetTempPath() & "PCL\" Then If PathUtils.IsSame(PathTemp, Path.GetTempPath() & "PCL\") Then
MyMsgBox("PCL 无法访问缓存文件夹,可能导致程序出错或无法正常使用!" & vbCrLf & vbCrLf & "错误原因:" & ex.GetDisplay(True), "缓存文件夹不可用") MyMsgBox("PCL 无法访问缓存文件夹,可能导致程序出错或无法正常使用!" & vbCrLf & vbCrLf & "错误原因:" & ex.GetDisplay(True), "缓存文件夹不可用")
Else Else
MyMsgBox("手动设置的缓存文件夹不可用,PCL 将使用默认缓存文件夹。" & vbCrLf & vbCrLf & "错误原因:" & ex.GetDisplay(True), "缓存文件夹不可用") MyMsgBox("手动设置的缓存文件夹不可用,PCL 将使用默认缓存文件夹。" & vbCrLf & vbCrLf & "错误原因:" & ex.GetDisplay(True), "缓存文件夹不可用")
@@ -92,8 +95,6 @@ RetryCacheCheck:
End Try End Try
DirectoryUtils.Create(PathTemp & "Cache\") DirectoryUtils.Create(PathTemp & "Cache\")
DirectoryUtils.Create(Paths.AppDataThenName) DirectoryUtils.Create(Paths.AppDataThenName)
'要求单例
WaitingMutex(e)
'设置 ToolTipService 默认值 '设置 ToolTipService 默认值
ToolTipService.InitialShowDelayProperty.OverrideMetadata(GetType(DependencyObject), New FrameworkPropertyMetadata(100)) ToolTipService.InitialShowDelayProperty.OverrideMetadata(GetType(DependencyObject), New FrameworkPropertyMetadata(100))
'设置网络配置默认值 '设置网络配置默认值
@@ -231,7 +232,7 @@ RetryCacheCheck:
End Function End Function
End Sub End Sub
Private Shared Sub ExtractLibwebp() '这个方法会调用 ModBase,进而调用 MeloongCore,所以不能放在 New 里 Private Shared Sub ExtractLibwebp() '这个方法会调用 ModBase,进而调用 MeloongCore,所以不能放在 New 里
SetDllDirectory(PathPure.Value.TrimEnd("\"c)) SetDllDirectory(PathUtils.RemoveSlashSuffix(PathPure.Value))
Try Try
ExtractResources(PathPure.Value & "libwebp.dll", "libwebp64") ExtractResources(PathPure.Value & "libwebp.dll", "libwebp64")
Catch ex As Exception Catch ex As Exception
@@ -44,8 +44,8 @@ Namespace Controls.Behaviors
Private Shared Sub AddCommandBindingsToTextBox(tb As TextBox) Private Shared Sub AddCommandBindingsToTextBox(tb As TextBox)
tb.CommandBindings.Add(New CommandBinding(ApplicationCommands.Copy, AddressOf OnCopyTextBox)) tb.CommandBindings.Add(New CommandBinding(ApplicationCommands.Copy, AddressOf OnCopyTextBox))
tb.CommandBindings.Add(New CommandBinding(ApplicationCommands.Cut, AddressOf OnCutTextBox)) tb.CommandBindings.Add(New CommandBinding(ApplicationCommands.Cut, AddressOf OnCutTextBox, AddressOf CanEditTextBox))
tb.CommandBindings.Add(New CommandBinding(ApplicationCommands.Paste, AddressOf OnPasteTextBox)) tb.CommandBindings.Add(New CommandBinding(ApplicationCommands.Paste, AddressOf OnPasteTextBox, AddressOf CanEditTextBox))
End Sub End Sub
Private Shared Sub AddCommandBindingsToRichTextBox(rtb As RichTextBox) Private Shared Sub AddCommandBindingsToRichTextBox(rtb As RichTextBox)
@@ -58,42 +58,61 @@ Namespace Controls.Behaviors
dg.CommandBindings.Add(New CommandBinding(ApplicationCommands.Copy, AddressOf OnCopyDataGrid)) dg.CommandBindings.Add(New CommandBinding(ApplicationCommands.Copy, AddressOf OnCopyDataGrid))
End Sub End Sub
Private Shared Sub SetClipboardText(text As String)
Forms.Clipboard.SetDataObject(text, False, retryTimes:=10, retryDelay:=100) '实际的复制
Try
System.Windows.Clipboard.Flush() '将数据持久化到系统剪贴板,避免应用程序退出后数据丢失
Catch ex As Exception '持久化失败不抛出异常
Logger.Warn(ex, $"剪贴板持久化失败(错误码:0x{ex.HResult:X8})")
End Try
End Sub
Private Shared Sub OnCopyTextBox(sender As Object, e As ExecutedRoutedEventArgs) Private Shared Sub OnCopyTextBox(sender As Object, e As ExecutedRoutedEventArgs)
Dim tb = TryCast(sender, TextBox) Dim tb = TryCast(sender, TextBox)
If tb Is Nothing OrElse tb.SelectionLength <= 0 Then Return If tb Is Nothing OrElse tb.SelectionLength <= 0 Then Return
Try Try
Forms.Clipboard.Clear() SetClipboardText(tb.SelectedText)
Forms.Clipboard.SetDataObject(tb.SelectedText, True)
Catch ex As Exception Catch ex As Exception
Logger.Warn(ex, "复制失败,请稍后再试", LogBehavior.Toast) Logger.Warn(ex, $"复制失败,请稍后再试(错误码:0x{ex.HResult:X8})", LogBehavior.Toast)
End Try End Try
e.Handled = True e.Handled = True
End Sub End Sub
Private Shared Sub CanEditTextBox(sender As Object, e As CanExecuteRoutedEventArgs)
Dim tb = TryCast(sender, TextBox)
e.CanExecute = tb IsNot Nothing AndAlso Not tb.IsReadOnly AndAlso
(e.Command IsNot ApplicationCommands.Cut OrElse tb.SelectionLength > 0)
e.Handled = True
End Sub
Private Shared Sub OnCutTextBox(sender As Object, e As ExecutedRoutedEventArgs) Private Shared Sub OnCutTextBox(sender As Object, e As ExecutedRoutedEventArgs)
Dim tb = TryCast(sender, TextBox) Dim tb = TryCast(sender, TextBox)
If tb Is Nothing OrElse tb.SelectionLength <= 0 Then Return e.Handled = True
If tb Is Nothing OrElse tb.IsReadOnly OrElse tb.SelectionLength <= 0 Then Return
Try Try
Forms.Clipboard.Clear() SetClipboardText(tb.SelectedText)
Forms.Clipboard.SetDataObject(tb.SelectedText, True)
Catch ex As Exception Catch ex As Exception
Logger.Warn(ex, "剪切失败,请稍后再试", LogBehavior.Toast) Logger.Warn(ex, $"剪切失败,请稍后再试(错误码:0x{ex.HResult:X8})", LogBehavior.Toast)
Return
End Try End Try
tb.SelectedText = String.Empty tb.SelectedText = String.Empty
e.Handled = True
End Sub End Sub
Private Shared Sub OnPasteTextBox(sender As Object, e As ExecutedRoutedEventArgs) Private Shared Sub OnPasteTextBox(sender As Object, e As ExecutedRoutedEventArgs)
Dim tb = TryCast(sender, TextBox) Dim tb = TryCast(sender, TextBox)
If tb Is Nothing Then Return e.Handled = True
If tb Is Nothing OrElse tb.IsReadOnly Then Return
Try Try
If Forms.Clipboard.ContainsText() Then Dim pasteText = Retrier.Attempt(func:=
Dim pasteText = Forms.Clipboard.GetText() Function(Attempt) As String
Return If(Forms.Clipboard.ContainsText(), Forms.Clipboard.GetText(), Nothing)
End Function, maxAttempts:=10, delay:=Function() TimeSpan.FromMilliseconds(100))
If pasteText IsNot Nothing Then
'将换行符替换为空格(#7825) '将换行符替换为空格(#7825)
If Not tb.AcceptsReturn Then pasteText = pasteText.ReplaceLineEndings(" ", mergeMultiple:=True) If Not tb.AcceptsReturn Then pasteText = pasteText.ReplaceLineEndings(" ", mergeMultiple:=True)
@@ -105,10 +124,8 @@ Namespace Controls.Behaviors
tb.SelectionLength = 0 tb.SelectionLength = 0
End If End If
Catch ex As Exception Catch ex As Exception
Logger.Warn(ex, "粘贴失败,请稍后再试", LogBehavior.Toast) Logger.Warn(ex, $"粘贴失败,请稍后再试(错误码:0x{ex.HResult:X8})", LogBehavior.Toast)
End Try End Try
e.Handled = True
End Sub End Sub
Private Shared Sub OnCopyRichTextBox(sender As Object, e As ExecutedRoutedEventArgs) Private Shared Sub OnCopyRichTextBox(sender As Object, e As ExecutedRoutedEventArgs)
@@ -119,10 +136,9 @@ Namespace Controls.Behaviors
If String.IsNullOrEmpty(textRange.Text) Then Return If String.IsNullOrEmpty(textRange.Text) Then Return
Try Try
Forms.Clipboard.Clear() SetClipboardText(textRange.Text)
Forms.Clipboard.SetDataObject(textRange.Text, True)
Catch ex As Exception Catch ex As Exception
Logger.Warn(ex, "复制失败,请稍后再试", LogBehavior.Toast) Logger.Warn(ex, $"复制失败,请稍后再试(错误码:0x{ex.HResult:X8})", LogBehavior.Toast)
End Try End Try
e.Handled = True e.Handled = True
@@ -134,16 +150,16 @@ Namespace Controls.Behaviors
Dim selection = New TextRange(rtb.Selection.Start, rtb.Selection.End) Dim selection = New TextRange(rtb.Selection.Start, rtb.Selection.End)
If String.IsNullOrEmpty(selection.Text) Then Return If String.IsNullOrEmpty(selection.Text) Then Return
e.Handled = True
Try Try
Forms.Clipboard.Clear() SetClipboardText(selection.Text)
Forms.Clipboard.SetDataObject(selection.Text, True)
Catch ex As Exception Catch ex As Exception
Logger.Warn(ex, "剪切失败,请稍后再试", LogBehavior.Toast) Logger.Warn(ex, $"剪切失败,请稍后再试(错误码:0x{ex.HResult:X8})", LogBehavior.Toast)
Return
End Try End Try
selection.Text = String.Empty selection.Text = String.Empty
e.Handled = True
End Sub End Sub
Private Shared Sub OnPasteRichTextBox(sender As Object, e As ExecutedRoutedEventArgs) Private Shared Sub OnPasteRichTextBox(sender As Object, e As ExecutedRoutedEventArgs)
@@ -151,9 +167,11 @@ Namespace Controls.Behaviors
If rtb Is Nothing Then Return If rtb Is Nothing Then Return
Try Try
If Not Forms.Clipboard.ContainsText() Then Return Dim pasteText = Retrier.Attempt(func:=
Function(Attempt) As String
Dim pasteText = Forms.Clipboard.GetText() Return If(Forms.Clipboard.ContainsText(), Forms.Clipboard.GetText(), Nothing)
End Function, maxAttempts:=2, delay:=Function() TimeSpan.FromMilliseconds(200))
If pasteText Is Nothing Then Return
Dim selection = rtb.Selection Dim selection = rtb.Selection
selection.Text = pasteText selection.Text = pasteText
@@ -162,7 +180,7 @@ Namespace Controls.Behaviors
rtb.CaretPosition = caretPos rtb.CaretPosition = caretPos
rtb.Selection.Select(caretPos, caretPos) rtb.Selection.Select(caretPos, caretPos)
Catch ex As Exception Catch ex As Exception
Logger.Warn(ex, "粘贴失败,请稍后再试", LogBehavior.Toast) Logger.Warn(ex, $"粘贴失败,请稍后再试(错误码:0x{ex.HResult:X8})", LogBehavior.Toast)
End Try End Try
e.Handled = True e.Handled = True
@@ -187,10 +205,9 @@ Namespace Controls.Behaviors
Dim sbStr = sb.ToString().TrimEnd(ControlChars.Cr, ControlChars.Lf) Dim sbStr = sb.ToString().TrimEnd(ControlChars.Cr, ControlChars.Lf)
Try Try
Forms.Clipboard.Clear() SetClipboardText(sbStr)
Forms.Clipboard.SetDataObject(sbStr, True)
Catch ex As Exception Catch ex As Exception
Logger.Warn(ex, "复制失败,请稍后再试", LogBehavior.Toast) Logger.Warn(ex, $"复制失败,请稍后再试(错误码:0x{ex.HResult:X8})", LogBehavior.Toast)
End Try End Try
e.Handled = True e.Handled = True
+29 -70
View File
@@ -102,73 +102,36 @@ Public Class MyCard
RunInUi(Sub() UseAnimation = RawUseAnimation, True) RunInUi(Sub() UseAnimation = RawUseAnimation, True)
End If End If
End Sub End Sub
Public Sub StackInstall()
StackInstall(SwapControl, SwapType, Title) '填充
TriggerForceResize() Private ContentBuilder As Action(Of Panel)
Public Sub SetContentBuilder(Builder As Action(Of Panel))
ContentBuilder = Builder
End Sub End Sub
Public Shared Sub StackInstall(ByRef Stack As StackPanel, Type As Integer, Optional CardTitle As String = "") Public Sub SetContentBuilder(Of T)(Items As IEnumerable(Of T), ItemBuilder As Func(Of T, UIElement))
'这一部分的代码是好几年前留下的究极屎坑,当时还不知道该咋正确调用这种方法,就写了这么一坨屎 SetContentBuilder(
'但是现在……反正勉强能用……懒得改了就这样吧.jpg Sub(Stack)
'别骂了别骂了.jpg For Each Item In Items
If Stack.Tag Is Nothing Then Return Stack.Children.Add(ItemBuilder(Item))
'排序
Dim HasDuplicates As Boolean = False
Select Case Type
Case 3
Stack.Tag = CType(Stack.Tag, List(Of DlOptiFineListEntry)).SortByComparison(Function(a, b) CompareVersionGE(a.DisplayName, b.DisplayName))
Case 4, 10
Stack.Tag = CType(Stack.Tag, List(Of DlLiteLoaderListEntry)).SortByComparison(Function(a, b) CompareVersionGE(a.Inherit, b.Inherit))
Case 6
Stack.Tag = CType(Stack.Tag, List(Of DlForgeVersionEntry)).OrderByDescending(Function(a) a).ToList
Case 8, 9
Stack.Tag = CType(Stack.Tag, List(Of ResourceVersion)).SortByComparison(Function(a, b) a.ReleaseDate > b.ReleaseDate)
HasDuplicates =
CType(Stack.Tag, List(Of ResourceVersion)).DistinctBy(Function(a) a.Display).Count <>
CType(Stack.Tag, List(Of ResourceVersion)).Count
End Select
'控件转换
Select Case Type
Case 6
ForgeDownloadListItemPreload(Stack, Stack.Tag, AddressOf ForgeSave_Click, True)
Case 8
PageDownloadResourceDetail.ResourceFilesCardPreload(Stack, Stack.Tag)
End Select
'实现控件虚拟化
For Each Data As Object In Stack.Tag
Select Case Type
Case 0
Stack.Children.Add(CType(Data, McInstance).ToListItem(AddressOf PageSelectRight.McInstanceListContent))
Case 2
Stack.Children.Add(McDownloadListItem(Data, AddressOf McDownloadMenuSave, True))
Case 3
Stack.Children.Add(OptiFineDownloadListItem(Data, AddressOf OptiFineSave_Click, True))
Case 6
Stack.Children.Add(ForgeDownloadListItem(Data, AddressOf ForgeSave_Click, True))
Case 7
'不能使用 AddressOf,这导致了 #535,原因完全不明,疑似是编译器 Bug
Stack.Children.Add(McDownloadListItem(Data, Sub(sender, e) FrmDownloadInstall.MinecraftSelected(sender, e), False))
Case 8
'若存在重复的版本名,则显示文件名而非版本名(#1344)
Stack.Children.Add(CType(Data, ResourceVersion).ToListItem(
AddressOf FrmDownloadResourceDetail.Save_Click, IsBadDisplay:=HasDuplicates))
Case 9
'若存在重复的版本名,则显示文件名而非版本名(#1344)
Stack.Children.Add(CType(Data, ResourceVersion).ToListItem(
AddressOf FrmDownloadResourceDetail.Install_Click, AddressOf FrmDownloadResourceDetail.Save_Click, IsBadDisplay:=HasDuplicates))
Case 10
Stack.Children.Add(LiteLoaderDownloadListItem(Data, AddressOf LiteLoaderSave_Click, True))
Case 11
Stack.Children.Add(CType(Data, HelpEntry).ToListItem)
Case 12
Stack.Children.Add(FabricDownloadListItem(CType(Data, JObject), AddressOf FrmDownloadInstall.Fabric_Selected))
Case 13
Stack.Children.Add(NeoForgeDownloadListItem(Data, AddressOf NeoForgeSave_Click, True))
Case Else
Logger.Error($"未知的虚拟化种类:{Type}")
End Select
Next Next
Stack.Children.Add(New FrameworkElement With {.Height = 18}) '下边距,同时适应折叠 Stack.Children.Add(New FrameworkElement With {.Height = 18})
Stack.Tag = Nothing End Sub)
End Sub
''' <summary>
''' 使用 ContentBuilder 生成卡片内容。
''' </summary>
Public Sub BuildContent()
If ContentBuilder Is Nothing Then Return
Dim Stack = TryCast(SwapControl, Panel)
If Stack Is Nothing Then Return
Try
ContentBuilder(Stack)
ContentBuilder = Nothing
Catch
Stack.Children.Clear()
Throw
End Try
TriggerForceResize()
End Sub End Sub
'动画 '动画
@@ -278,10 +241,6 @@ Public Class MyCard
Public SwapControl As FrameworkElement Public SwapControl As FrameworkElement
Public Property CanSwap As Boolean = False Public Property CanSwap As Boolean = False
''' <summary> ''' <summary>
''' 被折叠的种类,用于控件虚拟化。
''' </summary>
Public Property SwapType As Integer
''' <summary>
''' 是否已被折叠。 ''' 是否已被折叠。
''' </summary> ''' </summary>
Public Property IsSwapped As Boolean Public Property IsSwapped As Boolean
@@ -293,7 +252,7 @@ Public Class MyCard
_IsSwapped = value _IsSwapped = value
If SwapControl Is Nothing Then Return If SwapControl Is Nothing Then Return
'展开 '展开
If Not IsSwapped AndAlso TypeOf SwapControl Is StackPanel Then StackInstall(SwapControl, SwapType, Title) If Not IsSwapped Then BuildContent()
'若尚未加载,会在 Loaded 事件中触发无动画的折叠,不需要在这里进行 '若尚未加载,会在 Loaded 事件中触发无动画的折叠,不需要在这里进行
If Not IsLoaded Then Return If Not IsLoaded Then Return
'更新样式 '更新样式
+1 -1
View File
@@ -90,7 +90,7 @@ Public Class MyImage
Catch ex As Exception Catch ex As Exception
Logger.Warn(ex, $"加载图片失败({value})") Logger.Warn(ex, $"加载图片失败({value})")
Try Try
If value.StartsWithF(PathTemp) Then FileUtils.Delete(value) If value.StartsWithF(PathTemp, True) Then FileUtils.Delete(value)
Catch Catch
End Try End Try
End Try End Try
@@ -508,7 +508,8 @@ Public Class MyListItem
RaiseEvent Click(sender, e) RaiseEvent Click(sender, e)
If e.Handled Then Return If e.Handled Then Return
'触发自定义事件 '触发自定义事件
If CustomEventService.GetEventType(sender) <> CustomEvent.EventType.None Then If CustomEventService.GetEventType(sender) <> CustomEvent.EventType.None OrElse
CustomEventService.GetEvents(sender).Events.Count > 0 Then
RaiseCustomEvent() RaiseCustomEvent()
e.Handled = True e.Handled = True
End If End If
+44 -20
View File
@@ -11,6 +11,16 @@ Public Class FormMain
Dim FeatureList As New List(Of KeyValuePair(Of Integer, String)) Dim FeatureList As New List(Of KeyValuePair(Of Integer, String))
'统计更新日志条目 '统计更新日志条目
If BuildType = BuildTypes.Release Then If BuildType = BuildTypes.Release Then
If LastVersion < 413 Then 'Release 2.99.0.0
FeatureList.Add(New KeyValuePair(Of Integer, String)(5, "删除:为遵守相关法律法规,PCL 暂时不再提供 MC 下载功能,非常抱歉 qwq……"))
End If
If LastVersion < 411 Then 'Release 2.14.0.0
FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "新增:允许修改、添加、移除、更新已安装版本的各个组件,可从版本设置中进入"))
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:启动游戏时允许按 Esc 取消"))
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:允许复制主页的启动日志中的内容"))
FeatureCount += 19
BugCount += 26
End If
If LastVersion < 408 Then 'Release 2.13.1.1 If LastVersion < 408 Then 'Release 2.13.1.1
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:版本设置中,添加打开截图文件夹的快捷方式")) FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:版本设置中,添加打开截图文件夹的快捷方式"))
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:导出整合包时,可以勾选导出光影设置文件")) FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:导出整合包时,可以勾选导出光影设置文件"))
@@ -93,6 +103,16 @@ Public Class FormMain
'3:BUG+ IMP* FEAT- '3:BUG+ IMP* FEAT-
'2:BUG* IMP- '2:BUG* IMP-
'1:BUG- '1:BUG-
If LastVersion < 414 Then 'Snapshot 2.99.0.0
FeatureList.Add(New KeyValuePair(Of Integer, String)(5, "删除:为遵守相关法律法规,PCL 暂时不再提供 MC 下载功能,非常抱歉 qwq……"))
End If
If LastVersion < 412 Then 'Snapshot 2.14.0.0
FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "新增:允许修改、添加、移除、更新已安装版本的各个组件,可从版本设置中进入"))
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:启动游戏时允许按 Esc 取消"))
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:允许复制主页的启动日志中的内容"))
FeatureCount += 19
BugCount += 26
End If
If LastVersion < 410 Then 'Snapshot 2.13.1.1 If LastVersion < 410 Then 'Snapshot 2.13.1.1
If LastVersion = 409 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:更新 Mod 时可能更新成另一个 Mod 加载器的版本")) If LastVersion = 409 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:更新 Mod 时可能更新成另一个 Mod 加载器的版本"))
FeatureCount += 2 FeatureCount += 2
@@ -786,7 +806,7 @@ Public Class FormMain
If PanMsg.Children.Any Then If PanMsg.Children.Any Then
If e.Key = Key.Enter Then If e.Key = Key.Enter Then
CType(PanMsg.Children(0), Object).Btn1_Click() CType(PanMsg.Children(0), Object).Btn1_Click()
ElseIf e.Key = Key.Escape OrElse (e.Key = Key.W AndAlso Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) Then ElseIf e.Key = Key.Escape Then
Dim Msg As Object = PanMsg.Children(0) Dim Msg As Object = PanMsg.Children(0)
If TypeOf Msg IsNot MyMsgInput AndAlso TypeOf Msg IsNot MyMsgSelect AndAlso Msg.Btn3.Visibility = Visibility.Visible Then If TypeOf Msg IsNot MyMsgInput AndAlso TypeOf Msg IsNot MyMsgSelect AndAlso Msg.Btn3.Visibility = Visibility.Visible Then
Msg.Btn3_Click() Msg.Btn3_Click()
@@ -803,13 +823,13 @@ Public Class FormMain
' 在没有弹窗时:继续检查…… ' 在没有弹窗时:继续检查……
'========================== '==========================
'按 ESC:启动中则取消启动,否则返回上一级 '按 ESC 取消启动,或返回上一级
If e.Key = Key.Escape Then If e.Key = Key.Escape Then
If McLaunchLoader.State = LoadState.Loading Then If PageCurrent = FormMain.PageType.Launch AndAlso McLaunchLoader.State = LoadState.Loading Then
McLaunchCancel() FrmLaunchLeft.BtnCancel_Click()
Else Return
TriggerPageBack()
End If End If
TriggerPageBack()
End If End If
'更改隐藏版本可见性 '更改隐藏版本可见性
If e.Key = Key.F11 AndAlso PageCurrent = FormMain.PageType.InstanceSelect Then If e.Key = Key.F11 AndAlso PageCurrent = FormMain.PageType.InstanceSelect Then
@@ -834,11 +854,6 @@ Public Class FormMain
If TypeOf PageRight Is IRefreshable Then CType(PageRight, IRefreshable).Refresh() If TypeOf PageRight Is IRefreshable Then CType(PageRight, IRefreshable).Refresh()
Return Return
End If End If
'Ctrl + w 关闭程序
If e.Key = Key.W AndAlso Keyboard.Modifiers.HasFlag(ModifierKeys.Control) Then
EndProgram(True)
Return
End If
'调用启动游戏 '调用启动游戏
If e.Key = Key.Enter AndAlso PageCurrent = FormMain.PageType.Launch Then If e.Key = Key.Enter AndAlso PageCurrent = FormMain.PageType.Launch Then
If IsAprilEnabled AndAlso Not IsAprilGiveup Then If IsAprilEnabled AndAlso Not IsAprilGiveup Then
@@ -1026,14 +1041,14 @@ Public Class FormMain
If FilePathList.Count > 1 Then If FilePathList.Count > 1 Then
'必须要求全部为 jar 文件 '必须要求全部为 jar 文件
For Each File In PathList For Each File In PathList
If Not {"jar", "litemod", "disabled", "old"}.Contains(File.AfterLast(".").Lower) Then If Not {"jar", "litemod", "disabled", "old"}.Contains(PathUtils.GetExtension(File)) Then
Hint("一次请只拖入一个文件!", HintType.Red) Hint("一次请只拖入一个文件!", HintType.Red)
Return Return
End If End If
Next Next
End If End If
'主页 '主页
Dim Extension As String = FilePath.AfterLast(".").Lower Dim Extension As String = PathUtils.GetExtension(FilePath)
If Extension = "xaml" Then If Extension = "xaml" Then
Logger.Info("文件后缀为 XAML,作为主页加载") Logger.Info("文件后缀为 XAML,作为主页加载")
If FileUtils.Exists(Paths.Base & "PCL\Custom.xaml") Then If FileUtils.Exists(Paths.Base & "PCL\Custom.xaml") Then
@@ -1054,20 +1069,24 @@ Public Class FormMain
If PageInstanceMod.InstallMods(PathList) Then Return If PageInstanceMod.InstallMods(PathList) Then Return
'安装整合包 '安装整合包
If {"zip", "rar", "mrpack"}.Any(Function(t) t = Extension) Then '部分压缩包是 zip 格式但后缀为 rar,总之试一试 If {"zip", "rar", "mrpack"}.Any(Function(t) t = Extension) Then '部分压缩包是 zip 格式但后缀为 rar,总之试一试
Logger.Info("文件为压缩包,尝试作为整合包安装") Hint("正在读取压缩包……")
Hint("检测到压缩包拖入,正在准备安装……")
Try Try
Logger.Info("文件为压缩包,尝试作为整合包安装")
ModpackInstall(FilePath) ModpackInstall(FilePath)
RunInUi(Sub() FrmMain.PageChange(FormMain.PageType.TaskManager)) RunInUi(Sub() FrmMain.PageChange(FormMain.PageType.TaskManager))
Return Return
Catch ex As Exception Catch ex As Exception
If RestrictMcDownload AndAlso ex.Message = "为遵守相关法律法规,PCL 暂时不再提供 MC 下载功能,非常抱歉 qwq……" Then
Hint(ex.Message, HintType.Red)
Return
End If
If ex.IsCanceled Then Return If ex.IsCanceled Then Return
'安装失败,继续往后尝试 '安装失败,继续往后尝试
End Try End Try
End If End If
'RAR 处理 'RAR 处理
If Extension = "rar" Then If Extension = "rar" Then
Hint("PCL 无法处理 rar 格式的压缩包,请在解压后重新压缩为 zip 格式再试!") Hint("PCL 无法处理 rar 格式的压缩包,请在解压后重新压缩为 zip 格式再试!", HintType.Red)
Return Return
End If End If
'错误报告分析 '错误报告分析
@@ -1083,7 +1102,7 @@ Public Class FormMain
Logger.Error(ex, "自主错误报告分析失败") Logger.Error(ex, "自主错误报告分析失败")
End Try End Try
'未知操作 '未知操作
Hint("PCL 无法确定应当执行的文件拖拽操作……") Hint("PCL 无法确定适合的操作……", HintType.Red)
End Sub, "文件拖拽") End Sub, "文件拖拽")
End Sub End Sub
@@ -1225,9 +1244,10 @@ Public Class FormMain
OtherTest = 2 OtherTest = 2
InstanceOverall = 0 InstanceOverall = 0
InstanceSetup = 1 InstanceSetup = 1
InstanceMod = 2 InstanceModify = 2
InstanceModDisabled = 3 InstanceMod = 3
InstanceExport = 4 InstanceModDisabled = 4
InstanceExport = 5
End Enum End Enum
''' <summary> ''' <summary>
''' 获取次级页面的名称。若并非次级页面则返回空字符串,故可以以此判断是否为次级页面。 ''' 获取次级页面的名称。若并非次级页面则返回空字符串,故可以以此判断是否为次级页面。
@@ -1392,6 +1412,10 @@ Public Class FormMain
''' </summary> ''' </summary>
Private Sub PageChangeActual(Stack As PageStackData, Optional SubType As PageSubType = -1) Private Sub PageChangeActual(Stack As PageStackData, Optional SubType As PageSubType = -1)
If PageCurrent = Stack AndAlso (PageCurrentSub = SubType OrElse SubType = -1) Then Return If PageCurrent = Stack AndAlso (PageCurrentSub = SubType OrElse SubType = -1) Then Return
If Stack.Page = PageType.InstanceSetup AndAlso PageInstanceLeft.Instance IsNot Nothing AndAlso McInstanceLock.IsLocked(PageInstanceLeft.Instance) Then
Hint("该版本正在被修改,暂时无法进入版本设置!", HintType.Red)
Return
End If
AniControlEnabled += 1 AniControlEnabled += 1
Try Try
+27 -17
View File
@@ -5,13 +5,15 @@ Public Module ModBase
#Region "声明" #Region "声明"
'下列版本信息由更新器自动修改 'PCL-ISE 版本规则:主版本.次版本.修订版本;内部 VersionCode 继续用于更新兼容判断。
Public Const VersionBaseName As String = "2.13.1.1" '显示用版本名 Public Const ProductName As String = "Plain Craft Launcher (PCL) InfstarMC Edition"
Public Const ProductShortName As String = "PCL-ISE"
Public Const VersionBaseName As String = "0.1.0" '显示用版本名
Public Const CommitHash As String = "" 'Commit Hash,由 GitHub Workflow 自动替换 Public Const CommitHash As String = "" 'Commit Hash,由 GitHub Workflow 自动替换
#If RELEASE Then #If RELEASE Then
Public Const VersionCode As Integer = 408 '正式版 Public Const VersionCode As Integer = 413 '正式版
#Else #Else
Public Const VersionCode As Integer = 410 '快照版 Public Const VersionCode As Integer = 414 '快照版
#End If #End If
'版本信息 '版本信息
@@ -47,7 +49,7 @@ Public Module ModBase
''' <summary> ''' <summary>
''' 程序的缓存文件夹路径,以 \ 结尾。 ''' 程序的缓存文件夹路径,以 \ 结尾。
''' </summary> ''' </summary>
Public PathTemp As String = If(Settings.Get(Of String)("SystemSystemCache") = "", Path.GetTempPath() & "PCL\", Settings.Get(Of String)("SystemSystemCache")).ToString.Replace("/", "\").TrimEnd("\") & "\" Public PathTemp As String = PathUtils.AddSlashSuffix(If(Settings.Get(Of String)("SystemSystemCache") = "", Path.GetTempPath() & "PCL\", Settings.Get(Of String)("SystemSystemCache")).Replace("/", "\"))
''' <summary> ''' <summary>
''' 当前程序的语言。 ''' 当前程序的语言。
''' </summary> ''' </summary>
@@ -75,7 +77,7 @@ Public Module ModBase
''' <summary> ''' <summary>
''' 系统盘盘符,以 \ 结尾。例如 “C:\”。 ''' 系统盘盘符,以 \ 结尾。例如 “C:\”。
''' </summary> ''' </summary>
Public OsDrive As String = Environment.GetLogicalDrives().Where(Function(p) Directory.Exists(p)).First.Upper.First & ":\" '#3799 Public OsDrive As String = Environment.GetLogicalDrives().First(Function(p) Directory.Exists(p)) '#3799
Public ReadOnly Property ModeDebug As Boolean Public ReadOnly Property ModeDebug As Boolean
Get Get
@@ -576,7 +578,7 @@ Public Module ModBase
Try Try
If String.IsNullOrEmpty(Folder) Then Return False If String.IsNullOrEmpty(Folder) Then Return False
Folder = PathUtils.AddSlashSuffix(Folder) Folder = PathUtils.AddSlashSuffix(Folder)
If Folder.EndsWithF(":\System Volume Information\") OrElse Folder.EndsWithF(":\$RECYCLE.BIN\") Then Return False If Folder.EndsWithF(":\System Volume Information\", True) OrElse Folder.EndsWithF(":\$RECYCLE.BIN\", True) Then Return False
If Not DirectoryUtils.Exists(Folder) Then Return False If Not DirectoryUtils.Exists(Folder) Then Return False
Dim TestFilePath As String = $"{Folder}CheckPermission{GetUuid()}.txt" Dim TestFilePath As String = $"{Folder}CheckPermission{GetUuid()}.txt"
FileUtils.Write(TestFilePath, "临时文件,用于检测该文件夹的权限是否正常。") FileUtils.Write(TestFilePath, "临时文件,用于检测该文件夹的权限是否正常。")
@@ -593,7 +595,7 @@ Public Module ModBase
Public Sub CheckPermissionWithException(Folder As String) Public Sub CheckPermissionWithException(Folder As String)
If String.IsNullOrWhiteSpace(Folder) Then Throw New ArgumentNullException("文件夹名不能为空!") If String.IsNullOrWhiteSpace(Folder) Then Throw New ArgumentNullException("文件夹名不能为空!")
Folder = PathUtils.AddSlashSuffix(Folder) Folder = PathUtils.AddSlashSuffix(Folder)
If Folder.EndsWithF(":\System Volume Information\") OrElse Folder.EndsWithF(":\$RECYCLE.BIN\") Then Throw New UnauthorizedAccessException("没有对系统文件夹的权限!") If Folder.EndsWithF(":\System Volume Information\", True) OrElse Folder.EndsWithF(":\$RECYCLE.BIN\", True) Then Throw New UnauthorizedAccessException("没有对系统文件夹的权限!")
If Not DirectoryUtils.Exists(Folder) Then Throw New DirectoryNotFoundException("文件夹不存在!") If Not DirectoryUtils.Exists(Folder) Then Throw New DirectoryNotFoundException("文件夹不存在!")
Dim TestFilePath As String = $"{Folder}CheckPermission{GetUuid()}.txt" Dim TestFilePath As String = $"{Folder}CheckPermission{GetUuid()}.txt"
FileUtils.Write(TestFilePath, "临时文件,用于检测该文件夹的权限是否正常。") FileUtils.Write(TestFilePath, "临时文件,用于检测该文件夹的权限是否正常。")
@@ -788,7 +790,7 @@ Public Module ModBase
''' <summary> ''' <summary>
''' 可用于临时存放文件的,不含任何特殊字符的文件夹路径,以“\”结尾。 ''' 可用于临时存放文件的,不含任何特殊字符的文件夹路径,以“\”结尾。
''' </summary> ''' </summary>
Public PathPure As Lazy(Of String) = New Lazy(Of String)( Public PathPure As New Lazy(Of String)(
Function() Function()
If Paths.Base.IsAsciiOnly() Then If Paths.Base.IsAsciiOnly() Then
Return Paths.Base & "PCL\" Return Paths.Base & "PCL\"
@@ -845,6 +847,12 @@ Public Module ModBase
Return (Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000 Return (Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000
End Function End Function
''' <summary> ''' <summary>
''' 获取十进制 Unix 时间戳。
''' </summary>
Public Function ToUnixTimestampUtc(Time As Date) As Long
Return (Time.ToUniversalTime().Ticks - 621355968000000000) / 10000000
End Function
''' <summary>
''' 时间戳转化为日期。 ''' 时间戳转化为日期。
''' </summary> ''' </summary>
Public Function GetDate(timeStamp As Integer) As Date Public Function GetDate(timeStamp As Integer) As Date
@@ -1021,21 +1029,23 @@ Public Module ModBase
''' <summary> ''' <summary>
''' 设置剪贴板。将在另一线程运行,且不会抛出异常。 ''' 设置剪贴板。将在另一线程运行,且不会抛出异常。
''' </summary> ''' </summary>
Public Sub ClipboardSet(Text As String, Optional ShowSuccessHint As Boolean = True, Optional SuccessHint As String = "") Public Sub ClipboardSet(Text As String, Optional ShowSuccessHint As Boolean = True)
RunInThread( RunInThread(
Sub()
RunInUi(
Sub() Sub()
Try Try
Retrier.Attempt(delay:=Function(Attempt) TimeSpan.FromMilliseconds(200), maxAttempts:=4, isRetryAllowed:=Function(ex) True, action:=
Sub()
RunInUiWait(
Sub()
My.Computer.Clipboard.Clear() My.Computer.Clipboard.Clear()
If Not String.IsNullOrEmpty(Text) Then My.Computer.Clipboard.SetText(Text) If Not String.IsNullOrEmpty(Text) Then My.Computer.Clipboard.SetText(Text)
If ShowSuccessHint Then Hint(If(String.IsNullOrEmpty(SuccessHint), "已成功复制!", SuccessHint), HintType.Green)
Catch ex As Exception
Logger.Error(ex, behavior:=LogBehavior.None)
Hint("复制失败,可能由于剪贴板被其他程序占用!", HintType.Red)
End Try
End Sub) End Sub)
End Sub) End Sub)
If ShowSuccessHint Then Hint("已成功复制!", HintType.Green)
Catch ex As Exception
Logger.Error(ex, "可能由于剪贴板被其他程序占用,文本复制失败", LogBehavior.Toast)
End Try
End Sub)
End Sub End Sub
''' <summary> ''' <summary>
''' 获取剪贴板文本。将在 UI 线程运行,且不会抛出异常。 ''' 获取剪贴板文本。将在 UI 线程运行,且不会抛出异常。
@@ -1396,7 +1396,7 @@ FinishExCatch:
Public Overrides Sub Start(Optional Input As Object = Nothing, Optional IsForceRestart As Boolean = False) Public Overrides Sub Start(Optional Input As Object = Nothing, Optional IsForceRestart As Boolean = False)
If Input IsNot Nothing Then Files = New ConcurrentQueue(Of NetFile)(CType(Input, IEnumerable(Of NetFile))) If Input IsNot Nothing Then Files = New ConcurrentQueue(Of NetFile)(CType(Input, IEnumerable(Of NetFile)))
'去重 '去重
Files = New ConcurrentQueue(Of NetFile)(Files.DistinctBy(Function(a) a.LocalPath)) Files = New ConcurrentQueue(Of NetFile)(Files.DistinctBy(Function(a) PathUtils.ForCompare(a.LocalPath).Lower()))
'设置剩余文件数 '设置剩余文件数
SyncLock FileRemainLock SyncLock FileRemainLock
FileRemain += Files.Where(Function(f) f.State <> NetState.Finished).Count FileRemain += Files.Where(Function(f) f.State <> NetState.Finished).Count
@@ -1421,7 +1421,7 @@ FinishExCatch:
Next Next
If Not File.HasAvailableSource() Then Throw New ArgumentException("输入的下载链接不正确!") If Not File.HasAvailableSource() Then Throw New ArgumentException("输入的下载链接不正确!")
File.LocalPath = File.LocalPath.Replace("/", "\") File.LocalPath = File.LocalPath.Replace("/", "\")
If Not File.LocalPath.Lower.Contains(":\") Then Throw New ArgumentException("输入的本地文件地址不正确:" & File.LocalPath) If Not File.LocalPath.Contains(":\") Then Throw New ArgumentException("输入的本地文件地址不正确:" & File.LocalPath)
If File.LocalPath.EndsWithF("\") Then Throw New ArgumentException("请输入含文件名的完整文件路径:" & File.LocalPath) If File.LocalPath.EndsWithF("\") Then Throw New ArgumentException("请输入含文件名的完整文件路径:" & File.LocalPath)
DirectoryUtils.Create(PathUtils.RemoveLastPart(File.LocalPath)) '创建目标文件夹 DirectoryUtils.Create(PathUtils.RemoveLastPart(File.LocalPath)) '创建目标文件夹
Next Next
@@ -1519,7 +1519,7 @@ FinishExCatch:
Select(Function(FolderName) (FolderName, File.LocalPath.IndexOfF(FolderName, True))). Select(Function(FolderName) (FolderName, File.LocalPath.IndexOfF(FolderName, True))).
Where(Function(kv) kv.Item2 >= 0).ToList Where(Function(kv) kv.Item2 >= 0).ToList
If Not TypeIndexes.Any Then If Not TypeIndexes.Any Then
If File.LocalName.EndsWithF(".jar") Then If PathUtils.GetExtension(File.LocalName) = "jar" Then
TypeIndexes.Add(("\versions\", 1)) '总是对 jar 进行版本文件检查,以包括另存为 jar 的情况 TypeIndexes.Add(("\versions\", 1)) '总是对 jar 进行版本文件检查,以包括另存为 jar 的情况
Else Else
Return Nothing Return Nothing
@@ -1547,7 +1547,7 @@ FinishExCatch:
Case "versions\" Case "versions\"
'版本 jar 或 json:查找 MC 文件夹下的各个版本文件夹 '版本 jar 或 json:查找 MC 文件夹下的各个版本文件夹
For Each VersionFolder In VersionFolders For Each VersionFolder In VersionFolders
For Each Candidate In DirectoryUtils.EnumerateFiles(VersionFolder, searchPattern:="*." & PathUtils.GetLastPart(File.LocalPath).AfterLast(".").Lower) For Each Candidate In DirectoryUtils.EnumerateFiles(VersionFolder, searchPattern:="*." & PathUtils.GetExtension(File.LocalPath))
If CheckCandidate(Candidate, File.Check) Then Return Candidate If CheckCandidate(Candidate, File.Check) Then Return Candidate
Next Next
Next Next
@@ -382,7 +382,7 @@ Public Class ValidateFolderPath
Public Overrides Function Validate(Str As String) As String Public Overrides Function Validate(Str As String) As String
'去除尾部斜线,统一为 \ '去除尾部斜线,统一为 \
Str = Str.Replace("/", "\") Str = Str.Replace("/", "\")
If Not Str.TrimEnd("\").EndsWithF(":") Then Str = Str.TrimEnd("\") If Not PathUtils.RemoveSlashSuffix(Str).EndsWithF(":") Then Str = PathUtils.RemoveSlashSuffix(Str)
'检查是否为空 '检查是否为空
Dim LengthCheck As String = New ValidateNullOrWhiteSpace().Validate(Str) Dim LengthCheck As String = New ValidateNullOrWhiteSpace().Validate(Str)
If LengthCheck <> "" Then Return LengthCheck If LengthCheck <> "" Then Return LengthCheck
@@ -92,7 +92,7 @@ Public Class CrashAnalyzer
'尝试视作压缩包解压 '尝试视作压缩包解压
Try Try
Dim Info = FileUtils.GetInfo(FilePath) Dim Info = FileUtils.GetInfo(FilePath)
If Info.Exists AndAlso Info.Length > 0 AndAlso Not FilePath.EndsWithF(".jar", True) Then If Info.Exists AndAlso Info.Length > 0 AndAlso Not PathUtils.GetExtension(FilePath) = "jar" Then
FileUtils.ExtractToDirectory(FilePath, TempFolder & "Temp\") FileUtils.ExtractToDirectory(FilePath, TempFolder & "Temp\")
Logger.Info($"已解压导入的日志文件:{FilePath}") Logger.Info($"已解压导入的日志文件:{FilePath}")
GoTo Extracted GoTo Extracted
@@ -163,9 +163,9 @@ Extracted:
Else Else
TargetType = AnalyzeFileType.ExtraLogFile TargetType = AnalyzeFileType.ExtraLogFile
End If End If
ElseIf MatchName.EndsWithF(".log", True) Then ElseIf PathUtils.GetExtension(MatchName) = "log" Then
TargetType = AnalyzeFileType.ExtraLogFile TargetType = AnalyzeFileType.ExtraLogFile
ElseIf MatchName.EndsWithF(".txt", True) Then ElseIf PathUtils.GetExtension(MatchName) = "txt" Then
TargetType = AnalyzeFileType.ExtraReportFile TargetType = AnalyzeFileType.ExtraReportFile
Else Else
Logger.Info($"{MatchName} 分类为 Ignore") Logger.Info($"{MatchName} 分类为 Ignore")
@@ -242,8 +242,6 @@ Public Module ModDownload
DlPreferMojang = DeltaTime < 4000 DlPreferMojang = DeltaTime < 4000
Logger.Info($"Mojang 官方源加载耗时:{DeltaTime}ms,{If(DlPreferMojang, "可优先使用官方源", "不优先使用官方源")}") Logger.Info($"Mojang 官方源加载耗时:{DeltaTime}ms,{If(DlPreferMojang, "可优先使用官方源", "不优先使用官方源")}")
End If End If
'添加 PCL 特供项
If FileUtils.Exists(PathTemp & "Cache\download.json") Then Versions.Merge(FileUtils.ReadAsJson(PathTemp & "Cache\download.json"))
'返回 '返回
Loader.Output = New DlClientListResult With {.IsOfficial = True, .SourceName = "Mojang 官方源", .Value = Json} Loader.Output = New DlClientListResult With {.IsOfficial = True, .SourceName = "Mojang 官方源", .Value = Json}
'MC 更新提示 'MC 更新提示
@@ -438,7 +436,7 @@ Public Module ModDownload
.FileName = If(Name(i).ContainsIgnoreCase("pre"), "preview_", "") & "OptiFine_" & Name(i).Replace(" ", "_") & ".jar", .FileName = If(Name(i).ContainsIgnoreCase("pre"), "preview_", "") & "OptiFine_" & Name(i).Replace(" ", "_") & ".jar",
.RequiredForgeVersion = Forge(i).Replace("Forge ", "").Replace("#", "")} .RequiredForgeVersion = Forge(i).Replace("Forge ", "").Replace("#", "")}
If Entry.RequiredForgeVersion.Contains("N/A") Then Entry.RequiredForgeVersion = Nothing If Entry.RequiredForgeVersion.Contains("N/A") Then Entry.RequiredForgeVersion = Nothing
Entry.InstanceName = Entry.Inherit & "-OptiFine_" & Name(i).ToString.Replace(" ", "_").Replace(Entry.Inherit & "_", "") Entry.InstanceName = Entry.Inherit & "-OptiFine_" & Name(i).AfterFirst(" ").Replace(" ", "_")
Versions.Add(Entry) Versions.Add(Entry)
Next Next
Loader.Output = New DlOptiFineListResult With {.IsOfficial = True, .SourceName = "OptiFine 官方源", .Value = Versions} Loader.Output = New DlOptiFineListResult With {.IsOfficial = True, .SourceName = "OptiFine 官方源", .Value = Versions}
@@ -581,7 +579,7 @@ Public Module ModDownload
If Version <> other.Version Then If Version <> other.Version Then
Return Version.CompareTo(other.Version) Return Version.CompareTo(other.Version)
Else Else
Return CompareVersion(VersionName, other.VersionName) Return McUtils.CompareVersion(VersionName, other.VersionName)
End If End If
End Function End Function
End Class End Class
@@ -18,7 +18,7 @@ Public Module ModJava
'强制选择的 Java '强制选择的 Java
Dim SelectedJavaSetting = Settings.Get(Of String)("LaunchArgumentJavaSelect") Dim SelectedJavaSetting = Settings.Get(Of String)("LaunchArgumentJavaSelect")
Dim SelectedJava = If(SelectedJavaSetting.StartsWithF("{"), SelectedJavaSetting.DeserializeJson(Of JObject)()("Path").ToString, "") Dim SelectedJava = If(SelectedJavaSetting.StartsWithF("{"), SelectedJavaSetting.DeserializeJson(Of JObject)()("Path").ToString, "")
Dim Match = OldJavaList.FirstOrDefault(Function(j) j.Folder.Equals(SelectedJava, StringComparison.OrdinalIgnoreCase)) Dim Match = OldJavaList.FirstOrDefault(Function(j) PathUtils.IsSame(j.Folder, SelectedJava))
If Match IsNot Nothing Then If Match IsNot Nothing Then
OldJavaList.Remove(Match) OldJavaList.Remove(Match)
OldJavaList.Insert(0, Match) OldJavaList.Insert(0, Match)
@@ -78,7 +78,7 @@ Public Module ModJava
Return Return
End If End If
'不再移除它 '不再移除它
Configs.JavaRemovedList.Edit(Sub(ByRef List) List.RemoveIf(Function(f) f.Equals(NewJava.Folder, StringComparison.OrdinalIgnoreCase))) Configs.JavaRemovedList.Edit(Sub(ByRef List) List.RemoveIf(Function(f) PathUtils.IsSame(f, NewJava.Folder)))
'加入列表顶部 '加入列表顶部
Configs.JavaList.Edit( Configs.JavaList.Edit(
Sub(ByRef List) Sub(ByRef List)
@@ -150,16 +150,16 @@ Public Module ModJava
End Sub End Sub
'原版 26-:通过版本号判断 '原版 26-:通过版本号判断
If (Not Instance.Version.Vaild AndAlso Instance.ReleaseTime >= New Date(2024, 4, 2)) OrElse If (Not Instance.Version.Valid AndAlso Instance.ReleaseTime >= New Date(2024, 4, 2)) OrElse
(Instance.Version.Vaild AndAlso Instance.Version.Vanilla >= New Version(20, 0, 5)) Then (Instance.Version.Valid AndAlso Instance.Version.Vanilla >= New Version(20, 0, 5)) Then
'1.20.5+(24w14a+):至少 Java 21 '1.20.5+(24w14a+):至少 Java 21
AddConstraint(ValueRange(Of Version).AtLeast(New Version(21, 0))) AddConstraint(ValueRange(Of Version).AtLeast(New Version(21, 0)))
ElseIf (Not Instance.Version.Vaild AndAlso Instance.ReleaseTime >= New Date(2021, 11, 16)) OrElse ElseIf (Not Instance.Version.Valid AndAlso Instance.ReleaseTime >= New Date(2021, 11, 16)) OrElse
(Instance.Version.Vaild AndAlso Instance.Version.Vanilla.Major >= 18) Then (Instance.Version.Valid AndAlso Instance.Version.Vanilla.Major >= 18) Then
'1.18 pre2+:至少 Java 17 '1.18 pre2+:至少 Java 17
AddConstraint(ValueRange(Of Version).AtLeast(New Version(17, 0))) AddConstraint(ValueRange(Of Version).AtLeast(New Version(17, 0)))
ElseIf (Not Instance.Version.Vaild AndAlso Instance.ReleaseTime >= New Date(2021, 5, 11)) OrElse ElseIf (Not Instance.Version.Valid AndAlso Instance.ReleaseTime >= New Date(2021, 5, 11)) OrElse
(Instance.Version.Vaild AndAlso Instance.Version.Vanilla.Major >= 17) Then (Instance.Version.Valid AndAlso Instance.Version.Vanilla.Major >= 17) Then
'1.17+ (21w19a+):至少 Java 16 '1.17+ (21w19a+):至少 Java 16
AddConstraint(ValueRange(Of Version).AtLeast(New Version(16, 0))) AddConstraint(ValueRange(Of Version).AtLeast(New Version(16, 0)))
ElseIf Instance.ReleaseTime.Year >= 2017 Then 'Minecraft 1.12 与 1.11 的分界线正好是 2017 年,太棒了 ElseIf Instance.ReleaseTime.Year >= 2017 Then 'Minecraft 1.12 与 1.11 的分界线正好是 2017 年,太棒了
@@ -185,7 +185,7 @@ Public Module ModJava
End If End If
'OptiFine 检测 'OptiFine 检测
If Instance.Version.HasOptiFine AndAlso Instance.Version.Vaild Then '不管非标准版本 If Instance.Version.HasOptiFine AndAlso Instance.Version.Valid Then '不管非标准版本
If Instance.Version.Vanilla.Major < 7 Then If Instance.Version.Vanilla.Major < 7 Then
'<1.7:至多 Java 8 '<1.7:至多 Java 8
AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0))) AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0)))
@@ -199,11 +199,11 @@ Public Module ModJava
End If End If
'Forge 检测(部分测试结果见 #8432) 'Forge 检测(部分测试结果见 #8432)
If Instance.Version.HasForge Then If Instance.Version.ModLoaders.HasFlagF(ModLoaders.Forge) Then
If Instance.Version.Vanilla >= New Version(6, 0, 1) AndAlso Instance.Version.Vanilla <= New Version(7, 0, 2) Then If Instance.Version.Vanilla >= New Version(6, 0, 1) AndAlso Instance.Version.Vanilla <= New Version(7, 0, 2) Then
'1.6.1 - 1.7.2:必须 Java 7 '1.6.1 - 1.7.2:必须 Java 7
AddConstraint(ValueRange(Of Version).ClosedOpen(New Version(7, 0), New Version(8, 0))) AddConstraint(ValueRange(Of Version).ClosedOpen(New Version(7, 0), New Version(8, 0)))
ElseIf Instance.Version.Vanilla.Major <= 12 OrElse Not Instance.Version.Vaild Then '非标准版本 ElseIf Instance.Version.Vanilla.Major <= 12 OrElse Not Instance.Version.Valid Then '非标准版本
'<=1.12:Java 8 '<=1.12:Java 8
AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0))) AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0)))
ElseIf Instance.Version.Vanilla.Major <= 14 Then ElseIf Instance.Version.Vanilla.Major <= 14 Then
@@ -212,38 +212,38 @@ Public Module ModJava
ElseIf Instance.Version.Vanilla.Major = 15 Then ElseIf Instance.Version.Vanilla.Major = 15 Then
'1.15:Java 8 - 15 '1.15:Java 8 - 15
AddConstraint(ValueRange(Of Version).ClosedOpen(New Version(8, 0), New Version(16, 0))) AddConstraint(ValueRange(Of Version).ClosedOpen(New Version(8, 0), New Version(16, 0)))
ElseIf CompareVersionGE(Instance.Version.Forge, "34.0.0") AndAlso CompareVersionGE("36.2.25", Instance.Version.Forge) Then ElseIf McUtils.CompareVersionGE(Instance.Version.ModLoaderVersions(ModLoaders.Forge), "34.0.0") AndAlso McUtils.CompareVersionGE("36.2.25", Instance.Version.ModLoaderVersions(ModLoaders.Forge)) Then
'1.16.3~5,Forge 34.0.0 ~ 36.2.25:最高 Java 8u320 '1.16.3~5,Forge 34.0.0 ~ 36.2.25:最高 Java 8u320
AddConstraint(ValueRange(Of Version).AtMost(New Version(8, 0, 320))) AddConstraint(ValueRange(Of Version).AtMost(New Version(8, 0, 320)))
ElseIf CompareVersionGE(Instance.Version.Forge, "36.2.26") AndAlso CompareVersionGE("36.999999.999999", Instance.Version.Forge) Then ElseIf McUtils.CompareVersionGE(Instance.Version.ModLoaderVersions(ModLoaders.Forge), "36.2.26") AndAlso McUtils.CompareVersionGE("36.999999.999999", Instance.Version.ModLoaderVersions(ModLoaders.Forge)) Then
'1.16.5,Forge 36.2.26+,<37.0.0:最高 Java 23 '1.16.5,Forge 36.2.26+,<37.0.0:最高 Java 23
AddConstraint(ValueRange(Of Version).LessThan(New Version(24, 0))) AddConstraint(ValueRange(Of Version).LessThan(New Version(24, 0)))
ElseIf CompareVersionGE(Instance.Version.Forge, "37.0.0") AndAlso CompareVersionGE("37.0.79", Instance.Version.Forge) Then ElseIf McUtils.CompareVersionGE(Instance.Version.ModLoaderVersions(ModLoaders.Forge), "37.0.0") AndAlso McUtils.CompareVersionGE("37.0.79", Instance.Version.ModLoaderVersions(ModLoaders.Forge)) Then
'1.17.1,Forge 37.0.0 ~ 37.0.79:最高 Java 16 '1.17.1,Forge 37.0.0 ~ 37.0.79:最高 Java 16
AddConstraint(ValueRange(Of Version).LessThan(New Version(17, 0))) AddConstraint(ValueRange(Of Version).LessThan(New Version(17, 0)))
ElseIf Instance.Version.Vanilla.Major = 18 AndAlso Instance.Version.HasOptiFine Then '#305 ElseIf Instance.Version.Vanilla.Major = 18 AndAlso Instance.Version.HasOptiFine Then '#305
'1.18:若安装了 OptiFine,最高 Java 18 '1.18:若安装了 OptiFine,最高 Java 18
AddConstraint(ValueRange(Of Version).LessThan(New Version(19, 0))) AddConstraint(ValueRange(Of Version).LessThan(New Version(19, 0)))
ElseIf CompareVersionGE(Instance.Version.Forge, "45.0.21") AndAlso CompareVersionGE("45.0.65", Instance.Version.Forge) Then ElseIf McUtils.CompareVersionGE(Instance.Version.ModLoaderVersions(ModLoaders.Forge), "45.0.21") AndAlso McUtils.CompareVersionGE("45.0.65", Instance.Version.ModLoaderVersions(ModLoaders.Forge)) Then
'1.19.4,Forge 45.0.21 ~ 45.0.65:最高 Java 19 '1.19.4,Forge 45.0.21 ~ 45.0.65:最高 Java 19
AddConstraint(ValueRange(Of Version).LessThan(New Version(20, 0))) AddConstraint(ValueRange(Of Version).LessThan(New Version(20, 0)))
ElseIf CompareVersionGE(Instance.Version.Forge, "45.0.66") AndAlso CompareVersionGE("47.4.8", Instance.Version.Forge) Then ElseIf McUtils.CompareVersionGE(Instance.Version.ModLoaderVersions(ModLoaders.Forge), "45.0.66") AndAlso McUtils.CompareVersionGE("47.4.8", Instance.Version.ModLoaderVersions(ModLoaders.Forge)) Then
'1.19.4~1.20.1,Forge 45.0.66 ~ 47.4.8:最高 Java 21 '1.19.4~1.20.1,Forge 45.0.66 ~ 47.4.8:最高 Java 21
AddConstraint(ValueRange(Of Version).LessThan(New Version(22, 0))) AddConstraint(ValueRange(Of Version).LessThan(New Version(22, 0)))
End If End If
End If End If
'NeoForge 检测 'NeoForge 检测
If Instance.Version.HasNeoForge Then If Instance.Version.ModLoaders.HasFlagF(ModLoaders.NeoForge) Then
If Instance.Version.Vanilla = New Version(20, 0, 1) OrElse If Instance.Version.Vanilla = New Version(20, 0, 1) OrElse
(CompareVersionGE("20.2.62-beta", Instance.Version.NeoForge) AndAlso Not Instance.Version.NeoForge.Contains("25w14craftmine")) Then (McUtils.CompareVersionGE("20.2.62-beta", Instance.Version.ModLoaderVersions(ModLoaders.NeoForge)) AndAlso Not Instance.Version.ModLoaderVersions(ModLoaders.NeoForge).Contains("25w14craftmine")) Then
'1.20.1,以及 1.20.2 的 20.2.62-beta 之前:最高 Java 21 '1.20.1,以及 1.20.2 的 20.2.62-beta 之前:最高 Java 21
AddConstraint(ValueRange(Of Version).LessThan(New Version(22, 0))) AddConstraint(ValueRange(Of Version).LessThan(New Version(22, 0)))
End If End If
End If End If
'Fabric 检测 'Fabric 检测
If Instance.Version.HasFabric AndAlso Instance.Version.Vaild Then '不管非标准版本 If Instance.Version.ModLoaders.HasFlagF(ModLoaders.Fabric) AndAlso Instance.Version.Valid Then '不管非标准版本
If Instance.Version.Vanilla.Major >= 15 AndAlso Instance.Version.Vanilla.Major <= 16 Then If Instance.Version.Vanilla.Major >= 15 AndAlso Instance.Version.Vanilla.Major <= 16 Then
'1.15 - 1.16:Java 8+ '1.15 - 1.16:Java 8+
AddConstraint(ValueRange(Of Version).AtLeast(New Version(8, 0))) AddConstraint(ValueRange(Of Version).AtLeast(New Version(8, 0)))
@@ -251,15 +251,14 @@ Public Module ModJava
'1.18+:Java 17+ '1.18+:Java 17+
AddConstraint(ValueRange(Of Version).AtLeast(New Version(17, 0))) AddConstraint(ValueRange(Of Version).AtLeast(New Version(17, 0)))
End If End If
If Instance.Version.Fabric <> "未知版本" AndAlso If Instance.Version.ModLoaderVersions(ModLoaders.Fabric) <> "未知版本" AndAlso McUtils.CompareVersion(Instance.Version.ModLoaderVersions(ModLoaders.Fabric), "0.17.0") < 0 Then
CompareVersion(Instance.Version.Fabric, "0.17.0") < 0 Then
'Fabric Loader 0.16.x 及更早版本的 Mixin/ASM 不兼容 Java 25 'Fabric Loader 0.16.x 及更早版本的 Mixin/ASM 不兼容 Java 25
AddConstraint(ValueRange(Of Version).LessThan(New Version(25, 0))) AddConstraint(ValueRange(Of Version).LessThan(New Version(25, 0)))
End If End If
End If End If
'LiteLoader 检测 'LiteLoader 检测
If Instance.Version.HasLiteLoader AndAlso Instance.Version.Vaild Then '不管非标准版本 If Instance.Version.ModLoaders.HasFlagF(ModLoaders.LiteLoader) AndAlso Instance.Version.Valid Then '不管非标准版本
'最高 Java 8 '最高 Java 8
AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0))) AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0)))
End If End If
@@ -411,9 +410,9 @@ Public Module ModJava
Dim Components = DownloadListWorker.LastResult Dim Components = DownloadListWorker.LastResult
'确定要下载的目标 Java '确定要下载的目标 Java
Dim JavaToDownload As JavaDownloadInfo = If( Dim JavaToDownload As JavaDownloadInfo = Components.FirstOrDefault(
Components.FirstOrDefault(Function(Comp) Comp.Java("version")("name").ToString = RecommendedComponent), '优先选择指定的 Component Function(Comp) Comp.Java("version")("name").ToString = RecommendedComponent, '优先选择指定的 Component
Components.FirstOrDefault(Function(Comp) Range.Contains(Comp.JavaVersion))) Function(Comp) Range.Contains(Comp.JavaVersion))
If JavaToDownload Is Nothing Then If JavaToDownload Is Nothing Then
Logger.Warn($"Mojang 提供的 Java 列表中没有合适的 Java,无法自动下载") Logger.Warn($"Mojang 提供的 Java 列表中没有合适的 Java,无法自动下载")
'提示需要手动下载 Java '提示需要手动下载 Java
@@ -48,7 +48,7 @@ Public Module ModLaunch
End Try End Try
'检查版本 '检查版本
CurrentLaunchOptions.Instance.Load() CurrentLaunchOptions.Instance.Load()
If CurrentLaunchOptions.Instance.State = McInstanceState.Error Then If CurrentLaunchOptions.Instance.State = McInstance.McInstanceState.Error Then
Hint($"无法启动 {CurrentLaunchOptions.Instance.Name}:{CurrentLaunchOptions.Instance.Info}", HintType.Red) Hint($"无法启动 {CurrentLaunchOptions.Instance.Name}:{CurrentLaunchOptions.Instance.Info}", HintType.Red)
Return False Return False
End If End If
@@ -57,6 +57,10 @@ Public Module ModLaunch
FrmLaunchLeft.RefreshButtonsUI() FrmLaunchLeft.RefreshButtonsUI()
FrmLaunchLeft.RefreshPage(False, False) FrmLaunchLeft.RefreshPage(False, False)
End If End If
If McInstanceSelected IsNot Nothing AndAlso McInstanceLock.IsLocked(McInstanceSelected) Then
Hint($"版本 {McInstanceSelected.Name} 正在被修改,请稍后再启动!", HintType.Red)
Return False
End If
FrmMain.AprilGiveup() FrmMain.AprilGiveup()
'禁止进入版本选择页面(否则就可以在启动中切换 McInstanceSelected 了) '禁止进入版本选择页面(否则就可以在启动中切换 McInstanceSelected 了)
FrmMain.PageStack = FrmMain.PageStack.Where(Function(p) p.Page <> FormMain.PageType.InstanceSelect).ToList FrmMain.PageStack = FrmMain.PageStack.Where(Function(p) p.Page <> FormMain.PageType.InstanceSelect).ToList
@@ -70,7 +74,12 @@ Public Module ModLaunch
''' </summary> ''' </summary>
Public Sub McLaunchLog(Text As String, Optional Level As LogLevel = LogLevel.Info) Public Sub McLaunchLog(Text As String, Optional Level As LogLevel = LogLevel.Info)
Text = FilterUserName(FilterAccessToken(Text, "*"), "*") Text = FilterUserName(FilterAccessToken(Text, "*"), "*")
If ModeDebug Then RunInUi(Sub() FrmLaunchRight.LabLog.Text += $"{vbCrLf}[{Date.Now:HH':'mm':'ss'.'fff}] {Text}") If ModeDebug Then
RunInUi(
Sub()
FrmLaunchRight.LabLog.AppendText($"[{Date.Now:HH':'mm':'ss'.'fff}] {Text}{vbCrLf}")
End Sub)
End If
Logger.Log(Text, Level) Logger.Log(Text, Level)
End Sub End Sub
@@ -196,25 +205,6 @@ NextInner:
End Try End Try
End Sub End Sub
''' <summary>
''' 取消正在进行的游戏启动。
''' </summary>
Public Sub McLaunchCancel()
If McLaunchLoaderReal IsNot Nothing Then
McLaunchLoaderReal.Cancel()
McLaunchLog("已取消启动")
Try
If McLaunchWatcher IsNot Nothing Then
McLaunchWatcher.Kill()
ElseIf McLaunchProcess IsNot Nothing Then
If Not McLaunchProcess.HasExited Then McLaunchProcess.Kill()
End If
Catch ex As Exception
Logger.Error(ex, "取消启动结束进程失败", LogBehavior.Toast)
End Try
End If
End Sub
#End Region #End Region
#Region "内存优化" #Region "内存优化"
@@ -249,7 +239,7 @@ NextInner:
'检查版本 '检查版本
If McInstanceSelected Is Nothing Then Throw New Exception("未选择 Minecraft 版本!") If McInstanceSelected Is Nothing Then Throw New Exception("未选择 Minecraft 版本!")
McInstanceSelected.Load() McInstanceSelected.Load()
If McInstanceSelected.State = McInstanceState.Error Then Throw New Exception("Minecraft 存在问题:" & McInstanceSelected.Info) If McInstanceSelected.State = McInstance.McInstanceState.Error Then Throw New Exception("Minecraft 存在问题:" & McInstanceSelected.Info)
'检查输入信息 '检查输入信息
Dim CheckResult As String = "" Dim CheckResult As String = ""
RunInUiWait(Sub() CheckResult = McLoginAble(McLoginInput())) RunInUiWait(Sub() CheckResult = McLoginAble(McLoginInput()))
@@ -557,7 +547,7 @@ NextInner:
'检查是否已经登录完成 '检查是否已经登录完成
Dim ExpiresAt = Settings.Get(Of Long)("CacheMsV2Expires") Dim ExpiresAt = Settings.Get(Of Long)("CacheMsV2Expires")
If Not Data.IsForceRestarting AndAlso '不要求强行重启 If Not Data.IsForceRestarting AndAlso '不要求强行重启
ExpiresAt > 0 AndAlso ExpiresAt > GetUnixTimestampUtc() AndAlso 'AccessToken 尚未过期 ExpiresAt > 0 AndAlso ExpiresAt > ToUnixTimestampUtc(Date.UtcNow) AndAlso 'AccessToken 尚未过期
Input.UserName = Settings.Get(Of String)("CacheMsV2Name") Then Input.UserName = Settings.Get(Of String)("CacheMsV2Name") Then
Data.Output = New McLoginResult With { Data.Output = New McLoginResult With {
.Name = Input.UserName, .Type = "Microsoft", .Name = Input.UserName, .Type = "Microsoft",
@@ -1052,7 +1042,7 @@ Retry:
Dim ResultJson As JObject = Result.DeserializeJson() Dim ResultJson As JObject = Result.DeserializeJson()
Return ( Return (
ResultJson("access_token").ToString, ResultJson("access_token").ToString,
ResultJson("expires_in").ToObject(Of Integer) + GetUnixTimestampUtc() - 1200) '提前 20 分钟视作过期 ResultJson("expires_in").ToObject(Of Integer) + GetUnixTimestampUtc() - 3600) '提前 1 小时视作过期
End Function End Function
'微软登录步骤 5:验证微软账号是否持有 MC,这也会刷新 XGP '微软登录步骤 5:验证微软账号是否持有 MC,这也会刷新 XGP
Private Sub MsLoginStep5(AccessToken As String) Private Sub MsLoginStep5(AccessToken As String)
@@ -1382,16 +1372,11 @@ NextInstance:
'Log4j 漏洞防御参数 'Log4j 漏洞防御参数
Args.Add("-Dlog4j2.formatMsgNoLookups=true") Args.Add("-Dlog4j2.formatMsgNoLookups=true")
'编码(#4700、#5892、#5909、#6934、#8950) '编码(#4700、#5892、#5909、#6934、#8950、#9241)
Dim EncodingName As String Dim UseCompatEncoding = McLaunchJavaSelected.Version.Major >= 18
Select Case McLaunchJavaSelected.Version.Major Dim EncodingName = If(UseCompatEncoding,
Case Is >= 21 'Java 21+ 会固定使用 UTF-8 McLaunchJavaSelected.NativeEncodingName, 'Java 18+ 会添加 COMPAT,因此 Java 会回退到 native.encoding
EncodingName = "UTF-8" McLaunchJavaSelected.FileEncodingName) 'Java 17- 直接使用 file.encoding
Case Is >= 18 'Java 18~20 时,启动 MC 时会添加 COMPAT 参数,因此 Java 会回退到 native.encoding
EncodingName = McLaunchJavaSelected.NativeEncodingName
Case Else 'Java 17- 时,直接使用 file.encoding
EncodingName = McLaunchJavaSelected.FileEncodingName
End Select
Try Try
McLaunchEncoding = Encoding.GetEncoding(If(EncodingName.Equals("GBK", StringComparison.OrdinalIgnoreCase), "GB18030", EncodingName)) McLaunchEncoding = Encoding.GetEncoding(If(EncodingName.Equals("GBK", StringComparison.OrdinalIgnoreCase), "GB18030", EncodingName))
Catch ex As Exception Catch ex As Exception
@@ -1405,7 +1390,7 @@ NextInstance:
Dim StderrArgName = If(McLaunchJavaSelected.Version.Major < 19, "sun.stderr.encoding", "stderr.encoding") Dim StderrArgName = If(McLaunchJavaSelected.Version.Major < 19, "sun.stderr.encoding", "stderr.encoding")
If Not Args.Any(Function(a) a.StartsWithF($"-D{StdoutArgName}=")) Then Args.Add($"-D{StdoutArgName}={LaunchEncodingName}") If Not Args.Any(Function(a) a.StartsWithF($"-D{StdoutArgName}=")) Then Args.Add($"-D{StdoutArgName}={LaunchEncodingName}")
If Not Args.Any(Function(a) a.StartsWithF($"-D{StderrArgName}=")) Then Args.Add($"-D{StderrArgName}={LaunchEncodingName}") If Not Args.Any(Function(a) a.StartsWithF($"-D{StderrArgName}=")) Then Args.Add($"-D{StderrArgName}={LaunchEncodingName}")
If McLaunchJavaSelected.Version.Major >= 18 AndAlso McLaunchJavaSelected.Version.Major < 21 Then Args.Add("-Dfile.encoding=COMPAT") '使用 COMPAT 防止部分 Mod 配置文件不兼容 UTF-8 If UseCompatEncoding AndAlso Not Args.Any(Function(a) a.StartsWithF("-Dfile.encoding=")) Then Args.Add("-Dfile.encoding=COMPAT") '防止老版本 MC 与部分 Mod 不兼容 UTF-8
'为 JLW 添加 -jar,它必须放在最后 '为 JLW 添加 -jar,它必须放在最后
If UseJLW Then If UseJLW Then
@@ -1465,7 +1450,8 @@ NextInstance:
Arg &= " " & CustomArg Arg &= " " & CustomArg
'把 OptiFineForgeTweaker 放在参数的末尾 '把 OptiFineForgeTweaker 放在参数的末尾
If (McInstanceSelected.Version.HasForge OrElse McInstanceSelected.Version.HasLiteLoader) AndAlso McInstanceSelected.Version.HasOptiFine Then If (McInstanceSelected.Version.ModLoaders.HasFlagF(ModLoaders.Forge) OrElse McInstanceSelected.Version.ModLoaders.HasFlagF(ModLoaders.LiteLoader)) AndAlso
McInstanceSelected.Version.HasOptiFine AndAlso Not McInstanceSelected.Version.OptiFineAsMod Then
If Arg.Contains("--tweakClass optifine.OptiFineForgeTweaker") Then If Arg.Contains("--tweakClass optifine.OptiFineForgeTweaker") Then
McLaunchLog("发现正确的 OptiFineForge TweakClass,目前的游戏参数:" & Arg) McLaunchLog("发现正确的 OptiFineForge TweakClass,目前的游戏参数:" & Arg)
Arg = Arg.Replace(" --tweakClass optifine.OptiFineForgeTweaker", "") & " --tweakClass optifine.OptiFineForgeTweaker" Arg = Arg.Replace(" --tweakClass optifine.OptiFineForgeTweaker", "") & " --tweakClass optifine.OptiFineForgeTweaker"
@@ -1520,10 +1506,10 @@ NextInstance:
Private Iterator Function McLaunchArgumentsReplace(Arguments As List(Of String)) As IEnumerable(Of (Key As String, Value As String)) Private Iterator Function McLaunchArgumentsReplace(Arguments As List(Of String)) As IEnumerable(Of (Key As String, Value As String))
'基础参数 '基础参数
Yield ("${classpath_separator}", ";") Yield ("${classpath_separator}", ";")
Yield ("${natives_directory}", GetNativesFolder().TrimEnd("\"c)) Yield ("${natives_directory}", PathUtils.RemoveSlashSuffix(GetNativesFolder()))
Yield ("${library_directory}", PathUtils.ToShortPath(McFolderSelected & "libraries")) Yield ("${library_directory}", PathUtils.ToShortPath(McFolderSelected & "libraries"))
Yield ("${libraries_directory}", PathUtils.ToShortPath(McFolderSelected & "libraries")) Yield ("${libraries_directory}", PathUtils.ToShortPath(McFolderSelected & "libraries"))
Yield ("${pure_directory}", PathPure.Value.TrimEnd("\"c)) '由 PCL 添加,这会允许在分割参数并去重后再替换路径,防止路径中的特殊字符影响参数分割和去重 Yield ("${pure_directory}", PathUtils.RemoveSlashSuffix(PathPure.Value)) '由 PCL 添加,这会允许在分割参数并去重后再替换路径,防止路径中的特殊字符影响参数分割和去重
Yield ("${launcher_name}", "PCL") Yield ("${launcher_name}", "PCL")
Yield ("${launcher_version}", VersionCode) Yield ("${launcher_version}", VersionCode)
Yield ("${version_name}", McInstanceSelected.Name) Yield ("${version_name}", McInstanceSelected.Name)
@@ -1568,9 +1554,8 @@ NextInstance:
Case Else Case Else
TargetSize = New Size(854, 480) TargetSize = New Size(854, 480)
End Select End Select
If McInstanceSelected.Version.Vanilla.Major <= 12 AndAlso If McInstanceSelected.Version.Vanilla.Major <= 12 AndAlso McLaunchJavaSelected.Version >= New Version(8, 0, 200) AndAlso McLaunchJavaSelected.Version <= New Version(8, 0, 321) AndAlso
McLaunchJavaSelected.Version >= New Version(8, 0, 200) AndAlso McLaunchJavaSelected.Version <= New Version(8, 0, 321) AndAlso Not McInstanceSelected.Version.HasOptiFine AndAlso Not McInstanceSelected.Version.ModLoaders.HasFlagF(ModLoaders.Forge) Then '修复 1.12.2- 下 JRE 8u200~321 下窗口大小为设置大小的 DPI% 倍(#3463)
Not McInstanceSelected.Version.HasOptiFine AndAlso Not McInstanceSelected.Version.HasForge Then '修复 1.12.2- 下 JRE 8u200~321 下窗口大小为设置大小的 DPI% 倍(#3463)
McLaunchLog($"已应用窗口大小过大修复(Java 版本:{McLaunchJavaSelected.Version})") McLaunchLog($"已应用窗口大小过大修复(Java 版本:{McLaunchJavaSelected.Version})")
TargetSize.Width /= DPI / 96 TargetSize.Width /= DPI / 96
TargetSize.Height /= DPI / 96 TargetSize.Height /= DPI / 96
@@ -1694,7 +1679,7 @@ NextInstance:
End Try End Try
For Each Entry In Zip.Entries For Each Entry In Zip.Entries
Dim FileName As String = Entry.FullName Dim FileName As String = Entry.FullName
If FileName.EndsWithF(".dll", True) Then If PathUtils.GetExtension(FileName) = "dll" Then
'实际解压文件的步骤 '实际解压文件的步骤
Dim FilePath As String = Target & FileName Dim FilePath As String = Target & FileName
ExistFiles.Add(FilePath) ExistFiles.Add(FilePath)
@@ -1738,8 +1723,9 @@ NextInstance:
''' <summary> ''' <summary>
''' 获取 Natives 文件夹路径,以 \ 结尾。 ''' 获取 Natives 文件夹路径,以 \ 结尾。
''' </summary> ''' </summary>
Private Function GetNativesFolder() As String Public Function GetNativesFolder(Optional Instance As McInstance = Nothing) As String
Dim Result As String = PathUtils.AddSlashSuffix(PathUtils.ToShortPath(McInstanceSelected.PathVersion)) & McInstanceSelected.Name & "-natives\" If Instance Is Nothing Then Instance = McInstanceSelected
Dim Result As String = PathUtils.AddSlashSuffix(PathUtils.ToShortPath(Instance.PathVersion)) & Instance.Name & "-natives\"
If Not (IsGBKEncoding OrElse Result.IsAsciiOnly()) Then If Not (IsGBKEncoding OrElse Result.IsAsciiOnly()) Then
Result = Paths.AppData & ".minecraft\bin\natives\" Result = Paths.AppData & ".minecraft\bin\natives\"
If Not Result.IsAsciiOnly() Then If Not Result.IsAsciiOnly() Then
@@ -1865,7 +1851,7 @@ NextInstance:
Dim CurrentLang As String = ReadIni(SetupFileAddress, "lang", "none") Dim CurrentLang As String = ReadIni(SetupFileAddress, "lang", "none")
Dim RequiredLang As String = If(CurrentLang = "none" OrElse Not DirectoryUtils.Exists(McInstanceSelected.PathIndie & "saves"), '#3844,整合包可能已经自带了 options.txt Dim RequiredLang As String = If(CurrentLang = "none" OrElse Not DirectoryUtils.Exists(McInstanceSelected.PathIndie & "saves"), '#3844,整合包可能已经自带了 options.txt
If(Settings.Get(Of Boolean)("ToolHelpChinese"), "zh_cn", "en_us"), CurrentLang.Lower) If(Settings.Get(Of Boolean)("ToolHelpChinese"), "zh_cn", "en_us"), CurrentLang.Lower)
If Not McInstanceSelected.Version.Vaild OrElse McInstanceSelected.Version.Vanilla.Major <= 10 Then If Not McInstanceSelected.Version.Valid OrElse McInstanceSelected.Version.Vanilla.Major <= 10 Then
'将最后两位改为大写,前面的部分保留 '将最后两位改为大写,前面的部分保留
RequiredLang = RequiredLang.Substring(0, RequiredLang.Length - 2) & RequiredLang.Substring(RequiredLang.Length - 2).Upper RequiredLang = RequiredLang.Substring(0, RequiredLang.Length - 2) & RequiredLang.Substring(RequiredLang.Length - 2).Upper
End If End If
@@ -1908,53 +1894,40 @@ NextInstance:
If McLoginLoader.Input.Type = McLoginType.Legacy AndAlso Settings.Get(Of Integer)("LaunchSkinType") = 4 AndAlso FileUtils.Exists(Paths.AppDataThenName & "CustomSkin.png") Then If McLoginLoader.Input.Type = McLoginType.Legacy AndAlso Settings.Get(Of Integer)("LaunchSkinType") = 4 AndAlso FileUtils.Exists(Paths.AppDataThenName & "CustomSkin.png") Then
Dim MetaFileAddress As String = PathTemp & "pack.mcmeta" Dim MetaFileAddress As String = PathTemp & "pack.mcmeta"
Dim PackPicAddress As String = PathTemp & "pack.png" Dim PackPicAddress As String = PathTemp & "pack.png"
Dim PackFormat As Integer If McInstanceSelected.Version.Vanilla.Major < 6 Then
Select Case McInstanceSelected.Version.Vanilla.Major
Case 0, 1, 2, 3, 4, 5
'更早的版本没有资源包
McLaunchLog("Minecraft 版本过老,尚不支持自定义离线皮肤") McLaunchLog("Minecraft 版本过老,尚不支持自定义离线皮肤")
GoTo IgnoreCustomSkin GoTo IgnoreCustomSkin
End If
Dim PackVersionJToken As JToken = McInstanceSelected.JsonVersion?("pack_version")
If TypeOf PackVersionJToken Is JObject Then PackVersionJToken = DirectCast(PackVersionJToken, JObject)("resource") '从 1.17 开始,可以从 pack_version.resource 获取资源包版本
Dim PackVersion As Integer = Val(PackVersionJToken?.ToString)
If PackVersion <= 0 Then '没有对应字段的老版本 MC,或兜底
Select Case McInstanceSelected.Version.Vanilla.Major
Case 6, 7, 8 Case 6, 7, 8
PackFormat = 1 PackVersion = 1
Case 9, 10 Case 9, 10
PackFormat = 2 PackVersion = 2
Case 11, 12 Case 11, 12
PackFormat = 3 PackVersion = 3
Case 13, 14 Case 13, 14
PackFormat = 4 PackVersion = 4
Case 15 Case 15
PackFormat = 5 PackVersion = 5
Case 16 Case 16
PackFormat = 6 PackVersion = If(McInstanceSelected.Version.Vanilla.Build <= 1, 5, 6)
Case 17 Case 17
PackFormat = 7 PackVersion = 7
Case 18 Case 18
If McInstanceSelected.Version.Vanilla.Minor <= 2 Then PackVersion = 8
PackFormat = 8 Case Else '旧版快照可能没有可用的资源包格式声明
Else PackVersion = 17
PackFormat = 9
End If
Case 19
If McInstanceSelected.Version.Vanilla.Minor <= 3 Then
PackFormat = 9
Else
PackFormat = 12
End If
Case 20
If McInstanceSelected.Version.Vanilla.Minor <= 1 Then
PackFormat = 15
Else
PackFormat = 17
End If
Case Else '快照版是 9999
PackFormat = 17
'https://zh.minecraft.wiki/w/数据包#数据包版本
End Select End Select
McLaunchLog("正在构建自定义皮肤资源包,格式为:" & PackFormat) End If
McLaunchLog($"正在构建自定义皮肤资源包,版本号 {PackVersion},MC 版本 {McInstanceSelected.Version.Vanilla}")
'准备文件 '准备文件
Dim Bit As New MyBitmap(PathImage & "Heads/Logo.png") Dim Bit As New MyBitmap(PathImage & "Heads/Logo.png")
Bit.Save(PackPicAddress) Bit.Save(PackPicAddress)
FileUtils.Write(MetaFileAddress, "{""pack"":{""pack_format"":" & PackFormat & ",""description"":""PCL 自定义离线皮肤资源包""}}") FileUtils.Write(MetaFileAddress, "{""pack"":{""pack_format"":" & PackVersion & ",""description"":""PCL 自定义离线皮肤资源包""}}")
Dim Skin As New MyBitmap(Paths.AppDataThenName & "CustomSkin.png") Dim Skin As New MyBitmap(Paths.AppDataThenName & "CustomSkin.png")
If (McInstanceSelected.Version.Vanilla.Major = 6 OrElse McInstanceSelected.Version.Vanilla.Major = 7) AndAlso Skin.Pic.Height = 64 Then If (McInstanceSelected.Version.Vanilla.Major = 6 OrElse McInstanceSelected.Version.Vanilla.Major = 7) AndAlso Skin.Pic.Height = 64 Then
McLaunchLog("该 Minecraft 版本不支持双层皮肤,已进行裁剪") McLaunchLog("该 Minecraft 版本不支持双层皮肤,已进行裁剪")
File diff suppressed because it is too large Load Diff
@@ -8,6 +8,10 @@ Public Module ModModpack
''' 弹窗要求选择一个整合包文件并进行安装。 ''' 弹窗要求选择一个整合包文件并进行安装。
''' </summary> ''' </summary>
Public Sub ModpackInstall() Public Sub ModpackInstall()
If RestrictMcDownload Then
Hint("为遵守相关法律法规,PCL 暂时不再提供 MC 下载功能,非常抱歉 qwq……", HintType.Red)
Return
End If
Dim File As String = Dialogs.SelectFile("选择整合包压缩文件", False, filter:={({"rar", "zip", "mrpack"}, "整合包文件")}).FirstOrDefault() '选择整合包文件 Dim File As String = Dialogs.SelectFile("选择整合包压缩文件", False, filter:={({"rar", "zip", "mrpack"}, "整合包文件")}).FirstOrDefault() '选择整合包文件
If String.IsNullOrEmpty(File) Then Return If String.IsNullOrEmpty(File) Then Return
RunInThread( RunInThread(
@@ -74,13 +78,14 @@ Public Module ModModpack
Catch ex As Exception Catch ex As Exception
If ex.GetDisplay(True).Contains("Error.WinIOError") Then If ex.GetDisplay(True).Contains("Error.WinIOError") Then
Throw New Exception("打开整合包文件失败", ex) Throw New Exception("打开整合包文件失败", ex)
ElseIf File.EndsWithF(".rar", True) Then ElseIf PathUtils.GetExtension(File) = "rar" Then
Throw New Exception("PCL 无法处理 rar 格式的压缩包,请在解压后重新压缩为 zip 格式再试", ex) Throw New Exception("PCL 无法处理 rar 格式的压缩包,请在解压后重新压缩为 zip 格式再试", ex)
Else Else
Throw New Exception("打开整合包文件失败,文件可能损坏或为不支持的压缩包格式", ex) Throw New Exception("打开整合包文件失败,文件可能损坏或为不支持的压缩包格式", ex)
End If End If
End Try End Try
'执行对应的安装方法 '执行对应的安装方法
If RestrictMcDownload Then Throw New Exception("为遵守相关法律法规,PCL 暂时不再提供 MC 下载功能,非常抱歉 qwq……")
Select Case PackType Select Case PackType
Case 0 Case 0
Logger.Info("整合包种类:CurseForge") Logger.Info("整合包种类:CurseForge")
@@ -119,8 +124,8 @@ Public Module ModModpack
''' 对路径末尾是否为 \ 没有要求。 ''' 对路径末尾是否为 \ 没有要求。
''' </summary> ''' </summary>
Private Sub CopyOverrideDirectory(OverridesFolder As String, VersionFolder As String, Loader As LoaderBase) Private Sub CopyOverrideDirectory(OverridesFolder As String, VersionFolder As String, Loader As LoaderBase)
If Not OverridesFolder.EndsWithF("\") Then OverridesFolder += "\" OverridesFolder = PathUtils.AddSlashSuffix(OverridesFolder)
If Not VersionFolder.EndsWithF("\") Then VersionFolder += "\" VersionFolder = PathUtils.AddSlashSuffix(VersionFolder)
'复制文件 '复制文件
If DirectoryUtils.Exists(OverridesFolder) Then If DirectoryUtils.Exists(OverridesFolder) Then
Logger.Info($"处理整合包覆写文件夹:{OverridesFolder} → {VersionFolder}") Logger.Info($"处理整合包覆写文件夹:{OverridesFolder} → {VersionFolder}")
@@ -174,25 +179,25 @@ Public Module ModModpack
If InstanceName = "" Then InstanceName = MyMsgBoxInput("输入版本名称", "", "", New Collection(Of Validate) From {Validate}) If InstanceName = "" Then InstanceName = MyMsgBoxInput("输入版本名称", "", "", New Collection(Of Validate) From {Validate})
If String.IsNullOrEmpty(InstanceName) Then Throw New OperationCanceledException If String.IsNullOrEmpty(InstanceName) Then Throw New OperationCanceledException
End If End If
Dim VersionFolder As String = PathUtils.AddSlashSuffix(Path.Combine(McFolderSelected, "versions", InstanceName) & "\")
'获取 Mod API 版本信息 '获取 Mod API 版本信息
Dim TargetVersion As New McVersion Dim TargetVersion As New McVersion(Json("minecraft")("version").ToString)
TargetVersion.VanillaName = Json("minecraft")("version").ToString
For Each Entry In If(Json("minecraft")("modLoaders"), {}) For Each Entry In If(Json("minecraft")("modLoaders"), {})
Dim Id As String = If(Entry("id"), "").ToString.Lower Dim Id As String = If(Entry("id"), "").ToString.Lower
If Id.StartsWithF("forge-") Then If Id.StartsWithF("forge-") Then
'Forge 指定 'Forge 指定
If Id.Contains("recommended") Then Throw New Exception("该整合包版本过老,已不支持进行安装!") If Id.Contains("recommended") Then Throw New Exception("该整合包版本过老,已不支持进行安装!")
Logger.Info($"整合包 Forge 版本:{Id}") Logger.Info($"整合包 Forge 版本:{Id}")
TargetVersion.Forge = Id.Replace("forge-", "") TargetVersion.ModLoaderVersions(ModLoaders.Forge) = Id.Replace("forge-", "")
ElseIf Id.StartsWithF("neoforge-") Then ElseIf Id.StartsWithF("neoforge-") Then
'NeoForge 指定 'NeoForge 指定
Logger.Info($"整合包 NeoForge 版本:{Id}") Logger.Info($"整合包 NeoForge 版本:{Id}")
TargetVersion.NeoForge = Id.Replace("neoforge-", "") TargetVersion.ModLoaderVersions(ModLoaders.NeoForge) = Id.Replace("neoforge-", "")
ElseIf Id.StartsWithF("fabric-") Then ElseIf Id.StartsWithF("fabric-") Then
'Fabric 指定 'Fabric 指定
Logger.Info($"整合包 Fabric 版本:{Id}") Logger.Info($"整合包 Fabric 版本:{Id}")
TargetVersion.Fabric = Id.Replace("fabric-", "") TargetVersion.ModLoaderVersions(ModLoaders.Fabric) = Id.Replace("fabric-", "")
Else Else
NotifyIncompatibleLoader(Id) NotifyIncompatibleLoader(Id)
End If End If
@@ -202,12 +207,12 @@ Public Module ModModpack
Dim InstallLoaders As New List(Of LoaderBase) Dim InstallLoaders As New List(Of LoaderBase)
Dim OverrideHome As String = If(Json("overrides"), "") Dim OverrideHome As String = If(Json("overrides"), "")
If OverrideHome <> "" Then If OverrideHome <> "" Then
Dim OverridesFolder As String = InstallTemp & ArchiveBaseFolder & If(OverrideHome = "." OrElse OverrideHome = "./", "", OverrideHome) '#5613
If Not PathUtils.IsParentOf(InstallTemp, OverridesFolder) Then Throw New Exception("整合包的 overrides 路径超出了解压目录,请向整合包作者反馈此问题!")
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件", InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
Sub(Task As LoaderTask(Of String, Integer)) Sub(Task As LoaderTask(Of String, Integer))
ExtractModpackFiles(InstallTemp, FileAddress, Task.CreateCancellationToken(), Task.CreateSyncProgressProvider(ToPercentage:=0.6)) ExtractModpackFiles(InstallTemp, FileAddress, Task.CreateCancellationToken(), Task.CreateSyncProgressProvider(ToPercentage:=0.6))
CopyOverrideDirectory( CopyOverrideDirectory(OverridesFolder, VersionFolder, Task)
InstallTemp & ArchiveBaseFolder & If(OverrideHome = "." OrElse OverrideHome = "./", "", OverrideHome), '#5613
$"{McFolderSelected}versions\{InstanceName}", Task)
End Sub) With { End Sub) With {
.ProgressWeight = FileUtils.GetInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s .ProgressWeight = FileUtils.GetInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s
End If End If
@@ -293,7 +298,7 @@ Public Module ModModpack
Dim File = ResourceVersion.FromPlatformJson(ModJson, Type) Dim File = ResourceVersion.FromPlatformJson(ModJson, Type)
If Not File.DownloadAvailable Then Continue For If Not File.DownloadAvailable Then Continue For
'实际的添加 '实际的添加
FileList.Add(Id, File.ToNetFile($"{McFolderSelected}versions\{InstanceName}\{TargetFolder}\", FileList.Add(Id, File.ToNetFile(PathUtils.AddSlashSuffix(Path.Combine(VersionFolder, TargetFolder)),
ResourceVersion.DownloadReason.ModPack, TargetVersion.VanillaName, TargetVersion.ModLoaders)) ResourceVersion.DownloadReason.ModPack, TargetVersion.VanillaName, TargetVersion.ModLoaders))
Task.Progress += 1 / (1 + ModList.Count) Task.Progress += 1 / (1 + ModList.Count)
Next Next
@@ -307,15 +312,9 @@ Public Module ModModpack
End If End If
'构造加载器 '构造加载器
Dim Request As New McInstallRequest With { Dim Target As New McComponents(TargetVersion.VanillaName, TargetVersion.ModLoaderVersions)
.NewInstanceName = InstanceName, Dim MergeLoaders = McInstallLoadersGet(New McInstallRequest With {
.VersionFolder = $"{McFolderSelected}versions\{InstanceName}\", .NewInstanceName = InstanceName, .InstanceFolder = VersionFolder, .Components = Target})
.MinecraftName = TargetVersion.VanillaName,
.ForgeVersion = TargetVersion.Forge,
.NeoForgeVersion = TargetVersion.NeoForge,
.FabricVersion = TargetVersion.Fabric
}
Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request)
'构造总加载器 '构造总加载器
Dim Loaders As New List(Of LoaderBase) Dim Loaders As New List(Of LoaderBase)
Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)}) Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)})
@@ -323,7 +322,6 @@ Public Module ModModpack
Loaders.Add(New LoaderTask(Of String, String)("最终整理文件", Loaders.Add(New LoaderTask(Of String, String)("最终整理文件",
Sub(Task As LoaderTask(Of String, String)) Sub(Task As LoaderTask(Of String, String))
'设置图标 '设置图标
Dim VersionFolder As String = $"{McFolderSelected}versions\{InstanceName}\"
If Logo IsNot Nothing AndAlso FileUtils.Exists(Logo) Then If Logo IsNot Nothing AndAlso FileUtils.Exists(Logo) Then
FileUtils.Copy(Logo, VersionFolder & "PCL\Logo.png") FileUtils.Copy(Logo, VersionFolder & "PCL\Logo.png")
WriteIni(VersionFolder & "PCL\Setup.ini", "Logo", "PCL\Logo.png") WriteIni(VersionFolder & "PCL\Setup.ini", "Logo", "PCL\Logo.png")
@@ -337,10 +335,10 @@ Public Module ModModpack
FileUtils.Delete(MapFile) FileUtils.Delete(MapFile)
Next Next
'删除原始整合包文件 '删除原始整合包文件
For Each Target As String In {VersionFolder & "原始整合包.zip", VersionFolder & "原始整合包.mrpack"} For Each FileToDelete As String In {VersionFolder & "原始整合包.zip", VersionFolder & "原始整合包.mrpack"}
If FileUtils.Exists(Target) Then If FileUtils.Exists(FileToDelete) Then
Logger.Info($"删除原始整合包文件:{Target}") Logger.Info($"删除原始整合包文件:{FileToDelete}")
FileUtils.Delete(Target) FileUtils.Delete(FileToDelete)
End If End If
Next Next
If FileUtils.Exists(FileAddress) AndAlso PathUtils.GetFileNameWithoutExtension(FileAddress) = "modpack" Then If FileUtils.Exists(FileAddress) AndAlso PathUtils.GetFileNameWithoutExtension(FileAddress) = "modpack" Then
@@ -358,7 +356,7 @@ Public Module ModModpack
'启动 '启动
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState} Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
Loader.Start(Request.VersionFolder) Loader.Start(VersionFolder)
LoaderTaskbarAdd(Loader) LoaderTaskbarAdd(Loader)
FrmMain.BtnExtraDownload.ShowRefresh() FrmMain.BtnExtraDownload.ShowRefresh()
Return Loader Return Loader
@@ -376,20 +374,21 @@ Public Module ModModpack
End Try End Try
If Json("dependencies") Is Nothing OrElse Json("dependencies")("minecraft") Is Nothing Then Throw New Exception("Modrinth 整合包未提供 Minecraft 版本信息") If Json("dependencies") Is Nothing OrElse Json("dependencies")("minecraft") Is Nothing Then Throw New Exception("Modrinth 整合包未提供 Minecraft 版本信息")
'获取 Mod API 版本信息 '获取 Mod API 版本信息
Dim TargetVersion As New McVersion 'TODO: 改为使用类似 InstallPackMMC 中,一个版本名一个字典的方式来存储它,不要用 Unknown
Dim TargetVersion As New McVersion("Unknown")
For Each Entry As JProperty In If(Json("dependencies"), {}) For Each Entry As JProperty In If(Json("dependencies"), {})
Select Case Entry.Name.Lower Select Case Entry.Name.Lower
Case "minecraft" Case "minecraft"
TargetVersion.VanillaName = Entry.Value.ToString TargetVersion.VanillaName = Entry.Value.ToString
Case "forge" 'eg. 14.23.5.2859 / 1.19-41.1.0 Case "forge" 'eg. 14.23.5.2859 / 1.19-41.1.0
TargetVersion.Forge = Entry.Value.ToString TargetVersion.ModLoaderVersions(ModLoaders.Forge) = Entry.Value.ToString
Logger.Info($"整合包 Forge 版本:{TargetVersion.Forge}") Logger.Info($"整合包 Forge 版本:{TargetVersion.ModLoaderVersions(ModLoaders.Forge)}")
Case "neoforge", "neo-forge" 'eg. 20.6.98-beta Case "neoforge", "neo-forge" 'eg. 20.6.98-beta
TargetVersion.NeoForge = Entry.Value.ToString TargetVersion.ModLoaderVersions(ModLoaders.NeoForge) = Entry.Value.ToString
Logger.Info($"整合包 NeoForge 版本:{TargetVersion.NeoForge}") Logger.Info($"整合包 NeoForge 版本:{TargetVersion.ModLoaderVersions(ModLoaders.NeoForge)}")
Case "fabric-loader" 'eg. 0.14.14 Case "fabric-loader" 'eg. 0.14.14
TargetVersion.Fabric = Entry.Value.ToString TargetVersion.ModLoaderVersions(ModLoaders.Fabric) = Entry.Value.ToString
Logger.Info($"整合包 Fabric 版本:{TargetVersion.Fabric}") Logger.Info($"整合包 Fabric 版本:{TargetVersion.ModLoaderVersions(ModLoaders.Fabric)}")
Case Else Case Else
NotifyIncompatibleLoader(Entry.Name) NotifyIncompatibleLoader(Entry.Name)
End Select End Select
@@ -402,18 +401,15 @@ Public Module ModModpack
If InstanceName = "" Then InstanceName = MyMsgBoxInput("输入版本名称", "", "", New Collection(Of Validate) From {Validate}) If InstanceName = "" Then InstanceName = MyMsgBoxInput("输入版本名称", "", "", New Collection(Of Validate) From {Validate})
If String.IsNullOrEmpty(InstanceName) Then Throw New OperationCanceledException If String.IsNullOrEmpty(InstanceName) Then Throw New OperationCanceledException
End If End If
Dim VersionFolder As String = Path.Combine(McFolderSelected, "versions", InstanceName) & "\"
'解压 '解压
Dim InstallTemp As String = RequestTaskTempFolder() Dim InstallTemp As String = RequestTaskTempFolder()
Dim InstallLoaders As New List(Of LoaderBase) Dim InstallLoaders As New List(Of LoaderBase)
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件", InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
Sub(Task As LoaderTask(Of String, Integer)) Sub(Task As LoaderTask(Of String, Integer))
ExtractModpackFiles(InstallTemp, FileAddress, Task.CreateCancellationToken(), Task.CreateSyncProgressProvider(ToPercentage:=0.5)) ExtractModpackFiles(InstallTemp, FileAddress, Task.CreateCancellationToken(), Task.CreateSyncProgressProvider(ToPercentage:=0.5))
CopyOverrideDirectory( CopyOverrideDirectory(InstallTemp & ArchiveBaseFolder & "overrides", VersionFolder, Task)
InstallTemp & ArchiveBaseFolder & "overrides", CopyOverrideDirectory(InstallTemp & ArchiveBaseFolder & "client-overrides", VersionFolder, Task)
McFolderSelected & "versions\" & InstanceName, Task)
CopyOverrideDirectory(
InstallTemp & ArchiveBaseFolder & "client-overrides",
McFolderSelected & "versions\" & InstanceName, Task)
End Sub) With {.ProgressWeight = FileUtils.GetInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s End Sub) With {.ProgressWeight = FileUtils.GetInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s
'获取下载文件列表 '获取下载文件列表
Dim FileList As New List(Of NetFile) Dim FileList As New List(Of NetFile)
@@ -436,8 +432,8 @@ Public Module ModModpack
Urls = ResourceVersion.ParseModrinthTrackArguments(Urls, Urls = ResourceVersion.ParseModrinthTrackArguments(Urls,
ResourceVersion.DownloadReason.ModPack, TargetVersion.VanillaName, TargetVersion.ModLoaders).ToList 'Modrinth 来源追踪信息 ResourceVersion.DownloadReason.ModPack, TargetVersion.VanillaName, TargetVersion.ModLoaders).ToList 'Modrinth 来源追踪信息
Urls = Urls.Distinct.ToList() Urls = Urls.Distinct.ToList()
Dim TargetPath As String = Path.Combine(McFolderSelected, "versions", InstanceName, File("path").ToString().TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)) Dim TargetPath As String = Path.Combine(VersionFolder, File("path").ToString().TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar))
If Not PathUtils.IsParentOf($"{McFolderSelected}versions\{InstanceName}\", TargetPath) Then If Not PathUtils.IsParentOf(VersionFolder, TargetPath) Then
MyMsgBox($"整合包的文件路径超出了版本文件夹,请向整合包作者反馈此问题!{vbCrLf}目标:{Path.GetFullPath(TargetPath)}{vbCrLf}错误的文件:{TargetPath}", "文件路径校验失败", IsWarn:=True) MyMsgBox($"整合包的文件路径超出了版本文件夹,请向整合包作者反馈此问题!{vbCrLf}目标:{Path.GetFullPath(TargetPath)}{vbCrLf}错误的文件:{TargetPath}", "文件路径校验失败", IsWarn:=True)
Throw New OperationCanceledException Throw New OperationCanceledException
End If End If
@@ -449,15 +445,10 @@ Public Module ModModpack
End If End If
'构造加载器 '构造加载器
Dim Request As New McInstallRequest With { Dim Target As New McComponents(TargetVersion.VanillaName, TargetVersion.ModLoaderVersions)
.NewInstanceName = InstanceName, Dim MergeLoaders = McInstallLoadersGet(New McInstallRequest With {
.VersionFolder = $"{McFolderSelected}versions\{InstanceName}\", .NewInstanceName = InstanceName, .InstanceFolder = VersionFolder,
.MinecraftName = TargetVersion.VanillaName, .Components = Target})
.ForgeVersion = TargetVersion.Forge,
.NeoForgeVersion = TargetVersion.NeoForge,
.FabricVersion = TargetVersion.Fabric
}
Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request)
'构造总加载器 '构造总加载器
Dim Loaders As New List(Of LoaderBase) Dim Loaders As New List(Of LoaderBase)
Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)}) Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)})
@@ -465,7 +456,6 @@ Public Module ModModpack
Loaders.Add(New LoaderTask(Of String, String)("最终整理文件", Loaders.Add(New LoaderTask(Of String, String)("最终整理文件",
Sub(Task As LoaderTask(Of String, String)) Sub(Task As LoaderTask(Of String, String))
'设置图标 '设置图标
Dim VersionFolder As String = $"{McFolderSelected}versions\{InstanceName}\"
If Logo IsNot Nothing AndAlso FileUtils.Exists(Logo) Then If Logo IsNot Nothing AndAlso FileUtils.Exists(Logo) Then
FileUtils.Copy(Logo, VersionFolder & "PCL\Logo.png") FileUtils.Copy(Logo, VersionFolder & "PCL\Logo.png")
WriteIni(VersionFolder & "PCL\Setup.ini", "Logo", "PCL\Logo.png") WriteIni(VersionFolder & "PCL\Setup.ini", "Logo", "PCL\Logo.png")
@@ -473,10 +463,10 @@ Public Module ModModpack
Logger.Info($"已设置整合包 Logo:{Logo}") Logger.Info($"已设置整合包 Logo:{Logo}")
End If End If
'删除原始整合包文件 '删除原始整合包文件
For Each Target As String In {VersionFolder & "原始整合包.zip", VersionFolder & "原始整合包.mrpack"} For Each FileToDelete As String In {VersionFolder & "原始整合包.zip", VersionFolder & "原始整合包.mrpack"}
If FileUtils.Exists(Target) Then If FileUtils.Exists(FileToDelete) Then
Logger.Info($"删除原始整合包文件:{Target}") Logger.Info($"删除原始整合包文件:{FileToDelete}")
FileUtils.Delete(Target) FileUtils.Delete(FileToDelete)
End If End If
Next Next
If FileUtils.Exists(FileAddress) AndAlso PathUtils.GetFileNameWithoutExtension(FileAddress) = "modpack" Then If FileUtils.Exists(FileAddress) AndAlso PathUtils.GetFileNameWithoutExtension(FileAddress) = "modpack" Then
@@ -494,7 +484,7 @@ Public Module ModModpack
'启动 '启动
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState} Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
Loader.Start(Request.VersionFolder) Loader.Start(VersionFolder)
LoaderTaskbarAdd(Loader) LoaderTaskbarAdd(Loader)
FrmMain.BtnExtraDownload.ShowRefresh() FrmMain.BtnExtraDownload.ShowRefresh()
Return Loader Return Loader
@@ -515,24 +505,21 @@ Public Module ModModpack
If Validate.Validate(InstanceName) <> "" Then InstanceName = "" If Validate.Validate(InstanceName) <> "" Then InstanceName = ""
If InstanceName = "" Then InstanceName = MyMsgBoxInput("输入版本名称", "", "", New Collection(Of Validate) From {Validate}) If InstanceName = "" Then InstanceName = MyMsgBoxInput("输入版本名称", "", "", New Collection(Of Validate) From {Validate})
If String.IsNullOrEmpty(InstanceName) Then Throw New OperationCanceledException If String.IsNullOrEmpty(InstanceName) Then Throw New OperationCanceledException
Dim VersionFolder As String = Path.Combine(McFolderSelected, "versions", InstanceName) & "\"
'解压 '解压
Dim InstallTemp As String = RequestTaskTempFolder() Dim InstallTemp As String = RequestTaskTempFolder()
Dim InstallLoaders As New List(Of LoaderBase) Dim InstallLoaders As New List(Of LoaderBase)
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件", InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
Sub(Task As LoaderTask(Of String, Integer)) Sub(Task As LoaderTask(Of String, Integer))
ExtractModpackFiles(InstallTemp, FileAddress, Task.CreateCancellationToken(), Task.CreateSyncProgressProvider(ToPercentage:=0.6)) ExtractModpackFiles(InstallTemp, FileAddress, Task.CreateCancellationToken(), Task.CreateSyncProgressProvider(ToPercentage:=0.6))
CopyOverrideDirectory( CopyOverrideDirectory(InstallTemp & ArchiveBaseFolder & "minecraft", VersionFolder, Task)
InstallTemp & ArchiveBaseFolder & "minecraft",
McFolderSelected & "versions\" & InstanceName, Task)
End Sub) With {.ProgressWeight = FileUtils.GetInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s End Sub) With {.ProgressWeight = FileUtils.GetInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s
'构造游戏本体安装加载器 '构造游戏本体安装加载器
If Json("gameVersion") Is Nothing Then Throw New Exception("该 HMCL 整合包未提供游戏版本信息,无法安装!") If Json("gameVersion") Is Nothing Then Throw New Exception("该 HMCL 整合包未提供游戏版本信息,无法安装!")
Dim Request As New McInstallRequest With { Dim Target As New McComponents(Json("gameVersion").ToString, New Dictionary(Of ModLoaders, String))
.NewInstanceName = InstanceName, Dim MergeLoaders = McInstallLoadersGet(New McInstallRequest With {
.VersionFolder = $"{McFolderSelected}versions\{InstanceName}\", .NewInstanceName = InstanceName, .InstanceFolder = VersionFolder,
.MinecraftName = Json("gameVersion").ToString .Components = Target})
}
Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request)
'构造总加载器 '构造总加载器
Dim Loaders As New List(Of LoaderBase) From { Dim Loaders As New List(Of LoaderBase) From {
New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)}, New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)},
@@ -546,7 +533,7 @@ Public Module ModModpack
End If End If
'启动 '启动
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState} Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
Loader.Start(Request.VersionFolder) Loader.Start(VersionFolder)
LoaderTaskbarAdd(Loader) LoaderTaskbarAdd(Loader)
FrmMain.BtnExtraDownload.ShowRefresh() FrmMain.BtnExtraDownload.ShowRefresh()
Return Loader Return Loader
@@ -568,16 +555,22 @@ Public Module ModModpack
If Validate.Validate(InstanceName) <> "" Then InstanceName = "" If Validate.Validate(InstanceName) <> "" Then InstanceName = ""
If InstanceName = "" Then InstanceName = MyMsgBoxInput("输入版本名称", "", "", New Collection(Of Validate) From {Validate}) If InstanceName = "" Then InstanceName = MyMsgBoxInput("输入版本名称", "", "", New Collection(Of Validate) From {Validate})
If String.IsNullOrEmpty(InstanceName) Then Throw New OperationCanceledException If String.IsNullOrEmpty(InstanceName) Then Throw New OperationCanceledException
Dim VersionFolder As String = Path.Combine(McFolderSelected, "versions", InstanceName) & "\"
'解压 '解压
Dim InstallTemp As String = RequestTaskTempFolder() Dim InstallTemp As String = RequestTaskTempFolder()
Dim SetupFile As String = $"{McFolderSelected}versions\{InstanceName}\PCL\Setup.ini" Dim SetupFile As String = VersionFolder & "PCL\Setup.ini"
Dim InstallLoaders As New List(Of LoaderBase) Dim InstallLoaders As New List(Of LoaderBase)
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件", InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
Sub(Task As LoaderTask(Of String, Integer)) Sub(Task As LoaderTask(Of String, Integer))
ExtractModpackFiles(InstallTemp, FileAddress, Task.CreateCancellationToken(), Task.CreateSyncProgressProvider(ToPercentage:=0.55)) ExtractModpackFiles(InstallTemp, FileAddress, Task.CreateCancellationToken(), Task.CreateSyncProgressProvider(ToPercentage:=0.55))
CopyOverrideDirectory( 'Prism 优先使用 minecraft/ 而非 .minecraft/(#9272)
InstallTemp & ArchiveBaseFolder & ".minecraft", Dim MmcGameRoot As String
McFolderSelected & "versions\" & InstanceName, Task) If DirectoryUtils.Exists(InstallTemp & ArchiveBaseFolder & ".minecraft") AndAlso Not DirectoryUtils.Exists(InstallTemp & ArchiveBaseFolder & "minecraft") Then
MmcGameRoot = InstallTemp & ArchiveBaseFolder & ".minecraft"
Else
MmcGameRoot = InstallTemp & ArchiveBaseFolder & "minecraft"
End If
CopyOverrideDirectory(MmcGameRoot, VersionFolder, Task)
'读取 MMC 设置文件(#2655) '读取 MMC 设置文件(#2655)
Try Try
Dim MmcSetupFile As String = InstallTemp & ArchiveBaseFolder & "instance.cfg" Dim MmcSetupFile As String = InstallTemp & ArchiveBaseFolder & "instance.cfg"
@@ -611,7 +604,7 @@ Public Module ModModpack
If Logo <> "" AndAlso FileUtils.Exists($"{InstallTemp}{ArchiveBaseFolder}{Logo}.png") Then If Logo <> "" AndAlso FileUtils.Exists($"{InstallTemp}{ArchiveBaseFolder}{Logo}.png") Then
WriteIni(SetupFile, "LogoCustom", True) WriteIni(SetupFile, "LogoCustom", True)
WriteIni(SetupFile, "Logo", "PCL\Logo.png") WriteIni(SetupFile, "Logo", "PCL\Logo.png")
FileUtils.Copy($"{InstallTemp}{ArchiveBaseFolder}{Logo}.png", $"{McFolderSelected}versions\{InstanceName}\PCL\Logo.png") FileUtils.Copy($"{InstallTemp}{ArchiveBaseFolder}{Logo}.png", VersionFolder & "PCL\Logo.png")
Logger.Info($"迁移 MultiMC 版本独立设置:版本图标({Logo}.png)") Logger.Info($"迁移 MultiMC 版本独立设置:版本图标({Logo}.png)")
End If End If
'JVM 参数 'JVM 参数
@@ -633,19 +626,20 @@ Public Module ModModpack
End Sub) With {.ProgressWeight = FileUtils.GetInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s End Sub) With {.ProgressWeight = FileUtils.GetInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s
'构造版本安装请求 '构造版本安装请求
If PackJson("components") Is Nothing Then Throw New Exception("该 MMC 整合包未提供游戏版本信息,无法安装!") If PackJson("components") Is Nothing Then Throw New Exception("该 MMC 整合包未提供游戏版本信息,无法安装!")
Dim Request As New McInstallRequest With {.NewInstanceName = InstanceName, .VersionFolder = $"{McFolderSelected}versions\{InstanceName}\"} Dim MinecraftVersion As String = Nothing
Dim ModLoaderVersions As New Dictionary(Of ModLoaders, String)
For Each Component As JObject In PackJson("components") For Each Component As JObject In PackJson("components")
If Not Component.ContainsKey("uid") Then Continue For If Not Component.ContainsKey("uid") Then Continue For
Dim UID As String = Component("uid").ToString Dim UID As String = Component("uid").ToString
Select Case UID Select Case UID
Case "net.minecraft" Case "net.minecraft"
Request.MinecraftName = Component("version") MinecraftVersion = Component("version")
Case "net.minecraftforge" Case "net.minecraftforge"
Request.ForgeVersion = Component("version") ModLoaderVersions(ModLoaders.Forge) = Component("version")
Case "net.neoforged" Case "net.neoforged"
Request.NeoForgeVersion = Component("version") ModLoaderVersions(ModLoaders.NeoForge) = Component("version")
Case "net.fabricmc.fabric-loader" Case "net.fabricmc.fabric-loader"
Request.FabricVersion = Component("version") ModLoaderVersions(ModLoaders.Fabric) = Component("version")
Case Else Case Else
If UID.StartsWithF("org.lwjgl") Then '#8210 If UID.StartsWithF("org.lwjgl") Then '#8210
Logger.Info($"已跳过 LWJGL 项:{UID}") Logger.Info($"已跳过 LWJGL 项:{UID}")
@@ -655,7 +649,10 @@ Public Module ModModpack
End Select End Select
Next Next
'构造加载器 '构造加载器
Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request) Dim Target As New McComponents(MinecraftVersion, ModLoaderVersions)
Dim MergeLoaders = McInstallLoadersGet(New McInstallRequest With {
.NewInstanceName = InstanceName, .InstanceFolder = VersionFolder,
.Components = Target})
'构造总加载器 '构造总加载器
Dim Loaders As New List(Of LoaderBase) Dim Loaders As New List(Of LoaderBase)
Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)}) Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)})
@@ -670,7 +667,7 @@ Public Module ModModpack
'启动 '启动
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState} Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
Loader.Start(Request.VersionFolder) Loader.Start(VersionFolder)
LoaderTaskbarAdd(Loader) LoaderTaskbarAdd(Loader)
FrmMain.BtnExtraDownload.ShowRefresh() FrmMain.BtnExtraDownload.ShowRefresh()
Return Loader Return Loader
@@ -695,16 +692,15 @@ Public Module ModModpack
If InstanceName = "" Then InstanceName = MyMsgBoxInput("输入版本名称", "", "", New Collection(Of Validate) From {Validate}) If InstanceName = "" Then InstanceName = MyMsgBoxInput("输入版本名称", "", "", New Collection(Of Validate) From {Validate})
If String.IsNullOrEmpty(InstanceName) Then Throw New OperationCanceledException If String.IsNullOrEmpty(InstanceName) Then Throw New OperationCanceledException
End If End If
Dim VersionFolder As String = Path.Combine(McFolderSelected, "versions", InstanceName) & "\"
'解压 '解压
Dim InstallTemp As String = RequestTaskTempFolder() Dim InstallTemp As String = RequestTaskTempFolder()
Dim SetupFile As String = $"{McFolderSelected}versions\{InstanceName}\PCL\Setup.ini" Dim SetupFile As String = VersionFolder & "PCL\Setup.ini"
Dim InstallLoaders As New List(Of LoaderBase) Dim InstallLoaders As New List(Of LoaderBase)
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件", InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
Sub(Task As LoaderTask(Of String, Integer)) Sub(Task As LoaderTask(Of String, Integer))
ExtractModpackFiles(InstallTemp, FileAddress, Task.CreateCancellationToken(), Task.CreateSyncProgressProvider(ToPercentage:=0.6)) ExtractModpackFiles(InstallTemp, FileAddress, Task.CreateCancellationToken(), Task.CreateSyncProgressProvider(ToPercentage:=0.6))
CopyOverrideDirectory( CopyOverrideDirectory(InstallTemp & ArchiveBaseFolder & "overrides", VersionFolder, Task)
InstallTemp & ArchiveBaseFolder & "overrides",
McFolderSelected & "versions\" & InstanceName, Task)
'JVM 参数 'JVM 参数
If Json("launchInfo") IsNot Nothing Then If Json("launchInfo") IsNot Nothing Then
Dim LaunchInfo As JObject = Json("launchInfo") Dim LaunchInfo As JObject = Json("launchInfo")
@@ -723,16 +719,26 @@ Public Module ModModpack
Hint("PCL 不支持安装 Quilt 整合包!", HintType.Red) Hint("PCL 不支持安装 Quilt 整合包!", HintType.Red)
Throw New OperationCanceledException Throw New OperationCanceledException
End If End If
Dim Request As New McInstallRequest With { Dim ModLoaderVersions As New Dictionary(Of ModLoaders, String)
.NewInstanceName = InstanceName, If Addons.ContainsKey("forge") Then ModLoaderVersions(ModLoaders.Forge) = Addons("forge")
.VersionFolder = $"{McFolderSelected}versions\{InstanceName}\", If Addons.ContainsKey("neoforge") Then ModLoaderVersions(ModLoaders.NeoForge) = Addons("neoforge")
.MinecraftName = Addons("game"), If Addons.ContainsKey("fabric") Then ModLoaderVersions(ModLoaders.Fabric) = Addons("fabric")
.OptiFineVersion = If(Addons.ContainsKey("optifine"), Addons("optifine"), Nothing), Dim OptiFineEntry As JObject = Nothing
.ForgeVersion = If(Addons.ContainsKey("forge"), Addons("forge"), Nothing), If Addons.ContainsKey("optifine") Then
.NeoForgeVersion = If(Addons.ContainsKey("neoforge"), Addons("neoforge"), Nothing), Dim MinecraftVersion = Addons("game")
.FabricVersion = If(Addons.ContainsKey("fabric"), Addons("fabric"), Nothing) If MinecraftVersion.EndsWithF(".0") Then MinecraftVersion = MinecraftVersion.Substring(0, MinecraftVersion.Length - 2)
} Dim OptiFineFileSuffix = "_HD_" & Addons("optifine").AfterLast("HD_") & ".jar"
Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request) '从镜像源拉取 OptiFine 列表
If DlOptiFineListBmclapiLoader.State <> LoadState.Finished Then DlOptiFineListBmclapiLoader.WaitForExit()
Dim Entry = DlOptiFineListBmclapiLoader.Output.Value.FirstOrDefault(
Function(v) v.Inherit = MinecraftVersion AndAlso v.FileName.EndsWithF(OptiFineFileSuffix))
If Entry Is Nothing Then Throw New Exception($"OptiFine 列表中没有整合包所需的 OptiFine {Addons("optifine")}(Minecraft {Addons("game")})")
OptiFineEntry = JObject.FromObject(Entry)
End If
Dim Target As New McComponents(Addons("game"), ModLoaderVersions, OptiFineEntry)
Dim MergeLoaders = McInstallLoadersGet(New McInstallRequest With {
.NewInstanceName = InstanceName, .InstanceFolder = VersionFolder,
.Components = Target})
'构造总加载器 '构造总加载器
Dim Loaders As New List(Of LoaderBase) Dim Loaders As New List(Of LoaderBase)
Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)}) Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)})
@@ -748,7 +754,7 @@ Public Module ModModpack
'启动 '启动
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState} Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
'If Archive IsNot Nothing Then Archive.Dispose() '解除占用,以免在加载器中触发 “正由另一进程使用,因此该进程无法访问此文件” 'If Archive IsNot Nothing Then Archive.Dispose() '解除占用,以免在加载器中触发 “正由另一进程使用,因此该进程无法访问此文件”
Loader.Start(Request.VersionFolder) Loader.Start(VersionFolder)
LoaderTaskbarAdd(Loader) LoaderTaskbarAdd(Loader)
FrmMain.BtnExtraDownload.ShowRefresh() FrmMain.BtnExtraDownload.ShowRefresh()
Return Loader Return Loader
@@ -319,13 +319,16 @@ Public Module ModWatcher
GetClassName(hwnd, str, str.Capacity) GetClassName(hwnd, str, str.Capacity)
Dim ClassName As String = str.ToString Dim ClassName As String = str.ToString
If Not (ClassName = "GLFW30" OrElse ClassName = "SDL_app" OrElse ClassName = "LWJGL" OrElse ClassName = "SunAwtFrame") Then Return If Not (ClassName = "GLFW30" OrElse ClassName = "SDL_app" OrElse ClassName = "LWJGL" OrElse ClassName = "SunAwtFrame") Then Return
'SDL 会先创建隐藏窗口,待初始化完成后才显示(#9073)
If ClassName = "SDL_app" AndAlso Not IsWindowVisible(hwnd) Then Return
'获取窗口标题名 '获取窗口标题名
str = New StringBuilder(512) str = New StringBuilder(512)
GetWindowText(hwnd, str, str.Capacity) GetWindowText(hwnd, str, str.Capacity)
Dim WindowText As String = str.ToString Dim WindowText As String = str.ToString
'有的 Mod 可以修改窗口标题,所以不能检测是否为 Minecraft 打头,这并不准确 '有的 Mod 可以修改窗口标题,所以不能检测是否为 Minecraft 打头,这并不准确
'部分版本会搞个 GLFW message window 出来所以得反选 '部分版本会搞个 GLFW message window,高版本还有个 Hidden Utility Window,都要反选
If Not (WindowText.StartsWithF("FML") OrElse (WindowText <> "PopupMessageWindow") AndAlso Not WindowText.StartsWithF("GLFW")) Then Return If String.IsNullOrWhiteSpace(WindowText) OrElse WindowText = "SDL_app" OrElse WindowText = "PopupMessageWindow" OrElse
WindowText.StartsWithF("GLFW") OrElse WindowText.EndsWithF("Hidden Utility Window") Then Return
'获取窗口关联的进程 '获取窗口关联的进程
Dim ProcessId As Integer Dim ProcessId As Integer
GetWindowThreadProcessId(hwnd, ProcessId) GetWindowThreadProcessId(hwnd, ProcessId)
@@ -346,6 +349,7 @@ Public Module ModWatcher
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (hWnd As Integer, str As StringBuilder, maxCount As Integer) As Integer Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (hWnd As Integer, str As StringBuilder, maxCount As Integer) As Integer
Private Declare Function SetWindowText Lib "user32" Alias "SetWindowTextA" (hWnd As Integer, str As String) As Boolean Private Declare Function SetWindowText Lib "user32" Alias "SetWindowTextA" (hWnd As Integer, str As String) As Boolean
Private Declare Function ShowWindow Lib "user32" (hWnd As IntPtr, cmdWindow As UInteger) As Boolean Private Declare Function ShowWindow Lib "user32" (hWnd As IntPtr, cmdWindow As UInteger) As Boolean
Private Declare Function IsWindowVisible Lib "user32" (hWnd As IntPtr) As Boolean
Private Declare Function GetWindowThreadProcessId Lib "user32" (hWnd As IntPtr, ByRef lpdwProcessId As Integer) As Integer Private Declare Function GetWindowThreadProcessId Lib "user32" (hWnd As IntPtr, ByRef lpdwProcessId As Integer) As Integer
'崩溃处理 '崩溃处理
+27 -10
View File
@@ -1,6 +1,9 @@
#Region "附加属性" #Region "附加属性"
Imports PCL.FormMain
''' <summary> ''' <summary>
''' 用于在 XAML 中初始化列表对象。 ''' 用于在 XAML 中初始化列表对象。
''' 附加属性无法在 XAML 中为每个对象初始化独立的列表对象,因此需要一个包装类,然后在 XAML 中显式初始化。 ''' 附加属性无法在 XAML 中为每个对象初始化独立的列表对象,因此需要一个包装类,然后在 XAML 中显式初始化。
@@ -272,9 +275,23 @@ Public Class CustomEvent
Hint(Arg0.Replace("\n", vbCrLf), If(Args.Length = 1, HintType.Blue, EnumUtils.FromString(Of HintType)(Arg1))) Hint(Arg0.Replace("\n", vbCrLf), If(Args.Length = 1, HintType.Blue, EnumUtils.FromString(Of HintType)(Arg1)))
Case EventType.切换页面 Case EventType.切换页面
RunInUi(Sub() FrmMain.PageChange( RunInUi(
EnumUtils.FromString(Of FormMain.PageType)(Arg0), Sub()
If(Args.Length = 1, FormMain.PageSubType.Default, EnumUtils.FromString(Of FormMain.PageSubType)(Arg1)))) Dim TargetPage = EnumUtils.FromString(Of PageType)(Arg0)
If TargetPage = PageType.InstanceSetup Then
If McInstanceSelected Is Nothing Then
Hint("请先选择一个 Minecraft 版本!", HintType.Red)
Return
End If
If McInstanceLock.IsLocked(McInstanceSelected) Then
Hint("该版本正在被修改,暂时无法进入版本设置!", HintType.Red)
Return
End If
PageInstanceLeft.Instance = McInstanceSelected
End If
FrmMain.PageChange(TargetPage,
If(Args.Length = 1, PageSubType.Default, EnumUtils.FromString(Of PageSubType)(Arg1)))
End Sub)
Case EventType.导入整合包, EventType.安装整合包 Case EventType.导入整合包, EventType.安装整合包
RunInUi(Sub() ModpackInstall()) RunInUi(Sub() ModpackInstall())
@@ -295,7 +312,7 @@ Public Class CustomEvent
Case 2 Case 2
PageOtherTest.StartCustomDownload(Arg0, Arg1) PageOtherTest.StartCustomDownload(Arg0, Arg1)
Case Else Case Else
PageOtherTest.StartCustomDownload(Arg0, Arg(1), Args(2)) PageOtherTest.StartCustomDownload(Arg0, Args(1), Args(2))
End Select End Select
Catch Catch
PageOtherTest.StartCustomDownload(Arg0, "未知") PageOtherTest.StartCustomDownload(Arg0, "未知")
@@ -304,16 +321,16 @@ Public Class CustomEvent
Case EventType.修改设置, EventType.写入设置 Case EventType.修改设置, EventType.写入设置
If Args.Length = 1 Then Throw New Exception($"EventType {Type} 需要至少 2 个以 | 分割的参数,例如 UiLauncherTransparent|400") If Args.Length = 1 Then Throw New Exception($"EventType {Type} 需要至少 2 个以 | 分割的参数,例如 UiLauncherTransparent|400")
Settings.SetSafe(Arg0, Arg1, Instance:=McInstanceSelected) Settings.SetSafe(Arg0, Args(1), Instance:=McInstanceSelected)
If Args.Length = 2 Then Hint($"已写入设置:{Arg0} → {Arg1}", HintType.Green) If Args.Length = 2 Then Hint($"已写入设置:{Arg0} → {Args(1)}", HintType.Green)
Case EventType.修改变量, EventType.写入变量 Case EventType.修改变量, EventType.写入变量
If Args.Length = 1 Then Throw New Exception($"EventType {Type} 需要至少 2 个以 | 分割的参数,例如 VariableName|SomeValue") If Args.Length = 1 Then Throw New Exception($"EventType {Type} 需要至少 2 个以 | 分割的参数,例如 VariableName|SomeValue")
Try Try
RegistryUtils.Write($"HKEY_CURRENT_USER\Software\{RegFolder}", $"CustomEvent{Arg0}", Arg1) RegistryUtils.Write($"HKEY_CURRENT_USER\Software\{RegFolder}", $"CustomEvent{Arg0}", Args(1))
If Args.Length = 2 Then Hint($"已写入变量:{Arg0} → {Arg1}", HintType.Green) If Args.Length = 2 Then Hint($"已写入变量:{Arg0} → {Args(1)}", HintType.Green)
Catch ex As Exception Catch ex As Exception
Logger.Warn(ex, $"写入变量失败:{Arg0} → {Arg1}", LogBehavior.Toast) Logger.Warn(ex, $"写入变量失败:{Arg0} → {Args(1)}", LogBehavior.Toast)
End Try End Try
Case EventType.加入房间 Case EventType.加入房间
@@ -353,7 +370,7 @@ Public Class CustomEvent
Dim RawFileName As String Dim RawFileName As String
Try Try
RawFileName = PathUtils.GetLastPart(RelativeUrl) RawFileName = PathUtils.GetLastPart(RelativeUrl)
If Not RawFileName.EndsWithF(".json", True) Then Throw New Exception("未指向 .json 后缀的文件") If Not PathUtils.GetExtension(RawFileName) = "json" Then Throw New Exception("未指向 .json 后缀的文件")
Catch ex As Exception Catch ex As Exception
Throw New Exception("联网帮助页面须指向一个帮助 JSON 文件,并在同路径下包含相应 XAML 文件!" & vbCrLf & Throw New Exception("联网帮助页面须指向一个帮助 JSON 文件,并在同路径下包含相应 XAML 文件!" & vbCrLf &
"例如:" & vbCrLf & "例如:" & vbCrLf &
+8 -7
View File
@@ -414,6 +414,7 @@ EndHint:
Public FrmInstanceModDisabled As PageInstanceModDisabled Public FrmInstanceModDisabled As PageInstanceModDisabled
Public FrmInstanceSetup As PageInstanceSetup Public FrmInstanceSetup As PageInstanceSetup
Public FrmInstanceExport As PageInstanceExport Public FrmInstanceExport As PageInstanceExport
Public FrmDownloadModify As PageDownloadModify
'资源信息分页声明 '资源信息分页声明
Public FrmDownloadResourceDetail As PageDownloadResourceDetail Public FrmDownloadResourceDetail As PageDownloadResourceDetail
@@ -510,7 +511,7 @@ EndHint:
EventData = If(JsonData("EventData"), "") EventData = If(JsonData("EventData"), "")
IsEvent = True IsEvent = True
Else Else
Dim XamlAddress As String = FilePath.Lower.Replace(".json", ".xaml") Dim XamlAddress As String = Path.ChangeExtension(FilePath, ".xaml")
If FileUtils.Exists(XamlAddress) Then If FileUtils.Exists(XamlAddress) Then
XamlContent = FileUtils.ReadAsString(XamlAddress) XamlContent = FileUtils.ReadAsString(XamlAddress)
IsEvent = False IsEvent = False
@@ -596,9 +597,9 @@ EndHint:
'读取自带文件 '读取自带文件
For Each File In DirectoryUtils.EnumerateFiles(PathTemp & "Help", True) For Each File In DirectoryUtils.EnumerateFiles(PathTemp & "Help", True)
'跳过非 json 文件与以 . 开头的文件夹 '跳过非 json 文件与以 . 开头的文件夹
If PathUtils.RemoveLastPart(File).Replace(PathTemp & "Help", "").Contains("\.") OrElse PathUtils.GetExtension(File) <> "json" Then Continue For If PathUtils.RemoveLastPart(File).Substring((PathTemp & "Help").Length).Contains("\.") OrElse PathUtils.GetExtension(File) <> "json" Then Continue For
'检查忽略列表 '检查忽略列表
Dim RealPath As String = File.Replace(PathTemp & "Help\", "") Dim RealPath As String = File.Substring((PathTemp & "Help\").Length)
For Each Ignore In IgnoreList For Each Ignore In IgnoreList
If RealPath.RegexCheck(Ignore) Then If RealPath.RegexCheck(Ignore) Then
Logger.Trace($"已忽略 {RealPath}:{Ignore}") Logger.Trace($"已忽略 {RealPath}:{Ignore}")
@@ -857,10 +858,10 @@ NextFile:
Else Else
Text = Text.Replace("{version}", Replacer(McInstanceSelected.Version.VanillaName)) Text = Text.Replace("{version}", Replacer(McInstanceSelected.Version.VanillaName))
End If End If
Text = Text.Replace("{forge}", Replacer(If(McInstanceSelected.Version.HasForge, McInstanceSelected.Version.Forge, ""))) Text = Text.Replace("{forge}", Replacer(If(McInstanceSelected.Version.ModLoaders.HasFlagF(ModLoaders.Forge), McInstanceSelected.Version.ModLoaderVersions(ModLoaders.Forge), "")))
Text = Text.Replace("{fabric}", Replacer(If(McInstanceSelected.Version.HasFabric, McInstanceSelected.Version.Fabric, ""))) Text = Text.Replace("{fabric}", Replacer(If(McInstanceSelected.Version.ModLoaders.HasFlagF(ModLoaders.Fabric), McInstanceSelected.Version.ModLoaderVersions(ModLoaders.Fabric), "")))
Text = Text.Replace("{neoforge}", Replacer(If(McInstanceSelected.Version.HasNeoForge, McInstanceSelected.Version.NeoForge, ""))) Text = Text.Replace("{neoforge}", Replacer(If(McInstanceSelected.Version.ModLoaders.HasFlagF(ModLoaders.NeoForge), McInstanceSelected.Version.ModLoaderVersions(ModLoaders.NeoForge), "")))
Text = Text.Replace("{liteloader}", Replacer(If(McInstanceSelected.Version.HasLiteLoader, "true", ""))) Text = Text.Replace("{liteloader}", Replacer(If(McInstanceSelected.Version.ModLoaders.HasFlagF(ModLoaders.LiteLoader), "true", "")))
Else Else
Text = Text.Replace("{version_path}", Replacer(Nothing)) : Text = Text.Replace("{verpath}", Replacer(Nothing)) Text = Text.Replace("{version_path}", Replacer(Nothing)) : Text = Text.Replace("{verpath}", Replacer(Nothing))
Text = Text.Replace("{version_indie}", Replacer(Nothing)) : Text = Text.Replace("{verindie}", Replacer(Nothing)) Text = Text.Replace("{version_indie}", Replacer(Nothing)) : Text = Text.Replace("{verindie}", Replacer(Nothing))
+1 -1
View File
@@ -320,7 +320,7 @@ Public Module ModMusic
Hint("由于音频设备变更,音乐播放功能在重启 PCL 后才能恢复!", HintType.Red) Hint("由于音频设备变更,音乐播放功能在重启 PCL 后才能恢复!", HintType.Red)
Thread.Sleep(1000000000) Thread.Sleep(1000000000)
End If End If
If Not (MusicCurrent.EndsWithF(".wav", True) OrElse MusicCurrent.EndsWithF(".mp3", True) OrElse MusicCurrent.EndsWithF(".flac", True)) OrElse If Not ({"wav", "mp3", "flac"}.Contains(PathUtils.GetExtension(MusicCurrent))) OrElse
ex.Message.Contains("0xC00D36C4") Then '#5096:不支持给定的 URL 的字节流类型。 (异常来自 HRESULT:0xC00D36C4) ex.Message.Contains("0xC00D36C4") Then '#5096:不支持给定的 URL 的字节流类型。 (异常来自 HRESULT:0xC00D36C4)
Hint("播放音乐失败(" & PathUtils.GetLastPart(MusicCurrent) & "):PCL 可能不支持此音乐格式,请将格式转换为 .wav、.mp3 或 .flac 后再试", HintType.Red) Hint("播放音乐失败(" & PathUtils.GetLastPart(MusicCurrent) & "):PCL 可能不支持此音乐格式,请将格式转换为 .wav、.mp3 或 .flac 后再试", HintType.Red)
Else Else
@@ -15,6 +15,7 @@ Friend Module ModSecret
#Region "网络鉴权" #Region "网络鉴权"
Public Const RestrictMcDownload As Boolean = True
Friend Function SecretCdnSign(UrlWithMark As String) As String Friend Function SecretCdnSign(UrlWithMark As String) As String
If Not UrlWithMark.EndsWithF("{CDN}") Then Return UrlWithMark If Not UrlWithMark.EndsWithF("{CDN}") Then Return UrlWithMark
Return UrlWithMark.Replace("{CDN}", "").Replace(" ", "%20") Return UrlWithMark.Replace("{CDN}", "").Replace(" ", "%20")
@@ -25,7 +25,7 @@ Public Class LocalResourceFile
End Get End Get
End Property End Property
''' <summary> ''' <summary>
''' 假若该资源未被禁用时,文件的文件名。 ''' 假若该资源未被禁用时,文件的文件名。包含 .jar/.litemod 等后缀。
''' </summary> ''' </summary>
Public ReadOnly Property EnabledName As String Public ReadOnly Property EnabledName As String
Get Get
@@ -57,7 +57,7 @@ Public Class LocalResourceFile
Public Property Display As String Public Property Display As String
Get Get
If _Display Is Nothing Then _Display = ProjectVersion?.Display If _Display Is Nothing Then _Display = ProjectVersion?.Display
If _Display Is Nothing Then _Display = PathUtils.GetFileNameWithoutExtension(File.FullName) If _Display Is Nothing Then _Display = PathUtils.GetFileNameWithoutExtension(EnabledName)
Return _Display Return _Display
End Get End Get
Set(value As String) Set(value As String)
@@ -238,7 +238,7 @@ Public Class LocalResourceFile
Private _CurseForgeHash As UInteger? Private _CurseForgeHash As UInteger?
''' <summary> ''' <summary>
''' 获取用于 Modrinth 信息获取的 Hash 值(SHA1)。 ''' 获取用于 Modrinth 信息获取的 Hash 值(SHA1)。固定为小写。
''' </summary> ''' </summary>
Public ReadOnly Property ModrinthHash As String Public ReadOnly Property ModrinthHash As String
Get Get
@@ -269,7 +269,7 @@ Public Class LocalResourceFile
End Function End Function
Public Overrides Function Equals(obj As Object) As Boolean Public Overrides Function Equals(obj As Object) As Boolean
Dim Target = TryCast(obj, LocalResourceFile) Dim Target = TryCast(obj, LocalResourceFile)
Return Target IsNot Nothing AndAlso File.FullName = Target.File.FullName Return Target IsNot Nothing AndAlso PathUtils.IsSame(File.FullName, Target.File.FullName)
End Function End Function
#End Region #End Region
@@ -24,11 +24,10 @@ Public Module LocalResourceLoaders
'获取 Mod 文件夹下的可用文件列表 '获取 Mod 文件夹下的可用文件列表
Dim ModFileList As New List(Of String) Dim ModFileList As New List(Of String)
If DirectoryUtils.Exists(Loader.Input) Then If DirectoryUtils.Exists(Loader.Input) Then
Dim RawName As String = Loader.Input.Lower
For Each File In DirectoryUtils.EnumerateFiles(Loader.Input, True) For Each File In DirectoryUtils.EnumerateFiles(Loader.Input, True)
If File.BeforeLast("\").Lower & "\" <> RawName Then If Not PathUtils.IsSame(PathUtils.RemoveLastPart(File), Loader.Input) Then
'仅当 Forge 1.13- 且文件夹名与版本号相同时,才加载该子文件夹下的 Mod '仅当 Forge 1.13- 且文件夹名与版本号相同时,才加载该子文件夹下的 Mod
If Not (PageInstanceLeft.Instance IsNot Nothing AndAlso PageInstanceLeft.Instance.Version.HasForge AndAlso If Not (PageInstanceLeft.Instance IsNot Nothing AndAlso PageInstanceLeft.Instance.Version.ModLoaders.HasFlagF(ModLoaders.Forge) AndAlso
PageInstanceLeft.Instance.Version.Vanilla.Major < 13 AndAlso PageInstanceLeft.Instance.Version.Vanilla.Major < 13 AndAlso
PathUtils.GetLastPart(PathUtils.RemoveLastPart(File)) = $"1.{PageInstanceLeft.Instance.Version.Vanilla.Major}.{PageInstanceLeft.Instance.Version.Vanilla.Build}") Then PathUtils.GetLastPart(PathUtils.RemoveLastPart(File)) = $"1.{PageInstanceLeft.Instance.Version.Vanilla.Major}.{PageInstanceLeft.Instance.Version.Vanilla.Build}") Then
Continue For Continue For
@@ -36,12 +35,9 @@ Public Module LocalResourceLoaders
End If End If
Static IsModFile As Func(Of String, Boolean) = Static IsModFile As Func(Of String, Boolean) =
Function(FullName As String) Function(FullName As String)
If FullName Is Nothing OrElse Not FullName.Contains(".") Then Return False If FullName Is Nothing Then Return False
FullName = FullName.Lower If {"disabled", "old"}.Contains(PathUtils.GetExtension(FullName)) Then FullName = PathUtils.GetFileNameWithoutExtension(FullName)
If FullName.EndsWithF(".jar", True) OrElse FullName.EndsWithF(".zip", True) OrElse FullName.EndsWithF(".litemod", True) OrElse Return {"jar", "zip", "litemod"}.Contains(PathUtils.GetExtension(FullName))
FullName.EndsWithF(".jar.disabled", True) OrElse FullName.EndsWithF(".zip.disabled", True) OrElse FullName.EndsWithF(".litemod.disabled", True) OrElse
FullName.EndsWithF(".jar.old", True) OrElse FullName.EndsWithF(".zip.old", True) OrElse FullName.EndsWithF(".litemod.old", True) Then Return True
Return False
End Function End Function
If IsModFile(File) Then ModFileList.Add(File) If IsModFile(File) Then ModFileList.Add(File)
Next Next
@@ -271,7 +267,7 @@ Public Module LocalResourceLoaders
Dim EntryVersion As String = Entry("gameVersion") Dim EntryVersion As String = Entry("gameVersion")
Return EntryVersion IsNot Nothing AndAlso EntryVersion = VanillaVersion Return EntryVersion IsNot Nothing AndAlso EntryVersion = VanillaVersion
End Function). End Function).
MaxByAll(Function(Entry) Entry("gameVersion").ToString, New VersionComparer) '只保留最新的 MC 版本 MaxByAll(Function(Entry) Entry("gameVersion").ToString, Comparer(Of String).Create(Function(x, y) McUtils.CompareVersion(x, y))) '只保留最新的 MC 版本
For Each Entry In LatestFiles For Each Entry In LatestFiles
Dim FileId = Entry("fileId").ToObject(Of Integer) Dim FileId = Entry("fileId").ToObject(Of Integer)
If Not UpdateFileIds.ContainsKey(FileId) Then UpdateFileIds(FileId) = New List(Of LocalResourceFile) If Not UpdateFileIds.ContainsKey(FileId) Then UpdateFileIds(FileId) = New List(Of LocalResourceFile)
@@ -184,11 +184,11 @@ Public Class ResourceProject
If Not NewFile.DownloadAvailable Then Continue For If Not NewFile.DownloadAvailable Then Continue For
ModLoaders = ModLoaders Or NewFile.ModLoaders ModLoaders = ModLoaders Or NewFile.ModLoaders
Dim GameVersions = File("gameVersions").ToObject(Of List(Of String)) Dim GameVersions = File("gameVersions").ToObject(Of List(Of String))
If Not GameVersions.Any(Function(v) McVersion.IsFormatFit(v)) Then Continue For If Not GameVersions.Any(Function(v) McVersion.IsStandardFormat(v)) Then Continue For
Files.Add(New KeyValuePair(Of Integer, List(Of String))(File("id"), GameVersions)) Files.Add(New KeyValuePair(Of Integer, List(Of String))(File("id"), GameVersions))
Next Next
For Each File In If(Data("latestFilesIndexes"), New JArray) '这俩玩意儿包含的文件不一样,见 #3599 For Each File In If(Data("latestFilesIndexes"), New JArray) '这俩玩意儿包含的文件不一样,见 #3599
If Not McVersion.IsFormatFit(File("gameVersion")) Then Continue For If Not McVersion.IsStandardFormat(File("gameVersion")) Then Continue For
Files.Add(New KeyValuePair(Of Integer, List(Of String))(File("fileId"), {File("gameVersion").ToString}.ToList)) Files.Add(New KeyValuePair(Of Integer, List(Of String))(File("fileId"), {File("gameVersion").ToString}.ToList))
Next Next
CurseForgeFileIds = Files.Select(Function(f) f.Key).Distinct.ToList CurseForgeFileIds = Files.Select(Function(f) f.Key).Distinct.ToList
@@ -99,12 +99,12 @@ Public Class ResourceSearcher
Dim Address As String = $"https://api.modrinth.com/v2/search?limit={RESULT_PAGE_SIZE}&index=relevance" Dim Address As String = $"https://api.modrinth.com/v2/search?limit={RESULT_PAGE_SIZE}&index=relevance"
If Not String.IsNullOrEmpty(SearchText) Then Address += "&query=" & StringUtils.UrlEscape(SearchText) If Not String.IsNullOrEmpty(SearchText) Then Address += "&query=" & StringUtils.UrlEscape(SearchText)
If Storage.ModrinthOffset > 0 Then Address += "&offset=" & Storage.ModrinthOffset If Storage.ModrinthOffset > 0 Then Address += "&offset=" & Storage.ModrinthOffset
'facets=[["categories:'game-mechanics'"],["categories:'forge',categories:'fabric'"],["versions:1.19.3"],["project_type:mod"]] 'facets=[["categories:'game-mechanics'"],["categories:forge","categories:fabric"],["versions:1.19.3"],["project_type:mod"]]
Dim Facets As New List(Of String) Dim Facets As New List(Of String)
Facets.Add($"[""project_type:{Type.ToString.Lower}""]") Facets.Add($"[""project_type:{Type.ToString.Lower}""]")
If Not String.IsNullOrEmpty(Tag) Then Facets.Add($"[""categories:'{Tag.AfterLast("/")}'""]") If Not String.IsNullOrEmpty(Tag) Then Facets.Add($"[""categories:'{Tag.AfterLast("/")}'""]")
If ModLoaders <> ModLoaders.None AndAlso Not IgnoreModLoaderFilter Then If ModLoaders <> ModLoaders.None AndAlso Not IgnoreModLoaderFilter Then
Facets.Add($"[""categories:'{ModLoaders.Flags.Select(Function(f) f.ToString.Lower).Join("',categories:'")}'""]") Facets.Add($"[""categories:{ModLoaders.Flags.Select(Function(f) f.ToString.Lower).Join(""",""categories:")}""]")
End If End If
If Not String.IsNullOrEmpty(GameVersion) Then Facets.Add($"[""versions:'{GameVersion}'""]") If Not String.IsNullOrEmpty(GameVersion) Then Facets.Add($"[""versions:'{GameVersion}'""]")
Address += "&facets=[" & String.Join(",", Facets) & "]" Address += "&facets=[" & String.Join(",", Facets) & "]"
@@ -378,21 +378,19 @@ NextPage:
If Request.Type <> ResourceTypes.Any AndAlso Not Project.Types.HasFlagF(Request.Type) Then Continue For If Request.Type <> ResourceTypes.Any AndAlso Not Project.Types.HasFlagF(Request.Type) Then Continue For
If Not String.IsNullOrEmpty(Request.Tag) AndAlso If Not String.IsNullOrEmpty(Request.Tag) AndAlso
Not JsonEntry("categories").Any(Function(c) c.ToString = Request.Tag.AfterLast("/")) Then Continue For 'Project.Tags 已经转换成中文了,只能从 json 判 Not JsonEntry("categories").Any(Function(c) c.ToString = Request.Tag.AfterLast("/")) Then Continue For 'Project.Tags 已经转换成中文了,只能从 json 判
If Request.ModLoaders <> ModLoaders.None AndAlso Not IgnoreModLoaderFilter AndAlso
Not Project.ModLoaders.Flags.Intersect(Request.ModLoaders.Flags).Any() Then Continue For
If Not String.IsNullOrEmpty(Request.GameVersion) AndAlso
Not Project.UnsafeGameVersions.Any(Function(d) d = Request.GameVersion) Then Continue For
ProjectList.Add(Project) ProjectList.Add(Project)
Next Next
Dim Facets As New List(Of String) If ProjectList.Any AndAlso Request.ModLoaders <> ModLoaders.None AndAlso Not IgnoreModLoaderFilter AndAlso
If Request.ModLoaders <> ModLoaders.None AndAlso Not IgnoreModLoaderFilter Then Not String.IsNullOrEmpty(Request.GameVersion) Then
Facets.Add($"[""categories:'{Request.ModLoaders.Flags.Select(Function(f) f.ToString.Lower).Join("'"",""categories:'")}'""]") Dim ProjectIds = ProjectList.Select(Function(p) p.Id).Join(""",""project_id:")
End If Dim Loaders = Request.ModLoaders.Flags.Select(Function(f) f.ToString.Lower).Join(""",""categories:")
If Not String.IsNullOrEmpty(Request.GameVersion) Then Facets.Add($"[""versions:'{Request.GameVersion}'""]") Dim FilterUrl = $"https://api.modrinth.com/v2/search?limit={ProjectList.Count}&facets=" &
If ProjectList.Any AndAlso Facets.Any Then $"[[""project_id:{ProjectIds}""],[""categories:{Loaders}""],[""versions:{Request.GameVersion}""]]"
Facets.Add("[""project_id:" & ProjectList.Select(Function(p) p.Id).Join(""",""project_id:") & """]") Dim MatchedIds = DlModRequest(FilterUrl)("hits").Select(Function(h) h("project_id").ToString).ToList
Dim FilterUrl As String = $"https://api.modrinth.com/v2/search?limit={Math.Min(ProjectList.Count, 100)}&facets=[" & String.Join(",", Facets) & "]"
Logger.Info($"开始 Modrinth 直接获取筛选:{FilterUrl}")
Dim MatchedIds As New List(Of String)
For Each JsonEntry As JObject In DlModRequest(FilterUrl)("hits")
MatchedIds.Add(JsonEntry("project_id").ToString)
Next
ProjectList.KeepIf(Function(p) MatchedIds.Contains(p.Id)) ProjectList.KeepIf(Function(p) MatchedIds.Contains(p.Id))
End If End If
'更新结果 '更新结果
@@ -90,7 +90,7 @@ Public Class ResourceVersion
''' </summary> ''' </summary>
Public DownloadUrls As List(Of String) Public DownloadUrls As List(Of String)
''' <summary> ''' <summary>
''' 对应文件的 SHA1 或 MD5。 ''' 对应文件的 SHA1 或 MD5。固定为小写。
''' </summary> ''' </summary>
Public Hash As String = Nothing Public Hash As String = Nothing
''' <summary> ''' <summary>
@@ -127,8 +127,9 @@ Public Class ResourceVersion
.DownloadCount = Data("downloadCount") .DownloadCount = Data("downloadCount")
.FileName = Data("fileName") .FileName = Data("fileName")
.Size = Data("fileLength") .Size = Data("fileLength")
.Hash = CType(Data("hashes"), JArray).ToList.FirstOrDefault(Function(s) s("algo").ToObject(Of Integer) = 1)?("value") If .Size = 0 Then .Size = -1 'CurseForge 可能错误地返回 0 尺寸(#9104)
If .Hash Is Nothing Then .Hash = CType(Data("hashes"), JArray).ToList.FirstOrDefault(Function(s) s("algo").ToObject(Of Integer) = 2)?("value") .Hash = CType(Data("hashes"), JArray).ToList.FirstOrDefault(Function(s) s("algo").ToObject(Of Integer) = 1)?("value")?.ToString().Lower()
If .Hash Is Nothing Then .Hash = CType(Data("hashes"), JArray).ToList.FirstOrDefault(Function(s) s("algo").ToObject(Of Integer) = 2)?("value")?.ToString().Lower()
'DownloadAddress 'DownloadAddress
Dim Url = Data("downloadUrl").ToString Dim Url = Data("downloadUrl").ToString
If Url = "" Then Url = $"https://edge.forgecdn.net/files/{CInt(.Id.ToString.Substring(0, 4))}/{CInt(.Id.ToString.Substring(4))}/{ .FileName}" If Url = "" Then Url = $"https://edge.forgecdn.net/files/{CInt(.Id.ToString.Substring(0, 4))}/{CInt(.Id.ToString.Substring(4))}/{ .FileName}"
@@ -145,13 +146,13 @@ Public Class ResourceVersion
'GameVersions 'GameVersions
Dim RawVersions As List(Of String) = Data("gameVersions").Select(Function(t) t.ToString.Trim.Lower).ToList Dim RawVersions As List(Of String) = Data("gameVersions").Select(Function(t) t.ToString.Trim.Lower).ToList
.GameVersions = RawVersions. .GameVersions = RawVersions.
Where(Function(v) McVersion.IsFormatFit(v)). Where(Function(v) McVersion.IsStandardFormat(v)).
Select(Function(v) v.Replace("-snapshot", " 预览版")). Select(Function(v) v.Replace("-snapshot", " 预览版")).
Distinct.ToList Distinct.ToList
If .GameVersions.IsSingle Then If .GameVersions.IsSingle Then
.GameVersions = .GameVersions.ToList .GameVersions = .GameVersions.ToList
ElseIf .GameVersions.Count > 1 Then ElseIf .GameVersions.Count > 1 Then
.GameVersions = .GameVersions.SortByComparison(AddressOf CompareVersionGE).ToList .GameVersions = .GameVersions.SortByComparison(AddressOf McUtils.CompareVersionGE).ToList
If .ResourceType = ResourceTypes.ModPack Then .GameVersions = { .GameVersions(0)}.ToList '整合包理应只 “支持” 一个版本 If .ResourceType = ResourceTypes.ModPack Then .GameVersions = { .GameVersions(0)}.ToList '整合包理应只 “支持” 一个版本
Else Else
.GameVersions = New List(Of String) From {"未知版本"} .GameVersions = New List(Of String) From {"未知版本"}
@@ -176,7 +177,7 @@ Public Class ResourceVersion
.FileName = File("filename") .FileName = File("filename")
.DownloadUrls = New List(Of String) From {File("url"), DlSourceModGet(File("url"))}.Distinct.ToList '同时添加了镜像源 .DownloadUrls = New List(Of String) From {File("url"), DlSourceModGet(File("url"))}.Distinct.ToList '同时添加了镜像源
.Size = File("size") .Size = File("size")
.Hash = File("hashes")("sha1") .Hash = File("hashes")("sha1")?.ToString.Lower()
End If End If
'类别与 Loaders '类别与 Loaders
'结果可能混杂着 Mod、数据包和服务端插件 '结果可能混杂着 Mod、数据包和服务端插件
@@ -208,7 +209,7 @@ Public Class ResourceVersion
If .GameVersions.IsSingle Then If .GameVersions.IsSingle Then
'无需处理 '无需处理
ElseIf .GameVersions.Count > 1 Then ElseIf .GameVersions.Count > 1 Then
.GameVersions = .GameVersions.SortByComparison(AddressOf CompareVersionGE).ToList .GameVersions = .GameVersions.SortByComparison(AddressOf McUtils.CompareVersionGE).ToList
If .ResourceType = ResourceTypes.ModPack Then .GameVersions = New List(Of String) From { .GameVersions(0)} '整合包理应只 “支持” 一个版本 If .ResourceType = ResourceTypes.ModPack Then .GameVersions = New List(Of String) From { .GameVersions(0)} '整合包理应只 “支持” 一个版本
ElseIf RawVersions.Any(Function(v) v.RegexCheck("[0-9]{2}w[0-9]{2}[a-z]")) Then ElseIf RawVersions.Any(Function(v) v.RegexCheck("[0-9]{2}w[0-9]{2}[a-z]")) Then
.GameVersions = RawVersions.Where(Function(v) v.RegexCheck("[0-9]{2}w[0-9]{2}[a-z]")).ToList .GameVersions = RawVersions.Where(Function(v) v.RegexCheck("[0-9]{2}w[0-9]{2}[a-z]")).ToList
@@ -355,7 +356,7 @@ Public Class ResourceVersion
.Info = .Info =
Iterator Function() Iterator Function()
If ModLoaders <> ModLoaders.None Then Yield ModLoaders.Flags.Join("、"c) If ModLoaders <> ModLoaders.None Then Yield ModLoaders.Flags.Join("、"c)
If .Title <> FileName.BeforeLast(".") Then Yield FileName.BeforeLast(".") If .Title <> PathUtils.GetFileNameWithoutExtension(FileName) Then Yield PathUtils.GetFileNameWithoutExtension(FileName)
If Dependencies.Any Then Yield $"{Dependencies.Count} 项前置" If Dependencies.Any Then Yield $"{Dependencies.Count} 项前置"
If GameVersions.All(Function(v) Not v.Contains(".") OrElse {"w", "snapshot", "rc", "pre", "experimental", "-"}.Any(Function(s) v.ContainsIgnoreCase(s))) Then Yield $"游戏版本 {GameVersions.Join("、"c)}" If GameVersions.All(Function(v) Not v.Contains(".") OrElse {"w", "snapshot", "rc", "pre", "experimental", "-"}.Any(Function(s) v.ContainsIgnoreCase(s))) Then Yield $"游戏版本 {GameVersions.Join("、"c)}"
If DownloadCount > 0 Then Yield $"下载 {If(DownloadCount > 100000, Math.Round(DownloadCount / 10000) & " 万", DownloadCount.ToString() + " ")}次" 'CurseForge 的下载次数经常错误地返回 0 If DownloadCount > 0 Then Yield $"下载 {If(DownloadCount > 100000, Math.Round(DownloadCount / 10000) & " 万", DownloadCount.ToString() + " ")}次" 'CurseForge 的下载次数经常错误地返回 0
@@ -8,10 +8,10 @@ Imports System.Runtime.InteropServices
'查看程序集特性的值 '查看程序集特性的值
<Assembly: AssemblyTitle("Plain Craft Launcher 启动器")> <Assembly: AssemblyTitle("Plain Craft Launcher (PCL) InfstarMC Edition")>
<Assembly: AssemblyDescription("Minecraft 启动器")> <Assembly: AssemblyDescription("InfstarMC 基于 PCL 独立进行的第三方 Minecraft 启动器")>
<Assembly: AssemblyCompany("")> <Assembly: AssemblyCompany("InfstarMC")>
<Assembly: AssemblyProduct("Plain Craft Launcher")> <Assembly: AssemblyProduct("Plain Craft Launcher (PCL) InfstarMC Edition")>
<Assembly: AssemblyCopyright("Copyright © 成都瓜皮龙科技有限公司")> <Assembly: AssemblyCopyright("Copyright © 成都瓜皮龙科技有限公司")>
<Assembly: AssemblyConfiguration("PCL2 Config Mark")> <Assembly: AssemblyConfiguration("PCL2 Config Mark")>
<Assembly: ComVisible(False)> <Assembly: ComVisible(False)>
@@ -51,6 +51,6 @@ Imports System.Runtime.InteropServices
' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 ' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
' 方法是按如下所示使用“*” ' 方法是按如下所示使用“*”
<Assembly: AssemblyVersion("2.13.1.1")> <Assembly: AssemblyVersion("0.1.0")>
<Assembly: AssemblyFileVersion("2.13.1.1")> <Assembly: AssemblyFileVersion("0.1.0")>
<Assembly: NeutralResourcesLanguage("")> <Assembly: NeutralResourcesLanguage("")>
File diff suppressed because it is too large Load Diff
@@ -1,7 +1,7 @@
<local:MyPageRight <local:MyPageRight
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PCL" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:PCL" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" x:Class="PageDownloadInstall" mc:Ignorable="d" x:Class="PageDownloadInstall"
PanScroll="PanBack"> PanScroll="PanBack">
<Grid> <Grid>
@@ -14,19 +14,96 @@
</StackPanel.RenderTransform> </StackPanel.RenderTransform>
</StackPanel> </StackPanel>
<StackPanel Grid.Row="1" x:Name="PanSelect" Visibility="Collapsed" Opacity="0" IsHitTestVisible="False" Margin="0,0,0,45"> <StackPanel Grid.Row="1" x:Name="PanSelect" Visibility="Collapsed" Opacity="0" IsHitTestVisible="False" Margin="0,0,0,45">
<StackPanel.Resources>
<Style x:Key="ModifyCompareIcon" TargetType="{x:Type local:MyImage}">
<Setter Property="Width" Value="18" />
<Setter Property="Height" Value="18" />
<Setter Property="RenderOptions.BitmapScalingMode" Value="Linear" />
</Style>
<Style x:Key="ModifyCompareText" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource BasedOnTextBlock}">
<Setter Property="Margin" Value="7,0,0,0" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style x:Key="ModifyCompareArrow" TargetType="{x:Type Path}">
<Setter Property="Data" Value="M3,3 L7,7 3,11 M9,3 L13,7 9,11" />
<Setter Property="Margin" Value="10,0,18,0" />
<Setter Property="Height" Value="14" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Stroke" Value="{DynamicResource ColorBrushGray2}" />
<Setter Property="StrokeThickness" Value="1.4" />
<Setter Property="StrokeStartLineCap" Value="Round" />
<Setter Property="StrokeEndLineCap" Value="Round" />
<Setter Property="StrokeLineJoin" Value="Round" />
</Style>
<sys:String x:Key="ModifyRestoreLogo">M9.83,3.17 A1.18,1.18 0 0 1 9.83,4.83 L6.85,7.82 H14.5 A6.68,6.68 0 0 1 14.5,21.18 H11 A1.18,1.18 0 0 1 11,18.82 H14.5 A4.32,4.32 0 0 0 14.5,10.18 H6.85 L9.83,13.17 A1.18,1.18 0 0 1 8.17,14.83 L3.17,9.83 A1.18,1.18 0 0 1 3.17,8.17 L8.17,3.17 A1.18,1.18 0 0 1 9.83,3.17 Z</sys:String>
<sys:String x:Key="ModifyClearLogo">F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z</sys:String>
<Style x:Key="ModifyRestoreButton" TargetType="{x:Type local:MyIconButton}">
<Setter Property="Visibility" Value="Collapsed" />
<Setter Property="Height" Value="30" />
<Setter Property="Width" Value="30" />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="Margin" Value="0,5,32,0" />
<Setter Property="ToolTip" Value="撤销更改" />
<Setter Property="Cursor" Value="Hand" />
</Style>
<Style x:Key="ModifyClearButton" TargetType="{x:Type local:MyIconButton}">
<Setter Property="Height" Value="30" />
<Setter Property="Width" Value="30" />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="Margin" Value="0,5,32,0" />
<Setter Property="ToolTip" Value="取消安装" />
</Style>
</StackPanel.Resources>
<StackPanel.RenderTransform> <StackPanel.RenderTransform>
<TranslateTransform X="40" /> <TranslateTransform X="40" />
</StackPanel.RenderTransform> </StackPanel.RenderTransform>
<local:MyCard Margin="0,15,0,15" UseAnimation="False" Height="62"> <local:MyCard x:Name="CardSelectName" Margin="0,15,0,15" MinHeight="62">
<local:MyIconButton HorizontalAlignment="Left" Theme="Black" Opacity="0.6" x:Name="BtnBack" Margin="13,0,0,0" Height="26" Width="26" <local:MyIconButton HorizontalAlignment="Left" Theme="Black" Opacity="0.6" x:Name="BtnBack" Margin="13,0,0,0" Height="26" Width="26"
LogoScale="0.87" Logo="M1097 584 250 584 562 896C591 925 591 972 562 1001 533 1030 487 1030 458 1001L21 565C6 550-0 531 0 511L0 511 0 511C-0 492 6 472 21 457L458 21C487-7 533-7 562 21 591 50 591 97 562 126L250 438 1097 438C1137 438 1170 471 1170 511 1170 551 1137 584 1097 584L1097 584Z" /> LogoScale="0.87" Logo="M1097 584 250 584 562 896C591 925 591 972 562 1001 533 1030 487 1030 458 1001L21 565C6 550-0 531 0 511L0 511 0 511C-0 492 6 472 21 457L458 21C487-7 533-7 562 21 591 50 591 97 562 126L250 438 1097 438C1137 438 1170 471 1170 511 1170 551 1137 584 1097 584L1097 584Z" />
<local:MyImage x:Name="ImgLogo" Margin="52,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left" Height="32" RenderOptions.BitmapScalingMode="HighQuality" /> <local:MyImage x:Name="ImgLogo" Margin="52,15,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" Height="32" RenderOptions.BitmapScalingMode="HighQuality" />
<local:MyTextBox Margin="93,0,16,0" Padding="-1,0" MinHeight="30" FontSize="15" VerticalAlignment="Center" x:Name="TextSelectName" MaxLength="70" HasBackground="False" /> <local:MyTextBox Margin="93,16,16,0" Padding="-1,0" MinHeight="30" FontSize="15" VerticalAlignment="Top" x:Name="TextSelectName" MaxLength="70" HasBackground="False" ShowValidateResult="False" />
<TextBlock x:Name="LabSelectName" Visibility="Collapsed" Height="21" Margin="56,47,42,11" Padding="0,4,0,0" VerticalAlignment="Top" FontSize="12.5" Foreground="{DynamicResource ColorBrushGray3}" TextTrimming="CharacterEllipsis" />
<local:MyIconButton x:Name="BtnOriginalNameRestore" Style="{StaticResource ModifyRestoreButton}" Margin="0,16,6,0"
Theme="Black" LogoScale="0.55" Logo="{StaticResource ModifyRestoreLogo}" />
</local:MyCard> </local:MyCard>
<local:MyHint x:Name="HintSelectName" Theme="Red" Visibility="Collapsed" />
<local:MyHint Text="如果不安装 Fabric API,大多数 Mod 都会无法使用!" Margin="0,10,0,0" x:Name="HintFabricAPI" Theme="Red" /> <local:MyHint Text="如果不安装 Fabric API,大多数 Mod 都会无法使用!" Margin="0,10,0,0" x:Name="HintFabricAPI" Theme="Red" />
<local:MyHint Text="必须安装 OptiFabric 才能正常使用 OptiFine!" Margin="0,10,0,0" x:Name="HintOptiFabric" Theme="Red" /> <local:MyHint Text="必须安装 OptiFabric 才能正常使用 OptiFine!" Margin="0,10,0,0" x:Name="HintOptiFabric" Theme="Red" />
<local:MyHint Text="安装结束后,请在 Mod 下载中搜索 OptiFabric Origins 并下载,否则 OptiFine 会无法使用!" Margin="0,10,0,0" x:Name="HintOptiFabricOld" Theme="Yellow" /> <local:MyHint Text="安装结束后,请在 Mod 下载中搜索 OptiFabric Origins 并下载,否则 OptiFine 会无法使用!" Margin="0,10,0,0" x:Name="HintOptiFabricOld" Theme="Yellow" />
<local:MyHint Text="OptiFine 与一部分 Mod 的兼容性不佳,请谨慎安装。" Margin="0,10,0,0" x:Name="HintModOptiFine" Theme="Yellow" /> <local:MyHint Text="OptiFine 与一部分 Mod 的兼容性不佳,请谨慎安装。" Margin="0,10,0,0" x:Name="HintModOptiFine" Theme="Yellow" />
<local:MyCard Title="Minecraft" Height="40" Margin="0,12,0,0" x:Name="CardMinecraft" Visibility="Collapsed">
<Grid x:Name="PanMinecraftInfo" Height="18" Margin="132,11,35,0" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /><ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<local:MyImage x:Name="ImgMinecraft" Height="18" Width="18" Margin="0,0,7,0" RenderOptions.BitmapScalingMode="Linear" />
<TextBlock x:Name="LabMinecraftVersion" Grid.Column="1" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" />
</Grid>
<Grid x:Name="PanMinecraftCompare" Height="18" Margin="132,11,80,0" VerticalAlignment="Top" Visibility="Collapsed" IsHitTestVisible="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" /><ColumnDefinition Width="Auto" /><ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<DockPanel>
<local:MyImage x:Name="ImgMinecraftOld" Style="{StaticResource ModifyCompareIcon}" DockPanel.Dock="Left" />
<TextBlock x:Name="LabMinecraftOld" Style="{StaticResource ModifyCompareText}" />
</DockPanel>
<Path Grid.Column="1" Style="{StaticResource ModifyCompareArrow}" />
<DockPanel Grid.Column="2">
<local:MyImage x:Name="ImgMinecraftNew" Style="{StaticResource ModifyCompareIcon}" DockPanel.Dock="Left" />
<TextBlock x:Name="LabMinecraftNew" Style="{StaticResource ModifyCompareText}" />
</DockPanel>
</Grid>
<Grid Height="40" VerticalAlignment="Top" Background="Transparent" Cursor="Hand">
<Path HorizontalAlignment="Right" Margin="0,0,16,0" Stretch="Uniform" Height="6" Width="10" Fill="{StaticResource ColorBrush1}" RenderTransformOrigin="0.5,0.5">
<Path.Data>M2,4 l-2,2 10,10 10,-10 -2,-2 -8,8 -8,-8 z</Path.Data>
<Path.RenderTransform><RotateTransform Angle="270" /></Path.RenderTransform>
</Path>
</Grid>
<local:MyIconButton x:Name="BtnMinecraftRestore" Style="{StaticResource ModifyRestoreButton}" Theme="Black" LogoScale="0.55" Logo="{StaticResource ModifyRestoreLogo}" />
</local:MyCard>
<local:MyCard Title="Forge" Height="40" Margin="0,12,0,0" x:Name="CardForge" IsSwapped="True" CanSwap="True" SwapLogoRight="True"> <local:MyCard Title="Forge" Height="40" Margin="0,12,0,0" x:Name="CardForge" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanForge" /> <StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanForge" />
<Grid x:Name="PanForgeInfo" Height="18" Margin="132,11,15,0" VerticalAlignment="Top" Tag="True"> <Grid x:Name="PanForgeInfo" Height="18" Margin="132,11,15,0" VerticalAlignment="Top" Tag="True">
@@ -37,13 +114,25 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" /> <ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Image x:Name="ImgForge" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/Anvil.png" /> <local:MyImage x:Name="ImgForge" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/Anvil.png" />
<TextBlock x:Name="LabForge" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" /> <TextBlock x:Name="LabForge" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" />
</Grid> </Grid>
<Grid x:Name="BtnForgeClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}"> <Grid x:Name="PanForgeCompare" Height="18" Margin="132,11,80,0" VerticalAlignment="Top" Visibility="Collapsed" IsHitTestVisible="False">
<Path x:Name="BtnForgeClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center" <Grid.ColumnDefinitions>
Data="F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z" /> <ColumnDefinition Width="180" /><ColumnDefinition Width="Auto" /><ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<DockPanel>
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/Anvil.png" DockPanel.Dock="Left" />
<TextBlock x:Name="LabForgeOld" Style="{StaticResource ModifyCompareText}" />
</DockPanel>
<Path Grid.Column="1" Style="{StaticResource ModifyCompareArrow}" />
<DockPanel Grid.Column="2">
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/Anvil.png" DockPanel.Dock="Left" />
<TextBlock Style="{StaticResource ModifyCompareText}" />
</DockPanel>
</Grid> </Grid>
<local:MyIconButton x:Name="BtnForgeClear" Style="{StaticResource ModifyClearButton}" Theme="Black" LogoScale="0.5" Logo="{StaticResource ModifyClearLogo}" />
<local:MyIconButton x:Name="BtnForgeRestore" Style="{StaticResource ModifyRestoreButton}" Theme="Black" LogoScale="0.55" Logo="{StaticResource ModifyRestoreLogo}" />
</local:MyCard> </local:MyCard>
<local:MyCard Title="NeoForge" Height="40" Margin="0,12,0,0" x:Name="CardNeoForge" IsSwapped="True" CanSwap="True" SwapLogoRight="True"> <local:MyCard Title="NeoForge" Height="40" Margin="0,12,0,0" x:Name="CardNeoForge" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanNeoForge" /> <StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanNeoForge" />
@@ -55,13 +144,25 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" /> <ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Image x:Name="ImgNeoForge" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/NeoForge.png" /> <local:MyImage x:Name="ImgNeoForge" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/NeoForge.png" />
<TextBlock x:Name="LabNeoForge" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" /> <TextBlock x:Name="LabNeoForge" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" />
</Grid> </Grid>
<Grid x:Name="BtnNeoForgeClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}"> <Grid x:Name="PanNeoForgeCompare" Height="18" Margin="132,11,80,0" VerticalAlignment="Top" Visibility="Collapsed" IsHitTestVisible="False">
<Path x:Name="BtnNeoForgeClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center" <Grid.ColumnDefinitions>
Data="F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z" /> <ColumnDefinition Width="180" /><ColumnDefinition Width="Auto" /><ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<DockPanel>
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/NeoForge.png" DockPanel.Dock="Left" />
<TextBlock x:Name="LabNeoForgeOld" Style="{StaticResource ModifyCompareText}" />
</DockPanel>
<Path Grid.Column="1" Style="{StaticResource ModifyCompareArrow}" />
<DockPanel Grid.Column="2">
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/NeoForge.png" DockPanel.Dock="Left" />
<TextBlock Style="{StaticResource ModifyCompareText}" />
</DockPanel>
</Grid> </Grid>
<local:MyIconButton x:Name="BtnNeoForgeClear" Style="{StaticResource ModifyClearButton}" Theme="Black" LogoScale="0.5" Logo="{StaticResource ModifyClearLogo}" />
<local:MyIconButton x:Name="BtnNeoForgeRestore" Style="{StaticResource ModifyRestoreButton}" Theme="Black" LogoScale="0.55" Logo="{StaticResource ModifyRestoreLogo}" />
</local:MyCard> </local:MyCard>
<local:MyCard Title="Fabric" Height="40" Margin="0,12,0,0" x:Name="CardFabric" IsSwapped="True" CanSwap="True" SwapLogoRight="True"> <local:MyCard Title="Fabric" Height="40" Margin="0,12,0,0" x:Name="CardFabric" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanFabric" /> <StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanFabric" />
@@ -73,13 +174,25 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" /> <ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Image x:Name="ImgFabric" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/Fabric.png" /> <local:MyImage x:Name="ImgFabric" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/Fabric.png" />
<TextBlock x:Name="LabFabric" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" /> <TextBlock x:Name="LabFabric" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" />
</Grid> </Grid>
<Grid x:Name="BtnFabricClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}"> <Grid x:Name="PanFabricCompare" Height="18" Margin="132,11,80,0" VerticalAlignment="Top" Visibility="Collapsed" IsHitTestVisible="False">
<Path x:Name="BtnFabricClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center" <Grid.ColumnDefinitions>
Data="F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z" /> <ColumnDefinition Width="180" /><ColumnDefinition Width="Auto" /><ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<DockPanel>
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/Fabric.png" DockPanel.Dock="Left" />
<TextBlock x:Name="LabFabricOld" Style="{StaticResource ModifyCompareText}" />
</DockPanel>
<Path Grid.Column="1" Style="{StaticResource ModifyCompareArrow}" />
<DockPanel Grid.Column="2">
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/Fabric.png" DockPanel.Dock="Left" />
<TextBlock Style="{StaticResource ModifyCompareText}" />
</DockPanel>
</Grid> </Grid>
<local:MyIconButton x:Name="BtnFabricClear" Style="{StaticResource ModifyClearButton}" Theme="Black" LogoScale="0.5" Logo="{StaticResource ModifyClearLogo}" />
<local:MyIconButton x:Name="BtnFabricRestore" Style="{StaticResource ModifyRestoreButton}" Theme="Black" LogoScale="0.55" Logo="{StaticResource ModifyRestoreLogo}" />
</local:MyCard> </local:MyCard>
<local:MyCard Title="Fabric API" Height="40" Margin="0,12,0,0" x:Name="CardFabricApi" IsSwapped="True" CanSwap="True" SwapLogoRight="True"> <local:MyCard Title="Fabric API" Height="40" Margin="0,12,0,0" x:Name="CardFabricApi" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanFabricApi" /> <StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanFabricApi" />
@@ -91,13 +204,25 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" /> <ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Image x:Name="ImgFabricApi" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/Fabric.png" /> <local:MyImage x:Name="ImgFabricApi" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/Fabric.png" />
<TextBlock x:Name="LabFabricApi" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" /> <TextBlock x:Name="LabFabricApi" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" />
</Grid> </Grid>
<Grid x:Name="BtnFabricApiClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}"> <Grid x:Name="PanFabricApiCompare" Height="18" Margin="132,11,80,0" VerticalAlignment="Top" Visibility="Collapsed" IsHitTestVisible="False">
<Path x:Name="BtnFabricApiClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center" <Grid.ColumnDefinitions>
Data="F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z" /> <ColumnDefinition Width="180" /><ColumnDefinition Width="Auto" /><ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<DockPanel>
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/Fabric.png" DockPanel.Dock="Left" />
<TextBlock x:Name="LabFabricApiOld" Style="{StaticResource ModifyCompareText}" />
</DockPanel>
<Path Grid.Column="1" Style="{StaticResource ModifyCompareArrow}" />
<DockPanel Grid.Column="2">
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/Fabric.png" DockPanel.Dock="Left" />
<TextBlock Style="{StaticResource ModifyCompareText}" />
</DockPanel>
</Grid> </Grid>
<local:MyIconButton x:Name="BtnFabricApiClear" Style="{StaticResource ModifyClearButton}" Theme="Black" LogoScale="0.5" Logo="{StaticResource ModifyClearLogo}" />
<local:MyIconButton x:Name="BtnFabricApiRestore" Style="{StaticResource ModifyRestoreButton}" Theme="Black" LogoScale="0.55" Logo="{StaticResource ModifyRestoreLogo}" />
</local:MyCard> </local:MyCard>
<local:MyCard Title="OptiFine" Height="40" Margin="0,12,0,0" x:Name="CardOptiFine" IsSwapped="True" CanSwap="True" SwapLogoRight="True"> <local:MyCard Title="OptiFine" Height="40" Margin="0,12,0,0" x:Name="CardOptiFine" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top"> <StackPanel Margin="20,40,18,15" VerticalAlignment="Top">
@@ -111,13 +236,25 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" /> <ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Image x:Name="ImgOptiFine" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/GrassPath.png" /> <local:MyImage x:Name="ImgOptiFine" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/GrassPath.png" />
<TextBlock x:Name="LabOptiFine" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" /> <TextBlock x:Name="LabOptiFine" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" />
</Grid> </Grid>
<Grid x:Name="BtnOptiFineClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}"> <Grid x:Name="PanOptiFineCompare" Height="18" Margin="132,11,80,0" VerticalAlignment="Top" Visibility="Collapsed" IsHitTestVisible="False">
<Path x:Name="BtnOptiFineClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center" <Grid.ColumnDefinitions>
Data="F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z" /> <ColumnDefinition Width="180" /><ColumnDefinition Width="Auto" /><ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<DockPanel>
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/GrassPath.png" DockPanel.Dock="Left" />
<TextBlock x:Name="LabOptiFineOld" Style="{StaticResource ModifyCompareText}" />
</DockPanel>
<Path Grid.Column="1" Style="{StaticResource ModifyCompareArrow}" />
<DockPanel Grid.Column="2">
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/GrassPath.png" DockPanel.Dock="Left" />
<TextBlock Style="{StaticResource ModifyCompareText}" />
</DockPanel>
</Grid> </Grid>
<local:MyIconButton x:Name="BtnOptiFineClear" Style="{StaticResource ModifyClearButton}" Theme="Black" LogoScale="0.5" Logo="{StaticResource ModifyClearLogo}" />
<local:MyIconButton x:Name="BtnOptiFineRestore" Style="{StaticResource ModifyRestoreButton}" Theme="Black" LogoScale="0.55" Logo="{StaticResource ModifyRestoreLogo}" />
</local:MyCard> </local:MyCard>
<local:MyCard Title="OptiFabric" Height="40" Margin="0,12,0,0" x:Name="CardOptiFabric" IsSwapped="True" CanSwap="True" SwapLogoRight="True"> <local:MyCard Title="OptiFabric" Height="40" Margin="0,12,0,0" x:Name="CardOptiFabric" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanOptiFabric" /> <StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanOptiFabric" />
@@ -129,13 +266,25 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" /> <ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Image x:Name="ImgOptiFabric" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="HighQuality" Source="pack://application:,,,/images/Blocks/OptiFabric.png" /> <local:MyImage x:Name="ImgOptiFabric" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="HighQuality" Source="pack://application:,,,/images/Blocks/OptiFabric.png" />
<TextBlock x:Name="LabOptiFabric" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" /> <TextBlock x:Name="LabOptiFabric" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" />
</Grid> </Grid>
<Grid x:Name="BtnOptiFabricClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}"> <Grid x:Name="PanOptiFabricCompare" Height="18" Margin="132,11,80,0" VerticalAlignment="Top" Visibility="Collapsed" IsHitTestVisible="False">
<Path x:Name="BtnOptiFabricClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center" <Grid.ColumnDefinitions>
Data="F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z" /> <ColumnDefinition Width="180" /><ColumnDefinition Width="Auto" /><ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<DockPanel>
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/OptiFabric.png" DockPanel.Dock="Left" />
<TextBlock x:Name="LabOptiFabricOld" Style="{StaticResource ModifyCompareText}" />
</DockPanel>
<Path Grid.Column="1" Style="{StaticResource ModifyCompareArrow}" />
<DockPanel Grid.Column="2">
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/OptiFabric.png" DockPanel.Dock="Left" />
<TextBlock Style="{StaticResource ModifyCompareText}" />
</DockPanel>
</Grid> </Grid>
<local:MyIconButton x:Name="BtnOptiFabricClear" Style="{StaticResource ModifyClearButton}" Theme="Black" LogoScale="0.5" Logo="{StaticResource ModifyClearLogo}" />
<local:MyIconButton x:Name="BtnOptiFabricRestore" Style="{StaticResource ModifyRestoreButton}" Theme="Black" LogoScale="0.55" Logo="{StaticResource ModifyRestoreLogo}" />
</local:MyCard> </local:MyCard>
<local:MyCard Title="LiteLoader" Height="40" Margin="0,12,0,0" x:Name="CardLiteLoader" IsSwapped="True" CanSwap="True" SwapLogoRight="True"> <local:MyCard Title="LiteLoader" Height="40" Margin="0,12,0,0" x:Name="CardLiteLoader" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanLiteLoader" /> <StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanLiteLoader" />
@@ -147,13 +296,25 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" /> <ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Image x:Name="ImgLiteLoader" Margin="-1,0,6,0" SnapsToDevicePixels="True" Height="20" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/Egg.png" /> <local:MyImage x:Name="ImgLiteLoader" Margin="-1,0,6,0" SnapsToDevicePixels="True" Height="20" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/Egg.png" />
<TextBlock x:Name="LabLiteLoader" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" /> <TextBlock x:Name="LabLiteLoader" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" />
</Grid> </Grid>
<Grid x:Name="BtnLiteLoaderClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}"> <Grid x:Name="PanLiteLoaderCompare" Height="18" Margin="132,11,80,0" VerticalAlignment="Top" Visibility="Collapsed" IsHitTestVisible="False">
<Path x:Name="BtnLiteLoaderClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center" <Grid.ColumnDefinitions>
Data="F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z" /> <ColumnDefinition Width="180" /><ColumnDefinition Width="Auto" /><ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<DockPanel>
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/Egg.png" DockPanel.Dock="Left" />
<TextBlock x:Name="LabLiteLoaderOld" Style="{StaticResource ModifyCompareText}" />
</DockPanel>
<Path Grid.Column="1" Style="{StaticResource ModifyCompareArrow}" />
<DockPanel Grid.Column="2">
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/Egg.png" DockPanel.Dock="Left" />
<TextBlock Style="{StaticResource ModifyCompareText}" />
</DockPanel>
</Grid> </Grid>
<local:MyIconButton x:Name="BtnLiteLoaderClear" Style="{StaticResource ModifyClearButton}" Theme="Black" LogoScale="0.5" Logo="{StaticResource ModifyClearLogo}" />
<local:MyIconButton x:Name="BtnLiteLoaderRestore" Style="{StaticResource ModifyRestoreButton}" Theme="Black" LogoScale="0.55" Logo="{StaticResource ModifyRestoreLogo}" />
</local:MyCard> </local:MyCard>
</StackPanel> </StackPanel>
</Grid> </Grid>
@@ -163,7 +324,7 @@
LogoScale="0.95" Logo="M955 610h-59c-15 0-29 13-29 29v196c0 15-13 29-29 29h-649c-15 0-29-13-29-29v-196c0-15-13-29-29-29h-59c-15 0-29 13-29 29V905c0 43 35 78 78 78h787c43 0 78-35 78-78V640c0-15-13-29-29-29zM492 740c11 11 29 11 41 0l265-265c11-11 11-29 0-41l-41-41c-11-11-29-11-41 0l-110 110c-11 11-33 3-33-13V68C571 53 555 39 541 39h-59c-15 0-29 13-29 29v417c0 17-21 25-33 13l-110-110c-11-11-29-11-41 0L226 433c-11 11-11 29 0 41L492 740z" /> LogoScale="0.95" Logo="M955 610h-59c-15 0-29 13-29 29v196c0 15-13 29-29 29h-649c-15 0-29-13-29-29v-196c0-15-13-29-29-29h-59c-15 0-29 13-29 29V905c0 43 35 78 78 78h787c43 0 78-35 78-78V640c0-15-13-29-29-29zM492 740c11 11 29 11 41 0l265-265c11-11 11-29 0-41l-41-41c-11-11-29-11-41 0l-110 110c-11 11-33 3-33-13V68C571 53 555 39 541 39h-59c-15 0-29 13-29 29v417c0 17-21 25-33 13l-110-110c-11-11-29-11-41 0L226 433c-11 11-11 29 0 41L492 740z" />
</Grid> </Grid>
<local:MyCard HorizontalAlignment="Center" VerticalAlignment="Center" Margin="40,0" SnapsToDevicePixels="True" x:Name="PanLoad" UseAnimation="False"> <local:MyCard HorizontalAlignment="Center" VerticalAlignment="Center" Margin="40,0" SnapsToDevicePixels="True" x:Name="PanLoad" UseAnimation="False">
<local:MyLoading Text="正在获取版本列表" Margin="20,20,20,17" x:Name="LoadMinecraft" HorizontalAlignment="Center" VerticalAlignment="Center" /> <local:MyLoading Text="正在加载" Margin="20,20,20,17" x:Name="LoadMinecraft" HorizontalAlignment="Center" VerticalAlignment="Center" ShowProgress="True" />
<local:MyLoading Visibility="Collapsed" x:Name="LoadOptiFine" HasAnimation="False" /> <local:MyLoading Visibility="Collapsed" x:Name="LoadOptiFine" HasAnimation="False" />
<local:MyLoading Visibility="Collapsed" x:Name="LoadForge" HasAnimation="False" /> <local:MyLoading Visibility="Collapsed" x:Name="LoadForge" HasAnimation="False" />
<local:MyLoading Visibility="Collapsed" x:Name="LoadNeoForge" HasAnimation="False" /> <local:MyLoading Visibility="Collapsed" x:Name="LoadNeoForge" HasAnimation="False" />
@@ -1,12 +1,34 @@
Public Class PageDownloadInstall Public Class PageDownloadInstall
Private Sub LoaderInit() Handles Me.Initialized Public Sub ControlInit()
DisabledPageAnimControls.Add(BtnStart) DisabledPageAnimControls.Add(BtnStart)
LoadOptiFine.State = DlOptiFineListLoader
LoadLiteLoader.State = DlLiteLoaderListLoader
LoadFabric.State = DlFabricListLoader
LoadFabricApi.State = DlFabricApiLoader
LoadNeoForge.State = DlNeoForgeListLoader
LoadOptiFabric.State = DlOptiFabricLoader
McDownloadForgeRecommendedRefresh()
End Sub
Public Overridable Sub LoaderInit() Handles Me.Initialized
ControlInit()
If RestrictMcDownload Then
PageLoaderInit(LoadMinecraft, PanLoad, PanAllBack, Nothing, New LoaderTask(Of String, DlClientListResult)("关闭下载", Sub() Throw New Exception("为遵守相关法律法规,PCL 暂时不再提供 MC 下载功能,非常抱歉 qwq……")), AddressOf LoadMinecraft_OnFinish)
Else
PageLoaderInit(LoadMinecraft, PanLoad, PanAllBack, Nothing, DlClientListLoader, AddressOf LoadMinecraft_OnFinish) PageLoaderInit(LoadMinecraft, PanLoad, PanAllBack, Nothing, DlClientListLoader, AddressOf LoadMinecraft_OnFinish)
End If
End Sub End Sub
Private IsLoad As Boolean = False Private IsLoad As Boolean = False
Private Sub Init() Handles Me.Loaded Public Overridable Sub Init() Handles Me.Loaded
If IsLoad Then
IsSelectNameEdited = False
ExitSelectPage()
If DlClientListLoader.State = LoadState.Finished Then TrySelectWaitingVersion()
End If
IsLoad = True
PanBack.ScrollToHome() PanBack.ScrollToHome()
DlOptiFineListLoader.Start() DlOptiFineListLoader.Start()
DlLiteLoaderListLoader.Start() DlLiteLoaderListLoader.Start()
@@ -17,35 +39,19 @@ Public Class PageDownloadInstall
TextSelectName.ValidateRules = New ObjectModel.Collection(Of Validate) From {New ValidateFolderName(McFolderSelected & "versions")} TextSelectName.ValidateRules = New ObjectModel.Collection(Of Validate) From {New ValidateFolderName(McFolderSelected & "versions")}
TextSelectName.Validate() TextSelectName.Validate()
ReloadSelected() ReloadSelected()
'非重复加载部分
If IsLoad Then Return
IsLoad = True
McDownloadForgeRecommendedRefresh()
LoadOptiFine.State = DlOptiFineListLoader
LoadLiteLoader.State = DlLiteLoaderListLoader
LoadFabric.State = DlFabricListLoader
LoadFabricApi.State = DlFabricApiLoader
LoadNeoForge.State = DlNeoForgeListLoader
LoadOptiFabric.State = DlOptiFabricLoader
End Sub End Sub
#Region "页面切换" #Region "页面切换"
'页面切换动画 '页面切换动画
Public IsInSelectPage As Boolean = False Public IsInSelectPage As Boolean = False
Private IsFirstLoaded As Boolean = False
Private Sub EnterSelectPage() ''' <summary>
If IsInSelectPage Then Return ''' 选择页面 UI 初始化。
''' </summary>
Public Sub PrepareSelectPage()
IsInSelectPage = True IsInSelectPage = True
PanInner.Margin = New Thickness(25, 10, 25, 40) PanInner.Margin = New Thickness(25, 10, 25, 40)
AutoSelectedFabricApi = False
AutoSelectedOptiFabric = False
IsSelectNameEdited = False
PanSelect.Visibility = Visibility.Visible PanSelect.Visibility = Visibility.Visible
PanSelect.IsHitTestVisible = True PanSelect.IsHitTestVisible = True
PanMinecraft.IsHitTestVisible = False PanMinecraft.IsHitTestVisible = False
@@ -54,13 +60,9 @@ Public Class PageDownloadInstall
DisabledPageAnimControls.Remove(BtnStart) DisabledPageAnimControls.Remove(BtnStart)
BtnStart.Show = True BtnStart.Show = True
CardOptiFine.IsSwapped = True For Each Card In {CardOptiFine, CardLiteLoader, CardForge, CardNeoForge, CardFabric, CardFabricApi, CardOptiFabric}
CardLiteLoader.IsSwapped = True Card.IsSwapped = True
CardForge.IsSwapped = True Next
CardNeoForge.IsSwapped = True
CardFabric.IsSwapped = True
CardFabricApi.IsSwapped = True
CardOptiFabric.IsSwapped = True
'如果在选择页面按了刷新键,选择页的东西可能会由于动画被隐藏,但不会由于加载结束而再次显示,因此这里需要手动恢复 '如果在选择页面按了刷新键,选择页的东西可能会由于动画被隐藏,但不会由于加载结束而再次显示,因此这里需要手动恢复
For Each Control In GetAllAnimControls(PanSelect) For Each Control In GetAllAnimControls(PanSelect)
@@ -69,9 +71,16 @@ Public Class PageDownloadInstall
Control.RenderTransform = New TranslateTransform Control.RenderTransform = New TranslateTransform
End If End If
Next Next
End Sub
Private Sub EnterSelectPage()
If IsInSelectPage Then Return
AutoSelectedFabricApi = False
AutoSelectedOptiFabric = False
PrepareSelectPage()
'启动 Forge 加载 '启动 Forge 加载
If McVersion.IsFormatFit(VanillaName) Then If McVersion.IsStandardFormat(VanillaName) Then
Dim ForgeLoader = New LoaderTask(Of String, List(Of DlForgeVersionEntry))("DlForgeVersion " & VanillaName, AddressOf DlForgeVersionMain) Dim ForgeLoader = New LoaderTask(Of String, List(Of DlForgeVersionEntry))("DlForgeVersion " & VanillaName, AddressOf DlForgeVersionMain)
LoadForge.State = ForgeLoader LoadForge.State = ForgeLoader
ForgeLoader.Start(VanillaName) ForgeLoader.Start(VanillaName)
@@ -87,6 +96,16 @@ Public Class PageDownloadInstall
AaCode( AaCode(
Sub() Sub()
PanBack.ScrollToHome() PanBack.ScrollToHome()
ReloadComponentLists()
PanMinecraft.Visibility = Visibility.Collapsed
End Sub, After:=True),
AaOpacity(PanSelect, 1 - PanSelect.Opacity, 70, 100),
AaTranslateX(PanSelect, -CType(PanSelect.RenderTransform, TranslateTransform).X, 160, 100, Ease:=New AniEaseOutFluent(AniEasePower.ExtraStrong)),
AaCode(Sub() PanBack.IsHitTestVisible = True,, True)
}, "FrmDownloadInstall SelectPageSwitch", True)
End Sub
Public Sub ReloadComponentLists()
TextSelectName.Validate() TextSelectName.Validate()
OptiFine_Loaded() OptiFine_Loaded()
LiteLoader_Loaded() LiteLoader_Loaded()
@@ -96,26 +115,8 @@ Public Class PageDownloadInstall
FabricApi_Loaded() FabricApi_Loaded()
OptiFabric_Loaded() OptiFabric_Loaded()
ReloadSelected() ReloadSelected()
PanMinecraft.Visibility = Visibility.Collapsed
End Sub, After:=True),
AaOpacity(PanSelect, 1 - PanSelect.Opacity, 70, 100),
AaTranslateX(PanSelect, -CType(PanSelect.RenderTransform, TranslateTransform).X, 160, 100, Ease:=New AniEaseOutFluent(AniEasePower.ExtraStrong)),
AaCode(
Sub()
PanBack.IsHitTestVisible = True
'初始化 Binding
If IsFirstLoaded Then Return
IsFirstLoaded = True
BtnOptiFineClearInner.SetBinding(Shapes.Path.FillProperty, New Binding("Foreground") With {.Source = CardOptiFine.MainTextBlock, .Mode = BindingMode.OneWay})
BtnLiteLoaderClearInner.SetBinding(Shapes.Path.FillProperty, New Binding("Foreground") With {.Source = CardLiteLoader.MainTextBlock, .Mode = BindingMode.OneWay})
BtnForgeClearInner.SetBinding(Shapes.Path.FillProperty, New Binding("Foreground") With {.Source = CardForge.MainTextBlock, .Mode = BindingMode.OneWay})
BtnNeoForgeClearInner.SetBinding(Shapes.Path.FillProperty, New Binding("Foreground") With {.Source = CardNeoForge.MainTextBlock, .Mode = BindingMode.OneWay})
BtnFabricClearInner.SetBinding(Shapes.Path.FillProperty, New Binding("Foreground") With {.Source = CardFabric.MainTextBlock, .Mode = BindingMode.OneWay})
BtnFabricApiClearInner.SetBinding(Shapes.Path.FillProperty, New Binding("Foreground") With {.Source = CardFabricApi.MainTextBlock, .Mode = BindingMode.OneWay})
BtnOptiFabricClearInner.SetBinding(Shapes.Path.FillProperty, New Binding("Foreground") With {.Source = CardOptiFabric.MainTextBlock, .Mode = BindingMode.OneWay})
End Sub,, True)
}, "FrmDownloadInstall SelectPageSwitch", True)
End Sub End Sub
Public Sub ExitSelectPage() Handles BtnBack.Click Public Sub ExitSelectPage() Handles BtnBack.Click
If Not IsInSelectPage Then Return If Not IsInSelectPage Then Return
IsInSelectPage = False IsInSelectPage = False
@@ -144,10 +145,11 @@ Public Class PageDownloadInstall
End Sub,, True) End Sub,, True)
}, "FrmDownloadInstall SelectPageSwitch") }, "FrmDownloadInstall SelectPageSwitch")
End Sub End Sub
Public Sub MinecraftSelected(sender As MyListItem, e As MouseButtonEventArgs) Public Overridable Sub MinecraftSelected(sender As MyListItem, e As MouseButtonEventArgs)
VanillaName = sender.Title VanillaName = sender.Title
VanillaData = sender.Tag VanillaData = sender.Tag
VanillaIcon = sender.Logo VanillaIcon = sender.Logo
LabMinecraftVersion.Text = VanillaName
EnterSelectPage() EnterSelectPage()
End Sub End Sub
@@ -156,9 +158,9 @@ Public Class PageDownloadInstall
#Region "选择" #Region "选择"
'Minecraft 'Minecraft
Private VanillaName As String Public VanillaName As String
Private VanillaData As JObject Public VanillaData As JObject
Private VanillaIcon As String Public VanillaIcon As String
Private ReadOnly Property VanillaDrop As Integer Private ReadOnly Property VanillaDrop As Integer
Get Get
Return McVersion.VersionToDrop(VanillaName) Return McVersion.VersionToDrop(VanillaName)
@@ -166,24 +168,25 @@ Public Class PageDownloadInstall
End Property End Property
'附加组件 '附加组件
Private SelectedOptiFine As DlOptiFineListEntry = Nothing Public SelectedOptiFine As DlOptiFineListEntry = Nothing
Private SelectedLiteLoader As DlLiteLoaderListEntry = Nothing Public SelectedLiteLoader As DlLiteLoaderListEntry = Nothing
Private SelectedForge As DlForgeVersionEntry = Nothing Public SelectedForge As DlForgeVersionEntry = Nothing
Private SelectedNeoForge As DlNeoForgeListEntry = Nothing Public SelectedNeoForge As DlNeoForgeListEntry = Nothing
Private SelectedFabric As String = Nothing Public SelectedFabric As String = Nothing
Private SelectedFabricApi As ResourceVersion = Nothing Public SelectedFabricApi As ResourceVersion = Nothing
Private SelectedOptiFabric As ResourceVersion = Nothing Public SelectedOptiFabric As ResourceVersion = Nothing
''' <summary> ''' <summary>
''' 重载已选择的项目的显示。 ''' 重载已选择的项目的显示。
''' </summary> ''' </summary>
Private Sub ReloadSelected() Handles CardOptiFine.Swap, LoadOptiFine.StateChanged, CardForge.Swap, LoadForge.StateChanged, CardNeoForge.Swap, LoadNeoForge.StateChanged, CardFabric.Swap, LoadFabric.StateChanged, CardFabricApi.Swap, LoadFabricApi.StateChanged, CardOptiFabric.Swap, LoadOptiFabric.StateChanged, CardLiteLoader.Swap, LoadLiteLoader.StateChanged Public Overridable Sub ReloadSelected() Handles CardOptiFine.Swap, LoadOptiFine.StateChanged, CardForge.Swap, LoadForge.StateChanged, CardNeoForge.Swap, LoadNeoForge.StateChanged, CardFabric.Swap, LoadFabric.StateChanged, CardFabricApi.Swap, LoadFabricApi.StateChanged, CardOptiFabric.Swap, LoadOptiFabric.StateChanged, CardLiteLoader.Swap, LoadLiteLoader.StateChanged
Static Ongoing As Boolean = False '#3742 中,LoadOptiFineGetError 会初始化 LoadOptiFine,触发事件 LoadOptiFine.StateChanged,导致再次调用 SelectReload Static Ongoing As Boolean = False '#3742 中,LoadOptiFineGetError 会初始化 LoadOptiFine,触发事件 LoadOptiFine.StateChanged,导致再次调用 SelectReload
If VanillaName Is Nothing OrElse Ongoing Then Return If VanillaName Is Nothing OrElse Ongoing Then Return
Ongoing = True Ongoing = True
'主预览 '主预览
SelectNameUpdate() SelectNameUpdate()
ImgLogo.Source = GetSelectLogo() ImgLogo.Source = GetSelectLogo()
ImgMinecraft.Source = VanillaIcon
'OptiFine 'OptiFine
Dim OptiFineError As String = LoadOptiFineGetError() Dim OptiFineError As String = LoadOptiFineGetError()
CardOptiFine.MainSwap.Visibility = If(OptiFineError Is Nothing, Visibility.Visible, Visibility.Collapsed) CardOptiFine.MainSwap.Visibility = If(OptiFineError Is Nothing, Visibility.Visible, Visibility.Collapsed)
@@ -222,7 +225,7 @@ Public Class PageDownloadInstall
End If End If
End If End If
'Forge 'Forge
If Not McVersion.IsFormatFit(VanillaName) Then If Not McVersion.IsStandardFormat(VanillaName) Then
CardForge.Visibility = Visibility.Collapsed CardForge.Visibility = Visibility.Collapsed
Else Else
CardForge.Visibility = Visibility.Visible CardForge.Visibility = Visibility.Visible
@@ -356,7 +359,7 @@ Public Class PageDownloadInstall
''' <summary> ''' <summary>
''' 清空已选择的项目。 ''' 清空已选择的项目。
''' </summary> ''' </summary>
Private Sub ClearSelected() Public Sub ClearSelected()
VanillaName = Nothing VanillaName = Nothing
VanillaData = Nothing VanillaData = Nothing
VanillaIcon = Nothing VanillaIcon = Nothing
@@ -367,7 +370,10 @@ Public Class PageDownloadInstall
SelectedFabric = Nothing SelectedFabric = Nothing
SelectedFabricApi = Nothing SelectedFabricApi = Nothing
SelectedOptiFabric = Nothing SelectedOptiFabric = Nothing
IsSelectNameEdited = False For Each ClearButton In {BtnOptiFineClear, BtnLiteLoaderClear, BtnForgeClear, BtnNeoForgeClear,
BtnFabricClear, BtnFabricApiClear, BtnOptiFabricClear}
ClearButton.Visibility = Visibility.Collapsed
Next
End Sub End Sub
'信息栏动画 '信息栏动画
Private Sub SetPanelVisibility(Panel As Grid, IsVisible As Boolean) Private Sub SetPanelVisibility(Panel As Grid, IsVisible As Boolean)
@@ -389,7 +395,7 @@ Public Class PageDownloadInstall
''' <summary> ''' <summary>
''' 获取版本图标。 ''' 获取版本图标。
''' </summary> ''' </summary>
Private Function GetSelectLogo() As String Public Overridable Function GetSelectLogo() As String
If SelectedFabric IsNot Nothing Then If SelectedFabric IsNot Nothing Then
Return "pack://application:,,,/images/Blocks/Fabric.png" Return "pack://application:,,,/images/Blocks/Fabric.png"
ElseIf SelectedForge IsNot Nothing Then ElseIf SelectedForge IsNot Nothing Then
@@ -409,7 +415,7 @@ Public Class PageDownloadInstall
''' <summary> ''' <summary>
''' 获取默认版本名。 ''' 获取默认版本名。
''' </summary> ''' </summary>
Private Function GetSelectName() As String Public Function GetSelectName() As String
Dim Name As String = VanillaName Dim Name As String = VanillaName
If SelectedFabric IsNot Nothing Then Name += "-Fabric " & SelectedFabric.BeforeFirst("+") If SelectedFabric IsNot Nothing Then Name += "-Fabric " & SelectedFabric.BeforeFirst("+")
If SelectedForge IsNot Nothing Then Name += "-Forge_" & SelectedForge.VersionName If SelectedForge IsNot Nothing Then Name += "-Forge_" & SelectedForge.VersionName
@@ -418,36 +424,39 @@ Public Class PageDownloadInstall
If SelectedOptiFine IsNot Nothing Then Name += "-OptiFine_" & SelectedOptiFine.DisplayName.Replace(VanillaName & " ", "").Replace(" ", "_") If SelectedOptiFine IsNot Nothing Then Name += "-OptiFine_" & SelectedOptiFine.DisplayName.Replace(VanillaName & " ", "").Replace(" ", "_")
Return Name Return Name
End Function End Function
Private IsSelectNameEdited As Boolean = False Public IsSelectNameEdited As Boolean = False
Private IsSelectNameChanging As Boolean = False
Private Sub SelectNameUpdate() Private Sub SelectNameUpdate()
If IsSelectNameEdited OrElse IsSelectNameChanging Then Return If IsSelectNameEdited Then Return
IsSelectNameChanging = True
TextSelectName.Text = GetSelectName() TextSelectName.Text = GetSelectName()
IsSelectNameChanging = False IsSelectNameEdited = False
End Sub End Sub
Private Sub TextSelectName_TextChanged(sender As Object, e As TextChangedEventArgs) Handles TextSelectName.TextChanged Private Sub TextSelectName_TextChanged(sender As Object, e As TextChangedEventArgs) Handles TextSelectName.TextChanged
If IsSelectNameChanging Then Return
IsSelectNameEdited = True IsSelectNameEdited = True
ReloadSelected()
End Sub End Sub
Private Sub TextSelectName_ValidateChanged(sender As Object, e As EventArgs) Handles TextSelectName.ValidateChanged Private Sub TextSelectName_ValidateChanged(sender As Object, e As EventArgs) Handles TextSelectName.ValidateChanged
BtnStart.IsEnabled = TextSelectName.IsValidated BtnStart.IsEnabled = TextSelectName.IsValidated
HintSelectName.Text = TextSelectName.ValidateResult
HintSelectName.Visibility = If(TextSelectName.IsValidated, Visibility.Collapsed, Visibility.Visible)
End Sub End Sub
#End Region #End Region
#Region "加载器" #Region "加载器"
Public MinecraftVersions As JArray
Private Sub LoadMinecraft_OnFinish()
MinecraftVersions = CType(DlClientListLoader.Output.Value("versions").DeepClone(), JArray)
ShowMinecraftVersionList()
TrySelectWaitingVersion() '自动选择版本
End Sub
'结果数据化 '结果数据化
Private Sub LoadMinecraft_OnFinish() Public Overridable Sub ShowMinecraftVersionList()
ExitSelectPage() '返回 ExitSelectPage() '返回
Try Try
Dim Dict As New Dictionary(Of String, List(Of JObject)) From { Dim Dict As New Dictionary(Of String, List(Of JObject)) From {
{"正式版", New List(Of JObject)}, {"预览版", New List(Of JObject)}, {"远古版", New List(Of JObject)}, {"愚人节版", New List(Of JObject)} {"正式版", New List(Of JObject)}, {"预览版", New List(Of JObject)}, {"远古版", New List(Of JObject)}, {"愚人节版", New List(Of JObject)}
} }
Dim Versions As JArray = DlClientListLoader.Output.Value("versions") For Each Version As JObject In MinecraftVersions
For Each Version As JObject In Versions
'确定分类 '确定分类
Dim Type As String = Version("type") Dim Type As String = Version("type")
Select Case Type Select Case Type
@@ -498,7 +507,7 @@ Public Class PageDownloadInstall
'清空当前 '清空当前
PanMinecraft.Children.Clear() PanMinecraft.Children.Clear()
'添加最新版本 '添加最新版本
Dim CardInfo As New MyCard With {.Title = "最新版本", .Margin = New Thickness(0, 15, 0, 15), .SwapType = 2} Dim CardInfo As New MyCard With {.Title = "最新版本", .Margin = New Thickness(0, 15, 0, 15)}
Dim PinnedVersions As New List(Of JObject) Dim PinnedVersions As New List(Of JObject)
Dim Release As JObject = Dict("正式版")(0).DeepClone() Dim Release As JObject = Dict("正式版")(0).DeepClone()
Release("lore") = "最新正式版,发布于 " & Release("releaseTime").Value(Of Date).ToString("yyyy'/'MM'/'dd HH':'mm") Release("lore") = "最新正式版,发布于 " & Release("releaseTime").Value(Of Date).ToString("yyyy'/'MM'/'dd HH':'mm")
@@ -508,36 +517,41 @@ Public Class PageDownloadInstall
Snapshot("lore") = "最新预览版,发布于 " & Snapshot("releaseTime").Value(Of Date).ToString("yyyy'/'MM'/'dd HH':'mm") Snapshot("lore") = "最新预览版,发布于 " & Snapshot("releaseTime").Value(Of Date).ToString("yyyy'/'MM'/'dd HH':'mm")
PinnedVersions.Add(Snapshot) PinnedVersions.Add(Snapshot)
End If End If
Dim PanInfo As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0), .Tag = PinnedVersions} Dim PanInfo As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0)}
MyCard.StackInstall(PanInfo, 7)
CardInfo.Children.Add(PanInfo) CardInfo.Children.Add(PanInfo)
CardInfo.SwapControl = PanInfo
CardInfo.SetContentBuilder(PinnedVersions, '使用 Lambda 转发点击事件,避免 #535
Function(Version) McDownloadListItem(Version, Sub(sender, e) MinecraftSelected(sender, e), False))
CardInfo.BuildContent()
PanMinecraft.Children.Insert(0, CardInfo) PanMinecraft.Children.Insert(0, CardInfo)
'添加其他版本 '添加其他版本
For Each Pair As KeyValuePair(Of String, List(Of JObject)) In Dict For Each Pair As KeyValuePair(Of String, List(Of JObject)) In Dict
If Not Pair.Value.Any() Then Continue For If Not Pair.Value.Any() Then Continue For
'增加卡片 '增加卡片
Dim NewCard As New MyCard With {.Title = Pair.Key, .Margin = New Thickness(0, 0, 0, 15), .SwapType = 7} Dim NewCard As New MyCard With {.Title = Pair.Key, .Margin = New Thickness(0, 0, 0, 15)}
Dim NewStack As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0), .Tag = Pair.Value} Dim NewStack As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0)}
NewCard.Children.Add(NewStack) NewCard.Children.Add(NewStack)
NewCard.SwapControl = NewStack NewCard.SwapControl = NewStack
NewCard.SetContentBuilder(Pair.Value,
Function(Version) McDownloadListItem(Version, Sub(sender, e) MinecraftSelected(sender, e), False))
NewCard.IsSwapped = True NewCard.IsSwapped = True
PanMinecraft.Children.Add(NewCard) PanMinecraft.Children.Add(NewCard)
Next Next
'自动选择版本
If VersionWaitingSelect Is Nothing Then Exit Try
Logger.Info($"自动选择 MC 版本:{VersionWaitingSelect}")
For Each Version As JObject In Versions
If Version("id").ToString <> VersionWaitingSelect Then Continue For
Dim Item = McDownloadListItem(Version, Sub()
End Sub, False).Init()
MinecraftSelected(Item, Nothing)
VersionWaitingSelect = Nothing
Return
Next
Catch ex As Exception Catch ex As Exception
Logger.Error(ex, "可视化安装版本列表出错") Logger.Error(ex, "可视化安装版本列表出错")
End Try End Try
End Sub End Sub
Private Sub TrySelectWaitingVersion()
If VersionWaitingSelect Is Nothing Then Return
For Each Version As JObject In MinecraftVersions
If Version("id").ToString <> VersionWaitingSelect Then Continue For
Logger.Info($"自动选择 MC 版本:{VersionWaitingSelect}")
MinecraftSelected(McDownloadListItem(Version, Nothing, False).Init(), Nothing)
VersionWaitingSelect = Nothing
Return
Next
End Sub
''' <summary> ''' <summary>
''' 当 MC 版本列表加载完时,立即自动选择的版本。用于外部调用。 ''' 当 MC 版本列表加载完时,立即自动选择的版本。用于外部调用。
''' </summary> ''' </summary>
@@ -550,16 +564,16 @@ Public Class PageDownloadInstall
''' <summary> ''' <summary>
''' 获取 OptiFine 的加载异常信息。若正常则返回 Nothing。 ''' 获取 OptiFine 的加载异常信息。若正常则返回 Nothing。
''' </summary> ''' </summary>
Private Function LoadOptiFineGetError() As String Public Function LoadOptiFineGetError() As String
If SelectedNeoForge IsNot Nothing Then Return "与 NeoForge 不兼容" If SelectedNeoForge IsNot Nothing Then Return "与 NeoForge 不兼容"
'检查 Forge 1.13 - 1.14.3:全部不兼容 '检查 Forge 1.13 - 1.14.3:全部不兼容
If SelectedForge IsNot Nothing AndAlso If SelectedForge IsNot Nothing AndAlso
CompareVersion(VanillaName, "1.13") >= 0 AndAlso CompareVersion("1.14.3", VanillaName) >= 0 Then McUtils.CompareVersionGE(VanillaName, "1.13") AndAlso McUtils.CompareVersionGE("1.14.3", VanillaName) Then
Return "与 Forge 不兼容" Return "与 Forge 不兼容"
End If End If
'检查 Fabric 1.20.5+:全部不兼容 '检查 Fabric 1.20.5+:全部不兼容
If SelectedFabric IsNot Nothing AndAlso If SelectedFabric IsNot Nothing AndAlso
CompareVersion(VanillaName, "1.20.4") > 0 Then McUtils.CompareVersion(VanillaName, "1.20.4") > 0 Then
Return "与 Fabric 不兼容" Return "与 Fabric 不兼容"
End If End If
'检查 Loader '检查 Loader
@@ -584,12 +598,12 @@ Public Class PageDownloadInstall
End Function End Function
'检查某个 OptiFine 是否与某个 Forge 兼容 '检查某个 OptiFine 是否与某个 Forge 兼容
Private Function IsOptiFineSuitForForge(OptiFine As DlOptiFineListEntry, Forge As DlForgeVersionEntry) Public Shared Function IsOptiFineSuitForForge(OptiFine As DlOptiFineListEntry, Forge As DlForgeVersionEntry) As Boolean
If Forge.Inherit <> OptiFine.Inherit Then Return False '不是同一个大版本 If Forge.Inherit <> OptiFine.Inherit Then Return False '不是同一个大版本
If OptiFine.RequiredForgeVersion Is Nothing Then Return False '不兼容 Forge If OptiFine.RequiredForgeVersion Is Nothing Then Return False '不兼容 Forge
If String.IsNullOrWhiteSpace(OptiFine.RequiredForgeVersion) Then Return True '#4183 If String.IsNullOrWhiteSpace(OptiFine.RequiredForgeVersion) Then Return True '#4183
If OptiFine.RequiredForgeVersion.Contains(".") Then 'XX.X.XXX If OptiFine.RequiredForgeVersion.Contains(".") Then 'XX.X.XXX
Return CompareVersion(Forge.Version.ToString, OptiFine.RequiredForgeVersion) = 0 Return McUtils.CompareVersion(Forge.Version.ToString, OptiFine.RequiredForgeVersion) = 0
Else 'XXXX Else 'XXXX
Return Forge.Version.Revision = OptiFine.RequiredForgeVersion Return Forge.Version.Revision = OptiFine.RequiredForgeVersion
End If End If
@@ -619,7 +633,7 @@ Public Class PageDownloadInstall
Function(Left As DlOptiFineListEntry, Right As DlOptiFineListEntry) As Boolean Function(Left As DlOptiFineListEntry, Right As DlOptiFineListEntry) As Boolean
If Not Left.IsPreview AndAlso Right.IsPreview Then Return True If Not Left.IsPreview AndAlso Right.IsPreview Then Return True
If Left.IsPreview AndAlso Not Right.IsPreview Then Return False If Left.IsPreview AndAlso Not Right.IsPreview Then Return False
Return CompareVersionGE(Left.DisplayName, Right.DisplayName) Return McUtils.CompareVersionGE(Left.DisplayName, Right.DisplayName)
End Function) End Function)
'可视化 '可视化
PanOptiFine.Children.Clear() PanOptiFine.Children.Clear()
@@ -641,12 +655,12 @@ Public Class PageDownloadInstall
CardOptiFine.IsSwapped = True CardOptiFine.IsSwapped = True
ReloadSelected() ReloadSelected()
End Sub End Sub
Private Sub OptiFine_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnOptiFineClear.MouseLeftButtonUp Private Sub OptiFine_Clear(sender As Object, e As EventArgs) Handles BtnOptiFineClear.Click
SelectedOptiFine = Nothing SelectedOptiFine = Nothing
SelectedOptiFabric = Nothing SelectedOptiFabric = Nothing
BtnOptiFabricClear.Visibility = Visibility.Collapsed
AutoSelectedOptiFabric = False AutoSelectedOptiFabric = False
CardOptiFine.IsSwapped = True CardOptiFine.IsSwapped = True
e.Handled = True
Forge_Loaded() Forge_Loaded()
NeoForge_Loaded() NeoForge_Loaded()
ReloadSelected() ReloadSelected()
@@ -659,7 +673,7 @@ Public Class PageDownloadInstall
''' <summary> ''' <summary>
''' 获取 LiteLoader 的加载异常信息。若正常则返回 Nothing。 ''' 获取 LiteLoader 的加载异常信息。若正常则返回 Nothing。
''' </summary> ''' </summary>
Private Function LoadLiteLoaderGetError() As String Public Function LoadLiteLoaderGetError() As String
'检查 Loader '检查 Loader
If GetLoaderError(LoadLiteLoader) IsNot Nothing Then Return GetLoaderError(LoadLiteLoader) If GetLoaderError(LoadLiteLoader) IsNot Nothing Then Return GetLoaderError(LoadLiteLoader)
'检查版本 '检查版本
@@ -699,10 +713,9 @@ Public Class PageDownloadInstall
CardLiteLoader.IsSwapped = True CardLiteLoader.IsSwapped = True
ReloadSelected() ReloadSelected()
End Sub End Sub
Private Sub LiteLoader_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnLiteLoaderClear.MouseLeftButtonUp Private Sub LiteLoader_Clear(sender As Object, e As EventArgs) Handles BtnLiteLoaderClear.Click
SelectedLiteLoader = Nothing SelectedLiteLoader = Nothing
CardLiteLoader.IsSwapped = True CardLiteLoader.IsSwapped = True
e.Handled = True
ReloadSelected() ReloadSelected()
End Sub End Sub
@@ -713,8 +726,8 @@ Public Class PageDownloadInstall
''' <summary> ''' <summary>
''' 获取 Forge 的加载异常信息。若正常则返回 Nothing。 ''' 获取 Forge 的加载异常信息。若正常则返回 Nothing。
''' </summary> ''' </summary>
Private Function LoadForgeGetError() As String Public Function LoadForgeGetError() As String
If CompareVersionGE("1.5.1", VanillaName) AndAlso CompareVersionGE(VanillaName, "1.1") Then Return "无" If McUtils.CompareVersionGE("1.5.1", VanillaName) AndAlso McUtils.CompareVersionGE(VanillaName, "1.1") Then Return "无"
'检查 Loader '检查 Loader
If GetLoaderError(LoadForge) IsNot Nothing Then Return GetLoaderError(LoadForge) If GetLoaderError(LoadForge) IsNot Nothing Then Return GetLoaderError(LoadForge)
Dim Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)) = LoadForge.State Dim Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)) = LoadForge.State
@@ -725,7 +738,7 @@ Public Class PageDownloadInstall
If SelectedNeoForge IsNot Nothing Then Return "与 NeoForge 不兼容" If SelectedNeoForge IsNot Nothing Then Return "与 NeoForge 不兼容"
If SelectedFabric IsNot Nothing Then Return "与 Fabric 不兼容" If SelectedFabric IsNot Nothing Then Return "与 Fabric 不兼容"
If SelectedOptiFine IsNot Nothing AndAlso If SelectedOptiFine IsNot Nothing AndAlso
CompareVersionGE(VanillaName, "1.13") AndAlso CompareVersionGE("1.14.3", VanillaName) Then McUtils.CompareVersionGE(VanillaName, "1.13") AndAlso McUtils.CompareVersionGE("1.14.3", VanillaName) Then
Return "与 OptiFine 不兼容" '1.13 ~ 1.14.3 OptiFine 检查 Return "与 OptiFine 不兼容" '1.13 ~ 1.14.3 OptiFine 检查
End If End If
If SelectedOptiFine IsNot Nothing AndAlso Not IsOptiFineSuitForForge(SelectedOptiFine, Version) Then Continue For If SelectedOptiFine IsNot Nothing AndAlso Not IsOptiFineSuitForForge(SelectedOptiFine, Version) Then Continue For
@@ -775,10 +788,9 @@ Public Class PageDownloadInstall
OptiFine_Loaded() OptiFine_Loaded()
ReloadSelected() ReloadSelected()
End Sub End Sub
Private Sub Forge_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnForgeClear.MouseLeftButtonUp Private Sub Forge_Clear(sender As Object, e As EventArgs) Handles BtnForgeClear.Click
SelectedForge = Nothing SelectedForge = Nothing
CardForge.IsSwapped = True CardForge.IsSwapped = True
e.Handled = True
OptiFine_Loaded() OptiFine_Loaded()
ReloadSelected() ReloadSelected()
End Sub End Sub
@@ -790,7 +802,7 @@ Public Class PageDownloadInstall
''' <summary> ''' <summary>
''' 获取 NeoForge 的加载异常信息。若正常则返回 Nothing。 ''' 获取 NeoForge 的加载异常信息。若正常则返回 Nothing。
''' </summary> ''' </summary>
Private Function LoadNeoForgeGetError() As String Public Function LoadNeoForgeGetError() As String
If SelectedOptiFine IsNot Nothing Then Return "与 OptiFine 不兼容" If SelectedOptiFine IsNot Nothing Then Return "与 OptiFine 不兼容"
If SelectedForge IsNot Nothing Then Return "与 Forge 不兼容" If SelectedForge IsNot Nothing Then Return "与 Forge 不兼容"
If SelectedFabric IsNot Nothing Then Return "与 Fabric 不兼容" If SelectedFabric IsNot Nothing Then Return "与 Fabric 不兼容"
@@ -832,10 +844,9 @@ Public Class PageDownloadInstall
OptiFine_Loaded() OptiFine_Loaded()
ReloadSelected() ReloadSelected()
End Sub End Sub
Private Sub NeoForge_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnNeoForgeClear.MouseLeftButtonUp Private Sub NeoForge_Clear(sender As Object, e As EventArgs) Handles BtnNeoForgeClear.Click
SelectedNeoForge = Nothing SelectedNeoForge = Nothing
CardNeoForge.IsSwapped = True CardNeoForge.IsSwapped = True
e.Handled = True
OptiFine_Loaded() OptiFine_Loaded()
ReloadSelected() ReloadSelected()
End Sub End Sub
@@ -847,9 +858,9 @@ Public Class PageDownloadInstall
''' <summary> ''' <summary>
''' 获取 Fabric 的加载异常信息。若正常则返回 Nothing。 ''' 获取 Fabric 的加载异常信息。若正常则返回 Nothing。
''' </summary> ''' </summary>
Private Function LoadFabricGetError() As String Public Function LoadFabricGetError() As String
'检查 OptiFine 1.20.5+:没有 OptiFabric 故全部不兼容 '检查 OptiFine 1.20.5+:没有 OptiFabric 故全部不兼容
If SelectedOptiFine IsNot Nothing AndAlso CompareVersionGE(VanillaName, "1.20.5") Then Return "与 OptiFine 不兼容" If SelectedOptiFine IsNot Nothing AndAlso McUtils.CompareVersionGE(VanillaName, "1.20.5") Then Return "与 OptiFine 不兼容"
'检查 Loader '检查 Loader
If GetLoaderError(LoadFabric) IsNot Nothing Then Return GetLoaderError(LoadFabric) If GetLoaderError(LoadFabric) IsNot Nothing Then Return GetLoaderError(LoadFabric)
'检查版本 '检查版本
@@ -879,9 +890,10 @@ Public Class PageDownloadInstall
If Not Versions.Any() Then Return If Not Versions.Any() Then Return
'可视化 '可视化
PanFabric.Children.Clear() PanFabric.Children.Clear()
PanFabric.Tag = Versions
CardFabric.SwapControl = PanFabric CardFabric.SwapControl = PanFabric
CardFabric.SwapType = 12 CardFabric.SetContentBuilder(Versions.OfType(Of JObject),
Function(Version) FabricDownloadListItem(Version, AddressOf Fabric_Selected))
If Not CardFabric.IsSwapped Then CardFabric.BuildContent()
Catch ex As Exception Catch ex As Exception
Logger.Error(ex, "可视化 Fabric 安装版本列表出错") Logger.Error(ex, "可视化 Fabric 安装版本列表出错")
End Try End Try
@@ -895,14 +907,15 @@ Public Class PageDownloadInstall
CardFabric.IsSwapped = True CardFabric.IsSwapped = True
ReloadSelected() ReloadSelected()
End Sub End Sub
Private Sub Fabric_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnFabricClear.MouseLeftButtonUp Private Sub Fabric_Clear(sender As Object, e As EventArgs) Handles BtnFabricClear.Click
SelectedFabric = Nothing SelectedFabric = Nothing
SelectedFabricApi = Nothing SelectedFabricApi = Nothing
BtnFabricApiClear.Visibility = Visibility.Collapsed
AutoSelectedFabricApi = False AutoSelectedFabricApi = False
SelectedOptiFabric = Nothing SelectedOptiFabric = Nothing
BtnOptiFabricClear.Visibility = Visibility.Collapsed
AutoSelectedOptiFabric = False AutoSelectedOptiFabric = False
CardFabric.IsSwapped = True CardFabric.IsSwapped = True
e.Handled = True
ReloadSelected() ReloadSelected()
End Sub End Sub
@@ -911,14 +924,14 @@ Public Class PageDownloadInstall
#Region "Fabric API 列表" #Region "Fabric API 列表"
''' <summary> ''' <summary>
''' 判断某 Fabric API 是否适配当前选择的原版版本。 ''' 判断某 Fabric API 是否适配指定的原版版本。
''' </summary> ''' </summary>
Public Function IsFabricApiCompatible(FabricApi As ResourceVersion) As Boolean Public Shared Function IsFabricApiCompatible(FabricApi As ResourceVersion, MinecraftVersion As String) As Boolean
Dim FabricApiName = FabricApi.Display Dim FabricApiName = FabricApi.Display
Try Try
If FabricApiName Is Nothing OrElse VanillaName Is Nothing Then Return False If FabricApiName Is Nothing OrElse MinecraftVersion Is Nothing Then Return False
FabricApiName = FabricApiName.Lower FabricApiName = FabricApiName.Lower
Dim TargetName = VanillaName.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3").Lower Dim TargetName = MinecraftVersion.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3").Lower
If FabricApiName.StartsWithF("[" & TargetName & "]") Then Return True If FabricApiName.StartsWithF("[" & TargetName & "]") Then Return True
If Not FabricApiName.Contains("/") OrElse Not FabricApiName.Contains("]") Then Return False If Not FabricApiName.Contains("/") OrElse Not FabricApiName.Contains("]") Then Return False
'直接的判断(例如 1.18.1/22w03a) '直接的判断(例如 1.18.1/22w03a)
@@ -946,7 +959,7 @@ Public Class PageDownloadInstall
End While End While
Return True Return True
Catch ex As Exception Catch ex As Exception
Logger.Warn(ex, $"判断 Fabric API 版本适配性出错({FabricApiName}, {VanillaName})") Logger.Warn(ex, $"判断 Fabric API 版本适配性出错({FabricApiName}, {MinecraftVersion})")
Return False Return False
End Try End Try
End Function End Function
@@ -954,12 +967,12 @@ Public Class PageDownloadInstall
''' <summary> ''' <summary>
''' 获取 FabricApi 的加载异常信息。若正常则返回 Nothing。 ''' 获取 FabricApi 的加载异常信息。若正常则返回 Nothing。
''' </summary> ''' </summary>
Private Function LoadFabricApiGetError() As String Public Function LoadFabricApiGetError() As String
'检查 Loader '检查 Loader
If GetLoaderError(LoadFabricApi) IsNot Nothing Then Return GetLoaderError(LoadFabricApi) If GetLoaderError(LoadFabricApi) IsNot Nothing Then Return GetLoaderError(LoadFabricApi)
If DlFabricApiLoader.Output Is Nothing Then Return If(SelectedFabric Is Nothing, "需要安装 Fabric", "获取中……") If DlFabricApiLoader.Output Is Nothing Then Return If(SelectedFabric Is Nothing, "需要安装 Fabric", "获取中……")
'检查版本 '检查版本
If DlFabricApiLoader.Output.Any(Function(f) IsFabricApiCompatible(f)) Then If DlFabricApiLoader.Output.Any(Function(f) IsFabricApiCompatible(f, VanillaName)) Then
Return If(SelectedFabric Is Nothing, "需要安装 Fabric", Nothing) Return If(SelectedFabric Is Nothing, "需要安装 Fabric", Nothing)
Else Else
Return "无" Return "无"
@@ -971,7 +984,7 @@ Public Class PageDownloadInstall
If LoadFabricApiGetError() IsNot Nothing Then e.Handled = True If LoadFabricApiGetError() IsNot Nothing Then e.Handled = True
End Sub End Sub
Private AutoSelectedFabricApi As Boolean = False Public AutoSelectedFabricApi As Boolean = False
''' <summary> ''' <summary>
''' 尝试重新可视化 FabricApi 版本列表。 ''' 尝试重新可视化 FabricApi 版本列表。
''' </summary> ''' </summary>
@@ -982,7 +995,7 @@ Public Class PageDownloadInstall
'获取版本列表 '获取版本列表
Dim Versions As New List(Of ResourceVersion) Dim Versions As New List(Of ResourceVersion)
For Each Version In DlFabricApiLoader.Output For Each Version In DlFabricApiLoader.Output
If IsFabricApiCompatible(Version) Then If IsFabricApiCompatible(Version, VanillaName) Then
If Not Version.Display.StartsWithF("[") Then If Not Version.Display.StartsWithF("[") Then
Logger.Warn($"已特判修改 Fabric API 显示名:{Version.Display}") Logger.Warn($"已特判修改 Fabric API 显示名:{Version.Display}")
Version.Display = "[" & VanillaName & "] " & Version.Display Version.Display = "[" & VanillaName & "] " & Version.Display
@@ -995,7 +1008,6 @@ Public Class PageDownloadInstall
'可视化 '可视化
PanFabricApi.Children.Clear() PanFabricApi.Children.Clear()
For Each Version In Versions For Each Version In Versions
If Not IsFabricApiCompatible(Version) Then Continue For
PanFabricApi.Children.Add(FabricApiDownloadListItem(Version, AddressOf FabricApi_Selected)) PanFabricApi.Children.Add(FabricApiDownloadListItem(Version, AddressOf FabricApi_Selected))
Next Next
'自动选择 Fabric API '自动选择 Fabric API
@@ -1016,10 +1028,9 @@ Public Class PageDownloadInstall
CardFabricApi.IsSwapped = True CardFabricApi.IsSwapped = True
ReloadSelected() ReloadSelected()
End Sub End Sub
Private Sub FabricApi_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnFabricApiClear.MouseLeftButtonUp Private Sub FabricApi_Clear(sender As Object, e As EventArgs) Handles BtnFabricApiClear.Click
SelectedFabricApi = Nothing SelectedFabricApi = Nothing
CardFabricApi.IsSwapped = True CardFabricApi.IsSwapped = True
e.Handled = True
ReloadSelected() ReloadSelected()
End Sub End Sub
@@ -1028,23 +1039,23 @@ Public Class PageDownloadInstall
#Region "OptiFabric 列表" #Region "OptiFabric 列表"
''' <summary> ''' <summary>
''' 判断某 OptiFabric 是否适配当前选择的原版版本。 ''' 判断某 OptiFabric 是否适配指定的原版版本。
''' </summary> ''' </summary>
Private Function IsOptiFabricCompatible(ModFile As ResourceVersion) As Boolean Public Shared Function IsOptiFabricCompatible(ModFile As ResourceVersion, MinecraftVersion As String) As Boolean
Try Try
If VanillaName Is Nothing Then Return False If MinecraftVersion Is Nothing Then Return False
Return ModFile.GameVersions.Contains(VanillaName) Return ModFile.GameVersions.Contains(MinecraftVersion)
Catch ex As Exception Catch ex As Exception
Logger.Warn(ex, $"判断 OptiFabric 版本适配性出错({VanillaName})") Logger.Warn(ex, $"判断 OptiFabric 版本适配性出错({MinecraftVersion})")
Return False Return False
End Try End Try
End Function End Function
Private AutoSelectedOptiFabric As Boolean = False Public AutoSelectedOptiFabric As Boolean = False
''' <summary> ''' <summary>
''' 获取 OptiFabric 的加载异常信息。若正常则返回 Nothing。 ''' 获取 OptiFabric 的加载异常信息。若正常则返回 Nothing。
''' </summary> ''' </summary>
Private Function LoadOptiFabricGetError() As String Public Function LoadOptiFabricGetError() As String
If VanillaDrop >= 140 AndAlso VanillaDrop <= 150 Then Return "不兼容老版本 Fabric,请手动下载 OptiFabric Origins" If VanillaDrop >= 140 AndAlso VanillaDrop <= 150 Then Return "不兼容老版本 Fabric,请手动下载 OptiFabric Origins"
'检查 Loader '检查 Loader
If GetLoaderError(LoadOptiFabric) IsNot Nothing Then Return GetLoaderError(LoadOptiFabric) If GetLoaderError(LoadOptiFabric) IsNot Nothing Then Return GetLoaderError(LoadOptiFabric)
@@ -1056,7 +1067,7 @@ Public Class PageDownloadInstall
Return "获取中……" Return "获取中……"
End If End If
For Each Version In DlOptiFabricLoader.Output For Each Version In DlOptiFabricLoader.Output
If Not IsOptiFabricCompatible(Version) Then Continue For '2135# If Not IsOptiFabricCompatible(Version, VanillaName) Then Continue For '2135#
If SelectedFabric Is Nothing AndAlso SelectedOptiFine Is Nothing Then Return "需要安装 OptiFine 与 Fabric" If SelectedFabric Is Nothing AndAlso SelectedOptiFine Is Nothing Then Return "需要安装 OptiFine 与 Fabric"
If SelectedFabric Is Nothing Then Return "需要安装 Fabric" If SelectedFabric Is Nothing Then Return "需要安装 Fabric"
If SelectedOptiFine Is Nothing Then Return "需要安装 OptiFine" If SelectedOptiFine Is Nothing Then Return "需要安装 OptiFine"
@@ -1080,7 +1091,7 @@ Public Class PageDownloadInstall
'获取版本列表 '获取版本列表
Dim Versions As New List(Of ResourceVersion) Dim Versions As New List(Of ResourceVersion)
For Each Version In DlOptiFabricLoader.Output For Each Version In DlOptiFabricLoader.Output
If IsOptiFabricCompatible(Version) Then Versions.Add(Version) If IsOptiFabricCompatible(Version, VanillaName) Then Versions.Add(Version)
Next Next
If Not Versions.Any() Then Return If Not Versions.Any() Then Return
'排序 '排序
@@ -1088,7 +1099,6 @@ Public Class PageDownloadInstall
'可视化 '可视化
PanOptiFabric.Children.Clear() PanOptiFabric.Children.Clear()
For Each Version In Versions For Each Version In Versions
If Not IsOptiFabricCompatible(Version) Then Continue For
PanOptiFabric.Children.Add(OptiFabricDownloadListItem(Version, AddressOf OptiFabric_Selected)) PanOptiFabric.Children.Add(OptiFabricDownloadListItem(Version, AddressOf OptiFabric_Selected))
Next Next
'自动选择 OptiFabric '自动选择 OptiFabric
@@ -1108,10 +1118,9 @@ Public Class PageDownloadInstall
CardOptiFabric.IsSwapped = True CardOptiFabric.IsSwapped = True
ReloadSelected() ReloadSelected()
End Sub End Sub
Private Sub OptiFabric_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnOptiFabricClear.MouseLeftButtonUp Private Sub OptiFabric_Clear(sender As Object, e As EventArgs) Handles BtnOptiFabricClear.Click
SelectedOptiFabric = Nothing SelectedOptiFabric = Nothing
CardOptiFabric.IsSwapped = True CardOptiFabric.IsSwapped = True
e.Handled = True
ReloadSelected() ReloadSelected()
End Sub End Sub
@@ -1119,10 +1128,42 @@ Public Class PageDownloadInstall
#Region "安装" #Region "安装"
''' <summary>
''' 获取当前选择的组件信息。
''' </summary>
Public Function GetSelectedComponents() As McComponents
Return New McComponents(
minecraftVersion:=VanillaName,
optiFineEntry:=If(SelectedOptiFine Is Nothing, Nothing, JObject.FromObject(SelectedOptiFine)),
forgeVersion:=SelectedForge?.VersionName,
neoForgeVersion:=SelectedNeoForge?.VersionName,
fabricVersion:=SelectedFabric,
fabricApiVersion:=If(SelectedFabricApi?.Version, If(SelectedFabricApi?.Id, SelectedFabricApi?.Display)),
optiFabricVersion:=If(SelectedOptiFabric?.Version, If(SelectedOptiFabric?.Id, SelectedOptiFabric?.Display)),
liteLoaderVersion:=SelectedLiteLoader?.JsonToken?("version").ToString())
End Function
Private Sub TextSelectName_KeyDown(sender As Object, e As KeyEventArgs) Handles TextSelectName.KeyDown Private Sub TextSelectName_KeyDown(sender As Object, e As KeyEventArgs) Handles TextSelectName.KeyDown
If e.Key = Key.Enter AndAlso IsInSelectPage AndAlso BtnStart.IsEnabled Then BtnStart_Click() If e.Key = Key.Enter AndAlso IsInSelectPage AndAlso BtnStart.IsEnabled Then BtnStart_Click()
End Sub End Sub
Private Sub BtnStart_Click() Handles BtnStart.Click Private Sub BtnStart_Click() Handles BtnStart.Click
'提交安装申请
Dim InstanceName As String = TextSelectName.Text
Dim Request As New McInstallRequest With {
.NewInstanceName = InstanceName,
.InstanceFolder = $"{McFolderSelected}versions\{InstanceName}\",
.Components = GetSelectedComponents(),
.MinecraftJson = VanillaData("url").ToString,
.ForgeEntry = SelectedForge,
.NeoForgeEntry = SelectedNeoForge,
.FabricApi = SelectedFabricApi,
.OptiFabric = SelectedOptiFabric,
.LiteLoaderEntry = SelectedLiteLoader
}
StartInstall(Request)
End Sub
Public Overridable Sub StartInstall(Request As McInstallRequest)
'确认版本隔离 '确认版本隔离
If (SelectedForge IsNot Nothing OrElse SelectedNeoForge IsNot Nothing OrElse SelectedFabric IsNot Nothing) AndAlso If (SelectedForge IsNot Nothing OrElse SelectedNeoForge IsNot Nothing OrElse SelectedFabric IsNot Nothing) AndAlso
(Settings.Get(Of Integer)("LaunchArgumentIndieV2") = 0 OrElse Settings.Get(Of Integer)("LaunchArgumentIndieV2") = 2) Then (Settings.Get(Of Integer)("LaunchArgumentIndieV2") = 0 OrElse Settings.Get(Of Integer)("LaunchArgumentIndieV2") = 2) Then
@@ -1132,22 +1173,7 @@ Public Class PageDownloadInstall
Return Return
End If End If
End If End If
'提交安装申请 If Not McInstallStart(Request) Then Return
Dim InstanceName As String = TextSelectName.Text
Dim Request As New McInstallRequest With {
.NewInstanceName = InstanceName,
.VersionFolder = $"{McFolderSelected}versions\{InstanceName}\",
.MinecraftJson = VanillaData("url").ToString,
.MinecraftName = VanillaName,
.OptiFineEntry = SelectedOptiFine,
.ForgeEntry = SelectedForge,
.NeoForgeEntry = SelectedNeoForge,
.FabricVersion = SelectedFabric,
.FabricApi = SelectedFabricApi,
.OptiFabric = SelectedOptiFabric,
.LiteLoaderEntry = SelectedLiteLoader
}
If Not McInstall(Request) Then Return
'返回,这样在再次进入安装页面时这个版本就会显示文件夹已重复 '返回,这样在再次进入安装页面时这个版本就会显示文件夹已重复
ExitSelectPage() ExitSelectPage()
End Sub End Sub
@@ -0,0 +1,727 @@
''' <summary>
''' 版本修改页:完整还原已安装组件后,复用安装页的选择交互。
''' </summary>
Public Class PageDownloadModify
Inherits PageDownloadInstall
Private InitLoader As LoaderTask(Of McInstance, Object)
Private ForgeVersionLoader As LoaderTask(Of String, List(Of DlForgeVersionEntry))
Private InitialTarget As McComponents '进入时的组件快照
''' <summary>
''' 原版的版本号是否可靠(不是通过 json 模糊分析)。
''' </summary>
Private IsVanillaVersionReliable As Boolean
Private OriginalName As String
Private CustomLogo As String
''' <summary>
''' 开始修改 MC 版本时,记录当前选择的组件,以在修改 MC 版本后尝试自动选中相同组件。
''' </summary>
Private ReadOnly PendingComponents As New Dictionary(Of MyLoading, String)
Private IsRestoringComponents As Boolean
Public Overrides Sub LoaderInit() '父级 Handles Me.Initialized
ControlInit()
InitLoader = New LoaderTask(Of McInstance, Object)("初始化版本修改", AddressOf LoadInstance)
PageLoaderInit(LoadMinecraft, PanLoad, PanAllBack, Nothing, InitLoader, AddressOf LoadInstanceFinished, Function() PageInstanceLeft.Instance)
End Sub
Public Overrides Sub Init() '父级 Handles Me.Loaded
PanBack.ScrollToHome()
Static IsLoad As Boolean = False
If IsLoad Then PageLoaderRestart()
IsLoad = True
End Sub
Private Sub PageUnloaded() Handles Me.Unloaded
InitLoader.Cancel()
End Sub
''' <summary>
''' 读取或初始化版本的安装清单。
''' </summary>
Private Sub LoadInstance(Task As LoaderTask(Of McInstance, Object))
'旧状态清理
RunInUiWait(
Sub()
AniStop("FrmDownloadInstall SelectPageSwitch")
IsSelectNameEdited = False
ClearSelected()
PendingComponents.Clear()
InitialTarget = Nothing
IsInSelectPage = False
PanAllBack.IsHitTestVisible = False
End Sub)
Task.Input.Config.DiscardCache()
'获取 Components
Dim Manifest = Configs.McInstallManifest.Get(Task.Input.Config)
If Manifest IsNot Nothing AndAlso Manifest.SchemaVersion > Versions.McInstallManifestVersion Then
Logger.Warn($"安装清单由更新版本的 PCL 创建(清单版本:{Manifest.SchemaVersion},当前版本:{Versions.McInstallManifestVersion})")
Throw New Exception($"更新 PCL 后才能修改它!点击【设置 → 其他 → 检查更新】以更新。")
End If
If Manifest IsNot Nothing Then
Dim LatestManifest = Manifest.RefreshModComponents(Task.Input.PathIndie)
If LatestManifest IsNot Manifest Then
Manifest = LatestManifest
Configs.McInstallManifest.Set(Manifest, Task.Input.Config)
Task.Input.Config.Save()
End If
End If
Dim Components As McComponents
Dim OptiFineVersion As String = Nothing
If Manifest IsNot Nothing Then
'有清单:从清单读取
Components = Manifest.Components
If Components Is Nothing Then Throw New Exception("安装清单缺少组件信息,无法还原已安装组件。")
IsVanillaVersionReliable = True
Else
'无清单:通过版本 JSON 获取
Dim VersionInfo = New McInstance(Task.Input.PathVersion).Version
If VersionInfo.VanillaName = "Unknown" Then Throw New Exception("无法确定 Minecraft 版本,无法修改")
Dim UnknownLoaders = VersionInfo.ModLoaderVersions.
Where(Function(Loader) String.IsNullOrWhiteSpace(Loader.Value) OrElse Loader.Value = "未知版本").
Select(Function(Loader) Loader.Key.ToString()).ToList()
If UnknownLoaders.Any() Then Throw New Exception($"无法确定 {UnknownLoaders.Join("、")} 的版本,无法修改")
Components = New McComponents(VersionInfo.VanillaName, VersionInfo.ModLoaderVersions)
OptiFineVersion = VersionInfo.OptiFine
IsVanillaVersionReliable = VersionInfo.Reliable
End If
Dim MinecraftVersion = Components.MinecraftVersion
If String.IsNullOrWhiteSpace(MinecraftVersion) Then Throw New Exception("无法确定当前 Minecraft 版本,请检查版本文件")
'等待子列表加载器完成获取
Dim LoaderList As New Dictionary(Of String, LoaderBase) From {
{"Minecraft", DlClientListLoader}, {"OptiFine", DlOptiFineListLoader}, {"LiteLoader", DlLiteLoaderListLoader},
{"NeoForge", DlNeoForgeListLoader}, {"Fabric", DlFabricListLoader}, {"Fabric API", DlFabricApiLoader}, {"OptiFabric", DlOptiFabricLoader}}
Dim ForgeLoader As LoaderTask(Of String, List(Of DlForgeVersionEntry)) = Nothing
If McVersion.IsStandardFormat(MinecraftVersion) Then
ForgeLoader = New LoaderTask(Of String, List(Of DlForgeVersionEntry))("DlForgeVersion " & MinecraftVersion, AddressOf DlForgeVersionMain)
LoaderList.Add("Forge", ForgeLoader)
End If
For Each Loader In LoaderList.Values
Loader.Start(If(Loader Is ForgeLoader, MinecraftVersion, Nothing), Task.IsForceRestarting)
Next
Do
If Task.IsCanceled Then Return
For Each Pair In LoaderList
Dim Loader = Pair.Value
If Loader.State = LoadState.Failed Then
If Loader Is ForgeLoader AndAlso Loader.Error.Message = "无" Then Continue For 'Forge 无版本时不报错
Throw New Exception($"获取 {Pair.Key} 版本列表失败,请稍后再试,或使用 VPN 改善网络环境", Loader.Error)
ElseIf Loader.State = LoadState.Canceled Then
Throw New Exception($"获取 {Pair.Key} 版本列表被中断,请稍后再试,或使用 VPN 改善网络环境")
End If
Next
If LoaderList.Values.All(Function(Loader) Loader.State = LoadState.Finished OrElse Loader.State = LoadState.Failed) Then Exit Do
Task.Progress = 0.7 * LoaderList.Values.Average(Function(Loader) Loader.Progress)
Thread.Sleep(10)
Loop
If Task.IsCanceled Then Return
Task.Progress = 0.7
'Minecraft
MinecraftVersions = CType(DlClientListLoader.Output.Value("versions").DeepClone(), JArray)
VanillaData = MinecraftVersions.OfType(Of JObject).FirstOrDefault(
Function(v) v("id").ToString.Replace("∞", "infinite") = MinecraftVersion.Replace("∞", "infinite"))
If VanillaData Is Nothing Then Throw New Exception($"Minecraft 列表中没有已安装的 {MinecraftVersion}")
VanillaData("id") = MinecraftVersion
'OptiFine
If Manifest IsNot Nothing Then
SelectedOptiFine = Components.OptiFineEntry?.ToObject(Of DlOptiFineListEntry)()
If SelectedOptiFine IsNot Nothing AndAlso Not DlOptiFineListLoader.Output.Value.Any(Function(v) v.FileName = SelectedOptiFine.FileName) Then
'如果 OptiFine 是 pre 版,可能只在 BMCLAPI 下有,官方源删了。如果是这种缺失数据的情况,换用 BMCLAPI 源作为输出
If DlOptiFineListBmclapiLoader.State <> LoadState.Finished Then DlOptiFineListBmclapiLoader.WaitForExit()
DlOptiFineListLoader.Output = DlOptiFineListBmclapiLoader.Output
End If
ElseIf OptiFineVersion IsNot Nothing AndAlso OptiFineVersion <> "未知版本" Then
SelectedOptiFine = DlOptiFineListLoader.Output.Value.FirstOrDefault(
Function(v) v.Inherit = MinecraftVersion AndAlso
v.InstanceName.AfterLast("-OptiFine_").Replace("HD_U_", "") = OptiFineVersion.Replace("HD_U_", ""))
If SelectedOptiFine Is Nothing Then Throw New Exception($"OptiFine 列表中没有已安装的 {OptiFineVersion}")
End If
'Forge
If Components.ModLoaderVersions.ContainsKey(ModLoaders.Forge) Then
SelectedForge = ForgeLoader?.Output?.FirstOrDefault(
Function(v) v.Inherit = MinecraftVersion AndAlso v.VersionName = Components.ModLoaderVersions(ModLoaders.Forge) AndAlso
v.Category <> "universal" AndAlso v.Category <> "client" AndAlso (SelectedOptiFine Is Nothing OrElse IsOptiFineSuitForForge(SelectedOptiFine, v)))
If SelectedForge Is Nothing Then Throw New Exception($"Forge 列表中没有已安装的 {Components.ModLoaderVersions(ModLoaders.Forge)}")
End If
'NeoForge
If Components.ModLoaderVersions.ContainsKey(ModLoaders.NeoForge) Then
SelectedNeoForge = DlNeoForgeListLoader.Output.Value.FirstOrDefault(
Function(v) v.Inherit = MinecraftVersion AndAlso v.VersionName = Components.ModLoaderVersions(ModLoaders.NeoForge))
If SelectedNeoForge Is Nothing Then Throw New Exception($"NeoForge 列表中没有已安装的 {Components.ModLoaderVersions(ModLoaders.NeoForge)}")
End If
'Fabric
If Components.ModLoaderVersions.ContainsKey(ModLoaders.Fabric) Then
Dim FabricEntry = DlFabricListLoader.Output.Value("loader").OfType(Of JObject).FirstOrDefault(
Function(v) v("version").ToString = Components.ModLoaderVersions(ModLoaders.Fabric),
Function(v) v("version").ToString.Replace("+build", "") = Components.ModLoaderVersions(ModLoaders.Fabric))
If FabricEntry Is Nothing OrElse Not DlFabricListLoader.Output.Value("game").OfType(Of JObject).
Any(Function(v) v("version").ToString = MinecraftVersion.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3")) Then
Throw New Exception($"Fabric 列表中没有已安装的 {Components.ModLoaderVersions(ModLoaders.Fabric)}")
End If
SelectedFabric = FabricEntry("version").ToString
End If
'LiteLoader
If Components.ModLoaderVersions.ContainsKey(ModLoaders.LiteLoader) Then
SelectedLiteLoader = DlLiteLoaderListLoader.Output.Value.FirstOrDefault(
Function(v) v.Inherit = MinecraftVersion AndAlso v.JsonToken("version").ToString = Components.ModLoaderVersions(ModLoaders.LiteLoader))
If SelectedLiteLoader Is Nothing Then Throw New Exception($"LiteLoader 列表中没有已安装的 {Components.ModLoaderVersions(ModLoaders.LiteLoader)}")
End If
'Fabric API
If Components.FabricApiVersion IsNot Nothing Then
SelectedFabricApi = DlFabricApiLoader.Output.FirstOrDefault(
Function(v) (v.Version = Components.FabricApiVersion OrElse v.Id = Components.FabricApiVersion OrElse
v.Display = Components.FabricApiVersion) AndAlso IsFabricApiCompatible(v, MinecraftVersion))
If SelectedFabricApi Is Nothing Then Throw New Exception($"Fabric API 列表中没有已安装的 {Components.FabricApiVersion}")
End If
'OptiFabric
If Components.OptiFabricVersion IsNot Nothing Then
SelectedOptiFabric = DlOptiFabricLoader.Output.FirstOrDefault(
Function(v) (v.Version = Components.OptiFabricVersion OrElse v.Id = Components.OptiFabricVersion OrElse
v.Display = Components.OptiFabricVersion) AndAlso IsOptiFabricCompatible(v, MinecraftVersion))
If SelectedOptiFabric Is Nothing Then Throw New Exception($"OptiFabric 列表中没有已安装的 {Components.OptiFabricVersion}")
End If
'扫描尚未识别的 Mod 组件
Dim ManagedFiles As New List(Of McInstallManifest.ManagedFile)
If Manifest IsNot Nothing Then ManagedFiles.AddRange(Manifest.ManagedFiles)
#Region "从 mods 文件夹中获取以 Mod 形式安装的组件信息"
'获取 mods 文件夹下的 jar 文件与 SHA1
Dim Mods As New Lazy(Of List(Of (File As FileInfo, Sha1 As String)))(
Function()
Dim ModsFolder = Task.Input.PathIndie & "mods\"
Dim Files = DirectoryUtils.EnumerateFiles(ModsFolder, searchPattern:="*.jar").ToList()
If SelectedLiteLoader IsNot Nothing Then 'LiteLoader 的 OptiFine 安装目录
Files.AddRange(DirectoryUtils.EnumerateFiles(Path.Combine(ModsFolder, MinecraftVersion), searchPattern:="*.jar"))
End If
Dim Entries As New List(Of (File As FileInfo, Sha1 As String))
For Each FilePath In Files
If Task.IsCanceled Then Return Entries
Dim File = FileUtils.GetInfo(FilePath)
Entries.Add((File, New LocalResourceFile(File).ModrinthHash))
Task.Progress = 0.7 + 0.28 * Entries.Count / Files.Count
Next
Return Entries
End Function)
'Fabric API
If SelectedFabric IsNot Nothing AndAlso SelectedFabricApi Is Nothing Then
Dim FabricApiList = DlFabricApiLoader.Output.Where(Function(v) IsFabricApiCompatible(v, MinecraftVersion)).ToList()
Dim Matches As New List(Of (File As FileInfo, Sha1 As String, Version As ResourceVersion))
For Each Entry In Mods.Value
Dim Match = FabricApiList.FirstOrDefault(Function(v) v.Hash = Entry.Sha1)
If Match Is Nothing Then Continue For
Matches.Add((Entry.File, Entry.Sha1, Match))
Next
Dim SelectedFile = SelectMod("Fabric API", Matches)
If SelectedFile IsNot Nothing Then
SelectedFabricApi = SelectedFile.Value.Version
ManagedFiles.Add(New McInstallManifest.ManagedFile("mods/" & SelectedFile.Value.File.Name.Lower(), SelectedFile.Value.Sha1, SelectedFile.Value.File.LastWriteTimeUtc, "FabricApi"))
End If
End If
If Task.IsCanceled Then Return
'OptiFabric
If SelectedFabric IsNot Nothing AndAlso SelectedOptiFabric Is Nothing Then
Dim OptiFabricList = DlOptiFabricLoader.Output.Where(Function(v) IsOptiFabricCompatible(v, MinecraftVersion)).ToList()
Dim HasMd5 = OptiFabricList.Any(Function(v) If(v.Hash, "").Length = 32) 'CurseForge 的 OptiFabric 信息可能提供 SHA1 或 MD5
Dim Matches As New List(Of (File As FileInfo, Sha1 As String, Version As ResourceVersion))
For Each Entry In Mods.Value
Dim Match = OptiFabricList.FirstOrDefault(Function(v) v.Hash = Entry.Sha1)
If Match Is Nothing AndAlso HasMd5 Then
Dim Md5 = CryptographyUtils.ComputeFileHash(Entry.File.FullName, CryptographyUtils.HashMethod.Md5)
Match = OptiFabricList.FirstOrDefault(Function(v) v.Hash = Md5)
End If
If Match Is Nothing Then Continue For
Matches.Add((Entry.File, Entry.Sha1, Match))
Next
Dim SelectedFile = SelectMod("OptiFabric", Matches)
If SelectedFile IsNot Nothing Then
SelectedOptiFabric = SelectedFile.Value.Version
ManagedFiles.Add(New McInstallManifest.ManagedFile("mods/" & SelectedFile.Value.File.Name.Lower(), SelectedFile.Value.Sha1, SelectedFile.Value.File.LastWriteTimeUtc, "OptiFabric"))
End If
End If
If Task.IsCanceled Then Return
'OptiFine
Dim OptiFineFiles As New List(Of (File As FileInfo, Sha1 As String, OriginalName As String))
If SelectedOptiFine Is Nothing AndAlso (SelectedForge IsNot Nothing OrElse SelectedLiteLoader IsNot Nothing OrElse SelectedFabric IsNot Nothing) Then 'OptiFine 只在有 Mod 加载器时才看在不在 mods 文件夹中
Static OptiFineHashes As JObject = FileUtils.ReadAsJson("Minecraft\OptiFineHashes.json", type:=GetType(Resource)) 'JSON 内容:{"SHA1": "原始文件名", ...}
OptiFineFiles = Mods.Value.
Select(Function(Entry) (Entry.File, Entry.Sha1, OriginalName:=OptiFineHashes(Entry.Sha1)?.ToString())). '用本地文件的 SHA1 匹配下载时的原始文件名,再根据原始文件名取得下载源信息
Where(Function(Entry) Entry.OriginalName IsNot Nothing).ToList()
Dim Matches As New List(Of (File As FileInfo, Sha1 As String, Version As DlOptiFineListEntry))
For Each Entry In OptiFineFiles '遍历 hash 匹配到了的本地文件
Dim Match = DlOptiFineListLoader.Output.Value.FirstOrDefault(
Function(v) v.Inherit = MinecraftVersion AndAlso v.FileName = Entry.OriginalName AndAlso (SelectedForge Is Nothing OrElse IsOptiFineSuitForForge(v, SelectedForge)))
If Match Is Nothing Then
Hint($"无法从 OptiFine 列表还原 {Entry.File.Name} 对应的 {Entry.OriginalName},将跳过该文件。")
Continue For
End If
Matches.Add((Entry.File, Entry.Sha1, Match))
Next
Dim SelectedFile = SelectMod("OptiFine", Matches)
If SelectedFile IsNot Nothing Then
SelectedOptiFine = SelectedFile.Value.Version
Dim RelativePath = PathUtils.RemoveExtendedPrefix(SelectedFile.Value.File.FullName).Substring(Task.Input.PathIndie.Length).Replace("\", "/").Lower()
ManagedFiles.Add(New McInstallManifest.ManagedFile(RelativePath, SelectedFile.Value.Sha1, SelectedFile.Value.File.LastWriteTimeUtc, "OptiFine"))
End If
End If
If Task.IsCanceled Then Return
#End Region
'保存 Manifest
If IsVanillaVersionReliable Then
Components = New McComponents(MinecraftVersion, Components.ModLoaderVersions,
If(SelectedOptiFine Is Nothing, Nothing, JObject.FromObject(SelectedOptiFine)),
If(SelectedFabricApi?.Version, If(SelectedFabricApi?.Id, SelectedFabricApi?.Display)),
If(SelectedOptiFabric?.Version, If(SelectedOptiFabric?.Id, SelectedOptiFabric?.Display)))
If Manifest Is Nothing OrElse Not Components.IsEquivalentTo(Manifest.Components) OrElse Not ManagedFiles.SequenceEqual(Manifest.ManagedFiles) Then
Configs.McInstallManifest.Set(New McInstallManifest(Versions.McInstallManifestVersion, Components, Date.UtcNow, ManagedFiles), Task.Input.Config)
Task.Input.Config.Save()
Logger.Info($"已为 {Task.Input.Name} 更新安装清单")
End If
End If
If Task.IsCanceled Then Return
ForgeVersionLoader = ForgeLoader
End Sub
Private Function SelectMod(Of T)(ComponentName As String, Matches As List(Of (File As FileInfo, Sha1 As String, Version As T))) As (File As FileInfo, Sha1 As String, Version As T)?
If Matches.Count = 0 Then Return Nothing
If Matches.Count = 1 Then Return Matches(0)
Dim Choice As Integer? = Nothing
RunInUiWait(Sub() Choice = MyMsgBoxSelect(Matches.Select(Function(Entry) CType(New MyRadioBox With {.Text = Entry.File.Name}, IMyRadio)), $"选择当前使用的 {ComponentName}"))
If Not Choice.HasValue Then Throw New Exception($"发现了多个 {ComponentName}")
Return Matches(Choice.Value)
End Function
''' <summary>
''' 初始化修改页
''' </summary>
Private Sub LoadInstanceFinished(Task As LoaderTask(Of McInstance, Object))
'还原原版信息
OriginalName = Task.Input.Name
CustomLogo = Nothing
If ReadIni(Task.Input.PathVersion & "PCL\Setup.ini", "LogoCustom", False) Then
CustomLogo = If(Task.Input.Logo.EndsWithF("PCL\Logo.png"), Task.Input.PathVersion & "PCL\Logo.png", Task.Input.Logo)
End If
Dim Item = McDownloadListItem(VanillaData, Nothing, False).Init()
VanillaName = Item.Title
VanillaIcon = Item.Logo
LabMinecraftVersion.Text = VanillaName
TextSelectName.Text = OriginalName
IsSelectNameEdited = Not String.Equals(OriginalName, GetSelectName(), StringComparison.Ordinal)
TextSelectName.ValidateRules = New ObjectModel.Collection(Of Validate) From {
New ValidateFolderName(McFolderSelected & "versions", IgnoreList:=New List(Of String) From {OriginalName})}
'修改页 UI
CardMinecraft.Visibility = If(RestrictMcDownload, Visibility.Collapsed, Visibility.Visible)
BtnBack.Visibility = Visibility.Collapsed
ImgLogo.Margin = New Thickness(15, 15, 0, 0)
TextSelectName.Margin = New Thickness(56, 16, 16, 0)
LabSelectName.Text = $"原名 :   {OriginalName}"
UpdateOriginalNameVisibility()
BtnStart.Text = "开始修改"
BtnStart.Logo = FrmInstanceLeft.ItemModify.Logo
AutoSelectedFabricApi = SelectedFabric IsNot Nothing
AutoSelectedOptiFabric = SelectedFabric IsNot Nothing AndAlso SelectedOptiFine IsNot Nothing
If ForgeVersionLoader IsNot Nothing Then LoadForge.State = ForgeVersionLoader
PrepareSelectPage()
ReloadComponentLists()
'进入动画
PanMinecraft.Visibility = Visibility.Collapsed
PanMinecraft.Opacity = 0
PanMinecraft.RenderTransform = New TranslateTransform(-50, 0)
PanSelect.Opacity = 1
PanSelect.RenderTransform = New TranslateTransform
PanBack.IsHitTestVisible = True
PanAllBack.IsHitTestVisible = True
InitialTarget = GetSelectedComponents()
'设置原始数据
BtnMinecraftRestore.Tag = VanillaData
BtnOptiFineRestore.Tag = SelectedOptiFine
BtnForgeRestore.Tag = SelectedForge
BtnNeoForgeRestore.Tag = SelectedNeoForge
BtnFabricRestore.Tag = SelectedFabric
BtnFabricApiRestore.Tag = SelectedFabricApi
BtnOptiFabricRestore.Tag = SelectedOptiFabric
BtnLiteLoaderRestore.Tag = SelectedLiteLoader
ImgMinecraftOld.Source = VanillaIcon
LabMinecraftOld.Text = LabMinecraftVersion.Text
LabOptiFineOld.Text = If(SelectedOptiFine Is Nothing, "无", LabOptiFine.Text)
LabForgeOld.Text = If(SelectedForge Is Nothing, "无", LabForge.Text)
LabNeoForgeOld.Text = If(SelectedNeoForge Is Nothing, "无", LabNeoForge.Text)
LabFabricOld.Text = If(SelectedFabric Is Nothing, "无", LabFabric.Text)
LabLiteLoaderOld.Text = If(SelectedLiteLoader Is Nothing, "无", LabLiteLoader.Text)
LabFabricApiOld.Text = If(SelectedFabricApi Is Nothing, "无", LabFabricApi.Text)
LabOptiFabricOld.Text = If(SelectedOptiFabric Is Nothing, "无", LabOptiFabric.Text)
RefreshComparisons()
End Sub
Public Overrides Function GetSelectLogo() As String
Return If(CustomLogo, MyBase.GetSelectLogo())
End Function
Public Overrides Sub ShowMinecraftVersionList()
If IsInSelectPage Then
'把当前已选择的组件记录到 PendingComponents
Dim Components = GetSelectedComponents()
For Each Component In {
(Loader:=LoadOptiFine, Version:=Components.OptiFineVersion),
(Loader:=LoadFabricApi, Version:=Components.FabricApiVersion),
(Loader:=LoadOptiFabric, Version:=Components.OptiFabricVersion),
(Loader:=LoadForge, Version:=SelectedForge?.VersionName),
(Loader:=LoadNeoForge, Version:=SelectedNeoForge?.VersionName),
(Loader:=LoadFabric, Version:=SelectedFabric),
(Loader:=LoadLiteLoader, Version:=SelectedLiteLoader?.JsonToken?("version").ToString())}
'保留尚未加载完成的组件选择
If Component.Version IsNot Nothing Then PendingComponents(Component.Loader) = Component.Version
Next
End If
MyBase.ShowMinecraftVersionList()
End Sub
Public Overrides Sub ReloadSelected()
If IsRestoringComponents Then Return
If IsInSelectPage AndAlso VanillaName IsNot Nothing AndAlso PendingComponents.Any() Then
IsRestoringComponents = True
Try
Dim HasProcessed As Boolean = False
'优先恢复 Mod 加载器,再匹配兼容的附加组件
For Each LoadingComponent In {LoadFabric, LoadForge, LoadNeoForge, LoadLiteLoader, LoadOptiFine, LoadFabricApi, LoadOptiFabric}
Dim Version As String = Nothing
If Not PendingComponents.TryGetValue(LoadingComponent, Version) Then Continue For
If Not LoadingComponent.State.IsLoader Then Continue For
Dim Loader = DirectCast(LoadingComponent.State, LoaderBase)
'检查前置组件
If LoadingComponent Is LoadOptiFine Then
If {LoadFabric, LoadForge, LoadNeoForge, LoadLiteLoader}.Any(Function(Component) PendingComponents.ContainsKey(Component)) Then Continue For
ElseIf LoadingComponent Is LoadFabricApi Then
If PendingComponents.ContainsKey(LoadFabric) Then Continue For
ElseIf LoadingComponent Is LoadForge Then
If Not McVersion.IsStandardFormat(VanillaName) Then
PendingComponents.Remove(LoadingComponent)
Continue For
End If
If DirectCast(Loader, LoaderTask(Of String, List(Of DlForgeVersionEntry))).Input <> VanillaName Then Continue For
If Loader.State = LoadState.Failed AndAlso Loader.Error.Message = "无" Then
PendingComponents.Remove(LoadingComponent)
Continue For
End If
ElseIf LoadingComponent Is LoadOptiFabric Then
If PendingComponents.ContainsKey(LoadFabric) OrElse PendingComponents.ContainsKey(LoadOptiFine) Then Continue For
End If
If Loader.State <> LoadState.Finished Then Continue For
'移除已处理的组件
PendingComponents.Remove(LoadingComponent)
HasProcessed = True
'匹配目标 MC 版本下兼容的组件
If LoadingComponent Is LoadFabric Then
If SelectedFabric Is Nothing AndAlso LoadFabricGetError() Is Nothing Then
Dim Versions = DlFabricListLoader.Output.Value("loader").OfType(Of JObject)
SelectedFabric = If(Versions.FirstOrDefault(Function(v) v("version").ToString = Version), Versions.FirstOrDefault())?("version").ToString()
End If
ElseIf LoadingComponent Is LoadOptiFine Then
If SelectedOptiFine Is Nothing AndAlso LoadOptiFineGetError() Is Nothing Then
Dim Versions = DlOptiFineListLoader.Output.Value.Where(Function(v) v.Inherit = VanillaName AndAlso
(SelectedForge Is Nothing OrElse IsOptiFineSuitForForge(v, SelectedForge)))
SelectedOptiFine = If(Versions.FirstOrDefault(Function(v) v.InstanceName.AfterLast("-OptiFine_") = Version),
Versions.OrderBy(Function(v) v.IsPreview).
ThenByDescending(Function(v) v.DisplayName, Comparer(Of String).Create(AddressOf McUtils.CompareVersion)).FirstOrDefault())
End If
ElseIf LoadingComponent Is LoadForge Then
If SelectedForge Is Nothing AndAlso LoadForgeGetError() Is Nothing Then
Dim Versions = DirectCast(Loader, LoaderTask(Of String, List(Of DlForgeVersionEntry))).Output.Where(
Function(v) v.Inherit = VanillaName AndAlso
v.Category <> "universal" AndAlso v.Category <> "client" AndAlso
(SelectedOptiFine Is Nothing OrElse IsOptiFineSuitForForge(SelectedOptiFine, v)))
SelectedForge = If(Versions.FirstOrDefault(Function(v) v.VersionName = Version), Versions.OrderByDescending(Function(v) v).FirstOrDefault())
End If
ElseIf LoadingComponent Is LoadNeoForge Then
If SelectedNeoForge Is Nothing AndAlso LoadNeoForgeGetError() Is Nothing Then
Dim Versions = DlNeoForgeListLoader.Output.Value.Where(Function(v) v.Inherit = VanillaName)
SelectedNeoForge = If(Versions.FirstOrDefault(Function(v) v.VersionName = Version), Versions.FirstOrDefault())
End If
ElseIf LoadingComponent Is LoadLiteLoader Then
If SelectedLiteLoader Is Nothing AndAlso LoadLiteLoaderGetError() Is Nothing Then
Dim Versions = DlLiteLoaderListLoader.Output.Value.Where(Function(v) v.Inherit = VanillaName)
SelectedLiteLoader = If(Versions.FirstOrDefault(Function(v) v.JsonToken("version").ToString = Version), Versions.FirstOrDefault())
End If
ElseIf LoadingComponent Is LoadFabricApi Then
If SelectedFabricApi Is Nothing AndAlso LoadFabricApiGetError() Is Nothing Then
Dim Versions = DlFabricApiLoader.Output.Where(Function(v) IsFabricApiCompatible(v, VanillaName))
SelectedFabricApi = If(Versions.FirstOrDefault(Function(v) v.Version = Version OrElse v.Id = Version OrElse v.Display = Version),
Versions.OrderByDescending(Function(v) v.ReleaseDate).FirstOrDefault())
End If
If SelectedFabricApi IsNot Nothing Then AutoSelectedFabricApi = True
ElseIf LoadingComponent Is LoadOptiFabric Then
If SelectedOptiFabric Is Nothing AndAlso LoadOptiFabricGetError() Is Nothing Then
Dim Versions = DlOptiFabricLoader.Output.Where(Function(v) IsOptiFabricCompatible(v, VanillaName))
SelectedOptiFabric = If(Versions.FirstOrDefault(Function(v) v.Version = Version OrElse v.Id = Version OrElse v.Display = Version),
Versions.OrderByDescending(Function(v) v.ReleaseDate).FirstOrDefault())
End If
If SelectedOptiFabric IsNot Nothing Then AutoSelectedOptiFabric = True
End If
Next
'更新组件列表
If HasProcessed Then ReloadComponentLists()
Finally
IsRestoringComponents = False
End Try
End If
'刷新当前选择与原始版本的对比
MyBase.ReloadSelected()
If InitialTarget IsNot Nothing Then RefreshComparisons()
End Sub
Private Sub RefreshComparisons()
Dim Current = GetSelectedComponents()
ImgMinecraftNew.Source = VanillaIcon
'Minecraft
Dim IsMinecraftUnchanged = InitialTarget.MinecraftVersion = Current.MinecraftVersion
ShowComparison(PanMinecraftCompare, PanMinecraftInfo, InitialTarget.MinecraftVersion, Current.MinecraftVersion,
LabMinecraftVersion.Text, BtnMinecraftRestore, True)
'OptiFine
Dim OriginalOptiFine = TryCast(BtnOptiFineRestore.Tag, DlOptiFineListEntry)
ShowComparison(PanOptiFineCompare, PanOptiFineInfo, InitialTarget.OptiFineVersion, Current.OptiFineVersion,
LabOptiFine.Text, BtnOptiFineRestore, InitialTarget.OptiFineEntry Is Nothing OrElse
IsMinecraftUnchanged AndAlso CardOptiFine.MainSwap.Visibility = Visibility.Visible AndAlso OriginalOptiFine IsNot Nothing AndAlso
(SelectedForge Is Nothing OrElse IsOptiFineSuitForForge(OriginalOptiFine, SelectedForge)), BtnOptiFineClear)
'Fabric API
ShowComparison(PanFabricApiCompare, PanFabricApiInfo, InitialTarget.FabricApiVersion, Current.FabricApiVersion,
LabFabricApi.Text, BtnFabricApiRestore, InitialTarget.FabricApiVersion Is Nothing OrElse
IsMinecraftUnchanged AndAlso SelectedFabric IsNot Nothing AndAlso CardFabricApi.MainSwap.Visibility = Visibility.Visible, BtnFabricApiClear)
'OptiFabric
ShowComparison(PanOptiFabricCompare, PanOptiFabricInfo, InitialTarget.OptiFabricVersion, Current.OptiFabricVersion,
LabOptiFabric.Text, BtnOptiFabricRestore, InitialTarget.OptiFabricVersion Is Nothing OrElse
IsMinecraftUnchanged AndAlso SelectedFabric IsNot Nothing AndAlso SelectedOptiFine IsNot Nothing AndAlso CardOptiFabric.MainSwap.Visibility = Visibility.Visible, BtnOptiFabricClear)
'Mod Loader
For Each Loader In {
(Kind:=ModLoaders.Forge, Card:=CardForge, ComparePanel:=PanForgeCompare, CurrentPanel:=PanForgeInfo, Label:=LabForge, RestoreButton:=BtnForgeRestore, ClearButton:=BtnForgeClear),
(Kind:=ModLoaders.NeoForge, Card:=CardNeoForge, ComparePanel:=PanNeoForgeCompare, CurrentPanel:=PanNeoForgeInfo, Label:=LabNeoForge, RestoreButton:=BtnNeoForgeRestore, ClearButton:=BtnNeoForgeClear),
(Kind:=ModLoaders.Fabric, Card:=CardFabric, ComparePanel:=PanFabricCompare, CurrentPanel:=PanFabricInfo, Label:=LabFabric, RestoreButton:=BtnFabricRestore, ClearButton:=BtnFabricClear),
(Kind:=ModLoaders.LiteLoader, Card:=CardLiteLoader, ComparePanel:=PanLiteLoaderCompare, CurrentPanel:=PanLiteLoaderInfo, Label:=LabLiteLoader, RestoreButton:=BtnLiteLoaderRestore, ClearButton:=BtnLiteLoaderClear)}
Dim OldVersion As String = Nothing, NewVersion As String = Nothing
InitialTarget.ModLoaderVersions.TryGetValue(Loader.Kind, OldVersion)
Current.ModLoaderVersions.TryGetValue(Loader.Kind, NewVersion)
Dim CanRestore = OldVersion Is Nothing OrElse IsMinecraftUnchanged AndAlso
Loader.Card.MainSwap.Visibility = Visibility.Visible AndAlso Loader.RestoreButton.Tag IsNot Nothing
If Loader.Kind = ModLoaders.Forge AndAlso OldVersion IsNot Nothing AndAlso CanRestore Then
CanRestore = SelectedOptiFine Is Nothing OrElse IsOptiFineSuitForForge(SelectedOptiFine, DirectCast(Loader.RestoreButton.Tag, DlForgeVersionEntry))
End If
If Loader.Kind = ModLoaders.Fabric AndAlso OldVersion IsNot Nothing Then
Dim TargetVersion = VanillaName.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3")
CanRestore = CardFabric.Visibility = Visibility.Visible AndAlso CardFabric.MainSwap.Visibility = Visibility.Visible AndAlso
DlFabricListLoader.Output.Value("loader").OfType(Of JObject).Any(Function(v) v("version").ToString = OldVersion) AndAlso
DlFabricListLoader.Output.Value("game").OfType(Of JObject).Any(Function(v) v("version").ToString = TargetVersion)
End If
ShowComparison(Loader.ComparePanel, Loader.CurrentPanel, OldVersion, NewVersion, Loader.Label.Text,
Loader.RestoreButton, CanRestore, Loader.ClearButton)
Next
UpdateStartButton()
End Sub
Private Sub ShowComparison(ComparePanel As Grid, CurrentPanel As FrameworkElement, OldVersion As String, NewVersion As String, NewText As String,
RestoreButton As MyIconButton, CanRestore As Boolean, Optional ClearButton As FrameworkElement = Nothing)
'获取不可安装的原因
Dim UnavailableReason As String = Nothing
If NewVersion Is Nothing Then
If RestoreButton Is BtnOptiFineRestore Then
UnavailableReason = LoadOptiFineGetError()
ElseIf RestoreButton Is BtnForgeRestore Then
UnavailableReason = If(McVersion.IsStandardFormat(VanillaName), LoadForgeGetError(), "无")
ElseIf RestoreButton Is BtnNeoForgeRestore Then
UnavailableReason = If(CardNeoForge.Visibility = Visibility.Collapsed, "无", LoadNeoForgeGetError())
ElseIf RestoreButton Is BtnFabricRestore Then
UnavailableReason = If(CardFabric.Visibility = Visibility.Collapsed, "无", LoadFabricGetError())
ElseIf RestoreButton Is BtnLiteLoaderRestore Then
UnavailableReason = If(CardLiteLoader.Visibility = Visibility.Collapsed, "无", LoadLiteLoaderGetError())
ElseIf RestoreButton Is BtnFabricApiRestore Then
UnavailableReason = LoadFabricApiGetError()
ElseIf RestoreButton Is BtnOptiFabricRestore Then
UnavailableReason = LoadOptiFabricGetError()
End If
NewText = If(UnavailableReason, "可以添加")
End If
'修改对比
Dim IsPending = NewVersion Is Nothing AndAlso (NewText = "获取中……" OrElse NewText.StartsWithF("获取失败:"))
Dim IsChanged = OldVersion <> NewVersion AndAlso Not IsPending
CanRestore = IsChanged AndAlso OldVersion IsNot Nothing AndAlso CanRestore
ComparePanel.Visibility = If(IsChanged, Visibility.Visible, Visibility.Collapsed)
CurrentPanel.Visibility = If(IsChanged, Visibility.Collapsed, Visibility.Visible)
'还原与取消安装按钮
RestoreButton.Visibility = If(CanRestore, Visibility.Visible, Visibility.Collapsed)
If ClearButton IsNot Nothing Then ClearButton.ToolTip = If(OldVersion Is Nothing, "取消安装", "移除")
Dim HasClearButton = ClearButton IsNot Nothing AndAlso ClearButton.Visibility = Visibility.Visible
RestoreButton.Margin = New Thickness(0, 5, If(CanRestore AndAlso HasClearButton, 60, 32), 0)
ComparePanel.Margin = New Thickness(ComparePanel.Margin.Left, ComparePanel.Margin.Top,
If(CanRestore AndAlso HasClearButton, 110, 80), ComparePanel.Margin.Bottom)
If Not IsChanged Then Return
'显示修改前已安装组件的对比卡片
If OldVersion IsNot Nothing Then
Dim Card = CType(ComparePanel.Parent, MyCard)
If Card.Visibility = Visibility.Collapsed Then
Card.IsSwapped = True
If Card.MainSwap IsNot Nothing Then Card.MainSwap.Visibility = Visibility.Collapsed
Card.Visibility = Visibility.Visible
End If
End If
Dim OldSide = CType(ComparePanel.Children(0), DockPanel)
Dim NewSide = CType(ComparePanel.Children(2), DockPanel)
OldSide.Children(0).Visibility = If(OldVersion Is Nothing, Visibility.Collapsed, Visibility.Visible)
CType(OldSide.Children(1), TextBlock).Margin = If(OldVersion Is Nothing, New Thickness(0), New Thickness(7, 0, 0, 0))
NewSide.Children(0).Visibility = If(NewVersion Is Nothing, Visibility.Collapsed, Visibility.Visible)
Dim NewLabel = CType(NewSide.Children(1), TextBlock)
NewLabel.Margin = If(NewVersion Is Nothing, New Thickness(0), New Thickness(7, 0, 0, 0))
NewLabel.Text = If(NewVersion Is Nothing, "移除", NewText)
If NewVersion Is Nothing AndAlso UnavailableReason IsNot Nothing Then
NewLabel.Inlines.Add(New Run($"({If(UnavailableReason = "无", "无可用版本", UnavailableReason)})") With {.Foreground = ColorGray4})
End If
End Sub
Private Sub RestoreComponent(sender As Object, e As EventArgs) Handles BtnMinecraftRestore.Click, BtnOptiFineRestore.Click, BtnForgeRestore.Click,
BtnNeoForgeRestore.Click, BtnFabricRestore.Click, BtnFabricApiRestore.Click, BtnOptiFabricRestore.Click, BtnLiteLoaderRestore.Click
Dim Button = DirectCast(sender, MyIconButton)
'恢复初始选择
If Button Is BtnMinecraftRestore Then
IsRestoringComponents = True
Try
PendingComponents.Clear()
MinecraftSelected(McDownloadListItem(DirectCast(Button.Tag, JObject), Nothing, False).Init(), Nothing)
If ForgeVersionLoader IsNot Nothing Then LoadForge.State = ForgeVersionLoader
'恢复初始的组件
SelectedOptiFine = TryCast(BtnOptiFineRestore.Tag, DlOptiFineListEntry)
SelectedForge = TryCast(BtnForgeRestore.Tag, DlForgeVersionEntry)
SelectedNeoForge = TryCast(BtnNeoForgeRestore.Tag, DlNeoForgeListEntry)
SelectedFabric = TryCast(BtnFabricRestore.Tag, String)
SelectedLiteLoader = TryCast(BtnLiteLoaderRestore.Tag, DlLiteLoaderListEntry)
SelectedFabricApi = TryCast(BtnFabricApiRestore.Tag, ResourceVersion)
SelectedOptiFabric = TryCast(BtnOptiFabricRestore.Tag, ResourceVersion)
AutoSelectedFabricApi = SelectedFabric IsNot Nothing
AutoSelectedOptiFabric = SelectedFabric IsNot Nothing AndAlso SelectedOptiFine IsNot Nothing
Finally
IsRestoringComponents = False
End Try
ReloadComponentLists()
Return
ElseIf Button Is BtnOptiFineRestore Then
SelectedOptiFine = TryCast(Button.Tag, DlOptiFineListEntry)
AutoSelectedOptiFabric = SelectedOptiFine IsNot Nothing AndAlso SelectedFabric IsNot Nothing
If SelectedOptiFine Is Nothing Then SelectedOptiFabric = Nothing
ElseIf Button Is BtnForgeRestore Then
SelectedForge = TryCast(Button.Tag, DlForgeVersionEntry)
ElseIf Button Is BtnNeoForgeRestore Then
SelectedNeoForge = TryCast(Button.Tag, DlNeoForgeListEntry)
ElseIf Button Is BtnFabricRestore Then
SelectedFabric = TryCast(Button.Tag, String)
AutoSelectedFabricApi = SelectedFabric IsNot Nothing
AutoSelectedOptiFabric = SelectedFabric IsNot Nothing AndAlso SelectedOptiFine IsNot Nothing
If SelectedFabric Is Nothing Then
SelectedFabricApi = Nothing
SelectedOptiFabric = Nothing
End If
ElseIf Button Is BtnFabricApiRestore Then
SelectedFabricApi = TryCast(Button.Tag, ResourceVersion)
AutoSelectedFabricApi = True
ElseIf Button Is BtnOptiFabricRestore Then
SelectedOptiFabric = TryCast(Button.Tag, ResourceVersion)
AutoSelectedOptiFabric = True
ElseIf Button Is BtnLiteLoaderRestore Then
SelectedLiteLoader = TryCast(Button.Tag, DlLiteLoaderListEntry)
End If
DirectCast(Button.Parent, MyCard).IsSwapped = True
'刷新组件
ReloadComponentLists()
End Sub
Private Sub CardMinecraft_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles CardMinecraft.MouseLeftButtonUp
ShowMinecraftVersionList()
e.Handled = True
End Sub
Private Sub UpdateStartButton() Handles TextSelectName.TextChanged, TextSelectName.ValidateChanged
BtnStart.IsEnabled = TextSelectName.IsValidated AndAlso InitialTarget IsNot Nothing AndAlso
Not PendingComponents.Any() AndAlso
(Not IsVanillaVersionReliable OrElse Not InitialTarget.IsEquivalentTo(GetSelectedComponents()) OrElse
Not String.Equals(TextSelectName.Text, OriginalName, StringComparison.Ordinal))
End Sub
Private Sub UpdateOriginalNameVisibility() Handles TextSelectName.TextChanged
Dim IsNameChanged = Not String.Equals(TextSelectName.Text, OriginalName, StringComparison.Ordinal)
Dim NewVisibility = If(IsNameChanged, Visibility.Visible, Visibility.Collapsed)
If LabSelectName.Visibility = NewVisibility Then Return
LabSelectName.Visibility = NewVisibility
BtnOriginalNameRestore.Visibility = NewVisibility
Dim RightMargin = If(IsNameChanged, 40, 16)
If IsInSelectPage AndAlso AniControlEnabled = 0 Then
AniStart(AaDouble(Sub(Delta) TextSelectName.Margin = New Thickness(56, 16, TextSelectName.Margin.Right + Delta, 0),
RightMargin - TextSelectName.Margin.Right, 150,, New AniEaseOutFluent), "DownloadModify Name Margin")
Else
AniStop("DownloadModify Name Margin")
TextSelectName.Margin = New Thickness(56, 16, RightMargin, 0)
End If
End Sub
Private Sub BtnOriginalNameRestore_Click(sender As Object, e As EventArgs) Handles BtnOriginalNameRestore.Click
TextSelectName.Text = OriginalName
IsSelectNameEdited = Not String.Equals(OriginalName, GetSelectName(), StringComparison.Ordinal)
End Sub
Public Overrides Sub StartInstall(Request As McInstallRequest)
UpdateStartButton()
If Not BtnStart.IsEnabled Then Return
Dim TargetInstance = InitLoader.Input
Dim Target = Request.Components
Dim InstanceName = Request.NewInstanceName
Dim IsNameChanged = Not String.Equals(InstanceName, TargetInstance.Name, StringComparison.Ordinal)
Request.NewInstanceName = If(IsNameChanged, InstanceName, Nothing)
Request.InstanceFolder = TargetInstance.PathVersion
'警告
Dim Warnings As New List(Of String)
If (SelectedForge IsNot Nothing OrElse SelectedNeoForge IsNot Nothing OrElse SelectedFabric IsNot Nothing) AndAlso
Not PathUtils.IsSame(TargetInstance.PathIndie, TargetInstance.PathVersion) Then
Warnings.Add("该版本尚未开启版本隔离,可能与别的 MC 版本共用同一个 Mod 和存档文件夹。" & vbCrLf &
"修改版本后,已有的 Mod、存档等需要手动迁移到新的游戏目录。")
End If
Dim IsMinecraftVersionChanged = InitialTarget.MinecraftVersion <> Target.MinecraftVersion
If Not IsVanillaVersionReliable AndAlso Not IsMinecraftVersionChanged Then
Warnings.Add($"PCL 未能精准识别该版本,请确认它是不是 Minecraft {InitialTarget.MinecraftVersion},如果不正确,请取消并选择正确的版本!")
End If
If Not Configs.HintModifyInstanceWarning.Get() Then
Warnings.Add("如果该版本安装了 PCL 不兼容的加载器,或经过第三方修改,这些改动将会丢失!")
End If
Dim IsModLoaderChanged As Boolean = False
For Each Loader In InitialTarget.ModLoaderVersions.Keys.Union(Target.ModLoaderVersions.Keys)
Dim CurrentVersion As String = Nothing, TargetVersion As String = Nothing
Dim HasCurrent = InitialTarget.ModLoaderVersions.TryGetValue(Loader, CurrentVersion)
Dim HasTarget = Target.ModLoaderVersions.TryGetValue(Loader, TargetVersion)
If HasCurrent <> HasTarget OrElse CurrentVersion <> TargetVersion Then
IsModLoaderChanged = True
Exit For
End If
Next
If (IsModLoaderChanged OrElse IsMinecraftVersionChanged) AndAlso InitialTarget.ModLoaderVersions.Any Then
Warnings.Add("现有的 Mod、资源包等在修改后可能会不兼容!")
End If
If Warnings.Any() Then
If MyMsgBox("请注意:" & vbCrLf & "• " & Warnings.Join(vbCrLf & "• "), "修改确认", "开始修改", "取消", IsWarn:=True) <> 1 Then Return
End If
If Not Configs.HintModifyInstanceWarning.Get() Then Configs.HintModifyInstanceWarning.Set(True)
'开始修改
If IsVanillaVersionReliable AndAlso InitialTarget.IsEquivalentTo(Target) Then
'仅重命名
Try
Dim OldPath = TargetInstance.PathVersion
Dim RenamedInstance = TargetInstance.Rename(InstanceName)
If PageInstanceLeft.Instance IsNot Nothing AndAlso PathUtils.IsSame(PageInstanceLeft.Instance.PathVersion, OldPath) Then PageInstanceLeft.Instance = RenamedInstance
If McInstanceSelected IsNot Nothing AndAlso PathUtils.IsSame(McInstanceSelected.PathVersion, OldPath) Then McInstanceSelected = RenamedInstance
PageInstanceLeft.ReloadCurrentJava()
Hint("重命名成功!", HintType.Green)
LoaderFolderRun(McInstanceListLoader, McFolderSelected, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\")
FrmMain.PageBack()
Catch ex As Exception
Logger.Error(ex, "重命名版本失败", LogBehavior.Alert)
End Try
Else
'实际修改
If McModificationStart(Request, TargetInstance) Then FrmMain.PageBack()
End If
End Sub
End Class
@@ -64,10 +64,10 @@ Public Class PageDownloadResourceDetail
If IsXSpecial Then Return 1 If IsXSpecial Then Return 1
If IsYSpecial Then Return -1 If IsYSpecial Then Return -1
'比较版本号 '比较版本号
Dim VersionCodeSort = -CompareVersion(x.Replace(x.BeforeFirst(" ") & " ", ""), y.Replace(y.BeforeFirst(" ") & " ", "")) Dim VersionCodeSort = -McUtils.CompareVersion(x.Replace(x.BeforeFirst(" ") & " ", ""), y.Replace(y.BeforeFirst(" ") & " ", ""))
If VersionCodeSort <> 0 Then Return VersionCodeSort If VersionCodeSort <> 0 Then Return VersionCodeSort
'比较全部 '比较全部
Return -CompareVersion(x, y) Return -McUtils.CompareVersion(x, y)
End Function End Function
Public Sub New(Optional Topmost As String = "") Public Sub New(Optional Topmost As String = "")
Me.Topmost = If(Topmost, "") Me.Topmost = If(Topmost, "")
@@ -98,7 +98,7 @@ Public Class PageDownloadResourceDetail
Dim UpdateFilters = Dim UpdateFilters =
Sub() Sub()
Filters = Results.SelectMany(Function(v) v.GameVersions).Select(Function(v) GetGroupedVersionName(v, GroupedDrop, GroupedOld)). Filters = Results.SelectMany(Function(v) v.GameVersions).Select(Function(v) GetGroupedVersionName(v, GroupedDrop, GroupedOld)).
Distinct.OrderByDescending(Function(s) s, New VersionComparer).ToList Distinct.OrderByDescending(Function(s) s, Comparer(Of String).Create(Function(x, y) McUtils.CompareVersion(x, y))).ToList
End Sub End Sub
'确定分组方式 '确定分组方式
@@ -174,7 +174,7 @@ GroupDone:
Dim Loaders As New List(Of String) Dim Loaders As New List(Of String)
If Project.ModLoaders.Flags.Count > 1 AndAlso '工程至少有两个加载器 If Project.ModLoaders.Flags.Count > 1 AndAlso '工程至少有两个加载器
Version.ResourceType = ResourceTypes.Mod AndAlso '是 Mod Version.ResourceType = ResourceTypes.Mod AndAlso '是 Mod
McVersion.IsFormatFit(VerName) Then '不是 “快照版本” 之类的 McVersion.IsStandardFormat(VerName) Then '不是 “快照版本” 之类的
Loaders.AddRange(Version.ModLoaders.Flags.Select(Function(f) f.ToString & " ")) Loaders.AddRange(Version.ModLoaders.Flags.Select(Function(f) f.ToString & " "))
End If End If
If Not Loaders.Any() Then Loaders.Add("") '保底加一个空的,确保它在一张卡片里 If Not Loaders.Any() Then Loaders.Add("") '保底加一个空的,确保它在一张卡片里
@@ -206,16 +206,17 @@ GroupDone:
If Not Pair.Value.Any() Then Continue For If Not Pair.Value.Any() Then Continue For
If Pair.Key = TargetCardName.Replace("(所选版本)", "") Then Continue For If Pair.Key = TargetCardName.Replace("(所选版本)", "") Then Continue For
'增加卡片 '增加卡片
Dim NewCard As New MyCard With {.Title = Pair.Key, .Margin = New Thickness(0, 0, 0, 15), .SwapType = If(TargetResourceType = ResourceTypes.ModPack, 9, 8)} '9 是安装,8 是另存为 Dim NewCard As New MyCard With {.Title = Pair.Key, .Margin = New Thickness(0, 0, 0, 15)}
Dim NewStack As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0), .Tag = Pair.Value} Dim NewStack As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0), .Tag = Pair.Value}
NewCard.Children.Add(NewStack) NewCard.Children.Add(NewStack)
NewCard.SwapControl = NewStack NewCard.SwapControl = NewStack
NewCard.SetContentBuilder(AddressOf InitializeStackResourceVersions)
PanResults.Children.Add(NewCard) PanResults.Children.Add(NewCard)
'确定卡片是否默认展开 '确定卡片是否默认展开
If Pair.Key = TargetCardName OrElse If Pair.Key = TargetCardName OrElse
(FrmMain.PageCurrent.Additional IsNot Nothing AndAlso '#2761 (FrmMain.PageCurrent.Additional IsNot Nothing AndAlso '#2761
CType(FrmMain.PageCurrent.Additional(1), List(Of String)).Contains(NewCard.Title)) Then CType(FrmMain.PageCurrent.Additional(1), List(Of String)).Contains(NewCard.Title)) Then
MyCard.StackInstall(NewStack, If(TargetResourceType = ResourceTypes.ModPack, 9, 8), Pair.Key) '9 是安装,8 是另存为 NewCard.BuildContent()
Else Else
NewCard.IsSwapped = True NewCard.IsSwapped = True
End If End If
@@ -240,13 +241,53 @@ GroupDone:
Logger.Error(ex, "可视化工程下载列表出错") Logger.Error(ex, "可视化工程下载列表出错")
End Try End Try
End Sub End Sub
''' <summary>
''' 根据卡片中的 StackPanel.Tag 将资源版本列表数据实例化为 UI。
''' </summary>
Private Sub InitializeStackResourceVersions(Stack As StackPanel)
Dim Versions = CType(Stack.Tag, List(Of ResourceVersion)).SortByComparison(Function(a, b) a.ReleaseDate > b.ReleaseDate)
Dim HasDuplicates = Versions.DistinctBy(Function(a) a.Display).Count <> Versions.Count
Dim Dependences As List(Of String)
'获取并添加前置信息
If TargetResourceType = ResourceTypes.ModPack Then
Dependences = New List(Of String) '整合包的前置信息会显示其中包含的一大堆 Mod,不是真正意义的前置
Else
Dependences = Versions.SelectMany(Function(f) f.Dependencies).Distinct.Ordered.ToList()
End If
If Dependences.Any() Then
Dependences = Dependences.Where(
Function(d)
If Not ResourceProject.Cache.ContainsKey(d) Then Logger.Warn($"未找到 ID {d} 的前置信息")
Return ResourceProject.Cache.ContainsKey(d)
End Function).ToList
'添加开头间隔
Stack.Children.Add(New TextBlock With {.Text = "前置资源", .FontSize = 14, .HorizontalAlignment = HorizontalAlignment.Left, .Margin = New Thickness(6, 2, 0, 5)})
'添加前置列表
For Each Dep In Dependences
Dim Item = ResourceProject.Cache(Dep).ToResourceItem(False, False)
Stack.Children.Add(Item)
Next
'添加结尾间隔
Stack.Children.Add(New TextBlock With {.Text = "版本列表", .FontSize = 14, .HorizontalAlignment = HorizontalAlignment.Left, .Margin = New Thickness(6, 12, 0, 5)})
End If
'实例化每个元素
For Each Version In Versions
If Not RestrictMcDownload AndAlso TargetResourceType = ResourceTypes.ModPack Then
Stack.Children.Add(Version.ToListItem(AddressOf Install_Click, AddressOf Save_Click, IsBadDisplay:=HasDuplicates))
Else
Stack.Children.Add(Version.ToListItem(AddressOf Save_Click, IsBadDisplay:=HasDuplicates))
End If
Next
Stack.Children.Add(New FrameworkElement With {.Height = 18})
Stack.Tag = Nothing
End Sub
Private Function GetGroupedVersionName(Name As String, GroupedByDrop As Boolean, FoldOld As Boolean) As String Private Function GetGroupedVersionName(Name As String, GroupedByDrop As Boolean, FoldOld As Boolean) As String
Dim Drop = McVersion.VersionToDrop(Name) Dim Drop = McVersion.VersionToDrop(Name)
If Name Is Nothing Then If Name Is Nothing Then
Return "其他" Return "其他"
ElseIf Name.Contains("w") OrElse Drop = 209 Then ElseIf Name.Contains("w") OrElse Drop = 209 Then
Return "快照版" Return "快照版"
ElseIf Not McVersion.IsFormatFit(Name) OrElse (FoldOld AndAlso Drop < 120) Then ElseIf Not McVersion.IsStandardFormat(Name) OrElse (FoldOld AndAlso Drop < 120) Then
Return "远古版" Return "远古版"
ElseIf GroupedByDrop Then ElseIf GroupedByDrop Then
Return McVersion.DropToVersion(Drop) Return McVersion.DropToVersion(Drop)
@@ -469,19 +510,23 @@ GroupDone:
'弹窗要求选择保存位置 '弹窗要求选择保存位置
Dim Target As String Dim Target As String
Dim Ext As String = If(File.ResourceType = ResourceTypes.Mod, Dim Ext As String = If(File.ResourceType = ResourceTypes.Mod,
If(File.FileName.EndsWithF(".litemod"), "litemod", "jar"), If(PathUtils.GetExtension(File.FileName) = "litemod", "litemod", "jar"),
If(File.FileName.EndsWithF(".mrpack"), "mrpack", "zip")) If(PathUtils.GetExtension(File.FileName) = "mrpack", "mrpack", "zip"))
Target = Dialogs.SaveFile("选择保存位置", FileName, DefaultFolder, {(Ext, Desc & "文件")}) Target = Dialogs.SaveFile("选择保存位置", FileName, DefaultFolder, {(Ext, Desc & "文件")})
If Target Is Nothing Then Return If Target Is Nothing Then Return
'构造步骤加载器 '构造步骤加载器
Dim LoaderName As String = Desc & "下载:" & PathUtils.GetFileNameWithoutExtension(Target) & " " Dim LoaderName As String = Desc & "下载:" & PathUtils.GetFileNameWithoutExtension(Target) & " "
If Target <> DefaultFolder Then CachedFolder(File.ResourceType) = PathUtils.RemoveLastPart(Target) If Not PathUtils.IsSame(PathUtils.RemoveLastPart(Target), DefaultFolder) Then CachedFolder(File.ResourceType) = PathUtils.RemoveLastPart(Target)
Dim Loaders As New List(Of LoaderBase) Dim Loaders As New List(Of LoaderBase)
Loaders.Add(New LoaderDownload("下载文件", { Loaders.Add(New LoaderDownload("下载文件", {
File.ToNetFile(Target, If(TargetResourceType = ResourceTypes.Any, ResourceVersion.DownloadReason.Dependency, ResourceVersion.DownloadReason.Standalone)) File.ToNetFile(Target, If(TargetResourceType = ResourceTypes.Any, ResourceVersion.DownloadReason.Dependency, ResourceVersion.DownloadReason.Standalone))
}) With {.ProgressWeight = 6, .Block = True}) }) With {.ProgressWeight = 6, .Block = True})
'启动 '启动
Dim Loader As New LoaderCombo(Of Integer)(LoaderName, Loaders) With {.OnStateChanged = AddressOf LoaderStateChangedHintOnly} Dim Loader As New LoaderCombo(Of Integer)(LoaderName, Loaders) With {.OnStateChanged =
Sub(MyLoader)
LoaderStateChangedHintOnly(MyLoader)
If MyLoader.State = LoadState.Finished AndAlso File.ResourceType = ResourceTypes.ModPack Then OpenExplorer(Target)
End Sub}
Loader.Start(1) Loader.Start(1)
LoaderTaskbarAdd(Loader) LoaderTaskbarAdd(Loader)
FrmMain.BtnExtraDownload.ShowRefresh() FrmMain.BtnExtraDownload.ShowRefresh()
@@ -510,29 +555,4 @@ GroupDone:
PageLoaderRestart(IsForceRestart:=True) PageLoaderRestart(IsForceRestart:=True)
End Sub End Sub
''' <summary>
''' 预载包含大量 ProjectVersion 的卡片,添加必要的元素和前置列表。
''' </summary>
Public Shared Sub ResourceFilesCardPreload(Stack As StackPanel, Files As List(Of ResourceVersion))
'获取卡片对应的前置 ID
'如果为整合包就不会有 Dependencies 信息,所以不用管
Dim Deps As List(Of String) = Files.SelectMany(Function(f) f.Dependencies).Distinct.ToList()
Deps.Sort()
If Not Deps.Any() Then Return
Deps = Deps.Where(
Function(dep)
If Not ResourceProject.Cache.ContainsKey(dep) Then Logger.Warn($"未找到 ID {dep} 的前置信息")
Return ResourceProject.Cache.ContainsKey(dep)
End Function).ToList
'添加开头间隔
Stack.Children.Add(New TextBlock With {.Text = "前置资源", .FontSize = 14, .HorizontalAlignment = HorizontalAlignment.Left, .Margin = New Thickness(6, 2, 0, 5)})
'添加前置列表
For Each Dep In Deps
Dim Item = ResourceProject.Cache(Dep).ToResourceItem(False, False)
Stack.Children.Add(Item)
Next
'添加结尾间隔
Stack.Children.Add(New TextBlock With {.Text = "版本列表", .FontSize = 14, .HorizontalAlignment = HorizontalAlignment.Left, .Margin = New Thickness(6, 12, 0, 5)})
End Sub
End Class End Class
@@ -90,11 +90,11 @@ Public Class PageResource
Next Next
Return ComboSearchLoader.Items(0) Return ComboSearchLoader.Items(0)
End Function End Function
If TargetInstance.Version.HasForge Then If TargetInstance.Version.ModLoaders.HasFlagF(ModLoaders.Forge) Then
ComboSearchLoader.SelectedItem = GetTargetItemByName("Forge") ComboSearchLoader.SelectedItem = GetTargetItemByName("Forge")
ElseIf TargetInstance.Version.HasFabric Then ElseIf TargetInstance.Version.ModLoaders.HasFlagF(ModLoaders.Fabric) Then
ComboSearchLoader.SelectedItem = GetTargetItemByName("Fabric") ComboSearchLoader.SelectedItem = GetTargetItemByName("Fabric")
ElseIf TargetInstance.Version.HasNeoForge Then ElseIf TargetInstance.Version.ModLoaders.HasFlagF(ModLoaders.NeoForge) Then
ComboSearchLoader.SelectedItem = GetTargetItemByName("NeoForge") ComboSearchLoader.SelectedItem = GetTargetItemByName("NeoForge")
End If End If
TargetInstance = Nothing TargetInstance = Nothing
@@ -335,7 +335,7 @@ Public Class MyLocalModItem
BtnUpdate.Visibility = Visibility.Collapsed BtnUpdate.Visibility = Visibility.Collapsed
End If End If
'标题与描述 '标题与描述
Dim DescFileName As String = Entry.File.Name.BeforeLast(".") Dim DescFileName As String = PathUtils.GetFileNameWithoutExtension(Entry.EnabledName)
Dim NewDescription As String Dim NewDescription As String
If Settings.Get(Of Integer)("ToolModLocalNameStyle") = 1 Then If Settings.Get(Of Integer)("ToolModLocalNameStyle") = 1 Then
'标题显示文件名,详情显示译名 '标题显示文件名,详情显示译名
@@ -22,14 +22,14 @@ Public Class PageInstanceExport
End Sub End Sub
Private Sub PageInstanceExport_Loaded() Handles Me.Loaded Private Sub PageInstanceExport_Loaded() Handles Me.Loaded
AniControlEnabled += 1 AniControlEnabled += 1
If CurrentInstance <> PageInstanceLeft.Instance.PathVersion Then RefreshAll() '切换到了另一个版本,重置页面 If Not PathUtils.IsSame(CurrentInstance, PageInstanceLeft.Instance.PathVersion) Then RefreshAll() '切换到了另一个版本,重置页面
CustomEventService.SetEventData(BtnAdvancedHelp, CustomEventService.SetEventData(BtnAdvancedHelp,
If(BuildType = BuildTypes.Release, "指南/整合包制作 - Public.json", "指南/整合包制作 - Snapshot.json")) If(BuildType = BuildTypes.Release, "指南/整合包制作 - Public.json", "指南/整合包制作 - Snapshot.json"))
AniControlEnabled -= 1 AniControlEnabled -= 1
End Sub End Sub
Public Sub RefreshAll() Implements IRefreshable.Refresh Public Sub RefreshAll() Implements IRefreshable.Refresh
Logger.Info("刷新导出页面") Logger.Info("刷新导出页面")
HintOptiFine.Visibility = If(PageInstanceLeft.Instance.Version.HasOptiFine, Visibility.Visible, Visibility.Collapsed) HintOptiFine.Visibility = If(PageInstanceLeft.Instance.Version.HasOptiFine AndAlso Not PageInstanceLeft.Instance.Version.OptiFineAsMod, Visibility.Visible, Visibility.Collapsed)
CurrentInstance = PageInstanceLeft.Instance.PathVersion CurrentInstance = PageInstanceLeft.Instance.PathVersion
TextExportName.Text = "" TextExportName.Text = ""
TextExportName.HintText = PageInstanceLeft.Instance.Name TextExportName.HintText = PageInstanceLeft.Instance.Name
@@ -114,7 +114,7 @@ Public Class PageInstanceExport
'确认选项是否应该被显示 '确认选项是否应该被显示
Dim IsVisible = Dim IsVisible =
Function(TargetOption As ExportOption) As Boolean Function(TargetOption As ExportOption) As Boolean
'检查需要 OptiFine 或 Mod 加载器 '检查需要 OptiFine(例如光影)或 Mod 加载器(例如 mods/)
If TargetOption.RequireOptiFine AndAlso Not PageInstanceLeft.Instance.Version.HasOptiFine Then Return False If TargetOption.RequireOptiFine AndAlso Not PageInstanceLeft.Instance.Version.HasOptiFine Then Return False
If TargetOption.RequireModLoader AndAlso Not PageInstanceLeft.Instance.Modable Then Return False If TargetOption.RequireModLoader AndAlso Not PageInstanceLeft.Instance.Modable Then Return False
If TargetOption.RequireModLoaderOrOptiFine AndAlso Not PageInstanceLeft.Instance.Version.HasOptiFine AndAlso Not PageInstanceLeft.Instance.Modable Then Return False If TargetOption.RequireModLoaderOrOptiFine AndAlso Not PageInstanceLeft.Instance.Version.HasOptiFine AndAlso Not PageInstanceLeft.Instance.Modable Then Return False
@@ -257,9 +257,9 @@ Public Class PageInstanceExport
For Each ShaderPackCheckBox In PanOptionsShaderPacks.Children.OfType(Of MyCheckBox)() For Each ShaderPackCheckBox In PanOptionsShaderPacks.Children.OfType(Of MyCheckBox)()
If Not ShaderPackCheckBox.Checked Then Continue For If Not ShaderPackCheckBox.Checked Then Continue For
Dim ShaderPackOption = GetExportOption(ShaderPackCheckBox) Dim ShaderPackOption = GetExportOption(ShaderPackCheckBox)
Dim SettingFileName = ShaderPackOption.Rules.TrimEnd("/") & ".txt" Dim SettingsFile = $"shaderpacks/{ShaderPackOption.Title}.txt"
If Not FileUtils.Exists(Path.Combine(PageInstanceLeft.Instance.PathIndie, SettingFileName)) Then Continue For If Not FileUtils.Exists(Path.Combine(PageInstanceLeft.Instance.PathIndie, SettingsFile)) Then Continue For
Rules.Add(StringUtils.LikePatternEscape($"shaderpacks/{SettingFileName}")) Rules.Add(StringUtils.LikePatternEscape(SettingsFile))
Next Next
End If End If
@@ -457,11 +457,11 @@ Public Class PageInstanceExport
'缓存所需参数 '缓存所需参数
Dim CacheFolder = RequestTaskTempFolder() Dim CacheFolder = RequestTaskTempFolder()
Dim OverridesFolder = CacheFolder & "modpack\overrides\" Dim OverridesFolder = CacheFolder & "modpack\overrides\"
Dim McVersion = PageInstanceLeft.Instance Dim TargetInstance = PageInstanceLeft.Instance
Dim IncludeJava As Boolean = Dim IncludeJava As Boolean =
(RulesOverrides Is Nothing AndAlso CheckOptionsJava.Visibility = Visibility.Visible AndAlso CheckOptionsJava.Checked) OrElse (RulesOverrides Is Nothing AndAlso CheckOptionsJava.Visibility = Visibility.Visible AndAlso CheckOptionsJava.Checked) OrElse
(RulesOverrides IsNot Nothing AndAlso CheckOptionsJava.Checked AndAlso Settings.Get(Of Integer)("VersionArgumentJavaV2", McVersion) = 2) (RulesOverrides IsNot Nothing AndAlso CheckOptionsJava.Checked AndAlso Settings.Get(Of Integer)("VersionArgumentJavaV2", TargetInstance) = 2)
Dim PathIndie As String = McVersion.PathIndie Dim PathIndie As String = TargetInstance.PathIndie
Dim CheckHostedAssets As Boolean = Not CheckAdvancedInclude.Checked Dim CheckHostedAssets As Boolean = Not CheckAdvancedInclude.Checked
Dim ModrinthUploadMode As Boolean = CheckAdvancedModrinth.Checked Dim ModrinthUploadMode As Boolean = CheckAdvancedModrinth.Checked
Dim IncludePCL As Boolean = CheckOptionsPcl.Checked Dim IncludePCL As Boolean = CheckOptionsPcl.Checked
@@ -498,7 +498,7 @@ Public Class PageInstanceExport
'文件夹:进一步搜索 '文件夹:进一步搜索
For Each SubFolder In Folder.EnumerateDirectories("*", SearchOption.TopDirectoryOnly) For Each SubFolder In Folder.EnumerateDirectories("*", SearchOption.TopDirectoryOnly)
'跳过部分又没用文件又多的文件夹,加快搜索 '跳过部分又没用文件又多的文件夹,加快搜索
If {"assets", "versions", "libraries"}.Contains(SubFolder.Name) AndAlso PathIndie = PathUtils.RemoveExtendedPrefix(Folder.FullName) Then Continue For If {"assets", "versions", "libraries"}.Contains(SubFolder.Name) AndAlso PathUtils.IsSame(PathIndie, Folder.FullName) Then Continue For
If {"structureCacheV1", ".fabric", ".git", "avatar-cache", "cosmetic-cache"}.Contains(SubFolder.Name) Then Continue For If {"structureCacheV1", ".fabric", ".git", "avatar-cache", "cosmetic-cache"}.Contains(SubFolder.Name) Then Continue For
SearchFolder(SubFolder) SearchFolder(SubFolder)
Next Next
@@ -536,10 +536,10 @@ Public Class PageInstanceExport
Loader.Progress = 0.85 Loader.Progress = 0.85
'复制 Java '复制 Java
If IncludeJava Then If IncludeJava Then
Dim JavaToExport = SelectOrDownloadJava(McVersion, False, Loader.CreateCancellationToken, Loader.CreateSyncProgressProvider(0.85, 0.93)) Dim JavaToExport = SelectOrDownloadJava(TargetInstance, False, Loader.CreateCancellationToken, Loader.CreateSyncProgressProvider(0.85, 0.93))
If JavaToExport Is Nothing Then Throw New FileNotFoundException("版本文件夹中没有找到可导出的 Java。") If JavaToExport Is Nothing Then Throw New FileNotFoundException("版本文件夹中没有找到可导出的 Java。")
Dim JavaFolder = PathUtils.RemoveLastPart(JavaToExport.Folder) Dim JavaFolder = PathUtils.RemoveLastPart(JavaToExport.Folder)
Dim DestFolder As String = Path.Combine(OverridesFolder, JavaFolder.AfterFirst(McVersion.PathVersion, True)) Dim DestFolder As String = Path.Combine(OverridesFolder, JavaFolder.AfterFirst(TargetInstance.PathVersion, True))
DirectoryUtils.Copy(JavaFolder, DestFolder) DirectoryUtils.Copy(JavaFolder, DestFolder)
Logger.Info($"已复制版本文件夹中的 Java:{JavaFolder} → {DestFolder}") Logger.Info($"已复制版本文件夹中的 Java:{JavaFolder} → {DestFolder}")
End If End If
@@ -563,7 +563,7 @@ Public Class PageInstanceExport
Next Next
Loader.Progress = 0.97 Loader.Progress = 0.97
'复制 PCL 版本设置 '复制 PCL 版本设置
If DirectoryUtils.Exists(McVersion.PathVersion & "PCL\") Then DirectoryUtils.Copy(McVersion.PathVersion & "PCL\", OverridesFolder & "PCL\") If DirectoryUtils.Exists(TargetInstance.PathVersion & "PCL\") Then DirectoryUtils.Copy(TargetInstance.PathVersion & "PCL\", OverridesFolder & "PCL\")
WriteIni(OverridesFolder & "PCL\Setup.ini", "IsStar", False) WriteIni(OverridesFolder & "PCL\Setup.ini", "IsStar", False)
'复制 PCL 本体(正式版) '复制 PCL 本体(正式版)
If BuildType = BuildTypes.Release AndAlso IncludePCL Then FileUtils.Copy(PathExe, CacheFolder & "Plain Craft Launcher.exe") If BuildType = BuildTypes.Release AndAlso IncludePCL Then FileUtils.Copy(PathExe, CacheFolder & "Plain Craft Launcher.exe")
@@ -686,16 +686,16 @@ Public Class PageInstanceExport
Next Next
Loader.Progress = 0.2 Loader.Progress = 0.2
'导出最终 JSON 文件 '导出最终 JSON 文件
Dim Dependencies As New JObject From {{"minecraft", McVersion.Version.VanillaName}} Dim Dependencies As New JObject From {{"minecraft", TargetInstance.Version.VanillaName}}
If McVersion.Version.HasForge Then Dependencies.Add("forge", McVersion.Version.Forge) If TargetInstance.Version.ModLoaders.HasFlagF(ModLoaders.Forge) Then Dependencies.Add("forge", TargetInstance.Version.ModLoaderVersions(ModLoaders.Forge))
If McVersion.Version.HasFabric Then Dependencies.Add("fabric-loader", McVersion.Version.Fabric) If TargetInstance.Version.ModLoaders.HasFlagF(ModLoaders.Fabric) Then Dependencies.Add("fabric-loader", TargetInstance.Version.ModLoaderVersions(ModLoaders.Fabric))
If McVersion.Version.HasNeoForge Then Dependencies.Add("neoforge", McVersion.Version.NeoForge) If TargetInstance.Version.ModLoaders.HasFlagF(ModLoaders.NeoForge) Then Dependencies.Add("neoforge", TargetInstance.Version.ModLoaderVersions(ModLoaders.NeoForge))
Dim ResultJson As New JObject From { Dim ResultJson As New JObject From {
{"game", "minecraft"}, {"game", "minecraft"},
{"formatVersion", 1}, {"formatVersion", 1},
{"versionId", PackVersion}, {"versionId", PackVersion},
{"name", PackName}, {"name", PackName},
{"summary", McVersion.Info}, {"summary", TargetInstance.Info},
{"files", Files}, {"files", Files},
{"dependencies", Dependencies} {"dependencies", Dependencies}
} }
@@ -13,21 +13,29 @@
</x:Array> </x:Array>
</local:MyListItem.Buttons> </local:MyListItem.Buttons>
</local:MyListItem> </local:MyListItem>
<local:MyListItem x:Name="ItemMod" IsScaleAnimationEnabled="False" Type="RadioBox" Tag="2" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="Mod 管理" <local:MyListItem x:Name="ItemModify" IsScaleAnimationEnabled="False" Type="RadioBox" Tag="2" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="修改" LogoScale="0.95"
Logo="M789.504 912.896h-195.072a35.328 35.328 0 0 1-34.816-35.328v-77.312a39.936 39.936 0 0 0-40.448-39.936H481.28a40.448 40.448 0 0 0-40.448 39.936v77.312a34.816 34.816 0 0 1-34.816 35.328H204.8a41.984 41.984 0 0 1-40.448-42.496v-200.192a35.328 35.328 0 0 1 35.328-35.328h72.704a39.936 39.936 0 0 0 39.936-39.936v-37.888a39.936 39.936 0 0 0-39.936-40.448H199.68a35.328 35.328 0 0 1-35.328-35.328V287.744A41.984 41.984 0 0 1 204.8 245.76h176.64v-32.768a102.4 102.4 0 0 1 102.4-102.4h33.792a102.4 102.4 0 0 1 102.4 102.4v32.768h170.496a41.984 41.984 0 0 1 41.984 41.984V460.8h28.672a102.4 102.4 0 0 1 102.4 102.4v33.792a102.4 102.4 0 0 1-102.4 102.4h-28.672V870.4a41.984 41.984 0 0 1-43.008 42.496z m-159.744-70.144h131.584V665.6a34.304 34.304 0 0 1 34.816-34.816h64a31.744 31.744 0 0 0 31.744-31.744V563.2a31.744 31.744 0 0 0-31.744-31.744h-64a34.816 34.816 0 0 1-34.816-35.328V316.416h-177.152a35.328 35.328 0 0 1-35.328-35.328V212.992a31.744 31.744 0 0 0-31.744-31.744h-33.792a31.744 31.744 0 0 0-31.744 31.744v68.096a35.328 35.328 0 0 1-34.816 35.328H234.496v130.048h37.888a110.592 110.592 0 0 1 110.08 110.592v37.888a110.592 110.592 0 0 1-110.08 110.592h-37.888v137.216h136.192v-42.496a110.592 110.592 0 0 1 110.592-110.08h37.888a110.592 110.592 0 0 1 110.592 110.08z" > Logo="F1 M652.624 145.904c62.256-62.272 163.2-62.272 225.472 0 62.272 62.256 62.272 163.2 0 225.472L477.808 771.664a28.8 28.8 0 0 1-10.816 6.8L242.4 857.376c-47.04 16.544-92.32-28.736-75.776-75.776l78.896-224.592c1.44-4.08 3.76-7.776 6.816-10.816L652.624 145.904z M828.604 195.396a89.44 89.44 0 0 0-126.488 0L308.48 589.032 241.68 781.12 433.768 714.32 828.604 321.884a89.44 89.44 0 0 0 0-126.488z M606.256 194.816a35 35 0 0 1 49.504 0l173.728 173.728a35 35 0 0 1-49.504 49.504L606.256 244.32a35 35 0 0 1 0-49.504z M261.984 539.104a35 35 0 0 1 49.504 0l173.728 173.728a35 35 0 0 1-49.504 49.504L261.984 588.608a35 35 0 0 1 0-49.504z" >
<local:MyListItem.Buttons> <local:MyListItem.Buttons>
<x:Array Type="{x:Type local:MyIconButton}"> <x:Array Type="{x:Type local:MyIconButton}">
<local:MyIconButton Tag="2" ToolTip="刷新" Click="Refresh_Click" LogoScale="0.85" Logo="M512.0 838.3c-80.2 0-153.4-29.3-210.2-77.4l75.5-75.5c11.5-11.5 25.8-22.0 25.8-37.0a27.2 27.2 0 0 0-27.1-27.1H104.0c-27.1 0-27.1 23.9-27.1 27.1v271.9a27.1 27.1 0 0 0 27.1 27.1c15.0 0 27.8-16.6 42.5-31.2l77.9-77.9c76.6 67.7 177.1 108.9 287.4 108.9 221.7 0 404.5-166.0 431.2-380.6h-109.8c-25.9 154.2-159.7 271.9-321.3 271.9zM919.9 76.6c-15.0 0-27.8 16.6-42.5 31.3L799.5 185.8c-76.5-67.7-177.1-108.9-287.4-108.9-221.8 0-404.5 166.1-431.3 380.6H190.6c25.9-154.2 159.7-271.9 321.4-271.9 80.2 0 153.4 29.3 210.1 77.4l-75.5 75.5c-11.6 11.5-25.8 22.0-25.8 37.1a27.2 27.2 0 0 0 27.1 27.1h271.9c27.1 0 27.1-23.9 27.1-27.1V103.8a27.1 27.1 0 0 0-27.1-27.1z" /> <local:MyIconButton Tag="2" ToolTip="刷新" Click="Refresh_Click" LogoScale="0.85" Logo="M512.0 838.3c-80.2 0-153.4-29.3-210.2-77.4l75.5-75.5c11.5-11.5 25.8-22.0 25.8-37.0a27.2 27.2 0 0 0-27.1-27.1H104.0c-27.1 0-27.1 23.9-27.1 27.1v271.9a27.1 27.1 0 0 0 27.1 27.1c15.0 0 27.8-16.6 42.5-31.2l77.9-77.9c76.6 67.7 177.1 108.9 287.4 108.9 221.7 0 404.5-166.0 431.2-380.6h-109.8c-25.9 154.2-159.7 271.9-321.3 271.9zM919.9 76.6c-15.0 0-27.8 16.6-42.5 31.3L799.5 185.8c-76.5-67.7-177.1-108.9-287.4-108.9-221.8 0-404.5 166.1-431.3 380.6H190.6c25.9-154.2 159.7-271.9 321.4-271.9 80.2 0 153.4 29.3 210.1 77.4l-75.5 75.5c-11.6 11.5-25.8 22.0-25.8 37.1a27.2 27.2 0 0 0 27.1 27.1h271.9c27.1 0 27.1-23.9 27.1-27.1V103.8a27.1 27.1 0 0 0-27.1-27.1z" />
</x:Array> </x:Array>
</local:MyListItem.Buttons> </local:MyListItem.Buttons>
</local:MyListItem> </local:MyListItem>
<local:MyListItem x:Name="ItemModDisabled" IsScaleAnimationEnabled="False" Type="RadioBox" Tag="3" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="Mod 管理" <local:MyListItem x:Name="ItemMod" IsScaleAnimationEnabled="False" Type="RadioBox" Tag="3" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="Mod 管理"
Logo="M789.504 912.896h-195.072a35.328 35.328 0 0 1-34.816-35.328v-77.312a39.936 39.936 0 0 0-40.448-39.936H481.28a40.448 40.448 0 0 0-40.448 39.936v77.312a34.816 34.816 0 0 1-34.816 35.328H204.8a41.984 41.984 0 0 1-40.448-42.496v-200.192a35.328 35.328 0 0 1 35.328-35.328h72.704a39.936 39.936 0 0 0 39.936-39.936v-37.888a39.936 39.936 0 0 0-39.936-40.448H199.68a35.328 35.328 0 0 1-35.328-35.328V287.744A41.984 41.984 0 0 1 204.8 245.76h176.64v-32.768a102.4 102.4 0 0 1 102.4-102.4h33.792a102.4 102.4 0 0 1 102.4 102.4v32.768h170.496a41.984 41.984 0 0 1 41.984 41.984V460.8h28.672a102.4 102.4 0 0 1 102.4 102.4v33.792a102.4 102.4 0 0 1-102.4 102.4h-28.672V870.4a41.984 41.984 0 0 1-43.008 42.496z m-159.744-70.144h131.584V665.6a34.304 34.304 0 0 1 34.816-34.816h64a31.744 31.744 0 0 0 31.744-31.744V563.2a31.744 31.744 0 0 0-31.744-31.744h-64a34.816 34.816 0 0 1-34.816-35.328V316.416h-177.152a35.328 35.328 0 0 1-35.328-35.328V212.992a31.744 31.744 0 0 0-31.744-31.744h-33.792a31.744 31.744 0 0 0-31.744 31.744v68.096a35.328 35.328 0 0 1-34.816 35.328H234.496v130.048h37.888a110.592 110.592 0 0 1 110.08 110.592v37.888a110.592 110.592 0 0 1-110.08 110.592h-37.888v137.216h136.192v-42.496a110.592 110.592 0 0 1 110.592-110.08h37.888a110.592 110.592 0 0 1 110.592 110.08z" >
<local:MyListItem.Buttons>
<x:Array Type="{x:Type local:MyIconButton}">
<local:MyIconButton Tag="3" ToolTip="刷新" Click="Refresh_Click" LogoScale="0.85" Logo="M512.0 838.3c-80.2 0-153.4-29.3-210.2-77.4l75.5-75.5c11.5-11.5 25.8-22.0 25.8-37.0a27.2 27.2 0 0 0-27.1-27.1H104.0c-27.1 0-27.1 23.9-27.1 27.1v271.9a27.1 27.1 0 0 0 27.1 27.1c15.0 0 27.8-16.6 42.5-31.2l77.9-77.9c76.6 67.7 177.1 108.9 287.4 108.9 221.7 0 404.5-166.0 431.2-380.6h-109.8c-25.9 154.2-159.7 271.9-321.3 271.9zM919.9 76.6c-15.0 0-27.8 16.6-42.5 31.3L799.5 185.8c-76.5-67.7-177.1-108.9-287.4-108.9-221.8 0-404.5 166.1-431.3 380.6H190.6c25.9-154.2 159.7-271.9 321.4-271.9 80.2 0 153.4 29.3 210.1 77.4l-75.5 75.5c-11.6 11.5-25.8 22.0-25.8 37.1a27.2 27.2 0 0 0 27.1 27.1h271.9c27.1 0 27.1-23.9 27.1-27.1V103.8a27.1 27.1 0 0 0-27.1-27.1z" />
</x:Array>
</local:MyListItem.Buttons>
</local:MyListItem>
<local:MyListItem x:Name="ItemModDisabled" IsScaleAnimationEnabled="False" Type="RadioBox" Tag="4" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="Mod 管理"
Logo="M789.504 912.896h-195.072a35.328 35.328 0 0 1-34.816-35.328v-77.312a39.936 39.936 0 0 0-40.448-39.936H481.28a40.448 40.448 0 0 0-40.448 39.936v77.312a34.816 34.816 0 0 1-34.816 35.328H204.8a41.984 41.984 0 0 1-40.448-42.496v-200.192a35.328 35.328 0 0 1 35.328-35.328h72.704a39.936 39.936 0 0 0 39.936-39.936v-37.888a39.936 39.936 0 0 0-39.936-40.448H199.68a35.328 35.328 0 0 1-35.328-35.328V287.744A41.984 41.984 0 0 1 204.8 245.76h176.64v-32.768a102.4 102.4 0 0 1 102.4-102.4h33.792a102.4 102.4 0 0 1 102.4 102.4v32.768h170.496a41.984 41.984 0 0 1 41.984 41.984V460.8h28.672a102.4 102.4 0 0 1 102.4 102.4v33.792a102.4 102.4 0 0 1-102.4 102.4h-28.672V870.4a41.984 41.984 0 0 1-43.008 42.496z m-159.744-70.144h131.584V665.6a34.304 34.304 0 0 1 34.816-34.816h64a31.744 31.744 0 0 0 31.744-31.744V563.2a31.744 31.744 0 0 0-31.744-31.744h-64a34.816 34.816 0 0 1-34.816-35.328V316.416h-177.152a35.328 35.328 0 0 1-35.328-35.328V212.992a31.744 31.744 0 0 0-31.744-31.744h-33.792a31.744 31.744 0 0 0-31.744 31.744v68.096a35.328 35.328 0 0 1-34.816 35.328H234.496v130.048h37.888a110.592 110.592 0 0 1 110.08 110.592v37.888a110.592 110.592 0 0 1-110.08 110.592h-37.888v137.216h136.192v-42.496a110.592 110.592 0 0 1 110.592-110.08h37.888a110.592 110.592 0 0 1 110.592 110.08z" /> Logo="M789.504 912.896h-195.072a35.328 35.328 0 0 1-34.816-35.328v-77.312a39.936 39.936 0 0 0-40.448-39.936H481.28a40.448 40.448 0 0 0-40.448 39.936v77.312a34.816 34.816 0 0 1-34.816 35.328H204.8a41.984 41.984 0 0 1-40.448-42.496v-200.192a35.328 35.328 0 0 1 35.328-35.328h72.704a39.936 39.936 0 0 0 39.936-39.936v-37.888a39.936 39.936 0 0 0-39.936-40.448H199.68a35.328 35.328 0 0 1-35.328-35.328V287.744A41.984 41.984 0 0 1 204.8 245.76h176.64v-32.768a102.4 102.4 0 0 1 102.4-102.4h33.792a102.4 102.4 0 0 1 102.4 102.4v32.768h170.496a41.984 41.984 0 0 1 41.984 41.984V460.8h28.672a102.4 102.4 0 0 1 102.4 102.4v33.792a102.4 102.4 0 0 1-102.4 102.4h-28.672V870.4a41.984 41.984 0 0 1-43.008 42.496z m-159.744-70.144h131.584V665.6a34.304 34.304 0 0 1 34.816-34.816h64a31.744 31.744 0 0 0 31.744-31.744V563.2a31.744 31.744 0 0 0-31.744-31.744h-64a34.816 34.816 0 0 1-34.816-35.328V316.416h-177.152a35.328 35.328 0 0 1-35.328-35.328V212.992a31.744 31.744 0 0 0-31.744-31.744h-33.792a31.744 31.744 0 0 0-31.744 31.744v68.096a35.328 35.328 0 0 1-34.816 35.328H234.496v130.048h37.888a110.592 110.592 0 0 1 110.08 110.592v37.888a110.592 110.592 0 0 1-110.08 110.592h-37.888v137.216h136.192v-42.496a110.592 110.592 0 0 1 110.592-110.08h37.888a110.592 110.592 0 0 1 110.592 110.08z" />
<local:MyListItem x:Name="ItemExport" IsScaleAnimationEnabled="False" Type="RadioBox" Tag="4" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="导出" <local:MyListItem x:Name="ItemExport" IsScaleAnimationEnabled="False" Type="RadioBox" Tag="5" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="导出"
Logo="M511 995a128 128 0 0 1-57-13L70 791a126 126 0 0 1-70-113V311a126 126 0 0 1 15-60V248c1-2 3-5 5-8a127 127 0 0 1 49-42L454 13a128 128 0 0 1 112 0l383 190a126 126 0 0 1 72 113v360a126 126 0 0 1-70 115L568 984c-17 7-37 11-57 11z m42-470v370l360-178c14-7 23-21 23-38v-335L554 524zM85 330v347a42 42 0 0 0 23 38l360 178V523L85 330zM135 260l375 189 137-65L286 188 135 260z m245-118l363 197 150-71-365-180a42 42 0 0 0-37 0l-111 53z"> Logo="M511 995a128 128 0 0 1-57-13L70 791a126 126 0 0 1-70-113V311a126 126 0 0 1 15-60V248c1-2 3-5 5-8a127 127 0 0 1 49-42L454 13a128 128 0 0 1 112 0l383 190a126 126 0 0 1 72 113v360a126 126 0 0 1-70 115L568 984c-17 7-37 11-57 11z m42-470v370l360-178c14-7 23-21 23-38v-335L554 524zM85 330v347a42 42 0 0 0 23 38l360 178V523L85 330zM135 260l375 189 137-65L286 188 135 260z m245-118l363 197 150-71-365-180a42 42 0 0 0-37 0l-111 53z">
<local:MyListItem.Buttons> <local:MyListItem.Buttons>
<x:Array Type="{x:Type local:MyIconButton}"> <x:Array Type="{x:Type local:MyIconButton}">
<local:MyIconButton Tag="4" ToolTip="刷新" Click="Refresh_Click" LogoScale="0.85" Logo="M512.0 838.3c-80.2 0-153.4-29.3-210.2-77.4l75.5-75.5c11.5-11.5 25.8-22.0 25.8-37.0a27.2 27.2 0 0 0-27.1-27.1H104.0c-27.1 0-27.1 23.9-27.1 27.1v271.9a27.1 27.1 0 0 0 27.1 27.1c15.0 0 27.8-16.6 42.5-31.2l77.9-77.9c76.6 67.7 177.1 108.9 287.4 108.9 221.7 0 404.5-166.0 431.2-380.6h-109.8c-25.9 154.2-159.7 271.9-321.3 271.9zM919.9 76.6c-15.0 0-27.8 16.6-42.5 31.3L799.5 185.8c-76.5-67.7-177.1-108.9-287.4-108.9-221.8 0-404.5 166.1-431.3 380.6H190.6c25.9-154.2 159.7-271.9 321.4-271.9 80.2 0 153.4 29.3 210.1 77.4l-75.5 75.5c-11.6 11.5-25.8 22.0-25.8 37.1a27.2 27.2 0 0 0 27.1 27.1h271.9c27.1 0 27.1-23.9 27.1-27.1V103.8a27.1 27.1 0 0 0-27.1-27.1z" /> <local:MyIconButton Tag="5" ToolTip="刷新" Click="Refresh_Click" LogoScale="0.85" Logo="M512.0 838.3c-80.2 0-153.4-29.3-210.2-77.4l75.5-75.5c11.5-11.5 25.8-22.0 25.8-37.0a27.2 27.2 0 0 0-27.1-27.1H104.0c-27.1 0-27.1 23.9-27.1 27.1v271.9a27.1 27.1 0 0 0 27.1 27.1c15.0 0 27.8-16.6 42.5-31.2l77.9-77.9c76.6 67.7 177.1 108.9 287.4 108.9 221.7 0 404.5-166.0 431.2-380.6h-109.8c-25.9 154.2-159.7 271.9-321.3 271.9zM919.9 76.6c-15.0 0-27.8 16.6-42.5 31.3L799.5 185.8c-76.5-67.7-177.1-108.9-287.4-108.9-221.8 0-404.5 166.1-431.3 380.6H190.6c25.9-154.2 159.7-271.9 321.4-271.9 80.2 0 153.4 29.3 210.1 77.4l-75.5 75.5c-11.6 11.5-25.8 22.0-25.8 37.1a27.2 27.2 0 0 0 27.1 27.1h271.9c27.1 0 27.1-23.9 27.1-27.1V103.8a27.1 27.1 0 0 0-27.1-27.1z" />
</x:Array> </x:Array>
</local:MyListItem.Buttons> </local:MyListItem.Buttons>
</local:MyListItem> </local:MyListItem>
@@ -6,6 +6,10 @@ Public Class PageInstanceLeft
''' </summary> ''' </summary>
Public Shared Instance As McInstance = Nothing Public Shared Instance As McInstance = Nothing
Private Sub PageInstanceLeft_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
PageSetupUI.HiddenRefresh()
End Sub
Public Sub RefreshModDisabled() Handles Me.Loaded Public Sub RefreshModDisabled() Handles Me.Loaded
If Instance IsNot Nothing AndAlso Instance.Modable Then If Instance IsNot Nothing AndAlso Instance.Modable Then
ItemMod.Visibility = Visibility.Visible ItemMod.Visibility = Visibility.Visible
@@ -26,7 +30,7 @@ Public Class PageInstanceLeft
''' <summary> ''' <summary>
''' 勾选事件改变页面。 ''' 勾选事件改变页面。
''' </summary> ''' </summary>
Private Sub PageCheck(sender As FrameworkElement, e As RouteEventArgs) Handles ItemOverall.Check, ItemMod.Check, ItemModDisabled.Check, ItemSetup.Check, ItemExport.Check Private Sub PageCheck(sender As FrameworkElement, e As RouteEventArgs) Handles ItemOverall.Check, ItemMod.Check, ItemModDisabled.Check, ItemSetup.Check, ItemModify.Check, ItemExport.Check
'尚未初始化控件属性时,sender.Tag 为 Nothing,会导致切换到页面 0 '尚未初始化控件属性时,sender.Tag 为 Nothing,会导致切换到页面 0
'若使用 IsLoaded,则会导致模拟点击不被执行(模拟点击切换页面时,控件的 IsLoaded 为 False) '若使用 IsLoaded,则会导致模拟点击不被执行(模拟点击切换页面时,控件的 IsLoaded 为 False)
If sender.Tag IsNot Nothing Then PageChange(Val(sender.Tag)) If sender.Tag IsNot Nothing Then PageChange(Val(sender.Tag))
@@ -45,11 +49,14 @@ Public Class PageInstanceLeft
If FrmInstanceModDisabled Is Nothing Then FrmInstanceModDisabled = New PageInstanceModDisabled If FrmInstanceModDisabled Is Nothing Then FrmInstanceModDisabled = New PageInstanceModDisabled
Return FrmInstanceModDisabled Return FrmInstanceModDisabled
Case FormMain.PageSubType.InstanceSetup Case FormMain.PageSubType.InstanceSetup
If IsNothing(FrmInstanceSetup) Then FrmInstanceSetup = New PageInstanceSetup If FrmInstanceSetup Is Nothing Then FrmInstanceSetup = New PageInstanceSetup
Return FrmInstanceSetup Return FrmInstanceSetup
Case FormMain.PageSubType.InstanceExport Case FormMain.PageSubType.InstanceExport
If FrmInstanceExport Is Nothing Then FrmInstanceExport = New PageInstanceExport If FrmInstanceExport Is Nothing Then FrmInstanceExport = New PageInstanceExport
Return FrmInstanceExport Return FrmInstanceExport
Case FormMain.PageSubType.InstanceModify
If FrmDownloadModify Is Nothing Then FrmDownloadModify = New PageDownloadModify
Return FrmDownloadModify
Case Else Case Else
Throw New Exception("未知的版本设置子页面种类:" & ID) Throw New Exception("未知的版本设置子页面种类:" & ID)
End Select End Select
@@ -60,6 +67,10 @@ Public Class PageInstanceLeft
''' </summary> ''' </summary>
Public Sub PageChange(ID As FormMain.PageSubType) Public Sub PageChange(ID As FormMain.PageSubType)
If PageID = ID Then Return If PageID = ID Then Return
If Instance IsNot Nothing AndAlso McInstanceLock.IsLocked(Instance) Then
Hint("该版本正在被修改,请稍后再切换页面!", HintType.Red)
Return
End If
AniControlEnabled += 1 AniControlEnabled += 1
Try Try
PageChangeRun(PageGet(ID)) PageChangeRun(PageGet(ID))
@@ -98,13 +109,20 @@ Public Class PageInstanceLeft
Refresh(FrmMain.PageCurrentSub) Refresh(FrmMain.PageCurrentSub)
End Sub End Sub
Public Sub Refresh(SubType As FormMain.PageSubType) Public Sub Refresh(SubType As FormMain.PageSubType)
If Instance IsNot Nothing AndAlso McInstanceLock.IsLocked(Instance) Then
Hint("该版本正在被修改,请稍后再刷新!", HintType.Red)
Return
End If
Select Case SubType Select Case SubType
Case FormMain.PageSubType.InstanceMod Case FormMain.PageSubType.InstanceMod
PageInstanceMod.Refresh() PageInstanceMod.Refresh()
ItemMod.Checked = True ItemMod.Checked = True
Case FormMain.PageSubType.InstanceExport Case FormMain.PageSubType.InstanceExport
If FrmInstanceExport IsNot Nothing Then FrmInstanceExport.RefreshAll() FrmInstanceExport?.RefreshAll()
ItemExport.Checked = True ItemExport.Checked = True
Case FormMain.PageSubType.InstanceModify
FrmDownloadModify?.PageLoaderRestart()
ItemModify.Checked = True
End Select End Select
End Sub End Sub
@@ -284,7 +284,7 @@ Public Class PageInstanceMod
''' 返回输入的文件是否为一个 Mod 文件,仅用于判断拖拽行为。 ''' 返回输入的文件是否为一个 Mod 文件,仅用于判断拖拽行为。
''' </summary> ''' </summary>
Public Shared Function InstallMods(FilePathList As IEnumerable(Of String)) As Boolean Public Shared Function InstallMods(FilePathList As IEnumerable(Of String)) As Boolean
Dim Extension As String = FilePathList.First.AfterLast(".").Lower Dim Extension As String = PathUtils.GetExtension(FilePathList.First)
'检查文件扩展名 '检查文件扩展名
If Not {"jar", "litemod", "disabled", "old"}.Any(Function(t) t = Extension) Then Return False If Not {"jar", "litemod", "disabled", "old"}.Any(Function(t) t = Extension) Then Return False
Logger.Info("文件为 jar/litemod 格式,尝试作为 Mod 安装") Logger.Info("文件为 jar/litemod 格式,尝试作为 Mod 安装")
@@ -307,15 +307,15 @@ Public Class PageInstanceMod
Install: Install:
Try Try
For Each ModFile In FilePathList For Each ModFile In FilePathList
Dim NewFileName = PathUtils.GetLastPart(ModFile).Replace(".disabled", "").Replace(".old", "") Dim NewFileName = (New LocalResourceFile(ModFile)).EnabledName
If Not NewFileName.Contains(".") Then NewFileName += ".jar" '#4227 If Not NewFileName.Contains(".") Then NewFileName += ".jar" '#4227
NewFileName = Instance.PathIndie & "mods\" & NewFileName NewFileName = Instance.PathIndie & "mods\" & NewFileName
If ModFile = NewFileName Then Continue For '防止安装相同文件导致文件被删除 If PathUtils.IsSame(ModFile, NewFileName) Then Continue For '防止安装相同文件导致文件被删除
FileUtils.Delete(NewFileName, True) FileUtils.Delete(NewFileName, True)
FileUtils.Copy(ModFile, NewFileName) FileUtils.Copy(ModFile, NewFileName)
Next Next
If FilePathList.IsSingle Then If FilePathList.IsSingle Then
Hint($"已安装 {PathUtils.GetLastPart(FilePathList.First).Replace(".disabled", "").Replace(".old", "")}!", HintType.Green) Hint($"已安装 {(New LocalResourceFile(FilePathList.First)).EnabledName}!", HintType.Green)
Else Else
Hint($"已安装 {FilePathList.Count} 个 Mod!", HintType.Green) Hint($"已安装 {FilePathList.Count} 个 Mod!", HintType.Green)
End If End If
@@ -553,8 +553,8 @@ Install:
Dim File As ResourceVersion = Entry.UpdateFile Dim File As ResourceVersion = Entry.UpdateFile
If Not File.DownloadAvailable Then Continue For If Not File.DownloadAvailable Then Continue For
'确认更新后的文件名 '确认更新后的文件名
Dim CurrentReplaceName = Entry.ProjectVersion.FileName.Replace(".jar", "").Replace(".old", "").Replace(".disabled", "") Dim CurrentReplaceName = PathUtils.GetFileNameWithoutExtension(Entry.ProjectVersion.FileName)
Dim NewestReplaceName = Entry.UpdateFile.FileName.Replace(".jar", "").Replace(".old", "").Replace(".disabled", "") Dim NewestReplaceName = PathUtils.GetFileNameWithoutExtension(Entry.UpdateFile.FileName)
Dim CurrentSegs = CurrentReplaceName.Split("-"c).ToList() Dim CurrentSegs = CurrentReplaceName.Split("-"c).ToList()
Dim NewestSegs = NewestReplaceName.Split("-"c).ToList() Dim NewestSegs = NewestReplaceName.Split("-"c).ToList()
Dim Shortened As Boolean = False Dim Shortened As Boolean = False
@@ -104,51 +104,21 @@ Public Class PageInstanceOverall
'重命名版本 '重命名版本
Private Sub BtnDisplayRename_Click(sender As Object, e As EventArgs) Handles BtnDisplayRename.Click Private Sub BtnDisplayRename_Click(sender As Object, e As EventArgs) Handles BtnDisplayRename.Click
Try Try
If McInstanceLock.IsLocked(PageInstanceLeft.Instance) Then
Hint("该版本正在被修改,请稍后再重命名!", HintType.Red)
Return
End If
'确认输入的新名称 '确认输入的新名称
Dim OldName As String = PageInstanceLeft.Instance.Name Dim OldName As String = PageInstanceLeft.Instance.Name
Dim OldPath As String = PageInstanceLeft.Instance.PathVersion
'修改此部分的同时修改快速安装的版本名检测* '修改此部分的同时修改快速安装的版本名检测*
Dim NewName As String = MyMsgBoxInput("重命名版本", "", OldName, New ObjectModel.Collection(Of Validate) From { Dim NewName As String = MyMsgBoxInput("重命名版本", "", OldName, New ObjectModel.Collection(Of Validate) From {
New ValidateFolderName(McFolderSelected & "versions", IgnoreList:=New List(Of String) From {OldName}) New ValidateFolderName(McFolderSelected & "versions", IgnoreList:=New List(Of String) From {OldName})
}) })
If String.IsNullOrWhiteSpace(NewName) Then Return If String.IsNullOrWhiteSpace(NewName) Then Return
Dim NewPath As String = McFolderSelected & "versions\" & NewName & "\" Dim NewInstance = PageInstanceLeft.Instance.Rename(NewName)
'重新读取版本 JSON 信息,避免 JsonObject 中已被合并的项被重新存储
Dim JsonObject As JObject
Dim OldJsonPath As String = PageInstanceLeft.Instance.GetJsonPath()
Try
JsonObject = FileUtils.ReadAsJson(OldJsonPath)
Catch ex As Exception
Logger.Warn(ex, "在重命名读取 JSON 时失败")
JsonObject = PageInstanceLeft.Instance.JsonObject
End Try
'重命名主文件夹
DirectoryUtils.Move(OldPath, NewPath)
'清理 ini 缓存
PageInstanceLeft.Instance.ResetSettingsCache()
'重命名 jar 文件与 natives 文件夹
'不能进行遍历重命名,否则在版本名很短的时候容易误伤其他文件(#6443)
If DirectoryUtils.Exists($"{NewPath}{OldName}-natives") Then DirectoryUtils.Move($"{NewPath}{OldName}-natives", $"{NewPath}{NewName}-natives")
If FileUtils.Exists($"{NewPath}{OldName}.jar") Then FileUtils.Move($"{NewPath}{OldName}.jar", $"{NewPath}{NewName}.jar")
'替换版本设置文件中的路径
If FileUtils.Exists(NewPath & "PCL\Setup.ini") Then
FileUtils.Write(NewPath & "PCL\Setup.ini", FileUtils.ReadAsString(NewPath & "PCL\Setup.ini").Replace(OldPath, NewPath))
End If
'更改已选中的版本
If ReadIni(McFolderSelected & "PCL.ini", "Version") = OldName Then
WriteIni(McFolderSelected & "PCL.ini", "Version", NewName)
End If
'更新版本 Json
Try
JsonObject("id") = NewName
FileUtils.Delete(NewPath & PathUtils.GetLastPart(OldJsonPath))
FileUtils.Write(NewPath & NewName & ".json", JsonObject.ToString)
Catch ex As Exception
Logger.Warn(ex, "重命名版本 json 失败")
End Try
'刷新与提示 '刷新与提示
Hint("重命名成功!", HintType.Green) Hint("重命名成功!", HintType.Green)
PageInstanceLeft.Instance = New McInstance(NewName).Load() PageInstanceLeft.Instance = NewInstance
If Not IsNothing(McInstanceSelected) AndAlso McInstanceSelected.Equals(PageInstanceLeft.Instance) Then WriteIni(McFolderSelected & "PCL.ini", "Version", NewName) If Not IsNothing(McInstanceSelected) AndAlso McInstanceSelected.Equals(PageInstanceLeft.Instance) Then WriteIni(McFolderSelected & "PCL.ini", "Version", NewName)
Reload() Reload()
LoaderFolderRun(McInstanceListLoader, McFolderSelected, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\") LoaderFolderRun(McInstanceListLoader, McFolderSelected, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\")
@@ -267,6 +237,10 @@ Public Class PageInstanceOverall
'补全文件 '补全文件
Private Sub BtnManageCheck_Click(sender As Object, e As EventArgs) Handles BtnManageCheck.Click Private Sub BtnManageCheck_Click(sender As Object, e As EventArgs) Handles BtnManageCheck.Click
Try Try
If McInstanceLock.IsLocked(PageInstanceLeft.Instance) Then
Hint("该版本正在被修改,请稍后再补全文件!", HintType.Red)
Return
End If
'忽略文件检查提示 '忽略文件检查提示
If ShouldIgnoreFileCheck(PageInstanceLeft.Instance) Then If ShouldIgnoreFileCheck(PageInstanceLeft.Instance) Then
Hint("请先关闭 [版本设置 → 设置 → 高级启动选项 → 关闭文件校验],然后再尝试补全文件!", HintType.Blue) Hint("请先关闭 [版本设置 → 设置 → 高级启动选项 → 关闭文件校验],然后再尝试补全文件!", HintType.Blue)
@@ -304,8 +278,12 @@ Public Class PageInstanceOverall
'修改此代码时,同时修改 PageSelectRight 中的代码 '修改此代码时,同时修改 PageSelectRight 中的代码
Private Sub BtnManageDelete_Click(sender As Object, e As EventArgs) Handles BtnManageDelete.Click Private Sub BtnManageDelete_Click(sender As Object, e As EventArgs) Handles BtnManageDelete.Click
Try Try
If McInstanceLock.IsLocked(PageInstanceLeft.Instance) Then
Hint("该版本正在被修改,请稍后再删除!", HintType.Red)
Return
End If
Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown
Dim IsHintIndie As Boolean = PageInstanceLeft.Instance.State <> McInstanceState.Error AndAlso PageInstanceLeft.Instance.PathIndie <> McFolderSelected Dim IsHintIndie As Boolean = PageInstanceLeft.Instance.State <> McInstance.McInstanceState.Error AndAlso PageInstanceLeft.Instance.PathIndie <> McFolderSelected
Dim SavesFolder = DirectoryUtils.GetInfo(PageInstanceLeft.Instance.PathIndie & "saves\") Dim SavesFolder = DirectoryUtils.GetInfo(PageInstanceLeft.Instance.PathIndie & "saves\")
Dim SaveEntries = If(IsHintIndie AndAlso SavesFolder.Exists, SavesFolder.EnumerateDirectories.OrderByDescending(Function(Save) Save.LastWriteTime).Select(Function(Save) $"{Save.Name}(上次修改:{StringUtils.FormatTimeSpan(Save.LastWriteTime - Date.Now, False)})").ToList, New List(Of String)) Dim SaveEntries = If(IsHintIndie AndAlso SavesFolder.Exists, SavesFolder.EnumerateDirectories.OrderByDescending(Function(Save) Save.LastWriteTime).Select(Function(Save) $"{Save.Name}(上次修改:{StringUtils.FormatTimeSpan(Save.LastWriteTime - Date.Now, False)})").ToList, New List(Of String))
Select Case MyMsgBox($"你确定要{If(IsShiftPressed, "永久", "")}删除版本 {PageInstanceLeft.Instance.Name} 吗?" & Select Case MyMsgBox($"你确定要{If(IsShiftPressed, "永久", "")}删除版本 {PageInstanceLeft.Instance.Name} 吗?" &
@@ -499,7 +499,7 @@ PreFin:
ComboArgumentJavaSelect.Items.Add(JavaItem) ComboArgumentJavaSelect.Items.Add(JavaItem)
Dim Buttons As New List(Of MyIconButton) Dim Buttons As New List(Of MyIconButton)
'从列表中移除按钮 '从列表中移除按钮
Dim IsOfficial As Boolean = JavaEntry.Folder.StartsWithF($"{Paths.AppData}.minecraft\runtime\") Dim IsOfficial As Boolean = PathUtils.IsParentOf($"{Paths.AppData}.minecraft\runtime\", JavaEntry.Folder)
Dim DeleteButton As New MyIconButton With {.Logo = Logo.IconButtonStop, .Height = 24, .Width = 24} Dim DeleteButton As New MyIconButton With {.Logo = Logo.IconButtonStop, .Height = 24, .Width = 24}
DeleteButton.IsEnabled = Not IsOfficial DeleteButton.IsEnabled = Not IsOfficial
DeleteButton.ToolTip = If(DeleteButton.IsEnabled, "从列表中移除", "无法移除官方 Java") DeleteButton.ToolTip = If(DeleteButton.IsEnabled, "从列表中移除", "无法移除官方 Java")
@@ -245,7 +245,8 @@ Retry:
{"Cherry Blossom", "樱花披风"}, {"15th Anniversary", "15 周年纪念披风"}, {"Purple Heart", "紫色心形披风"}, {"Cherry Blossom", "樱花披风"}, {"15th Anniversary", "15 周年纪念披风"}, {"Purple Heart", "紫色心形披风"},
{"Follower's", "追随者披风"}, {"MCC 15th Year", "MCC 15 周年披风"}, {"Minecraft Experience", "村民救援披风"}, {"Follower's", "追随者披风"}, {"MCC 15th Year", "MCC 15 周年披风"}, {"Minecraft Experience", "村民救援披风"},
{"Mojang Office", "Mojang 办公室披风"}, {"Home", "家园披风"}, {"Menace", "入侵披风"}, {"Yearn", "渴望披风"}, {"Mojang Office", "Mojang 办公室披风"}, {"Home", "家园披风"}, {"Menace", "入侵披风"}, {"Yearn", "渴望披风"},
{"Common", "普通披风"}, {"Pan", "薄煎饼披风"}, {"Founder's", "创始人披风"}, {"Copper", "铜披风"}, {"Zombie Horse", "僵尸马披风"}, {"Builder", "建造者披风"}, {"Crafter", "工匠披风"} {"Common", "普通披风"}, {"Pan", "薄煎饼披风"}, {"Founder's", "创始人披风"}, {"Copper", "铜披风"}, {"Zombie Horse", "僵尸马披风"}, {"Builder", "建造者披风"}, {"Crafter", "工匠披风"},
{"Aurora", "极光披风"}
} }
Dim SelectionControl As New List(Of IMyRadio) From { Dim SelectionControl As New List(Of IMyRadio) From {
New MyRadioBox With {.Text = "无披风", .Checked = Not SkinData("capes").Any(Function(c) c("state")?.ToString = "ACTIVE")}} New MyRadioBox With {.Text = "无披风", .Checked = Not SkinData("capes").Any(Function(c) c("state")?.ToString = "ACTIVE")}}
@@ -65,7 +65,7 @@ Public Class PageLaunchLeft
'确认 Minecraft 版本存在 '确认 Minecraft 版本存在
Dim Selection As String = ReadIni(McFolderSelected & "PCL.ini", "Version") Dim Selection As String = ReadIni(McFolderSelected & "PCL.ini", "Version")
Dim Instance As McInstance = If(Selection = "", Nothing, New McInstance(Selection)) Dim Instance As McInstance = If(Selection = "", Nothing, New McInstance(Selection))
If Instance Is Nothing OrElse Not Instance.PathVersion.StartsWithF(McFolderSelected) OrElse Not Instance.Check() Then If Instance Is Nothing OrElse Not PathUtils.IsParentOf(McFolderSelected, Instance.PathVersion) OrElse Not Instance.Check() Then
'无效的版本 '无效的版本
Logger.Info($"当前选择的 Minecraft 版本无效:{If(Instance Is Nothing, "null", Instance.PathVersion)}", If(IsNothing(Instance), LogBehavior.None, LogBehavior.ToastIfDebug)) Logger.Info($"当前选择的 Minecraft 版本无效:{If(Instance Is Nothing, "null", Instance.PathVersion)}", If(IsNothing(Instance), LogBehavior.None, LogBehavior.ToastIfDebug))
If Not McInstanceListLoader.State = LoadState.Finished Then LoaderFolderRun(McInstanceListLoader, McFolderSelected, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\", WaitForExit:=True) If Not McInstanceListLoader.State = LoadState.Finished Then LoaderFolderRun(McInstanceListLoader, McFolderSelected, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\", WaitForExit:=True)
@@ -691,12 +691,28 @@ ExitRefresh:
End If End If
End Sub End Sub
'取消按钮 '取消按钮
Private Sub BtnCancel_Click() Handles BtnCancel.Click Public Sub BtnCancel_Click() Handles BtnCancel.Click
McLaunchCancel() If McLaunchLoaderReal IsNot Nothing Then
McLaunchLoaderReal.Cancel()
McLaunchLog("已取消启动")
Try
If McLaunchWatcher IsNot Nothing Then
McLaunchWatcher.Kill()
ElseIf McLaunchProcess IsNot Nothing Then
If Not McLaunchProcess.HasExited Then McLaunchProcess.Kill()
End If
Catch ex As Exception
Logger.Error(ex, "取消启动结束进程失败", LogBehavior.Toast)
End Try
End If
End Sub End Sub
'版本设置按钮 '版本设置按钮
Private Sub BtnMore_Click() Handles BtnMore.Click Private Sub BtnMore_Click() Handles BtnMore.Click
If McLaunchLoader.State = LoadState.Loading Then Return If McLaunchLoader.State = LoadState.Loading Then Return
If McInstanceLock.IsLocked(McInstanceSelected) Then
Hint("该版本正在被修改,暂时无法进入版本设置!", HintType.Red)
Return
End If
McInstanceSelected.Load() McInstanceSelected.Load()
PageInstanceLeft.Instance = McInstanceSelected PageInstanceLeft.Instance = McInstanceSelected
FrmMain.PageChange(FormMain.PageType.InstanceSetup, 0) FrmMain.PageChange(FormMain.PageType.InstanceSetup, 0)
@@ -3,6 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:behaviors="clr-namespace:PCL.Controls.Behaviors"
xmlns:local="clr-namespace:PCL" PanScroll="PanBack"> xmlns:local="clr-namespace:PCL" PanScroll="PanBack">
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack"> <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
<StackPanel Name="PanMain" Margin="25,25,25,10" Grid.IsSharedSizeScope="True"> <StackPanel Name="PanMain" Margin="25,25,25,10" Grid.IsSharedSizeScope="True">
@@ -11,7 +12,9 @@
<StackPanel x:Name="PanCustom"> <StackPanel x:Name="PanCustom">
</StackPanel> </StackPanel>
<local:MyCard Margin="0,0,0,15" Title="启动日志" x:Name="PanLog"> <local:MyCard Margin="0,0,0,15" Title="启动日志" x:Name="PanLog">
<TextBlock x:Name="LabLog" Margin="20,38,20,18" TextTrimming="None" TextWrapping="Wrap" /> <local:MyTextBox x:Name="LabLog" Margin="15,34,15,13" Padding="0,4" IsReadOnly="True" IsUndoEnabled="False"
AcceptsReturn="True" TextWrapping="Wrap" BorderThickness="0" HasBackground="False"
VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Disabled" />
</local:MyCard> </local:MyCard>
</StackPanel> </StackPanel>
</local:MyScrollViewer> </local:MyScrollViewer>
@@ -101,23 +101,40 @@ Public Class PageLaunchRight
Url = "https://pcl.wyc-w.top/index.xaml" Url = "https://pcl.wyc-w.top/index.xaml"
Case 15 Case 15
Logger.Info("主页预设:Minecraft 芝士站") Logger.Info("主页预设:Minecraft 芝士站")
Url = "https://www.xxag.top/mkss" Url = "https://mkss-services.awaeric.xyz"
Case 16 Case 16
Logger.Info("主页预设:整合包推荐引擎") Logger.Info("主页预设:整合包推荐引擎")
Url = "https://qawsedrftgyhujiko.fun/pcl2/Custom.xaml" Url = "https://qawsedrftgyhujiko.fun/pcl2/Custom.xaml"
Case 17 Case 17
Logger.Info("Bangumi 番剧主页") Logger.Info("主页预设:Bangumi 番剧主页")
Url = "https://bangumi.p.kaphia.qzz.io" Url = "https://bangumi.p.kaphia.top"
Case 18 Case 18
Logger.Info("Bilibili 热门") Logger.Info("主页预设:Bilibili 热门")
Url = "https://bilibili.p.kaphia.qzz.io" Url = "https://bilibili.p.kaphia.top"
Case 19 Case 19
Logger.Info("Music 云音乐热门") Logger.Info("主页预设:Music 云音乐热门")
Url = "https://cloudmusic.p.kaphia.qzz.io" Url = "https://cloudmusic.p.kaphia.top"
Case 20
Logger.Info("主页预设:模组推荐")
Url = "https://g-fish.dpdns.org/download/ModPage.xaml"
Case 21
Logger.Info("主页预设:每日游戏推荐")
Url = "https://pcl.xn--9iq209asnop94b.top/download/GamePage.xaml"
Case 22
Logger.Info("主页预设:Wallpaper 每日壁纸")
Url = "https://wallpaper.p.kaphia.top"
Case 23
Logger.Info("主页预设:Weather 天气预报")
Url = "https://weather-p.kaphia.top/{variable:PCLDailyWeatherPage:choose}.xaml"
Case 24
Logger.Info("主页预设:新一代狗屁王大杂烩主页")
Url = "https://end.g-fish.dpdns.org/Custom.xaml"
End Select End Select
End Select End Select
'联网下载 '联网下载
If Not String.IsNullOrWhiteSpace(Url) Then If Not String.IsNullOrWhiteSpace(Url) Then
'替换自定义变量与设置
Url = ArgumentReplace(Url, AddressOf WebUtility.HtmlEncode)
If Url = Settings.Get(Of String)("CacheSavedPageUrl") AndAlso FileUtils.Exists(PathTemp & "Cache\Custom.xaml") Then If Url = Settings.Get(Of String)("CacheSavedPageUrl") AndAlso FileUtils.Exists(PathTemp & "Cache\Custom.xaml") Then
'缓存可用 '缓存可用
Logger.Info("主页自定义数据来源:联网缓存文件") Logger.Info("主页自定义数据来源:联网缓存文件")
@@ -145,8 +162,6 @@ Public Class PageLaunchRight
Dim Address As String = Task.Input.Address '#3721 中连续触发两次导致内容变化 Dim Address As String = Task.Input.Address '#3721 中连续触发两次导致内容变化
Dim ShouldRefresh As Boolean = Task.Input.ShouldRefresh Dim ShouldRefresh As Boolean = Task.Input.ShouldRefresh
Try Try
'替换自定义变量与设置
Address = ArgumentReplace(Address, AddressOf WebUtility.HtmlEncode)
'获取版本校验地址 '获取版本校验地址
Dim VersionAddress As String Dim VersionAddress As String
If Address.Contains(".xaml") Then If Address.Contains(".xaml") Then
@@ -45,7 +45,7 @@ Public Class PageLoginLegacy
If LoginData.UserName.Trim = "" Then Return "玩家名不能为空!" If LoginData.UserName.Trim = "" Then Return "玩家名不能为空!"
If LoginData.UserName.Contains("""") Then Return "玩家名不能包含英文引号!" If LoginData.UserName.Contains("""") Then Return "玩家名不能包含英文引号!"
If McInstanceSelected IsNot Nothing AndAlso If McInstanceSelected IsNot Nothing AndAlso
McInstanceSelected.Version.Vaild AndAlso McInstanceSelected.Version.Vanilla >= New Version(20, 0, 3) AndAlso McInstanceSelected.Version.Valid AndAlso McInstanceSelected.Version.Vanilla >= New Version(20, 0, 3) AndAlso
LoginData.UserName.Trim.Length > 16 Then LoginData.UserName.Trim.Length > 16 Then
Return "自 1.20.3 起,玩家名至多只能包含 16 个字符!" Return "自 1.20.3 起,玩家名至多只能包含 16 个字符!"
End If End If
@@ -358,8 +358,9 @@ Public Class PageLinkMain
'复制邀请码 '复制邀请码
Private Sub Copy() Handles BtnFinishCopy.Click Private Sub Copy() Handles BtnFinishCopy.Click
Dim CodeText As String = $"在 PCL 启动器中输入邀请码【{GetInviteCode()}】,即可加入联机房间!" Dim CodeText As String = $"在 PCL 启动器中输入邀请码【{GetInviteCode()}】,即可加入联机房间!"
ClipboardSet(CodeText, SuccessHint:="已复制邀请码!") ClipboardSet(CodeText, False)
Settings.Set("LinkLastAutoJoinInviteCode", CodeText) Settings.Set("LinkLastAutoJoinInviteCode", CodeText)
Hint("已复制邀请码!", HintType.Green)
End Sub End Sub
Private Function GetInviteCode() As String Private Function GetInviteCode() As String
Return $"{NetworkName}-{NetworkSecret}-{INVITE_CODE_VERSION.ToString.PadLeft(2, "0"c)}{ _ Return $"{NetworkName}-{NetworkSecret}-{INVITE_CODE_VERSION.ToString.PadLeft(2, "0"c)}{ _
@@ -368,7 +369,8 @@ Public Class PageLinkMain
'复制 IP '复制 IP
Private Sub BtnFinishIp_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles BtnFinishIp.MouseLeftButtonUp Private Sub BtnFinishIp_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles BtnFinishIp.MouseLeftButtonUp
ClipboardSet(ClientAddress, SuccessHint:="已复制服务器地址!") ClipboardSet(ClientAddress, False)
Hint("已复制服务器地址!", HintType.Green)
End Sub End Sub
#End Region #End Region
@@ -12,6 +12,7 @@
<RowDefinition Height="45" /> <RowDefinition Height="45" />
<RowDefinition Height="45" /> <RowDefinition Height="45" />
<RowDefinition Height="45" /> <RowDefinition Height="45" />
<RowDefinition Height="45" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="40" /> <ColumnDefinition Width="40" />
@@ -20,7 +21,8 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<local:MyListItem IsHitTestVisible="False" Title="龙腾猫跃" Margin="-5,0,15,0" Info="Plain Craft Launcher 的开发者!" Grid.Row="0" Grid.Column="1" /> <local:MyListItem IsHitTestVisible="False" Title="龙腾猫跃" Margin="-5,0,15,0" Info="Plain Craft Launcher 的开发者!" Grid.Row="0" Grid.Column="1" />
<local:MyListItem IsHitTestVisible="False" Title="HerobrineXia" Margin="-5,0,15,0" Info="Plain Craft Launcher 的协力开发者!" Grid.Row="1" Grid.Column="1" /> <local:MyListItem IsHitTestVisible="False" Title="HerobrineXia" Margin="-5,0,15,0" Info="Plain Craft Launcher 的协力开发者!" Grid.Row="1" Grid.Column="1" />
<local:MyListItem IsHitTestVisible="False" Title="Plain Craft Launcher 2" Margin="-5,0,15,0" Info="当前版本:%VERSION%(%VERSIONCODE%.%BRANCH%)" Grid.Row="2" Grid.Column="1" x:Name="ItemAboutPcl" /> <local:MyListItem IsHitTestVisible="False" Title="Coldsmile_7" Margin="-5,0,15,0" Info="Plain Craft Launcher (PCL) InfstarMC Edition 的开发者!" Grid.Row="2" Grid.Column="1" />
<local:MyListItem IsHitTestVisible="False" Title="Plain Craft Launcher (PCL) InfstarMC Edition" Margin="-5,0,15,0" Info="当前版本:%VERSION%(%VERSIONCODE%.%BRANCH%)" Grid.Row="3" Grid.Column="1" x:Name="ItemAboutPcl" />
<Image Source="/Plain Craft Launcher 2;component/Images/Heads/LTCat.jpg" Grid.Row="0" Margin="3"> <Image Source="/Plain Craft Launcher 2;component/Images/Heads/LTCat.jpg" Grid.Row="0" Margin="3">
<Image.Clip> <Image.Clip>
<EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" /> <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
@@ -36,10 +38,16 @@
<EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" /> <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
</Image.Clip> </Image.Clip>
</Image> </Image>
<Image Source="/Plain Craft Launcher 2;component/Images/Heads/Logo.png" Grid.Row="3" Margin="3">
<Image.Clip>
<EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
</Image.Clip>
</Image>
<local:MyButton Text="赞助 PCL !" Grid.Column="2" Grid.Row="0" Height="35" ColorType="Highlight" <local:MyButton Text="赞助 PCL !" Grid.Column="2" Grid.Row="0" Height="35" ColorType="Highlight"
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://meloong.com/afd/a/LTCat" /> local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://meloong.com/afd/a/LTCat" />
<local:MyButton Text="检查更新" Grid.Column="2" Grid.Row="2" Height="35" <local:MyButton Text="项目仓库" Grid.Column="2" Grid.Row="2" Height="35"
x:Name="BtnAboutUpdate" /> local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://git.infstar.cn/InfStarMC/PCL-ISE" />
<local:MyButton Text="检查更新" Grid.Column="2" Grid.Row="3" Height="35" x:Name="BtnAboutUpdate" />
</Grid> </Grid>
</local:MyCard> </local:MyCard>
<local:MyCard Margin="0,0,0,15" Title="特别鸣谢"> <local:MyCard Margin="0,0,0,15" Title="特别鸣谢">
@@ -131,388 +139,391 @@
<local:MyCard Margin="0,0,0,15" Title="赞助者"> <local:MyCard Margin="0,0,0,15" Title="赞助者">
<StackPanel Margin="25,40,19,20"> <StackPanel Margin="25,40,19,20">
<TextBlock HorizontalAlignment="Left" Text="以下是近期的高级赞助者们!感谢大家对 PCL 的支持!!!" Margin="0,0,25,5.5" TextTrimming="None" TextWrapping="WrapWithOverflow" /> <TextBlock HorizontalAlignment="Left" Text="以下是近期的高级赞助者们!感谢大家对 PCL 的支持!!!" Margin="0,0,25,5.5" TextTrimming="None" TextWrapping="WrapWithOverflow" />
<WrapPanel Margin="0,0,0,10"> <TextBlock Margin="0,1.5,6,0" Text="L" /> <WrapPanel Margin="0,0,0,10"> <TextBlock Margin="0,1.5,6,0" Text="J" />
<TextBlock Margin="0,1.5,6,0" Text="Z" /> <TextBlock Margin="0,1.5,6,0" Text="R" />
<TextBlock Margin="0,1.5,6,0" Text="N" /> <TextBlock Margin="0,1.5,6,0" Text="D" />
<TextBlock Margin="0,1.5,6,0" Text="P." /> <TextBlock Margin="0,1.5,6,0" Text="hj" />
<TextBlock Margin="0,1.5,6,0" Text="...:" /> <TextBlock Margin="0,1.5,6,0" Text="乖" />
<TextBlock Margin="0,1.5,6,0" Text="妖" /> <TextBlock Margin="0,1.5,6,0" Text="吕" />
<TextBlock Margin="0,1.5,6,0" Text="FL" /> <TextBlock Margin="0,1.5,6,0" Text="宇" />
<TextBlock Margin="0,1.5,6,0" Text="萧" /> <TextBlock Margin="0,1.5,6,0" Text="FF" />
<TextBlock Margin="0,1.5,6,0" Text="乔" /> <TextBlock Margin="0,1.5,6,0" Text="金" />
<TextBlock Margin="0,1.5,6,0" Text="cc" /> <TextBlock Margin="0,1.5,6,0" Text="夏" />
<TextBlock Margin="0,1.5,6,0" Text="!" /> <TextBlock Margin="0,1.5,6,0" Text="鸿" />
<TextBlock Margin="0,1.5,6,0" Text="一" /> <TextBlock Margin="0,1.5,6,0" Text="ツ" />
<TextBlock Margin="0,1.5,6,0" Text="韆" /> <TextBlock Margin="0,1.5,6,0" Text="枫" />
<TextBlock Margin="0,1.5,6,0" Text="莫" /> <TextBlock Margin="0,1.5,6,0" Text="SF" />
<TextBlock Margin="0,1.5,6,0" Text="无" /> <TextBlock Margin="0,1.5,6,0" Text="kll" />
<TextBlock Margin="0,1.5,6,0" Text="澍" /> <TextBlock Margin="0,1.5,6,0" Text="02" />
<TextBlock Margin="0,1.5,6,0" Text="ttt" /> <TextBlock Margin="0,1.5,6,0" Text="YY" />
<TextBlock Margin="0,1.5,6,0" Text="88" /> <TextBlock Margin="0,1.5,6,0" Text="……" />
<TextBlock Margin="0,1.5,6,0" Text="33" /> <TextBlock Margin="0,1.5,6,0" Text="shy" />
<TextBlock Margin="0,1.5,6,0" Text="kk" /> <TextBlock Margin="0,1.5,6,0" Text="lost" />
<TextBlock Margin="0,1.5,6,0" Text="jjb" /> <TextBlock Margin="0,1.5,6,0" Text="A仔" />
<TextBlock Margin="0,1.5,6,0" Text="狮." /> <TextBlock Margin="0,1.5,6,0" Text="111" />
<TextBlock Margin="0,1.5,6,0" Text="+1" /> <TextBlock Margin="0,1.5,6,0" Text="afaf" />
<TextBlock Margin="0,1.5,6,0" Text="Iirs" /> <TextBlock Margin="0,1.5,6,0" Text="pen" />
<TextBlock Margin="0,1.5,6,0" Text="Lio" /> <TextBlock Margin="0,1.5,6,0" Text="lulu" />
<TextBlock Margin="0,1.5,6,0" Text="lrw" /> <TextBlock Margin="0,1.5,6,0" Text="xyty" />
<TextBlock Margin="0,1.5,6,0" Text="LLL" /> <TextBlock Margin="0,1.5,6,0" Text="排骨" />
<TextBlock Margin="0,1.5,6,0" Text="Sht" /> <TextBlock Margin="0,1.5,6,0" Text="踏雾" />
<TextBlock Margin="0,1.5,6,0" Text="Nul" /> <TextBlock Margin="0,1.5,6,0" Text="Zsol" />
<TextBlock Margin="0,1.5,6,0" Text="250" /> <TextBlock Margin="0,1.5,6,0" Text="景仪" />
<TextBlock Margin="0,1.5,6,0" Text="Life" /> <TextBlock Margin="0,1.5,6,0" Text="可可" />
<TextBlock Margin="0,1.5,6,0" Text="kile" /> <TextBlock Margin="0,1.5,6,0" Text="白夜" />
<TextBlock Margin="0,1.5,6,0" Text="pbc" /> <TextBlock Margin="0,1.5,6,0" Text="汤圆" />
<TextBlock Margin="0,1.5,6,0" Text="Aya" /> <TextBlock Margin="0,1.5,6,0" Text="星月" />
<TextBlock Margin="0,1.5,6,0" Text="mjp" /> <TextBlock Margin="0,1.5,6,0" Text="皓月" />
<TextBlock Margin="0,1.5,6,0" Text="azzt" /> <TextBlock Margin="0,1.5,6,0" Text="老建" />
<TextBlock Margin="0,1.5,6,0" Text="小小" /> <TextBlock Margin="0,1.5,6,0" Text="阿琼" />
<TextBlock Margin="0,1.5,6,0" Text="冒子" /> <TextBlock Margin="0,1.5,6,0" Text="晓月" />
<TextBlock Margin="0,1.5,6,0" Text="王道" /> <TextBlock Margin="0,1.5,6,0" Text="朝雨" />
<TextBlock Margin="0,1.5,6,0" Text="鸡人" /> <TextBlock Margin="0,1.5,6,0" Text="迟雨" />
<TextBlock Margin="0,1.5,6,0" Text="喵根" /> <TextBlock Margin="0,1.5,6,0" Text="折纸" />
<TextBlock Margin="0,1.5,6,0" Text="折花" /> <TextBlock Margin="0,1.5,6,0" Text="张某" />
<TextBlock Margin="0,1.5,6,0" Text="归墟" /> <TextBlock Margin="0,1.5,6,0" Text="残月" />
<TextBlock Margin="0,1.5,6,0" Text="墨渊" /> <TextBlock Margin="0,1.5,6,0" Text="缘分" />
<TextBlock Margin="0,1.5,6,0" Text="键来" /> <TextBlock Margin="0,1.5,6,0" Text="静态" />
<TextBlock Margin="0,1.5,6,0" Text="睚眦" /> <TextBlock Margin="0,1.5,6,0" Text="铭熙" />
<TextBlock Margin="0,1.5,6,0" Text="而后" /> <TextBlock Margin="0,1.5,6,0" Text="酒狐" />
<TextBlock Margin="0,1.5,6,0" Text="南安" /> <TextBlock Margin="0,1.5,6,0" Text="誌異" />
<TextBlock Margin="0,1.5,6,0" Text="曰久" /> <TextBlock Margin="0,1.5,6,0" Text="灵喵" />
<TextBlock Margin="0,1.5,6,0" Text="小翼" /> <TextBlock Margin="0,1.5,6,0" Text="曦宇" />
<TextBlock Margin="0,1.5,6,0" Text="鲤逐" /> <TextBlock Margin="0,1.5,6,0" Text="木辛" />
<TextBlock Margin="0,1.5,6,0" Text="陌天" /> <TextBlock Margin="0,1.5,6,0" Text="轻梦" />
<TextBlock Margin="0,1.5,6,0" Text="eqw" /> <TextBlock Margin="0,1.5,6,0" Text="熊猫" />
<TextBlock Margin="0,1.5,6,0" Text="缘泠" /> <TextBlock Margin="0,1.5,6,0" Text="鱼柳" />
<TextBlock Margin="0,1.5,6,0" Text="烨落" />
<TextBlock Margin="0,1.5,6,0" Text="安安" />
<TextBlock Margin="0,1.5,6,0" Text="寒叙" />
<TextBlock Margin="0,1.5,6,0" Text="诶嘿" />
<TextBlock Margin="0,1.5,6,0" Text="月月" />
<TextBlock Margin="0,1.5,6,0" Text="凌迟" />
<TextBlock Margin="0,1.5,6,0" Text="泰吉" />
<TextBlock Margin="0,1.5,6,0" Text="楚渡" />
<TextBlock Margin="0,1.5,6,0" Text="将夜" />
<TextBlock Margin="0,1.5,6,0" Text="滴雨" />
<TextBlock Margin="0,1.5,6,0" Text="无萤" />
<TextBlock Margin="0,1.5,6,0" Text="秋水" />
<TextBlock Margin="0,1.5,6,0" Text="团子" />
<TextBlock Margin="0,1.5,6,0" Text="川儿" />
<TextBlock Margin="0,1.5,6,0" Text="冥曌" />
<TextBlock Margin="0,1.5,6,0" Text="红中" />
<TextBlock Margin="0,1.5,6,0" Text="小云" />
<TextBlock Margin="0,1.5,6,0" Text="诗人" />
<TextBlock Margin="0,1.5,6,0" Text="狐狸" />
<TextBlock Margin="0,1.5,6,0" Text="游阙" />
<TextBlock Margin="0,1.5,6,0" Text="悠真" />
<TextBlock Margin="0,1.5,6,0" Text="天雨" />
<TextBlock Margin="0,1.5,6,0" Text="章凤" />
<TextBlock Margin="0,1.5,6,0" Text="空空" />
<TextBlock Margin="0,1.5,6,0" Text="去衣" />
<TextBlock Margin="0,1.5,6,0" Text="老抽" />
<TextBlock Margin="0,1.5,6,0" Text="七海" />
<TextBlock Margin="0,1.5,6,0" Text="品茗" />
<TextBlock Margin="0,1.5,6,0" Text="白桦" />
<TextBlock Margin="0,1.5,6,0" Text="狐斋" />
<TextBlock Margin="0,1.5,6,0" Text="猫神" />
<TextBlock Margin="0,1.5,6,0" Text="乙总" />
<TextBlock Margin="0,1.5,6,0" Text="屺野" />
<TextBlock Margin="0,1.5,6,0" Text="极光" />
<TextBlock Margin="0,1.5,6,0" Text="哈哈" />
<TextBlock Margin="0,1.5,6,0" Text="小洛" />
<TextBlock Margin="0,1.5,6,0" Text="璃辰" />
<TextBlock Margin="0,1.5,6,0" Text="茉莉" />
<TextBlock Margin="0,1.5,6,0" Text="旅人" />
<TextBlock Margin="0,1.5,6,0" Text="航卡" />
<TextBlock Margin="0,1.5,6,0" Text="班总" />
<TextBlock Margin="0,1.5,6,0" Text="小圆" />
<TextBlock Margin="0,1.5,6,0" Text="梨子" /> <TextBlock Margin="0,1.5,6,0" Text="梨子" />
<TextBlock Margin="0,1.5,6,0" Text="风?" /> <TextBlock Margin="0,1.5,6,0" Text="小本" />
<TextBlock Margin="0,1.5,6,0" Text="瓜瓜" /> <TextBlock Margin="0,1.5,6,0" Text="起媛" />
<TextBlock Margin="0,1.5,6,0" Text="阳光" /> <TextBlock Margin="0,1.5,6,0" Text="夏天" />
<TextBlock Margin="0,1.5,6,0" Text="菜菜" /> <TextBlock Margin="0,1.5,6,0" Text="泪痕" />
<TextBlock Margin="0,1.5,6,0" Text="副连" /> <TextBlock Margin="0,1.5,6,0" Text="南北" />
<TextBlock Margin="0,1.5,6,0" Text="如舟" /> <TextBlock Margin="0,1.5,6,0" Text="灰鱼" />
<TextBlock Margin="0,1.5,6,0" Text="bow" /> <TextBlock Margin="0,1.5,6,0" Text="灰尘" />
<TextBlock Margin="0,1.5,6,0" Text="Byet" /> <TextBlock Margin="0,1.5,6,0" Text="骗铭" />
<TextBlock Margin="0,1.5,6,0" Text="Kian" /> <TextBlock Margin="0,1.5,6,0" Text="青轴" />
<TextBlock Margin="0,1.5,6,0" Text="ZDD" /> <TextBlock Margin="0,1.5,6,0" Text="谷雨" />
<TextBlock Margin="0,1.5,6,0" Text="Um1" /> <TextBlock Margin="0,1.5,6,0" Text="呈祥" />
<TextBlock Margin="0,1.5,6,0" Text="2514" /> <TextBlock Margin="0,1.5,6,0" Text="猫玖" />
<TextBlock Margin="0,1.5,6,0" Text="4440" /> <TextBlock Margin="0,1.5,6,0" Text="枕月" />
<TextBlock Margin="0,1.5,6,0" Text="Fu_jc" /> <TextBlock Margin="0,1.5,6,0" Text="刘革" />
<TextBlock Margin="0,1.5,6,0" Text="LEVA" /> <TextBlock Margin="0,1.5,6,0" Text="折花" />
<TextBlock Margin="0,1.5,6,0" Text="ZNLL" /> <TextBlock Margin="0,1.5,6,0" Text="应星" />
<TextBlock Margin="0,1.5,6,0" Text="joker" /> <TextBlock Margin="0,1.5,6,0" Text="没有" />
<TextBlock Margin="0,1.5,6,0" Text="花园" />
<TextBlock Margin="0,1.5,6,0" Text="华烬" />
<TextBlock Margin="0,1.5,6,0" Text="啥猫" />
<TextBlock Margin="0,1.5,6,0" Text="阿伯" />
<TextBlock Margin="0,1.5,6,0" Text="妄想" />
<TextBlock Margin="0,1.5,6,0" Text="树叶" />
<TextBlock Margin="0,1.5,6,0" Text="拾玖" />
<TextBlock Margin="0,1.5,6,0" Text="让人" />
<TextBlock Margin="0,1.5,6,0" Text="晓光" />
<TextBlock Margin="0,1.5,6,0" Text="懿尘" />
<TextBlock Margin="0,1.5,6,0" Text="尾巴" />
<TextBlock Margin="0,1.5,6,0" Text="枯骨" />
<TextBlock Margin="0,1.5,6,0" Text="鹤升" />
<TextBlock Margin="0,1.5,6,0" Text="遊泠" />
<TextBlock Margin="0,1.5,6,0" Text="裕茗" />
<TextBlock Margin="0,1.5,6,0" Text="沁园" />
<TextBlock Margin="0,1.5,6,0" Text="尹伊" />
<TextBlock Margin="0,1.5,6,0" Text="枫音" />
<TextBlock Margin="0,1.5,6,0" Text="古色" />
<TextBlock Margin="0,1.5,6,0" Text="傻鱼" />
<TextBlock Margin="0,1.5,6,0" Text="幻灭" />
<TextBlock Margin="0,1.5,6,0" Text="小琪" />
<TextBlock Margin="0,1.5,6,0" Text="小寒" />
<TextBlock Margin="0,1.5,6,0" Text="星狗" />
<TextBlock Margin="0,1.5,6,0" Text="玄米" />
<TextBlock Margin="0,1.5,6,0" Text="菜狗" />
<TextBlock Margin="0,1.5,6,0" Text="ZWL" />
<TextBlock Margin="0,1.5,6,0" Text="1234" />
<TextBlock Margin="0,1.5,6,0" Text=" 清空" />
<TextBlock Margin="0,1.5,6,0" Text="7104" />
<TextBlock Margin="0,1.5,6,0" Text="2333" />
<TextBlock Margin="0,1.5,6,0" Text="5-DF" />
<TextBlock Margin="0,1.5,6,0" Text="qqes" />
<TextBlock Margin="0,1.5,6,0" Text="Patxi" />
<TextBlock Margin="0,1.5,6,0" Text="Sage" />
<TextBlock Margin="0,1.5,6,0" Text="R301" />
<TextBlock Margin="0,1.5,6,0" Text="RXZY" /> <TextBlock Margin="0,1.5,6,0" Text="RXZY" />
<TextBlock Margin="0,1.5,6,0" Text="白成y" /> <TextBlock Margin="0,1.5,6,0" Text="Siwill" />
<TextBlock Margin="0,1.5,6,0" Text="mww" /> <TextBlock Margin="0,1.5,6,0" Text="kassa" />
<TextBlock Margin="0,1.5,6,0" Text="Rofni" /> <TextBlock Margin="0,1.5,6,0" Text="ssdsb" />
<TextBlock Margin="0,1.5,6,0" Text="mzqc" /> <TextBlock Margin="0,1.5,6,0" Text="Ztdzz" />
<TextBlock Margin="0,1.5,6,0" Text="jason" /> <TextBlock Margin="0,1.5,6,0" Text="xiboa" />
<TextBlock Margin="0,1.5,6,0" Text="youzi" /> <TextBlock Margin="0,1.5,6,0" Text="ZERO" />
<TextBlock Margin="0,1.5,6,0" Text="xxlzta" /> <TextBlock Margin="0,1.5,6,0" Text="pr1nk" />
<TextBlock Margin="0,1.5,6,0" Text="end_x" /> <TextBlock Margin="0,1.5,6,0" Text="BBUH" />
<TextBlock Margin="0,1.5,6,0" Text="QWYJ" /> <TextBlock Margin="0,1.5,6,0" Text="ALISA" />
<TextBlock Margin="0,1.5,6,0" Text="qwqw" /> <TextBlock Margin="0,1.5,6,0" Text="89623" />
<TextBlock Margin="0,1.5,6,0" Text="Angel" /> <TextBlock Margin="0,1.5,6,0" Text="IDXLZ" />
<TextBlock Margin="0,1.5,6,0" Text="15041" /> <TextBlock Margin="0,1.5,6,0" Text="翎羽絮" />
<TextBlock Margin="0,1.5,6,0" Text="晓空er" /> <TextBlock Margin="0,1.5,6,0" Text="硫酸梅" />
<TextBlock Margin="0,1.5,6,0" Text="kaoyu" /> <TextBlock Margin="0,1.5,6,0" Text="死鱼梦" />
<TextBlock Margin="0,1.5,6,0" Text="肥富帅" /> <TextBlock Margin="0,1.5,6,0" Text="秦始皇" />
<TextBlock Margin="0,1.5,6,0" Text="小王子" /> <TextBlock Margin="0,1.5,6,0" Text="春忆雪" />
<TextBlock Margin="0,1.5,6,0" Text="狐狸涩" /> <TextBlock Margin="0,1.5,6,0" Text="冷空气" />
<TextBlock Margin="0,1.5,6,0" Text="大涡流" /> <TextBlock Margin="0,1.5,6,0" Text="天崇域" />
<TextBlock Margin="0,1.5,6,0" Text="无名氏" /> <TextBlock Margin="0,1.5,6,0" Text="白与沧" />
<TextBlock Margin="0,1.5,6,0" Text="紫风狐" /> <TextBlock Margin="0,1.5,6,0" Text="柳蕴訫" />
<TextBlock Margin="0,1.5,6,0" Text="杨巨焕" /> <TextBlock Margin="0,1.5,6,0" Text="苟子丶" />
<TextBlock Margin="0,1.5,6,0" Text="范桂圆" /> <TextBlock Margin="0,1.5,6,0" Text="午時夢" />
<TextBlock Margin="0,1.5,6,0" Text="芸芸云" /> <TextBlock Margin="0,1.5,6,0" Text="清水石" />
<TextBlock Margin="0,1.5,6,0" Text="快夏波" /> <TextBlock Margin="0,1.5,6,0" Text="鹿鹤云" />
<TextBlock Margin="0,1.5,6,0" Text="陈知远" /> <TextBlock Margin="0,1.5,6,0" Text="洛沫兀" />
<TextBlock Margin="0,1.5,6,0" Text="梦华书" /> <TextBlock Margin="0,1.5,6,0" Text="。。。" />
<TextBlock Margin="0,1.5,6,0" Text="樱咲夜" />
<TextBlock Margin="0,1.5,6,0" Text="星期天" /> <TextBlock Margin="0,1.5,6,0" Text="星期天" />
<TextBlock Margin="0,1.5,6,0" Text="铸日者" /> <TextBlock Margin="0,1.5,6,0" Text="制花人" />
<TextBlock Margin="0,1.5,6,0" Text="晴云雯" /> <TextBlock Margin="0,1.5,6,0" Text="aleMk" />
<TextBlock Margin="0,1.5,6,0" Text="叶龙辉" /> <TextBlock Margin="0,1.5,6,0" Text="张炜国" />
<TextBlock Margin="0,1.5,6,0" Text="小零蚀" /> <TextBlock Margin="0,1.5,6,0" Text="咦嘻嘻" />
<TextBlock Margin="0,1.5,6,0" Text="唐墨白" /> <TextBlock Margin="0,1.5,6,0" Text="拟态虫" />
<TextBlock Margin="0,1.5,6,0" Text="香子兰" /> <TextBlock Margin="0,1.5,6,0" Text="蜥蜴拔" />
<TextBlock Margin="0,1.5,6,0" Text="胡萝北" /> <TextBlock Margin="0,1.5,6,0" Text="鬼画符" />
<TextBlock Margin="0,1.5,6,0" Text="乐永昶" /> <TextBlock Margin="0,1.5,6,0" Text="孙无为" />
<TextBlock Margin="0,1.5,6,0" Text="lingke" /> <TextBlock Margin="0,1.5,6,0" Text="浅陌菌" />
<TextBlock Margin="0,1.5,6,0" Text="东雪莲" /> <TextBlock Margin="0,1.5,6,0" Text="黯域星" />
<TextBlock Margin="0,1.5,6,0" Text="殷疯子" /> <TextBlock Margin="0,1.5,6,0" Text="愿不惑" />
<TextBlock Margin="0,1.5,6,0" Text="伊文斯" /> <TextBlock Margin="0,1.5,6,0" Text="三色堇" />
<TextBlock Margin="0,1.5,6,0" Text="丁乙恒" /> <TextBlock Margin="0,1.5,6,0" Text="訫卜變" />
<TextBlock Margin="0,1.5,6,0" Text="朔望月" /> <TextBlock Margin="0,1.5,6,0" Text="周家逸" />
<TextBlock Margin="0,1.5,6,0" Text="V0rt3x" /> <TextBlock Margin="0,1.5,6,0" Text="水芃叁" />
<TextBlock Margin="0,1.5,6,0" Text="提灯狼" /> <TextBlock Margin="0,1.5,6,0" Text="无限旡" />
<TextBlock Margin="0,1.5,6,0" Text="王孟清" /> <TextBlock Margin="0,1.5,6,0" Text="Enrico" />
<TextBlock Margin="0,1.5,6,0" Text="牛排鸭" /> <TextBlock Margin="0,1.5,6,0" Text="???" />
<TextBlock Margin="0,1.5,6,0" Text="王靖淳" /> <TextBlock Margin="0,1.5,6,0" Text="小南新" />
<TextBlock Margin="0,1.5,6,0" Text="嘿嘿地" /> <TextBlock Margin="0,1.5,6,0" Text="星千织" />
<TextBlock Margin="0,1.5,6,0" Text="鹿露璐" /> <TextBlock Margin="0,1.5,6,0" Text="CX330" />
<TextBlock Margin="0,1.5,6,0" Text="琪露诺" /> <TextBlock Margin="0,1.5,6,0" Text="林空月" />
<TextBlock Margin="0,1.5,6,0" Text="亚波人" /> <TextBlock Margin="0,1.5,6,0" Text="花开丶" />
<TextBlock Margin="0,1.5,6,0" Text="卿天岚" /> <TextBlock Margin="0,1.5,6,0" Text="琉璃子" />
<TextBlock Margin="0,1.5,6,0" Text="哈士奇" /> <TextBlock Margin="0,1.5,6,0" Text="冰冰冰" />
<TextBlock Margin="0,1.5,6,0" Text="江溪渺" /> <TextBlock Margin="0,1.5,6,0" Text="老花眼" />
<TextBlock Margin="0,1.5,6,0" Text="浮千傀" /> <TextBlock Margin="0,1.5,6,0" Text="哈贝吧" />
<TextBlock Margin="0,1.5,6,0" Text="恰啦啦" /> <TextBlock Margin="0,1.5,6,0" Text="蘑菇头" />
<TextBlock Margin="0,1.5,6,0" Text="梦痕丿" /> <TextBlock Margin="0,1.5,6,0" Text="小黄刀" />
<TextBlock Margin="0,1.5,6,0" Text="三叶铭" /> <TextBlock Margin="0,1.5,6,0" Text="大破林" />
<TextBlock Margin="0,1.5,6,0" Text="卿姬昉" /> <TextBlock Margin="0,1.5,6,0" Text="倾伏离" />
<TextBlock Margin="0,1.5,6,0" Text="疯一回" /> <TextBlock Margin="0,1.5,6,0" Text="豆角君" />
<TextBlock Margin="0,1.5,6,0" Text="李困困" /> <TextBlock Margin="0,1.5,6,0" Text="灵玄机" />
<TextBlock Margin="0,1.5,6,0" Text="厄尔萨" /> <TextBlock Margin="0,1.5,6,0" Text="Sherry" />
<TextBlock Margin="0,1.5,6,0" Text="伏拉夫" /> <TextBlock Margin="0,1.5,6,0" Text="lizi756" />
<TextBlock Margin="0,1.5,6,0" Text="α-蒎烯" /> <TextBlock Margin="0,1.5,6,0" Text="letitbe" />
<TextBlock Margin="0,1.5,6,0" Text="你的JK" /> <TextBlock Margin="0,1.5,6,0" Text="Ikorito" />
<TextBlock Margin="0,1.5,6,0" Text="jiujinh" /> <TextBlock Margin="0,1.5,6,0" Text="MCDD" />
<TextBlock Margin="0,1.5,6,0" Text="慕qwq" /> <TextBlock Margin="0,1.5,6,0" Text="nihility" />
<TextBlock Margin="0,1.5,6,0" Text="Twicat" /> <TextBlock Margin="0,1.5,6,0" Text="小宇Bo" />
<TextBlock Margin="0,1.5,6,0" Text="stenac" /> <TextBlock Margin="0,1.5,6,0" Text="2789th" />
<TextBlock Margin="0,1.5,6,0" Text="JOKER" /> <TextBlock Margin="0,1.5,6,0" Text="S1NKO" />
<TextBlock Margin="0,1.5,6,0" Text="半日谣." /> <TextBlock Margin="0,1.5,6,0" Text="Rimlue" />
<TextBlock Margin="0,1.5,6,0" Text="EX星辞" /> <TextBlock Margin="0,1.5,6,0" Text="FMDQI" />
<TextBlock Margin="0,1.5,6,0" Text="Tefrish" /> <TextBlock Margin="0,1.5,6,0" Text="Aevrud" />
<TextBlock Margin="0,1.5,6,0" Text="Triple9" /> <TextBlock Margin="0,1.5,6,0" Text="Larry76" />
<TextBlock Margin="0,1.5,6,0" Text="gediao" /> <TextBlock Margin="0,1.5,6,0" Text="Miantu" />
<TextBlock Margin="0,1.5,6,0" Text="Zen1th" /> <TextBlock Margin="0,1.5,6,0" Text="Nature." />
<TextBlock Margin="0,1.5,6,0" Text="CH南宫" /> <TextBlock Margin="0,1.5,6,0" Text="Autism." />
<TextBlock Margin="0,1.5,6,0" Text="qiumin" /> <TextBlock Margin="0,1.5,6,0" Text="MXUER" />
<TextBlock Margin="0,1.5,6,0" Text="cui白烁" /> <TextBlock Margin="0,1.5,6,0" Text="suifeng" />
<TextBlock Margin="0,1.5,6,0" Text="anyayu" /> <TextBlock Margin="0,1.5,6,0" Text="Yangeh" />
<TextBlock Margin="0,1.5,6,0" Text="SISENG" /> <TextBlock Margin="0,1.5,6,0" Text="Anmisu" />
<TextBlock Margin="0,1.5,6,0" Text="ro-suto" /> <TextBlock Margin="0,1.5,6,0" Text="TinkerZ" />
<TextBlock Margin="0,1.5,6,0" Text="GAGUA" /> <TextBlock Margin="0,1.5,6,0" Text="LaoGou" />
<TextBlock Margin="0,1.5,6,0" Text="Yi_xuan" /> <TextBlock Margin="0,1.5,6,0" Text="lkhkykk" />
<TextBlock Margin="0,1.5,6,0" Text="MC芬达" /> <TextBlock Margin="0,1.5,6,0" Text="N/Azais" />
<TextBlock Margin="0,1.5,6,0" Text="shallow" /> <TextBlock Margin="0,1.5,6,0" Text="KISSHIP" />
<TextBlock Margin="0,1.5,6,0" Text="ETL蓝夢" /> <TextBlock Margin="0,1.5,6,0" Text="Vanessa" />
<TextBlock Margin="0,1.5,6,0" Text="𝕳轩辕氏" /> <TextBlock Margin="0,1.5,6,0" Text="lanyuan" />
<TextBlock Margin="0,1.5,6,0" Text="谜茶360" /> <TextBlock Margin="0,1.5,6,0" Text="BBBKKK" />
<TextBlock Margin="0,1.5,6,0" Text="颜沐Jun" /> <TextBlock Margin="0,1.5,6,0" Text="mchim1" />
<TextBlock Margin="0,1.5,6,0" Text="CS2七松" /> <TextBlock Margin="0,1.5,6,0" Text="MANBA" />
<TextBlock Margin="0,1.5,6,0" Text="Eventful" /> <TextBlock Margin="0,1.5,6,0" Text="Tairitsu_" />
<TextBlock Margin="0,1.5,6,0" Text="Molioos" /> <TextBlock Margin="0,1.5,6,0" Text="宇陌୧⍤⃝🇨🇳" />
<TextBlock Margin="0,1.5,6,0" Text="logicum" /> <TextBlock Margin="0,1.5,6,0" Text="我不在线" />
<TextBlock Margin="0,1.5,6,0" Text="Mr_loba" /> <TextBlock Margin="0,1.5,6,0" Text="南吕十三" />
<TextBlock Margin="0,1.5,6,0" Text="一叶知秋" /> <TextBlock Margin="0,1.5,6,0" Text="Parapha" />
<TextBlock Margin="0,1.5,6,0" Text="我妻狗卷" /> <TextBlock Margin="0,1.5,6,0" Text="开始学习" />
<TextBlock Margin="0,1.5,6,0" Text="御神凌霜" /> <TextBlock Margin="0,1.5,6,0" Text="对方和你" />
<TextBlock Margin="0,1.5,6,0" Text="狼烟居士" /> <TextBlock Margin="0,1.5,6,0" Text="雪辰千叶" />
<TextBlock Margin="0,1.5,6,0" Text="千风伶羽" /> <TextBlock Margin="0,1.5,6,0" Text="残风尘缘" />
<TextBlock Margin="0,1.5,6,0" Text="mumiao" /> <TextBlock Margin="0,1.5,6,0" Text="黄粱一梦" />
<TextBlock Margin="0,1.5,6,0" Text="欧刃赛卌" /> <TextBlock Margin="0,1.5,6,0" Text="腊肉清蒸" />
<TextBlock Margin="0,1.5,6,0" Text="蓝白幻影" /> <TextBlock Margin="0,1.5,6,0" Text="画下花夏" />
<TextBlock Margin="0,1.5,6,0" Text="鸭梨山大" /> <TextBlock Margin="0,1.5,6,0" Text="蛮列后备" />
<TextBlock Margin="0,1.5,6,0" Text="一条鱼瓯" /> <TextBlock Margin="0,1.5,6,0" Text="魔法少年" />
<TextBlock Margin="0,1.5,6,0" Text="苇渚泊舟" /> <TextBlock Margin="0,1.5,6,0" Text="戒者有八" />
<TextBlock Margin="0,1.5,6,0" Text="花开富贵" /> <TextBlock Margin="0,1.5,6,0" Text="躺平如尸" />
<TextBlock Margin="0,1.5,6,0" Text="多肉藕片" /> <TextBlock Margin="0,1.5,6,0" Text="萝卜奶酪" />
<TextBlock Margin="0,1.5,6,0" Text="浮屠阿仙" /> <TextBlock Margin="0,1.5,6,0" Text="Erinneys" />
<TextBlock Margin="0,1.5,6,0" Text="繁星萤火" /> <TextBlock Margin="0,1.5,6,0" Text="病名為愛" />
<TextBlock Margin="0,1.5,6,0" Text="沐风沐雨" /> <TextBlock Margin="0,1.5,6,0" Text="伊卡洛斯" />
<TextBlock Margin="0,1.5,6,0" Text="金槍羽翼" /> <TextBlock Margin="0,1.5,6,0" Text="云鹰肥将" />
<TextBlock Margin="0,1.5,6,0" Text="龘羅孖倆" /> <TextBlock Margin="0,1.5,6,0" Text="爱莉信徒" />
<TextBlock Margin="0,1.5,6,0" Text="鸽子王雷" /> <TextBlock Margin="0,1.5,6,0" Text="user晚曦" />
<TextBlock Margin="0,1.5,6,0" Text="红莲焚霞" /> <TextBlock Margin="0,1.5,6,0" Text="欣某在此" />
<TextBlock Margin="0,1.5,6,0" Text="老凌野少" /> <TextBlock Margin="0,1.5,6,0" Text="繁星末路" />
<TextBlock Margin="0,1.5,6,0" Text="孤酒青魇" /> <TextBlock Margin="0,1.5,6,0" Text="鲸鱼橘子" />
<TextBlock Margin="0,1.5,6,0" Text="天空奇己" /> <TextBlock Margin="0,1.5,6,0" Text="坠入幻想" />
<TextBlock Margin="0,1.5,6,0" Text="霜月秋阳" /> <TextBlock Margin="0,1.5,6,0" Text="卑微老万" />
<TextBlock Margin="0,1.5,6,0" Text="啃瓜咸鱼" /> <TextBlock Margin="0,1.5,6,0" Text="乌云踏雪" />
<TextBlock Margin="0,1.5,6,0" Text="淡墨染星" /> <TextBlock Margin="0,1.5,6,0" Text="公积金卡" />
<TextBlock Margin="0,1.5,6,0" Text="2k2k114" /> <TextBlock Margin="0,1.5,6,0" Text="各务原念" />
<TextBlock Margin="0,1.5,6,0" Text="肉蛋葱鸡" /> <TextBlock Margin="0,1.5,6,0" Text="风间琉璃" />
<TextBlock Margin="0,1.5,6,0" Text="傻鹅二号" /> <TextBlock Margin="0,1.5,6,0" Text="杨枝甘露" />
<TextBlock Margin="0,1.5,6,0" Text="动漫阿宅" /> <TextBlock Margin="0,1.5,6,0" Text="晚安晚安" />
<TextBlock Margin="0,1.5,6,0" Text="只若初见" /> <TextBlock Margin="0,1.5,6,0" Text="牛奶曲奇" />
<TextBlock Margin="0,1.5,6,0" Text="Sakuzyo" /> <TextBlock Margin="0,1.5,6,0" Text="魔法空间" />
<TextBlock Margin="0,1.5,6,0" Text="1415145" /> <TextBlock Margin="0,1.5,6,0" Text="一路向北" />
<TextBlock Margin="0,1.5,6,0" Text="消失的喵" /> <TextBlock Margin="0,1.5,6,0" Text="百里无焉" />
<TextBlock Margin="0,1.5,6,0" Text="春山如笑" /> <TextBlock Margin="0,1.5,6,0" Text="皮蛋肥猫" />
<TextBlock Margin="0,1.5,6,0" Text="幻天阿绝" /> <TextBlock Margin="0,1.5,6,0" Text="身披坚甲" />
<TextBlock Margin="0,1.5,6,0" Text="忘道蜂枫" /> <TextBlock Margin="0,1.5,6,0" Text="近则不逊" />
<TextBlock Margin="0,1.5,6,0" Text="深海无渊" /> <TextBlock Margin="0,1.5,6,0" Text="卡白小洛" />
<TextBlock Margin="0,1.5,6,0" Text="心软的神" /> <TextBlock Margin="0,1.5,6,0" Text="即墨非墨" />
<TextBlock Margin="0,1.5,6,0" Text="砂锅豆腐" /> <TextBlock Margin="0,1.5,6,0" Text="准备冒险" />
<TextBlock Margin="0,1.5,6,0" Text="好人好逝" /> <TextBlock Margin="0,1.5,6,0" Text="喰梦优音" />
<TextBlock Margin="0,1.5,6,0" Text="登峰破晓" /> <TextBlock Margin="0,1.5,6,0" Text="雨夜色中" />
<TextBlock Margin="0,1.5,6,0" Text="东隅未殁" /> <TextBlock Margin="0,1.5,6,0" Text="沃特拉奇" />
<TextBlock Margin="0,1.5,6,0" Text="星花银影" /> <TextBlock Margin="0,1.5,6,0" Text="闲鱼教授" />
<TextBlock Margin="0,1.5,6,0" Text="往事如昔" /> <TextBlock Margin="0,1.5,6,0" Text="北川暮雨" />
<TextBlock Margin="0,1.5,6,0" Text="林克之空" /> <TextBlock Margin="0,1.5,6,0" Text="生根发芽" />
<TextBlock Margin="0,1.5,6,0" Text="0X00001" /> <TextBlock Margin="0,1.5,6,0" Text="暮霭凝缘" />
<TextBlock Margin="0,1.5,6,0" Text="MuMAA" /> <TextBlock Margin="0,1.5,6,0" Text="孤灯裹尘" />
<TextBlock Margin="0,1.5,6,0" Text="溯枫qwq" /> <TextBlock Margin="0,1.5,6,0" Text="日薪月亿" />
<TextBlock Margin="0,1.5,6,0" Text="Tacenda" /> <TextBlock Margin="0,1.5,6,0" Text="北默千鹤" />
<TextBlock Margin="0,1.5,6,0" Text="sky fung" /> <TextBlock Margin="0,1.5,6,0" Text="咸鱼一条" />
<TextBlock Margin="0,1.5,6,0" Text="👁👄👃" /> <TextBlock Margin="0,1.5,6,0" Text="禾辞共振" />
<TextBlock Margin="0,1.5,6,0" Text="秋慕owo" /> <TextBlock Margin="0,1.5,6,0" Text="雨宫冰莲" />
<TextBlock Margin="0,1.5,6,0" Text="船长Yuki" /> <TextBlock Margin="0,1.5,6,0" Text="BG4LHO" />
<TextBlock Margin="0,1.5,6,0" Text="HIMEKO" /> <TextBlock Margin="0,1.5,6,0" Text="没啥想法" />
<TextBlock Margin="0,1.5,6,0" Text="是小61呀" /> <TextBlock Margin="0,1.5,6,0" Text="古色沉竹" />
<TextBlock Margin="0,1.5,6,0" Text="47号虾球" /> <TextBlock Margin="0,1.5,6,0" Text="W污狮W" />
<TextBlock Margin="0,1.5,6,0" Text="Alancnel" /> <TextBlock Margin="0,1.5,6,0" Text="jinyucao" />
<TextBlock Margin="0,1.5,6,0" Text="Muertesl" /> <TextBlock Margin="0,1.5,6,0" Text="MESGRZ" />
<TextBlock Margin="0,1.5,6,0" Text="猫幺Suro" /> <TextBlock Margin="0,1.5,6,0" Text="qiewang" />
<TextBlock Margin="0,1.5,6,0" Text="i&apos;g&apos;d&apos;y&apos;f&apos;s" /> <TextBlock Margin="0,1.5,6,0" Text="bao9898" />
<TextBlock Margin="0,1.5,6,0" Text="xiaochen" /> <TextBlock Margin="0,1.5,6,0" Text="幻沫_123" />
<TextBlock Margin="0,1.5,6,0" Text="ㅤSIMV9r" /> <TextBlock Margin="0,1.5,6,0" Text="Yuting.Li" />
<TextBlock Margin="0,1.5,6,0" Text="alpHabet" /> <TextBlock Margin="0,1.5,6,0" Text="Zaaaako" />
<TextBlock Margin="0,1.5,6,0" Text="Charlotte" /> <TextBlock Margin="0,1.5,6,0" Text="Tianqiao" />
<TextBlock Margin="0,1.5,6,0" Text="paperfish" /> <TextBlock Margin="0,1.5,6,0" Text="EMILIIIIA" />
<TextBlock Margin="0,1.5,6,0" Text="stealtoup" /> <TextBlock Margin="0,1.5,6,0" Text="阿尔法.@" />
<TextBlock Margin="0,1.5,6,0" Text="bingyan0" /> <TextBlock Margin="0,1.5,6,0" Text="neymarjr" />
<TextBlock Margin="0,1.5,6,0" Text="MselQAQ" /> <TextBlock Margin="0,1.5,6,0" Text="AsUWish" />
<TextBlock Margin="0,1.5,6,0" Text="UZQueen" /> <TextBlock Margin="0,1.5,6,0" Text="waxberry" />
<TextBlock Margin="0,1.5,6,0" Text="hihospital" /> <TextBlock Margin="0,1.5,6,0" Text="彼岸的***" />
<TextBlock Margin="0,1.5,6,0" Text="cutegirl3k" /> <TextBlock Margin="0,1.5,6,0" Text="liminalily" />
<TextBlock Margin="0,1.5,6,0" Text="Prifo_lone" /> <TextBlock Margin="0,1.5,6,0" Text="sansisthe" />
<TextBlock Margin="0,1.5,6,0" Text="有....暗星!" /> <TextBlock Margin="0,1.5,6,0" Text="RickyZhu" />
<TextBlock Margin="0,1.5,6,0" Text="Hitsser 1.0" /> <TextBlock Margin="0,1.5,6,0" Text="TenKings" />
<TextBlock Margin="0,1.5,6,0" Text="一只憨火龙" /> <TextBlock Margin="0,1.5,6,0" Text="yvnchvan" />
<TextBlock Margin="0,1.5,6,0" Text="异色收集癖" /> <TextBlock Margin="0,1.5,6,0" Text="喵呜派zzz" />
<TextBlock Margin="0,1.5,6,0" Text="DsxiaKsxia" /> <TextBlock Margin="0,1.5,6,0" Text="Mz小狐仙" />
<TextBlock Margin="0,1.5,6,0" Text="Plainsman" /> <TextBlock Margin="0,1.5,6,0" Text="huangwu" />
<TextBlock Margin="0,1.5,6,0" Text="湛蓝一四七" /> <TextBlock Margin="0,1.5,6,0" Text="玖月Yiyue" />
<TextBlock Margin="0,1.5,6,0" Text="凛果与百橙" /> <TextBlock Margin="0,1.5,6,0" Text="石头WAW" />
<TextBlock Margin="0,1.5,6,0" Text="叶落才知秋" /> <TextBlock Margin="0,1.5,6,0" Text="YiYi_Sama" />
<TextBlock Margin="0,1.5,6,0" Text="好喝的氦三" /> <TextBlock Margin="0,1.5,6,0" Text="theancato" />
<TextBlock Margin="0,1.5,6,0" Text="星河奈叶风" /> <TextBlock Margin="0,1.5,6,0" Text="Feng_mee" />
<TextBlock Margin="0,1.5,6,0" Text="魔法且河豚" /> <TextBlock Margin="0,1.5,6,0" Text="_東森浮星_" />
<TextBlock Margin="0,1.5,6,0" Text="魏小泓泓泓" /> <TextBlock Margin="0,1.5,6,0" Text="dark 寿司s" />
<TextBlock Margin="0,1.5,6,0" Text="阴冷潮湿酱" /> <TextBlock Margin="0,1.5,6,0" Text="zhouuohz" />
<TextBlock Margin="0,1.5,6,0" Text="一只白色缨" /> <TextBlock Margin="0,1.5,6,0" Text="躺床上发霉" />
<TextBlock Margin="0,1.5,6,0" Text="结局的椰子" /> <TextBlock Margin="0,1.5,6,0" Text="装睡的少爷" />
<TextBlock Margin="0,1.5,6,0" Text="三玖二拾柒" /> <TextBlock Margin="0,1.5,6,0" Text="无想的贤者" />
<TextBlock Margin="0,1.5,6,0" Text="旭旭大魔王" /> <TextBlock Margin="0,1.5,6,0" Text="我不是陌行" />
<TextBlock Margin="0,1.5,6,0" Text="离人如飘萍" /> <TextBlock Margin="0,1.5,6,0" Text="海绵体宝宝" />
<TextBlock Margin="0,1.5,6,0" Text="秋葉逐殘夏" /> <TextBlock Margin="0,1.5,6,0" Text="猫不吃咸鱼" />
<TextBlock Margin="0,1.5,6,0" Text="水星有海吗" /> <TextBlock Margin="0,1.5,6,0" Text="别太失望啦" />
<TextBlock Margin="0,1.5,6,0" Text="吃饱爱睡觉" /> <TextBlock Margin="0,1.5,6,0" Text="土豆粉底液" />
<TextBlock Margin="0,1.5,6,0" Text="ac纯萌新cy" /> <TextBlock Margin="0,1.5,6,0" Text="沈默已陌鑫" />
<TextBlock Margin="0,1.5,6,0" Text="岁月suiyue" /> <TextBlock Margin="0,1.5,6,0" Text="向神明祈祷" />
<TextBlock Margin="0,1.5,6,0" Text="陌然MajorC" /> <TextBlock Margin="0,1.5,6,0" Text="Zurkey106" />
<TextBlock Margin="0,1.5,6,0" Text="-Pareidolia." /> <TextBlock Margin="0,1.5,6,0" Text="stargolven" />
<TextBlock Margin="0,1.5,6,0" Text="蛙蛙大王Kzr" /> <TextBlock Margin="0,1.5,6,0" Text="拒绝过阿姨" />
<TextBlock Margin="0,1.5,6,0" Text="Aurorawalk" /> <TextBlock Margin="0,1.5,6,0" Text="拟态深埋箱" />
<TextBlock Margin="0,1.5,6,0" Text="Yhcmdbfwz" /> <TextBlock Margin="0,1.5,6,0" Text="凤凰院凶真" />
<TextBlock Margin="0,1.5,6,0" Text="Ayers赜蠹嬭" /> <TextBlock Margin="0,1.5,6,0" Text="时间&轮盘" />
<TextBlock Margin="0,1.5,6,0" Text="We1J1uSh1" /> <TextBlock Margin="0,1.5,6,0" Text="无心于刹那" />
<TextBlock Margin="0,1.5,6,0" Text="xinghen233" /> <TextBlock Margin="0,1.5,6,0" Text="粗心忘带头" />
<TextBlock Margin="0,1.5,6,0" Text="kingkongair" /> <TextBlock Margin="0,1.5,6,0" Text="提线的人偶" />
<TextBlock Margin="0,1.5,6,0" Text="最后一只斑鸠" /> <TextBlock Margin="0,1.5,6,0" Text="非慕所不敬" />
<TextBlock Margin="0,1.5,6,0" Text="随便写的名字" /> <TextBlock Margin="0,1.5,6,0" Text="天生会炸鱼" />
<TextBlock Margin="0,1.5,6,0" Text="芥末味青豌豆" /> <TextBlock Margin="0,1.5,6,0" Text="世界第一矮" />
<TextBlock Margin="0,1.5,6,0" Text="鹅给金华额度" /> <TextBlock Margin="0,1.5,6,0" Text="雷焰不虚误" />
<TextBlock Margin="0,1.5,6,0" Text="LisaPatorsed" /> <TextBlock Margin="0,1.5,6,0" Text="一株腐竹吖" />
<TextBlock Margin="0,1.5,6,0" Text="鸡屁股配大蒜" /> <TextBlock Margin="0,1.5,6,0" Text="七曜不打针" />
<TextBlock Margin="0,1.5,6,0" Text="我真没想开摆" /> <TextBlock Margin="0,1.5,6,0" Text="Ferrum000" />
<TextBlock Margin="0,1.5,6,0" Text="我不想取名字" /> <TextBlock Margin="0,1.5,6,0" Text="小白pandk" />
<TextBlock Margin="0,1.5,6,0" Text="平安京的封兽" /> <TextBlock Margin="0,1.5,6,0" Text="LapalceXIII" />
<TextBlock Margin="0,1.5,6,0" Text="被压力的鸭梨" /> <TextBlock Margin="0,1.5,6,0" Text="charlesmin" />
<TextBlock Margin="0,1.5,6,0" Text="小田love白子" /> <TextBlock Margin="0,1.5,6,0" Text="DeepSleep" />
<TextBlock Margin="0,1.5,6,0" Text="Sasha_Xiong" /> <TextBlock Margin="0,1.5,6,0" Text="rishaoshan" />
<TextBlock Margin="0,1.5,6,0" Text="赤狸_crisnow" /> <TextBlock Margin="0,1.5,6,0" Text="LJJ有擎天柱" />
<TextBlock Margin="0,1.5,6,0" Text="dorod月gour" /> <TextBlock Margin="0,1.5,6,0" Text="builder天识" />
<TextBlock Margin="0,1.5,6,0" Text="Name0emaN" /> <TextBlock Margin="0,1.5,6,0" Text="Cathan猫皁" />
<TextBlock Margin="0,1.5,6,0" Text="happypickles" /> <TextBlock Margin="0,1.5,6,0" Text="猫熊NICEST" />
<TextBlock Margin="0,1.5,6,0" Text="无中生有为dd" /> <TextBlock Margin="0,1.5,6,0" Text="heaven伯特" />
<TextBlock Margin="0,1.5,6,0" Text="polarisdream" /> <TextBlock Margin="0,1.5,6,0" Text="礼央子sama" />
<TextBlock Margin="0,1.5,6,0" Text="YHKooooooy" /> <TextBlock Margin="0,1.5,6,0" Text="蠢萌der小白" />
<TextBlock Margin="0,1.5,6,0" Text="Sakura2403ly" /> <TextBlock Margin="0,1.5,6,0" Text="Dying Bone" />
<TextBlock Margin="0,1.5,6,0" Text="Flying Potato" /> <TextBlock Margin="0,1.5,6,0" Text="DaisyCrown" />
<TextBlock Margin="0,1.5,6,0" Text="陈昊骏泥碳GR" /> <TextBlock Margin="0,1.5,6,0" Text="2127864647" />
<TextBlock Margin="0,1.5,6,0" Text="funnyzhuling" /> <TextBlock Margin="0,1.5,6,0" Text="Z123456789" />
<TextBlock Margin="0,1.5,6,0" Text="Turbo_Zhang" /> <TextBlock Margin="0,1.5,6,0" Text="MOMOHAN" />
<TextBlock Margin="0,1.5,6,0" Text="SUBLawrence" /> <TextBlock Margin="0,1.5,6,0" Text="舸沫_Official" />
<TextBlock Margin="0,1.5,6,0" Text="Epsilon-9新人" /> <TextBlock Margin="0,1.5,6,0" Text="ξKō Hei-seiξ" />
<TextBlock Margin="0,1.5,6,0" Text="TheDovebach" /> <TextBlock Margin="0,1.5,6,0" Text="CookieNeko" />
<TextBlock Margin="0,1.5,6,0" Text="wobushi_furry" /> <TextBlock Margin="0,1.5,6,0" Text="伊吹风子大人" />
<TextBlock Margin="0,1.5,6,0" Text="FTC夏日的余晖" /> <TextBlock Margin="0,1.5,6,0" Text="バベルの悪霊" />
<TextBlock Margin="0,1.5,6,0" Text="Isshiki Satoshi" /> <TextBlock Margin="0,1.5,6,0" Text="沉默的玄晓羲" />
<TextBlock Margin="0,1.5,6,0" Text="Srive_Schegler" /> <TextBlock Margin="0,1.5,6,0" Text="为什么水无鱼" />
<TextBlock Margin="0,1.5,6,0" Text="1080p安全卫士" /> <TextBlock Margin="0,1.5,6,0" Text="无敌猫猫刺客" />
<TextBlock Margin="0,1.5,6,0" Text="开心上学玩游戏" /> <TextBlock Margin="0,1.5,6,0" Text="久恋丿如初漾" />
<TextBlock Margin="0,1.5,6,0" Text="重生之发财传奇" /> <TextBlock Margin="0,1.5,6,0" Text="肥宅不是胖子" />
<TextBlock Margin="0,1.5,6,0" Text="某科学的小狐狸" /> <TextBlock Margin="0,1.5,6,0" Text="路边的小野寺" />
<TextBlock Margin="0,1.5,6,0" Text="爱喝可乐的猫猫" /> <TextBlock Margin="0,1.5,6,0" Text="要一个手抓饼" />
<TextBlock Margin="0,1.5,6,0" Text="Vsinger-洛天依" /> <TextBlock Margin="0,1.5,6,0" Text="从未擦肩而过" />
<TextBlock Margin="0,1.5,6,0" Text="芙利莲最も強い" /> <TextBlock Margin="0,1.5,6,0" Text="瓜皮的大甘蔗" />
<TextBlock Margin="0,1.5,6,0" Text="洋葱香菜炒蛋子" /> <TextBlock Margin="0,1.5,6,0" Text="LI_JIACHENG" />
<TextBlock Margin="0,1.5,6,0" Text="不开心,不快乐" /> <TextBlock Margin="0,1.5,6,0" Text="mouyangjun" />
<TextBlock Margin="0,1.5,6,0" Text="忧郁的破产鼠鼠" /> <TextBlock Margin="0,1.5,6,0" Text="我no想取名字" />
<TextBlock Margin="0,1.5,6,0" Text="我的世界爱好者" /> <TextBlock Margin="0,1.5,6,0" Text="机电2516汪帅" />
<TextBlock Margin="0,1.5,6,0" Text="半透明的小透明" /> <TextBlock Margin="0,1.5,6,0" Text="QueNanNNN" />
<TextBlock Margin="0,1.5,6,0" Text="到处闲逛的咸鱼" /> <TextBlock Margin="0,1.5,6,0" Text="huamozhuke" />
<TextBlock Margin="0,1.5,6,0" Text="什么落寒都不是" /> <TextBlock Margin="0,1.5,6,0" Text="Titanic Rising" />
<TextBlock Margin="0,1.5,6,0" Text="没什么名字好取" /> <TextBlock Margin="0,1.5,6,0" Text="Skymunchrex" />
<TextBlock Margin="0,1.5,6,0" Text="Wanderer5147" /> <TextBlock Margin="0,1.5,6,0" Text="☆百☆岁☆枭☆" />
<TextBlock Margin="0,1.5,6,0" Text="取个名字耗阳寿" /> <TextBlock Margin="0,1.5,6,0" Text="CarbonDHeat" />
<TextBlock Margin="0,1.5,6,0" Text="北熊何时与狼行" /> <TextBlock Margin="0,1.5,6,0" Text="不爱fa电的小安" />
<TextBlock Margin="0,1.5,6,0" Text="向着有风的地方" /> <TextBlock Margin="0,1.5,6,0" Text="是enderman噢" />
<TextBlock Margin="0,1.5,6,0" Text="AAA神秘人批发" /> <TextBlock Margin="0,1.5,6,0" Text="无敌兵王陈亚蒙" />
<TextBlock Margin="0,1.5,6,0" Text="Bangbang2025" /> <TextBlock Margin="0,1.5,6,0" Text="申鹤红绳牵我魂" />
<TextBlock Margin="0,1.5,6,0" Text="贝吉蒙Begimon" /> <TextBlock Margin="0,1.5,6,0" Text="背不完的郭同学" />
<TextBlock Margin="0,1.5,6,0" Text="取个名字真难0.0" /> <TextBlock Margin="0,1.5,6,0" Text="追随自己的星星" />
<TextBlock Margin="0,1.5,6,0" Text="NoTuring_Steve" /> <TextBlock Margin="0,1.5,6,0" Text="帅道无上大宗师" />
<TextBlock Margin="0,1.5,6,0" Text="VividerCafe2379" /> <TextBlock Margin="0,1.5,6,0" Text="LucidZuowang" />
<TextBlock Margin="0,1.5,6,0" Text="冰冰watermelon" /> <TextBlock Margin="0,1.5,6,0" Text="AAA.果粒橙批发" />
<TextBlock Margin="0,1.5,6,0" Text="DOODOODOOD" /> <TextBlock Margin="0,1.5,6,0" Text="Omega•Epsilon" />
<TextBlock Margin="0,1.5,6,0" Text="怕做苦力的苦力怕" /> <TextBlock Margin="0,1.5,6,0" Text="Ries1ing_嗷菲兽" />
<TextBlock Margin="0,1.5,6,0" Text="小句号就是小句号" /> <TextBlock Margin="0,1.5,6,0" Text="Understand You" />
<TextBlock Margin="0,1.5,6,0" Text="弹跳爆炸小鱼珠🐟" /> <TextBlock Margin="0,1.5,6,0" Text="AAADasAbi9527" />
<TextBlock Margin="0,1.5,6,0" Text="Xanadu Silk Fable" /> <TextBlock Margin="0,1.5,6,0" Text="筱筱雨吖_Official" />
<TextBlock Margin="0,1.5,6,0" Text="看我的都得白内障了" /> <TextBlock Margin="0,1.5,6,0" Text="DecadeMagenta" />
<TextBlock Margin="0,1.5,6,0" Text="【点击请输入文本】" /> <TextBlock Margin="0,1.5,6,0" Text="0223YUKIUKOORI" />
<TextBlock Margin="0,1.5,6,0" Text="AAA难办批发商乌鸦" /> <TextBlock Margin="0,1.5,6,0" Text="☆Etritz__㯊龝.ET☆" />
<TextBlock Margin="0,1.5,6,0" Text="zhuanzhuanzhaun!" /> <TextBlock Margin="0,1.5,6,0" Text="NineTailed123213" />
<TextBlock Margin="0,1.5,6,0" Text="你上课记得记得你先忙" /> <TextBlock Margin="0,1.5,6,0" Text="于电子星海寻极星间" />
<TextBlock Margin="0,1.5,6,0" Text="``Aurora. (极光)" />
<TextBlock Margin="0,1.5,6,0" Text="来自少女前线的刀客塔" />
<TextBlock Margin="0,1.5,6,0" Text="￴￴ ￴￴此恨绵绵无绝期" />
<TextBlock Margin="0,1.5,6,0" Text="3425789779@qq.com" />
<TextBlock Margin="0,1.5,6,0" Text="AdeleineSummy的粉丝" />
<TextBlock Margin="0,1.5,6,0" Text="有趣的灵魂肘击无趣的肉体" />
<TextBlock Margin="0,1.5,6,0" Text="我是pipikeng我喂自己袋盐" />
<TextBlock Margin="0,1.5,6,0" Text="人族人亚族人属智人种晚期智人亚种" />
</WrapPanel> </WrapPanel>
<Grid Height="35"> <Grid Height="35">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@@ -534,13 +545,13 @@
<TextBlock Text="隐私声明与个人信息保护政策" TextWrapping="Wrap" Margin="0,10,0,4" FontWeight="Bold" /> <TextBlock Text="隐私声明与个人信息保护政策" TextWrapping="Wrap" Margin="0,10,0,4" FontWeight="Bold" />
<TextBlock Margin="0,0,0,2" LineHeight="17" Text="PCL 没有收集个人信息,所以这里其实啥都不用写……&#xa;留个这个其实只是为了让你知道 PCL 没有收集个人信息,诶嘿。" TextWrapping="Wrap" /> <TextBlock Margin="0,0,0,2" LineHeight="17" Text="PCL 没有收集个人信息,所以这里其实啥都不用写……&#xa;留个这个其实只是为了让你知道 PCL 没有收集个人信息,诶嘿。" TextWrapping="Wrap" />
<TextBlock Text="其他信息" TextWrapping="Wrap" Margin="0,10,0,4" FontWeight="Bold" /> <TextBlock Text="其他信息" TextWrapping="Wrap" Margin="0,10,0,4" FontWeight="Bold" />
<TextBlock Margin="0,0,0,2" LineHeight="17" Text="Copyright © 成都瓜皮龙科技有限公司&#13;计算机软件著作权登记号:2020SR0875133&#13;违法违规行为举报投诉邮箱:admin@meloong.com&#13;非 MINECRAFT 官方产品。未经 MOJANG 或 MICROSOFT 批准,也不与 MOJANG 或 MICROSOFT 关联。" TextWrapping="Wrap" /> <TextBlock Margin="0,0,0,2" LineHeight="17" Text="Copyright © 成都瓜皮龙科技有限公司&#13;计算机软件著作权登记号:2020SR0875133&#13;本软件是 InfstarMC 基于 Plain Craft Launcher (PCL) 独立进行的第三方二次开发版本,不代表 PCL 官方。&#13;非 MINECRAFT 官方产品。未经 MOJANG 或 MICROSOFT 批准,也不与 MOJANG 或 MICROSOFT 关联。" TextWrapping="Wrap" />
<StackPanel Orientation="Horizontal" Height="35" HorizontalAlignment="Left" Margin="0,12,0,0"> <StackPanel Orientation="Horizontal" Height="35" HorizontalAlignment="Left" Margin="0,12,0,0">
<local:MyButton Text="用户协议与免责声明" Width="170" Padding="13,0" Margin="0,0,20,0" ColorType="Highlight" <local:MyButton Text="用户协议与免责声明" Width="170" Padding="13,0" Margin="0,0,20,0" ColorType="Highlight"
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://shimo.im/docs/rGrd8pY8xWkt6ryW#anchor-RnU9" /> local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://shimo.im/docs/rGrd8pY8xWkt6ryW#anchor-RnU9" />
<local:MyButton Text="PCL 分发有限许可" Width="170" Padding="13,0" Margin="0,0,20,0" <local:MyButton Text="PCL 分发有限许可" Width="170" Padding="13,0" Margin="0,0,20,0"
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://shimo.im/docs/rGrd8pY8xWkt6ryW#anchor-X0bo" /> local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://shimo.im/docs/rGrd8pY8xWkt6ryW#anchor-X0bo" />
<local:MyButton Text="开源代码" Width="140" Padding="13,0" local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://github.com/Hex-Dragon/PCL2" /> <local:MyButton Text="开源代码" Width="140" Padding="13,0" local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://git.infstar.cn/InfStarMC/PCL-ISE" />
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</local:MyCard> </local:MyCard>
@@ -599,14 +610,14 @@
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://opensource.org/licenses/MIT" /> local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://opensource.org/licenses/MIT" />
<TextBlock Grid.Row="6" Grid.Column="0" Text="NAudio" TextWrapping="Wrap" FontWeight="Bold" /> <TextBlock Grid.Row="6" Grid.Column="0" Text="NAudio" TextWrapping="Wrap" FontWeight="Bold" />
<TextBlock Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2015 Mark Heath (mark.heath@gmail.com)&#xa;Licensed under the MIT License." TextWrapping="Wrap" /> <TextBlock Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2015 Mark Heath (mark.heath@gmail.com)&#xa;Licensed under the Microsoft Public License (Ms-PL)." TextWrapping="Wrap" />
<local:MyButton Grid.Row="7" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left" <local:MyButton Grid.Row="7" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://github.com/naudio/NAudio" /> local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://github.com/naudio/NAudio" />
<local:MyButton Grid.Row="7" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left" <local:MyButton Grid.Row="7" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://github.com/naudio/NAudio/blob/master/license.txt" /> local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://github.com/naudio/NAudio/blob/f98664ba3d5966a736dd5cf45329e542be5fece8/license.txt" />
<TextBlock Grid.Row="8" Grid.Column="0" Text="Ookii.Dialogs" TextWrapping="Wrap" FontWeight="Bold" /> <TextBlock Grid.Row="8" Grid.Column="0" Text="Ookii.Dialogs" TextWrapping="Wrap" FontWeight="Bold" />
<TextBlock Grid.Row="8" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2009 Sven Groot (Ookii.org)" TextWrapping="Wrap" /> <TextBlock Grid.Row="8" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2009 Sven Groot (Ookii.org)&#xa;Licensed under the BSD 3-Clause License." TextWrapping="Wrap" />
<local:MyButton Grid.Row="9" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left" <local:MyButton Grid.Row="9" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://www.ookii.org/Software/Dialogs/" /> local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://www.ookii.org/Software/Dialogs/" />
<local:MyButton Grid.Row="9" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left" <local:MyButton Grid.Row="9" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
@@ -628,29 +639,36 @@
<TextBlock Grid.Row="14" Grid.Column="0" Text="Iconfont" TextWrapping="Wrap" FontWeight="Bold" /> <TextBlock Grid.Row="14" Grid.Column="0" Text="Iconfont" TextWrapping="Wrap" FontWeight="Bold" />
<TextBlock Grid.Row="14" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © ALIMAMA MUX, powered by alimama THX." TextWrapping="Wrap" /> <TextBlock Grid.Row="14" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © ALIMAMA MUX, powered by alimama THX." TextWrapping="Wrap" />
<local:MyButton Grid.Row="15" Grid.Column="2" MinWidth="140" Text="查看原网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left" <local:MyButton Grid.Row="15" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://www.iconfont.cn/" /> local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://www.iconfont.cn/" />
<TextBlock Grid.Row="16" Grid.Column="0" Text="EasyTier" TextWrapping="Wrap" FontWeight="Bold" /> <!--<TextBlock Grid.Row="16" Grid.Column="0" Text="EasyTier" TextWrapping="Wrap" FontWeight="Bold" />
<TextBlock Grid.Row="16" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2024-present Easytier Programme within The Commons Conservancy&#xa;Licensed under the GNU Lesser General Public License." TextWrapping="Wrap" /> <TextBlock Grid.Row="16" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2024-present Easytier Programme within The Commons Conservancy&#xa;Licensed under the GNU Lesser General Public License." TextWrapping="Wrap" />
<local:MyButton Grid.Row="17" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left" <local:MyButton Grid.Row="17" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://easytier.cn/" /> local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://easytier.cn/" />
<local:MyButton Grid.Row="17" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left" <local:MyButton Grid.Row="17" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://github.com/EasyTier/EasyTier/blob/main/LICENSE" /> local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://github.com/EasyTier/EasyTier/blob/main/LICENSE" />-->
<TextBlock Grid.Row="18" Grid.Column="0" Text="ThrottleDebounce" TextWrapping="Wrap" FontWeight="Bold" /> <TextBlock Grid.Row="16" Grid.Column="0" Text="ThrottleDebounce" TextWrapping="Wrap" FontWeight="Bold" />
<TextBlock Grid.Row="18" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2026 Ben Hutchison&#xa;Licensed under the Apache License, Version 2.0." TextWrapping="Wrap" /> <TextBlock Grid.Row="16" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2026 Ben Hutchison&#xa;Licensed under the Apache License, Version 2.0." TextWrapping="Wrap" />
<local:MyButton Grid.Row="19" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left" <local:MyButton Grid.Row="17" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://github.com/Aldaviva/ThrottleDebounce" /> local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://github.com/Aldaviva/ThrottleDebounce" />
<local:MyButton Grid.Row="19" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left" <local:MyButton Grid.Row="17" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://licenses.nuget.org/Apache-2.0" /> local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://licenses.nuget.org/Apache-2.0" />
<TextBlock Grid.Row="20" Grid.Column="0" Text="CacheCow" TextWrapping="Wrap" FontWeight="Bold" /> <TextBlock Grid.Row="18" Grid.Column="0" Text="CacheCow" TextWrapping="Wrap" FontWeight="Bold" />
<TextBlock Grid.Row="20" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © Ali Kheyrollahi&#xa;Licensed under the MIT License." TextWrapping="Wrap" /> <TextBlock Grid.Row="18" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © Ali Kheyrollahi&#xa;Licensed under the MIT License." TextWrapping="Wrap" />
<local:MyButton Grid.Row="21" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left" <local:MyButton Grid.Row="19" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://github.com/aliostad/CacheCow" /> local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://github.com/aliostad/CacheCow" />
<local:MyButton Grid.Row="21" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left" <local:MyButton Grid.Row="19" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://licenses.nuget.org/MIT" /> local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://licenses.nuget.org/MIT" />
<TextBlock Grid.Row="20" Grid.Column="0" Text="Transformer Discovery Service" TextWrapping="Wrap" FontWeight="Bold" />
<TextBlock Grid.Row="20" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2020 huangyuhui &lt;huanghongxun2008@126.com&gt; and contributors&#xa;Licensed under the GNU General Public License, Version 3 or later, with additional terms.&#xa;本组件作为独立组件分发,可依 GPL 复制、修改及再分发。" TextWrapping="Wrap" />
<local:MyButton Grid.Row="21" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://github.com/HMCL-dev/HMCL/tree/main/minecraft/libraries/HMCLTransformerDiscoveryService" />
<local:MyButton Grid.Row="21" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://github.com/HMCL-dev/HMCL/blob/main/LICENSE" />
</Grid> </Grid>
</local:MyCard> </local:MyCard>
</StackPanel> </StackPanel>
@@ -48,12 +48,13 @@ Public Class PageOtherHelp
If Item.Types.Contains(Type) Then TypeItems.Add(Item) If Item.Types.Contains(Type) Then TypeItems.Add(Item)
Next Next
'增加卡片 '增加卡片
Dim NewCard As New MyCard With {.Title = Type, .Margin = New Thickness(0, 0, 0, 15), .SwapType = 11} Dim NewCard As New MyCard With {.Title = Type, .Margin = New Thickness(0, 0, 0, 15)}
Dim NewStack As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0), .Tag = TypeItems} Dim NewStack As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0)}
NewCard.Children.Add(NewStack) NewCard.Children.Add(NewStack)
NewCard.SwapControl = NewStack NewCard.SwapControl = NewStack
NewCard.SetContentBuilder(TypeItems, Function(Entry) Entry.ToListItem)
If Type = "指南" Then If Type = "指南" Then
MyCard.StackInstall(NewStack, 11, "指南") NewCard.BuildContent()
Else Else
NewCard.IsSwapped = True NewCard.IsSwapped = True
End If End If
@@ -64,7 +64,7 @@ Public Class PageSelectLeft
</ContextMenu> </ContextMenu>
) )
End Select End Select
If (Folder.Type = McFolder.Types.Vanilla OrElse Folder.Type = McFolder.Types.RenamedVanilla) AndAlso Folder.Location = Paths.Base & ".minecraft\" AndAlso McFolderList.IsSingle Then CType(ContMenu.FindName("Delete"), MyMenuItem).Header = "清空" If (Folder.Type = McFolder.Types.Vanilla OrElse Folder.Type = McFolder.Types.RenamedVanilla) AndAlso PathUtils.IsSame(Folder.Location, Paths.Base & ".minecraft\") AndAlso McFolderList.IsSingle Then CType(ContMenu.FindName("Delete"), MyMenuItem).Header = "清空"
'注册事件 '注册事件
If Not Folder.Type = McFolder.Types.Vanilla Then CType(ContMenu.FindName("Remove"), MyMenuItem).AddHandler(MyMenuItem.ClickEvent, New RoutedEventHandler(AddressOf FrmSelectLeft.Remove_Click)) If Not Folder.Type = McFolder.Types.Vanilla Then CType(ContMenu.FindName("Remove"), MyMenuItem).AddHandler(MyMenuItem.ClickEvent, New RoutedEventHandler(AddressOf FrmSelectLeft.Remove_Click))
CType(ContMenu.FindName("Open"), MyMenuItem).AddHandler(MyMenuItem.ClickEvent, New RoutedEventHandler(AddressOf FrmSelectLeft.Open_Click)) CType(ContMenu.FindName("Open"), MyMenuItem).AddHandler(MyMenuItem.ClickEvent, New RoutedEventHandler(AddressOf FrmSelectLeft.Open_Click))
@@ -116,7 +116,7 @@ Public Class PageSelectLeft
'确认勾选状态 '确认勾选状态
For i = 0 To McFolderList.Count - 1 For i = 0 To McFolderList.Count - 1
If McFolderList(i).Location = McFolderSelected Then If PathUtils.IsSame(McFolderList(i).Location, McFolderSelected) Then
CType(FrmSelectLeft.PanList.Children(i + 1), MyListItem).Checked = True '去掉第一个标题 CType(FrmSelectLeft.PanList.Children(i + 1), MyListItem).Checked = True '去掉第一个标题
Return Return
End If End If
@@ -139,9 +139,9 @@ Public Class PageSelectLeft
'添加文件夹 '添加文件夹
Public Sub Add_Click() Public Sub Add_Click()
Dim NewFolder As String = "" Dim NewFolder As String = ""
'检查是否有下载任务 '下载或版本修改期间禁止更改游戏文件夹
If HasDownloadingTask() Then If McInstanceLock.IsAnyLocked OrElse HasDownloadingTask() Then
Hint("在下载任务进行时,无法添加游戏文件夹!", HintType.Red) Hint("请在其他任务完成后,再添加游戏文件夹!", HintType.Red)
Return Return
End If End If
Try Try
@@ -150,8 +150,11 @@ Public Class PageSelectLeft
If NewFolder Is Nothing Then Return If NewFolder Is Nothing Then Return
If NewFolder.Contains("!") OrElse NewFolder.Contains(";") Then Hint("Minecraft 文件夹路径中不能含有感叹号或分号!", HintType.Red) : Return If NewFolder.Contains("!") OrElse NewFolder.Contains(";") Then Hint("Minecraft 文件夹路径中不能含有感叹号或分号!", HintType.Red) : Return
'要求输入显示名称 '要求输入显示名称
Dim SplitedNames As String() = NewFolder.TrimEnd("\").Split("\") Dim DefaultName = PathUtils.GetLastPart(NewFolder)
Dim DefaultName As String = If(SplitedNames.Last = ".minecraft", If(SplitedNames.Count >= 3, SplitedNames(SplitedNames.Count - 2), ""), SplitedNames.Last) If DefaultName = ".minecraft" Then
Dim ParentFolder = PathUtils.RemoveLastPart(NewFolder)
DefaultName = If(ParentFolder.EndsWithF(":"), "", PathUtils.GetLastPart(ParentFolder))
End If
If DefaultName.Length > 40 Then DefaultName = DefaultName.Substring(0, 39) If DefaultName.Length > 40 Then DefaultName = DefaultName.Substring(0, 39)
Dim NewName As String = MyMsgBoxInput("输入显示名称", "输入该文件夹在左边栏列表中显示的名称。", DefaultName, Dim NewName As String = MyMsgBoxInput("输入显示名称", "输入该文件夹在左边栏列表中显示的名称。", DefaultName,
New ObjectModel.Collection(Of Validate) From {New ValidateNullOrWhiteSpace, New ValidateLength(1, 30), New ValidateExcept({">", "|"})}) New ObjectModel.Collection(Of Validate) From {New ValidateNullOrWhiteSpace, New ValidateLength(1, 30), New ValidateExcept({">", "|"})})
@@ -169,7 +172,7 @@ Public Class PageSelectLeft
RunInThread( RunInThread(
Sub() Sub()
Try Try
If Not FolderPath.EndsWithF("\") Then FolderPath &= "\" FolderPath = PathUtils.AddSlashSuffix(FolderPath)
'检查文件夹权限 '检查文件夹权限
If Not CheckPermission(FolderPath) Then If Not CheckPermission(FolderPath) Then
If ShowHint Then If ShowHint Then
@@ -195,7 +198,7 @@ Public Class PageSelectLeft
For i = 0 To Folders.Count - 1 For i = 0 To Folders.Count - 1
Dim Folder As String = Folders(i) Dim Folder As String = Folders(i)
If Folder = "" Then Continue For If Folder = "" Then Continue For
If Folder.Split(">")(1) = FolderPath Then If PathUtils.IsSame(Folder.Split(">")(1), FolderPath) Then
IsAdded = True IsAdded = True
If Folder.Split(">")(0) = DisplayName Then If Folder.Split(">")(0) = DisplayName Then
If ShowHint Then Hint("此文件夹已在列表中!", HintType.Blue) If ShowHint Then Hint("此文件夹已在列表中!", HintType.Blue)
@@ -246,9 +249,9 @@ Public Class PageSelectLeft
'创建文件夹 '创建文件夹
Public Sub Create_Click() Public Sub Create_Click()
'检查是否有下载任务 '下载或版本修改期间禁止更改游戏文件夹
If HasDownloadingTask() Then If McInstanceLock.IsAnyLocked OrElse HasDownloadingTask() Then
Hint("在下载任务进行时,无法创建游戏文件夹!", HintType.Red) Hint("请在其他任务完成后,再创建游戏文件夹!", HintType.Red)
Return Return
End If End If
CreateMcFolderInCurrentPath() CreateMcFolderInCurrentPath()
@@ -270,6 +273,10 @@ Public Class PageSelectLeft
Dim Target As McFolder = CType(CType(CType(sender.Parent, ContextMenu).Parent, Primitives.Popup).PlacementTarget, MyListItem).Tag Dim Target As McFolder = CType(CType(CType(sender.Parent, ContextMenu).Parent, Primitives.Popup).PlacementTarget, MyListItem).Tag
'若为 “移除”,则提醒是否删除 PCL 的配置文件 '若为 “移除”,则提醒是否删除 PCL 的配置文件
If Target.Type = McFolder.Types.Custom Then If Target.Type = McFolder.Types.Custom Then
If McInstanceLock.IsAnyLocked OrElse HasDownloadingTask() Then
Hint("请在其他任务完成后,再移除游戏文件夹!", HintType.Red)
Return
End If
Select Case MyMsgBox("是否需要清理 PCL 在该文件夹中的配置文件?" & vbCrLf & "这包括各个版本的独立设置(如自定义图标、第三方登录配置)等,对游戏本身没有影响。", "配置文件清理", "删除", "保留", "取消") Select Case MyMsgBox("是否需要清理 PCL 在该文件夹中的配置文件?" & vbCrLf & "这包括各个版本的独立设置(如自定义图标、第三方登录配置)等,对游戏本身没有影响。", "配置文件清理", "删除", "保留", "取消")
Case 1 Case 1
'删除配置文件 '删除配置文件
@@ -292,6 +299,10 @@ Public Class PageSelectLeft
End Try End Try
End Sub End Sub
Public Sub Delete_Click(sender As Object, e As RoutedEventArgs) Public Sub Delete_Click(sender As Object, e As RoutedEventArgs)
If McInstanceLock.IsAnyLocked OrElse HasDownloadingTask() Then
Hint("请在其他任务完成后,再删除游戏文件夹!", HintType.Red)
Return
End If
Dim Target As McFolder = CType(CType(CType(sender.Parent, ContextMenu).Parent, Primitives.Popup).PlacementTarget, MyListItem).Tag Dim Target As McFolder = CType(CType(CType(sender.Parent, ContextMenu).Parent, Primitives.Popup).PlacementTarget, MyListItem).Tag
Dim DeleteText As String = If((Target.Type = McFolder.Types.Vanilla OrElse Target.Type = McFolder.Types.RenamedVanilla) AndAlso Target.Location = Paths.Base & ".minecraft\" AndAlso McFolderList.IsSingle, "清空", "删除") Dim DeleteText As String = If((Target.Type = McFolder.Types.Vanilla OrElse Target.Type = McFolder.Types.RenamedVanilla) AndAlso Target.Location = Paths.Base & ".minecraft\" AndAlso McFolderList.IsSingle, "清空", "删除")
If MyMsgBox("你确定要" & DeleteText & "这个文件夹吗?" & vbCrLf & If MyMsgBox("你确定要" & DeleteText & "这个文件夹吗?" & vbCrLf &
@@ -328,7 +339,7 @@ Public Class PageSelectLeft
Dim DeletedName As String = Nothing Dim DeletedName As String = Nothing
Dim Folders = Settings.Get(Of String)("LaunchFolders").Split("|"c, True).ToList Dim Folders = Settings.Get(Of String)("LaunchFolders").Split("|"c, True).ToList
For Each Folder In Folders.ToList For Each Folder In Folders.ToList
If Not Folder.EndsWithF(Target.Location) Then Continue For If Not Folder.Contains(">") OrElse Not PathUtils.IsSame(Folder.AfterFirst(">"), Target.Location) Then Continue For
DeletedName = Folder.BeforeFirst(">") DeletedName = Folder.BeforeFirst(">")
Folders.Remove(Folder) Folders.Remove(Folder)
Exit For Exit For
@@ -349,7 +360,7 @@ Public Class PageSelectLeft
End Sub End Sub
Public Shared Sub RefreshCurrent(Folder As String) Public Shared Sub RefreshCurrent(Folder As String)
WriteIni(Folder & "PCL.ini", "InstanceCache", "") '删除缓存以强制要求下一次加载时更新列表 WriteIni(Folder & "PCL.ini", "InstanceCache", "") '删除缓存以强制要求下一次加载时更新列表
If Folder = McFolderSelected Then LoaderFolderRun(McInstanceListLoader, McFolderSelected, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\") If PathUtils.IsSame(Folder, McFolderSelected) Then LoaderFolderRun(McInstanceListLoader, McFolderSelected, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\")
End Sub End Sub
Public Sub Rename_Click(sender As Object, e As RoutedEventArgs) Public Sub Rename_Click(sender As Object, e As RoutedEventArgs)
Dim Folder As McFolder = CType(CType(CType(sender.Parent, ContextMenu).Parent, Primitives.Popup).PlacementTarget, MyListItem).Tag Dim Folder As McFolder = CType(CType(CType(sender.Parent, ContextMenu).Parent, Primitives.Popup).PlacementTarget, MyListItem).Tag
@@ -365,7 +376,7 @@ Public Class PageSelectLeft
For i = 0 To Folders.Count - 1 For i = 0 To Folders.Count - 1
Dim FolderCurrent As String = Folders(i) Dim FolderCurrent As String = Folders(i)
If FolderCurrent = "" Then Continue For If FolderCurrent = "" Then Continue For
If FolderCurrent.Split(">")(1) = Folder.Location Then If PathUtils.IsSame(FolderCurrent.Split(">")(1), Folder.Location) Then
IsAdded = True IsAdded = True
If FolderCurrent.Split(">")(0) = NewName Then If FolderCurrent.Split(">")(0) = NewName Then
'名称未修改 '名称未修改
@@ -390,9 +401,9 @@ Public Class PageSelectLeft
'点击选项 '点击选项
Public Sub Folder_Change(sender As MyListItem, e As RouteEventArgs) Public Sub Folder_Change(sender As MyListItem, e As RouteEventArgs)
If Not e.RaiseByMouse OrElse Not sender.Checked Then Return If Not e.RaiseByMouse OrElse Not sender.Checked Then Return
'检查是否有下载任务 '下载或版本修改期间禁止更改游戏文件夹
If HasDownloadingTask(True) Then If McInstanceLock.IsAnyLocked OrElse HasDownloadingTask(True) Then
Hint("在下载任务进行时,无法切换游戏文件夹!", HintType.Red) Hint("请在其他任务完成后,再切换游戏文件夹!", HintType.Red)
e.Handled = True e.Handled = True
Return Return
End If End If
@@ -30,6 +30,8 @@ Public Class PageSelectRight
For Each Card As KeyValuePair(Of McInstanceCardType, List(Of McInstance)) In McInstanceList.ToArray For Each Card As KeyValuePair(Of McInstanceCardType, List(Of McInstance)) In McInstanceList.ToArray
'确认是否为隐藏版本显示状态 '确认是否为隐藏版本显示状态
If Card.Key = McInstanceCardType.Hidden Xor ShowHidden Then Continue For If Card.Key = McInstanceCardType.Hidden Xor ShowHidden Then Continue For
Dim Instances = Card.Value.Where(Function(Instance) Not McInstanceLock.IsLocked(Instance)).ToList
If Not Instances.Any Then Continue For
#Region "确认卡片名称" #Region "确认卡片名称"
Dim CardName As String = "" Dim CardName As String = ""
Select Case Card.Key Select Case Card.Key
@@ -40,11 +42,11 @@ Public Class PageSelectRight
Dim IsNeoForgeExists As Boolean = False Dim IsNeoForgeExists As Boolean = False
Dim IsFabricExists As Boolean = False Dim IsFabricExists As Boolean = False
Dim IsLiteExists As Boolean = False Dim IsLiteExists As Boolean = False
For Each Instance As McInstance In Card.Value For Each Instance As McInstance In Instances
If Instance.Version.HasFabric Then IsFabricExists = True If Instance.Version.ModLoaders.HasFlagF(ModLoaders.Fabric) Then IsFabricExists = True
If Instance.Version.HasLiteLoader Then IsLiteExists = True If Instance.Version.ModLoaders.HasFlagF(ModLoaders.LiteLoader) Then IsLiteExists = True
If Instance.Version.HasForge Then IsForgeExists = True If Instance.Version.ModLoaders.HasFlagF(ModLoaders.Forge) Then IsForgeExists = True
If Instance.Version.HasNeoForge Then IsNeoForgeExists = True If Instance.Version.ModLoaders.HasFlagF(ModLoaders.NeoForge) Then IsNeoForgeExists = True
Next Next
If If(IsLiteExists, 1, 0) + If(IsForgeExists, 1, 0) + If(IsFabricExists, 1, 0) + If(IsNeoForgeExists, 1, 0) > 1 Then If If(IsLiteExists, 1, 0) + If(IsForgeExists, 1, 0) + If(IsFabricExists, 1, 0) + If(IsNeoForgeExists, 1, 0) > 1 Then
CardName = "可安装 Mod" CardName = "可安装 Mod"
@@ -72,17 +74,18 @@ Public Class PageSelectRight
End Select End Select
#End Region #End Region
'建立控件 '建立控件
Dim CardTitle As String = CardName & If(CardName = "收藏夹", "", " (" & Card.Value.Count & ")") Dim CardTitle As String = CardName & If(CardName = "收藏夹", "", " (" & Instances.Count & ")")
Dim NewCard As New MyCard With {.Title = CardTitle, .Margin = New Thickness(0, 0, 0, 15), .SwapType = 0} Dim NewCard As New MyCard With {.Title = CardTitle, .Margin = New Thickness(0, 0, 0, 15)}
Dim NewStack As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0), .Tag = Card.Value} Dim NewStack As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0)}
NewCard.Children.Add(NewStack) NewCard.Children.Add(NewStack)
NewCard.SwapControl = NewStack NewCard.SwapControl = NewStack
NewCard.SetContentBuilder(Instances, Function(Instance) Instance.ToListItem(AddressOf McInstanceListContent))
PanMain.Children.Add(NewCard) PanMain.Children.Add(NewCard)
'确定卡片是否展开 '确定卡片是否展开
If Card.Key = McInstanceCardType.Rubbish OrElse Card.Key = McInstanceCardType.Error OrElse Card.Key = McInstanceCardType.Fool Then If Card.Key = McInstanceCardType.Rubbish OrElse Card.Key = McInstanceCardType.Error OrElse Card.Key = McInstanceCardType.Fool Then
NewCard.IsSwapped = True NewCard.IsSwapped = True
Else Else
MyCard.StackInstall(NewStack, 0, CardTitle) NewCard.BuildContent()
End If End If
Next Next
@@ -128,7 +131,8 @@ Public Class PageSelectRight
BtnStar.LogoScale = 1.1 BtnStar.LogoScale = 1.1
BtnStar.Logo = Logo.IconButtonLikeLine BtnStar.Logo = Logo.IconButtonLikeLine
End If End If
AddHandler BtnStar.Click, Sub() AddHandler BtnStar.Click,
Sub()
WriteIni(Instance.PathVersion & "PCL\Setup.ini", "IsStar", Not Instance.IsStar) WriteIni(Instance.PathVersion & "PCL\Setup.ini", "IsStar", Not Instance.IsStar)
McInstanceListForceRefresh = True McInstanceListForceRefresh = True
LoaderFolderRun(McInstanceListLoader, McFolderSelected, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\") LoaderFolderRun(McInstanceListLoader, McFolderSelected, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\")
@@ -136,7 +140,7 @@ Public Class PageSelectRight
Dim BtnDel As New MyIconButton With {.LogoScale = 1.1, .Logo = Logo.IconButtonDelete} Dim BtnDel As New MyIconButton With {.LogoScale = 1.1, .Logo = Logo.IconButtonDelete}
BtnDel.ToolTip = "删除" BtnDel.ToolTip = "删除"
AddHandler BtnDel.Click, Sub() DeleteInstance(sender, Instance) AddHandler BtnDel.Click, Sub() DeleteInstance(sender, Instance)
If Instance.State <> McInstanceState.Error Then If Instance.State <> McInstance.McInstanceState.Error Then
Dim BtnCont As New MyIconButton With {.LogoScale = 1.1, .Logo = Logo.IconButtonSetup} Dim BtnCont As New MyIconButton With {.LogoScale = 1.1, .Logo = Logo.IconButtonSetup}
BtnCont.ToolTip = "设置" BtnCont.ToolTip = "设置"
AddHandler BtnCont.Click, AddHandler BtnCont.Click,
@@ -166,6 +170,10 @@ Public Class PageSelectRight
'点击选项 '点击选项
Public Shared Sub Item_Click(sender As MyListItem, e As EventArgs) Public Shared Sub Item_Click(sender As MyListItem, e As EventArgs)
Dim Instance As McInstance = sender.Tag Dim Instance As McInstance = sender.Tag
If McInstanceLock.IsLocked(Instance) Then
Hint("该版本正在被修改,请稍后再选择!", HintType.Red)
Return
End If
If New McInstance(Instance.PathVersion).Check Then If New McInstance(Instance.PathVersion).Check Then
'正常版本 '正常版本
McInstanceSelected = Instance McInstanceSelected = Instance
@@ -183,8 +191,12 @@ Public Class PageSelectRight
'修改此代码时,同时修改 PageInstanceOverall 中的代码 '修改此代码时,同时修改 PageInstanceOverall 中的代码
Public Shared Sub DeleteInstance(Item As MyListItem, Instance As McInstance) Public Shared Sub DeleteInstance(Item As MyListItem, Instance As McInstance)
Try Try
If McInstanceLock.IsLocked(Instance) Then
Hint("该版本正在被修改,请稍后再删除!", HintType.Red)
Return
End If
Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown
Dim IsHintIndie As Boolean = Instance.State <> McInstanceState.Error AndAlso Instance.PathIndie <> McFolderSelected Dim IsHintIndie As Boolean = Instance.State <> McInstance.McInstanceState.Error AndAlso Instance.PathIndie <> McFolderSelected
Dim SavesFolder = DirectoryUtils.GetInfo(Instance.PathIndie & "saves\") Dim SavesFolder = DirectoryUtils.GetInfo(Instance.PathIndie & "saves\")
Dim SaveEntries = If(IsHintIndie AndAlso SavesFolder.Exists, SavesFolder.EnumerateDirectories.OrderByDescending(Function(Save) Save.LastWriteTime).Select(Function(Save) $"{Save.Name}(上次修改:{StringUtils.FormatTimeSpan(Save.LastWriteTime - Date.Now, False)})").ToList, New List(Of String)) Dim SaveEntries = If(IsHintIndie AndAlso SavesFolder.Exists, SavesFolder.EnumerateDirectories.OrderByDescending(Function(Save) Save.LastWriteTime).Select(Function(Save) $"{Save.Name}(上次修改:{StringUtils.FormatTimeSpan(Save.LastWriteTime - Date.Now, False)})").ToList, New List(Of String))
Select Case MyMsgBox($"你确定要{If(IsShiftPressed, "永久", "")}删除版本 {Instance.Name} 吗?" & Select Case MyMsgBox($"你确定要{If(IsShiftPressed, "永久", "")}删除版本 {Instance.Name} 吗?" &
@@ -207,7 +219,7 @@ Public Class PageSelectRight
Dim Card As MyCard = Parent.Parent Dim Card As MyCard = Parent.Parent
Card.Title = Card.Title.Replace(Parent.Children.Count - 1, Parent.Children.Count - 2) '有一个占位符 Card.Title = Card.Title.Replace(Parent.Children.Count - 1, Parent.Children.Count - 2) '有一个占位符
Parent.Children.Remove(Item) Parent.Children.Remove(Item)
If McInstanceSelected IsNot Nothing AndAlso Instance.PathVersion = McInstanceSelected.PathVersion Then If McInstanceSelected IsNot Nothing AndAlso PathUtils.IsSame(Instance.PathVersion, McInstanceSelected.PathVersion) Then
'删除当前版本就更改选择 '删除当前版本就更改选择
McInstanceSelected = MyVirtualizingElement.TryInit(Parent.Children(0)).Tag McInstanceSelected = MyVirtualizingElement.TryInit(Parent.Children(0)).Tag
End If End If
@@ -27,8 +27,6 @@
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="9" /> <RowDefinition Height="9" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="9" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" Text="默认版本隔离" Margin="0,0,25,0" /> <TextBlock Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" Text="默认版本隔离" Margin="0,0,25,0" />
<local:MyComboBox local:SettingService.Key="LaunchArgumentIndieV2" x:Name="ComboArgumentIndieV2" <local:MyComboBox local:SettingService.Key="LaunchArgumentIndieV2" x:Name="ComboArgumentIndieV2"
@@ -106,9 +104,6 @@
</local:MyTextBox.ValidateRules> </local:MyTextBox.ValidateRules>
</local:MyTextBox> </local:MyTextBox>
</Grid> </Grid>
<local:MyCheckBox Margin="0,8,0,4" Text="在正版登录时验证 SSL 证书" Grid.Row="12" Height="22" Grid.ColumnSpan="2"
x:Name="CheckDownloadCert" local:SettingService.Key="ToolDownloadCert"
ToolTip="开启验证可以提高安全性,但可能会导致正版登录失败(见 #3018)。&#xa;若登录因为 “未能为 SSL/TLS 安全通道建立信任关系” 失败,请考虑关闭此选项。" />
</Grid> </Grid>
</StackPanel> </StackPanel>
</local:MyCard> </local:MyCard>
@@ -291,6 +286,7 @@
<local:MyCheckBox Height="28" Text="禁用 Java Launch Wrapper" local:SettingService.Key="LaunchAdvanceDisableJLW" ToolTip="是否使用 Java Launch Wrapper 修复 Java 18- 在中文路径下可能无法正常启动的问题。&#xa;详见:https://github.com/00ll00/java_launch_wrapper" /> <local:MyCheckBox Height="28" Text="禁用 Java Launch Wrapper" local:SettingService.Key="LaunchAdvanceDisableJLW" ToolTip="是否使用 Java Launch Wrapper 修复 Java 18- 在中文路径下可能无法正常启动的问题。&#xa;详见:https://github.com/00ll00/java_launch_wrapper" />
<local:MyCheckBox Height="28" Text="禁用 LWJGL Unsafe Agent" local:SettingService.Key="LaunchAdvanceDisableLUA" ToolTip="是否使用 LWJGL Unsafe Agent 修复 LWJGL 3.4.1 的一个性能问题。&#xa;详见:https://github.com/HMCL-dev/lwjgl-unsafe-agent" /> <local:MyCheckBox Height="28" Text="禁用 LWJGL Unsafe Agent" local:SettingService.Key="LaunchAdvanceDisableLUA" ToolTip="是否使用 LWJGL Unsafe Agent 修复 LWJGL 3.4.1 的一个性能问题。&#xa;详见:https://github.com/HMCL-dev/lwjgl-unsafe-agent" />
<local:MyCheckBox Height="28" Text="使用高性能显卡" local:SettingService.Key="LaunchAdvanceGraphicCard" ToolTip="自动在 Windows 设置 → 系统 → 屏幕 → 显示卡 中将 PCL 和 Java 改为使用高性能显卡。&#xa;如果同时有集成显卡和独立显卡,这能确保 MC 使用独立显卡进行渲染,以大幅提高帧率。" /> <local:MyCheckBox Height="28" Text="使用高性能显卡" local:SettingService.Key="LaunchAdvanceGraphicCard" ToolTip="自动在 Windows 设置 → 系统 → 屏幕 → 显示卡 中将 PCL 和 Java 改为使用高性能显卡。&#xa;如果同时有集成显卡和独立显卡,这能确保 MC 使用独立显卡进行渲染,以大幅提高帧率。" />
<local:MyCheckBox Height="28" Text="在正版登录时验证 SSL 证书" local:SettingService.Key="ToolDownloadCert" ToolTip="开启验证可以提高安全性,但可能会导致正版登录失败(见 #3018)。&#xa;若登录因为 “未能为 SSL/TLS 安全通道建立信任关系” 失败,请考虑关闭此选项。" />
</StackPanel> </StackPanel>
<local:MyHint Text="版本独立设置中还有更多高级选项可供调整。" Margin="0,5,0,10" Theme="Blue" Grid.Row="11" Grid.ColumnSpan="2" <local:MyHint Text="版本独立设置中还有更多高级选项可供调整。" Margin="0,5,0,10" Theme="Blue" Grid.Row="11" Grid.ColumnSpan="2"
CanClose="True" RelativeSetup="HintMoreAdvancedSetup" /> CanClose="True" RelativeSetup="HintMoreAdvancedSetup" />
@@ -396,7 +396,7 @@ PreFin:
AddHandler DownButton.Click, Sub() MoveJavaInList(JavaEntry, 1) AddHandler DownButton.Click, Sub() MoveJavaInList(JavaEntry, 1)
Buttons.Add(DownButton) Buttons.Add(DownButton)
'从列表中移除按钮 '从列表中移除按钮
Dim IsOfficial As Boolean = JavaEntry.Folder.StartsWithF($"{Paths.AppData}.minecraft\runtime\") Dim IsOfficial As Boolean = PathUtils.IsParentOf($"{Paths.AppData}.minecraft\runtime\", JavaEntry.Folder)
Dim DeleteButton As New MyIconButton With {.Logo = Logo.IconButtonStop, .Height = 24, .Width = 24} Dim DeleteButton As New MyIconButton With {.Logo = Logo.IconButtonStop, .Height = 24, .Width = 24}
DeleteButton.IsEnabled = Not IsOfficial DeleteButton.IsEnabled = Not IsOfficial
DeleteButton.ToolTip = If(DeleteButton.IsEnabled, "从列表中移除", "无法移除官方 Java") DeleteButton.ToolTip = If(DeleteButton.IsEnabled, "从列表中移除", "无法移除官方 Java")
@@ -235,18 +235,16 @@
<local:MyHint Margin="0,15,0,-4" Text="内容见 ModSetup" Theme="Blue" x:Name="HintCustom" /> <local:MyHint Margin="0,15,0,-4" Text="内容见 ModSetup" Theme="Blue" x:Name="HintCustom" />
<local:MyHint Margin="0,15,0,6" Text="请谨慎使用陌生人提供的主页,恶意代码可能会造成损害!" Theme="Yellow" x:Name="HintCustomWarn" <local:MyHint Margin="0,15,0,6" Text="请谨慎使用陌生人提供的主页,恶意代码可能会造成损害!" Theme="Yellow" x:Name="HintCustomWarn"
CanClose="True" RelativeSetup="HintCustomWarn" /> CanClose="True" RelativeSetup="HintCustomWarn" />
<Grid Height="35" x:Name="PanCustomLocal" Visibility="Collapsed" Margin="0,10,0,5"> <WrapPanel x:Name="PanCustomLocal" Visibility="Collapsed" Margin="0,0,0,5">
<Grid.ColumnDefinitions> <local:MyButton x:Name="BtnCustomRefresh" Text="刷新主页"
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" /> MinWidth="140" Height="35" Padding="13,0" Margin="0,10,20,0" HorizontalAlignment="Left" ColorType="Highlight" />
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" /> <local:MyButton x:Name="BtnCustomFile" Text="生成教学文件"
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" /> MinWidth="140" Height="35" Padding="13,0" Margin="0,10,20,0" HorizontalAlignment="Left" />
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" /> <local:MyButton x:Name="BtnCustomTutorial" Text="查看教程"
</Grid.ColumnDefinitions> MinWidth="140" Height="35" Padding="13,0" Margin="0,10,20,0" HorizontalAlignment="Left" />
<local:MyButton x:Name="BtnCustomRefresh" Grid.Column="0" Height="32" MinWidth="140" Text="刷新主页" Padding="13,0" Margin="0,0,20,0" ColorType="Highlight" /> <local:MyButton x:Name="BtnCustomFolder" Text="打开主页文件夹"
<local:MyButton x:Name="BtnCustomFile" Grid.Column="1" Height="32" MinWidth="140" Text="生成教学文件" Padding="13,0" Margin="0,0,20,0" /> MinWidth="140" Height="35" Padding="13,0" Margin="0,10,20,0" HorizontalAlignment="Left" />
<local:MyButton x:Name="BtnCustomTutorial" Grid.Column="2" Height="32" MinWidth="140" Text="查看教程" Padding="13,0" Margin="0,0,20,0" /> </WrapPanel>
<local:MyButton x:Name="BtnCustomOpen" Grid.Column="3" Height="32" MinWidth="140" Text="打开主页文件夹" Padding="13,0" Margin="0,0,20,0" />
</Grid>
<Grid x:Name="PanCustomNet" Visibility="Collapsed" Margin="0,10,0,5"> <Grid x:Name="PanCustomNet" Visibility="Collapsed" Margin="0,10,0,5">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Name" /> <ColumnDefinition Width="Auto" SharedSizeGroup="Name" />
@@ -288,6 +286,11 @@
<local:MyComboBoxItem local:SettingService.Value="17" Content="Bangumi 番剧主页(作者:wzyaeu)" /> <local:MyComboBoxItem local:SettingService.Value="17" Content="Bangumi 番剧主页(作者:wzyaeu)" />
<local:MyComboBoxItem local:SettingService.Value="18" Content="Bilibili 热门(作者:wzyaeu)" /> <local:MyComboBoxItem local:SettingService.Value="18" Content="Bilibili 热门(作者:wzyaeu)" />
<local:MyComboBoxItem local:SettingService.Value="19" Content="Music 云音乐热门(作者:wzyaeu)" /> <local:MyComboBoxItem local:SettingService.Value="19" Content="Music 云音乐热门(作者:wzyaeu)" />
<local:MyComboBoxItem local:SettingService.Value="20" Content="模组推荐(作者:llsgllsg)" />
<local:MyComboBoxItem local:SettingService.Value="21" Content="每日游戏推荐(作者:llsgllsg)" />
<local:MyComboBoxItem local:SettingService.Value="22" Content="Wallpaper 每日壁纸(作者:wzyaeu)" />
<local:MyComboBoxItem local:SettingService.Value="23" Content="Weather 天气预报(作者:wzyaeu)" />
<local:MyComboBoxItem local:SettingService.Value="24" Content="新一代狗屁王大杂烩主页(作者:llsgllsg)" />
</local:MyComboBox> </local:MyComboBox>
</Grid> </Grid>
</StackPanel> </StackPanel>
@@ -334,6 +337,7 @@
<local:MyCheckBox Height="22" VerticalAlignment="Center" Grid.Row="4" Grid.Column="3" Text="版本管理" x:Name="CheckHiddenFunctionSelect" local:SettingService.Key="UiHiddenFunctionSelect" ToolTip="隐藏启动按钮下方的版本选择与版本设置入口。" /> <local:MyCheckBox Height="22" VerticalAlignment="Center" Grid.Row="4" Grid.Column="3" Text="版本管理" x:Name="CheckHiddenFunctionSelect" local:SettingService.Key="UiHiddenFunctionSelect" ToolTip="隐藏启动按钮下方的版本选择与版本设置入口。" />
<local:MyCheckBox Height="22" VerticalAlignment="Center" Grid.Row="4" Grid.Column="4" Text="Mod 更新" x:Name="CheckHiddenFunctionModUpdate" local:SettingService.Key="UiHiddenFunctionModUpdate" ToolTip="禁止在 Mod 管理页面更新 Mod。" /> <local:MyCheckBox Height="22" VerticalAlignment="Center" Grid.Row="4" Grid.Column="4" Text="Mod 更新" x:Name="CheckHiddenFunctionModUpdate" local:SettingService.Key="UiHiddenFunctionModUpdate" ToolTip="禁止在 Mod 管理页面更新 Mod。" />
<local:MyCheckBox Height="22" VerticalAlignment="Center" Grid.Row="4" Grid.Column="5" Text="功能隐藏" x:Name="CheckHiddenFunctionHidden" local:SettingService.Key="UiHiddenFunctionHidden" ToolTip="仅隐藏本选项卡,设置的禁用内容依然有效。&#xa;可以在保留个性化设置页面的情况下避免隐藏设置被修改。" /> <local:MyCheckBox Height="22" VerticalAlignment="Center" Grid.Row="4" Grid.Column="5" Text="功能隐藏" x:Name="CheckHiddenFunctionHidden" local:SettingService.Key="UiHiddenFunctionHidden" ToolTip="仅隐藏本选项卡,设置的禁用内容依然有效。&#xa;可以在保留个性化设置页面的情况下避免隐藏设置被修改。" />
<local:MyCheckBox Height="22" VerticalAlignment="Center" Grid.Row="4" Grid.Column="6" Text="版本修改" x:Name="CheckHiddenFunctionModify" local:SettingService.Key="UiHiddenFunctionInstanceModify" ToolTip="隐藏版本设置中的版本修改功能。" />
</Grid> </Grid>
</StackPanel> </StackPanel>
</local:MyCard> </local:MyCard>
@@ -306,19 +306,6 @@ Refresh:
"你可以在生成教学文件后直接刷新主页,对照着进行修改,更有助于理解。" & vbCrLf & "你可以在生成教学文件后直接刷新主页,对照着进行修改,更有助于理解。" & vbCrLf &
"直接将主页文件拖进 PCL 窗口也可以快捷加载。", "主页自定义教程") "直接将主页文件拖进 PCL 窗口也可以快捷加载。", "主页自定义教程")
End Sub End Sub
Private Sub BtnCustomOpen_Click(sender As Object, e As EventArgs) Handles BtnCustomOpen.Click
Try
Dim CustomPath As String = Paths.Base & "PCL\Custom.xaml"
If FileUtils.Exists(CustomPath) Then
OpenExplorer(CustomPath)
Else
OpenExplorer(Paths.Base & "PCL\")
Hint("未找到 Custom.xaml,已打开 PCL 文件夹。", HintType.Blue)
End If
Catch ex As Exception
Logger.Error(ex, "打开主页文件失败")
End Try
End Sub
Public Shared Sub OnMainPageTypeChanged() Public Shared Sub OnMainPageTypeChanged()
If FrmSetupUI Is Nothing Then Return If FrmSetupUI Is Nothing Then Return
Select Case CInt(Settings.Get(Of Integer)("UiCustomType")) Select Case CInt(Settings.Get(Of Integer)("UiCustomType"))
@@ -354,6 +341,14 @@ Refresh:
End Select End Select
FrmSetupUI.CardCustom.TriggerForceResize() FrmSetupUI.CardCustom.TriggerForceResize()
End Sub End Sub
Private Sub BtnCustomFolder_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnCustomFolder.Click
Dim CustomPath As String = Paths.Base & "PCL\Custom.xaml"
If FileUtils.Exists(CustomPath) Then
OpenExplorer(CustomPath)
Else
OpenExplorer(Paths.Base & "PCL\")
End If
End Sub
'主题 '主题
Private Sub LabLauncherTheme5Unlock_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles LabLauncherTheme5Unlock.MouseLeftButtonUp Private Sub LabLauncherTheme5Unlock_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles LabLauncherTheme5Unlock.MouseLeftButtonUp
@@ -458,6 +453,10 @@ Refresh:
If FrmSetupUI IsNot Nothing Then If FrmSetupUI IsNot Nothing Then
FrmSetupUI.CardSwitch.Visibility = If(Not HiddenForceShow AndAlso Settings.Get(Of Boolean)("UiHiddenFunctionHidden"), Visibility.Collapsed, Visibility.Visible) FrmSetupUI.CardSwitch.Visibility = If(Not HiddenForceShow AndAlso Settings.Get(Of Boolean)("UiHiddenFunctionHidden"), Visibility.Collapsed, Visibility.Visible)
End If End If
'版本设置子页面
If FrmInstanceLeft IsNot Nothing Then
FrmInstanceLeft.ItemModify.Visibility = If(Not HiddenForceShow AndAlso Settings.Get(Of Boolean)("UiHiddenFunctionInstanceModify"), Visibility.Collapsed, Visibility.Visible)
End If
'设置子页面 '设置子页面
If FrmSetupLeft IsNot Nothing Then If FrmSetupLeft IsNot Nothing Then
FrmSetupLeft.ItemLaunch.Visibility = If(Not HiddenForceShow AndAlso Settings.Get(Of Boolean)("UiHiddenSetupLaunch"), Visibility.Collapsed, Visibility.Visible) FrmSetupLeft.ItemLaunch.Visibility = If(Not HiddenForceShow AndAlso Settings.Get(Of Boolean)("UiHiddenSetupLaunch"), Visibility.Collapsed, Visibility.Visible)
@@ -158,6 +158,7 @@ Public Class Settings
New Setting("UiHiddenPageOther", False, OnChanged:=AddressOf PageSetupUI.HiddenRefresh), New Setting("UiHiddenPageOther", False, OnChanged:=AddressOf PageSetupUI.HiddenRefresh),
New Setting("UiHiddenFunctionSelect", False, OnChanged:=AddressOf PageSetupUI.HiddenRefresh), New Setting("UiHiddenFunctionSelect", False, OnChanged:=AddressOf PageSetupUI.HiddenRefresh),
New Setting("UiHiddenFunctionModUpdate", False, OnChanged:=AddressOf PageSetupUI.HiddenRefresh), New Setting("UiHiddenFunctionModUpdate", False, OnChanged:=AddressOf PageSetupUI.HiddenRefresh),
New Setting("UiHiddenFunctionInstanceModify", False, OnChanged:=AddressOf PageSetupUI.HiddenRefresh),
New Setting("UiHiddenFunctionHidden", False, OnChanged:=AddressOf PageSetupUI.HiddenRefresh), New Setting("UiHiddenFunctionHidden", False, OnChanged:=AddressOf PageSetupUI.HiddenRefresh),
New Setting("UiHiddenSetupLaunch", False, OnChanged:=AddressOf PageSetupUI.HiddenRefresh), New Setting("UiHiddenSetupLaunch", False, OnChanged:=AddressOf PageSetupUI.HiddenRefresh),
New Setting("UiHiddenSetupUi", False, OnChanged:=AddressOf PageSetupUI.HiddenRefresh), New Setting("UiHiddenSetupUi", False, OnChanged:=AddressOf PageSetupUI.HiddenRefresh),
@@ -93,7 +93,8 @@ Public Class PageSpeedLeft
Tb.Text = Loader.Error.GetDisplay(True) Tb.Text = Loader.Error.GetDisplay(True)
AddHandler Tb.MouseLeftButtonDown, AddHandler Tb.MouseLeftButtonDown,
Sub(sender As TextBlock, e As EventArgs) Sub(sender As TextBlock, e As EventArgs)
ClipboardSet(sender.Text, SuccessHint:="已复制错误详情!") ClipboardSet(sender.Text, False)
Hint("已复制错误详情!", HintType.Green)
End Sub End Sub
Card.Children.Add(Tb) Card.Children.Add(Tb)
#End Region #End Region
@@ -282,6 +282,7 @@
<Compile Include="Pages\PageDownload\PageDownloadInstall.xaml.vb"> <Compile Include="Pages\PageDownload\PageDownloadInstall.xaml.vb">
<DependentUpon>PageDownloadInstall.xaml</DependentUpon> <DependentUpon>PageDownloadInstall.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Pages\PageDownload\PageDownloadModify.vb" />
<Compile Include="Pages\PageDownload\Resource\PageDownloadCompDetail.xaml.vb"> <Compile Include="Pages\PageDownload\Resource\PageDownloadCompDetail.xaml.vb">
<DependentUpon>PageDownloadCompDetail.xaml</DependentUpon> <DependentUpon>PageDownloadCompDetail.xaml</DependentUpon>
</Compile> </Compile>
Binary file not shown.
Binary file not shown.