Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b9a50df91 | ||
|
|
e4b0489a22 | ||
|
|
5ef81e490a | ||
|
|
ade6e04c6e | ||
|
|
9dcb799fb0 | ||
|
|
073546ba19 | ||
|
|
f8584954b8 | ||
|
|
a91d5e0079 | ||
|
|
5dcbd570a8 | ||
|
|
87a89e877d | ||
|
|
bc4ed0797f | ||
|
|
b4a8193bae | ||
|
|
0a3258974a | ||
|
|
d69ef35547 | ||
|
|
1ca8b78388 | ||
|
|
0e0d12fdce | ||
|
|
565b49321f | ||
|
|
8f4e6bbe05 | ||
|
|
d1e969d742 | ||
|
|
6d5c73fe98 | ||
|
|
8122a65e86 | ||
|
|
8f76864574 | ||
|
|
06125e2b89 | ||
|
|
39c49c8b9d | ||
|
|
8230882954 | ||
|
|
87cd3bc69d | ||
|
|
4759945743 | ||
|
|
092b2a2edd | ||
|
|
140348e10d | ||
|
|
05c8a47a06 | ||
|
|
b8651f109f | ||
|
|
e5d4db3a6d | ||
|
|
7c75fe4d4b | ||
|
|
9afbfe3f64 | ||
|
|
639de1b48a | ||
|
|
904de2af71 | ||
|
|
2fe1ceb40f | ||
|
|
0ce35fe7e5 | ||
|
|
7fb6e5d68e | ||
|
|
c7ccdd65dc | ||
|
|
bbb4fc31a8 | ||
|
|
d6c8266c58 | ||
|
|
217d34a237 | ||
|
|
671cf8bdf5 | ||
|
|
fd7b722346 | ||
|
|
cf7f8cbe19 | ||
|
|
2a15bcf2c6 |
No files matched your search
@@ -1,19 +1,6 @@
|
||||
《PCL 分发有限许可》
|
||||
|
||||
版权所有 © 成都瓜皮龙科技有限公司(以下简称 “许可方”)。
|
||||
除本许可明确授予的权利外,许可方保留软件及其相关知识产权中的全部权利。
|
||||
|
||||
一、定义
|
||||
|
||||
“软件” 是指 Plain Craft Launcher(以下简称 “PCL”)及许可方有权许可的相关源代码、二进制文件、资源文件、文档和随附内容。软件不包括已明确标注为第三方内容的部分,本许可亦不限制第三方内容原有许可证所明确授予的权利。
|
||||
“原样副本” 是指未经任何方式改变的软件副本。传输、存储、镜像、压缩或解压原样副本,不视为改变。
|
||||
“收费” 是指以获得、下载、复制、分发、安装、预装或使用原样副本为对价或条件,直接或间接向接收者收取费用,包括但不限于付费下载、会员访问门槛、强制赞助或其他变相收费方式。承载平台通过与获得原样副本无直接对应关系的一般广告、一般赞助、平台服务或其他独立业务取得收入,不视为收费。
|
||||
本许可适用于对软件进行复制、分发或其他被明确授权的行为。您行使本许可明确授予的任何权利,即表示同意遵守本许可。
|
||||
|
||||
二、权利
|
||||
|
||||
在持续不收费的前提下,许可方授予您一项全球范围内、非排他、免版税、不得再许可的有限许可:您可以存储、镜像、备份、上传、下载、向公众提供、传播和再分发原样副本。
|
||||
每一位从您处获得原样副本的接收者,均可在遵守本许可的前提下,直接从许可方获得本许可明确授予的相应权利。
|
||||
详见 https://shimo.im/docs/rGrd8pY8xWkt6ryW#anchor-X0bo
|
||||
|
||||
————————————————————————————————————
|
||||
|
||||
|
||||
+1
-1
Submodule MeloongCore updated: e5c8e65e2b...98157d65c7.
@@ -0,0 +1,246 @@
|
||||
# 如果要从更高级别的目录继承 .editorconfig 设置,请删除以下行
|
||||
root = true
|
||||
|
||||
# c# 文件
|
||||
[*.cs]
|
||||
|
||||
#### Core EditorConfig 选项 ####
|
||||
|
||||
# 缩进和间距
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
tab_width = 4
|
||||
|
||||
# 新行首选项
|
||||
end_of_line = crlf
|
||||
insert_final_newline = false
|
||||
|
||||
#### .NET 代码操作 ####
|
||||
|
||||
# 类型成员
|
||||
dotnet_hide_advanced_members = false
|
||||
dotnet_member_insertion_location = with_other_members_of_the_same_kind
|
||||
dotnet_property_generation_behavior = prefer_throwing_properties
|
||||
|
||||
# 符号搜索
|
||||
dotnet_search_reference_assemblies = true
|
||||
|
||||
#### .NET 编码约定 ####
|
||||
|
||||
# 组织 Using
|
||||
dotnet_separate_import_directive_groups = false
|
||||
dotnet_sort_system_directives_first = false
|
||||
file_header_template = unset
|
||||
|
||||
# this. 和 Me. 首选项
|
||||
dotnet_style_qualification_for_event = false
|
||||
dotnet_style_qualification_for_field = false
|
||||
dotnet_style_qualification_for_method = false
|
||||
dotnet_style_qualification_for_property = false
|
||||
|
||||
# 语言关键字与 bcl 类型首选项
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true
|
||||
dotnet_style_predefined_type_for_member_access = true
|
||||
|
||||
# 括号首选项
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
|
||||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
|
||||
|
||||
# 修饰符首选项
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members
|
||||
|
||||
# 表达式级首选项
|
||||
dotnet_prefer_system_hash_code = true
|
||||
dotnet_style_coalesce_expression = true
|
||||
dotnet_style_collection_initializer = true
|
||||
dotnet_style_explicit_tuple_names = true
|
||||
dotnet_style_namespace_match_folder = true
|
||||
dotnet_style_null_propagation = true
|
||||
dotnet_style_object_initializer = true
|
||||
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||
dotnet_style_prefer_auto_properties = true:suggestion
|
||||
dotnet_style_prefer_collection_expression = when_types_loosely_match
|
||||
dotnet_style_prefer_compound_assignment = true
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
|
||||
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
|
||||
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true
|
||||
dotnet_style_prefer_inferred_tuple_names = true
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
|
||||
dotnet_style_prefer_simplified_boolean_expressions = true
|
||||
dotnet_style_prefer_simplified_interpolation = true
|
||||
|
||||
# 字段首选项
|
||||
dotnet_style_readonly_field = true
|
||||
|
||||
# 参数首选项
|
||||
dotnet_code_quality_unused_parameters = all
|
||||
|
||||
# 禁止显示首选项
|
||||
dotnet_remove_unnecessary_suppression_exclusions = none
|
||||
|
||||
# 新行首选项
|
||||
dotnet_style_allow_multiple_blank_lines_experimental = true
|
||||
dotnet_style_allow_statement_immediately_after_block_experimental = true
|
||||
|
||||
#### c# 编码约定 ####
|
||||
|
||||
# var 首选项
|
||||
csharp_style_var_elsewhere = false
|
||||
csharp_style_var_for_built_in_types = false
|
||||
csharp_style_var_when_type_is_apparent = false
|
||||
|
||||
# Expression-bodied 成员
|
||||
csharp_style_expression_bodied_accessors = true
|
||||
csharp_style_expression_bodied_constructors = false
|
||||
csharp_style_expression_bodied_indexers = true
|
||||
csharp_style_expression_bodied_lambdas = true
|
||||
csharp_style_expression_bodied_local_functions = false
|
||||
csharp_style_expression_bodied_methods = false
|
||||
csharp_style_expression_bodied_operators = false
|
||||
csharp_style_expression_bodied_properties = true
|
||||
|
||||
# 模式匹配首选项
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true
|
||||
csharp_style_prefer_extended_property_pattern = true
|
||||
csharp_style_prefer_not_pattern = true
|
||||
csharp_style_prefer_pattern_matching = true:suggestion
|
||||
csharp_style_prefer_switch_expression = true
|
||||
|
||||
# Null 检查首选项
|
||||
csharp_style_conditional_delegate_call = true
|
||||
|
||||
# 修饰符首选项
|
||||
csharp_prefer_static_anonymous_function = true
|
||||
csharp_prefer_static_local_function = true
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
|
||||
csharp_style_prefer_readonly_struct = true
|
||||
csharp_style_prefer_readonly_struct_member = true
|
||||
|
||||
# 代码块首选项
|
||||
csharp_prefer_braces = true
|
||||
csharp_prefer_simple_using_statement = true
|
||||
csharp_prefer_system_threading_lock = true
|
||||
csharp_style_namespace_declarations = file_scoped:suggestion
|
||||
csharp_style_prefer_method_group_conversion = true
|
||||
csharp_style_prefer_primary_constructors = true
|
||||
csharp_style_prefer_top_level_statements = true
|
||||
|
||||
# 表达式级首选项
|
||||
csharp_prefer_simple_default_expression = true
|
||||
csharp_style_deconstructed_variable_declaration = true
|
||||
csharp_style_implicit_object_creation_when_type_is_apparent = true
|
||||
csharp_style_inlined_variable_declaration = true
|
||||
csharp_style_prefer_implicitly_typed_lambda_expression = true
|
||||
csharp_style_prefer_index_operator = true
|
||||
csharp_style_prefer_local_over_anonymous_function = true
|
||||
csharp_style_prefer_null_check_over_type_check = true
|
||||
csharp_style_prefer_range_operator = true
|
||||
csharp_style_prefer_tuple_swap = true
|
||||
csharp_style_prefer_unbound_generic_type_in_nameof = true
|
||||
csharp_style_prefer_utf8_string_literals = true
|
||||
csharp_style_throw_expression = true
|
||||
csharp_style_unused_value_assignment_preference = discard_variable
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable
|
||||
|
||||
# "using" 指令首选项
|
||||
csharp_using_directive_placement = outside_namespace
|
||||
|
||||
# 新行首选项
|
||||
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
|
||||
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
|
||||
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
|
||||
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
|
||||
csharp_style_allow_embedded_statements_on_same_line_experimental = true
|
||||
|
||||
#### C# 格式规则 ####
|
||||
|
||||
# 新行首选项
|
||||
csharp_new_line_before_catch = false
|
||||
csharp_new_line_before_else = false
|
||||
csharp_new_line_before_finally = false
|
||||
csharp_new_line_before_members_in_anonymous_types = false
|
||||
csharp_new_line_before_members_in_object_initializers = false
|
||||
csharp_new_line_before_open_brace = none
|
||||
csharp_new_line_between_query_expression_clauses = false
|
||||
|
||||
# 缩进首选项
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_case_contents_when_block = false
|
||||
csharp_indent_labels = flush_left
|
||||
csharp_indent_switch_labels = false
|
||||
|
||||
# 空格键首选项
|
||||
csharp_space_after_cast = true
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = ignore
|
||||
csharp_space_around_declaration_statements = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_between_square_brackets = false
|
||||
|
||||
# 包装首选项
|
||||
csharp_preserve_single_line_blocks = true
|
||||
csharp_preserve_single_line_statements = true
|
||||
|
||||
#### 命名样式 ####
|
||||
|
||||
# 命名规则
|
||||
|
||||
dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion
|
||||
dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface
|
||||
dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始
|
||||
|
||||
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion
|
||||
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型
|
||||
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
|
||||
|
||||
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion
|
||||
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员
|
||||
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
|
||||
|
||||
# 符号规范
|
||||
|
||||
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.interface.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum
|
||||
dotnet_naming_symbols.类型.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.类型.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.非字段成员.required_modifiers =
|
||||
|
||||
# 命名样式
|
||||
|
||||
dotnet_naming_style.帕斯卡拼写法.required_prefix =
|
||||
dotnet_naming_style.帕斯卡拼写法.required_suffix =
|
||||
dotnet_naming_style.帕斯卡拼写法.word_separator =
|
||||
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.以_i_开始.required_prefix = I
|
||||
dotnet_naming_style.以_i_开始.required_suffix =
|
||||
dotnet_naming_style.以_i_开始.word_separator =
|
||||
dotnet_naming_style.以_i_开始.capitalization = pascal_case
|
||||
+23
-6
@@ -1,11 +1,28 @@
|
||||
namespace PCLCS;
|
||||
public static class Configs {
|
||||
|
||||
/// <summary>当前 Java 相关配置的版本。0 代表尚未从老的 Settings 系统中迁移。</summary>
|
||||
public static readonly ConfigEntry<int> JavaConfigVersion = new(nameof(JavaConfigVersion), 0);
|
||||
public static readonly ConfigEntry<ConcurrentList<Java>> JavaList = new(nameof(JavaList), []);
|
||||
public static readonly ConfigEntry<List<string>> JavaRemovedList = new(nameof(JavaRemovedList), []);
|
||||
public static readonly ConfigEntry<bool> InstanceMigratedJava = new(nameof(InstanceMigratedJava), false);
|
||||
public static readonly ConfigEntry<Java> InstanceForcedJava = new(nameof(InstanceForcedJava), null);
|
||||
public static readonly DynamicConfigEntry<McInstallManifest> McInstallManifest = new("MinecraftInstallManifest", null);
|
||||
|
||||
// Java
|
||||
|
||||
/// <summary> 当前 Java 相关配置的版本。0 代表尚未从老的 Settings 系统中迁移。</summary>
|
||||
public static readonly ConfigEntry<int> JavaConfigVersion = new("JavaConfigVersion", 0);
|
||||
public static readonly ConfigEntry<ConcurrentList<Java>> JavaList = new("JavaList", []);
|
||||
public static readonly ConfigEntry<List<string>> JavaRemovedList = new("JavaRemovedList", []);
|
||||
public static readonly DynamicConfigEntry<bool> JavaMigrated = new("InstanceMigratedJava", false);
|
||||
public static readonly DynamicConfigEntry<Java> JavaForced = new("InstanceForcedJava", null);
|
||||
|
||||
// 提示
|
||||
|
||||
public static readonly ConfigEntry<bool> HintModifyInstanceWarning = new("HintModifyInstanceWarning", false);
|
||||
|
||||
static Configs() {
|
||||
/// 当 JavaForced 获取到一个不在 JavaList 中的值时,将其重置为 null。
|
||||
JavaForced.PreviewGet += (args, provider) => {
|
||||
if (args.Value is not null && !JavaList.Get()!.Contains(args.Value)) {
|
||||
args.Value = null;
|
||||
JavaForced.Reset(provider);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+10
-5
@@ -17,10 +17,12 @@ public enum ResourceTypes {
|
||||
DataPack = 16,
|
||||
/// <summary>服务端插件。</summary>
|
||||
Plugin = 32,
|
||||
/// <summary>地图。</summary>
|
||||
Map = 64,
|
||||
/// <summary>同时包含数据包以及 Mod。</summary>
|
||||
ModOrDataPack = Mod | DataPack,
|
||||
/// <summary>允许任意种类,或种类未知。</summary>
|
||||
Any = Mod | ModPack | ResourcePack | Shader | DataPack | Plugin,
|
||||
Any = Mod | ModPack | ResourcePack | Shader | DataPack | Plugin | Map,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -42,9 +44,8 @@ public enum ModLoaders {
|
||||
Forge = 1,
|
||||
LiteLoader = 2,
|
||||
Fabric = 4,
|
||||
Quilt = 8,
|
||||
NeoForge = 16,
|
||||
All = Forge | LiteLoader | Fabric | Quilt | NeoForge,
|
||||
All = Forge | LiteLoader | Fabric | NeoForge,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -67,13 +68,17 @@ public static class Versions {
|
||||
/// <summary>
|
||||
/// 版本管理中 Mod 信息缓存的版本号。
|
||||
/// </summary>
|
||||
public const int LocalModCacheVersion = 17;
|
||||
public const int LocalModCacheVersion = 21;
|
||||
/// <summary>
|
||||
/// Minecraft 本地实例信息缓存的版本号。
|
||||
/// </summary>
|
||||
public const int McInstanceCacheVersion = 38;
|
||||
public const int McInstanceCacheVersion = 39;
|
||||
/// <summary>
|
||||
/// Java 相关配置的版本号。
|
||||
/// </summary>
|
||||
public const int JavaConfigVersion = 1;
|
||||
/// <summary>
|
||||
/// Minecraft 安装清单的版本号。
|
||||
/// </summary>
|
||||
public const int McInstallManifestVersion = 1;
|
||||
}
|
||||
+45
-11
@@ -19,6 +19,13 @@ namespace PCLCS;
|
||||
/// <para/> 若为 null,则还需要调用 <see cref="CheckAsync"/> 以获取版本号。
|
||||
/// </summary>
|
||||
public Version? Version = version;
|
||||
/// <summary>Java 的默认字符编码。</summary>
|
||||
[JsonIgnore] public string? FileEncodingName;
|
||||
/// <summary>
|
||||
/// Java 根据系统环境确定的本地字符编码,仅 Java 17+ 可用。
|
||||
/// <para/>若系统开启了 UTF-8,部分 Java 会错误地返回 JDK,因此不能使用 <see cref="Encoding.Default"/>。
|
||||
/// </summary>
|
||||
[JsonIgnore] public string? NativeEncodingName;
|
||||
|
||||
/// <summary>
|
||||
/// 检查该 Java 是否存在问题,并获取其版本号。
|
||||
@@ -31,7 +38,7 @@ namespace PCLCS;
|
||||
if (!FileUtils.Exists(JavaExePath)) throw new FileNotFoundException("未找到 java.exe 文件", JavaExePath);
|
||||
|
||||
// 运行 -version
|
||||
output = (await TaskUtils.RunProgramAsync(JavaExePath, "-version", 15000, cancellationToken: cancellationToken).NoCapture()).Output.Lower();
|
||||
output = (await TaskUtils.RunProgramAsync(JavaExePath, "-XshowSettings:properties -version", 15000, cancellationToken: cancellationToken).NoCapture()).Output.Lower();
|
||||
if (output == "") throw new InvalidOperationException("尝试运行该 Java 失败");
|
||||
Logger.Trace($"Java 检查输出:{JavaExePath}{Environment.NewLine}{output}");
|
||||
if (output.Contains("/lib/ext exists")) throw new InvalidOperationException("无法运行该 Java,请在删除 Java 文件夹中的 /lib/ext 文件夹后再试");
|
||||
@@ -46,7 +53,16 @@ namespace PCLCS;
|
||||
Version = new Version(segments.Take(4).Join("."));
|
||||
if (Version.Major is <= 4 or >= 100) throw new InvalidOperationException($"分析详细信息失败,获取的版本为 {Version}{(output.Length < 500 ? $"\n原始输出为:\n{output}" : "")}");
|
||||
|
||||
// 获取 Java 的相关属性
|
||||
if (!output.Contains("64-bit")) throw new InvalidOperationException("该 Java 为 32 位版本,请安装 64 位的 Java");
|
||||
string? GetProperty(string name) {
|
||||
string prefix = name + " = ";
|
||||
string? line = output.Split('\n').Select(l => l.Trim()).FirstOrDefault(l => l.StartsWithF(prefix));
|
||||
return line?[prefix.Length..].Trim();
|
||||
}
|
||||
FileEncodingName = GetProperty("file.encoding");
|
||||
NativeEncodingName = GetProperty("native.encoding") ?? FileEncodingName; // 旧版本没有 native.encoding
|
||||
|
||||
Logger.Info($"检查 Java 成功:{this}");
|
||||
available = true;
|
||||
} catch (Exception ex) {
|
||||
@@ -59,15 +75,21 @@ namespace PCLCS;
|
||||
}
|
||||
return available!.Value;
|
||||
}
|
||||
|
||||
public void InvalidateChecked() {
|
||||
isChecked = 0;
|
||||
available = null;
|
||||
}
|
||||
|
||||
[JsonIgnore] private int isChecked = 0;
|
||||
/// <summary>若已进行过检查,表示该检查是否成功。若未检查,则为 null。</summary>
|
||||
[JsonIgnore] internal bool? available = null;
|
||||
|
||||
// 相同检查
|
||||
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 int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Folder);
|
||||
public override int GetHashCode() => StringComparer.CurrentCultureIgnoreCase.GetHashCode(PathUtils.ForCompare(Folder));
|
||||
|
||||
/// <summary>用户友好的描述文本。</summary>
|
||||
public override string ToString()
|
||||
@@ -95,8 +117,8 @@ public static class JavaUtils {
|
||||
javas = await JavaUtils.CheckAllAsync(javas, cancellationToken, progress).NoCapture();
|
||||
javas.Sort((left, right) => {
|
||||
// 优先使用带完整 Java 的文件夹列表中的 Java
|
||||
var isLeftInCandidateFolder = JavaUtils.CandidateFolders.Any(folder => PathUtils.IsParentOf(folder, left.Folder));
|
||||
var isRightInCandidateFolder = JavaUtils.CandidateFolders.Any(folder => PathUtils.IsParentOf(folder, right.Folder));
|
||||
var isLeftInCandidateFolder = JavaUtils.CandidateFolders.Value.Any(folder => PathUtils.IsParentOf(folder, left.Folder));
|
||||
var isRightInCandidateFolder = JavaUtils.CandidateFolders.Value.Any(folder => PathUtils.IsParentOf(folder, right.Folder));
|
||||
if (isLeftInCandidateFolder != isRightInCandidateFolder) return isLeftInCandidateFolder ? -1 : 1;
|
||||
// 其次优先使用主版本号接近 21 的 Java
|
||||
var leftDistance = left.Version is null ? int.MaxValue : Math.Abs(left.Version.Major - 21);
|
||||
@@ -111,7 +133,8 @@ public static class JavaUtils {
|
||||
/// <summary>
|
||||
/// 存在完整 Java 的文件夹列表。
|
||||
/// </summary>
|
||||
internal static readonly List<string> CandidateFolders = [
|
||||
internal static readonly Lazy<List<string>> CandidateFolders = new(() => {
|
||||
List<string> folders = [
|
||||
Paths.AppData + @".minecraft\runtime\", // 这也是 PCL 下载 Java 的路径
|
||||
Paths.AppData + @".hmcl\java\",
|
||||
Paths.AppData + @"ATLauncher\runtimes\minecraft\",
|
||||
@@ -134,13 +157,23 @@ public static class JavaUtils {
|
||||
..(Environment.GetEnvironmentVariable("JDK_HOME") + ";" + Environment.GetEnvironmentVariable("JAVA_HOME")).
|
||||
Split(";", true).Select(path => path.Trim(' ', '"'))
|
||||
];
|
||||
return folders.Select(f => {
|
||||
try {
|
||||
return PathUtils.ForCompare(f);
|
||||
} catch (Exception ex) {
|
||||
Logger.Warn(ex, $"Java 候选文件夹路径无效({f})");
|
||||
return null;
|
||||
}
|
||||
}).Where(f => f is not null).Cast<string>().ToList();
|
||||
});
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 从常见文件夹和环境变量中搜索 Java,并将搜索结果写入设置。
|
||||
/// </summary>
|
||||
public static async Task RefreshListAsync(CancellationToken c = default, ProgressProvider? p = null) {
|
||||
// 搜索
|
||||
List<Java> javaList = await SearchFoldersAsync(true, CandidateFolders, c, p?.SplitTo(0.5)).NoCapture();
|
||||
List<Java> javaList = await SearchFoldersAsync(true, CandidateFolders.Value, c, p?.SplitTo(0.5)).NoCapture();
|
||||
// 将新发现的 Java 排在最前,原有列表保留既有顺序排在后面
|
||||
var oldJavaList = Configs.JavaList.Get()!;
|
||||
oldJavaList = await JavaUtils.CheckAllAsync(oldJavaList, c, p?.SplitTo(0.9)).NoCapture(); // 检查原有列表中的 Java
|
||||
@@ -169,15 +202,16 @@ public static class JavaUtils {
|
||||
string targetFolder = PathUtils.AddSlashSuffix(PathUtils.ForCompare(PathUtils.RemoveLastPart(file)));
|
||||
// 判断文件夹是否包含重解析点(例如符号链接)
|
||||
static bool HasReparsePoint(FileSystemInfo info) {
|
||||
if (PathUtils.IsParentOf(Paths.AppData + @".minecraft\runtime\", info.FullName)) return false; // 不筛查 PCL 的下载路径(#8928)
|
||||
do {
|
||||
if (info.Attributes.HasFlag(FileAttributes.ReparsePoint)) return true;
|
||||
if (info.Attributes.HasFlagF(FileAttributes.ReparsePoint)) return true;
|
||||
info = info is FileInfo fileInfo ? fileInfo.Directory : ((DirectoryInfo) info).Parent;
|
||||
} while (info is not null);
|
||||
return false;
|
||||
}
|
||||
// 结果
|
||||
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));
|
||||
}
|
||||
} catch (UnauthorizedAccessException) {
|
||||
@@ -194,14 +228,14 @@ public static class JavaUtils {
|
||||
if (results.All(r => r.HasReparsePoint)) {
|
||||
Logger.Warn("找到的所有 Java 都包含重解析点,保留全部结果");
|
||||
} else if (results.Any(r => r.HasReparsePoint)) {
|
||||
Logger.Info($"找到以下的 Java 包含重解析点,将它们从列表中排除:{results.Where(r => r.HasReparsePoint).Select(r => r.Folder).Join('、')}");
|
||||
Logger.Warn($"找到的以下 Java 包含重解析点,将它们从列表中排除:{results.Where(r => r.HasReparsePoint).Select(r => r.Folder).Join('、')}");
|
||||
results = [..results.Where(r => !r.HasReparsePoint)];
|
||||
}
|
||||
// 筛除特殊路径
|
||||
if (results.All(r => r.IsSpecialPath)) {
|
||||
Logger.Warn("找到的所有 Java 都包含特殊路径,保留全部结果");
|
||||
} else if (results.Any(r => r.IsSpecialPath)) {
|
||||
Logger.Info($"找到以下的 Java 包含特殊路径,将它们从列表中排除:{results.Where(r => r.IsSpecialPath).Select(r => r.Folder).Join('、')}");
|
||||
Logger.Warn($"找到的以下 Java 包含特殊路径,将它们从列表中排除:{results.Where(r => r.IsSpecialPath).Select(r => r.Folder).Join('、')}");
|
||||
results = [..results.Where(r => !r.IsSpecialPath)];
|
||||
}
|
||||
|
||||
|
||||
File renamed without changes.
@@ -0,0 +1,30 @@
|
||||
namespace PCLCS;
|
||||
|
||||
public static class LaunchUtils {
|
||||
|
||||
/// <summary>
|
||||
/// 释放补丁文件并返回完整文件路径。
|
||||
/// </summary>
|
||||
/// <param name="patchFilePath">补丁文件路径
|
||||
/// <param name="outputDirectory">补丁文件的释放目录。</param>
|
||||
public static string ExtractPatch(string patchFilePath, string outputDirectory) {
|
||||
Logger.Info($"选定的资源 {patchFilePath} 输出路径:{outputDirectory}");
|
||||
lock (ExtractPatchLock) { // 避免 OptiFine 和 Forge 安装时同时释放导致冲突
|
||||
try {
|
||||
FileUtils.ExtractResources(patchFilePath, Path.Combine(outputDirectory, PathUtils.GetLastPart(patchFilePath)), typeof(LaunchUtils));
|
||||
} catch (Exception ex) {
|
||||
if (!FileUtils.Exists(outputDirectory)) throw new FileNotFoundException($"释放 {patchFilePath} 失败", ex);
|
||||
Logger.Warn(ex, $"{patchFilePath} 文件重新释放失败,将尝试更换文件名重新生成");
|
||||
patchFilePath = Path.Combine(outputDirectory, $"{PathUtils.GetFileNameWithoutExtension(patchFilePath)}2.{PathUtils.GetExtension(patchFilePath)}");
|
||||
try {
|
||||
FileUtils.ExtractResources(patchFilePath, Path.Combine(outputDirectory, PathUtils.GetLastPart(patchFilePath)), typeof(LaunchUtils));
|
||||
} catch (Exception ex2) {
|
||||
throw new FileNotFoundException($"释放 {patchFilePath} 最终尝试失败", ex2);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Path.Combine(outputDirectory, PathUtils.GetLastPart(patchFilePath));
|
||||
}
|
||||
private static readonly object ExtractPatchLock = new();
|
||||
|
||||
}
|
||||
File renamed without changes.
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
+4
-1
@@ -27,6 +27,7 @@
|
||||
<Using Include="System.Collections.Generic" />
|
||||
<Using Include="System.Collections.Concurrent" />
|
||||
<Using Include="System.Text" />
|
||||
<Using Include="System.Text.RegularExpressions" />
|
||||
<Using Include="System.Net" />
|
||||
<Using Include="System.Runtime.CompilerServices" />
|
||||
<Using Include="System.Runtime.InteropServices" />
|
||||
@@ -55,8 +56,10 @@
|
||||
|
||||
<!-- 内嵌的资源 -->
|
||||
<ItemGroup>
|
||||
<None Remove="Resource\WikiEntries.txt" />
|
||||
<EmbeddedResource Include="Launch\JavaWrapper.jar" LogicalName="Launch\JavaWrapper.jar" />
|
||||
<EmbeddedResource Include="Launch\LwjglUnsafeAgent.jar" LogicalName="Launch\LwjglUnsafeAgent.jar" />
|
||||
<EmbeddedResource Include="Resource\WikiEntries.txt" LogicalName="Resource\WikiEntries.txt" />
|
||||
<EmbeddedResource Include="Minecraft\OptiFineHashes.json" LogicalName="Minecraft\OptiFineHashes.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.VisualBasic" />
|
||||
|
||||
@@ -9,7 +9,6 @@ public static class Resource {
|
||||
1 => ModLoaders.Forge,
|
||||
3 => ModLoaders.LiteLoader,
|
||||
4 => ModLoaders.Fabric,
|
||||
5 => ModLoaders.Quilt,
|
||||
6 => ModLoaders.NeoForge,
|
||||
_ => ModLoaders.None,
|
||||
};
|
||||
@@ -24,7 +23,6 @@ public static class Resource {
|
||||
ModLoaders.Forge => 1,
|
||||
ModLoaders.LiteLoader => 3,
|
||||
ModLoaders.Fabric => 4,
|
||||
ModLoaders.Quilt => 5,
|
||||
ModLoaders.NeoForge => 6,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
+5166
-665
File diff suppressed because it is too large.
Load diff
@@ -1,6 +1,5 @@
|
||||
Imports System.Reflection
|
||||
Imports System.Windows.Threading
|
||||
Imports Microsoft.Win32
|
||||
|
||||
Public Class Application
|
||||
|
||||
@@ -10,7 +9,10 @@ Public Class Application
|
||||
'核心初始化
|
||||
MeloongCore.Main.Init("PCL")
|
||||
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)}
|
||||
End If
|
||||
'提升主线程优先级
|
||||
Thread.CurrentThread.Priority = ThreadPriority.Highest
|
||||
'执行开发版测试
|
||||
@@ -23,9 +25,7 @@ Public Class Application
|
||||
End If
|
||||
'检查 .NET Framework 版本
|
||||
Try
|
||||
Using ndpKey As RegistryKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey("SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\")
|
||||
If ndpKey?.GetValue("Release") IsNot Nothing AndAlso CInt(ndpKey.GetValue("Release")) < 528040 Then OldEnvironmentAssert()
|
||||
End Using
|
||||
If CInt(RegistryUtils.TryRead("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full", "Release", 999999)) < 528040 Then OldEnvironmentAssert()
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, "检查 .NET Framework 版本失败")
|
||||
End Try
|
||||
@@ -50,7 +50,7 @@ Public Class Application
|
||||
Catch ex As Exception
|
||||
Environment.Exit(ProcessReturnValues.Fail)
|
||||
End Try
|
||||
ElseIf e.Args(0).StartsWithF("--memory") Then
|
||||
ElseIf e.Args(0) = "--memory" Then
|
||||
'内存优化
|
||||
Dim Ram = My.Computer.Info.AvailablePhysicalMemory
|
||||
Try
|
||||
@@ -73,7 +73,7 @@ Public Class Application
|
||||
CheckPermissionWithException(Paths.Base & "PCL\")
|
||||
Catch ex As Exception
|
||||
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 &
|
||||
"3. 右键 PCL 选择属性,打开 兼容性 中的 以管理员身份运行此程序。",
|
||||
MsgBoxStyle.Critical, "运行环境错误")
|
||||
@@ -84,7 +84,7 @@ RetryCacheCheck:
|
||||
DirectoryUtils.Create(PathTemp)
|
||||
CheckPermissionWithException(PathTemp)
|
||||
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), "缓存文件夹不可用")
|
||||
Else
|
||||
MyMsgBox("手动设置的缓存文件夹不可用,PCL 将使用默认缓存文件夹。" & vbCrLf & vbCrLf & "错误原因:" & ex.GetDisplay(True), "缓存文件夹不可用")
|
||||
@@ -95,8 +95,6 @@ RetryCacheCheck:
|
||||
End Try
|
||||
DirectoryUtils.Create(PathTemp & "Cache\")
|
||||
DirectoryUtils.Create(Paths.AppDataThenName)
|
||||
'要求单例
|
||||
WaitingMutex(e)
|
||||
'设置 ToolTipService 默认值
|
||||
ToolTipService.InitialShowDelayProperty.OverrideMetadata(GetType(DependencyObject), New FrameworkPropertyMetadata(100))
|
||||
'设置网络配置默认值
|
||||
@@ -218,7 +216,7 @@ RetryCacheCheck:
|
||||
|
||||
'动态 DLL 加载
|
||||
Private Sub New() '这里必须尽早调用,且不能使用任何库,否则加载 MeloongCore 就会导致崩溃
|
||||
Static Prefixes As String() = {"NAudio", "Newtonsoft.Json", "Ookii.Dialogs.Wpf", "Imazen.WebP", "CacheCow.Common", "CacheCow.Client.FileStore", "CacheCow.Client", "ThrottleDebounce", "System.Net.Http.Formatting", "PCLCS", "MeloongCore.Wpf", "MeloongCore"}
|
||||
Static Prefixes As String() = {"NAudio", "Newtonsoft.Json", "Ookii.Dialogs.Wpf", "Imazen.WebP", "CacheCow.Common", "CacheCow.Client.FileStore", "CacheCow.Client", "ThrottleDebounce", "System.Net.Http.Formatting", "Microsoft.Win32.Registry", "PCLCS", "MeloongCore.Wpf", "MeloongCore"}
|
||||
Static LoadedAssemblies As New ConcurrentDictionary(Of String, Lazy(Of Assembly))(StringComparer.Ordinal) '缓存
|
||||
AddHandler AppDomain.CurrentDomain.AssemblyResolve,
|
||||
Function(sender As Object, Args As ResolveEventArgs) As Assembly
|
||||
@@ -234,17 +232,15 @@ RetryCacheCheck:
|
||||
End Function
|
||||
End Sub
|
||||
Private Shared Sub ExtractLibwebp() '这个方法会调用 ModBase,进而调用 MeloongCore,所以不能放在 New 里
|
||||
SetDllDirectory(PathPure.TrimEnd("\"c))
|
||||
SetDllDirectory(PathUtils.RemoveSlashSuffix(PathPure.Value))
|
||||
Try
|
||||
ExtractResources(PathPure & "libwebp.dll", "libwebp64")
|
||||
ExtractResources(PathPure.Value & "libwebp.dll", "libwebp64")
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, "写入 libwebp.dll 失败") '防止同时加载多个图片时,同时写入文件导致文件占用,进而导致崩溃
|
||||
End Try
|
||||
End Sub
|
||||
Private Declare Function SetDllDirectory Lib "kernel32" Alias "SetDllDirectoryA" (lpPathName As String) As Boolean
|
||||
|
||||
'切换窗口
|
||||
|
||||
'控件模板事件
|
||||
Private Sub MyIconButton_Click(sender As Object, e As EventArgs)
|
||||
Select Case Settings.Get(Of McLoginType)("LoginType")
|
||||
@@ -290,12 +286,4 @@ RetryCacheCheck:
|
||||
End Select
|
||||
End Sub
|
||||
|
||||
Public Shared ShowingTooltips As New List(Of Border)
|
||||
Private Sub TooltipLoaded(sender As Border, e As EventArgs)
|
||||
ShowingTooltips.Add(sender)
|
||||
End Sub
|
||||
Private Sub TooltipUnloaded(sender As Border, e As RoutedEventArgs)
|
||||
ShowingTooltips.Remove(sender)
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -44,8 +44,8 @@ Namespace Controls.Behaviors
|
||||
|
||||
Private Shared Sub AddCommandBindingsToTextBox(tb As TextBox)
|
||||
tb.CommandBindings.Add(New CommandBinding(ApplicationCommands.Copy, AddressOf OnCopyTextBox))
|
||||
tb.CommandBindings.Add(New CommandBinding(ApplicationCommands.Cut, AddressOf OnCutTextBox))
|
||||
tb.CommandBindings.Add(New CommandBinding(ApplicationCommands.Paste, AddressOf OnPasteTextBox))
|
||||
tb.CommandBindings.Add(New CommandBinding(ApplicationCommands.Cut, AddressOf OnCutTextBox, AddressOf CanEditTextBox))
|
||||
tb.CommandBindings.Add(New CommandBinding(ApplicationCommands.Paste, AddressOf OnPasteTextBox, AddressOf CanEditTextBox))
|
||||
End Sub
|
||||
|
||||
Private Shared Sub AddCommandBindingsToRichTextBox(rtb As RichTextBox)
|
||||
@@ -58,42 +58,61 @@ Namespace Controls.Behaviors
|
||||
dg.CommandBindings.Add(New CommandBinding(ApplicationCommands.Copy, AddressOf OnCopyDataGrid))
|
||||
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)
|
||||
Dim tb = TryCast(sender, TextBox)
|
||||
If tb Is Nothing OrElse tb.SelectionLength <= 0 Then Return
|
||||
|
||||
Try
|
||||
Forms.Clipboard.Clear()
|
||||
Forms.Clipboard.SetDataObject(tb.SelectedText, True)
|
||||
SetClipboardText(tb.SelectedText)
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, "复制失败,请稍后再试", LogBehavior.Toast)
|
||||
Logger.Warn(ex, $"复制失败,请稍后再试(错误码:0x{ex.HResult:X8})", LogBehavior.Toast)
|
||||
End Try
|
||||
|
||||
e.Handled = True
|
||||
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)
|
||||
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
|
||||
Forms.Clipboard.Clear()
|
||||
Forms.Clipboard.SetDataObject(tb.SelectedText, True)
|
||||
SetClipboardText(tb.SelectedText)
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, "剪切失败,请稍后再试", LogBehavior.Toast)
|
||||
Logger.Warn(ex, $"剪切失败,请稍后再试(错误码:0x{ex.HResult:X8})", LogBehavior.Toast)
|
||||
Return
|
||||
End Try
|
||||
|
||||
tb.SelectedText = String.Empty
|
||||
e.Handled = True
|
||||
End Sub
|
||||
|
||||
Private Shared Sub OnPasteTextBox(sender As Object, e As ExecutedRoutedEventArgs)
|
||||
Dim tb = TryCast(sender, TextBox)
|
||||
If tb Is Nothing Then Return
|
||||
e.Handled = True
|
||||
If tb Is Nothing OrElse tb.IsReadOnly Then Return
|
||||
|
||||
Try
|
||||
If Forms.Clipboard.ContainsText() Then
|
||||
Dim pasteText = Forms.Clipboard.GetText()
|
||||
Dim pasteText = Retrier.Attempt(func:=
|
||||
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)
|
||||
If Not tb.AcceptsReturn Then pasteText = pasteText.ReplaceLineEndings(" ", mergeMultiple:=True)
|
||||
@@ -105,10 +124,8 @@ Namespace Controls.Behaviors
|
||||
tb.SelectionLength = 0
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, "粘贴失败,请稍后再试", LogBehavior.Toast)
|
||||
Logger.Warn(ex, $"粘贴失败,请稍后再试(错误码:0x{ex.HResult:X8})", LogBehavior.Toast)
|
||||
End Try
|
||||
|
||||
e.Handled = True
|
||||
End Sub
|
||||
|
||||
Private Shared Sub OnCopyRichTextBox(sender As Object, e As ExecutedRoutedEventArgs)
|
||||
@@ -119,10 +136,9 @@ Namespace Controls.Behaviors
|
||||
If String.IsNullOrEmpty(textRange.Text) Then Return
|
||||
|
||||
Try
|
||||
Forms.Clipboard.Clear()
|
||||
Forms.Clipboard.SetDataObject(textRange.Text, True)
|
||||
SetClipboardText(textRange.Text)
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, "复制失败,请稍后再试", LogBehavior.Toast)
|
||||
Logger.Warn(ex, $"复制失败,请稍后再试(错误码:0x{ex.HResult:X8})", LogBehavior.Toast)
|
||||
End Try
|
||||
|
||||
e.Handled = True
|
||||
@@ -134,16 +150,16 @@ Namespace Controls.Behaviors
|
||||
|
||||
Dim selection = New TextRange(rtb.Selection.Start, rtb.Selection.End)
|
||||
If String.IsNullOrEmpty(selection.Text) Then Return
|
||||
e.Handled = True
|
||||
|
||||
Try
|
||||
Forms.Clipboard.Clear()
|
||||
Forms.Clipboard.SetDataObject(selection.Text, True)
|
||||
SetClipboardText(selection.Text)
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, "剪切失败,请稍后再试", LogBehavior.Toast)
|
||||
Logger.Warn(ex, $"剪切失败,请稍后再试(错误码:0x{ex.HResult:X8})", LogBehavior.Toast)
|
||||
Return
|
||||
End Try
|
||||
|
||||
selection.Text = String.Empty
|
||||
e.Handled = True
|
||||
End Sub
|
||||
|
||||
Private Shared Sub OnPasteRichTextBox(sender As Object, e As ExecutedRoutedEventArgs)
|
||||
@@ -151,9 +167,11 @@ Namespace Controls.Behaviors
|
||||
If rtb Is Nothing Then Return
|
||||
|
||||
Try
|
||||
If Not Forms.Clipboard.ContainsText() Then Return
|
||||
|
||||
Dim pasteText = Forms.Clipboard.GetText()
|
||||
Dim pasteText = Retrier.Attempt(func:=
|
||||
Function(Attempt) As String
|
||||
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
|
||||
|
||||
selection.Text = pasteText
|
||||
@@ -162,7 +180,7 @@ Namespace Controls.Behaviors
|
||||
rtb.CaretPosition = caretPos
|
||||
rtb.Selection.Select(caretPos, caretPos)
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, "粘贴失败,请稍后再试", LogBehavior.Toast)
|
||||
Logger.Warn(ex, $"粘贴失败,请稍后再试(错误码:0x{ex.HResult:X8})", LogBehavior.Toast)
|
||||
End Try
|
||||
|
||||
e.Handled = True
|
||||
@@ -187,10 +205,9 @@ Namespace Controls.Behaviors
|
||||
Dim sbStr = sb.ToString().TrimEnd(ControlChars.Cr, ControlChars.Lf)
|
||||
|
||||
Try
|
||||
Forms.Clipboard.Clear()
|
||||
Forms.Clipboard.SetDataObject(sbStr, True)
|
||||
SetClipboardText(sbStr)
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, "复制失败,请稍后再试", LogBehavior.Toast)
|
||||
Logger.Warn(ex, $"复制失败,请稍后再试(错误码:0x{ex.HResult:X8})", LogBehavior.Toast)
|
||||
End Try
|
||||
|
||||
e.Handled = True
|
||||
|
||||
@@ -102,73 +102,36 @@ Public Class MyCard
|
||||
RunInUi(Sub() UseAnimation = RawUseAnimation, True)
|
||||
End If
|
||||
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
|
||||
Public Shared Sub StackInstall(ByRef Stack As StackPanel, Type As Integer, Optional CardTitle As String = "")
|
||||
'这一部分的代码是好几年前留下的究极屎坑,当时还不知道该咋正确调用这种方法,就写了这么一坨屎
|
||||
'但是现在……反正勉强能用……懒得改了就这样吧.jpg
|
||||
'别骂了别骂了.jpg
|
||||
If Stack.Tag Is Nothing Then Return
|
||||
'排序
|
||||
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
|
||||
Public Sub SetContentBuilder(Of T)(Items As IEnumerable(Of T), ItemBuilder As Func(Of T, UIElement))
|
||||
SetContentBuilder(
|
||||
Sub(Stack)
|
||||
For Each Item In Items
|
||||
Stack.Children.Add(ItemBuilder(Item))
|
||||
Next
|
||||
Stack.Children.Add(New FrameworkElement With {.Height = 18}) '下边距,同时适应折叠
|
||||
Stack.Tag = Nothing
|
||||
Stack.Children.Add(New FrameworkElement With {.Height = 18})
|
||||
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
|
||||
|
||||
'动画
|
||||
@@ -278,10 +241,6 @@ Public Class MyCard
|
||||
Public SwapControl As FrameworkElement
|
||||
Public Property CanSwap As Boolean = False
|
||||
''' <summary>
|
||||
''' 被折叠的种类,用于控件虚拟化。
|
||||
''' </summary>
|
||||
Public Property SwapType As Integer
|
||||
''' <summary>
|
||||
''' 是否已被折叠。
|
||||
''' </summary>
|
||||
Public Property IsSwapped As Boolean
|
||||
@@ -293,7 +252,7 @@ Public Class MyCard
|
||||
_IsSwapped = value
|
||||
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 事件中触发无动画的折叠,不需要在这里进行
|
||||
If Not IsLoaded Then Return
|
||||
'更新样式
|
||||
|
||||
@@ -90,7 +90,7 @@ Public Class MyImage
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, $"加载图片失败({value})")
|
||||
Try
|
||||
If value.StartsWithF(PathTemp) Then FileUtils.Delete(value)
|
||||
If value.StartsWithF(PathTemp, True) Then FileUtils.Delete(value)
|
||||
Catch
|
||||
End Try
|
||||
End Try
|
||||
|
||||
@@ -508,7 +508,8 @@ Public Class MyListItem
|
||||
RaiseEvent Click(sender, e)
|
||||
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()
|
||||
e.Handled = True
|
||||
End If
|
||||
|
||||
@@ -76,8 +76,7 @@ Public Class MyLoading
|
||||
If Ex Is Nothing Then
|
||||
LabText.Text = "未知错误"
|
||||
Else
|
||||
Ex = Ex.RootException()
|
||||
LabText.Text = If(Ex.IsBadNetwork(), "网络环境不佳,请稍后再试,或使用 VPN 改善网络环境", Ex.Message)
|
||||
LabText.Text = Ex.GetDisplay(False)
|
||||
End If
|
||||
Else
|
||||
LabText.Text = TextError
|
||||
|
||||
@@ -18,10 +18,6 @@ Public Class MyScrollViewer
|
||||
End If
|
||||
e.Handled = True
|
||||
PerformVerticalOffsetDelta(-e.Delta)
|
||||
'关闭 Tooltip (#2552)
|
||||
For Each TooltipBorder In Application.ShowingTooltips
|
||||
AniStart(AaOpacity(TooltipBorder, -1, 100), $"Hide Tooltip {GetUuid()}")
|
||||
Next
|
||||
End Sub
|
||||
Public Sub PerformVerticalOffsetDelta(Delta As Double)
|
||||
AniStart(
|
||||
|
||||
@@ -169,8 +169,8 @@
|
||||
<StackPanel HorizontalAlignment="Right" VerticalAlignment="Bottom" Grid.Row="1" Margin="15">
|
||||
<local:MyExtraButton x:Name="BtnExtraBack" HorizontalAlignment="Right" VerticalAlignment="Center" ToolTip="返回顶部" Visibility="Collapsed"
|
||||
Logo="M858.496 188.9024 173.1072 188.9024c-30.2848 0-54.8352-24.5504-54.8352-54.8352L118.272 106.6496c0-30.2848 24.5504-54.8352 54.8352-54.8352l685.3888 0c30.2848 0 54.8352 24.5504 54.8352 54.8352l0 27.4176C913.3312 164.352 888.7808 188.9024 858.496 188.9024L858.496 188.9024zM150.6048 550.8608c0 0 300.0064-240.3584 303.0272-243.328 13.9776-13.5936 31.1808-21.8624 48.8192-24.7552 1.7152-0.3072 3.4304-0.5888 5.1456-0.768 2.7392-0.3072 5.4528-0.3584 8.192-0.3328 2.7392-0.0256 5.4272 0.0256 8.1664 0.3328 1.7408 0.1792 3.4304 0.4864 5.1456 0.768 17.664 2.8928 34.8672 11.1616 48.8192 24.7552 3.0464 2.944 303.0016 243.328 303.0016 243.328 32.384 31.5136 29.6192 63.9744-2.7392 95.5136-32.3328 31.5392-75.648 2.9696-108.0064-28.544l-185.8816-147.1232 0 485.8368c0 30.3104-24.5248 54.8608-54.8352 54.8608l-27.392 0c-30.2848 0-54.8352-24.5504-54.8352-54.8608L447.232 470.7072l-185.8304 147.0976c-32.3584 31.5392-75.6992 60.1344-108.032 28.5696C121.0368 614.8352 118.272 582.3744 150.6048 550.8608L150.6048 550.8608zM150.6048 550.8608" />
|
||||
<local:MyExtraButton x:Name="BtnExtraDownload" HorizontalAlignment="Right" VerticalAlignment="Center" ToolTip="下载管理" Visibility="Collapsed"
|
||||
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" />
|
||||
<local:MyExtraButton x:Name="BtnExtraDownload" HorizontalAlignment="Right" VerticalAlignment="Center" ToolTip="任务管理" Visibility="Collapsed"
|
||||
LogoScale="1.1" Logo="M484.693 225.28 314.027 389.12l-68.267-68.267c-13.653-13.653-40.96-13.653-47.787 0-13.653 13.654-13.653 34.134 0 47.787l95.574 95.573c13.653 13.654 34.133 13.654 47.786 0L532.48 273.067c13.653-13.654 13.653-34.134 0-47.787s-34.133-13.653-47.787 0M819.2 341.333H614.4c-20.48 0-34.133 13.654-34.133 34.134S593.92 409.6 614.4 409.6h204.8c20.48 0 34.133-13.653 34.133-34.133s-13.653-34.134-34.133-34.134m0 341.334H614.4c-20.48 0-34.133 13.653-34.133 34.133s13.653 34.133 34.133 34.133h204.8c20.48 0 34.133-13.653 34.133-34.133s-13.653-34.133-34.133-34.133m-477.867-102.4c-75.093 0-136.533 61.44-136.533 136.533s61.44 136.533 136.533 136.533 136.534-61.44 136.534-136.533-61.44-136.533-136.534-136.533m0 204.8c-40.96 0-68.266-27.307-68.266-68.267s27.306-68.267 68.266-68.267S409.6 675.84 409.6 716.8s-27.307 68.267-68.267 68.267" />
|
||||
<local:MyExtraButton x:Name="BtnExtraApril" HorizontalAlignment="Right" VerticalAlignment="Center" ToolTip="投降" Visibility="Collapsed"
|
||||
Logo="M124.96 32c-28.64 0-60.064 23.2-60.064 51.904v856.192c0 28.576 30.688 51.904 59.296 51.904s60.064-23.328 60.064-51.904V83.904C184.256 55.2 153.536 32 124.96 32z m616.352 71.264c-168.416 0-168.416-69.024-336.8-69.024-97.216 0-172.256 45.344-172.256 45.344l-0.736 516.736s75.776-45.28 172.96-45.28c168.416 0 168.416 69.024 336.8 69.024 105.344 0 211.168-55.328 211.168-55.328V48.064c0.032 0-105.792 55.2-211.136 55.2z m0 0" />
|
||||
<local:MyExtraButton x:Name="BtnExtraShutdown" HorizontalAlignment="Right" VerticalAlignment="Center" ToolTip="关闭 Minecraft" Visibility="Collapsed"
|
||||
|
||||
@@ -11,6 +11,22 @@ Public Class FormMain
|
||||
Dim FeatureList As New List(Of KeyValuePair(Of Integer, String))
|
||||
'统计更新日志条目
|
||||
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
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:版本设置中,添加打开截图文件夹的快捷方式"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:导出整合包时,可以勾选导出光影设置文件"))
|
||||
FeatureCount += 23
|
||||
BugCount += 28
|
||||
End If
|
||||
If LastVersion < 406 Then 'Release 2.13.0.1
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "新增:重做 Java 管理与相关设置,允许调整 Java 优先级、指定 Java 版本范围等"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:导出整合包时允许自动导出版本文件夹中的 Java"))
|
||||
@@ -87,6 +103,27 @@ Public Class FormMain
|
||||
'3:BUG+ IMP* FEAT-
|
||||
'2:BUG* IMP-
|
||||
'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 = 409 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:更新 Mod 时可能更新成另一个 Mod 加载器的版本"))
|
||||
FeatureCount += 2
|
||||
BugCount += 6
|
||||
End If
|
||||
If LastVersion < 409 Then 'Snapshot 2.13.1.0
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:版本设置中,添加打开截图文件夹的快捷方式"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:导出整合包时,可以勾选导出光影设置文件"))
|
||||
FeatureCount += 21
|
||||
BugCount += 22
|
||||
End If
|
||||
If LastVersion < 407 Then 'Snapshot 2.13.0.1
|
||||
If LastVersion = 405 Then
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:使用部分主页预设时崩溃"))
|
||||
@@ -150,7 +187,7 @@ Public Class FormMain
|
||||
If LastVersion < 385 Then 'Snapshot 2.12.5
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复:无法下载 NeoForge 26.1"))
|
||||
If LastVersion = 384 Then
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复:下载可能失败,提示下载管理刷新线程出错"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复:下载可能失败,提示任务管理刷新线程出错"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复:无法保存选择的 Minecraft 文件夹"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:使用中文搜索 Mod 时,部分结果会忽略筛选条件"))
|
||||
End If
|
||||
@@ -786,8 +823,14 @@ Public Class FormMain
|
||||
' 在没有弹窗时:继续检查……
|
||||
'==========================
|
||||
|
||||
'按 ESC 返回上一级
|
||||
If e.Key = Key.Escape Then TriggerPageBack()
|
||||
'按 ESC 取消启动,或返回上一级
|
||||
If e.Key = Key.Escape Then
|
||||
If PageCurrent = FormMain.PageType.Launch AndAlso McLaunchLoader.State = LoadState.Loading Then
|
||||
FrmLaunchLeft.BtnCancel_Click()
|
||||
Return
|
||||
End If
|
||||
TriggerPageBack()
|
||||
End If
|
||||
'更改隐藏版本可见性
|
||||
If e.Key = Key.F11 AndAlso PageCurrent = FormMain.PageType.InstanceSelect Then
|
||||
FrmSelectRight.ShowHidden = Not FrmSelectRight.ShowHidden
|
||||
@@ -998,14 +1041,14 @@ Public Class FormMain
|
||||
If FilePathList.Count > 1 Then
|
||||
'必须要求全部为 jar 文件
|
||||
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)
|
||||
Return
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
'主页
|
||||
Dim Extension As String = FilePath.AfterLast(".").Lower
|
||||
Dim Extension As String = PathUtils.GetExtension(FilePath)
|
||||
If Extension = "xaml" Then
|
||||
Logger.Info("文件后缀为 XAML,作为主页加载")
|
||||
If FileUtils.Exists(Paths.Base & "PCL\Custom.xaml") Then
|
||||
@@ -1026,18 +1069,24 @@ Public Class FormMain
|
||||
If PageInstanceMod.InstallMods(PathList) Then Return
|
||||
'安装整合包
|
||||
If {"zip", "rar", "mrpack"}.Any(Function(t) t = Extension) Then '部分压缩包是 zip 格式但后缀为 rar,总之试一试
|
||||
Logger.Info("文件为压缩包,尝试作为整合包安装")
|
||||
Hint("正在读取压缩包……")
|
||||
Try
|
||||
Logger.Info("文件为压缩包,尝试作为整合包安装")
|
||||
ModpackInstall(FilePath)
|
||||
RunInUi(Sub() FrmMain.PageChange(FormMain.PageType.TaskManager))
|
||||
Return
|
||||
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
|
||||
'安装失败,继续往后尝试
|
||||
End Try
|
||||
End If
|
||||
'RAR 处理
|
||||
If Extension = "rar" Then
|
||||
Hint("PCL 无法处理 rar 格式的压缩包,请在解压后重新压缩为 zip 格式再试!")
|
||||
Hint("PCL 无法处理 rar 格式的压缩包,请在解压后重新压缩为 zip 格式再试!", HintType.Red)
|
||||
Return
|
||||
End If
|
||||
'错误报告分析
|
||||
@@ -1053,7 +1102,7 @@ Public Class FormMain
|
||||
Logger.Error(ex, "自主错误报告分析失败")
|
||||
End Try
|
||||
'未知操作
|
||||
Hint("PCL 无法确定应当执行的文件拖拽操作……")
|
||||
Hint("PCL 无法确定适合的操作……", HintType.Red)
|
||||
End Sub, "文件拖拽")
|
||||
End Sub
|
||||
|
||||
@@ -1157,9 +1206,9 @@ Public Class FormMain
|
||||
''' </summary>
|
||||
InstanceSelect = 5
|
||||
''' <summary>
|
||||
''' 下载管理。这是一个副页面。
|
||||
''' 任务管理。这是一个副页面。
|
||||
''' </summary>
|
||||
DownloadManager = 6
|
||||
TaskManager = 6
|
||||
''' <summary>
|
||||
''' 版本设置。这是一个副页面。
|
||||
''' </summary>
|
||||
@@ -1195,9 +1244,10 @@ Public Class FormMain
|
||||
OtherTest = 2
|
||||
InstanceOverall = 0
|
||||
InstanceSetup = 1
|
||||
InstanceMod = 2
|
||||
InstanceModDisabled = 3
|
||||
InstanceExport = 4
|
||||
InstanceModify = 2
|
||||
InstanceMod = 3
|
||||
InstanceModDisabled = 4
|
||||
InstanceExport = 5
|
||||
End Enum
|
||||
''' <summary>
|
||||
''' 获取次级页面的名称。若并非次级页面则返回空字符串,故可以以此判断是否为次级页面。
|
||||
@@ -1206,8 +1256,8 @@ Public Class FormMain
|
||||
Select Case Stack.Page
|
||||
Case PageType.InstanceSelect
|
||||
Return "版本选择"
|
||||
Case PageType.DownloadManager
|
||||
Return "下载管理"
|
||||
Case PageType.TaskManager
|
||||
Return "任务管理"
|
||||
Case PageType.InstanceSetup
|
||||
Return "版本设置 - " & If(PageInstanceLeft.Instance Is Nothing, "未知版本", PageInstanceLeft.Instance.Name)
|
||||
Case PageType.ResourceDetail
|
||||
@@ -1362,6 +1412,10 @@ Public Class FormMain
|
||||
''' </summary>
|
||||
Private Sub PageChangeActual(Stack As PageStackData, Optional SubType As PageSubType = -1)
|
||||
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
|
||||
Try
|
||||
|
||||
@@ -1429,7 +1483,7 @@ Public Class FormMain
|
||||
If FrmSelectLeft Is Nothing Then FrmSelectLeft = New PageSelectLeft
|
||||
If FrmSelectRight Is Nothing Then FrmSelectRight = New PageSelectRight
|
||||
PageChangeAnim(FrmSelectLeft, FrmSelectRight)
|
||||
Case PageType.DownloadManager '下载管理
|
||||
Case PageType.TaskManager '任务管理
|
||||
If FrmSpeedLeft Is Nothing Then FrmSpeedLeft = New PageSpeedLeft
|
||||
If FrmSpeedRight Is Nothing Then FrmSpeedRight = New PageSpeedRight
|
||||
PageChangeAnim(FrmSpeedLeft, FrmSpeedRight)
|
||||
@@ -1449,7 +1503,7 @@ Public Class FormMain
|
||||
BtnExtraApril.ShowRefresh()
|
||||
#End Region
|
||||
|
||||
Logger.Info($"切换主要页面:{Stack}, {SubType}")
|
||||
Logger.Info($"切换主要页面:{Stack.Page}, {SubType}")
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex, $"切换主要页面失败(ID {PageCurrent.Page})")
|
||||
Finally
|
||||
@@ -1461,8 +1515,8 @@ Public Class FormMain
|
||||
AniStop("PageLeft PageChange") '停止左边栏变更导致的右页面切换动画,防止它与本动画一起触发多次 PageOnEnter
|
||||
AniControlEnabled += 1
|
||||
'清除新页面关联性
|
||||
If Not IsNothing(TargetLeft.Parent) Then TargetLeft.SetValue(ContentPresenter.ContentProperty, Nothing)
|
||||
If Not IsNothing(TargetRight) AndAlso Not IsNothing(TargetRight.Parent) Then TargetRight.SetValue(ContentPresenter.ContentProperty, Nothing)
|
||||
If TargetLeft.Parent IsNot Nothing Then TargetLeft.SetValue(ContentPresenter.ContentProperty, Nothing)
|
||||
If TargetRight IsNot Nothing AndAlso TargetRight.Parent IsNot Nothing Then TargetRight.SetValue(ContentPresenter.ContentProperty, Nothing)
|
||||
PageLeft = TargetLeft
|
||||
PageRight = TargetRight
|
||||
'触发页面通用动画
|
||||
@@ -1603,12 +1657,12 @@ Public Class FormMain
|
||||
MusicControlNext()
|
||||
End Sub
|
||||
|
||||
'下载管理
|
||||
'任务管理
|
||||
Private Sub BtnExtraDownload_Click(sender As Object, e As EventArgs) Handles BtnExtraDownload.Click
|
||||
PageChange(PageType.DownloadManager)
|
||||
PageChange(PageType.TaskManager)
|
||||
End Sub
|
||||
Private Function BtnExtraDownload_ShowCheck() As Boolean
|
||||
Return HasDownloadingTask() AndAlso Not PageCurrent = PageType.DownloadManager
|
||||
Return HasDownloadingTask() AndAlso Not PageCurrent = PageType.TaskManager
|
||||
End Function
|
||||
|
||||
'投降
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -79,7 +79,7 @@ Public Module ModAnimation
|
||||
''' 已经执行的动画长度。如果为负数则为延迟。
|
||||
''' </summary>
|
||||
''' <remarks></remarks>
|
||||
Public TimeFinished As Integer
|
||||
Public TimeFinished As Double
|
||||
''' <summary>
|
||||
''' 已经完成的百分比。
|
||||
''' </summary>
|
||||
@@ -853,9 +853,8 @@ Sleeper:
|
||||
''' <summary>
|
||||
''' 动画定时器事件。
|
||||
''' </summary>
|
||||
Public Sub AniTimer(DeltaTick As Integer)
|
||||
Public Sub AniTimer(DeltaTick As Double)
|
||||
Try
|
||||
|
||||
If DeltaTick / AniSpeed > 100 Then Logger.Info($"两个动画帧间隔 {DeltaTick} ms")
|
||||
Dim i As Integer = -1
|
||||
'循环每个动画组
|
||||
@@ -1034,6 +1033,7 @@ NextAni:
|
||||
End If
|
||||
|
||||
Case AniType.Code
|
||||
Ani.TimeFinished = Ani.TimeTotal
|
||||
CType(Ani.Value, ThreadStart)()
|
||||
|
||||
Case AniType.ScaleTransform
|
||||
|
||||
@@ -6,12 +6,12 @@ Public Module ModBase
|
||||
#Region "声明"
|
||||
|
||||
'下列版本信息由更新器自动修改
|
||||
Public Const VersionBaseName As String = "2.13.0.1" '显示用版本名
|
||||
Public Const VersionBaseName As String = "2.99.0.0" '显示用版本名
|
||||
Public Const CommitHash As String = "" 'Commit Hash,由 GitHub Workflow 自动替换
|
||||
#If RELEASE Then
|
||||
Public Const VersionCode As Integer = 406 '正式版
|
||||
Public Const VersionCode As Integer = 413 '正式版
|
||||
#Else
|
||||
Public Const VersionCode As Integer = 407 '快照版
|
||||
Public Const VersionCode As Integer = 414 '快照版
|
||||
#End If
|
||||
|
||||
'版本信息
|
||||
@@ -47,7 +47,7 @@ Public Module ModBase
|
||||
''' <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>
|
||||
@@ -75,7 +75,7 @@ Public Module ModBase
|
||||
''' <summary>
|
||||
''' 系统盘盘符,以 \ 结尾。例如 “C:\”。
|
||||
''' </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
|
||||
Get
|
||||
@@ -465,57 +465,7 @@ Public Module ModBase
|
||||
|
||||
#Region "文件"
|
||||
|
||||
'=============================
|
||||
' 注册表
|
||||
'=============================
|
||||
|
||||
''' <summary>
|
||||
''' 读取注册表键。如果失败则返回默认值。
|
||||
''' </summary>
|
||||
Public Function ReadReg(Key As String, Optional DefaultValue As String = "") As String
|
||||
Try
|
||||
Return If(My.Computer.Registry.CurrentUser.OpenSubKey("Software\" & RegFolder, False)?.GetValue(Key), DefaultValue)
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex, $"读取注册表出错:{Key}", LogBehavior.Toast)
|
||||
Return DefaultValue
|
||||
End Try
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 写入注册表键。
|
||||
''' </summary>
|
||||
Public Sub WriteReg(Key As String, Value As String, Optional ThrowException As Boolean = False)
|
||||
Try
|
||||
Dim SubKey As Microsoft.Win32.RegistryKey = My.Computer.Registry.CurrentUser.OpenSubKey("Software\" & RegFolder, True)
|
||||
If SubKey Is Nothing Then SubKey = My.Computer.Registry.CurrentUser.CreateSubKey("Software\" & RegFolder) '如果不存在就创建
|
||||
SubKey.SetValue(Key, Value)
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, $"写入注册表出错:{Key}", If(ThrowException, LogBehavior.Toast, LogBehavior.ToastIfDebug))
|
||||
If ThrowException Then Throw
|
||||
End Try
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 是否存在某个注册表键。
|
||||
''' </summary>
|
||||
Public Function HasReg(Key As String) As Boolean
|
||||
Return ReadReg(Key, Nothing) IsNot Nothing
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 删除注册表键。
|
||||
''' </summary>
|
||||
Public Sub DeleteReg(Key As String, Optional ThrowException As Boolean = False)
|
||||
Try
|
||||
Dim SubKey As Microsoft.Win32.RegistryKey = My.Computer.Registry.CurrentUser.OpenSubKey("Software\" & RegFolder, True)
|
||||
If SubKey?.GetValue(Key) Is Nothing Then Return
|
||||
SubKey.DeleteValue(Key)
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, $"删除注册表出错:{Key}", If(ThrowException, LogBehavior.Toast, LogBehavior.ToastIfDebug))
|
||||
If ThrowException Then Throw
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
'=============================
|
||||
'ini
|
||||
'=============================
|
||||
|
||||
Private ReadOnly IniCache As New ConcurrentDictionary(Of String, ConcurrentDictionary(Of String, String))
|
||||
''' <summary>
|
||||
@@ -617,7 +567,8 @@ Public Module ModBase
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
'文件校验
|
||||
'权限检查
|
||||
|
||||
''' <summary>
|
||||
''' 检查是否拥有某一文件夹的 I/O 权限。如果文件夹不存在,会返回 False。
|
||||
''' </summary>
|
||||
@@ -625,7 +576,7 @@ Public Module ModBase
|
||||
Try
|
||||
If String.IsNullOrEmpty(Folder) Then Return False
|
||||
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
|
||||
Dim TestFilePath As String = $"{Folder}CheckPermission{GetUuid()}.txt"
|
||||
FileUtils.Write(TestFilePath, "临时文件,用于检测该文件夹的权限是否正常。")
|
||||
@@ -642,7 +593,7 @@ Public Module ModBase
|
||||
Public Sub CheckPermissionWithException(Folder As String)
|
||||
If String.IsNullOrWhiteSpace(Folder) Then Throw New ArgumentNullException("文件夹名不能为空!")
|
||||
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("文件夹不存在!")
|
||||
Dim TestFilePath As String = $"{Folder}CheckPermission{GetUuid()}.txt"
|
||||
FileUtils.Write(TestFilePath, "临时文件,用于检测该文件夹的权限是否正常。")
|
||||
@@ -837,8 +788,8 @@ Public Module ModBase
|
||||
''' <summary>
|
||||
''' 可用于临时存放文件的,不含任何特殊字符的文件夹路径,以“\”结尾。
|
||||
''' </summary>
|
||||
Public PathPure As String = GetPureASCIIDir()
|
||||
Private Function GetPureASCIIDir() As String
|
||||
Public PathPure As New Lazy(Of String)(
|
||||
Function()
|
||||
If Paths.Base.IsAsciiOnly() Then
|
||||
Return Paths.Base & "PCL\"
|
||||
ElseIf Paths.AppDataThenName.IsAsciiOnly() Then
|
||||
@@ -848,7 +799,7 @@ Public Module ModBase
|
||||
Else
|
||||
Return OsDrive & "ProgramData\PCL\"
|
||||
End If
|
||||
End Function
|
||||
End Function)
|
||||
|
||||
''' <summary>
|
||||
''' 指示接取到这个异常的函数进行重试。
|
||||
@@ -894,6 +845,12 @@ Public Module ModBase
|
||||
Return (Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 获取十进制 Unix 时间戳。
|
||||
''' </summary>
|
||||
Public Function ToUnixTimestampUtc(Time As Date) As Long
|
||||
Return (Time.ToUniversalTime().Ticks - 621355968000000000) / 10000000
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 时间戳转化为日期。
|
||||
''' </summary>
|
||||
Public Function GetDate(timeStamp As Integer) As Date
|
||||
@@ -1076,7 +1033,7 @@ Public Module ModBase
|
||||
Try
|
||||
Retrier.Attempt(delay:=Function(Attempt) TimeSpan.FromMilliseconds(200), maxAttempts:=4, isRetryAllowed:=Function(ex) True, action:=
|
||||
Sub()
|
||||
RunInUi(
|
||||
RunInUiWait(
|
||||
Sub()
|
||||
My.Computer.Clipboard.Clear()
|
||||
If Not String.IsNullOrEmpty(Text) Then My.Computer.Clipboard.SetText(Text)
|
||||
|
||||
@@ -217,8 +217,7 @@ Retry:
|
||||
Optional Content As Object = Nothing, Optional Headers As Dictionary(Of String, String) = Nothing,
|
||||
Optional SimulateBrowserHeaders As Boolean = False, Optional Timeout As Integer = 25000, Optional Encoding As Encoding = Nothing) As Byte()
|
||||
If RunInUi() AndAlso Not Url.Contains("//127.") Then Throw New Exception("在 UI 线程执行了网络请求")
|
||||
Dim Request As HttpRequestMessage = Nothing, CancelToken As CancellationTokenSource = Nothing,
|
||||
Response As HttpResponseMessage = Nothing, HostIp As String = Nothing
|
||||
Dim Request As HttpRequestMessage = Nothing, CancelToken As CancellationTokenSource = Nothing, Response As HttpResponseMessage = Nothing
|
||||
Try
|
||||
'构建 RequestClient
|
||||
Directory.CreateDirectory(PathTemp & "Cache\Http\")
|
||||
@@ -255,8 +254,6 @@ Retry:
|
||||
Retrier.Attempt(delay:=Function() TimeSpan.FromMilliseconds(200), isRetryAllowed:=Function(ex) TypeOf ex Is FileNotFoundException OrElse TypeOf ex Is IOException, action:=
|
||||
Sub(Attempt)
|
||||
CancelToken?.Dispose() : CancelToken = New CancellationTokenSource(Timeout)
|
||||
HostIp = DNSLookup(Request, CancelToken.Token)
|
||||
If HostIp IsNot Nothing Then IPReliability.GetOrAdd(HostIp, -0.01) '预先降低一点,这样快速的重复请求会使用不同的 IP 以提高成功率
|
||||
Dim ClonedRequest As HttpRequestMessage = Request.Clone() 'HttpRequestMessage 只能发送一次,重试时需要克隆一个新的
|
||||
Try
|
||||
Response = RequestClient.SendAsync(ClonedRequest, HttpCompletionOption.ResponseHeadersRead, CancelToken.Token).GetResultWithTimeout(CancelToken, Timeout)
|
||||
@@ -274,26 +271,22 @@ Retry:
|
||||
End Using
|
||||
End Using
|
||||
If Not Response.IsSuccessStatusCode Then
|
||||
RecordIPReliability(HostIp, -0.7)
|
||||
Dim ResponseMessage = ResponseBytes.GetString(Encoding)
|
||||
Throw New HttpRequestCodeException(
|
||||
$"错误码 {Response.StatusCode} ({CInt(Response.StatusCode)}),{Method},{Url},{HostIp}" &
|
||||
$"错误码 {Response.StatusCode} ({CInt(Response.StatusCode)}),{Method},{Url}" &
|
||||
If(String.IsNullOrEmpty(ResponseMessage), "", vbCrLf & ResponseMessage), Response.StatusCode, ResponseMessage)
|
||||
End If
|
||||
'输出
|
||||
RecordIPReliability(HostIp, 0.5)
|
||||
Return ResponseBytes
|
||||
Catch ex As HttpRequestException
|
||||
Throw
|
||||
Catch ex As TimeoutException
|
||||
Throw New WebException($"连接服务器超时,请稍后再试,或使用 VPN 改善网络环境({Method}, {Url},IP:{HostIp})", WebExceptionStatus.Timeout)
|
||||
Throw New WebException($"连接服务器超时,请稍后再试,或使用 VPN 改善网络环境({Method}, {Url})", WebExceptionStatus.Timeout)
|
||||
Catch ex As Exception
|
||||
ex.ThrowIfCanceled()
|
||||
RecordIPReliability(HostIp, -1)
|
||||
If TypeOf ex IsNot TaskCanceledException Then ex.ThrowIfCanceled() 'GetResultWithTimeout 会抛出 TaskCanceledException
|
||||
If ex.IsBadNetwork Then
|
||||
Throw New WebException($"网络请求失败,请稍后再试,或使用 VPN 改善网络环境({Method}, {Url},IP:{HostIp})", WebExceptionStatus.Timeout)
|
||||
Throw New WebException($"网络请求失败,请稍后再试,或使用 VPN 改善网络环境({Method}, {Url})", WebExceptionStatus.Timeout)
|
||||
Else
|
||||
Throw New Exception($"网络请求出现意外异常({Method}, {Url},{HostIp})", ex)
|
||||
Throw New Exception($"网络请求出现意外异常({Method}, {Url})", ex)
|
||||
End If
|
||||
Finally
|
||||
Request?.Dispose()
|
||||
@@ -304,72 +297,6 @@ Retry:
|
||||
Private RequestClient As HttpClient = Nothing
|
||||
Private RequestClientLock As New Object
|
||||
|
||||
''' <summary>
|
||||
''' 进行 DNS 解析。它仅在选择的 IP 与系统默认的不一致时才对 URL 中的 Host 进行替换。
|
||||
''' 返回请求时应使用的 IP。若解析失败,则返回 Nothing。
|
||||
''' </summary>
|
||||
Private Function DNSLookup(Request As HttpRequestMessage, CancelToken As CancellationToken) As String
|
||||
Dim Host = Request.RequestUri.Host
|
||||
'不对部分有严格 SNI 限制的域名进行 DNS 解析(#8295)
|
||||
If {"mojang.com", "minecraft.net", "minecraftservices.com"}.Any(Function(h) Host.ContainsIgnoreCase(h)) Then Return Nothing
|
||||
'若一分钟内已失败过,则不再重复尝试解析,以减少断网时的 Exception 数量
|
||||
Dim LastFailureTime As Date
|
||||
If DNSFailureRecord.TryGetValue(Host, LastFailureTime) Then
|
||||
If Date.Now - LastFailureTime < New TimeSpan(0, 1, 0) Then
|
||||
Return Nothing
|
||||
Else
|
||||
DNSFailureRecord.Remove(Host)
|
||||
End If
|
||||
End If
|
||||
'初步 DNS 解析
|
||||
Dim Candidates As IPAddress()
|
||||
Try
|
||||
Dim AsyncResult = Dns.BeginGetHostAddresses(Host, Nothing, Nothing)
|
||||
If WaitHandle.WaitAny(New WaitHandle() {AsyncResult.AsyncWaitHandle, CancelToken.WaitHandle}) Then Throw New TimeoutException("DNS 解析超时")
|
||||
Candidates = Dns.EndGetHostAddresses(AsyncResult).Distinct.ToArray
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, $"DNS 解析失败({Host})")
|
||||
DNSFailureRecord(Host) = Date.Now
|
||||
Return Nothing
|
||||
End Try
|
||||
If Not Candidates.Any Then
|
||||
Logger.Info($"DNS 解析无结果({Host})")
|
||||
Return Nothing
|
||||
End If
|
||||
'若同时存在 IPv4 和 IPv6 地址,仅选择其中一类(因为 GFW 可能只屏蔽了 IPv4 或 IPv6)
|
||||
Dim Reliabilities = Candidates.ToDictionary(Function(i) i, Function(i) IPReliability.GetOrDefault(i.ToString, 0))
|
||||
Dim IPv4Targets = Candidates.Where(Function(i) i.AddressFamily = AddressFamily.InterNetwork).ToArray
|
||||
Dim IPv6Targets = Candidates.Where(Function(i) i.AddressFamily = AddressFamily.InterNetworkV6).ToArray
|
||||
If IPv4Targets.Any AndAlso IPv6Targets.Any Then
|
||||
Dim IPv4Reliability = IPv4Targets.Max(Function(i) Reliabilities(i))
|
||||
Dim IPv6Reliability = IPv6Targets.Max(Function(i) Reliabilities(i))
|
||||
If Host = "api.modrinth.com" Then IPv6Reliability -= 0.1 '让 Modrinth 优先使用 IPv4 地址(#6887)
|
||||
Logger.Trace(Function() $"DNS IPv4/IPv6 选择({Host}),IPv4 {IPv4Reliability:0.000},IPv6 {IPv6Reliability:0.000}")
|
||||
Candidates = If(IPv4Reliability >= IPv6Reliability, IPv4Targets, IPv6Targets)
|
||||
End If
|
||||
'选择可靠度最高的 IP
|
||||
Dim Target As IPAddress = Candidates.MaxBy(Function(i) Reliabilities(i))
|
||||
Request.Headers.Host = Request.RequestUri.Authority
|
||||
Request.RequestUri = (New UriBuilder(Request.RequestUri) With {.Host = If(Target.AddressFamily = AddressFamily.InterNetworkV6, $"[{Target}]", Target.ToString)}).Uri
|
||||
Return Target.ToString
|
||||
End Function
|
||||
Private DNSFailureRecord As New ConcurrentDictionary(Of String, Date)
|
||||
''' <summary>
|
||||
''' 记录每个 IP 地址的请求可靠度。
|
||||
''' 通常取值 -1 ~ +0.5,越高越好。未尝试过的 IP 应视为 0。
|
||||
''' </summary>
|
||||
Private IPReliability As New ConcurrentDictionary(Of String, Double)
|
||||
''' <summary>
|
||||
''' 根据请求结果,记录 IP 地址的可靠度。
|
||||
''' </summary>
|
||||
Private Sub RecordIPReliability(IP As String, Result As Double)
|
||||
If IP Is Nothing Then Return
|
||||
IPReliability.AddOrUpdate(IP,
|
||||
Function() Result * 0.5,
|
||||
Function(k, v) v * 0.5 + Result * 0.5
|
||||
)
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "多线程下载引擎"
|
||||
@@ -976,7 +903,7 @@ StartThread:
|
||||
Logger.Log($"{LocalName}:开始,起始点 {Th.DownloadStart},{Th.Source.Url}", If(Th.DownloadStart = 0, LogLevel.Info, LogLevel.Trace))
|
||||
Dim ResultStream As Stream = Nothing, HttpRequest As HttpRequestMessage = Nothing,
|
||||
Response As HttpResponseMessage = Nothing, ResponseStream As Stream = Nothing,
|
||||
CancelToken As CancellationTokenSource = Nothing, HostIp As String = Nothing
|
||||
CancelToken As CancellationTokenSource = Nothing
|
||||
'部分下载源真的特别慢,并且只需要一个请求,例如 Ping 为 20s,如果增长太慢,就会造成类似 2.5s 5s 7.5s 10s 12.5s... 的极大延迟
|
||||
'延迟过长会导致某些特别慢的链接迟迟不被掐死
|
||||
Dim Timeout As Integer = Math.Min(Math.Max(ConnectAverage, 15000) * (1 + Th.Source.FailCount), 30000)
|
||||
@@ -987,7 +914,6 @@ StartThread:
|
||||
HttpRequest = New HttpRequestMessage(HttpMethod.Get, Th.Source.Url)
|
||||
SecretHeadersSign(Th.Source.Url, HttpRequest, SimulateBrowserHeaders)
|
||||
CancelToken = New CancellationTokenSource(Timeout)
|
||||
HostIp = DNSLookup(HttpRequest, CancelToken.Token) 'DNS 预解析
|
||||
If Not Th.IsFirstThread Then HttpRequest.Headers.Range = New Headers.RangeHeaderValue(Th.DownloadStart, Nothing)
|
||||
Dim ContentLength As Long = 0
|
||||
Response = ThreadClient.SendAsync(HttpRequest, HttpCompletionOption.ResponseHeadersRead, CancelToken.Token).GetResultWithTimeout(CancelToken, Timeout)
|
||||
@@ -1119,15 +1045,13 @@ SourceBreak:
|
||||
'本线程完成
|
||||
Th.State = NetState.Finished
|
||||
Logger.Trace(Function() $"{LocalName}:完成,已下载 {Th.DownloadDone}({Th.DownloadStart}~{Th.DownloadEnd})")
|
||||
RecordIPReliability(HostIp, 0.5)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
If ex.IsBadNetwork OrElse TypeOf ex Is TaskCanceledException Then
|
||||
Logger.Info(New TimeoutException($"{LocalName}:超时({Timeout}ms),{ex.Message},IP:{If(String.IsNullOrEmpty(HostIp), "自动", "")}"))
|
||||
Logger.Info(New TimeoutException($"{LocalName}:超时({Timeout}ms),{ex.Message}"))
|
||||
Else
|
||||
Logger.Info(ex, $"{LocalName}:出错,IP:{HostIp}")
|
||||
Logger.Info(ex, $"{LocalName}:出错")
|
||||
End If
|
||||
RecordIPReliability(HostIp, -0.7)
|
||||
SourceFail(Th, ex, False)
|
||||
Finally
|
||||
'释放资源
|
||||
@@ -1423,8 +1347,8 @@ Retry:
|
||||
End Get
|
||||
Set(value As Integer)
|
||||
_FailCount = value
|
||||
If State = LoadState.Loading AndAlso value >= Math.Min(10000, Math.Max(FileRemain * 5.5, NetTaskThreadLimit * 5.5 + 3)) Then
|
||||
Logger.Warn($"由于同加载器中失败次数过多引发强制失败:连续失败了 {value} 次")
|
||||
If State = LoadState.Loading AndAlso value >= Math.Min(10000, Math.Max(FileRemain * 8, NetTaskThreadLimit * 8 + 3)) Then
|
||||
Logger.Error($"由于同加载器中失败次数过多引发强制失败:连续失败了 {value} 次", LogBehavior.ToastIfDebug)
|
||||
On Error Resume Next
|
||||
Dim ExList As New List(Of Exception)
|
||||
For Each File In Files
|
||||
@@ -1472,7 +1396,7 @@ FinishExCatch:
|
||||
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)))
|
||||
'去重
|
||||
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
|
||||
FileRemain += Files.Where(Function(f) f.State <> NetState.Finished).Count
|
||||
@@ -1497,7 +1421,7 @@ FinishExCatch:
|
||||
Next
|
||||
If Not File.HasAvailableSource() Then Throw New ArgumentException("输入的下载链接不正确!")
|
||||
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)
|
||||
DirectoryUtils.Create(PathUtils.RemoveLastPart(File.LocalPath)) '创建目标文件夹
|
||||
Next
|
||||
@@ -1595,7 +1519,7 @@ FinishExCatch:
|
||||
Select(Function(FolderName) (FolderName, File.LocalPath.IndexOfF(FolderName, True))).
|
||||
Where(Function(kv) kv.Item2 >= 0).ToList
|
||||
If Not TypeIndexes.Any Then
|
||||
If File.LocalName.EndsWithF(".jar") Then
|
||||
If PathUtils.GetExtension(File.LocalName) = "jar" Then
|
||||
TypeIndexes.Add(("\versions\", 1)) '总是对 jar 进行版本文件检查,以包括另存为 jar 的情况
|
||||
Else
|
||||
Return Nothing
|
||||
@@ -1623,7 +1547,7 @@ FinishExCatch:
|
||||
Case "versions\"
|
||||
'版本 jar 或 json:查找 MC 文件夹下的各个版本文件夹
|
||||
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
|
||||
Next
|
||||
Next
|
||||
@@ -1917,7 +1841,7 @@ FinishExCatch:
|
||||
End Class
|
||||
|
||||
''' <summary>
|
||||
''' 是否有正在进行中、需要在下载管理页面显示的下载任务?
|
||||
''' 是否有正在进行中、需要在任务管理页面显示的下载任务?
|
||||
''' </summary>
|
||||
Public Function HasDownloadingTask(Optional IgnoreCustomDownload As Boolean = False) As Boolean
|
||||
Return LoaderTaskbar.Any(
|
||||
|
||||
@@ -260,16 +260,16 @@ Public Class ValidateFolderName
|
||||
Inherits Validate
|
||||
Public Property Folder As String
|
||||
Public Property UseMinecraftCharCheck As Boolean = True
|
||||
Public Property IgnoreCase As Boolean = True
|
||||
Private ReadOnly PathIgnore As IEnumerable(Of String)
|
||||
Private ReadOnly IgnoreList As List(Of String)
|
||||
Public Sub New()
|
||||
End Sub
|
||||
Public Sub New(Folder As String, Optional UseMinecraftCharCheck As Boolean = True, Optional IgnoreCase As Boolean = True)
|
||||
Public Sub New(Folder As String, Optional UseMinecraftCharCheck As Boolean = True, Optional IgnoreList As List(Of String) = Nothing)
|
||||
Me.Folder = Folder
|
||||
Me.IgnoreCase = IgnoreCase
|
||||
Me.UseMinecraftCharCheck = UseMinecraftCharCheck
|
||||
On Error Resume Next
|
||||
PathIgnore = DirectoryUtils.EnumerateDirectories(Folder)
|
||||
Me.IgnoreList = IgnoreList
|
||||
End Sub
|
||||
Public Overrides Function Validate(Str As String) As String
|
||||
Try
|
||||
@@ -294,12 +294,14 @@ Public Class ValidateFolderName
|
||||
If Str.RegexCheck(".{2,}~\d") Then Return "文件夹名不能包含这一特殊格式!"
|
||||
'检查文件夹重名
|
||||
Dim Arr As New List(Of String)
|
||||
Dim folderName As String
|
||||
If PathIgnore IsNot Nothing Then
|
||||
For Each FolderPath In PathIgnore
|
||||
Arr.Add(PathUtils.GetLastPart(FolderPath))
|
||||
folderName = PathUtils.GetLastPart(FolderPath)
|
||||
If IgnoreList Is Nothing OrElse Not IgnoreList.Contains(folderName, StringComparer.OrdinalIgnoreCase) Then Arr.Add(folderName)
|
||||
Next
|
||||
End If
|
||||
Dim SameNameCheck = New ValidateExceptSame(Arr, "不可与现有文件夹重名!", IgnoreCase).Validate(Str)
|
||||
Dim SameNameCheck = New ValidateExceptSame(Arr, "不可与现有文件夹重名!", True).Validate(Str)
|
||||
If Not SameNameCheck = "" Then Return SameNameCheck
|
||||
Return ""
|
||||
Catch ex As Exception
|
||||
@@ -380,13 +382,16 @@ Public Class ValidateFolderPath
|
||||
Public Overrides Function Validate(Str As String) As String
|
||||
'去除尾部斜线,统一为 \
|
||||
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)
|
||||
If LengthCheck <> "" Then Return LengthCheck
|
||||
'检查长度
|
||||
LengthCheck = New ValidateLength(1, 254).Validate(Str)
|
||||
If LengthCheck <> "" Then Return LengthCheck
|
||||
'检查 NTFS 8.3 文件名(#4505)
|
||||
Dim Match = Str.RegexSeek(".{2,}~\d")
|
||||
If Match IsNot Nothing Then Return $"文件夹名不能包含 {Match} 这一特殊格式!"
|
||||
'检查开头
|
||||
If Str.StartsWithF("\\Mac\") Then GoTo Fin
|
||||
For Each Drive In Environment.GetLogicalDrives().Where(Function(p) DirectoryUtils.Exists(p))
|
||||
@@ -413,8 +418,6 @@ Fin:
|
||||
'检查特殊字符串
|
||||
Dim InvalidStrCheck As String = New ValidateExceptSame({"CON", "PRN", "AUX", "CLOCK$", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"}, "文件夹名不可为 %!").Validate(SubStr)
|
||||
If Not InvalidStrCheck = "" Then Return InvalidStrCheck
|
||||
'检查 NTFS 8.3 文件名(#4505)
|
||||
If Str.RegexCheck(".{2,}~\d") Then Return "文件夹名不能包含这一特殊格式!"
|
||||
Next
|
||||
Return ""
|
||||
End Function
|
||||
|
||||
@@ -92,7 +92,7 @@ Public Class CrashAnalyzer
|
||||
'尝试视作压缩包解压
|
||||
Try
|
||||
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\")
|
||||
Logger.Info($"已解压导入的日志文件:{FilePath}")
|
||||
GoTo Extracted
|
||||
@@ -163,9 +163,9 @@ Extracted:
|
||||
Else
|
||||
TargetType = AnalyzeFileType.ExtraLogFile
|
||||
End If
|
||||
ElseIf MatchName.EndsWithF(".log", True) Then
|
||||
ElseIf PathUtils.GetExtension(MatchName) = "log" Then
|
||||
TargetType = AnalyzeFileType.ExtraLogFile
|
||||
ElseIf MatchName.EndsWithF(".txt", True) Then
|
||||
ElseIf PathUtils.GetExtension(MatchName) = "txt" Then
|
||||
TargetType = AnalyzeFileType.ExtraReportFile
|
||||
Else
|
||||
Logger.Info($"{MatchName} 分类为 Ignore")
|
||||
@@ -514,7 +514,6 @@ Done:
|
||||
If LogMc.Contains("signer information does not match signer information of other classes in the same package") Then AppendReason(CrashReason.文件或内容校验失败, If(LogMc.RegexSeek("(?<=class "")[^']+(?=""'s signer information)"), "").TrimEnd(vbCrLf))
|
||||
If LogMc.Contains("Maybe try a lower resolution resourcepack?") Then AppendReason(CrashReason.材质过大或显卡配置不足)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: net.minecraft.world.server.ChunkManager$ProxyTicketManager.shouldForceTicks(J)Z") AndAlso LogMc.Contains("OptiFine") Then AppendReason(CrashReason.OptiFine导致无法加载世界)
|
||||
If LogMc.Contains("Unsupported class file major version") Then AppendReason(CrashReason.Java版本不兼容)
|
||||
If LogMc.Contains("com.electronwill.nightconfig.core.io.ParsingException: Not enough data available") AndAlso Not CrashReasons.ContainsKey(CrashReason.Mod配置文件导致游戏崩溃) Then AppendReason(CrashReason.NightConfig的Bug)
|
||||
If LogMc.Contains("Cannot find launch target fmlclient, unable to launch") Then AppendReason(CrashReason.Forge安装不完整)
|
||||
If LogMc.Contains("Invalid paths argument, contained no existing paths") AndAlso LogMc.Contains("libraries\net\minecraftforge\fmlcore") Then AppendReason(CrashReason.Forge安装不完整)
|
||||
@@ -522,7 +521,9 @@ Done:
|
||||
If LogMc.Contains("has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to") Then AppendReason(CrashReason.Mod需要Java11)
|
||||
If LogMc.Contains("java.lang.RuntimeException: java.lang.NoSuchMethodException: no such method: sun.misc.Unsafe.defineAnonymousClass(Class,byte[],Object[])Class/invokeVirtual") Then AppendReason(CrashReason.Mod需要Java11)
|
||||
If LogMc.Contains("java.lang.IllegalArgumentException: The requested compatibility level JAVA_11 could not be set. Level is not supported by the active JRE or ASM version") Then AppendReason(CrashReason.Mod需要Java11)
|
||||
If LogMc.Contains("Unsupported class file major version") Then AppendReason(CrashReason.Java版本不兼容)
|
||||
If LogMc.Contains("Unsupported major.minor version") Then AppendReason(CrashReason.Java版本不兼容)
|
||||
If LogMc.Contains("Level is not supported by the active JRE or ASM version") Then AppendReason(CrashReason.Java版本不兼容)
|
||||
If LogMc.Contains("Invalid maximum heap size") Then AppendReason(CrashReason.使用32位Java导致JVM无法分配足够多的内存)
|
||||
If LogMc.Contains("Could not reserve enough space") Then
|
||||
If LogMc.Contains("for 1048576KB object heap") Then
|
||||
|
||||
@@ -65,7 +65,7 @@ Public Module ModDownload
|
||||
|
||||
#Region "下载资源文件"
|
||||
If ShouldIgnoreFileCheck(Instance) Then
|
||||
Logger.Info("已跳过所有 Assets 检查")
|
||||
Logger.Warn("设置要求跳过所有 Assets 检查,文件可能不是最新的", LogBehavior.None)
|
||||
Else
|
||||
Dim LoadersAssets As New List(Of LoaderBase)
|
||||
'获取资源文件索引地址
|
||||
@@ -78,13 +78,23 @@ Public Module ModDownload
|
||||
If IndexFile Is Nothing Then
|
||||
Task.Output = New List(Of NetFile)
|
||||
Logger.Warn($"未找到版本 {Instance.Name} 的合适的资源索引下载地址,游戏 assets 可能缺失")
|
||||
ElseIf DownloadAssetIndexInBackground Then
|
||||
Return
|
||||
End If
|
||||
Dim IndexBackgroundFile = PathTemp & "Cache\" & IndexFile.LocalName & "_background"
|
||||
If FileUtils.Exists(IndexBackgroundFile) AndAlso
|
||||
FileUtils.GetInfo(IndexBackgroundFile).LastWriteTime.Date > FileUtils.GetInfo(IndexFile.LocalPath).LastWriteTime.Date Then
|
||||
Logger.Info($"发现后台更新的资源索引缓存文件:{IndexBackgroundFile}")
|
||||
FileUtils.Move(IndexBackgroundFile, IndexFile.LocalPath)
|
||||
End If
|
||||
If DownloadAssetIndexInBackground Then
|
||||
'允许后台更新索引
|
||||
If IndexFile.Check.Check(IndexFile.LocalPath) Is Nothing Then
|
||||
Task.Output = New List(Of NetFile)
|
||||
Else
|
||||
Task.Output = New List(Of NetFile) From {IndexFile}
|
||||
End If
|
||||
Else '强制要求重新下载
|
||||
Else
|
||||
'强制要求重新下载
|
||||
RealAddressMain = IndexFile.LocalPath
|
||||
TempAddressMain = PathTemp & "Cache\" & IndexFile.LocalName
|
||||
IndexFile.LocalPath = TempAddressMain
|
||||
@@ -114,28 +124,24 @@ Public Module ModDownload
|
||||
Dim LoadersAssetsUpdate As New List(Of LoaderBase)
|
||||
LoadersAssetsUpdate.Add(New LoaderTask(Of String, List(Of NetFile))("后台分析资源文件索引地址",
|
||||
Sub(Task As LoaderTask(Of String, List(Of NetFile)))
|
||||
Dim BackAssetsFile As NetFile = DlClientAssetIndexGet(Instance)
|
||||
If BackAssetsFile Is Nothing Then
|
||||
Dim IndexFile As NetFile = DlClientAssetIndexGet(Instance)
|
||||
If IndexFile Is Nothing Then
|
||||
Logger.Warn($"未找到版本 {Instance.Name} 的合适的资源索引下载地址,游戏 assets 可能缺失")
|
||||
Task.Cancel()
|
||||
Throw New OperationCanceledException
|
||||
End If
|
||||
RealAddressBg = BackAssetsFile.LocalPath
|
||||
TempAddressBg = PathTemp & "Cache\" & BackAssetsFile.LocalName & "_background"
|
||||
BackAssetsFile.LocalPath = TempAddressBg
|
||||
Task.Output = New List(Of NetFile) From {BackAssetsFile}
|
||||
RealAddressBg = IndexFile.LocalPath
|
||||
TempAddressBg = PathTemp & "Cache\" & IndexFile.LocalName & "_background"
|
||||
IndexFile.LocalPath = TempAddressBg
|
||||
Task.Output = New List(Of NetFile) From {IndexFile}
|
||||
'检查是否需要更新:每天只更新一次
|
||||
If FileUtils.Exists(RealAddressBg) AndAlso Math.Abs((FileUtils.GetInfo(RealAddressBg).LastWriteTime.Date - Now.Date).TotalDays) < 1 Then
|
||||
Logger.Info("无需更新资源文件索引,取消")
|
||||
Dim LastUpdate As Date = FileUtils.GetInfo(RealAddressBg).LastWriteTime.Date
|
||||
If FileUtils.Exists(RealAddressBg) AndAlso Math.Abs((LastUpdate - Now.Date).TotalDays) < 1 Then
|
||||
Logger.Info($"无需更新资源文件索引,上次更新于 {LastUpdate}")
|
||||
Task.Cancel()
|
||||
End If
|
||||
End Sub))
|
||||
LoadersAssetsUpdate.Add(New LoaderDownload("后台下载资源文件索引", New List(Of NetFile)))
|
||||
LoadersAssetsUpdate.Add(New LoaderTask(Of List(Of NetFile), String)("后台复制资源文件索引",
|
||||
Sub(Task As LoaderTask(Of List(Of NetFile), String))
|
||||
FileUtils.Move(TempAddressBg, RealAddressBg)
|
||||
McLaunchLog("后台更新资源文件索引成功:" & TempAddressBg)
|
||||
End Sub))
|
||||
Dim Updater As New LoaderCombo(Of String)("后台更新资源文件索引", LoadersAssetsUpdate)
|
||||
Logger.Info("开始后台检查资源文件索引")
|
||||
Updater.Start()
|
||||
@@ -236,8 +242,6 @@ Public Module ModDownload
|
||||
DlPreferMojang = DeltaTime < 4000
|
||||
Logger.Info($"Mojang 官方源加载耗时:{DeltaTime}ms,{If(DlPreferMojang, "可优先使用官方源", "不优先使用官方源")}")
|
||||
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}
|
||||
'MC 更新提示
|
||||
@@ -432,7 +436,7 @@ Public Module ModDownload
|
||||
.FileName = If(Name(i).ContainsIgnoreCase("pre"), "preview_", "") & "OptiFine_" & Name(i).Replace(" ", "_") & ".jar",
|
||||
.RequiredForgeVersion = Forge(i).Replace("Forge ", "").Replace("#", "")}
|
||||
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)
|
||||
Next
|
||||
Loader.Output = New DlOptiFineListResult With {.IsOfficial = True, .SourceName = "OptiFine 官方源", .Value = Versions}
|
||||
@@ -575,7 +579,7 @@ Public Module ModDownload
|
||||
If Version <> other.Version Then
|
||||
Return Version.CompareTo(other.Version)
|
||||
Else
|
||||
Return CompareVersion(VersionName, other.VersionName)
|
||||
Return McUtils.CompareVersion(VersionName, other.VersionName)
|
||||
End If
|
||||
End Function
|
||||
End Class
|
||||
@@ -1193,15 +1197,15 @@ Public Module ModDownload
|
||||
Urls.Add((Url, If(Url.Contains("modrinth"), 20, 10)))
|
||||
Urls.Add((Url, 30))
|
||||
End If
|
||||
Dim Exs As String = ""
|
||||
Dim Errors As New List(Of Exception)
|
||||
For Each Source In Urls
|
||||
Try
|
||||
Return NetRequestByClient(Source.Url, Method, Content, ContentType, Timeout:=Source.Timeout * 1000, Encoding:=Encoding.UTF8, RequireJson:=True).DeserializeJson()
|
||||
Catch ex As Exception
|
||||
Exs += ex.Message + vbCrLf
|
||||
Errors.Add(ex)
|
||||
End Try
|
||||
Next
|
||||
Throw New Exception(Exs)
|
||||
Throw New AggregateException(Errors)
|
||||
End Function
|
||||
|
||||
#End Region
|
||||
|
||||
@@ -18,7 +18,7 @@ Public Module ModJava
|
||||
'强制选择的 Java
|
||||
Dim SelectedJavaSetting = Settings.Get(Of String)("LaunchArgumentJavaSelect")
|
||||
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
|
||||
OldJavaList.Remove(Match)
|
||||
OldJavaList.Insert(0, Match)
|
||||
@@ -78,7 +78,7 @@ Public Module ModJava
|
||||
Return
|
||||
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(
|
||||
Sub(ByRef List)
|
||||
@@ -101,11 +101,12 @@ Public Module ModJava
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 手动触发搜索 Java。
|
||||
''' 手动触发搜索 Java,强制检查所有 Java。
|
||||
''' </summary>
|
||||
Public Sub ManuallySearchJava(Optional CancellationToken As CancellationToken = Nothing)
|
||||
RunInThread(
|
||||
Sub()
|
||||
Configs.JavaList.Get().ForAll(Sub(java) java.InvalidateChecked()) '重置已检查的状态
|
||||
JavaListRefreshWorker.Start(cancellationToken:=CancellationToken)
|
||||
JavaListRefreshWorker.WaitIfRunningAsync(cancellationToken:=CancellationToken).Run() 'worker 开始/结束时会自动重载 UI
|
||||
If Configs.JavaList.Get().Any Then
|
||||
@@ -142,20 +143,23 @@ Public Module ModJava
|
||||
Dim AddConstraint =
|
||||
Sub(Constraint As ValueRange(Of Version))
|
||||
JavaRange = JavaRange?.Intersect(Constraint)
|
||||
If JavaRange.IsEmpty Then Logger.Warn($"Java 版本要求冲突(当前追加的要求为 {Constraint},版本为 {Instance.VersionDisplayName})")
|
||||
If JavaRange.IsEmpty Then
|
||||
Logger.Warn($"Java 版本要求冲突,若启动失败请在版本设置手动指定 Java:当前追加的要求为 {Constraint},游戏版本为 {Instance.VersionDisplayName}", LogBehavior.Toast)
|
||||
JavaRange = Constraint
|
||||
End If
|
||||
End Sub
|
||||
|
||||
'原版 26-:通过版本号判断
|
||||
If (Not Instance.Version.Vaild AndAlso Instance.ReleaseTime >= New Date(2024, 4, 2)) OrElse
|
||||
(Instance.Version.Vaild AndAlso Instance.Version.Vanilla >= New Version(20, 0, 5)) Then
|
||||
If (Not Instance.Version.Valid AndAlso Instance.ReleaseTime >= New Date(2024, 4, 2)) OrElse
|
||||
(Instance.Version.Valid AndAlso Instance.Version.Vanilla >= New Version(20, 0, 5)) Then
|
||||
'1.20.5+(24w14a+):至少 Java 21
|
||||
AddConstraint(ValueRange(Of Version).AtLeast(New Version(21, 0)))
|
||||
ElseIf (Not Instance.Version.Vaild AndAlso Instance.ReleaseTime >= New Date(2021, 11, 16)) OrElse
|
||||
(Instance.Version.Vaild AndAlso Instance.Version.Vanilla.Major >= 18) Then
|
||||
ElseIf (Not Instance.Version.Valid AndAlso Instance.ReleaseTime >= New Date(2021, 11, 16)) OrElse
|
||||
(Instance.Version.Valid AndAlso Instance.Version.Vanilla.Major >= 18) Then
|
||||
'1.18 pre2+:至少 Java 17
|
||||
AddConstraint(ValueRange(Of Version).AtLeast(New Version(17, 0)))
|
||||
ElseIf (Not Instance.Version.Vaild AndAlso Instance.ReleaseTime >= New Date(2021, 5, 11)) OrElse
|
||||
(Instance.Version.Vaild AndAlso Instance.Version.Vanilla.Major >= 17) Then
|
||||
ElseIf (Not Instance.Version.Valid AndAlso Instance.ReleaseTime >= New Date(2021, 5, 11)) OrElse
|
||||
(Instance.Version.Valid AndAlso Instance.Version.Vanilla.Major >= 17) Then
|
||||
'1.17+ (21w19a+):至少 Java 16
|
||||
AddConstraint(ValueRange(Of Version).AtLeast(New Version(16, 0)))
|
||||
ElseIf Instance.ReleaseTime.Year >= 2017 Then 'Minecraft 1.12 与 1.11 的分界线正好是 2017 年,太棒了
|
||||
@@ -181,7 +185,7 @@ Public Module ModJava
|
||||
End If
|
||||
|
||||
'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
|
||||
'<1.7:至多 Java 8
|
||||
AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0)))
|
||||
@@ -195,11 +199,11 @@ Public Module ModJava
|
||||
End If
|
||||
|
||||
'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
|
||||
'1.6.1 - 1.7.2:必须 Java 7
|
||||
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
|
||||
AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0)))
|
||||
ElseIf Instance.Version.Vanilla.Major <= 14 Then
|
||||
@@ -208,38 +212,38 @@ Public Module ModJava
|
||||
ElseIf Instance.Version.Vanilla.Major = 15 Then
|
||||
'1.15:Java 8 - 15
|
||||
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
|
||||
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
|
||||
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
|
||||
AddConstraint(ValueRange(Of Version).LessThan(New Version(17, 0)))
|
||||
ElseIf Instance.Version.Vanilla.Major = 18 AndAlso Instance.Version.HasOptiFine Then '#305
|
||||
'1.18:若安装了 OptiFine,最高 Java 18
|
||||
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
|
||||
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
|
||||
AddConstraint(ValueRange(Of Version).LessThan(New Version(22, 0)))
|
||||
End If
|
||||
End If
|
||||
|
||||
'NeoForge 检测
|
||||
If Instance.Version.HasNeoForge Then
|
||||
If Instance.Version.ModLoaders.HasFlagF(ModLoaders.NeoForge) Then
|
||||
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
|
||||
AddConstraint(ValueRange(Of Version).LessThan(New Version(22, 0)))
|
||||
End If
|
||||
End If
|
||||
|
||||
'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
|
||||
'1.15 - 1.16:Java 8+
|
||||
AddConstraint(ValueRange(Of Version).AtLeast(New Version(8, 0)))
|
||||
@@ -247,10 +251,14 @@ Public Module ModJava
|
||||
'1.18+:Java 17+
|
||||
AddConstraint(ValueRange(Of Version).AtLeast(New Version(17, 0)))
|
||||
End If
|
||||
If Instance.Version.ModLoaderVersions(ModLoaders.Fabric) <> "未知版本" AndAlso McUtils.CompareVersion(Instance.Version.ModLoaderVersions(ModLoaders.Fabric), "0.17.0") < 0 Then
|
||||
'Fabric Loader 0.16.x 及更早版本的 Mixin/ASM 不兼容 Java 25
|
||||
AddConstraint(ValueRange(Of Version).LessThan(New Version(25, 0)))
|
||||
End If
|
||||
End If
|
||||
|
||||
'LiteLoader 检测
|
||||
If Instance.Version.HasLiteLoader AndAlso Instance.Version.Vaild Then '不管非标准版本
|
||||
If Instance.Version.ModLoaders.HasFlagF(ModLoaders.LiteLoader) AndAlso Instance.Version.Valid Then '不管非标准版本
|
||||
'最高 Java 8
|
||||
AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0)))
|
||||
End If
|
||||
@@ -294,7 +302,7 @@ Public Module ModJava
|
||||
|
||||
Case 3 '=============================== 强制指定特定 Java ===============================
|
||||
|
||||
Dim ChosenJava = Configs.InstanceForcedJava.Get(Instance.Config)
|
||||
Dim ChosenJava = Configs.JavaForced.Get(Instance.Config)
|
||||
Logger.Info($"版本设置中强制指定的 Java:{Instance.PathVersion} → {ChosenJava}")
|
||||
If ChosenJava Is Nothing Then
|
||||
If TryFixOrHint Then
|
||||
@@ -305,7 +313,7 @@ Public Module ModJava
|
||||
p?.Skip() : Return Nothing '<==== 设置中未指定 Java
|
||||
End If
|
||||
If Not Configs.JavaList.Get().Contains(ChosenJava) Then
|
||||
Configs.InstanceForcedJava.Reset(Instance.Config)
|
||||
Configs.JavaForced.Reset(Instance.Config)
|
||||
UpdateJavaLists()
|
||||
If TryFixOrHint Then
|
||||
SwitchToInstanceSetup(Instance)
|
||||
@@ -314,7 +322,7 @@ Public Module ModJava
|
||||
p?.Skip() : Return Nothing '<==== 设置中指定的 Java 不在列表中
|
||||
End If
|
||||
If Not ChosenJava.CheckAsync(c).Run() Then
|
||||
Configs.InstanceForcedJava.Reset(Instance.Config)
|
||||
Configs.JavaForced.Reset(Instance.Config)
|
||||
UpdateJavaLists()
|
||||
If TryFixOrHint Then
|
||||
SwitchToInstanceSetup(Instance)
|
||||
@@ -365,14 +373,46 @@ Public Module ModJava
|
||||
|
||||
'电脑中没有合适的 Java,获取 Mojang 提供的 Java 下载列表
|
||||
Logger.Warn($"电脑中没有合适的 Java,开始获取 Mojang 提供的 Java 下载列表")
|
||||
Dim Components = DownloadJavaList()
|
||||
c.ThrowIfCancellationRequested()
|
||||
p?.Set(0.4)
|
||||
Static DownloadListWorker As New RedoableWorker(Of List(Of JavaDownloadInfo))(
|
||||
Function(cancellationToken)
|
||||
'下载 Mojang 提供的 Java 列表
|
||||
Dim IndexFileStr As String = NetRequestByLoader(DlVersionListOrder(
|
||||
{"https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"},
|
||||
{"https://bmclapi2.bangbang93.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"}
|
||||
), IsJson:=True)
|
||||
cancellationToken?.ThrowIfCancellationRequested()
|
||||
'解析 Java 列表
|
||||
Dim Results = New List(Of JavaDownloadInfo)
|
||||
For Each Prop As JProperty In IndexFileStr.DeserializeJson(Of JObject)()("windows-x64")
|
||||
cancellationToken?.ThrowIfCancellationRequested()
|
||||
If Prop.Name = "minecraft-java-exe" Then Continue For '不是完整 Java
|
||||
Try
|
||||
Dim Entry = Prop.Value.FirstOrDefault
|
||||
If Entry Is Nothing Then Continue For
|
||||
Dim JavaName = Entry("version")("name").ToString '可能的名称格式:"12"、"8u51-cacert462b08"、"17.0.8.1"、"17.0.1.12.1"、"21.0.7"
|
||||
If JavaName.Contains(".") Then
|
||||
Dim Parts As String() = JavaName.Split(".")
|
||||
Results.Add(New JavaDownloadInfo With {.ComponentName = Prop.Name, .Java = Entry, .JavaVersion = New Version(Val(Parts(0)), Val(Parts(1)), If(Parts.Length >= 3, Val(Parts(2)), 0), If(Parts.Length >= 4, Val(Parts(3)), 0))})
|
||||
ElseIf JavaName.StartsWithF("8u51-") Then
|
||||
Results.Add(New JavaDownloadInfo With {.ComponentName = Prop.Name, .Java = Entry, .JavaVersion = New Version(8, 0, 51, 0)})
|
||||
Else
|
||||
Results.Add(New JavaDownloadInfo With {.ComponentName = Prop.Name, .Java = Entry, .JavaVersion = New Version(Val(JavaName.BeforeFirstOfAny({"u", "."})), 0, 0, 0)})
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, $"解析 Mojang 提供的 Java 列表项失败,已跳过({Prop.Name})")
|
||||
End Try
|
||||
Next
|
||||
Return Results
|
||||
End Function)
|
||||
If Not DownloadListWorker.HasSucceeded Then DownloadListWorker.Start(c, p?.SplitTo(0.4))
|
||||
DownloadListWorker.WaitIfRunning(cancellationToken:=c)
|
||||
p?.Set(0.4, skiped:=True)
|
||||
Dim Components = DownloadListWorker.LastResult
|
||||
|
||||
'确定要下载的目标 Java
|
||||
Dim JavaToDownload As JavaDownloadInfo = If(
|
||||
Components.FirstOrDefault(Function(Comp) Comp.Java("version")("name").ToString = RecommendedComponent), '优先选择指定的 Component
|
||||
Components.FirstOrDefault(Function(Comp) Range.Contains(Comp.JavaVersion)))
|
||||
Dim JavaToDownload As JavaDownloadInfo = Components.FirstOrDefault(
|
||||
Function(Comp) Comp.Java("version")("name").ToString = RecommendedComponent, '优先选择指定的 Component
|
||||
Function(Comp) Range.Contains(Comp.JavaVersion))
|
||||
If JavaToDownload Is Nothing Then
|
||||
Logger.Warn($"Mojang 提供的 Java 列表中没有合适的 Java,无法自动下载")
|
||||
'提示需要手动下载 Java
|
||||
@@ -443,7 +483,7 @@ Public Module ModJava
|
||||
''' 从老版本 PCL 迁移版本独立的 Java 设置。
|
||||
''' </summary>
|
||||
Private Sub MigrateInstanceJavaSettings(Instance As McInstance, Optional c As CancellationToken = Nothing, Optional p As ProgressProvider = Nothing)
|
||||
If Configs.InstanceMigratedJava.Get(Instance.Config) Then p?.Skip() : Return
|
||||
If Configs.JavaMigrated.Get(Instance.Config) Then p?.Skip() : Return
|
||||
If FrmInstanceSetup IsNot Nothing Then RunInUiWait(Sub() FrmInstanceSetup.ComboArgumentJava.IsEnabled = False)
|
||||
Try
|
||||
Logger.Info($"正在尝试迁移版本独立的 Java 设置({Instance.Name})")
|
||||
@@ -462,10 +502,10 @@ Public Module ModJava
|
||||
Configs.JavaList.Edit(Sub(ByRef List) List.Add(ForcedJava)) '加入列表底部
|
||||
Logger.Info($"已将强制指定的 Java 加入 Java 列表底部:{ForcedJava}")
|
||||
End If
|
||||
Configs.InstanceForcedJava.Set(ForcedJava, Instance.Config)
|
||||
Configs.JavaForced.Set(ForcedJava, Instance.Config)
|
||||
Settings.Set("VersionArgumentJavaV2", 3, Instance)
|
||||
UpdateJavaLists()
|
||||
Configs.InstanceMigratedJava.Set(True, Instance.Config) '完成
|
||||
Configs.JavaMigrated.Set(True, Instance.Config) '完成
|
||||
p?.Finish() : Return
|
||||
NoForcedJava:
|
||||
Dim JavaFound = JavaUtils.SearchFoldersAsync(True, {Instance.PathVersion}, c, p?.SplitTo(1)).Run()
|
||||
@@ -478,7 +518,7 @@ NoForcedJava:
|
||||
Logger.Info("没有需要迁移的内容,自动选择 Java")
|
||||
Settings.Set("VersionArgumentJavaV2", 0, Instance)
|
||||
End If
|
||||
Configs.InstanceMigratedJava.Set(True, Instance.Config) '完成
|
||||
Configs.JavaMigrated.Set(True, Instance.Config) '完成
|
||||
Finally
|
||||
If FrmInstanceSetup IsNot Nothing Then RunInUiWait(
|
||||
Sub()
|
||||
@@ -499,44 +539,6 @@ NoForcedJava:
|
||||
Public Property JavaVersion As Version
|
||||
End Class
|
||||
|
||||
''' <summary>
|
||||
''' 下载 Mojang 提供的 Java 列表。
|
||||
''' <para/> 若本次打开程序已经下载过,则会直接返回缓存的结果。
|
||||
''' </summary>
|
||||
Private Function DownloadJavaList() As List(Of JavaDownloadInfo)
|
||||
'TODO: 写一个更简单的 Lazy,允许重置缓存
|
||||
Static Components As New Lazy(Of List(Of JavaDownloadInfo))(
|
||||
Function()
|
||||
'下载 Mojang 提供的 Java 列表
|
||||
Dim IndexFileStr As String = NetRequestByLoader(DlVersionListOrder(
|
||||
{"https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"},
|
||||
{"https://bmclapi2.bangbang93.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"}
|
||||
), IsJson:=True)
|
||||
'解析 Java 列表
|
||||
Dim Results = New List(Of JavaDownloadInfo)
|
||||
For Each Prop As JProperty In IndexFileStr.DeserializeJson(Of JObject)()("windows-x64")
|
||||
If Prop.Name = "minecraft-java-exe" Then Continue For '不是完整 Java
|
||||
Try
|
||||
Dim Entry = Prop.Value.FirstOrDefault
|
||||
If Entry Is Nothing Then Continue For
|
||||
Dim JavaName = Entry("version")("name").ToString '可能的名称格式:"12"、"8u51-cacert462b08"、"17.0.8.1"、"17.0.1.12.1"、"21.0.7"
|
||||
If JavaName.Contains(".") Then
|
||||
Dim Parts As String() = JavaName.Split(".")
|
||||
Results.Add(New JavaDownloadInfo With {.ComponentName = Prop.Name, .Java = Entry, .JavaVersion = New Version(Val(Parts(0)), Val(Parts(1)), If(Parts.Length >= 3, Val(Parts(2)), 0), If(Parts.Length >= 4, Val(Parts(3)), 0))})
|
||||
ElseIf JavaName.StartsWithF("8u51-") Then
|
||||
Results.Add(New JavaDownloadInfo With {.ComponentName = Prop.Name, .Java = Entry, .JavaVersion = New Version(8, 0, 51, 0)})
|
||||
Else
|
||||
Results.Add(New JavaDownloadInfo With {.ComponentName = Prop.Name, .Java = Entry, .JavaVersion = New Version(Val(JavaName.BeforeFirstOfAny({"u", "."})), 0, 0, 0)})
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, $"解析 Mojang 提供的 Java 列表项失败,已跳过({Prop.Name})")
|
||||
End Try
|
||||
Next
|
||||
Return Results
|
||||
End Function)
|
||||
Return Components.Value
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 构建下载指定 Java 的加载器。
|
||||
''' </summary>
|
||||
|
||||
@@ -48,7 +48,7 @@ Public Module ModLaunch
|
||||
End Try
|
||||
'检查版本
|
||||
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)
|
||||
Return False
|
||||
End If
|
||||
@@ -57,6 +57,10 @@ Public Module ModLaunch
|
||||
FrmLaunchLeft.RefreshButtonsUI()
|
||||
FrmLaunchLeft.RefreshPage(False, False)
|
||||
End If
|
||||
If McInstanceSelected IsNot Nothing AndAlso McInstanceLock.IsLocked(McInstanceSelected) Then
|
||||
Hint($"版本 {McInstanceSelected.Name} 正在被修改,请稍后再启动!", HintType.Red)
|
||||
Return False
|
||||
End If
|
||||
FrmMain.AprilGiveup()
|
||||
'禁止进入版本选择页面(否则就可以在启动中切换 McInstanceSelected 了)
|
||||
FrmMain.PageStack = FrmMain.PageStack.Where(Function(p) p.Page <> FormMain.PageType.InstanceSelect).ToList
|
||||
@@ -70,7 +74,12 @@ Public Module ModLaunch
|
||||
''' </summary>
|
||||
Public Sub McLaunchLog(Text As String, Optional Level As LogLevel = LogLevel.Info)
|
||||
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)
|
||||
End Sub
|
||||
|
||||
@@ -230,7 +239,7 @@ NextInner:
|
||||
'检查版本
|
||||
If McInstanceSelected Is Nothing Then Throw New Exception("未选择 Minecraft 版本!")
|
||||
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 = ""
|
||||
RunInUiWait(Sub() CheckResult = McLoginAble(McLoginInput()))
|
||||
@@ -463,7 +472,8 @@ NextInner:
|
||||
End Function
|
||||
|
||||
'登录主模块加载器
|
||||
Public McLoginLoader As New LoaderTask(Of McLoginData, McLoginResult)("登录", AddressOf McLoginStart, AddressOf McLoginInput, ThreadPriority.BelowNormal) With {.ReloadTimeout = 1, .ProgressWeight = 15, .Block = False}
|
||||
Public McLoginLoader As New LoaderTask(Of McLoginData, McLoginResult)("登录", AddressOf McLoginStart, AddressOf McLoginInput, ThreadPriority.BelowNormal) With
|
||||
{.ReloadTimeout = 1, .ProgressWeight = 15, .Block = False}
|
||||
Public Function McLoginInput() As McLoginData
|
||||
Dim LoginData As McLoginData = Nothing
|
||||
Dim LoginType = Settings.Get(Of McLoginType)("LoginType")
|
||||
@@ -537,7 +547,7 @@ NextInner:
|
||||
'检查是否已经登录完成
|
||||
Dim ExpiresAt = Settings.Get(Of Long)("CacheMsV2Expires")
|
||||
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
|
||||
Data.Output = New McLoginResult With {
|
||||
.Name = Input.UserName, .Type = "Microsoft",
|
||||
@@ -1032,7 +1042,7 @@ Retry:
|
||||
Dim ResultJson As JObject = Result.DeserializeJson()
|
||||
Return (
|
||||
ResultJson("access_token").ToString,
|
||||
ResultJson("expires_in").ToObject(Of Integer) + GetUnixTimestampUtc() - 1200) '提前 20 分钟视作过期
|
||||
ResultJson("expires_in").ToObject(Of Integer) + GetUnixTimestampUtc() - 3600) '提前 1 小时视作过期
|
||||
End Function
|
||||
'微软登录步骤 5:验证微软账号是否持有 MC,这也会刷新 XGP
|
||||
Private Sub MsLoginStep5(AccessToken As String)
|
||||
@@ -1175,6 +1185,7 @@ Retry:
|
||||
|
||||
#Region "启动参数"
|
||||
Private McLaunchArgument As String
|
||||
Private McLaunchEncoding As Encoding
|
||||
|
||||
'主方法
|
||||
Private Sub McLaunchArgumentMain(Loader As LoaderTask(Of String, Integer))
|
||||
@@ -1296,8 +1307,8 @@ NextInstance:
|
||||
'LUA
|
||||
Dim UseLUA As Boolean =
|
||||
Not Settings.Get(Of Boolean)("LaunchAdvanceDisableLUA") AndAlso Not Settings.Get(Of Boolean)("VersionAdvanceDisableLUA", McInstanceSelected) AndAlso
|
||||
McLibListGet(McInstanceSelected, False).Any(Function(e) e.OriginalName = "org.lwjgl:lwjgl:3.4.1")
|
||||
If UseLUA Then Args.Add($"-javaagent:""{ExtractPatch("LUA")}""")
|
||||
McLibListGet(McInstanceSelected, False).Select(Function(e) e.OriginalName).Contains("org.lwjgl:lwjgl:3.4.1")
|
||||
If UseLUA Then Args.Add($"-javaagent:""{LaunchUtils.ExtractPatch("Launch\LwjglUnsafeAgent.jar", PathPure.Value)}""")
|
||||
McLaunchLog("使用 LUA:" & UseLUA)
|
||||
|
||||
#Region "内存管理"
|
||||
@@ -1361,14 +1372,29 @@ NextInstance:
|
||||
'Log4j 漏洞防御参数
|
||||
Args.Add("-Dlog4j2.formatMsgNoLookups=true")
|
||||
|
||||
'编码(#4700、#5892、#5909)
|
||||
If McLaunchJavaSelected.Version.Major > 8 AndAlso Not Args.Any(Function(a) a.StartsWithF("-Dstdout.encoding=")) Then Args.Add("-Dstdout.encoding=UTF-8")
|
||||
If McLaunchJavaSelected.Version.Major > 8 AndAlso Not Args.Any(Function(a) a.StartsWithF("-Dstderr.encoding=")) Then Args.Add("-Dstderr.encoding=UTF-8")
|
||||
If McLaunchJavaSelected.Version.Major >= 18 AndAlso Not Args.Any(Function(a) a.StartsWithF("-Dfile.encoding=")) Then Args.Add("-Dfile.encoding=COMPAT") 'Dfile.encoding 需要放在 Dstdout.encoding 后面(#6934)
|
||||
'编码(#4700、#5892、#5909、#6934、#8950、#9241)
|
||||
Dim UseCompatEncoding = McLaunchJavaSelected.Version.Major >= 18
|
||||
Dim EncodingName = If(UseCompatEncoding,
|
||||
McLaunchJavaSelected.NativeEncodingName, 'Java 18+ 会添加 COMPAT,因此 Java 会回退到 native.encoding
|
||||
McLaunchJavaSelected.FileEncodingName) 'Java 17- 直接使用 file.encoding
|
||||
Try
|
||||
McLaunchEncoding = Encoding.GetEncoding(If(EncodingName.Equals("GBK", StringComparison.OrdinalIgnoreCase), "GB18030", EncodingName))
|
||||
Catch ex As Exception
|
||||
Dim FallbackEncoding = Encoding.GetEncoding(Globalization.CultureInfo.CurrentCulture.TextInfo.ANSICodePage) '老版本 Java 的编码选择行为
|
||||
McLaunchLog($"无法识别 Java 编码 {If(EncodingName, "Nothing")},已回退为 {FallbackEncoding.WebName}", LogLevel.Warn)
|
||||
McLaunchEncoding = FallbackEncoding
|
||||
End Try
|
||||
McLaunchLog($"使用编码:{McLaunchEncoding.WebName}")
|
||||
Dim LaunchEncodingName = If(McLaunchEncoding.CodePage = 54936, "GBK", McLaunchEncoding.WebName)
|
||||
Dim StdoutArgName = If(McLaunchJavaSelected.Version.Major < 19, "sun.stdout.encoding", "stdout.encoding") '老版本 Java 应该设置 sun.std
|
||||
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{StderrArgName}=")) Then Args.Add($"-D{StderrArgName}={LaunchEncodingName}")
|
||||
If UseCompatEncoding AndAlso Not Args.Any(Function(a) a.StartsWithF("-Dfile.encoding=")) Then Args.Add("-Dfile.encoding=COMPAT") '防止老版本 MC 与部分 Mod 不兼容 UTF-8
|
||||
|
||||
'为 JLW 添加 -jar,它必须放在最后
|
||||
If UseJLW Then
|
||||
Args.Add("-jar") : Args.Add(ExtractPatch("JavaWrapper"))
|
||||
Args.Add("-jar") : Args.Add(LaunchUtils.ExtractPatch("Launch\JavaWrapper.jar", PathPure.Value))
|
||||
End If
|
||||
|
||||
'再次去重并输出
|
||||
@@ -1424,7 +1450,8 @@ NextInstance:
|
||||
Arg &= " " & CustomArg
|
||||
|
||||
'把 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
|
||||
McLaunchLog("发现正确的 OptiFineForge TweakClass,目前的游戏参数:" & Arg)
|
||||
Arg = Arg.Replace(" --tweakClass optifine.OptiFineForgeTweaker", "") & " --tweakClass optifine.OptiFineForgeTweaker"
|
||||
@@ -1479,10 +1506,10 @@ NextInstance:
|
||||
Private Iterator Function McLaunchArgumentsReplace(Arguments As List(Of String)) As IEnumerable(Of (Key As String, Value As String))
|
||||
'基础参数
|
||||
Yield ("${classpath_separator}", ";")
|
||||
Yield ("${natives_directory}", GetNativesFolder().TrimEnd("\"c))
|
||||
Yield ("${natives_directory}", PathUtils.RemoveSlashSuffix(GetNativesFolder()))
|
||||
Yield ("${library_directory}", PathUtils.ToShortPath(McFolderSelected & "libraries"))
|
||||
Yield ("${libraries_directory}", PathUtils.ToShortPath(McFolderSelected & "libraries"))
|
||||
Yield ("${pure_directory}", PathPure.TrimEnd("\"c)) '由 PCL 添加,这会允许在分割参数并去重后再替换路径,防止路径中的特殊字符影响参数分割和去重
|
||||
Yield ("${pure_directory}", PathUtils.RemoveSlashSuffix(PathPure.Value)) '由 PCL 添加,这会允许在分割参数并去重后再替换路径,防止路径中的特殊字符影响参数分割和去重
|
||||
Yield ("${launcher_name}", "PCL")
|
||||
Yield ("${launcher_version}", VersionCode)
|
||||
Yield ("${version_name}", McInstanceSelected.Name)
|
||||
@@ -1527,9 +1554,8 @@ NextInstance:
|
||||
Case Else
|
||||
TargetSize = New Size(854, 480)
|
||||
End Select
|
||||
If McInstanceSelected.Version.Vanilla.Major <= 12 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.HasForge Then '修复 1.12.2- 下 JRE 8u200~321 下窗口大小为设置大小的 DPI% 倍(#3463)
|
||||
If McInstanceSelected.Version.Vanilla.Major <= 12 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)
|
||||
McLaunchLog($"已应用窗口大小过大修复(Java 版本:{McLaunchJavaSelected.Version})")
|
||||
TargetSize.Width /= DPI / 96
|
||||
TargetSize.Height /= DPI / 96
|
||||
@@ -1653,7 +1679,7 @@ NextInstance:
|
||||
End Try
|
||||
For Each Entry In Zip.Entries
|
||||
Dim FileName As String = Entry.FullName
|
||||
If FileName.EndsWithF(".dll", True) Then
|
||||
If PathUtils.GetExtension(FileName) = "dll" Then
|
||||
'实际解压文件的步骤
|
||||
Dim FilePath As String = Target & FileName
|
||||
ExistFiles.Add(FilePath)
|
||||
@@ -1697,8 +1723,9 @@ NextInstance:
|
||||
''' <summary>
|
||||
''' 获取 Natives 文件夹路径,以 \ 结尾。
|
||||
''' </summary>
|
||||
Private Function GetNativesFolder() As String
|
||||
Dim Result As String = PathUtils.AddSlashSuffix(PathUtils.ToShortPath(McInstanceSelected.PathVersion)) & McInstanceSelected.Name & "-natives\"
|
||||
Public Function GetNativesFolder(Optional Instance As McInstance = Nothing) As String
|
||||
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
|
||||
Result = Paths.AppData & ".minecraft\bin\natives\"
|
||||
If Not Result.IsAsciiOnly() Then
|
||||
@@ -1824,7 +1851,7 @@ NextInstance:
|
||||
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
|
||||
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
|
||||
End If
|
||||
@@ -1867,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
|
||||
Dim MetaFileAddress As String = PathTemp & "pack.mcmeta"
|
||||
Dim PackPicAddress As String = PathTemp & "pack.png"
|
||||
Dim PackFormat As Integer
|
||||
Select Case McInstanceSelected.Version.Vanilla.Major
|
||||
Case 0, 1, 2, 3, 4, 5
|
||||
'更早的版本没有资源包
|
||||
If McInstanceSelected.Version.Vanilla.Major < 6 Then
|
||||
McLaunchLog("Minecraft 版本过老,尚不支持自定义离线皮肤")
|
||||
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
|
||||
PackFormat = 1
|
||||
PackVersion = 1
|
||||
Case 9, 10
|
||||
PackFormat = 2
|
||||
PackVersion = 2
|
||||
Case 11, 12
|
||||
PackFormat = 3
|
||||
PackVersion = 3
|
||||
Case 13, 14
|
||||
PackFormat = 4
|
||||
PackVersion = 4
|
||||
Case 15
|
||||
PackFormat = 5
|
||||
PackVersion = 5
|
||||
Case 16
|
||||
PackFormat = 6
|
||||
PackVersion = If(McInstanceSelected.Version.Vanilla.Build <= 1, 5, 6)
|
||||
Case 17
|
||||
PackFormat = 7
|
||||
PackVersion = 7
|
||||
Case 18
|
||||
If McInstanceSelected.Version.Vanilla.Minor <= 2 Then
|
||||
PackFormat = 8
|
||||
Else
|
||||
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/数据包#数据包版本
|
||||
PackVersion = 8
|
||||
Case Else '旧版快照可能没有可用的资源包格式声明
|
||||
PackVersion = 17
|
||||
End Select
|
||||
McLaunchLog("正在构建自定义皮肤资源包,格式为:" & PackFormat)
|
||||
End If
|
||||
McLaunchLog($"正在构建自定义皮肤资源包,版本号 {PackVersion},MC 版本 {McInstanceSelected.Version.Vanilla}")
|
||||
'准备文件
|
||||
Dim Bit As New MyBitmap(PathImage & "Heads/Logo.png")
|
||||
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")
|
||||
If (McInstanceSelected.Version.Vanilla.Major = 6 OrElse McInstanceSelected.Version.Vanilla.Major = 7) AndAlso Skin.Pic.Height = 64 Then
|
||||
McLaunchLog("该 Minecraft 版本不支持双层皮肤,已进行裁剪")
|
||||
@@ -1997,7 +2011,7 @@ IgnoreCustomSkin:
|
||||
'输出 bat
|
||||
Try
|
||||
Dim CmdString As String =
|
||||
$"{If(McLaunchJavaSelected.Version.Major > 8, "chcp 65001>nul" & vbCrLf, "")}" &
|
||||
$"chcp {If(McLaunchEncoding.CodePage = 54936 AndAlso IsGBKEncoding, 936, McLaunchEncoding.CodePage)}>nul" & vbCrLf &
|
||||
"@echo off" & vbCrLf &
|
||||
$"title 启动 - {McInstanceSelected.Name}" & vbCrLf &
|
||||
"echo 游戏正在启动,请稍候。" & vbCrLf &
|
||||
@@ -2010,7 +2024,7 @@ IgnoreCustomSkin:
|
||||
FileUtils.Write(
|
||||
filePath:=If(CurrentLaunchOptions.SaveBatch, Paths.Base & "PCL\LatestLaunch.bat"),
|
||||
text:=FilterAccessToken(CmdString, "F").Replace("%", "%%"),
|
||||
encoding:=If(McLaunchJavaSelected.Version.Major > 8, Encoding.UTF8, Encoding.Default))
|
||||
encoding:=McLaunchEncoding)
|
||||
If CurrentLaunchOptions.SaveBatch IsNot Nothing Then
|
||||
McLaunchLog("导出启动脚本完成,强制结束启动过程")
|
||||
CanceledHint = "导出启动脚本成功!"
|
||||
@@ -2092,6 +2106,8 @@ IgnoreCustomSkin:
|
||||
'设置其他参数
|
||||
StartInfo.WorkingDirectory = McInstanceSelected.PathIndie
|
||||
StartInfo.UseShellExecute = False
|
||||
StartInfo.StandardOutputEncoding = McLaunchEncoding
|
||||
StartInfo.StandardErrorEncoding = McLaunchEncoding
|
||||
StartInfo.RedirectStandardOutput = True
|
||||
StartInfo.RedirectStandardError = True
|
||||
StartInfo.CreateNoWindow = True
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -8,6 +8,10 @@ Public Module ModModpack
|
||||
''' 弹窗要求选择一个整合包文件并进行安装。
|
||||
''' </summary>
|
||||
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() '选择整合包文件
|
||||
If String.IsNullOrEmpty(File) Then Return
|
||||
RunInThread(
|
||||
@@ -74,13 +78,14 @@ Public Module ModModpack
|
||||
Catch ex As Exception
|
||||
If ex.GetDisplay(True).Contains("Error.WinIOError") Then
|
||||
Throw New Exception("打开整合包文件失败", ex)
|
||||
ElseIf File.EndsWithF(".rar", True) Then
|
||||
ElseIf PathUtils.GetExtension(File) = "rar" Then
|
||||
Throw New Exception("PCL 无法处理 rar 格式的压缩包,请在解压后重新压缩为 zip 格式再试", ex)
|
||||
Else
|
||||
Throw New Exception("打开整合包文件失败,文件可能损坏或为不支持的压缩包格式", ex)
|
||||
End If
|
||||
End Try
|
||||
'执行对应的安装方法
|
||||
If RestrictMcDownload Then Throw New Exception("为遵守相关法律法规,PCL 暂时不再提供 MC 下载功能,非常抱歉 qwq……")
|
||||
Select Case PackType
|
||||
Case 0
|
||||
Logger.Info("整合包种类:CurseForge")
|
||||
@@ -109,37 +114,18 @@ Public Module ModModpack
|
||||
End Try
|
||||
End Function
|
||||
|
||||
Private Sub ExtractModpackFiles(InstallTemp As String, FileAddress As String, Loader As LoaderBase, ProgressIncrement As Double)
|
||||
'解压文件
|
||||
Dim RetryCount As Integer = 1
|
||||
Dim InitialProgress = Loader.Progress
|
||||
Retry:
|
||||
Try
|
||||
Loader.Progress = InitialProgress
|
||||
Private Sub ExtractModpackFiles(InstallTemp As String, FileAddress As String, Optional c As CancellationToken = Nothing, Optional p As ProgressProvider = Nothing)
|
||||
DirectoryUtils.Delete(InstallTemp)
|
||||
Dim RawProgress As Double = Loader.Progress
|
||||
FileUtils.ExtractToDirectory(FileAddress, InstallTemp, progressHandler:=Sub(Percentage) Loader.Progress = RawProgress + Percentage * ProgressIncrement)
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, $"第 {RetryCount} 次解压尝试失败")
|
||||
'完全不知道为啥会出现文件正在被另一进程使用的问题,总之加个重试
|
||||
If RetryCount < 5 Then
|
||||
Thread.Sleep(RetryCount * 2000)
|
||||
If Loader IsNot Nothing AndAlso Loader.LoadingState <> MyLoading.MyLoadingState.Run Then Return
|
||||
RetryCount += 1
|
||||
GoTo Retry
|
||||
Else
|
||||
Throw New Exception("解压整合包文件失败", ex)
|
||||
End If
|
||||
End Try
|
||||
Loader.Progress = InitialProgress + ProgressIncrement
|
||||
c.ThrowIfCancellationRequested()
|
||||
FileUtils.ExtractToDirectory(FileAddress, InstallTemp, c, p)
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 从整合包的 override 目录复制文件,同时设置 PCL 的配置文件与版本隔离。
|
||||
''' 对路径末尾是否为 \ 没有要求。
|
||||
''' </summary>
|
||||
Private Sub CopyOverrideDirectory(OverridesFolder As String, VersionFolder As String, Loader As LoaderBase)
|
||||
If Not OverridesFolder.EndsWithF("\") Then OverridesFolder += "\"
|
||||
If Not VersionFolder.EndsWithF("\") Then VersionFolder += "\"
|
||||
OverridesFolder = PathUtils.AddSlashSuffix(OverridesFolder)
|
||||
VersionFolder = PathUtils.AddSlashSuffix(VersionFolder)
|
||||
'复制文件
|
||||
If DirectoryUtils.Exists(OverridesFolder) Then
|
||||
Logger.Info($"处理整合包覆写文件夹:{OverridesFolder} → {VersionFolder}")
|
||||
@@ -193,27 +179,26 @@ Retry:
|
||||
If InstanceName = "" Then InstanceName = MyMsgBoxInput("输入版本名称", "", "", New Collection(Of Validate) From {Validate})
|
||||
If String.IsNullOrEmpty(InstanceName) Then Throw New OperationCanceledException
|
||||
End If
|
||||
Dim VersionFolder As String = PathUtils.AddSlashSuffix(Path.Combine(McFolderSelected, "versions", InstanceName) & "\")
|
||||
|
||||
'获取 Mod API 版本信息
|
||||
Dim TargetVersion As New McVersion
|
||||
TargetVersion.VanillaName = Json("minecraft")("version").ToString
|
||||
Dim TargetVersion As New McVersion(Json("minecraft")("version").ToString)
|
||||
For Each Entry In If(Json("minecraft")("modLoaders"), {})
|
||||
Dim Id As String = If(Entry("id"), "").ToString.Lower
|
||||
If Id.StartsWithF("forge-") Then
|
||||
'Forge 指定
|
||||
If Id.Contains("recommended") Then Throw New Exception("该整合包版本过老,已不支持进行安装!")
|
||||
Logger.Info($"整合包 Forge 版本:{Id}")
|
||||
TargetVersion.Forge = Id.Replace("forge-", "")
|
||||
TargetVersion.ModLoaderVersions(ModLoaders.Forge) = Id.Replace("forge-", "")
|
||||
ElseIf Id.StartsWithF("neoforge-") Then
|
||||
'NeoForge 指定
|
||||
Logger.Info($"整合包 NeoForge 版本:{Id}")
|
||||
TargetVersion.NeoForge = Id.Replace("neoforge-", "")
|
||||
TargetVersion.ModLoaderVersions(ModLoaders.NeoForge) = Id.Replace("neoforge-", "")
|
||||
ElseIf Id.StartsWithF("fabric-") Then
|
||||
'Fabric 指定
|
||||
Logger.Info($"整合包 Fabric 版本:{Id}")
|
||||
TargetVersion.Fabric = Id.Replace("fabric-", "")
|
||||
TargetVersion.ModLoaderVersions(ModLoaders.Fabric) = Id.Replace("fabric-", "")
|
||||
Else
|
||||
'ElseIf Id.StartsWithF("quilt-") Then
|
||||
NotifyIncompatibleLoader(Id)
|
||||
End If
|
||||
Next
|
||||
@@ -222,12 +207,12 @@ Retry:
|
||||
Dim InstallLoaders As New List(Of LoaderBase)
|
||||
Dim OverrideHome As String = If(Json("overrides"), "")
|
||||
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)("解压整合包文件",
|
||||
Sub(Task As LoaderTask(Of String, Integer))
|
||||
ExtractModpackFiles(InstallTemp, FileAddress, Task, 0.6)
|
||||
CopyOverrideDirectory(
|
||||
InstallTemp & ArchiveBaseFolder & If(OverrideHome = "." OrElse OverrideHome = "./", "", OverrideHome), '#5613
|
||||
$"{McFolderSelected}versions\{InstanceName}", Task)
|
||||
ExtractModpackFiles(InstallTemp, FileAddress, Task.CreateCancellationToken(), Task.CreateSyncProgressProvider(ToPercentage:=0.6))
|
||||
CopyOverrideDirectory(OverridesFolder, VersionFolder, Task)
|
||||
End Sub) With {
|
||||
.ProgressWeight = FileUtils.GetInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s
|
||||
End If
|
||||
@@ -296,13 +281,15 @@ Retry:
|
||||
Dim TargetFolder As String, Type As ResourceTypes
|
||||
If ModJson("modules").Any Then 'modules 可能返回 null(#1006)
|
||||
Dim ModuleNames = CType(ModJson("modules"), JArray).Select(Function(l) l("name").ToString).ToList
|
||||
If ModuleNames.Contains("META-INF") OrElse ModuleNames.Contains("mcmod.info") OrElse
|
||||
If ModuleNames.Contains("META-INF") OrElse ModuleNames.Contains("mcmod.info") OrElse ModuleNames.Contains("fabric.mod.json") OrElse
|
||||
ModJson?("FileName")?.ToString.EndsWithF(".jar", True) Then
|
||||
TargetFolder = "mods" : Type = ResourceTypes.Mod
|
||||
ElseIf ModuleNames.Contains("pack.mcmeta") Then
|
||||
TargetFolder = "resourcepacks" : Type = ResourceTypes.ResourcePack
|
||||
Else
|
||||
ElseIf ModuleNames.Contains("shaders") Then
|
||||
TargetFolder = "shaderpacks" : Type = ResourceTypes.Shader
|
||||
Else
|
||||
TargetFolder = "saves" : Type = ResourceTypes.Map
|
||||
End If
|
||||
Else
|
||||
TargetFolder = "mods" : Type = ResourceTypes.Mod
|
||||
@@ -311,7 +298,7 @@ Retry:
|
||||
Dim File = ResourceVersion.FromPlatformJson(ModJson, Type)
|
||||
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))
|
||||
Task.Progress += 1 / (1 + ModList.Count)
|
||||
Next
|
||||
@@ -325,15 +312,9 @@ Retry:
|
||||
End If
|
||||
|
||||
'构造加载器
|
||||
Dim Request As New McInstallRequest With {
|
||||
.NewInstanceName = InstanceName,
|
||||
.VersionFolder = $"{McFolderSelected}versions\{InstanceName}\",
|
||||
.MinecraftName = TargetVersion.VanillaName,
|
||||
.ForgeVersion = TargetVersion.Forge,
|
||||
.NeoForgeVersion = TargetVersion.NeoForge,
|
||||
.FabricVersion = TargetVersion.Fabric
|
||||
}
|
||||
Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request)
|
||||
Dim Target As New McComponents(TargetVersion.VanillaName, TargetVersion.ModLoaderVersions)
|
||||
Dim MergeLoaders = McInstallLoadersGet(New McInstallRequest With {
|
||||
.NewInstanceName = InstanceName, .InstanceFolder = VersionFolder, .Components = Target})
|
||||
'构造总加载器
|
||||
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)})
|
||||
@@ -341,18 +322,23 @@ Retry:
|
||||
Loaders.Add(New 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
|
||||
FileUtils.Copy(Logo, VersionFolder & "PCL\Logo.png")
|
||||
WriteIni(VersionFolder & "PCL\Setup.ini", "Logo", "PCL\Logo.png")
|
||||
WriteIni(VersionFolder & "PCL\Setup.ini", "LogoCustom", "True")
|
||||
Logger.Info($"已设置整合包 Logo:{Logo}")
|
||||
End If
|
||||
'解压地图文件
|
||||
Dim SaveFolder As String = $"{VersionFolder}saves\"
|
||||
For Each MapFile In DirectoryUtils.EnumerateFiles(SaveFolder, searchPattern:="*.zip")
|
||||
FileUtils.ExtractToDirectory(MapFile, SaveFolder, Task.CreateCancellationToken())
|
||||
FileUtils.Delete(MapFile)
|
||||
Next
|
||||
'删除原始整合包文件
|
||||
For Each Target As String In {VersionFolder & "原始整合包.zip", VersionFolder & "原始整合包.mrpack"}
|
||||
If FileUtils.Exists(Target) Then
|
||||
Logger.Info($"删除原始整合包文件:{Target}")
|
||||
FileUtils.Delete(Target)
|
||||
For Each FileToDelete As String In {VersionFolder & "原始整合包.zip", VersionFolder & "原始整合包.mrpack"}
|
||||
If FileUtils.Exists(FileToDelete) Then
|
||||
Logger.Info($"删除原始整合包文件:{FileToDelete}")
|
||||
FileUtils.Delete(FileToDelete)
|
||||
End If
|
||||
Next
|
||||
If FileUtils.Exists(FileAddress) AndAlso PathUtils.GetFileNameWithoutExtension(FileAddress) = "modpack" Then
|
||||
@@ -370,10 +356,9 @@ Retry:
|
||||
|
||||
'启动
|
||||
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
|
||||
Loader.Start(Request.VersionFolder)
|
||||
Loader.Start(VersionFolder)
|
||||
LoaderTaskbarAdd(Loader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
RunInUi(Sub() FrmMain.PageChange(FormMain.PageType.DownloadManager))
|
||||
Return Loader
|
||||
End Function
|
||||
|
||||
@@ -389,22 +374,22 @@ Retry:
|
||||
End Try
|
||||
If Json("dependencies") Is Nothing OrElse Json("dependencies")("minecraft") Is Nothing Then Throw New Exception("Modrinth 整合包未提供 Minecraft 版本信息")
|
||||
'获取 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"), {})
|
||||
Select Case Entry.Name.Lower
|
||||
Case "minecraft"
|
||||
TargetVersion.VanillaName = Entry.Value.ToString
|
||||
Case "forge" 'eg. 14.23.5.2859 / 1.19-41.1.0
|
||||
TargetVersion.Forge = Entry.Value.ToString
|
||||
Logger.Info($"整合包 Forge 版本:{TargetVersion.Forge}")
|
||||
TargetVersion.ModLoaderVersions(ModLoaders.Forge) = Entry.Value.ToString
|
||||
Logger.Info($"整合包 Forge 版本:{TargetVersion.ModLoaderVersions(ModLoaders.Forge)}")
|
||||
Case "neoforge", "neo-forge" 'eg. 20.6.98-beta
|
||||
TargetVersion.NeoForge = Entry.Value.ToString
|
||||
Logger.Info($"整合包 NeoForge 版本:{TargetVersion.NeoForge}")
|
||||
TargetVersion.ModLoaderVersions(ModLoaders.NeoForge) = Entry.Value.ToString
|
||||
Logger.Info($"整合包 NeoForge 版本:{TargetVersion.ModLoaderVersions(ModLoaders.NeoForge)}")
|
||||
Case "fabric-loader" 'eg. 0.14.14
|
||||
TargetVersion.Fabric = Entry.Value.ToString
|
||||
Logger.Info($"整合包 Fabric 版本:{TargetVersion.Fabric}")
|
||||
TargetVersion.ModLoaderVersions(ModLoaders.Fabric) = Entry.Value.ToString
|
||||
Logger.Info($"整合包 Fabric 版本:{TargetVersion.ModLoaderVersions(ModLoaders.Fabric)}")
|
||||
Case Else
|
||||
'Case "quilt-loader" 'eg. 1.0.0
|
||||
NotifyIncompatibleLoader(Entry.Name)
|
||||
End Select
|
||||
Next
|
||||
@@ -416,18 +401,15 @@ Retry:
|
||||
If InstanceName = "" Then InstanceName = MyMsgBoxInput("输入版本名称", "", "", New Collection(Of Validate) From {Validate})
|
||||
If String.IsNullOrEmpty(InstanceName) Then Throw New OperationCanceledException
|
||||
End If
|
||||
Dim VersionFolder As String = Path.Combine(McFolderSelected, "versions", InstanceName) & "\"
|
||||
'解压
|
||||
Dim InstallTemp As String = RequestTaskTempFolder()
|
||||
Dim InstallLoaders As New List(Of LoaderBase)
|
||||
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
|
||||
Sub(Task As LoaderTask(Of String, Integer))
|
||||
ExtractModpackFiles(InstallTemp, FileAddress, Task, 0.5)
|
||||
CopyOverrideDirectory(
|
||||
InstallTemp & ArchiveBaseFolder & "overrides",
|
||||
McFolderSelected & "versions\" & InstanceName, Task)
|
||||
CopyOverrideDirectory(
|
||||
InstallTemp & ArchiveBaseFolder & "client-overrides",
|
||||
McFolderSelected & "versions\" & InstanceName, Task)
|
||||
ExtractModpackFiles(InstallTemp, FileAddress, Task.CreateCancellationToken(), Task.CreateSyncProgressProvider(ToPercentage:=0.5))
|
||||
CopyOverrideDirectory(InstallTemp & ArchiveBaseFolder & "overrides", VersionFolder, Task)
|
||||
CopyOverrideDirectory(InstallTemp & ArchiveBaseFolder & "client-overrides", VersionFolder, Task)
|
||||
End Sub) With {.ProgressWeight = FileUtils.GetInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s
|
||||
'获取下载文件列表
|
||||
Dim FileList As New List(Of NetFile)
|
||||
@@ -450,28 +432,23 @@ Retry:
|
||||
Urls = ResourceVersion.ParseModrinthTrackArguments(Urls,
|
||||
ResourceVersion.DownloadReason.ModPack, TargetVersion.VanillaName, TargetVersion.ModLoaders).ToList 'Modrinth 来源追踪信息
|
||||
Urls = Urls.Distinct.ToList()
|
||||
Dim TargetPath As String = $"{McFolderSelected}versions\{InstanceName}\{File("path")}"
|
||||
If Not Path.GetFullPath(TargetPath).StartsWithF(Path.GetFullPath($"{McFolderSelected}versions\{InstanceName}\")) Then
|
||||
Dim TargetPath As String = Path.Combine(VersionFolder, File("path").ToString().TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar))
|
||||
If Not PathUtils.IsParentOf(VersionFolder, TargetPath) Then
|
||||
MyMsgBox($"整合包的文件路径超出了版本文件夹,请向整合包作者反馈此问题!{vbCrLf}目标:{Path.GetFullPath(TargetPath)}{vbCrLf}错误的文件:{TargetPath}", "文件路径校验失败", IsWarn:=True)
|
||||
Throw New OperationCanceledException
|
||||
End If
|
||||
FileList.Add(New NetFile(Urls, TargetPath,
|
||||
New FileChecker With {.ActualSize = File("fileSize").ToObject(Of Long), .Hash = File("hashes")("sha1").ToString}, True))
|
||||
New FileChecker With {.Hash = File("hashes")("sha1").ToString}, True)) 'Modrinth 整合包不应校验文件大小(他们官方自己说的)
|
||||
Next
|
||||
If FileList.Any Then
|
||||
InstallLoaders.Add(New LoaderDownload("下载额外文件", FileList) With {.ProgressWeight = FileList.Count * 1.5}) '每个 Mod 需要 1.5s
|
||||
End If
|
||||
|
||||
'构造加载器
|
||||
Dim Request As New McInstallRequest With {
|
||||
.NewInstanceName = InstanceName,
|
||||
.VersionFolder = $"{McFolderSelected}versions\{InstanceName}\",
|
||||
.MinecraftName = TargetVersion.VanillaName,
|
||||
.ForgeVersion = TargetVersion.Forge,
|
||||
.NeoForgeVersion = TargetVersion.NeoForge,
|
||||
.FabricVersion = TargetVersion.Fabric
|
||||
}
|
||||
Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request)
|
||||
Dim Target As New McComponents(TargetVersion.VanillaName, TargetVersion.ModLoaderVersions)
|
||||
Dim MergeLoaders = McInstallLoadersGet(New McInstallRequest With {
|
||||
.NewInstanceName = InstanceName, .InstanceFolder = VersionFolder,
|
||||
.Components = Target})
|
||||
'构造总加载器
|
||||
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)})
|
||||
@@ -479,7 +456,6 @@ Retry:
|
||||
Loaders.Add(New 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
|
||||
FileUtils.Copy(Logo, VersionFolder & "PCL\Logo.png")
|
||||
WriteIni(VersionFolder & "PCL\Setup.ini", "Logo", "PCL\Logo.png")
|
||||
@@ -487,10 +463,10 @@ Retry:
|
||||
Logger.Info($"已设置整合包 Logo:{Logo}")
|
||||
End If
|
||||
'删除原始整合包文件
|
||||
For Each Target As String In {VersionFolder & "原始整合包.zip", VersionFolder & "原始整合包.mrpack"}
|
||||
If FileUtils.Exists(Target) Then
|
||||
Logger.Info($"删除原始整合包文件:{Target}")
|
||||
FileUtils.Delete(Target)
|
||||
For Each FileToDelete As String In {VersionFolder & "原始整合包.zip", VersionFolder & "原始整合包.mrpack"}
|
||||
If FileUtils.Exists(FileToDelete) Then
|
||||
Logger.Info($"删除原始整合包文件:{FileToDelete}")
|
||||
FileUtils.Delete(FileToDelete)
|
||||
End If
|
||||
Next
|
||||
If FileUtils.Exists(FileAddress) AndAlso PathUtils.GetFileNameWithoutExtension(FileAddress) = "modpack" Then
|
||||
@@ -508,10 +484,9 @@ Retry:
|
||||
|
||||
'启动
|
||||
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
|
||||
Loader.Start(Request.VersionFolder)
|
||||
Loader.Start(VersionFolder)
|
||||
LoaderTaskbarAdd(Loader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
RunInUi(Sub() FrmMain.PageChange(FormMain.PageType.DownloadManager))
|
||||
Return Loader
|
||||
End Function
|
||||
|
||||
@@ -530,24 +505,21 @@ Retry:
|
||||
If Validate.Validate(InstanceName) <> "" Then InstanceName = ""
|
||||
If InstanceName = "" Then InstanceName = MyMsgBoxInput("输入版本名称", "", "", New Collection(Of Validate) From {Validate})
|
||||
If String.IsNullOrEmpty(InstanceName) Then Throw New OperationCanceledException
|
||||
Dim VersionFolder As String = Path.Combine(McFolderSelected, "versions", InstanceName) & "\"
|
||||
'解压
|
||||
Dim InstallTemp As String = RequestTaskTempFolder()
|
||||
Dim InstallLoaders As New List(Of LoaderBase)
|
||||
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
|
||||
Sub(Task As LoaderTask(Of String, Integer))
|
||||
ExtractModpackFiles(InstallTemp, FileAddress, Task, 0.6)
|
||||
CopyOverrideDirectory(
|
||||
InstallTemp & ArchiveBaseFolder & "minecraft",
|
||||
McFolderSelected & "versions\" & InstanceName, Task)
|
||||
ExtractModpackFiles(InstallTemp, FileAddress, Task.CreateCancellationToken(), Task.CreateSyncProgressProvider(ToPercentage:=0.6))
|
||||
CopyOverrideDirectory(InstallTemp & ArchiveBaseFolder & "minecraft", VersionFolder, Task)
|
||||
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 整合包未提供游戏版本信息,无法安装!")
|
||||
Dim Request As New McInstallRequest With {
|
||||
.NewInstanceName = InstanceName,
|
||||
.VersionFolder = $"{McFolderSelected}versions\{InstanceName}\",
|
||||
.MinecraftName = Json("gameVersion").ToString
|
||||
}
|
||||
Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request)
|
||||
Dim Target As New McComponents(Json("gameVersion").ToString, New Dictionary(Of ModLoaders, String))
|
||||
Dim MergeLoaders = McInstallLoadersGet(New McInstallRequest With {
|
||||
.NewInstanceName = InstanceName, .InstanceFolder = VersionFolder,
|
||||
.Components = Target})
|
||||
'构造总加载器
|
||||
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)},
|
||||
@@ -561,10 +533,9 @@ Retry:
|
||||
End If
|
||||
'启动
|
||||
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
|
||||
Loader.Start(Request.VersionFolder)
|
||||
Loader.Start(VersionFolder)
|
||||
LoaderTaskbarAdd(Loader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
RunInUi(Sub() FrmMain.PageChange(FormMain.PageType.DownloadManager))
|
||||
Return Loader
|
||||
End Function
|
||||
|
||||
@@ -584,16 +555,22 @@ Retry:
|
||||
If Validate.Validate(InstanceName) <> "" Then InstanceName = ""
|
||||
If InstanceName = "" Then InstanceName = MyMsgBoxInput("输入版本名称", "", "", New Collection(Of Validate) From {Validate})
|
||||
If String.IsNullOrEmpty(InstanceName) Then Throw New OperationCanceledException
|
||||
Dim VersionFolder As String = Path.Combine(McFolderSelected, "versions", InstanceName) & "\"
|
||||
'解压
|
||||
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)
|
||||
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
|
||||
Sub(Task As LoaderTask(Of String, Integer))
|
||||
ExtractModpackFiles(InstallTemp, FileAddress, Task, 0.55)
|
||||
CopyOverrideDirectory(
|
||||
InstallTemp & ArchiveBaseFolder & ".minecraft",
|
||||
McFolderSelected & "versions\" & InstanceName, Task)
|
||||
ExtractModpackFiles(InstallTemp, FileAddress, Task.CreateCancellationToken(), Task.CreateSyncProgressProvider(ToPercentage:=0.55))
|
||||
'Prism 优先使用 minecraft/ 而非 .minecraft/(#9272)
|
||||
Dim MmcGameRoot As String
|
||||
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)
|
||||
Try
|
||||
Dim MmcSetupFile As String = InstallTemp & ArchiveBaseFolder & "instance.cfg"
|
||||
@@ -627,7 +604,7 @@ Retry:
|
||||
If Logo <> "" AndAlso FileUtils.Exists($"{InstallTemp}{ArchiveBaseFolder}{Logo}.png") Then
|
||||
WriteIni(SetupFile, "LogoCustom", True)
|
||||
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)")
|
||||
End If
|
||||
'JVM 参数
|
||||
@@ -649,20 +626,20 @@ Retry:
|
||||
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 整合包未提供游戏版本信息,无法安装!")
|
||||
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")
|
||||
If Not Component.ContainsKey("uid") Then Continue For
|
||||
Dim UID As String = Component("uid").ToString
|
||||
Select Case UID
|
||||
Case "net.minecraft"
|
||||
Request.MinecraftName = Component("version")
|
||||
MinecraftVersion = Component("version")
|
||||
Case "net.minecraftforge"
|
||||
Request.ForgeVersion = Component("version")
|
||||
ModLoaderVersions(ModLoaders.Forge) = Component("version")
|
||||
Case "net.neoforged"
|
||||
Request.NeoForgeVersion = Component("version")
|
||||
ModLoaderVersions(ModLoaders.NeoForge) = Component("version")
|
||||
Case "net.fabricmc.fabric-loader"
|
||||
Request.FabricVersion = Component("version")
|
||||
'Case "org.quiltmc.quilt-loader" 'eg. 1.0.0
|
||||
ModLoaderVersions(ModLoaders.Fabric) = Component("version")
|
||||
Case Else
|
||||
If UID.StartsWithF("org.lwjgl") Then '#8210
|
||||
Logger.Info($"已跳过 LWJGL 项:{UID}")
|
||||
@@ -672,7 +649,10 @@ Retry:
|
||||
End Select
|
||||
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)
|
||||
Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)})
|
||||
@@ -687,10 +667,9 @@ Retry:
|
||||
|
||||
'启动
|
||||
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
|
||||
Loader.Start(Request.VersionFolder)
|
||||
Loader.Start(VersionFolder)
|
||||
LoaderTaskbarAdd(Loader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
RunInUi(Sub() FrmMain.PageChange(FormMain.PageType.DownloadManager))
|
||||
Return Loader
|
||||
End Function
|
||||
|
||||
@@ -713,16 +692,15 @@ Retry:
|
||||
If InstanceName = "" Then InstanceName = MyMsgBoxInput("输入版本名称", "", "", New Collection(Of Validate) From {Validate})
|
||||
If String.IsNullOrEmpty(InstanceName) Then Throw New OperationCanceledException
|
||||
End If
|
||||
Dim VersionFolder As String = Path.Combine(McFolderSelected, "versions", InstanceName) & "\"
|
||||
'解压
|
||||
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)
|
||||
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
|
||||
Sub(Task As LoaderTask(Of String, Integer))
|
||||
ExtractModpackFiles(InstallTemp, FileAddress, Task, 0.6)
|
||||
CopyOverrideDirectory(
|
||||
InstallTemp & ArchiveBaseFolder & "overrides",
|
||||
McFolderSelected & "versions\" & InstanceName, Task)
|
||||
ExtractModpackFiles(InstallTemp, FileAddress, Task.CreateCancellationToken(), Task.CreateSyncProgressProvider(ToPercentage:=0.6))
|
||||
CopyOverrideDirectory(InstallTemp & ArchiveBaseFolder & "overrides", VersionFolder, Task)
|
||||
'JVM 参数
|
||||
If Json("launchInfo") IsNot Nothing Then
|
||||
Dim LaunchInfo As JObject = Json("launchInfo")
|
||||
@@ -738,19 +716,29 @@ Retry:
|
||||
Next
|
||||
If Not Addons.ContainsKey("game") Then Throw New Exception("该 MCBBS 整合包未提供游戏版本信息,无法安装!")
|
||||
If Addons.ContainsKey("quilt") Then
|
||||
Hint("PCL 暂不支持安装需要 Quilt 的整合包!", HintType.Red)
|
||||
Hint("PCL 不支持安装 Quilt 整合包!", HintType.Red)
|
||||
Throw New OperationCanceledException
|
||||
End If
|
||||
Dim Request As New McInstallRequest With {
|
||||
.NewInstanceName = InstanceName,
|
||||
.VersionFolder = $"{McFolderSelected}versions\{InstanceName}\",
|
||||
.MinecraftName = Addons("game"),
|
||||
.OptiFineVersion = If(Addons.ContainsKey("optifine"), Addons("optifine"), Nothing),
|
||||
.ForgeVersion = If(Addons.ContainsKey("forge"), Addons("forge"), Nothing),
|
||||
.NeoForgeVersion = If(Addons.ContainsKey("neoforge"), Addons("neoforge"), Nothing),
|
||||
.FabricVersion = If(Addons.ContainsKey("fabric"), Addons("fabric"), Nothing)
|
||||
}
|
||||
Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request)
|
||||
Dim ModLoaderVersions As New Dictionary(Of ModLoaders, String)
|
||||
If Addons.ContainsKey("forge") Then ModLoaderVersions(ModLoaders.Forge) = Addons("forge")
|
||||
If Addons.ContainsKey("neoforge") Then ModLoaderVersions(ModLoaders.NeoForge) = Addons("neoforge")
|
||||
If Addons.ContainsKey("fabric") Then ModLoaderVersions(ModLoaders.Fabric) = Addons("fabric")
|
||||
Dim OptiFineEntry As JObject = Nothing
|
||||
If Addons.ContainsKey("optifine") Then
|
||||
Dim MinecraftVersion = Addons("game")
|
||||
If MinecraftVersion.EndsWithF(".0") Then MinecraftVersion = MinecraftVersion.Substring(0, MinecraftVersion.Length - 2)
|
||||
Dim OptiFineFileSuffix = "_HD_" & Addons("optifine").AfterLast("HD_") & ".jar"
|
||||
'从镜像源拉取 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)
|
||||
Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)})
|
||||
@@ -766,10 +754,9 @@ Retry:
|
||||
'启动
|
||||
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
|
||||
'If Archive IsNot Nothing Then Archive.Dispose() '解除占用,以免在加载器中触发 “正由另一进程使用,因此该进程无法访问此文件”
|
||||
Loader.Start(Request.VersionFolder)
|
||||
Loader.Start(VersionFolder)
|
||||
LoaderTaskbarAdd(Loader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
RunInUi(Sub() FrmMain.PageChange(FormMain.PageType.DownloadManager))
|
||||
Return Loader
|
||||
End Function
|
||||
|
||||
@@ -784,7 +771,7 @@ Retry:
|
||||
Dim Loader As New LoaderCombo(Of String)("解压压缩包", {
|
||||
New LoaderTask(Of String, Integer)("解压压缩包",
|
||||
Sub(Task As LoaderTask(Of String, Integer))
|
||||
ExtractModpackFiles(TargetFolder, FileAddress, Task, 0.9)
|
||||
ExtractModpackFiles(TargetFolder, FileAddress, Task.CreateCancellationToken(), Task.CreateSyncProgressProvider(ToPercentage:=0.9))
|
||||
OpenExplorer(TargetFolder)
|
||||
Thread.Sleep(400) '避免文件争用
|
||||
'查找解压后的 exe 文件
|
||||
@@ -860,7 +847,7 @@ Retry:
|
||||
Dim Loader As New LoaderCombo(Of String)("解压压缩包", {
|
||||
New LoaderTask(Of String, Integer)("解压压缩包",
|
||||
Sub(Task As LoaderTask(Of String, Integer))
|
||||
ExtractModpackFiles(TargetFolder, FileAddress, Task, 0.95)
|
||||
ExtractModpackFiles(TargetFolder, FileAddress, Task.CreateCancellationToken(), Task.CreateSyncProgressProvider(ToPercentage:=0.95))
|
||||
'加入文件夹列表
|
||||
PageSelectLeft.AddFolder(TargetFolder & ArchiveBaseFolder, PathUtils.GetLastPart(TargetFolder), False)
|
||||
Thread.Sleep(400) '避免文件争用
|
||||
|
||||
@@ -8,7 +8,8 @@ Public Module ModWatcher
|
||||
Dim IsRunning As Boolean = False
|
||||
Dim TriggerLauncherShutdown As Boolean = True
|
||||
For Each Watcher In McWatcherList
|
||||
If Watcher.State = Watcher.MinecraftState.Loading OrElse Watcher.State = Watcher.MinecraftState.Running Then
|
||||
If Watcher.State = Watcher.MinecraftState.Loading OrElse Watcher.State = Watcher.MinecraftState.Running OrElse
|
||||
(Watcher.State = Watcher.MinecraftState.Crashed AndAlso Not Watcher.GameProcess.HasExited) Then
|
||||
IsRunning = True
|
||||
Exit For
|
||||
ElseIf Watcher.State = Watcher.MinecraftState.Crashed OrElse Watcher.State = Watcher.MinecraftState.Canceled Then
|
||||
@@ -91,7 +92,9 @@ Public Module ModWatcher
|
||||
RunInNewThread(
|
||||
Sub()
|
||||
Try
|
||||
Do Until State = MinecraftState.Ended OrElse State = MinecraftState.Crashed OrElse State = MinecraftState.Canceled OrElse Loader.State = LoadState.Canceled
|
||||
Do Until State = MinecraftState.Ended OrElse State = MinecraftState.Canceled OrElse
|
||||
(Loader.State = LoadState.Canceled AndAlso State <> MinecraftState.Crashed) OrElse
|
||||
(State = MinecraftState.Crashed AndAlso GameProcess.HasExited)
|
||||
TimerWindow()
|
||||
TimerLog()
|
||||
'设置窗口标题
|
||||
@@ -103,6 +106,7 @@ Public Module ModWatcher
|
||||
Thread.Sleep(64)
|
||||
Next
|
||||
Loop
|
||||
If State = MinecraftState.Crashed Then WatcherStateChanged()
|
||||
WatcherLog("Minecraft 日志监控已退出")
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex, "Minecraft 日志监控主循环出错")
|
||||
@@ -314,14 +318,17 @@ Public Module ModWatcher
|
||||
Dim str As New StringBuilder(512)
|
||||
GetClassName(hwnd, str, str.Capacity)
|
||||
Dim ClassName As String = str.ToString
|
||||
If Not (ClassName = "GLFW30" 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)
|
||||
GetWindowText(hwnd, str, str.Capacity)
|
||||
Dim WindowText As String = str.ToString
|
||||
'有的 Mod 可以修改窗口标题,所以不能检测是否为 Minecraft 打头,这并不准确
|
||||
'部分版本会搞个 GLFW message window 出来所以得反选
|
||||
If Not (WindowText.StartsWithF("FML") OrElse (WindowText <> "PopupMessageWindow") AndAlso Not WindowText.StartsWithF("GLFW")) Then Return
|
||||
'部分版本会搞个 GLFW message window,高版本还有个 Hidden Utility Window,都要反选
|
||||
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
|
||||
GetWindowThreadProcessId(hwnd, ProcessId)
|
||||
@@ -342,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 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 IsWindowVisible Lib "user32" (hWnd As IntPtr) As Boolean
|
||||
Private Declare Function GetWindowThreadProcessId Lib "user32" (hWnd As IntPtr, ByRef lpdwProcessId As Integer) As Integer
|
||||
|
||||
'崩溃处理
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
|
||||
|
||||
#Region "附加属性"
|
||||
|
||||
Imports PCL.FormMain
|
||||
|
||||
''' <summary>
|
||||
''' 用于在 XAML 中初始化列表对象。
|
||||
''' 附加属性无法在 XAML 中为每个对象初始化独立的列表对象,因此需要一个包装类,然后在 XAML 中显式初始化。
|
||||
@@ -178,6 +181,8 @@ Public Class CustomEvent
|
||||
Logger.Info($"执行自定义事件:{Type}, {Arg}")
|
||||
If Arg Is Nothing Then Arg = ""
|
||||
Dim Args As String() = Arg.Split("|")
|
||||
Dim Arg0 As String = Arg.BeforeFirst("|")
|
||||
Dim Arg1 As String = Arg.AfterFirst("|")
|
||||
Try
|
||||
Select Case Type
|
||||
Case EventType.打开网页
|
||||
@@ -194,16 +199,16 @@ Public Class CustomEvent
|
||||
Sub()
|
||||
Try
|
||||
'确认实际路径
|
||||
Dim ActualPaths = GetAbsoluteUrls(Args(0), Type)
|
||||
Dim ActualPaths = GetAbsoluteUrls(Arg0, Type)
|
||||
Dim Location = ActualPaths(0), WorkingDir = ActualPaths(1)
|
||||
Logger.Info($"打开类自定义事件实际路径:{Location},工作目录:{WorkingDir}")
|
||||
'执行
|
||||
If Type = EventType.打开帮助 Then
|
||||
PageOtherHelp.EnterHelpPage(Location)
|
||||
Else
|
||||
If Not EventSafetyConfirm("即将执行:" & Location & If(Args.Length >= 2, " " & Args(1), "")) Then Return
|
||||
If Not EventSafetyConfirm("即将执行:" & Location & If(Args.Length >= 2, " " & Arg1, "")) Then Return
|
||||
Dim Info As New ProcessStartInfo With {
|
||||
.Arguments = If(Args.Length >= 2, Args(1), ""),
|
||||
.Arguments = If(Args.Length >= 2, Arg1, ""),
|
||||
.FileName = Location,
|
||||
.WorkingDirectory = WorkingDir
|
||||
}
|
||||
@@ -215,19 +220,19 @@ Public Class CustomEvent
|
||||
End Sub)
|
||||
|
||||
Case EventType.启动游戏
|
||||
If Args(0) = "\current" Then
|
||||
If Arg0 = "\current" Then
|
||||
If McInstanceSelected Is Nothing Then
|
||||
Hint("请先选择一个 Minecraft 版本!", HintType.Red)
|
||||
Return
|
||||
Else
|
||||
Args(0) = McInstanceSelected.Name
|
||||
Arg0 = McInstanceSelected.Name
|
||||
End If
|
||||
End If
|
||||
RunInUi(
|
||||
Sub()
|
||||
If McLaunchStart(New McLaunchOptions With
|
||||
{.ServerIp = If(Args.Length >= 2, Args(1), Nothing), .Instance = New McInstance(Args(0))}) Then
|
||||
Hint($"正在启动 {Args(0)}……")
|
||||
{.ServerIp = If(Args.Length >= 2, Args(1), Nothing), .Instance = New McInstance(Arg0)}) Then
|
||||
Hint($"正在启动 {Arg0}……")
|
||||
End If
|
||||
End Sub)
|
||||
|
||||
@@ -264,51 +269,69 @@ Public Class CustomEvent
|
||||
|
||||
Case EventType.弹出窗口
|
||||
If Args.Length = 1 Then Throw New Exception($"EventType {Type} 需要至少 2 个以 | 分割的参数,例如 弹窗标题|弹窗内容")
|
||||
MyMsgBox(Args(1).Replace("\n", vbCrLf), Args(0).Replace("\n", vbCrLf), If(Args.Length > 2, Args(2), "确定"))
|
||||
MyMsgBox(Arg1.BeforeLast("|").Replace("\n", vbCrLf), Arg0.Replace("\n", vbCrLf), If(Args.Length > 2, Arg1.AfterLast("|"), "确定"))
|
||||
|
||||
Case EventType.弹出提示
|
||||
Hint(Args(0).Replace("\n", vbCrLf), If(Args.Length = 1, HintType.Blue, EnumUtils.FromString(Of HintType)(Args(1))))
|
||||
Hint(Arg0.Replace("\n", vbCrLf), If(Args.Length = 1, HintType.Blue, EnumUtils.FromString(Of HintType)(Arg1)))
|
||||
|
||||
Case EventType.切换页面
|
||||
RunInUi(Sub() FrmMain.PageChange(
|
||||
EnumUtils.FromString(Of FormMain.PageType)(Args(0)),
|
||||
If(Args.Length = 1, FormMain.PageSubType.Default, EnumUtils.FromString(Of FormMain.PageSubType)(Args(1)))))
|
||||
RunInUi(
|
||||
Sub()
|
||||
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.安装整合包
|
||||
RunInUi(Sub() ModpackInstall())
|
||||
|
||||
Case EventType.下载文件
|
||||
Args(0) = Args(0).Replace("\", "/")
|
||||
If Not (Args(0).StartsWithF("http://", True) OrElse Args(0).StartsWithF("https://", True)) Then
|
||||
Arg0 = Arg0.Replace("\", "/")
|
||||
If Not (Arg0.StartsWithF("http://", True) OrElse Arg0.StartsWithF("https://", True)) Then
|
||||
MyMsgBox("EventData 必须为以 http:// 或 https:// 开头的网址。" & vbCrLf & "PCL 不支持其他乱七八糟的下载协议。", "事件执行失败")
|
||||
Return
|
||||
End If
|
||||
If Not EventSafetyConfirm("即将从该网址下载文件:" & vbCrLf & Args(0)) Then Return
|
||||
If Not EventSafetyConfirm("即将从该网址下载文件:" & vbCrLf & Arg0) Then Return
|
||||
RunInUi(
|
||||
Sub()
|
||||
Try
|
||||
Select Case Args.Length
|
||||
Case 1
|
||||
PageOtherTest.StartCustomDownload(Args(0), PathUtils.GetLastPart(Args(0)))
|
||||
PageOtherTest.StartCustomDownload(Arg0, PathUtils.GetLastPart(Arg0))
|
||||
Case 2
|
||||
PageOtherTest.StartCustomDownload(Args(0), Args(1))
|
||||
PageOtherTest.StartCustomDownload(Arg0, Arg1)
|
||||
Case Else
|
||||
PageOtherTest.StartCustomDownload(Args(0), Args(1), Args(2))
|
||||
PageOtherTest.StartCustomDownload(Arg0, Args(1), Args(2))
|
||||
End Select
|
||||
Catch
|
||||
PageOtherTest.StartCustomDownload(Args(0), "未知")
|
||||
PageOtherTest.StartCustomDownload(Arg0, "未知")
|
||||
End Try
|
||||
End Sub)
|
||||
|
||||
Case EventType.修改设置, EventType.写入设置
|
||||
If Args.Length = 1 Then Throw New Exception($"EventType {Type} 需要至少 2 个以 | 分割的参数,例如 UiLauncherTransparent|400")
|
||||
Settings.SetSafe(Args(0), Args(1), Instance:=McInstanceSelected)
|
||||
If Args.Length = 2 Then Hint($"已写入设置:{Args(0)} → {Args(1)}", HintType.Green)
|
||||
Settings.SetSafe(Arg0, Args(1), Instance:=McInstanceSelected)
|
||||
If Args.Length = 2 Then Hint($"已写入设置:{Arg0} → {Args(1)}", HintType.Green)
|
||||
|
||||
Case EventType.修改变量, EventType.写入变量
|
||||
If Args.Length = 1 Then Throw New Exception($"EventType {Type} 需要至少 2 个以 | 分割的参数,例如 VariableName|SomeValue")
|
||||
WriteReg("CustomEvent" & Args(0), Args(1))
|
||||
If Args.Length = 2 Then Hint($"已写入变量:{Args(0)} → {Args(1)}", HintType.Green)
|
||||
Try
|
||||
RegistryUtils.Write($"HKEY_CURRENT_USER\Software\{RegFolder}", $"CustomEvent{Arg0}", Args(1))
|
||||
If Args.Length = 2 Then Hint($"已写入变量:{Arg0} → {Args(1)}", HintType.Green)
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, $"写入变量失败:{Arg0} → {Args(1)}", LogBehavior.Toast)
|
||||
End Try
|
||||
|
||||
Case EventType.加入房间
|
||||
RunInUi(
|
||||
@@ -347,7 +370,7 @@ Public Class CustomEvent
|
||||
Dim RawFileName As String
|
||||
Try
|
||||
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
|
||||
Throw New Exception("联网帮助页面须指向一个帮助 JSON 文件,并在同路径下包含相应 XAML 文件!" & vbCrLf &
|
||||
"例如:" & vbCrLf &
|
||||
|
||||
@@ -414,6 +414,7 @@ EndHint:
|
||||
Public FrmInstanceModDisabled As PageInstanceModDisabled
|
||||
Public FrmInstanceSetup As PageInstanceSetup
|
||||
Public FrmInstanceExport As PageInstanceExport
|
||||
Public FrmDownloadModify As PageDownloadModify
|
||||
|
||||
'资源信息分页声明
|
||||
Public FrmDownloadResourceDetail As PageDownloadResourceDetail
|
||||
@@ -510,7 +511,7 @@ EndHint:
|
||||
EventData = If(JsonData("EventData"), "")
|
||||
IsEvent = True
|
||||
Else
|
||||
Dim XamlAddress As String = FilePath.Lower.Replace(".json", ".xaml")
|
||||
Dim XamlAddress As String = Path.ChangeExtension(FilePath, ".xaml")
|
||||
If FileUtils.Exists(XamlAddress) Then
|
||||
XamlContent = FileUtils.ReadAsString(XamlAddress)
|
||||
IsEvent = False
|
||||
@@ -596,9 +597,9 @@ EndHint:
|
||||
'读取自带文件
|
||||
For Each File In DirectoryUtils.EnumerateFiles(PathTemp & "Help", True)
|
||||
'跳过非 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
|
||||
If RealPath.RegexCheck(Ignore) Then
|
||||
Logger.Trace($"已忽略 {RealPath}:{Ignore}")
|
||||
@@ -799,26 +800,16 @@ NextFile:
|
||||
''' 如果失败,则抛出异常。
|
||||
''' </summary>
|
||||
Public Sub SetGPUPreference(Executeable As String)
|
||||
Const REG_KEY As String = "Software\Microsoft\DirectX\UserGpuPreferences"
|
||||
Const REG_KEY As String = "HKEY_CURRENT_USER\Software\Microsoft\DirectX\UserGpuPreferences"
|
||||
Const REG_VALUE As String = "GpuPreference=2;"
|
||||
'查看现有设置
|
||||
Using ReadOnlyKey = My.Computer.Registry.CurrentUser.OpenSubKey(REG_KEY, False)
|
||||
If ReadOnlyKey IsNot Nothing Then
|
||||
If REG_VALUE = ReadOnlyKey.GetValue(Executeable)?.ToString() Then
|
||||
If RegistryUtils.Read(REG_KEY, Executeable).result?.ToString() = REG_VALUE Then
|
||||
Logger.Info($"无需调整显卡设置:{Executeable}")
|
||||
Return
|
||||
End If
|
||||
Else
|
||||
'创建父级键
|
||||
Logger.Info($"需要创建显卡设置的父级键")
|
||||
My.Computer.Registry.CurrentUser.CreateSubKey(REG_KEY)
|
||||
End If
|
||||
End Using
|
||||
'写入新设置
|
||||
Using WriteKey = My.Computer.Registry.CurrentUser.OpenSubKey(REG_KEY, True)
|
||||
WriteKey.SetValue(Executeable, REG_VALUE)
|
||||
RegistryUtils.Write(REG_KEY, Executeable, REG_VALUE)
|
||||
Logger.Info($"已调整显卡设置:{Executeable}")
|
||||
End Using
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
@@ -867,11 +858,19 @@ NextFile:
|
||||
Else
|
||||
Text = Text.Replace("{version}", Replacer(McInstanceSelected.Version.VanillaName))
|
||||
End If
|
||||
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.ModLoaders.HasFlagF(ModLoaders.Fabric), McInstanceSelected.Version.ModLoaderVersions(ModLoaders.Fabric), "")))
|
||||
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.ModLoaders.HasFlagF(ModLoaders.LiteLoader), "true", "")))
|
||||
Else
|
||||
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("{name}", Replacer(Nothing))
|
||||
Text = Text.Replace("{version}", Replacer(Nothing))
|
||||
Text = Text.Replace("{forge}", Replacer(Nothing))
|
||||
Text = Text.Replace("{fabric}", Replacer(Nothing))
|
||||
Text = Text.Replace("{neoforge}", Replacer(Nothing))
|
||||
Text = Text.Replace("{liteloader}", Replacer(Nothing))
|
||||
End If
|
||||
'登录信息
|
||||
If McLoginLoader.State = LoadState.Finished Then
|
||||
@@ -896,8 +895,8 @@ NextFile:
|
||||
Text = Text.RegexReplace("\{hint\}", Function() Replacer(PageOtherTest.GetRandomHint()))
|
||||
Text = Text.RegexReplace("\{cave\}", Function() Replacer(PageOtherTest.GetRandomCave()))
|
||||
Text = Text.RegexReplace("\{setup:([a-zA-Z0-9]+)\}", Function(m) Replacer(Settings.GetSafe(m.Groups(1).Value, McInstanceSelected)))
|
||||
Text = Text.RegexReplace("\{varible:([^:\}]+)(?::([^\}]+))?\}", Function(m) Replacer(ReadReg("CustomEvent" & m.Groups(1).Value, m.Groups(2).Value)))
|
||||
Text = Text.RegexReplace("\{variable:([^:\}]+)(?::([^\}]+))?\}", Function(m) Replacer(ReadReg("CustomEvent" & m.Groups(1).Value, m.Groups(2).Value)))
|
||||
Text = Text.RegexReplace("\{varible:([^:\}]+)(?::([^\}]+))?\}", Function(m) Replacer(RegistryUtils.TryRead($"HKEY_CURRENT_USER\Software\{RegFolder}", $"CustomEvent{m.Groups(1).Value}", m.Groups(2).Value)))
|
||||
Text = Text.RegexReplace("\{variable:([^:\}]+)(?::([^\}]+))?\}", Function(m) Replacer(RegistryUtils.TryRead($"HKEY_CURRENT_USER\Software\{RegFolder}", $"CustomEvent{m.Groups(1).Value}", m.Groups(2).Value)))
|
||||
Return Text
|
||||
End Function
|
||||
|
||||
|
||||
@@ -320,7 +320,7 @@ Public Module ModMusic
|
||||
Hint("由于音频设备变更,音乐播放功能在重启 PCL 后才能恢复!", HintType.Red)
|
||||
Thread.Sleep(1000000000)
|
||||
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)
|
||||
Hint("播放音乐失败(" & PathUtils.GetLastPart(MusicCurrent) & "):PCL 可能不支持此音乐格式,请将格式转换为 .wav、.mp3 或 .flac 后再试", HintType.Red)
|
||||
Else
|
||||
|
||||
@@ -15,6 +15,7 @@ Friend Module ModSecret
|
||||
|
||||
#Region "网络鉴权"
|
||||
|
||||
Public Const RestrictMcDownload As Boolean = True
|
||||
Friend Function SecretCdnSign(UrlWithMark As String) As String
|
||||
If Not UrlWithMark.EndsWithF("{CDN}") Then Return UrlWithMark
|
||||
Return UrlWithMark.Replace("{CDN}", "").Replace(" ", "%20")
|
||||
|
||||
@@ -25,7 +25,7 @@ Public Class LocalResourceFile
|
||||
End Get
|
||||
End Property
|
||||
''' <summary>
|
||||
''' 假若该资源未被禁用时,文件的文件名。
|
||||
''' 假若该资源未被禁用时,文件的文件名。包含 .jar/.litemod 等后缀。
|
||||
''' </summary>
|
||||
Public ReadOnly Property EnabledName As String
|
||||
Get
|
||||
@@ -57,7 +57,7 @@ Public Class LocalResourceFile
|
||||
Public Property Display As String
|
||||
Get
|
||||
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
|
||||
End Get
|
||||
Set(value As String)
|
||||
@@ -238,7 +238,7 @@ Public Class LocalResourceFile
|
||||
Private _CurseForgeHash As UInteger?
|
||||
|
||||
''' <summary>
|
||||
''' 获取用于 Modrinth 信息获取的 Hash 值(SHA1)。
|
||||
''' 获取用于 Modrinth 信息获取的 Hash 值(SHA1)。固定为小写。
|
||||
''' </summary>
|
||||
Public ReadOnly Property ModrinthHash As String
|
||||
Get
|
||||
@@ -269,7 +269,7 @@ Public Class LocalResourceFile
|
||||
End Function
|
||||
Public Overrides Function Equals(obj As Object) As Boolean
|
||||
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 Region
|
||||
|
||||
@@ -24,11 +24,10 @@ Public Module LocalResourceLoaders
|
||||
'获取 Mod 文件夹下的可用文件列表
|
||||
Dim ModFileList As New List(Of String)
|
||||
If DirectoryUtils.Exists(Loader.Input) Then
|
||||
Dim RawName As String = Loader.Input.Lower
|
||||
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
|
||||
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
|
||||
PathUtils.GetLastPart(PathUtils.RemoveLastPart(File)) = $"1.{PageInstanceLeft.Instance.Version.Vanilla.Major}.{PageInstanceLeft.Instance.Version.Vanilla.Build}") Then
|
||||
Continue For
|
||||
@@ -36,12 +35,9 @@ Public Module LocalResourceLoaders
|
||||
End If
|
||||
Static IsModFile As Func(Of String, Boolean) =
|
||||
Function(FullName As String)
|
||||
If FullName Is Nothing OrElse Not FullName.Contains(".") Then Return False
|
||||
FullName = FullName.Lower
|
||||
If FullName.EndsWithF(".jar", True) OrElse FullName.EndsWithF(".zip", True) OrElse FullName.EndsWithF(".litemod", True) OrElse
|
||||
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
|
||||
If FullName Is Nothing Then Return False
|
||||
If {"disabled", "old"}.Contains(PathUtils.GetExtension(FullName)) Then FullName = PathUtils.GetFileNameWithoutExtension(FullName)
|
||||
Return {"jar", "zip", "litemod"}.Contains(PathUtils.GetExtension(FullName))
|
||||
End Function
|
||||
If IsModFile(File) Then ModFileList.Add(File)
|
||||
Next
|
||||
@@ -120,12 +116,12 @@ Public Module LocalResourceLoaders
|
||||
Next
|
||||
Logger.Info($"有 {Mods.Where(Function(m) m.Project Is Nothing).Count} 个 Mod 需要联网获取信息,{Mods.Where(Function(m) m.Project IsNot Nothing).Count} 个 Mod 需要更新信息")
|
||||
If Not Mods.Any Then Return
|
||||
'获取作为检查目标的加载器和版本
|
||||
'获取作为检查目标的版本
|
||||
'此处不应向下扩展检查的 MC 小版本,例如 Mod 在更新 1.16.5 后,对早期的 1.16.2 版本发布了修补补丁,这会导致 PCL 将 1.16.5 版本的 Mod 降级到 1.16.2
|
||||
Dim TargetMcVersion As McVersion = Loader.Input.Target
|
||||
Dim VanillaVersion = TargetMcVersion.VanillaName
|
||||
'开始网络获取
|
||||
Logger.Info($"目标加载器:{ModLoaders},版本:{VanillaVersion}")
|
||||
Logger.Info($"正在检查 Mod 信息的版本:{VanillaVersion},加载器 {ModLoaders}")
|
||||
Dim EndedThreadCount As Integer = 0, IsFailed As Boolean = False
|
||||
Dim CurrentTaskThread As Thread = Thread.CurrentThread
|
||||
'从 Modrinth 获取信息
|
||||
@@ -140,6 +136,7 @@ Public Module LocalResourceLoaders
|
||||
'步骤 2:尝试读取工程信息缓存,构建其他 Mod 的对应关系
|
||||
If ModrinthVersion.Count = 0 Then Return
|
||||
Dim ModrinthMapping As New Dictionary(Of String, List(Of LocalResourceFile))
|
||||
Dim ModrinthLoaders As New Dictionary(Of LocalResourceFile, List(Of String))
|
||||
For Each Entry In Mods
|
||||
If Not ModrinthVersion.ContainsKey(Entry.ModrinthHash) Then Continue For
|
||||
If ModrinthVersion(Entry.ModrinthHash)("files")(0)("hashes")("sha1") <> Entry.ModrinthHash Then Continue For
|
||||
@@ -149,6 +146,8 @@ Public Module LocalResourceLoaders
|
||||
ModrinthMapping(ProjectId).Add(Entry)
|
||||
'记录对应的 ProjectVersion
|
||||
Dim File = ResourceVersion.FromPlatformJson(ModrinthVersion(Entry.ModrinthHash), ResourceTypes.Mod)
|
||||
ModrinthLoaders(Entry) = ModrinthVersion(Entry.ModrinthHash)("loaders").
|
||||
Select(Function(l) l.ToString.Lower).Distinct.OrderBy(Function(l) l).ToList
|
||||
If Entry.ProjectVersion Is Nothing OrElse Entry.ProjectVersion.ReleaseDate < File.ReleaseDate Then
|
||||
Entry.ProjectVersion = File
|
||||
Else
|
||||
@@ -169,17 +168,20 @@ Public Module LocalResourceLoaders
|
||||
Next
|
||||
Logger.Info($"已从 Modrinth 获取本地 Mod 信息,继续获取更新信息")
|
||||
'步骤 4:获取更新信息
|
||||
If ModLoaders = ModLoaders.None Then
|
||||
Logger.Warn("该 Minecraft 版本没有可用的 Mod 加载器,不获取 Mod 更新信息")
|
||||
Return
|
||||
End If
|
||||
Dim ModrinthUpdate As JObject = DlModRequest("https://api.modrinth.com/v2/version_files/update", HttpMethod.Post,
|
||||
$"{{""hashes"": [""{ModrinthMapping.SelectMany(Function(l) l.Value.Select(Function(m) m.ModrinthHash)).Join(""",""")}""], ""algorithm"": ""sha1"",
|
||||
""loaders"": [""{ModLoaders.Flags.Join(""",""").Lower}""],""game_versions"": [""{VanillaVersion}""]}}", "application/json")
|
||||
Dim ModsToUpdate = ModrinthMapping.SelectMany(Function(l) l.Value).ToList
|
||||
If Not ModsToUpdate.Any Then Return
|
||||
Dim UpdateData As String = ModsToUpdate.
|
||||
Select(Function(m) $"{{""hash"": ""{m.ModrinthHash}"", ""loaders"": [""{ModrinthLoaders(m).Join(""",""")}""], ""game_versions"": [""{VanillaVersion}""]}}").
|
||||
Join(",")
|
||||
Dim ModrinthUpdate As JObject = DlModRequest("https://api.modrinth.com/v2/version_files/update_individual", HttpMethod.Post,
|
||||
$"{{""algorithm"": ""sha1"", ""hashes"": [{UpdateData}]}}", "application/json")
|
||||
For Each Entry In Mods
|
||||
If Not ModrinthUpdate.ContainsKey(Entry.ModrinthHash) OrElse Entry.ProjectVersion Is Nothing Then Continue For
|
||||
Dim UpdateFile = ResourceVersion.FromPlatformJson(ModrinthUpdate(Entry.ModrinthHash), ResourceTypes.Mod)
|
||||
If Not UpdateFile.DownloadAvailable Then Continue For
|
||||
Dim UpdateLoaders = ModrinthUpdate(Entry.ModrinthHash)("loaders").
|
||||
Select(Function(l) l.ToString.Lower).Distinct.OrderBy(Function(l) l).ToList
|
||||
If Not UpdateLoaders.SequenceEqual(ModrinthLoaders(Entry)) Then Continue For
|
||||
Logger.Trace(Function() $"本地文件 {Entry.ProjectVersion.FileName} 在 Modrinth 上的最新版为 {UpdateFile.FileName}")
|
||||
If Entry.ProjectVersion.ReleaseDate >= UpdateFile.ReleaseDate OrElse Entry.ProjectVersion.Hash = UpdateFile.Hash Then Continue For
|
||||
'设置更新日志与更新文件
|
||||
@@ -217,6 +219,7 @@ Public Module LocalResourceLoaders
|
||||
|
||||
'步骤 2:尝试读取工程信息缓存,构建其他 Mod 的对应关系
|
||||
Dim ProjectIdToLocalFiles As New Dictionary(Of Integer, List(Of LocalResourceFile))
|
||||
Dim CurseForgeLoaders As New Dictionary(Of LocalResourceFile, ModLoaders)
|
||||
For Each Project In CurseForgeRaw
|
||||
Dim ProjectId = Project("id").ToString
|
||||
Dim Hash As UInteger = Project("file")("fileFingerprint")
|
||||
@@ -226,6 +229,7 @@ Public Module LocalResourceLoaders
|
||||
ProjectIdToLocalFiles.AddIntoValueCollection(ProjectId, Entry)
|
||||
'记录对应的 ProjectVersion
|
||||
Dim File = ResourceVersion.FromPlatformJson(Project("file"), ResourceTypes.Mod)
|
||||
CurseForgeLoaders(Entry) = File.ModLoaders
|
||||
If Entry.ProjectVersion Is Nothing OrElse Entry.ProjectVersion.ReleaseDate < File.ReleaseDate Then Entry.ProjectVersion = File
|
||||
Next
|
||||
Next
|
||||
@@ -242,36 +246,36 @@ Public Module LocalResourceLoaders
|
||||
If ProjectJson("isAvailable") IsNot Nothing AndAlso Not ProjectJson("isAvailable").ToObject(Of Boolean) Then Continue For
|
||||
'设置 Entry 中的工程信息
|
||||
Dim Project As New ResourceProject(ProjectJson)
|
||||
Dim TargetLoaders As ModLoaders = ModLoaders.None
|
||||
For Each LocalFile In ProjectIdToLocalFiles(Project.Id) '倒查防止 CurseForge 返回的内容有漏
|
||||
If LocalFile.Project?.Platform = ResourcePlatforms.Modrinth Then
|
||||
LocalFile.Project = LocalFile.Project '再次触发修改事件
|
||||
Else
|
||||
LocalFile.Project = Project
|
||||
TargetLoaders = TargetLoaders Or LocalFile.Project.ModLoaders
|
||||
End If
|
||||
Next
|
||||
TargetLoaders = TargetLoaders And ModLoaders '与目标 MC 版本的加载器取交集
|
||||
'查找或许版本更新的文件列表
|
||||
If ModLoaders = ModLoaders.None Then Continue For '无法判断 MC 版本使用的加载器,跳过更新检查
|
||||
'查找可能有版本更新的文件列表
|
||||
Dim LatestFiles = ProjectJson("latestFilesIndexes").Where(
|
||||
Function(Entry)
|
||||
'Mod Loader 匹配
|
||||
If Entry("modLoader") Is Nothing Then Return False
|
||||
Dim EntryLoader As ModLoaders = Resource.FromCurseForgeModLoaderType(Entry("modLoader").ToObject(Of Integer))
|
||||
If Not TargetLoaders.HasFlag(EntryLoader) Then Return False
|
||||
Dim EntryLoader As ModLoaders = If(Entry("modLoader") Is Nothing, ModLoaders.None,
|
||||
Resource.FromCurseForgeModLoaderType(Entry("modLoader").ToObject(Of Integer)))
|
||||
If CurseForgeLoaders(LocalFile) = ModLoaders.None Then
|
||||
If EntryLoader <> ModLoaders.None Then Return False
|
||||
ElseIf Not CurseForgeLoaders(LocalFile).HasFlagF(EntryLoader) Then
|
||||
Return False
|
||||
End If
|
||||
'MC 版本匹配
|
||||
Dim EntryVersion As String = Entry("gameVersion")
|
||||
Return EntryVersion IsNot Nothing AndAlso EntryVersion = VanillaVersion
|
||||
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
|
||||
Dim FileId = Entry("fileId").ToObject(Of Integer)
|
||||
If Not UpdateFileIds.ContainsKey(FileId) Then UpdateFileIds(FileId) = New List(Of LocalResourceFile)
|
||||
UpdateFileIds(FileId).AddRange(ProjectIdToLocalFiles(Project.Id))
|
||||
UpdateFileIds(FileId).Add(LocalFile)
|
||||
FileIdToProjectSlug(FileId) = Project.Slug
|
||||
Next
|
||||
Next
|
||||
Next
|
||||
Logger.Info($"已从 CurseForge 获取本地 Mod 信息,需要获取 {UpdateFileIds.Count} 个用于检查更新的文件信息")
|
||||
If Not UpdateFileIds.Any() Then Return
|
||||
|
||||
@@ -283,6 +287,10 @@ Public Module LocalResourceLoaders
|
||||
Dim File = ResourceVersion.FromPlatformJson(FileJson, ResourceTypes.Mod)
|
||||
If Not File.DownloadAvailable Then Continue For
|
||||
For Each Entry As LocalResourceFile In UpdateFileIds(File.Id)
|
||||
If File.ModLoaders <> CurseForgeLoaders(Entry) Then
|
||||
Logger.Warn($"从 CurseForge 获取到的更新文件 {File.FileName} 加载器为 {File.ModLoaders},与本地文件 {Entry.File.Name} 的加载器 {CurseForgeLoaders(Entry)} 不一致,已忽略", LogBehavior.None)
|
||||
Continue For
|
||||
End If
|
||||
If UpdateFiles.ContainsKey(Entry) AndAlso UpdateFiles(Entry).ReleaseDate >= File.ReleaseDate Then Continue For
|
||||
UpdateFiles(Entry) = File
|
||||
Next
|
||||
|
||||
@@ -87,7 +87,7 @@ Public Class ResourceProject
|
||||
Get
|
||||
If Not LoadedDatabase Then
|
||||
LoadedDatabase = True
|
||||
If Types.HasFlag(ResourceTypes.Mod) OrElse Types.HasFlag(ResourceTypes.DataPack) Then
|
||||
If Types.HasFlagF(ResourceTypes.Mod) OrElse Types.HasFlagF(ResourceTypes.DataPack) Then
|
||||
_WikiEntry = WikiEntry.All.Value.FirstOrDefault(Function(c) c.Slugs.GetOrDefault(Platform) = Slug)
|
||||
End If
|
||||
End If
|
||||
@@ -184,11 +184,11 @@ Public Class ResourceProject
|
||||
If Not NewFile.DownloadAvailable Then Continue For
|
||||
ModLoaders = ModLoaders Or NewFile.ModLoaders
|
||||
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))
|
||||
Next
|
||||
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))
|
||||
Next
|
||||
CurseForgeFileIds = Files.Select(Function(f) f.Key).Distinct.ToList
|
||||
@@ -415,7 +415,7 @@ Public Class ResourceProject
|
||||
If Not MissingProjectIds.Any() Then Return
|
||||
'联网获取剩余的工程对象
|
||||
Dim Projects As JArray
|
||||
If Platform.HasFlag(ResourcePlatforms.CurseForge) Then
|
||||
If Platform.HasFlagF(ResourcePlatforms.CurseForge) Then
|
||||
Projects = DlModRequest("https://api.curseforge.com/v1/mods",
|
||||
HttpMethod.Post, "{""modIds"": [" & MissingProjectIds.Join(","c) & "]}", "application/json")("data")
|
||||
Else
|
||||
@@ -502,7 +502,6 @@ Public Class ResourceProject
|
||||
'获取 Mod 加载器描述
|
||||
Dim ModLoaderDescriptionFull As String, ModLoaderDescriptionPart As String
|
||||
Dim ModLoadersForDesc = ModLoaders.Flags().ToList()
|
||||
If Settings.Get(Of Boolean)("ToolDownloadIgnoreQuilt") Then ModLoadersForDesc.Remove(ModLoaders.Quilt)
|
||||
Select Case ModLoadersForDesc.Count
|
||||
Case 0
|
||||
If ModLoadersForDesc.IsSingle Then
|
||||
@@ -519,8 +518,7 @@ Public Class ResourceProject
|
||||
Dim NewestDrop As Integer = If(Drops.Any, Drops.First, 9999)
|
||||
If ModLoadersForDesc.Contains(ModLoaders.Forge) AndAlso
|
||||
(NewestDrop < 140 OrElse ModLoadersForDesc.Contains(ModLoaders.Fabric)) AndAlso
|
||||
(NewestDrop < 200 OrElse ModLoadersForDesc.Contains(ModLoaders.NeoForge)) AndAlso
|
||||
(NewestDrop < 140 OrElse ModLoadersForDesc.Contains(ModLoaders.Quilt) OrElse Settings.Get(Of Boolean)("ToolDownloadIgnoreQuilt")) Then
|
||||
(NewestDrop < 200 OrElse ModLoadersForDesc.Contains(ModLoaders.NeoForge)) Then
|
||||
ModLoaderDescriptionFull = "任意"
|
||||
ModLoaderDescriptionPart = ""
|
||||
Else
|
||||
|
||||
@@ -21,8 +21,8 @@ Public Class ResourceSearcher
|
||||
''' </summary>
|
||||
Public ReadOnly Property CanContinue As Boolean
|
||||
Get
|
||||
If Tag.StartsWithF("/") OrElse Not Sources.HasFlag(ResourcePlatforms.CurseForge) Then Storage.CurseForgeTotal = 0
|
||||
If Tag.EndsWithF("/") OrElse Not Sources.HasFlag(ResourcePlatforms.Modrinth) Then Storage.ModrinthTotal = 0
|
||||
If Tag.StartsWithF("/") OrElse Not Sources.HasFlagF(ResourcePlatforms.CurseForge) Then Storage.CurseForgeTotal = 0
|
||||
If Tag.EndsWithF("/") OrElse Not Sources.HasFlagF(ResourcePlatforms.Modrinth) Then Storage.ModrinthTotal = 0
|
||||
If Storage.CurseForgeTotal = -1 OrElse Storage.ModrinthTotal = -1 Then Return True
|
||||
Return Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal
|
||||
End Get
|
||||
@@ -99,12 +99,12 @@ Public Class ResourceSearcher
|
||||
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 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)
|
||||
Facets.Add($"[""project_type:{Type.ToString.Lower}""]")
|
||||
If Not String.IsNullOrEmpty(Tag) Then Facets.Add($"[""categories:'{Tag.AfterLast("/")}'""]")
|
||||
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
|
||||
If Not String.IsNullOrEmpty(GameVersion) Then Facets.Add($"[""versions:'{GameVersion}'""]")
|
||||
Address += "&facets=[" & String.Join(",", Facets) & "]"
|
||||
@@ -179,11 +179,6 @@ Public Class ResourceSearcher
|
||||
End If
|
||||
End If
|
||||
|
||||
'拒绝 1.13- Quilt(这个版本根本没有 Quilt)
|
||||
If Request.ModLoaders = ModLoaders.Quilt AndAlso CompareVersion(If(Request.GameVersion, "1.15"), "1.14") = -1 Then
|
||||
Throw New Exception("Quilt 不支持 Minecraft " & Request.GameVersion)
|
||||
End If
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "中文搜索"
|
||||
@@ -246,7 +241,7 @@ Public Class ResourceSearcher
|
||||
Next
|
||||
End Function
|
||||
'CurseForge
|
||||
If Request.Sources.HasFlag(ResourcePlatforms.CurseForge) Then
|
||||
If Request.Sources.HasFlagF(ResourcePlatforms.CurseForge) Then
|
||||
'数据库搜索
|
||||
Static CurseForgeSearchEntries As List(Of SearchEntry(Of WikiEntry)) = GetSearchEntries(ResourcePlatforms.CurseForge).ToList()
|
||||
Dim CurseForgeSearchResults = Search(CurseForgeSearchEntries, RawSearchText, 100, 0.25)
|
||||
@@ -263,7 +258,7 @@ Public Class ResourceSearcher
|
||||
End If
|
||||
End If
|
||||
'Modrinth
|
||||
If Request.Sources.HasFlag(ResourcePlatforms.Modrinth) Then
|
||||
If Request.Sources.HasFlagF(ResourcePlatforms.Modrinth) Then
|
||||
'数据库搜索
|
||||
Static ModrinthSearchEntries As List(Of SearchEntry(Of WikiEntry)) = GetSearchEntries(ResourcePlatforms.Modrinth).ToList()
|
||||
Dim ModrinthSearchResults = Search(ModrinthSearchEntries, RawSearchText, 100, 0.25)
|
||||
@@ -308,7 +303,7 @@ NextPage:
|
||||
Try
|
||||
|
||||
'CurseForge 搜索
|
||||
If Request.Sources.HasFlag(ResourcePlatforms.CurseForge) AndAlso
|
||||
If Request.Sources.HasFlagF(ResourcePlatforms.CurseForge) AndAlso
|
||||
Not (Storage.CurseForgeTotal > -1 AndAlso Storage.CurseForgeTotal <= Storage.CurseForgeOffset) AndAlso '剩余的未显示的搜索结果不足
|
||||
(Not IsChineseSearch OrElse (IsChineseSearch AndAlso Not String.IsNullOrEmpty(CurseForgeAltSearchText))) Then '如果是中文搜索,就只在有对应搜索关键词的时候才继续
|
||||
WorkThreads.Add(RunInNewThread(
|
||||
@@ -322,7 +317,7 @@ NextPage:
|
||||
For Each JsonEntry As JObject In RequestResult("data")
|
||||
Dim Project As New ResourceProject(JsonEntry)
|
||||
If Request.Type = ResourceTypes.ResourcePack AndAlso Project.Tags.Contains("数据包") Then Continue For 'CurseForge 将一些数据包分类成了资源包
|
||||
If Request.Type <> ResourceTypes.Any AndAlso Not Project.Types.HasFlag(Request.Type) Then Continue For '过滤分区不匹配的搜索结果(#8265)
|
||||
If Request.Type <> ResourceTypes.Any AndAlso Not Project.Types.HasFlagF(Request.Type) Then Continue For '过滤分区不匹配的搜索结果(#8265)
|
||||
ProjectList.Add(Project)
|
||||
Next
|
||||
'更新结果
|
||||
@@ -340,7 +335,7 @@ NextPage:
|
||||
End If
|
||||
|
||||
'Modrinth 搜索
|
||||
If Request.Sources.HasFlag(ResourcePlatforms.Modrinth) AndAlso
|
||||
If Request.Sources.HasFlagF(ResourcePlatforms.Modrinth) AndAlso
|
||||
Not (Storage.ModrinthTotal > -1 AndAlso Storage.ModrinthTotal <= Storage.ModrinthOffset) AndAlso '剩余的未显示的搜索结果不足
|
||||
(Not IsChineseSearch OrElse (IsChineseSearch AndAlso Not String.IsNullOrEmpty(ModrinthAltSearchText))) Then '如果是中文搜索,就只在有对应搜索关键词的时候才继续
|
||||
WorkThreads.Add(RunInNewThread(
|
||||
@@ -368,7 +363,7 @@ NextPage:
|
||||
End If
|
||||
|
||||
'Modrinth 直接获取工程
|
||||
If Request.Sources.HasFlag(ResourcePlatforms.Modrinth) AndAlso
|
||||
If Request.Sources.HasFlagF(ResourcePlatforms.Modrinth) AndAlso
|
||||
Not (Storage.ModrinthTotal > -1 AndAlso Storage.ModrinthTotal <= Storage.ModrinthOffset) AndAlso '剩余的未显示的搜索结果不足
|
||||
ModrinthSlugs.Any Then '有直接获取的 Slug
|
||||
WorkThreads.Add(RunInNewThread(
|
||||
@@ -380,7 +375,7 @@ NextPage:
|
||||
For Each JsonEntry As JObject In DlModRequest(ModrinthUrl)
|
||||
Dim Project As New ResourceProject(JsonEntry)
|
||||
'应用筛选
|
||||
If Request.Type <> ResourceTypes.Any AndAlso Not Project.Types.HasFlag(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
|
||||
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
|
||||
@@ -389,6 +384,15 @@ NextPage:
|
||||
Not Project.UnsafeGameVersions.Any(Function(d) d = Request.GameVersion) Then Continue For
|
||||
ProjectList.Add(Project)
|
||||
Next
|
||||
If ProjectList.Any AndAlso Request.ModLoaders <> ModLoaders.None AndAlso Not IgnoreModLoaderFilter AndAlso
|
||||
Not String.IsNullOrEmpty(Request.GameVersion) Then
|
||||
Dim ProjectIds = ProjectList.Select(Function(p) p.Id).Join(""",""project_id:")
|
||||
Dim Loaders = Request.ModLoaders.Flags.Select(Function(f) f.ToString.Lower).Join(""",""categories:")
|
||||
Dim FilterUrl = $"https://api.modrinth.com/v2/search?limit={ProjectList.Count}&facets=" &
|
||||
$"[[""project_id:{ProjectIds}""],[""categories:{Loaders}""],[""versions:{Request.GameVersion}""]]"
|
||||
Dim MatchedIds = DlModRequest(FilterUrl)("hits").Select(Function(h) h("project_id").ToString).ToList
|
||||
ProjectList.KeepIf(Function(p) MatchedIds.Contains(p.Id))
|
||||
End If
|
||||
'更新结果
|
||||
ProjectList.ForEach(Sub(p) SearchResults.Add(p))
|
||||
Logger.Info($"从 Modrinth 直接获取到了 {ProjectList.Count} 个工程")
|
||||
@@ -410,7 +414,7 @@ NextPage:
|
||||
|
||||
'仅保留兼容 Forge 的 Mod,或老版本中没有标注任何 Mod Loader 的 Mod
|
||||
If IgnoreModLoaderFilter Then
|
||||
RawResults.KeepIf(Function(p) p.ModLoaders = ModLoaders.None OrElse p.ModLoaders.HasFlag(ModLoaders.Forge))
|
||||
RawResults.KeepIf(Function(p) p.ModLoaders = ModLoaders.None OrElse p.ModLoaders.HasFlagF(ModLoaders.Forge))
|
||||
End If
|
||||
|
||||
'确保存在结果
|
||||
|
||||
@@ -90,7 +90,7 @@ Public Class ResourceVersion
|
||||
''' </summary>
|
||||
Public DownloadUrls As List(Of String)
|
||||
''' <summary>
|
||||
''' 对应文件的 SHA1 或 MD5。
|
||||
''' 对应文件的 SHA1 或 MD5。固定为小写。
|
||||
''' </summary>
|
||||
Public Hash As String = Nothing
|
||||
''' <summary>
|
||||
@@ -127,8 +127,9 @@ Public Class ResourceVersion
|
||||
.DownloadCount = Data("downloadCount")
|
||||
.FileName = Data("fileName")
|
||||
.Size = Data("fileLength")
|
||||
.Hash = CType(Data("hashes"), JArray).ToList.FirstOrDefault(Function(s) s("algo").ToObject(Of Integer) = 1)?("value")
|
||||
If .Hash Is Nothing Then .Hash = CType(Data("hashes"), JArray).ToList.FirstOrDefault(Function(s) s("algo").ToObject(Of Integer) = 2)?("value")
|
||||
If .Size = 0 Then .Size = -1 'CurseForge 可能错误地返回 0 尺寸(#9104)
|
||||
.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
|
||||
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}"
|
||||
@@ -145,13 +146,13 @@ Public Class ResourceVersion
|
||||
'GameVersions
|
||||
Dim RawVersions As List(Of String) = Data("gameVersions").Select(Function(t) t.ToString.Trim.Lower).ToList
|
||||
.GameVersions = RawVersions.
|
||||
Where(Function(v) McVersion.IsFormatFit(v)).
|
||||
Where(Function(v) McVersion.IsStandardFormat(v)).
|
||||
Select(Function(v) v.Replace("-snapshot", " 预览版")).
|
||||
Distinct.ToList
|
||||
If .GameVersions.IsSingle Then
|
||||
.GameVersions = .GameVersions.ToList
|
||||
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 '整合包理应只 “支持” 一个版本
|
||||
Else
|
||||
.GameVersions = New List(Of String) From {"未知版本"}
|
||||
@@ -176,7 +177,7 @@ Public Class ResourceVersion
|
||||
.FileName = File("filename")
|
||||
.DownloadUrls = New List(Of String) From {File("url"), DlSourceModGet(File("url"))}.Distinct.ToList '同时添加了镜像源
|
||||
.Size = File("size")
|
||||
.Hash = File("hashes")("sha1")
|
||||
.Hash = File("hashes")("sha1")?.ToString.Lower()
|
||||
End If
|
||||
'类别与 Loaders
|
||||
'结果可能混杂着 Mod、数据包和服务端插件
|
||||
@@ -185,7 +186,7 @@ Public Class ResourceVersion
|
||||
For Each Loader As ModLoaders In EnumUtils.GetAllFlags(Of ModLoaders)()
|
||||
If RawLoaders.Contains(Loader.ToString.Lower) Then .ModLoaders = .ModLoaders Or Loader
|
||||
Next
|
||||
If .ResourceType.HasFlag(ResourceTypes.Mod) OrElse .ResourceType.HasFlag(ResourceTypes.DataPack) Then
|
||||
If .ResourceType.HasFlagF(ResourceTypes.Mod) OrElse .ResourceType.HasFlagF(ResourceTypes.DataPack) Then
|
||||
If RawLoaders.Intersect({"bukkit", "folia", "paper", "purpur", "spigot"}).Any() Then .ResourceType = ResourceTypes.Plugin 'Veinminer Enchantment 同时支持服务端与 Fabric
|
||||
If RawLoaders.Contains("datapack") Then .ResourceType = ResourceTypes.DataPack
|
||||
If .ModLoaders.Flags().Any() Then .ResourceType = ResourceTypes.Mod
|
||||
@@ -208,7 +209,7 @@ Public Class ResourceVersion
|
||||
If .GameVersions.IsSingle 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)} '整合包理应只 “支持” 一个版本
|
||||
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
|
||||
@@ -348,14 +349,14 @@ Public Class ResourceVersion
|
||||
.Height = 42
|
||||
.Type = MyListItem.CheckType.Clickable
|
||||
.Tag = Me
|
||||
.Title = If(IsBadDisplay, FileName, Display)
|
||||
.Title = If(IsBadDisplay, FileName, Display).BeforeLastOfAny({".zip", ".jar", ".mrpack", ".litemod"}, True)
|
||||
.Logo = $"{PathImage}ReleaseTypes/{ReleaseType}.png"
|
||||
AddHandler .Click, ClickHandler
|
||||
'描述
|
||||
.Info =
|
||||
Iterator Function()
|
||||
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 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
|
||||
|
||||
@@ -51,6 +51,6 @@ Imports System.Runtime.InteropServices
|
||||
' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
|
||||
' 方法是按如下所示使用“*”
|
||||
|
||||
<Assembly: AssemblyVersion("2.13.0.1")>
|
||||
<Assembly: AssemblyFileVersion("2.13.0.1")>
|
||||
<Assembly: AssemblyVersion("2.99.0.0")>
|
||||
<Assembly: AssemblyFileVersion("2.99.0.0")>
|
||||
<Assembly: NeutralResourcesLanguage("")>
|
||||
@@ -140,16 +140,6 @@ Namespace My.Resources
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 查找 System.Byte[] 类型的本地化资源。
|
||||
'''</summary>
|
||||
Friend ReadOnly Property JavaWrapper() As Byte()
|
||||
Get
|
||||
Dim obj As Object = ResourceManager.GetObject("JavaWrapper", resourceCulture)
|
||||
Return CType(obj,Byte())
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 查找 System.Byte[] 类型的本地化资源。
|
||||
'''</summary>
|
||||
@@ -160,16 +150,6 @@ Namespace My.Resources
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 查找 System.Byte[] 类型的本地化资源。
|
||||
'''</summary>
|
||||
Friend ReadOnly Property LUA() As Byte()
|
||||
Get
|
||||
Dim obj As Object = ResourceManager.GetObject("LUA", resourceCulture)
|
||||
Return CType(obj,Byte())
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 查找 System.Byte[] 类型的本地化资源。
|
||||
'''</summary>
|
||||
|
||||
@@ -142,21 +142,18 @@
|
||||
<data name="Imazen.WebP" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Imazen.WebP.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="JavaWrapper" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\java-wrapper.jar;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="libwebp64" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\libwebp64.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="LUA" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\lwjgl-unsafe-agent.jar;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="MeloongCore" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\..\PCLCS\bin\MeloongCore.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="MeloongCore.Wpf" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\..\PCLCS\bin\MeloongCore.Wpf.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="Microsoft.Win32.Registry" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\..\PCLCS\bin\Microsoft.Win32.Registry.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="NAudio" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\NAudio.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,7 +1,7 @@
|
||||
<local:MyPageRight
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
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"
|
||||
PanScroll="PanBack">
|
||||
<Grid>
|
||||
@@ -14,19 +14,96 @@
|
||||
</StackPanel.RenderTransform>
|
||||
</StackPanel>
|
||||
<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>
|
||||
<TranslateTransform X="40" />
|
||||
</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"
|
||||
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:MyTextBox Margin="93,0,16,0" Padding="-1,0" MinHeight="30" FontSize="15" VerticalAlignment="Center" x:Name="TextSelectName" MaxLength="70" HasBackground="False" />
|
||||
<local:MyImage x:Name="ImgLogo" Margin="52,15,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" Height="32" RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
<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: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="必须安装 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="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">
|
||||
<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">
|
||||
@@ -37,13 +114,25 @@
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
</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" />
|
||||
</Grid>
|
||||
<Grid x:Name="BtnForgeClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}">
|
||||
<Path x:Name="BtnForgeClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
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" />
|
||||
<Grid x:Name="PanForgeCompare" 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 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>
|
||||
<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 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" />
|
||||
@@ -55,13 +144,25 @@
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
</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" />
|
||||
</Grid>
|
||||
<Grid x:Name="BtnNeoForgeClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}">
|
||||
<Path x:Name="BtnNeoForgeClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
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" />
|
||||
<Grid x:Name="PanNeoForgeCompare" 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 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>
|
||||
<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 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" />
|
||||
@@ -73,13 +174,25 @@
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
</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" />
|
||||
</Grid>
|
||||
<Grid x:Name="BtnFabricClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}">
|
||||
<Path x:Name="BtnFabricClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
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" />
|
||||
<Grid x:Name="PanFabricCompare" 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 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>
|
||||
<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 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" />
|
||||
@@ -91,13 +204,25 @@
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
</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" />
|
||||
</Grid>
|
||||
<Grid x:Name="BtnFabricApiClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}">
|
||||
<Path x:Name="BtnFabricApiClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
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" />
|
||||
<Grid x:Name="PanFabricApiCompare" 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 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>
|
||||
<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 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">
|
||||
@@ -111,13 +236,25 @@
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
</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" />
|
||||
</Grid>
|
||||
<Grid x:Name="BtnOptiFineClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}">
|
||||
<Path x:Name="BtnOptiFineClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
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" />
|
||||
<Grid x:Name="PanOptiFineCompare" 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 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>
|
||||
<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 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" />
|
||||
@@ -129,13 +266,25 @@
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
</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" />
|
||||
</Grid>
|
||||
<Grid x:Name="BtnOptiFabricClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}">
|
||||
<Path x:Name="BtnOptiFabricClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
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" />
|
||||
<Grid x:Name="PanOptiFabricCompare" 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 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>
|
||||
<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 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" />
|
||||
@@ -147,13 +296,25 @@
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
</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" />
|
||||
</Grid>
|
||||
<Grid x:Name="BtnLiteLoaderClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}">
|
||||
<Path x:Name="BtnLiteLoaderClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
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" />
|
||||
<Grid x:Name="PanLiteLoaderCompare" 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 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>
|
||||
<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>
|
||||
</StackPanel>
|
||||
</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" />
|
||||
</Grid>
|
||||
<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="LoadForge" HasAnimation="False" />
|
||||
<local:MyLoading Visibility="Collapsed" x:Name="LoadNeoForge" HasAnimation="False" />
|
||||
|
||||
@@ -1,12 +1,34 @@
|
||||
Public Class PageDownloadInstall
|
||||
|
||||
Private Sub LoaderInit() Handles Me.Initialized
|
||||
Public Sub ControlInit()
|
||||
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)
|
||||
End If
|
||||
End Sub
|
||||
|
||||
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()
|
||||
DlOptiFineListLoader.Start()
|
||||
DlLiteLoaderListLoader.Start()
|
||||
@@ -17,35 +39,19 @@ Public Class PageDownloadInstall
|
||||
TextSelectName.ValidateRules = New ObjectModel.Collection(Of Validate) From {New ValidateFolderName(McFolderSelected & "versions")}
|
||||
TextSelectName.Validate()
|
||||
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
|
||||
|
||||
#Region "页面切换"
|
||||
|
||||
'页面切换动画
|
||||
Public IsInSelectPage As Boolean = False
|
||||
Private IsFirstLoaded As Boolean = False
|
||||
Private Sub EnterSelectPage()
|
||||
If IsInSelectPage Then Return
|
||||
|
||||
''' <summary>
|
||||
''' 选择页面 UI 初始化。
|
||||
''' </summary>
|
||||
Public Sub PrepareSelectPage()
|
||||
IsInSelectPage = True
|
||||
|
||||
PanInner.Margin = New Thickness(25, 10, 25, 40)
|
||||
|
||||
AutoSelectedFabricApi = False
|
||||
AutoSelectedOptiFabric = False
|
||||
IsSelectNameEdited = False
|
||||
PanSelect.Visibility = Visibility.Visible
|
||||
PanSelect.IsHitTestVisible = True
|
||||
PanMinecraft.IsHitTestVisible = False
|
||||
@@ -54,13 +60,9 @@ Public Class PageDownloadInstall
|
||||
|
||||
DisabledPageAnimControls.Remove(BtnStart)
|
||||
BtnStart.Show = True
|
||||
CardOptiFine.IsSwapped = True
|
||||
CardLiteLoader.IsSwapped = True
|
||||
CardForge.IsSwapped = True
|
||||
CardNeoForge.IsSwapped = True
|
||||
CardFabric.IsSwapped = True
|
||||
CardFabricApi.IsSwapped = True
|
||||
CardOptiFabric.IsSwapped = True
|
||||
For Each Card In {CardOptiFine, CardLiteLoader, CardForge, CardNeoForge, CardFabric, CardFabricApi, CardOptiFabric}
|
||||
Card.IsSwapped = True
|
||||
Next
|
||||
|
||||
'如果在选择页面按了刷新键,选择页的东西可能会由于动画被隐藏,但不会由于加载结束而再次显示,因此这里需要手动恢复
|
||||
For Each Control In GetAllAnimControls(PanSelect)
|
||||
@@ -69,9 +71,16 @@ Public Class PageDownloadInstall
|
||||
Control.RenderTransform = New TranslateTransform
|
||||
End If
|
||||
Next
|
||||
End Sub
|
||||
|
||||
Private Sub EnterSelectPage()
|
||||
If IsInSelectPage Then Return
|
||||
AutoSelectedFabricApi = False
|
||||
AutoSelectedOptiFabric = False
|
||||
PrepareSelectPage()
|
||||
|
||||
'启动 Forge 加载
|
||||
If McVersion.IsFormatFit(VanillaName) Then
|
||||
If McVersion.IsStandardFormat(VanillaName) Then
|
||||
Dim ForgeLoader = New LoaderTask(Of String, List(Of DlForgeVersionEntry))("DlForgeVersion " & VanillaName, AddressOf DlForgeVersionMain)
|
||||
LoadForge.State = ForgeLoader
|
||||
ForgeLoader.Start(VanillaName)
|
||||
@@ -87,6 +96,16 @@ Public Class PageDownloadInstall
|
||||
AaCode(
|
||||
Sub()
|
||||
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()
|
||||
OptiFine_Loaded()
|
||||
LiteLoader_Loaded()
|
||||
@@ -96,26 +115,8 @@ Public Class PageDownloadInstall
|
||||
FabricApi_Loaded()
|
||||
OptiFabric_Loaded()
|
||||
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
|
||||
|
||||
Public Sub ExitSelectPage() Handles BtnBack.Click
|
||||
If Not IsInSelectPage Then Return
|
||||
IsInSelectPage = False
|
||||
@@ -144,10 +145,11 @@ Public Class PageDownloadInstall
|
||||
End Sub,, True)
|
||||
}, "FrmDownloadInstall SelectPageSwitch")
|
||||
End Sub
|
||||
Public Sub MinecraftSelected(sender As MyListItem, e As MouseButtonEventArgs)
|
||||
Public Overridable Sub MinecraftSelected(sender As MyListItem, e As MouseButtonEventArgs)
|
||||
VanillaName = sender.Title
|
||||
VanillaData = sender.Tag
|
||||
VanillaIcon = sender.Logo
|
||||
LabMinecraftVersion.Text = VanillaName
|
||||
EnterSelectPage()
|
||||
End Sub
|
||||
|
||||
@@ -156,9 +158,9 @@ Public Class PageDownloadInstall
|
||||
#Region "选择"
|
||||
|
||||
'Minecraft
|
||||
Private VanillaName As String
|
||||
Private VanillaData As JObject
|
||||
Private VanillaIcon As String
|
||||
Public VanillaName As String
|
||||
Public VanillaData As JObject
|
||||
Public VanillaIcon As String
|
||||
Private ReadOnly Property VanillaDrop As Integer
|
||||
Get
|
||||
Return McVersion.VersionToDrop(VanillaName)
|
||||
@@ -166,24 +168,25 @@ Public Class PageDownloadInstall
|
||||
End Property
|
||||
|
||||
'附加组件
|
||||
Private SelectedOptiFine As DlOptiFineListEntry = Nothing
|
||||
Private SelectedLiteLoader As DlLiteLoaderListEntry = Nothing
|
||||
Private SelectedForge As DlForgeVersionEntry = Nothing
|
||||
Private SelectedNeoForge As DlNeoForgeListEntry = Nothing
|
||||
Private SelectedFabric As String = Nothing
|
||||
Private SelectedFabricApi As ResourceVersion = Nothing
|
||||
Private SelectedOptiFabric As ResourceVersion = Nothing
|
||||
Public SelectedOptiFine As DlOptiFineListEntry = Nothing
|
||||
Public SelectedLiteLoader As DlLiteLoaderListEntry = Nothing
|
||||
Public SelectedForge As DlForgeVersionEntry = Nothing
|
||||
Public SelectedNeoForge As DlNeoForgeListEntry = Nothing
|
||||
Public SelectedFabric As String = Nothing
|
||||
Public SelectedFabricApi As ResourceVersion = Nothing
|
||||
Public SelectedOptiFabric As ResourceVersion = Nothing
|
||||
|
||||
''' <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
|
||||
If VanillaName Is Nothing OrElse Ongoing Then Return
|
||||
Ongoing = True
|
||||
'主预览
|
||||
SelectNameUpdate()
|
||||
ImgLogo.Source = GetSelectLogo()
|
||||
ImgMinecraft.Source = VanillaIcon
|
||||
'OptiFine
|
||||
Dim OptiFineError As String = LoadOptiFineGetError()
|
||||
CardOptiFine.MainSwap.Visibility = If(OptiFineError Is Nothing, Visibility.Visible, Visibility.Collapsed)
|
||||
@@ -222,7 +225,7 @@ Public Class PageDownloadInstall
|
||||
End If
|
||||
End If
|
||||
'Forge
|
||||
If Not McVersion.IsFormatFit(VanillaName) Then
|
||||
If Not McVersion.IsStandardFormat(VanillaName) Then
|
||||
CardForge.Visibility = Visibility.Collapsed
|
||||
Else
|
||||
CardForge.Visibility = Visibility.Visible
|
||||
@@ -356,7 +359,7 @@ Public Class PageDownloadInstall
|
||||
''' <summary>
|
||||
''' 清空已选择的项目。
|
||||
''' </summary>
|
||||
Private Sub ClearSelected()
|
||||
Public Sub ClearSelected()
|
||||
VanillaName = Nothing
|
||||
VanillaData = Nothing
|
||||
VanillaIcon = Nothing
|
||||
@@ -367,7 +370,10 @@ Public Class PageDownloadInstall
|
||||
SelectedFabric = Nothing
|
||||
SelectedFabricApi = Nothing
|
||||
SelectedOptiFabric = Nothing
|
||||
IsSelectNameEdited = False
|
||||
For Each ClearButton In {BtnOptiFineClear, BtnLiteLoaderClear, BtnForgeClear, BtnNeoForgeClear,
|
||||
BtnFabricClear, BtnFabricApiClear, BtnOptiFabricClear}
|
||||
ClearButton.Visibility = Visibility.Collapsed
|
||||
Next
|
||||
End Sub
|
||||
'信息栏动画
|
||||
Private Sub SetPanelVisibility(Panel As Grid, IsVisible As Boolean)
|
||||
@@ -389,7 +395,7 @@ Public Class PageDownloadInstall
|
||||
''' <summary>
|
||||
''' 获取版本图标。
|
||||
''' </summary>
|
||||
Private Function GetSelectLogo() As String
|
||||
Public Overridable Function GetSelectLogo() As String
|
||||
If SelectedFabric IsNot Nothing Then
|
||||
Return "pack://application:,,,/images/Blocks/Fabric.png"
|
||||
ElseIf SelectedForge IsNot Nothing Then
|
||||
@@ -409,7 +415,7 @@ Public Class PageDownloadInstall
|
||||
''' <summary>
|
||||
''' 获取默认版本名。
|
||||
''' </summary>
|
||||
Private Function GetSelectName() As String
|
||||
Public Function GetSelectName() As String
|
||||
Dim Name As String = VanillaName
|
||||
If SelectedFabric IsNot Nothing Then Name += "-Fabric " & SelectedFabric.BeforeFirst("+")
|
||||
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(" ", "_")
|
||||
Return Name
|
||||
End Function
|
||||
Private IsSelectNameEdited As Boolean = False
|
||||
Private IsSelectNameChanging As Boolean = False
|
||||
Public IsSelectNameEdited As Boolean = False
|
||||
Private Sub SelectNameUpdate()
|
||||
If IsSelectNameEdited OrElse IsSelectNameChanging Then Return
|
||||
IsSelectNameChanging = True
|
||||
If IsSelectNameEdited Then Return
|
||||
TextSelectName.Text = GetSelectName()
|
||||
IsSelectNameChanging = False
|
||||
IsSelectNameEdited = False
|
||||
End Sub
|
||||
Private Sub TextSelectName_TextChanged(sender As Object, e As TextChangedEventArgs) Handles TextSelectName.TextChanged
|
||||
If IsSelectNameChanging Then Return
|
||||
IsSelectNameEdited = True
|
||||
ReloadSelected()
|
||||
End Sub
|
||||
Private Sub TextSelectName_ValidateChanged(sender As Object, e As EventArgs) Handles TextSelectName.ValidateChanged
|
||||
BtnStart.IsEnabled = TextSelectName.IsValidated
|
||||
HintSelectName.Text = TextSelectName.ValidateResult
|
||||
HintSelectName.Visibility = If(TextSelectName.IsValidated, Visibility.Collapsed, Visibility.Visible)
|
||||
End Sub
|
||||
|
||||
#End 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() '返回
|
||||
Try
|
||||
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)}
|
||||
}
|
||||
Dim Versions As JArray = DlClientListLoader.Output.Value("versions")
|
||||
For Each Version As JObject In Versions
|
||||
For Each Version As JObject In MinecraftVersions
|
||||
'确定分类
|
||||
Dim Type As String = Version("type")
|
||||
Select Case Type
|
||||
@@ -498,7 +507,7 @@ Public Class PageDownloadInstall
|
||||
'清空当前
|
||||
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 Release As JObject = Dict("正式版")(0).DeepClone()
|
||||
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")
|
||||
PinnedVersions.Add(Snapshot)
|
||||
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}
|
||||
MyCard.StackInstall(PanInfo, 7)
|
||||
Dim PanInfo As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0)}
|
||||
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)
|
||||
'添加其他版本
|
||||
For Each Pair As KeyValuePair(Of String, List(Of JObject)) In Dict
|
||||
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 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 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)}
|
||||
NewCard.Children.Add(NewStack)
|
||||
NewCard.SwapControl = NewStack
|
||||
NewCard.SetContentBuilder(Pair.Value,
|
||||
Function(Version) McDownloadListItem(Version, Sub(sender, e) MinecraftSelected(sender, e), False))
|
||||
NewCard.IsSwapped = True
|
||||
PanMinecraft.Children.Add(NewCard)
|
||||
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
|
||||
Logger.Error(ex, "可视化安装版本列表出错")
|
||||
End Try
|
||||
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>
|
||||
''' 当 MC 版本列表加载完时,立即自动选择的版本。用于外部调用。
|
||||
''' </summary>
|
||||
@@ -550,16 +564,16 @@ Public Class PageDownloadInstall
|
||||
''' <summary>
|
||||
''' 获取 OptiFine 的加载异常信息。若正常则返回 Nothing。
|
||||
''' </summary>
|
||||
Private Function LoadOptiFineGetError() As String
|
||||
Public Function LoadOptiFineGetError() As String
|
||||
If SelectedNeoForge IsNot Nothing Then Return "与 NeoForge 不兼容"
|
||||
'检查 Forge 1.13 - 1.14.3:全部不兼容
|
||||
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 不兼容"
|
||||
End If
|
||||
'检查 Fabric 1.20.5+:全部不兼容
|
||||
If SelectedFabric IsNot Nothing AndAlso
|
||||
CompareVersion(VanillaName, "1.20.4") > 0 Then
|
||||
McUtils.CompareVersion(VanillaName, "1.20.4") > 0 Then
|
||||
Return "与 Fabric 不兼容"
|
||||
End If
|
||||
'检查 Loader
|
||||
@@ -584,12 +598,12 @@ Public Class PageDownloadInstall
|
||||
End Function
|
||||
|
||||
'检查某个 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 OptiFine.RequiredForgeVersion Is Nothing Then Return False '不兼容 Forge
|
||||
If String.IsNullOrWhiteSpace(OptiFine.RequiredForgeVersion) Then Return True '#4183
|
||||
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
|
||||
Return Forge.Version.Revision = OptiFine.RequiredForgeVersion
|
||||
End If
|
||||
@@ -619,7 +633,7 @@ Public Class PageDownloadInstall
|
||||
Function(Left As DlOptiFineListEntry, Right As DlOptiFineListEntry) As Boolean
|
||||
If Not Left.IsPreview AndAlso Right.IsPreview Then Return True
|
||||
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)
|
||||
'可视化
|
||||
PanOptiFine.Children.Clear()
|
||||
@@ -641,12 +655,12 @@ Public Class PageDownloadInstall
|
||||
CardOptiFine.IsSwapped = True
|
||||
ReloadSelected()
|
||||
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
|
||||
SelectedOptiFabric = Nothing
|
||||
BtnOptiFabricClear.Visibility = Visibility.Collapsed
|
||||
AutoSelectedOptiFabric = False
|
||||
CardOptiFine.IsSwapped = True
|
||||
e.Handled = True
|
||||
Forge_Loaded()
|
||||
NeoForge_Loaded()
|
||||
ReloadSelected()
|
||||
@@ -659,7 +673,7 @@ Public Class PageDownloadInstall
|
||||
''' <summary>
|
||||
''' 获取 LiteLoader 的加载异常信息。若正常则返回 Nothing。
|
||||
''' </summary>
|
||||
Private Function LoadLiteLoaderGetError() As String
|
||||
Public Function LoadLiteLoaderGetError() As String
|
||||
'检查 Loader
|
||||
If GetLoaderError(LoadLiteLoader) IsNot Nothing Then Return GetLoaderError(LoadLiteLoader)
|
||||
'检查版本
|
||||
@@ -699,10 +713,9 @@ Public Class PageDownloadInstall
|
||||
CardLiteLoader.IsSwapped = True
|
||||
ReloadSelected()
|
||||
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
|
||||
CardLiteLoader.IsSwapped = True
|
||||
e.Handled = True
|
||||
ReloadSelected()
|
||||
End Sub
|
||||
|
||||
@@ -713,8 +726,8 @@ Public Class PageDownloadInstall
|
||||
''' <summary>
|
||||
''' 获取 Forge 的加载异常信息。若正常则返回 Nothing。
|
||||
''' </summary>
|
||||
Private Function LoadForgeGetError() As String
|
||||
If CompareVersionGE("1.5.1", VanillaName) AndAlso CompareVersionGE(VanillaName, "1.1") Then Return "无"
|
||||
Public Function LoadForgeGetError() As String
|
||||
If McUtils.CompareVersionGE("1.5.1", VanillaName) AndAlso McUtils.CompareVersionGE(VanillaName, "1.1") Then Return "无"
|
||||
'检查 Loader
|
||||
If GetLoaderError(LoadForge) IsNot Nothing Then Return GetLoaderError(LoadForge)
|
||||
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 SelectedFabric IsNot Nothing Then Return "与 Fabric 不兼容"
|
||||
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 检查
|
||||
End If
|
||||
If SelectedOptiFine IsNot Nothing AndAlso Not IsOptiFineSuitForForge(SelectedOptiFine, Version) Then Continue For
|
||||
@@ -775,10 +788,9 @@ Public Class PageDownloadInstall
|
||||
OptiFine_Loaded()
|
||||
ReloadSelected()
|
||||
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
|
||||
CardForge.IsSwapped = True
|
||||
e.Handled = True
|
||||
OptiFine_Loaded()
|
||||
ReloadSelected()
|
||||
End Sub
|
||||
@@ -790,7 +802,7 @@ Public Class PageDownloadInstall
|
||||
''' <summary>
|
||||
''' 获取 NeoForge 的加载异常信息。若正常则返回 Nothing。
|
||||
''' </summary>
|
||||
Private Function LoadNeoForgeGetError() As String
|
||||
Public Function LoadNeoForgeGetError() As String
|
||||
If SelectedOptiFine IsNot Nothing Then Return "与 OptiFine 不兼容"
|
||||
If SelectedForge IsNot Nothing Then Return "与 Forge 不兼容"
|
||||
If SelectedFabric IsNot Nothing Then Return "与 Fabric 不兼容"
|
||||
@@ -832,10 +844,9 @@ Public Class PageDownloadInstall
|
||||
OptiFine_Loaded()
|
||||
ReloadSelected()
|
||||
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
|
||||
CardNeoForge.IsSwapped = True
|
||||
e.Handled = True
|
||||
OptiFine_Loaded()
|
||||
ReloadSelected()
|
||||
End Sub
|
||||
@@ -847,9 +858,9 @@ Public Class PageDownloadInstall
|
||||
''' <summary>
|
||||
''' 获取 Fabric 的加载异常信息。若正常则返回 Nothing。
|
||||
''' </summary>
|
||||
Private Function LoadFabricGetError() As String
|
||||
Public Function LoadFabricGetError() As String
|
||||
'检查 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
|
||||
If GetLoaderError(LoadFabric) IsNot Nothing Then Return GetLoaderError(LoadFabric)
|
||||
'检查版本
|
||||
@@ -879,9 +890,10 @@ Public Class PageDownloadInstall
|
||||
If Not Versions.Any() Then Return
|
||||
'可视化
|
||||
PanFabric.Children.Clear()
|
||||
PanFabric.Tag = Versions
|
||||
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
|
||||
Logger.Error(ex, "可视化 Fabric 安装版本列表出错")
|
||||
End Try
|
||||
@@ -895,14 +907,15 @@ Public Class PageDownloadInstall
|
||||
CardFabric.IsSwapped = True
|
||||
ReloadSelected()
|
||||
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
|
||||
SelectedFabricApi = Nothing
|
||||
BtnFabricApiClear.Visibility = Visibility.Collapsed
|
||||
AutoSelectedFabricApi = False
|
||||
SelectedOptiFabric = Nothing
|
||||
BtnOptiFabricClear.Visibility = Visibility.Collapsed
|
||||
AutoSelectedOptiFabric = False
|
||||
CardFabric.IsSwapped = True
|
||||
e.Handled = True
|
||||
ReloadSelected()
|
||||
End Sub
|
||||
|
||||
@@ -911,14 +924,14 @@ Public Class PageDownloadInstall
|
||||
#Region "Fabric API 列表"
|
||||
|
||||
''' <summary>
|
||||
''' 判断某 Fabric API 是否适配当前选择的原版版本。
|
||||
''' 判断某 Fabric API 是否适配指定的原版版本。
|
||||
''' </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
|
||||
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
|
||||
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 Not FabricApiName.Contains("/") OrElse Not FabricApiName.Contains("]") Then Return False
|
||||
'直接的判断(例如 1.18.1/22w03a)
|
||||
@@ -946,7 +959,7 @@ Public Class PageDownloadInstall
|
||||
End While
|
||||
Return True
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, $"判断 Fabric API 版本适配性出错({FabricApiName}, {VanillaName})")
|
||||
Logger.Warn(ex, $"判断 Fabric API 版本适配性出错({FabricApiName}, {MinecraftVersion})")
|
||||
Return False
|
||||
End Try
|
||||
End Function
|
||||
@@ -954,12 +967,12 @@ Public Class PageDownloadInstall
|
||||
''' <summary>
|
||||
''' 获取 FabricApi 的加载异常信息。若正常则返回 Nothing。
|
||||
''' </summary>
|
||||
Private Function LoadFabricApiGetError() As String
|
||||
Public Function LoadFabricApiGetError() As String
|
||||
'检查 Loader
|
||||
If GetLoaderError(LoadFabricApi) IsNot Nothing Then Return GetLoaderError(LoadFabricApi)
|
||||
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)
|
||||
Else
|
||||
Return "无"
|
||||
@@ -971,7 +984,7 @@ Public Class PageDownloadInstall
|
||||
If LoadFabricApiGetError() IsNot Nothing Then e.Handled = True
|
||||
End Sub
|
||||
|
||||
Private AutoSelectedFabricApi As Boolean = False
|
||||
Public AutoSelectedFabricApi As Boolean = False
|
||||
''' <summary>
|
||||
''' 尝试重新可视化 FabricApi 版本列表。
|
||||
''' </summary>
|
||||
@@ -982,7 +995,7 @@ Public Class PageDownloadInstall
|
||||
'获取版本列表
|
||||
Dim Versions As New List(Of ResourceVersion)
|
||||
For Each Version In DlFabricApiLoader.Output
|
||||
If IsFabricApiCompatible(Version) Then
|
||||
If IsFabricApiCompatible(Version, VanillaName) Then
|
||||
If Not Version.Display.StartsWithF("[") Then
|
||||
Logger.Warn($"已特判修改 Fabric API 显示名:{Version.Display}")
|
||||
Version.Display = "[" & VanillaName & "] " & Version.Display
|
||||
@@ -995,7 +1008,6 @@ Public Class PageDownloadInstall
|
||||
'可视化
|
||||
PanFabricApi.Children.Clear()
|
||||
For Each Version In Versions
|
||||
If Not IsFabricApiCompatible(Version) Then Continue For
|
||||
PanFabricApi.Children.Add(FabricApiDownloadListItem(Version, AddressOf FabricApi_Selected))
|
||||
Next
|
||||
'自动选择 Fabric API
|
||||
@@ -1016,10 +1028,9 @@ Public Class PageDownloadInstall
|
||||
CardFabricApi.IsSwapped = True
|
||||
ReloadSelected()
|
||||
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
|
||||
CardFabricApi.IsSwapped = True
|
||||
e.Handled = True
|
||||
ReloadSelected()
|
||||
End Sub
|
||||
|
||||
@@ -1028,23 +1039,23 @@ Public Class PageDownloadInstall
|
||||
#Region "OptiFabric 列表"
|
||||
|
||||
''' <summary>
|
||||
''' 判断某 OptiFabric 是否适配当前选择的原版版本。
|
||||
''' 判断某 OptiFabric 是否适配指定的原版版本。
|
||||
''' </summary>
|
||||
Private Function IsOptiFabricCompatible(ModFile As ResourceVersion) As Boolean
|
||||
Public Shared Function IsOptiFabricCompatible(ModFile As ResourceVersion, MinecraftVersion As String) As Boolean
|
||||
Try
|
||||
If VanillaName Is Nothing Then Return False
|
||||
Return ModFile.GameVersions.Contains(VanillaName)
|
||||
If MinecraftVersion Is Nothing Then Return False
|
||||
Return ModFile.GameVersions.Contains(MinecraftVersion)
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, $"判断 OptiFabric 版本适配性出错({VanillaName})")
|
||||
Logger.Warn(ex, $"判断 OptiFabric 版本适配性出错({MinecraftVersion})")
|
||||
Return False
|
||||
End Try
|
||||
End Function
|
||||
|
||||
Private AutoSelectedOptiFabric As Boolean = False
|
||||
Public AutoSelectedOptiFabric As Boolean = False
|
||||
''' <summary>
|
||||
''' 获取 OptiFabric 的加载异常信息。若正常则返回 Nothing。
|
||||
''' </summary>
|
||||
Private Function LoadOptiFabricGetError() As String
|
||||
Public Function LoadOptiFabricGetError() As String
|
||||
If VanillaDrop >= 140 AndAlso VanillaDrop <= 150 Then Return "不兼容老版本 Fabric,请手动下载 OptiFabric Origins"
|
||||
'检查 Loader
|
||||
If GetLoaderError(LoadOptiFabric) IsNot Nothing Then Return GetLoaderError(LoadOptiFabric)
|
||||
@@ -1056,7 +1067,7 @@ Public Class PageDownloadInstall
|
||||
Return "获取中……"
|
||||
End If
|
||||
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 Then Return "需要安装 Fabric"
|
||||
If SelectedOptiFine Is Nothing Then Return "需要安装 OptiFine"
|
||||
@@ -1080,7 +1091,7 @@ Public Class PageDownloadInstall
|
||||
'获取版本列表
|
||||
Dim Versions As New List(Of ResourceVersion)
|
||||
For Each Version In DlOptiFabricLoader.Output
|
||||
If IsOptiFabricCompatible(Version) Then Versions.Add(Version)
|
||||
If IsOptiFabricCompatible(Version, VanillaName) Then Versions.Add(Version)
|
||||
Next
|
||||
If Not Versions.Any() Then Return
|
||||
'排序
|
||||
@@ -1088,7 +1099,6 @@ Public Class PageDownloadInstall
|
||||
'可视化
|
||||
PanOptiFabric.Children.Clear()
|
||||
For Each Version In Versions
|
||||
If Not IsOptiFabricCompatible(Version) Then Continue For
|
||||
PanOptiFabric.Children.Add(OptiFabricDownloadListItem(Version, AddressOf OptiFabric_Selected))
|
||||
Next
|
||||
'自动选择 OptiFabric
|
||||
@@ -1108,10 +1118,9 @@ Public Class PageDownloadInstall
|
||||
CardOptiFabric.IsSwapped = True
|
||||
ReloadSelected()
|
||||
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
|
||||
CardOptiFabric.IsSwapped = True
|
||||
e.Handled = True
|
||||
ReloadSelected()
|
||||
End Sub
|
||||
|
||||
@@ -1119,10 +1128,42 @@ Public Class PageDownloadInstall
|
||||
|
||||
#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
|
||||
If e.Key = Key.Enter AndAlso BtnStart.IsEnabled Then BtnStart_Click()
|
||||
If e.Key = Key.Enter AndAlso IsInSelectPage AndAlso BtnStart.IsEnabled Then BtnStart_Click()
|
||||
End Sub
|
||||
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
|
||||
(Settings.Get(Of Integer)("LaunchArgumentIndieV2") = 0 OrElse Settings.Get(Of Integer)("LaunchArgumentIndieV2") = 2) Then
|
||||
@@ -1132,22 +1173,7 @@ Public Class PageDownloadInstall
|
||||
Return
|
||||
End If
|
||||
End If
|
||||
'提交安装申请
|
||||
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
|
||||
If Not McInstallStart(Request) Then Return
|
||||
'返回,这样在再次进入安装页面时这个版本就会显示文件夹已重复
|
||||
ExitSelectPage()
|
||||
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 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
|
||||
'比较全部
|
||||
Return -CompareVersion(x, y)
|
||||
Return -McUtils.CompareVersion(x, y)
|
||||
End Function
|
||||
Public Sub New(Optional Topmost As String = "")
|
||||
Me.Topmost = If(Topmost, "")
|
||||
@@ -98,7 +98,7 @@ Public Class PageDownloadResourceDetail
|
||||
Dim UpdateFilters =
|
||||
Sub()
|
||||
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
|
||||
|
||||
'确定分组方式
|
||||
@@ -174,11 +174,8 @@ GroupDone:
|
||||
Dim Loaders As New List(Of String)
|
||||
If Project.ModLoaders.Flags.Count > 1 AndAlso '工程至少有两个加载器
|
||||
Version.ResourceType = ResourceTypes.Mod AndAlso '是 Mod
|
||||
McVersion.IsFormatFit(VerName) Then '不是 “快照版本” 之类的
|
||||
For Each Loader In Version.ModLoaders.Flags
|
||||
If Loader = ModLoaders.Quilt AndAlso Settings.Get(Of Boolean)("ToolDownloadIgnoreQuilt") Then Continue For
|
||||
Loaders.Add(Loader.ToString & " ")
|
||||
Next
|
||||
McVersion.IsStandardFormat(VerName) Then '不是 “快照版本” 之类的
|
||||
Loaders.AddRange(Version.ModLoaders.Flags.Select(Function(f) f.ToString & " "))
|
||||
End If
|
||||
If Not Loaders.Any() Then Loaders.Add("") '保底加一个空的,确保它在一张卡片里
|
||||
'实际添加
|
||||
@@ -209,16 +206,17 @@ GroupDone:
|
||||
If Not Pair.Value.Any() 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}
|
||||
NewCard.Children.Add(NewStack)
|
||||
NewCard.SwapControl = NewStack
|
||||
NewCard.SetContentBuilder(AddressOf InitializeStackResourceVersions)
|
||||
PanResults.Children.Add(NewCard)
|
||||
'确定卡片是否默认展开
|
||||
If Pair.Key = TargetCardName OrElse
|
||||
(FrmMain.PageCurrent.Additional IsNot Nothing AndAlso '#2761
|
||||
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
|
||||
NewCard.IsSwapped = True
|
||||
End If
|
||||
@@ -243,13 +241,53 @@ GroupDone:
|
||||
Logger.Error(ex, "可视化工程下载列表出错")
|
||||
End Try
|
||||
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
|
||||
Dim Drop = McVersion.VersionToDrop(Name)
|
||||
If Name Is Nothing Then
|
||||
Return "其他"
|
||||
ElseIf Name.Contains("w") OrElse Drop = 209 Then
|
||||
Return "快照版"
|
||||
ElseIf Not McVersion.IsFormatFit(Name) OrElse (FoldOld AndAlso Drop < 120) Then
|
||||
ElseIf Not McVersion.IsStandardFormat(Name) OrElse (FoldOld AndAlso Drop < 120) Then
|
||||
Return "远古版"
|
||||
ElseIf GroupedByDrop Then
|
||||
Return McVersion.DropToVersion(Drop)
|
||||
@@ -329,7 +367,7 @@ GroupDone:
|
||||
Loader.Start(McFolderSelected & "versions\" & InstanceName & "\")
|
||||
LoaderTaskbarAdd(Loader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
FrmMain.BtnExtraDownload.Ribble()
|
||||
FrmMain.PageChange(FormMain.PageType.TaskManager)
|
||||
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex, "下载资源整合包失败")
|
||||
@@ -472,19 +510,23 @@ GroupDone:
|
||||
'弹窗要求选择保存位置
|
||||
Dim Target As String
|
||||
Dim Ext As String = If(File.ResourceType = ResourceTypes.Mod,
|
||||
If(File.FileName.EndsWithF(".litemod"), "litemod", "jar"),
|
||||
If(File.FileName.EndsWithF(".mrpack"), "mrpack", "zip"))
|
||||
If(PathUtils.GetExtension(File.FileName) = "litemod", "litemod", "jar"),
|
||||
If(PathUtils.GetExtension(File.FileName) = "mrpack", "mrpack", "zip"))
|
||||
Target = Dialogs.SaveFile("选择保存位置", FileName, DefaultFolder, {(Ext, Desc & "文件")})
|
||||
If Target Is Nothing Then Return
|
||||
'构造步骤加载器
|
||||
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)
|
||||
Loaders.Add(New LoaderDownload("下载文件", {
|
||||
File.ToNetFile(Target, If(TargetResourceType = ResourceTypes.Any, ResourceVersion.DownloadReason.Dependency, ResourceVersion.DownloadReason.Standalone))
|
||||
}) 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)
|
||||
LoaderTaskbarAdd(Loader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
@@ -513,29 +555,4 @@ GroupDone:
|
||||
PageLoaderRestart(IsForceRestart:=True)
|
||||
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
|
||||
@@ -34,6 +34,7 @@
|
||||
<TextBlock VerticalAlignment="Center" Grid.Row="2" HorizontalAlignment="Left" Text="版本" Margin="0,0,18,0" />
|
||||
<local:MyComboBox x:Name="TextSearchVersion" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" IsEditable="True" IsTextSearchEnabled="False">
|
||||
<local:MyComboBoxItem Content="全部 (也可自行输入)" IsSelected="True" />
|
||||
<local:MyComboBoxItem Content="26.2" />
|
||||
<local:MyComboBoxItem Content="26.1" />
|
||||
<local:MyComboBoxItem Content="1.21.11" />
|
||||
<local:MyComboBoxItem Content="1.21.8" />
|
||||
@@ -51,7 +52,6 @@
|
||||
<local:MyComboBoxItem Content="Forge" Tag="1" />
|
||||
<local:MyComboBoxItem Content="NeoForge" Tag="16" />
|
||||
<local:MyComboBoxItem Content="Fabric" Tag="4" />
|
||||
<local:MyComboBoxItem Content="Quilt" Tag="8" />
|
||||
<local:MyComboBoxItem Content="LiteLoader" Tag="2" />
|
||||
</local:MyComboBox>
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="4" Grid.Row="2" HorizontalAlignment="Left" Text="类型" Margin="0,0,18,0" />
|
||||
|
||||
@@ -90,11 +90,11 @@ Public Class PageResource
|
||||
Next
|
||||
Return ComboSearchLoader.Items(0)
|
||||
End Function
|
||||
If TargetInstance.Version.HasForge Then
|
||||
If TargetInstance.Version.ModLoaders.HasFlagF(ModLoaders.Forge) Then
|
||||
ComboSearchLoader.SelectedItem = GetTargetItemByName("Forge")
|
||||
ElseIf TargetInstance.Version.HasFabric Then
|
||||
ElseIf TargetInstance.Version.ModLoaders.HasFlagF(ModLoaders.Fabric) Then
|
||||
ComboSearchLoader.SelectedItem = GetTargetItemByName("Fabric")
|
||||
ElseIf TargetInstance.Version.HasNeoForge Then
|
||||
ElseIf TargetInstance.Version.ModLoaders.HasFlagF(ModLoaders.NeoForge) Then
|
||||
ComboSearchLoader.SelectedItem = GetTargetItemByName("NeoForge")
|
||||
End If
|
||||
TargetInstance = Nothing
|
||||
|
||||
@@ -335,7 +335,7 @@ Public Class MyLocalModItem
|
||||
BtnUpdate.Visibility = Visibility.Collapsed
|
||||
End If
|
||||
'标题与描述
|
||||
Dim DescFileName As String = Entry.File.Name.BeforeLast(".")
|
||||
Dim DescFileName As String = PathUtils.GetFileNameWithoutExtension(Entry.EnabledName)
|
||||
Dim NewDescription As String
|
||||
If Settings.Get(Of Integer)("ToolModLocalNameStyle") = 1 Then
|
||||
'标题显示文件名,详情显示译名
|
||||
|
||||
@@ -181,6 +181,18 @@
|
||||
RequireModLoaderOrOptiFine="True" />
|
||||
</local:MyCheckBox.Tag>
|
||||
</local:MyCheckBox>
|
||||
<StackPanel Margin="30,0,0,0"
|
||||
Visibility="{Binding Checked, ElementName=CheckOptionsShaderPacks, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<local:MyCheckBox x:Name="CheckOptionsShaderPackSettings">
|
||||
<local:MyCheckBox.Tag>
|
||||
<local:ExportOption
|
||||
Title="光影包设置"
|
||||
Description="已选光影包的自定义设置"
|
||||
ShowRules="shaderpacks/*.txt"
|
||||
DefaultChecked="True" />
|
||||
</local:MyCheckBox.Tag>
|
||||
</local:MyCheckBox>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="30,0,0,0" x:Name="PanOptionsShaderPacks"
|
||||
Visibility="{Binding Checked, ElementName=CheckOptionsShaderPacks, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
</StackPanel>
|
||||
|
||||
@@ -22,14 +22,14 @@ Public Class PageInstanceExport
|
||||
End Sub
|
||||
Private Sub PageInstanceExport_Loaded() Handles Me.Loaded
|
||||
AniControlEnabled += 1
|
||||
If CurrentInstance <> PageInstanceLeft.Instance.PathVersion Then RefreshAll() '切换到了另一个版本,重置页面
|
||||
If Not PathUtils.IsSame(CurrentInstance, PageInstanceLeft.Instance.PathVersion) Then RefreshAll() '切换到了另一个版本,重置页面
|
||||
CustomEventService.SetEventData(BtnAdvancedHelp,
|
||||
If(BuildType = BuildTypes.Release, "指南/整合包制作 - Public.json", "指南/整合包制作 - Snapshot.json"))
|
||||
AniControlEnabled -= 1
|
||||
End Sub
|
||||
Public Sub RefreshAll() Implements IRefreshable.Refresh
|
||||
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
|
||||
TextExportName.Text = ""
|
||||
TextExportName.HintText = PageInstanceLeft.Instance.Name
|
||||
@@ -76,6 +76,7 @@ Public Class PageInstanceExport
|
||||
If Not SubFolder.EnumerateFileSystemInfos().Any() Then Continue For
|
||||
Dim NewCheckBox As New MyCheckBox With {. _
|
||||
Tag = New ExportOption With {.Title = SubFolder.Name, .DefaultChecked = True, .Rules = StringUtils.LikePatternEscape($"{Folder}/{SubFolder.Name}/")}}
|
||||
If AcceptCompressedFile Then GetExportOption(NewCheckBox).Description = "文件夹"
|
||||
If Panel Is PanOptionsSaves Then GetExportOption(NewCheckBox).Description = SubFolder.LastWriteTime.ToString("yyyy'/'MM'/'dd HH':'mm")
|
||||
Panel.Children.Add(NewCheckBox)
|
||||
Next
|
||||
@@ -113,7 +114,7 @@ Public Class PageInstanceExport
|
||||
'确认选项是否应该被显示
|
||||
Dim IsVisible =
|
||||
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.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
|
||||
@@ -249,9 +250,22 @@ Public Class PageInstanceExport
|
||||
For Each CheckBox In GetAllOptions(False)
|
||||
If Not CheckBox.Checked Then Continue For
|
||||
Dim TargetOption = GetExportOption(CheckBox)
|
||||
If TargetOption.Rules Is Nothing Then Continue For
|
||||
Dim Rules = If(TargetOption.Rules?.Split("|"c).ToList(), New List(Of String))
|
||||
|
||||
'动态 Rules
|
||||
If CheckBox Is CheckOptionsShaderPackSettings Then '光影包设置:只导出勾选的光影包的设置
|
||||
For Each ShaderPackCheckBox In PanOptionsShaderPacks.Children.OfType(Of MyCheckBox)()
|
||||
If Not ShaderPackCheckBox.Checked Then Continue For
|
||||
Dim ShaderPackOption = GetExportOption(ShaderPackCheckBox)
|
||||
Dim SettingsFile = $"shaderpacks/{ShaderPackOption.Title}.txt"
|
||||
If Not FileUtils.Exists(Path.Combine(PageInstanceLeft.Instance.PathIndie, SettingsFile)) Then Continue For
|
||||
Rules.Add(StringUtils.LikePatternEscape(SettingsFile))
|
||||
Next
|
||||
End If
|
||||
|
||||
If Not Rules.Any() Then Continue For
|
||||
Yield $"# {TargetOption.Title}"
|
||||
For Each Rule In TargetOption.Rules.Split("|"c)
|
||||
For Each Rule In Rules
|
||||
Yield Rule
|
||||
Next
|
||||
Yield ""
|
||||
@@ -412,7 +426,7 @@ Public Class PageInstanceExport
|
||||
Dim LoaderName As String = $"导出整合包:" & PackName
|
||||
For Each OngoingLoader In LoaderTaskbar
|
||||
If OngoingLoader.Name <> LoaderName Then Continue For
|
||||
FrmMain.PageChange(FormMain.PageType.DownloadManager)
|
||||
FrmMain.PageChange(FormMain.PageType.TaskManager)
|
||||
Return
|
||||
Next
|
||||
|
||||
@@ -443,11 +457,11 @@ Public Class PageInstanceExport
|
||||
'缓存所需参数
|
||||
Dim CacheFolder = RequestTaskTempFolder()
|
||||
Dim OverridesFolder = CacheFolder & "modpack\overrides\"
|
||||
Dim McVersion = PageInstanceLeft.Instance
|
||||
Dim TargetInstance = PageInstanceLeft.Instance
|
||||
Dim IncludeJava As Boolean =
|
||||
(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)
|
||||
Dim PathIndie As String = McVersion.PathIndie
|
||||
(RulesOverrides IsNot Nothing AndAlso CheckOptionsJava.Checked AndAlso Settings.Get(Of Integer)("VersionArgumentJavaV2", TargetInstance) = 2)
|
||||
Dim PathIndie As String = TargetInstance.PathIndie
|
||||
Dim CheckHostedAssets As Boolean = Not CheckAdvancedInclude.Checked
|
||||
Dim ModrinthUploadMode As Boolean = CheckAdvancedModrinth.Checked
|
||||
Dim IncludePCL As Boolean = CheckOptionsPcl.Checked
|
||||
@@ -484,7 +498,7 @@ Public Class PageInstanceExport
|
||||
'文件夹:进一步搜索
|
||||
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
|
||||
SearchFolder(SubFolder)
|
||||
Next
|
||||
@@ -522,10 +536,10 @@ Public Class PageInstanceExport
|
||||
Loader.Progress = 0.85
|
||||
'复制 Java
|
||||
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。")
|
||||
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)
|
||||
Logger.Info($"已复制版本文件夹中的 Java:{JavaFolder} → {DestFolder}")
|
||||
End If
|
||||
@@ -549,7 +563,7 @@ Public Class PageInstanceExport
|
||||
Next
|
||||
Loader.Progress = 0.97
|
||||
'复制 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)
|
||||
'复制 PCL 本体(正式版)
|
||||
If BuildType = BuildTypes.Release AndAlso IncludePCL Then FileUtils.Copy(PathExe, CacheFolder & "Plain Craft Launcher.exe")
|
||||
@@ -608,7 +622,7 @@ Public Class PageInstanceExport
|
||||
Try
|
||||
If ModrinthUploadMode Then Return 'Modrinth 上传模式下,不从 CurseForge 获取信息
|
||||
Dim CurseForgeHashes = Loader.Input.Select(Function(m) m.CurseForgeHash)
|
||||
Dim CurseForgeRaw As JContainer = DlModRequest("https://api.curseforge.com/v1/fingerprints/432/", HttpMethod.Post,
|
||||
Dim CurseForgeRaw As JContainer = DlModRequest("https://api.curseforge.com/v1/fingerprints/432", HttpMethod.Post,
|
||||
$"{{""fingerprints"": [{CurseForgeHashes.Join(",")}]}}", "application/json")("data")("exactMatches")
|
||||
For Each ResultJson As JObject In CurseForgeRaw
|
||||
If Not ResultJson.ContainsKey("file") Then Continue For
|
||||
@@ -672,16 +686,16 @@ Public Class PageInstanceExport
|
||||
Next
|
||||
Loader.Progress = 0.2
|
||||
'导出最终 JSON 文件
|
||||
Dim Dependencies As New JObject From {{"minecraft", McVersion.Version.VanillaName}}
|
||||
If McVersion.Version.HasForge Then Dependencies.Add("forge", McVersion.Version.Forge)
|
||||
If McVersion.Version.HasFabric Then Dependencies.Add("fabric-loader", McVersion.Version.Fabric)
|
||||
If McVersion.Version.HasNeoForge Then Dependencies.Add("neoforge", McVersion.Version.NeoForge)
|
||||
Dim Dependencies As New JObject From {{"minecraft", TargetInstance.Version.VanillaName}}
|
||||
If TargetInstance.Version.ModLoaders.HasFlagF(ModLoaders.Forge) Then Dependencies.Add("forge", TargetInstance.Version.ModLoaderVersions(ModLoaders.Forge))
|
||||
If TargetInstance.Version.ModLoaders.HasFlagF(ModLoaders.Fabric) Then Dependencies.Add("fabric-loader", TargetInstance.Version.ModLoaderVersions(ModLoaders.Fabric))
|
||||
If TargetInstance.Version.ModLoaders.HasFlagF(ModLoaders.NeoForge) Then Dependencies.Add("neoforge", TargetInstance.Version.ModLoaderVersions(ModLoaders.NeoForge))
|
||||
Dim ResultJson As New JObject From {
|
||||
{"game", "minecraft"},
|
||||
{"formatVersion", 1},
|
||||
{"versionId", PackVersion},
|
||||
{"name", PackName},
|
||||
{"summary", McVersion.Info},
|
||||
{"summary", TargetInstance.Info},
|
||||
{"files", Files},
|
||||
{"dependencies", Dependencies}
|
||||
}
|
||||
@@ -712,7 +726,7 @@ Public Class PageInstanceExport
|
||||
LoaderTaskbarAdd(MainLoader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
FrmMain.BtnExtraDownload.Ribble()
|
||||
FrmMain.PageChange(FormMain.PageType.DownloadManager)
|
||||
FrmMain.PageChange(FormMain.PageType.TaskManager)
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
@@ -13,21 +13,29 @@
|
||||
</x:Array>
|
||||
</local:MyListItem.Buttons>
|
||||
</local:MyListItem>
|
||||
<local:MyListItem x:Name="ItemMod" IsScaleAnimationEnabled="False" Type="RadioBox" Tag="2" 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 x:Name="ItemModify" IsScaleAnimationEnabled="False" Type="RadioBox" Tag="2" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="修改" LogoScale="0.95"
|
||||
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>
|
||||
<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" />
|
||||
</x:Array>
|
||||
</local:MyListItem.Buttons>
|
||||
</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" />
|
||||
<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">
|
||||
<local:MyListItem.Buttons>
|
||||
<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>
|
||||
</local:MyListItem.Buttons>
|
||||
</local:MyListItem>
|
||||
|
||||
@@ -6,6 +6,10 @@ Public Class PageInstanceLeft
|
||||
''' </summary>
|
||||
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
|
||||
If Instance IsNot Nothing AndAlso Instance.Modable Then
|
||||
ItemMod.Visibility = Visibility.Visible
|
||||
@@ -26,7 +30,7 @@ Public Class PageInstanceLeft
|
||||
''' <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
|
||||
'若使用 IsLoaded,则会导致模拟点击不被执行(模拟点击切换页面时,控件的 IsLoaded 为 False)
|
||||
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
|
||||
Return FrmInstanceModDisabled
|
||||
Case FormMain.PageSubType.InstanceSetup
|
||||
If IsNothing(FrmInstanceSetup) Then FrmInstanceSetup = New PageInstanceSetup
|
||||
If FrmInstanceSetup Is Nothing Then FrmInstanceSetup = New PageInstanceSetup
|
||||
Return FrmInstanceSetup
|
||||
Case FormMain.PageSubType.InstanceExport
|
||||
If FrmInstanceExport Is Nothing Then FrmInstanceExport = New PageInstanceExport
|
||||
Return FrmInstanceExport
|
||||
Case FormMain.PageSubType.InstanceModify
|
||||
If FrmDownloadModify Is Nothing Then FrmDownloadModify = New PageDownloadModify
|
||||
Return FrmDownloadModify
|
||||
Case Else
|
||||
Throw New Exception("未知的版本设置子页面种类:" & ID)
|
||||
End Select
|
||||
@@ -60,6 +67,10 @@ Public Class PageInstanceLeft
|
||||
''' </summary>
|
||||
Public Sub PageChange(ID As FormMain.PageSubType)
|
||||
If PageID = ID Then Return
|
||||
If Instance IsNot Nothing AndAlso McInstanceLock.IsLocked(Instance) Then
|
||||
Hint("该版本正在被修改,请稍后再切换页面!", HintType.Red)
|
||||
Return
|
||||
End If
|
||||
AniControlEnabled += 1
|
||||
Try
|
||||
PageChangeRun(PageGet(ID))
|
||||
@@ -98,13 +109,20 @@ Public Class PageInstanceLeft
|
||||
Refresh(FrmMain.PageCurrentSub)
|
||||
End Sub
|
||||
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
|
||||
Case FormMain.PageSubType.InstanceMod
|
||||
PageInstanceMod.Refresh()
|
||||
ItemMod.Checked = True
|
||||
Case FormMain.PageSubType.InstanceExport
|
||||
If FrmInstanceExport IsNot Nothing Then FrmInstanceExport.RefreshAll()
|
||||
FrmInstanceExport?.RefreshAll()
|
||||
ItemExport.Checked = True
|
||||
Case FormMain.PageSubType.InstanceModify
|
||||
FrmDownloadModify?.PageLoaderRestart()
|
||||
ItemModify.Checked = True
|
||||
End Select
|
||||
End Sub
|
||||
|
||||
|
||||
@@ -284,7 +284,7 @@ Public Class PageInstanceMod
|
||||
''' 返回输入的文件是否为一个 Mod 文件,仅用于判断拖拽行为。
|
||||
''' </summary>
|
||||
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
|
||||
Logger.Info("文件为 jar/litemod 格式,尝试作为 Mod 安装")
|
||||
@@ -307,12 +307,15 @@ Public Class PageInstanceMod
|
||||
Install:
|
||||
Try
|
||||
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
|
||||
FileUtils.Copy(ModFile, Instance.PathIndie & "mods\" & NewFileName)
|
||||
NewFileName = Instance.PathIndie & "mods\" & NewFileName
|
||||
If PathUtils.IsSame(ModFile, NewFileName) Then Continue For '防止安装相同文件导致文件被删除
|
||||
FileUtils.Delete(NewFileName, True)
|
||||
FileUtils.Copy(ModFile, NewFileName)
|
||||
Next
|
||||
If FilePathList.IsSingle Then
|
||||
Hint($"已安装 {PathUtils.GetLastPart(FilePathList.First).Replace(".disabled", "").Replace(".old", "")}!", HintType.Green)
|
||||
Hint($"已安装 {(New LocalResourceFile(FilePathList.First)).EnabledName}!", HintType.Green)
|
||||
Else
|
||||
Hint($"已安装 {FilePathList.Count} 个 Mod!", HintType.Green)
|
||||
End If
|
||||
@@ -550,8 +553,8 @@ Install:
|
||||
Dim File As ResourceVersion = Entry.UpdateFile
|
||||
If Not File.DownloadAvailable Then Continue For
|
||||
'确认更新后的文件名
|
||||
Dim CurrentReplaceName = Entry.ProjectVersion.FileName.Replace(".jar", "").Replace(".old", "").Replace(".disabled", "")
|
||||
Dim NewestReplaceName = Entry.UpdateFile.FileName.Replace(".jar", "").Replace(".old", "").Replace(".disabled", "")
|
||||
Dim CurrentReplaceName = PathUtils.GetFileNameWithoutExtension(Entry.ProjectVersion.FileName)
|
||||
Dim NewestReplaceName = PathUtils.GetFileNameWithoutExtension(Entry.UpdateFile.FileName)
|
||||
Dim CurrentSegs = CurrentReplaceName.Split("-"c).ToList()
|
||||
Dim NewestSegs = NewestReplaceName.Split("-"c).ToList()
|
||||
Dim Shortened As Boolean = False
|
||||
|
||||
@@ -62,18 +62,12 @@
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
<local:MyCard Margin="0,0,0,15" Title="快捷方式" x:Name="PanFolder">
|
||||
<StackPanel Margin="25,40,25,15">
|
||||
<Grid Margin="0,2,0,7" Height="35">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<local:MyButton x:Name="BtnFolderVersion" Text="版本文件夹" MinWidth="140" Padding="13,0" Margin="0,0,20,0" />
|
||||
<local:MyButton x:Name="BtnFolderSaves" Text="存档文件夹" MinWidth="140" Padding="13,0" Margin="0,0,20,0" Grid.Column="1" />
|
||||
<local:MyButton x:Name="BtnFolderMods" Text="Mod 文件夹" MinWidth="140" Padding="13,0" Margin="0,0,20,0" Grid.Column="2" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<WrapPanel Margin="25,30,25,20">
|
||||
<local:MyButton x:Name="BtnFolderVersion" Text="版本文件夹" MinWidth="140" Padding="13,0" Margin="0,13,20,0" Height="35" />
|
||||
<local:MyButton x:Name="BtnFolderSaves" Text="存档文件夹" MinWidth="140" Padding="13,0" Margin="0,13,20,0" Height="35"/>
|
||||
<local:MyButton x:Name="BtnFolderMods" Text="Mod 文件夹" MinWidth="140" Padding="13,0" Margin="0,13,20,0" Height="35"/>
|
||||
<local:MyButton x:Name="BtnFolderScreen" Text="截图文件夹" MinWidth="140" Padding="13,0" Margin="0,13,20,0" Height="35" />
|
||||
</WrapPanel>
|
||||
</local:MyCard>
|
||||
<local:MyCard Margin="0,0,0,15" Title="高级管理" x:Name="PanManage">
|
||||
<StackPanel Margin="25,40,25,15">
|
||||
|
||||
@@ -104,50 +104,21 @@ Public Class PageInstanceOverall
|
||||
'重命名版本
|
||||
Private Sub BtnDisplayRename_Click(sender As Object, e As EventArgs) Handles BtnDisplayRename.Click
|
||||
Try
|
||||
If McInstanceLock.IsLocked(PageInstanceLeft.Instance) Then
|
||||
Hint("该版本正在被修改,请稍后再重命名!", HintType.Red)
|
||||
Return
|
||||
End If
|
||||
'确认输入的新名称
|
||||
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 {
|
||||
New ValidateFolderName(McFolderSelected & "versions", IgnoreCase:=False)})
|
||||
New ValidateFolderName(McFolderSelected & "versions", IgnoreList:=New List(Of String) From {OldName})
|
||||
})
|
||||
If String.IsNullOrWhiteSpace(NewName) Then Return
|
||||
Dim NewPath As String = McFolderSelected & "versions\" & 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
|
||||
Dim NewInstance = PageInstanceLeft.Instance.Rename(NewName)
|
||||
'刷新与提示
|
||||
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)
|
||||
Reload()
|
||||
LoaderFolderRun(McInstanceListLoader, McFolderSelected, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\")
|
||||
@@ -157,7 +128,7 @@ Public Class PageInstanceOverall
|
||||
End Sub
|
||||
|
||||
'版本图标
|
||||
Private Sub ComboDisplayLogo_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles ComboDisplayLogo.SelectionChanged
|
||||
Private Sub ComboDisplayLogo_SelectionChanged() Handles ItemDisplayLogoCustom.PreviewMouseLeftButtonUp, ComboDisplayLogo.SelectionChanged
|
||||
If Not (IsLoad AndAlso AniControlEnabled = 0) Then Return
|
||||
'选择 自定义 时修改图片
|
||||
Try
|
||||
@@ -228,6 +199,13 @@ Public Class PageInstanceOverall
|
||||
OpenExplorer(Folder)
|
||||
End Sub
|
||||
|
||||
'截图文件夹
|
||||
Private Sub BtnFolderScreen_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnFolderScreen.Click
|
||||
Dim Folder As String = PageInstanceLeft.Instance.PathIndie & "screenshots\"
|
||||
DirectoryUtils.Create(Folder)
|
||||
OpenExplorer(Folder)
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "卡片:管理"
|
||||
@@ -259,6 +237,10 @@ Public Class PageInstanceOverall
|
||||
'补全文件
|
||||
Private Sub BtnManageCheck_Click(sender As Object, e As EventArgs) Handles BtnManageCheck.Click
|
||||
Try
|
||||
If McInstanceLock.IsLocked(PageInstanceLeft.Instance) Then
|
||||
Hint("该版本正在被修改,请稍后再补全文件!", HintType.Red)
|
||||
Return
|
||||
End If
|
||||
'忽略文件检查提示
|
||||
If ShouldIgnoreFileCheck(PageInstanceLeft.Instance) Then
|
||||
Hint("请先关闭 [版本设置 → 设置 → 高级启动选项 → 关闭文件校验],然后再尝试补全文件!", HintType.Blue)
|
||||
@@ -296,10 +278,17 @@ Public Class PageInstanceOverall
|
||||
'修改此代码时,同时修改 PageSelectRight 中的代码
|
||||
Private Sub BtnManageDelete_Click(sender As Object, e As EventArgs) Handles BtnManageDelete.Click
|
||||
Try
|
||||
If McInstanceLock.IsLocked(PageInstanceLeft.Instance) Then
|
||||
Hint("该版本正在被修改,请稍后再删除!", HintType.Red)
|
||||
Return
|
||||
End If
|
||||
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 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} 吗?" &
|
||||
If(IsHintIndie, vbCrLf & "由于该版本开启了版本隔离,删除版本时该版本对应的存档、资源包、Mod 等文件也将被一并删除!", ""),
|
||||
If(IsHintIndie, vbCrLf & "该版本对应的存档、资源包、Mod 等文件也将被一并删除!" &
|
||||
If(SaveEntries.Any, vbCrLf & vbCrLf & "这会删除以下存档:" & vbCrLf & "· " & SaveEntries.Join(vbCrLf & "· "), ""), ""),
|
||||
"版本删除确认", , "取消",, IsHintIndie OrElse IsShiftPressed)
|
||||
Case 1
|
||||
PageInstanceLeft.Instance.ResetSettingsCache()
|
||||
|
||||
@@ -83,7 +83,7 @@ Public Class PageInstanceSetup
|
||||
Settings.Reset("VersionServerLogin", Instance:=PageInstanceLeft.Instance)
|
||||
Settings.Reset("VersionArgumentIndieV2", Instance:=PageInstanceLeft.Instance)
|
||||
Settings.Reset("VersionAdvanceAssets", Instance:=PageInstanceLeft.Instance)
|
||||
Configs.InstanceForcedJava.Reset(PageInstanceLeft.Instance.Config)
|
||||
Configs.JavaForced.Reset(PageInstanceLeft.Instance.Config)
|
||||
JavaListRefreshWorker.Start()
|
||||
|
||||
Logger.Info("已初始化版本独立设置")
|
||||
@@ -474,7 +474,7 @@ PreFin:
|
||||
'========================================== 显示结果 ==========================================
|
||||
BtnArgumentJavaSearch.IsEnabled = True
|
||||
ComboArgumentJavaSelect.IsEnabled = True
|
||||
Dim SelectedJava As Java = Configs.InstanceForcedJava.Get(PageInstanceLeft.Instance.Config)
|
||||
Dim SelectedJava As Java = Configs.JavaForced.Get(PageInstanceLeft.Instance.Config)
|
||||
'更新下拉框文本
|
||||
If Not Configs.JavaList.Get().Any() Then
|
||||
LabArgumentJava.Text = "未找到 Java,点击以导入已有的 Java"
|
||||
@@ -492,14 +492,14 @@ PreFin:
|
||||
AddHandler JavaItem.MouseLeftButtonUp, Sub(sender As Object, e As MouseButtonEventArgs) e.Handled = True
|
||||
AddHandler JavaItem.Click,
|
||||
Sub()
|
||||
Configs.InstanceForcedJava.Set(JavaEntry, PageInstanceLeft.Instance.Config)
|
||||
Configs.JavaForced.Set(JavaEntry, PageInstanceLeft.Instance.Config)
|
||||
UpdateJavaList()
|
||||
ComboArgumentJavaSelect.IsDropDownOpen = False
|
||||
End Sub
|
||||
ComboArgumentJavaSelect.Items.Add(JavaItem)
|
||||
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}
|
||||
DeleteButton.IsEnabled = Not IsOfficial
|
||||
DeleteButton.ToolTip = If(DeleteButton.IsEnabled, "从列表中移除", "无法移除官方 Java")
|
||||
|
||||
@@ -245,7 +245,8 @@ Retry:
|
||||
{"Cherry Blossom", "樱花披风"}, {"15th Anniversary", "15 周年纪念披风"}, {"Purple Heart", "紫色心形披风"},
|
||||
{"Follower's", "追随者披风"}, {"MCC 15th Year", "MCC 15 周年披风"}, {"Minecraft Experience", "村民救援披风"},
|
||||
{"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 {
|
||||
New MyRadioBox With {.Text = "无披风", .Checked = Not SkinData("capes").Any(Function(c) c("state")?.ToString = "ACTIVE")}}
|
||||
|
||||
@@ -65,7 +65,7 @@ Public Class PageLaunchLeft
|
||||
'确认 Minecraft 版本存在
|
||||
Dim Selection As String = ReadIni(McFolderSelected & "PCL.ini", "Version")
|
||||
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))
|
||||
If Not McInstanceListLoader.State = LoadState.Finished Then LoaderFolderRun(McInstanceListLoader, McFolderSelected, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\", WaitForExit:=True)
|
||||
@@ -691,7 +691,7 @@ ExitRefresh:
|
||||
End If
|
||||
End Sub
|
||||
'取消按钮
|
||||
Private Sub BtnCancel_Click() Handles BtnCancel.Click
|
||||
Public Sub BtnCancel_Click() Handles BtnCancel.Click
|
||||
If McLaunchLoaderReal IsNot Nothing Then
|
||||
McLaunchLoaderReal.Cancel()
|
||||
McLaunchLog("已取消启动")
|
||||
@@ -709,6 +709,10 @@ ExitRefresh:
|
||||
'版本设置按钮
|
||||
Private Sub BtnMore_Click() Handles BtnMore.Click
|
||||
If McLaunchLoader.State = LoadState.Loading Then Return
|
||||
If McInstanceLock.IsLocked(McInstanceSelected) Then
|
||||
Hint("该版本正在被修改,暂时无法进入版本设置!", HintType.Red)
|
||||
Return
|
||||
End If
|
||||
McInstanceSelected.Load()
|
||||
PageInstanceLeft.Instance = McInstanceSelected
|
||||
FrmMain.PageChange(FormMain.PageType.InstanceSetup, 0)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:behaviors="clr-namespace:PCL.Controls.Behaviors"
|
||||
xmlns:local="clr-namespace:PCL" PanScroll="PanBack">
|
||||
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
|
||||
<StackPanel Name="PanMain" Margin="25,25,25,10" Grid.IsSharedSizeScope="True">
|
||||
@@ -11,7 +12,9 @@
|
||||
<StackPanel x:Name="PanCustom">
|
||||
</StackPanel>
|
||||
<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>
|
||||
</StackPanel>
|
||||
</local:MyScrollViewer>
|
||||
|
||||
@@ -89,7 +89,7 @@ Public Class PageLaunchRight
|
||||
Url = "https://files.mcimirror.top/PCL"
|
||||
Case 11
|
||||
Logger.Info("主页预设:杂志主页")
|
||||
Url = "http://118.195.192.193:26995/d/magazine-homepage-pcl/Custom.xaml"
|
||||
Url = "https://gh-proxy.com/https://github.com/Neclyon/Magazine-Homepage-PCL/raw/main/output/Custom.xaml"
|
||||
Case 12
|
||||
Logger.Info("主页预设:PCL GitHub 仪表盘")
|
||||
Url = "https://ddf.pcl-community.top/Custom.xaml"
|
||||
@@ -101,17 +101,40 @@ Public Class PageLaunchRight
|
||||
Url = "https://pcl.wyc-w.top/index.xaml"
|
||||
Case 15
|
||||
Logger.Info("主页预设:Minecraft 芝士站")
|
||||
Url = "https://www.xxag.top/mkss"
|
||||
Url = "https://mkss-services.awaeric.xyz"
|
||||
Case 16
|
||||
Logger.Info("主页预设:整合包推荐引擎")
|
||||
Url = "https://qawsedrftgyhujiko.fun/pcl2/Custom.xaml"
|
||||
Case 17
|
||||
Logger.Info("Bangumi 番剧主页")
|
||||
Url = "https://bangumi.p.kaphia.qzz.io"
|
||||
Logger.Info("主页预设:Bangumi 番剧主页")
|
||||
Url = "https://bangumi.p.kaphia.top"
|
||||
Case 18
|
||||
Logger.Info("主页预设:Bilibili 热门")
|
||||
Url = "https://bilibili.p.kaphia.top"
|
||||
Case 19
|
||||
Logger.Info("主页预设:Music 云音乐热门")
|
||||
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
|
||||
'联网下载
|
||||
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
|
||||
'缓存可用
|
||||
Logger.Info("主页自定义数据来源:联网缓存文件")
|
||||
@@ -139,8 +162,6 @@ Public Class PageLaunchRight
|
||||
Dim Address As String = Task.Input.Address '#3721 中连续触发两次导致内容变化
|
||||
Dim ShouldRefresh As Boolean = Task.Input.ShouldRefresh
|
||||
Try
|
||||
'替换自定义变量与设置
|
||||
Address = ArgumentReplace(Address, AddressOf WebUtility.HtmlEncode)
|
||||
'获取版本校验地址
|
||||
Dim VersionAddress As String
|
||||
If Address.Contains(".xaml") Then
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<TextBlock Name="TextEmail" Text="加载中" Margin="8,4,8,0" FontSize="12" HorizontalAlignment="Center" TextTrimming="CharacterEllipsis" Foreground="{DynamicResource ColorBrushGray3}" />
|
||||
</StackPanel>
|
||||
<local:MyIconButton ToolTip="更换角色" Grid.Row="1" Height="14" Opacity="0" x:Name="BtnEdit" ToolTipService.InitialShowDelay="50" Grid.Column="0" HorizontalAlignment="Center" Logo="F1 M 53.2929,21.2929L 54.7071,22.7071C 56.4645,24.4645 56.4645,27.3137 54.7071,29.0711L 52.2323,31.5459L 44.4541,23.7677L 46.9289,21.2929C 48.6863,19.5355 51.5355,19.5355 53.2929,21.2929 Z M 31.7262,52.052L 23.948,44.2738L 43.0399,25.182L 50.818,32.9601L 31.7262,52.052 Z M 23.2409,47.1023L 28.8977,52.7591L 21.0463,54.9537L 23.2409,47.1023 Z" Margin="8,0,0,0" VerticalAlignment="Center" />
|
||||
<local:MyIconButton ToolTip="退出登录" Grid.Row="1" Height="14" Opacity="0" x:Name="BtnExit" ToolTipService.InitialShowDelay="50" Grid.Column="2" HorizontalAlignment="Center" Logo="F1 M 51.0071,19.0027L 51.0071,27.0038L 48.0067,27.0038L 48.0067,22.0031L 25.0035,22.0031L 25.0035,54.0075L 48.0067,54.0075L 48.0067,49.0068L 51.0071,49.0068L 51.0071,57.008L 22.1698,57.008L 22.0031,19.0027L 51.0071,19.0027 Z M 30.0042,35.0049L 45.2563,35.0049L 37.005,28.0039L 47.0066,28.0039L 58.5082,38.0053L 47.0066,48.0067L 37.005,48.0067L 45.2563,41.0057L 30.0042,41.0057L 30.0042,35.0049 Z" Margin="0,0,8,0" VerticalAlignment="Center" />
|
||||
<local:MyIconButton ToolTip="切换账号" Grid.Row="1" Height="14" Opacity="0" x:Name="BtnExit" ToolTipService.InitialShowDelay="50" Grid.Column="2" HorizontalAlignment="Center" Logo="M512 0A512 512 0 1 0 1024 512 512 512 0 0 0 512 0z m0 941.407086A429.407086 429.407086 0 1 1 941.407086 512 429.407086 429.407086 0 0 1 512 941.407086z m235.139657-368.64H264.045714a43.505371 43.505371 0 0 0-31.246628 72.060343l159.305143 166.765714a41.281829 41.281829 0 1 0 59.626057-56.788114l-94.997943-99.474286H747.227429a41.311086 41.311086 0 1 0 0-82.622172zM262.670629 450.940343H745.764571a43.505371 43.505371 0 0 0 31.246629-72.352914l-159.305143-166.765715a41.281829 41.281829 0 1 0-59.713828 57.022172l94.968685 99.474285H262.670629a41.281829 41.281829 0 0 0 0 82.563658z" Margin="0,0,8,0" VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
<local:MySkin HorizontalAlignment="Center" VerticalAlignment="Top" x:Name="Skin" HasCape="False" />
|
||||
</Grid>
|
||||
@@ -45,7 +45,7 @@ Public Class PageLoginLegacy
|
||||
If LoginData.UserName.Trim = "" Then Return "玩家名不能为空!"
|
||||
If LoginData.UserName.Contains("""") Then Return "玩家名不能包含英文引号!"
|
||||
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
|
||||
Return "自 1.20.3 起,玩家名至多只能包含 16 个字符!"
|
||||
End If
|
||||
|
||||
@@ -18,7 +18,7 @@ Public Class PageLoginMs
|
||||
ComboAccounts.Items.Add(Item)
|
||||
Next
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex, $"微软登录信息出错,登录信息已被重置({Settings.Get(Of String)("LoginMsJson")})", LogBehavior.Toast)
|
||||
Logger.Error(ex, $"微软登录信息出错,登录信息已被重置", LogBehavior.Toast)
|
||||
Settings.Set("LoginMsJson", "{}")
|
||||
End Try
|
||||
'如果不保留输入,刷新列表后自动选择第一项
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
<Separator />
|
||||
<local:MyMenuItem Click="BtnSkinCape_Click" Header="修改披风"/>
|
||||
<local:MyMenuItem Click="BtnSkinCapeRefresh_Click" Header="刷新披风列表"/>
|
||||
<local:MyMenuItem Header="使用 CDKEY 兑换奖励"
|
||||
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://www.minecraft.net/zh-hans/redeem" />
|
||||
</ContextMenu>
|
||||
</local:MyIconButton.ContextMenu>
|
||||
</local:MyIconButton>
|
||||
@@ -36,8 +38,10 @@
|
||||
LogoScale="1.1" Logo="M462.336 924.891429L73.142857 950.857143l25.965714-389.193143 467.017143-467.017143a73.398857 73.398857 0 0 1 103.789715 0l259.437714 259.437714a73.398857 73.398857 0 0 1 0 103.789715l-467.017143 467.017143z m155.684571-778.349715L202.861714 561.664l259.474286 259.474286L877.458286 405.942857 618.057143 146.541714zM151.003429 873.033143l233.508571-25.965714-207.579429-207.579429-25.965714 233.545143z m544.841142-492.982857l51.894858 51.894857-233.508572 233.508571-51.931428-51.894857 233.545142-233.508571z">
|
||||
<local:MyIconButton.ContextMenu>
|
||||
<ContextMenu Closed="HidePanel" HorizontalOffset="10" VerticalOffset="18">
|
||||
<local:MyMenuItem Header="修改密码" local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://account.live.com/password/Change" />
|
||||
<local:MyMenuItem Header="修改玩家名" local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://www.minecraft.net/zh-hans/msaprofile/mygames/editprofile" />
|
||||
<local:MyMenuItem Header="修改密码"
|
||||
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://account.live.com/password/Change" />
|
||||
<local:MyMenuItem Header="修改玩家名"
|
||||
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://www.minecraft.net/zh-hans/msaprofile/mygames/editprofile" />
|
||||
</ContextMenu>
|
||||
</local:MyIconButton.ContextMenu>
|
||||
</local:MyIconButton>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
</StackPanel>
|
||||
<local:MyIconButton ToolTip="更改密码" Grid.Row="1" Height="14" Opacity="0" x:Name="BtnEdit" ToolTipService.InitialShowDelay="50" Grid.Column="0" HorizontalAlignment="Center" Logo="F1 M 53.2929,21.2929L 54.7071,22.7071C 56.4645,24.4645 56.4645,27.3137 54.7071,29.0711L 52.2323,31.5459L 44.4541,23.7677L 46.9289,21.2929C 48.6863,19.5355 51.5355,19.5355 53.2929,21.2929 Z M 31.7262,52.052L 23.948,44.2738L 43.0399,25.182L 50.818,32.9601L 31.7262,52.052 Z M 23.2409,47.1023L 28.8977,52.7591L 21.0463,54.9537L 23.2409,47.1023 Z" Margin="8,0,0,0" VerticalAlignment="Center"
|
||||
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://login.mc-user.com:233/account/changepw" />
|
||||
<local:MyIconButton ToolTip="退出登录" Grid.Row="1" Height="14" Opacity="0" x:Name="BtnExit" ToolTipService.InitialShowDelay="50" Grid.Column="2" HorizontalAlignment="Center" Logo="F1 M 51.0071,19.0027L 51.0071,27.0038L 48.0067,27.0038L 48.0067,22.0031L 25.0035,22.0031L 25.0035,54.0075L 48.0067,54.0075L 48.0067,49.0068L 51.0071,49.0068L 51.0071,57.008L 22.1698,57.008L 22.0031,19.0027L 51.0071,19.0027 Z M 30.0042,35.0049L 45.2563,35.0049L 37.005,28.0039L 47.0066,28.0039L 58.5082,38.0053L 47.0066,48.0067L 37.005,48.0067L 45.2563,41.0057L 30.0042,41.0057L 30.0042,35.0049 Z" Margin="0,0,8,0" VerticalAlignment="Center" />
|
||||
<local:MyIconButton ToolTip="切换账号" Grid.Row="1" Height="14" Opacity="0" x:Name="BtnExit" ToolTipService.InitialShowDelay="50" Grid.Column="2" HorizontalAlignment="Center" Logo="M512 0A512 512 0 1 0 1024 512 512 512 0 0 0 512 0z m0 941.407086A429.407086 429.407086 0 1 1 941.407086 512 429.407086 429.407086 0 0 1 512 941.407086z m235.139657-368.64H264.045714a43.505371 43.505371 0 0 0-31.246628 72.060343l159.305143 166.765714a41.281829 41.281829 0 1 0 59.626057-56.788114l-94.997943-99.474286H747.227429a41.311086 41.311086 0 1 0 0-82.622172zM262.670629 450.940343H745.764571a43.505371 43.505371 0 0 0 31.246629-72.352914l-159.305143-166.765715a41.281829 41.281829 0 1 0-59.713828 57.022172l94.968685 99.474285H262.670629a41.281829 41.281829 0 0 0 0 82.563658z" Margin="0,0,8,0" VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
<local:MySkin HorizontalAlignment="Center" VerticalAlignment="Top" x:Name="Skin" HasCape="False" />
|
||||
</Grid>
|
||||
@@ -487,8 +487,8 @@ Public Class PageLinkMain
|
||||
If FileUtils.Exists(PathEasyTier & "EasyTier.zip") Then
|
||||
'解压
|
||||
Dim ExtractPath As String = RequestTaskTempFolder()
|
||||
FileUtils.ExtractToDirectory(PathEasyTier & "EasyTier.zip", ExtractPath,
|
||||
progressHandler:=Sub(Percentage) Task.Progress = Percentage * 0.05)
|
||||
'FileUtils.ExtractToDirectory(PathEasyTier & "EasyTier.zip", ExtractPath,
|
||||
' progressHandler:=Sub(Percentage) Task.Progress = Percentage * 0.05)
|
||||
DirectoryUtils.Copy(DirectoryUtils.EnumerateDirectories(ExtractPath, True).First, PathEasyTier)
|
||||
'重命名
|
||||
FileUtils.Move(PathEasyTier & "easytier-core.exe", PathEasyTier & "联机模块.exe")
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="45" />
|
||||
<RowDefinition Height="45" />
|
||||
<RowDefinition Height="45" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="40" />
|
||||
@@ -18,21 +19,27 @@
|
||||
<ColumnDefinition Width="150" />
|
||||
</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="Plain Craft Launcher 2" Margin="-5,0,15,0" Info="当前版本:%VERSION%(%VERSIONCODE%.%BRANCH%)" Grid.Row="1" Grid.Column="1" x:Name="ItemAboutPcl" />
|
||||
<Image Source="/Plain Craft Launcher 2;component/Images/Heads/Logo.png" Grid.Row="1" Margin="3">
|
||||
<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" />
|
||||
<Image Source="/Plain Craft Launcher 2;component/Images/Heads/LTCat.jpg" Grid.Row="0" Margin="3">
|
||||
<Image.Clip>
|
||||
<EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
|
||||
</Image.Clip>
|
||||
</Image>
|
||||
<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/HerobrineXia.png" Grid.Row="1" Margin="3">
|
||||
<Image.Clip>
|
||||
<EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
|
||||
</Image.Clip>
|
||||
</Image>
|
||||
<Image Source="/Plain Craft Launcher 2;component/Images/Heads/Logo.png" Grid.Row="2" 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:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://meloong.com/afd/a/LTCat" />
|
||||
<local:MyButton Text="查看源代码" Grid.Column="2" Grid.Row="1" Height="35"
|
||||
local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://github.com/Hex-Dragon/PCL2" />
|
||||
<local:MyButton Text="检查更新" Grid.Column="2" Grid.Row="2" Height="35"
|
||||
x:Name="BtnAboutUpdate" />
|
||||
</Grid>
|
||||
</local:MyCard>
|
||||
<local:MyCard Margin="0,0,0,15" Title="特别鸣谢">
|
||||
@@ -124,335 +131,391 @@
|
||||
<local:MyCard Margin="0,0,0,15" Title="赞助者">
|
||||
<StackPanel Margin="25,40,19,20">
|
||||
<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="." />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="7" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="ln" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="sc" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="龙" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="人" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="溈" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="域" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="菌" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="。" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="東" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="喆" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="辞" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="溯" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="乔" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="liy" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="xu" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="lor" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="💢" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="ikii" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="ym" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="tstt" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="hjk" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="s11" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="gxx" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Lhy" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="jojo" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="沙轻" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="阳阳" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="画卿" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="幽灵" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="御衡" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="语墨" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="若希" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="少帅" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="小怂" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="叶虚" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="伍十" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="计计" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="雪雪" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="李镜" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="迪勒" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="。。" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="优优" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="青烬" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="苏籁" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="阿江" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="绵绵" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="萧白" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="枫灵" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="豆腐" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="无聊" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="闻序" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="风鸢" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="殉情" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="朱磊" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="先森" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="文博" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="月白" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="角慕" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="楽天" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="九杳" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="昨易" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="W.H" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="嘟嘟" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="断疆" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="麋鹿" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="云心" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="睚眦" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="诡奇" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="咲狐" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="白云" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="泽闻" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="逍遥" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="木鱼" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="凌月" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="梓昕" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="白菜" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="云上" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="老赵" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="拾岳" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="济川" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="樾安" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="蜜狳" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="七月" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="北瓜" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="昔涟" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="浅璃" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="成盏" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="而后" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="南安" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="ryan" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="xx_α" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="欸·柚" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="liutd" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="KIO3" />
|
||||
<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="Dr.松" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="LiMu" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="KING" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="ᝰᧁꫀꪑⅈꪀⅈ💫" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="ming" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="CIYE_" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="祇–仆" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Pluto" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="LYX_7" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="刘666" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Dong" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Lecen" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Hikari" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Vertin" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="35035" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Dye乄" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="白椰菜" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="小白龙" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="TeRiRi" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="索菲亚" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="穷哥们" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="华华子" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="欢喜果" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="大涡流" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="无名氏" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="power" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="苏清绾" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="四月初" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="雪落骆" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="秋天爽" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="茏三颜" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="月落蝶" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="蒲咩咩" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="糖不哭" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="风雨颜" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="花江湖" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="大馒头" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="啊哈哈" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="云与海" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="V0rtex" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="杨子普" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="大阿科" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="夷希微" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="吃不饱" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="爱熙熙" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="丁老王" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="吕雪源" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="赵亦勋" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="为南西" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="下午觉" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="李寒衣" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="水不湿" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="椛芷茈" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="李察维" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="十六夜" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="皮小风" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="陈知远" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="TaLula" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="N线🧵" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="秋辞白." />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Keil_v5" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="laimati" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="TraSok" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="CH2N8" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Ls-盛望" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="FiLin12" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="cz_awa" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="SYMYR" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="524950" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Trigger" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="CH南宫" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="bajie79" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="_xundu" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="TSLOLY" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="ncg303" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="cqg36k" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="YueYue" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="火羽白S" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="AMumi" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="GAGUA" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="shijie02" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="LULULU" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="iso3824" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Yununy" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="pnd6ou" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Liuaovo" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Akashi7" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="softtype" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="dumpyy" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="LO_WSY" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Mirtium" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="adccadc" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Bug虫爷" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="DylanPL" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="moxuan" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="elyruirui" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="井上青叶" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="懒人ROY" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="殇殇月月" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="日隅汐音" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="小猫渔民" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="凤辞时笙" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="清苍星海" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="芒果挺忙" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="只鱼二三" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="エンソウ" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="高露威姿" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="雪本柚子" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="萌新求带" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="顾炎武月" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="刻记牛杂" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="小飞小飞" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="纯真雪豹" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="罗兹瓦尔" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="安静的雨" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="北牧辰曦" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="下次一定" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="飞天土钩" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="曲线生活" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="管那么多" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="我叫杨戬" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="乐风昭苏" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="丨离凡丨" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="如图建系" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="酒筵辞夕" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="踏马的淦" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="夜殇羽镰" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="星尘墨韵" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="聆听冰风" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="鸭梨山大" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="一条鱼瓯" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Kang_XL" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Crispykit" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="vorinshe" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="凉曦luke" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Eiluosha" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="GKD小哥" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="是小61呀" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Disbelief" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Manatsu" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Ace_墨尘" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Murousa" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="mulberry" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="lyh20218" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="HIK4RI39" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Gwen_jin" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="EthanCAi" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Maplefor" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Dryadove" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Xiaomo_y" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="DWS3333" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="EnochHcx" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="hihospital" />
|
||||
<WrapPanel Margin="0,0,0,10">
<TextBlock Margin="0,1.5,6,0" Text="J" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="R" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="D" />
|
||||
<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="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="鸿" />
|
||||
<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="kll" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="02" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="YY" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="……" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="shy" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="lost" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="A仔" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="111" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="afaf" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="pen" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="lulu" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="xyty" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="排骨" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="踏雾" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Zsol" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="景仪" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="可可" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="白夜" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="汤圆" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="星月" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="皓月" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="老建" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="阿琼" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="晓月" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="朝雨" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="迟雨" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="折纸" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="张某" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="残月" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="缘分" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="静态" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="铭熙" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="酒狐" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="誌異" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="灵喵" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="曦宇" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="木辛" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="轻梦" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="熊猫" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="鱼柳" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="梨子" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="小本" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="起媛" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="夏天" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="泪痕" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="南北" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="灰鱼" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="灰尘" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="骗铭" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="青轴" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="谷雨" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="呈祥" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="猫玖" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="枕月" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="刘革" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="折花" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="应星" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="没有" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="花园" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="华烬" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="啥猫" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="阿伯" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="妄想" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="树叶" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="拾玖" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="让人" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="晓光" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="懿尘" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="尾巴" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="枯骨" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="鹤升" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="遊泠" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="裕茗" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="沁园" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="尹伊" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="枫音" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="古色" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="傻鱼" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="幻灭" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="小琪" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="小寒" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="星狗" />
|
||||
<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="Siwill" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="kassa" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="ssdsb" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Ztdzz" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="xiboa" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="ZERO" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="pr1nk" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="BBUH" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="ALISA" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="89623" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="IDXLZ" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="翎羽絮" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="硫酸梅" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="死鱼梦" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="秦始皇" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="春忆雪" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="冷空气" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="天崇域" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="白与沧" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="柳蕴訫" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="苟子丶" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="午時夢" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="清水石" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="鹿鹤云" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="洛沫兀" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="。。。" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="樱咲夜" />
|
||||
<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="水芃叁" />
|
||||
<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="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="Sherry" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="lizi756" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="letitbe" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Ikorito" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="MCDD" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="nihility" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="小宇Bo" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="2789th" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="S1NKO" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Rimlue" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="FMDQI" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Aevrud" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Larry76" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Miantu" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Nature." />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Autism." />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="MXUER" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="suifeng" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Yangeh" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Anmisu" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="TinkerZ" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="LaoGou" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="lkhkykk" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="N/Azais" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="KISSHIP" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Vanessa" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="lanyuan" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="BBBKKK" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="mchim1" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="MANBA" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Tairitsu_" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="宇陌୧⍤⃝🇨🇳" />
|
||||
<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="魔法少年" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="戒者有八" />
|
||||
<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="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="百里无焉" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="皮蛋肥猫" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="身披坚甲" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="近则不逊" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="卡白小洛" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="即墨非墨" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="准备冒险" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="喰梦优音" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="雨夜色中" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="沃特拉奇" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="闲鱼教授" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="北川暮雨" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="生根发芽" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="暮霭凝缘" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="孤灯裹尘" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="日薪月亿" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="北默千鹤" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="咸鱼一条" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="禾辞共振" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="雨宫冰莲" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="BG4LHO" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="没啥想法" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="古色沉竹" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="W污狮W" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="jinyucao" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="MESGRZ" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="qiewang" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="bao9898" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="幻沫_123" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Yuting.Li" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Zaaaako" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Tianqiao" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="EMILIIIIA" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="阿尔法.@" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="neymarjr" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="AsUWish" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="waxberry" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="彼岸的***" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="liminalily" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="sansisthe" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="RickyZhu" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="TenKings" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="yvnchvan" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="喵呜派zzz" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Mz小狐仙" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="huangwu" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="玖月Yiyue" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="石头WAW" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="YiYi_Sama" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="theancato" />
|
||||
<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="Com_XDN" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="SEIGLE404" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Kevin2022" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="梓晨Otaku" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="凌点工作室" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="千秋万载猹" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="超级大月光" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="精酿糯米酒" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="不「成」器" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="无常之客星" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="第一年冬天" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="大王上早八" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="爱吃小笼包" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="红楼彡揽月" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="画海听竹者" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="白哥咯咯咯" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="懒得喷喷喷" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="估计不会玩" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="伊南宁门特" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="秋山有希子" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="氧化氧化氧" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="傻悠不知道" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="不爱吃灼芋" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="山枕檀痕涴" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="你是妖怪叭" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="不知名玩家" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="湛蓝一四七" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="翰墨续丹青" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="凛果与百橙" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Touma000" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="回忆de天空" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Yunlimeng" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="GODxianyu" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Yhcmdbfwz" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Equilibrium" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="陌泽陌泽MZ" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="咖啡R3borN" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="1234567890" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="GUGUGAGA" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="2366445223" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="arrovartsk77" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Elegytairitsu" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="南小彩的梦想" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="最后一只斑鸠" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="刻晴天下第一" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="幸运的蓝耀星" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="整没用的红球" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="端午节吃饺子" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="DLurker7840" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Don_Quixote" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="funnyzhuling" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="AYGY暗夜古韵" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Kecdl_Chacha" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="对方正在输入 ..." />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Chihaya-Anon" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="虚域染指亦起舞" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="半人半灵的麻薯" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="霜雪焉能见太阳" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="章鱼哥的琪露诺" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="重生之发财传奇" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="某科学的小狐狸" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="AAA神秘人批发" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="贝吉蒙Begimon" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Kozume_Kenma" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Taskmgr-MEMZ" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="漫天星火为谁舞?" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="常凱申(刘小胖)" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="赚够三千万就瘦瘦" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="大道五十天衍四九" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="爱停电用户_查尔德" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="狸猫曾在学校烤面包" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="(´o̴̶̷̤ㅂo̴̶̷̤`)" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="克洛伊.冯.爱因兹贝伦" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="(*^ワ^*)^_^$$" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="GongzhuHuanerAwA" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="幽兰黛尔【Druandal】" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="codm我要玩一辈子cx-9" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="dark 寿司s" />
|
||||
<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="Zurkey106" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="stargolven" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="拒绝过阿姨" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="拟态深埋箱" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="凤凰院凶真" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="时间&轮盘" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="无心于刹那" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="粗心忘带头" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="提线的人偶" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="非慕所不敬" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="天生会炸鱼" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="世界第一矮" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="雷焰不虚误" />
|
||||
<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="小白pandk" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="LapalceXIII" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="charlesmin" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="DeepSleep" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="rishaoshan" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="LJJ有擎天柱" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="builder天识" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Cathan猫皁" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="猫熊NICEST" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="heaven伯特" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="礼央子sama" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="蠢萌der小白" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Dying Bone" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="DaisyCrown" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="2127864647" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Z123456789" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="MOMOHAN" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="舸沫_Official" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="ξKō Hei-seiξ" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="CookieNeko" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="伊吹风子大人" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="バベルの悪霊" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="沉默的玄晓羲" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="为什么水无鱼" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="无敌猫猫刺客" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="久恋丿如初漾" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="肥宅不是胖子" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="路边的小野寺" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="要一个手抓饼" />
|
||||
<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="mouyangjun" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="我no想取名字" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="机电2516汪帅" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="QueNanNNN" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="huamozhuke" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Titanic Rising" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Skymunchrex" />
|
||||
<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="不爱fa电的小安" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="是enderman噢" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="无敌兵王陈亚蒙" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="申鹤红绳牵我魂" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="背不完的郭同学" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="追随自己的星星" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="帅道无上大宗师" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="LucidZuowang" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="AAA.果粒橙批发" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Omega•Epsilon" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Ries1ing_嗷菲兽" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Understand You" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="AAADasAbi9527" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="筱筱雨吖_Official" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="DecadeMagenta" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="0223YUKIUKOORI" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="☆Etritz__㯊龝.ET☆" />
|
||||
<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="``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="[email protected]" />
|
||||
<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>
|
||||
<Grid Height="35">
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -539,14 +602,14 @@
|
||||
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="2" Grid.ColumnSpan="2" Text="Copyright © 2015 Mark Heath ([email protected])
Licensed under the MIT License." TextWrapping="Wrap" />
|
||||
<TextBlock Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2015 Mark Heath ([email protected])
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: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: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="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)
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: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"
|
||||
@@ -568,29 +631,36 @@
|
||||
|
||||
<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" />
|
||||
<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/" />
|
||||
|
||||
<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
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: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: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="18" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2026 Ben Hutchison
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"
|
||||
<TextBlock Grid.Row="16" Grid.Column="0" Text="ThrottleDebounce" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="16" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2026 Ben Hutchison
Licensed under the Apache License, Version 2.0." TextWrapping="Wrap" />
|
||||
<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: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" />
|
||||
|
||||
<TextBlock Grid.Row="20" Grid.Column="0" Text="CacheCow" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="20" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © Ali Kheyrollahi
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"
|
||||
<TextBlock Grid.Row="18" Grid.Column="0" Text="CacheCow" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="18" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © Ali Kheyrollahi
Licensed under the MIT License." TextWrapping="Wrap" />
|
||||
<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: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" />
|
||||
|
||||
<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 <[email protected]> and contributors
Licensed under the GNU General Public License, Version 3 or later, with additional terms.
本组件作为独立组件分发,可依 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>
|
||||
</local:MyCard>
|
||||
</StackPanel>
|
||||
|
||||
@@ -27,5 +27,8 @@ Public Class PageOtherAbout
|
||||
Private Sub BtnDonateOutput_Click(sender As Object, e As EventArgs) Handles BtnDonateOutput.Click
|
||||
GeneratePotatoCode()
|
||||
End Sub
|
||||
Private Sub BtnAboutUpdate_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnAboutUpdate.Click
|
||||
UpdateCheckByButton()
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -48,12 +48,13 @@ Public Class PageOtherHelp
|
||||
If Item.Types.Contains(Type) Then TypeItems.Add(Item)
|
||||
Next
|
||||
'增加卡片
|
||||
Dim NewCard As New MyCard With {.Title = Type, .Margin = New Thickness(0, 0, 0, 15), .SwapType = 11}
|
||||
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 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)}
|
||||
NewCard.Children.Add(NewStack)
|
||||
NewCard.SwapControl = NewStack
|
||||
NewCard.SetContentBuilder(TypeItems, Function(Entry) Entry.ToListItem)
|
||||
If Type = "指南" Then
|
||||
MyCard.StackInstall(NewStack, 11, "指南")
|
||||
NewCard.BuildContent()
|
||||
Else
|
||||
NewCard.IsSwapped = True
|
||||
End If
|
||||
|
||||
@@ -64,7 +64,7 @@ Public Class PageSelectLeft
|
||||
</ContextMenu>
|
||||
)
|
||||
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))
|
||||
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
|
||||
If McFolderList(i).Location = McFolderSelected Then
|
||||
If PathUtils.IsSame(McFolderList(i).Location, McFolderSelected) Then
|
||||
CType(FrmSelectLeft.PanList.Children(i + 1), MyListItem).Checked = True '去掉第一个标题
|
||||
Return
|
||||
End If
|
||||
@@ -139,9 +139,9 @@ Public Class PageSelectLeft
|
||||
'添加文件夹
|
||||
Public Sub Add_Click()
|
||||
Dim NewFolder As String = ""
|
||||
'检查是否有下载任务
|
||||
If HasDownloadingTask() Then
|
||||
Hint("在下载任务进行时,无法添加游戏文件夹!", HintType.Red)
|
||||
'下载或版本修改期间禁止更改游戏文件夹
|
||||
If McInstanceLock.IsAnyLocked OrElse HasDownloadingTask() Then
|
||||
Hint("请在其他任务完成后,再添加游戏文件夹!", HintType.Red)
|
||||
Return
|
||||
End If
|
||||
Try
|
||||
@@ -150,8 +150,11 @@ Public Class PageSelectLeft
|
||||
If NewFolder Is Nothing Then Return
|
||||
If NewFolder.Contains("!") OrElse NewFolder.Contains(";") Then Hint("Minecraft 文件夹路径中不能含有感叹号或分号!", HintType.Red) : Return
|
||||
'要求输入显示名称
|
||||
Dim SplitedNames As String() = NewFolder.TrimEnd("\").Split("\")
|
||||
Dim DefaultName As String = If(SplitedNames.Last = ".minecraft", If(SplitedNames.Count >= 3, SplitedNames(SplitedNames.Count - 2), ""), SplitedNames.Last)
|
||||
Dim DefaultName = PathUtils.GetLastPart(NewFolder)
|
||||
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)
|
||||
Dim NewName As String = MyMsgBoxInput("输入显示名称", "输入该文件夹在左边栏列表中显示的名称。", DefaultName,
|
||||
New ObjectModel.Collection(Of Validate) From {New ValidateNullOrWhiteSpace, New ValidateLength(1, 30), New ValidateExcept({">", "|"})})
|
||||
@@ -169,7 +172,7 @@ Public Class PageSelectLeft
|
||||
RunInThread(
|
||||
Sub()
|
||||
Try
|
||||
If Not FolderPath.EndsWithF("\") Then FolderPath &= "\"
|
||||
FolderPath = PathUtils.AddSlashSuffix(FolderPath)
|
||||
'检查文件夹权限
|
||||
If Not CheckPermission(FolderPath) Then
|
||||
If ShowHint Then
|
||||
@@ -195,7 +198,7 @@ Public Class PageSelectLeft
|
||||
For i = 0 To Folders.Count - 1
|
||||
Dim Folder As String = Folders(i)
|
||||
If Folder = "" Then Continue For
|
||||
If Folder.Split(">")(1) = FolderPath Then
|
||||
If PathUtils.IsSame(Folder.Split(">")(1), FolderPath) Then
|
||||
IsAdded = True
|
||||
If Folder.Split(">")(0) = DisplayName Then
|
||||
If ShowHint Then Hint("此文件夹已在列表中!", HintType.Blue)
|
||||
@@ -246,9 +249,9 @@ Public Class PageSelectLeft
|
||||
|
||||
'创建文件夹
|
||||
Public Sub Create_Click()
|
||||
'检查是否有下载任务
|
||||
If HasDownloadingTask() Then
|
||||
Hint("在下载任务进行时,无法创建游戏文件夹!", HintType.Red)
|
||||
'下载或版本修改期间禁止更改游戏文件夹
|
||||
If McInstanceLock.IsAnyLocked OrElse HasDownloadingTask() Then
|
||||
Hint("请在其他任务完成后,再创建游戏文件夹!", HintType.Red)
|
||||
Return
|
||||
End If
|
||||
CreateMcFolderInCurrentPath()
|
||||
@@ -270,6 +273,10 @@ Public Class PageSelectLeft
|
||||
Dim Target As McFolder = CType(CType(CType(sender.Parent, ContextMenu).Parent, Primitives.Popup).PlacementTarget, MyListItem).Tag
|
||||
'若为 “移除”,则提醒是否删除 PCL 的配置文件
|
||||
If Target.Type = McFolder.Types.Custom Then
|
||||
If McInstanceLock.IsAnyLocked OrElse HasDownloadingTask() Then
|
||||
Hint("请在其他任务完成后,再移除游戏文件夹!", HintType.Red)
|
||||
Return
|
||||
End If
|
||||
Select Case MyMsgBox("是否需要清理 PCL 在该文件夹中的配置文件?" & vbCrLf & "这包括各个版本的独立设置(如自定义图标、第三方登录配置)等,对游戏本身没有影响。", "配置文件清理", "删除", "保留", "取消")
|
||||
Case 1
|
||||
'删除配置文件
|
||||
@@ -292,6 +299,10 @@ Public Class PageSelectLeft
|
||||
End Try
|
||||
End Sub
|
||||
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 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 &
|
||||
@@ -328,7 +339,7 @@ Public Class PageSelectLeft
|
||||
Dim DeletedName As String = Nothing
|
||||
Dim Folders = Settings.Get(Of String)("LaunchFolders").Split("|"c, True).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(">")
|
||||
Folders.Remove(Folder)
|
||||
Exit For
|
||||
@@ -349,7 +360,7 @@ Public Class PageSelectLeft
|
||||
End Sub
|
||||
Public Shared Sub RefreshCurrent(Folder As String)
|
||||
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
|
||||
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
|
||||
@@ -365,7 +376,7 @@ Public Class PageSelectLeft
|
||||
For i = 0 To Folders.Count - 1
|
||||
Dim FolderCurrent As String = Folders(i)
|
||||
If FolderCurrent = "" Then Continue For
|
||||
If FolderCurrent.Split(">")(1) = Folder.Location Then
|
||||
If PathUtils.IsSame(FolderCurrent.Split(">")(1), Folder.Location) Then
|
||||
IsAdded = True
|
||||
If FolderCurrent.Split(">")(0) = NewName Then
|
||||
'名称未修改
|
||||
@@ -390,9 +401,9 @@ Public Class PageSelectLeft
|
||||
'点击选项
|
||||
Public Sub Folder_Change(sender As MyListItem, e As RouteEventArgs)
|
||||
If Not e.RaiseByMouse OrElse Not sender.Checked Then Return
|
||||
'检查是否有下载任务
|
||||
If HasDownloadingTask(True) Then
|
||||
Hint("在下载任务进行时,无法切换游戏文件夹!", HintType.Red)
|
||||
'下载或版本修改期间禁止更改游戏文件夹
|
||||
If McInstanceLock.IsAnyLocked OrElse HasDownloadingTask(True) Then
|
||||
Hint("请在其他任务完成后,再切换游戏文件夹!", HintType.Red)
|
||||
e.Handled = True
|
||||
Return
|
||||
End If
|
||||
|
||||
@@ -30,6 +30,8 @@ Public Class PageSelectRight
|
||||
For Each Card As KeyValuePair(Of McInstanceCardType, List(Of McInstance)) In McInstanceList.ToArray
|
||||
'确认是否为隐藏版本显示状态
|
||||
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 "确认卡片名称"
|
||||
Dim CardName As String = ""
|
||||
Select Case Card.Key
|
||||
@@ -40,11 +42,11 @@ Public Class PageSelectRight
|
||||
Dim IsNeoForgeExists As Boolean = False
|
||||
Dim IsFabricExists As Boolean = False
|
||||
Dim IsLiteExists As Boolean = False
|
||||
For Each Instance As McInstance In Card.Value
|
||||
If Instance.Version.HasFabric Then IsFabricExists = True
|
||||
If Instance.Version.HasLiteLoader Then IsLiteExists = True
|
||||
If Instance.Version.HasForge Then IsForgeExists = True
|
||||
If Instance.Version.HasNeoForge Then IsNeoForgeExists = True
|
||||
For Each Instance As McInstance In Instances
|
||||
If Instance.Version.ModLoaders.HasFlagF(ModLoaders.Fabric) Then IsFabricExists = True
|
||||
If Instance.Version.ModLoaders.HasFlagF(ModLoaders.LiteLoader) Then IsLiteExists = True
|
||||
If Instance.Version.ModLoaders.HasFlagF(ModLoaders.Forge) Then IsForgeExists = True
|
||||
If Instance.Version.ModLoaders.HasFlagF(ModLoaders.NeoForge) Then IsNeoForgeExists = True
|
||||
Next
|
||||
If If(IsLiteExists, 1, 0) + If(IsForgeExists, 1, 0) + If(IsFabricExists, 1, 0) + If(IsNeoForgeExists, 1, 0) > 1 Then
|
||||
CardName = "可安装 Mod"
|
||||
@@ -72,17 +74,18 @@ Public Class PageSelectRight
|
||||
End Select
|
||||
#End Region
|
||||
'建立控件
|
||||
Dim CardTitle As String = CardName & If(CardName = "收藏夹", "", " (" & Card.Value.Count & ")")
|
||||
Dim NewCard As New MyCard With {.Title = CardTitle, .Margin = New Thickness(0, 0, 0, 15), .SwapType = 0}
|
||||
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 CardTitle As String = CardName & If(CardName = "收藏夹", "", " (" & Instances.Count & ")")
|
||||
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)}
|
||||
NewCard.Children.Add(NewStack)
|
||||
NewCard.SwapControl = NewStack
|
||||
NewCard.SetContentBuilder(Instances, Function(Instance) Instance.ToListItem(AddressOf McInstanceListContent))
|
||||
PanMain.Children.Add(NewCard)
|
||||
'确定卡片是否展开
|
||||
If Card.Key = McInstanceCardType.Rubbish OrElse Card.Key = McInstanceCardType.Error OrElse Card.Key = McInstanceCardType.Fool Then
|
||||
NewCard.IsSwapped = True
|
||||
Else
|
||||
MyCard.StackInstall(NewStack, 0, CardTitle)
|
||||
NewCard.BuildContent()
|
||||
End If
|
||||
Next
|
||||
|
||||
@@ -128,7 +131,8 @@ Public Class PageSelectRight
|
||||
BtnStar.LogoScale = 1.1
|
||||
BtnStar.Logo = Logo.IconButtonLikeLine
|
||||
End If
|
||||
AddHandler BtnStar.Click, Sub()
|
||||
AddHandler BtnStar.Click,
|
||||
Sub()
|
||||
WriteIni(Instance.PathVersion & "PCL\Setup.ini", "IsStar", Not Instance.IsStar)
|
||||
McInstanceListForceRefresh = True
|
||||
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}
|
||||
BtnDel.ToolTip = "删除"
|
||||
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}
|
||||
BtnCont.ToolTip = "设置"
|
||||
AddHandler BtnCont.Click,
|
||||
@@ -166,6 +170,10 @@ Public Class PageSelectRight
|
||||
'点击选项
|
||||
Public Shared Sub Item_Click(sender As MyListItem, e As EventArgs)
|
||||
Dim Instance As McInstance = sender.Tag
|
||||
If McInstanceLock.IsLocked(Instance) Then
|
||||
Hint("该版本正在被修改,请稍后再选择!", HintType.Red)
|
||||
Return
|
||||
End If
|
||||
If New McInstance(Instance.PathVersion).Check Then
|
||||
'正常版本
|
||||
McInstanceSelected = Instance
|
||||
@@ -183,10 +191,17 @@ Public Class PageSelectRight
|
||||
'修改此代码时,同时修改 PageInstanceOverall 中的代码
|
||||
Public Shared Sub DeleteInstance(Item As MyListItem, Instance As McInstance)
|
||||
Try
|
||||
If McInstanceLock.IsLocked(Instance) Then
|
||||
Hint("该版本正在被修改,请稍后再删除!", HintType.Red)
|
||||
Return
|
||||
End If
|
||||
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 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} 吗?" &
|
||||
If(IsHintIndie, vbCrLf & "由于该版本开启了版本隔离,删除版本时该版本对应的存档、资源包、Mod 等文件也将被一并删除!", ""),
|
||||
If(IsHintIndie, vbCrLf & "该版本对应的存档、资源包、Mod 等文件也将被一并删除!", "") &
|
||||
If(SaveEntries.Any, vbCrLf & vbCrLf & "这会删除以下存档:" & vbCrLf & "· " & SaveEntries.Join(vbCrLf & "· "), ""),
|
||||
"版本删除确认", , "取消",, True)
|
||||
Case 1
|
||||
Instance.ResetSettingsCache()
|
||||
@@ -204,7 +219,7 @@ Public Class PageSelectRight
|
||||
Dim Card As MyCard = Parent.Parent
|
||||
Card.Title = Card.Title.Replace(Parent.Children.Count - 1, Parent.Children.Count - 2) '有一个占位符
|
||||
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
|
||||
End If
|
||||
|
||||
@@ -286,6 +286,7 @@
|
||||
<local:MyCheckBox Height="28" Text="禁用 Java Launch Wrapper" local:SettingService.Key="LaunchAdvanceDisableJLW" ToolTip="是否使用 Java Launch Wrapper 修复 Java 18- 在中文路径下可能无法正常启动的问题。
详见: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 的一个性能问题。
详见:https://github.com/HMCL-dev/lwjgl-unsafe-agent" />
|
||||
<local:MyCheckBox Height="28" Text="使用高性能显卡" local:SettingService.Key="LaunchAdvanceGraphicCard" ToolTip="自动在 Windows 设置 → 系统 → 屏幕 → 显示卡 中将 PCL 和 Java 改为使用高性能显卡。
如果同时有集成显卡和独立显卡,这能确保 MC 使用独立显卡进行渲染,以大幅提高帧率。" />
|
||||
<local:MyCheckBox Height="28" Text="在正版登录时验证 SSL 证书" local:SettingService.Key="ToolDownloadCert" ToolTip="开启验证可以提高安全性,但可能会导致正版登录失败(见 #3018)。
若登录因为 “未能为 SSL/TLS 安全通道建立信任关系” 失败,请考虑关闭此选项。" />
|
||||
</StackPanel>
|
||||
<local:MyHint Text="版本独立设置中还有更多高级选项可供调整。" Margin="0,5,0,10" Theme="Blue" Grid.Row="11" Grid.ColumnSpan="2"
|
||||
CanClose="True" RelativeSetup="HintMoreAdvancedSetup" />
|
||||
|
||||
@@ -396,7 +396,7 @@ PreFin:
|
||||
AddHandler DownButton.Click, Sub() MoveJavaInList(JavaEntry, 1)
|
||||
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}
|
||||
DeleteButton.IsEnabled = Not IsOfficial
|
||||
DeleteButton.ToolTip = If(DeleteButton.IsEnabled, "从列表中移除", "无法移除官方 Java")
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
<RowDefinition Height="27" />
|
||||
<RowDefinition Height="27" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" Text="文件下载源" Margin="0,0,25,0" />
|
||||
<local:MyComboBox x:Name="ComboDownloadSource" Grid.ColumnSpan="2" local:SettingService.Key="ToolDownloadSource" Grid.Column="1">
|
||||
@@ -44,9 +43,6 @@
|
||||
ToolTip="设置下载的速度上限,以避免在下载时导致其他需要联网的程序卡死" />
|
||||
<TextBlock VerticalAlignment="Top" Grid.Row="6" HorizontalAlignment="Left" Text="目标文件夹" Margin="0,5,25,5" />
|
||||
<TextBlock Margin="0,5" HorizontalAlignment="Left" Text="请在 启动 → 版本选择 → 文件夹列表 中更改下载目标文件夹。
在某个文件夹或游戏版本上右键,即可选择打开对应文件夹。" Grid.Row="6" Grid.Column="1" VerticalAlignment="Center" Opacity="0.5" />
|
||||
<local:MyCheckBox Margin="0,2,0,4" Text="在正版登录时验证 SSL 证书" Grid.Row="7" Height="22" Grid.ColumnSpan="2"
|
||||
x:Name="CheckDownloadCert" local:SettingService.Key="ToolDownloadCert"
|
||||
ToolTip="开启验证可以提高安全性,但可能会导致正版登录失败(见 #3018)。
若登录因为 “未能为 SSL/TLS 安全通道建立信任关系” 失败,请考虑关闭此选项。" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
@@ -64,7 +60,6 @@
|
||||
<RowDefinition Height="7" />
|
||||
<RowDefinition Height="28" />
|
||||
<RowDefinition Height="7" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" Text="来源" Margin="0,0,25,0" />
|
||||
<local:MyComboBox x:Name="ComboDownloadMod" Grid.Row="0" Grid.ColumnSpan="2" local:SettingService.Key="ToolDownloadMod" Grid.Column="1">
|
||||
@@ -87,8 +82,6 @@
|
||||
<local:MyComboBoxItem Content="标题显示译名,详情显示文件名" IsSelected="True" />
|
||||
<local:MyComboBoxItem Content="标题显示文件名,详情显示译名" />
|
||||
</local:MyComboBox>
|
||||
<local:MyCheckBox Margin="0,2,0,4" Text="在显示 Mod 加载器时忽略 Quilt" Grid.Row="6" Height="22" Grid.ColumnSpan="2"
|
||||
x:Name="CheckDownloadIgnoreQuilt" local:SettingService.Key="ToolDownloadIgnoreQuilt" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
|
||||
@@ -235,16 +235,16 @@
|
||||
<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"
|
||||
CanClose="True" RelativeSetup="HintCustomWarn" />
|
||||
<Grid Height="35" x:Name="PanCustomLocal" Visibility="Collapsed" Margin="0,10,0,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<local:MyButton x:Name="BtnCustomRefresh" Grid.Column="0" MinWidth="140" Text="刷新主页" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" />
|
||||
<local:MyButton x:Name="BtnCustomFile" Grid.Column="1" MinWidth="140" Text="生成教学文件" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
|
||||
<local:MyButton x:Name="BtnCustomTutorial" Grid.Column="2" MinWidth="140" Text="查看教程" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
|
||||
</Grid>
|
||||
<WrapPanel x:Name="PanCustomLocal" Visibility="Collapsed" Margin="0,0,0,5">
|
||||
<local:MyButton x:Name="BtnCustomRefresh" Text="刷新主页"
|
||||
MinWidth="140" Height="35" Padding="13,0" Margin="0,10,20,0" HorizontalAlignment="Left" ColorType="Highlight" />
|
||||
<local:MyButton x:Name="BtnCustomFile" Text="生成教学文件"
|
||||
MinWidth="140" Height="35" Padding="13,0" Margin="0,10,20,0" HorizontalAlignment="Left" />
|
||||
<local:MyButton x:Name="BtnCustomTutorial" Text="查看教程"
|
||||
MinWidth="140" Height="35" Padding="13,0" Margin="0,10,20,0" HorizontalAlignment="Left" />
|
||||
<local:MyButton x:Name="BtnCustomFolder" Text="打开主页文件夹"
|
||||
MinWidth="140" Height="35" Padding="13,0" Margin="0,10,20,0" HorizontalAlignment="Left" />
|
||||
</WrapPanel>
|
||||
<Grid x:Name="PanCustomNet" Visibility="Collapsed" Margin="0,10,0,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Name" />
|
||||
@@ -284,6 +284,13 @@
|
||||
<local:MyComboBoxItem local:SettingService.Value="15" Content="Minecraft 芝士站(作者:awaEric233)" />
|
||||
<local:MyComboBoxItem local:SettingService.Value="16" Content="整合包推荐引擎(作者:CHMYGZ)" />
|
||||
<local:MyComboBoxItem local:SettingService.Value="17" Content="Bangumi 番剧主页(作者:wzyaeu)" />
|
||||
<local:MyComboBoxItem local:SettingService.Value="18" Content="Bilibili 热门(作者: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>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
@@ -330,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="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="仅隐藏本选项卡,设置的禁用内容依然有效。
可以在保留个性化设置页面的情况下避免隐藏设置被修改。" />
|
||||
<local:MyCheckBox Height="22" VerticalAlignment="Center" Grid.Row="4" Grid.Column="6" Text="版本修改" x:Name="CheckHiddenFunctionModify" local:SettingService.Key="UiHiddenFunctionInstanceModify" ToolTip="隐藏版本设置中的版本修改功能。" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
|
||||
@@ -341,6 +341,14 @@ Refresh:
|
||||
End Select
|
||||
FrmSetupUI.CardCustom.TriggerForceResize()
|
||||
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
|
||||
@@ -445,6 +453,10 @@ Refresh:
|
||||
If FrmSetupUI IsNot Nothing Then
|
||||
FrmSetupUI.CardSwitch.Visibility = If(Not HiddenForceShow AndAlso Settings.Get(Of Boolean)("UiHiddenFunctionHidden"), Visibility.Collapsed, Visibility.Visible)
|
||||
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
|
||||
FrmSetupLeft.ItemLaunch.Visibility = If(Not HiddenForceShow AndAlso Settings.Get(Of Boolean)("UiHiddenSetupLaunch"), Visibility.Collapsed, Visibility.Visible)
|
||||
|
||||
@@ -120,7 +120,6 @@ Public Class Settings
|
||||
New Setting("ToolDownloadVersion", 1, Source:=Sources.Registry),
|
||||
New Setting("ToolDownloadTranslate", 0, Source:=Sources.Registry),
|
||||
New Setting("ToolDownloadTranslateV2", 1, Source:=Sources.Registry),
|
||||
New Setting("ToolDownloadIgnoreQuilt", True, Source:=Sources.Registry),
|
||||
New Setting("ToolDownloadCert", False, Source:=Sources.Registry, OnChanged:=AddressOf ModNet.ShouldValidateSslCertificateOnLogin),
|
||||
New Setting("ToolDownloadMod", 2, Source:=Sources.Registry),
|
||||
New Setting("ToolModLocalNameStyle", 0, Source:=Sources.Registry),
|
||||
@@ -159,6 +158,7 @@ Public Class Settings
|
||||
New Setting("UiHiddenPageOther", False, OnChanged:=AddressOf PageSetupUI.HiddenRefresh),
|
||||
New Setting("UiHiddenFunctionSelect", 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("UiHiddenSetupLaunch", False, OnChanged:=AddressOf PageSetupUI.HiddenRefresh),
|
||||
New Setting("UiHiddenSetupUi", False, OnChanged:=AddressOf PageSetupUI.HiddenRefresh),
|
||||
@@ -261,7 +261,11 @@ Public Class Settings
|
||||
Case Sources.Normal
|
||||
WriteIni("Setup", Key, Value)
|
||||
Case Sources.Registry
|
||||
WriteReg(Key, Value)
|
||||
Try
|
||||
RegistryUtils.Write($"HKEY_CURRENT_USER\Software\{RegFolder}", Key, Value)
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, $"写入注册表失败({Key} → {Value})", LogBehavior.Toast)
|
||||
End Try
|
||||
Case Sources.Instance
|
||||
If Instance Is Nothing Then Throw New Exception($"保存版本独立设置 {Key} 时未提供目标版本")
|
||||
WriteIni(Instance.PathVersion & "PCL\Setup.ini", Key, Value)
|
||||
@@ -327,7 +331,7 @@ Public Class Settings
|
||||
Case Sources.Normal
|
||||
GotValue = ReadIni("Setup", Key, DefaultValue)
|
||||
Case Sources.Registry
|
||||
GotValue = ReadReg(Key, DefaultValue)
|
||||
GotValue = RegistryUtils.TryRead($"HKEY_CURRENT_USER\Software\{RegFolder}", Key, DefaultValue)
|
||||
Case Sources.Instance
|
||||
If Instance Is Nothing Then
|
||||
Throw New Exception($"读取版本设置 {Key} 时未提供目标版本")
|
||||
@@ -386,7 +390,11 @@ Public Class Settings
|
||||
Case Sources.Normal
|
||||
DeleteIniKey("Setup", Key)
|
||||
Case Sources.Registry
|
||||
DeleteReg(Key)
|
||||
Try
|
||||
RegistryUtils.Delete($"HKEY_CURRENT_USER\Software\{RegFolder}", Key)
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, $"删除注册表失败({Key})", LogBehavior.Toast)
|
||||
End Try
|
||||
Case Sources.Instance
|
||||
If Instance Is Nothing Then Throw New Exception($"重置版本设置 {Key} 时未提供目标版本")
|
||||
DeleteIniKey(Instance.PathVersion & "PCL\Setup.ini", Key)
|
||||
@@ -414,7 +422,12 @@ Public Class Settings
|
||||
Case Sources.Normal
|
||||
Return HasIniKey("Setup", Key)
|
||||
Case Sources.Registry
|
||||
Return HasReg(Key)
|
||||
Try
|
||||
Return RegistryUtils.Has($"HKEY_CURRENT_USER\Software\{RegFolder}", Key)
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, $"判断注册表 {Key} 是否存在时出错")
|
||||
Return False
|
||||
End Try
|
||||
Case Else 'Source.Instance
|
||||
If Instance Is Nothing Then Throw New Exception($"判断版本设置 {Key} 是否存在时未提供目标版本")
|
||||
Return HasIniKey(Instance.PathVersion & "PCL\Setup.ini", Key)
|
||||
|
||||
@@ -32,7 +32,7 @@ Public Class PageSpeedLeft
|
||||
'定时器任务
|
||||
Private ReadOnly RightCards As New Dictionary(Of String, MyCard)
|
||||
Private Sub Watcher()
|
||||
If Not FrmMain.PageCurrent = FormMain.PageType.DownloadManager Then Return
|
||||
If Not FrmMain.PageCurrent = FormMain.PageType.TaskManager Then Return
|
||||
Try
|
||||
|
||||
#Region "更新左边栏"
|
||||
@@ -55,7 +55,7 @@ Public Class PageSpeedLeft
|
||||
#End Region
|
||||
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex, "下载管理左栏监视出错")
|
||||
Logger.Error(ex, "任务管理左栏监视出错")
|
||||
End Try
|
||||
If FrmSpeedRight Is Nothing OrElse FrmSpeedRight.PanMain Is Nothing Then Return
|
||||
Try
|
||||
@@ -63,7 +63,7 @@ Public Class PageSpeedLeft
|
||||
TaskRefresh(Loader)
|
||||
Next
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex, "下载管理右栏监视出错")
|
||||
Logger.Error(ex, "任务管理右栏监视出错")
|
||||
End Try
|
||||
End Sub
|
||||
Public Sub TaskRefresh(Loader As LoaderBase)
|
||||
@@ -106,7 +106,7 @@ Public Class PageSpeedLeft
|
||||
#Region "进度不同,更新卡片"
|
||||
Try
|
||||
If Card.Children.Count < LoaderList.Count * 2 Then
|
||||
Logger.Warn($"刷新下载管理卡片 {Loader.Name} 失败:卡片中仅有 {Card.Children.Count} 个子项,要求至少有 {LoaderList.Count * 2} 个子项")
|
||||
Logger.Warn($"刷新任务管理卡片 {Loader.Name} 失败:卡片中仅有 {Card.Children.Count} 个子项,要求至少有 {LoaderList.Count * 2} 个子项")
|
||||
Exit Try
|
||||
End If
|
||||
Dim Row As Integer = 0
|
||||
@@ -133,12 +133,12 @@ Public Class PageSpeedLeft
|
||||
Row += 1
|
||||
Next
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex, $"刷新下载管理卡片 {Loader.Name} 失败")
|
||||
Logger.Error(ex, $"刷新任务管理卡片 {Loader.Name} 失败")
|
||||
End Try
|
||||
#End Region
|
||||
End Select
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex, $"更新下载管理显示失败({Loader.State})")
|
||||
Logger.Error(ex, $"更新任务管理显示失败({Loader.State})")
|
||||
End Try
|
||||
ElseIf Not (Loader.State = LoadState.Canceled OrElse Loader.State = LoadState.Finished) Then
|
||||
Try
|
||||
@@ -177,23 +177,23 @@ Public Class PageSpeedLeft
|
||||
Try
|
||||
Card = GetObjectFromXML(CardXAML)
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, "新建下载管理卡片失败")
|
||||
Logger.Warn(ex, "新建任务管理卡片失败")
|
||||
Logger.Info($"出错的卡片内容:{vbCrLf}{CardXAML}")
|
||||
Throw
|
||||
End Try
|
||||
FrmSpeedRight.PanMain.Children.Insert(0, Card)
|
||||
RightCards.Add(Loader.Name, Card)
|
||||
Logger.Info($"新建下载管理卡片:{Loader.Name}")
|
||||
Logger.Info($"新建任务管理卡片:{Loader.Name}")
|
||||
'添加取消按钮
|
||||
Dim Cancel As New MyIconButton With {.Name = "BtnCancel", .Logo = "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", .Height = 20, .Margin = New Thickness(0, 10, 10, 0), .LogoScale = 1.1, .HorizontalAlignment = HorizontalAlignment.Right, .VerticalAlignment = VerticalAlignment.Top}
|
||||
Card.Children.Add(Cancel)
|
||||
AddHandler Cancel.Click,
|
||||
Sub(sender As MyIconButton, e As EventArgs)
|
||||
AniDispose(sender, False)
|
||||
AniDispose(Card, True, Sub() If FrmSpeedRight.PanMain.Children.Count = 0 AndAlso FrmMain.PageCurrent = FormMain.PageType.DownloadManager Then FrmMain.PageBack())
|
||||
AniDispose(Card, True, Sub() If FrmSpeedRight.PanMain.Children.Count = 0 AndAlso FrmMain.PageCurrent = FormMain.PageType.TaskManager Then FrmMain.PageBack())
|
||||
RightCards.Remove(Loader.Name)
|
||||
LoaderTaskbar.Remove(Loader)
|
||||
Logger.Info($"关闭下载管理卡片:{Loader.Name},且移出任务列表")
|
||||
Logger.Info($"关闭任务管理卡片:{Loader.Name},且移出任务列表")
|
||||
RunInThread(Sub() Loader.Cancel())
|
||||
End Sub
|
||||
'如果已经失败,再刷新一次,修改成失败的控件
|
||||
@@ -203,11 +203,11 @@ Public Class PageSpeedLeft
|
||||
End If
|
||||
#End Region
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex, "添加下载管理卡片失败")
|
||||
Logger.Error(ex, "添加任务管理卡片失败")
|
||||
End Try
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Logger.Error(ex, "刷新下载管理显示失败")
|
||||
Logger.Error(ex, "刷新任务管理显示失败")
|
||||
End Try
|
||||
End Sub
|
||||
Public Sub TaskRemove(Loader As Object)
|
||||
@@ -218,7 +218,7 @@ Public Class PageSpeedLeft
|
||||
Dim Card As Grid = RightCards(Loader.Name)
|
||||
FrmSpeedRight.PanMain.Children.Remove(Card)
|
||||
RightCards.Remove(Loader.Name)
|
||||
Logger.Info($"移除下载管理卡片:{Loader.Name}")
|
||||
Logger.Info($"移除任务管理卡片:{Loader.Name}")
|
||||
End Sub)
|
||||
End If
|
||||
End Sub
|
||||
@@ -227,7 +227,7 @@ Public Class PageSpeedLeft
|
||||
''' 若没有任务,尝试返回主页。
|
||||
''' </summary>
|
||||
Private Sub TryReturnToHome()
|
||||
If FrmSpeedRight.PanMain.Children.Count = 0 AndAlso FrmMain.PageCurrent = FormMain.PageType.DownloadManager Then
|
||||
If FrmSpeedRight.PanMain.Children.Count = 0 AndAlso FrmMain.PageCurrent = FormMain.PageType.TaskManager Then
|
||||
FrmMain.PageBack()
|
||||
End If
|
||||
End Sub
|
||||
|
||||
@@ -282,6 +282,7 @@
|
||||
<Compile Include="Pages\PageDownload\PageDownloadInstall.xaml.vb">
|
||||
<DependentUpon>PageDownloadInstall.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Pages\PageDownload\PageDownloadModify.vb" />
|
||||
<Compile Include="Pages\PageDownload\Resource\PageDownloadCompDetail.xaml.vb">
|
||||
<DependentUpon>PageDownloadCompDetail.xaml</DependentUpon>
|
||||
</Compile>
|
||||
@@ -939,9 +940,14 @@
|
||||
<LogicalName>MeloongCore.Wpf.dll</LogicalName>
|
||||
<Link>Resources\MeloongCore.Wpf.dll</Link>
|
||||
</None>
|
||||
<None Include="..\PCLCS\bin\Microsoft.Win32.Registry.dll">
|
||||
<LogicalName>Microsoft.Win32.Registry.dll</LogicalName>
|
||||
<Link>Resources\Microsoft.Win32.Registry.dll</Link>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\Heads\ChongQing.png" />
|
||||
<Resource Include="Images\Heads\HerobrineXia.png" />
|
||||
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
|
||||
|
||||
Binary file not shown.
@@ -12,6 +12,6 @@
|
||||
<br/>
|
||||
|
||||
### 相关资源
|
||||
- [PCL 下载](https://meloong.com/afd/p/0164034c016c11ebafcb52540025c377):下载可免费使用的正式版 PCL!
|
||||
- [PCL 下载](https://meloong.com/pcl):下载可免费使用的正式版 PCL!
|
||||
- [PCL 功能投票](https://github.com/Meloong-Git/PCL/discussions/2):来参加投票吧,开发者会优先处理票数较高的项目!
|
||||
- [帮助文档库](https://github.com/LTCatt/PCL2Help):PCL 内置帮助文档的存储库
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loaded 100 of 105 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user