Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67d830ea29 | ||
|
|
a84601d99c | ||
|
|
a5a3800957 | ||
|
|
baba653a67 | ||
|
|
5733cbdb0e | ||
|
|
6e1072d042 | ||
|
|
efe58c9748 | ||
|
|
5b58e3868d | ||
|
|
0a48feba18 | ||
|
|
7d89b90ae1 | ||
|
|
f6cae4ea23 | ||
|
|
a09baee5f8 | ||
|
|
fa3d5bc09b | ||
|
|
c169532794 | ||
|
|
37313e4d6e | ||
|
|
dd9cdaf15f | ||
|
|
6a2f95e96f | ||
|
|
d682b59803 | ||
|
|
02347239b4 | ||
|
|
dc4eda533c | ||
|
|
d86ad945a6 | ||
|
|
12be800391 | ||
|
|
c47b80df36 | ||
|
|
43b0acba08 | ||
|
|
c1cad542c2 | ||
|
|
09f9b8f1e5 | ||
|
|
4241060010 | ||
|
|
0dab617b64 | ||
|
|
515b7e89c5 | ||
|
|
d83f98b34a | ||
|
|
e2030970a3 | ||
|
|
e623d3464a | ||
|
|
81346f0821 | ||
|
|
810c6e8618 | ||
|
|
a5199d32ed | ||
|
|
b058e7722d | ||
|
|
4beef0fb5d | ||
|
|
eb195373ea | ||
|
|
65ca2e0cf4 | ||
|
|
09e420ad6b | ||
|
|
95429cc9cc | ||
|
|
5a78a46b28 | ||
|
|
3ae624d4fb | ||
|
|
91f39f2afd | ||
|
|
65da5b67f7 | ||
|
|
268cb092dd | ||
|
|
9728af60d0 | ||
|
|
7affc1e24f | ||
|
|
3076f3a6b7 | ||
|
|
02c8b7211b | ||
|
|
5843649cfa | ||
|
|
f78250020e | ||
|
|
031cd898f6 | ||
|
|
1b272a6f06 | ||
|
|
f2e9be66c1 | ||
|
|
c719803849 | ||
|
|
2b485233cb | ||
|
|
5198298336 | ||
|
|
a1a2d53d31 | ||
|
|
ec6784d57d | ||
|
|
83ebc61d8a | ||
|
|
63d48b09e3 | ||
|
|
4a25b066d2 | ||
|
|
8c8b1066bf | ||
|
|
fb4386952d | ||
|
|
fa054d9a9e | ||
|
|
6705444fb4 | ||
|
|
c98e0367d0 | ||
|
|
2c7b532188 | ||
|
|
e2a3dd3281 | ||
|
|
e712280c7b | ||
|
|
8308577549 | ||
|
|
f0c634db78 | ||
|
|
7b623dbc2d | ||
|
|
037e6b19b4 | ||
|
|
5ff7aed46e | ||
|
|
3779d74e93 | ||
|
|
3b8de89309 | ||
|
|
f18ede2c7a | ||
|
|
4158e95f87 | ||
|
|
a77c2385cd | ||
|
|
fe5dc2d07f | ||
|
|
17c2049d2c | ||
|
|
0eebb8ec78 | ||
|
|
58d40976a7 | ||
|
|
b2f2bcf4b6 | ||
|
|
2a6708cc08 | ||
|
|
c9c00bc23f | ||
|
|
7fa092eb25 | ||
|
|
d4e692b69f | ||
|
|
8479c47da0 | ||
|
|
971aa0034d | ||
|
|
b717bb31c8 | ||
|
|
3037a9e384 | ||
|
|
6794b24292 | ||
|
|
d774de5436 | ||
|
|
e4ae1a057a | ||
|
|
41250d8387 | ||
|
|
79778490d5 | ||
|
|
e1e084aea3 | ||
|
|
836d55e445 | ||
|
|
3574931bfb | ||
|
|
c9aabd6a55 | ||
|
|
e6c6975047 | ||
|
|
9c41a807c3 | ||
|
|
a0fe5903f0 | ||
|
|
06be5b5b17 | ||
|
|
73d43310c9 | ||
|
|
9b54ea3dee | ||
|
|
fa5026ccd7 | ||
|
|
ca3e7f40e1 | ||
|
|
8004385d6f | ||
|
|
2c2bd2d489 | ||
|
|
80cf5957f2 | ||
|
|
7d139cbd73 | ||
|
|
8a770f63cf | ||
|
|
07e824b0a1 | ||
|
|
794c3003f3 | ||
|
|
d956fb2521 | ||
|
|
6005856661 | ||
|
|
920817cc34 | ||
|
|
c1167632a6 | ||
|
|
352799c0ed | ||
|
|
3e5ba70516 | ||
|
|
9420c37640 | ||
|
|
ce164dd6fd | ||
|
|
c1a26a119f | ||
|
|
10cc561ac6 | ||
|
|
ba883713d9 | ||
|
|
d01372495b | ||
|
|
5c2a78e5e0 | ||
|
|
74a5e1d6ab | ||
|
|
800347fa37 | ||
|
|
cf74de5789 | ||
|
|
1748fec3dd | ||
|
|
854546444b | ||
|
|
1224c1a427 | ||
|
|
c02045bfe0 | ||
|
|
820e4c0cef | ||
|
|
e99293ecd2 | ||
|
|
fd7049ceed | ||
|
|
11ecc11566 | ||
|
|
78d8a778c7 | ||
|
|
f4448eddf4 | ||
|
|
9fc2a502ef | ||
|
|
2392752756 | ||
|
|
3fd6c9d483 | ||
|
|
ba0929983c | ||
|
|
b5adc1175c |
No files matched your search
@@ -1,5 +1,5 @@
|
||||
name: "Minecraft 崩溃"
|
||||
description: "PCL2 提示 “Minecraft 出现错误”,或游戏崩溃"
|
||||
description: "PCL 提示 “Minecraft 出现错误”,或游戏崩溃"
|
||||
labels: [· Bug, 新反馈]
|
||||
body:
|
||||
- type: checkboxes
|
||||
@@ -8,13 +8,11 @@ body:
|
||||
label: "检查项"
|
||||
description: "请逐个检查下列项目,并勾选确认。"
|
||||
options:
|
||||
- label: "**我所启动的游戏不是整合包,且未安装任何 Mod。** 安装 Mod 后的游戏崩溃不是 PCL2 的原因,请 **不要** 提交反馈。你可以在 QQ 群里或者论坛找人求助,但这里并不是你求助的地方。"
|
||||
- label: "**我所启动的游戏不是整合包,且未安装任何 Mod。** 安装 Mod 后的游戏崩溃基本不是 PCL 的原因,请 **不要** 提交反馈。你可以在论坛或社区找人求助,但这里并不是你求助的地方。"
|
||||
required: false
|
||||
- label: "**我已尝试使用 HMCL 启动,HMCL 没有出现问题。** 如果 HMCL 也无法启动就不是 PCL 导致的问题,请 **不要** 提交反馈。[下载 HMCL](https://hmcl.huangyuhui.net/download)"
|
||||
required: true
|
||||
- label: "**我已尝试使用其他启动器,其他启动器没有出现问题。** 如果其他启动器也存在问题,证明是游戏本身的问题,并非 PCL2 的问题,请 **不要** 提交反馈。[下载其他启动器](https://www.mcbbs.net/forum.php?mod=forumdisplay&fid=43&filter=typeid&typeid=908)"
|
||||
required: true
|
||||
- label: "我已点击 `设置 → 启动器 → 检查更新` 确认了启动器已为最新版,且最新版未修复这个 Bug。[怎样更新?](https://shimo.im/docs/qKPttVvXKqPD8YDC#anchor-oTgb)"
|
||||
required: true
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 中搜索,确认了这一 Bug 未被提交过。"
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一 Bug 未被提交过。"
|
||||
required: true
|
||||
- type: textarea
|
||||
id: "yml-2"
|
||||
@@ -26,8 +24,16 @@ body:
|
||||
- type: textarea
|
||||
id: "yml-3"
|
||||
attributes:
|
||||
label: 错误报告、日志与附件
|
||||
description: "上传 PCL2 提供的错误报告(在崩溃时选择导出错误报告)。如果没有,也可以上传 PCL 文件夹中的 Log1~5.txt。"
|
||||
label: PCL 错误报告、日志与附件
|
||||
description: "上传 PCL 提供的错误报告(在崩溃时选择导出错误报告)。如果没有,也可以上传 PCL 文件夹中的 Log1~5.txt。"
|
||||
placeholder: "先点击这个文本框,然后再将文件直接拖拽到文本框中以上传。"
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
- type: textarea
|
||||
id: "yml-4"
|
||||
attributes:
|
||||
label: HMCL 启动脚本
|
||||
description: "在 HMCL 中进入版本列表,点击版本右侧的三个点,选择 生成启动脚本。"
|
||||
placeholder: "先点击这个文本框,然后再将文件直接拖拽到文本框中以上传。"
|
||||
validations:
|
||||
required: false
|
||||
@@ -1,5 +1,5 @@
|
||||
name: "下载或登录时卡住或失败"
|
||||
description: "下载游戏、Mod 或整合包,登录正版账号时出现问题"
|
||||
description: "下载游戏、Mod 或整合包,或登录正版账号时出现问题"
|
||||
labels: [· Bug, 新反馈]
|
||||
body:
|
||||
- type: checkboxes
|
||||
@@ -8,13 +8,11 @@ body:
|
||||
label: "检查项"
|
||||
description: "请逐个检查下列项目,并勾选确认。"
|
||||
options:
|
||||
- label: "**我已尝试使用其他启动器,其他启动器没有出现问题。** 如果其他启动器也存在问题,证明是网络环境不佳[(解决方法)](https://shimo.im/docs/qKPttVvXKqPD8YDC#anchor-0hDr),并非 PCL2 的问题,请 **不要** 提交反馈。[下载其他启动器](https://www.mcbbs.net/forum.php?mod=forumdisplay&fid=43&filter=typeid&typeid=908)"
|
||||
- label: "**我已尝试使用其他启动器,其他启动器没有出现问题。** 如果其他启动器也存在问题,证明是网络环境不佳[(解决方法)](https://shimo.im/docs/qKPttVvXKqPD8YDC#anchor-0hDr),并非 PCL 的问题,请 **不要** 提交反馈。[下载 HMCL](https://hmcl.huangyuhui.net/download)"
|
||||
required: false
|
||||
- label: "我知晓大多数此类问题都是网络环境不佳导致的,但我确实认为我的问题可能是 PCL 导致的,和网络环境无关。"
|
||||
required: true
|
||||
- label: "我已知晓大多数此类问题都是网络环境不佳导致的,但我确实认为我的问题是 PCL2 导致的,和我的网络环境无关。"
|
||||
required: true
|
||||
- label: "我已点击 `设置 → 启动器 → 检查更新` 确认了启动器已为最新版,且最新版未修复这个 Bug。[怎样更新?](https://shimo.im/docs/qKPttVvXKqPD8YDC#anchor-oTgb)"
|
||||
required: true
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 中搜索,确认了这一 Bug 未被提交过。"
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一 Bug 未被提交过。"
|
||||
required: true
|
||||
- type: textarea
|
||||
id: "yml-2"
|
||||
@@ -40,4 +38,4 @@ body:
|
||||
description: "上传 PCL 文件夹中的 Log1~5.txt。[日志在哪?](https://shimo.im/docs/qKPttVvXKqPD8YDC#anchor-fvZW)"
|
||||
placeholder: "先点击这个文本框,然后再将文件直接拖拽到文本框中以上传。"
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
@@ -8,9 +8,7 @@ body:
|
||||
label: "检查项"
|
||||
description: "请逐个检查下列项目,并勾选确认。"
|
||||
options:
|
||||
- label: "我已点击 `设置 → 启动器 → 检查更新` 确认了启动器已为最新版,且最新版未修复这个 Bug。[怎样更新?](https://shimo.im/docs/qKPttVvXKqPD8YDC#anchor-oTgb)"
|
||||
required: true
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 中搜索,确认了这一 Bug 未被提交过。"
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一 Bug 未被提交过。"
|
||||
required: true
|
||||
- type: textarea
|
||||
id: "yml-2"
|
||||
@@ -36,4 +34,4 @@ body:
|
||||
description: "上传 PCL 文件夹中的 Log1~5.txt。[日志在哪?](https://shimo.im/docs/qKPttVvXKqPD8YDC#anchor-fvZW)"
|
||||
placeholder: "先点击这个文本框,然后再将文件直接拖拽到文本框中以上传。"
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
@@ -8,9 +8,7 @@ body:
|
||||
label: "检查项"
|
||||
description: "请逐个检查下列项目,并勾选确认。"
|
||||
options:
|
||||
- label: "我已点击 `设置 → 启动器 → 检查更新` 确认了启动器已为最新版,且最新版未实现这项建议。[怎样更新?](https://shimo.im/docs/qKPttVvXKqPD8YDC#anchor-oTgb)"
|
||||
required: true
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 中搜索,确认了这一建议未被提交过。"
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一建议未被提交过。"
|
||||
required: true
|
||||
- label: "我已查看 [功能投票页面](https://github.com/Hex-Dragon/PCL2/discussions/categories/%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8/),确认了这一建议未在投票列表中。"
|
||||
required: true
|
||||
|
||||
@@ -2,7 +2,10 @@ blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 帮助文档反馈
|
||||
url: https://github.com/LTCatt/PCL2Help/issues
|
||||
about: 提交 PCL2 帮助系统(更多 → 帮助)相关的反馈
|
||||
about: 提交与 PCL 帮助文档(更多 → 帮助)中的具体内容相关的反馈
|
||||
- name: 提问
|
||||
url: https://github.com/Hex-Dragon/PCL2/discussions/new?category=%E6%8F%90%E9%97%AE
|
||||
about: 我不想提交反馈,只是想问一些问题
|
||||
about: 我想问一些 PCL 相关的问题……
|
||||
- name: 讨论
|
||||
url: https://github.com/Hex-Dragon/PCL2/discussions/new?category=%E8%AE%A8%E8%AE%BA
|
||||
about: 我想讨论一些 PCL 相关的事情……
|
||||
@@ -8,13 +8,11 @@ body:
|
||||
label: "检查项"
|
||||
description: "请逐个检查下列项目,并勾选确认。"
|
||||
options:
|
||||
- label: "我已点击 `设置 → 启动器 → 检查更新` 确认了启动器已为最新版,且最新版未实现这一功能。[怎样更新?](https://shimo.im/docs/qKPttVvXKqPD8YDC#anchor-oTgb)"
|
||||
required: true
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 中搜索,确认了这一提案未被提交过。"
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一提案未被提交过。"
|
||||
required: true
|
||||
- label: "我已查看 [功能投票页面](https://github.com/Hex-Dragon/PCL2/discussions/categories/%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8/),确认了这一提案未在投票列表中。"
|
||||
required: true
|
||||
- label: "我已知晓还没做的新功能真的太多了,忙不过来,所以新功能提案几乎不会被处理,也不建议再提交新功能提案 qwq……"
|
||||
- label: "我知晓还没做的新功能真的太多了,忙不过来,所以新功能提案几乎不会被处理,也不建议再提交新功能提案 qwq……"
|
||||
required: true
|
||||
- type: textarea
|
||||
id: "yml-2"
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Window}">
|
||||
<ContentPresenter/>
|
||||
<ContentPresenter />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
@@ -133,9 +133,9 @@
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<Border BorderBrush="{DynamicResource ColorBrush1}" BorderThickness="1.5" CornerRadius="3" MaxWidth="500" Background="White" Margin="0,0,4,4" SnapsToDevicePixels="True">
|
||||
<Border Loaded="TooltipLoaded" Unloaded="TooltipUnloaded" BorderBrush="{DynamicResource ColorBrush1}" BorderThickness="1.5" CornerRadius="4" MaxWidth="500" Background="#E5FFFFFF" Margin="0,0,4,4" SnapsToDevicePixels="True">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect Opacity="0.4" BlurRadius="4" ShadowDepth="2" />
|
||||
<DropShadowEffect Opacity="0.3" BlurRadius="4" ShadowDepth="2" Color="{DynamicResource ColorObject1}" />
|
||||
</Border.Effect>
|
||||
<TextBlock Text="{TemplateBinding ContentControl.Content}" TextWrapping="Wrap" Foreground="{DynamicResource ColorBrush1}" Margin="7,5" FontSize="12"/>
|
||||
</Border>
|
||||
@@ -147,7 +147,7 @@
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<Border BorderBrush="{DynamicResource ColorBrush1}" BorderThickness="1" CornerRadius="3" Background="White" Margin="0,0,4,4" SnapsToDevicePixels="True">
|
||||
<Border Loaded="TooltipLoaded" Unloaded="TooltipUnloaded" BorderBrush="{DynamicResource ColorBrush1}" BorderThickness="1" CornerRadius="3" Background="White" Margin="0,0,4,4" SnapsToDevicePixels="True">
|
||||
<TextBlock Text="{TemplateBinding ContentControl.Content}" TextWrapping="Wrap" Foreground="{DynamicResource ColorBrush1}" Margin="7,5" FontSize="12"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
@@ -277,7 +277,7 @@
|
||||
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
|
||||
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
|
||||
<Setter Property="UndoLimit" Value="15" />
|
||||
<Setter Property="MaxLength" Value="10000" />
|
||||
<Setter Property="MaxLength" Value="1000" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ColorBrushBg0}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
@@ -292,8 +292,8 @@
|
||||
Margin="5,0,0,0" Cursor="IBeam" />
|
||||
<!-- 由于未知原因,TextView 会有 2,0 的 Margin -->
|
||||
</Border>
|
||||
<Border Margin="{TemplateBinding Padding}" IsHitTestVisible="False" VerticalAlignment="Center" BorderThickness="0">
|
||||
<TextBlock x:Name="labHint" Foreground="{DynamicResource ColorBrush1}" Opacity="0.3" Margin="7,0,0,0" Padding="{TemplateBinding BorderThickness}" FontSize="{TemplateBinding FontSize}" FontFamily="Resources/#PCL English, Microsoft YaHei UI" />
|
||||
<Border Margin="{TemplateBinding Padding}" IsHitTestVisible="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" BorderThickness="0">
|
||||
<TextBlock x:Name="labHint" Foreground="{DynamicResource ColorBrush1}" Opacity="0.25" Margin="7,0,0,0" Padding="{TemplateBinding BorderThickness}" FontSize="{TemplateBinding FontSize}" FontFamily="Resources/#PCL English, Microsoft YaHei UI" />
|
||||
</Border>
|
||||
<TextBlock Grid.Row="1" x:Name="labWrong" Height="0" Foreground="{TemplateBinding BorderBrush}" Padding="0,4,0,0" />
|
||||
</Grid>
|
||||
@@ -345,7 +345,7 @@
|
||||
|
||||
<!-- ComboBox -->
|
||||
<DataTemplate x:Key="ComboBoxItemTemplateWithDelete">
|
||||
<local:MyListItem Title="{Binding}" FontSize="13" PaddingRight="40" MaxHeight="26" IsScaleAnimationEnabled="False" Margin="-1,0,0,0">
|
||||
<local:MyListItem Title="{Binding}" FontSize="13" MaxHeight="26" IsScaleAnimationEnabled="False" Margin="-3,0,0,0">
|
||||
<local:MyListItem.Buttons>
|
||||
<x:Array Type="local:MyIconButton">
|
||||
<local:MyIconButton Grid.Column="1" Height="20" Width="20" VerticalAlignment="Center" Tag="{Binding}" Click="MyIconButton_Click" Logo="F1 M 26.9166,22.1667L 37.9999,33.25L 49.0832,22.1668L 53.8332,26.9168L 42.7499,38L 53.8332,49.0834L 49.0833,53.8334L 37.9999,42.75L 26.9166,53.8334L 22.1666,49.0833L 33.25,38L 22.1667,26.9167L 26.9166,22.1667 Z" />
|
||||
@@ -367,7 +367,10 @@
|
||||
<Grid>
|
||||
<Border x:Name="border" Background="{TemplateBinding Background}" CornerRadius="3" BorderThickness="1" BorderBrush="{TemplateBinding Foreground}" />
|
||||
<ToggleButton Margin="0,0,1.5,0" Foreground="{TemplateBinding Foreground}" IsTabStop="False" SnapsToDevicePixels="False" Focusable="False" IsChecked="{Binding IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press" />
|
||||
<ContentPresenter Margin="8.5,0,21.5,0" IsHitTestVisible="False" Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" VerticalAlignment="Center" />
|
||||
<ContentPresenter x:Name="PART_Content" Margin="8.5,0,21.5,0" IsHitTestVisible="False" VerticalAlignment="Center"
|
||||
Content="{TemplateBinding SelectionBoxItem}"
|
||||
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
|
||||
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" />
|
||||
<Popup x:Name="PART_Popup" SnapsToDevicePixels="True" VerticalOffset="-1.5" AllowsTransparency="True" IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Placement="Bottom" PopupAnimation="Fade">
|
||||
<Grid x:Name="PanPopup">
|
||||
<Border Background="White" CornerRadius="3" />
|
||||
@@ -463,6 +466,11 @@
|
||||
</Style>
|
||||
<Style TargetType="ComboBoxItem" x:Key="MyComboBoxItem">
|
||||
<Setter Property="OverridesDefaultStyle" Value="True" />
|
||||
<Setter Property="ToolTipService.Placement" Value="Relative" />
|
||||
<Setter Property="ToolTipService.VerticalOffset" Value="-2" />
|
||||
<Setter Property="ToolTipService.HorizontalOffset" Value="200" />
|
||||
<Setter Property="ToolTipService.InitialShowDelay" Value="150" />
|
||||
<Setter Property="ToolTipService.BetweenShowDelay" Value="150" />
|
||||
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
|
||||
<Setter Property="Background" Value="{StaticResource ColorBrushTransparent}" />
|
||||
<Setter Property="Template">
|
||||
|
||||
@@ -19,26 +19,36 @@ Public Class Application
|
||||
|
||||
'开始
|
||||
Private Sub Application_Startup(sender As Object, e As StartupEventArgs) Handles Me.Startup
|
||||
ApplicationStartTick = GetTimeTick()
|
||||
SecretOnApplicationStart()
|
||||
Try
|
||||
SecretOnApplicationStart()
|
||||
'检查参数调用
|
||||
If e.Args.Length > 0 Then
|
||||
If e.Args(0) = "--update" Then
|
||||
'自动更新
|
||||
UpdateReplace(e.Args(1), e.Args(2).Trim(""""), e.Args(3).Trim(""""), e.Args(4))
|
||||
Environment.[Exit](Result.Cancel)
|
||||
Exit Sub
|
||||
ElseIf e.Args(0).StartsWith("--link") Then
|
||||
'稍作等待后切换到联机页面
|
||||
Thread.Sleep(1000)
|
||||
'FormMain.IsLinkRestart = True
|
||||
Environment.Exit(Result.Cancel)
|
||||
ElseIf e.Args(0).StartsWithF("--memory") Then
|
||||
'内存优化
|
||||
Dim Ram = My.Computer.Info.AvailablePhysicalMemory
|
||||
Try
|
||||
PageOtherTest.MemoryOptimizeInternal()
|
||||
Catch ex As Exception
|
||||
MsgBox(ex.Message, MsgBoxStyle.Critical, "内存优化失败")
|
||||
Environment.Exit(-1)
|
||||
End Try
|
||||
If My.Computer.Info.AvailablePhysicalMemory < Ram Then '避免 ULong 相减出现负数
|
||||
Environment.Exit(0)
|
||||
Else
|
||||
Environment.Exit((My.Computer.Info.AvailablePhysicalMemory - Ram) / 1024) '返回清理的内存量(K)
|
||||
End If
|
||||
#If DEBUG Then
|
||||
ElseIf e.Args(0) = "--make" Then
|
||||
'制作更新包
|
||||
UpdateMake(e.Args(1))
|
||||
Environment.[Exit](Result.Cancel)
|
||||
Exit Sub
|
||||
ElseIf e.Args(0) = "--edit1" Then
|
||||
ExeEdit(e.Args(1), True)
|
||||
Environment.Exit(Result.Cancel)
|
||||
ElseIf e.Args(0) = "--edit2" Then
|
||||
ExeEdit(e.Args(1), False)
|
||||
Environment.Exit(Result.Cancel)
|
||||
#End If
|
||||
End If
|
||||
End If
|
||||
@@ -49,24 +59,36 @@ Public Class Application
|
||||
Directory.CreateDirectory(PathTemp)
|
||||
If Not CheckPermission(PathTemp) Then Throw New Exception("PCL 没有对 " & PathTemp & " 的访问权限")
|
||||
Catch ex As Exception
|
||||
MyMsgBox("手动设置的缓存文件夹不可用,PCL 将使用默认缓存文件夹。" & vbCrLf & "错误原因:" & GetExceptionDetail(ex), "缓存文件夹不可用")
|
||||
Setup.Set("SystemSystemCache", "")
|
||||
PathTemp = IO.Path.GetTempPath() & "PCL\"
|
||||
If PathTemp = IO.Path.GetTempPath() & "PCL\" Then
|
||||
MyMsgBox("PCL 无法访问缓存文件夹,可能导致程序出错或无法正常使用!" & vbCrLf & "错误原因:" & GetExceptionDetail(ex), "缓存文件夹不可用")
|
||||
Else
|
||||
MyMsgBox("手动设置的缓存文件夹不可用,PCL 将使用默认缓存文件夹。" & vbCrLf & "错误原因:" & GetExceptionDetail(ex), "缓存文件夹不可用")
|
||||
Setup.Set("SystemSystemCache", "")
|
||||
PathTemp = IO.Path.GetTempPath() & "PCL\"
|
||||
End If
|
||||
End Try
|
||||
Directory.CreateDirectory(PathTemp & "Cache")
|
||||
Directory.CreateDirectory(PathTemp & "Download")
|
||||
Directory.CreateDirectory(PathAppdata)
|
||||
'检测单例
|
||||
#If Not DEBUG Then
|
||||
Dim WindowHwnd As IntPtr = FindWindow(Nothing, "Plain Craft Launcher 2 ")
|
||||
Dim WindowHwnd As IntPtr = FindWindow(Nothing, "Plain Craft Launcher ")
|
||||
If WindowHwnd = IntPtr.Zero Then FindWindow(Nothing, "Plain Craft Launcher 2 ")
|
||||
If WindowHwnd <> IntPtr.Zero Then
|
||||
'将已有的 PCL 窗口拖出来
|
||||
ShowWindowToTop(WindowHwnd)
|
||||
'播放提示音并退出
|
||||
Beep()
|
||||
Environment.[Exit](Result.Cancel)
|
||||
Exit Sub
|
||||
End If
|
||||
#End If
|
||||
'设置 ToolTipService 默认值
|
||||
ToolTipService.InitialShowDelayProperty.OverrideMetadata(GetType(DependencyObject), New FrameworkPropertyMetadata(300))
|
||||
ToolTipService.BetweenShowDelayProperty.OverrideMetadata(GetType(DependencyObject), New FrameworkPropertyMetadata(400))
|
||||
ToolTipService.ShowDurationProperty.OverrideMetadata(GetType(DependencyObject), New FrameworkPropertyMetadata(9999999))
|
||||
ToolTipService.PlacementProperty.OverrideMetadata(GetType(DependencyObject), New FrameworkPropertyMetadata(Primitives.PlacementMode.Bottom))
|
||||
ToolTipService.HorizontalOffsetProperty.OverrideMetadata(GetType(DependencyObject), New FrameworkPropertyMetadata(8.0))
|
||||
ToolTipService.VerticalOffsetProperty.OverrideMetadata(GetType(DependencyObject), New FrameworkPropertyMetadata(4.0))
|
||||
'设置初始窗口
|
||||
If Setup.Get("UiLauncherLogo") AndAlso Not FormMain.IsLinkRestart Then
|
||||
FrmStart = New SplashScreen("Images\icon.ico")
|
||||
@@ -77,9 +99,10 @@ Public Class Application
|
||||
'日志初始化
|
||||
LogStart()
|
||||
'添加日志
|
||||
Log("[Start] 程序版本:" & VersionDisplayName & "(" & VersionCode & ")")
|
||||
Log("[Start] 识别码:" & UniqueAddress & If(ThemeCheckOne(9), ",已解锁反馈主题", ""))
|
||||
Log("[Start] 程序路径:" & PathWithName)
|
||||
Log($"[Start] 程序版本:{VersionDisplayName} ({VersionCode})")
|
||||
Log($"[Start] 识别码:{UniqueAddress}{If(ThemeCheckOne(9), ",已解锁反馈主题", "")}")
|
||||
Log($"[Start] 程序路径:{PathWithName}")
|
||||
Log($"[Start] 系统编码:{Encoding.Default} ({Encoding.Default.CodePage}, GBK={IsGBKEncoding})")
|
||||
'检测压缩包运行
|
||||
If Path.Contains(IO.Path.GetTempPath()) OrElse Path.Contains("AppData\Local\Temp\") Then
|
||||
MyMsgBox("PCL 正在临时文件夹运行,设置、游戏存档等很可能无法保存,且部分功能会无法使用或出错。" & vbCrLf & "请将 PCL 从压缩文件中解压,或是更换文件夹后再继续使用!", "环境警告", "我知道了", IsWarn:=True)
|
||||
@@ -88,13 +111,11 @@ Public Class Application
|
||||
Setup.Load("SystemDebugMode")
|
||||
Setup.Load("SystemDebugAnim")
|
||||
Setup.Load("ToolDownloadThread")
|
||||
Setup.Load("ToolDownloadCert")
|
||||
'网络配置初始化
|
||||
ServicePointManager.Expect100Continue = True
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 Or SecurityProtocolType.Tls Or SecurityProtocolType.Tls11 Or SecurityProtocolType.Tls12
|
||||
ServicePointManager.DefaultConnectionLimit = 1024
|
||||
ServicePointManager.ServerCertificateValidationCallback = New Security.RemoteCertificateValidationCallback(Function() As Boolean
|
||||
Return True
|
||||
End Function)
|
||||
'计时
|
||||
Log("[Start] 第一阶段加载用时:" & GetTimeTick() - ApplicationStartTick & " ms")
|
||||
ApplicationStartTick = GetTimeTick()
|
||||
@@ -104,7 +125,12 @@ Public Class Application
|
||||
#End If
|
||||
AniControlEnabled += 1
|
||||
Catch ex As Exception
|
||||
MsgBox(GetExceptionDetail(ex, True), MsgBoxStyle.Critical, "PCL 初始化错误")
|
||||
Dim FilePath As String = Nothing
|
||||
Try
|
||||
FilePath = PathWithName
|
||||
Catch
|
||||
End Try
|
||||
MsgBox(GetExceptionDetail(ex, True) & vbCrLf & "PCL 所在路径:" & If(String.IsNullOrEmpty(FilePath), "获取失败", FilePath), MsgBoxStyle.Critical, "PCL 初始化错误")
|
||||
FormMain.EndProgramForce(Result.Exception)
|
||||
End Try
|
||||
End Sub
|
||||
@@ -148,7 +174,7 @@ Public Class Application
|
||||
Private Shared ReadOnly AssemblyJsonLock As New Object
|
||||
Private Shared ReadOnly AssemblyDialogLock As New Object
|
||||
Public Shared Function AssemblyResolve(sender As Object, args As ResolveEventArgs) As Assembly
|
||||
If args.Name.StartsWith("NAudio") Then
|
||||
If args.Name.StartsWithF("NAudio") Then
|
||||
SyncLock AssemblyNAudioLock
|
||||
If AssemblyNAudio Is Nothing Then
|
||||
Log("[Start] 加载 DLL:NAudio")
|
||||
@@ -156,7 +182,7 @@ Public Class Application
|
||||
End If
|
||||
Return AssemblyNAudio
|
||||
End SyncLock
|
||||
ElseIf args.Name.StartsWith("Newtonsoft.Json") Then
|
||||
ElseIf args.Name.StartsWithF("Newtonsoft.Json") Then
|
||||
SyncLock AssemblyJsonLock
|
||||
If AssemblyJson Is Nothing Then
|
||||
Log("[Start] 加载 DLL:Json")
|
||||
@@ -164,7 +190,7 @@ Public Class Application
|
||||
End If
|
||||
Return AssemblyJson
|
||||
End SyncLock
|
||||
ElseIf args.Name.StartsWith("Ookii.Dialogs.Wpf") Then
|
||||
ElseIf args.Name.StartsWithF("Ookii.Dialogs.Wpf") Then
|
||||
SyncLock AssemblyDialogLock
|
||||
If AssemblyDialog Is Nothing Then
|
||||
Log("[Start] 加载 DLL:Dialogs")
|
||||
@@ -179,49 +205,56 @@ Public Class Application
|
||||
|
||||
'切换窗口
|
||||
|
||||
|
||||
'控件模板事件
|
||||
Private Sub MyIconButton_Click(sender As Object, e As EventArgs)
|
||||
If Setup.Get("LoginType") = McLoginType.Legacy Then
|
||||
'离线
|
||||
Dim Names As New List(Of String)
|
||||
Names.AddRange(Setup.Get("LoginLegacyName").ToString.Split("¨"))
|
||||
Names.Remove(sender.Tag)
|
||||
Setup.Set("LoginLegacyName", Join(Names, "¨"))
|
||||
FrmLoginLegacy.ComboName.ItemsSource = Names
|
||||
FrmLoginLegacy.ComboName.Text = If(Names.Count > 0, Names(0), "")
|
||||
Else
|
||||
'非离线
|
||||
Dim Token As String = GetStringFromEnum(Setup.Get("LoginType"))
|
||||
Dim Dict As New Dictionary(Of String, String)
|
||||
Dim Names As New List(Of String)
|
||||
Dim Passs As New List(Of String)
|
||||
If Not Setup.Get("Login" & Token & "Email") = "" Then Names.AddRange(Setup.Get("Login" & Token & "Email").ToString.Split("¨"))
|
||||
If Not Setup.Get("Login" & Token & "Pass") = "" Then Passs.AddRange(Setup.Get("Login" & Token & "Pass").ToString.Split("¨"))
|
||||
For i = 0 To Names.Count - 1
|
||||
Dict.Add(Names(i), Passs(i))
|
||||
Next
|
||||
Dict.Remove(sender.Tag)
|
||||
Setup.Set("Login" & Token & "Email", Join(Dict.Keys.ToArray, "¨"))
|
||||
Setup.Set("Login" & Token & "Pass", Join(Dict.Values.ToArray, "¨"))
|
||||
Select Case Token
|
||||
Case "Mojang"
|
||||
FrmLoginMojang.ComboName.ItemsSource = Dict.Keys
|
||||
FrmLoginMojang.ComboName.Text = If(Dict.Keys.Count > 0, Dict.Keys(0), "")
|
||||
FrmLoginMojang.TextPass.Password = If(Dict.Values.Count > 0, Dict.Values(0), "")
|
||||
Case "Nide"
|
||||
FrmLoginNide.ComboName.ItemsSource = Dict.Keys
|
||||
FrmLoginNide.ComboName.Text = If(Dict.Keys.Count > 0, Dict.Keys(0), "")
|
||||
FrmLoginNide.TextPass.Password = If(Dict.Values.Count > 0, Dict.Values(0), "")
|
||||
Case "Auth"
|
||||
FrmLoginAuth.ComboName.ItemsSource = Dict.Keys
|
||||
FrmLoginAuth.ComboName.Text = If(Dict.Keys.Count > 0, Dict.Keys(0), "")
|
||||
FrmLoginAuth.TextPass.Password = If(Dict.Values.Count > 0, Dict.Values(0), "")
|
||||
Case Else
|
||||
DebugAssert(True)
|
||||
Exit Sub
|
||||
End Select
|
||||
End If
|
||||
Select Case Setup.Get("LoginType")
|
||||
Case McLoginType.Ms
|
||||
'微软
|
||||
Dim MsJson As JObject = GetJson(Setup.Get("LoginMsJson"))
|
||||
MsJson.Remove(sender.Tag)
|
||||
Setup.Set("LoginMsJson", MsJson.ToString(Newtonsoft.Json.Formatting.None))
|
||||
FrmLoginMs.ComboAccounts.Items.Remove(sender.Parent)
|
||||
Case McLoginType.Legacy
|
||||
'离线
|
||||
Dim Names As New List(Of String)
|
||||
Names.AddRange(Setup.Get("LoginLegacyName").ToString.Split("¨"))
|
||||
Names.Remove(sender.Tag)
|
||||
Setup.Set("LoginLegacyName", Join(Names, "¨"))
|
||||
FrmLoginLegacy.ComboName.ItemsSource = Names
|
||||
FrmLoginLegacy.ComboName.Text = If(Names.Any, Names(0), "")
|
||||
Case Else
|
||||
'第三方
|
||||
Dim Token As String = GetStringFromEnum(Setup.Get("LoginType"))
|
||||
Dim Dict As New Dictionary(Of String, String)
|
||||
Dim Names As New List(Of String)
|
||||
Dim Passs As New List(Of String)
|
||||
If Not Setup.Get("Login" & Token & "Email") = "" Then Names.AddRange(Setup.Get("Login" & Token & "Email").ToString.Split("¨"))
|
||||
If Not Setup.Get("Login" & Token & "Pass") = "" Then Passs.AddRange(Setup.Get("Login" & Token & "Pass").ToString.Split("¨"))
|
||||
For i = 0 To Names.Count - 1
|
||||
Dict.Add(Names(i), Passs(i))
|
||||
Next
|
||||
Dict.Remove(sender.Tag)
|
||||
Setup.Set("Login" & Token & "Email", Join(Dict.Keys, "¨"))
|
||||
Setup.Set("Login" & Token & "Pass", Join(Dict.Values, "¨"))
|
||||
Select Case Token
|
||||
Case "Nide"
|
||||
FrmLoginNide.ComboName.ItemsSource = Dict.Keys
|
||||
FrmLoginNide.ComboName.Text = If(Dict.Keys.Any, Dict.Keys(0), "")
|
||||
FrmLoginNide.TextPass.Password = If(Dict.Values.Any, Dict.Values(0), "")
|
||||
Case "Auth"
|
||||
FrmLoginAuth.ComboName.ItemsSource = Dict.Keys
|
||||
FrmLoginAuth.ComboName.Text = If(Dict.Keys.Any, Dict.Keys(0), "")
|
||||
FrmLoginAuth.TextPass.Password = If(Dict.Values.Any, Dict.Values(0), "")
|
||||
End Select
|
||||
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
|
||||
@@ -134,23 +134,21 @@
|
||||
|
||||
'实现自定义事件
|
||||
Private Sub Button_MouseUp(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonUp
|
||||
If IsMouseDown Then
|
||||
Log("[Control] 按下按钮:" & Text)
|
||||
RaiseEvent Click(sender, e)
|
||||
If Not String.IsNullOrEmpty(Tag) Then
|
||||
If Tag.ToString.StartsWith("链接-") OrElse Tag.ToString.StartsWith("启动-") Then
|
||||
Hint("主页自定义按钮语法已更新,且不再兼容老版本语法,请查看新的自定义示例!")
|
||||
End If
|
||||
If Not IsMouseDown Then Exit Sub
|
||||
Log("[Control] 按下按钮:" & Text)
|
||||
RaiseEvent Click(sender, e)
|
||||
If Not String.IsNullOrEmpty(Tag) Then
|
||||
If Tag.ToString.StartsWithF("链接-") OrElse Tag.ToString.StartsWithF("启动-") Then
|
||||
Hint("主页自定义按钮语法已更新,且不再兼容老版本语法,请查看新的自定义示例!")
|
||||
End If
|
||||
ModEvent.TryStartEvent(EventType, EventData)
|
||||
End If
|
||||
ModEvent.TryStartEvent(EventType, EventData)
|
||||
End Sub
|
||||
Public Property EventType As String
|
||||
Get
|
||||
Return GetValue(EventTypeProperty)
|
||||
End Get
|
||||
|
||||
Set(ByVal value As String)
|
||||
Set(value As String)
|
||||
SetValue(EventTypeProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
@@ -159,8 +157,7 @@
|
||||
Get
|
||||
Return GetValue(EventDataProperty)
|
||||
End Get
|
||||
|
||||
Set(ByVal value As String)
|
||||
Set(value As String)
|
||||
SetValue(EventDataProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
|
||||
@@ -41,10 +41,6 @@
|
||||
End Sub
|
||||
Private IsLoad As Boolean = False
|
||||
Private Sub MyCard_Loaded() Handles Me.Loaded
|
||||
'卡片的进入时动画已被页面通用切换动画替代
|
||||
'If LoadAnimation AndAlso IsLoad Then
|
||||
' StartHeightAnimation(Math.Min(50, ActualHeight), ActualHeight - Math.Min(50, ActualHeight), True)
|
||||
'End If
|
||||
If IsLoad Then Exit Sub
|
||||
'初次加载限定
|
||||
If Not Title = "" AndAlso IsNothing(MainTextBlock) Then
|
||||
@@ -113,7 +109,7 @@
|
||||
Case 6
|
||||
ForgeDownloadListItemPreload(Stack, Stack.Tag, AddressOf ForgeSave_Click, True)
|
||||
Case 8
|
||||
CompFilesCardPreload(Stack, Stack.Tag, AddressOf FrmDownloadMod.ProjectClick)
|
||||
CompFilesCardPreload(Stack, Stack.Tag)
|
||||
End Select
|
||||
'实现控件虚拟化
|
||||
For Each Data As Object In Stack.Tag
|
||||
@@ -134,7 +130,7 @@
|
||||
FrmDownloadInstall.MinecraftSelected(sender, e)
|
||||
End Sub, False))
|
||||
Case 8
|
||||
If Distinct(CType(Stack.Tag, List(Of CompFile)), Function(a, b) a.DisplayName = b.DisplayName).Count <>
|
||||
If CType(Stack.Tag, List(Of CompFile)).Distinct(Function(a, b) a.DisplayName = b.DisplayName).Count <>
|
||||
CType(Stack.Tag, List(Of CompFile)).Count Then
|
||||
'存在重复的名称(#1344)
|
||||
Stack.Children.Add(CType(Data, CompFile).ToListItem(AddressOf FrmDownloadCompDetail.Save_Click, BadDisplayName:=True))
|
||||
@@ -143,7 +139,7 @@
|
||||
Stack.Children.Add(CType(Data, CompFile).ToListItem(AddressOf FrmDownloadCompDetail.Save_Click))
|
||||
End If
|
||||
Case 9
|
||||
If Distinct(CType(Stack.Tag, List(Of CompFile)), Function(a, b) a.DisplayName = b.DisplayName).Count <>
|
||||
If CType(Stack.Tag, List(Of CompFile)).Distinct(Function(a, b) a.DisplayName = b.DisplayName).Count <>
|
||||
CType(Stack.Tag, List(Of CompFile)).Count Then
|
||||
'存在重复的名称(#1344)
|
||||
Stack.Children.Add(CType(Data, CompFile).ToListItem(AddressOf FrmDownloadCompDetail.Install_Click, AddressOf FrmDownloadCompDetail.Save_Click, BadDisplayName:=True))
|
||||
@@ -155,6 +151,8 @@
|
||||
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 Else
|
||||
Log("未知的虚拟化种类:" & Type, LogLevel.Feedback)
|
||||
End Select
|
||||
@@ -164,7 +162,9 @@
|
||||
End Sub
|
||||
|
||||
'事件
|
||||
Public Property HasMouseAnimation As Boolean = True
|
||||
Private Sub MyCard_MouseEnter(sender As Object, e As MouseEventArgs) Handles Me.MouseEnter
|
||||
If Not HasMouseAnimation Then Exit Sub
|
||||
Dim AniList As New List(Of AniData)
|
||||
If Not IsNothing(MainTextBlock) Then AniList.Add(AaColor(MainTextBlock, TextBlock.ForegroundProperty, "ColorBrush2", 150))
|
||||
If Not IsNothing(MainSwap) Then AniList.Add(AaColor(MainSwap, Shapes.Path.FillProperty, "ColorBrush2", 150))
|
||||
@@ -176,6 +176,7 @@
|
||||
AniStart(AniList, "MyCard Mouse " & Uuid)
|
||||
End Sub
|
||||
Private Sub MyCard_MouseLeave(sender As Object, e As MouseEventArgs) Handles Me.MouseLeave
|
||||
If Not HasMouseAnimation Then Exit Sub
|
||||
Dim AniList As New List(Of AniData)
|
||||
If Not IsNothing(MainTextBlock) Then AniList.Add(AaColor(MainTextBlock, TextBlock.ForegroundProperty, "ColorBrush1", 250))
|
||||
If Not IsNothing(MainSwap) Then AniList.Add(AaColor(MainSwap, Shapes.Path.FillProperty, "ColorBrush1", 250))
|
||||
@@ -208,7 +209,7 @@
|
||||
Dim AnimList As New List(Of AniData)
|
||||
If DeltaHeight > 10 OrElse (DeltaHeight < -10 AndAlso Not IsNothing(SwapControl)) Then '如果不是需要折叠的卡片,高度减小时的弹跳会吞掉按钮下边框
|
||||
'高度增加较大,使用弹起动画
|
||||
Dim Delta As Double = MathRange(Math.Abs(DeltaHeight) * 0.05, 3, 10) * Math.Sign(DeltaHeight)
|
||||
Dim Delta As Double = MathClamp(Math.Abs(DeltaHeight) * 0.05, 3, 10) * Math.Sign(DeltaHeight)
|
||||
AnimList.AddRange({
|
||||
AaHeight(Me, DeltaHeight + Delta, 300, If(IsLoadAnimation, 30, 0), If(DeltaHeight > FrmMain.Height, New AniEaseInFluent(AniEasePower.ExtraStrong), New AniEaseOutFluent(AniEasePower.ExtraStrong))),
|
||||
AaHeight(Me, -Delta, 150, 260, Ease:=New AniEaseOutFluent(AniEasePower.Strong))
|
||||
@@ -216,7 +217,7 @@
|
||||
Else
|
||||
'普通的改变就行啦
|
||||
AnimList.AddRange({
|
||||
AaHeight(Me, DeltaHeight, MathRange(Math.Abs(DeltaHeight) * 4, 150, 250),, New AniEaseOutFluent)
|
||||
AaHeight(Me, DeltaHeight, MathClamp(Math.Abs(DeltaHeight) * 4, 150, 250),, New AniEaseOutFluent)
|
||||
})
|
||||
End If
|
||||
AnimList.Add(AaCode(Sub()
|
||||
|
||||
@@ -128,4 +128,10 @@
|
||||
End If
|
||||
End Sub
|
||||
|
||||
Public ReadOnly Property ContentPresenter As ContentPresenter
|
||||
Get
|
||||
Return Template.FindName("PART_Content", Me)
|
||||
End Get
|
||||
End Property
|
||||
|
||||
End Class
|
||||
@@ -16,6 +16,11 @@
|
||||
<DropShadowEffect ShadowDepth="0" BlurRadius="10" Opacity="0.2" />
|
||||
</Border.Effect>
|
||||
</Border>
|
||||
<Border x:Name="PanProgress" CornerRadius="1000" BorderThickness="0.001" Background="#2FFFFFFF" ClipToBounds="True" Visibility="Collapsed">
|
||||
<Border.Clip>
|
||||
<RectangleGeometry x:Name="RectProgress" Rect="0,5,40,35" />
|
||||
</Border.Clip>
|
||||
</Border>
|
||||
<Path x:Name="Path" Stretch="Uniform" Margin="12" Fill="{DynamicResource ColorBrush8}" RenderTransformOrigin="0.5,0.5">
|
||||
<Path.RenderTransform>
|
||||
<ScaleTransform />
|
||||
|
||||
@@ -4,6 +4,24 @@
|
||||
Public Event Click(sender As Object, e As MouseButtonEventArgs) '自定义事件
|
||||
Public Event RightClick(sender As Object, e As MouseButtonEventArgs)
|
||||
|
||||
'进度条
|
||||
Private _Progress As Double = 0
|
||||
Public Property Progress As Double
|
||||
Get
|
||||
Return _Progress
|
||||
End Get
|
||||
Set(value As Double)
|
||||
If _Progress = value Then Exit Property
|
||||
_Progress = value
|
||||
If value < 0.0001 Then
|
||||
PanProgress.Visibility = Visibility.Collapsed
|
||||
Else
|
||||
PanProgress.Visibility = Visibility.Visible
|
||||
RectProgress.Rect = New Rect(0, 40 * (1 - value), 40, 40 * value)
|
||||
End If
|
||||
End Set
|
||||
End Property
|
||||
|
||||
'自定义属性
|
||||
Public Uuid As Integer = GetUuid()
|
||||
Private _Logo As String = ""
|
||||
@@ -84,7 +102,7 @@
|
||||
Get
|
||||
Return _CanRightClick
|
||||
End Get
|
||||
Set(ByVal value As Boolean)
|
||||
Set(value As Boolean)
|
||||
_CanRightClick = value
|
||||
End Set
|
||||
End Property
|
||||
@@ -161,7 +179,7 @@
|
||||
|
||||
AniStop("MyExtraButton Color " & Uuid)
|
||||
If IsMouseOver Then
|
||||
PanColor.SetResourceReference(BackgroundProperty, "ColorBrush2")
|
||||
PanColor.SetResourceReference(BackgroundProperty, "ColorBrush4")
|
||||
Else
|
||||
PanColor.SetResourceReference(BackgroundProperty, "ColorBrush3")
|
||||
End If
|
||||
|
||||
@@ -11,17 +11,17 @@
|
||||
If _IsWarn = value Then Exit Property
|
||||
_IsWarn = value
|
||||
If _IsWarn Then
|
||||
BorderBrush = New MyColor("#99FF4444")
|
||||
Gradient1.Color = New MyColor("#88FFBBBB")
|
||||
Gradient2.Color = New MyColor("#88FF8888")
|
||||
BorderBrush = New MyColor("#CCFF4444")
|
||||
Gradient1.Color = New MyColor("#BBFFBBBB")
|
||||
Gradient2.Color = New MyColor("#BBFF8888")
|
||||
Path.Fill = New MyColor("#BF0000")
|
||||
LabText.Foreground = New MyColor("#BF0000")
|
||||
BtnClose.Foreground = New MyColor("#BF0000")
|
||||
Path.Data = (New GeometryConverter).ConvertFromString("F1 M 58.5832,55.4172L 17.4169,55.4171C 15.5619,53.5621 15.5619,50.5546 17.4168,48.6996L 35.201,15.8402C 37.056,13.9852 40.0635,13.9852 41.9185,15.8402L 58.5832,48.6997C 60.4382,50.5546 60.4382,53.5622 58.5832,55.4172 Z M 34.0417,25.7292L 36.0208,41.9584L 39.9791,41.9583L 41.9583,25.7292L 34.0417,25.7292 Z M 38,44.3333C 36.2511,44.3333 34.8333,45.7511 34.8333,47.5C 34.8333,49.2489 36.2511,50.6667 38,50.6667C 39.7489,50.6667 41.1666,49.2489 41.1666,47.5C 41.1666,45.7511 39.7489,44.3333 38,44.3333 Z ")
|
||||
Else
|
||||
BorderBrush = New MyColor("#994D76FF")
|
||||
Gradient1.Color = New MyColor("#88B0D0FF")
|
||||
Gradient2.Color = New MyColor("#889EBAFF")
|
||||
BorderBrush = New MyColor("#CC4D76FF")
|
||||
Gradient1.Color = New MyColor("#BBB0D0FF")
|
||||
Gradient2.Color = New MyColor("#BB9EBAFF")
|
||||
Path.Fill = New MyColor("#0062BF")
|
||||
LabText.Foreground = New MyColor("#0062BF")
|
||||
BtnClose.Foreground = New MyColor("#0062BF")
|
||||
@@ -34,7 +34,7 @@
|
||||
Get
|
||||
Return LabText.Text
|
||||
End Get
|
||||
Set(ByVal value As String)
|
||||
Set(value As String)
|
||||
LabText.Text = value
|
||||
End Set
|
||||
End Property
|
||||
@@ -43,13 +43,12 @@
|
||||
Get
|
||||
Return BtnClose.Visibility = Visibility.Visible
|
||||
End Get
|
||||
Set(ByVal value As Boolean)
|
||||
Set(value As Boolean)
|
||||
BtnClose.Visibility = If(value, Visibility.Visible, Visibility.Collapsed)
|
||||
End Set
|
||||
End Property
|
||||
|
||||
Public RelativeSetup As String = ""
|
||||
|
||||
Private Sub MyHint_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
|
||||
If CanClose AndAlso Setup.Get(RelativeSetup) Then
|
||||
Visibility = Visibility.Collapsed
|
||||
@@ -60,6 +59,40 @@
|
||||
AniDispose(Me, False)
|
||||
End Sub
|
||||
|
||||
'触发点击事件
|
||||
Private IsMouseDown As Boolean = False
|
||||
Private Sub MyHint_MouseUp(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonUp
|
||||
If Not IsMouseDown Then Return
|
||||
IsMouseDown = False
|
||||
Log("[Control] 按下提示条" & If(String.IsNullOrEmpty(Name), "", ":" & Name))
|
||||
e.Handled = True
|
||||
ModEvent.TryStartEvent(EventType, EventData)
|
||||
End Sub
|
||||
Private Sub MyHint_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonDown
|
||||
IsMouseDown = True
|
||||
End Sub
|
||||
Private Sub MyHint_MouseLeave() Handles Me.MouseLeave
|
||||
IsMouseDown = False
|
||||
End Sub
|
||||
Public Property EventType As String
|
||||
Get
|
||||
Return GetValue(EventTypeProperty)
|
||||
End Get
|
||||
Set(value As String)
|
||||
SetValue(EventTypeProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
Public Shared ReadOnly EventTypeProperty As DependencyProperty = DependencyProperty.Register("EventType", GetType(String), GetType(MyHint), New PropertyMetadata(Nothing))
|
||||
Public Property EventData As String
|
||||
Get
|
||||
Return GetValue(EventDataProperty)
|
||||
End Get
|
||||
Set(value As String)
|
||||
SetValue(EventDataProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
Public Shared ReadOnly EventDataProperty As DependencyProperty = DependencyProperty.Register("EventData", GetType(String), GetType(MyHint), New PropertyMetadata(Nothing))
|
||||
|
||||
End Class
|
||||
Partial Public Module ModAnimation
|
||||
Public Sub AniDispose(Control As MyHint, RemoveFromChildren As Boolean, Optional CallBack As ParameterizedThreadStart = Nothing)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Public Class MyIconButton
|
||||
|
||||
'声明
|
||||
Public Event Click(sender As Object, e As EventArgs) '自定义事件
|
||||
'自定义事件
|
||||
Public Event Click(sender As Object, e As EventArgs)
|
||||
|
||||
'自定义属性
|
||||
|
||||
@@ -50,13 +50,31 @@
|
||||
|
||||
'触发点击事件
|
||||
Private Sub Button_MouseUp(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonUp
|
||||
If IsMouseDown Then
|
||||
Log("[Control] 按下图标按钮" & If(String.IsNullOrEmpty(Name), "", ":" & Name))
|
||||
RaiseEvent Click(sender, e)
|
||||
e.Handled = True
|
||||
Button_MouseUp()
|
||||
End If
|
||||
If Not IsMouseDown Then Return
|
||||
Log("[Control] 按下图标按钮" & If(String.IsNullOrEmpty(Name), "", ":" & Name))
|
||||
RaiseEvent Click(sender, e)
|
||||
e.Handled = True
|
||||
Button_MouseUp()
|
||||
ModEvent.TryStartEvent(EventType, EventData)
|
||||
End Sub
|
||||
Public Property EventType As String
|
||||
Get
|
||||
Return GetValue(EventTypeProperty)
|
||||
End Get
|
||||
Set(value As String)
|
||||
SetValue(EventTypeProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
Public Shared ReadOnly EventTypeProperty As DependencyProperty = DependencyProperty.Register("EventType", GetType(String), GetType(MyIconButton), New PropertyMetadata(Nothing))
|
||||
Public Property EventData As String
|
||||
Get
|
||||
Return GetValue(EventDataProperty)
|
||||
End Get
|
||||
Set(value As String)
|
||||
SetValue(EventDataProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
Public Shared ReadOnly EventDataProperty As DependencyProperty = DependencyProperty.Register("EventData", GetType(String), GetType(MyIconButton), New PropertyMetadata(Nothing))
|
||||
|
||||
'鼠标点击判定(务必放在点击事件之后,以使得 Button_MouseUp 先于 Button_MouseLeave 执行)
|
||||
Private IsMouseDown As Boolean = False
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<Border x:Name="PanBack" x:Class="MyIconTextButton"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center" Background="{StaticResource ColorBrushSemiTransparent}" CornerRadius="13.5" MinHeight="27" MaxHeight="27">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path x:Name="ShapeLogo" HorizontalAlignment="Left" RenderTransformOrigin="0.5,0.5" Margin="12,0,0,0" Stretch="Uniform" Fill="White" MaxHeight="16" MaxWidth="16" VerticalAlignment="Center" />
|
||||
<TextBlock x:Name="LabText" VerticalAlignment="Center" Grid.Column="1" Foreground="White" Margin="7,0,12,1" Grid.ColumnSpan="2" SnapsToDevicePixels="False" UseLayoutRounding="False" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
@@ -0,0 +1,186 @@
|
||||
Public Class MyIconTextButton
|
||||
|
||||
'基础
|
||||
|
||||
Public Uuid As Integer = GetUuid()
|
||||
Public Event Check(sender As Object, raiseByMouse As Boolean)
|
||||
Public Event Change(sender As Object, raiseByMouse As Boolean)
|
||||
Public Sub RaiseChange()
|
||||
RaiseEvent Change(Me, False)
|
||||
End Sub '使外部程序可以引发本控件的 Change 事件
|
||||
|
||||
'自定义属性
|
||||
|
||||
Public Property Logo As String
|
||||
Get
|
||||
Return ShapeLogo.Data.ToString
|
||||
End Get
|
||||
Set(value As String)
|
||||
ShapeLogo.Data = (New GeometryConverter).ConvertFromString(value)
|
||||
End Set
|
||||
End Property
|
||||
Private _LogoScale As Double = 1
|
||||
Public Property LogoScale() As Double
|
||||
Get
|
||||
Return _LogoScale
|
||||
End Get
|
||||
Set(value As Double)
|
||||
_LogoScale = value
|
||||
If Not IsNothing(ShapeLogo) Then ShapeLogo.RenderTransform = New ScaleTransform With {.ScaleX = LogoScale, .ScaleY = LogoScale}
|
||||
End Set
|
||||
End Property
|
||||
|
||||
Public Property Text As String
|
||||
Get
|
||||
Return GetValue(TextProperty)
|
||||
End Get
|
||||
Set(value As String)
|
||||
SetValue(TextProperty, value)
|
||||
End Set
|
||||
End Property '内容
|
||||
Public Shared ReadOnly TextProperty As DependencyProperty = DependencyProperty.Register("Text", GetType(String), GetType(MyIconTextButton), New PropertyMetadata(New PropertyChangedCallback(
|
||||
Sub(sender As DependencyObject, e As DependencyPropertyChangedEventArgs)
|
||||
If Not IsNothing(sender) Then CType(sender, MyIconTextButton).LabText.Text = e.NewValue
|
||||
End Sub)))
|
||||
Public Enum ColorState
|
||||
Black
|
||||
Highlight
|
||||
End Enum
|
||||
Private _ColorType As ColorState = ColorState.Black
|
||||
Public Property ColorType As ColorState
|
||||
Get
|
||||
Return _ColorType
|
||||
End Get
|
||||
Set(value As ColorState)
|
||||
_ColorType = value
|
||||
RefreshColor()
|
||||
End Set
|
||||
End Property '颜色类别
|
||||
Public Shared ReadOnly ColorTypeProperty As DependencyProperty =
|
||||
DependencyProperty.Register("ColorType", GetType(ColorState), GetType(MyIconTextButton), New PropertyMetadata())
|
||||
|
||||
'点击事件
|
||||
|
||||
Public Event Click(sender As Object, e As RouteEventArgs)
|
||||
Private IsMouseDown As Boolean = False
|
||||
Private Sub MyIconTextButton_MouseUp() Handles Me.MouseLeftButtonUp
|
||||
If Not IsMouseDown Then Exit Sub
|
||||
Log("[Control] 按下带图标按钮:" & Text)
|
||||
IsMouseDown = False
|
||||
RaiseEvent Click(Me, New RouteEventArgs(True))
|
||||
ModEvent.TryStartEvent(EventType, EventData)
|
||||
RefreshColor()
|
||||
End Sub
|
||||
Private Sub MyIconTextButton_MouseDown() Handles Me.MouseLeftButtonDown
|
||||
IsMouseDown = True
|
||||
RefreshColor()
|
||||
End Sub
|
||||
Private Sub MyIconTextButton_MouseLeave() Handles Me.MouseLeave
|
||||
IsMouseDown = False
|
||||
RefreshColor()
|
||||
End Sub
|
||||
Public Property EventType As String
|
||||
Get
|
||||
Return GetValue(EventTypeProperty)
|
||||
End Get
|
||||
Set(value As String)
|
||||
SetValue(EventTypeProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
Public Shared ReadOnly EventTypeProperty As DependencyProperty = DependencyProperty.Register("EventType", GetType(String), GetType(MyIconTextButton), New PropertyMetadata(Nothing))
|
||||
Public Property EventData As String
|
||||
Get
|
||||
Return GetValue(EventDataProperty)
|
||||
End Get
|
||||
Set(value As String)
|
||||
SetValue(EventDataProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
Public Shared ReadOnly EventDataProperty As DependencyProperty = DependencyProperty.Register("EventData", GetType(String), GetType(MyIconTextButton), New PropertyMetadata(Nothing))
|
||||
|
||||
'动画
|
||||
|
||||
Private Const AnimationTimeOfMouseIn As Integer = 100 '鼠标指向动画长度
|
||||
Private Const AnimationTimeOfMouseOut As Integer = 150 '鼠标移出动画长度
|
||||
Private Sub RefreshColor(Optional obj = Nothing, Optional e = Nothing) Handles Me.MouseEnter, Me.Loaded, Me.IsEnabledChanged
|
||||
Try
|
||||
If IsLoaded AndAlso AniControlEnabled = 0 AndAlso Not False.Equals(e) Then '防止默认属性变更触发动画,若强制不执行动画,则 e 为 False
|
||||
|
||||
Select Case ColorType
|
||||
Case ColorState.Black
|
||||
If IsMouseDown Then
|
||||
'按下
|
||||
AniStart(AaColor(Me, BackgroundProperty, "ColorBrush6", 70), "MyIconTextButton Color " & Uuid)
|
||||
ElseIf IsMouseOver Then
|
||||
'指向
|
||||
AniStart({
|
||||
AaColor(ShapeLogo, Shapes.Path.FillProperty, "ColorBrush3", AnimationTimeOfMouseIn),
|
||||
AaColor(LabText, TextBlock.ForegroundProperty, "ColorBrush3", AnimationTimeOfMouseIn)
|
||||
}, "MyIconTextButton Checked " & Uuid)
|
||||
AniStart(AaColor(Me, BackgroundProperty, "ColorBrushBg1", AnimationTimeOfMouseIn), "MyIconTextButton Color " & Uuid)
|
||||
ElseIf IsEnabled Then
|
||||
'正常
|
||||
AniStart({
|
||||
AaColor(ShapeLogo, Shapes.Path.FillProperty, "ColorBrush1", AnimationTimeOfMouseOut),
|
||||
AaColor(LabText, TextBlock.ForegroundProperty, "ColorBrush1", AnimationTimeOfMouseOut)
|
||||
}, "MyIconTextButton Checked " & Uuid)
|
||||
AniStart(AaColor(Me, BackgroundProperty, ColorSemiTransparent - Background, AnimationTimeOfMouseOut), "MyIconTextButton Color " & Uuid)
|
||||
Else
|
||||
'禁用
|
||||
AniStart({
|
||||
AaColor(ShapeLogo, Shapes.Path.FillProperty, "ColorBrushGray5", 100),
|
||||
AaColor(LabText, TextBlock.ForegroundProperty, "ColorBrushGray5", 100)
|
||||
}, "MyIconTextButton Checked " & Uuid)
|
||||
AniStart(AaColor(Me, BackgroundProperty, ColorSemiTransparent - Background, AnimationTimeOfMouseOut), "MyIconTextButton Color " & Uuid)
|
||||
End If
|
||||
Case ColorState.Highlight
|
||||
If IsMouseDown Then
|
||||
'按下
|
||||
AniStart(AaColor(Me, BackgroundProperty, "ColorBrush6", 70), "MyIconTextButton Color " & Uuid)
|
||||
ElseIf IsMouseOver Then
|
||||
'指向
|
||||
AniStart({
|
||||
AaColor(ShapeLogo, Shapes.Path.FillProperty, "ColorBrush3", AnimationTimeOfMouseIn),
|
||||
AaColor(LabText, TextBlock.ForegroundProperty, "ColorBrush3", AnimationTimeOfMouseIn)
|
||||
}, "MyIconTextButton Checked " & Uuid)
|
||||
AniStart(AaColor(Me, BackgroundProperty, "ColorBrushBg1", AnimationTimeOfMouseIn), "MyIconTextButton Color " & Uuid)
|
||||
ElseIf IsEnabled Then
|
||||
'正常
|
||||
AniStart({
|
||||
AaColor(ShapeLogo, Shapes.Path.FillProperty, "ColorBrush3", AnimationTimeOfMouseOut),
|
||||
AaColor(LabText, TextBlock.ForegroundProperty, "ColorBrush3", AnimationTimeOfMouseOut)
|
||||
}, "MyIconTextButton Checked " & Uuid)
|
||||
AniStart(AaColor(Me, BackgroundProperty, ColorSemiTransparent - Background, AnimationTimeOfMouseOut), "MyIconTextButton Color " & Uuid)
|
||||
Else
|
||||
'禁用
|
||||
AniStart({
|
||||
AaColor(ShapeLogo, Shapes.Path.FillProperty, "ColorBrushGray5", 100),
|
||||
AaColor(LabText, TextBlock.ForegroundProperty, "ColorBrushGray5", 100)
|
||||
}, "MyIconTextButton Checked " & Uuid)
|
||||
AniStart(AaColor(Me, BackgroundProperty, ColorSemiTransparent - Background, AnimationTimeOfMouseOut), "MyIconTextButton Color " & Uuid)
|
||||
End If
|
||||
End Select
|
||||
|
||||
Else
|
||||
|
||||
'不使用动画
|
||||
AniStop("MyIconTextButton Checked " & Uuid)
|
||||
AniStop("MyIconTextButton Color " & Uuid)
|
||||
Select Case ColorType
|
||||
Case ColorState.Black
|
||||
Background = ColorSemiTransparent
|
||||
ShapeLogo.SetResourceReference(Shapes.Path.FillProperty, If(IsEnabled, "ColorBrush1", "ColorBrushGray5"))
|
||||
LabText.SetResourceReference(TextBlock.ForegroundProperty, If(IsEnabled, "ColorBrush1", "ColorBrushGray5"))
|
||||
Case ColorState.Highlight
|
||||
Background = ColorSemiTransparent
|
||||
ShapeLogo.SetResourceReference(Shapes.Path.FillProperty, If(IsEnabled, "ColorBrush3", "ColorBrushGray5"))
|
||||
LabText.SetResourceReference(TextBlock.ForegroundProperty, If(IsEnabled, "ColorBrush3", "ColorBrushGray5"))
|
||||
End Select
|
||||
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "刷新带图标按钮颜色出错")
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -11,7 +11,7 @@
|
||||
<ColumnDefinition x:Name="ColumnPaddingLeft" Width="0" />
|
||||
<ColumnDefinition x:Name="ColumnLogo" Width="4" />
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition x:Name="ColumnPaddingRight" Width="0" />
|
||||
<ColumnDefinition x:Name="ColumnPaddingRight" Width="4" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="1*" />
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
If _RectBack Is Nothing Then
|
||||
Dim Rect As New Border With {
|
||||
.Name = "RectBack",
|
||||
.CornerRadius = New CornerRadius(If(IsScaleAnimationEnabled, 6, 0)),
|
||||
.CornerRadius = New CornerRadius(If(IsScaleAnimationEnabled OrElse Height > 40, 6, 0)),
|
||||
.RenderTransform = If(IsScaleAnimationEnabled, New ScaleTransform(0.8, 0.8), Nothing),
|
||||
.RenderTransformOrigin = New Point(0.5, 0.5),
|
||||
.BorderThickness = New Thickness(GetWPFSize(1)),
|
||||
@@ -104,42 +104,34 @@
|
||||
ColumnPaddingLeft.Width = New GridLength(value)
|
||||
End Set
|
||||
End Property
|
||||
Public Property PaddingRight As Integer
|
||||
Get
|
||||
Return ColumnPaddingRight.Width.Value
|
||||
End Get
|
||||
Set(value As Integer)
|
||||
ColumnPaddingRight.Width = New GridLength(value)
|
||||
End Set
|
||||
End Property
|
||||
''' <summary>
|
||||
''' 右边距的最小值。
|
||||
''' 在存在右侧按钮时,右边距会被自动设置为 5 + 按钮数 * 25。
|
||||
''' </summary>
|
||||
Public Property MinPaddingRight As Integer = 4
|
||||
|
||||
'按钮
|
||||
Private _Buttons As IEnumerable
|
||||
Public Property Buttons As IEnumerable
|
||||
Private _Buttons As IEnumerable(Of MyIconButton)
|
||||
Public Property Buttons As IEnumerable(Of MyIconButton)
|
||||
Get
|
||||
Return _Buttons
|
||||
End Get
|
||||
Set(value As IEnumerable)
|
||||
Set(value As IEnumerable(Of MyIconButton))
|
||||
_Buttons = value
|
||||
'没有特殊按钮,移除原 Stack
|
||||
If Not IsNothing(ButtonStack) Then
|
||||
If ButtonStack IsNot Nothing Then
|
||||
Children.Remove(ButtonStack)
|
||||
ButtonStack = Nothing
|
||||
End If
|
||||
'获取按钮数量
|
||||
Dim Count As Integer = 0
|
||||
For Each Counter In value
|
||||
Count += 1
|
||||
Next
|
||||
'添加新 Stack
|
||||
Select Case Count
|
||||
Select Case value.Count
|
||||
Case 0
|
||||
'没有按钮,不添加新的
|
||||
Case 1
|
||||
'只有一个按钮
|
||||
For Each Btn As MyIconButton In value
|
||||
If Btn.Height.Equals(Double.NaN) Then Btn.Height = 23
|
||||
If Btn.Width.Equals(Double.NaN) Then Btn.Width = 23
|
||||
If Btn.Height.Equals(Double.NaN) Then Btn.Height = 25
|
||||
If Btn.Width.Equals(Double.NaN) Then Btn.Width = 25
|
||||
With Btn
|
||||
.Opacity = 0
|
||||
.Margin = New Thickness(0, 0, 5, 0)
|
||||
@@ -149,22 +141,18 @@
|
||||
.SnapsToDevicePixels = False
|
||||
.UseLayoutRounding = False
|
||||
End With
|
||||
SetColumn(Btn, 4)
|
||||
SetColumnSpan(Btn, 4)
|
||||
SetRowSpan(Btn, 4)
|
||||
SetColumnSpan(Btn, 10) : SetRowSpan(Btn, 10)
|
||||
Children.Add(Btn)
|
||||
ButtonStack = Btn
|
||||
Next
|
||||
Case Else
|
||||
'有复数按钮,使用 StackPanel
|
||||
ButtonStack = New StackPanel With {.Opacity = 0, .Margin = New Thickness(0, 0, 5, 0), .SnapsToDevicePixels = False, .Orientation = Orientation.Horizontal, .HorizontalAlignment = HorizontalAlignment.Right, .VerticalAlignment = VerticalAlignment.Center, .UseLayoutRounding = False}
|
||||
SetColumn(ButtonStack, 4)
|
||||
SetColumnSpan(ButtonStack, 4)
|
||||
SetRowSpan(ButtonStack, 4)
|
||||
SetColumnSpan(ButtonStack, 10) : SetRowSpan(ButtonStack, 10)
|
||||
'构造按钮
|
||||
For Each Btn As MyIconButton In value
|
||||
If Btn.Height.Equals(Double.NaN) Then Btn.Height = 23
|
||||
If Btn.Width.Equals(Double.NaN) Then Btn.Width = 23
|
||||
If Btn.Height.Equals(Double.NaN) Then Btn.Height = 25
|
||||
If Btn.Width.Equals(Double.NaN) Then Btn.Width = 25
|
||||
CType(ButtonStack, StackPanel).Children.Add(Btn)
|
||||
Next
|
||||
Children.Add(ButtonStack)
|
||||
@@ -222,19 +210,21 @@
|
||||
If Not IsNothing(PathLogo) Then Children.Remove(PathLogo)
|
||||
'添加新 Logo
|
||||
If Not _Logo = "" Then
|
||||
If _Logo.ToLower.StartsWith("http") Then
|
||||
If _Logo.StartsWithF("http", True) Then
|
||||
'网络图片
|
||||
PathLogo = New Image With {
|
||||
.Tag = Me,
|
||||
.IsHitTestVisible = LogoClickable,
|
||||
.Source = New ImageSourceConverter().ConvertFromString(_Logo),
|
||||
.RenderTransformOrigin = New Point(0.5, 0.5),
|
||||
.RenderTransform = New ScaleTransform With {.ScaleX = LogoScale, .ScaleY = LogoScale},
|
||||
.SnapsToDevicePixels = True, .UseLayoutRounding = False}
|
||||
RenderOptions.SetBitmapScalingMode(PathLogo, BitmapScalingMode.Linear)
|
||||
ElseIf _Logo.ToLower.EndsWith(".png") OrElse _Logo.ToLower.EndsWith(".jpg") Then
|
||||
ElseIf _Logo.EndsWithF(".png", True) OrElse _Logo.EndsWithF(".jpg", True) Then
|
||||
'位图
|
||||
Dim Bitmap = New MyBitmap(_Logo)
|
||||
PathLogo = New Canvas With {
|
||||
.Tag = Me,
|
||||
.IsHitTestVisible = LogoClickable,
|
||||
.Background = Bitmap,
|
||||
.RenderTransformOrigin = New Point(0.5, 0.5),
|
||||
@@ -252,6 +242,7 @@
|
||||
Else
|
||||
'矢量图
|
||||
PathLogo = New Shapes.Path With {
|
||||
.Tag = Me,
|
||||
.IsHitTestVisible = LogoClickable, .HorizontalAlignment = HorizontalAlignment.Center, .VerticalAlignment = VerticalAlignment.Center, .Stretch = Stretch.Uniform,
|
||||
.Data = (New GeometryConverter).ConvertFromString(_Logo),
|
||||
.RenderTransformOrigin = New Point(0.5, 0.5),
|
||||
@@ -267,7 +258,7 @@
|
||||
If LogoClickable Then
|
||||
AddHandler PathLogo.MouseLeave, Sub(sender, e) IsLogoDown = False
|
||||
AddHandler PathLogo.MouseLeftButtonDown, Sub(sender, e) IsLogoDown = True
|
||||
AddHandler PathLogo.MouseLeftButtonUp, Sub(sender, e) If IsLogoDown Then IsLogoDown = False : RaiseEvent LogoClick(sender, e)
|
||||
AddHandler PathLogo.MouseLeftButtonUp, Sub(sender, e) If IsLogoDown Then IsLogoDown = False : RaiseEvent LogoClick(sender.Tag, e)
|
||||
End If
|
||||
End If
|
||||
'改变行距
|
||||
@@ -286,6 +277,9 @@
|
||||
End Property
|
||||
|
||||
'图标的点击
|
||||
''' <summary>
|
||||
''' 该 Logo 是否可用点击触发事件。需要在 Logo 属性之前设置。
|
||||
''' </summary>
|
||||
Public Property LogoClickable As Boolean = False
|
||||
Private IsLogoDown As Boolean = False
|
||||
|
||||
@@ -318,7 +312,8 @@
|
||||
'添加竖条控件
|
||||
If IsNothing(RectCheck) Then
|
||||
RectCheck = New Border With {.Width = 5, .Height = If(Checked, Double.NaN, 0), .CornerRadius = New CornerRadius(2, 2, 2, 2),
|
||||
.VerticalAlignment = If(Checked, VerticalAlignment.Stretch, VerticalAlignment.Center), .HorizontalAlignment = HorizontalAlignment.Left, .UseLayoutRounding = False, .SnapsToDevicePixels = False,
|
||||
.VerticalAlignment = If(Checked, VerticalAlignment.Stretch, VerticalAlignment.Center),
|
||||
.HorizontalAlignment = HorizontalAlignment.Left, .UseLayoutRounding = False, .SnapsToDevicePixels = False,
|
||||
.Margin = If(Checked, New Thickness(-1, 6, 0, 6), New Thickness(-1, 0, 0, 0))}
|
||||
RectCheck.SetResourceReference(Border.BackgroundProperty, "ColorBrush3")
|
||||
SetRowSpan(RectCheck, 4)
|
||||
@@ -333,7 +328,7 @@
|
||||
ColumnCheck.Width = New GridLength(If(_Type = CheckType.None OrElse _Type = CheckType.Clickable, If(Height < 40, 4, 2), 6))
|
||||
ColumnLogo.Width = New GridLength(If(_Logo = "", 0, 34) + If(Height < 40, 0, 4))
|
||||
If Not IsNothing(PathLogo) Then
|
||||
If _Logo.EndsWith(".png") Then
|
||||
If _Logo.EndsWithF(".png", True) Then
|
||||
PathLogo.Margin = New Thickness(4, 5, 3, 5)
|
||||
Else
|
||||
PathLogo.Margin = New Thickness(If(Height < 40, 6, 8), 8, If(Height < 40, 4, 6), 8)
|
||||
@@ -447,7 +442,7 @@
|
||||
RectCheck.VerticalAlignment = VerticalAlignment.Center
|
||||
RectCheck.Margin = New Thickness(-1, 0, 0, 0)
|
||||
End If
|
||||
Anim.Add(AaColor(Me, ForegroundProperty, "ColorBrush3", 200))
|
||||
Anim.Add(AaColor(Me, ForegroundProperty, If(Height < 40, "ColorBrush3", "ColorBrush2"), 200))
|
||||
Else
|
||||
'由有变无
|
||||
If Not IsNothing(RectCheck) Then
|
||||
@@ -469,7 +464,7 @@
|
||||
RectCheck.Opacity = 1
|
||||
RectCheck.VerticalAlignment = VerticalAlignment.Stretch
|
||||
End If
|
||||
SetResourceReference(ForegroundProperty, "ColorBrush3")
|
||||
SetResourceReference(ForegroundProperty, If(Height < 40, "ColorBrush3", "ColorBrush2"))
|
||||
Else
|
||||
If Not IsNothing(RectCheck) Then
|
||||
RectCheck.Height = 0
|
||||
@@ -526,10 +521,6 @@
|
||||
Log("[Control] 按下复选列表项(" & (Not Checked).ToString & "):" & Title)
|
||||
SetChecked(Not Checked, True, True)
|
||||
End Select
|
||||
'AniStart({
|
||||
' AaScaleTransform(Me, 1.004 - CType(RenderTransform, ScaleTransform).ScaleX, 150,, New AniEaseOutFluent),
|
||||
' AaScaleTransform(Me, -0.004, 100, 150, New AniEaseInFluent)
|
||||
' }, "MyListItem Scale " & Uuid)
|
||||
End Sub
|
||||
|
||||
'鼠标点击判定
|
||||
@@ -537,15 +528,12 @@
|
||||
Private Sub Button_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles Me.PreviewMouseLeftButtonDown
|
||||
If IsMouseDirectlyOver AndAlso Not Type = CheckType.None Then
|
||||
IsMouseDown = True
|
||||
If Not IsNothing(ButtonStack) Then ButtonStack.IsHitTestVisible = False
|
||||
If ButtonStack IsNot Nothing Then ButtonStack.IsHitTestVisible = False
|
||||
End If
|
||||
'AniStart({
|
||||
' AaScaleTransform(Me, 0.985 - CType(RenderTransform, ScaleTransform).ScaleX, 50,, New AniEaseOutFluent)
|
||||
' }, "MyListItem Scale " & Uuid)
|
||||
End Sub
|
||||
Private Sub Button_MouseLeave(sender As Object, e As Object) Handles Me.MouseLeave, Me.PreviewMouseLeftButtonUp
|
||||
IsMouseDown = False
|
||||
If Not IsNothing(ButtonStack) Then ButtonStack.IsHitTestVisible = True
|
||||
If ButtonStack IsNot Nothing Then ButtonStack.IsHitTestVisible = True
|
||||
End Sub
|
||||
|
||||
'实现自定义事件
|
||||
@@ -599,7 +587,11 @@
|
||||
'有动画
|
||||
Dim Ani As New List(Of AniData)
|
||||
If IsMouseOver AndAlso IsMouseOverAnimationEnabled Then
|
||||
If ButtonStack IsNot Nothing Then Ani.Add(AaOpacity(ButtonStack, 1 - ButtonStack.Opacity, Time * 0.7, Time * 0.3))
|
||||
If ButtonStack IsNot Nothing Then
|
||||
Ani.Add(AaOpacity(ButtonStack, 1 - ButtonStack.Opacity, Time * 0.7, Time * 0.3))
|
||||
Ani.Add(AaDouble(Sub(i) ColumnPaddingRight.Width = New GridLength(Math.Max(0, ColumnPaddingRight.Width.Value + i)),
|
||||
Math.Max(MinPaddingRight, 5 + Buttons.Count * 25) - ColumnPaddingRight.Width.Value, Time * 0.3, Time * 0.7))
|
||||
End If
|
||||
Ani.AddRange({
|
||||
AaColor(RectBack, Border.BackgroundProperty, If(IsMouseDown, "ColorBrush6", "ColorBrushBg1"), Time),
|
||||
AaOpacity(RectBack, 1 - RectBack.Opacity, Time,, New AniEaseOutFluent)
|
||||
@@ -613,27 +605,39 @@
|
||||
End If
|
||||
End If
|
||||
Else
|
||||
If ButtonStack IsNot Nothing Then Ani.Add(AaOpacity(ButtonStack, -ButtonStack.Opacity, Time * 0.5))
|
||||
If ButtonStack IsNot Nothing Then
|
||||
Ani.Add(AaOpacity(ButtonStack, -ButtonStack.Opacity, Time * 0.4))
|
||||
Ani.Add(AaDouble(Sub(i) ColumnPaddingRight.Width = New GridLength(Math.Max(0, ColumnPaddingRight.Width.Value + i)),
|
||||
MinPaddingRight - ColumnPaddingRight.Width.Value, Time * 0.4))
|
||||
End If
|
||||
Ani.Add(AaOpacity(RectBack, -RectBack.Opacity, Time))
|
||||
If IsScaleAnimationEnabled Then Ani.AddRange({
|
||||
AaColor(RectBack, Border.BackgroundProperty, If(IsMouseDown, "ColorBrush6", "ColorBrush7"), Time),
|
||||
AaScaleTransform(Me, 1 - CType(Me.RenderTransform, ScaleTransform).ScaleX, Time * 3,, New AniEaseOutFluent),
|
||||
AaScaleTransform(RectBack, 0.996 - CType(RectBack.RenderTransform, ScaleTransform).ScaleX, Time,, New AniEaseOutFluent),
|
||||
AaScaleTransform(RectBack, -0.246, 1,,, True)
|
||||
})
|
||||
If IsScaleAnimationEnabled Then
|
||||
Ani.AddRange({
|
||||
AaColor(RectBack, Border.BackgroundProperty, If(IsMouseDown, "ColorBrush6", "ColorBrush7"), Time),
|
||||
AaScaleTransform(Me, 1 - CType(RenderTransform, ScaleTransform).ScaleX, Time * 3,, New AniEaseOutFluent),
|
||||
AaScaleTransform(RectBack, 0.996 - CType(RectBack.RenderTransform, ScaleTransform).ScaleX, Time,, New AniEaseOutFluent),
|
||||
AaScaleTransform(RectBack, -0.246, 1,,, True)
|
||||
})
|
||||
End If
|
||||
End If
|
||||
AniStart(Ani, "ListItem Color " & Uuid)
|
||||
Else
|
||||
'无动画
|
||||
If IsMouseOver AndAlso IsMouseOverAnimationEnabled Then
|
||||
If ButtonStack IsNot Nothing Then ButtonStack.Opacity = 1
|
||||
If ButtonStack IsNot Nothing Then
|
||||
ButtonStack.Opacity = 1
|
||||
ColumnPaddingRight.Width = New GridLength(Math.Max(MinPaddingRight, 5 + Buttons.Count * 25))
|
||||
End If
|
||||
'由于鼠标已经移入,所以直接实例化 RectBack
|
||||
RectBack.Background = ColorBg1
|
||||
RectBack.Opacity = 1
|
||||
RectBack.RenderTransform = New ScaleTransform(1, 1)
|
||||
Me.RenderTransform = New ScaleTransform(1, 1)
|
||||
Else
|
||||
If ButtonStack IsNot Nothing Then ButtonStack.Opacity = 0
|
||||
If ButtonStack IsNot Nothing Then
|
||||
ButtonStack.Opacity = 0
|
||||
ColumnPaddingRight.Width = New GridLength(MinPaddingRight)
|
||||
End If
|
||||
Me.RenderTransform = New ScaleTransform(1, 1)
|
||||
If _RectBack IsNot Nothing Then
|
||||
If IsScaleAnimationEnabled Then RectBack.RenderTransform = New ScaleTransform(0.75, 0.75)
|
||||
@@ -647,10 +651,11 @@
|
||||
|
||||
Private Sub MyListItem_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
|
||||
If Checked Then
|
||||
SetResourceReference(ForegroundProperty, "ColorBrush3")
|
||||
SetResourceReference(ForegroundProperty, If(Height < 40, "ColorBrush3", "ColorBrush2"))
|
||||
Else
|
||||
SetResourceReference(ForegroundProperty, "ColorBrush1")
|
||||
End If
|
||||
ColumnPaddingRight.Width = New GridLength(MinPaddingRight)
|
||||
If EventType = "打开帮助" Then
|
||||
Try
|
||||
Dim NewElem = New HelpEntry(GetEventAbsoluteUrls(EventData, EventType)(0)).SetToListItem(Me)
|
||||
|
||||
@@ -46,7 +46,7 @@ Public Class MyLoading
|
||||
Get
|
||||
Return _Text
|
||||
End Get
|
||||
Set(ByVal value As String)
|
||||
Set(value As String)
|
||||
_Text = value
|
||||
RefreshText()
|
||||
End Set
|
||||
@@ -57,7 +57,7 @@ Public Class MyLoading
|
||||
Get
|
||||
Return _TextError
|
||||
End Get
|
||||
Set(ByVal value As String)
|
||||
Set(value As String)
|
||||
_TextError = value
|
||||
RefreshText()
|
||||
End Set
|
||||
@@ -67,29 +67,35 @@ Public Class MyLoading
|
||||
''' </summary>
|
||||
Public Property TextErrorInherit As Boolean = True
|
||||
|
||||
Private Sub RefreshText() Handles Me.IsErrorChanged, Me.Loaded
|
||||
If InnerState = MyLoadingState.Error Then
|
||||
If TextErrorInherit AndAlso State.IsLoader Then
|
||||
Dim Ex As Exception = CType(State, Object).Error
|
||||
If Ex Is Nothing Then
|
||||
LabText.Text = "未知错误"
|
||||
Private Sub RefreshText() Handles Me.IsErrorChanged, Me.Loaded, _State.ProgressChanged
|
||||
RunInUi(
|
||||
Sub()
|
||||
If InnerState = MyLoadingState.Error Then
|
||||
If TextErrorInherit AndAlso State.IsLoader Then
|
||||
Dim Ex As Exception = CType(State, Object).Error
|
||||
If Ex Is Nothing Then
|
||||
LabText.Text = "未知错误"
|
||||
Else
|
||||
Do While Ex.InnerException IsNot Nothing
|
||||
Ex = Ex.InnerException
|
||||
Loop
|
||||
LabText.Text = StrTrim(Ex.Message)
|
||||
If {"远程主机强迫关闭了", "远程方已关闭传输流", "未能解析此远程名称", "由于目标计算机积极拒绝",
|
||||
"操作已超时", "操作超时", "服务器超时", "连接超时"}.Any(Function(s) LabText.Text.Contains(s)) Then
|
||||
LabText.Text = "网络环境不佳,请重试或尝试使用 VPN"
|
||||
End If
|
||||
End If
|
||||
Else
|
||||
Do While Ex.InnerException IsNot Nothing
|
||||
Ex = Ex.InnerException
|
||||
Loop
|
||||
LabText.Text = StrTrim(Ex.Message)
|
||||
If LabText.Text.Contains("远程主机强迫关闭了一个现有的连接") OrElse LabText.Text.Contains("操作已超时") OrElse LabText.Text.Contains("操作超时") OrElse LabText.Text.Contains("服务器超时") OrElse LabText.Text.Contains("连接超时") Then LabText.Text = "网络环境不佳,请重试或尝试使用 VPN"
|
||||
LabText.Text = TextError
|
||||
End If
|
||||
Else
|
||||
LabText.Text = TextError
|
||||
If ShowProgress AndAlso State.IsLoader Then
|
||||
LabText.Text = Text & " - " & Math.Floor(CType(State, Object).Progress * 100) & "%"
|
||||
Else
|
||||
LabText.Text = Text
|
||||
End If
|
||||
End If
|
||||
Else
|
||||
If ShowProgress AndAlso State.IsLoader Then
|
||||
LabText.Text = Text & " - " & Math.Floor(CType(State, Object).Progress * 100) & "%"
|
||||
Else
|
||||
LabText.Text = Text
|
||||
End If
|
||||
End If
|
||||
End Sub)
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
@@ -180,46 +186,7 @@ Public Class MyLoading
|
||||
If Not HasAnimation OrElse IsLooping OrElse Not InnerState = MyLoadingState.Run OrElse AniSpeed > 10 OrElse Not IsLoaded Then Exit Sub
|
||||
IsLooping = True
|
||||
ErrorAnimationWaiting = True
|
||||
If ShowProgress Then
|
||||
RefreshText()
|
||||
AniStart({
|
||||
AaRotateTransform(PathPickaxe, -20 - CType(PathPickaxe.RenderTransform, RotateTransform).Angle, 350, 250, New AniEaseInBack(AniEasePower.Weak)),
|
||||
AaCode(AddressOf RefreshText, 100), '这是为了省下一个 Timer 线程……
|
||||
AaCode(AddressOf RefreshText, 200),
|
||||
AaCode(AddressOf RefreshText, 300),
|
||||
AaCode(AddressOf RefreshText, 400),
|
||||
AaCode(AddressOf RefreshText, 500),
|
||||
AaRotateTransform(PathPickaxe, 50, 900,, New AniEaseOutFluent, True),
|
||||
AaRotateTransform(PathPickaxe, 25, 900,, New AniEaseOutElastic(AniEasePower.Weak)),
|
||||
AaCode(Sub()
|
||||
PathLeft.Opacity = 1
|
||||
PathLeft.Margin = New Thickness(7, 41, 0, 0)
|
||||
PathRight.Opacity = 1
|
||||
PathRight.Margin = New Thickness(14, 41, 0, 0)
|
||||
ErrorAnimationWaiting = False
|
||||
RefreshText()
|
||||
End Sub),
|
||||
AaCode(AddressOf RefreshText, 100),
|
||||
AaCode(AddressOf RefreshText, 200),
|
||||
AaCode(AddressOf RefreshText, 300),
|
||||
AaCode(AddressOf RefreshText, 400),
|
||||
AaCode(AddressOf RefreshText, 500),
|
||||
AaCode(AddressOf RefreshText, 600),
|
||||
AaCode(AddressOf RefreshText, 700),
|
||||
AaCode(AddressOf RefreshText, 800),
|
||||
AaOpacity(PathLeft, -1, 100, 70),
|
||||
AaX(PathLeft, -5, 180, 20, New AniEaseOutFluent),
|
||||
AaY(PathLeft, -6, 180, 20, New AniEaseOutFluent),
|
||||
AaOpacity(PathRight, -1, 100, 70),
|
||||
AaX(PathRight, 5, 180, 20, New AniEaseOutFluent),
|
||||
AaY(PathRight, -6, 180, 20, New AniEaseOutFluent),
|
||||
AaCode(Sub()
|
||||
IsLooping = False
|
||||
AniLoop()
|
||||
End Sub,, True)
|
||||
}, "MyLoader Loop " & Uuid & "/" & GetUuid())
|
||||
Else
|
||||
AniStart({
|
||||
AniStart({
|
||||
AaRotateTransform(PathPickaxe, -20 - CType(PathPickaxe.RenderTransform, RotateTransform).Angle, 350, 250, New AniEaseInBack(AniEasePower.Weak)),
|
||||
AaRotateTransform(PathPickaxe, 50, 900,, New AniEaseOutFluent, True),
|
||||
AaRotateTransform(PathPickaxe, 25, 900,, New AniEaseOutElastic(AniEasePower.Weak)),
|
||||
@@ -241,6 +208,8 @@ Public Class MyLoading
|
||||
AniLoop()
|
||||
End Sub,, True)
|
||||
}, "MyLoader Loop " & Uuid & "/" & GetUuid())
|
||||
If ShowProgress Then
|
||||
|
||||
End If
|
||||
End Sub
|
||||
|
||||
@@ -291,6 +260,7 @@ Public Interface ILoadingTrigger
|
||||
ReadOnly Property IsLoader As Boolean
|
||||
Property LoadingState As MyLoadingState
|
||||
Event LoadingStateChanged(NewState As MyLoadingState, OldState As MyLoadingState)
|
||||
Event ProgressChanged(NewProgress As Double, OldProgress As Double)
|
||||
End Interface
|
||||
|
||||
Public Class MyLoadingStateSimulator
|
||||
@@ -310,4 +280,5 @@ Public Class MyLoadingStateSimulator
|
||||
Public ReadOnly Property IsLoader As Boolean = False Implements ILoadingTrigger.IsLoader
|
||||
|
||||
Public Event LoadingStateChanged(NewState As MyLoadingState, OldState As MyLoadingState) Implements ILoadingTrigger.LoadingStateChanged
|
||||
Public Event ProgressChanged(NewProgress As Double, OldProgress As Double) Implements ILoadingTrigger.ProgressChanged
|
||||
End Class
|
||||
@@ -6,6 +6,8 @@
|
||||
Dim IconControl As Shapes.Path = GetTemplateChild("Icon")
|
||||
If IconControl IsNot Nothing Then IconControl.Data = (New GeometryConverter).ConvertFromString(Icon)
|
||||
End If
|
||||
'对父级设置透明度
|
||||
CType(Parent, ContextMenu).Opacity = Setup.Get("UiLauncherTransparent") / 1000 + 0.4
|
||||
End Sub
|
||||
|
||||
'基础
|
||||
|
||||
@@ -58,11 +58,12 @@
|
||||
Interop.ComponentDispatcher.PopModal()
|
||||
'动画
|
||||
AniStart({
|
||||
AaCode(Sub()
|
||||
If WaitingMyMsgBox.Count = 0 Then
|
||||
AniStart(AaColor(FrmMain.PanMsg, Grid.BackgroundProperty, New MyColor(0, 0, 0, 0) - FrmMain.PanMsg.Background, 200, Ease:=New AniEaseOutFluent(AniEasePower.Weak)))
|
||||
End If
|
||||
End Sub, 30),
|
||||
AaCode(
|
||||
Sub()
|
||||
If Not WaitingMyMsgBox.Any() Then
|
||||
AniStart(AaColor(FrmMain.PanMsg, Grid.BackgroundProperty, New MyColor(0, 0, 0, 0) - FrmMain.PanMsg.Background, 200, Ease:=New AniEaseOutFluent(AniEasePower.Weak)))
|
||||
End If
|
||||
End Sub, 30),
|
||||
AaOpacity(Me, -Opacity, 80, 20),
|
||||
AaDouble(Sub(i) TransformPos.Y += i, 20 - TransformPos.Y, 150, 0, New AniEaseOutFluent),
|
||||
AaDouble(Sub(i) TransformRotate.Angle += i, 6 - TransformRotate.Angle, 150, 0, New AniEaseInFluent(AniEasePower.Weak)),
|
||||
|
||||
@@ -63,11 +63,12 @@
|
||||
Interop.ComponentDispatcher.PopModal()
|
||||
'动画
|
||||
AniStart({
|
||||
AaCode(Sub()
|
||||
If WaitingMyMsgBox.Count = 0 Then
|
||||
AniStart(AaColor(FrmMain.PanMsg, Grid.BackgroundProperty, New MyColor(0, 0, 0, 0) - FrmMain.PanMsg.Background, 200, Ease:=New AniEaseOutFluent(AniEasePower.Weak)))
|
||||
End If
|
||||
End Sub, 30),
|
||||
AaCode(
|
||||
Sub()
|
||||
If Not WaitingMyMsgBox.Any() Then
|
||||
AniStart(AaColor(FrmMain.PanMsg, Grid.BackgroundProperty, New MyColor(0, 0, 0, 0) - FrmMain.PanMsg.Background, 200, Ease:=New AniEaseOutFluent(AniEasePower.Weak)))
|
||||
End If
|
||||
End Sub, 30),
|
||||
AaOpacity(Me, -Opacity, 80, 20),
|
||||
AaDouble(Sub(i) TransformPos.Y += i, 20 - TransformPos.Y, 150, 0, New AniEaseOutFluent),
|
||||
AaDouble(Sub(i) TransformRotate.Angle += i, 6 - TransformRotate.Angle, 150, 0, New AniEaseInFluent(AniEasePower.Weak)),
|
||||
|
||||
@@ -56,11 +56,12 @@
|
||||
Interop.ComponentDispatcher.PopModal()
|
||||
'动画
|
||||
AniStart({
|
||||
AaCode(Sub()
|
||||
If WaitingMyMsgBox.Count = 0 Then
|
||||
AniStart(AaColor(FrmMain.PanMsg, Grid.BackgroundProperty, New MyColor(0, 0, 0, 0) - FrmMain.PanMsg.Background, 200, Ease:=New AniEaseOutFluent(AniEasePower.Weak)))
|
||||
End If
|
||||
End Sub, 30),
|
||||
AaCode(
|
||||
Sub()
|
||||
If Not WaitingMyMsgBox.Any() Then
|
||||
AniStart(AaColor(FrmMain.PanMsg, Grid.BackgroundProperty, New MyColor(0, 0, 0, 0) - FrmMain.PanMsg.Background, 200, Ease:=New AniEaseOutFluent(AniEasePower.Weak)))
|
||||
End If
|
||||
End Sub, 30),
|
||||
AaOpacity(Me, -Opacity, 80, 20),
|
||||
AaDouble(Sub(i) TransformPos.Y += i, 20 - TransformPos.Y, 150, 0, New AniEaseOutFluent),
|
||||
AaDouble(Sub(i) TransformRotate.Angle += i, 6 - TransformRotate.Angle, 150, 0, New AniEaseInFluent(AniEasePower.Weak)),
|
||||
|
||||
@@ -29,21 +29,28 @@
|
||||
'逐个进入动画
|
||||
Dim AniList As New List(Of AniData)
|
||||
Dim Id As Integer = 0, Delay As Integer = 0
|
||||
Dim Controls = GetAllAnimControls()
|
||||
For Each Element As FrameworkElement In Controls
|
||||
Element.Opacity = 0
|
||||
Element.RenderTransform = New TranslateTransform(-25, 0)
|
||||
AniList.Add(AaOpacity(Element, If(TypeOf Element Is TextBlock, 0.6, 1), 200, Delay, New AniEaseOutFluent(AniEasePower.Weak)))
|
||||
AniList.Add(AaTranslateX(Element, 5, 200, Delay, New AniEaseOutFluent))
|
||||
AniList.Add(AaTranslateX(Element, 20, 300, Delay, New AniEaseOutBack(AniEasePower.Weak)))
|
||||
If TypeOf Element Is MyListItem Then
|
||||
AniList.Add(AaCode(Sub()
|
||||
CType(Element, MyListItem).IsMouseOverAnimationEnabled = True
|
||||
CType(Element, MyListItem).RefreshColor(Me, New EventArgs)
|
||||
End Sub, Delay + 280))
|
||||
For Each Element As FrameworkElement In GetAllAnimControls(True)
|
||||
If Element.Visibility = Visibility.Collapsed Then
|
||||
'还原之前的隐藏动画可能导致的改变(#2436)
|
||||
Element.Opacity = 1
|
||||
Element.RenderTransform = New TranslateTransform(0, 0)
|
||||
If TypeOf Element Is MyListItem Then CType(Element, MyListItem).IsMouseOverAnimationEnabled = True
|
||||
Else
|
||||
Element.Opacity = 0
|
||||
Element.RenderTransform = New TranslateTransform(-25, 0)
|
||||
If TypeOf Element Is MyListItem Then CType(Element, MyListItem).IsMouseOverAnimationEnabled = False
|
||||
AniList.Add(AaOpacity(Element, If(TypeOf Element Is TextBlock, 0.6, 1), 200, Delay, New AniEaseOutFluent(AniEasePower.Weak)))
|
||||
AniList.Add(AaTranslateX(Element, 5, 200, Delay, New AniEaseOutFluent))
|
||||
AniList.Add(AaTranslateX(Element, 20, 300, Delay, New AniEaseOutBack(AniEasePower.Weak)))
|
||||
If TypeOf Element Is MyListItem Then
|
||||
AniList.Add(AaCode(Sub()
|
||||
CType(Element, MyListItem).IsMouseOverAnimationEnabled = True
|
||||
CType(Element, MyListItem).RefreshColor(Me, New EventArgs)
|
||||
End Sub, Delay + 280))
|
||||
End If
|
||||
Delay += Math.Max(8, 20 - Id) * 2.5
|
||||
Id += 1
|
||||
End If
|
||||
Delay += Math.Max(8, 20 - Id) * 2.5
|
||||
Id += 1
|
||||
Next
|
||||
AniStart(AniList, "PageLeft PageChange " & Uuid)
|
||||
End If
|
||||
@@ -74,23 +81,22 @@
|
||||
End Sub
|
||||
|
||||
'遍历获取所有需要生成动画的控件
|
||||
Private Function GetAllAnimControls() As List(Of FrameworkElement)
|
||||
Private Function GetAllAnimControls(Optional IgnoreInvisibility As Boolean = False) As List(Of FrameworkElement)
|
||||
Dim AllControls As New List(Of FrameworkElement)
|
||||
GetAllAnimControls(AnimatedControl, AllControls)
|
||||
GetAllAnimControls(AnimatedControl, AllControls, IgnoreInvisibility)
|
||||
Return AllControls
|
||||
End Function
|
||||
Private Sub GetAllAnimControls(Element As FrameworkElement, ByRef AllControls As List(Of FrameworkElement))
|
||||
If Element.Visibility = Visibility.Collapsed Then Exit Sub
|
||||
Private Sub GetAllAnimControls(Element As FrameworkElement, ByRef AllControls As List(Of FrameworkElement), IgnoreInvisibility As Boolean)
|
||||
If Not IgnoreInvisibility AndAlso Element.Visibility = Visibility.Collapsed Then Exit Sub
|
||||
If TypeOf Element Is MyTextButton Then
|
||||
AllControls.Add(Element)
|
||||
ElseIf TypeOf Element Is MyListItem Then
|
||||
CType(Element, MyListItem).IsMouseOverAnimationEnabled = False
|
||||
AllControls.Add(Element)
|
||||
ElseIf TypeOf Element Is ContentControl Then
|
||||
GetAllAnimControls(CType(Element, ContentControl).Content, AllControls)
|
||||
GetAllAnimControls(CType(Element, ContentControl).Content, AllControls, IgnoreInvisibility)
|
||||
ElseIf TypeOf Element Is Panel Then
|
||||
For Each Element2 As FrameworkElement In CType(Element, Panel).Children
|
||||
GetAllAnimControls(Element2, AllControls)
|
||||
GetAllAnimControls(Element2, AllControls, IgnoreInvisibility)
|
||||
Next
|
||||
Else
|
||||
AllControls.Add(Element)
|
||||
|
||||
@@ -2,6 +2,18 @@
|
||||
Inherits AdornerDecorator
|
||||
Public PageUuid As Integer = GetUuid()
|
||||
|
||||
'“返回顶部” 按钮检测的滚动区域
|
||||
Public Property PanScroll As MyScrollViewer
|
||||
Get
|
||||
Return GetValue(PanScrollProperty)
|
||||
End Get
|
||||
Set(value As MyScrollViewer)
|
||||
SetValue(PanScrollProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
Private Shared ReadOnly PanScrollProperty =
|
||||
DependencyProperty.Register("PanScroll", GetType(MyScrollViewer), GetType(MyPageRight), New PropertyMetadata(Nothing))
|
||||
|
||||
'当前状态
|
||||
Public Enum PageStates
|
||||
Empty '默认状态,页面全空
|
||||
@@ -68,7 +80,7 @@
|
||||
Loop
|
||||
RunInUiWait(Sub() FinishedInvoke(RealLoader))
|
||||
Thread.Sleep(20) '由于大量初始化控件会导致掉帧,延迟触发 State 改变事件
|
||||
End Sub
|
||||
End Sub
|
||||
End If
|
||||
AddHandler RealLoader.OnStateChangedUi, Sub(Loader As LoaderBase, NewState As LoadState, OldState As LoadState) RunInUi(Sub() PageLoaderState(Loader, NewState, OldState))
|
||||
'隐藏 UI
|
||||
@@ -77,7 +89,7 @@
|
||||
If PanAlways IsNot Nothing Then PanAlways.Visibility = Visibility.Collapsed
|
||||
'初次运行加载器
|
||||
If PageLoaderAutoRun Then
|
||||
If PageLoader.GetType.Name.StartsWith("LoaderTask") Then
|
||||
If PageLoader.GetType.Name.StartsWithF("LoaderTask") Then
|
||||
PageLoader.Start(CType(PageLoader, Object).StartGetInput(Nothing, PageLoaderInputInvoke))
|
||||
Else
|
||||
Dim Input = Nothing
|
||||
@@ -95,7 +107,7 @@
|
||||
'重试
|
||||
Public Sub PageLoaderRestart(Optional Input As Object = Nothing, Optional IsForceRestart As Boolean = True) '由外部调用的重试
|
||||
If Not PageLoaderAutoRun Then Exit Sub
|
||||
If PageLoader.GetType.Name.StartsWith("LoaderTask") Then
|
||||
If PageLoader.GetType.Name.StartsWithF("LoaderTask") Then
|
||||
PageLoader.Start(CType(PageLoader, Object).StartGetInput(Input, PageLoaderInputInvoke), IsForceRestart:=IsForceRestart)
|
||||
Else
|
||||
If Input Is Nothing AndAlso PageLoaderInputInvoke IsNot Nothing Then Input = PageLoaderInputInvoke()
|
||||
@@ -152,6 +164,7 @@
|
||||
''' </summary>
|
||||
Public Sub PageOnExit()
|
||||
If ModeDebug Then Log("[UI] 已触发 PageOnExit")
|
||||
RaiseEvent PageExit()
|
||||
Select Case PageState
|
||||
Case PageStates.ContentEnter, PageStates.ContentStay
|
||||
PageState = PageStates.PageExit
|
||||
@@ -168,6 +181,7 @@
|
||||
Case PageStates.PageExit, PageStates.Empty
|
||||
End Select
|
||||
End Sub
|
||||
Public Event PageExit()
|
||||
''' <summary>
|
||||
''' 即将切换到其他页面,需要强制完成页面状态清理。
|
||||
''' 需要立即切换至 Empty。
|
||||
@@ -325,17 +339,13 @@
|
||||
|
||||
'逐个进入动画
|
||||
Public Sub TriggerEnterAnimation(ParamArray Elements As FrameworkElement())
|
||||
'去除 Nothing 项
|
||||
Dim RealElements As New List(Of FrameworkElement)
|
||||
For Each Element In Elements
|
||||
If Element IsNot Nothing Then RealElements.Add(Element)
|
||||
Next
|
||||
|
||||
Dim RealElements = Elements.Where(Function(e) e IsNot Nothing)
|
||||
For Each Element In RealElements
|
||||
Element.Visibility = Visibility.Visible '页面均处于默认的隐藏状态
|
||||
Next
|
||||
Dim AniList As New List(Of AniData)
|
||||
Dim Delay As Integer = 0
|
||||
'基础动画
|
||||
For Each Element In RealElements
|
||||
For Each Control As FrameworkElement In GetAllAnimControls(Element, True)
|
||||
'还原被隐藏的卡片的消失动画
|
||||
@@ -351,18 +361,20 @@
|
||||
Delay += 40
|
||||
Next
|
||||
Next
|
||||
'滚动条动画
|
||||
Dim Scroll As MyScrollBar = GetFirstScrollViewer(RealElements)
|
||||
If Scroll IsNot Nothing Then
|
||||
If TypeOf Scroll.RenderTransform IsNot TranslateTransform Then Scroll.RenderTransform = New TranslateTransform(10, 0)
|
||||
AniList.Add(AaTranslateX(Scroll, -CType(Scroll.RenderTransform, TranslateTransform).X, 350, 0, New AniEaseOutFluent))
|
||||
End If
|
||||
'结束
|
||||
AniList.Add(AaCode(Sub() PageOnEnterAnimationFinished(),, True))
|
||||
AniStart(AniList, "PageRight PageChange " & PageUuid)
|
||||
End Sub
|
||||
|
||||
'逐个退出动画
|
||||
Public Sub TriggerExitAnimation(ParamArray Elements As FrameworkElement())
|
||||
'去除 Nothing 项
|
||||
Dim RealElements As New List(Of FrameworkElement)
|
||||
For Each Element In Elements
|
||||
If Element IsNot Nothing Then RealElements.Add(Element)
|
||||
Next
|
||||
|
||||
Dim RealElements = Elements.Where(Function(e) e IsNot Nothing)
|
||||
Dim AniList As New List(Of AniData)
|
||||
Dim Delay As Integer = 0
|
||||
For Each Element In RealElements
|
||||
@@ -373,6 +385,13 @@
|
||||
Delay += 20
|
||||
Next
|
||||
Next
|
||||
'滚动条动画
|
||||
Dim Scroll As MyScrollBar = GetFirstScrollViewer(RealElements)
|
||||
If Scroll IsNot Nothing Then
|
||||
If TypeOf Scroll.RenderTransform IsNot TranslateTransform Then Scroll.RenderTransform = New TranslateTransform
|
||||
AniList.Add(AaTranslateX(Scroll, 10 - CType(Scroll.RenderTransform, TranslateTransform).X, 90, 0, New AniEaseInFluent))
|
||||
End If
|
||||
'结束
|
||||
AniList.Add(AaCode(Sub()
|
||||
For Each Element In RealElements
|
||||
Element.Visibility = Visibility.Collapsed
|
||||
@@ -402,6 +421,27 @@
|
||||
End If
|
||||
End Sub
|
||||
|
||||
'查找列表中的第一个滚动条
|
||||
Private Function GetFirstScrollViewer(Elements As IEnumerable(Of FrameworkElement)) As MyScrollBar
|
||||
Dim Viewer As MyScrollViewer = Nothing
|
||||
For Each Element In Elements
|
||||
If TypeOf Element Is MyScrollViewer Then
|
||||
Viewer = Element
|
||||
GoTo FindViewer
|
||||
End If
|
||||
For Each Control In LogicalTreeHelper.GetChildren(Element)
|
||||
If TypeOf Control Is MyScrollViewer Then
|
||||
Viewer = Control
|
||||
GoTo FindViewer
|
||||
End If
|
||||
Next
|
||||
Next
|
||||
Return Nothing
|
||||
FindViewer:
|
||||
If Viewer.ComputedVerticalScrollBarVisibility <> Visibility.Visible Then Return Nothing
|
||||
Return Viewer.ScrollBar
|
||||
End Function
|
||||
|
||||
#End Region
|
||||
|
||||
End Class
|
||||
@@ -4,6 +4,6 @@
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center" Background="{StaticResource ColorBrushSemiTransparent}" CornerRadius="13.5" MinHeight="27" MaxHeight="27">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path x:Name="ShapeLogo" HorizontalAlignment="Left" RenderTransformOrigin="0.5,0.5" Margin="12,0,0,0" Stretch="Uniform" Fill="White" MaxHeight="16" MaxWidth="16" VerticalAlignment="Center" />
|
||||
<TextBlock x:Name="LabText" VerticalAlignment="Center" Grid.Column="1" Foreground="White" Margin="8,0,12,0" Grid.ColumnSpan="2" SnapsToDevicePixels="False" UseLayoutRounding="False" />
|
||||
<TextBlock x:Name="LabText" VerticalAlignment="Center" Grid.Column="1" Foreground="White" Margin="8,0,12,1" Grid.ColumnSpan="2" SnapsToDevicePixels="False" UseLayoutRounding="False" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
@@ -252,7 +252,7 @@
|
||||
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "刷新按钮颜色出错")
|
||||
Log(ex, "刷新单选按钮颜色出错")
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ Public Class MyResizer
|
||||
|
||||
Private Declare Function GetMonitorInfo Lib "user32" (hMonitor As IntPtr, lpmi As MONITORINFO) As Boolean
|
||||
|
||||
Friend Declare Function MonitorFromWindow Lib "User32" (handle As IntPtr, flags As Integer) As IntPtr
|
||||
Private Declare Function MonitorFromWindow Lib "User32" (handle As IntPtr, flags As Integer) As IntPtr
|
||||
|
||||
Private Sub connectMouseHandlers(element As UIElement)
|
||||
AddHandler element.MouseLeftButtonDown, AddressOf Me.element_MouseLeftButtonDown
|
||||
|
||||
@@ -2,17 +2,31 @@
|
||||
Inherits ScrollViewer
|
||||
|
||||
Public Property DeltaMuity As Double = 1
|
||||
|
||||
|
||||
Private RealOffset As Double
|
||||
Private Sub MyScrollViewer_PreviewMouseWheel(sender As Object, e As MouseWheelEventArgs) Handles Me.PreviewMouseWheel
|
||||
If e.Delta = 0 OrElse ActualHeight = 0 OrElse ScrollableHeight = 0 Then Exit Sub
|
||||
Dim SourceTypeName As String = e.Source.GetType.Name
|
||||
If Content.TemplatedParent Is Nothing AndAlso (SourceTypeName.Contains("Combo") OrElse SourceTypeName = "MyTextBox") Then Exit Sub '确定可视树,防止滚动问题
|
||||
PerformVerticalOffsetDelta(-e.Delta)
|
||||
Dim SourceType = e.Source.GetType
|
||||
If Content.TemplatedParent Is Nothing AndAlso (
|
||||
(GetType(ComboBox).IsAssignableFrom(SourceType) AndAlso CType(e.Source, ComboBox).IsDropDownOpen) OrElse
|
||||
(GetType(TextBox).IsAssignableFrom(SourceType) AndAlso CType(e.Source, TextBox).AcceptsReturn) OrElse
|
||||
GetType(ComboBoxItem).IsAssignableFrom(SourceType) OrElse
|
||||
TypeOf e.Source Is CheckBox
|
||||
) Then
|
||||
'如果当前是在对有滚动条的下拉框或文本框执行,则不接管操作
|
||||
Exit Sub
|
||||
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(AaDouble(Sub(AnimDelta As Double)
|
||||
RealOffset = MathRange(RealOffset + AnimDelta, 0, ExtentHeight - ActualHeight)
|
||||
RealOffset = MathClamp(RealOffset + AnimDelta, 0, ExtentHeight - ActualHeight)
|
||||
ScrollToVerticalOffset(RealOffset)
|
||||
End Sub, Delta * DeltaMuity, 300,, New AniEaseOutFluent(6)))
|
||||
End Sub
|
||||
@@ -24,4 +38,9 @@
|
||||
FrmMain.BtnExtraBack.ShowRefresh()
|
||||
End Sub
|
||||
|
||||
Public ScrollBar As MyScrollBar
|
||||
Private Sub Load() Handles Me.Loaded
|
||||
ScrollBar = GetTemplateChild("PART_VerticalScrollBar")
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -1,7 +1,7 @@
|
||||
Class MySearchBox
|
||||
Inherits MyCard
|
||||
|
||||
Public Event TextChanged(ByVal sender As Object, ByVal e As EventArgs)
|
||||
Public Event TextChanged(sender As Object, e As EventArgs)
|
||||
Private Sub MySearchBox_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
|
||||
TextBox.Focus()
|
||||
End Sub
|
||||
@@ -11,7 +11,7 @@
|
||||
Get
|
||||
Return TextBox.HintText
|
||||
End Get
|
||||
Set(ByVal value As String)
|
||||
Set(value As String)
|
||||
TextBox.HintText = value
|
||||
End Set
|
||||
End Property
|
||||
@@ -19,7 +19,7 @@
|
||||
Get
|
||||
Return TextBox.Text
|
||||
End Get
|
||||
Set(ByVal value As String)
|
||||
Set(value As String)
|
||||
TextBox.Text = value
|
||||
End Set
|
||||
End Property
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
MinHeight="16" UseLayoutRounding="False" SnapsToDevicePixels="False" BorderBrush="{DynamicResource ColorBrushBg0}" Name="PanBack" Background="{StaticResource ColorBrushSemiTransparent}" Focusable="True">
|
||||
<Grid x:Name="PanMain" MinHeight="16" MaxHeight="16" IsHitTestVisible="False">
|
||||
<Line Height="4" Name="LineBack" Stroke="{DynamicResource ColorBrushBg0}" Opacity="0.3" StrokeThickness="1" X2="10000" Y1="2" Y2="2" HorizontalAlignment="Right" Width="0" SnapsToDevicePixels="True" Margin="0,0,1,0" />
|
||||
<Line Height="4" Name="LineFore" Stroke="{Binding ElementName=PanBack, Path=BorderBrush}" StrokeThickness="1" X2="10000" Y1="2" Y2="2" Width="0" HorizontalAlignment="Left" SnapsToDevicePixels="True" Margin="1,0,0,0" />
|
||||
<Line Height="4" Name="LineFore" Stroke="{Binding ElementName=PanBack, Path=BorderBrush}" StrokeThickness="2" X2="10000" Y1="2" Y2="2" Width="0" HorizontalAlignment="Left" SnapsToDevicePixels="True" Margin="1,0,0,0" />
|
||||
<Ellipse HorizontalAlignment="Left" Width="10" Height="10"
|
||||
Stroke="{Binding BorderBrush, ElementName=PanBack}" StrokeThickness="1.2"
|
||||
Fill="{DynamicResource ColorBrushBg0}" Name="ShapeDot" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5">
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
Set(newValue As Integer)
|
||||
Try
|
||||
|
||||
newValue = MathRange(newValue, 0, MaxValue)
|
||||
newValue = MathClamp(newValue, 0, MaxValue)
|
||||
If _Value = newValue Then Exit Property
|
||||
|
||||
'触发 Preview 事件,修改新值
|
||||
@@ -88,7 +88,7 @@
|
||||
AniStop("MySlider KeyPopup " & Uuid)
|
||||
End Sub
|
||||
Public Sub DragDoing()
|
||||
Dim Percent As Double = MathRange((Mouse.GetPosition(PanMain).X - ShapeDot.Width / 2) / (ActualWidth - ShapeDot.Width), 0, 1)
|
||||
Dim Percent As Double = MathClamp((Mouse.GetPosition(PanMain).X - ShapeDot.Width / 2) / (ActualWidth - ShapeDot.Width), 0, 1)
|
||||
Dim NewValue As Integer = Percent * MaxValue
|
||||
If Not NewValue = Value Then
|
||||
Value = NewValue
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
Return GetValue(EventTypeProperty)
|
||||
End Get
|
||||
|
||||
Set(ByVal value As String)
|
||||
Set(value As String)
|
||||
SetValue(EventTypeProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
@@ -101,7 +101,7 @@
|
||||
Return GetValue(EventDataProperty)
|
||||
End Get
|
||||
|
||||
Set(ByVal value As String)
|
||||
Set(value As String)
|
||||
SetValue(EventDataProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
<Window
|
||||
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" mc:Ignorable="d"
|
||||
x:Name="WindMain" x:Class="FormMain" Style="{StaticResource MyWindow}"
|
||||
AllowDrop="True" Topmost="True" Title="Plain Craft Launcher 2 " AllowsTransparency="True" MinHeight="470" MinWidth="810" ScrollViewer.VerticalScrollBarVisibility="Disabled" Icon="/Plain Craft Launcher 2;component/Images/icon.ico" WindowStyle="None" ResizeMode="CanMinimize" Background="{x:Null}" d:DesignWidth="870" d:DesignHeight="520" Width="850" Height="500" RenderTransformOrigin="0.5,0.5">
|
||||
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" mc:Ignorable="d"
|
||||
x:Name="WindMain" x:Class="FormMain" Style="{StaticResource MyWindow}"
|
||||
AllowDrop="True" Topmost="True" Title="Plain Craft Launcher " AllowsTransparency="True" MinHeight="470" MinWidth="810"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Disabled" Icon="/Plain Craft Launcher 2;component/Images/icon.ico" WindowStyle="None"
|
||||
ResizeMode="CanMinimize" Background="{x:Null}" d:DesignWidth="870" d:DesignHeight="520" Width="850" Height="500"
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<Window.TaskbarItemInfo>
|
||||
<TaskbarItemInfo ThumbnailClipMargin="{Binding Margin, ElementName=PanForm}" />
|
||||
</Window.TaskbarItemInfo>
|
||||
@@ -131,7 +134,7 @@
|
||||
<Grid x:Name="PanTitleInner" Visibility="Collapsed" Margin="-16,0,0,0" Opacity="0" HorizontalAlignment="Left" Width="{Binding ActualWidth, ElementName=PanTitle, Mode=OneWay}">
|
||||
<local:MyIconButton HorizontalAlignment="Left" Theme="White" x:Name="BtnTitleInner" Margin="12,0,0,0" Height="28" Width="28"
|
||||
LogoScale="0.87" Logo="M1097.162313 584.707482 250.272424 584.707482 562.833701 896.95123C591.749274 925.859979 591.749274 972.659975 562.833701 1001.568724 533.91812 1030.477473 487.058315 1030.477473 458.142742 1001.568724L21.580946 565.451235C6.757449 550.631234-0.361728 531.131238 0.028357 511.679985L0.028357 511.631236 0.028357 511.582486C-0.361728 492.13124 6.757449 472.582488 21.580946 457.762487L458.142742 21.644997C487.058315-7.215002 533.91812-7.215002 562.833701 21.644997 591.749274 50.553746 591.749274 97.4025 562.833701 126.262499L250.272424 438.55499 1097.162313 438.55499C1137.585605 438.55499 1170.304578 471.266238 1170.304578 511.631236 1170.304578 551.947483 1137.585605 584.707482 1097.162313 584.707482L1097.162313 584.707482Z" />
|
||||
<TextBlock SnapsToDevicePixels="False" IsHitTestVisible="False" Name="LabTitleInner" VerticalAlignment="Center" Margin="47,1,0,0" HorizontalAlignment="Left" Foreground="{DynamicResource ColorBrush8}" FontSize="15" />
|
||||
<TextBlock SnapsToDevicePixels="False" IsHitTestVisible="False" Name="LabTitleInner" VerticalAlignment="Center" Margin="47,1,60,0" HorizontalAlignment="Left" Foreground="{DynamicResource ColorBrush8}" FontSize="15" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid x:Name="PanLeft" Grid.Row="1" HorizontalAlignment="Left" SnapsToDevicePixels="True">
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
@@ -577,10 +577,10 @@ Public Module ModAnimation
|
||||
Public Class AniEaseLinear
|
||||
Inherits AniEase
|
||||
Public Overrides Function GetValue(t As Double) As Double
|
||||
Return MathRange(t, 0, 1)
|
||||
Return MathClamp(t, 0, 1)
|
||||
End Function
|
||||
Public Overrides Function GetDelta(t1 As Double, t0 As Double) As Double
|
||||
Return MathRange(t1, 0, 1) - MathRange(t0, 0, 1)
|
||||
Return MathClamp(t1, 0, 1) - MathClamp(t0, 0, 1)
|
||||
End Function
|
||||
End Class
|
||||
|
||||
@@ -595,7 +595,7 @@ Public Module ModAnimation
|
||||
p = Power
|
||||
End Sub
|
||||
Public Overrides Function GetValue(t As Double) As Double
|
||||
Return MathRange(t, 0, 1) ^ p
|
||||
Return MathClamp(t, 0, 1) ^ p
|
||||
End Function
|
||||
End Class
|
||||
''' <summary>
|
||||
@@ -608,7 +608,7 @@ Public Module ModAnimation
|
||||
p = Power
|
||||
End Sub
|
||||
Public Overrides Function GetValue(t As Double) As Double
|
||||
Return 1 - MathRange(1 - t, 0, 1) ^ p
|
||||
Return 1 - MathClamp(1 - t, 0, 1) ^ p
|
||||
End Function
|
||||
End Class
|
||||
''' <summary>
|
||||
@@ -636,7 +636,7 @@ Public Module ModAnimation
|
||||
p = 3 - Power * 0.5
|
||||
End Sub
|
||||
Public Overrides Function GetValue(t As Double) As Double
|
||||
t = MathRange(t, 0, 1)
|
||||
t = MathClamp(t, 0, 1)
|
||||
Return t ^ p * Math.Cos(1.5 * Math.PI * (1 - t))
|
||||
End Function
|
||||
End Class
|
||||
@@ -650,7 +650,7 @@ Public Module ModAnimation
|
||||
p = 3 - Power * 0.5
|
||||
End Sub
|
||||
Public Overrides Function GetValue(t As Double) As Double
|
||||
t = MathRange(t, 0, 1)
|
||||
t = MathClamp(t, 0, 1)
|
||||
Return 1 - (1 - t) ^ p * Math.Cos(1.5 * Math.PI * t)
|
||||
End Function
|
||||
End Class
|
||||
@@ -694,7 +694,7 @@ Public Module ModAnimation
|
||||
p = Power + 4
|
||||
End Sub
|
||||
Public Overrides Function GetValue(t As Double) As Double
|
||||
t = MathRange(t, 0, 1)
|
||||
t = MathClamp(t, 0, 1)
|
||||
Return t ^ ((p - 1) * 0.25) * Math.Cos((p - 3.5) * Math.PI * (1 - t) ^ 1.5)
|
||||
End Function
|
||||
End Class
|
||||
@@ -708,7 +708,7 @@ Public Module ModAnimation
|
||||
p = Power + 4
|
||||
End Sub
|
||||
Public Overrides Function GetValue(t As Double) As Double
|
||||
t = 1 - MathRange(t, 0, 1)
|
||||
t = 1 - MathClamp(t, 0, 1)
|
||||
Return 1 - t ^ ((p - 1) * 0.25) * Math.Cos((p - 3.5) * Math.PI * (1 - t) ^ 1.5)
|
||||
End Function
|
||||
End Class
|
||||
@@ -766,7 +766,7 @@ Public Module ModAnimation
|
||||
''' <summary>
|
||||
''' 开始动画执行。
|
||||
''' </summary>
|
||||
Public Sub AniStartRun()
|
||||
Public Sub AniStart()
|
||||
'初始化计时器
|
||||
AniLastTick = GetTimeTick()
|
||||
AniFPSTimer = AniLastTick
|
||||
@@ -777,12 +777,12 @@ Public Module ModAnimation
|
||||
Log("[Animation] 动画线程开始")
|
||||
Do While True
|
||||
'两帧之间的间隔时间
|
||||
Dim DeltaTime As Long = MathRange(GetTimeTick() - AniLastTick, 0, 100000)
|
||||
Dim DeltaTime As Long = MathClamp(GetTimeTick() - AniLastTick, 0, 100000)
|
||||
If DeltaTime < 3 Then GoTo Sleeper
|
||||
AniLastTick = GetTimeTick()
|
||||
'记录 FPS
|
||||
If ModeDebug Then
|
||||
If MathRange(AniLastTick - AniFPSTimer, 0, 100000) >= 500 Then
|
||||
If MathClamp(AniLastTick - AniFPSTimer, 0, 100000) >= 500 Then
|
||||
AniFPS = AniFPSCounter
|
||||
AniFPSCounter = 0
|
||||
AniFPSTimer = AniLastTick
|
||||
@@ -869,7 +869,7 @@ NextAni:
|
||||
Loop
|
||||
|
||||
'如果当前动画组都执行完毕则删除
|
||||
If Entry.Data.Count = 0 Then
|
||||
If Not Entry.Data.Any() Then
|
||||
'为了避免新添加的动画影响顺序,不能 RemoveAt(i)
|
||||
'为了允许动画在执行中添加同名动画组,不能按名字移除
|
||||
For Current = 0 To AniGroups.Count - 1
|
||||
@@ -904,7 +904,7 @@ NextAni:
|
||||
Case AniTypeSub.Y
|
||||
DeltaTop(Ani.Obj, Delta)
|
||||
Case AniTypeSub.Opacity
|
||||
Ani.Obj.Opacity = MathRange(Ani.Obj.Opacity + Delta, 0, 1)
|
||||
Ani.Obj.Opacity = MathClamp(Ani.Obj.Opacity + Delta, 0, 1)
|
||||
Case AniTypeSub.Width
|
||||
Dim Obj As FrameworkElement = Ani.Obj
|
||||
Obj.Width = Math.Max(If(Double.IsNaN(Obj.Width), Obj.ActualWidth, Obj.Width) + Delta, 0)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
Imports System.IO.Compression
|
||||
Imports System.Management
|
||||
Imports System.Runtime.CompilerServices
|
||||
Imports System.Security.Cryptography
|
||||
Imports System.Security.Principal
|
||||
Imports System.Text.RegularExpressions
|
||||
Imports System.Windows.Markup
|
||||
Imports Newtonsoft.Json
|
||||
|
||||
@@ -11,12 +11,12 @@ Public Module ModBase
|
||||
#Region "声明"
|
||||
|
||||
'下列版本信息由更新器自动修改
|
||||
Public Const VersionBaseName As String = "2.5.2" '不含分支前缀的显示用版本名
|
||||
Public Const VersionStandardCode As String = "2.5.2." & VersionBranchCode '标准格式的四段式版本号
|
||||
Public Const VersionBaseName As String = "2.7.3" '不含分支前缀的显示用版本名
|
||||
Public Const VersionStandardCode As String = "2.7.3." & VersionBranchCode '标准格式的四段式版本号
|
||||
#If BETA Then
|
||||
Public Const VersionCode As Integer = 286 'Release
|
||||
Public Const VersionCode As Integer = 323 'Release
|
||||
#Else
|
||||
Public Const VersionCode As Integer = 285 'Snapshot
|
||||
Public Const VersionCode As Integer = 324 'Snapshot
|
||||
#End If
|
||||
'自动生成的版本信息
|
||||
Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName
|
||||
@@ -46,7 +46,7 @@ Public Module ModBase
|
||||
''' <summary>
|
||||
''' 程序内嵌图片文件夹路径,以“/”结尾。
|
||||
''' </summary>
|
||||
Public PathImage As String = "pack://application:,,,/images/"
|
||||
Public PathImage As String = "pack://application:,,,/Plain Craft Launcher 2;component/Images/"
|
||||
''' <summary>
|
||||
''' 当前程序的语言。
|
||||
''' </summary>
|
||||
@@ -58,7 +58,7 @@ Public Module ModBase
|
||||
''' <summary>
|
||||
''' 程序的打开计时。
|
||||
''' </summary>
|
||||
Public ApplicationStartTick As Long
|
||||
Public ApplicationStartTick As Long = GetTimeTick()
|
||||
''' <summary>
|
||||
''' 程序打开时的时间。
|
||||
''' </summary>
|
||||
@@ -76,6 +76,10 @@ Public Module ModBase
|
||||
''' </summary>
|
||||
Public Is32BitSystem As Boolean = Not Environment.Is64BitOperatingSystem
|
||||
''' <summary>
|
||||
''' 是否使用 GBK 编码。
|
||||
''' </summary>
|
||||
Public IsGBKEncoding As Boolean = Encoding.Default.CodePage = 936
|
||||
''' <summary>
|
||||
''' 操作系统版本。Win10 为 10.0。
|
||||
''' </summary>
|
||||
Public OsVersion As Version = Environment.OSVersion.Version
|
||||
@@ -110,6 +114,14 @@ Public Module ModBase
|
||||
''' </summary>
|
||||
Public Const IconButtonDelete As String = "M520.192 0C408.43 0 317.44 82.87 313.563 186.734H52.736c-29.038 0-52.663 21.943-52.663 49.079s23.625 49.152 52.663 49.152h58.075v550.473c0 103.35 75.118 187.757 167.717 187.757h472.43c92.599 0 167.716-83.894 167.716-187.757V285.477h52.59c29.038 0 52.59-21.943 52.663-49.08-0.073-27.135-23.625-49.151-52.663-49.151H726.235C723.237 83.017 631.955 0 520.192 0zM404.846 177.957c3.803-50.03 50.176-89.015 107.447-89.015 57.197 0 103.57 38.985 106.788 89.015H404.92zM284.379 933.669c-33.353 0-69.997-39.351-69.997-95.525v-549.01H833.39v549.522c0 56.247-36.645 95.525-69.998 95.525H284.379v-0.512z M357.23 800.695a48.274 48.274 0 0 0 47.616-49.006V471.7a48.274 48.274 0 0 0-47.543-49.08 48.274 48.274 0 0 0-47.69 49.006V751.69c0 27.282 20.846 49.006 47.617 49.006z m166.62 0a48.274 48.274 0 0 0 47.688-49.006V471.7a48.274 48.274 0 0 0-47.689-49.08 48.274 48.274 0 0 0-47.543 49.006V751.69c0 27.282 21.431 49.006 47.543 49.006z m142.92 0a48.274 48.274 0 0 0 47.543-49.006V471.7a48.274 48.274 0 0 0-47.543-49.08 48.274 48.274 0 0 0-47.616 49.006V751.69c0 27.282 20.773 49.006 47.543 49.006z"
|
||||
''' <summary>
|
||||
''' 图标按钮,禁止,1x
|
||||
''' </summary>
|
||||
Public Const IconButtonStop As String = "M508 990.4c-261.6 0-474.4-212-474.4-474.4S246.4 41.6 508 41.6s474.4 212 474.4 474.4S769.6 990.4 508 990.4zM508 136.8c-209.6 0-379.2 169.6-379.2 379.2 0 209.6 169.6 379.2 379.2 379.2s379.2-169.6 379.2-379.2C887.2 306.4 717.6 136.8 508 136.8zM697.6 563.2 318.4 563.2c-26.4 0-47.2-21.6-47.2-47.2 0-26.4 21.6-47.2 47.2-47.2l379.2 0c26.4 0 47.2 21.6 47.2 47.2C744.8 542.4 724 563.2 697.6 563.2z"
|
||||
''' <summary>
|
||||
''' 图标按钮,勾选,1x
|
||||
''' </summary>
|
||||
Public Const IconButtonCheck As String = "M512 0a512 512 0 1 0 512 512A512 512 0 0 0 512 0z m0 921.6a409.6 409.6 0 1 1 409.6-409.6 409.6 409.6 0 0 1-409.6 409.6z M716.8 339.968l-256 253.44L328.192 460.8A51.2 51.2 0 0 0 256 532.992l168.448 168.96a51.2 51.2 0 0 0 72.704 0l289.28-289.792A51.2 51.2 0 0 0 716.8 339.968z"
|
||||
''' <summary>
|
||||
''' 图标按钮,笔,1x
|
||||
''' </summary>
|
||||
Public Const IconButtonEdit As String = "M732.64 64.32C688.576 21.216 613.696 21.216 569.6 64.32L120.128 499.52c-17.6 12.896-26.432 30.144-30.848 51.68L32 870.048c0 25.856 8.8 56 26.432 73.248 17.632 17.216 17.632 48.704 88.64 48.704h13.248l326.08-56c22.016-4.32 39.68-12.928 52.864-30.176l449.472-435.2c22.048-21.536 35.264-47.36 35.264-77.536 0-30.176-13.216-56-35.264-77.568l-256.096-251.2zM139.712 903.776l56-326.912 311.04-295.136 267.104 269.44-310.976 295.168-323.168 57.44zM844.576 467.84l-273.984-260.672 61.856-59.84c8.832-8.512 26.528-8.512 39.776 0l234.24 226.496c4.384 4.288 8.832 12.8 8.832 17.088s-4.416 8.544-8.864 12.8l-61.856 64.128z"
|
||||
@@ -158,6 +170,14 @@ Public Module ModBase
|
||||
''' </summary>
|
||||
Public Const IconButtonUser As String = "M660.338 528.065c63.61-46.825 105.131-121.964 105.131-206.83 0-141.7-115.29-256.987-256.997-256.987-141.706 0-256.998 115.288-256.998 256.987 0 85.901 42.52 161.887 107.456 208.562-152.1 59.92-260.185 207.961-260.185 381.077 0 21.276 17.253 38.53 38.53 38.53 21.278 0 38.53-17.254 38.53-38.53 0-183.426 149.232-332.671 332.667-332.671 1.589 0 3.113-0.207 4.694-0.244 0.8 0.056 1.553 0.244 2.362 0.244 183.434 0 332.664 149.245 332.664 332.671 0 21.276 17.255 38.53 38.533 38.53 21.277 0 38.53-17.254 38.53-38.53 0-174.885-110.354-324.13-264.917-382.809z m-331.803-206.83c0-99.22 80.72-179.927 179.935-179.927s179.937 80.708 179.937 179.927c0 99.203-80.721 179.91-179.937 179.91s-179.935-80.708-179.935-179.91z"
|
||||
''' <summary>
|
||||
''' 图标按钮,盾牌,1x
|
||||
''' </summary>
|
||||
Public Const IconButtonShield As String = "M511.488256 95.184408c35.310345 22.516742 95.184408 55.78011 167.34033 84.437781 75.738131 29.681159 148.405797 40.93953 191.392304 45.033483v353.615193c0 73.691154-50.662669 164.781609-136.123938 244.101949C649.65917 901.181409 558.568716 942.12094 512 942.12094c-46.568716 0-137.65917-40.93953-222.096952-119.748126C204.441779 742.54073 153.77911 651.450275 153.77911 577.247376v-353.103448c42.474763-4.093953 116.165917-15.352324 191.904048-45.545227 75.226387-30.192904 133.565217-63.456272 165.805098-83.414293M512 0c-4.093953 0-8.187906 1.535232-11.258371 3.582209l-14.84058 10.234882c-1.023488 0.511744-67.550225 47.592204-170.410794 88.531735-100.813593 39.916042-198.556722 41.963018-199.58021 41.963018l-25.075462 0.511744c-10.746627 0.511744-18.934533 8.187906-18.934533 18.422789v414.000999c0 216.97951 286.064968 446.24088 440.09995 446.24088s440.09995-229.261369 440.09995-445.729136V163.758121c0-10.234883-8.69965-18.422789-18.934533-18.422789l-24.563718-0.511744c-1.023488 0-98.766617-2.046977-199.58021-41.963018-103.372314-40.93953-170.410795-88.01999-170.922538-88.531734L523.258371 3.582209c-3.070465-2.558721-7.164418-3.582209-11.258371-3.582209z M743.308346 410.930535l-260.477761 260.477761c-15.864068 15.864068-41.963018 15.864068-57.827087 0l-144.823588-144.823588c-15.864068-15.864068-15.864068-41.963018 0-57.827087 8.187906-8.187906 18.422789-11.770115 29.169415-11.770115 10.234883 0 20.981509 4.093953 29.169416 11.770115l115.654173 115.654173L685.993003 352.591704c15.864068-15.864068 41.963018-15.864068 57.827087 0 15.352324 16.375812 15.352324 42.474763-0.511744 58.338831z"
|
||||
''' <summary>
|
||||
''' 图标按钮,离线,0.85x
|
||||
''' </summary>
|
||||
Public Const IconButtonOffline As String = "M533.293176 788.841412a60.235294 60.235294 0 1 1 85.202824 85.202823l-42.616471 42.586353c-129.355294 129.385412-339.124706 129.385412-468.510117 0-129.385412-129.385412-129.385412-339.124706 0-468.510117l42.586353-42.616471a60.235294 60.235294 0 1 1 85.202823 85.202824l-42.61647 42.586352a210.823529 210.823529 0 1 0 298.164706 298.164706l42.586352-42.61647z m255.548236-255.548236l42.61647-42.586352a210.823529 210.823529 0 1 0-298.164706-298.164706l-42.586352 42.61647a60.235294 60.235294 0 1 1-85.202824-85.202823l42.616471-42.586353c129.355294-129.385412 339.124706-129.385412 468.510117 0 129.385412 129.385412 129.385412 339.124706 0 468.510117l-42.586353 42.616471a60.235294 60.235294 0 1 1-85.202823-85.202824zM192.542118 192.542118a60.235294 60.235294 0 0 1 85.202823 0l553.712941 553.712941a60.235294 60.235294 0 0 1-85.202823 85.202823L192.542118 277.744941a60.235294 60.235294 0 0 1 0-85.202823z"
|
||||
''' <summary>
|
||||
''' 图标,音符,1x
|
||||
''' </summary>
|
||||
Public Const IconMusic As String = "M348.293565 716.53287V254.797913c0-41.672348 28.004174-78.358261 68.919652-90.37913L815.994435 40.826435c62.775652-18.610087 125.907478 26.579478 125.907478 89.933913v539.158261c8.013913 42.25113-8.94887 89.177043-47.014956 127.109565a232.848696 232.848696 0 0 1-170.785392 65.758609c-61.885217-2.938435-111.081739-33.435826-129.113043-80.050087-18.031304-46.614261-2.137043-102.177391 41.672348-145.853218a232.848696 232.848696 0 0 1 170.785391-65.80313c21.014261 1.024 40.514783 5.164522 57.878261 12.065391V233.338435c0-12.109913-10.551652-20.034783-20.569044-20.034783a24.620522 24.620522 0 0 0-5.787826 0.934957L439.785739 338.18713a19.545043 19.545043 0 0 0-14.825739 19.144348v438.984348H423.846957c11.53113 43.987478-5.164522 94.208-45.412174 134.322087a232.848696 232.848696 0 0 1-170.785392 65.758609c-61.885217-2.938435-111.081739-33.435826-129.113043-80.050087-18.031304-46.614261-2.137043-102.177391 41.672348-145.853218a232.848696 232.848696 0 0 1 170.785391-65.80313c20.791652 1.024 40.069565 5.075478 57.299478 11.842783z"
|
||||
@@ -463,11 +483,11 @@ Public Module ModBase
|
||||
Const Digits As String = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/+="
|
||||
'零与负数的处理
|
||||
If String.IsNullOrEmpty(Input) Then Return "0"
|
||||
Dim IsNegative As Boolean = Input.StartsWith("-")
|
||||
Dim IsNegative As Boolean = Input.StartsWithF("-")
|
||||
If IsNegative Then Input = Input.TrimStart("-")
|
||||
'转换为十进制
|
||||
Dim RealNum As Long = 0, Scale As Long = 1
|
||||
For Each Digit In Input.Reverse.Select(Function(l) Digits.IndexOf(l))
|
||||
For Each Digit In Input.Reverse.Select(Function(l) Digits.IndexOfF(l))
|
||||
RealNum += Digit * Scale
|
||||
Scale *= FromRadix
|
||||
Next
|
||||
@@ -517,7 +537,6 @@ Public Module ModBase
|
||||
''' 获取两数间的百分比。小数点精确到 6 位。
|
||||
''' </summary>
|
||||
''' <returns></returns>
|
||||
''' <remarks></remarks>
|
||||
Public Function MathPercent(ValueA As Double, ValueB As Double, Percent As Double) As Double
|
||||
Return Math.Round(ValueA * (1 - Percent) + ValueB * Percent, 6) '解决 Double 计算错误
|
||||
End Function
|
||||
@@ -531,7 +550,7 @@ Public Module ModBase
|
||||
''' <summary>
|
||||
''' 将数值限定在某个范围内。
|
||||
''' </summary>
|
||||
Public Function MathRange(value As Double, min As Double, max As Double) As Double
|
||||
Public Function MathClamp(value As Double, min As Double, max As Double) As Double
|
||||
Return Math.Max(min, Math.Min(max, value))
|
||||
End Function
|
||||
|
||||
@@ -556,7 +575,7 @@ Public Module ModBase
|
||||
''' <summary>
|
||||
''' 重命名一个注册表子键。不可用于包含子键的子键。
|
||||
''' </summary>
|
||||
Public Sub RegRename(parentKey As Microsoft.Win32.RegistryKey, subKeyName As String, newSubKeyName As String)
|
||||
Public Sub RenameReg(parentKey As Microsoft.Win32.RegistryKey, subKeyName As String, newSubKeyName As String)
|
||||
If parentKey.GetSubKeyNames().Contains(newSubKeyName) Then parentKey.DeleteSubKeyTree(newSubKeyName, False)
|
||||
Dim SourceKey As Microsoft.Win32.RegistryKey = parentKey.OpenSubKey(subKeyName)
|
||||
If IsNothing(SourceKey) Then Exit Sub '没有目标项
|
||||
@@ -607,82 +626,53 @@ Public Module ModBase
|
||||
End Sub
|
||||
|
||||
'Ini 文件
|
||||
Private Class IniCache
|
||||
Public FileContent As String = ""
|
||||
Public KVCache As New Dictionary(Of String, String)
|
||||
End Class
|
||||
Private ReadOnly IniCacheList As New Dictionary(Of String, IniCache)
|
||||
Private ReadOnly IniCache As New Dictionary(Of String, Dictionary(Of String, String))
|
||||
''' <summary>
|
||||
''' 清除某 Ini 文件的运行时缓存。
|
||||
''' 清除某 ini 文件的运行时缓存。
|
||||
''' </summary>
|
||||
''' <param name="FileName">文件完整路径或简写文件名。简写将会使用“ApplicationName\文件名.ini”作为路径。</param>
|
||||
Public Sub IniClearCache(FileName As String)
|
||||
'还原文件路径
|
||||
If Not FileName.Contains(":\") Then FileName = Path & "PCL\" & FileName & ".ini"
|
||||
'检索缓存
|
||||
If IniCacheList.ContainsKey(FileName) Then IniCacheList.Remove(FileName)
|
||||
If Not FileName.Contains(":\") Then FileName = $"{Path}PCL\{FileName}.ini"
|
||||
If IniCache.ContainsKey(FileName) Then IniCache.Remove(FileName)
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 获取 Ini 文件内容,这可能会使用到缓存。
|
||||
''' 获取 ini 文件缓存。如果没有,则新读取 ini 文件内容。
|
||||
''' 在文件不存在或读取失败时返回 Nothing。
|
||||
''' </summary>
|
||||
''' <param name="FileName">文件完整路径或简写文件名。简写将会使用“ApplicationName\文件名.ini”作为路径。</param>
|
||||
Private Function IniGetContent(FileName As String) As IniCache
|
||||
Private Function IniGetContent(FileName As String) As Dictionary(Of String, String)
|
||||
Try
|
||||
'还原文件路径
|
||||
If Not FileName.Contains(":\") Then FileName = Path & "PCL\" & FileName & ".ini"
|
||||
If Not FileName.Contains(":\") Then FileName = $"{Path}PCL\{FileName}.ini"
|
||||
'检索缓存
|
||||
If IniCacheList.ContainsKey(FileName) Then
|
||||
'返回缓存中的信息
|
||||
Dim Cache As New IniCache
|
||||
IniCacheList.TryGetValue(FileName, Cache)
|
||||
Return Cache '防止ByRef导致缓存变更
|
||||
Else
|
||||
Dim Ini As IniCache
|
||||
If File.Exists(FileName) Then
|
||||
'返回文件信息并且记入缓存
|
||||
Dim Cache As String = (vbCrLf & ReadFile(FileName) & vbCrLf).Replace(vbCrLf, vbCr).Replace(vbLf, vbCr).Replace(vbCr, vbCrLf).Replace(vbCrLf & vbCrLf, vbCrLf).Replace(vbNullChar, "")
|
||||
Ini = New IniCache With {.FileContent = Cache}
|
||||
Else
|
||||
'返回空文件信息
|
||||
Ini = New IniCache With {.FileContent = ""}
|
||||
End If
|
||||
If Not IniCacheList.ContainsKey(FileName) Then IniCacheList.Add(FileName, Ini) '避免多线程异步执行此方法时造成多次添加项
|
||||
Return Ini
|
||||
End If
|
||||
If IniCache.ContainsKey(FileName) Then Return IniCache(FileName)
|
||||
'读取文件
|
||||
If Not File.Exists(FileName) Then Return Nothing
|
||||
Dim Ini As New Dictionary(Of String, String)
|
||||
For Each Line In ReadFile(FileName).Split(vbCrLf.ToArray(), StringSplitOptions.RemoveEmptyEntries)
|
||||
Dim Index As Integer = Line.IndexOfF(":")
|
||||
If Index > 0 Then Ini(Line.Substring(0, Index)) = Line.Substring(Index + 1) '可能会有重复键,见 #3616
|
||||
Next
|
||||
IniCache(FileName) = Ini
|
||||
Return Ini
|
||||
Catch ex As Exception
|
||||
Log(ex, "读取文件失败:" & FileName, LogLevel.Hint)
|
||||
Return New IniCache With {.FileContent = ""}
|
||||
Log(ex, $"生成 ini 文件缓存失败({FileName})", LogLevel.Hint)
|
||||
Return Nothing
|
||||
End Try
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 读取Ini文件,这可能会使用到缓存。
|
||||
''' 读取 ini 文件。这可能会使用到缓存。
|
||||
''' </summary>
|
||||
''' <param name="FileName">文件完整路径或简写文件名。简写将会使用“ApplicationName\文件名.ini”作为路径。</param>
|
||||
''' <param name="Key">键。</param>
|
||||
''' <param name="DefaultValue">没有找到键时返回的默认值。</param>
|
||||
Public Function ReadIni(FileName As String, Key As String, Optional DefaultValue As String = "") As String
|
||||
Try
|
||||
'获取目前文件
|
||||
Dim NowIni As IniCache = IniGetContent(FileName)
|
||||
If IsNothing(NowIni) Then Return DefaultValue
|
||||
'使用缓存
|
||||
If NowIni.KVCache.ContainsKey(Key) Then Return If(NowIni.KVCache(Key), DefaultValue)
|
||||
'没有缓存,读取文件
|
||||
If NowIni.FileContent.Contains(vbCrLf & Key & ":") Then
|
||||
Dim Ret As String = Mid(NowIni.FileContent, NowIni.FileContent.IndexOf(vbCrLf & Key & ":") + 3)
|
||||
Ret = If(Ret.Contains(vbCrLf), Mid(Ret, 1, Ret.IndexOf(vbCrLf)), Ret).Replace(Key & ":", "")
|
||||
NowIni.KVCache.Add(Key, If(Ret = vbLf, "", Ret))
|
||||
Return If(Ret = vbLf, "", Ret)
|
||||
Else
|
||||
Return DefaultValue
|
||||
End If
|
||||
Catch ex As Exception
|
||||
'读取失败
|
||||
Return DefaultValue
|
||||
End Try
|
||||
Dim Content = IniGetContent(FileName)
|
||||
If Content Is Nothing OrElse Not Content.ContainsKey(Key) Then Return DefaultValue
|
||||
Return Content(Key)
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 写入ini文件,这会更新缓存。
|
||||
''' 写入 ini 文件,这会更新缓存。
|
||||
''' </summary>
|
||||
''' <param name="FileName">文件完整路径或简写文件名。简写将会使用“ApplicationName\文件名.ini”作为路径。</param>
|
||||
''' <param name="Key">键。</param>
|
||||
@@ -690,47 +680,41 @@ Public Module ModBase
|
||||
''' <remarks></remarks>
|
||||
Public Sub WriteIni(FileName As String, Key As String, Value As String)
|
||||
Try
|
||||
|
||||
'还原文件路径
|
||||
If Not FileName.Contains(":\") Then FileName = Path & "PCL\" & FileName & ".ini"
|
||||
If IsNothing(Value) Then Value = ""
|
||||
Value = Value.Replace(vbCrLf, "")
|
||||
'创建文件夹
|
||||
If Not Directory.Exists(GetPathFromFullPath(FileName)) Then Directory.CreateDirectory(GetPathFromFullPath(FileName))
|
||||
'预处理
|
||||
Key = Key.Replace(vbCr, "").Replace(vbLf, "").Replace(":", "")
|
||||
Value = Value.Replace(vbCr, "").Replace(vbLf, "") '允许 Value 中包含冒号
|
||||
'获取目前文件
|
||||
Dim NowFile As String = IniGetContent(FileName).FileContent
|
||||
If Not NowFile.EndsWith(vbCrLf) Then NowFile += vbCrLf
|
||||
'如果值一样就不处理
|
||||
If NowFile.Contains(vbCrLf & Key & ":" & Value & vbCrLf) Then Exit Sub
|
||||
'处理文件
|
||||
Dim FindResult As String = ReadIni(FileName, Key)
|
||||
If FindResult = "" AndAlso Not NowFile.Contains(vbCrLf & Key & ":") Then
|
||||
'不存在这个键
|
||||
NowFile = NowFile & vbCrLf & Key & ":" & Value & vbCrLf
|
||||
Else
|
||||
'存在这个键
|
||||
NowFile = NowFile.Replace(vbCrLf & Key & ":" & FindResult & vbCrLf, vbCrLf & Key & ":" & Value & vbCrLf)
|
||||
End If
|
||||
WriteFile(FileName, NowFile)
|
||||
'刷新目前缓存
|
||||
IniCacheList(FileName).FileContent = NowFile
|
||||
IniCacheList(FileName).KVCache.Remove(Key)
|
||||
IniCacheList(FileName).KVCache.Add(Key, Value)
|
||||
|
||||
Dim Content As Dictionary(Of String, String) = IniGetContent(FileName)
|
||||
If Content Is Nothing Then Content = New Dictionary(Of String, String)
|
||||
If Content.ContainsKey(Key) AndAlso Content(Key) = Value Then Exit Sub '如果值一样就不处理
|
||||
'更新缓存
|
||||
Content(Key) = Value
|
||||
'写入文件
|
||||
Dim FileContent As New StringBuilder
|
||||
For Each Pair In Content
|
||||
FileContent.Append(Pair.Key)
|
||||
FileContent.Append(":")
|
||||
FileContent.Append(Pair.Value)
|
||||
FileContent.Append(vbCrLf)
|
||||
Next
|
||||
If Not FileName.Contains(":\") Then FileName = $"{Path}PCL\{FileName}.ini"
|
||||
WriteFile(FileName, FileContent.ToString)
|
||||
Catch ex As Exception
|
||||
Log(ex, "写入文件失败:" & FileName & "/" & Key & ": " & Value)
|
||||
Log(ex, $"写入文件失败({FileName} -> {Key}:{Value})")
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
'路径处理
|
||||
''' <summary>
|
||||
''' 从文件路径或者 Url 获取不包含文件名的路径,或获取文件夹的父文件夹路径。不包含路径将会抛出异常。
|
||||
''' 从文件路径或者 Url 获取不包含文件名的路径,或获取文件夹的父文件夹路径。
|
||||
''' 取决于原路径格式,路径以 / 或 \ 结尾。
|
||||
''' 不包含路径将会抛出异常。
|
||||
''' </summary>
|
||||
Public Function GetPathFromFullPath(FilePath As String) As String
|
||||
If Not (FilePath.Contains("\") OrElse FilePath.Contains("/")) Then Throw New Exception("不包含路径:" & FilePath)
|
||||
If FilePath.EndsWith("\") OrElse FilePath.EndsWith("/") Then
|
||||
If FilePath.EndsWithF("\") OrElse FilePath.EndsWithF("/") Then
|
||||
'是文件夹路径
|
||||
Dim IsRight As Boolean = FilePath.EndsWith("\")
|
||||
Dim IsRight As Boolean = FilePath.EndsWithF("\")
|
||||
FilePath = Left(FilePath, Len(FilePath) - 1)
|
||||
GetPathFromFullPath = Left(FilePath, FilePath.LastIndexOfAny({"\", "/"})) & If(IsRight, "\", "/")
|
||||
If GetPathFromFullPath = "" Then Throw New Exception("不包含路径:" & FilePath)
|
||||
@@ -745,11 +729,13 @@ Public Module ModBase
|
||||
''' </summary>
|
||||
Public Function GetFileNameFromPath(FilePath As String) As String
|
||||
FilePath = FilePath.Replace("/", "\")
|
||||
If FilePath.EndsWith("\") Then Throw New Exception("不包含文件名:" & FilePath)
|
||||
Dim FileName As String = FilePath.Split("\").Last.Split("?").First '去掉网络参数后的 ?
|
||||
If FileName.Length = 0 Then Throw New Exception("不包含文件名:" & FilePath)
|
||||
If FileName.Length > 250 Then Throw New PathTooLongException("文件名过长:" & FilePath)
|
||||
Return FileName
|
||||
If FilePath.EndsWithF("\") Then Throw New Exception("不包含文件名:" & FilePath)
|
||||
If FilePath.Contains("?") Then FilePath = FilePath.Substring(0, FilePath.IndexOfF("?")) '去掉网络参数后的 ?
|
||||
If FilePath.Contains("\") Then FilePath = FilePath.Substring(FilePath.LastIndexOfF("\") + 1)
|
||||
Dim length As Integer = FilePath.Length
|
||||
If length = 0 Then Throw New Exception("不包含文件名:" & FilePath)
|
||||
If length > 250 Then Throw New PathTooLongException("文件名过长:" & FilePath)
|
||||
Return FilePath
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 从文件路径或者 Url 获取不包含路径与扩展名的文件名。不包含文件名将会抛出异常。
|
||||
@@ -757,7 +743,7 @@ Public Module ModBase
|
||||
Public Function GetFileNameWithoutExtentionFromPath(FilePath As String) As String
|
||||
Dim Name As String = GetFileNameFromPath(FilePath)
|
||||
If Name.Contains(".") Then
|
||||
Return Name.Substring(0, Name.LastIndexOf("."))
|
||||
Return Name.Substring(0, Name.LastIndexOfF("."))
|
||||
Else
|
||||
Return Name
|
||||
End If
|
||||
@@ -766,8 +752,8 @@ Public Module ModBase
|
||||
''' 从文件夹路径获取文件夹名。
|
||||
''' </summary>
|
||||
Public Function GetFolderNameFromPath(FolderPath As String) As String
|
||||
If FolderPath.EndsWith(":\") OrElse FolderPath.EndsWith(":\\") Then Return FolderPath.Substring(0, 1)
|
||||
If FolderPath.EndsWith("\") OrElse FolderPath.EndsWith("/") Then FolderPath = Left(FolderPath, FolderPath.Length - 1)
|
||||
If FolderPath.EndsWithF(":\") OrElse FolderPath.EndsWithF(":\\") Then Return FolderPath.Substring(0, 1)
|
||||
If FolderPath.EndsWithF("\") OrElse FolderPath.EndsWithF("/") Then FolderPath = Left(FolderPath, FolderPath.Length - 1)
|
||||
Return GetFileNameFromPath(FolderPath)
|
||||
End Function
|
||||
|
||||
@@ -784,7 +770,7 @@ Public Module ModBase
|
||||
'复制文件
|
||||
File.Copy(FromPath, ToPath, True)
|
||||
Catch ex As Exception
|
||||
Throw New Exception("复制文件出错:" & FromPath & " -> " & ToPath, ex)
|
||||
Throw New Exception("复制文件出错:" & FromPath & " → " & ToPath, ex)
|
||||
End Try
|
||||
End Sub
|
||||
''' <summary>
|
||||
@@ -1020,7 +1006,7 @@ Public Module ModBase
|
||||
Public Function SelectFolder(Optional Title As String = "选择文件夹") As String
|
||||
Dim folderDialog As New Ookii.Dialogs.Wpf.VistaFolderBrowserDialog With {.ShowNewFolderButton = True, .RootFolder = Environment.SpecialFolder.Desktop, .Description = Title, .UseDescriptionForTitle = True}
|
||||
folderDialog.ShowDialog()
|
||||
SelectFolder = If(String.IsNullOrEmpty(folderDialog.SelectedPath), "", folderDialog.SelectedPath & If(folderDialog.SelectedPath.EndsWith("\"), "", "\"))
|
||||
SelectFolder = If(String.IsNullOrEmpty(folderDialog.SelectedPath), "", folderDialog.SelectedPath & If(folderDialog.SelectedPath.EndsWithF("\"), "", "\"))
|
||||
Log("[UI] 选择文件夹返回:" & SelectFolder)
|
||||
End Function
|
||||
|
||||
@@ -1031,8 +1017,8 @@ Public Module ModBase
|
||||
Public Function CheckPermission(Path As String) As Boolean
|
||||
Try
|
||||
If Path = "" Then Return False
|
||||
If Not Path.EndsWith("\") Then Path += "\"
|
||||
If Path.EndsWith(":\System Volume Information\") OrElse Path.EndsWith(":\$RECYCLE.BIN\") Then Return False
|
||||
If Not Path.EndsWithF("\") Then Path += "\"
|
||||
If Path.EndsWithF(":\System Volume Information\") OrElse Path.EndsWithF(":\$RECYCLE.BIN\") Then Return False
|
||||
If Not Directory.Exists(Path) Then Return False
|
||||
Dim FileName As String = "CheckPermission" & GetUuid()
|
||||
If File.Exists(Path & FileName) Then File.Delete(Path & FileName)
|
||||
@@ -1049,7 +1035,7 @@ Public Module ModBase
|
||||
''' </summary>
|
||||
Public Sub CheckPermissionWithException(Path As String)
|
||||
If String.IsNullOrWhiteSpace(Path) Then Throw New ArgumentNullException("文件夹名不能为空!")
|
||||
If Not Path.EndsWith("\") Then Path += "\"
|
||||
If Not Path.EndsWithF("\") Then Path += "\"
|
||||
If Not Directory.Exists(Path) Then Throw New DirectoryNotFoundException("文件夹不存在!")
|
||||
If File.Exists(Path & "CheckPermission") Then File.Delete(Path & "CheckPermission")
|
||||
File.Create(Path & "CheckPermission").Dispose()
|
||||
@@ -1058,7 +1044,7 @@ Public Module ModBase
|
||||
''' <summary>
|
||||
''' 获取文件 MD5,若失败则返回空字符串。
|
||||
''' </summary>
|
||||
Public Function GetAuthMD5(FilePath As String) As String
|
||||
Public Function GetFileMD5(FilePath As String) As String
|
||||
Dim Retry As Boolean = False
|
||||
Re:
|
||||
Try
|
||||
@@ -1087,7 +1073,7 @@ Re:
|
||||
''' <summary>
|
||||
''' 获取文件 SHA256,若失败则返回空字符串。
|
||||
''' </summary>
|
||||
Public Function GetAuthSHA256(FilePath As String) As String
|
||||
Public Function GetFileSHA256(FilePath As String) As String
|
||||
Dim Retry As Boolean = False
|
||||
Re:
|
||||
Try
|
||||
@@ -1118,7 +1104,7 @@ Re:
|
||||
''' <summary>
|
||||
''' 获取文件 SHA1,若失败则返回空字符串。
|
||||
''' </summary>
|
||||
Public Function GetAuthSHA1(FilePath As String) As String
|
||||
Public Function GetFileSHA1(FilePath As String) As String
|
||||
Dim Retry As Boolean = False
|
||||
Re:
|
||||
Try
|
||||
@@ -1206,11 +1192,11 @@ Re:
|
||||
If MinSize >= 0 AndAlso MinSize > FileSize Then Return "文件大小应大于 " & MinSize & " B,实际为 " & FileSize & " B"
|
||||
If Not String.IsNullOrEmpty(Hash) Then
|
||||
If Hash.Length < 35 Then 'MD5
|
||||
If Hash <> GetAuthMD5(LocalPath) Then Return "文件 MD5 应为 " & Hash & ",实际为 " & GetAuthMD5(LocalPath)
|
||||
If Hash.ToLowerInvariant <> GetFileMD5(LocalPath) Then Return "文件 MD5 应为 " & Hash & ",实际为 " & GetFileMD5(LocalPath)
|
||||
ElseIf Hash.Length = 64 Then 'SHA256
|
||||
If Hash <> GetAuthSHA256(LocalPath) Then Return "文件 SHA256 应为 " & Hash & ",实际为 " & GetAuthSHA256(LocalPath)
|
||||
If Hash.ToLowerInvariant <> GetFileSHA256(LocalPath) Then Return "文件 SHA256 应为 " & Hash & ",实际为 " & GetFileSHA256(LocalPath)
|
||||
Else 'SHA1 (40)
|
||||
If Hash <> GetAuthSHA1(LocalPath) Then Return "文件 SHA1 应为 " & Hash & ",实际为 " & GetAuthSHA1(LocalPath)
|
||||
If Hash.ToLowerInvariant <> GetFileSHA1(LocalPath) Then Return "文件 SHA1 应为 " & Hash & ",实际为 " & GetFileSHA1(LocalPath)
|
||||
End If
|
||||
End If
|
||||
If IsJson Then
|
||||
@@ -1233,35 +1219,35 @@ Re:
|
||||
''' <summary>
|
||||
''' 尝试根据后缀名判断文件种类并解压文件,支持 gz 与 zip,会尝试将 jar 以 zip 方式解压。
|
||||
''' 会尝试创建,但不会清空目标文件夹。
|
||||
''' 成功返回 True,并非压缩文件或失败返回 False。
|
||||
''' </summary>
|
||||
Public Function ExtractFile(CompressFilePath As String, DestDirectory As String, Optional Encode As Encoding = Nothing) As Boolean
|
||||
Try
|
||||
Directory.CreateDirectory(DestDirectory)
|
||||
If CompressFilePath.EndsWith(".zip") OrElse CompressFilePath.EndsWith(".jar") OrElse CompressFilePath.EndsWith(".mrpack") Then
|
||||
'以 zip 方式解压
|
||||
ZipFile.ExtractToDirectory(CompressFilePath, DestDirectory, If(Encode, Encoding.GetEncoding("GB18030")))
|
||||
Return True
|
||||
ElseIf CompressFilePath.EndsWith(".gz") Then
|
||||
'以 gz 方式解压
|
||||
Dim stream As New GZipStream(New FileStream(CompressFilePath, FileMode.Open, FileAccess.ReadWrite), CompressionMode.Decompress)
|
||||
Dim decompressedFile As New FileStream(DestDirectory & GetFileNameFromPath(CompressFilePath).ToLower.Replace(".tar", "").Replace(".gz", ""), FileMode.OpenOrCreate, FileAccess.Write)
|
||||
Dim data As Integer = stream.ReadByte()
|
||||
While data <> -1
|
||||
decompressedFile.WriteByte(data)
|
||||
data = stream.ReadByte()
|
||||
End While
|
||||
decompressedFile.Close()
|
||||
stream.Close()
|
||||
Return True
|
||||
Else
|
||||
Return False
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "尝试解压文件失败")
|
||||
Return False
|
||||
End Try
|
||||
End Function
|
||||
Public Sub ExtractFile(CompressFilePath As String, DestDirectory As String, Optional Encode As Encoding = Nothing)
|
||||
Directory.CreateDirectory(DestDirectory)
|
||||
If CompressFilePath.EndsWithF(".gz", True) Then
|
||||
'以 gz 方式解压
|
||||
Dim stream As New GZipStream(New FileStream(CompressFilePath, FileMode.Open, FileAccess.ReadWrite), CompressionMode.Decompress)
|
||||
Dim decompressedFile As New FileStream(DestDirectory & GetFileNameFromPath(CompressFilePath).ToLower.Replace(".tar", "").Replace(".gz", ""), FileMode.OpenOrCreate, FileAccess.Write)
|
||||
Dim data As Integer = stream.ReadByte()
|
||||
While data <> -1
|
||||
decompressedFile.WriteByte(data)
|
||||
data = stream.ReadByte()
|
||||
End While
|
||||
decompressedFile.Close()
|
||||
stream.Close()
|
||||
Else
|
||||
'以 zip 方式解压
|
||||
Using Archive = ZipFile.Open(CompressFilePath, ZipArchiveMode.Read, If(Encode, Encoding.GetEncoding("GB18030")))
|
||||
For Each Entry As ZipArchiveEntry In Archive.Entries
|
||||
Dim DestinationPath As String = IO.Path.Combine(DestDirectory, Entry.FullName)
|
||||
If DestinationPath.EndsWithF("\") OrElse DestinationPath.EndsWithF("/") Then
|
||||
Continue For '不创建空文件夹
|
||||
Else
|
||||
Directory.CreateDirectory(GetPathFromFullPath(DestinationPath))
|
||||
Entry.ExtractToFile(DestinationPath, True)
|
||||
End If
|
||||
Next
|
||||
End Using
|
||||
End If
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 删除文件夹,返回删除的文件个数。通过参数选择是否抛出异常。
|
||||
@@ -1285,7 +1271,7 @@ Re:
|
||||
Next
|
||||
Temp = Directory.GetDirectories(Path)
|
||||
For Each str As String In Temp
|
||||
DeleteDirectory(str)
|
||||
DeleteDirectory(str, IgnoreIssue)
|
||||
Next
|
||||
Try
|
||||
Directory.Delete(Path, True)
|
||||
@@ -1303,9 +1289,9 @@ Re:
|
||||
''' </summary>
|
||||
Public Sub CopyDirectory(FromPath As String, ToPath As String)
|
||||
FromPath = FromPath.Replace("/", "\")
|
||||
If Not FromPath.EndsWith("\") Then FromPath &= "\"
|
||||
If Not FromPath.EndsWithF("\") Then FromPath &= "\"
|
||||
ToPath = ToPath.Replace("/", "\")
|
||||
If Not ToPath.EndsWith("\") Then ToPath &= "\"
|
||||
If Not ToPath.EndsWithF("\") Then ToPath &= "\"
|
||||
For Each File In EnumerateFiles(FromPath)
|
||||
CopyFile(File.FullName, File.FullName.Replace(FromPath, ToPath))
|
||||
Next
|
||||
@@ -1313,8 +1299,10 @@ Re:
|
||||
''' <summary>
|
||||
''' 遍历文件夹中的所有文件。
|
||||
''' </summary>
|
||||
Public Function EnumerateFiles(Directory As String) As List(Of FileInfo)
|
||||
Return New DirectoryInfo(Directory).EnumerateFiles("*", SearchOption.AllDirectories).ToList
|
||||
Public Function EnumerateFiles(Directory As String) As IEnumerable(Of FileInfo)
|
||||
Dim Info As New DirectoryInfo(Directory)
|
||||
If Not Info.Exists Then Return New List(Of FileInfo)
|
||||
Return Info.EnumerateFiles("*", SearchOption.AllDirectories)
|
||||
End Function
|
||||
|
||||
#End Region
|
||||
@@ -1350,7 +1338,7 @@ Re:
|
||||
Loop
|
||||
DescList = DescList.Distinct.ToList
|
||||
Dim Desc As String = Join(DescList, vbCrLf & "→ ")
|
||||
Dim Stack As String = If(StackList.Count > 0, vbCrLf & Join(StackList, vbCrLf), "")
|
||||
Dim Stack As String = If(StackList.Any, vbCrLf & Join(StackList, vbCrLf), "")
|
||||
|
||||
'常见错误(记得同时修改下面的)
|
||||
Dim CommonReason As String = Nothing
|
||||
@@ -1360,6 +1348,8 @@ Re:
|
||||
CommonReason = "PCL 的权限不足。请尝试右键 PCL,选择以管理员身份运行。"
|
||||
ElseIf TypeOf InnerEx Is OutOfMemoryException Then
|
||||
CommonReason = "你的电脑运行内存不足,导致 PCL 无法继续运行。请在关闭一部分不需要的程序后再试。"
|
||||
ElseIf TypeOf InnerEx Is Runtime.InteropServices.COMException Then
|
||||
CommonReason = "由于操作系统或显卡存在问题,导致出现错误。请尝试重启 PCL。"
|
||||
ElseIf {"远程主机强迫关闭了", "远程方已关闭传输流", "未能解析此远程名称", "由于目标计算机积极拒绝",
|
||||
"操作已超时", "操作超时", "服务器超时", "连接超时"}.Any(Function(s) Desc.Contains(s)) Then
|
||||
CommonReason = "你的网络环境不佳,导致难以连接到服务器。请检查网络,多重试几次,或尝试使用 VPN。"
|
||||
@@ -1406,6 +1396,8 @@ Re:
|
||||
CommonReason = "PCL 的权限不足。请尝试右键 PCL,选择以管理员身份运行。"
|
||||
ElseIf TypeOf InnerEx Is OutOfMemoryException Then
|
||||
CommonReason = "你的电脑运行内存不足,导致 PCL 无法继续运行。请在关闭一部分不需要的程序后再试。"
|
||||
ElseIf TypeOf InnerEx Is Runtime.InteropServices.COMException Then
|
||||
CommonReason = "由于操作系统或显卡存在问题,导致出现错误。请尝试重启 PCL。"
|
||||
ElseIf {"远程主机强迫关闭了", "远程方已关闭传输流", "未能解析此远程名称", "由于目标计算机积极拒绝",
|
||||
"操作已超时", "操作超时", "服务器超时", "连接超时"}.Any(Function(s) Desc.Contains(s)) Then
|
||||
CommonReason = "你的网络环境不佳,导致难以连接到服务器。请检查网络,多重试几次,或尝试使用 VPN。"
|
||||
@@ -1432,21 +1424,23 @@ Re:
|
||||
''' </summary>
|
||||
''' <param name="FileSize">以字节为单位的大小表示。</param>
|
||||
Public Function GetString(FileSize As Long) As String
|
||||
Dim IsNegative = FileSize < 0
|
||||
If IsNegative Then FileSize *= -1
|
||||
If FileSize < 1000 Then
|
||||
'B 级
|
||||
Return FileSize & " B"
|
||||
Return If(IsNegative, "-", "") & FileSize & " B"
|
||||
ElseIf FileSize < 1024 * 1000 Then
|
||||
'K 级
|
||||
Dim RoundResult As String = Math.Round(FileSize / 1024)
|
||||
Return Math.Round(FileSize / 1024, CInt(MathRange(3 - RoundResult.Length, 0, 2))) & " K"
|
||||
Return If(IsNegative, "-", "") & Math.Round(FileSize / 1024, CInt(MathClamp(3 - RoundResult.Length, 0, 2))) & " K"
|
||||
ElseIf FileSize < 1024 * 1024 * 1000 Then
|
||||
'M 级
|
||||
Dim RoundResult As String = Math.Round(FileSize / 1024 / 1024)
|
||||
Return Math.Round(FileSize / 1024 / 1024, CInt(MathRange(3 - RoundResult.Length, 0, 2))) & " M"
|
||||
Return If(IsNegative, "-", "") & Math.Round(FileSize / 1024 / 1024, CInt(MathClamp(3 - RoundResult.Length, 0, 2))) & " M"
|
||||
Else
|
||||
'G 级
|
||||
Dim RoundResult As String = Math.Round(FileSize / 1024 / 1024 / 1024)
|
||||
Return Math.Round(FileSize / 1024 / 1024 / 1024, CInt(MathRange(3 - RoundResult.Length, 0, 2))) & " G"
|
||||
Return If(IsNegative, "-", "") & Math.Round(FileSize / 1024 / 1024 / 1024, CInt(MathClamp(3 - RoundResult.Length, 0, 2))) & " G"
|
||||
End If
|
||||
End Function
|
||||
|
||||
@@ -1461,6 +1455,14 @@ Re:
|
||||
End Try
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 将第一个字符转换为大写,其余字符转换为小写。
|
||||
''' </summary>
|
||||
<Extension> Public Function Capitalize(word As String) As String
|
||||
If String.IsNullOrEmpty(word) Then Return word
|
||||
Return word.Substring(0, 1).ToUpperInvariant() & word.Substring(1).ToLowerInvariant()
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 将字符串统一至某个长度,过短则以 Code 将其右侧填充,过长则截取靠左的指定长度。
|
||||
''' </summary>
|
||||
@@ -1535,6 +1537,48 @@ Re:
|
||||
Next
|
||||
Return tmp.ToString()
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 检查字符串中的字符是否均为 ASCII 字符。
|
||||
''' </summary>
|
||||
<Extension> Public Function IsASCII(Input As String) As Boolean
|
||||
Return Input.All(Function(c) AscW(c) < 128)
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 高速的 StartsWith。
|
||||
''' </summary>
|
||||
<Extension> <MethodImpl(MethodImplOptions.AggressiveInlining)>
|
||||
Public Function StartsWithF(Str As String, Prefix As String, Optional IgnoreCase As Boolean = False) As Boolean
|
||||
Return Str.StartsWith(Prefix, If(IgnoreCase, StringComparison.OrdinalIgnoreCase, StringComparison.Ordinal))
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 高速的 EndsWith。
|
||||
''' </summary>
|
||||
<Extension> <MethodImpl(MethodImplOptions.AggressiveInlining)>
|
||||
Public Function EndsWithF(Str As String, Suffix As String, Optional IgnoreCase As Boolean = False) As Boolean
|
||||
Return Str.EndsWith(Suffix, If(IgnoreCase, StringComparison.OrdinalIgnoreCase, StringComparison.Ordinal))
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 支持可变大小写判断的 Contains。
|
||||
''' </summary>
|
||||
<Extension> <MethodImpl(MethodImplOptions.AggressiveInlining)>
|
||||
Public Function ContainsF(Str As String, SubStr As String, Optional IgnoreCase As Boolean = False) As Boolean
|
||||
Return Str.IndexOf(SubStr, If(IgnoreCase, StringComparison.OrdinalIgnoreCase, StringComparison.Ordinal)) >= 0
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 高速的 IndexOf。
|
||||
''' </summary>
|
||||
<Extension> <MethodImpl(MethodImplOptions.AggressiveInlining)>
|
||||
Public Function IndexOfF(Str As String, SubStr As String, Optional IgnoreCase As Boolean = False) As Integer
|
||||
Return Str.IndexOf(SubStr, If(IgnoreCase, StringComparison.OrdinalIgnoreCase, StringComparison.Ordinal))
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 高速的 LastIndexOf。
|
||||
''' </summary>
|
||||
<Extension> <MethodImpl(MethodImplOptions.AggressiveInlining)>
|
||||
Public Function LastIndexOfF(Str As String, SubStr As String, Optional IgnoreCase As Boolean = False) As Integer
|
||||
Return Str.LastIndexOf(SubStr, If(IgnoreCase, StringComparison.OrdinalIgnoreCase, StringComparison.Ordinal))
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 输入 And 字符不会报错的 Val。
|
||||
@@ -1548,19 +1592,19 @@ Re:
|
||||
''' 为字符串进行 XML 转义。
|
||||
''' </summary>
|
||||
Public Function EscapeXML(Str As String) As String
|
||||
Return Str.Replace("&", "&").Replace("<", "<").Replace(">", ">").Replace("'", "'").Replace("""", """)
|
||||
Return Str.Replace("&", "&").Replace("<", "<").Replace(">", ">").Replace("'", "'").Replace("""", """).Replace(vbCrLf, "
")
|
||||
End Function
|
||||
|
||||
'正则
|
||||
''' <summary>
|
||||
''' 搜索字符串中的所有正则匹配项。
|
||||
''' </summary>
|
||||
Public Function RegexSearch(str As String, regex As String, Optional options As RegularExpressions.RegexOptions = RegularExpressions.RegexOptions.None) As List(Of String)
|
||||
Public Function RegexSearch(str As String, regex As String, Optional options As RegexOptions = RegularExpressions.RegexOptions.None) As List(Of String)
|
||||
Try
|
||||
RegexSearch = New List(Of String)
|
||||
Dim RegexSearchRes = New RegularExpressions.Regex(regex, options).Matches(str)
|
||||
Dim RegexSearchRes = New Regex(regex, options).Matches(str)
|
||||
If RegexSearchRes Is Nothing Then Return RegexSearch
|
||||
For Each item As RegularExpressions.Match In RegexSearchRes
|
||||
For Each item As Match In RegexSearchRes
|
||||
RegexSearch.Add(item.Value)
|
||||
Next
|
||||
Catch ex As Exception
|
||||
@@ -1571,7 +1615,7 @@ Re:
|
||||
''' <summary>
|
||||
''' 获取字符串中的第一个正则匹配项,若无匹配则返回 Nothing。
|
||||
''' </summary>
|
||||
Public Function RegexSeek(str As String, regex As String, Optional options As RegularExpressions.RegexOptions = RegularExpressions.RegexOptions.None) As String
|
||||
Public Function RegexSeek(str As String, regex As String, Optional options As RegexOptions = RegularExpressions.RegexOptions.None) As String
|
||||
Try
|
||||
Dim Result = RegularExpressions.Regex.Match(str, regex, options).Value
|
||||
Return If(Result = "", Nothing, Result)
|
||||
@@ -1583,7 +1627,7 @@ Re:
|
||||
''' <summary>
|
||||
''' 检查字符串是否匹配某正则模式。
|
||||
''' </summary>
|
||||
Public Function RegexCheck(str As String, regex As String, Optional options As RegularExpressions.RegexOptions = RegularExpressions.RegexOptions.None) As Boolean
|
||||
Public Function RegexCheck(str As String, regex As String, Optional options As RegexOptions = RegularExpressions.RegexOptions.None) As Boolean
|
||||
Try
|
||||
Return RegularExpressions.Regex.IsMatch(str, regex, options)
|
||||
Catch ex As Exception
|
||||
@@ -1594,7 +1638,13 @@ Re:
|
||||
''' <summary>
|
||||
''' 进行正则替换,会抛出错误。
|
||||
''' </summary>
|
||||
Public Function RegexReplace(Input As String, Replacement As String, Regex As String, Optional options As RegularExpressions.RegexOptions = RegularExpressions.RegexOptions.None) As String
|
||||
Public Function RegexReplace(Input As String, Replacement As String, Regex As String, Optional options As RegexOptions = RegularExpressions.RegexOptions.None) As String
|
||||
Return RegularExpressions.Regex.Replace(Input, Regex, Replacement, options)
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 对每个正则匹配分别进行替换,会抛出错误。
|
||||
''' </summary>
|
||||
Public Function RegexReplaceEach(Input As String, Replacement As MatchEvaluator, Regex As String, Optional options As RegexOptions = RegularExpressions.RegexOptions.None) As String
|
||||
Return RegularExpressions.Regex.Replace(Input, Regex, Replacement, options)
|
||||
End Function
|
||||
|
||||
@@ -1612,14 +1662,14 @@ Re:
|
||||
Source = Source.ToLower.Replace(" ", "")
|
||||
Query = Query.ToLower.Replace(" ", "")
|
||||
Dim sourceLength As Integer = Source.Length, queryLength As Integer = Query.Length '用于计算最后因数的长度缓存
|
||||
Do While qp < Query.Length
|
||||
Do While qp < queryLength
|
||||
'对 qp 作为开始位置计算
|
||||
Dim sp As Integer = 0, lenMax As Integer = 0, spMax As Integer = 0
|
||||
'查找以 qp 为头的最大子串
|
||||
Do While sp < Source.Length
|
||||
'对每个 sp 作为开始位置计算最大子串
|
||||
Dim len As Integer = 0
|
||||
Do While (qp + len) < Query.Length AndAlso (sp + len) < Source.Length AndAlso Source(sp + len) = Query(qp + len)
|
||||
Do While (qp + len) < queryLength AndAlso (sp + len) < Source.Length AndAlso Source(sp + len) = Query(qp + len)
|
||||
len += 1
|
||||
Loop
|
||||
'存储 len
|
||||
@@ -1634,7 +1684,7 @@ Re:
|
||||
Source = Source.Substring(0, spMax) & If(Source.Count > spMax + lenMax, Source.Substring(spMax + lenMax), String.Empty) '将源中的对应字段替换空
|
||||
'存储 lenSum
|
||||
Dim IncWeight = (Math.Pow(1.4, 3 + lenMax) - 3.6) '根据长度加成
|
||||
IncWeight *= (1 + 0.3 * Math.Max(0, 3 - Math.Abs(qp - spMax))) '根据位置加成
|
||||
IncWeight *= 1 + 0.3 * Math.Max(0, 3 - Math.Abs(qp - spMax)) '根据位置加成
|
||||
lenSum += IncWeight
|
||||
End If
|
||||
'根据结果增加 qp
|
||||
@@ -1684,23 +1734,24 @@ Re:
|
||||
Public Function Search(Of T)(Entries As List(Of SearchEntry(Of T)), Query As String, Optional MaxBlurCount As Integer = 5, Optional MinBlurSimilarity As Double = 0.1) As List(Of SearchEntry(Of T))
|
||||
'初始化
|
||||
Dim ResultList As New List(Of SearchEntry(Of T))
|
||||
If Entries.Count = 0 Then Return ResultList
|
||||
If Not Entries.Any() Then Return ResultList
|
||||
'进行搜索,获取相似信息
|
||||
For Each Entry In Entries
|
||||
Entry.Similarity = SearchSimilarityWeighted(Entry.SearchSource, Query)
|
||||
Entry.AbsoluteRight = False
|
||||
For Each Pair In Entry.SearchSource
|
||||
If Pair.Key.ToLower.Replace(" ", "").Contains(Query.ToLower.Replace(" ", "")) Then Entry.AbsoluteRight = True
|
||||
If Pair.Key.Replace(" ", "").ContainsF(Query.Replace(" ", ""), True) Then Entry.AbsoluteRight = True
|
||||
Next
|
||||
Next
|
||||
'按照相似度进行排序
|
||||
Entries = Sort(Entries, Function(Left As SearchEntry(Of T), Right As SearchEntry(Of T)) As Boolean
|
||||
If Left.AbsoluteRight Xor Right.AbsoluteRight Then
|
||||
Return Left.AbsoluteRight
|
||||
Else
|
||||
Return Left.Similarity > Right.Similarity
|
||||
End If
|
||||
End Function)
|
||||
Entries = Sort(Entries,
|
||||
Function(Left, Right) As Boolean
|
||||
If Left.AbsoluteRight Xor Right.AbsoluteRight Then
|
||||
Return Left.AbsoluteRight
|
||||
Else
|
||||
Return Left.Similarity > Right.Similarity
|
||||
End If
|
||||
End Function)
|
||||
'返回结果
|
||||
Dim BlurCount As Integer = 0
|
||||
For Each Entry In Entries
|
||||
@@ -1728,16 +1779,13 @@ Re:
|
||||
Return principal.IsInRole(WindowsBuiltInRole.Administrator)
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 以管理员权限重新运行当前程序。返回是否成功。
|
||||
''' 以管理员权限运行当前程序,并等待程序运行结束。
|
||||
''' 返回程序的返回代码,如果运行失败将抛出异常。
|
||||
''' </summary>
|
||||
Public Function RerunAsAdmin(Argument As String) As Boolean
|
||||
Try
|
||||
Process.Start(New ProcessStartInfo(PathWithName) With {.Verb = "runas", .Arguments = Argument})
|
||||
Return True
|
||||
Catch ex As Exception
|
||||
Log(ex, "以管理员权限运行程序失败")
|
||||
Return False
|
||||
End Try
|
||||
Public Function RunAsAdmin(Argument As String) As Integer
|
||||
Dim NewProcess = Process.Start(New ProcessStartInfo(PathWithName) With {.Verb = "runas", .Arguments = Argument})
|
||||
NewProcess.WaitForExit()
|
||||
Return NewProcess.ExitCode
|
||||
End Function
|
||||
|
||||
Private Uuid As Integer = 1
|
||||
@@ -1769,15 +1817,11 @@ Re:
|
||||
''' <summary>
|
||||
''' 数组去重。
|
||||
''' </summary>
|
||||
Public Function Distinct(Of T)(Arr As ICollection(Of T), Optional IsEqual As CompareThreadStart(Of T) = Nothing) As List(Of T)
|
||||
<Extension> Public Function Distinct(Of T)(Arr As ICollection(Of T), IsEqual As CompareThreadStart(Of T)) As List(Of T)
|
||||
Dim ResultArray As New List(Of T)
|
||||
For i = 0 To Arr.Count - 1
|
||||
For ii = i + 1 To Arr.Count - 1
|
||||
If IsEqual Is Nothing Then
|
||||
If Arr(i).Equals(Arr(ii)) Then GoTo NextElement
|
||||
Else
|
||||
If IsEqual(Arr(i), Arr(ii)) Then GoTo NextElement
|
||||
End If
|
||||
If IsEqual(Arr(i), Arr(ii)) Then GoTo NextElement
|
||||
Next
|
||||
ResultArray.Add(Arr(i))
|
||||
NextElement:
|
||||
@@ -1789,7 +1833,7 @@ NextElement:
|
||||
''' 获取格式类似于“11:08:52.037”的当前时间的字符串。
|
||||
''' </summary>
|
||||
Public Function GetTimeNow() As String
|
||||
Return Date.Now.ToString("HH:mm:ss.fff")
|
||||
Return Date.Now.ToString("HH':'mm':'ss'.'fff")
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 获取系统运行时间(毫秒),保证为正 Long 且大于 1,但可能突变减小。
|
||||
@@ -1950,9 +1994,10 @@ NextElement:
|
||||
Log("[System] 执行外部命令并等待返回结果:" & FileName & " " & Arguments)
|
||||
Using Program As New Process() With {.StartInfo = Info}
|
||||
Program.Start()
|
||||
Dim Result As String = Program.StandardOutput.ReadToEnd & Program.StandardError.ReadToEnd
|
||||
Program.WaitForExit(Timeout)
|
||||
If Not Program.HasExited Then Program.Kill()
|
||||
Return Program.StandardOutput.ReadToEnd & Program.StandardError.ReadToEnd
|
||||
Return Result
|
||||
End Using
|
||||
End Function
|
||||
|
||||
@@ -2024,9 +2069,9 @@ NextElement:
|
||||
''' 按照既定的函数进行选择排序。
|
||||
''' </summary>
|
||||
''' <param name="SortRule">传入两个对象,若第一个对象应该排在前面,则返回 True。</param>
|
||||
Public Function Sort(Of T)(List As IList(Of T), SortRule As CompareThreadStart(Of T)) As List(Of T)
|
||||
<Extension> Public Function Sort(Of T)(List As IList(Of T), SortRule As CompareThreadStart(Of T)) As List(Of T)
|
||||
Dim NewList As New List(Of T)
|
||||
While List.Count > 0
|
||||
While List.Any
|
||||
Dim Highest = List(0)
|
||||
For i = 1 To List.Count - 1
|
||||
If SortRule(List(i), Highest) Then Highest = List(i)
|
||||
@@ -2054,7 +2099,7 @@ NextElement:
|
||||
Dim AllArguments() As String = Command.Split(" ")
|
||||
For i = 0 To AllArguments.Length - 1
|
||||
If AllArguments(i) = "-" & Name Then
|
||||
If AllArguments.Length = i + 1 OrElse AllArguments(i + 1).StartsWith("-") Then Return True
|
||||
If AllArguments.Length = i + 1 OrElse AllArguments(i + 1).StartsWithF("-") Then Return True
|
||||
Return AllArguments(i + 1)
|
||||
End If
|
||||
Next
|
||||
@@ -2081,6 +2126,7 @@ NextElement:
|
||||
''' </summary>
|
||||
Public Sub OpenWebsite(Url As String)
|
||||
Try
|
||||
If Not Url.StartsWithF("http", True) Then Throw New Exception(Url & " 不是一个有效的网址,它必须以 http 开头!")
|
||||
Log("[System] 正在打开网页:" & Url)
|
||||
Process.Start(Url)
|
||||
Catch ex As Exception
|
||||
@@ -2292,6 +2338,16 @@ Retry:
|
||||
Return Thread.CurrentThread.ManagedThreadId = UiThreadId
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 检查某个控件是否位于主窗口可视区域内,且控件本身可见。
|
||||
''' </summary>
|
||||
<Extension> Public Function IsVisibleInForm(element As FrameworkElement) As Boolean
|
||||
If Not element.IsVisible Then Return False
|
||||
Dim bounds As Rect = element.TransformToAncestor(FrmMain).TransformBounds(New Rect(0, 0, element.ActualWidth, element.ActualHeight))
|
||||
Dim rect As New Rect(0, 0, FrmMain.ActualWidth, FrmMain.ActualHeight)
|
||||
Return rect.Contains(bounds.TopLeft) OrElse rect.Contains(bounds.BottomRight)
|
||||
End Function
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "Debug"
|
||||
@@ -2586,11 +2642,11 @@ Retry:
|
||||
''' <summary>
|
||||
''' 将数组随机打乱。
|
||||
''' </summary>
|
||||
Public Function RandomChaos(Of T)(array As IList(Of T)) As IList(Of T)
|
||||
RandomChaos = New List(Of T)
|
||||
Do While array.Count > 0
|
||||
Public Function Shuffle(Of T)(array As IList(Of T)) As IList(Of T)
|
||||
Shuffle = New List(Of T)
|
||||
Do While array.Any
|
||||
Dim i As Integer = RandomInteger(0, array.Count - 1)
|
||||
RandomChaos.Add(array(i))
|
||||
Shuffle.Add(array(i))
|
||||
array.RemoveAt(i)
|
||||
Loop
|
||||
End Function
|
||||
|
||||
@@ -153,10 +153,14 @@
|
||||
End Select
|
||||
End Get
|
||||
Set(value As Double)
|
||||
If _Progress = value Then Exit Property
|
||||
Dim OldValue = _Progress
|
||||
_Progress = value
|
||||
RaiseEvent ProgressChanged(value, OldValue)
|
||||
End Set
|
||||
End Property
|
||||
Private _Progress As Double = -1
|
||||
Public Event ProgressChanged(NewProgress As Double, OldProgress As Double) Implements ILoadingTrigger.ProgressChanged
|
||||
''' <summary>
|
||||
''' 计算总进度时的权重。它应该为预计时间(秒)。
|
||||
''' </summary>
|
||||
@@ -236,12 +240,16 @@
|
||||
''' </summary>
|
||||
Public ReadOnly Property IsAborted As Boolean
|
||||
Get
|
||||
Return LastRunningThread Is Nothing OrElse
|
||||
Not ReferenceEquals(Thread.CurrentThread, LastRunningThread) OrElse
|
||||
State = LoadState.Aborted
|
||||
Return IsAbortedWithThread(Thread.CurrentThread)
|
||||
End Get
|
||||
End Property
|
||||
''' <summary>
|
||||
''' 当前执行线程是否应当中断。需要手动提供加载器线程,用于需要跨线程检查的情况。
|
||||
''' </summary>
|
||||
Public Function IsAbortedWithThread(Thread As Thread) As Boolean
|
||||
Return LastRunningThread Is Nothing OrElse Not ReferenceEquals(Thread, LastRunningThread) OrElse State = LoadState.Aborted
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 在输入相同时使用原有结果的超时,单位为毫秒。
|
||||
''' </summary>
|
||||
Public ReloadTimeout As Integer = -1
|
||||
@@ -437,10 +445,6 @@
|
||||
For Each Loader In Loaders
|
||||
Loader.Abort()
|
||||
Next
|
||||
'从列表删除
|
||||
SyncLock LoaderTaskbarLock
|
||||
If LoaderTaskbar.Contains(Me) Then LoaderTaskbar.Remove(Me)
|
||||
End SyncLock
|
||||
End Sub)
|
||||
End Sub
|
||||
|
||||
@@ -486,7 +490,7 @@
|
||||
Select Case Loader.State
|
||||
Case LoadState.Finished
|
||||
'检查是否需要重启
|
||||
If Loader.GetType.Name.StartsWith("LoaderTask") Then '类型名后面带有泛型,必须用 StartsWith
|
||||
If Loader.GetType.Name.StartsWithF("LoaderTask") Then '类型名后面带有泛型,必须用 StartsWith
|
||||
If CType(Loader, Object).ShouldStart(If(Input IsNot Nothing AndAlso Loader.GetType.GenericTypeArguments.First Is Input.GetType, Input, Nothing), IgnoreReloadTimeout:=True) Then
|
||||
If ModeDebug Then Log("[Loader] 由于输入条件变更,重启已完成的加载器 " & Loader.Name)
|
||||
GoTo Restart
|
||||
@@ -496,7 +500,7 @@
|
||||
End If
|
||||
Case LoadState.Loading
|
||||
'检查是否需要重启
|
||||
If Loader.GetType.Name.StartsWith("LoaderTask") Then
|
||||
If Loader.GetType.Name.StartsWithF("LoaderTask") Then
|
||||
If CType(Loader, Object).ShouldStart(If(Input IsNot Nothing AndAlso Loader.GetType.GenericTypeArguments.First Is Input.GetType, Input, Nothing), IgnoreReloadTimeout:=True) Then
|
||||
If ModeDebug Then Log("[Loader] 由于输入条件变更,重启进行中的加载器 " & Loader.Name, LogLevel.Developer)
|
||||
GoTo Restart
|
||||
@@ -513,9 +517,9 @@ Restart:
|
||||
If Input IsNot Nothing Then
|
||||
'若输入类型与下一个加载器相同才继续
|
||||
Dim LoaderType As String = Loader.GetType.Name
|
||||
If LoaderType.StartsWith("LoaderTask") OrElse LoaderType.StartsWith("LoaderCombo") Then
|
||||
If LoaderType.StartsWithF("LoaderTask") OrElse LoaderType.StartsWithF("LoaderCombo") Then
|
||||
Loader.Start(If(Loader.GetType.GenericTypeArguments.First Is Input.GetType, Input, Nothing), IsForceRestarting)
|
||||
ElseIf LoaderType.StartsWith("LoaderDownload") Then
|
||||
ElseIf LoaderType.StartsWithF("LoaderDownload") Then
|
||||
Loader.Start(If(TypeOf Input Is List(Of NetFile), Input, Nothing), IsForceRestarting)
|
||||
Else
|
||||
Throw New Exception("未知的加载器类型(" & LoaderType & ")")
|
||||
@@ -541,7 +545,7 @@ Restart:
|
||||
Public Shared Sub GetLoaderList(Loader As Object, ByRef List As List(Of LoaderBase), Optional RequireShow As Boolean = True)
|
||||
For Each SubLoader In Loader.Loaders
|
||||
If SubLoader.Show OrElse Not RequireShow Then List.Add(SubLoader)
|
||||
If SubLoader.GetType.Name.StartsWith("LoaderCombo") Then GetLoaderList(SubLoader, List)
|
||||
If SubLoader.GetType.Name.StartsWithF("LoaderCombo") Then GetLoaderList(SubLoader, List)
|
||||
Next
|
||||
End Sub
|
||||
''' <summary>
|
||||
@@ -567,10 +571,11 @@ Restart:
|
||||
Public LoaderTaskbarProgress As Double = 0 '平滑后的进度
|
||||
Private LoaderTaskbarProgressLast As Shell.TaskbarItemProgressState = Shell.TaskbarItemProgressState.None
|
||||
|
||||
Public Sub LoaderTaskbarAdd(Loader As LoaderBase)
|
||||
Public Sub LoaderTaskbarAdd(Of T)(Loader As LoaderCombo(Of T))
|
||||
If FrmSpeedLeft IsNot Nothing Then FrmSpeedLeft.TaskRemove(Loader)
|
||||
SyncLock LoaderTaskbarLock
|
||||
LoaderTaskbar.Add(Loader)
|
||||
Log($"[Taskbar] {Loader.Name} 已加入任务列表")
|
||||
End SyncLock
|
||||
End Sub
|
||||
Public Sub LoaderTaskbarProgressRefresh()
|
||||
@@ -582,19 +587,16 @@ Restart:
|
||||
'外显任务是否已经全部完成
|
||||
Dim IsAllDownloadTaskCompleted As Boolean = True
|
||||
For Each Loader In LoaderTaskbar
|
||||
If Loader.Show AndAlso Loader.State = LoadState.Loading Then IsAllDownloadTaskCompleted = False
|
||||
If Loader.State = LoadState.Loading Then IsAllDownloadTaskCompleted = False
|
||||
Next
|
||||
'若单个任务已中止或全部任务已完成,则刷新并移除
|
||||
Dim RemovedTask As New List(Of LoaderBase)
|
||||
For i = 0 To LoaderTaskbar.Count - 1
|
||||
If (IsAllDownloadTaskCompleted OrElse LoaderTaskbar(i).State = LoadState.Aborted) AndAlso LoaderTaskbar(i).Show Then
|
||||
If FrmSpeedLeft IsNot Nothing Then FrmSpeedLeft.TaskRefresh(LoaderTaskbar(i))
|
||||
RemovedTask.Add(LoaderTaskbar(i))
|
||||
For Each Task In LoaderTaskbar.ToList()
|
||||
If IsAllDownloadTaskCompleted OrElse Task.State = LoadState.Aborted OrElse Task.State = LoadState.Waiting Then
|
||||
If FrmSpeedLeft IsNot Nothing Then FrmSpeedLeft.TaskRefresh(Task)
|
||||
LoaderTaskbar.Remove(Task)
|
||||
Log($"[Taskbar] {Task.Name} 已移出任务列表")
|
||||
End If
|
||||
Next
|
||||
For Each Task In RemovedTask
|
||||
LoaderTaskbar.Remove(Task)
|
||||
Next
|
||||
End SyncLock
|
||||
'更新平滑后的进度
|
||||
If NewProgress <= 0 OrElse NewProgress >= 1 OrElse LoaderTaskbarProgress > NewProgress Then
|
||||
@@ -602,8 +604,9 @@ Restart:
|
||||
Else
|
||||
LoaderTaskbarProgress = LoaderTaskbarProgress * 0.9 + NewProgress * 0.1
|
||||
End If
|
||||
RunInUi(Sub() FrmMain.BtnExtraDownload.Progress = LoaderTaskbarProgress)
|
||||
'更新任务栏信息
|
||||
If LoaderTaskbar.Count = 0 OrElse LoaderTaskbarProgress = 1 Then
|
||||
If Not LoaderTaskbar.Any() OrElse LoaderTaskbarProgress = 1 Then
|
||||
NewState = Shell.TaskbarItemProgressState.None
|
||||
ElseIf LoaderTaskbarProgress < 0.015 Then
|
||||
NewState = Shell.TaskbarItemProgressState.Indeterminate
|
||||
@@ -624,13 +627,13 @@ Restart:
|
||||
Try
|
||||
Dim Total As Double = 0, Count As Integer = 0
|
||||
SyncLock LoaderTaskbarLock
|
||||
For i = 0 To LoaderTaskbar.Count - 1
|
||||
Total += LoaderTaskbar(i).Progress
|
||||
For Each Task In LoaderTaskbar.ToList
|
||||
Total += Task.Progress
|
||||
Count += 1 '避免多线程影响导致计数出错
|
||||
Next
|
||||
End SyncLock
|
||||
If Count = 0 Then Return 1
|
||||
Return MathRange(Total / Count, 0, 1)
|
||||
Return MathClamp(Total / Count, 0, 1)
|
||||
Catch ex As Exception
|
||||
Log(ex, "获取任务栏进度出错", LogLevel.Feedback)
|
||||
Return 0.5
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -76,7 +76,7 @@ Public Class ValidateHttp
|
||||
Public Sub New()
|
||||
End Sub '用于 XAML 初始化
|
||||
Public Overrides Function Validate(Str As String) As String
|
||||
If Str.EndsWith("/") Then Str = Str.Substring(0, Str.Length - 1)
|
||||
If Str.EndsWithF("/") Then Str = Str.Substring(0, Str.Length - 1)
|
||||
If Not RegexCheck(Str, "^(http[s]?)\://") Then Return "输入的网址无效!"
|
||||
Return ""
|
||||
End Function
|
||||
@@ -149,7 +149,7 @@ Public Class ValidateExcept
|
||||
End Sub
|
||||
Public Overrides Function Validate(Str As String) As String
|
||||
For Each Ch As String In Excepts
|
||||
If Str.IndexOf(Ch, StringComparison.OrdinalIgnoreCase) >= 0 Then
|
||||
If Str.IndexOfF(Ch, StringComparison.OrdinalIgnoreCase) >= 0 Then
|
||||
If IsNothing(ErrorMessage) Then ErrorMessage = ""
|
||||
Return ErrorMessage.Replace("%", Ch)
|
||||
End If
|
||||
@@ -220,13 +220,13 @@ Public Class ValidateFolderName
|
||||
Dim LengthCheck As String = New ValidateNullOrWhiteSpace().Validate(Str)
|
||||
If Not LengthCheck = "" Then Return LengthCheck
|
||||
'检查空格
|
||||
If Str.StartsWith(" ") Then Return "文件夹名不能以空格开头!"
|
||||
If Str.EndsWith(" ") Then Return "文件夹名不能以空格结尾!"
|
||||
If Str.StartsWithF(" ") Then Return "文件夹名不能以空格开头!"
|
||||
If Str.EndsWithF(" ") Then Return "文件夹名不能以空格结尾!"
|
||||
'检查长度
|
||||
LengthCheck = New ValidateLength(1, 100).Validate(Str)
|
||||
If Not LengthCheck = "" Then Return LengthCheck
|
||||
'检查尾部小数点
|
||||
If Str.EndsWith(".") Then Return "文件夹名不能以小数点结尾!"
|
||||
If Str.EndsWithF(".") Then Return "文件夹名不能以小数点结尾!"
|
||||
'检查特殊字符
|
||||
Dim CharactCheck As String = New ValidateExcept(IO.Path.GetInvalidFileNameChars() & If(UseMinecraftCharCheck, "!;", ""), "文件夹名不可包含 % 字符!").Validate(Str)
|
||||
If Not CharactCheck = "" Then Return CharactCheck
|
||||
@@ -273,13 +273,13 @@ Public Class ValidateFileName
|
||||
Dim LengthCheck As String = New ValidateNullOrWhiteSpace().Validate(Str)
|
||||
If Not LengthCheck = "" Then Return LengthCheck
|
||||
'检查空格
|
||||
If Str.StartsWith(" ") Then Return "文件名不能以空格开头!"
|
||||
If Str.EndsWith(" ") Then Return "文件名不能以空格结尾!"
|
||||
If Str.StartsWithF(" ") Then Return "文件名不能以空格开头!"
|
||||
If Str.EndsWithF(" ") Then Return "文件名不能以空格结尾!"
|
||||
'检查长度
|
||||
LengthCheck = New ValidateLength(1, 253).Validate(Str & If(ParentFolder, ""))
|
||||
If Not LengthCheck = "" Then Return LengthCheck
|
||||
'检查尾部小数点
|
||||
If Str.EndsWith(".") Then Return "文件名不能以小数点结尾!"
|
||||
If Str.EndsWithF(".") Then Return "文件名不能以小数点结尾!"
|
||||
'检查特殊字符
|
||||
Dim CharactCheck As String = New ValidateExcept(IO.Path.GetInvalidFileNameChars() & If(UseMinecraftCharCheck, "!;", ""), "文件名不可包含 % 字符!").Validate(Str)
|
||||
If Not CharactCheck = "" Then Return CharactCheck
|
||||
@@ -319,7 +319,7 @@ Public Class ValidateFolderPath
|
||||
Public Overrides Function Validate(Str As String) As String
|
||||
'去除尾部斜线,统一为 \
|
||||
Str = Str.Replace("/", "\")
|
||||
If Not Str.TrimEnd("\").EndsWith(":") Then Str = Str.TrimEnd("\")
|
||||
If Not Str.TrimEnd("\").EndsWithF(":") Then Str = Str.TrimEnd("\")
|
||||
'检查是否为空
|
||||
Dim LengthCheck As String = New ValidateNullOrWhiteSpace().Validate(Str)
|
||||
If Not LengthCheck = "" Then Return LengthCheck
|
||||
@@ -327,15 +327,15 @@ Public Class ValidateFolderPath
|
||||
LengthCheck = New ValidateLength(1, 254).Validate(Str)
|
||||
If Not LengthCheck = "" Then Return LengthCheck
|
||||
'检查开头
|
||||
If Str.StartsWith("\\Mac\") Then GoTo Fin
|
||||
If Str.StartsWithF("\\Mac\") Then GoTo Fin
|
||||
For Each Drive As DriveInfo In My.Computer.FileSystem.Drives
|
||||
If Str.ToUpper = Drive.Name Then Return ""
|
||||
If Str.ToUpper.StartsWith(Drive.Name) Then GoTo Fin
|
||||
If Str.StartsWithF(Drive.Name, True) Then GoTo Fin
|
||||
Next
|
||||
Return "文件夹路径头存在错误!"
|
||||
Fin:
|
||||
'对首层以外的路径检查
|
||||
For i = If(Str.StartsWith("\\Mac\"), 2, 1) To Str.Split("\").Count - 1
|
||||
For i = If(Str.StartsWithF("\\Mac\"), 2, 1) To Str.Split("\").Count - 1
|
||||
Dim SubStr As String = Str.Split("\")(i)
|
||||
'检查是否为空
|
||||
Dim SubLengthCheck As String = New ValidateNullOrWhiteSpace().Validate(SubStr)
|
||||
@@ -344,10 +344,10 @@ Fin:
|
||||
Dim CharactCheck As String = New ValidateExcept(IO.Path.GetInvalidFileNameChars() & If(UseMinecraftCharCheck, "!;", ""), "路径中存在无效字符!").Validate(SubStr)
|
||||
If Not CharactCheck = "" Then Return CharactCheck
|
||||
'检查头部空格
|
||||
If SubStr.StartsWith(" ") Then Return "文件夹名不能以空格开头!"
|
||||
If SubStr.EndsWith(" ") Then Return "文件夹名不能以空格结尾!"
|
||||
If SubStr.StartsWithF(" ") Then Return "文件夹名不能以空格开头!"
|
||||
If SubStr.EndsWithF(" ") Then Return "文件夹名不能以空格结尾!"
|
||||
'检查尾部小数点
|
||||
If SubStr.EndsWith(".") Then Return "文件夹名不能以小数点结尾!"
|
||||
If SubStr.EndsWithF(".") Then Return "文件夹名不能以小数点结尾!"
|
||||
'检查特殊字符串
|
||||
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
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
'一个万能的自动图片类型转换工具类
|
||||
|
||||
Imports System.Drawing.Imaging
|
||||
|
||||
Public Class MyBitmap
|
||||
|
||||
''' <summary>
|
||||
@@ -26,10 +28,10 @@ Public Class MyBitmap
|
||||
Public Shared Widening Operator CType(Image As MyBitmap) As ImageSource
|
||||
Dim Bitmap = Image.Pic
|
||||
Dim rect = New System.Drawing.Rectangle(0, 0, Bitmap.Width, Bitmap.Height)
|
||||
Dim bitmapData = Bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb)
|
||||
Dim bitmapData = Bitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb)
|
||||
Try
|
||||
Dim size = rect.Width * rect.Height * 4
|
||||
Return BitmapSource.Create(Bitmap.Width, Bitmap.Height, DPI, DPI, PixelFormats.Bgra32, Nothing, bitmapData.Scan0, size, bitmapData.Stride)
|
||||
Return BitmapSource.Create(Bitmap.Width, Bitmap.Height, Bitmap.HorizontalResolution, Bitmap.VerticalResolution, PixelFormats.Bgra32, Nothing, bitmapData.Scan0, size, bitmapData.Stride)
|
||||
Finally
|
||||
Bitmap.UnlockBits(bitmapData)
|
||||
End Try
|
||||
@@ -52,7 +54,8 @@ Public Class MyBitmap
|
||||
End Sub
|
||||
Public Sub New(FilePathOrResourceName As String)
|
||||
Try
|
||||
If FilePathOrResourceName.StartsWith(PathImage) Then
|
||||
FilePathOrResourceName = FilePathOrResourceName.Replace("pack://application:,,,/images/", PathImage)
|
||||
If FilePathOrResourceName.StartsWithF(PathImage) Then
|
||||
'使用缓存
|
||||
If BitmapCache.ContainsKey(FilePathOrResourceName) Then
|
||||
Pic = BitmapCache(FilePathOrResourceName).Pic
|
||||
@@ -71,10 +74,10 @@ Public Class MyBitmap
|
||||
Pic = My.Application.TryFindResource(FilePathOrResourceName)
|
||||
If Pic Is Nothing Then
|
||||
Pic = New System.Drawing.Bitmap(1, 1)
|
||||
Log(ex, "加载位图失败(" & FilePathOrResourceName & ")")
|
||||
Log(ex, $"加载位图失败({FilePathOrResourceName})")
|
||||
Throw
|
||||
Else
|
||||
Log(ex, "指定类型有误的位图加载(" & FilePathOrResourceName & ")", LogLevel.Developer)
|
||||
Log(ex, $"指定类型有误的位图加载({FilePathOrResourceName})", LogLevel.Developer)
|
||||
Exit Try
|
||||
End If
|
||||
End Try
|
||||
@@ -102,41 +105,27 @@ Public Class MyBitmap
|
||||
End Using
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 获取旋转的图片,这个方法不会导致原对象改变且会返回一个新的对象。图片长宽必须相等。
|
||||
''' </summary>
|
||||
''' <param name="angle">旋转角度(单位为角度)。</param>
|
||||
Public Function Rotation(angle As Double) As System.Drawing.Bitmap
|
||||
Dim img As System.Drawing.Image = Me.Pic
|
||||
Dim bitSize As Single = img.Width
|
||||
Dim bmp As New System.Drawing.Bitmap(CInt(bitSize), CInt(bitSize))
|
||||
Using g As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmp)
|
||||
g.TranslateTransform(bitSize / 2, bitSize / 2)
|
||||
g.RotateTransform(angle)
|
||||
g.TranslateTransform(-bitSize / 2, -bitSize / 2)
|
||||
g.DrawImage(img, New System.Drawing.Rectangle(0, 0, img.Width, img.Width))
|
||||
End Using
|
||||
Return bmp
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 获取裁切的图片,这个方法不会导致原对象改变且会返回一个新的对象。
|
||||
''' </summary>
|
||||
Public Function Clip(rect As System.Drawing.Rectangle) As System.Drawing.Bitmap
|
||||
Dim img As System.Drawing.Image = Pic
|
||||
Dim bmp As New System.Drawing.Bitmap(rect.Width, rect.Height)
|
||||
Public Function Clip(X As Integer, Y As Integer, Width As Integer, Height As Integer) As MyBitmap
|
||||
Dim bmp As New System.Drawing.Bitmap(Width, Height, Pic.PixelFormat)
|
||||
bmp.SetResolution(Pic.HorizontalResolution, Pic.VerticalResolution)
|
||||
Using g As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmp)
|
||||
g.DrawImageUnscaled(img, rect)
|
||||
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor
|
||||
g.TranslateTransform(-X, -Y)
|
||||
g.DrawImage(Pic, New System.Drawing.Rectangle(0, 0, Pic.Width, Pic.Height))
|
||||
End Using
|
||||
Return bmp
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 获取左右翻转的图片,这个方法不会导致原对象改变。
|
||||
''' 获取旋转或翻转后的图片,这个方法不会导致原对象改变且会返回一个新的对象。
|
||||
''' </summary>
|
||||
Public Function LeftRightFilp() As System.Drawing.Bitmap
|
||||
Public Function RotateFlip(Type As System.Drawing.RotateFlipType) As MyBitmap
|
||||
Dim bmp As New System.Drawing.Bitmap(Pic)
|
||||
bmp.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipX)
|
||||
bmp.SetResolution(Pic.HorizontalResolution, Pic.VerticalResolution)
|
||||
bmp.RotateFlip(Type)
|
||||
Return bmp
|
||||
End Function
|
||||
|
||||
@@ -146,7 +135,7 @@ Public Class MyBitmap
|
||||
Public Sub Save(FilePath As String)
|
||||
Dim encoder As BitmapEncoder = New PngBitmapEncoder()
|
||||
encoder.Frames.Add(BitmapFrame.Create(Me))
|
||||
Using fileStream = New System.IO.FileStream(FilePath, FileMode.Create)
|
||||
Using fileStream As New FileStream(FilePath, FileMode.Create)
|
||||
encoder.Save(fileStream)
|
||||
End Using
|
||||
End Sub
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -80,7 +80,7 @@
|
||||
Log(ex, "确认崩溃日志时间失败(" & LogFile & ")")
|
||||
End Try
|
||||
Next
|
||||
If RightLogs.Count = 0 Then Log("[Crash] 未发现可能可用的日志文件")
|
||||
If Not RightLogs.Any() Then Log("[Crash] 未发现可能可用的日志文件")
|
||||
|
||||
'将可能可用的日志文件导出
|
||||
For Each FilePath In RightLogs
|
||||
@@ -94,7 +94,7 @@
|
||||
Log(ex, "读取可能的崩溃日志文件失败(" & FilePath & ")")
|
||||
End Try
|
||||
Next
|
||||
If LatestLog IsNot Nothing AndAlso LatestLog.Count > 0 Then
|
||||
If LatestLog IsNot Nothing AndAlso LatestLog.Any Then
|
||||
Dim RawOutput As String = Join(LatestLog, vbCrLf)
|
||||
Log("[Crash] 以下为游戏输出的最后一段内容:" & vbCrLf & RawOutput)
|
||||
WriteFile(TempFolder & "RawOutput.log", RawOutput)
|
||||
@@ -110,33 +110,34 @@
|
||||
Public Sub Import(FilePath As String)
|
||||
Log("[Crash] 步骤 1:自主导入日志文件")
|
||||
|
||||
'解压压缩包
|
||||
'尝试视作压缩包解压
|
||||
Try
|
||||
Dim Info As New FileInfo(FilePath)
|
||||
If Not Info.Exists OrElse Info.Length = 0 Then Exit Try
|
||||
If Not FilePath.ToLower.EndsWith(".jar") AndAlso ExtractFile(FilePath, TempFolder & "Temp\") Then
|
||||
'解压成功
|
||||
If Info.Exists AndAlso Info.Length > 0 AndAlso Not FilePath.EndsWithF(".jar", True) Then
|
||||
ExtractFile(FilePath, TempFolder & "Temp\")
|
||||
Log("[Crash] 已解压导入的日志文件:" & FilePath)
|
||||
Else
|
||||
'解压失败
|
||||
CopyFile(FilePath, TempFolder & "Temp\" & GetFileNameFromPath(FilePath))
|
||||
Log("[Crash] 已复制导入的日志文件:" & FilePath)
|
||||
GoTo Extracted
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "解压导入文件中的压缩包失败")
|
||||
Catch
|
||||
End Try
|
||||
'并非压缩包
|
||||
CopyFile(FilePath, TempFolder & "Temp\" & GetFileNameFromPath(FilePath))
|
||||
Log("[Crash] 已复制导入的日志文件:" & FilePath)
|
||||
Extracted:
|
||||
|
||||
'导入其中的日志文件
|
||||
For Each TargetFile As FileInfo In New DirectoryInfo(TempFolder & "Temp\").EnumerateFiles
|
||||
For Each TargetFile As FileInfo In New DirectoryInfo(TempFolder & "Temp\").EnumerateFiles.ToList()
|
||||
Try
|
||||
If Not TargetFile.Exists OrElse TargetFile.Length = 0 Then Continue For
|
||||
Dim Ext As String = TargetFile.Extension.ToLower
|
||||
If Ext = ".log" OrElse Ext = ".txt" Then
|
||||
If TargetFile.Name.StartsWith("crash-") Then
|
||||
If TargetFile.Name.StartsWithF("crash-") Then
|
||||
AnalyzeRawFiles.Add(New KeyValuePair(Of String, String())(TargetFile.FullName, ReadFile(TargetFile.FullName).Split(vbCrLf.ToCharArray)))
|
||||
Else
|
||||
AnalyzeRawFiles.Add(New KeyValuePair(Of String, String())(TargetFile.FullName, ReadFile(TargetFile.FullName, Encoding.UTF8).Split(vbCrLf.ToCharArray)))
|
||||
End If
|
||||
Else
|
||||
File.Delete(TargetFile.FullName)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "导入单个日志文件失败")
|
||||
@@ -165,37 +166,37 @@
|
||||
DirectFile = Nothing
|
||||
Dim TotalFiles As New List(Of KeyValuePair(Of AnalyzeFileType, KeyValuePair(Of String, String())))
|
||||
For Each LogFile In AnalyzeRawFiles
|
||||
Dim FileName As String = GetFileNameFromPath(LogFile.Key)
|
||||
Dim MatchName As String = GetFileNameFromPath(LogFile.Key).ToLower
|
||||
Dim TargetType As AnalyzeFileType
|
||||
If FileName.StartsWith("hs_err") Then
|
||||
If MatchName.StartsWithF("hs_err") Then
|
||||
TargetType = AnalyzeFileType.HsErr
|
||||
DirectFile = LogFile
|
||||
ElseIf FileName.StartsWith("crash-") Then
|
||||
ElseIf MatchName.StartsWithF("crash-") Then
|
||||
TargetType = AnalyzeFileType.CrashReport
|
||||
DirectFile = LogFile
|
||||
ElseIf FileName = "latest.log" OrElse FileName = "latest log.txt" OrElse
|
||||
FileName = "debug.log" OrElse FileName = "debug log.txt" OrElse
|
||||
FileName = "游戏崩溃前的输出.txt" OrElse FileName = "rawoutput.log" Then
|
||||
ElseIf MatchName = "latest.log" OrElse MatchName = "latest log.txt" OrElse
|
||||
MatchName = "debug.log" OrElse MatchName = "debug log.txt" OrElse
|
||||
MatchName = "游戏崩溃前的输出.txt" OrElse MatchName = "rawoutput.log" Then
|
||||
TargetType = AnalyzeFileType.MinecraftLog
|
||||
If DirectFile Is Nothing Then DirectFile = LogFile
|
||||
ElseIf FileName = "启动器日志.txt" OrElse FileName = "PCL2 启动器日志.txt" OrElse FileName = "PCL 启动器日志.txt" OrElse FileName = "log1.txt" Then
|
||||
ElseIf MatchName = "启动器日志.txt" OrElse MatchName = "PCL2 启动器日志.txt" OrElse MatchName = "PCL 启动器日志.txt" OrElse MatchName = "log1.txt" Then
|
||||
If LogFile.Value.Any(Function(s) s.Contains("以下为游戏输出的最后一段内容")) Then
|
||||
TargetType = AnalyzeFileType.MinecraftLog
|
||||
If DirectFile Is Nothing Then DirectFile = LogFile
|
||||
Else
|
||||
TargetType = AnalyzeFileType.ExtraLog
|
||||
End If
|
||||
ElseIf FileName.EndsWith(".log") OrElse FileName.EndsWith(".txt") Then
|
||||
ElseIf MatchName.EndsWithF(".log", True) OrElse MatchName.EndsWithF(".txt", True) Then
|
||||
TargetType = AnalyzeFileType.ExtraLog
|
||||
Else
|
||||
Log("[Crash] " & FileName & " 分类为 Ignore")
|
||||
Log("[Crash] " & MatchName & " 分类为 Ignore")
|
||||
Continue For
|
||||
End If
|
||||
If LogFile.Value.Count = 0 Then
|
||||
Log("[Crash] " & FileName & " 由于内容为空跳过")
|
||||
If Not LogFile.Value.Any() Then
|
||||
Log("[Crash] " & MatchName & " 由于内容为空跳过")
|
||||
Else
|
||||
TotalFiles.Add(New KeyValuePair(Of AnalyzeFileType, KeyValuePair(Of String, String()))(TargetType, LogFile))
|
||||
Log("[Crash] " & FileName & " 分类为 " & GetStringFromEnum(TargetType))
|
||||
Log("[Crash] " & MatchName & " 分类为 " & GetStringFromEnum(TargetType))
|
||||
End If
|
||||
Next
|
||||
|
||||
@@ -206,7 +207,7 @@
|
||||
For Each File In TotalFiles
|
||||
If SelectType = File.Key Then SelectedFiles.Add(File.Value)
|
||||
Next
|
||||
If SelectedFiles.Count = 0 Then Continue For
|
||||
If Not SelectedFiles.Any() Then Continue For
|
||||
Try
|
||||
'根据文件类别判断
|
||||
Select Case SelectType
|
||||
@@ -235,6 +236,7 @@
|
||||
End If
|
||||
Case AnalyzeFileType.MinecraftLog
|
||||
LogMc = ""
|
||||
LogMcDebug = ""
|
||||
'创建文件名词典
|
||||
Dim FileNameDict As New Dictionary(Of String, KeyValuePair(Of String, String()))
|
||||
For Each SelectedFile In SelectedFiles
|
||||
@@ -272,11 +274,21 @@
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
'查找 Debug Log
|
||||
For Each FileName As String In {"debug.log", "debug log.txt"}
|
||||
If FileNameDict.ContainsKey(FileName) Then
|
||||
Dim CurrentLog = FileNameDict(FileName)
|
||||
LogMcDebug += GetHeadTailLines(CurrentLog.Value, 1000, 0)
|
||||
Log("[Crash] 导入分析:" & CurrentLog.Key & ",作为 Minecraft Debug 日志")
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
'检查错误
|
||||
If LogMc = "" Then
|
||||
LogMc = Nothing
|
||||
Throw New Exception("无法找到匹配的 Minecraft Log")
|
||||
End If
|
||||
If LogMcDebug = "" Then LogMcDebug = Nothing
|
||||
Case AnalyzeFileType.ExtraLog
|
||||
'全部丢过去
|
||||
For Each SelectedFile In SelectedFiles
|
||||
@@ -294,7 +306,7 @@
|
||||
If ResultCount = 0 Then
|
||||
Log("[Crash] 步骤 2:准备日志文本完成,没有任何可供分析的日志")
|
||||
Else
|
||||
Log(("[Crash] 步骤 2:准备日志文本完成,找到" & If(LogMc Is Nothing, "", "游戏日志、") & If(LogHs Is Nothing, "", "虚拟机日志、") & If(LogCrash Is Nothing, "", "崩溃日志、")).TrimEnd("、") & "用作分析")
|
||||
Log(("[Crash] 步骤 2:准备日志文本完成,找到" & If(LogMc Is Nothing, "", "游戏日志、") & If(LogMcDebug Is Nothing, "", "游戏 Debug 日志、") & If(LogHs Is Nothing, "", "虚拟机日志、") & If(LogCrash Is Nothing, "", "崩溃日志、")).TrimEnd("、") & "用作分析")
|
||||
End If
|
||||
Return ResultCount
|
||||
|
||||
@@ -315,7 +327,7 @@
|
||||
End Function
|
||||
|
||||
'3:根据文本分析崩溃原因
|
||||
Private LogMc As String = Nothing, LogHs As String = Nothing, LogCrash As String = Nothing
|
||||
Private LogMc As String = Nothing, LogMcDebug As String = Nothing, LogHs As String = Nothing, LogCrash As String = Nothing
|
||||
Private LogAll As String
|
||||
'可能导致崩溃的原因与附加信息
|
||||
Private CrashReasons As New Dictionary(Of CrashReason, List(Of String))
|
||||
@@ -324,12 +336,14 @@
|
||||
''' </summary>
|
||||
Private Enum CrashReason
|
||||
Mod文件被解压
|
||||
MixinBootstrap缺失
|
||||
内存不足
|
||||
使用JDK
|
||||
显卡不支持OpenGL
|
||||
使用OpenJ9
|
||||
Java版本过高
|
||||
Java版本不兼容
|
||||
Mod名称包含特殊字符
|
||||
显卡驱动不支持导致无法设置像素格式
|
||||
极短的程序输出
|
||||
Intel驱动不兼容导致EXCEPTION_ACCESS_VIOLATION 'https://bugs.mojang.com/browse/MC-32606
|
||||
@@ -343,9 +357,8 @@
|
||||
ModMixin失败
|
||||
Mod加载器报错
|
||||
Mod初始化失败
|
||||
崩溃日志堆栈分析发现关键字
|
||||
崩溃日志堆栈分析发现Mod名称
|
||||
MC日志堆栈分析发现关键字
|
||||
堆栈分析发现关键字
|
||||
堆栈分析发现Mod名称
|
||||
OptiFine导致无法加载世界 'https://www.minecraftforum.net/forums/support/java-edition-support/3051132-exception-ticking-world
|
||||
特定方块导致崩溃
|
||||
特定实体导致崩溃
|
||||
@@ -360,65 +373,75 @@
|
||||
低版本Forge与高版本Java不兼容
|
||||
版本Json中存在多个Forge
|
||||
Mod过多导致超出ID限制
|
||||
NightConfig的Bug
|
||||
ShadersMod与Optifine同时安装
|
||||
Forge安装不完整
|
||||
Mod需要Java11
|
||||
Mod缺少前置或MC版本错误
|
||||
End Enum
|
||||
''' <summary>
|
||||
''' 根据 AnalyzeLogs 与可能的版本信息分析崩溃原因。
|
||||
''' </summary>
|
||||
Public Sub Analyze(Optional Version As McVersion = Nothing)
|
||||
Log("[Crash] 步骤 3:分析崩溃原因")
|
||||
LogAll = (If(LogMc, "") & If(LogHs, "") & If(LogCrash, "")).ToLower
|
||||
LogAll = (If(LogMc, "") & If(LogMcDebug, "") & If(LogHs, "") & If(LogCrash, ""))
|
||||
|
||||
'1. 精准日志匹配 1
|
||||
'1. 精准日志匹配,中/高优先级
|
||||
AnalyzeCrit1()
|
||||
If CrashReasons.Count > 0 Then GoTo Done
|
||||
If CrashReasons.Any Then GoTo Done
|
||||
AnalyzeCrit2()
|
||||
If CrashReasons.Any Then GoTo Done
|
||||
|
||||
'2. 堆栈分析
|
||||
If LogAll.Contains("forge") OrElse LogAll.Contains("fabric") OrElse LogAll.Contains("liteloader") Then
|
||||
'来源崩溃日志的分析
|
||||
If LogAll.Contains("orge") OrElse LogAll.Contains("abric") OrElse LogAll.Contains("uilt") OrElse LogAll.Contains("iteloader") Then
|
||||
Dim Keywords As New List(Of String)
|
||||
'崩溃日志
|
||||
If LogCrash IsNot Nothing Then
|
||||
Log("[Crash] 开始进行崩溃日志堆栈分析")
|
||||
Dim StackLogs As String = LogCrash.Split("System Details").First
|
||||
Dim Keywords = AnalyzeStackKeyword(StackLogs)
|
||||
If Keywords.Count > 0 Then
|
||||
Dim Names = AnalyzeModName(Keywords)
|
||||
If Names Is Nothing Then
|
||||
AppendReason(CrashReason.崩溃日志堆栈分析发现关键字, Keywords)
|
||||
Else
|
||||
AppendReason(CrashReason.崩溃日志堆栈分析发现Mod名称, Names)
|
||||
End If
|
||||
GoTo Done
|
||||
End If
|
||||
Keywords.AddRange(AnalyzeStackKeyword(StackLogs))
|
||||
End If
|
||||
'来自 Minecraft 日志的分析
|
||||
'Minecraft 日志
|
||||
If LogMc IsNot Nothing Then
|
||||
Dim Fatals = RegexSearch(LogMc, "/FATAL] [\w\W]+?(?=[\n]+\[)")
|
||||
Log("[Crash] 开始进行 Minecraft 日志堆栈分析,发现 " & Fatals.Count & " 个报错项")
|
||||
If Fatals.Count > 0 Then
|
||||
Dim Keywords As New List(Of String)
|
||||
If Fatals.Any Then
|
||||
For Each Fatal In Fatals
|
||||
Keywords.AddRange(AnalyzeStackKeyword(Fatal))
|
||||
Next
|
||||
If Keywords.Count > 0 Then
|
||||
AppendReason(CrashReason.MC日志堆栈分析发现关键字, Keywords.Distinct.ToList)
|
||||
GoTo Done
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
'虚拟机日志
|
||||
If LogHs IsNot Nothing Then
|
||||
Log("[Crash] 开始进行虚拟机堆栈分析")
|
||||
Dim StackLogs As String = LogHs.Split("Registers:").First.Split("T H R E A D").Last
|
||||
Keywords.AddRange(AnalyzeStackKeyword(StackLogs))
|
||||
End If
|
||||
'Mod 名称分析
|
||||
If Keywords.Any Then
|
||||
Dim Names = AnalyzeModName(Keywords)
|
||||
If Names Is Nothing Then
|
||||
AppendReason(CrashReason.堆栈分析发现关键字, Keywords)
|
||||
Else
|
||||
AppendReason(CrashReason.堆栈分析发现Mod名称, Names)
|
||||
End If
|
||||
GoTo Done
|
||||
End If
|
||||
Else
|
||||
Log("[Crash] 可能并未安装 Mod,不进行堆栈分析")
|
||||
End If
|
||||
|
||||
'3. 精准日志匹配 2
|
||||
AnalyzeCrit2()
|
||||
'3. 精准日志匹配,低优先级
|
||||
AnalyzeCrit3()
|
||||
|
||||
'输出到日志
|
||||
Done:
|
||||
If CrashReasons.Count = 0 Then
|
||||
If Not CrashReasons.Any() Then
|
||||
Log("[Crash] 步骤 3:分析崩溃原因完成,未找到可能的原因")
|
||||
Else
|
||||
Log("[Crash] 步骤 3:分析崩溃原因完成,找到 " & CrashReasons.Count & " 条可能的原因")
|
||||
For Each Reason In CrashReasons
|
||||
Log("[Crash] - " & GetStringFromEnum(Reason.Key) & If(Reason.Value.Count > 0, "(" & Join(Reason.Value, ";") & ")", ""))
|
||||
Log("[Crash] - " & GetStringFromEnum(Reason.Key) & If(Reason.Value.Any, "(" & Join(Reason.Value, ";") & ")", ""))
|
||||
Next
|
||||
End If
|
||||
End Sub
|
||||
@@ -434,7 +457,7 @@ Done:
|
||||
Else
|
||||
CrashReasons.Add(Reason, New List(Of String)(If(Additional, {})))
|
||||
End If
|
||||
Log("[Crash] 可能的崩溃原因:" & GetStringFromEnum(Reason) & If(Additional IsNot Nothing AndAlso Additional.Count > 0, "(" & Join(Additional, ";") & ")", ""))
|
||||
Log("[Crash] 可能的崩溃原因:" & GetStringFromEnum(Reason) & If(Additional IsNot Nothing AndAlso Additional.Any, "(" & Join(Additional, ";") & ")", ""))
|
||||
End Sub
|
||||
Private Sub AppendReason(Reason As CrashReason, Additional As String)
|
||||
AppendReason(Reason, If(String.IsNullOrEmpty(Additional), Nothing, New List(Of String) From {Additional}))
|
||||
@@ -464,9 +487,12 @@ Done:
|
||||
If LogMc.Contains("java.lang.ClassCastException: java.base/jdk") Then AppendReason(CrashReason.使用JDK)
|
||||
If LogMc.Contains("java.lang.ClassCastException: class jdk.") Then AppendReason(CrashReason.使用JDK)
|
||||
If LogMc.Contains("TRANSFORMER/net.optifine/net.optifine.reflect.Reflector.<clinit>(Reflector.java") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraft.client.renderer.texture.SpriteContents.<init>(net.minecraft.resources.ResourceLocation, ") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraft.client.renderer.block.model.BakedQuad.<init>(int[], int, net.minecraft.core.Direction, net.minecraft.client.renderer.texture.TextureAtlasSprite, boolean, boolean)'") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraft.server.level.DistanceManager.addRegionTicket(net.minecraft.server.level.TicketType, net.minecraft.world.level.ChunkPos, int, java.lang.Object, boolean)'") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraft.client.renderer.texture.SpriteContents.<init>") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'java.lang.String com.mojang.blaze3d.systems.RenderSystem.getBackendDescription") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraft.client.renderer.block.model.BakedQuad.<init>") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraftforge.client.gui.overlay.ForgeGui.renderSelectedItemName") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraft.server.level.DistanceManager") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'net.minecraft.network.chat.FormattedText net.minecraft.client.gui.Font.ellipsize") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("Open J9 is not supported") OrElse LogMc.Contains("OpenJ9 is incompatible") OrElse LogMc.Contains(".J9VMInternals.") Then AppendReason(CrashReason.使用OpenJ9)
|
||||
If LogMc.Contains("java.lang.NoSuchFieldException: ucp") Then AppendReason(CrashReason.Java版本过高)
|
||||
If LogMc.Contains("because module java.base does not export") Then AppendReason(CrashReason.Java版本过高)
|
||||
@@ -474,15 +500,24 @@ Done:
|
||||
If LogMc.Contains("java.lang.ClassNotFoundException: java.lang.invoke.LambdaMetafactory") Then AppendReason(CrashReason.Java版本过高)
|
||||
If LogMc.Contains("The directories below appear to be extracted jar files. Fix this before you continue.") Then AppendReason(CrashReason.Mod文件被解压)
|
||||
If LogMc.Contains("Extracted mod jars found, loading will NOT continue") Then AppendReason(CrashReason.Mod文件被解压)
|
||||
If LogMc.Contains("java.lang.ClassNotFoundException: org.spongepowered.asm.launch.MixinTweaker") Then AppendReason(CrashReason.MixinBootstrap缺失)
|
||||
If LogMc.Contains("Couldn't set pixel format") Then AppendReason(CrashReason.显卡驱动不支持导致无法设置像素格式)
|
||||
If LogMc.Contains("java.lang.OutOfMemoryError") Then AppendReason(CrashReason.内存不足)
|
||||
If LogMc.Contains("java.lang.RuntimeException: Shaders Mod detected. Please remove it, OptiFine has built-in support for shaders.") Then AppendReason(CrashReason.ShadersMod与Optifine同时安装)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: sun.security.util.ManifestEntryVerifier") Then AppendReason(CrashReason.低版本Forge与高版本Java不兼容)
|
||||
If LogMc.Contains("1282: Invalid operation") Then AppendReason(CrashReason.光影或资源包导致OpenGL1282错误)
|
||||
If LogMc.Contains("signer information does not match signer information of other classes in the same package") Then AppendReason(CrashReason.文件或内容校验失败, If(RegexSeek(LogMc, "(?<=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("java.lang.UnsupportedClassVersionError: net/fabricmc/loader/impl/launch/knot/KnotClient : Unsupported major.minor version") Then AppendReason(CrashReason.Java版本不兼容)
|
||||
If LogMc.Contains("com.electronwill.nightconfig.core.io.ParsingException: Not enough data available") Then AppendReason(CrashReason.NightConfig的Bug)
|
||||
If LogMc.Contains("Cannot find launch target fmlclient, unable to launch") Then AppendReason(CrashReason.Forge安装不完整)
|
||||
If LogMc.Contains("Invalid module name: '' is not a Java identifier") Then AppendReason(CrashReason.Mod名称包含特殊字符)
|
||||
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 major.minor 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
|
||||
AppendReason(CrashReason.使用32位Java导致JVM无法分配足够多的内存)
|
||||
@@ -490,27 +525,17 @@ Done:
|
||||
AppendReason(CrashReason.内存不足)
|
||||
End If
|
||||
End If
|
||||
'确定的 Mod 导致崩溃
|
||||
If LogMc.Contains("Caught exception from ") Then AppendReason(CrashReason.确定Mod导致游戏崩溃, TryAnalyzeModName(RegexSeek(LogMc, "(?<=Caught exception from )[^\n]+?")?.TrimEnd((vbCrLf & " ").ToCharArray)))
|
||||
'Mod 重复安装
|
||||
If LogMc.Contains("DuplicateModsFoundException") Then AppendReason(CrashReason.Mod重复安装, RegexSearch(LogMc, "(?<=\n\t[\w]+ : [A-Z]{1}:[^\n]+(/|\\))[^/\\\n]+?.jar", RegularExpressions.RegexOptions.IgnoreCase))
|
||||
If LogMc.Contains("Found a duplicate mod") Then AppendReason(CrashReason.Mod重复安装, RegexSearch(If(RegexSeek(LogMc, "Found a duplicate mod[^\n]+"), ""), "[^\\/]+.jar", RegularExpressions.RegexOptions.IgnoreCase))
|
||||
If LogMc.Contains("ModResolutionException: Duplicate") Then AppendReason(CrashReason.Mod重复安装, RegexSearch(If(RegexSeek(LogMc, "ModResolutionException: Duplicate[^\n]+"), ""), "[^\\/]+.jar", RegularExpressions.RegexOptions.IgnoreCase))
|
||||
'Mod 导致的崩溃
|
||||
If LogMc.Contains("Mixin prepare failed ") OrElse LogMc.Contains("Mixin apply failed ") OrElse LogMc.Contains("mixin.injection.throwables.") OrElse LogMc.Contains(".mixins.json] FAILED during )") Then
|
||||
Dim ModId As String = RegexSeek(LogMc, "(?<=in )[^./ ]+(?=.mixins.json.+failed injection check)")
|
||||
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=in mixins.)[^./ ]+(?=.json.+failed injection check)")
|
||||
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= failed .+ in )[^./ ]+(?=.mixins.json)")
|
||||
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= failed .+ in mixins.)[^./ ]+(?=.json)")
|
||||
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= in config \[)[^./ ]+(?=.mixins.json\] FAILED during )")
|
||||
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= in config \[mixins.)[^./ ]+(?=.json\] FAILED during )")
|
||||
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= in callback )[^./ ]+(?=.mixins.json:)")
|
||||
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= in callback mixins.)[^./ ]+(?=.json:)")
|
||||
AppendReason(CrashReason.ModMixin失败, TryAnalyzeModName(If(ModId, "").TrimEnd((vbCrLf & " ").ToCharArray)))
|
||||
End If
|
||||
If LogMc.Contains("Caught exception from ") Then AppendReason(CrashReason.确定Mod导致游戏崩溃, TryAnalyzeModName(RegexSeek(LogMc, "(?<=Caught exception from )[^\n]+?")?.TrimEnd((vbCrLf & " ").ToCharArray)))
|
||||
'仅当没有其他分析结果时显示
|
||||
If CrashReasons.Count = 0 Then
|
||||
If LogMc.Contains("An exception was thrown, the game will display an error screen and halt.") Then AppendReason(CrashReason.Forge报错, RegexSeek(LogMc, "(?<=the game will display an error screen and halt.[\n\r]+[^\n]+?Exception: )[\s\S]+?(?=\n\tat)")?.Trim(vbCrLf))
|
||||
If LogMc.Contains("A potential solution has been determined:") Then AppendReason(CrashReason.Fabric报错并给出解决方案, Join(RegexSearch(If(RegexSeek(LogMc, "(?<=A potential solution has been determined:\n)((\t)+ - [^\n]+\n)+"), ""), "(?<=(\t)+)[^\n]+"), vbLf))
|
||||
'Mod 缺少前置
|
||||
If LogMc.Contains("Missing or unsupported mandatory dependencies:") Then
|
||||
AppendReason(CrashReason.Mod缺少前置或MC版本错误,
|
||||
RegexSearch(LogMc, "(?<=Missing or unsupported mandatory dependencies:)([\n\r]+\t(.*))+", RegularExpressions.RegexOptions.IgnoreCase).
|
||||
Select(Function(s) s.Trim((vbCrLf & vbTab & " ").ToCharArray)).Distinct().ToList())
|
||||
End If
|
||||
End If
|
||||
|
||||
@@ -541,21 +566,56 @@ Done:
|
||||
End If
|
||||
End If
|
||||
If LogCrash.Contains("Multiple entries with same key: ") Then AppendReason(CrashReason.确定Mod导致游戏崩溃, TryAnalyzeModName(If(RegexSeek(LogCrash, "(?<=Multiple entries with same key: )[^=]+"), "").TrimEnd((vbCrLf & " ").ToCharArray)))
|
||||
If LogCrash.Contains("due to errors, provided by ") Then AppendReason(CrashReason.确定Mod导致游戏崩溃, TryAnalyzeModName(If(RegexSeek(LogCrash, "(?<=due to errors, provided by ')[^']+"), "").TrimEnd((vbCrLf & " ").ToCharArray)))
|
||||
If LogCrash.Contains("LoaderExceptionModCrash: Caught exception from ") Then AppendReason(CrashReason.确定Mod导致游戏崩溃, TryAnalyzeModName(If(RegexSeek(LogCrash, "(?<=LoaderExceptionModCrash: Caught exception from )[^\n]+"), "").TrimEnd((vbCrLf & " ").ToCharArray)))
|
||||
If LogCrash.Contains("Failed loading config file ") Then AppendReason(CrashReason.Mod配置文件导致游戏崩溃, {TryAnalyzeModName(If(RegexSeek(LogCrash, "(?<=Failed loading config file .+ for modid )[^\n]+"), "").TrimEnd(vbCrLf)).First, If(RegexSeek(LogCrash, "(?<=Failed loading config file ).+(?= of type)"), "").TrimEnd(vbCrLf)})
|
||||
End If
|
||||
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 进行精准日志匹配。匹配优先级低于堆栈分析的崩溃。
|
||||
''' 进行精准日志匹配。匹配优先级高于堆栈分析的崩溃,但低于上面的。
|
||||
''' 如果第一步已经找到了原因则不执行该检测。
|
||||
''' </summary>
|
||||
Private Sub AnalyzeCrit2()
|
||||
|
||||
'游戏日志分析
|
||||
If LogMc IsNot Nothing Then
|
||||
If Not (LogMc.Contains("at net.") OrElse LogMc.Contains("INFO]")) AndAlso
|
||||
LogHs Is Nothing AndAlso LogCrash Is Nothing AndAlso LogMc.Length < 100 Then
|
||||
'常规信息
|
||||
If LogMc.Contains("An exception was thrown, the game will display an error screen and halt.") Then AppendReason(CrashReason.Forge报错, RegexSeek(LogMc, "(?<=the game will display an error screen and halt.[\n\r]+[^\n]+?Exception: )[\s\S]+?(?=\n\tat)")?.Trim(vbCrLf))
|
||||
If LogMc.Contains("A potential solution has been determined:") Then AppendReason(CrashReason.Fabric报错并给出解决方案, Join(RegexSearch(If(RegexSeek(LogMc, "(?<=A potential solution has been determined:\n)((\t)+ - [^\n]+\n)+"), ""), "(?<=(\t)+)[^\n]+"), vbLf))
|
||||
If LogMc.Contains("A potential solution has been determined, this may resolve your problem:") Then AppendReason(CrashReason.Fabric报错并给出解决方案, Join(RegexSearch(If(RegexSeek(LogMc, "(?<=A potential solution has been determined, this may resolve your problem:\n)((\t)+ - [^\n]+\n)+"), ""), "(?<=(\t)+)[^\n]+"), vbLf))
|
||||
If LogMc.Contains("确定了一种可能的解决方法,这样做可能会解决你的问题:") Then AppendReason(CrashReason.Fabric报错并给出解决方案, Join(RegexSearch(If(RegexSeek(LogMc, "(?<=确定了一种可能的解决方法,这样做可能会解决你的问题:\n)((\t)+ - [^\n]+\n)+"), ""), "(?<=(\t)+)[^\n]+"), vbLf))
|
||||
End If
|
||||
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 进行精准日志匹配。匹配优先级低于堆栈分析的崩溃。
|
||||
''' </summary>
|
||||
Private Sub AnalyzeCrit3()
|
||||
|
||||
'游戏日志分析
|
||||
If LogMc IsNot Nothing Then
|
||||
'Mixin 崩溃
|
||||
If LogMc.Contains("Mixin prepare failed ") OrElse LogMc.Contains("Mixin apply failed ") OrElse LogMc.Contains("MixinApplyError") OrElse
|
||||
LogMc.Contains("mixin.injection.throwables.") OrElse LogMc.Contains(".mixins.json] FAILED during )") Then
|
||||
Dim ModId As String = RegexSeek(LogMc, "(?<=in )[^./ ]+(?=.mixins.json.+failed injection check)")
|
||||
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=in mixins.)[^./ ]+(?=.json.+failed injection check)")
|
||||
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= failed .+ in )[^./ ]+(?=.mixins.json)")
|
||||
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= failed .+ in mixins.)[^./ ]+(?=.json)")
|
||||
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= failed mixins.)[^./ ]+(?=.json:)")
|
||||
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= in config \[)[^./ ]+(?=.mixins.json\] FAILED during )")
|
||||
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= in config \[mixins.)[^./ ]+(?=.json\] FAILED during )")
|
||||
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=from mod )[^./ ]+(?=\] from)")
|
||||
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=for mod )[^./ ]+(?= failed)")
|
||||
'兜底名称判断
|
||||
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "[^./ \]]+(?=.mixins.json)")
|
||||
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=mixins.)[^./ \]]+(?=.json)")
|
||||
AppendReason(CrashReason.ModMixin失败, TryAnalyzeModName(If(ModId, "").TrimEnd((vbCrLf & " ").ToCharArray)))
|
||||
Else
|
||||
'在 #3104 的情况下,这一句导致 OptiFabric 的 Mixin 失败错判为 Fabric Loader 加载失败
|
||||
If LogMc.Contains("due to errors, provided by ") Then AppendReason(CrashReason.确定Mod导致游戏崩溃, TryAnalyzeModName(If(RegexSeek(LogMc, "(?<=due to errors, provided by ')[^']+"), "").TrimEnd((vbCrLf & " ").ToCharArray)))
|
||||
End If
|
||||
'极短的程序输出
|
||||
If Not (LogMc.Contains("at net.") OrElse LogMc.Contains("INFO]")) AndAlso LogHs Is Nothing AndAlso LogCrash Is Nothing AndAlso LogMc.Length < 100 Then
|
||||
AppendReason(CrashReason.极短的程序输出, LogMc)
|
||||
End If
|
||||
'Mixin 失败可以导致大量 Mod 实例创建失败
|
||||
@@ -584,17 +644,17 @@ Done:
|
||||
'If Not Stack.Contains(".") Then Continue For
|
||||
For Each IgnoreStack In {
|
||||
"java", "sun", "javax", "jdk", "oolloo",
|
||||
"org.lwjgl", "com.sun", "net.minecraftforge", "com.mojang", "net.minecraft", "cpw.mods", "com.google", "org.apache", "org.spongepowered", "net.fabricmc", "com.mumfrey",
|
||||
"org.lwjgl", "com.sun", "net.minecraftforge", "paulscode.sound", "com.mojang", "net.minecraft", "cpw.mods", "com.google", "org.apache", "org.spongepowered", "net.fabricmc", "com.mumfrey",
|
||||
"com.electronwill.nightconfig", "it.unimi.dsi",
|
||||
"MojangTricksIntelDriversForPerformance_javaw"}
|
||||
If Stack.StartsWith(IgnoreStack) Then GoTo NextStack
|
||||
If Stack.StartsWithF(IgnoreStack) Then GoTo NextStack
|
||||
Next
|
||||
PossibleStacks.Add(Stack.Trim) '.Split("$").First)
|
||||
NextStack:
|
||||
Next
|
||||
PossibleStacks = PossibleStacks.Distinct.ToList
|
||||
Log("[Crash] 找到 " & PossibleStacks.Count & " 条可能的堆栈信息")
|
||||
If PossibleStacks.Count = 0 Then Return New List(Of String)
|
||||
If Not PossibleStacks.Any() Then Return New List(Of String)
|
||||
For Each Stack As String In PossibleStacks
|
||||
Log("[Crash] - " & Stack)
|
||||
Next
|
||||
@@ -605,20 +665,21 @@ NextStack:
|
||||
Dim Splited = Stack.Split(".")
|
||||
For i = 0 To Math.Min(3, Splited.Count - 1) '最多取前 4 节
|
||||
Dim Word As String = Splited(i)
|
||||
If Word.Length <= 2 OrElse Word.StartsWith("func_") Then Continue For
|
||||
If Word.Length <= 2 OrElse Word.StartsWithF("func_") Then Continue For
|
||||
If {"com", "org", "net", "asm", "fml", "mod", "jar", "sun", "lib", "map", "gui", "dev", "nio", "api", "dsi", "top",
|
||||
"core", "init", "mods", "main", "file", "game", "load", "read", "done", "util", "tile", "item", "base",
|
||||
"core", "init", "mods", "main", "file", "game", "load", "read", "done", "util", "tile", "item", "base", "oshi",
|
||||
"forge", "setup", "block", "model", "mixin", "event", "unimi", "netty",
|
||||
"common", "server", "config", "loader", "launch", "entity", "assist", "client", "plugin", "modapi", "mojang", "shader", "events", "github", "recipe",
|
||||
"preinit", "preload", "machine", "reflect", "channel", "general", "handler", "content",
|
||||
"fastutil", "optifine", "scheduler", "minecraft", "transformers", "universal", "internal", "multipart", "minecraftforge", "override", "blockentity"
|
||||
"gitlab", "common", "server", "config", "loader", "launch", "entity", "assist", "client", "plugin", "modapi", "mojang", "shader", "events", "github", "recipe",
|
||||
"preinit", "preload", "machine", "reflect", "channel", "general", "handler", "content", "systems", "modules",
|
||||
"fastutil", "optifine", "internal", "platform", "override",
|
||||
"listeners", "scheduler", "minecraft", "transformers", "universal", "multipart", "minecraftforge", "blockentity"
|
||||
}.Contains(Word.ToLower) Then Continue For
|
||||
PossibleWords.Add(Word.Trim)
|
||||
Next
|
||||
Next
|
||||
PossibleWords = PossibleWords.Distinct.ToList
|
||||
Log("[Crash] 从堆栈信息中找到 " & PossibleWords.Count & " 个可能的 Mod ID 关键词")
|
||||
If PossibleWords.Count > 0 Then Log("[Crash] - " & Join(PossibleWords, ", "))
|
||||
If PossibleWords.Any Then Log("[Crash] - " & Join(PossibleWords, ", "))
|
||||
If PossibleWords.Count > 10 Then
|
||||
Log("[Crash] 关键词过多,考虑匹配出错,不纳入考虑")
|
||||
Return New List(Of String)
|
||||
@@ -628,7 +689,7 @@ NextStack:
|
||||
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 根据 Mod 关键词与详细信息(崩溃报告第二部分)与,获取实际的 Mod 名称。
|
||||
''' 根据 Mod 关键词尝试获取实际的 Mod 名称。
|
||||
''' 若失败则返回 Nothing。
|
||||
''' </summary>
|
||||
Private Function AnalyzeModName(Keywords As List(Of String)) As List(Of String)
|
||||
@@ -643,60 +704,95 @@ NextStack:
|
||||
Next
|
||||
Keywords = RealKeywords
|
||||
|
||||
'获取崩溃报告对应部分
|
||||
If LogCrash Is Nothing Then Return Nothing
|
||||
If Not LogCrash.Contains("A detailed walkthrough of the error") Then Return Nothing
|
||||
Dim Details As String = LogCrash.Replace("A detailed walkthrough of the error", "¨")
|
||||
Dim IsFabricDetail As Boolean = Details.Contains("Fabric Mods") '是否为 Fabric 信息格式
|
||||
If IsFabricDetail Then
|
||||
Details = Details.Replace("Fabric Mods", "¨")
|
||||
Log("[Crash] 检测到 Fabric Mod 信息格式")
|
||||
End If
|
||||
Details = Details.Split("¨").Last
|
||||
|
||||
'[Forge] 获取所有包含 .jar 的行
|
||||
'[Fabric] 获取所有包含 Mod 信息的行
|
||||
Dim ModNameLines As New List(Of String)
|
||||
For Each Line In Details.Split(vbLf)
|
||||
If Line.ToLower.Contains(".jar") OrElse
|
||||
(IsFabricDetail AndAlso Line.StartsWith(vbTab & vbTab) AndAlso Not RegexCheck(Line, "\t\tfabric[\w-]*: Fabric")) Then ModNameLines.Add(Line)
|
||||
Next
|
||||
Log("[Crash] 找到 " & ModNameLines.Count & " 个可能的 Mod 项目行")
|
||||
|
||||
'获取 Mod ID 与关键词的匹配行
|
||||
Dim HintLines As New List(Of String)
|
||||
For Each KeyWord As String In Keywords
|
||||
For Each ModString As String In ModNameLines
|
||||
Dim RealModString As String = ModString.ToLower.Replace("_", "")
|
||||
If Not RealModString.Contains(KeyWord.ToLower.Replace("_", "")) Then Continue For
|
||||
If RealModString.Contains("minecraft.jar") OrElse RealModString.Contains(" forge-") OrElse RealModString.Contains(" mixin-") Then Continue For
|
||||
HintLines.Add(ModString.Trim(vbCrLf.ToCharArray))
|
||||
Exit For
|
||||
Next
|
||||
Next
|
||||
HintLines = HintLines.Distinct.ToList
|
||||
Log("[Crash] 找到 " & HintLines.Count & " 个可能的崩溃 Mod 匹配行")
|
||||
For Each ModName As String In HintLines
|
||||
Log("[Crash] - " & ModName)
|
||||
Next
|
||||
|
||||
'从 Mod 匹配行中提取 .jar 文件的名称
|
||||
For Each Line As String In HintLines
|
||||
Dim Name As String
|
||||
'从崩溃报告获取 Mod 信息
|
||||
If LogCrash IsNot Nothing AndAlso LogCrash.Contains("A detailed walkthrough of the error") Then
|
||||
Dim Details As String = LogCrash.Replace("A detailed walkthrough of the error", "¨")
|
||||
Dim IsFabricDetail As Boolean = Details.Contains("Fabric Mods") '是否为 Fabric 信息格式
|
||||
If IsFabricDetail Then
|
||||
Name = RegexSeek(Line, "(?<=: )[^\n]+(?= [^\n]+)")
|
||||
Else
|
||||
Name = RegexSeek(Line, "(?<=\()[^\t]+.jar(?=\))|(?<=(\t\t)|(\| ))[^\t\|]+.jar", RegularExpressions.RegexOptions.IgnoreCase)
|
||||
Details = Details.Replace("Fabric Mods", "¨")
|
||||
Log("[Crash] 崩溃报告中检测到 Fabric Mod 信息格式")
|
||||
End If
|
||||
If Name IsNot Nothing Then ModFileNames.Add(Name)
|
||||
Next
|
||||
ModFileNames = ModFileNames.Distinct.ToList
|
||||
Log("[Crash] 找到 " & ModFileNames.Count & " 个可能的崩溃 Mod 文件名")
|
||||
For Each ModFileName As String In ModFileNames
|
||||
Log("[Crash] - " & ModFileName)
|
||||
Next
|
||||
Details = Details.Split("¨").Last
|
||||
|
||||
Return If(ModFileNames.Count = 0, Nothing, ModFileNames)
|
||||
'[Forge] 获取所有包含 .jar 的行
|
||||
'[Fabric] 获取所有包含 Mod 信息的行
|
||||
Dim ModNameLines As New List(Of String)
|
||||
For Each Line In Details.Split(vbLf)
|
||||
If Line.ContainsF(".jar", True) OrElse
|
||||
(IsFabricDetail AndAlso Line.StartsWithF(vbTab & vbTab) AndAlso Not RegexCheck(Line, "\t\tfabric[\w-]*: Fabric")) Then ModNameLines.Add(Line)
|
||||
Next
|
||||
Log("[Crash] 崩溃报告中找到 " & ModNameLines.Count & " 个可能的 Mod 项目行")
|
||||
|
||||
'获取 Mod ID 与关键词的匹配行
|
||||
Dim HintLines As New List(Of String)
|
||||
For Each KeyWord As String In Keywords
|
||||
For Each ModString As String In ModNameLines
|
||||
Dim RealModString As String = ModString.ToLower.Replace("_", "")
|
||||
If Not RealModString.Contains(KeyWord.ToLower.Replace("_", "")) Then Continue For
|
||||
If RealModString.Contains("minecraft.jar") OrElse RealModString.Contains(" forge-") OrElse RealModString.Contains(" mixin-") Then Continue For
|
||||
HintLines.Add(ModString.Trim(vbCrLf.ToCharArray))
|
||||
Exit For
|
||||
Next
|
||||
Next
|
||||
HintLines = HintLines.Distinct.ToList
|
||||
Log("[Crash] 崩溃报告中找到 " & HintLines.Count & " 个可能的崩溃 Mod 匹配行")
|
||||
For Each ModLine As String In HintLines
|
||||
Log("[Crash] - " & ModLine)
|
||||
Next
|
||||
|
||||
'从 Mod 匹配行中提取 .jar 文件的名称
|
||||
For Each Line As String In HintLines
|
||||
Dim Name As String
|
||||
If IsFabricDetail Then
|
||||
Name = RegexSeek(Line, "(?<=: )[^\n]+(?= [^\n]+)")
|
||||
Else
|
||||
Name = RegexSeek(Line, "(?<=\()[^\t]+.jar(?=\))|(?<=(\t\t)|(\| ))[^\t\|]+.jar", RegularExpressions.RegexOptions.IgnoreCase)
|
||||
End If
|
||||
If Name IsNot Nothing Then ModFileNames.Add(Name)
|
||||
Next
|
||||
|
||||
End If
|
||||
|
||||
'从 debug.log 获取 Mod 信息
|
||||
If LogMcDebug IsNot Nothing Then
|
||||
|
||||
'Forge: Found valid mod file YungsBetterStrongholds-1.20-Forge-4.0.1.jar with {betterstrongholds} mods - versions {1.20-Forge-4.0.1}
|
||||
Dim ModNameLines As List(Of String) = RegexSearch(LogMcDebug, "(?<=valid mod file ).*", RegularExpressions.RegexOptions.Multiline)
|
||||
Log("[Crash] Debug 信息中找到 " & ModNameLines.Count & " 个可能的 Mod 项目行")
|
||||
|
||||
'获取 Mod ID 与关键词的匹配行
|
||||
Dim HintLines As New List(Of String)
|
||||
For Each KeyWord As String In Keywords
|
||||
For Each ModString As String In ModNameLines
|
||||
If ModString.Contains($"{{{KeyWord}}}") Then HintLines.Add(ModString)
|
||||
Next
|
||||
Next
|
||||
HintLines = HintLines.Distinct.ToList
|
||||
Log("[Crash] Debug 信息中找到 " & HintLines.Count & " 个可能的崩溃 Mod 匹配行")
|
||||
For Each ModLine As String In HintLines
|
||||
Log("[Crash] - " & ModLine)
|
||||
Next
|
||||
|
||||
'从 Mod 匹配行中提取 .jar 文件的名称
|
||||
For Each Line As String In HintLines
|
||||
Dim Name As String
|
||||
Name = RegexSeek(Line, ".*(?= with)")
|
||||
If Name IsNot Nothing Then ModFileNames.Add(Name)
|
||||
Next
|
||||
|
||||
End If
|
||||
|
||||
'输出
|
||||
ModFileNames = ModFileNames.Distinct.ToList
|
||||
If Not ModFileNames.Any() Then
|
||||
Return Nothing
|
||||
Else
|
||||
Log("[Crash] 找到 " & ModFileNames.Count & " 个可能的崩溃 Mod 文件名")
|
||||
For Each ModFileName As String In ModFileNames
|
||||
Log("[Crash] - " & ModFileName)
|
||||
Next
|
||||
Return ModFileNames
|
||||
End If
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 尝试获取 Mod 名称,若失败则返回原关键字。
|
||||
@@ -752,7 +848,7 @@ NextStack:
|
||||
FileName = "游戏崩溃前的输出.txt"
|
||||
End Select
|
||||
If File.Exists(OutputFile) Then
|
||||
WriteFile(TempFolder & "Report\" & FileName, SecretFilter(ReadFile(OutputFile)))
|
||||
WriteFile(TempFolder & "Report\" & FileName, SecretFilter(ReadFile(OutputFile), If(FileName = "启动脚本.bat", "F", "*")))
|
||||
End If
|
||||
Next
|
||||
'导出报告
|
||||
@@ -772,13 +868,11 @@ NextStack:
|
||||
Private Function GetAnalyzeResult(IsHandAnalyze As Boolean) As String
|
||||
|
||||
'没有结果的处理
|
||||
If CrashReasons.Count = 0 Then
|
||||
If Not CrashReasons.Any() Then
|
||||
If IsHandAnalyze Then
|
||||
Return "很抱歉,PCL 无法确定错误原因。"
|
||||
Else
|
||||
Return "很抱歉,你的游戏出现了一些问题……" & vbCrLf &
|
||||
"如果要寻求帮助,请向他人发送错误报告文件,而不是发送这个窗口的截图。" & vbCrLf &
|
||||
"你也可以查看错误报告,其中可能会有出错的原因。"
|
||||
Return $"很抱歉,你的游戏出现了一些问题……{vbCrLf}如果要寻求帮助,请导出错误报告并发给他人,而不是发送这个窗口的截图。"
|
||||
End If
|
||||
End If
|
||||
|
||||
@@ -790,7 +884,7 @@ NextStack:
|
||||
Case CrashReason.Mod文件被解压
|
||||
Results.Add("由于 Mod 文件被解压了,导致游戏无法继续运行。\n直接把整个 Mod 文件放进 Mod 文件夹中即可,若解压就会导致游戏出错。\n\n请删除 Mod 文件夹中已被解压的 Mod,然后再启动游戏。")
|
||||
Case CrashReason.内存不足
|
||||
Results.Add("Minecraft 内存不足,导致其无法继续运行。\n这很可能是由于你为游戏分配的内存不足,或是游戏的配置要求过高。\n\n你可以在启动设置中增加为游戏分配的内存,删除配置要求较高的材质、Mod、光影。\n如果这依然不奏效,请在开始游戏前尽量关闭其他软件,或者……换台电脑?\h")
|
||||
Results.Add("Minecraft 内存不足,导致其无法继续运行。\n这很可能是因为电脑内存不足、游戏分配的内存不足,或是配置要求过高。\n\n你可以尝试在 更多 → 百宝箱 中选择 内存优化,然后再启动游戏。\n如果还是不行,请在启动设置中增加为游戏分配的内存,并删除配置要求较高的材质、Mod、光影。\n如果依然不奏效,请在开始游戏前尽量关闭其他软件,或者……换台电脑?\h")
|
||||
Case CrashReason.使用OpenJ9
|
||||
Results.Add("游戏因为使用 Open J9 而崩溃了。\n请在启动设置的 Java 选择一项中改用非 OpenJ9 的 Java,然后再启动游戏。")
|
||||
Case CrashReason.使用JDK
|
||||
@@ -799,19 +893,29 @@ NextStack:
|
||||
Results.Add("游戏似乎因为你所使用的 Java 版本过高而崩溃了。\n请在启动设置的 Java 选择一项中改用较低版本的 Java,然后再启动游戏。\n如果没有,可以从网络中下载、安装一个。")
|
||||
Case CrashReason.Java版本不兼容
|
||||
Results.Add("游戏不兼容你当前使用的 Java。\n如果没有合适的 Java,可以从网络中下载、安装一个。")
|
||||
Case CrashReason.Mod名称包含特殊字符
|
||||
Results.Add("由于有 Mod 的名称包含特殊字符,导致游戏崩溃。\n请尝试修改 Mod 文件名,让它只包含英文字母、数字、减号(-)、下划线(_)和小数点,然后再启动游戏。")
|
||||
Case CrashReason.MixinBootstrap缺失
|
||||
Results.Add("由于缺失 MixinBootstrap,导致游戏崩溃。\n请尝试安装 MixinBootstrap。若安装后依然崩溃,可以尝试在文件名前添加英文感叹号。")
|
||||
Case CrashReason.使用32位Java导致JVM无法分配足够多的内存
|
||||
If Environment.Is64BitOperatingSystem Then
|
||||
Results.Add("你似乎正在使用 32 位 Java,这会导致 Minecraft 无法使用 1GB 以上的内存,进而造成崩溃。\n\n请在启动设置的 Java 选择一项中改用 64 位的 Java 再启动游戏,然后再启动游戏。\n如果你没有安装 64 位的 Java,你可以从网络中下载、安装一个。")
|
||||
Results.Add("你似乎正在使用 32 位 Java,这会导致 Minecraft 无法使用所需的内存,进而造成崩溃。\n\n请在启动设置的 Java 选择一项中改用 64 位的 Java 再启动游戏,然后再启动游戏。\n如果你没有安装 64 位的 Java,你可以从网络中下载、安装一个。")
|
||||
Else
|
||||
Results.Add("你正在使用 32 位的操作系统,这会导致 Minecraft 无法使用 1GB 以上的内存,进而造成崩溃。\n\n你或许只能重装 64 位的操作系统来解决此问题。\n如果你的电脑内存在 2GB 以内,那或许只能换台电脑了……\h")
|
||||
Results.Add("你正在使用 32 位的操作系统,这会导致 Minecraft 无法使用所需的内存,进而造成崩溃。\n\n你或许只能重装 64 位的操作系统来解决此问题。\n如果你的电脑内存在 2GB 以内,那或许只能换台电脑了……\h")
|
||||
End If
|
||||
Case CrashReason.崩溃日志堆栈分析发现关键字, CrashReason.MC日志堆栈分析发现关键字
|
||||
Case CrashReason.Mod缺少前置或MC版本错误
|
||||
If Additional.Any Then
|
||||
Results.Add("由于未满足 Mod 的依赖项,导致游戏退出。\n未满足的依赖项:\n - " & Join(Additional, "\n - ") & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。")
|
||||
Else
|
||||
Results.Add("由于未满足 Mod 的依赖项,导致游戏退出。\n请根据错误报告中的日志信息进行对应处理,如果看不懂英文可以使用翻译软件。\h")
|
||||
End If
|
||||
Case CrashReason.堆栈分析发现关键字
|
||||
If Additional.Count = 1 Then
|
||||
Results.Add("你的游戏遇到了一些问题,这可能是某些 Mod 所引起的,PCL 找到了一个可疑的关键词:" & Additional.First & "。\n\n如果你知道它对应的 Mod,那么有可能就是它引起的错误,你也可以查看错误报告获取详情。\h")
|
||||
Else
|
||||
Results.Add("你的游戏遇到了一些问题,这可能是某些 Mod 所引起的,PCL 找到了以下可疑的关键词:\n - " & Join(Additional, ", ") & "\n\n如果你知道这些关键词对应的 Mod,那么有可能就是它引起的错误,你也可以查看错误报告获取详情。\h")
|
||||
End If
|
||||
Case CrashReason.崩溃日志堆栈分析发现Mod名称
|
||||
Case CrashReason.堆栈分析发现Mod名称
|
||||
If Additional.Count = 1 Then
|
||||
Results.Add("名为 " & Additional.First & " 的 Mod 可能导致了游戏出错。\n\e\h")
|
||||
Else
|
||||
@@ -861,12 +965,16 @@ NextStack:
|
||||
End If
|
||||
Case CrashReason.OptiFine与Forge不兼容
|
||||
Results.Add("由于 OptiFine 与当前版本的 Forge 不兼容,导致了游戏崩溃。\n\n请前往 OptiFine 官网(https://optifine.net/downloads)查看 OptiFine 所兼容的 Forge 版本,并严格按照对应版本重新安装游戏。")
|
||||
Case CrashReason.ShadersMod与Optifine同时安装
|
||||
Results.Add("无需同时安装 Optifine 和 Shaders Mod,Optifine 已经集成了 Shaders Mod 的功能。\n在删除 Shaders Mod 后,游戏即可正常运行。")
|
||||
Case CrashReason.低版本Forge与高版本Java不兼容
|
||||
Results.Add("由于低版本 Forge 与当前 Java 不兼容,导致了游戏崩溃。\n\n请尝试以下解决方案:\n - 更新 Forge 到 36.2.26 或更高版本\n - 换用版本低于 8.0.320 的 Java")
|
||||
Results.Add("由于低版本 Forge 与当前 Java 不兼容,导致了游戏崩溃。\n\n请尝试以下解决方案:\n - 更新 Forge 到 36.2.26 或更高版本\n - 换用版本低于 1.8.0.320 的 Java")
|
||||
Case CrashReason.版本Json中存在多个Forge
|
||||
Results.Add("可能由于使用其他启动器修改了 Forge 版本,当前版本的文件存在异常,导致了游戏崩溃。\n请尝试重新全新安装 Forge,而非使用其他启动器修改 Forge 版本。")
|
||||
Case CrashReason.玩家手动触发调试崩溃
|
||||
Results.Add("* 事实上,你的游戏没有任何问题,这是你自己触发的崩溃。\n* 你难道没有更重要的事要做吗?")
|
||||
Case CrashReason.Mod需要Java11
|
||||
Results.Add("你所安装的部分 Mod 似乎需要使用 Java 11 启动。\n请在启动设置的 Java 选择一项中改用 Java 11,然后再启动游戏。\n如果你没有安装 Java 11,你可以从网络中下载、安装一个。")
|
||||
Case CrashReason.极短的程序输出
|
||||
Results.Add($"程序返回了以下信息:\n{Additional.First}\n\h")
|
||||
Case CrashReason.OptiFine导致无法加载世界 'https://www.minecraftforum.net/forums/support/java-edition-support/3051132-exception-ticking-world
|
||||
@@ -879,12 +987,16 @@ NextStack:
|
||||
End If
|
||||
Case CrashReason.材质过大或显卡配置不足
|
||||
Results.Add("你所使用的材质分辨率过高,或显卡配置不足,导致游戏无法继续运行。\n\n如果你正在使用高清材质,请将它移除。\n如果你没有使用材质,那么你可能需要更新显卡驱动,或者换个更好的显卡……\h")
|
||||
Case CrashReason.NightConfig的Bug
|
||||
Results.Add("由于 Night Config 存在问题,导致了游戏崩溃。\n你可以尝试安装 Night Config Fixes 模组,这或许能解决此问题。\h")
|
||||
Case CrashReason.光影或资源包导致OpenGL1282错误
|
||||
Results.Add("你所使用的光影或材质导致游戏出现了一些问题……\n\n请尝试删除你所添加的这些额外资源。\h")
|
||||
Case CrashReason.Mod过多导致超出ID限制
|
||||
Results.Add("你所安装的 Mod 过多,超出了游戏的 ID 限制,导致了游戏崩溃。\n请尝试安装 JEID 等修复 Mod,或删除部分大型 Mod。")
|
||||
Case CrashReason.文件或内容校验失败
|
||||
Results.Add("部分文件或内容校验失败,导致游戏出现了问题。\n\n请尝试删除游戏(包括 Mod)并重新下载,或尝试在重新下载时使用 VPN。\h")
|
||||
Case CrashReason.Forge安装不完整
|
||||
Results.Add("由于 Forge 安装不完整,导致游戏无法正常运行。\n请尝试重新安装 Forge。\h")
|
||||
Case CrashReason.Fabric报错
|
||||
If Additional.Count = 1 Then
|
||||
Results.Add("Fabric 提供了以下错误信息:\n" & Additional.First & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。")
|
||||
@@ -924,8 +1036,10 @@ NextStack:
|
||||
Replace("\e", If(IsHandAnalyze, "", vbCrLf & "你可以查看错误报告了解错误具体是如何发生的。")).
|
||||
Replace(vbCrLf, vbCr).Replace(vbLf, vbCr).Replace(vbCr, vbCrLf).
|
||||
Trim(vbCrLf.ToCharArray) &
|
||||
If(Results.Any(Function(r) r.EndsWith("\h")),
|
||||
If(IsHandAnalyze, "", vbCrLf & "如果要寻求帮助,请向他人发送错误报告文件,而不是发送这个窗口的截图。"), "")
|
||||
If(Not Results.Any(Function(r) r.EndsWithF("\h")) OrElse IsHandAnalyze, "",
|
||||
vbCrLf & "如果要寻求帮助,请向他人发送错误报告文件,而不是发送这个窗口的截图。" &
|
||||
If(If(PageSetupSystem.IsLauncherNewest(), True), "",
|
||||
vbCrLf & vbCrLf & "此外,你正在使用老版本 PCL,更新到最新版可能会修复这个问题。" & vbCrLf & "你可以点击 设置 → 启动器 → 检查更新 来更新 PCL。"))
|
||||
End Function
|
||||
|
||||
End Class
|
||||
@@ -19,14 +19,7 @@
|
||||
Dim Checker As New FileChecker(MinSize:=1024, ActualSize:=If(Version.JsonObject("downloads")("client")("size"), -1), Hash:=Version.JsonObject("downloads")("client")("sha1"))
|
||||
If ReturnNothingOnFileUseable Then
|
||||
'是否跳过
|
||||
Dim IsSetupSkip As Boolean = Setup.Get("LaunchAdvanceAssets")
|
||||
Select Case Setup.Get("VersionAdvanceAssets", Version:=Version)
|
||||
Case 0 '使用全局设置
|
||||
Case 1 '开启
|
||||
IsSetupSkip = False
|
||||
Case 2 '关闭
|
||||
IsSetupSkip = True
|
||||
End Select
|
||||
Dim IsSetupSkip As Boolean = ShouldIgnoreFileCheck(Version)
|
||||
If IsSetupSkip AndAlso File.Exists(Version.Path & Version.Name & ".jar") Then Return Nothing '跳过校验
|
||||
If Checker.Check(Version.Path & Version.Name & ".jar") Is Nothing Then Return Nothing '通过校验
|
||||
End If
|
||||
@@ -52,7 +45,7 @@
|
||||
If IndexUrl = "" Then
|
||||
Return Nothing
|
||||
Else
|
||||
Return New NetFile(DlSourceLauncherOrMetaGet(IndexUrl, False), IndexAddress, New FileChecker(CanUseExistsFile:=False, IsJson:=True))
|
||||
Return New NetFile(DlSourceLauncherOrMetaGet(IndexUrl), IndexAddress, New FileChecker(CanUseExistsFile:=False, IsJson:=True))
|
||||
End If
|
||||
End Function
|
||||
|
||||
@@ -64,43 +57,33 @@
|
||||
|
||||
#Region "下载支持库文件"
|
||||
Dim LoadersLib As New List(Of LoaderBase) From {
|
||||
New LoaderTask(Of String, List(Of NetFile))("分析缺失支持库文件", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(Version)) With {.ProgressWeight = 1},
|
||||
New LoaderDownload("下载支持库文件", New List(Of NetFile)) With {.ProgressWeight = 15}
|
||||
}
|
||||
New LoaderTask(Of String, List(Of NetFile))("分析缺失支持库文件", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(Version)) With {.ProgressWeight = 1},
|
||||
New LoaderDownload("下载支持库文件", New List(Of NetFile)) With {.ProgressWeight = 15}
|
||||
}
|
||||
'构造加载器
|
||||
Loaders.Add(New LoaderCombo(Of String)("下载支持库文件(主加载器)", LoadersLib) With {.Block = False, .Show = False, .ProgressWeight = 16})
|
||||
#End Region
|
||||
|
||||
#Region "下载资源文件"
|
||||
Dim IsSetupSkip As Boolean = Setup.Get("LaunchAdvanceAssets")
|
||||
Select Case Setup.Get("VersionAdvanceAssets", Version:=Version)
|
||||
Case 0
|
||||
'使用全局设置
|
||||
Case 1
|
||||
'开启
|
||||
IsSetupSkip = False
|
||||
Case 2
|
||||
'关闭
|
||||
IsSetupSkip = True
|
||||
End Select
|
||||
Dim IsSetupSkip As Boolean = ShouldIgnoreFileCheck(Version)
|
||||
If IsSetupSkip Then Log("[Download] 已跳过 Assets 下载")
|
||||
If (Not SkipAssetsDownloadWhileSetupRequired) OrElse Not IsSetupSkip Then
|
||||
Dim LoadersAssets As New List(Of LoaderBase)
|
||||
'获取资源文件索引地址
|
||||
LoadersAssets.Add(New LoaderTask(Of String, List(Of NetFile))("分析资源文件索引地址",
|
||||
Sub(Task As LoaderTask(Of String, List(Of NetFile)))
|
||||
Try
|
||||
Dim IndexFile = DlClientAssetIndexGet(Version)
|
||||
Dim IndexFileInfo As New FileInfo(IndexFile.LocalPath)
|
||||
If AssetsIndexBehaviour <> AssetsIndexExistsBehaviour.AlwaysDownload AndAlso 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
|
||||
Catch ex As Exception
|
||||
Throw New Exception("分析资源文件索引地址失败", ex)
|
||||
End Try
|
||||
End Sub) With {.ProgressWeight = 0.5, .Show = False})
|
||||
Sub(Task As LoaderTask(Of String, List(Of NetFile)))
|
||||
Try
|
||||
Dim IndexFile = DlClientAssetIndexGet(Version)
|
||||
Dim IndexFileInfo As New FileInfo(IndexFile.LocalPath)
|
||||
If AssetsIndexBehaviour <> AssetsIndexExistsBehaviour.AlwaysDownload AndAlso 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
|
||||
Catch ex As Exception
|
||||
Throw New Exception("分析资源文件索引地址失败", ex)
|
||||
End Try
|
||||
End Sub) With {.ProgressWeight = 0.5, .Show = False})
|
||||
'下载资源文件索引
|
||||
LoadersAssets.Add(New LoaderDownload("下载资源文件索引", New List(Of NetFile)) With {.ProgressWeight = 2})
|
||||
'要求独立更新索引
|
||||
@@ -108,27 +91,34 @@
|
||||
Dim LoadersAssetsUpdate As New List(Of LoaderBase)
|
||||
Dim TempAddress As String = Nothing
|
||||
Dim RealAddress As String = Nothing
|
||||
LoadersAssetsUpdate.Add(New LoaderTask(Of String, List(Of NetFile))("后台分析资源文件索引地址", Sub(Task As LoaderTask(Of String, List(Of NetFile)))
|
||||
Dim BackAssetsFile As NetFile = DlClientAssetIndexGet(Version)
|
||||
RealAddress = BackAssetsFile.LocalPath
|
||||
TempAddress = PathTemp & "Cache\" & BackAssetsFile.LocalName
|
||||
BackAssetsFile.LocalPath = TempAddress
|
||||
Task.Output = New List(Of NetFile) From {BackAssetsFile}
|
||||
End Sub))
|
||||
LoadersAssetsUpdate.Add(New LoaderTask(Of String, List(Of NetFile))("后台分析资源文件索引地址",
|
||||
Sub(Task As LoaderTask(Of String, List(Of NetFile)))
|
||||
Dim BackAssetsFile As NetFile = DlClientAssetIndexGet(Version)
|
||||
RealAddress = BackAssetsFile.LocalPath
|
||||
TempAddress = PathTemp & "Cache\" & BackAssetsFile.LocalName
|
||||
BackAssetsFile.LocalPath = TempAddress
|
||||
Task.Output = New List(Of NetFile) From {BackAssetsFile}
|
||||
'检查是否需要更新:每天只更新一次
|
||||
If File.Exists(RealAddress) AndAlso Math.Abs((File.GetLastWriteTime(RealAddress).Date - Now.Date).TotalDays) < 1 Then
|
||||
Log("[Download] 无需更新资源文件索引")
|
||||
Task.Abort()
|
||||
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))
|
||||
CopyFile(TempAddress, RealAddress)
|
||||
McLaunchLog("后台更新资源文件索引成功:" & TempAddress)
|
||||
End Sub))
|
||||
LoadersAssetsUpdate.Add(New LoaderTask(Of List(Of NetFile), String)("后台复制资源文件索引",
|
||||
Sub(Task As LoaderTask(Of List(Of NetFile), String))
|
||||
CopyFile(TempAddress, RealAddress)
|
||||
McLaunchLog("后台更新资源文件索引成功:" & TempAddress)
|
||||
End Sub))
|
||||
Dim Updater As New LoaderCombo(Of String)("后台更新资源文件索引", LoadersAssetsUpdate)
|
||||
Log("[Download] 开始后台更新资源文件索引")
|
||||
Log("[Download] 开始后台检查资源文件索引")
|
||||
Updater.Start()
|
||||
End If
|
||||
'获取资源文件地址
|
||||
LoadersAssets.Add(New LoaderTask(Of String, List(Of NetFile))("分析缺失资源文件",
|
||||
Sub(Task As LoaderTask(Of String, List(Of NetFile)))
|
||||
Task.Output = McAssetsFixList(McAssetsGetIndexName(Version), CheckAssetsHash, Task)
|
||||
End Sub) With {.ProgressWeight = 3})
|
||||
Sub(Task As LoaderTask(Of String, List(Of NetFile)))
|
||||
Task.Output = McAssetsFixList(McAssetsGetIndexName(Version), CheckAssetsHash, Task)
|
||||
End Sub) With {.ProgressWeight = 3})
|
||||
'下载资源文件
|
||||
LoadersAssets.Add(New LoaderDownload("下载资源文件", New List(Of NetFile)) With {.ProgressWeight = 25})
|
||||
'构造加载器
|
||||
@@ -238,9 +228,7 @@
|
||||
''' </summary>
|
||||
Public DlClientListBmclapiLoader As New LoaderTask(Of Integer, DlClientListResult)("DlClientList Bmclapi", AddressOf DlClientListBmclapiMain)
|
||||
Private Sub DlClientListBmclapiMain(Loader As LoaderTask(Of Integer, DlClientListResult))
|
||||
Dim Json As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/mc/game/version_manifest.json",
|
||||
BackupUrl:="https://download.mcbbs.net/mc/game/version_manifest.json",
|
||||
IsJson:=True)
|
||||
Dim Json As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/mc/game/version_manifest.json", IsJson:=True)
|
||||
Try
|
||||
Dim Versions As JArray = Json("versions")
|
||||
If Versions.Count < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Json.ToString & ")")
|
||||
@@ -267,7 +255,7 @@
|
||||
End Select
|
||||
'确认版本格式标准
|
||||
Id = Id.Replace("_", "-") '1.7.10_pre4 在版本列表中显示为 1.7.10-pre4
|
||||
If Id <> "1.0" AndAlso Id.EndsWith(".0") Then Id = Left(Id, Id.Length - 2) 'OptiFine 1.8 的下载会触发此问题,显示版本为 1.8.0
|
||||
If Id <> "1.0" AndAlso Id.EndsWithF(".0") Then Id = Left(Id, Id.Length - 2) 'OptiFine 1.8 的下载会触发此问题,显示版本为 1.8.0
|
||||
'查找版本并开始
|
||||
For Each Version As JObject In DlClientListLoader.Output.Value("versions")
|
||||
If Version("id") = Id Then
|
||||
@@ -325,7 +313,7 @@
|
||||
Return _inherit
|
||||
End Get
|
||||
Set(value As String)
|
||||
If value.EndsWith(".0") Then value = Left(value, value.Length - 2)
|
||||
If value.EndsWithF(".0") Then value = Left(value, value.Length - 2)
|
||||
_inherit = value
|
||||
End Set
|
||||
End Property
|
||||
@@ -385,9 +373,9 @@
|
||||
Dim Entry As New DlOptiFineListEntry With {
|
||||
.NameDisplay = Name(i).Replace("HD U ", "").Replace(".0 ", " "),
|
||||
.ReleaseTime = Join({ReleaseTime(i).Split(".")(2), ReleaseTime(i).Split(".")(1), ReleaseTime(i).Split(".")(0)}, "/"),
|
||||
.IsPreview = Name(i).ToLower.Contains("pre"),
|
||||
.IsPreview = Name(i).ContainsF("pre", True),
|
||||
.Inherit = Name(i).ToString.Split(" ")(0),
|
||||
.NameFile = If(Name(i).ToLower.Contains("pre"), "preview_", "") & "OptiFine_" & Name(i).Replace(" ", "_") & ".jar",
|
||||
.NameFile = If(Name(i).ContainsF("pre", True), "preview_", "") & "OptiFine_" & Name(i).Replace(" ", "_") & ".jar",
|
||||
.RequiredForgeVersion = Forge(i).Replace("Forge ", "").Replace("#", "")}
|
||||
If Entry.RequiredForgeVersion.Contains("N/A") Then Entry.RequiredForgeVersion = Nothing
|
||||
Entry.NameVersion = Entry.Inherit & "-OptiFine_" & Name(i).ToString.Replace(" ", "_").Replace(Entry.Inherit & "_", "")
|
||||
@@ -401,15 +389,14 @@
|
||||
''' </summary>
|
||||
Public DlOptiFineListBmclapiLoader As New LoaderTask(Of Integer, DlOptiFineListResult)("DlOptiFineList Bmclapi", AddressOf DlOptiFineListBmclapiMain)
|
||||
Private Sub DlOptiFineListBmclapiMain(Loader As LoaderTask(Of Integer, DlOptiFineListResult))
|
||||
Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/optifine/versionList",
|
||||
BackupUrl:="https://download.mcbbs.net/optifine/versionList", IsJson:=True)
|
||||
Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/optifine/versionList", IsJson:=True)
|
||||
Try
|
||||
Dim Versions As New List(Of DlOptiFineListEntry)
|
||||
For Each Token As JObject In Json
|
||||
Dim Entry As New DlOptiFineListEntry With {
|
||||
.NameDisplay = (Token("mcversion").ToString & Token("type").ToString.Replace("HD_U", "").Replace("_", " ") & " " & Token("patch").ToString).Replace(".0 ", " "),
|
||||
.ReleaseTime = "",
|
||||
.IsPreview = Token("patch").ToString.ToLower.Contains("pre"),
|
||||
.IsPreview = Token("patch").ToString.ContainsF("pre", True),
|
||||
.Inherit = Token("mcversion").ToString,
|
||||
.NameFile = Token("filename").ToString,
|
||||
.RequiredForgeVersion = If(Token("forge"), "").ToString.Replace("Forge ", "").Replace("#", "")
|
||||
@@ -472,7 +459,7 @@
|
||||
''' </summary>
|
||||
Public DlForgeListOfficialLoader As New LoaderTask(Of Integer, DlForgeListResult)("DlForgeList Official", AddressOf DlForgeListOfficialMain)
|
||||
Private Sub DlForgeListOfficialMain(Loader As LoaderTask(Of Integer, DlForgeListResult))
|
||||
Dim Result As String = NetGetCodeByRequestRetry("http://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.2.4.html", Encoding.Default, "text/html")
|
||||
Dim Result As String = NetGetCodeByRequestRetry("https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.2.4.html", Encoding.Default, "text/html", UseBrowserUserAgent:=True)
|
||||
If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
|
||||
'获取所有版本信息
|
||||
Dim Names As List(Of String) = RegexSearch(Result, "(?<=a href=""index_)[0-9.]+(_pre[0-9]?)?(?=.html)")
|
||||
@@ -486,8 +473,7 @@
|
||||
''' </summary>
|
||||
Public DlForgeListBmclapiLoader As New LoaderTask(Of Integer, DlForgeListResult)("DlForgeList Bmclapi", AddressOf DlForgeListBmclapiMain)
|
||||
Private Sub DlForgeListBmclapiMain(Loader As LoaderTask(Of Integer, DlForgeListResult))
|
||||
Dim Result As String = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft", Encoding.Default,
|
||||
BackupUrl:="https://download.mcbbs.net/forge/minecraft")
|
||||
Dim Result As String = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft", Encoding.Default)
|
||||
If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
|
||||
'获取所有版本信息
|
||||
Dim Names As List(Of String) = RegexSearch(Result, "[0-9.]+(_pre[0-9]?)?")
|
||||
@@ -610,7 +596,7 @@
|
||||
Public Sub DlForgeVersionOfficialMain(Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)))
|
||||
Dim Result As String
|
||||
Try
|
||||
Result = NetGetCodeByDownload("http://files.minecraftforge.net/maven/net/minecraftforge/forge/index_" & Loader.Input & ".html")
|
||||
Result = NetGetCodeByDownload("https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_" & Loader.Input & ".html", UseBrowserUserAgent:=True)
|
||||
Catch ex As Exception
|
||||
If GetExceptionSummary(ex).Contains("(404)") Then
|
||||
Throw New Exception("没有可用版本")
|
||||
@@ -622,7 +608,7 @@
|
||||
Dim Versions As New List(Of DlForgeVersionEntry)
|
||||
Try
|
||||
'分割版本信息
|
||||
Dim VersionCodes = Mid(Result, 1, Result.LastIndexOf("</table>")).Split("<td class=""download-version")
|
||||
Dim VersionCodes = Mid(Result, 1, Result.LastIndexOfF("</table>")).Split("<td class=""download-version")
|
||||
'获取所有版本信息
|
||||
For i = 1 To VersionCodes.Count - 1
|
||||
Dim VersionCode = VersionCodes(i)
|
||||
@@ -642,22 +628,22 @@
|
||||
Dim ReleaseDate As New Date(ReleaseTimeSplit(0), ReleaseTimeSplit(1), ReleaseTimeSplit(2), '年月日
|
||||
ReleaseTimeSplit(3), ReleaseTimeSplit(4), ReleaseTimeSplit(5), '时分秒
|
||||
0, DateTimeKind.Utc) '以 UTC 时间作为标准
|
||||
Dim ReleaseTime As String = ReleaseDate.ToLocalTime.ToString("yyyy/MM/dd HH:mm") '时区与格式转换
|
||||
Dim ReleaseTime As String = ReleaseDate.ToLocalTime.ToString("yyyy'/'MM'/'dd HH':'mm") '时区与格式转换
|
||||
'分类与 MD5 获取
|
||||
Dim MD5 As String, Category As String
|
||||
If VersionCode.Contains("classifier-installer""") Then
|
||||
'类型为 installer.jar,支持范围 ~753 (~ 1.6.1 部分), 738~684 (1.5.2 全部)
|
||||
VersionCode = VersionCode.Substring(VersionCode.IndexOf("installer.jar"))
|
||||
VersionCode = VersionCode.Substring(VersionCode.IndexOfF("installer.jar"))
|
||||
MD5 = RegexSeek(VersionCode, "(?<=MD5:</strong> )[^<]+")
|
||||
Category = "installer"
|
||||
ElseIf VersionCode.Contains("classifier-universal""") Then
|
||||
'类型为 universal.zip,支持范围 751~449 (1.6.1 部分), 682~183 (1.5.1 ~ 1.3.2 部分)
|
||||
VersionCode = VersionCode.Substring(VersionCode.IndexOf("universal.zip"))
|
||||
VersionCode = VersionCode.Substring(VersionCode.IndexOfF("universal.zip"))
|
||||
MD5 = RegexSeek(VersionCode, "(?<=MD5:</strong> )[^<]+")
|
||||
Category = "universal"
|
||||
ElseIf VersionCode.Contains("client.zip") Then
|
||||
'类型为 client.zip,支持范围 182~ (1.3.2 部分 ~)
|
||||
VersionCode = VersionCode.Substring(VersionCode.IndexOf("client.zip"))
|
||||
VersionCode = VersionCode.Substring(VersionCode.IndexOfF("client.zip"))
|
||||
MD5 = RegexSeek(VersionCode, "(?<=MD5:</strong> )[^<]+")
|
||||
Category = "client"
|
||||
Else
|
||||
@@ -673,7 +659,7 @@
|
||||
Catch ex As Exception
|
||||
Throw New Exception("版本列表解析失败(" & Result & ")", ex)
|
||||
End Try
|
||||
If Versions.Count = 0 Then Throw New Exception("没有可用版本")
|
||||
If Not Versions.Any() Then Throw New Exception("没有可用版本")
|
||||
Loader.Output = Versions
|
||||
End Sub
|
||||
|
||||
@@ -681,8 +667,7 @@
|
||||
''' Forge 版本列表,BMCLAPI。
|
||||
''' </summary>
|
||||
Public Sub DlForgeVersionBmclapiMain(Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)))
|
||||
Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft/" & Loader.Input,
|
||||
BackupUrl:="https://download.mcbbs.net/forge/minecraft/" & Loader.Input, IsJson:=True)
|
||||
Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft/" & Loader.Input, IsJson:=True)
|
||||
Dim Versions As New List(Of DlForgeVersionEntry)
|
||||
Try
|
||||
Dim Recommended As String = McDownloadForgeRecommendedGet(Loader.Input)
|
||||
@@ -723,14 +708,14 @@
|
||||
'基础信息获取
|
||||
Dim Entry = New DlForgeVersionEntry With {.Hash = Hash, .Category = Category, .Version = Name, .Branch = Branch, .Inherit = Inherit, .IsRecommended = Recommended = Name}
|
||||
Dim TimeSplit = Token("modified").ToString.Split("-"c, "T"c, ":"c, "."c, " "c, "/"c)
|
||||
Entry.ReleaseTime = Token("modified").ToObject(Of Date).ToLocalTime.ToString("yyyy/MM/dd HH:mm")
|
||||
Entry.ReleaseTime = Token("modified").ToObject(Of Date).ToLocalTime.ToString("yyyy'/'MM'/'dd HH':'mm")
|
||||
'添加项
|
||||
Versions.Add(Entry)
|
||||
Next
|
||||
Catch ex As Exception
|
||||
Throw New Exception("版本列表解析失败(" & Json.ToString & ")", ex)
|
||||
End Try
|
||||
If Versions.Count = 0 Then Throw New Exception("没有可用版本")
|
||||
If Not Versions.Any() Then Throw New Exception("没有可用版本")
|
||||
Loader.Output = Versions
|
||||
End Sub
|
||||
|
||||
@@ -816,12 +801,12 @@
|
||||
''' </summary>
|
||||
Public DlLiteLoaderListOfficialLoader As New LoaderTask(Of Integer, DlLiteLoaderListResult)("DlLiteLoaderList Official", AddressOf DlLiteLoaderListOfficialMain)
|
||||
Private Sub DlLiteLoaderListOfficialMain(Loader As LoaderTask(Of Integer, DlLiteLoaderListResult))
|
||||
Dim Result As JObject = NetGetCodeByRequestRetry("http://dl.liteloader.com/versions/versions.json", IsJson:=True)
|
||||
Dim Result As JObject = NetGetCodeByRequestRetry("https://dl.liteloader.com/versions/versions.json", IsJson:=True)
|
||||
Try
|
||||
Dim Json As JObject = Result("versions")
|
||||
Dim Versions As New List(Of DlLiteLoaderListEntry)
|
||||
For Each Pair As KeyValuePair(Of String, JToken) In Json
|
||||
If Pair.Key.StartsWith("1.6") OrElse Pair.Key.StartsWith("1.5") Then Continue For
|
||||
If Pair.Key.StartsWithF("1.6") OrElse Pair.Key.StartsWithF("1.5") Then Continue For
|
||||
Dim RealEntry As JToken = If(Pair.Value("artefacts"), Pair.Value("snapshots"))("com.mumfrey:liteloader")("latest")
|
||||
Versions.Add(New DlLiteLoaderListEntry With {
|
||||
.Inherit = Pair.Key,
|
||||
@@ -829,7 +814,7 @@
|
||||
.IsPreview = RealEntry("stream").ToString.ToLower = "snapshot",
|
||||
.FileName = "liteloader-installer-" & Pair.Key & If(Pair.Key = "1.8" OrElse Pair.Key = "1.9", ".0", "") & "-00-SNAPSHOT.jar",
|
||||
.MD5 = RealEntry("md5"),
|
||||
.ReleaseTime = GetLocalTime(GetDate(RealEntry("timestamp"))).ToString("yyyy/MM/dd HH:mm"),
|
||||
.ReleaseTime = GetLocalTime(GetDate(RealEntry("timestamp"))).ToString("yyyy'/'MM'/'dd HH':'mm"),
|
||||
.JsonToken = RealEntry
|
||||
})
|
||||
Next
|
||||
@@ -844,13 +829,12 @@
|
||||
''' </summary>
|
||||
Public DlLiteLoaderListBmclapiLoader As New LoaderTask(Of Integer, DlLiteLoaderListResult)("DlLiteLoaderList Bmclapi", AddressOf DlLiteLoaderListBmclapiMain)
|
||||
Private Sub DlLiteLoaderListBmclapiMain(Loader As LoaderTask(Of Integer, DlLiteLoaderListResult))
|
||||
Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json",
|
||||
BackupUrl:="https://download.mcbbs.net/maven/com/mumfrey/liteloader/versions.json", IsJson:=True)
|
||||
Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json", IsJson:=True)
|
||||
Try
|
||||
Dim Json As JObject = Result("versions")
|
||||
Dim Versions As New List(Of DlLiteLoaderListEntry)
|
||||
For Each Pair As KeyValuePair(Of String, JToken) In Json
|
||||
If Pair.Key.StartsWith("1.6") OrElse Pair.Key.StartsWith("1.5") Then Continue For
|
||||
If Pair.Key.StartsWithF("1.6") OrElse Pair.Key.StartsWithF("1.5") Then Continue For
|
||||
Dim RealEntry As JToken = If(Pair.Value("artefacts"), Pair.Value("snapshots"))("com.mumfrey:liteloader")("latest")
|
||||
Versions.Add(New DlLiteLoaderListEntry With {
|
||||
.Inherit = Pair.Key,
|
||||
@@ -858,7 +842,7 @@
|
||||
.IsPreview = RealEntry("stream").ToString.ToLower = "snapshot",
|
||||
.FileName = "liteloader-installer-" & Pair.Key & If(Pair.Key = "1.8" OrElse Pair.Key = "1.9", ".0", "") & "-00-SNAPSHOT.jar",
|
||||
.MD5 = RealEntry("md5"),
|
||||
.ReleaseTime = GetLocalTime(GetDate(RealEntry("timestamp"))).ToString("yyyy/MM/dd HH:mm"),
|
||||
.ReleaseTime = GetLocalTime(GetDate(RealEntry("timestamp"))).ToString("yyyy'/'MM'/'dd HH':'mm"),
|
||||
.JsonToken = RealEntry
|
||||
})
|
||||
Next
|
||||
@@ -931,8 +915,7 @@
|
||||
''' </summary>
|
||||
Public DlFabricListBmclapiLoader As New LoaderTask(Of Integer, DlFabricListResult)("DlFabricList Bmclapi", AddressOf DlFabricListBmclapiMain)
|
||||
Private Sub DlFabricListBmclapiMain(Loader As LoaderTask(Of Integer, DlFabricListResult))
|
||||
Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/fabric-meta/v2/versions",
|
||||
BackupUrl:="https://download.mcbbs.net/fabric-meta/v2/versions", IsJson:=True)
|
||||
Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/fabric-meta/v2/versions", IsJson:=True)
|
||||
Try
|
||||
Dim Output = New DlFabricListResult With {.IsOfficial = False, .SourceName = "BMCLAPI", .Value = Result}
|
||||
If Output.Value("game") Is Nothing OrElse Output.Value("loader") Is Nothing OrElse Output.Value("installer") Is Nothing Then Throw New Exception("获取到的列表缺乏必要项")
|
||||
@@ -960,34 +943,26 @@
|
||||
|
||||
Public Function DlSourceResourceGet(MojangBase As String) As String()
|
||||
MojangBase = MojangBase.Replace("http://resources.download.minecraft.net", "https://resources.download.minecraft.net")
|
||||
Return {MojangBase.Replace("https://resources.download.minecraft.net", "https://download.mcbbs.net/assets"),
|
||||
MojangBase,
|
||||
MojangBase.Replace("https://resources.download.minecraft.net", "https://bmclapi2.bangbang93.com/assets")
|
||||
Return {
|
||||
MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/assets").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/assets").Replace("https://resources.download.minecraft.net", "https://bmclapi2.bangbang93.com/assets"),
|
||||
MojangBase
|
||||
}
|
||||
End Function
|
||||
|
||||
Public Function DlSourceLibraryGet(MojangBase As String) As String()
|
||||
Return {MojangBase.Replace("https://libraries.minecraft.net", "https://download.mcbbs.net/maven"),
|
||||
MojangBase.Replace("https://libraries.minecraft.net", "https://download.mcbbs.net/libraries"),
|
||||
MojangBase,
|
||||
MojangBase.Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/maven"),
|
||||
MojangBase.Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/libraries")
|
||||
Return {
|
||||
MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/maven").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/maven").Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/maven"),
|
||||
MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/libraries").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/libraries").Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/libraries"),
|
||||
MojangBase
|
||||
}
|
||||
End Function
|
||||
|
||||
Public Function DlSourceLauncherOrMetaGet(MojangBase As String, Optional IsStatic As Boolean = True) As String()
|
||||
Public Function DlSourceLauncherOrMetaGet(MojangBase As String) As String()
|
||||
If MojangBase Is Nothing Then Throw New Exception("无对应的 json 下载地址")
|
||||
If IsStatic Then
|
||||
Return {MojangBase.Replace("https://launcher.mojang.com", "https://download.mcbbs.net").Replace("https://launchermeta.mojang.com", "https://download.mcbbs.net"),
|
||||
MojangBase,
|
||||
MojangBase.Replace("https://launcher.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com")
|
||||
}
|
||||
Else
|
||||
Return {MojangBase.Replace("https://launcher.mojang.com", "https://download.mcbbs.net").Replace("https://launchermeta.mojang.com", "https://download.mcbbs.net"),
|
||||
MojangBase,
|
||||
MojangBase.Replace("https://launcher.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com")
|
||||
}
|
||||
End If
|
||||
Return {
|
||||
MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launcher.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com"),
|
||||
MojangBase
|
||||
}
|
||||
End Function
|
||||
|
||||
Private Sub DlSourceLoader(Of InputType, OutputType)(MainLoader As LoaderTask(Of InputType, OutputType),
|
||||
|
||||
@@ -0,0 +1,776 @@
|
||||
Public Module ModJava
|
||||
Public JavaListCacheVersion As Integer = 5
|
||||
|
||||
''' <summary>
|
||||
''' 目前所有可用的 Java。
|
||||
''' </summary>
|
||||
Public JavaList As New List(Of JavaEntry)
|
||||
|
||||
Public Class JavaEntry
|
||||
|
||||
'路径
|
||||
''' <summary>
|
||||
''' Java.exe 文件的完整路径。
|
||||
''' </summary>
|
||||
Public ReadOnly Property PathJava As String
|
||||
Get
|
||||
Return PathFolder & "java.exe"
|
||||
End Get
|
||||
End Property
|
||||
''' <summary>
|
||||
''' Javaw.exe 文件的完整路径。
|
||||
''' </summary>
|
||||
Public ReadOnly Property PathJavaw As String
|
||||
Get
|
||||
Return PathFolder & "javaw.exe"
|
||||
End Get
|
||||
End Property
|
||||
''' <summary>
|
||||
''' Javaw.exe 文件所在文件夹的路径,以 \ 结尾。
|
||||
''' </summary>
|
||||
Public PathFolder As String
|
||||
''' <summary>
|
||||
''' 是否为用户手动导入的 Java。
|
||||
''' </summary>
|
||||
Public IsUserImport As Boolean
|
||||
|
||||
'版本信息
|
||||
''' <summary>
|
||||
''' Java 的详细版本。若不足 4 位会在前方补 1,例如 1.16.0.1。
|
||||
''' 其大版本号为 Minor。
|
||||
''' </summary>
|
||||
Public Version As Version
|
||||
''' <summary>
|
||||
''' Java 的大版本号。
|
||||
''' </summary>
|
||||
Public ReadOnly Property VersionCode As Integer
|
||||
Get
|
||||
Return Version.Minor
|
||||
End Get
|
||||
End Property
|
||||
''' <summary>
|
||||
''' 是否为 Java Runtime Environment。
|
||||
''' </summary>
|
||||
Public IsJre As Boolean
|
||||
''' <summary>
|
||||
''' 是否为 64 位 Java。
|
||||
''' </summary>
|
||||
Public Is64Bit As Boolean
|
||||
''' <summary>
|
||||
''' 是否已设置环境变量。
|
||||
''' </summary>
|
||||
Public ReadOnly Property HasEnvironment As Boolean
|
||||
Get
|
||||
If PathFolder Is Nothing OrElse PathEnv Is Nothing Then Return False
|
||||
Return PathEnv.Replace("\", "").Replace("/", "").ContainsF(PathFolder.Replace("\", ""), True)
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'序列化
|
||||
Public Function ToJson() As JObject
|
||||
Return New JObject({New JProperty("Path", PathFolder), New JProperty("VersionString", Version.ToString), New JProperty("IsJre", IsJre), New JProperty("Is64Bit", Is64Bit), New JProperty("IsUserImport", IsUserImport)})
|
||||
End Function
|
||||
Public Shared Function FromJson(Data As JObject) As JavaEntry
|
||||
Return New JavaEntry(Data("Path"), Data("IsUserImport")) With {.Version = New Version(Data("VersionString")), .IsJre = Data("IsJre"), .Is64Bit = Data("Is64Bit")}
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 转化为用户友好的字符串输出。
|
||||
''' </summary>
|
||||
Public Overrides Function ToString() As String
|
||||
Dim VersionString = Version.ToString
|
||||
If VersionString.StartsWithF("1.") Then VersionString = Mid(VersionString, 3)
|
||||
Return If(IsJre, "JRE ", "JDK ") & VersionCode & " (" & VersionString & ")" & If(Is64Bit, "", ",32 位") & If(IsUserImport, ",手动导入", "") & ":" & PathFolder
|
||||
End Function
|
||||
|
||||
'构造
|
||||
''' <summary>
|
||||
''' 输入 javaw.exe 文件所在文件夹的路径,不限制结尾。
|
||||
''' </summary>
|
||||
Public Sub New(Folder As String, IsUserImport As Boolean)
|
||||
If Not Folder.EndsWithF("\") Then Folder += "\"
|
||||
PathFolder = Folder.Replace("/", "\")
|
||||
Me.IsUserImport = IsUserImport
|
||||
End Sub
|
||||
|
||||
'方法
|
||||
Private IsChecked As Boolean = False
|
||||
''' <summary>
|
||||
''' 检查并获取 Java 详细信息。在 Java 存在异常时抛出错误。
|
||||
''' </summary>
|
||||
Public Sub Check()
|
||||
If IsChecked Then Exit Sub
|
||||
Dim Output As String = Nothing
|
||||
Try
|
||||
'确定文件存在
|
||||
If Not File.Exists(PathJavaw) Then
|
||||
Throw New FileNotFoundException("未找到 javaw.exe 文件", PathJavaw)
|
||||
End If
|
||||
If Not File.Exists(PathFolder & "java.exe") Then
|
||||
Throw New FileNotFoundException("未找到 java.exe 文件", PathFolder & "java.exe")
|
||||
End If
|
||||
IsJre = Not File.Exists(PathFolder & "javac.exe")
|
||||
'运行 -version
|
||||
Output = ShellAndGetOutput(PathFolder & "java.exe", "-version", 15000).ToLower
|
||||
If Output = "" Then Throw New ApplicationException("尝试运行该 Java 失败")
|
||||
If ModeDebug Then Log("[Java] Java 检查输出:" & PathFolder & "java.exe" & vbCrLf & Output)
|
||||
If Output.Contains("/lib/ext exists") Then Throw New ApplicationException("无法运行该 Java,请在删除 Java 文件夹中的 /lib/ext 文件夹后再试")
|
||||
'获取详细信息
|
||||
Dim VersionString = If(RegexSeek(Output, "(?<=version "")[^""]+"), If(RegexSeek(Output, "(?<=openjdk )[0-9]+"), "")).Replace("_", ".").Split("-").First
|
||||
If VersionString.Split(".").Count > 4 Then VersionString = VersionString.Replace(".0.", ".") '#3493,VersionString = "21.0.2.0.2"
|
||||
Do While VersionString.Split(".").Count < 4
|
||||
If VersionString.StartsWithF("1.") Then
|
||||
VersionString = VersionString & ".0"
|
||||
Else
|
||||
VersionString = "1." & VersionString
|
||||
End If
|
||||
Loop
|
||||
If VersionString = "" Then Throw New ApplicationException($"未找到该 Java 的版本号{If(Output.Length < 500, $"{vbCrLf}输出为:{vbCrLf}{Output}", "")}")
|
||||
Version = New Version(VersionString)
|
||||
If Version.Minor = 0 Then
|
||||
Log("[Java] 疑似 X.0.X.X 格式版本号:" & Version.ToString)
|
||||
Version = New Version(1, Version.Major, Version.Build, Version.Revision)
|
||||
End If
|
||||
Is64Bit = Output.Contains("64-bit")
|
||||
If Version.Minor <= 4 OrElse Version.Minor >= 25 Then Throw New Exception("分析详细信息失败,获取的版本为 " & Version.ToString)
|
||||
'基于 #2249 发现 JRE 17 似乎也导致了 Forge 安装失败,干脆禁用更多版本的 JRE
|
||||
If IsJre AndAlso VersionCode >= 16 Then Throw New Exception("由于高版本 JRE 对游戏的兼容性很差,因此不再允许使用。你可以使用对应版本的 JDK,而非 JRE!")
|
||||
Catch ex As ApplicationException
|
||||
Throw ex
|
||||
Catch ex As ThreadInterruptedException
|
||||
Throw ex
|
||||
Catch ex As Exception
|
||||
Log("[Java] 检查失败的 Java 输出:" & PathFolder & "java.exe" & vbCrLf & If(Output, "无程序输出"))
|
||||
Throw New Exception("检查 Java 失败(" & If(PathJavaw, "Nothing") & ")", ex)
|
||||
End Try
|
||||
IsChecked = True
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
|
||||
''' <summary>
|
||||
''' Path 环境变量。
|
||||
''' </summary>
|
||||
Private ReadOnly Property PathEnv As String
|
||||
Get
|
||||
If _PathEnv Is Nothing Then _PathEnv = If(Environment.GetEnvironmentVariable("Path"), "")
|
||||
Return _PathEnv
|
||||
End Get
|
||||
End Property
|
||||
Private _PathEnv As String = Nothing
|
||||
|
||||
''' <summary>
|
||||
''' JAVA_HOME 环境变量。
|
||||
''' </summary>
|
||||
Private ReadOnly Property PathJavaHome As String
|
||||
Get
|
||||
If _PathJavaHome Is Nothing Then _PathJavaHome = If(Environment.GetEnvironmentVariable("JAVA_HOME"), "")
|
||||
Return _PathJavaHome
|
||||
End Get
|
||||
End Property
|
||||
Private _PathJavaHome As String = Nothing
|
||||
|
||||
''' <summary>
|
||||
''' 初始化 Java 列表,但除非没有 Java,否则不进行检查。
|
||||
''' </summary>
|
||||
Public Sub JavaListInit()
|
||||
JavaList = New List(Of JavaEntry)
|
||||
Try
|
||||
If Setup.Get("CacheJavaListVersion") < JavaListCacheVersion Then
|
||||
'不使用缓存
|
||||
Log("[Java] 要求 Java 列表缓存更新")
|
||||
Setup.Set("CacheJavaListVersion", JavaListCacheVersion)
|
||||
Else
|
||||
'使用缓存
|
||||
For Each JsonEntry In GetJson(Setup.Get("LaunchArgumentJavaAll"))
|
||||
JavaList.Add(JavaEntry.FromJson(JsonEntry))
|
||||
Next
|
||||
End If
|
||||
If Not JavaList.Any() Then
|
||||
Log("[Java] 初始化未找到可用的 Java,将自动触发搜索", LogLevel.Developer)
|
||||
JavaSearchLoader.Start(0)
|
||||
Else
|
||||
Log("[Java] 缓存中有 " & JavaList.Count & " 个可用的 Java:")
|
||||
JavaList.ForEach(Sub(j) Log($"[Java] - {j}"))
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "初始化 Java 列表失败", LogLevel.Feedback)
|
||||
Setup.Set("LaunchArgumentJavaAll", "[]")
|
||||
End Try
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 根据要求返回最适合的 Java,若找不到则返回 Nothing。
|
||||
''' 最小与最大版本在与输入相同时也会通过。
|
||||
''' 必须在工作线程调用。
|
||||
''' </summary>
|
||||
Public Function JavaSelect(CancelException As String, Optional MinVersion As Version = Nothing, Optional MaxVersion As Version = Nothing,
|
||||
Optional RelatedVersion As McVersion = Nothing) As JavaEntry
|
||||
Try
|
||||
Dim AllowedJavaList As New List(Of JavaEntry)
|
||||
|
||||
'添加特定的 Java
|
||||
Dim JavaPreList As New Dictionary(Of String, Boolean)
|
||||
If PathMcFolder.Split("\").Count > 3 Then
|
||||
JavaSearchFolder(GetPathFromFullPath(PathMcFolder), JavaPreList, False, True) 'Minecraft 文件夹的父文件夹(如果不是根目录的话)
|
||||
End If
|
||||
JavaSearchFolder(PathMcFolder, JavaPreList, False, True) 'Minecraft 文件夹
|
||||
If RelatedVersion IsNot Nothing Then JavaSearchFolder(RelatedVersion.Path, JavaPreList, False, True) '所选版本文件夹
|
||||
Dim TargetJavaList As New List(Of JavaEntry)
|
||||
For Each Entry In JavaPreList
|
||||
TargetJavaList.Add(New JavaEntry(Entry.Key, Entry.Value))
|
||||
Next
|
||||
|
||||
'检查特定的 Java
|
||||
If TargetJavaList.Any Then
|
||||
TargetJavaList = JavaCheckList(TargetJavaList)
|
||||
Log("[Java] 检查后找到 " & TargetJavaList.Count & " 个特定路径下的 Java:")
|
||||
For Each Java In TargetJavaList
|
||||
Log($"[Java] - {Java}")
|
||||
Next
|
||||
End If
|
||||
|
||||
#Region "添加用户指定的 Java,储存到 UserJava 中"
|
||||
|
||||
Dim UserJava As JavaEntry = Nothing
|
||||
|
||||
'获取版本独立设置中指定的 Java
|
||||
If RelatedVersion IsNot Nothing AndAlso Setup.Get("VersionArgumentJavaSelect", Version:=RelatedVersion).ToString.StartsWithF("{") Then
|
||||
Try
|
||||
UserJava = JavaEntry.FromJson(GetJson(Setup.Get("VersionArgumentJavaSelect", Version:=RelatedVersion)))
|
||||
UserJava.Check()
|
||||
Catch ex As ThreadInterruptedException
|
||||
Throw
|
||||
Catch ex As Exception
|
||||
UserJava = Nothing
|
||||
Setup.Reset("VersionArgumentJavaSelect", Version:=RelatedVersion)
|
||||
Log(ex, "版本独立设置中指定的 Java 已无法使用,此设置已重置", LogLevel.Hint)
|
||||
End Try
|
||||
End If
|
||||
|
||||
'获取全局设置中指定的 Java
|
||||
If UserJava Is Nothing AndAlso Setup.Get("LaunchArgumentJavaSelect") <> "" Then
|
||||
Try
|
||||
UserJava = JavaEntry.FromJson(GetJson(Setup.Get("LaunchArgumentJavaSelect")))
|
||||
UserJava.Check()
|
||||
Catch ex As ThreadInterruptedException
|
||||
Throw
|
||||
Catch ex As Exception
|
||||
UserJava = Nothing
|
||||
Setup.Reset("LaunchArgumentJavaSelect")
|
||||
Log(ex, "全局设置中指定的 Java 已无法使用,此设置已重置", LogLevel.Hint)
|
||||
End Try
|
||||
End If
|
||||
|
||||
'添加到特定 Java 列表
|
||||
If UserJava IsNot Nothing Then
|
||||
Log($"[Java] 用户指定的 Java:{UserJava}")
|
||||
TargetJavaList.Add(UserJava)
|
||||
End If
|
||||
|
||||
#End Region
|
||||
|
||||
RetryGet:
|
||||
'等待进行中的搜索结束
|
||||
If JavaSearchLoader.State <> LoadState.Finished AndAlso JavaSearchLoader.State <> LoadState.Waiting Then JavaSearchLoader.WaitForExit()
|
||||
Select Case JavaSearchLoader.State
|
||||
Case LoadState.Failed
|
||||
Throw JavaSearchLoader.Error
|
||||
Case LoadState.Aborted
|
||||
Throw New ThreadInterruptedException("Java 搜索加载器已中断")
|
||||
End Select
|
||||
|
||||
'生成完整的 Java 列表
|
||||
Dim AllJavaList As New List(Of JavaEntry)
|
||||
AllJavaList.AddRange(TargetJavaList)
|
||||
AllJavaList.AddRange(JavaList)
|
||||
|
||||
'根据选定条件进行过滤
|
||||
For Each Java In AllJavaList
|
||||
If MinVersion IsNot Nothing AndAlso Java.Version < MinVersion Then Continue For
|
||||
If MaxVersion IsNot Nothing AndAlso Java.Version > MaxVersion Then Continue For
|
||||
If Java.Is64Bit AndAlso Is32BitSystem Then Continue For
|
||||
AllowedJavaList.Add(Java)
|
||||
Next
|
||||
|
||||
'若未找到适合的 Java,尝试触发搜索
|
||||
If Not AllowedJavaList.Any() AndAlso JavaSearchLoader.State = LoadState.Waiting Then
|
||||
Log("[Java] 未找到满足条件的 Java,尝试进行搜索")
|
||||
JavaSearchLoader.Start(IsForceRestart:=True)
|
||||
GoTo RetryGet
|
||||
End If
|
||||
|
||||
#Region "检查用户指定的 Java 是否可用"
|
||||
|
||||
'确保指定的 Java 可用
|
||||
If UserJava Is Nothing Then GoTo ExitUserJavaCheck
|
||||
If AllowedJavaList.Any(Function(j) j.PathFolder = UserJava.PathFolder) Then
|
||||
Log("[Java] 使用用户指定的 Java:" & UserJava.PathFolder)
|
||||
AllowedJavaList = New List(Of JavaEntry) From {UserJava}
|
||||
GoTo UserPass
|
||||
End If
|
||||
|
||||
'指定的 Java 不可用,弹窗要求选择
|
||||
Log("[Java] 发现用户指定的不兼容 Java:" & UserJava.ToString)
|
||||
Log($"[Java] 目前实际可用的 Java 列表:")
|
||||
For Each Java In AllowedJavaList
|
||||
Log($"[Java] - {Java}")
|
||||
Next
|
||||
Dim Requirement As String = ""
|
||||
Dim ShowRevision As Boolean = False
|
||||
If (MinVersion Is Nothing OrElse MinVersion.Minor = 0) AndAlso (MaxVersion IsNot Nothing AndAlso MaxVersion.Minor < 999) Then
|
||||
ShowRevision = MaxVersion.MinorRevision < 999
|
||||
Requirement = "最高兼容到 Java " & MaxVersion.Minor & If(ShowRevision, "." & MaxVersion.MajorRevision & "." & MaxVersion.MinorRevision, "")
|
||||
ElseIf (MinVersion IsNot Nothing AndAlso MinVersion.Minor > 0) AndAlso (MaxVersion Is Nothing OrElse MaxVersion.Minor >= 999) Then
|
||||
ShowRevision = MinVersion.MinorRevision > 0 OrElse MinVersion.MajorRevision > 0
|
||||
Requirement = "至少需要 Java " & MinVersion.Minor & If(ShowRevision, "." & MinVersion.MajorRevision & "." & MinVersion.MinorRevision, "")
|
||||
ElseIf (MinVersion IsNot Nothing AndAlso MinVersion.Minor > 0) AndAlso (MaxVersion IsNot Nothing AndAlso MaxVersion.Minor < 999) Then
|
||||
ShowRevision = MinVersion.MinorRevision > 0 OrElse MinVersion.MajorRevision > 0 OrElse MaxVersion.MinorRevision < 999
|
||||
Dim Left As String = MinVersion.Minor & If(ShowRevision, "." & MinVersion.MajorRevision & "." & MinVersion.MinorRevision, "")
|
||||
Dim Right As String = MaxVersion.Minor & If(ShowRevision, "." & MaxVersion.MajorRevision & "." & MaxVersion.MinorRevision, "")
|
||||
Requirement = "需要 Java " & If(Left = Right, Left, Left & " ~ " & Right)
|
||||
End If
|
||||
Dim JavaCurrent As String = UserJava.VersionCode & If(ShowRevision, "." & UserJava.Version.MajorRevision & "." & UserJava.Version.MinorRevision, "")
|
||||
If Setup.Get("LaunchAdvanceJava") OrElse (RelatedVersion IsNot Nothing AndAlso Setup.Get("VersionAdvanceJava", RelatedVersion)) Then
|
||||
'直接跳过弹窗
|
||||
Log("[Java] 设置中指定了使用 Java " & JavaCurrent & ",但当前版本" & Requirement & ",这可能会导致游戏崩溃!", LogLevel.Debug)
|
||||
AllowedJavaList = New List(Of JavaEntry) From {UserJava}
|
||||
Else
|
||||
Select Case MyMsgBox("你在设置中手动指定了使用 Java " & JavaCurrent & ",但当前" & Requirement & "。" & vbCrLf &
|
||||
"如果强制使用该 Java,可能导致游戏崩溃。" & vbCrLf &
|
||||
"你也可以将 游戏 Java 设置修改为 自动选择合适的 Java。" & vbCrLf &
|
||||
vbCrLf &
|
||||
" - 指定的 Java:" & UserJava.ToString,
|
||||
"Java 兼容性警告", "让 PCL 自动选择", "强制使用该 Java", "取消")
|
||||
Case 1 '让 PCL 自动选择
|
||||
Case 2 '强制使用指定的 Java
|
||||
Log("[Java] 已强制使用用户指定的不兼容 Java")
|
||||
AllowedJavaList = New List(Of JavaEntry) From {UserJava}
|
||||
Case 3 '取消启动
|
||||
Throw New Exception(CancelException)
|
||||
End Select
|
||||
End If
|
||||
|
||||
ExitUserJavaCheck:
|
||||
#End Region
|
||||
|
||||
'若依然未找到适合的 Java,直接返回
|
||||
If Not AllowedJavaList.Any() Then Return Nothing
|
||||
|
||||
'优先使用特定目录下的 Java
|
||||
For Each Java In AllowedJavaList
|
||||
'如果在官启文件夹启动,会将官启自带 Java 错误视作 MC 文件夹指定 Java,导致了 #2054 的第二例
|
||||
If Java.PathFolder.Contains(".minecraft\cache\java") Then Continue For
|
||||
If TargetJavaList.Contains(Java) Then
|
||||
'直接使用指定的 Java
|
||||
AllowedJavaList = New List(Of JavaEntry) From {Java}
|
||||
Log("[Java] 优先使用特定路径下的 Java:" & Java.ToString)
|
||||
GoTo UserPass
|
||||
End If
|
||||
Next
|
||||
UserPass:
|
||||
|
||||
'对适合的 Java 进行排序
|
||||
AllowedJavaList = Sort(AllowedJavaList, AddressOf JavaSorter)
|
||||
Log($"[Java] 排序后的 Java 优先顺序:")
|
||||
For Each Java In AllowedJavaList
|
||||
Log($"[Java] - {Java}")
|
||||
Next
|
||||
|
||||
'检查选定的 Java,若测试失败则尝试进行搜索
|
||||
Dim SelectedJava = AllowedJavaList.First
|
||||
Try
|
||||
SelectedJava.Check()
|
||||
Catch ex As ThreadInterruptedException
|
||||
Throw
|
||||
Catch ex As Exception
|
||||
If ex.InnerException IsNot Nothing AndAlso TypeOf (ex.InnerException) Is ThreadInterruptedException Then Throw ex.InnerException
|
||||
Log(ex, "最终选定的 Java 已无法使用,尝试进行搜索")
|
||||
AllowedJavaList = New List(Of JavaEntry)
|
||||
JavaSearchLoader.Start(IsForceRestart:=True)
|
||||
GoTo RetryGet
|
||||
End Try
|
||||
|
||||
'返回
|
||||
Log("[Java] 最终选定的 Java:" & AllowedJavaList.First.ToString)
|
||||
Return SelectedJava
|
||||
|
||||
Catch ex As ThreadInterruptedException
|
||||
Log(ex, "查找符合条件的 Java 时出现加载器中断")
|
||||
Return Nothing
|
||||
Catch ex As Exception
|
||||
If ex.Message = "$$" Then Throw ex
|
||||
Log(ex, "查找符合条件的 Java 失败", LogLevel.Feedback)
|
||||
Return Nothing
|
||||
End Try
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 是否强制指定了 64 位 Java。如果没有强制指定,返回是否安装了 64 位 Java。
|
||||
''' </summary>
|
||||
Public Function JavaIs64Bit(Optional RelatedVersion As McVersion = Nothing) As Boolean
|
||||
Try
|
||||
'检查强制指定
|
||||
Dim UserSetup As String = Setup.Get("LaunchArgumentJavaSelect")
|
||||
If RelatedVersion IsNot Nothing Then
|
||||
Dim UserSetupVersion As String = Setup.Get("VersionArgumentJavaSelect", Version:=RelatedVersion)
|
||||
If UserSetupVersion <> "使用全局设置" Then UserSetup = UserSetupVersion
|
||||
End If
|
||||
If UserSetup <> "" Then
|
||||
Dim UserJava As JavaEntry = Nothing
|
||||
Try
|
||||
UserJava = JavaEntry.FromJson(GetJson(UserSetup))
|
||||
Catch ex As Exception
|
||||
Log(ex, "版本指定的 Java 信息已损坏,已重置版本设置中指定的 Java")
|
||||
Setup.Set("VersionArgumentJavaSelect", "使用全局设置", Version:=RelatedVersion)
|
||||
GoTo NoUserJava
|
||||
End Try
|
||||
For Each Java In JavaList
|
||||
If Java.PathFolder = UserJava.PathFolder Then Return UserJava.Is64Bit
|
||||
Next
|
||||
End If
|
||||
NoUserJava:
|
||||
'检查列表
|
||||
For Each Java In JavaList
|
||||
If Java.Is64Bit Then Return True
|
||||
Next
|
||||
Return False
|
||||
Catch ex As Exception
|
||||
Log(ex, "检查 Java 类别时出错", LogLevel.Feedback)
|
||||
Setup.Set("LaunchArgumentJavaSelect", "")
|
||||
Return True
|
||||
End Try
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 将 Java 按照适用性排序。
|
||||
''' </summary>
|
||||
Public Function JavaSorter(Left As JavaEntry, Right As JavaEntry) As Boolean
|
||||
'1. 尽量在当前文件夹或当前 Minecraft 文件夹
|
||||
Dim ProgramPathParent As String, MinecraftPathParent As String = ""
|
||||
ProgramPathParent = If(New DirectoryInfo(Path).Parent, New DirectoryInfo(Path)).FullName
|
||||
If PathMcFolder <> "" Then MinecraftPathParent = If(New DirectoryInfo(PathMcFolder).Parent, New DirectoryInfo(PathMcFolder)).FullName
|
||||
If Left.PathFolder.StartsWithF(ProgramPathParent) AndAlso Not Right.PathFolder.StartsWithF(ProgramPathParent) Then Return True
|
||||
If Not Left.PathFolder.StartsWithF(ProgramPathParent) AndAlso Right.PathFolder.StartsWithF(ProgramPathParent) Then Return False
|
||||
If PathMcFolder <> "" Then
|
||||
If Left.PathFolder.StartsWithF(MinecraftPathParent) AndAlso Not Right.PathFolder.StartsWithF(MinecraftPathParent) Then Return True
|
||||
If Not Left.PathFolder.StartsWithF(MinecraftPathParent) AndAlso Right.PathFolder.StartsWithF(MinecraftPathParent) Then Return False
|
||||
End If
|
||||
'2. 尽量使用 64 位
|
||||
If Left.Is64Bit AndAlso Not Right.Is64Bit Then Return True
|
||||
If Not Left.Is64Bit AndAlso Right.Is64Bit Then Return False
|
||||
'3. 尽量不使用 JDK
|
||||
If Left.IsJre AndAlso Not Right.IsJre Then Return True
|
||||
If Not Left.IsJre AndAlso Right.IsJre Then Return False
|
||||
'4. Java 大版本
|
||||
If Left.VersionCode <> Right.VersionCode Then
|
||||
' Java 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21...
|
||||
Dim Weight = {0, 1, 2, 3, 4, 5, 6, 14, 30, 10, 12, 15, 13, 9, 8, 7, 11, 31, 29, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28}
|
||||
Return Weight.ElementAtOrDefault(Left.VersionCode) >= Weight.ElementAtOrDefault(Right.VersionCode)
|
||||
End If
|
||||
'5. 最次级版本号更接近 51
|
||||
Return Math.Abs(Left.Version.Revision - 51) <= Math.Abs(Right.Version.Revision - 51)
|
||||
End Function
|
||||
|
||||
#Region "搜索"
|
||||
|
||||
''' <summary>
|
||||
''' 模糊搜索并获取所有可用的 Java,并在结束后更新设置页面显示。输出将直接写入 JavaList。
|
||||
''' </summary>
|
||||
Public JavaSearchLoader As New LoaderTask(Of Integer, Integer)("查找 Java", AddressOf JavaSearchLoaderSub) With {.ProgressWeight = 2}
|
||||
Private Sub JavaSearchLoaderSub(Loader As LoaderTask(Of Integer, Integer))
|
||||
If FrmSetupLaunch IsNot Nothing Then
|
||||
RunInUiWait(Sub()
|
||||
FrmSetupLaunch.ComboArgumentJava.Items.Clear()
|
||||
FrmSetupLaunch.ComboArgumentJava.Items.Add(New ComboBoxItem With {.Content = "加载中……", .IsSelected = True})
|
||||
End Sub)
|
||||
End If
|
||||
If FrmVersionSetup IsNot Nothing Then
|
||||
RunInUiWait(Sub()
|
||||
FrmVersionSetup.ComboArgumentJava.Items.Clear()
|
||||
FrmVersionSetup.ComboArgumentJava.Items.Add(New ComboBoxItem With {.Content = "加载中……", .IsSelected = True})
|
||||
End Sub)
|
||||
End If
|
||||
|
||||
Try
|
||||
|
||||
'可能包含 Java 的文件夹列表,以 “\” 结尾,且仅包含 “\”
|
||||
'Key:文件夹地址
|
||||
'Value: 是否为玩家手动导入
|
||||
Dim JavaPreList As New Dictionary(Of String, Boolean)
|
||||
|
||||
#Region "模糊查找可能可用的 Java"
|
||||
|
||||
'查找环境变量中的 Java
|
||||
For Each PathInEnv As String In Split((PathEnv & ";" & PathJavaHome).Replace("\\", "\").Replace("/", "\"), ";")
|
||||
PathInEnv = PathInEnv.Trim(" """.ToCharArray())
|
||||
If PathInEnv = "" Then Continue For
|
||||
If Not PathInEnv.EndsWithF("\") Then PathInEnv += "\"
|
||||
'粗略检查有效性
|
||||
If File.Exists(PathInEnv & "javaw.exe") Then JavaPreList(PathInEnv) = False
|
||||
Next
|
||||
'查找磁盘中的 Java
|
||||
For Each Disk As DriveInfo In DriveInfo.GetDrives()
|
||||
JavaSearchFolder(Disk.Name, JavaPreList, False)
|
||||
Next
|
||||
'查找 APPDATA 文件夹中的 Java
|
||||
JavaSearchFolder(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\", JavaPreList, False)
|
||||
JavaSearchFolder(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) & "\", JavaPreList, False)
|
||||
'查找启动器目录中的 Java
|
||||
JavaSearchFolder(Path, JavaPreList, False, IsFullSearch:=True)
|
||||
'查找所选 Minecraft 文件夹中的 Java
|
||||
If Not String.IsNullOrWhiteSpace(PathMcFolder) AndAlso Path <> PathMcFolder Then
|
||||
JavaSearchFolder(PathMcFolder, JavaPreList, False, IsFullSearch:=True)
|
||||
End If
|
||||
|
||||
'若不全为符号链接,则清除符号链接的地址
|
||||
Dim JavaWithoutReparse As New Dictionary(Of String, Boolean)
|
||||
For Each Pair In JavaPreList
|
||||
Dim Folder As String = Pair.Key.Replace("\\", "\").Replace("/", "\")
|
||||
Dim Info As FileSystemInfo = New FileInfo(Folder & "javaw.exe")
|
||||
Do
|
||||
If Info.Attributes.HasFlag(FileAttributes.ReparsePoint) Then
|
||||
Log("[Java] 位于 " & Folder & " 的 Java 包含符号链接")
|
||||
Continue For
|
||||
End If
|
||||
Info = If(TypeOf Info Is FileInfo, CType(Info, FileInfo).Directory, CType(Info, DirectoryInfo).Parent)
|
||||
Loop While Info IsNot Nothing
|
||||
Log("[Java] 位于 " & Folder & " 的 Java 不含符号链接")
|
||||
JavaWithoutReparse.Add(Pair.Key, Pair.Value)
|
||||
Next
|
||||
If JavaWithoutReparse.Any Then JavaPreList = JavaWithoutReparse
|
||||
|
||||
'若不全为特殊引用,则清除特殊引用的地址
|
||||
Dim JavaWithoutInherit As New Dictionary(Of String, Boolean)
|
||||
For Each Pair In JavaPreList
|
||||
If Pair.Key.Contains("javapath_target_") OrElse Pair.Key.Contains("javatmp") Then
|
||||
Log("[Java] 位于 " & Pair.Key & " 的 Java 包含特殊引用")
|
||||
Else
|
||||
Log("[Java] 位于 " & Pair.Key & " 的 Java 不含特殊引用")
|
||||
JavaWithoutInherit.Add(Pair.Key, Pair.Value)
|
||||
End If
|
||||
Next
|
||||
If JavaWithoutInherit.Any Then JavaPreList = JavaWithoutInherit
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "添加玩家手动导入的 Java"
|
||||
|
||||
Dim ImportedJava As String = Setup.Get("LaunchArgumentJavaAll")
|
||||
Try
|
||||
For Each JavaJsonObject In GetJson(ImportedJava)
|
||||
Dim Entry = JavaEntry.FromJson(JavaJsonObject)
|
||||
If Entry.IsUserImport Then JavaPreList(Entry.PathFolder) = True
|
||||
Next
|
||||
Catch ex As Exception
|
||||
Log(ex, "Java 列表已损坏", LogLevel.Feedback)
|
||||
Setup.Set("LaunchArgumentJavaAll", "[]")
|
||||
End Try
|
||||
|
||||
#End Region
|
||||
|
||||
'确保可用并获取详细信息,转入正式列表
|
||||
Dim NewJavaList As New List(Of JavaEntry)
|
||||
For Each Entry In JavaPreList.Distinct(Function(a, b) a.Key.ToLower = b.Key.ToLower) '#794
|
||||
NewJavaList.Add(New JavaEntry(Entry.Key, Entry.Value))
|
||||
Next
|
||||
NewJavaList = Sort(JavaCheckList(NewJavaList), AddressOf JavaSorter)
|
||||
|
||||
'修改设置项
|
||||
Dim AllList As New JArray
|
||||
For Each Java In NewJavaList
|
||||
AllList.Add(Java.ToJson)
|
||||
Next
|
||||
Setup.Set("LaunchArgumentJavaAll", AllList.ToString(Newtonsoft.Json.Formatting.None))
|
||||
JavaList = NewJavaList
|
||||
|
||||
Catch ex As Exception
|
||||
Log(ex, "搜索 Java 时出错", LogLevel.Feedback)
|
||||
JavaList = New List(Of JavaEntry)
|
||||
End Try
|
||||
|
||||
Log("[Java] Java 搜索完成,发现 " & JavaList.Count & " 个 Java")
|
||||
If FrmSetupLaunch IsNot Nothing Then RunInUi(Sub() FrmSetupLaunch.RefreshJavaComboBox())
|
||||
If FrmVersionSetup IsNot Nothing Then RunInUi(Sub() FrmVersionSetup.RefreshJavaComboBox())
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 多线程检查列表中的所有 Java 项。
|
||||
''' </summary>
|
||||
Private Function JavaCheckList(JavaEntries As List(Of JavaEntry)) As List(Of JavaEntry)
|
||||
Log("[Java] 开始确认列表 Java 状态,共 " & JavaEntries.Count & " 项")
|
||||
JavaCheckList = New List(Of JavaEntry)
|
||||
Dim ListLock As New Object
|
||||
|
||||
'启动检查线程
|
||||
Dim CheckThreads As New List(Of Thread)
|
||||
For Each Entry In JavaEntries
|
||||
Dim CheckThread As New Thread(Sub()
|
||||
Try
|
||||
Entry.Check()
|
||||
If ModeDebug Then Log("[Java] - " & Entry.ToString)
|
||||
SyncLock ListLock
|
||||
JavaCheckList.Add(Entry)
|
||||
End SyncLock
|
||||
Catch ex As ThreadInterruptedException
|
||||
Catch ex As Exception
|
||||
If Entry.IsUserImport Then
|
||||
Log(ex, "位于 " & Entry.PathFolder & " 的 Java 存在异常,将被自动移除", LogLevel.Hint)
|
||||
Else
|
||||
Log(ex, "位于 " & Entry.PathFolder & " 的 Java 存在异常")
|
||||
End If
|
||||
End Try
|
||||
End Sub)
|
||||
CheckThreads.Add(CheckThread)
|
||||
CheckThread.Start()
|
||||
Next
|
||||
|
||||
'等待构造线程完成
|
||||
Wait:
|
||||
Thread.Sleep(10)
|
||||
For Each CheckThread In CheckThreads
|
||||
If CheckThread.IsAlive Then GoTo Wait
|
||||
Next
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 模糊搜索指定文件夹下的 Java,并只进行粗略的检查。这不会搜索全部路径。
|
||||
''' </summary>
|
||||
''' <param name="OriginalPath">开始搜索的起始路径,不限制结尾。</param>
|
||||
''' <param name="IsFullSearch">搜索当前文件夹下的全部文件夹(此参数不会传递到子文件夹)。</param>
|
||||
Private Sub JavaSearchFolder(OriginalPath As String, ByRef Results As Dictionary(Of String, Boolean), Source As Boolean, Optional IsFullSearch As Boolean = False)
|
||||
Try
|
||||
Log("[Java] 开始" & If(IsFullSearch, "完全", "部分") & "遍历查找:" & OriginalPath)
|
||||
JavaSearchFolder(New DirectoryInfo(OriginalPath), Results, Source, IsFullSearch)
|
||||
Catch ex As UnauthorizedAccessException
|
||||
Log("[Java] 遍历查找 Java 时遭遇无权限的文件夹:" & OriginalPath)
|
||||
Catch ex As Exception
|
||||
Log(ex, "遍历查找 Java 时出错(" & OriginalPath & ")")
|
||||
End Try
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 模糊搜索指定文件夹下的 Java,并只进行粗略的检查。这不会搜索全部路径。
|
||||
''' </summary>
|
||||
''' <param name="OriginalPath">开始搜索的起始路径,不限制结尾。</param>
|
||||
''' <param name="IsFullSearch">搜索当前文件夹下的全部文件夹(此参数不会传递到子文件夹)。</param>
|
||||
Private Sub JavaSearchFolder(OriginalPath As DirectoryInfo, ByRef Results As Dictionary(Of String, Boolean), Source As Boolean, Optional IsFullSearch As Boolean = False)
|
||||
Try
|
||||
'确认目录存在
|
||||
If Not OriginalPath.Exists Then Exit Sub
|
||||
Dim Path As String = OriginalPath.FullName.Replace("\\", "\")
|
||||
If Not Path.EndsWithF("\") Then Path += "\"
|
||||
'若该目录有 Java,则加入结果
|
||||
If File.Exists(Path & "javaw.exe") Then Results(Path) = Source
|
||||
'查找其下的所有文件夹
|
||||
For Each FolderInfo As DirectoryInfo In OriginalPath.EnumerateDirectories
|
||||
If FolderInfo.Attributes.HasFlag(FileAttributes.ReparsePoint) Then Continue For '跳过符号链接
|
||||
Dim SearchEntry = GetFolderNameFromPath(FolderInfo.Name).ToLower '用于搜索的字符串
|
||||
If IsFullSearch OrElse
|
||||
FolderInfo.Parent.Name.ToLower = "users" OrElse
|
||||
SearchEntry.Contains("java") OrElse SearchEntry.Contains("jdk") OrElse SearchEntry.Contains("env") OrElse
|
||||
SearchEntry.Contains("环境") OrElse SearchEntry.Contains("run") OrElse SearchEntry.Contains("软件") OrElse
|
||||
SearchEntry.Contains("jre") OrElse SearchEntry = "bin" OrElse SearchEntry.Contains("mc") OrElse
|
||||
SearchEntry.Contains("soft") OrElse SearchEntry.Contains("cache") OrElse SearchEntry.Contains("temp") OrElse
|
||||
SearchEntry.Contains("corretto") OrElse SearchEntry.Contains("roaming") OrElse SearchEntry.Contains("users") OrElse
|
||||
SearchEntry.Contains("craft") OrElse SearchEntry.Contains("program") OrElse SearchEntry.Contains("世界") OrElse
|
||||
SearchEntry.Contains("net") OrElse SearchEntry.Contains("游戏") OrElse SearchEntry.Contains("oracle") OrElse
|
||||
SearchEntry.Contains("game") OrElse SearchEntry.Contains("file") OrElse SearchEntry.Contains("data") OrElse
|
||||
SearchEntry.Contains("jvm") OrElse SearchEntry.Contains("服务") OrElse SearchEntry.Contains("server") OrElse
|
||||
SearchEntry.Contains("客户") OrElse SearchEntry.Contains("client") OrElse SearchEntry.Contains("整合") OrElse
|
||||
SearchEntry.Contains("应用") OrElse SearchEntry.Contains("运行") OrElse SearchEntry.Contains("前置") OrElse
|
||||
SearchEntry.Contains("mojang") OrElse SearchEntry.Contains("官启") OrElse SearchEntry.Contains("新建文件夹") OrElse
|
||||
SearchEntry.Contains("eclipse") OrElse SearchEntry.Contains("microsoft") OrElse SearchEntry.Contains("hotspot") OrElse
|
||||
SearchEntry.Contains("runtime") OrElse SearchEntry.Contains("x86") OrElse SearchEntry.Contains("x64") OrElse
|
||||
SearchEntry.Contains("forge") OrElse SearchEntry.Contains("原版") OrElse SearchEntry.Contains("optifine") OrElse
|
||||
SearchEntry.Contains("官方") OrElse SearchEntry.Contains("启动") OrElse SearchEntry.Contains("hmcl") OrElse
|
||||
SearchEntry.Contains("mod") OrElse SearchEntry.Contains("高清") OrElse SearchEntry.Contains("download") OrElse
|
||||
SearchEntry.Contains("launch") OrElse SearchEntry.Contains("程序") OrElse SearchEntry.Contains("path") OrElse
|
||||
SearchEntry.Contains("version") OrElse SearchEntry.Contains("baka") OrElse SearchEntry.Contains("pcl") OrElse
|
||||
SearchEntry.Contains("zulu") OrElse
|
||||
SearchEntry.Contains("local") OrElse SearchEntry.Contains("packages") OrElse SearchEntry.Contains("4297127d64ec6") OrElse '官启文件夹
|
||||
SearchEntry.Contains("国服") OrElse SearchEntry.Contains("网易") OrElse SearchEntry.Contains("ext") OrElse '网易 Java 文件夹名
|
||||
SearchEntry.Contains("netease") OrElse SearchEntry.Contains("1.") OrElse SearchEntry.Contains("启动") Then
|
||||
JavaSearchFolder(FolderInfo, Results, Source)
|
||||
End If
|
||||
Next
|
||||
Catch ex As UnauthorizedAccessException
|
||||
Log("[Java] 遍历查找 Java 时遭遇无权限的文件夹:" & OriginalPath.FullName)
|
||||
Catch ex As Exception
|
||||
Log(ex, "遍历查找 Java 时出错(" & OriginalPath.FullName & ")")
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "下载"
|
||||
|
||||
''' <summary>
|
||||
''' 提示 Java 缺失,并弹窗确认是否自动下载。返回玩家选择是否下载。
|
||||
''' </summary>
|
||||
Public Function JavaDownloadConfirm(VersionDescription As String, Optional ForcedManualDownload As Boolean = False) As Boolean
|
||||
If ForcedManualDownload Then
|
||||
MyMsgBox($"PCL 未找到 {VersionDescription}。" & vbCrLf &
|
||||
$"请自行搜索并安装 {VersionDescription},安装后在 设置 → 启动选项 → 游戏 Java 中重新搜索或导入。",
|
||||
"未找到 Java")
|
||||
Return False
|
||||
Else
|
||||
Return MyMsgBox($"PCL 未找到 {VersionDescription},是否需要 PCL 自动下载?" & vbCrLf &
|
||||
$"如果你已经安装了 {VersionDescription},请在 设置 → 启动选项 → 游戏 Java 中手动导入。",
|
||||
"未找到 Java", "自动下载", "取消") = 1
|
||||
End If
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 获取下载 Java 8/14/17/21 的加载器。需要开启 IsForceRestart 以正常刷新 Java 列表。
|
||||
''' </summary>
|
||||
Public Function JavaFixLoaders(Version As Integer) As LoaderCombo(Of Integer)
|
||||
Dim JavaDownloadLoader As New LoaderDownload("下载 Java 文件", New List(Of NetFile)) With {.ProgressWeight = 10}
|
||||
Dim Loader = New LoaderCombo(Of Integer)($"下载 Java {Version}", {
|
||||
New LoaderTask(Of Integer, List(Of NetFile))("获取 Java 下载信息", AddressOf JavaFileList) With {.ProgressWeight = 2},
|
||||
JavaDownloadLoader,
|
||||
JavaSearchLoader
|
||||
})
|
||||
AddHandler JavaDownloadLoader.OnStateChangedThread,
|
||||
Sub(Raw As LoaderBase, NewState As LoadState, OldState As LoadState)
|
||||
If (NewState = LoadState.Failed OrElse NewState = LoadState.Aborted) AndAlso LastJavaBaseDir IsNot Nothing Then
|
||||
Log($"[Java] 由于下载未完成,清理未下载完成的 Java 文件:{LastJavaBaseDir}", LogLevel.Debug)
|
||||
DeleteDirectory(LastJavaBaseDir)
|
||||
ElseIf NewState = LoadState.Finished Then
|
||||
LastJavaBaseDir = Nothing
|
||||
End If
|
||||
End Sub
|
||||
JavaDownloadLoader.HasOnStateChangedThread = True
|
||||
Return Loader
|
||||
End Function
|
||||
Private LastJavaBaseDir As String = Nothing '用于在下载中断或失败时删除未完成下载的 Java 文件夹,防止残留只下了一半但 -version 能跑的 Java
|
||||
Private Sub JavaFileList(Loader As LoaderTask(Of Integer, List(Of NetFile)))
|
||||
Log("[Java] 开始获取 Java 下载信息")
|
||||
Dim IndexFileStr As String = NetGetCodeByDownload(
|
||||
{"https://bmclapi2.bangbang93.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json",
|
||||
"https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"},
|
||||
IsJson:=True)
|
||||
'获取下载地址
|
||||
Dim MainEntry As JObject = CType(GetJson(IndexFileStr), JObject)($"windows-x{If(Is32BitSystem, "86", "64")}")
|
||||
Dim Entries = MainEntry.Children.Reverse. '选择最靠后的一项(最新)
|
||||
SelectMany(Function(e As JProperty) CType(e.Value, JArray).Select(Function(v) New KeyValuePair(Of String, JObject)(e.Name, v)))
|
||||
Dim TargetEntry = Entries.First(Function(t) t.Value("version")("name").ToString.StartsWithF(Loader.Input))
|
||||
Dim Address As String = TargetEntry.Value("manifest")("url")
|
||||
Log($"[Java] 准备下载 Java {TargetEntry.Value("version")("name")}({TargetEntry.Key}):{Address}")
|
||||
'获取文件列表
|
||||
Dim ListFileStr As String = NetGetCodeByDownload({Address.Replace("piston-meta.mojang.com", "bmclapi2.bangbang93.com"), Address}, IsJson:=True)
|
||||
LastJavaBaseDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\.minecraft\runtime\" & TargetEntry.Key & "\"
|
||||
Dim Results As New List(Of NetFile)
|
||||
For Each File As JProperty In CType(GetJson(ListFileStr), JObject)("files")
|
||||
If CType(File.Value, JObject)("downloads")?("raw") Is Nothing Then Continue For
|
||||
Dim Info As JObject = CType(File.Value, JObject)("downloads")("raw")
|
||||
Dim Checker As New FileChecker(ActualSize:=Info("size"), Hash:=Info("sha1"))
|
||||
If Checker.Hash = "12976a6c2b227cbac58969c1455444596c894656" OrElse Checker.Hash = "c80e4bab46e34d02826eab226a4441d0970f2aba" OrElse Checker.Hash = "84d2102ad171863db04e7ee22a259d1f6c5de4a5" Then
|
||||
'跳过 3 个无意义大量重复文件(#3827)
|
||||
Continue For
|
||||
End If
|
||||
If Checker.Check(LastJavaBaseDir & File.Name) Is Nothing Then Continue For '跳过已存在的文件
|
||||
Dim Url As String = Info("url")
|
||||
Results.Add(New NetFile({Url.Replace("piston-data.mojang.com", "bmclapi2.bangbang93.com"), Url}, LastJavaBaseDir & File.Name, Checker))
|
||||
Next
|
||||
Loader.Output = Results
|
||||
Log($"[Java] 需要下载 {Results.Count} 个文件,目标文件夹:{LastJavaBaseDir}")
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
End Module
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,4 +1,6 @@
|
||||
Public Module ModModpack
|
||||
Imports System.IO.Compression
|
||||
|
||||
Public Module ModModpack
|
||||
|
||||
'触发整合包安装的外部接口
|
||||
''' <summary>
|
||||
@@ -14,13 +16,13 @@
|
||||
''' </summary>
|
||||
Public Function ModpackInstall(File As String, Optional VersionName As String = Nothing, Optional ShowHint As Boolean = True, Optional Logo As String = Nothing) As Boolean
|
||||
Log("[ModPack] 整合包安装请求:" & If(File, "null"))
|
||||
Dim Archive As Compression.ZipArchive = Nothing
|
||||
Dim Archive As ZipArchive = Nothing
|
||||
Dim ArchiveBaseFolder As String = ""
|
||||
Try
|
||||
'获取整合包种类与关键 Json
|
||||
Dim PackType As Integer = -1
|
||||
Try
|
||||
Archive = New Compression.ZipArchive(New FileStream(File, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
Archive = New ZipArchive(New FileStream(File, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
'从根目录判断整合包类型
|
||||
If Archive.GetEntry("mcbbs.packmeta") IsNot Nothing Then PackType = 3 : Exit Try 'MCBBS 整合包(优先于 manifest.json 判断)
|
||||
If Archive.GetEntry("modrinth.index.json") IsNot Nothing Then PackType = 4 : Exit Try 'Modrinth 整合包
|
||||
@@ -38,7 +40,7 @@
|
||||
For Each Entry In Archive.Entries
|
||||
Dim FullNames As String() = Entry.FullName.Split("/")
|
||||
ArchiveBaseFolder = FullNames(0) & "/"
|
||||
If Entry.FullName.EndsWith("/versions/") AndAlso FullNames.Count = 3 Then PackType = 9 : Exit Try '压缩包
|
||||
If Entry.FullName.EndsWithF("/versions/") AndAlso FullNames.Count = 3 Then PackType = 9 : Exit Try '压缩包
|
||||
'确定为一级目录下
|
||||
If FullNames.Count <> 2 Then Continue For
|
||||
'判断是否为关键文件
|
||||
@@ -59,7 +61,7 @@
|
||||
If GetExceptionDetail(ex, True).Contains("Error.WinIOError") Then
|
||||
Log(ex, "打开整合包文件失败", If(ShowHint, LogLevel.Hint, LogLevel.Normal))
|
||||
Return False
|
||||
ElseIf File.ToLower.EndsWith(".rar") Then
|
||||
ElseIf File.EndsWithF(".rar", True) Then
|
||||
Log(ex, "PCL 无法处理 rar 格式的压缩包,请在解压后重新压缩为 zip 格式再试", If(ShowHint, LogLevel.Hint, LogLevel.Normal))
|
||||
Return False
|
||||
Else
|
||||
@@ -109,7 +111,7 @@
|
||||
'整合包缓存清理
|
||||
Private IsInstallCacheCleared As Boolean = False
|
||||
Private IsInstallCacheClearing As Boolean = False
|
||||
Private Sub UnpackFiles(InstallTemp As String, FileAddress As String)
|
||||
Private Sub UnpackFiles(InstallTemp As String, FileAddress As String, Loader As LoaderBase)
|
||||
'清理缓存文件夹
|
||||
If Not IsInstallCacheCleared Then
|
||||
IsInstallCacheCleared = True
|
||||
@@ -135,9 +137,8 @@
|
||||
Try
|
||||
Retry:
|
||||
'完全不知道为啥会出现文件正在被另一进程使用的问题,总之多试试
|
||||
Directory.CreateDirectory(InstallTemp)
|
||||
DeleteDirectory(InstallTemp)
|
||||
Compression.ZipFile.ExtractToDirectory(FileAddress, InstallTemp, Encode)
|
||||
ExtractFile(FileAddress, InstallTemp, Encode)
|
||||
Catch ex As Exception
|
||||
Log(ex, "第 " & RetryCount & " 次解压尝试失败")
|
||||
If TypeOf ex Is ArgumentException Then
|
||||
@@ -146,6 +147,7 @@ Retry:
|
||||
End If
|
||||
If RetryCount < 5 Then
|
||||
Thread.Sleep(RetryCount * 2000)
|
||||
If Loader IsNot Nothing AndAlso Loader.LoadingState <> MyLoading.MyLoadingState.Run Then Exit Sub
|
||||
RetryCount += 1
|
||||
GoTo Retry
|
||||
Else
|
||||
@@ -184,7 +186,7 @@ Retry:
|
||||
Dim FabricVersion As String = Nothing
|
||||
For Each Entry In If(Json("minecraft")("modLoaders"), {})
|
||||
Dim Id As String = If(Entry("id"), "").ToString.ToLower
|
||||
If Id.StartsWith("forge-") Then
|
||||
If Id.StartsWithF("forge-") Then
|
||||
'Forge 指定
|
||||
If Id.Contains("recommended") Then
|
||||
Log("[ModPack] 该整合包版本过老,已不支持进行安装!", LogLevel.Hint)
|
||||
@@ -197,7 +199,7 @@ Retry:
|
||||
Catch ex As Exception
|
||||
Log(ex, "读取整合包 Forge 版本失败:" & Id)
|
||||
End Try
|
||||
ElseIf Id.StartsWith("fabric-") Then
|
||||
ElseIf Id.StartsWithF("fabric-") Then
|
||||
'Fabric 指定
|
||||
Try
|
||||
Log("[ModPack] 整合包 Fabric 版本:" & Id)
|
||||
@@ -215,13 +217,15 @@ Retry:
|
||||
If OverrideHome <> "" Then
|
||||
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
|
||||
Sub(Task As LoaderTask(Of String, Integer))
|
||||
UnpackFiles(InstallTemp, FileAddress)
|
||||
UnpackFiles(InstallTemp, FileAddress, Task)
|
||||
Task.Progress = 0.5
|
||||
Dim OverridePath As String = InstallTemp & ArchiveBaseFolder & OverrideHome
|
||||
'复制结果
|
||||
If Directory.Exists(InstallTemp & ArchiveBaseFolder & OverrideHome) Then
|
||||
CopyDirectory(InstallTemp & ArchiveBaseFolder & OverrideHome, PathMcFolder & "versions\" & VersionName)
|
||||
If Directory.Exists(OverridePath) Then
|
||||
CopyDirectory(OverridePath, PathMcFolder & "versions\" & VersionName)
|
||||
Log($"[ModPack] 整合包 override 复制:{OverridePath} -> {PathMcFolder & "versions\" & VersionName}")
|
||||
Else
|
||||
Log("[ModPack] 整合包中未找到 override 目录,已跳过")
|
||||
Log($"[ModPack] 整合包中未找到 override 文件夹:{OverridePath}")
|
||||
End If
|
||||
Task.Progress = 0.9
|
||||
'开启版本隔离
|
||||
@@ -230,95 +234,104 @@ Retry:
|
||||
.ProgressWeight = New FileInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s
|
||||
End If
|
||||
'获取 Mod 列表
|
||||
Dim ModFileList As New List(Of Integer)
|
||||
Dim ModList As New List(Of Integer)
|
||||
Dim ModOptionalList As New List(Of Integer)
|
||||
For Each ModEntry In If(Json("files"), {})
|
||||
If ModEntry("projectID") Is Nothing OrElse ModEntry("fileID") Is Nothing Then
|
||||
Hint("某项 Mod 缺少必要信息,已跳过:" & ModEntry.ToString)
|
||||
Continue For
|
||||
End If
|
||||
If ModEntry("required") IsNot Nothing AndAlso Not ModEntry("required").ToObject(Of Boolean) Then Continue For
|
||||
ModFileList.Add(ModEntry("fileID"))
|
||||
ModList.Add(ModEntry("fileID"))
|
||||
If ModEntry("required") IsNot Nothing AndAlso Not ModEntry("required").ToObject(Of Boolean) Then ModOptionalList.Add(ModEntry("fileID"))
|
||||
Next
|
||||
If ModFileList.Count > 0 Then
|
||||
If ModList.Any Then
|
||||
Dim ModDownloadLoaders As New List(Of LoaderBase)
|
||||
'获取 Mod 下载信息
|
||||
InstallLoaders.Add(New LoaderTask(Of Integer, JArray)("获取 Mod 下载信息",
|
||||
ModDownloadLoaders.Add(New LoaderTask(Of Integer, JArray)("获取 Mod 下载信息",
|
||||
Sub(Task As LoaderTask(Of Integer, JArray))
|
||||
Task.Output = GetJson(NetRequestRetry("https://api.curseforge.com/v1/mods/files", "POST", "{""fileIds"": [" & Join(ModFileList, ",") & "]}", "application/json"))("data")
|
||||
Task.Output = GetJson(NetRequestRetry("https://api.curseforge.com/v1/mods/files", "POST", "{""fileIds"": [" & Join(ModList, ",") & "]}", "application/json"))("data")
|
||||
'如果文件已被删除,则 API 会跳过那一项
|
||||
If ModFileList.Count > Task.Output.Count Then Throw New Exception("整合包所需要的部分 Mod 版本已被 Mod 作者删除,因此无法完成整合包安装,请联系整合包作者更新整合包中的 Mod 版本")
|
||||
End Sub) With {.ProgressWeight = ModFileList.Count / 10}) '每 10 Mod 需要 1s
|
||||
If ModList.Count > Task.Output.Count Then Throw New Exception("整合包所需要的部分 Mod 版本已被 Mod 作者删除,因此无法完成整合包安装,请联系整合包作者更新整合包中的 Mod 版本")
|
||||
End Sub) With {.ProgressWeight = ModList.Count / 10}) '每 10 Mod 需要 1s
|
||||
'构造 NetFile
|
||||
InstallLoaders.Add(New LoaderTask(Of JArray, List(Of NetFile))("构造 Mod 下载信息",
|
||||
ModDownloadLoaders.Add(New LoaderTask(Of JArray, List(Of NetFile))("构造 Mod 下载信息",
|
||||
Sub(Task As LoaderTask(Of JArray, List(Of NetFile)))
|
||||
Dim FileList As New Dictionary(Of Integer, NetFile)
|
||||
For Each ModJson In Task.Input
|
||||
Dim Id As Integer = ModJson("id").ToObject(Of Integer)
|
||||
'跳过重复的 Mod(疑似 CurseForge Bug)
|
||||
If FileList.ContainsKey(ModJson("id").ToObject(Of Integer)) Then Continue For
|
||||
'根据 modules 判断是资源包还是 Mod
|
||||
If FileList.ContainsKey(Id) Then Continue For
|
||||
'可选 Mod 提示
|
||||
If ModOptionalList.Contains(Id) Then
|
||||
If MyMsgBox("是否要下载整合包中的可选文件 " & ModJson("displayName").ToString & "?",
|
||||
"下载可选文件", "是", "否") = 2 Then
|
||||
Continue For
|
||||
End If
|
||||
End If
|
||||
'建立 CompFile
|
||||
Dim File As New CompFile(ModJson, CompType.Mod)
|
||||
If Not File.Available Then Continue For
|
||||
'根据 modules 和文件名后缀判断是资源包还是 Mod
|
||||
Dim IsResourcePack As Boolean
|
||||
If ModJson("modules").Any Then 'modules 可能返回 null(#1006)
|
||||
Dim ModuleNames = CType(ModJson("modules"), JArray).Select(Function(l) l("name").ToString).ToList
|
||||
IsResourcePack = Not ModuleNames.Contains("META-INF") AndAlso ModuleNames.Contains("pack.mcmeta")
|
||||
IsResourcePack =
|
||||
(Not ModuleNames.Contains("META-INF")) AndAlso (Not ModuleNames.Contains("mcmod.info")) AndAlso '不包含 META-INF 或 mcmod.info
|
||||
ModuleNames.Contains("pack.mcmeta") AndAlso '包含 pack.mcmeta
|
||||
(Not File.FileName.EndsWithF(".jar", True)) '文件后缀不是 .jar
|
||||
Else
|
||||
IsResourcePack = False
|
||||
End If
|
||||
'实际的添加
|
||||
Dim File As New CompFile(ModJson, CompType.Mod)
|
||||
If Not File.Available Then Continue For
|
||||
FileList.Add(ModJson("id"),
|
||||
File.ToNetFile($"{PathMcFolder}versions\{VersionName}\{If(IsResourcePack, "resourcepacks", "mods")}\"))
|
||||
Task.Progress += 1 / (1 + ModFileList.Count)
|
||||
FileList.Add(Id, File.ToNetFile($"{PathMcFolder}versions\{VersionName}\{If(IsResourcePack, "resourcepacks", "mods")}\"))
|
||||
Task.Progress += 1 / (1 + ModList.Count)
|
||||
Next
|
||||
Task.Output = FileList.Values.ToList
|
||||
End Sub) With {.ProgressWeight = ModFileList.Count / 200, .Show = False}) '每 200 Mod 需要 1s
|
||||
End Sub) With {.ProgressWeight = ModList.Count / 200, .Show = False}) '每 200 Mod 需要 1s
|
||||
'下载 Mod 文件
|
||||
InstallLoaders.Add(New LoaderDownload("下载 Mod", New List(Of NetFile)) With {.ProgressWeight = ModFileList.Count * 1.5}) '每个 Mod 需要 1.5s
|
||||
ModDownloadLoaders.Add(New LoaderDownload("下载 Mod", New List(Of NetFile)) With {.ProgressWeight = ModList.Count * 1.5}) '每个 Mod 需要 1.5s
|
||||
'构造加载器
|
||||
InstallLoaders.Add(New LoaderCombo(Of Integer)("下载 Mod(主加载器)", ModDownloadLoaders) With
|
||||
{.Show = False, .ProgressWeight = ModDownloadLoaders.Sum(Function(l) l.ProgressWeight)})
|
||||
End If
|
||||
|
||||
'构造加载器
|
||||
Dim Request As New McInstallRequest With {
|
||||
.TargetVersionName = VersionName,
|
||||
.TargetVersionFolder = $"{PathMcFolder}versions\{VersionName}\",
|
||||
.MinecraftName = Json("minecraft")("version").ToString,
|
||||
.ForgeVersion = ForgeVersion,
|
||||
.FabricVersion = FabricVersion
|
||||
}
|
||||
Dim InstallExpectTime As Double = 0
|
||||
For Each InstallLoader In InstallLoaders
|
||||
InstallExpectTime += InstallLoader.ProgressWeight
|
||||
Next
|
||||
Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request, True)
|
||||
If MergeLoaders Is Nothing Then Exit Sub
|
||||
Dim MergeExpectTime As Double = 0
|
||||
For Each MergeLoader In MergeLoaders
|
||||
MergeExpectTime += MergeLoader.ProgressWeight
|
||||
Next
|
||||
'构造 Libraries 加载器(为了使得 Mods 下载结束后再构造,这样才会下载 JumpLoader 文件)
|
||||
'构造 Libraries 加载器
|
||||
Dim LoadersLib As New List(Of LoaderBase)
|
||||
LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析游戏支持库文件(副加载器)", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionName))) With {.ProgressWeight = 1, .Show = False})
|
||||
LoadersLib.Add(New LoaderDownload("下载游戏支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 7, .Show = False})
|
||||
'构造总加载器
|
||||
Dim Loaders As New List(Of LoaderBase)
|
||||
Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallExpectTime})
|
||||
Loaders.Add(New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .ProgressWeight = MergeExpectTime})
|
||||
Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)})
|
||||
Loaders.Add(New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .ProgressWeight = MergeLoaders.Sum(Function(l) l.ProgressWeight)})
|
||||
Loaders.Add(New LoaderCombo(Of String)("下载游戏支持库文件", LoadersLib) With {.ProgressWeight = 8})
|
||||
Loaders.Add(New LoaderTask(Of String, String)("最终整理文件",
|
||||
Sub(Task As LoaderTask(Of String, String))
|
||||
'设置图标
|
||||
Dim VersionFolder As String = PathMcFolder & "versions\" & VersionName & "\"
|
||||
If Logo IsNot Nothing AndAlso File.Exists(Logo) Then
|
||||
File.Copy(Logo, VersionFolder & "PCL\Logo.png", True)
|
||||
WriteIni(VersionFolder & "PCL\Setup.ini", "Logo", "PCL\Logo.png")
|
||||
WriteIni(VersionFolder & "PCL\Setup.ini", "LogoCustom", "True")
|
||||
Log("[ModPack] 已设置整合包 Logo:" & Logo)
|
||||
End If
|
||||
'删除原始整合包文件
|
||||
For Each Target As String In {VersionFolder & "原始整合包.zip", VersionFolder & "原始整合包.mrpack"}
|
||||
If Not Setup.Get("ToolDownloadKeepModpack") AndAlso File.Exists(Target) Then
|
||||
Log("[ModPack] 根据设置要求删除原始整合包文件:" & Target)
|
||||
File.Delete(Target)
|
||||
End If
|
||||
Next
|
||||
End Sub) With {.ProgressWeight = 0.1, .Show = False})
|
||||
Sub(Task As LoaderTask(Of String, String))
|
||||
'设置图标
|
||||
Dim VersionFolder As String = PathMcFolder & "versions\" & VersionName & "\"
|
||||
If Logo IsNot Nothing AndAlso File.Exists(Logo) Then
|
||||
File.Copy(Logo, VersionFolder & "PCL\Logo.png", True)
|
||||
WriteIni(VersionFolder & "PCL\Setup.ini", "Logo", "PCL\Logo.png")
|
||||
WriteIni(VersionFolder & "PCL\Setup.ini", "LogoCustom", "True")
|
||||
Log("[ModPack] 已设置整合包 Logo:" & Logo)
|
||||
End If
|
||||
'删除原始整合包文件
|
||||
For Each Target As String In {VersionFolder & "原始整合包.zip", VersionFolder & "原始整合包.mrpack"}
|
||||
If Not Setup.Get("ToolDownloadKeepModpack") AndAlso File.Exists(Target) Then
|
||||
Log("[ModPack] 根据设置要求删除原始整合包文件:" & Target)
|
||||
File.Delete(Target)
|
||||
End If
|
||||
Next
|
||||
End Sub) With {.ProgressWeight = 0.1, .Show = False})
|
||||
|
||||
'重复任务检查
|
||||
Dim LoaderName As String = "CurseForge 整合包安装:" & VersionName & " "
|
||||
@@ -333,7 +346,7 @@ Retry:
|
||||
|
||||
'启动
|
||||
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
|
||||
Loader.Start(PathMcFolder & "versions\" & VersionName & "\")
|
||||
Loader.Start(Request.TargetVersionFolder)
|
||||
LoaderTaskbarAdd(Loader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
If ShowRibble Then FrmMain.BtnExtraDownload.Ribble()
|
||||
@@ -388,7 +401,7 @@ Retry:
|
||||
Dim InstallLoaders As New List(Of LoaderBase)
|
||||
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
|
||||
Sub(Task As LoaderTask(Of String, Integer))
|
||||
UnpackFiles(InstallTemp, FileAddress)
|
||||
UnpackFiles(InstallTemp, FileAddress, Task)
|
||||
Task.Progress = 0.5
|
||||
'复制 overrides 文件夹和 client-overrides 文件夹
|
||||
If Directory.Exists(InstallTemp & ArchiveBaseFolder & "overrides") Then
|
||||
@@ -420,58 +433,51 @@ Retry:
|
||||
End Select
|
||||
End If
|
||||
'添加下载文件
|
||||
FileList.Add(New NetFile(File("downloads").Select(Function(t) t.ToString).ToArray,
|
||||
PathMcFolder & "versions\" & VersionName & "\" & File("path").ToString,
|
||||
New FileChecker(ActualSize:=File("fileSize").ToObject(Of Long), Hash:=File("hashes")("sha1").ToString)))
|
||||
FileList.Add(New NetFile(File("downloads").Select(Function(t) t.ToString.Replace("://edge.forgecdn", "://media.forgecdn")).ToArray, '修复 #2390
|
||||
PathMcFolder & "versions\" & VersionName & "\" & File("path").ToString,
|
||||
New FileChecker(ActualSize:=File("fileSize").ToObject(Of Long), Hash:=File("hashes")("sha1").ToString), True))
|
||||
Next
|
||||
If FileList.Count > 0 Then
|
||||
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 {
|
||||
.TargetVersionName = VersionName,
|
||||
.TargetVersionFolder = $"{PathMcFolder}versions\{VersionName}\",
|
||||
.MinecraftName = MinecraftVersion,
|
||||
.ForgeVersion = ForgeVersion,
|
||||
.FabricVersion = FabricVersion
|
||||
}
|
||||
Dim InstallExpectTime As Double = 0
|
||||
For Each InstallLoader In InstallLoaders
|
||||
InstallExpectTime += InstallLoader.ProgressWeight
|
||||
Next
|
||||
Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request, True)
|
||||
If MergeLoaders Is Nothing Then Exit Sub
|
||||
Dim MergeExpectTime As Double = 0
|
||||
For Each MergeLoader In MergeLoaders
|
||||
MergeExpectTime += MergeLoader.ProgressWeight
|
||||
Next
|
||||
'构造 Libraries 加载器(为了使得 Mods 下载结束后再构造,这样才会下载 JumpLoader 文件)
|
||||
'构造 Libraries 加载器
|
||||
Dim LoadersLib As New List(Of LoaderBase)
|
||||
LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析游戏支持库文件(副加载器)", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionName))) With {.ProgressWeight = 1, .Show = False})
|
||||
LoadersLib.Add(New LoaderDownload("下载游戏支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 7, .Show = False})
|
||||
'构造总加载器
|
||||
Dim Loaders As New List(Of LoaderBase)
|
||||
Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallExpectTime})
|
||||
Loaders.Add(New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .ProgressWeight = MergeExpectTime})
|
||||
Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)})
|
||||
Loaders.Add(New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .ProgressWeight = MergeLoaders.Sum(Function(l) l.ProgressWeight)})
|
||||
Loaders.Add(New LoaderCombo(Of String)("下载游戏支持库文件", LoadersLib) With {.ProgressWeight = 8})
|
||||
Loaders.Add(New LoaderTask(Of String, String)("最终整理文件",
|
||||
Sub(Task As LoaderTask(Of String, String))
|
||||
'设置图标
|
||||
Dim VersionFolder As String = PathMcFolder & "versions\" & VersionName & "\"
|
||||
If Logo IsNot Nothing AndAlso File.Exists(Logo) Then
|
||||
File.Copy(Logo, VersionFolder & "PCL\Logo.png", True)
|
||||
WriteIni(VersionFolder & "PCL\Setup.ini", "Logo", "PCL\Logo.png")
|
||||
WriteIni(VersionFolder & "PCL\Setup.ini", "LogoCustom", "True")
|
||||
Log("[ModPack] 已设置整合包 Logo:" & Logo)
|
||||
End If
|
||||
'删除原始整合包文件
|
||||
For Each Target As String In {VersionFolder & "原始整合包.zip", VersionFolder & "原始整合包.mrpack"}
|
||||
If Not Setup.Get("ToolDownloadKeepModpack") AndAlso File.Exists(Target) Then
|
||||
Log("[ModPack] 根据设置要求删除原始整合包文件:" & Target)
|
||||
File.Delete(Target)
|
||||
End If
|
||||
Next
|
||||
End Sub) With {.ProgressWeight = 0.1, .Show = False})
|
||||
Sub(Task As LoaderTask(Of String, String))
|
||||
'设置图标
|
||||
Dim VersionFolder As String = PathMcFolder & "versions\" & VersionName & "\"
|
||||
If Logo IsNot Nothing AndAlso File.Exists(Logo) Then
|
||||
File.Copy(Logo, VersionFolder & "PCL\Logo.png", True)
|
||||
WriteIni(VersionFolder & "PCL\Setup.ini", "Logo", "PCL\Logo.png")
|
||||
WriteIni(VersionFolder & "PCL\Setup.ini", "LogoCustom", "True")
|
||||
Log("[ModPack] 已设置整合包 Logo:" & Logo)
|
||||
End If
|
||||
'删除原始整合包文件
|
||||
For Each Target As String In {VersionFolder & "原始整合包.zip", VersionFolder & "原始整合包.mrpack"}
|
||||
If Not Setup.Get("ToolDownloadKeepModpack") AndAlso File.Exists(Target) Then
|
||||
Log("[ModPack] 根据设置要求删除原始整合包文件:" & Target)
|
||||
File.Delete(Target)
|
||||
End If
|
||||
Next
|
||||
End Sub) With {.ProgressWeight = 0.1, .Show = False})
|
||||
|
||||
'重复任务检查
|
||||
Dim LoaderName As String = "Modrinth 整合包安装:" & VersionName & " "
|
||||
@@ -486,7 +492,7 @@ Retry:
|
||||
|
||||
'启动
|
||||
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
|
||||
Loader.Start(PathMcFolder & "versions\" & VersionName & "\")
|
||||
Loader.Start(Request.TargetVersionFolder)
|
||||
LoaderTaskbarAdd(Loader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
If ShowRibble Then FrmMain.BtnExtraDownload.Ribble()
|
||||
@@ -514,7 +520,7 @@ Retry:
|
||||
Dim InstallLoaders As New List(Of LoaderBase)
|
||||
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
|
||||
Sub(Task As LoaderTask(Of String, Integer))
|
||||
UnpackFiles(InstallTemp, FileAddress)
|
||||
UnpackFiles(InstallTemp, FileAddress, Task)
|
||||
Task.Progress = 0.5
|
||||
'复制结果
|
||||
If Directory.Exists(InstallTemp & ArchiveBaseFolder & "minecraft") Then
|
||||
@@ -533,18 +539,11 @@ Retry:
|
||||
End If
|
||||
Dim Request As New McInstallRequest With {
|
||||
.TargetVersionName = VersionName,
|
||||
.TargetVersionFolder = $"{PathMcFolder}versions\{VersionName}\",
|
||||
.MinecraftName = Json("gameVersion").ToString
|
||||
}
|
||||
Dim InstallExpectTime As Double = 0
|
||||
For Each InstallLoader In InstallLoaders
|
||||
InstallExpectTime += InstallLoader.ProgressWeight
|
||||
Next
|
||||
Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request, True)
|
||||
If MergeLoaders Is Nothing Then Exit Sub
|
||||
Dim MergeExpectTime As Double = 0
|
||||
For Each MergeLoader In MergeLoaders
|
||||
MergeExpectTime += MergeLoader.ProgressWeight
|
||||
Next
|
||||
'构造 Libraries 加载器(为了使得 Mods 下载结束后再构造,这样才会下载 JumpLoader 文件)
|
||||
Dim LoadersLib As New List(Of LoaderBase)
|
||||
LoadersLib.Add(New LoaderTask(Of String, String)("重命名版本 Json(副加载器)",
|
||||
@@ -565,8 +564,8 @@ Retry:
|
||||
LoadersLib.Add(New LoaderDownload("下载游戏支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 7, .Show = False})
|
||||
'构造总加载器
|
||||
Dim Loaders As New List(Of LoaderBase) From {
|
||||
New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .Block = False, .ProgressWeight = MergeExpectTime},
|
||||
New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .ProgressWeight = InstallExpectTime},
|
||||
New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .Block = False, .ProgressWeight = MergeLoaders.Sum(Function(l) l.ProgressWeight)},
|
||||
New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)},
|
||||
New LoaderCombo(Of String)("下载游戏支持库文件", LoadersLib) With {.ProgressWeight = 8}
|
||||
}
|
||||
|
||||
@@ -584,7 +583,7 @@ Retry:
|
||||
'启动
|
||||
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
|
||||
'If Archive IsNot Nothing Then Archive.Dispose() '解除占用,以免在加载器中触发 “正由另一进程使用,因此该进程无法访问此文件”
|
||||
Loader.Start(PathMcFolder & "versions\" & VersionName & "\")
|
||||
Loader.Start(Request.TargetVersionFolder)
|
||||
LoaderTaskbarAdd(Loader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
FrmMain.BtnExtraDownload.Ribble()
|
||||
@@ -607,12 +606,13 @@ Retry:
|
||||
If Validate.Validate(PackName) <> "" Then PackName = ""
|
||||
Dim VersionName As String = MyMsgBoxInput("输入版本名称", "", PackName, New ObjectModel.Collection(Of Validate) From {Validate})
|
||||
If VersionName Is Nothing Then Exit Sub
|
||||
'解压与配置文件
|
||||
Dim InstallTemp As String = PathTemp & "PackInstall\" & RandomInteger(0, 100000) & "\"
|
||||
'解压、配置设置文件
|
||||
Dim InstallTemp As String = $"{PathTemp}PackInstall\{RandomInteger(0, 100000)}\"
|
||||
Dim SetupFile As String = $"{PathMcFolder}versions\{VersionName}\PCL\Setup.ini"
|
||||
Dim InstallLoaders As New List(Of LoaderBase)
|
||||
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
|
||||
Sub(Task As LoaderTask(Of String, Integer))
|
||||
UnpackFiles(InstallTemp, FileAddress)
|
||||
UnpackFiles(InstallTemp, FileAddress, Task)
|
||||
Task.Progress = 0.5
|
||||
'复制结果
|
||||
If Directory.Exists(InstallTemp & ArchiveBaseFolder & ".minecraft") Then
|
||||
@@ -622,14 +622,52 @@ Retry:
|
||||
End If
|
||||
Task.Progress = 0.9
|
||||
'开启版本隔离
|
||||
WriteIni(PathMcFolder & "versions\" & VersionName & "\PCL\Setup.ini", "VersionArgumentIndie", 1)
|
||||
WriteIni(SetupFile, "VersionArgumentIndie", 1)
|
||||
'读取 MMC 设置文件(#2655)
|
||||
Try
|
||||
Dim MMCSetupFile As String = InstallTemp & ArchiveBaseFolder & "instance.cfg"
|
||||
If File.Exists(MMCSetupFile) Then
|
||||
'将其中的等号替换为冒号,以符合 ini 文件格式
|
||||
WriteFile(MMCSetupFile, ReadFile(MMCSetupFile).Replace("=", ":"))
|
||||
If ReadIni(MMCSetupFile, "OverrideCommands", False) Then
|
||||
Dim PreLaunchCommand As String = ReadIni(MMCSetupFile, "PreLaunchCommand")
|
||||
If PreLaunchCommand <> "" Then
|
||||
PreLaunchCommand = PreLaunchCommand.Replace("\""", """").
|
||||
Replace("$INST_JAVA", "{java}javaw.exe").
|
||||
Replace("$INST_MC_DIR\", "{minecraft}").Replace("$INST_MC_DIR", "{minecraft}").
|
||||
Replace("$INST_DIR\", "{verpath}").Replace("$INST_DIR", "{verpath}").
|
||||
Replace("$INST_ID", "{name}").Replace("$INST_NAME", "{name}")
|
||||
WriteIni(SetupFile, "VersionAdvanceRun", PreLaunchCommand)
|
||||
Log("[ModPack] 迁移 MultiMC 版本独立设置:启动前执行命令:" & PreLaunchCommand)
|
||||
End If
|
||||
End If
|
||||
If ReadIni(MMCSetupFile, "JoinServerOnLaunch", False) Then
|
||||
Dim ServerAddress As String = ReadIni(MMCSetupFile, "JoinServerOnLaunchAddress").Replace("\""", """")
|
||||
WriteIni(SetupFile, "VersionServerEnter", ServerAddress)
|
||||
Log("[ModPack] 迁移 MultiMC 版本独立设置:自动进入服务器:" & ServerAddress)
|
||||
End If
|
||||
If ReadIni(MMCSetupFile, "IgnoreJavaCompatibility", False) Then
|
||||
WriteIni(SetupFile, "VersionAdvanceJava", True)
|
||||
Log("[ModPack] 迁移 MultiMC 版本独立设置:忽略 Java 兼容性警告")
|
||||
End If
|
||||
Dim Logo As String = ReadIni(MMCSetupFile, "iconKey", "")
|
||||
If Logo <> "" AndAlso File.Exists($"{InstallTemp}{ArchiveBaseFolder}{Logo}.png") Then
|
||||
WriteIni(SetupFile, "LogoCustom", True)
|
||||
WriteIni(SetupFile, "Logo", "PCL\Logo.png")
|
||||
CopyFile($"{InstallTemp}{ArchiveBaseFolder}{Logo}.png", $"{PathMcFolder}versions\{VersionName}\PCL\Logo.png")
|
||||
Log($"[ModPack] 迁移 MultiMC 版本独立设置:版本图标({Logo}.png)")
|
||||
End If
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, $"读取 MMC 配置文件失败({InstallTemp}{ArchiveBaseFolder}instance.cfg)")
|
||||
End Try
|
||||
End Sub) With {.ProgressWeight = New FileInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s
|
||||
'构造版本安装请求
|
||||
If PackJson("components") Is Nothing Then
|
||||
Hint("该整合包未提供游戏版本信息,无法安装!", HintType.Critical)
|
||||
Exit Sub
|
||||
End If
|
||||
Dim Request As New McInstallRequest With {.TargetVersionName = VersionName}
|
||||
Dim Request As New McInstallRequest With {.TargetVersionName = VersionName, .TargetVersionFolder = $"{PathMcFolder}versions\{VersionName}\"}
|
||||
For Each Component In PackJson("components")
|
||||
Select Case If(Component("uid"), "").ToString
|
||||
Case "org.lwjgl"
|
||||
@@ -638,27 +676,24 @@ Retry:
|
||||
Request.MinecraftName = Component("version")
|
||||
Case "net.minecraftforge"
|
||||
Request.ForgeVersion = Component("version")
|
||||
Case "net.fabricmc.fabric-loader"
|
||||
Request.FabricVersion = Component("version")
|
||||
Case "org.quiltmc.quilt-loader" 'eg. 1.0.0
|
||||
Hint("PCL 暂不支持安装需要 Quilt 的整合包!", HintType.Critical)
|
||||
Exit Sub
|
||||
End Select
|
||||
Next
|
||||
'构造加载器
|
||||
Dim InstallExpectTime As Double = 0
|
||||
For Each InstallLoader In InstallLoaders
|
||||
InstallExpectTime += InstallLoader.ProgressWeight
|
||||
Next
|
||||
Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request, True)
|
||||
If MergeLoaders Is Nothing Then Exit Sub
|
||||
Dim MergeExpectTime As Double = 0
|
||||
For Each MergeLoader In MergeLoaders
|
||||
MergeExpectTime += MergeLoader.ProgressWeight
|
||||
Next
|
||||
'构造 Libraries 加载器(为了使得 Mods 下载结束后再构造,这样才会下载 JumpLoader 文件)
|
||||
'构造 Libraries 加载器
|
||||
Dim LoadersLib As New List(Of LoaderBase)
|
||||
LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析游戏支持库文件(副加载器)", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionName))) With {.ProgressWeight = 1, .Show = False})
|
||||
LoadersLib.Add(New LoaderDownload("下载游戏支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 7, .Show = False})
|
||||
'构造总加载器
|
||||
Dim Loaders As New List(Of LoaderBase)
|
||||
Loaders.Add(New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .Block = False, .ProgressWeight = MergeExpectTime})
|
||||
Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .ProgressWeight = InstallExpectTime})
|
||||
Loaders.Add(New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .Block = False, .ProgressWeight = MergeLoaders.Sum(Function(l) l.ProgressWeight)})
|
||||
Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)})
|
||||
Loaders.Add(New LoaderCombo(Of String)("下载游戏支持库文件", LoadersLib) With {.ProgressWeight = 8})
|
||||
|
||||
'重复任务检查
|
||||
@@ -674,7 +709,7 @@ Retry:
|
||||
|
||||
'启动
|
||||
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
|
||||
Loader.Start(PathMcFolder & "versions\" & VersionName & "\")
|
||||
Loader.Start(Request.TargetVersionFolder)
|
||||
LoaderTaskbarAdd(Loader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
FrmMain.BtnExtraDownload.Ribble()
|
||||
@@ -702,7 +737,7 @@ Retry:
|
||||
Dim InstallLoaders As New List(Of LoaderBase)
|
||||
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
|
||||
Sub(Task As LoaderTask(Of String, Integer))
|
||||
UnpackFiles(InstallTemp, FileAddress)
|
||||
UnpackFiles(InstallTemp, FileAddress, Task)
|
||||
Task.Progress = 0.5
|
||||
'复制结果
|
||||
If Directory.Exists(InstallTemp & ArchiveBaseFolder & "overrides") Then
|
||||
@@ -727,31 +762,28 @@ Retry:
|
||||
Hint("该整合包未提供游戏版本信息,无法安装!", HintType.Critical)
|
||||
Exit Sub
|
||||
End If
|
||||
If Addons.ContainsKey("quilt") Then
|
||||
Hint("PCL 暂不支持安装需要 Quilt 的整合包!", HintType.Critical)
|
||||
Exit Sub
|
||||
End If
|
||||
Dim Request As New McInstallRequest With {
|
||||
.TargetVersionName = VersionName,
|
||||
.TargetVersionFolder = $"{PathMcFolder}versions\{VersionName}\",
|
||||
.MinecraftName = Addons("game"),
|
||||
.OptiFineVersion = If(Addons.ContainsKey("optifine"), Addons("optifine"), Nothing),
|
||||
.ForgeVersion = If(Addons.ContainsKey("forge"), Addons("forge"), Nothing),
|
||||
.FabricVersion = If(Addons.ContainsKey("fabric"), Addons("fabric"), Nothing)
|
||||
}
|
||||
Dim InstallExpectTime As Double = 0
|
||||
For Each InstallLoader In InstallLoaders
|
||||
InstallExpectTime += InstallLoader.ProgressWeight
|
||||
Next
|
||||
Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request, True)
|
||||
If MergeLoaders Is Nothing Then Exit Sub
|
||||
Dim MergeExpectTime As Double = 0
|
||||
For Each MergeLoader In MergeLoaders
|
||||
MergeExpectTime += MergeLoader.ProgressWeight
|
||||
Next
|
||||
'构造 Libraries 加载器(为了使得 Mods 下载结束后再构造,这样才会下载 JumpLoader 文件)
|
||||
'构造 Libraries 加载器
|
||||
Dim LoadersLib As New List(Of LoaderBase)
|
||||
LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析游戏支持库文件(副加载器)", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionName))) With {.ProgressWeight = 1, .Show = False})
|
||||
LoadersLib.Add(New LoaderDownload("下载游戏支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 7, .Show = False})
|
||||
'构造总加载器
|
||||
Dim Loaders As New List(Of LoaderBase)
|
||||
Loaders.Add(New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .Block = False, .ProgressWeight = MergeExpectTime})
|
||||
Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .ProgressWeight = InstallExpectTime})
|
||||
Loaders.Add(New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .Block = False, .ProgressWeight = MergeLoaders.Sum(Function(l) l.ProgressWeight)})
|
||||
Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)})
|
||||
Loaders.Add(New LoaderCombo(Of String)("下载游戏支持库文件", LoadersLib) With {.ProgressWeight = 8})
|
||||
|
||||
'重复任务检查
|
||||
@@ -768,7 +800,7 @@ Retry:
|
||||
'启动
|
||||
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
|
||||
'If Archive IsNot Nothing Then Archive.Dispose() '解除占用,以免在加载器中触发 “正由另一进程使用,因此该进程无法访问此文件”
|
||||
Loader.Start(PathMcFolder & "versions\" & VersionName & "\")
|
||||
Loader.Start(Request.TargetVersionFolder)
|
||||
LoaderTaskbarAdd(Loader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
FrmMain.BtnExtraDownload.Ribble()
|
||||
@@ -788,7 +820,7 @@ Retry:
|
||||
If String.IsNullOrWhiteSpace(NewName) Then Exit Sub
|
||||
'解压
|
||||
Hint("正在解压压缩包……")
|
||||
UnpackFiles(TargetFolder, FileAddress)
|
||||
UnpackFiles(TargetFolder, FileAddress, Nothing)
|
||||
'加入文件夹列表
|
||||
PageSelectLeft.AddFolder(TargetFolder, NewName, False)
|
||||
Hint("已加入游戏文件夹列表:" & NewName, HintType.Finish)
|
||||
|
||||
@@ -95,8 +95,8 @@
|
||||
TimerLog()
|
||||
'设置窗口标题
|
||||
For i = 1 To 3
|
||||
If IsWindowFinished AndAlso WindowTitle <> "" AndAlso State = MinecraftState.Running AndAlso Not GameProcess.HasExited Then
|
||||
Dim RealTitle As String = WindowTitle.Replace("{time}", Date.Now.ToShortTimeString)
|
||||
If IsWindowFinished AndAlso IsWindowAppeared AndAlso WindowTitle <> "" AndAlso State = MinecraftState.Running AndAlso Not GameProcess.HasExited Then
|
||||
Dim RealTitle As String = WindowTitle.Replace("{date}", Date.Now.ToString("yyyy/M/d")).Replace("{time}", Date.Now.ToString("HH:mm:ss"))
|
||||
SetWindowText(WindowHandle, RealTitle.ToCharArray)
|
||||
End If
|
||||
Thread.Sleep(64)
|
||||
@@ -116,7 +116,7 @@
|
||||
Get
|
||||
Return _State
|
||||
End Get
|
||||
Set(ByVal value As MinecraftState)
|
||||
Set(value As MinecraftState)
|
||||
If _State = value Then Exit Property
|
||||
_State = value
|
||||
WatcherStateChanged()
|
||||
@@ -143,7 +143,7 @@
|
||||
'输出文本
|
||||
Dim Copyed As New List(Of String)
|
||||
SyncLock WaitingLogLock
|
||||
If WaitingLog.Count = 0 Then Exit Sub
|
||||
If Not WaitingLog.Any() Then Exit Sub
|
||||
Copyed = WaitingLog
|
||||
WaitingLog = New List(Of String)(1000)
|
||||
End SyncLock
|
||||
@@ -192,7 +192,7 @@
|
||||
If LogProgress < 2 AndAlso Text.Contains("Setting user:") Then
|
||||
WatcherLog("日志 2/5:游戏用户已设置") '仅确保支持 Minecraft 1.7+
|
||||
LogProgress = 2
|
||||
ElseIf LogProgress < 3 AndAlso Text.ToLower.Contains("lwjgl version") Then
|
||||
ElseIf LogProgress < 3 AndAlso Text.ContainsF("lwjgl version", True) Then
|
||||
WatcherLog("日志 3/5:LWJGL 版本已确认")
|
||||
LogProgress = 3
|
||||
ElseIf LogProgress < 4 AndAlso (Text.Contains("OpenAL initialized") OrElse Text.Contains("Starting up SoundSystem")) Then
|
||||
@@ -248,6 +248,9 @@
|
||||
|
||||
'窗口检查
|
||||
Private IsWindowAppeared As Boolean = False
|
||||
''' <summary>
|
||||
''' 窗口检查是否已经完成。这不一定代表着找到了窗口(如果没有找到,IsWindowAppeared 仍为 False)。
|
||||
''' </summary>
|
||||
Private IsWindowFinished As Boolean = False
|
||||
Private WindowHandle As IntPtr
|
||||
Private Sub TimerWindow()
|
||||
@@ -255,27 +258,36 @@
|
||||
If GameProcess.HasExited Then Exit Sub
|
||||
If IsWindowFinished Then Exit Sub
|
||||
'获取全部窗口,检查是否有新增的
|
||||
Dim MinecraftWindow = TryGetMinecraftWindow()
|
||||
Dim MinecraftWindow As KeyValuePair(Of IntPtr, String)? = Nothing
|
||||
Try
|
||||
MinecraftWindow = TryGetMinecraftWindow()
|
||||
Catch ex As ComponentModel.Win32Exception
|
||||
'拒绝访问(#1062)
|
||||
Log(ex, "由于反作弊或安全软件拦截,PCL 无法操作游戏窗口", LogLevel.Hint)
|
||||
IsWindowFinished = True
|
||||
End Try
|
||||
If MinecraftWindow Is Nothing Then Exit Sub
|
||||
Dim MinecraftWindowName = MinecraftWindow.Value.Value, MinecraftWindowHandle = MinecraftWindow.Value.Key
|
||||
'已找到窗口
|
||||
If Not MinecraftWindowName.StartsWith("FML") Then
|
||||
If Not MinecraftWindowName.StartsWithF("FML") Then
|
||||
'已找到 Minecraft 窗口
|
||||
WindowHandle = MinecraftWindowHandle
|
||||
WatcherLog("Minecraft 窗口已加载:" & MinecraftWindowName & "(" & MinecraftWindowHandle.ToInt64 & ")")
|
||||
WatcherLog($"Minecraft 窗口已加载:{MinecraftWindowName}({MinecraftWindowHandle.ToInt64})")
|
||||
IsWindowFinished = True
|
||||
'最大化
|
||||
If Setup.Get("LaunchArgumentWindowType") = 4 Then
|
||||
RunInNewThread(Sub()
|
||||
Try
|
||||
'如果最大化导致屏幕渲染大小不对,那是 MC 的 Bug,不是我的 Bug
|
||||
'……虽然我很想这样说,但总有人反馈,算了
|
||||
Thread.Sleep(2000)
|
||||
ShowWindow(WindowHandle, 3)
|
||||
Catch ex As Exception
|
||||
Log(ex, "最大化 Minecraft 窗口时出现错误")
|
||||
End Try
|
||||
End Sub, "MinecraftWindowMaximize")
|
||||
RunInNewThread(
|
||||
Sub()
|
||||
Try
|
||||
'如果最大化导致屏幕渲染大小不对,那是 MC 的 Bug,不是我的 Bug
|
||||
'……虽然我很想这样说,但总有人反馈,算了
|
||||
Thread.Sleep(2000)
|
||||
ShowWindow(WindowHandle, 3)
|
||||
WatcherLog($"已最大化 Minecraft 窗口:{MinecraftWindowHandle.ToInt64}")
|
||||
Catch ex As Exception
|
||||
Log(ex, "最大化 Minecraft 窗口时出现错误")
|
||||
End Try
|
||||
End Sub, "MinecraftWindowMaximize")
|
||||
End If
|
||||
ElseIf Not IsWindowAppeared Then
|
||||
'已找到 FML 窗口
|
||||
@@ -306,12 +318,16 @@
|
||||
Dim WindowText As String = str.ToString
|
||||
'有的 Mod 可以修改窗口标题,所以不能检测是否为 Minecraft 打头,这并不准确
|
||||
'部分版本会搞个 GLFW message window 出来所以得反选
|
||||
If Not (WindowText.StartsWith("FML") OrElse (WindowText <> "PopupMessageWindow") AndAlso Not WindowText.StartsWith("GLFW")) Then Exit Sub
|
||||
If Not (WindowText.StartsWithF("FML") OrElse (WindowText <> "PopupMessageWindow") AndAlso Not WindowText.StartsWithF("GLFW")) Then Exit Sub
|
||||
'获取窗口关联的进程
|
||||
Dim ProcessId As Integer
|
||||
GetWindowThreadProcessId(hwnd, ProcessId)
|
||||
Dim WindowProcess = Process.GetProcessById(ProcessId)
|
||||
If WindowProcess.StartTime < GameProcess.StartTime Then Exit Sub '需要是此后启动的进程
|
||||
Try
|
||||
If Process.GetProcessById(ProcessId).StartTime < GameProcess.StartTime Then Exit Sub '需要是此后启动的进程
|
||||
Catch ex As Exception
|
||||
Log(ex, "枚举 Minecraft 窗口进程失败")
|
||||
Exit Sub
|
||||
End Try
|
||||
'返回
|
||||
TryGetMinecraftWindow = New KeyValuePair(Of IntPtr, String)(hwnd, WindowText)
|
||||
End Sub, 0)
|
||||
|
||||
@@ -29,18 +29,19 @@
|
||||
<StackPanel Grid.Column="3" Grid.ColumnSpan="2" Grid.Row="1" VerticalAlignment="Bottom" Margin="0,1,0,0" Orientation="Horizontal"
|
||||
IsHitTestVisible="False" SnapsToDevicePixels="False" UseLayoutRounding="False">
|
||||
<TextBlock x:Name="LabTitle" TextTrimming="WordEllipsis" FontSize="14" Foreground="{DynamicResource ColorBrush1}" />
|
||||
<TextBlock x:Name="LabTitleRaw" TextTrimming="WordEllipsis" FontSize="12" Foreground="{StaticResource ColorBrushGray1}" Opacity="0.4" VerticalAlignment="Bottom" />
|
||||
<TextBlock x:Name="LabTitleRaw" TextTrimming="WordEllipsis" FontSize="12" Foreground="{StaticResource ColorBrushGray1}" Opacity="0.4" VerticalAlignment="Bottom" Visibility="Collapsed" />
|
||||
</StackPanel>
|
||||
<!-- Tag 与 详情 -->
|
||||
<StackPanel x:Name="PanTags" Grid.Row="2" Grid.Column="3" Orientation="Horizontal" VerticalAlignment="Bottom" Margin="-1,0,0,0">
|
||||
<StackPanel x:Name="PanTags" Grid.Row="2" Grid.Column="3" Orientation="Horizontal" VerticalAlignment="Bottom" Margin="-1,0,1,0" Visibility="Collapsed">
|
||||
<!--<Border Background="{DynamicResource ColorBrush6}" Padding="3,1" CornerRadius="3" Margin="0,0,4,0" SnapsToDevicePixels="True" UseLayoutRounding="False">
|
||||
<TextBlock Text="科技" Foreground="{DynamicResource ColorBrush2}" FontSize="11" />
|
||||
</Border>-->
|
||||
</StackPanel>
|
||||
<TextBlock x:Name="LabInfo" Grid.Row="2" Grid.Column="4" VerticalAlignment="Bottom" Margin="1,0,3,0.5"
|
||||
<TextBlock x:Name="LabInfo" Grid.Row="2" Grid.Column="4" VerticalAlignment="Bottom" Margin="0,0,3,0.5"
|
||||
TextTrimming="CharacterEllipsis" FontSize="12" Foreground="{StaticResource ColorBrushGray3}"
|
||||
IsHitTestVisible="True" SnapsToDevicePixels="False" UseLayoutRounding="False"
|
||||
ToolTipService.InitialShowDelay="150" ToolTipService.Placement="Relative" ToolTipService.PlacementRectangle="-8,-6,500,10000">
|
||||
IsHitTestVisible="True" SnapsToDevicePixels="False" UseLayoutRounding="False"
|
||||
ToolTipService.InitialShowDelay="100" ToolTipService.BetweenShowDelay="100"
|
||||
ToolTipService.Placement="Relative" ToolTipService.PlacementRectangle="-16,-10,500,10000">
|
||||
<TextBlock.ToolTip>
|
||||
<ToolTip x:Name="ToolTipInfo" Style="{StaticResource ExtendTextToolTip}" />
|
||||
</TextBlock.ToolTip>
|
||||
@@ -49,17 +50,17 @@
|
||||
<Grid Grid.Row="3" Grid.Column="3" Grid.ColumnSpan="2" VerticalAlignment="Bottom"
|
||||
IsHitTestVisible="False" SnapsToDevicePixels="False" UseLayoutRounding="False">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="CompItemVersion" />
|
||||
<ColumnDefinition Width="0.7*" x:Name="ColumnVersionMargin" />
|
||||
<ColumnDefinition x:Name="ColumnVersion1" Width="Auto" />
|
||||
<ColumnDefinition x:Name="ColumnVersion2" Width="Auto" SharedSizeGroup="CompItemVersion" />
|
||||
<ColumnDefinition x:Name="ColumnVersion3" Width="0.7*" />
|
||||
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="55" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" MinWidth="57" SharedSizeGroup="CompItemTime" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition x:Name="ColumnTime1" Width="Auto" />
|
||||
<ColumnDefinition x:Name="ColumnTime2" Width="Auto" MinWidth="57" SharedSizeGroup="CompItemTime" />
|
||||
<ColumnDefinition x:Name="ColumnTime3" Width="1*" />
|
||||
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="CompItemSource" />
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
Public Class MyCompItem
|
||||
|
||||
#Region "基础属性"
|
||||
Public Uuid As Integer = GetUuid()
|
||||
|
||||
#Region "Logo"
|
||||
|
||||
'Logo
|
||||
Private _Logo As String = ""
|
||||
Public Property Logo As String
|
||||
Get
|
||||
@@ -13,15 +14,15 @@
|
||||
_Logo = value
|
||||
Dim FileAddress = PathTemp & "CompLogo\" & GetHash(_Logo) & ".png"
|
||||
Try
|
||||
If _Logo.ToLower.StartsWith("http") Then
|
||||
If _Logo.StartsWithF("http", True) Then
|
||||
'网络图片
|
||||
If File.Exists(FileAddress) Then
|
||||
PathLogo.Source = New MyBitmap(FileAddress)
|
||||
ElseIf _Logo.ToLower.EndsWith(".webp") Then 'Modrinth 林业 Mod 使用了不支持的 WebP 格式 Logo
|
||||
ElseIf _Logo.EndsWithF(".webp", True) Then 'Modrinth 林业 Mod 使用了不支持的 WebP 格式 Logo
|
||||
Log($"[Comp] 发现不支持的 WebP 格式图标,已更改为默认图标:{_Logo}")
|
||||
PathLogo.Source = New MyBitmap("pack://application:,,,/images/Icons/NoIcon.png")
|
||||
PathLogo.Source = New MyBitmap(PathImage & "Icons/NoIcon.png")
|
||||
Else
|
||||
PathLogo.Source = New MyBitmap("pack://application:,,,/images/Icons/NoIcon.png")
|
||||
PathLogo.Source = New MyBitmap(PathImage & "Icons/NoIcon.png")
|
||||
RunInNewThread(Sub() LogoLoader(FileAddress), "Comp Logo Loader " & Uuid & "#", ThreadPriority.BelowNormal)
|
||||
End If
|
||||
Else
|
||||
@@ -44,43 +45,116 @@
|
||||
End Try
|
||||
End Set
|
||||
End Property
|
||||
|
||||
Private Sub LogoLoader(Address As String)
|
||||
Dim Retry As Boolean = False
|
||||
'后台加载 Logo
|
||||
Private Sub LogoLoader(LocalFileAddress As String)
|
||||
Dim Retried As Boolean = False
|
||||
Dim DownloadEnd As String = GetUuid()
|
||||
RetryStart:
|
||||
Try
|
||||
NetDownload(_Logo, Address & DownloadEnd)
|
||||
'CurseForge 图片使用缩略图
|
||||
Dim Url As String = _Logo
|
||||
If Url.Contains("/256/256/") AndAlso GetPixelSize(1) <= 1.25 AndAlso Not Retried Then
|
||||
Url = Url.Replace("/256/256/", "/64/64/") '#3075:部分 Mod 不存在 64x64 图标,所以重试时不再缩小
|
||||
End If
|
||||
'下载图片
|
||||
NetDownload(Url, LocalFileAddress & DownloadEnd, True)
|
||||
Dim LoadError As Exception = Nothing
|
||||
RunInUiWait(Sub()
|
||||
Try
|
||||
'在地址更换时取消加载
|
||||
If Not Address = PathTemp & "CompLogo\" & GetHash(_Logo) & ".png" Then Exit Sub
|
||||
'在完成正常加载后才保存缓存图片
|
||||
PathLogo.Source = New MyBitmap(Address & DownloadEnd)
|
||||
Catch ex As Exception
|
||||
Log(ex, "读取资源工程图标失败(" & Address & ")")
|
||||
File.Delete(Address & DownloadEnd)
|
||||
LoadError = ex
|
||||
End Try
|
||||
End Sub)
|
||||
RunInUiWait(
|
||||
Sub()
|
||||
Try
|
||||
'在地址更换时取消加载
|
||||
If LocalFileAddress <> $"{PathTemp}CompLogo\{GetHash(_Logo)}.png" Then Exit Sub
|
||||
'在完成正常加载后才保存缓存图片
|
||||
PathLogo.Source = New MyBitmap(LocalFileAddress & DownloadEnd)
|
||||
Catch ex As Exception
|
||||
Log(ex, $"读取资源工程图标失败({LocalFileAddress})")
|
||||
File.Delete(LocalFileAddress & DownloadEnd)
|
||||
LoadError = ex
|
||||
End Try
|
||||
End Sub)
|
||||
If LoadError IsNot Nothing Then Throw LoadError
|
||||
If File.Exists(Address) Then
|
||||
File.Delete(Address & DownloadEnd)
|
||||
If File.Exists(LocalFileAddress) Then
|
||||
File.Delete(LocalFileAddress & DownloadEnd)
|
||||
Else
|
||||
FileIO.FileSystem.MoveFile(Address & DownloadEnd, Address)
|
||||
FileIO.FileSystem.MoveFile(LocalFileAddress & DownloadEnd, LocalFileAddress)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
If Not Retry Then
|
||||
Retry = True
|
||||
If Not Retried Then
|
||||
Retried = True
|
||||
GoTo RetryStart
|
||||
Else
|
||||
Log(ex, $"下载资源工程图标失败({_Logo})")
|
||||
RunInUi(Sub() PathLogo.Source = New MyBitmap("pack://application:,,,/images/Icons/NoIcon.png"))
|
||||
RunInUi(Sub() PathLogo.Source = New MyBitmap(PathImage & "Icons/NoIcon.png"))
|
||||
End If
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
'标题
|
||||
Public Property Title As String
|
||||
Get
|
||||
Return LabTitle.Text
|
||||
End Get
|
||||
Set(value As String)
|
||||
If LabTitle.Text = value Then Exit Property
|
||||
LabTitle.Text = value
|
||||
End Set
|
||||
End Property
|
||||
|
||||
'副标题
|
||||
Public Property SubTitle As String
|
||||
Get
|
||||
Return If(LabTitleRaw?.Text, "")
|
||||
End Get
|
||||
Set(value As String)
|
||||
If LabTitleRaw.Text = value Then Exit Property
|
||||
LabTitleRaw.Text = value
|
||||
LabTitleRaw.Visibility = If(value = "", Visibility.Collapsed, Visibility.Visible)
|
||||
End Set
|
||||
End Property
|
||||
|
||||
'描述
|
||||
Public Property Description As String
|
||||
Get
|
||||
Return LabInfo.Text
|
||||
End Get
|
||||
Set(value As String)
|
||||
If LabInfo.Text = value Then Exit Property
|
||||
LabInfo.Text = value
|
||||
End Set
|
||||
End Property
|
||||
'指向时扩展描述
|
||||
Private Sub LabInfo_MouseEnter(sender As Object, e As MouseEventArgs) Handles LabInfo.MouseEnter
|
||||
If IsTextTrimmed(LabInfo) Then
|
||||
ToolTipInfo.Content = LabInfo.Text
|
||||
ToolTipInfo.Width = LabInfo.ActualWidth + 25
|
||||
LabInfo.ToolTip = ToolTipInfo
|
||||
Else
|
||||
LabInfo.ToolTip = Nothing
|
||||
End If
|
||||
End Sub
|
||||
Private Function IsTextTrimmed(textBlock As TextBlock) As Boolean
|
||||
Dim typeface As New Typeface(textBlock.FontFamily, textBlock.FontStyle, textBlock.FontWeight, textBlock.FontStretch)
|
||||
Dim formattedText As New FormattedText(textBlock.Text, Thread.CurrentThread.CurrentCulture, textBlock.FlowDirection, typeface, textBlock.FontSize, textBlock.Foreground, DPI)
|
||||
Return formattedText.Width > textBlock.ActualWidth
|
||||
End Function
|
||||
|
||||
'Tag
|
||||
Public WriteOnly Property Tags As List(Of String)
|
||||
Set(value As List(Of String))
|
||||
PanTags.Children.Clear()
|
||||
PanTags.Visibility = If(value.Any(), Visibility.Visible, Visibility.Collapsed)
|
||||
For Each TagText In value
|
||||
Dim NewTag = GetObjectFromXML(
|
||||
"<Border xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
|
||||
Background=""#11000000"" Padding=""3,1"" CornerRadius=""3"" Margin=""0,0,3,0""
|
||||
SnapsToDevicePixels=""True"" UseLayoutRounding=""False"">
|
||||
<TextBlock Text=""" & TagText & """ Foreground=""#868686"" FontSize=""11"" />
|
||||
</Border>")
|
||||
PanTags.Children.Add(NewTag)
|
||||
Next
|
||||
End Set
|
||||
End Property
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "点击"
|
||||
@@ -94,11 +168,43 @@ RetryStart:
|
||||
Log("[Control] 按下资源工程列表项:" & LabTitle.Text)
|
||||
End If
|
||||
End Sub
|
||||
Private Sub ProjectClick(sender As MyCompItem, e As EventArgs) Handles Me.Click
|
||||
'记录当前展开的卡片标题(#2712)
|
||||
Dim Titles As New List(Of String)
|
||||
If FrmMain.PageCurrent.Page = FormMain.PageType.CompDetail Then
|
||||
For Each Card As MyCard In FrmDownloadCompDetail.PanMain.Children
|
||||
If Card.Title <> "" AndAlso Not Card.IsSwaped Then Titles.Add(Card.Title)
|
||||
Next
|
||||
Log("[Comp] 记录当前已展开的卡片:" & String.Join("、", Titles))
|
||||
FrmMain.PageCurrent.Additional(1) = Titles
|
||||
End If
|
||||
'打开详情页
|
||||
Dim TargetVersion As String
|
||||
Dim TargetLoader As CompModLoaderType
|
||||
If FrmMain.PageCurrent.Page = FormMain.PageType.CompDetail Then
|
||||
TargetVersion = FrmMain.PageCurrent.Additional(2)
|
||||
TargetLoader = FrmMain.PageCurrent.Additional(3)
|
||||
Else
|
||||
Select Case CType(sender.Tag, CompProject).Type
|
||||
Case CompType.Mod
|
||||
TargetVersion = If(PageDownloadMod.Loader.Input.GameVersion, "")
|
||||
TargetLoader = PageDownloadMod.Loader.Input.ModLoader
|
||||
Case CompType.ModPack
|
||||
TargetVersion = If(PageDownloadPack.Loader.Input.GameVersion, "")
|
||||
Case Else 'CompType.ResourcePack
|
||||
'FUTURE: Res
|
||||
TargetVersion = "" 'If(PageDownloadResource.Loader.Input.GameVersion, "")
|
||||
End Select
|
||||
End If
|
||||
If CType(sender.Tag, CompProject).Type <> CompType.Mod Then TargetLoader = CompModLoaderType.Any
|
||||
FrmMain.PageChange(New FormMain.PageStackData With {.Page = FormMain.PageType.CompDetail,
|
||||
.Additional = {sender.Tag, New List(Of String), TargetVersion, TargetLoader}})
|
||||
End Sub
|
||||
|
||||
'鼠标点击判定
|
||||
Private IsMouseDown As Boolean = False
|
||||
Private Sub Button_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles Me.PreviewMouseLeftButtonDown
|
||||
If IsMouseOver Then IsMouseDown = True
|
||||
If IsMouseOver AndAlso CanInteraction Then IsMouseDown = True
|
||||
End Sub
|
||||
Private Sub Button_MouseLeave(sender As Object, e As Object) Handles Me.MouseLeave, Me.PreviewMouseLeftButtonUp
|
||||
IsMouseDown = False
|
||||
@@ -139,9 +245,12 @@ RetryStart:
|
||||
#End Region
|
||||
|
||||
Private StateLast As String
|
||||
Public HasAnimation As Boolean = True
|
||||
''' <summary>
|
||||
''' 是否允许交互。目前仅用于 PageDownloadCompDetail 的顶部栏展示:若关闭碰撞检测,则无法展开 Tooltip。
|
||||
''' </summary>
|
||||
Public CanInteraction As Boolean = True
|
||||
Public Sub RefreshColor(sender As Object, e As EventArgs) Handles Me.MouseEnter, Me.MouseLeave, Me.MouseLeftButtonDown, Me.MouseLeftButtonUp
|
||||
If Not HasAnimation Then Exit Sub
|
||||
If Not CanInteraction Then Exit Sub
|
||||
'判断当前颜色
|
||||
Dim StateNew As String, Time As Integer
|
||||
If IsMouseOver Then
|
||||
@@ -188,20 +297,4 @@ RetryStart:
|
||||
End If
|
||||
End Sub
|
||||
|
||||
'指向时扩展描述
|
||||
Private Sub LabInfo_MouseEnter(sender As Object, e As MouseEventArgs) Handles LabInfo.MouseEnter
|
||||
If IsTextTrimmed(LabInfo) Then
|
||||
ToolTipInfo.Content = LabInfo.Text
|
||||
ToolTipInfo.Width = LabInfo.ActualWidth + 25
|
||||
LabInfo.ToolTip = ToolTipInfo
|
||||
Else
|
||||
LabInfo.ToolTip = Nothing
|
||||
End If
|
||||
End Sub
|
||||
Private Function IsTextTrimmed(textBlock As TextBlock) As Boolean
|
||||
Dim typeface As New Typeface(textBlock.FontFamily, textBlock.FontStyle, textBlock.FontWeight, textBlock.FontStretch)
|
||||
Dim formattedText As New FormattedText(textBlock.Text, Thread.CurrentThread.CurrentCulture, textBlock.FlowDirection, typeface, textBlock.FontSize, textBlock.Foreground, DPI)
|
||||
Return formattedText.Width > textBlock.ActualWidth
|
||||
End Function
|
||||
|
||||
End Class
|
||||
@@ -0,0 +1,60 @@
|
||||
<Grid x:Class="MyLocalModItem"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:PCL;assembly="
|
||||
x:Name="PanBack" Height="44"
|
||||
RenderTransformOrigin="0.5,0.5" Background="{StaticResource ColorBrushSemiTransparent}" SnapsToDevicePixels="True">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="6" />
|
||||
<ColumnDefinition Width="34" />
|
||||
<ColumnDefinition Width="7" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="4" x:Name="ColumnPaddingRight" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="1*" />
|
||||
<RowDefinition Height="17" />
|
||||
<RowDefinition Height="18" />
|
||||
<RowDefinition Height="1*" />
|
||||
</Grid.RowDefinitions>
|
||||
<!-- Logo -->
|
||||
<Border Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" IsHitTestVisible="False" SnapsToDevicePixels="True" UseLayoutRounding="True"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Center" Width="34" Height="34">
|
||||
<Border.Clip>
|
||||
<RectangleGeometry Rect="0,0,34,34" RadiusX="6" RadiusY="6" />
|
||||
</Border.Clip>
|
||||
<Image x:Name="PathLogo" RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
</Border>
|
||||
<!-- 标题 -->
|
||||
<StackPanel Grid.Column="3" Grid.ColumnSpan="2" Grid.Row="1" VerticalAlignment="Bottom" Margin="0,1,0,0" Orientation="Horizontal"
|
||||
SnapsToDevicePixels="False" UseLayoutRounding="False">
|
||||
<TextBlock x:Name="LabTitle" TextTrimming="WordEllipsis" FontSize="14" IsHitTestVisible="False" />
|
||||
<TextBlock x:Name="LabTitleRaw" TextTrimming="WordEllipsis" FontSize="12" IsHitTestVisible="False"
|
||||
Foreground="{StaticResource ColorBrushGray1}" Opacity="0.4" VerticalAlignment="Bottom" Visibility="Collapsed" />
|
||||
<!-- 更新按钮 -->
|
||||
<local:MyIconButton Width="21" Height="21" Margin="-2,-1.6,0,0" x:Name="BtnUpdate" Theme="Black" Opacity="0.4" Visibility="Collapsed"
|
||||
ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="100" ToolTipService.VerticalOffset="-9"
|
||||
Logo="M640 768H384l-32-32V509H213L190 454l298-298h45l298 298L810 509h-138v226z m-224-64h192V477l32-32h93L512 223 290 445H384l32 32zM352 831h320v64h-320z" />
|
||||
</StackPanel>
|
||||
<!--<Grid Grid.Column="3" Grid.ColumnSpan="2" Grid.Row="1" VerticalAlignment="Bottom" Margin="0,1,0,0" IsHitTestVisible="False" SnapsToDevicePixels="False" UseLayoutRounding="False">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Name="LabTitle" TextTrimming="WordEllipsis" FontSize="14" Grid.Column="0" />
|
||||
<TextBlock x:Name="LabTitleRaw" TextTrimming="WordEllipsis" FontSize="12" Grid.Column="1"
|
||||
Foreground="{StaticResource ColorBrushGray1}" Opacity="0.4" VerticalAlignment="Bottom" Visibility="Collapsed" />
|
||||
<local:MyIconTextButton Grid.Column="2" Text="更新" />
|
||||
</Grid>-->
|
||||
<!-- Tag 与 详情 -->
|
||||
<StackPanel x:Name="PanTags" Grid.Row="2" Grid.Column="3" Orientation="Horizontal" VerticalAlignment="Bottom" Margin="-1,0,1,0" Visibility="Collapsed" IsHitTestVisible="False">
|
||||
<!--<Border Background="{DynamicResource ColorBrush6}" Padding="3,1" CornerRadius="3" Margin="0,0,4,0" SnapsToDevicePixels="True" UseLayoutRounding="False">
|
||||
<TextBlock Text="科技" Foreground="{DynamicResource ColorBrush2}" FontSize="11" />
|
||||
</Border>-->
|
||||
</StackPanel>
|
||||
<TextBlock x:Name="LabInfo" Grid.Row="2" Grid.Column="4" VerticalAlignment="Bottom" Margin="0,0,3,1"
|
||||
TextTrimming="CharacterEllipsis" FontSize="12" Foreground="{StaticResource ColorBrushGray3}"
|
||||
IsHitTestVisible="False" SnapsToDevicePixels="False" UseLayoutRounding="False" />
|
||||
</Grid>
|
||||
@@ -0,0 +1,536 @@
|
||||
Imports System.Windows.Forms
|
||||
|
||||
Public Class MyLocalModItem
|
||||
|
||||
#Region "基础属性"
|
||||
Public Uuid As Integer = GetUuid()
|
||||
|
||||
'Logo
|
||||
Private _Logo As String = ""
|
||||
Public Property Logo As String
|
||||
Get
|
||||
Return _Logo
|
||||
End Get
|
||||
Set(value As String)
|
||||
If _Logo = value OrElse value Is Nothing Then Exit Property
|
||||
_Logo = value
|
||||
Dim FileAddress = PathTemp & "CompLogo\" & GetHash(_Logo) & ".png"
|
||||
Try
|
||||
If _Logo.StartsWithF("http", True) Then
|
||||
'网络图片
|
||||
If File.Exists(FileAddress) Then
|
||||
PathLogo.Source = New MyBitmap(FileAddress)
|
||||
ElseIf _Logo.EndsWithF(".webp", True) Then 'Modrinth 林业 Mod 使用了不支持的 WebP 格式 Logo
|
||||
Log($"[LocalModItem] 发现不支持的 WebP 格式图标,已更改为默认图标:{_Logo}")
|
||||
PathLogo.Source = New MyBitmap(PathImage & "Icons/NoIcon.png")
|
||||
Else
|
||||
PathLogo.Source = New MyBitmap(PathImage & "Icons/NoIcon.png")
|
||||
RunInNewThread(Sub() LogoLoader(FileAddress), "Comp Logo Loader " & Uuid & "#", ThreadPriority.BelowNormal)
|
||||
End If
|
||||
Else
|
||||
'位图
|
||||
PathLogo.Source = New MyBitmap(_Logo)
|
||||
End If
|
||||
Catch ex As IOException
|
||||
Log(ex, "加载本地 Mod 图标时读取失败(" & FileAddress & ")")
|
||||
Catch ex As ArgumentException
|
||||
'考虑缓存的图片本身可能有误
|
||||
Log(ex, "可视化本地 Mod 图标失败(" & FileAddress & ")")
|
||||
Try
|
||||
File.Delete(FileAddress)
|
||||
Log("[LocalModItem] 已清理损坏的本地 Mod 图标:" & FileAddress)
|
||||
Catch exx As Exception
|
||||
Log(exx, "清理损坏的本地 Mod 图标缓存失败(" & FileAddress & ")", LogLevel.Hint)
|
||||
End Try
|
||||
Catch ex As Exception
|
||||
Log(ex, "加载本地 Mod 图标失败(" & value & ")")
|
||||
End Try
|
||||
End Set
|
||||
End Property
|
||||
'后台加载 Logo
|
||||
Private Sub LogoLoader(LocalFileAddress As String)
|
||||
Dim Retried As Boolean = False
|
||||
Dim DownloadEnd As String = GetUuid()
|
||||
RetryStart:
|
||||
Try
|
||||
'CurseForge 图片使用缩略图
|
||||
Dim Url As String = _Logo
|
||||
If Url.Contains("/256/256/") AndAlso GetPixelSize(1) <= 1.25 AndAlso Not Retried Then
|
||||
Url = Url.Replace("/256/256/", "/64/64/") '#3075:部分 Mod 不存在 64x64 图标,所以重试时不再缩小
|
||||
End If
|
||||
'下载图片
|
||||
NetDownload(Url, LocalFileAddress & DownloadEnd, True)
|
||||
Dim LoadError As Exception = Nothing
|
||||
RunInUiWait(
|
||||
Sub()
|
||||
Try
|
||||
'在地址更换时取消加载
|
||||
If LocalFileAddress <> $"{PathTemp}CompLogo\{GetHash(_Logo)}.png" Then Exit Sub
|
||||
'在完成正常加载后才保存缓存图片
|
||||
PathLogo.Source = New MyBitmap(LocalFileAddress & DownloadEnd)
|
||||
Catch ex As Exception
|
||||
Log(ex, "读取本地 Mod 图标失败(" & LocalFileAddress & ")")
|
||||
File.Delete(LocalFileAddress & DownloadEnd)
|
||||
LoadError = ex
|
||||
End Try
|
||||
End Sub)
|
||||
If LoadError IsNot Nothing Then Throw LoadError
|
||||
If File.Exists(LocalFileAddress) Then
|
||||
File.Delete(LocalFileAddress & DownloadEnd)
|
||||
Else
|
||||
FileIO.FileSystem.MoveFile(LocalFileAddress & DownloadEnd, LocalFileAddress)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
If Not Retried Then
|
||||
Retried = True
|
||||
GoTo RetryStart
|
||||
Else
|
||||
Log(ex, $"下载本地 Mod 图标失败({_Logo})")
|
||||
RunInUi(Sub() PathLogo.Source = New MyBitmap(PathImage & "Icons/NoIcon.png"))
|
||||
End If
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
'标题
|
||||
Private _Title As String
|
||||
Public Property Title As String
|
||||
Get
|
||||
Return _Title
|
||||
End Get
|
||||
Set(value As String)
|
||||
Dim RawValue = value
|
||||
Select Case Entry.State
|
||||
Case McMod.McModState.Fine
|
||||
LabTitle.TextDecorations = Nothing
|
||||
Case McMod.McModState.Disabled
|
||||
LabTitle.TextDecorations = TextDecorations.Strikethrough
|
||||
Case McMod.McModState.Unavaliable
|
||||
LabTitle.TextDecorations = TextDecorations.Strikethrough
|
||||
value &= " [错误]"
|
||||
End Select
|
||||
If LabTitle.Text = value Then Exit Property
|
||||
LabTitle.Text = value
|
||||
_Title = RawValue
|
||||
End Set
|
||||
End Property
|
||||
|
||||
'副标题
|
||||
Public Property SubTitle As String
|
||||
Get
|
||||
Return If(LabTitleRaw?.Text, "")
|
||||
End Get
|
||||
Set(value As String)
|
||||
If LabTitleRaw.Text = value Then Exit Property
|
||||
LabTitleRaw.Text = value
|
||||
LabTitleRaw.Visibility = If(value = "", Visibility.Collapsed, Visibility.Visible)
|
||||
End Set
|
||||
End Property
|
||||
|
||||
'描述
|
||||
Public Property Description As String
|
||||
Get
|
||||
Return LabInfo.Text
|
||||
End Get
|
||||
Set(value As String)
|
||||
If LabInfo.Text = value Then Exit Property
|
||||
LabInfo.Text = value
|
||||
End Set
|
||||
End Property
|
||||
|
||||
'Tag
|
||||
Public WriteOnly Property Tags As List(Of String)
|
||||
Set(value As List(Of String))
|
||||
PanTags.Children.Clear()
|
||||
PanTags.Visibility = If(value.Any(), Visibility.Visible, Visibility.Collapsed)
|
||||
For Each TagText In value
|
||||
Dim NewTag = GetObjectFromXML(
|
||||
"<Border xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
|
||||
Background=""#11000000"" Padding=""3,1"" CornerRadius=""3"" Margin=""0,0,3,0""
|
||||
SnapsToDevicePixels=""True"" UseLayoutRounding=""False"">
|
||||
<TextBlock Text=""" & TagText & """ Foreground=""#868686"" FontSize=""11"" />
|
||||
</Border>")
|
||||
PanTags.Children.Add(NewTag)
|
||||
Next
|
||||
End Set
|
||||
End Property
|
||||
|
||||
'相关联的 Mod
|
||||
Public Property Entry As McMod
|
||||
Get
|
||||
Return Tag
|
||||
End Get
|
||||
Set(value As McMod)
|
||||
Tag = value
|
||||
End Set
|
||||
End Property
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "点击与勾选"
|
||||
|
||||
'触发点击事件
|
||||
Public Event Click(sender As Object, e As MouseButtonEventArgs)
|
||||
Private Sub Button_MouseUp(sender As Object, e As MouseButtonEventArgs) Handles Me.PreviewMouseLeftButtonUp
|
||||
If IsMouseDown Then
|
||||
RaiseEvent Click(sender, e)
|
||||
If e.Handled Then Exit Sub
|
||||
Log("[Control] 按下本地 Mod 列表项:" & LabTitle.Text)
|
||||
End If
|
||||
End Sub
|
||||
|
||||
'鼠标点击判定
|
||||
Private IsMouseDown As Boolean = False
|
||||
Private Sub Button_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles Me.PreviewMouseLeftButtonDown
|
||||
If Not IsMouseDirectlyOver Then Exit Sub
|
||||
IsMouseDown = True
|
||||
If ButtonStack IsNot Nothing Then ButtonStack.IsHitTestVisible = False
|
||||
End Sub
|
||||
Private Sub Button_MouseLeave(sender As Object, e As Object) Handles Me.MouseLeave, Me.PreviewMouseLeftButtonUp
|
||||
IsMouseDown = False
|
||||
If ButtonStack IsNot Nothing Then ButtonStack.IsHitTestVisible = True
|
||||
End Sub
|
||||
|
||||
'滑动选中
|
||||
Private Shared SwipeStart As Integer, SwipeEnd As Integer
|
||||
Private Shared Swiping As Boolean = False
|
||||
Private Shared SwipToState As Boolean '被滑动到的目标应将 Checked 改为此值
|
||||
Private Sub Button_MouseSwipeStart(sender As Object, e As Object) Handles Me.MouseLeftButtonDown
|
||||
'开始滑动
|
||||
Dim Index = CType(Parent, StackPanel).Children.IndexOf(Me)
|
||||
SwipeStart = Index
|
||||
SwipeEnd = Index
|
||||
Swiping = True
|
||||
SwipToState = Not Checked
|
||||
FrmVersionMod.CardSelect.IsHitTestVisible = False '暂时禁用下边栏
|
||||
End Sub
|
||||
Private Sub Button_MouseSwipe(sender As Object, e As Object) Handles Me.MouseEnter, Me.MouseLeave, Me.MouseLeftButtonUp
|
||||
'结束滑动
|
||||
If Mouse.LeftButton <> MouseButtonState.Pressed OrElse Not Swiping Then
|
||||
Swiping = False
|
||||
FrmVersionMod.CardSelect.IsHitTestVisible = True
|
||||
Exit Sub
|
||||
End If
|
||||
'计算滑动范围
|
||||
Dim Index = CType(Parent, StackPanel).Children.IndexOf(Me)
|
||||
SwipeStart = Math.Min(SwipeStart, Index)
|
||||
SwipeEnd = Math.Max(SwipeEnd, Index)
|
||||
'勾选所有范围中的项
|
||||
If SwipeStart = SwipeEnd Then Exit Sub
|
||||
For i = SwipeStart To SwipeEnd
|
||||
Dim Item As MyLocalModItem = CType(Parent, StackPanel).Children(i)
|
||||
Item.InitLate(Item, e)
|
||||
Item.Checked = SwipToState
|
||||
Next
|
||||
End Sub
|
||||
|
||||
'勾选状态
|
||||
Public Event Check(sender As Object, e As RouteEventArgs)
|
||||
Public Event Changed(sender As Object, e As RouteEventArgs)
|
||||
Private _Checked As Boolean = False
|
||||
Public Property Checked As Boolean
|
||||
Get
|
||||
Return _Checked
|
||||
End Get
|
||||
Set(value As Boolean)
|
||||
Try
|
||||
'触发属性值修改
|
||||
Dim RawValue = _Checked
|
||||
If value = _Checked Then Exit Property
|
||||
_Checked = value
|
||||
Dim ChangedEventArgs As New RouteEventArgs(False)
|
||||
If IsInitialized Then
|
||||
RaiseEvent Changed(Me, ChangedEventArgs)
|
||||
If ChangedEventArgs.Handled Then
|
||||
_Checked = RawValue
|
||||
Exit Property
|
||||
End If
|
||||
End If
|
||||
If value Then
|
||||
Dim CheckEventArgs As New RouteEventArgs(False)
|
||||
RaiseEvent Check(Me, CheckEventArgs)
|
||||
If CheckEventArgs.Handled Then Exit Property
|
||||
End If
|
||||
'更改动画
|
||||
If IsVisibleInForm() Then
|
||||
Dim Anim As New List(Of AniData)
|
||||
If Checked Then
|
||||
'由无变有
|
||||
Dim Delta = 32 - RectCheck.ActualHeight
|
||||
Anim.Add(AaHeight(RectCheck, Delta * 0.4, 200,, New AniEaseOutFluent(AniEasePower.Weak)))
|
||||
Anim.Add(AaHeight(RectCheck, Delta * 0.6, 300,, New AniEaseOutBack(AniEasePower.Weak)))
|
||||
Anim.Add(AaOpacity(RectCheck, 1 - RectCheck.Opacity, 30))
|
||||
RectCheck.VerticalAlignment = VerticalAlignment.Center
|
||||
RectCheck.Margin = New Thickness(-3, 0, 0, 0)
|
||||
Anim.Add(AaColor(LabTitle, TextBlock.ForegroundProperty, If(Entry.State = McMod.McModState.Fine, "ColorBrush2", "ColorBrush5"), 200))
|
||||
Else
|
||||
'由有变无
|
||||
Anim.Add(AaHeight(RectCheck, -RectCheck.ActualHeight, 120,, New AniEaseInFluent(AniEasePower.Weak)))
|
||||
Anim.Add(AaOpacity(RectCheck, -RectCheck.Opacity, 70, 40))
|
||||
RectCheck.VerticalAlignment = VerticalAlignment.Center
|
||||
Anim.Add(AaColor(LabTitle, TextBlock.ForegroundProperty, If(LabTitle.TextDecorations Is Nothing, "ColorBrush1", "ColorBrushGray4"), 120))
|
||||
End If
|
||||
AniStart(Anim, "MyLocalModItem Checked " & Uuid)
|
||||
Else
|
||||
'不在窗口上时直接设置
|
||||
RectCheck.VerticalAlignment = VerticalAlignment.Center
|
||||
RectCheck.Margin = New Thickness(-3, 0, 0, 0)
|
||||
If Checked Then
|
||||
RectCheck.Height = 32
|
||||
RectCheck.Opacity = 1
|
||||
LabTitle.SetResourceReference(TextBlock.ForegroundProperty, If(Entry.State = McMod.McModState.Fine, "ColorBrush2", "ColorBrush5"))
|
||||
Else
|
||||
RectCheck.Height = 0
|
||||
RectCheck.Opacity = 0
|
||||
LabTitle.SetResourceReference(TextBlock.ForegroundProperty, If(Entry.State = McMod.McModState.Fine, "ColorBrush1", "ColorBrushGray4"))
|
||||
End If
|
||||
AniStop("MyLocalModItem Checked " & Uuid)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "设置 Checked 失败")
|
||||
End Try
|
||||
End Set
|
||||
End Property
|
||||
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "后加载内容"
|
||||
|
||||
'右下角状态指示图标
|
||||
Private ImgState As Image
|
||||
|
||||
'指向背景
|
||||
Private _RectBack As Border = Nothing
|
||||
Public ReadOnly Property RectBack As Border
|
||||
Get
|
||||
If _RectBack Is Nothing Then
|
||||
Dim Rect As New Border With {
|
||||
.Name = "RectBack",
|
||||
.CornerRadius = New CornerRadius(3),
|
||||
.RenderTransform = New ScaleTransform(0.8, 0.8),
|
||||
.RenderTransformOrigin = New Point(0.5, 0.5),
|
||||
.BorderThickness = New Thickness(GetWPFSize(1)),
|
||||
.SnapsToDevicePixels = True,
|
||||
.IsHitTestVisible = False,
|
||||
.Opacity = 0
|
||||
}
|
||||
Rect.SetResourceReference(Border.BackgroundProperty, "ColorBrush7")
|
||||
Rect.SetResourceReference(Border.BorderBrushProperty, "ColorBrush6")
|
||||
SetColumnSpan(Rect, 999)
|
||||
SetRowSpan(Rect, 999)
|
||||
Children.Insert(0, Rect)
|
||||
_RectBack = Rect
|
||||
'<!--<Border x:Name = "RectBack" CornerRadius="3" RenderTransformOrigin="0.5,0.5" SnapsToDevicePixels="True"
|
||||
'IsHitTestVisible = "False" Opacity="0" BorderThickness="1"
|
||||
'Grid.ColumnSpan = "4" Background="{DynamicResource ColorBrush7}" BorderBrush="{DynamicResource ColorBrush6}"/>-->
|
||||
End If
|
||||
Return _RectBack
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'按钮
|
||||
Public ButtonHandler As Action(Of MyLocalModItem, EventArgs)
|
||||
Public ButtonStack As FrameworkElement
|
||||
Private _Buttons As IEnumerable(Of MyIconButton)
|
||||
Public Property Buttons As IEnumerable(Of MyIconButton)
|
||||
Get
|
||||
Return _Buttons
|
||||
End Get
|
||||
Set(value As IEnumerable(Of MyIconButton))
|
||||
_Buttons = value
|
||||
'移除原 Stack
|
||||
If ButtonStack IsNot Nothing Then
|
||||
Children.Remove(ButtonStack)
|
||||
ButtonStack = Nothing
|
||||
End If
|
||||
If Not value.Any() Then Exit Property
|
||||
'添加新 Stack
|
||||
ButtonStack = New StackPanel With {.Opacity = 0, .Margin = New Thickness(0, 0, 5, 0), .SnapsToDevicePixels = False, .Orientation = Orientation.Horizontal,
|
||||
.HorizontalAlignment = HorizontalAlignment.Right, .VerticalAlignment = VerticalAlignment.Center, .UseLayoutRounding = False}
|
||||
SetColumnSpan(ButtonStack, 10) : SetRowSpan(ButtonStack, 10)
|
||||
'构造按钮
|
||||
For Each Btn As MyIconButton In value
|
||||
If Btn.Height.Equals(Double.NaN) Then Btn.Height = 25
|
||||
If Btn.Width.Equals(Double.NaN) Then Btn.Width = 25
|
||||
CType(ButtonStack, StackPanel).Children.Add(Btn)
|
||||
Next
|
||||
Children.Add(ButtonStack)
|
||||
End Set
|
||||
End Property
|
||||
|
||||
'勾选条
|
||||
Private _RectCheck As Border
|
||||
Public ReadOnly Property RectCheck As Border
|
||||
Get
|
||||
If _RectCheck Is Nothing Then
|
||||
_RectCheck = New Border With {.Width = 5, .Height = If(Checked, Double.NaN, 0), .CornerRadius = New CornerRadius(2, 2, 2, 2),
|
||||
.VerticalAlignment = If(Checked, VerticalAlignment.Stretch, VerticalAlignment.Center),
|
||||
.HorizontalAlignment = HorizontalAlignment.Left, .UseLayoutRounding = False, .SnapsToDevicePixels = False,
|
||||
.Margin = If(Checked, New Thickness(-3, 6, 0, 6), New Thickness(-3, 0, 0, 0))}
|
||||
_RectCheck.SetResourceReference(Border.BackgroundProperty, "ColorBrush3")
|
||||
SetRowSpan(_RectCheck, 10)
|
||||
Children.Add(_RectCheck)
|
||||
End If
|
||||
Return _RectCheck
|
||||
End Get
|
||||
End Property
|
||||
|
||||
#End Region
|
||||
|
||||
Public Sub Refresh()
|
||||
RunInUi(
|
||||
Sub()
|
||||
'更新
|
||||
If Entry.CanUpdate Then
|
||||
BtnUpdate.Visibility = Visibility.Visible
|
||||
Dim CurrentName = Entry.CompFile.FileName.Replace(".jar", "")
|
||||
Dim NewestName = Entry.UpdateFile.FileName.Replace(".jar", "")
|
||||
'简化名称对比
|
||||
Dim CurrentSegs = CurrentName.Split("-"c).ToList()
|
||||
Dim NewestSegs = NewestName.Split("-"c).ToList()
|
||||
Dim Shortened As Boolean = False
|
||||
For Each Seg In CurrentSegs.ToList()
|
||||
If Not NewestSegs.Contains(Seg) Then Continue For
|
||||
CurrentSegs.Remove(Seg)
|
||||
NewestSegs.Remove(Seg)
|
||||
Shortened = True
|
||||
Next
|
||||
If Shortened AndAlso CurrentSegs.Any() AndAlso NewestSegs.Any() Then
|
||||
CurrentName = Join(CurrentSegs, "-")
|
||||
NewestName = Join(NewestSegs, "-")
|
||||
Entry._Version = CurrentName '使用网络信息作为显示的版本号
|
||||
End If
|
||||
BtnUpdate.ToolTip = $"当前版本:{CurrentName} ({Entry.CompFile.ReleaseDate:yyyy/MM/dd HH:mm:ss}){vbCrLf}最新版本:{NewestName} ({Entry.UpdateFile.ReleaseDate:yyyy/MM/dd HH:mm:ss}){vbCrLf}点击以更新,右键查看更新日志。"
|
||||
Else
|
||||
BtnUpdate.Visibility = Visibility.Collapsed
|
||||
End If
|
||||
'标题
|
||||
If Entry.Comp Is Nothing Then
|
||||
Title = Entry.Name
|
||||
SubTitle = If(Entry.Version Is Nothing, "", " | " & Entry.Version)
|
||||
Else
|
||||
Dim Titles = Entry.Comp.GetControlTitle(False)
|
||||
Title = Titles.Key
|
||||
SubTitle = Titles.Value & If(Entry.Version Is Nothing, "", " | " & Entry.Version)
|
||||
End If
|
||||
If Checked Then
|
||||
LabTitle.SetResourceReference(TextBlock.ForegroundProperty, If(Entry.State = McMod.McModState.Fine, "ColorBrush2", "ColorBrush5"))
|
||||
Else
|
||||
LabTitle.SetResourceReference(TextBlock.ForegroundProperty, If(Entry.State = McMod.McModState.Fine, "ColorBrush1", "ColorBrushGray4"))
|
||||
End If
|
||||
'描述
|
||||
Dim NewDescription As String
|
||||
Select Case Entry.State
|
||||
Case McMod.McModState.Fine
|
||||
NewDescription = GetFileNameWithoutExtentionFromPath(Entry.Path)
|
||||
Case McMod.McModState.Disabled
|
||||
NewDescription = GetFileNameWithoutExtentionFromPath(Entry.Path.Replace(".disabled", "").Replace(".old", ""))
|
||||
Case Else 'McMod.McModState.Unavaliable
|
||||
NewDescription = GetFileNameFromPath(Entry.Path)
|
||||
End Select
|
||||
If Entry.Comp IsNot Nothing Then
|
||||
NewDescription += ": " & Entry.Comp.Description.Replace(vbCr, "").Replace(vbLf, "")
|
||||
ElseIf Entry.Description IsNot Nothing Then
|
||||
NewDescription += ": " & Entry.Description.Replace(vbCr, "").Replace(vbLf, "")
|
||||
ElseIf Not Entry.IsFileAvailable Then
|
||||
NewDescription += ": " & "存在错误,无法获取信息"
|
||||
End If
|
||||
Description = NewDescription
|
||||
'主 Logo
|
||||
Logo = If(Entry.Comp Is Nothing, PathImage & "Icons/NoIcon.png", Entry.Comp.GetControlLogo())
|
||||
'图标右下角的 Logo
|
||||
If Entry.State = McMod.McModState.Fine Then
|
||||
If ImgState IsNot Nothing Then
|
||||
Children.Remove(ImgState)
|
||||
ImgState = Nothing
|
||||
End If
|
||||
Else
|
||||
If ImgState Is Nothing Then
|
||||
ImgState = New Image With {
|
||||
.Width = 20, .Height = 20, .Margin = New Thickness(0, 0, -5, -3), .IsHitTestVisible = False,
|
||||
.HorizontalAlignment = HorizontalAlignment.Right, .VerticalAlignment = VerticalAlignment.Bottom
|
||||
}
|
||||
RenderOptions.SetBitmapScalingMode(ImgState, BitmapScalingMode.HighQuality)
|
||||
SetColumn(ImgState, 1) : SetRow(ImgState, 1) : SetRowSpan(ImgState, 2)
|
||||
Children.Add(ImgState)
|
||||
'<Image x:Name="ImgState" RenderOptions.BitmapScalingMode="HighQuality" Width="16" Height="16" Margin="0,0,-3,-1"
|
||||
' Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" IsHitTestVisible="False"
|
||||
' HorizontalAlignment="Right" VerticalAlignment="Bottom"
|
||||
' Source="/Images/Icons/Unavaliable.png" />
|
||||
End If
|
||||
ImgState.Source = New MyBitmap(PathImage & $"Icons/{Entry.State}.png")
|
||||
End If
|
||||
'标签
|
||||
If Entry.Comp IsNot Nothing Then Tags = Entry.Comp.Tags
|
||||
End Sub)
|
||||
End Sub
|
||||
|
||||
Public Sub RefreshColor(sender As Object, e As EventArgs) Handles Me.MouseEnter, Me.MouseLeave, Me.MouseLeftButtonDown, Me.MouseLeftButtonUp, Me.Changed
|
||||
InitLate(sender, e)
|
||||
'触发颜色动画
|
||||
Dim Time As Integer = If(IsMouseOver, 120, 180)
|
||||
Dim Ani As New List(Of AniData)
|
||||
'ButtonStack
|
||||
If ButtonStack IsNot Nothing Then
|
||||
If IsMouseOver Then
|
||||
Ani.Add(AaOpacity(ButtonStack, 1 - ButtonStack.Opacity, Time * 0.7, Time * 0.3))
|
||||
Ani.Add(AaDouble(Sub(i) ColumnPaddingRight.Width = New GridLength(Math.Max(0, ColumnPaddingRight.Width.Value + i)),
|
||||
5 + Buttons.Count * 25 - ColumnPaddingRight.Width.Value, Time * 0.3, Time * 0.7))
|
||||
Else
|
||||
Ani.Add(AaOpacity(ButtonStack, -ButtonStack.Opacity, Time * 0.4))
|
||||
Ani.Add(AaDouble(Sub(i) ColumnPaddingRight.Width = New GridLength(Math.Max(0, ColumnPaddingRight.Width.Value + i)),
|
||||
4 - ColumnPaddingRight.Width.Value, Time * 0.4))
|
||||
End If
|
||||
End If
|
||||
'RectBack
|
||||
If IsMouseOver OrElse Checked Then
|
||||
Ani.AddRange({
|
||||
AaColor(RectBack, Border.BackgroundProperty, If(IsMouseDown, "ColorBrush6", "ColorBrushBg1"), Time),
|
||||
AaOpacity(RectBack, 1 - RectBack.Opacity, Time,, New AniEaseOutFluent)
|
||||
})
|
||||
If IsMouseDown Then
|
||||
Ani.Add(AaScaleTransform(RectBack, 0.996 - CType(RectBack.RenderTransform, ScaleTransform).ScaleX, Time * 1.2,, New AniEaseOutFluent))
|
||||
Else
|
||||
Ani.Add(AaScaleTransform(RectBack, 1 - CType(RectBack.RenderTransform, ScaleTransform).ScaleX, Time * 1.2,, New AniEaseOutFluent))
|
||||
End If
|
||||
Else
|
||||
Ani.AddRange({
|
||||
AaOpacity(RectBack, -RectBack.Opacity, Time),
|
||||
AaScaleTransform(RectBack, 0.996 - CType(RectBack.RenderTransform, ScaleTransform).ScaleX, Time,, New AniEaseOutFluent),
|
||||
AaScaleTransform(RectBack, -0.196, 1,,, True)
|
||||
})
|
||||
End If
|
||||
AniStart(Ani, "LocalModItem Color " & Uuid)
|
||||
End Sub
|
||||
|
||||
'触发虚拟化内容
|
||||
Private Sub InitLate(sender As Object, e As EventArgs)
|
||||
If ButtonHandler IsNot Nothing Then
|
||||
ButtonHandler(sender, e)
|
||||
ButtonHandler = Nothing
|
||||
End If
|
||||
End Sub
|
||||
|
||||
'显示更新日志
|
||||
Private Sub ShowUpdateLog(sender As Object, e As MouseButtonEventArgs) Handles BtnUpdate.PreviewMouseRightButtonUp
|
||||
e.Handled = True
|
||||
Dim CurseForgeUrl As String = Entry.ChangelogUrls.FirstOrDefault(Function(x) x.Contains("curseforge.com"))
|
||||
Dim ModrinthUrl As String = Entry.ChangelogUrls.FirstOrDefault(Function(x) x.Contains("modrinth.com"))
|
||||
If CurseForgeUrl Is Nothing OrElse ModrinthUrl Is Nothing Then
|
||||
OpenWebsite(Entry.ChangelogUrls.First)
|
||||
Else
|
||||
Select Case MyMsgBox("要在哪个网站上查看更新日志?", "查看更新日志", "Modrinth", "CurseForge", "取消")
|
||||
Case 1
|
||||
OpenWebsite(ModrinthUrl)
|
||||
Case 2
|
||||
OpenWebsite(CurseForgeUrl)
|
||||
End Select
|
||||
End If
|
||||
End Sub
|
||||
|
||||
'触发更新
|
||||
Private Sub BtnUpdate_Click(sender As Object, e As EventArgs) Handles BtnUpdate.Click
|
||||
FrmVersionMod.UpdateMods({Entry})
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -13,21 +13,19 @@
|
||||
|
||||
Case "打开网页"
|
||||
Data(0) = Data(0).Replace("\", "/")
|
||||
If Not (Data(0).StartsWith("http://") OrElse Data(0).StartsWith("https://")) Then
|
||||
MyMsgBox("EventData 必须为以 http:// 或 https:// 开头的网址。" & vbCrLf & "如果想要启动程序,请将 EventType 改为 打开文件。", "事件执行失败")
|
||||
If Not Data(0).Contains("://") OrElse Data(0).StartsWithF("file", True) Then '为了支持更多协议(#2200)
|
||||
MyMsgBox("EventData 必须为一个网址。" & vbCrLf & "如果想要启动程序,请将 EventType 改为 打开文件。", "事件执行失败")
|
||||
Exit Sub
|
||||
End If
|
||||
Hint("正在打开网页,请稍候……")
|
||||
Hint("正在开启中,请稍候……")
|
||||
OpenWebsite(Data(0))
|
||||
|
||||
Case "打开文件", "打开帮助"
|
||||
RunInThread(Sub()
|
||||
Try
|
||||
|
||||
'确认实际路径
|
||||
Dim ActualPaths = GetEventAbsoluteUrls(Data(0), Type)
|
||||
Dim Location = ActualPaths(0), WorkingDir = ActualPaths(1)
|
||||
|
||||
'执行
|
||||
If Type = "打开文件" Then
|
||||
Dim Info As New ProcessStartInfo With {
|
||||
@@ -39,13 +37,14 @@
|
||||
Else '打开帮助
|
||||
PageOtherHelp.EnterHelpPage(Location)
|
||||
End If
|
||||
|
||||
Catch ex As Exception
|
||||
Log(ex, "执行打开类自定义事件失败", LogLevel.Msgbox)
|
||||
End Try
|
||||
End Sub)
|
||||
|
||||
Case "启动游戏"
|
||||
If McLaunchStart(New McLaunchOptions With {.ServerIp = If(Data.Length >= 2, Data(1), Nothing), .Version = New McVersion(Data(0))}) Then
|
||||
If McLaunchStart(New McLaunchOptions With
|
||||
{.ServerIp = If(Data.Length >= 2, Data(1), Nothing), .Version = New McVersion(Data(0))}) Then
|
||||
Hint("正在启动 " & Data(0) & "……")
|
||||
End If
|
||||
|
||||
@@ -54,6 +53,7 @@
|
||||
|
||||
Case "刷新主页"
|
||||
FrmLaunchRight.ForceRefresh()
|
||||
If Data(0) = "" Then Hint("已刷新主页!", HintType.Finish)
|
||||
|
||||
Case "刷新帮助"
|
||||
PageOtherLeft.RefreshHelp()
|
||||
@@ -61,16 +61,25 @@
|
||||
Case "今日人品"
|
||||
PageOtherTest.Jrrp()
|
||||
|
||||
Case "内存优化"
|
||||
RunInThread(Sub() PageOtherTest.MemoryOptimize(True))
|
||||
|
||||
Case "清理垃圾"
|
||||
PageOtherTest.RubbishClear()
|
||||
RunInThread(Sub() PageOtherTest.RubbishClear())
|
||||
|
||||
Case "弹出窗口"
|
||||
MyMsgBox(Data(1).Replace("\n", vbCrLf), Data(0).Replace("\n", vbCrLf))
|
||||
|
||||
Case "切换页面"
|
||||
FrmMain.PageChange(Val(Data(0)), Val(Data(1)))
|
||||
|
||||
Case "导入整合包"
|
||||
RunInThread(Sub() ModpackInstall())
|
||||
|
||||
Case "下载文件"
|
||||
Data(0) = Data(0).Replace("\", "/")
|
||||
If Not (Data(0).StartsWith("http://") OrElse Data(0).StartsWith("https://")) Then
|
||||
MyMsgBox("EventData 必须为以 http:// 或 https:// 开头的网址。" & vbCrLf & "PCL 不支持其他乱七八糟的协议。", "事件执行失败")
|
||||
If Not (Data(0).StartsWithF("http://", True) OrElse Data(0).StartsWithF("https://", True)) Then
|
||||
MyMsgBox("EventData 必须为以 http:// 或 https:// 开头的网址。" & vbCrLf & "PCL 不支持其他乱七八糟的下载协议。", "事件执行失败")
|
||||
Exit Sub
|
||||
End If
|
||||
PageOtherTest.StartCustomDownload(Data(0), GetFileNameFromPath(Data(0)))
|
||||
@@ -90,7 +99,7 @@
|
||||
Public Function GetEventAbsoluteUrls(RelativeUrl As String, EventType As String) As String()
|
||||
|
||||
'网页确认
|
||||
If RelativeUrl.ToLower.StartsWith("http") Then
|
||||
If RelativeUrl.StartsWithF("http", True) Then
|
||||
If RunInUi() Then
|
||||
Throw New Exception("MyListItem 在界面初始化时就需要获取帮助标题等信息,这会导致程序在网络请求时卡死。" & vbCrLf &
|
||||
"因此,请换用 MyListItem 以外的控件(例如 MyButton)作为联网帮助页面的入口!")
|
||||
@@ -99,7 +108,7 @@
|
||||
Dim RawFileName As String
|
||||
Try
|
||||
RawFileName = GetFileNameFromPath(RelativeUrl)
|
||||
If Not RawFileName.ToLower.EndsWith(".json") Then Throw New Exception("未指向 .json 后缀的文件")
|
||||
If Not RawFileName.EndsWithF(".json", True) Then Throw New Exception("未指向 .json 后缀的文件")
|
||||
Catch ex As Exception
|
||||
Throw New Exception("联网帮助页面须指向一个帮助 JSON 文件,并在同路径下包含相应 XAML 文件!" & vbCrLf &
|
||||
"例如:" & vbCrLf &
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
Public Module ModMusic
|
||||
Imports System.Security.Cryptography
|
||||
|
||||
Public Module ModMusic
|
||||
|
||||
#Region "播放列表"
|
||||
|
||||
''' <summary>
|
||||
''' 接下来要播放的音乐文件路径。未初始化时为 Nothing。
|
||||
''' </summary>
|
||||
Public MusicToplayList As List(Of String) = Nothing
|
||||
Public MusicWaitingList As List(Of String) = Nothing
|
||||
''' <summary>
|
||||
''' 全部音乐文件路径。未初始化时为 Nothing。
|
||||
''' </summary>
|
||||
@@ -22,20 +24,19 @@
|
||||
If MusicAllList Is Nothing Then
|
||||
MusicAllList = New List(Of String)
|
||||
Directory.CreateDirectory(Path & "PCL\Musics\")
|
||||
For Each File In My.Computer.FileSystem.GetFiles(Path & "PCL\Musics\", FileIO.SearchOption.SearchAllSubDirectories, "*.*")
|
||||
For Each File In EnumerateFiles(Path & "PCL\Musics\")
|
||||
'文件夹可能会被加入 .ini 文件夹配置文件、一些乱七八糟的 .jpg 文件啥的
|
||||
Dim Extend As String = File.Split(".").Last.ToLower
|
||||
If Not (Extend = "ini" OrElse Extend = "jpg" OrElse Extend = "txt" OrElse Extend = "cfg" OrElse Extend = "png") Then
|
||||
MusicAllList.Add(File)
|
||||
End If
|
||||
Dim Ext As String = File.Extension.ToLower
|
||||
If {".ini", ".jpg", ".txt", ".cfg", ".lrc", ".db", ".png"}.Contains(Ext) Then Continue For
|
||||
MusicAllList.Add(File.FullName)
|
||||
Next
|
||||
End If
|
||||
'打乱顺序播放
|
||||
MusicToplayList = RandomChaos(New List(Of String)(MusicAllList))
|
||||
If Not IgnoreFirst = "" AndAlso Not MusicToplayList.Count = 0 AndAlso MusicToplayList(0) = IgnoreFirst Then
|
||||
MusicWaitingList = If(Setup.Get("UiMusicRandom"), Shuffle(New List(Of String)(MusicAllList)), New List(Of String)(MusicAllList))
|
||||
If Not IgnoreFirst = "" AndAlso Not Not MusicWaitingList.Any() AndAlso MusicWaitingList(0) = IgnoreFirst Then
|
||||
'若需要避免成为第一项的为第一项,则将它放在最后
|
||||
MusicToplayList.RemoveAt(0)
|
||||
MusicToplayList.Add(IgnoreFirst)
|
||||
MusicWaitingList.RemoveAt(0)
|
||||
MusicWaitingList.Add(IgnoreFirst)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "初始化音乐列表失败", LogLevel.Feedback)
|
||||
@@ -46,12 +47,11 @@
|
||||
''' </summary>
|
||||
Private Function DequeueNextMusicAddress() As String
|
||||
'初始化,确保存在音乐
|
||||
If MusicAllList Is Nothing Then MusicListInit(False)
|
||||
If MusicAllList.Count = 0 Then Throw New Exception("在没有音乐时尝试获取音乐路径")
|
||||
If MusicAllList Is Nothing OrElse Not MusicAllList.Any() OrElse Not MusicWaitingList.Any() Then MusicListInit(False)
|
||||
'出列下一个音乐,如果出列结束则生成新列表
|
||||
DequeueNextMusicAddress = MusicToplayList(0)
|
||||
MusicToplayList.RemoveAt(0)
|
||||
If MusicToplayList.Count = 0 Then MusicListInit(False, DequeueNextMusicAddress)
|
||||
DequeueNextMusicAddress = MusicWaitingList(0)
|
||||
MusicWaitingList.RemoveAt(0)
|
||||
If Not MusicWaitingList.Any() Then MusicListInit(False, DequeueNextMusicAddress)
|
||||
End Function
|
||||
|
||||
#End Region
|
||||
@@ -65,7 +65,7 @@
|
||||
RunInUi(Sub()
|
||||
Try
|
||||
|
||||
If MusicAllList.Count = 0 Then
|
||||
If Not MusicAllList.Any() Then
|
||||
'无背景音乐
|
||||
FrmMain.BtnExtraMusic.Show = False
|
||||
Else
|
||||
@@ -106,7 +106,8 @@
|
||||
Case MusicStates.Play
|
||||
MusicPause()
|
||||
Case Else
|
||||
Hint("音乐目前为停止状态!", HintType.Critical)
|
||||
Log("[Music] 音乐目前为停止状态,已强制尝试开始播放", LogLevel.Debug)
|
||||
MusicRefreshPlay(False)
|
||||
End Select
|
||||
End If
|
||||
End Sub
|
||||
@@ -161,7 +162,7 @@
|
||||
Try
|
||||
|
||||
MusicListInit(True)
|
||||
If MusicAllList.Count = 0 Then
|
||||
If Not MusicAllList.Any() Then
|
||||
If MusicNAudio Is Nothing Then
|
||||
If ShowHint Then Hint("未检测到可用的背景音乐!", HintType.Critical)
|
||||
Else
|
||||
@@ -212,15 +213,15 @@
|
||||
''' 继续音乐播放,返回是否成功切换了状态。
|
||||
''' </summary>
|
||||
Public Function MusicResume() As Boolean
|
||||
If MusicState = MusicStates.Pause Then
|
||||
If MusicState = MusicStates.Play OrElse Not MusicAllList.Any() Then
|
||||
Return False
|
||||
Else
|
||||
RunInThread(Sub()
|
||||
MusicNAudio.Play()
|
||||
MusicRefreshUI()
|
||||
Log("[Music] 已恢复播放")
|
||||
End Sub)
|
||||
Return True
|
||||
Else
|
||||
Return False
|
||||
End If
|
||||
End Function
|
||||
|
||||
@@ -259,22 +260,31 @@
|
||||
PreviousVolume = Setup.Get("UiMusicVolume")
|
||||
CurrentWave.Volume = PreviousVolume / 1000
|
||||
End If
|
||||
Thread.Sleep(50)
|
||||
'更新进度条
|
||||
Dim Percent = Reader.CurrentTime.TotalMilliseconds / Reader.TotalTime.TotalMilliseconds
|
||||
RunInUi(Sub() FrmMain.BtnExtraMusic.Progress = Percent)
|
||||
Thread.Sleep(100)
|
||||
End While
|
||||
'当前音乐已播放结束,继续下一曲
|
||||
If CurrentWave.PlaybackState = NAudio.Wave.PlaybackState.Stopped AndAlso MusicAllList.Count > 0 Then MusicStartPlay(DequeueNextMusicAddress)
|
||||
If CurrentWave.PlaybackState = NAudio.Wave.PlaybackState.Stopped AndAlso MusicAllList.Any Then MusicStartPlay(DequeueNextMusicAddress)
|
||||
Catch ex As Exception
|
||||
Log("[Music] 播放音乐失败的文件完整路径:" & MusicCurrent)
|
||||
If TypeOf ex Is NAudio.MmException AndAlso (ex.Message.Contains("NoDriver") OrElse ex.Message.Contains("BadDeviceId")) Then
|
||||
Hint("由于音频设备变更,音乐播放功能在重启 PCL 后才能恢复!", HintType.Critical)
|
||||
Thread.Sleep(1000000000)
|
||||
End If
|
||||
If ex.Message.Contains("Got a frame at sample rate") OrElse ex.Message.Contains("does not support changes to") Then
|
||||
Hint("播放音乐失败(" & GetFileNameFromPath(MusicCurrent) & "):PCL 不支持播放音频属性在中途发生变化的音乐", HintType.Critical)
|
||||
ElseIf Not (MusicCurrent.ToLower.EndsWith(".wav") OrElse MusicCurrent.ToLower.EndsWith(".mp3") OrElse MusicCurrent.ToLower.EndsWith(".flac")) Then
|
||||
ElseIf Not (MusicCurrent.EndsWithF(".wav", True) OrElse MusicCurrent.EndsWithF(".mp3", True) OrElse MusicCurrent.EndsWithF(".flac", True)) Then
|
||||
Hint("播放音乐失败(" & GetFileNameFromPath(MusicCurrent) & "):PCL 可能不支持此音乐格式,请将格式转换为 .wav、.mp3 或 .flac 后再试", HintType.Critical)
|
||||
Else
|
||||
Log(ex, "播放音乐失败(" & GetFileNameFromPath(MusicCurrent) & ")", LogLevel.Hint)
|
||||
End If
|
||||
Log(ex, "播放音乐失败(" & MusicCurrent & ")", LogLevel.Developer)
|
||||
If MusicAllList.Count > 1 Then
|
||||
Thread.Sleep(1000)
|
||||
MusicStartPlay(DequeueNextMusicAddress)
|
||||
Thread.Sleep(1000)
|
||||
If TypeOf ex Is FileNotFoundException Then
|
||||
MusicRefreshPlay(True, IsFirstLoad)
|
||||
ElseIf MusicAllList.Count > 1 Then
|
||||
MusicStartPlay(DequeueNextMusicAddress(), IsFirstLoad)
|
||||
End If
|
||||
Finally
|
||||
If CurrentWave IsNot Nothing Then CurrentWave.Dispose()
|
||||
|
||||
@@ -25,7 +25,7 @@ Public Class CookieWebClient
|
||||
''' </summary>
|
||||
Public Timeout As Integer = 600000
|
||||
|
||||
Protected Overrides Function GetWebRequest(ByVal address As Uri) As WebRequest
|
||||
Protected Overrides Function GetWebRequest(address As Uri) As WebRequest
|
||||
Dim r As WebRequest = MyBase.GetWebRequest(address)
|
||||
Dim request = TryCast(r, HttpWebRequest)
|
||||
|
||||
@@ -37,19 +37,19 @@ Public Class CookieWebClient
|
||||
Return r
|
||||
End Function
|
||||
|
||||
Protected Overrides Function GetWebResponse(ByVal request As WebRequest, ByVal result As IAsyncResult) As WebResponse
|
||||
Protected Overrides Function GetWebResponse(request As WebRequest, result As IAsyncResult) As WebResponse
|
||||
Dim response As WebResponse = MyBase.GetWebResponse(request, result)
|
||||
ReadCookies(response)
|
||||
Return response
|
||||
End Function
|
||||
|
||||
Protected Overrides Function GetWebResponse(ByVal request As WebRequest) As WebResponse
|
||||
Protected Overrides Function GetWebResponse(request As WebRequest) As WebResponse
|
||||
Dim response As WebResponse = MyBase.GetWebResponse(request)
|
||||
ReadCookies(response)
|
||||
Return response
|
||||
End Function
|
||||
|
||||
Private Sub ReadCookies(ByVal r As WebResponse)
|
||||
Private Sub ReadCookies(r As WebResponse)
|
||||
Dim response = TryCast(r, HttpWebResponse)
|
||||
If response IsNot Nothing Then
|
||||
Dim cookies As CookieCollection = response.Cookies
|
||||
|
||||
File renamed without changes.
@@ -8,11 +8,11 @@ Imports System.Runtime.InteropServices
|
||||
|
||||
'查看程序集特性的值
|
||||
|
||||
<Assembly: AssemblyTitle("Plain Craft Launcher 2 启动器")>
|
||||
<Assembly: AssemblyTitle("Plain Craft Launcher 启动器")>
|
||||
<Assembly: AssemblyDescription("Minecraft 启动器 (制作:龙腾猫跃)")>
|
||||
<Assembly: AssemblyCompany("")>
|
||||
<Assembly: AssemblyProduct("Plain Craft Launcher 2")>
|
||||
<Assembly: AssemblyCopyright("Copyright © 龙腾猫跃 2016-2023. All Rights Reserved.")>
|
||||
<Assembly: AssemblyProduct("Plain Craft Launcher")>
|
||||
<Assembly: AssemblyCopyright("Copyright © 龙腾猫跃 2016. All Rights Reserved.")>
|
||||
<Assembly: AssemblyConfiguration("PCL2 Config Mark")>
|
||||
<Assembly: ComVisible(False)>
|
||||
|
||||
@@ -51,6 +51,6 @@ Imports System.Runtime.InteropServices
|
||||
' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
|
||||
' 方法是按如下所示使用“*”
|
||||
|
||||
<Assembly: AssemblyVersion("2.5.2.0")>
|
||||
<Assembly: AssemblyFileVersion("2.5.2.0")>
|
||||
<Assembly: AssemblyVersion("2.7.3.0")>
|
||||
<Assembly: AssemblyFileVersion("2.7.3.0")>
|
||||
<Assembly: NeutralResourcesLanguage("")>
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,9 +1,9 @@
|
||||
<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"
|
||||
mc:Ignorable="d" x:Class="PageDownloadClient"
|
||||
d:DesignWidth="778.571" d:DesignHeight="90.857">
|
||||
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"
|
||||
mc:Ignorable="d" x:Class="PageDownloadClient"
|
||||
PanScroll="{Binding ElementName=PanBack}">
|
||||
<Grid>
|
||||
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
|
||||
<StackPanel Orientation="Vertical" Margin="5">
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
Version("id") = "20w14∞"
|
||||
Version("type") = "special"
|
||||
Version.Add("lore", GetMcFoolName(Version("id")))
|
||||
Case "3d shareware v1.34", "1.rv-pre1", "15w14a", "2.0", "22w13oneblockatatime"
|
||||
Case "3d shareware v1.34", "1.rv-pre1", "15w14a", "2.0", "22w13oneblockatatime", "23w13a_or_b", "24w14potato"
|
||||
Type = "愚人节版"
|
||||
Version("type") = "special"
|
||||
Version.Add("lore", GetMcFoolName(Version("id")))
|
||||
@@ -55,9 +55,8 @@
|
||||
Next
|
||||
'排序
|
||||
For i = 0 To Dict.Keys.Count - 1
|
||||
Dict(Dict.Keys(i)) = Sort(Dict.Values(i), Function(Left As JObject, Right As JObject) As Boolean
|
||||
Return Left("releaseTime").Value(Of Date) > Right("releaseTime").Value(Of Date)
|
||||
End Function)
|
||||
Dict(Dict.Keys(i)) = Sort(Dict.Values(i),
|
||||
Function(a, b) a("releaseTime").Value(Of Date) > b("releaseTime").Value(Of Date))
|
||||
Next
|
||||
'清空当前
|
||||
PanMain.Children.Clear()
|
||||
@@ -65,11 +64,11 @@
|
||||
Dim CardInfo As New MyCard With {.Title = "最新版本", .Margin = New Thickness(0, 0, 0, 15), .SwapType = 2}
|
||||
Dim TopestVersions 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")
|
||||
Release("lore") = "最新正式版,发布于 " & Release("releaseTime").Value(Of Date).ToString("yyyy'/'MM'/'dd HH':'mm")
|
||||
TopestVersions.Add(Release)
|
||||
If Dict("正式版")(0)("releaseTime").Value(Of Date) < Dict("预览版")(0)("releaseTime").Value(Of Date) Then
|
||||
Dim Snapshot As JObject = Dict("预览版")(0).DeepClone()
|
||||
Snapshot("lore") = "最新预览版,发布于 " & Snapshot("releaseTime").Value(Of Date).ToString("yyyy/MM/dd HH:mm")
|
||||
Snapshot("lore") = "最新预览版,发布于 " & Snapshot("releaseTime").Value(Of Date).ToString("yyyy'/'MM'/'dd HH':'mm")
|
||||
TopestVersions.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 = TopestVersions}
|
||||
@@ -78,7 +77,7 @@
|
||||
PanMain.Children.Add(CardInfo)
|
||||
'添加其他版本
|
||||
For Each Pair As KeyValuePair(Of String, List(Of JObject)) In Dict
|
||||
If Pair.Value.Count = 0 Then Continue For
|
||||
If Not Pair.Value.Any() Then Continue For
|
||||
'增加卡片
|
||||
Dim NewCard As New MyCard With {.Title = Pair.Key & " (" & Pair.Value.Count & ")", .Margin = New Thickness(0, 0, 0, 15), .SwapType = 2}
|
||||
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}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<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"
|
||||
mc:Ignorable="d" x:Class="PageDownloadCompDetail"
|
||||
d:DesignWidth="778.571" d:DesignHeight="90.857">
|
||||
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"
|
||||
mc:Ignorable="d" x:Class="PageDownloadCompDetail"
|
||||
PanScroll="{Binding ElementName=PanBack}">
|
||||
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
|
||||
<Grid Margin="25,25,25,10">
|
||||
<Grid.RowDefinitions>
|
||||
@@ -16,7 +16,6 @@
|
||||
<StackPanel Orientation="Horizontal" Height="35">
|
||||
<local:MyButton x:Name="BtnIntroWeb" MinWidth="140" Text="转到 CurseForge" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" />
|
||||
<local:MyButton x:Name="BtnIntroWiki" MinWidth="140" Text="转到 MC 百科" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
|
||||
<local:MyButton x:Name="BtnIntroMCBBS" MinWidth="140" Text="转到 MCBBS" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
|
||||
<local:MyButton x:Name="BtnIntroCopy" MinWidth="140" Text="复制名称" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
Public Class PageDownloadCompDetail
|
||||
Private CompItem As MyCompItem = Nothing
|
||||
Private Project As CompProject
|
||||
|
||||
#Region "加载器"
|
||||
|
||||
@@ -9,9 +8,19 @@
|
||||
|
||||
'初始化加载器信息
|
||||
Private Sub PageDownloadCompDetail_Inited(sender As Object, e As EventArgs) Handles Me.Initialized
|
||||
Project = FrmMain.PageCurrent.Additional
|
||||
Project = FrmMain.PageCurrent.Additional(0)
|
||||
TargetVersion = FrmMain.PageCurrent.Additional(2)
|
||||
TargetLoader = FrmMain.PageCurrent.Additional(3)
|
||||
PageLoaderInit(Load, PanLoad, PanMain, CardIntro, CompFileLoader, AddressOf Load_OnFinish)
|
||||
End Sub
|
||||
Private Sub PageDownloadCompDetail_Loaded(sender As Object, e As EventArgs) Handles Me.Loaded
|
||||
'Initialized 只会执行一次
|
||||
Project = FrmMain.PageCurrent.Additional(0)
|
||||
TargetVersion = FrmMain.PageCurrent.Additional(2)
|
||||
TargetLoader = FrmMain.PageCurrent.Additional(3)
|
||||
End Sub
|
||||
Private Project As CompProject
|
||||
Private TargetVersion As String, TargetLoader As CompModLoaderType
|
||||
'自动重试
|
||||
Private Sub Load_State(sender As Object, state As MyLoading.MyLoadingState, oldState As MyLoading.MyLoadingState) Handles Load.StateChanged
|
||||
Select Case CompFileLoader.State
|
||||
@@ -39,51 +48,39 @@
|
||||
End Sub
|
||||
End Class
|
||||
Private Sub Load_OnFinish()
|
||||
|
||||
'初始化字典,并将当前版本排在最上面
|
||||
Dim TopVersion As String
|
||||
Select Case Project.Type
|
||||
Case CompType.Mod
|
||||
TopVersion = If(PageDownloadMod.Loader.Input.GameVersion, "")
|
||||
Case CompType.ModPack
|
||||
TopVersion = If(PageDownloadPack.Loader.Input.GameVersion, "")
|
||||
Case Else 'CompType.ResourcePack
|
||||
'FUTURE: Res
|
||||
TopVersion = Nothing 'If(PageDownloadResource.Loader.Input.GameVersion, "")
|
||||
End Select
|
||||
Dim Dict As New SortedDictionary(Of String, List(Of CompFile))(New VersionSorterWithSelect(TopVersion))
|
||||
'#8826 汇报了奇怪的这个 Try 块里出现 NPE 的问题,但实在找不到,就瞎加点检测
|
||||
Try
|
||||
Dict.Add("未知版本", New List(Of CompFile))
|
||||
If CompFileLoader.Output Is Nothing Then
|
||||
Log("[Comp] 列表加载输出为 Nothing,请反馈此问题", LogLevel.Feedback)
|
||||
Exit Try
|
||||
End If
|
||||
For Each Version As CompFile In CompFileLoader.Output
|
||||
If Version Is Nothing Then
|
||||
Log("[Comp] 列表中的一个版本为 Nothing,请反馈此问题", LogLevel.Feedback)
|
||||
Continue For
|
||||
Dim TargetCardName As String = If(TargetVersion <> "" OrElse TargetLoader <> CompModLoaderType.Any,
|
||||
$"所选版本:{TargetVersion} {If(TargetLoader <> CompModLoaderType.Any, TargetLoader, "")}", "")
|
||||
'初始化字典
|
||||
Dim Dict As New SortedDictionary(Of String, List(Of CompFile))(New VersionSorterWithSelect(TargetCardName))
|
||||
Dict.Add("未知版本", New List(Of CompFile))
|
||||
For Each Version As CompFile In CompFileLoader.Output
|
||||
For Each GameVersion In Version.GameVersions
|
||||
'决定添加到哪个版本
|
||||
Dim TargetCard As String
|
||||
If GameVersion Is Nothing Then
|
||||
TargetCard = "未知版本"
|
||||
ElseIf GameVersion.Contains("w") OrElse GameVersion.Contains("pre") OrElse GameVersion.Contains("rc") Then
|
||||
TargetCard = "快照版本"
|
||||
ElseIf GameVersion.StartsWith("1.0") Then
|
||||
TargetCard = "远古版本"
|
||||
Else
|
||||
TargetCard = GameVersion
|
||||
End If
|
||||
For Each GameVersion In Version.GameVersions
|
||||
'决定添加到哪个版本
|
||||
Dim TargetVersion As String
|
||||
If GameVersion Is Nothing Then
|
||||
TargetVersion = "未知版本"
|
||||
ElseIf GameVersion.Contains("w") Then
|
||||
TargetVersion = "快照版本"
|
||||
ElseIf GameVersion.Split(".").Count >= 2 Then
|
||||
TargetVersion = GameVersion
|
||||
Else
|
||||
TargetVersion = "未知版本"
|
||||
End If
|
||||
'实际进行添加
|
||||
If Not Dict.ContainsKey(TargetVersion) Then Dict.Add(TargetVersion, New List(Of CompFile))
|
||||
If Not Dict(TargetVersion).Contains(Version) Then Dict(TargetVersion).Add(Version)
|
||||
Next
|
||||
'实际进行添加
|
||||
If Not Dict.ContainsKey(TargetCard) Then Dict.Add(TargetCard, New List(Of CompFile))
|
||||
If Not Dict(TargetCard).Contains(Version) Then Dict(TargetCard).Add(Version)
|
||||
Next
|
||||
Catch ex As Exception
|
||||
Log(ex, "准备工程下载列表出错", LogLevel.Feedback)
|
||||
End Try
|
||||
Next
|
||||
'添加筛选的版本的卡片
|
||||
If TargetCardName <> "" Then
|
||||
Dict.Add(TargetCardName, New List(Of CompFile))
|
||||
For Each Version As CompFile In CompFileLoader.Output
|
||||
If Version.GameVersions.Contains(TargetVersion) AndAlso
|
||||
(TargetLoader = CompModLoaderType.Any OrElse Version.ModLoaders.Contains(TargetLoader)) Then
|
||||
If Not Dict(TargetCardName).Contains(Version) Then Dict(TargetCardName).Add(Version)
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
|
||||
#Region "转化为 UI"
|
||||
Try
|
||||
@@ -91,7 +88,7 @@
|
||||
PanMain.Children.Clear()
|
||||
'转化为 UI
|
||||
For Each Pair As KeyValuePair(Of String, List(Of CompFile)) In Dict
|
||||
If Pair.Value.Count = 0 Then Continue For
|
||||
If Not Pair.Value.Any() Then Continue For
|
||||
'增加卡片
|
||||
Dim NewCard As New MyCard With {.Title = Pair.Key, .Margin = New Thickness(0, 0, 0, 15), .SwapType = If(Project.Type = CompType.ModPack, 9, 8)} 'FUTURE: Res
|
||||
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}
|
||||
@@ -99,7 +96,9 @@
|
||||
NewCard.SwapControl = NewStack
|
||||
PanMain.Children.Add(NewCard)
|
||||
'确定卡片是否展开
|
||||
If Pair.Key = TopVersion Then
|
||||
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(Project.Type = CompType.ModPack, 9, 8), Pair.Key) 'FUTURE: Res
|
||||
Else
|
||||
NewCard.IsSwaped = True
|
||||
@@ -125,7 +124,7 @@
|
||||
Private IsFirstInit As Boolean = True
|
||||
Public Sub Init() Handles Me.PageEnter
|
||||
AniControlEnabled += 1
|
||||
Project = FrmMain.PageCurrent.Additional
|
||||
Project = FrmMain.PageCurrent.Additional(0)
|
||||
PanBack.ScrollToHome()
|
||||
|
||||
'重启加载器
|
||||
@@ -139,13 +138,13 @@
|
||||
'放置当前工程
|
||||
If CompItem IsNot Nothing Then PanIntro.Children.Remove(CompItem)
|
||||
CompItem = Project.ToCompItem(True, True)
|
||||
CompItem.CanInteraction = False
|
||||
CompItem.Margin = New Thickness(-7, -7, 0, 8)
|
||||
PanIntro.Children.Insert(0, CompItem)
|
||||
|
||||
'决定按钮显示
|
||||
BtnIntroWeb.Text = If(Project.FromCurseForge, "转到 CurseForge", "转到 Modrinth")
|
||||
BtnIntroWiki.Visibility = If(Project.WikiId = 0, Visibility.Collapsed, Visibility.Visible)
|
||||
BtnIntroMCBBS.Visibility = If(Project.MCBBS = 0, Visibility.Collapsed, Visibility.Visible)
|
||||
|
||||
AniControlEnabled -= 1
|
||||
End Sub
|
||||
@@ -211,10 +210,10 @@
|
||||
If Project.Type = CompType.Mod Then
|
||||
'获取 Mod 所需的加载器种类
|
||||
Dim AllowForge As Boolean? = Nothing, AllowFabric As Boolean? = Nothing
|
||||
If File.ModLoaders.Count > 0 Then '从文件中获取
|
||||
If File.ModLoaders.Any Then '从文件中获取
|
||||
AllowForge = File.ModLoaders.Contains(CompModLoaderType.Forge)
|
||||
AllowFabric = File.ModLoaders.Contains(CompModLoaderType.Fabric)
|
||||
ElseIf Project.ModLoaders.Count > 0 Then '从工程中获取
|
||||
ElseIf Project.ModLoaders.Any Then '从工程中获取
|
||||
AllowForge = Project.ModLoaders.Contains(CompModLoaderType.Forge)
|
||||
AllowFabric = Project.ModLoaders.Contains(CompModLoaderType.Fabric)
|
||||
End If
|
||||
@@ -253,7 +252,7 @@
|
||||
For Each Version As McVersion In McVersionList.Values.SelectMany(Function(l) l)
|
||||
If IsVersionSuitable(Version) Then SuitableVersions.Add(Version)
|
||||
Next
|
||||
If SuitableVersions.Count = 0 Then
|
||||
If Not SuitableVersions.Any() Then
|
||||
DefaultFolder = PathMcFolder
|
||||
If NeedLoad Then
|
||||
Hint("当前 MC 文件夹中没有找到适合这个 Mod 的版本!")
|
||||
@@ -272,19 +271,23 @@
|
||||
End If
|
||||
End If
|
||||
'获取基本信息
|
||||
Dim ChineseName As String = If(Project.TranslatedName = Project.RawName, "",
|
||||
Project.TranslatedName.Split(" (").First.Replace("\", "\").Replace("/", "/").Replace("|", "|").Replace(":", ":").Replace("<", "<").Replace(">", ">").Replace("*", "*").Replace("?", "?").Replace("""", "").Replace(": ", ":"))
|
||||
Dim FileName As String
|
||||
Select Case Setup.Get("ToolDownloadTranslate")
|
||||
Case 0
|
||||
FileName = If(ChineseName = "", "", "[" & ChineseName & "] ") & File.FileName
|
||||
Case 1
|
||||
FileName = If(ChineseName = "", "", ChineseName & "-") & File.FileName
|
||||
Case 2
|
||||
FileName = File.FileName & If(ChineseName = "", "", "-" & ChineseName)
|
||||
Case Else
|
||||
FileName = File.FileName
|
||||
End Select
|
||||
If Project.TranslatedName = Project.RawName Then
|
||||
FileName = File.FileName
|
||||
Else
|
||||
Dim ChineseName As String = Project.TranslatedName.Split(" (").First.Split(" - ").First.
|
||||
Replace("\", "\").Replace("/", "/").Replace("|", "|").Replace(":", ":").Replace("<", "<").Replace(">", ">").Replace("*", "*").Replace("?", "?").Replace("""", "").Replace(": ", ":")
|
||||
Select Case Setup.Get("ToolDownloadTranslate")
|
||||
Case 0
|
||||
FileName = $"[{ChineseName}] {File.FileName}"
|
||||
Case 1
|
||||
FileName = $"{ChineseName}-{File.FileName}"
|
||||
Case 2
|
||||
FileName = $"{File.FileName}-{ChineseName}"
|
||||
Case Else
|
||||
FileName = File.FileName
|
||||
End Select
|
||||
End If
|
||||
RunInUi(
|
||||
Sub()
|
||||
'弹窗要求选择保存位置
|
||||
@@ -319,9 +322,6 @@
|
||||
Private Sub BtnIntroWiki_Click(sender As Object, e As EventArgs) Handles BtnIntroWiki.Click
|
||||
OpenWebsite("https://www.mcmod.cn/class/" & Project.WikiId & ".html")
|
||||
End Sub
|
||||
Private Sub BtnIntroMCBBS_Click(sender As Object, e As EventArgs) Handles BtnIntroMCBBS.Click
|
||||
OpenWebsite("https://www.mcbbs.net/thread-" & Project.MCBBS & "-1-1.html")
|
||||
End Sub
|
||||
Private Sub BtnIntroCopy_Click(sender As Object, e As EventArgs) Handles BtnIntroCopy.Click
|
||||
ClipboardSet(CompItem.LabTitle.Text)
|
||||
End Sub
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<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"
|
||||
mc:Ignorable="d" x:Class="PageDownloadFabric"
|
||||
d:DesignWidth="778.571" d:DesignHeight="90.857">
|
||||
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"
|
||||
mc:Ignorable="d" x:Class="PageDownloadFabric"
|
||||
PanScroll="{Binding ElementName=PanBack}">
|
||||
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
|
||||
<StackPanel Orientation="Vertical" Margin="5">
|
||||
<local:MyCard Margin="20,20,20,0" Grid.IsSharedSizeScope="True" Title="Fabric 简介" x:Name="CardTip">
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<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"
|
||||
mc:Ignorable="d" x:Class="PageDownloadForge"
|
||||
d:DesignWidth="778.571" d:DesignHeight="90.857">
|
||||
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"
|
||||
mc:Ignorable="d" x:Class="PageDownloadForge"
|
||||
PanScroll="{Binding ElementName=PanBack}">
|
||||
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
|
||||
<StackPanel Orientation="Vertical" Margin="5">
|
||||
<local:MyCard Margin="20,20,20,0" Grid.IsSharedSizeScope="True" Title="Forge 简介" x:Name="CardTip">
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<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"
|
||||
mc:Ignorable="d" x:Class="PageDownloadInstall"
|
||||
d:DesignWidth="778.571" d:DesignHeight="90.857">
|
||||
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"
|
||||
mc:Ignorable="d" x:Class="PageDownloadInstall"
|
||||
PanScroll="{Binding ElementName=PanBack}">
|
||||
<Grid>
|
||||
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
|
||||
<Grid Margin="25,10,25,25">
|
||||
<StackPanel Grid.Row="1" Name="PanMinecraft" Grid.IsSharedSizeScope="True">
|
||||
<StackPanel Grid.Row="1" Name="PanMinecraft" Grid.IsSharedSizeScope="True" Margin="0,0,0,-10">
|
||||
<StackPanel.RenderTransform>
|
||||
<TranslateTransform />
|
||||
</StackPanel.RenderTransform>
|
||||
@@ -20,13 +20,12 @@
|
||||
<StackPanel Margin="25,37,25,15">
|
||||
<local:MyHint Text="你尚未选择安装 Fabric API,这会导致大多数 Mod 无法使用!" Margin="0,1,0,7" x:Name="HintFabricAPI" IsWarn="True" />
|
||||
<local:MyHint Text="你尚未选择安装 OptiFabric,这会导致 OptiFine 无法使用!" Margin="0,1,0,7" x:Name="HintOptiFabric" IsWarn="True" />
|
||||
<local:MyHint Text="安装结束后,请在 Mod 下载中搜索 OptiFabric Origins 并下载,否则 OptiFine 会无法使用!" Margin="0,1,0,7" x:Name="HintOptiFabricOld" IsWarn="False" />
|
||||
<Grid Margin="0,0,0,11">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<local:MyListItem Grid.Column="0" x:Name="ItemSelect" IsHitTestVisible="False" Margin="-7,0,0,0" Height="42" PaddingRight="15" SnapsToDevicePixels="True" Type="None" />
|
||||
<local:MyListItem Grid.Column="0" x:Name="ItemSelect" IsHitTestVisible="False" Margin="-7,0,0,0" Height="42" SnapsToDevicePixels="True" Type="None" />
|
||||
<local:MyButton Grid.Column="1" HorizontalAlignment="Right" ColorType="Highlight" Text="开始安装" Margin="0,2,0,0" x:Name="BtnSelectStart" MinWidth="140" Padding="13,0" Height="35" VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
<Grid Margin="0,0,0,5">
|
||||
@@ -100,9 +99,7 @@
|
||||
</Grid>
|
||||
</local:MyCard>
|
||||
<local:MyCard Title="Fabric" Height="40" Margin="0,15,0,0" x:Name="CardFabric" IsSwaped="True" CanSwap="True" SwapLogoRight="True">
|
||||
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top">
|
||||
<StackPanel Name="PanFabric" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanFabric" />
|
||||
<Grid x:Name="PanFabricInfo" Height="18" Margin="175,11,15,0" VerticalAlignment="Top" Tag="True">
|
||||
<Grid.RenderTransform>
|
||||
<TranslateTransform />
|
||||
|
||||
@@ -32,13 +32,14 @@
|
||||
#Region "页面切换"
|
||||
|
||||
'页面切换动画
|
||||
Private IsInSelectPage As Boolean = False
|
||||
Public IsInSelectPage As Boolean = False
|
||||
Private IsFirstLoaded As Boolean = False
|
||||
Private Sub EnterSelectPage()
|
||||
If IsInSelectPage Then Exit Sub
|
||||
IsInSelectPage = True
|
||||
|
||||
AutoSelectedFabricApi = False
|
||||
AutoSelectedOptiFabric = False
|
||||
IsSelectNameEdited = False
|
||||
PanSelect.Visibility = Visibility.Visible
|
||||
PanSelect.IsHitTestVisible = True
|
||||
@@ -272,11 +273,13 @@
|
||||
End If
|
||||
End Sub
|
||||
|
||||
Private IsReloading As Boolean = False '#3742 中,LoadOptiFineGetError 会初始化 LoadOptiFine,触发事件 LoadOptiFine.StateChanged,导致再次调用 SelectReload
|
||||
''' <summary>
|
||||
''' 重载已选择的项目的显示。
|
||||
''' </summary>
|
||||
Private Sub SelectReload() Handles CardOptiFine.Swap, LoadOptiFine.StateChanged, CardForge.Swap, LoadForge.StateChanged, CardFabric.Swap, LoadFabric.StateChanged, CardFabricApi.Swap, LoadFabricApi.StateChanged, CardOptiFabric.Swap, LoadOptiFabric.StateChanged, CardLiteLoader.Swap, LoadLiteLoader.StateChanged
|
||||
If SelectedMinecraftId Is Nothing Then Exit Sub
|
||||
If SelectedMinecraftId Is Nothing OrElse IsReloading Then Exit Sub
|
||||
IsReloading = True
|
||||
'主预览
|
||||
SelectNameUpdate()
|
||||
ItemSelect.Title = TextSelectName.Text
|
||||
@@ -384,17 +387,12 @@
|
||||
'主警告
|
||||
HintFabricAPI.Visibility = If(SelectedFabric IsNot Nothing AndAlso SelectedFabricApi Is Nothing, Visibility.Visible, Visibility.Collapsed)
|
||||
If SelectedFabric IsNot Nothing AndAlso SelectedOptiFine IsNot Nothing AndAlso SelectedOptiFabric Is Nothing Then
|
||||
If SelectedMinecraftId.StartsWith("1.14") OrElse SelectedMinecraftId.StartsWith("1.15") Then
|
||||
HintOptiFabric.Visibility = Visibility.Collapsed
|
||||
HintOptiFabricOld.Visibility = Visibility.Visible
|
||||
Else
|
||||
HintOptiFabric.Visibility = Visibility.Visible
|
||||
HintOptiFabricOld.Visibility = Visibility.Collapsed
|
||||
End If
|
||||
HintOptiFabric.Visibility = Visibility.Visible
|
||||
Else
|
||||
HintOptiFabric.Visibility = Visibility.Collapsed
|
||||
HintOptiFabricOld.Visibility = Visibility.Collapsed
|
||||
End If
|
||||
'结束
|
||||
IsReloading = False
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 清空已选择的项目。
|
||||
@@ -522,7 +520,7 @@
|
||||
Version("id") = "20w14∞"
|
||||
Version("type") = "special"
|
||||
Version.Add("lore", GetMcFoolName(Version("id")))
|
||||
Case "3d shareware v1.34", "1.rv-pre1", "15w14a", "2.0", "22w13oneblockatatime"
|
||||
Case "3d shareware v1.34", "1.rv-pre1", "15w14a", "2.0", "22w13oneblockatatime", "23w13a_or_b", "24w14potato"
|
||||
Type = "愚人节版"
|
||||
Version("type") = "special"
|
||||
Version.Add("lore", GetMcFoolName(Version("id")))
|
||||
@@ -548,11 +546,11 @@
|
||||
Dim CardInfo As New MyCard With {.Title = "最新版本", .Margin = New Thickness(0, 15, 0, 15), .SwapType = 2}
|
||||
Dim TopestVersions 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")
|
||||
Release("lore") = "最新正式版,发布于 " & Release("releaseTime").Value(Of Date).ToString("yyyy'/'MM'/'dd HH':'mm")
|
||||
TopestVersions.Add(Release)
|
||||
If Dict("正式版")(0)("releaseTime").Value(Of Date) < Dict("预览版")(0)("releaseTime").Value(Of Date) Then
|
||||
Dim Snapshot As JObject = Dict("预览版")(0).DeepClone()
|
||||
Snapshot("lore") = "最新预览版,发布于 " & Snapshot("releaseTime").Value(Of Date).ToString("yyyy/MM/dd HH:mm")
|
||||
Snapshot("lore") = "最新预览版,发布于 " & Snapshot("releaseTime").Value(Of Date).ToString("yyyy'/'MM'/'dd HH':'mm")
|
||||
TopestVersions.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 = TopestVersions}
|
||||
@@ -561,7 +559,7 @@
|
||||
PanMinecraft.Children.Insert(0, CardInfo)
|
||||
'添加其他版本
|
||||
For Each Pair As KeyValuePair(Of String, List(Of JObject)) In Dict
|
||||
If Pair.Value.Count = 0 Then Continue For
|
||||
If Not Pair.Value.Any() Then Continue For
|
||||
'增加卡片
|
||||
Dim NewCard As New MyCard With {.Title = Pair.Key & " (" & Pair.Value.Count & ")", .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}
|
||||
@@ -570,10 +568,23 @@
|
||||
NewCard.IsSwaped = True
|
||||
PanMinecraft.Children.Add(NewCard)
|
||||
Next
|
||||
'自动选择版本
|
||||
If McVersionWaitingForSelect Is Nothing Then Exit Try
|
||||
Log("[Download] 自动选择 MC 版本:" & McVersionWaitingForSelect)
|
||||
For Each Version As JObject In Versions
|
||||
If Version("id").ToString <> McVersionWaitingForSelect Then Continue For
|
||||
Dim Item = McDownloadListItem(Version, Sub()
|
||||
End Sub, False)
|
||||
MinecraftSelected(Item, Nothing)
|
||||
Next
|
||||
Catch ex As Exception
|
||||
Log(ex, "可视化安装版本列表出错", LogLevel.Feedback)
|
||||
End Try
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 当 MC 版本列表加载完时,立即自动选择的版本。用于外部调用。
|
||||
''' </summary>
|
||||
Public Shared McVersionWaitingForSelect As String = Nothing
|
||||
|
||||
#End Region
|
||||
|
||||
@@ -583,7 +594,7 @@
|
||||
''' 获取 OptiFine 的加载异常信息。若正常则返回 Nothing。
|
||||
''' </summary>
|
||||
Private Function LoadOptiFineGetError() As String
|
||||
If LoadOptiFine.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadOptiFine Is Nothing OrElse LoadOptiFine.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadOptiFine.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadOptiFine.State, Object).Error.Message
|
||||
'检查 Forge 1.13 - 1.14.3:全部不兼容
|
||||
If SelectedForge IsNot Nothing AndAlso
|
||||
@@ -642,7 +653,7 @@
|
||||
If SelectedForge IsNot Nothing AndAlso Not IsOptiFineSuitForForge(Version, SelectedForge) Then Continue For
|
||||
If Version.NameDisplay.StartsWith(SelectedMinecraftId & " ") Then Versions.Add(Version)
|
||||
Next
|
||||
If Versions.Count = 0 Then Exit Sub
|
||||
If Not Versions.Any() Then Exit Sub
|
||||
'排序
|
||||
Versions = Sort(Versions, Function(Left As DlOptiFineListEntry, Right As DlOptiFineListEntry) As Boolean
|
||||
If Not Left.IsPreview AndAlso Right.IsPreview Then Return True
|
||||
@@ -686,7 +697,7 @@
|
||||
''' </summary>
|
||||
Private Function LoadLiteLoaderGetError() As String
|
||||
If Not SelectedMinecraftId.Contains("1.") OrElse Val(SelectedMinecraftId.Split(".")(1)) > 12 Then Return "没有可用版本"
|
||||
If LoadLiteLoader.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadLiteLoader Is Nothing OrElse LoadLiteLoader.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadLiteLoader.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadLiteLoader.State, Object).Error.Message
|
||||
For Each Version As DlLiteLoaderListEntry In DlLiteLoaderListLoader.Output.Value
|
||||
If Version.Inherit = SelectedMinecraftId Then Return Nothing
|
||||
@@ -710,7 +721,7 @@
|
||||
For Each Version As DlLiteLoaderListEntry In DlLiteLoaderListLoader.Output.Value
|
||||
If Version.Inherit = SelectedMinecraftId Then Versions.Add(Version)
|
||||
Next
|
||||
If Versions.Count = 0 Then Exit Sub
|
||||
If Not Versions.Any() Then Exit Sub
|
||||
'可视化
|
||||
PanLiteLoader.Children.Clear()
|
||||
For Each Version In Versions
|
||||
@@ -790,7 +801,7 @@
|
||||
'获取要显示的版本
|
||||
Dim Versions As New List(Of DlForgeVersionEntry)
|
||||
Versions.AddRange(Loader.Output) '复制数组,以免 Output 在实例化后变空
|
||||
If Loader.Output.Count = 0 Then Exit Sub
|
||||
If Not Loader.Output.Any() Then Exit Sub
|
||||
PanForge.Children.Clear()
|
||||
Versions = Sort(Versions, Function(a, b) New Version(a.Version) > New Version(b.Version)).
|
||||
Where(Function(v)
|
||||
@@ -831,10 +842,10 @@
|
||||
''' 获取 Fabric 的加载异常信息。若正常则返回 Nothing。
|
||||
''' </summary>
|
||||
Private Function LoadFabricGetError() As String
|
||||
If LoadFabric.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadFabric Is Nothing OrElse LoadFabric.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadFabric.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadFabric.State, Object).Error.Message
|
||||
For Each Version As JObject In DlFabricListLoader.Output.Value("game")
|
||||
If Version("version").ToString = SelectedMinecraftId.Replace("∞", "infinite") Then
|
||||
If Version("version").ToString = SelectedMinecraftId.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3") Then
|
||||
If SelectedForge IsNot Nothing Then Return "与 Forge 不兼容"
|
||||
'If SelectedOptiFine IsNot Nothing Then Return "与 OptiFine 不兼容"
|
||||
Return Nothing
|
||||
@@ -856,19 +867,19 @@
|
||||
If DlFabricListLoader.State <> LoadState.Finished Then Exit Sub
|
||||
'获取版本列表
|
||||
Dim Versions As JArray = DlFabricListLoader.Output.Value("loader")
|
||||
If Versions.Count = 0 Then Exit Sub
|
||||
If Not Versions.Any() Then Exit Sub
|
||||
'可视化
|
||||
PanFabric.Children.Clear()
|
||||
For Each Version In Versions
|
||||
PanFabric.Children.Add(FabricDownloadListItem(Version, AddressOf Fabric_Selected))
|
||||
Next
|
||||
PanFabric.Tag = Versions
|
||||
CardFabric.SwapControl = PanFabric
|
||||
CardFabric.SwapType = 12
|
||||
Catch ex As Exception
|
||||
Log(ex, "可视化 Fabric 安装版本列表出错", LogLevel.Feedback)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
'选择与清除
|
||||
Private Sub Fabric_Selected(sender As MyListItem, e As EventArgs)
|
||||
Public Sub Fabric_Selected(sender As MyListItem, e As EventArgs)
|
||||
SelectedFabric = sender.Tag("version").ToString
|
||||
FabricApi_Loaded()
|
||||
OptiFabric_Loaded()
|
||||
@@ -894,7 +905,7 @@
|
||||
Public Shared Function IsSuitableFabricApi(DisplayName As String, MinecraftVersion As String) As Boolean
|
||||
Try
|
||||
If DisplayName Is Nothing OrElse MinecraftVersion Is Nothing Then Return False
|
||||
DisplayName = DisplayName.ToLower : MinecraftVersion = MinecraftVersion.ToLower
|
||||
DisplayName = DisplayName.ToLower : MinecraftVersion = MinecraftVersion.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3").ToLower
|
||||
If DisplayName.StartsWith("[" & MinecraftVersion & "]") Then Return True
|
||||
If Not DisplayName.Contains("/") OrElse Not DisplayName.Contains("]") Then Return False
|
||||
'直接的判断(例如 1.18.1/22w03a)
|
||||
@@ -931,11 +942,16 @@
|
||||
''' 获取 FabricApi 的加载异常信息。若正常则返回 Nothing。
|
||||
''' </summary>
|
||||
Private Function LoadFabricApiGetError() As String
|
||||
If SelectedFabric Is Nothing Then Return "需要安装 Fabric"
|
||||
If LoadFabricApi.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadFabricApi Is Nothing OrElse LoadFabricApi.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadFabricApi.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadFabricApi.State, Object).Error.Message
|
||||
If DlFabricApiLoader.Output Is Nothing Then
|
||||
If SelectedFabric Is Nothing Then Return "需要安装 Fabric"
|
||||
Return "正在获取版本列表……"
|
||||
End If
|
||||
For Each Version In DlFabricApiLoader.Output
|
||||
If IsSuitableFabricApi(Version.DisplayName, SelectedMinecraftId) Then Return Nothing
|
||||
If Not IsSuitableFabricApi(Version.DisplayName, SelectedMinecraftId) Then Continue For
|
||||
If SelectedFabric Is Nothing Then Return "需要安装 Fabric"
|
||||
Return Nothing
|
||||
Next
|
||||
Return "没有可用版本"
|
||||
End Function
|
||||
@@ -964,7 +980,7 @@
|
||||
Versions.Add(Version)
|
||||
End If
|
||||
Next
|
||||
If Versions.Count = 0 Then Exit Sub
|
||||
If Not Versions.Any() Then Exit Sub
|
||||
Versions = Sort(Versions, Function(a, b) a.ReleaseDate > b.ReleaseDate)
|
||||
'可视化
|
||||
PanFabricApi.Children.Clear()
|
||||
@@ -1013,18 +1029,25 @@
|
||||
End Try
|
||||
End Function
|
||||
|
||||
Private AutoSelectedOptiFabric As Boolean = False
|
||||
''' <summary>
|
||||
''' 获取 OptiFabric 的加载异常信息。若正常则返回 Nothing。
|
||||
''' </summary>
|
||||
Private Function LoadOptiFabricGetError() As String
|
||||
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"
|
||||
If SelectedMinecraftId.StartsWith("1.14") OrElse SelectedMinecraftId.StartsWith("1.15") Then Return "不兼容老版本 Fabric,请手动下载 OptiFabric Origins"
|
||||
If LoadOptiFabric.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadOptiFabric Is Nothing OrElse LoadOptiFabric.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadOptiFabric.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadOptiFabric.State, Object).Error.Message
|
||||
If DlOptiFabricLoader.Output Is Nothing Then
|
||||
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"
|
||||
Return "正在获取版本列表……"
|
||||
End If
|
||||
For Each Version In DlOptiFabricLoader.Output
|
||||
If IsSuitableOptiFabric(Version, SelectedMinecraftId) Then Return Nothing
|
||||
If Not IsSuitableOptiFabric(Version, SelectedMinecraftId) 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"
|
||||
Return Nothing '通过检查
|
||||
Next
|
||||
Return "没有可用版本"
|
||||
End Function
|
||||
@@ -1046,19 +1069,23 @@
|
||||
For Each Version In DlOptiFabricLoader.Output
|
||||
If IsSuitableOptiFabric(Version, SelectedMinecraftId) Then Versions.Add(Version)
|
||||
Next
|
||||
If Versions.Count = 0 Then Exit Sub
|
||||
If Not Versions.Any() Then Exit Sub
|
||||
'排序
|
||||
Versions = Sort(Versions, Function(Left As CompFile, Right As CompFile) As Boolean
|
||||
Return Left.ReleaseDate > Right.ReleaseDate
|
||||
End Function)
|
||||
Versions = Sort(Versions, Function(a, b) a.ReleaseDate > b.ReleaseDate)
|
||||
'可视化
|
||||
PanOptiFabric.Children.Clear()
|
||||
For Each Version In Versions
|
||||
If Not IsSuitableOptiFabric(Version, SelectedMinecraftId) Then Continue For
|
||||
PanOptiFabric.Children.Add(OptiFabricDownloadListItem(Version, AddressOf OptiFabric_Selected))
|
||||
Next
|
||||
'自动选择 OptiFabric
|
||||
If Not AutoSelectedOptiFabric Then
|
||||
AutoSelectedOptiFabric = True
|
||||
Log($"[Download] 已自动选择 OptiFabric:{CType(PanOptiFabric.Children(0), MyListItem).Title}")
|
||||
OptiFabric_Selected(PanOptiFabric.Children(0), Nothing)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "可视化 Fabric API 安装版本列表出错", LogLevel.Feedback)
|
||||
Log(ex, "可视化 OptiFabric 安装版本列表出错", LogLevel.Feedback)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
@@ -1095,6 +1122,7 @@
|
||||
'提交安装申请
|
||||
Dim Request As New McInstallRequest With {
|
||||
.TargetVersionName = TextSelectName.Text,
|
||||
.TargetVersionFolder = $"{PathMcFolder}versions\{TextSelectName.Text}\",
|
||||
.MinecraftJson = SelectedMinecraftJsonUrl,
|
||||
.MinecraftName = SelectedMinecraftId,
|
||||
.OptiFineEntry = SelectedOptiFine,
|
||||
|
||||
@@ -5,74 +5,74 @@
|
||||
<StackPanel Margin="0,12,0,0" Name="PanItem">
|
||||
|
||||
<TextBlock x:Name="LabGame" Text="Minecraft" Margin="13,6,5,4" Opacity="0.6" FontSize="12" />
|
||||
<local:MyListItem x:Name="ItemInstall" Checked="True" IsScaleAnimationEnabled="False" Type="RadioBox" Tag="1" PaddingRight="35" Height="36" VerticalAlignment="Top" Title="自动安装"
|
||||
<local:MyListItem x:Name="ItemInstall" Checked="True" IsScaleAnimationEnabled="False" Type="RadioBox" Tag="1" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="自动安装"
|
||||
LogoScale="0.87" Logo="M1091.291429 0H78.935771C35.34848 0.035109 0.029257 35.354331 0 78.935771v863.331475c0 43.534629 35.401143 78.994286 78.935771 78.994285H1091.291429c43.534629 0 78.994286-35.401143 78.994285-78.994285V78.871406C1170.156983 35.319223 1134.849463 0.064366 1091.291429 0z m-8.835658 87.771429v78.754377H87.771429v-78.760229h994.684342zM87.771429 933.425737V254.232869h994.684342v679.140205H87.771429v0.058515zM724.95104 340.00896l-206.19264 547.605943a43.903269 43.903269 0 0 1-82.154057-31.012572l206.139977-547.547428a43.944229 43.944229 0 0 1 82.20672 30.954057zM369.558674 545.909029l-85.489371 85.489371 85.489371 85.542034a43.885714 43.885714 0 0 1-62.025143 62.083657l-116.554605-116.560457a43.8272 43.8272 0 0 1 0-62.025143l116.560457-116.49024a43.885714 43.885714 0 0 1 62.019291 61.966629z m610.567315-37.566172a43.885714 43.885714 0 0 1 0 62.083657l-116.560458 116.560457a43.768686 43.768686 0 0 1-62.019291 0 43.885714 43.885714 0 0 1 0-62.083657l85.547886-85.547885-85.547886-85.542035a43.897417 43.897417 0 0 1 62.083657-62.083657l116.496092 116.618972z">
|
||||
<local:MyListItem.Buttons>
|
||||
<x:Array Type="{x:Type local:MyIconButton}">
|
||||
<local:MyIconButton Tag="1" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
|
||||
<local:MyIconButton Tag="1" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
|
||||
</x:Array>
|
||||
</local:MyListItem.Buttons>
|
||||
</local:MyListItem>
|
||||
<local:MyListItem x:Name="ItemHand" IsScaleAnimationEnabled="False" Type="RadioBox" PaddingRight="35" Height="36" VerticalAlignment="Top" Title="手动安装包"
|
||||
<local:MyListItem x:Name="ItemHand" IsScaleAnimationEnabled="False" Type="RadioBox" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="手动安装包"
|
||||
LogoScale="1.02" Logo="M292.693 787.627c0 30.72-24.746 55.466-55.466 55.466s-55.467-24.746-55.467-55.466 24.747-55.467 55.467-55.467c30.72-0.853 55.466 24.747 55.466 55.467z M1000.96 241.493c-2.56-5.973-4.267-11.946-6.827-17.92-0.853-6.826-4.266-12.8-8.533-17.92-7.68-9.386-18.773-15.36-31.573-15.36-6.827 0-13.654 1.707-19.627 5.12l-15.36 15.36-17.067 17.067-21.333 21.333L749.227 381.44c-14.507 14.507-38.4 14.507-52.907 0l-54.613-54.613c-14.507-14.507-14.507-38.4 0-52.907l132.266-132.267 14.507-14.506 20.48-20.48 16.213-16.214c3.414-5.973 5.12-11.946 5.12-19.626 0-12.8-5.973-24.747-16.213-32.427-5.973-4.267-11.947-6.827-18.773-7.68-5.12-2.56-10.24-4.267-16.214-5.973-36.693-12.8-76.8-19.627-117.76-19.627-7.68 0-14.506 0-22.186 0.853-188.587 11.947-337.92 168.107-337.92 360.96 0 16.214 0.853 32.427 3.413 48.64L56.32 663.04c-73.387 74.24-73.387 193.707 0 267.093l33.28 33.28c73.387 73.387 193.707 73.387 267.093 0l240.64-241.493c21.334 4.267 42.667 5.973 64 5.973 190.294 0 346.454-146.773 360.107-333.653 0.853-8.533 0.853-17.92 0.853-27.307 0.854-44.373-6.826-86.186-21.333-125.44zM658.773 639.147c-28.16 0-54.613-4.267-79.36-11.094-4.266 7.68-10.24 14.507-16.213 21.334l-258.56 258.56c-43.52 43.52-115.2 43.52-158.72 0l-29.013-29.014c-43.52-44.373-43.52-115.2 0-158.72l212.48-212.48 46.08-46.08c5.973-5.973 12.8-11.093 19.626-15.36-8.533-26.453-12.8-54.613-12.8-83.626v-1.707c0-152.747 123.734-275.627 276.48-275.627 18.774 0 37.547 1.707 55.467 5.974L597.333 207.36c-52.053 52.053-52.053 137.387 0 189.44l29.867 29.867c52.053 52.053 137.387 52.053 189.44 0L930.987 312.32c2.56 16.213 4.266 33.28 4.266 50.347 0 152.746-123.733 276.48-276.48 276.48z" />
|
||||
<TextBlock x:Name="LabHand" Visibility="Collapsed" Text="▼ 手动安装包" Padding="11,18,5,4" Opacity="0.6" FontSize="12" />
|
||||
<local:MyListItem x:Name="ItemClient" Visibility="Collapsed" IsScaleAnimationEnabled="False" Type="RadioBox" Tag="4" PaddingRight="35" Height="36" VerticalAlignment="Top" Title="Minecraft"
|
||||
<local:MyListItem x:Name="ItemClient" Visibility="Collapsed" IsScaleAnimationEnabled="False" Type="RadioBox" Tag="4" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="Minecraft"
|
||||
LogoScale="0.98" Logo="M989.7 510.8l-202.3-101c-10.8-5.4-17.7-16.5-17.7-28.6V172.7c0-26.7-16.6-50.6-41.6-59.9L550.5 46.3c-24.2-9-50.8-9-74.9 0.1l-176.9 66.8c-24.9 9.4-41.4 33.2-41.4 59.9v207.1c0 12.1-6.9 23.2-17.7 28.6l-204 101.9C13.9 521.6 0.1 543.8 0.2 568.1l0.5 226c0 23.2 12.6 44.5 32.9 55.8l223.8 124.6 1.2 0.7c18.9 10.5 41.9 10.4 60.8-0.2l2-1.1L497 873.2c9.9-5.7 22.1-5.6 32 0.1l176.7 102.5c18.9 10.7 42 10.8 61 0.3l3-1.7L992.4 851c20.4-11.3 33-32.8 33-56l-0.3-226.9c0-24.3-13.7-46.5-35.4-57.3zM321 192l115.3 46.1c27 10.8 44.7 36.9 44.7 66v163c0 5.9-6.2 9.8-11.6 7.2L339 409.5c-10.9-5.4-17.7-16.5-17.8-28.6L321 192z m-63.7 703.4L82.5 809.6c-10.9-5.4-17.9-16.5-17.9-28.7l-0.4-189.5L217.9 668c24.1 12 39.4 36.7 39.4 63.6v163.8z m64-274.3c-20.1 10.1-43.7 10-63.8 0l-0.2-0.1-149.1-75 149.1-74.4 16.8-8.4c9.2-4.6 20-4.5 29.1 0.3l18.1 9.4 129.8 64.3c5.8 2.9 6 11.1 0.2 14.2l-130 69.7z m141.9 203.8l-141.8 70.6-0.8-163.6c-0.1-27 15.1-51.8 39.2-63.9l108.8-54.5c5.3-2.7 11.6 1.2 11.6 7.1l0.8 175.6c0 12.1-6.9 23.2-17.8 28.7zM385 144l128-48 128 48-103 38.6c-16.1 6-33.8 6-49.9 0L385 144z m320 48l0.6 187.8c0 12.2-6.8 23.3-17.7 28.7l-131.4 65.7c-5.3 2.7-11.6-1.2-11.6-7.2V304.1c0-29.1 17.7-55.2 44.7-66L705 192z m0.7 710.3l-143.8-77.2c-10.4-5.6-16.9-16.4-16.9-28.2v-176c0-5.9 6.3-9.8 11.6-7.2l109.3 54.6c24 12 39.2 36.5 39.3 63.4l0.5 170.6z m-2-280.5l-129.2-70.5c-5.7-3.1-5.5-11.3 0.3-14.2l131-65.3 16.1-8.5c9.1-4.8 19.9-4.9 29.1-0.3l18.7 9.3L916.9 546l-146.2 76.4c-21.1 11-46.2 10.8-67-0.6z m66 279.4l-0.6-169.1c-0.1-27 15.1-51.7 39.3-63.8L961 592v188.7c0 11.9-6.6 22.7-17 28.3l-174.3 92.2z">
|
||||
<!--Logo="M469.333333 213.333333V128a42.666667 42.666667 0 1 1 85.333334 0v85.333333h127.829333C847.722667 213.333333 981.333333 347.029333 981.333333 512.149333v319.936A106.496 106.496 0 0 1 874.730667 938.666667h-85.461334c-48.661333 0-107.221333-29.290667-136.469333-68.266667l-64-85.333333c-6.144-8.170667-23.957333-17.066667-34.24-17.066667h-85.12c-10.282667 0-28.096 8.896-34.24 17.066667l-64 85.333333c-29.226667 38.976-87.808 68.266667-136.469333 68.266667H149.269333A106.496 106.496 0 0 1 42.666667 832.085333V512.149333C42.666667 347.029333 176.277333 213.333333 341.504 213.333333H469.333333z m213.162667 85.333334H341.504A213.333333 213.333333 0 0 0 128 512.149333v319.936c0 11.818667 9.450667 21.248 21.269333 21.248h85.461334c21.802667 0 55.082667-16.64 68.202666-34.133333l64-85.333333c22.272-29.696 65.408-51.2 102.506667-51.2h85.12c37.098667 0 80.213333 21.504 102.506667 51.2l64 85.333333c13.12 17.493333 46.4 34.133333 68.202666 34.133333h85.461334a21.162667 21.162667 0 0 0 21.269333-21.248V512.149333A213.333333 213.333333 0 0 0 682.496 298.666667zM362.666667 469.333333h42.666666a42.666667 42.666667 0 1 1 0 85.333334h-42.666666v42.666666a42.666667 42.666667 0 1 1-85.333334 0v-42.666666h-42.666666a42.666667 42.666667 0 1 1 0-85.333334h42.666666v-42.666666a42.666667 42.666667 0 1 1 85.333334 0v42.666666z m341.333333-21.333333a42.666667 42.666667 0 1 1 0-85.333333 42.666667 42.666667 0 0 1 0 85.333333z m0 213.333333a42.666667 42.666667 0 1 1 0-85.333333 42.666667 42.666667 0 0 1 0 85.333333z m-106.666667-106.666666a42.666667 42.666667 0 1 1 0-85.333334 42.666667 42.666667 0 0 1 0 85.333334z m213.333334 0a42.666667 42.666667 0 1 1 0-85.333334 42.666667 42.666667 0 0 1 0 85.333334z">-->
|
||||
<local:MyListItem.Buttons>
|
||||
<x:Array Type="{x:Type local:MyIconButton}">
|
||||
<local:MyIconButton Tag="4" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
|
||||
<local:MyIconButton Tag="4" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
|
||||
</x:Array>
|
||||
</local:MyListItem.Buttons>
|
||||
</local:MyListItem>
|
||||
<local:MyListItem x:Name="ItemOptiFine" Visibility="Collapsed" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="5" PaddingRight="35" Height="36" VerticalAlignment="Top" Title="OptiFine"
|
||||
<local:MyListItem x:Name="ItemOptiFine" Visibility="Collapsed" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="5" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="OptiFine"
|
||||
LogoScale="1.00" Logo="M439.667 538.133l-323.311-174.763c-6.554-3.641-13.836-5.098-20.389-5.098-23.301 0-45.147 20.389-45.147 48.788v341.516c0 35.681 18.205 67.721 48.060 83.741l323.311 174.763c6.554 3.641 13.836 5.825 20.389 5.825 23.301 0 45.147-21.117 45.147-49.516v-341.516c0-34.953-18.205-67.721-48.060-83.741zM414.909 920.427l-281.805-152.189c-4.369-2.913-9.467-9.467-9.467-19.661v-298.553l281.805 152.189c5.825 3.641 9.467 11.651 9.467 19.661v298.553zM952.304 358.274c-6.554 0-13.836 1.456-20.389 5.098l-323.311 175.491c-29.855 16.020-48.060 48.788-48.060 84.468v340.059c0 28.399 21.845 49.516 45.147 49.516 6.554 0 13.836-1.456 20.389-5.098l323.311-175.491c29.127-16.020 48.060-48.060 48.060-83.741v-341.516c0-28.399-21.845-48.788-45.147-48.788zM924.634 747.849c0 10.194-5.098 17.476-9.467 19.661l-281.805 152.917v-297.097c0-10.194 5.098-17.476 9.467-19.661l281.805-153.646v297.825zM902.060 283.272c15.292-8.010 22.574-21.845 21.117-34.953 1.456-13.836-5.825-26.943-21.117-34.953l-329.136-171.85c-14.563-8.010-31.312-11.651-48.788-11.651 0 0 0 0 0 0 0 0 0 0 0 0s-34.224 3.641-49.516 11.651l-329.865 171.122c-15.292 8.010-21.845 21.845-21.117 35.681-0.729 13.107 5.825 26.943 21.117 34.953l329.136 171.122c14.563 8.010 32.039 11.651 49.516 11.651 0 0 0 0 0.729 0 0 0 0 0 0.729 0s34.224-3.641 49.516-11.651l327.68-171.122zM523.407 393.227c-5.825 0-11.651-1.456-16.020-3.641l-272.338-141.266 272.338-141.995c4.369-2.185 10.194-3.641 16.748-3.641 5.825 0 11.651 1.456 16.020 3.641l272.338 141.995-273.067 141.266c-3.641 2.185-9.467 3.641-16.020 3.641z">
|
||||
<!--Logo="M12 0l-11 6v12.131l11 5.869 11-5.869v-12.066l-11-6.065zm-1 21.2l-6.664-3.555 4.201-2.801c1.08-.719-.066-2.359-1.243-1.575l-4.294 2.862v-7.901l8 4.363v8.607zm-6.867-14.63l6.867-3.746v4.426c0 1.323 2 1.324 2 0v-4.415l6.91 3.811-7.905 4.218-7.872-4.294zm8.867 6.03l8-4.269v7.8l-4.263-2.842c-1.181-.785-2.323.855-1.245 1.574l4.172 2.781-6.664 3.556v-8.6z">-->
|
||||
<local:MyListItem.Buttons>
|
||||
<x:Array Type="{x:Type local:MyIconButton}">
|
||||
<local:MyIconButton Tag="5" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
|
||||
<local:MyIconButton Tag="5" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
|
||||
</x:Array>
|
||||
</local:MyListItem.Buttons>
|
||||
</local:MyListItem>
|
||||
<local:MyListItem x:Name="ItemForge" Visibility="Collapsed" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="6" PaddingRight="35" Height="36" VerticalAlignment="Top" Title="Forge"
|
||||
<local:MyListItem x:Name="ItemForge" Visibility="Collapsed" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="6" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="Forge"
|
||||
LogoScale="1.00" Logo="M402.807089 481.189274c-12.040221-6.991228-27.412326-2.797719-34.397415 9.242502l-8.387018 14.512529c-6.987135 12.035104-2.793626 27.40721 9.246595 34.392298 12.040221 6.985088 27.408233 2.797719 34.393321-9.242502l8.387018-14.511505C418.930301 503.437997 414.842193 488.063845 402.807089 481.189274L402.807089 481.189274zM495.028467 919.955342 59.056024 919.955342c-18.485003 0-33.532721 15.046694-33.532721 33.536814 0 18.485003 15.046694 33.532721 33.532721 33.532721l435.972443 0c18.49012 0 33.537837-15.046694 33.537837-33.532721C528.566304 935.002037 513.518586 919.955342 495.028467 919.955342L495.028467 919.955342zM997.431901 660.155295 590.482469 446.039729l90.935082-157.474544c18.485003-32.031531 7.520277-73.090669-24.51023-91.581812L395.496589 46.070268c-32.031531-18.485003-73.094762-7.520277-91.579765 24.51023L18.74697 564.277644c-18.485003 32.031531-7.521301 73.089645 24.51023 91.579765l261.408685 150.907988c32.031531 18.491143 73.095785 7.526417 91.581812-24.505114l91.364871-158.330028 392.332527 239.48128c16.016789 9.247618 36.544311 3.76372 45.792952-12.249999l83.838454-145.219429C1019.033895 689.930435 1013.444597 669.39882 997.431901 660.155295L997.431901 660.155295zM355.078041 719.704552c-9.242502 16.012695-29.774117 21.497617-45.787836 12.251022L105.923448 614.57826c-16.016789-9.242502-21.497617-29.774117-12.255115-45.787836l251.522524-435.653171c9.246595-16.012695 29.774117-21.497617 45.792952-12.249999l203.25624 117.376291c16.018835 9.242502 21.497617 29.774117 12.255115 45.786813L355.078041 719.704552 355.078041 719.704552zM884.568391 788.601712 522.115382 564.492538l33.537837-58.048067L934.875147 701.539844 884.568391 788.601712 884.568391 788.601712zM520.184403 277.8174c-12.040221-6.987135-27.41335-2.792603-34.399461 9.246595l-75.456552 130.702807c-6.985088 12.040221-2.791579 27.412326 9.242502 34.397415 12.040221 6.987135 27.412326 2.791579 34.397415-9.242502l75.458599-130.706901C536.307615 300.174594 532.219507 284.807604 520.184403 277.8174L520.184403 277.8174zM520.184403 277.8174">
|
||||
<!--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="6" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
|
||||
<local:MyIconButton Tag="6" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
|
||||
</x:Array>
|
||||
</local:MyListItem.Buttons>
|
||||
</local:MyListItem>
|
||||
<local:MyListItem x:Name="ItemFabric" Visibility="Collapsed" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="7" PaddingRight="35" Height="36" VerticalAlignment="Top" Title="Fabric"
|
||||
<local:MyListItem x:Name="ItemFabric" Visibility="Collapsed" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="7" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="Fabric"
|
||||
LogoScale="1.02" Logo="M826.453333 170.666667c-100.266667-93.866667-248.96-76.373333-256-75.52a31.786667 31.786667 0 0 0-26.666666 22.613333c-1.066667 3.2-101.546667 330.666667-426.666667 445.013333a29.013333 29.013333 0 0 0-7.893333 4.48l-1.493334 1.066667a29.44 29.44 0 0 0-5.973333 6.613333 31.786667 31.786667 0 0 0-3.626667 7.68 5.333333 5.333333 0 0 1 0 1.493334 30.506667 30.506667 0 0 0 0 8.96c7.466667 116.266667 45.013333 204.586667 112 262.826666a303.36 303.36 0 0 0 194.773334 71.466667h11.52a32.426667 32.426667 0 0 0 27.093333-19.626667 694.186667 694.186667 0 0 1 159.36-234.666666l-7.68 55.466666A31.786667 31.786667 0 0 0 621.44 768h4.48a32.213333 32.213333 0 0 0 31.786667-27.733333l15.573333-112.426667 112.426667 8.32a32 32 0 1 0 4.693333-64l-56.96-4.266667c36.48-25.813333 74.026667-50.56 112-75.52l51.626667-34.133333a31.573333 31.573333 0 0 0 14.08-24.746667c6.186667-115.2-21.76-203.306667-84.693334-262.826666zM620.8 573.013333l-1.706667 1.493334a219.093333 219.093333 0 0 0-87.466666-116.693334 222.933333 222.933333 0 0 0-95.146667-33.28c18.986667-21.333333 36.266667-42.666667 51.626667-64a198.613333 198.613333 0 0 1 202.453333 158.08c-20.053333 14.506667-39.893333 29.44-59.52 45.226667a29.866667 29.866667 0 0 0-10.24 9.173333z m-228.693333 292.266667a238.72 238.72 0 0 1-141.44-55.893333 213.333333 213.333333 0 0 1-34.986667-39.466667 123.946667 123.946667 0 0 0 44.373333 8.106667 168.533333 168.533333 0 0 0 69.12-16.213334 29.44 29.44 0 0 0 7.04-4.266666l106.666667-85.333334a32 32 0 1 0-39.893333-49.92L298.666667 704a78.293333 78.293333 0 0 1-69.546667 1.706667c-32.426667-17.066667-52.693333-61.653333-62.506667-93.44a709.333333 709.333333 0 0 0 202.453334-123.733334 187.52 187.52 0 0 1 128 21.333334 171.946667 171.946667 0 0 1 68.906666 109.653333 756.48 756.48 0 0 0-173.866666 245.76zM810.666667 438.4l-64 42.666667A256 256 0 0 0 529.706667 298.666667a810.666667 810.666667 0 0 0 69.12-140.8c37.973333-1.066667 123.946667 4.053333 183.68 59.946666 45.866667 42.666667 67.84 108.8 65.493333 195.84z">
|
||||
<local:MyListItem.Buttons>
|
||||
<x:Array Type="{x:Type local:MyIconButton}">
|
||||
<local:MyIconButton Tag="7" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
|
||||
<local:MyIconButton Tag="7" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
|
||||
</x:Array>
|
||||
</local:MyListItem.Buttons>
|
||||
</local:MyListItem>
|
||||
<local:MyListItem x:Name="ItemLiteLoader" Visibility="Collapsed" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="8" PaddingRight="35" Height="36" VerticalAlignment="Top" Title="LiteLoader"
|
||||
<local:MyListItem x:Name="ItemLiteLoader" Visibility="Collapsed" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="8" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="LiteLoader"
|
||||
LogoScale="1.06" Logo="M517.41 186.99l22.45 83.78h-55.04l-18.76-70.02c-2.85-10.63-13.78-16.95-24.42-14.1-10.63 2.85-16.95 13.78-14.1 24.42l16 59.71H388.5l-12.31-45.95c-10.45-38.99 12.69-79.08 51.69-89.52s79.08 12.68 89.53 51.68z M538.58 98c40.37 0 73.1 32.73 73.1 73.1v79.74h-53.16V171.1c0-11.01-8.93-19.94-19.94-19.94-11.01 0-19.94 8.93-19.94 19.94v79.74h-53.16V171.1c0-40.37 32.73-73.1 73.1-73.1z M512 224.26c191.58 0 346.88 155.3 346.88 346.88v61.8c0 28.85 3.33 59.94 9.98 93.27l1.29 6.28c12.43 59.1-21.15 118.2-78.28 137.78C691.18 904.75 597.9 922 512 922c-83.94 0-174.95-16.47-273.01-49.41l-6.85-2.32c-56.4-19.32-89.85-77.16-78.74-135.5l0.46-2.27c7.51-35.71 11.27-68.89 11.27-99.55v-61.8c0-189.66 152.21-343.77 341.14-346.83l5.73-0.06z m-4.64 66.49c-151.21 2.45-273.32 124.58-275.75 275.8l-0.04 4.59v61.8l-0.04 5.93c-0.45 33.74-4.7 69.49-12.65 107.3-5.53 26.27 9.4 52.53 34.79 61.23 94.08 32.23 180.19 48.15 258.32 48.15s164.25-15.92 258.32-48.15c25.39-8.7 40.32-34.97 34.79-61.23-7.95-37.81-12.2-73.56-12.65-107.3l-0.04-5.93v-61.8c0-153.33-123.05-277.91-275.79-280.39l-4.64-0.04-4.62 0.04z M500.66 717.38l-53.28-87.19c-3.83-6.26-1.85-14.44 4.41-18.27 0.76-0.46 1.56-0.85 2.39-1.15L512 589.74l57.82 21.03c6.9 2.51 10.46 10.13 7.95 17.03-0.3 0.83-0.69 1.63-1.15 2.39l-53.28 87.19c-3.83 6.26-12.01 8.24-18.27 4.41-1.8-1.1-3.31-2.61-4.41-4.41z M684.77 689.42m-46.52 0a46.52 46.52 0 1 0 93.04 0 46.52 46.52 0 1 0-93.04 0Z M339.23 689.42m-46.52 0a46.52 46.52 0 1 0 93.04 0 46.52 46.52 0 1 0-93.04 0Z">
|
||||
<local:MyListItem.Buttons>
|
||||
<x:Array Type="{x:Type local:MyIconButton}">
|
||||
<local:MyIconButton Tag="8" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
|
||||
<local:MyIconButton Tag="8" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
|
||||
</x:Array>
|
||||
</local:MyListItem.Buttons>
|
||||
</local:MyListItem>
|
||||
<TextBlock Text="资源" Margin="13,18,5,4" Opacity="0.6" FontSize="12" />
|
||||
<local:MyListItem x:Name="ItemMod" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="10" PaddingRight="35" Height="36" VerticalAlignment="Top" Title="Mod"
|
||||
<local:MyListItem x:Name="ItemMod" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="10" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="Mod"
|
||||
LogoScale="0.97" 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="10" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
|
||||
<local:MyIconButton Tag="10" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
|
||||
</x:Array>
|
||||
</local:MyListItem.Buttons>
|
||||
</local:MyListItem>
|
||||
<local:MyListItem x:Name="ItemPack" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="11" PaddingRight="35" Height="36" VerticalAlignment="Top" Title="整合包"
|
||||
<local:MyListItem x:Name="ItemPack" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="11" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="整合包"
|
||||
LogoScale="0.98" Logo="M511.488 995.328a128.654222 128.654222 0 0 1-57.116444-13.112889L70.769778 791.808a126.833778 126.833778 0 0 1-70.769778-113.777778V311.608889a126.179556 126.179556 0 0 1 15.36-60.103111V248.604444c1.479111-2.901333 3.356444-5.603556 5.518222-8.021333a127.630222 127.630222 0 0 1 49.891556-42.325333L454.371556 13.368889a128.739556 128.739556 0 0 1 112.981333 0l383.601778 190.407111a126.862222 126.862222 0 0 1 72.049777 113.379556v360.874666a126.805333 126.805333 0 0 1-70.769777 115.939556L568.604444 984.32c-17.92 7.964444-37.461333 11.747556-57.116444 11.008z m42.638222-470.897778v370.204445l360.192-178.545778c14.449778-7.253333 23.552-21.987556 23.438222-38.087111v-335.928889L554.097778 524.430222zM85.248 330.666667v347.335111a42.268444 42.268444 0 0 0 23.438222 38.087111l360.192 178.545778V523.576889L85.248 330.666667zM135.537778 260.835556l375.950222 189.952 137.671111-65.564445L286.435556 188.074667 135.537778 260.864z m245.105778-118.471112l363.576888 197.973334 150.897778-71.480889-365.283555-180.224a42.922667 42.922667 0 0 0-37.518223 0l-111.672888 53.731555z">
|
||||
<local:MyListItem.Buttons>
|
||||
<x:Array Type="{x:Type local:MyIconButton}">
|
||||
<local:MyIconButton Tag="11" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
|
||||
<local:MyIconButton Tag="11" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
|
||||
</x:Array>
|
||||
</local:MyListItem.Buttons>
|
||||
</local:MyListItem>
|
||||
|
||||
@@ -99,11 +99,13 @@
|
||||
Case FormMain.PageSubType.DownloadMod
|
||||
PageDownloadMod.Storage = New CompProjectStorage
|
||||
PageDownloadMod.Page = 0
|
||||
CompProjectCache.Clear()
|
||||
If FrmDownloadMod IsNot Nothing Then FrmDownloadMod.PageLoaderRestart()
|
||||
ItemMod.Checked = True
|
||||
Case FormMain.PageSubType.DownloadPack
|
||||
PageDownloadPack.Storage = New CompProjectStorage
|
||||
PageDownloadPack.Page = 0
|
||||
CompProjectCache.Clear()
|
||||
If FrmDownloadPack IsNot Nothing Then FrmDownloadPack.PageLoaderRestart()
|
||||
ItemPack.Checked = True
|
||||
Case FormMain.PageSubType.DownloadClient
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<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"
|
||||
mc:Ignorable="d" x:Class="PageDownloadLiteLoader"
|
||||
d:DesignWidth="778.571" d:DesignHeight="90.857">
|
||||
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"
|
||||
mc:Ignorable="d" x:Class="PageDownloadLiteLoader"
|
||||
PanScroll="{Binding ElementName=PanBack}">
|
||||
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
|
||||
<StackPanel Orientation="Vertical" Margin="5">
|
||||
<local:MyCard Margin="20,20,20,0" Grid.IsSharedSizeScope="True" Title="LiteLoader 简介" x:Name="CardTip">
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
PanMain.Children.Clear()
|
||||
'转化为 UI
|
||||
For Each Pair As KeyValuePair(Of String, List(Of DlLiteLoaderListEntry)) In Dict
|
||||
If Pair.Value.Count = 0 Then Continue For
|
||||
If Not Pair.Value.Any() Then Continue For
|
||||
'增加卡片
|
||||
Dim NewCard As New MyCard With {.Title = Pair.Key & " (" & Pair.Value.Count & ")", .Margin = New Thickness(0, 0, 0, 15), .SwapType = 10}
|
||||
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}
|
||||
|
||||
@@ -1,31 +1,63 @@
|
||||
<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"
|
||||
mc:Ignorable="d" x:Class="PageDownloadMod"
|
||||
d:DesignWidth="778.571" d:DesignHeight="90.857">
|
||||
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"
|
||||
mc:Ignorable="d" x:Class="PageDownloadMod"
|
||||
PanScroll="{Binding ElementName=PanBack}">
|
||||
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
|
||||
<StackPanel Orientation="Vertical" Margin="25">
|
||||
<local:MyCard Title="搜索 Mod" Margin="0,0,0,15" x:Name="PanAlways">
|
||||
<StackPanel Margin="25,40,25,15">
|
||||
<Grid Margin="0,0,0,11" HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="70" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="1.3*" />
|
||||
<ColumnDefinition Width="1.2*" />
|
||||
<ColumnDefinition Width="50" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="28" />
|
||||
<RowDefinition Height="9" />
|
||||
<RowDefinition Height="28" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" Text="名称" Margin="0,0,25,0" />
|
||||
<local:MyTextBox x:Name="TextSearchName" Grid.Column="1" Grid.ColumnSpan="10" />
|
||||
<TextBlock VerticalAlignment="Center" Grid.Row="2" HorizontalAlignment="Left" Text="类型" Margin="0,0,25,0" />
|
||||
<local:MyComboBox x:Name="ComboSearchTag" Grid.Column="1" Grid.Row="2" MaxDropDownHeight="320">
|
||||
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" Text="名称" Margin="0,0,18,0" />
|
||||
<local:MyTextBox x:Name="TextSearchName" Grid.Column="1" Grid.ColumnSpan="2" />
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="4" HorizontalAlignment="Left" Text="来源" Margin="0,0,18,0" />
|
||||
<local:MyComboBox x:Name="ComboSearchSource" Grid.Column="5">
|
||||
<local:MyComboBoxItem Content="全部" Tag="3" IsSelected="True" />
|
||||
<local:MyComboBoxItem Content="CurseForge" Tag="1" />
|
||||
<local:MyComboBoxItem Content="Modrinth" Tag="2" />
|
||||
</local:MyComboBox>
|
||||
<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" MaxDropDownHeight="320">
|
||||
<local:MyComboBoxItem Content="全部 (也可自行输入)" IsSelected="True" />
|
||||
<local:MyComboBoxItem Content="1.20.6" />
|
||||
<local:MyComboBoxItem Content="1.20.4" />
|
||||
<local:MyComboBoxItem Content="1.20.1" />
|
||||
<local:MyComboBoxItem Content="1.19.4" />
|
||||
<local:MyComboBoxItem Content="1.19.2" />
|
||||
<local:MyComboBoxItem Content="1.18.2" />
|
||||
<local:MyComboBoxItem Content="1.17.1" />
|
||||
<local:MyComboBoxItem Content="1.16.5" />
|
||||
<local:MyComboBoxItem Content="1.15.2" />
|
||||
<local:MyComboBoxItem Content="1.14.4" />
|
||||
<local:MyComboBoxItem Content="1.12.2" />
|
||||
<local:MyComboBoxItem Content="1.11.2" />
|
||||
<local:MyComboBoxItem Content="1.10.2" />
|
||||
<local:MyComboBoxItem Content="1.8.9" />
|
||||
<local:MyComboBoxItem Content="1.7.10" />
|
||||
</local:MyComboBox>
|
||||
<local:MyComboBox x:Name="ComboSearchLoader" Visibility="Collapsed" Grid.Row="2" Grid.Column="2" MaxDropDownHeight="320" Margin="10,0,0,0">
|
||||
<local:MyComboBoxItem Content="任意 Mod 加载器" IsSelected="True" Tag="0" />
|
||||
<local:MyComboBoxItem Content="Forge" Tag="1" />
|
||||
<local:MyComboBoxItem Content="Fabric" Tag="4" />
|
||||
<local:MyComboBoxItem Content="Quilt" Tag="5" />
|
||||
<local:MyComboBoxItem Content="NeoForge" Tag="6" />
|
||||
</local:MyComboBox>
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="4" Grid.Row="2" HorizontalAlignment="Left" Text="类型" Margin="0,0,18,0" />
|
||||
<local:MyComboBox x:Name="ComboSearchTag" Grid.Column="5" Grid.Row="2" MaxDropDownHeight="320">
|
||||
<local:MyComboBoxItem Content="全部" Tag="" IsSelected="True" />
|
||||
<local:MyComboBoxItem Content="世界元素" Tag="406/worldgen" />
|
||||
<local:MyComboBoxItem Content="生物群系" Tag="407/" Margin="15,0,0,0" />
|
||||
@@ -53,33 +85,6 @@
|
||||
<local:MyComboBoxItem Content="改良" Tag="5191/utility" />
|
||||
<local:MyComboBoxItem Content="支持库" Tag="421/library" />
|
||||
</local:MyComboBox>
|
||||
<TextBlock VerticalAlignment="Center" Grid.Row="2" Grid.Column="3" HorizontalAlignment="Left" Text="版本" Margin="0,0,25,0" />
|
||||
<local:MyComboBox x:Name="TextSearchVersion" Grid.Row="2" Grid.Column="4" Grid.ColumnSpan="2" IsEditable="True" MaxDropDownHeight="320">
|
||||
<local:MyComboBoxItem Content="全部 (也可自行输入)" IsSelected="True" />
|
||||
<local:MyComboBoxItem Content="1.19.4" />
|
||||
<local:MyComboBoxItem Content="1.19.3" />
|
||||
<local:MyComboBoxItem Content="1.19.2" />
|
||||
<local:MyComboBoxItem Content="1.19.1" />
|
||||
<local:MyComboBoxItem Content="1.19" />
|
||||
<local:MyComboBoxItem Content="1.18.2" />
|
||||
<local:MyComboBoxItem Content="1.17.1" />
|
||||
<local:MyComboBoxItem Content="1.16.5" />
|
||||
<local:MyComboBoxItem Content="1.15.2" />
|
||||
<local:MyComboBoxItem Content="1.14.4" />
|
||||
<local:MyComboBoxItem Content="1.13.2" />
|
||||
<local:MyComboBoxItem Content="1.12.2" />
|
||||
<local:MyComboBoxItem Content="1.11.2" />
|
||||
<local:MyComboBoxItem Content="1.10.2" />
|
||||
<local:MyComboBoxItem Content="1.9.4" />
|
||||
<local:MyComboBoxItem Content="1.8.9" />
|
||||
<local:MyComboBoxItem Content="1.7.10" />
|
||||
</local:MyComboBox>
|
||||
<local:MyComboBox x:Name="ComboSearchLoader" Visibility="Collapsed" Grid.Row="2" Grid.Column="5" MaxDropDownHeight="320" Margin="10,0,0,0">
|
||||
<local:MyComboBoxItem Content="任意 Mod 加载器" IsSelected="True" Tag="0" />
|
||||
<local:MyComboBoxItem Content="Forge" Tag="1" />
|
||||
<local:MyComboBoxItem Content="Fabric" Tag="4" />
|
||||
<local:MyComboBoxItem Content="Quilt" Tag="5" />
|
||||
</local:MyComboBox>
|
||||
</Grid>
|
||||
<Grid Margin="0,4,0,6" Height="35">
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -92,6 +97,7 @@
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
<StackPanel x:Name="PanContent">
|
||||
<local:MyHint x:Name="HintError" Text="无法连接到 Modrinth,所以目前仅显示了来自 CurseForge 的内容,结果可能不全。
请尝试使用 VPN 或加速器以改善网络。" IsWarn="True" Margin="0,0,0,15" />
|
||||
<local:MyCard x:Name="CardProjects" UseAnimation="False">
|
||||
<StackPanel x:Name="PanProjects" Margin="12" />
|
||||
</local:MyCard>
|
||||
|
||||
@@ -8,17 +8,18 @@
|
||||
Public Shared Page As Integer = 0
|
||||
Private Sub PageDownloadMod_Inited(sender As Object, e As EventArgs) Handles Me.Initialized
|
||||
PageLoaderInit(Load, PanLoad, PanContent, PanAlways, Loader, AddressOf Load_OnFinish, AddressOf LoaderInput)
|
||||
McVersionHighest = Math.Max(McVersionHighest, Integer.Parse(CType(TextSearchVersion.Items(1), MyComboBoxItem).Content.ToString.Split(".")(1)))
|
||||
If McVersionHighest = -1 Then McVersionHighest = Math.Max(McVersionHighest, Integer.Parse(CType(TextSearchVersion.Items(1), MyComboBoxItem).Content.ToString.Split(".")(1)))
|
||||
End Sub
|
||||
Private Shared Function LoaderInput() As CompProjectRequest
|
||||
Dim Request As New CompProjectRequest(CompType.Mod, Storage, (Page + 1) * PageSize)
|
||||
If FrmDownloadMod IsNot Nothing AndAlso FrmDownloadMod.IsLoaded Then
|
||||
If FrmDownloadMod IsNot Nothing Then
|
||||
With Request
|
||||
.SearchText = FrmDownloadMod.TextSearchName.Text
|
||||
.GameVersion = If(FrmDownloadMod.TextSearchVersion.Text = "全部 (也可自行输入)", Nothing,
|
||||
If(FrmDownloadMod.TextSearchVersion.Text.Contains(".") OrElse FrmDownloadMod.TextSearchVersion.Text.Contains("w"), FrmDownloadMod.TextSearchVersion.Text, Nothing))
|
||||
.Tag = FrmDownloadMod.ComboSearchTag.SelectedItem.Tag
|
||||
.ModLoader = Val(FrmDownloadMod.ComboSearchLoader.SelectedItem.Tag)
|
||||
.Source = CType(Val(FrmDownloadMod.ComboSearchSource.SelectedItem.Tag), CompSourceType)
|
||||
End With
|
||||
End If
|
||||
Return Request
|
||||
@@ -31,9 +32,7 @@
|
||||
'列表项
|
||||
PanProjects.Children.Clear()
|
||||
For i = Math.Min(Page * PageSize, Storage.Results.Count - 1) To Math.Min((Page + 1) * PageSize - 1, Storage.Results.Count - 1)
|
||||
PanProjects.Children.Add(
|
||||
Storage.Results(i).ToCompItem(Loader.Input.GameVersion Is Nothing,
|
||||
Loader.Input.ModLoader = CompModLoaderType.Any, AddressOf ProjectClick))
|
||||
PanProjects.Children.Add(Storage.Results(i).ToCompItem(Loader.Input.GameVersion Is Nothing, Loader.Input.ModLoader = CompModLoaderType.Any))
|
||||
Next
|
||||
'页码
|
||||
CardPages.Visibility = If(Storage.Results.Count > 40 OrElse
|
||||
@@ -47,6 +46,13 @@
|
||||
BtnPageRight.IsEnabled = Storage.Results.Count > PageSize * (Page + 1) OrElse
|
||||
Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal
|
||||
BtnPageRight.Opacity = If(BtnPageRight.IsEnabled, 1, 0.2)
|
||||
'错误信息
|
||||
If Storage.ErrorMessage Is Nothing Then
|
||||
HintError.Visibility = Visibility.Collapsed
|
||||
Else
|
||||
HintError.Visibility = Visibility.Visible
|
||||
HintError.Text = Storage.ErrorMessage
|
||||
End If
|
||||
'强制返回顶部
|
||||
PanBack.ScrollToTop()
|
||||
Catch ex As Exception
|
||||
@@ -67,12 +73,6 @@
|
||||
End Select
|
||||
End Sub
|
||||
|
||||
'进入详情页面
|
||||
|
||||
Public Sub ProjectClick(sender As MyCompItem, e As EventArgs)
|
||||
FrmMain.PageChange(New FormMain.PageStackData With {.Page = FormMain.PageType.CompDetail, .Additional = sender.Tag})
|
||||
End Sub
|
||||
|
||||
'切换页码
|
||||
|
||||
Private Sub BtnPageFirst_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageFirst.Click
|
||||
@@ -113,13 +113,18 @@
|
||||
TextSearchName.Text = ""
|
||||
TextSearchVersion.Text = "全部 (也可自行输入)"
|
||||
TextSearchVersion.SelectedIndex = 0
|
||||
ComboSearchSource.SelectedIndex = 0
|
||||
ComboSearchTag.SelectedIndex = 0
|
||||
ComboSearchLoader.SelectedIndex = 0
|
||||
Loader.LastFinishedTime = 0 '要求强制重新开始
|
||||
End Sub
|
||||
|
||||
'版本选择
|
||||
Private Sub TextSearchVersion_TextChanged(sender As Object, e As TextChangedEventArgs) Handles TextSearchVersion.TextChanged
|
||||
'#3067:当下拉菜单展开时,程序会被 WPF 挂起,因而无法更新 Grid 布局,所以必须延迟到下拉菜单收起后才能更新
|
||||
Private Sub TextSearchVersion_TextChanged() Handles TextSearchVersion.TextChanged
|
||||
If Not TextSearchVersion.IsDropDownOpen Then UpdateSearchLoaderVisibility()
|
||||
End Sub
|
||||
Private Sub UpdateSearchLoaderVisibility() Handles TextSearchVersion.DropDownClosed
|
||||
If TextSearchVersion.Text.Contains(".") OrElse TextSearchVersion.Text.Contains("w") Then
|
||||
ComboSearchLoader.Visibility = Visibility.Visible
|
||||
Grid.SetColumnSpan(TextSearchVersion, 1)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<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"
|
||||
mc:Ignorable="d" x:Class="PageDownloadOptiFine"
|
||||
d:DesignWidth="778.571" d:DesignHeight="90.857">
|
||||
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"
|
||||
mc:Ignorable="d" x:Class="PageDownloadOptiFine"
|
||||
PanScroll="{Binding ElementName=PanBack}">
|
||||
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
|
||||
<StackPanel Orientation="Vertical" Margin="5">
|
||||
<local:MyCard Margin="20,20,20,0" Grid.IsSharedSizeScope="True" Title="OptiFine 简介" x:Name="CardTip">
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
PanMain.Children.Clear()
|
||||
'转化为 UI
|
||||
For Each Pair As KeyValuePair(Of String, List(Of DlOptiFineListEntry)) In Dict
|
||||
If Pair.Value.Count = 0 Then Continue For
|
||||
If Not Pair.Value.Any() Then Continue For
|
||||
'增加卡片
|
||||
Dim NewCard As New MyCard With {.Title = Pair.Key & " (" & Pair.Value.Count & ")", .Margin = New Thickness(0, 0, 0, 15), .SwapType = 3}
|
||||
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}
|
||||
|
||||
@@ -1,30 +1,55 @@
|
||||
<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"
|
||||
mc:Ignorable="d" x:Class="PageDownloadPack"
|
||||
d:DesignWidth="778.571" d:DesignHeight="90.857">
|
||||
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"
|
||||
mc:Ignorable="d" x:Class="PageDownloadPack"
|
||||
PanScroll="{Binding ElementName=PanBack}">
|
||||
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
|
||||
<StackPanel Orientation="Vertical" Margin="25">
|
||||
<local:MyCard Title="搜索整合包" Margin="0,0,0,15" x:Name="PanAlways">
|
||||
<StackPanel Margin="25,40,25,15">
|
||||
<Grid Margin="0,0,0,11" HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="70" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="2.5*" />
|
||||
<ColumnDefinition Width="50" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="28" />
|
||||
<RowDefinition Height="9" />
|
||||
<RowDefinition Height="28" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" Text="名称" Margin="0,0,25,0" />
|
||||
<local:MyTextBox x:Name="TextSearchName" Grid.Column="1" Grid.ColumnSpan="10" HintText="仅支持使用英文搜索" />
|
||||
<TextBlock VerticalAlignment="Center" Grid.Row="2" HorizontalAlignment="Left" Text="类型" Margin="0,0,25,0" />
|
||||
<local:MyComboBox x:Name="ComboSearchTag" Grid.Column="1" Grid.Row="2" MaxDropDownHeight="320">
|
||||
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" Text="名称" Margin="0,0,18,0" />
|
||||
<local:MyTextBox x:Name="TextSearchName" Grid.Column="1" />
|
||||
<TextBlock VerticalAlignment="Center" Grid.Column="3" HorizontalAlignment="Left" Text="来源" Margin="0,0,18,0" />
|
||||
<local:MyComboBox x:Name="ComboSearchSource" Grid.Column="4">
|
||||
<local:MyComboBoxItem Content="全部" Tag="3" IsSelected="True" />
|
||||
<local:MyComboBoxItem Content="CurseForge" Tag="1" />
|
||||
<local:MyComboBoxItem Content="Modrinth" Tag="2" />
|
||||
</local:MyComboBox>
|
||||
<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" IsEditable="True" MaxDropDownHeight="320">
|
||||
<local:MyComboBoxItem Content="全部 (也可自行输入)" IsSelected="True" />
|
||||
<local:MyComboBoxItem Content="1.20.6" />
|
||||
<local:MyComboBoxItem Content="1.20.4" />
|
||||
<local:MyComboBoxItem Content="1.20.1" />
|
||||
<local:MyComboBoxItem Content="1.19.4" />
|
||||
<local:MyComboBoxItem Content="1.19.2" />
|
||||
<local:MyComboBoxItem Content="1.18.2" />
|
||||
<local:MyComboBoxItem Content="1.17.1" />
|
||||
<local:MyComboBoxItem Content="1.16.5" />
|
||||
<local:MyComboBoxItem Content="1.15.2" />
|
||||
<local:MyComboBoxItem Content="1.14.4" />
|
||||
<local:MyComboBoxItem Content="1.12.2" />
|
||||
<local:MyComboBoxItem Content="1.11.2" />
|
||||
<local:MyComboBoxItem Content="1.10.2" />
|
||||
<local:MyComboBoxItem Content="1.8.9" />
|
||||
<local:MyComboBoxItem Content="1.7.10" />
|
||||
</local:MyComboBox>
|
||||
<TextBlock VerticalAlignment="Center" Grid.Row="2" Grid.Column="3" HorizontalAlignment="Left" Text="类型" Margin="0,0,18,0" />
|
||||
<local:MyComboBox x:Name="ComboSearchTag" Grid.Column="4" Grid.Row="2" MaxDropDownHeight="320">
|
||||
<local:MyComboBoxItem Tag="" Content="全部" IsSelected="True" />
|
||||
<local:MyComboBoxItem Tag="4484/" Content="多人" />
|
||||
<local:MyComboBoxItem Tag="/optimization" Content="性能优化" />
|
||||
@@ -40,32 +65,12 @@
|
||||
<local:MyComboBoxItem Tag="4471/" Content="科幻" />
|
||||
<local:MyComboBoxItem Tag="4736/" Content="空岛" />
|
||||
<local:MyComboBoxItem Tag="5128/" Content="原版改良" />
|
||||
<local:MyComboBoxItem Tag="4487/" Content="FTB" ToolTip="该整合包由 FTB 官方发布" />
|
||||
<local:MyComboBoxItem Tag="4487/" Content="FTB"
|
||||
ToolTip="由 FTB 官方发布" ToolTipService.HorizontalOffset="70" />
|
||||
<local:MyComboBoxItem Tag="4480/" Content="基于地图" />
|
||||
<local:MyComboBoxItem Tag="4481/lightweight" Content="轻量整合" />
|
||||
<local:MyComboBoxItem Tag="4482/" Content="大型整合" />
|
||||
</local:MyComboBox>
|
||||
<TextBlock VerticalAlignment="Center" Grid.Row="2" Grid.Column="3" HorizontalAlignment="Left" Text="版本" Margin="0,0,25,0" />
|
||||
<local:MyComboBox x:Name="TextSearchVersion" Grid.Row="2" Grid.Column="4" IsEditable="True" MaxDropDownHeight="320">
|
||||
<local:MyComboBoxItem Content="全部 (也可自行输入)" IsSelected="True" />
|
||||
<local:MyComboBoxItem Content="1.19.4" />
|
||||
<local:MyComboBoxItem Content="1.19.3" />
|
||||
<local:MyComboBoxItem Content="1.19.2" />
|
||||
<local:MyComboBoxItem Content="1.19.1" />
|
||||
<local:MyComboBoxItem Content="1.19" />
|
||||
<local:MyComboBoxItem Content="1.18.2" />
|
||||
<local:MyComboBoxItem Content="1.17.1" />
|
||||
<local:MyComboBoxItem Content="1.16.5" />
|
||||
<local:MyComboBoxItem Content="1.15.2" />
|
||||
<local:MyComboBoxItem Content="1.14.4" />
|
||||
<local:MyComboBoxItem Content="1.13.2" />
|
||||
<local:MyComboBoxItem Content="1.12.2" />
|
||||
<local:MyComboBoxItem Content="1.11.2" />
|
||||
<local:MyComboBoxItem Content="1.10.2" />
|
||||
<local:MyComboBoxItem Content="1.9.4" />
|
||||
<local:MyComboBoxItem Content="1.8.9" />
|
||||
<local:MyComboBoxItem Content="1.7.10" />
|
||||
</local:MyComboBox>
|
||||
</Grid>
|
||||
<Grid Margin="0,4,0,6" Height="35">
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -76,11 +81,12 @@
|
||||
<local:MyButton x:Name="BtnSearchRun" Text="搜索" MinWidth="140" Padding="13,0" Margin="0,0,20,0" ColorType="Highlight" />
|
||||
<local:MyButton x:Name="BtnSearchReset" Text="重置条件" MinWidth="140" Padding="13,0" Margin="0,0,20,0" Grid.Column="1" />
|
||||
<local:MyButton x:Name="BtnSearchInstall" Text="安装已有整合包" MinWidth="140" Padding="13,0" Margin="0,0,20,0" Grid.Column="2"
|
||||
ToolTip="在当前选择的 Minecraft 文件夹下安装整合包" />
|
||||
ToolTip="在当前选择的 Minecraft 文件夹下安装整合包" ToolTipService.HorizontalOffset="-55" ToolTipService.VerticalOffset="5" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
<StackPanel x:Name="PanContent">
|
||||
<local:MyHint x:Name="HintError" Text="无法连接到 Modrinth,所以目前仅显示了来自 CurseForge 的内容,结果可能不全。
请尝试使用 VPN 或加速器以改善网络。" IsWarn="True" Margin="0,0,0,15" />
|
||||
<local:MyCard x:Name="CardProjects" UseAnimation="False">
|
||||
<StackPanel x:Name="PanProjects" Margin="12" />
|
||||
</local:MyCard>
|
||||
|
||||
@@ -6,18 +6,19 @@
|
||||
Public Shared Loader As New LoaderTask(Of CompProjectRequest, Integer)("CompProject ModPack", AddressOf CompProjectsGet, AddressOf LoaderInput) With {.ReloadTimeout = 60 * 1000}
|
||||
Public Shared Storage As New CompProjectStorage
|
||||
Public Shared Page As Integer = 0
|
||||
Private Sub PageDownloadMod_Inited(sender As Object, e As EventArgs) Handles Me.Initialized
|
||||
Private Sub PageDownloadPack_Inited(sender As Object, e As EventArgs) Handles Me.Initialized
|
||||
PageLoaderInit(Load, PanLoad, PanContent, PanAlways, Loader, AddressOf Load_OnFinish, AddressOf LoaderInput)
|
||||
McVersionHighest = Math.Max(McVersionHighest, Integer.Parse(CType(TextSearchVersion.Items(1), MyComboBoxItem).Content.ToString.Split(".")(1)))
|
||||
If McVersionHighest = -1 Then McVersionHighest = Math.Max(McVersionHighest, Integer.Parse(CType(TextSearchVersion.Items(1), MyComboBoxItem).Content.ToString.Split(".")(1)))
|
||||
End Sub
|
||||
Private Shared Function LoaderInput() As CompProjectRequest
|
||||
Dim Request As New CompProjectRequest(CompType.ModPack, Storage, (Page + 1) * PageSize)
|
||||
If FrmDownloadPack IsNot Nothing AndAlso FrmDownloadPack.IsLoaded Then
|
||||
If FrmDownloadPack IsNot Nothing Then
|
||||
With Request
|
||||
.SearchText = FrmDownloadPack.TextSearchName.Text
|
||||
.GameVersion = If(FrmDownloadPack.TextSearchVersion.Text = "全部 (也可自行输入)", Nothing,
|
||||
If(FrmDownloadPack.TextSearchVersion.Text.Contains(".") OrElse FrmDownloadPack.TextSearchVersion.Text.Contains("w"), FrmDownloadPack.TextSearchVersion.Text, Nothing))
|
||||
.Tag = FrmDownloadPack.ComboSearchTag.SelectedItem.Tag
|
||||
.Source = CType(Val(FrmDownloadPack.ComboSearchSource.SelectedItem.Tag), CompSourceType)
|
||||
End With
|
||||
End If
|
||||
Return Request
|
||||
@@ -30,8 +31,7 @@
|
||||
'列表项
|
||||
PanProjects.Children.Clear()
|
||||
For i = Math.Min(Page * PageSize, Storage.Results.Count - 1) To Math.Min((Page + 1) * PageSize - 1, Storage.Results.Count - 1)
|
||||
PanProjects.Children.Add(
|
||||
Storage.Results(i).ToCompItem(Loader.Input.GameVersion Is Nothing, True, AddressOf ProjectClick))
|
||||
PanProjects.Children.Add(Storage.Results(i).ToCompItem(Loader.Input.GameVersion Is Nothing, True))
|
||||
Next
|
||||
'页码
|
||||
CardPages.Visibility = If(Storage.Results.Count > 40 OrElse
|
||||
@@ -45,6 +45,13 @@
|
||||
BtnPageRight.IsEnabled = Storage.Results.Count > PageSize * (Page + 1) OrElse
|
||||
Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal
|
||||
BtnPageRight.Opacity = If(BtnPageRight.IsEnabled, 1, 0.2)
|
||||
'错误信息
|
||||
If Storage.ErrorMessage Is Nothing Then
|
||||
HintError.Visibility = Visibility.Collapsed
|
||||
Else
|
||||
HintError.Visibility = Visibility.Visible
|
||||
HintError.Text = Storage.ErrorMessage
|
||||
End If
|
||||
'强制返回顶部
|
||||
PanBack.ScrollToTop()
|
||||
Catch ex As Exception
|
||||
@@ -65,12 +72,6 @@
|
||||
End Select
|
||||
End Sub
|
||||
|
||||
'进入详情页面
|
||||
|
||||
Public Sub ProjectClick(sender As MyCompItem, e As EventArgs)
|
||||
FrmMain.PageChange(New FormMain.PageStackData With {.Page = FormMain.PageType.CompDetail, .Additional = sender.Tag})
|
||||
End Sub
|
||||
|
||||
'切换页码
|
||||
|
||||
Private Sub BtnPageFirst_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageFirst.Click
|
||||
@@ -115,6 +116,7 @@
|
||||
TextSearchName.Text = ""
|
||||
TextSearchVersion.Text = "全部 (也可自行输入)"
|
||||
TextSearchVersion.SelectedIndex = 0
|
||||
ComboSearchSource.SelectedIndex = 0
|
||||
ComboSearchTag.SelectedIndex = 0
|
||||
Loader.LastFinishedTime = 0 '要求强制重新开始
|
||||
End Sub
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:PCL"
|
||||
Height="64" Width="64" UseLayoutRounding="True" RenderTransformOrigin="0.5,0.5" Background="{StaticResource ColorBrushSemiTransparent}"
|
||||
ToolTipService.ShowDuration="23333333" ToolTipService.Placement="Center" ToolTipService.VerticalOffset="-50" ToolTipService.HorizontalOffset="2" ToolTipService.InitialShowDelay="100">
|
||||
ToolTipService.Placement="Center" ToolTipService.VerticalOffset="-50" ToolTipService.HorizontalOffset="2" ToolTipService.InitialShowDelay="100">
|
||||
<Grid.Effect>
|
||||
<DropShadowEffect x:Name="ShadowSkin" BlurRadius="10" ShadowDepth="0" Color="{DynamicResource ColorObject2}" Opacity="0.2" />
|
||||
</Grid.Effect>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
'皮肤储存
|
||||
Private _Address As String
|
||||
Public Property Address() As String
|
||||
Public Property Address As String
|
||||
Get
|
||||
Return _Address
|
||||
End Get
|
||||
@@ -41,7 +41,7 @@
|
||||
End Sub
|
||||
|
||||
'保存皮肤
|
||||
Private Sub BtnSkinSave_Click(sender As Object, e As RoutedEventArgs) Handles BtnSkinSave.Click
|
||||
Public Sub BtnSkinSave_Click() Handles BtnSkinSave.Click
|
||||
Save(Loader)
|
||||
End Sub
|
||||
Public Shared Sub Save(Loader As LoaderTask(Of EqualableList(Of String), String))
|
||||
@@ -76,12 +76,19 @@
|
||||
''' </summary>
|
||||
Public Sub Load()
|
||||
Try
|
||||
Address = Loader.Output
|
||||
'检查文件存在
|
||||
If Address = "" Then Throw New Exception("皮肤加载器 " & Loader.Name & " 没有输出")
|
||||
Address = Loader.Output
|
||||
If String.IsNullOrEmpty(Address) Then Throw New Exception("皮肤加载器 " & Loader.Name & " 没有输出")
|
||||
If Not Address.StartsWith(PathImage) AndAlso Not File.Exists(Address) Then Throw New FileNotFoundException("皮肤文件未找到", Address)
|
||||
'加载
|
||||
Dim Image As New MyBitmap(Address)
|
||||
Dim Image As MyBitmap
|
||||
Try
|
||||
Image = New MyBitmap(Address)
|
||||
Catch ex As Exception '#2272
|
||||
Log(ex, $"皮肤文件已损坏:{Address}", LogLevel.Hint)
|
||||
File.Delete(Address)
|
||||
Exit Sub
|
||||
End Try
|
||||
ImgBack.Tag = Address
|
||||
'大小检查
|
||||
Dim Scale As Integer = Image.Pic.Width / 64
|
||||
@@ -97,7 +104,7 @@
|
||||
(Image.Pic.GetPixel(1, 1) <> Image.Pic.GetPixel(Scale * 41, Scale * 9) AndAlso '或是头部颜色和透明区均不一样
|
||||
Image.Pic.GetPixel(Image.Pic.Width - 1, Image.Pic.Height - 1) <> Image.Pic.GetPixel(Scale * 41, Scale * 9) AndAlso
|
||||
Image.Pic.GetPixel(Image.Pic.Width - 2, Image.Pic.Height / 2 - 2) <> Image.Pic.GetPixel(Scale * 41, Scale * 9)) Then
|
||||
ImgFore.Source = New CroppedBitmap(Image, New Int32Rect(Scale * 40, Scale * 8, Scale * 8, Scale * 8))
|
||||
ImgFore.Source = Image.Clip(Scale * 40, Scale * 8, Scale * 8, Scale * 8)
|
||||
Else
|
||||
ImgFore.Source = Nothing
|
||||
End If
|
||||
@@ -105,7 +112,7 @@
|
||||
ImgFore.Source = Nothing
|
||||
End If
|
||||
'脸层
|
||||
ImgBack.Source = New CroppedBitmap(Image, New Int32Rect(Scale * 8, Scale * 8, Scale * 8, Scale * 8))
|
||||
ImgBack.Source = Image.Clip(Scale * 8, Scale * 8, Scale * 8, Scale * 8)
|
||||
Log("[Skin] 载入头像成功:" & Loader.Name)
|
||||
Catch ex As Exception
|
||||
Log(ex, "载入头像失败(" & If(Address, "null") & "," & Loader.Name & ")", LogLevel.Hint)
|
||||
@@ -121,7 +128,7 @@
|
||||
End Sub
|
||||
|
||||
'刷新缓存
|
||||
Private Sub RefreshClick(sender As MyMenuItem, e As EventArgs) Handles BtnSkinRefresh.Click
|
||||
Public Sub RefreshClick() Handles BtnSkinRefresh.Click
|
||||
RefreshCache(Loader)
|
||||
End Sub
|
||||
''' <summary>
|
||||
@@ -138,50 +145,50 @@
|
||||
'由于 Abort 不是实时的,暂时不会释放文件,会导致删除报错,故只能取消执行
|
||||
Hint("有正在获取中的皮肤,请稍后再试!", HintType.Info)
|
||||
Else
|
||||
RunInThread(Sub()
|
||||
Try
|
||||
Hint("正在刷新皮肤……")
|
||||
'清空缓存
|
||||
Log("[Skin] 正在清空皮肤缓存")
|
||||
If Directory.Exists(PathTemp & "Cache\Skin") Then DeleteDirectory(PathTemp & "Cache\Skin")
|
||||
If Directory.Exists(PathTemp & "Cache\Uuid") Then DeleteDirectory(PathTemp & "Cache\Uuid")
|
||||
IniClearCache(PathTemp & "Cache\Skin\IndexMojang.ini")
|
||||
IniClearCache(PathTemp & "Cache\Skin\IndexMs.ini")
|
||||
IniClearCache(PathTemp & "Cache\Skin\IndexNide.ini")
|
||||
IniClearCache(PathTemp & "Cache\Skin\IndexAuth.ini")
|
||||
IniClearCache(PathTemp & "Cache\Uuid\Mojang.ini")
|
||||
'刷新控件
|
||||
For Each SkinLoader In If(sender IsNot Nothing, {sender}, {PageLaunchLeft.SkinMojang, PageLaunchLeft.SkinLegacy, PageLaunchLeft.SkinMs})
|
||||
SkinLoader.WaitForExit(IsForceRestart:=True)
|
||||
Next
|
||||
Hint("已刷新皮肤!", HintType.Finish)
|
||||
Catch ex As Exception
|
||||
Log(ex, "刷新皮肤缓存失败", LogLevel.Msgbox)
|
||||
End Try
|
||||
End Sub)
|
||||
RunInThread(
|
||||
Sub()
|
||||
Try
|
||||
Hint("正在刷新头像……")
|
||||
'清空缓存
|
||||
Log("[Skin] 正在清空皮肤缓存")
|
||||
If Directory.Exists(PathTemp & "Cache\Skin") Then DeleteDirectory(PathTemp & "Cache\Skin")
|
||||
If Directory.Exists(PathTemp & "Cache\Uuid") Then DeleteDirectory(PathTemp & "Cache\Uuid")
|
||||
IniClearCache(PathTemp & "Cache\Skin\IndexMs.ini")
|
||||
IniClearCache(PathTemp & "Cache\Skin\IndexNide.ini")
|
||||
IniClearCache(PathTemp & "Cache\Skin\IndexAuth.ini")
|
||||
IniClearCache(PathTemp & "Cache\Uuid\Mojang.ini")
|
||||
'刷新控件
|
||||
For Each SkinLoader In If(sender IsNot Nothing, {sender}, {PageLaunchLeft.SkinLegacy, PageLaunchLeft.SkinMs})
|
||||
SkinLoader.WaitForExit(IsForceRestart:=True)
|
||||
Next
|
||||
Hint("已刷新头像!", HintType.Finish)
|
||||
Catch ex As Exception
|
||||
Log(ex, "刷新皮肤缓存失败", LogLevel.Msgbox)
|
||||
End Try
|
||||
End Sub)
|
||||
End If
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 在更换正版皮肤后,刷新正版皮肤。
|
||||
''' </summary>
|
||||
''' <param name="SkinAddress">新的正版皮肤完整地址。</param>
|
||||
Public Shared Sub ReloadCache(SkinAddress As String, IsMsLogin As Boolean)
|
||||
RunInThread(Sub()
|
||||
Try
|
||||
'更新缓存
|
||||
Dim LoginType As String = If(IsMsLogin, "Ms", "Mojang")
|
||||
WriteIni(PathTemp & "Cache\Skin\Index" & LoginType & ".ini", Setup.Get("Cache" & LoginType & "Uuid"), SkinAddress)
|
||||
Log(String.Format("[Skin] 已写入皮肤地址缓存 {0} -> {1}", Setup.Get("Cache" & LoginType & "Uuid"), SkinAddress))
|
||||
'刷新控件
|
||||
For Each SkinLoader In If(IsMsLogin, {PageLaunchLeft.SkinMs, PageLaunchLeft.SkinLegacy}, {PageLaunchLeft.SkinMojang, PageLaunchLeft.SkinLegacy})
|
||||
SkinLoader.WaitForExit(IsForceRestart:=True)
|
||||
Next
|
||||
'完成提示
|
||||
Hint("更改皮肤成功!", HintType.Finish)
|
||||
Catch ex As Exception
|
||||
Log(ex, "更改正版皮肤后刷新皮肤失败", LogLevel.Feedback)
|
||||
End Try
|
||||
End Sub)
|
||||
Public Shared Sub ReloadCache(SkinAddress As String)
|
||||
RunInThread(
|
||||
Sub()
|
||||
Try
|
||||
'更新缓存
|
||||
WriteIni(PathTemp & "Cache\Skin\IndexMs.ini", Setup.Get("CacheMsUuid"), SkinAddress)
|
||||
Log(String.Format("[Skin] 已写入皮肤地址缓存 {0} -> {1}", Setup.Get("CacheMsUuid"), SkinAddress))
|
||||
'刷新控件
|
||||
For Each SkinLoader In {PageLaunchLeft.SkinMs, PageLaunchLeft.SkinLegacy}
|
||||
SkinLoader.WaitForExit(IsForceRestart:=True)
|
||||
Next
|
||||
'完成提示
|
||||
Hint("更改皮肤成功!", HintType.Finish)
|
||||
Catch ex As Exception
|
||||
Log(ex, "更改正版皮肤后刷新皮肤失败", LogLevel.Feedback)
|
||||
End Try
|
||||
End Sub)
|
||||
End Sub
|
||||
|
||||
'披风
|
||||
@@ -198,7 +205,7 @@
|
||||
End Set
|
||||
End Property
|
||||
Private IsChanging As Boolean = False
|
||||
Private Sub BtnSkinCape_Click(sender As Object, e As RoutedEventArgs) Handles BtnSkinCape.Click
|
||||
Public Sub BtnSkinCape_Click() Handles BtnSkinCape.Click
|
||||
'检查条件,获取新披风
|
||||
If IsChanging Then
|
||||
Hint("正在更改披风中,请稍候!")
|
||||
@@ -211,57 +218,60 @@
|
||||
Hint("正在获取披风列表,请稍候……")
|
||||
IsChanging = True
|
||||
'开始实际获取
|
||||
RunInNewThread(Sub()
|
||||
Try
|
||||
RunInNewThread(
|
||||
Sub()
|
||||
Try
|
||||
Retry:
|
||||
'获取登录信息
|
||||
If McLoginMsLoader.State <> LoadState.Finished Then McLoginMsLoader.WaitForExit(PageLoginMsSkin.GetLoginData())
|
||||
If McLoginMsLoader.State <> LoadState.Finished Then
|
||||
Hint("登录失败,无法更改披风!", HintType.Critical)
|
||||
Exit Sub
|
||||
End If
|
||||
Dim AccessToken As String = McLoginMsLoader.Output.AccessToken
|
||||
Dim Uuid As String = McLoginMsLoader.Output.Uuid
|
||||
Dim SkinData As JObject = GetJson(McLoginMsLoader.Output.ProfileJson)
|
||||
'获取玩家的所有披风
|
||||
Dim SelId As Integer? = Nothing
|
||||
RunInUiWait(Sub()
|
||||
Try
|
||||
Dim CapeNames As New Dictionary(Of String, String) From {
|
||||
{"Migrator", "迁移者披风"}, {"MapMaker", "Realms 地图制作者披风"}, {"Moderator", "Mojira 管理员披风"},
|
||||
{"Translator-Chinese", "Crowdin 中文翻译者披风"}, {"Translator", "Crowdin 翻译者披风"}, {"Cobalt", "Cobalt 披风"},
|
||||
{"Vanilla", "原版披风"}, {"Minecon2011", "Minecon 2011 参与者披风"}, {"Minecon2012", "Minecon 2012 参与者披风"},
|
||||
{"Minecon2013", "Minecon 2013 参与者披风"}, {"Minecon2015", "Minecon 2015 参与者披风"}, {"Minecon2016", "Minecon 2016 参与者披风"}
|
||||
}
|
||||
Dim SelectionControl As New List(Of IMyRadio) From {New MyRadioBox With {.Text = "无披风"}}
|
||||
For Each Cape In SkinData("capes")
|
||||
Dim CapeName As String = Cape("alias").ToString
|
||||
If CapeNames.ContainsKey(CapeName) Then CapeName = CapeNames(CapeName)
|
||||
SelectionControl.Add(New MyRadioBox With {.Text = CapeName})
|
||||
Next
|
||||
SelId = MyMsgBoxSelect(SelectionControl, "选择披风", "确定", "取消")
|
||||
Catch ex As Exception
|
||||
Log(ex, "获取玩家皮肤列表失败", LogLevel.Feedback)
|
||||
End Try
|
||||
End Sub)
|
||||
If SelId Is Nothing Then Exit Sub
|
||||
'发送请求
|
||||
Dim Result As String = NetRequestRetry("https://api.minecraftservices.com/minecraft/profile/capes/active",
|
||||
If(SelId = 0, "DELETE", "PUT"),
|
||||
If(SelId = 0, "", "{""capeId"": """ & SkinData("capes")(SelId - 1)("id").ToString & """}"),
|
||||
"application/json", Headers:=New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}})
|
||||
If Result.Contains("""errorMessage""") Then
|
||||
Hint("更改披风失败:" & GetJson(Result)("errorMessage"), HintType.Critical)
|
||||
Exit Sub
|
||||
Else
|
||||
Hint("更改披风成功!", HintType.Finish)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "更改披风失败", LogLevel.Hint)
|
||||
Finally
|
||||
IsChanging = False
|
||||
End Try
|
||||
End Sub, "Cape Change")
|
||||
'获取登录信息
|
||||
If McLoginMsLoader.State <> LoadState.Finished Then McLoginMsLoader.WaitForExit(PageLoginMsSkin.GetLoginData())
|
||||
If McLoginMsLoader.State <> LoadState.Finished Then
|
||||
Hint("登录失败,无法更改披风!", HintType.Critical)
|
||||
Exit Sub
|
||||
End If
|
||||
Dim AccessToken As String = McLoginMsLoader.Output.AccessToken
|
||||
Dim Uuid As String = McLoginMsLoader.Output.Uuid
|
||||
Dim SkinData As JObject = GetJson(McLoginMsLoader.Output.ProfileJson)
|
||||
'获取玩家的所有披风
|
||||
Dim SelId As Integer? = Nothing
|
||||
RunInUiWait(
|
||||
Sub()
|
||||
Try
|
||||
Dim CapeNames As New Dictionary(Of String, String) From {
|
||||
{"Migrator", "迁移者披风"}, {"MapMaker", "Realms 地图制作者披风"}, {"Moderator", "Mojira 管理员披风"},
|
||||
{"Translator-Chinese", "Crowdin 中文翻译者披风"}, {"Translator", "Crowdin 翻译者披风"}, {"Cobalt", "Cobalt 披风"},
|
||||
{"Vanilla", "原版披风"}, {"Minecon2011", "Minecon 2011 参与者披风"}, {"Minecon2012", "Minecon 2012 参与者披风"},
|
||||
{"Minecon2013", "Minecon 2013 参与者披风"}, {"Minecon2015", "Minecon 2015 参与者披风"}, {"Minecon2016", "Minecon 2016 参与者披风"},
|
||||
{"Cherry Blossom", "樱花披风"}
|
||||
}
|
||||
Dim SelectionControl As New List(Of IMyRadio) From {New MyRadioBox With {.Text = "无披风"}}
|
||||
For Each Cape In SkinData("capes")
|
||||
Dim CapeName As String = Cape("alias").ToString
|
||||
If CapeNames.ContainsKey(CapeName) Then CapeName = CapeNames(CapeName)
|
||||
SelectionControl.Add(New MyRadioBox With {.Text = CapeName})
|
||||
Next
|
||||
SelId = MyMsgBoxSelect(SelectionControl, "选择披风", "确定", "取消")
|
||||
Catch ex As Exception
|
||||
Log(ex, "获取玩家皮肤列表失败", LogLevel.Feedback)
|
||||
End Try
|
||||
End Sub)
|
||||
If SelId Is Nothing Then Exit Sub
|
||||
'发送请求
|
||||
Dim Result As String = NetRequestRetry("https://api.minecraftservices.com/minecraft/profile/capes/active",
|
||||
If(SelId = 0, "DELETE", "PUT"),
|
||||
If(SelId = 0, "", New JObject(New JProperty("capeId", SkinData("capes")(SelId - 1)("id"))).ToString(0)),
|
||||
"application/json", Headers:=New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}})
|
||||
If Result.Contains("""errorMessage""") Then
|
||||
Hint("更改披风失败:" & GetJson(Result)("errorMessage"), HintType.Critical)
|
||||
Exit Sub
|
||||
Else
|
||||
Hint("更改披风成功!", HintType.Finish)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "更改披风失败", LogLevel.Hint)
|
||||
Finally
|
||||
IsChanging = False
|
||||
End Try
|
||||
End Sub, "Cape Change")
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -33,7 +33,7 @@
|
||||
<Path Name="PathTypeOne" HorizontalAlignment="Left" RenderTransformOrigin="0.5,0.5" Margin="12,0,0,0" Stretch="Uniform" Fill="{DynamicResource ColorBrush3}" MaxHeight="16" MaxWidth="16" VerticalAlignment="Center"
|
||||
Data="">
|
||||
<Path.RenderTransform>
|
||||
<ScaleTransform ScaleX="1.4" ScaleY="1.4" />
|
||||
<ScaleTransform ScaleX="1.05" ScaleY="1.05" />
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
<TextBlock Name="LabTypeOne" Text="" FontSize="14" VerticalAlignment="Center" Grid.Column="1" Foreground="{DynamicResource ColorBrush3}" Margin="15,0,12,0" Grid.ColumnSpan="2" SnapsToDevicePixels="False" UseLayoutRounding="False" />
|
||||
@@ -48,12 +48,10 @@
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<local:MyRadioButton x:Name="RadioLoginType1" Tag="LoginType/1" Grid.Column="1" Grid.ColumnSpan="1" ColorType="Highlight" Text="Mojang" Margin="4,0"
|
||||
LogoScale="1.1" Logo="M9.183,18.967c-0.109-2.239,1.336-5.119,3.92-4.96c3.657-0.027,7.319-0.044,10.977,0.005 c3.712,0.214,6.596,2.759,9.652,4.533c0.181-1.697-0.197-3.717,1.237-4.982c1.899,2.091,3.143,4.894,5.677,6.334 c1.577,0.805,2.973-0.668,4.221-1.44c1.55,2.305,2.108,5.075,2.622,7.752c0.657,3.438,0.947,6.925,1.111,10.413 c-1.734-0.733-3.355-1.708-4.971-2.671c-1.396-4.933-5.349-8.656-9.723-11.059c-3.46-1.817-7.752-2.185-11.338-0.52 c-3.761,1.593-6.285,5.666-5.891,9.75c0.121,4.577,3.17,8.765,7.391,10.456c7.746,3.285,16.407,2.234,24.521,1.243 c0.454,2.315-1.511,4.527-3.695,4.889c-10.577,0.038-21.148-0.011-31.725,0.022c-2.217,0.279-4.079-1.879-3.964-4.008 C9.167,36.141,9.216,27.556,9.183,18.967z M40.114,8.09c0.294-0.801,0.872-1.417,1.542-1.924c1.379,2.589,2.742,5.482,2.311,8.491 c-0.332,2.54-4.42,2.507-5.052,0.163C38.349,12.527,39.209,10.178,40.114,8.09z M0,53 l0.1,0 z" Checked="True" />
|
||||
<local:MyRadioButton x:Name="RadioLoginType5" Tag="LoginType/5" Grid.Column="2" Grid.ColumnSpan="1" ColorType="Highlight" Text="微软" Margin="4,0"
|
||||
LogoScale="0.9" Logo="M481.882353 826.428235V527.661176l402.371765 2.409412v356.592941l-402.371765-60.235294z m0-628.85647l402.371765-60.235294v330.089411H481.882353V197.571765zM137.336471 525.251765h289.129411v293.948235l-289.129411-45.778824V525.251765z m0-55.416471V248.169412l289.129411-45.778824v265.035294h-289.129411z" />
|
||||
<local:MyRadioButton x:Name="RadioLoginType5" Tag="LoginType/5" Grid.Column="2" Grid.ColumnSpan="1" ColorType="Highlight" Text="正版" Margin="4,0"
|
||||
LogoScale="1" Logo="M511.488256 95.184408c35.310345 22.516742 95.184408 55.78011 167.34033 84.437781 75.738131 29.681159 148.405797 40.93953 191.392304 45.033483v353.615193c0 73.691154-50.662669 164.781609-136.123938 244.101949C649.65917 901.181409 558.568716 942.12094 512 942.12094c-46.568716 0-137.65917-40.93953-222.096952-119.748126C204.441779 742.54073 153.77911 651.450275 153.77911 577.247376v-353.103448c42.474763-4.093953 116.165917-15.352324 191.904048-45.545227 75.226387-30.192904 133.565217-63.456272 165.805098-83.414293M512 0c-4.093953 0-8.187906 1.535232-11.258371 3.582209l-14.84058 10.234882c-1.023488 0.511744-67.550225 47.592204-170.410794 88.531735-100.813593 39.916042-198.556722 41.963018-199.58021 41.963018l-25.075462 0.511744c-10.746627 0.511744-18.934533 8.187906-18.934533 18.422789v414.000999c0 216.97951 286.064968 446.24088 440.09995 446.24088s440.09995-229.261369 440.09995-445.729136V163.758121c0-10.234883-8.69965-18.422789-18.934533-18.422789l-24.563718-0.511744c-1.023488 0-98.766617-2.046977-199.58021-41.963018-103.372314-40.93953-170.410795-88.01999-170.922538-88.531734L523.258371 3.582209c-3.070465-2.558721-7.164418-3.582209-11.258371-3.582209z M743.308346 410.930535l-260.477761 260.477761c-15.864068 15.864068-41.963018 15.864068-57.827087 0l-144.823588-144.823588c-15.864068-15.864068-15.864068-41.963018 0-57.827087 8.187906-8.187906 18.422789-11.770115 29.169415-11.770115 10.234883 0 20.981509 4.093953 29.169416 11.770115l115.654173 115.654173L685.993003 352.591704c15.864068-15.864068 41.963018-15.864068 57.827087 0 15.352324 16.375812 15.352324 42.474763-0.511744 58.338831z" />
|
||||
<local:MyRadioButton x:Name="RadioLoginType0" Tag="LoginType/0" Grid.Column="3" Grid.ColumnSpan="1" ColorType="Highlight" Text="离线" Margin="4,0"
|
||||
LogoScale="0.95" Logo="M660.338 528.065c63.61-46.825 105.131-121.964 105.131-206.83 0-141.7-115.29-256.987-256.997-256.987-141.706 0-256.998 115.288-256.998 256.987 0 85.901 42.52 161.887 107.456 208.562-152.1 59.92-260.185 207.961-260.185 381.077 0 21.276 17.253 38.53 38.53 38.53 21.278 0 38.53-17.254 38.53-38.53 0-183.426 149.232-332.671 332.667-332.671 1.589 0 3.113-0.207 4.694-0.244 0.8 0.056 1.553 0.244 2.362 0.244 183.434 0 332.664 149.245 332.664 332.671 0 21.276 17.255 38.53 38.533 38.53 21.277 0 38.53-17.254 38.53-38.53 0-174.885-110.354-324.13-264.917-382.809z m-331.803-206.83c0-99.22 80.72-179.927 179.935-179.927s179.937 80.708 179.937 179.927c0 99.203-80.721 179.91-179.937 179.91s-179.935-80.708-179.935-179.91z" />
|
||||
LogoScale="0.85" Logo="M533.293176 788.841412a60.235294 60.235294 0 1 1 85.202824 85.202823l-42.616471 42.586353c-129.355294 129.385412-339.124706 129.385412-468.510117 0-129.385412-129.385412-129.385412-339.124706 0-468.510117l42.586353-42.616471a60.235294 60.235294 0 1 1 85.202823 85.202824l-42.61647 42.586352a210.823529 210.823529 0 1 0 298.164706 298.164706l42.586352-42.61647z m255.548236-255.548236l42.61647-42.586352a210.823529 210.823529 0 1 0-298.164706-298.164706l-42.586352 42.61647a60.235294 60.235294 0 1 1-85.202824-85.202823l42.616471-42.586353c129.355294-129.385412 339.124706-129.385412 468.510117 0 129.385412 129.385412 129.385412 339.124706 0 468.510117l-42.586353 42.616471a60.235294 60.235294 0 1 1-85.202823-85.202824zM192.542118 192.542118a60.235294 60.235294 0 0 1 85.202823 0l553.712941 553.712941a60.235294 60.235294 0 0 1-85.202823 85.202823L192.542118 277.744941a60.235294 60.235294 0 0 1 0-85.202823z" />
|
||||
<!--<local:MyRadioButton x:Name="RadioLoginType2" Tag="LoginType/2" Grid.Column="4" ColorType="Highlight" Text="统一通行证" Visibility="Collapsed"
|
||||
LogoScale="1.1" Logo="M834.5 684.1c-31.2-70.4-98.9-120.9-179.1-127.3 63.5-8.5 112.6-63 112.6-128.8 0-71.8-58.2-130-130-130s-130 58.2-130 130c0 65.9 49 120.3 112.6 128.8-80.2 6.4-148 57-179.1 127.3-8.7 19.7 6 42 27.6 42 12.1 0 22.7-7.5 27.7-18.5 24.3-53.9 78.5-91.5 141.3-91.5s117 37.6 141.3 91.5c5 11.1 15.6 18.5 27.7 18.5 21.4 0 36.1-22.3 27.4-42zM567.9 427.9c0-38.6 31.4-70 70-70s70 31.4 70 70-31.4 70-70 70-70-31.4-70-70zM460.3 347.9H216.9c-16.6 0-30 13.4-30 30s13.4 30 30 30h243.3c16.6 0 30-13.4 30-30 0.1-16.5-13.4-30-29.9-30zM367.4 459.6H216.9c-16.6 0-30 13.4-30 30s13.4 30 30 30h150.4c16.6 0 30-13.4 30-30 0.1-16.6-13.4-30-29.9-30zM297.4 571.2H217c-16.6 0-30 13.4-30 30s13.4 30 30 30h80.4c16.6 0 30-13.4 30-30 0-16.5-13.5-30-30-30zM900 236v552H124V236h776m0-60H124c-33.1 0-60 26.9-60 60v552c0 33.1 26.9 60 60 60h776c33.1 0 60-26.9 60-60V236c0-33.1-26.9-60-60-60z" />-->
|
||||
</Grid>
|
||||
|
||||
@@ -19,55 +19,57 @@
|
||||
RefreshButtonsUI()
|
||||
|
||||
'加载版本
|
||||
RunInNewThread(Sub()
|
||||
'确认 Minecraft 文件夹存在
|
||||
PathMcFolder = Setup.Get("LaunchFolderSelect").ToString.Replace("$", Path)
|
||||
If PathMcFolder = "" OrElse Not Directory.Exists(PathMcFolder) Then
|
||||
'无效的文件夹
|
||||
If PathMcFolder = "" Then
|
||||
Log("[Launch] 没有已储存的 Minecraft 文件夹")
|
||||
Else
|
||||
Log("[Launch] Minecraft 文件夹无效,该文件夹已不存在:" & PathMcFolder, LogLevel.Debug)
|
||||
End If
|
||||
McFolderListLoader.WaitForExit(IsForceRestart:=True)
|
||||
Setup.Set("LaunchFolderSelect", McFolderList(0).Path.Replace(Path, "$"))
|
||||
End If
|
||||
Log("[Launch] Minecraft 文件夹:" & PathMcFolder)
|
||||
If Setup.Get("SystemDebugDelay") Then Thread.Sleep(RandomInteger(500, 3000))
|
||||
'确认 Minecraft 版本存在
|
||||
Dim Selection As String = Setup.Get("LaunchVersionSelect")
|
||||
Dim Version As McVersion
|
||||
If Selection = "" Then
|
||||
Version = Nothing
|
||||
Else
|
||||
Version = New McVersion(Selection)
|
||||
End If
|
||||
If IsNothing(Version) OrElse Not (Version.Path.StartsWith(PathMcFolder) AndAlso Version.Check) Then
|
||||
'无效的版本
|
||||
Log("[Launch] Minecraft 版本无效" & If(IsNothing(Version), ",没有有效版本", ":" & Version.Path), If(IsNothing(Version), LogLevel.Normal, LogLevel.Debug))
|
||||
If Not McVersionListLoader.State = LoadState.Finished Then LoaderFolderRun(McVersionListLoader, PathMcFolder, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\", WaitForExit:=True)
|
||||
If McVersionList.Count = 0 OrElse McVersionList.First.Value(0).Logo.Contains("RedstoneBlock") Then
|
||||
Version = Nothing
|
||||
Setup.Set("LaunchVersionSelect", "")
|
||||
Log("[Launch] 无可用 Minecraft 版本")
|
||||
Else
|
||||
Version = McVersionList.First.Value(0)
|
||||
Setup.Set("LaunchVersionSelect", Version.Name)
|
||||
Log("[Launch] 自动选择 Minecraft 版本:" & Version.Path)
|
||||
End If
|
||||
End If
|
||||
RunInUi(Sub()
|
||||
McVersionCurrent = Version '绕这一圈是为了避免 McVersionCheck 触发第二次版本改变
|
||||
IsLoadFinished = True
|
||||
RefreshButtonsUI()
|
||||
RefreshPage(False, False) '有可能选择的版本变化了,需要重新刷新
|
||||
If McLoginAble() = "" Then McLoginLoader.Start() '自动登录
|
||||
End Sub)
|
||||
End Sub, "Version Check", ThreadPriority.AboveNormal)
|
||||
RunInNewThread(
|
||||
Sub()
|
||||
'确认 Minecraft 文件夹存在
|
||||
PathMcFolder = Setup.Get("LaunchFolderSelect").ToString.Replace("$", Path)
|
||||
If PathMcFolder = "" OrElse Not Directory.Exists(PathMcFolder) Then
|
||||
'无效的文件夹
|
||||
If PathMcFolder = "" Then
|
||||
Log("[Launch] 没有已储存的 Minecraft 文件夹")
|
||||
Else
|
||||
Log("[Launch] Minecraft 文件夹无效,该文件夹已不存在:" & PathMcFolder, LogLevel.Debug)
|
||||
End If
|
||||
McFolderListLoader.WaitForExit(IsForceRestart:=True)
|
||||
Setup.Set("LaunchFolderSelect", McFolderList(0).Path.Replace(Path, "$"))
|
||||
End If
|
||||
Log("[Launch] Minecraft 文件夹:" & PathMcFolder)
|
||||
If Setup.Get("SystemDebugDelay") Then Thread.Sleep(RandomInteger(500, 3000))
|
||||
'确认 Minecraft 版本存在
|
||||
Dim Selection As String = Setup.Get("LaunchVersionSelect")
|
||||
Dim Version As McVersion
|
||||
If Selection = "" Then
|
||||
Version = Nothing
|
||||
Else
|
||||
Version = New McVersion(Selection)
|
||||
End If
|
||||
If IsNothing(Version) OrElse Not (Version.Path.StartsWith(PathMcFolder) AndAlso Version.Check) Then
|
||||
'无效的版本
|
||||
Log("[Launch] Minecraft 版本无效" & If(IsNothing(Version), ",没有有效版本", ":" & Version.Path), If(IsNothing(Version), LogLevel.Normal, LogLevel.Debug))
|
||||
If Not McVersionListLoader.State = LoadState.Finished Then LoaderFolderRun(McVersionListLoader, PathMcFolder, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\", WaitForExit:=True)
|
||||
If Not McVersionList.Any() OrElse McVersionList.First.Value(0).Logo.Contains("RedstoneBlock") Then
|
||||
Version = Nothing
|
||||
Setup.Set("LaunchVersionSelect", "")
|
||||
Log("[Launch] 无可用 Minecraft 版本")
|
||||
Else
|
||||
Version = McVersionList.First.Value(0)
|
||||
Setup.Set("LaunchVersionSelect", Version.Name)
|
||||
Log("[Launch] 自动选择 Minecraft 版本:" & Version.Path)
|
||||
End If
|
||||
End If
|
||||
RunInUi(
|
||||
Sub()
|
||||
McVersionCurrent = Version '绕这一圈是为了避免 McVersionCheck 触发第二次版本改变
|
||||
IsLoadFinished = True
|
||||
RefreshButtonsUI()
|
||||
RefreshPage(False, False) '有可能选择的版本变化了,需要重新刷新
|
||||
If McLoginAble() = "" Then McLoginLoader.Start() '自动登录
|
||||
End Sub)
|
||||
End Sub, "Version Check", ThreadPriority.AboveNormal)
|
||||
|
||||
'改变页面
|
||||
Dim LoginType As McLoginType = Setup.Get("LoginType")
|
||||
If LoginType = McLoginType.Legacy OrElse LoginType = McLoginType.Mojang OrElse LoginType = McLoginType.Ms Then CType(FindName("RadioLoginType" & LoginType), MyRadioButton).Checked = True
|
||||
If LoginType = McLoginType.Legacy OrElse LoginType = McLoginType.Ms Then CType(FindName("RadioLoginType" & LoginType), MyRadioButton).Checked = True
|
||||
RefreshPage(False, False)
|
||||
|
||||
AniControlEnabled -= 1
|
||||
@@ -79,7 +81,45 @@
|
||||
''' 切换至启动中页面。
|
||||
''' </summary>
|
||||
Public Sub PageChangeToLaunching()
|
||||
LaunchingPreload()
|
||||
'修改登陆方式
|
||||
Select Case Setup.Get("LoginType")
|
||||
Case McLoginType.Legacy
|
||||
If PageLinkHiper.HiperState = LoadState.Finished Then
|
||||
LabLaunchingMethod.Text = "联机离线登录"
|
||||
Else
|
||||
LabLaunchingMethod.Text = "离线登录"
|
||||
End If
|
||||
Case McLoginType.Ms
|
||||
LabLaunchingMethod.Text = "正版登录"
|
||||
Case McLoginType.Nide
|
||||
LabLaunchingMethod.Text = "统一通行证"
|
||||
Case McLoginType.Auth
|
||||
LabLaunchingMethod.Text = "Authlib-Injector"
|
||||
End Select
|
||||
'初始化页面
|
||||
LabLaunchingName.Text = McVersionCurrent.Name
|
||||
LabLaunchingStage.Text = "初始化"
|
||||
LabLaunchingTitle.Text = If(McLaunchLoader.Input Is Nothing OrElse McLaunchLoader.Input.SaveBatch Is Nothing, "正在启动游戏", "正在导出启动脚本")
|
||||
LabLaunchingProgress.Text = "0.00 %"
|
||||
LabLaunchingProgress.Opacity = 1
|
||||
LabLaunchingDownload.Visibility = Visibility.Visible
|
||||
LabLaunchingProgressLeft.Opacity = 0.6
|
||||
LabLaunchingDownload.Visibility = Visibility.Visible
|
||||
LabLaunchingDownload.Text = "0 B/s"
|
||||
LabLaunchingDownload.Opacity = 0
|
||||
LabLaunchingDownload.Visibility = Visibility.Collapsed
|
||||
LabLaunchingDownloadLeft.Opacity = 0
|
||||
LabLaunchingDownloadLeft.Visibility = Visibility.Collapsed
|
||||
ProgressLaunchingFinished.Width = New GridLength(0, GridUnitType.Star)
|
||||
ProgressLaunchingUnfinished.Width = New GridLength(1, GridUnitType.Star)
|
||||
PanLaunchingHint.Opacity = 0
|
||||
PanLaunchingHint.Visibility = Visibility.Collapsed
|
||||
PanLaunchingInfo.Width = Double.NaN '重置宽度改变动画
|
||||
McLaunchProcess = Nothing
|
||||
McLaunchWatcher = Nothing
|
||||
'获取 “你知道吗” 提示
|
||||
LabLaunchingHint.Text = PageOtherTest.GetRandomHint()
|
||||
'初始化其他页面
|
||||
PanInput.IsHitTestVisible = False
|
||||
PanLaunching.IsHitTestVisible = False
|
||||
LoadLaunching.State.LoadingState = MyLoading.MyLoadingState.Run
|
||||
@@ -117,8 +157,6 @@
|
||||
|
||||
Private Enum PageType
|
||||
None
|
||||
Mojang
|
||||
MojangSkin
|
||||
Legacy
|
||||
Nide
|
||||
NideSkin
|
||||
@@ -134,12 +172,6 @@
|
||||
|
||||
Private Function PageGet(Type As PageType)
|
||||
Select Case Type
|
||||
Case PageType.Mojang
|
||||
If IsNothing(FrmLoginMojang) Then FrmLoginMojang = New PageLoginMojang
|
||||
Return FrmLoginMojang
|
||||
Case PageType.MojangSkin
|
||||
If IsNothing(FrmLoginMojangSkin) Then FrmLoginMojangSkin = New PageLoginMojangSkin
|
||||
Return FrmLoginMojangSkin
|
||||
Case PageType.Legacy
|
||||
If IsNothing(FrmLoginLegacy) Then FrmLoginLegacy = New PageLoginLegacy
|
||||
Return FrmLoginLegacy
|
||||
@@ -171,7 +203,7 @@
|
||||
''' <param name="Type">新页面的种类。</param>
|
||||
''' <param name="Anim">是否显示动画。</param>
|
||||
Private Function PageChange(Type As PageType, Anim As Boolean)
|
||||
Dim PageNew As Object = FrmLoginMojang '初始化一个东西,避免在执行时出现异常导致雪崩
|
||||
Dim PageNew As Object = FrmLoginMs '初始化一个东西,避免在执行时出现异常导致雪崩
|
||||
Try
|
||||
|
||||
#Region "确定更改的页面实例并实例化"
|
||||
@@ -232,14 +264,7 @@
|
||||
Select Case LoginPageType
|
||||
Case 0 '正版或离线
|
||||
UnknownType:
|
||||
If RadioLoginType1.Checked Then
|
||||
If Setup.Get("CacheMojangAccess") = "" Then
|
||||
Type = PageType.Mojang
|
||||
Else
|
||||
Type = PageType.MojangSkin
|
||||
End If
|
||||
Setup.Set("LoginType", McLoginType.Mojang)
|
||||
ElseIf RadioLoginType5.Checked Then
|
||||
If RadioLoginType5.Checked Then
|
||||
If Setup.Get("CacheMsAccess") = "" Then
|
||||
Type = PageType.Ms
|
||||
Else
|
||||
@@ -252,28 +277,19 @@ UnknownType:
|
||||
End If
|
||||
PanType.Visibility = Visibility.Visible
|
||||
PanTypeOne.Visibility = Visibility.Collapsed
|
||||
RadioLoginType1.Visibility = Visibility.Visible
|
||||
RadioLoginType5.Visibility = Visibility.Visible
|
||||
RadioLoginType0.Visibility = Visibility.Visible
|
||||
Case 1 '仅正版
|
||||
If RadioLoginType1.Checked Then
|
||||
If Setup.Get("CacheMojangAccess") = "" Then
|
||||
Type = PageType.Mojang
|
||||
Else
|
||||
Type = PageType.MojangSkin
|
||||
End If
|
||||
Setup.Set("LoginType", McLoginType.Mojang)
|
||||
If Setup.Get("CacheMsAccess") = "" Then
|
||||
Type = PageType.Ms
|
||||
Else
|
||||
If Setup.Get("CacheMsAccess") = "" Then
|
||||
Type = PageType.Ms
|
||||
Else
|
||||
Type = PageType.MsSkin
|
||||
End If
|
||||
Setup.Set("LoginType", McLoginType.Ms)
|
||||
Type = PageType.MsSkin
|
||||
End If
|
||||
PanType.Visibility = Visibility.Visible
|
||||
PanTypeOne.Visibility = Visibility.Collapsed
|
||||
RadioLoginType1.Visibility = Visibility.Visible
|
||||
Setup.Set("LoginType", McLoginType.Ms)
|
||||
PanType.Visibility = Visibility.Collapsed
|
||||
PanTypeOne.Visibility = Visibility.Visible
|
||||
PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonShield)
|
||||
LabTypeOne.Text = "正版登录"
|
||||
RadioLoginType5.Visibility = Visibility.Visible
|
||||
RadioLoginType0.Visibility = Visibility.Collapsed
|
||||
Case 2 '仅离线
|
||||
@@ -281,7 +297,7 @@ UnknownType:
|
||||
Setup.Set("LoginType", McLoginType.Legacy)
|
||||
PanType.Visibility = Visibility.Collapsed
|
||||
PanTypeOne.Visibility = Visibility.Visible
|
||||
PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonUser)
|
||||
PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonOffline)
|
||||
LabTypeOne.Text = "离线登录"
|
||||
Case 3 '统一通行证
|
||||
If Setup.Get("CacheNideAccess") = "" Then
|
||||
@@ -316,7 +332,7 @@ UnknownType:
|
||||
Dim Control As MyRadioButton = FindName("RadioLoginType" & Setup.Get("LoginType"))
|
||||
If Control IsNot Nothing Then Control.Checked = True
|
||||
End Sub
|
||||
Private Sub RadioLoginType_Change(sender As Object, raiseByMouse As Boolean) Handles RadioLoginType0.Check, RadioLoginType1.Check, RadioLoginType5.Check
|
||||
Private Sub RadioLoginType_Change(sender As Object, raiseByMouse As Boolean) Handles RadioLoginType0.Check, RadioLoginType5.Check
|
||||
If raiseByMouse Then RefreshPage(True, True)
|
||||
End Sub
|
||||
|
||||
@@ -324,54 +340,6 @@ UnknownType:
|
||||
|
||||
#Region "皮肤"
|
||||
|
||||
'Mojang 正版皮肤
|
||||
Public Shared SkinMojang As New LoaderTask(Of EqualableList(Of String), String)("Loader Skin Mojang", AddressOf SkinMojangLoad, AddressOf SkinMojangInput, ThreadPriority.AboveNormal)
|
||||
Private Shared Function SkinMojangInput() As EqualableList(Of String)
|
||||
'获取名称
|
||||
Return New EqualableList(Of String) From {Setup.Get("CacheMojangName"), Setup.Get("CacheMojangUuid")}
|
||||
End Function
|
||||
Private Shared Sub SkinMojangLoad(Data As LoaderTask(Of EqualableList(Of String), String))
|
||||
'清空已有皮肤
|
||||
'如果在输入时清空皮肤,若输入内容一样则不会执行 Load 方法,导致皮肤不被加载
|
||||
RunInUi(Sub() If FrmLoginMojangSkin IsNot Nothing AndAlso FrmLoginMojangSkin.Skin IsNot Nothing Then FrmLoginMojangSkin.Skin.Clear())
|
||||
'获取 Url
|
||||
Dim UserName As String = Data.Input(0)
|
||||
Dim Uuid As String = Data.Input(1)
|
||||
If UserName = "" Then
|
||||
Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(UserName)) & ".png"
|
||||
Log("[Minecraft] 获取正版皮肤失败,ID 为空")
|
||||
GoTo Finish
|
||||
End If
|
||||
Try
|
||||
Dim Result As String = McSkinGetAddress(Uuid, "Mojang")
|
||||
If Data.IsAborted Then Throw New ThreadInterruptedException("当前任务已取消:" & UserName)
|
||||
Result = McSkinDownload(Result)
|
||||
If Data.IsAborted Then Throw New ThreadInterruptedException("当前任务已取消:" & UserName)
|
||||
Data.Output = Result
|
||||
Catch ex As Exception
|
||||
If ex.GetType.Name = "ThreadInterruptedException" Then
|
||||
Data.Output = ""
|
||||
Exit Sub
|
||||
ElseIf GetExceptionSummary(ex).Contains("429") Then
|
||||
Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(UserName)) & ".png"
|
||||
Log("[Minecraft] 获取正版皮肤失败(" & UserName & "):获取皮肤太过频繁,请 5 分钟后再试!", LogLevel.Hint)
|
||||
ElseIf GetExceptionSummary(ex).Contains("未设置自定义皮肤") Then
|
||||
Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(UserName)) & ".png"
|
||||
Log("[Minecraft] 用户未设置自定义皮肤,跳过皮肤加载")
|
||||
Else
|
||||
Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(UserName)) & ".png"
|
||||
Log(ex, "获取正版皮肤失败(" & UserName & ")", LogLevel.Hint)
|
||||
End If
|
||||
End Try
|
||||
Finish:
|
||||
'刷新显示
|
||||
If FrmLoginMojangSkin IsNot Nothing Then
|
||||
RunInUi(AddressOf FrmLoginMojangSkin.Skin.Load)
|
||||
ElseIf Not Data.IsAborted Then '如果已经中断,Input 也被清空,就不会再次刷新
|
||||
Data.Input = Nothing '清空输入,因为皮肤实际上没有被渲染,如果不清空切换到页面的 Start 会由于输入相同而不渲染
|
||||
End If
|
||||
End Sub
|
||||
|
||||
'微软正版皮肤
|
||||
Public Shared SkinMs As New LoaderTask(Of EqualableList(Of String), String)("Loader Skin Ms", AddressOf SkinMsLoad, AddressOf SkinMsInput, ThreadPriority.AboveNormal)
|
||||
Private Shared Function SkinMsInput() As EqualableList(Of String)
|
||||
@@ -428,7 +396,7 @@ Finish:
|
||||
Select Case Type
|
||||
Case 0
|
||||
If FrmLoginLegacy IsNot Nothing AndAlso FrmLoginLegacy.IsReloaded Then
|
||||
Return New EqualableList(Of String) From {0, If(FrmLoginLegacy.ComboName.Text, "")}
|
||||
Return New EqualableList(Of String) From {0, If(FrmLoginLegacy.ComboName.Text.Trim, "")}
|
||||
ElseIf Setup.Get("LoginLegacyName") = "" Then
|
||||
Return New EqualableList(Of String) From {0, ""}
|
||||
Else
|
||||
@@ -592,17 +560,19 @@ Finish:
|
||||
|
||||
'全部皮肤加载器
|
||||
'需要放在其中元素的后面,否则会因为它提前被加载而莫名其妙变成 Nothing
|
||||
Public Shared SkinLoaders As New List(Of LoaderTask(Of EqualableList(Of String), String)) From {SkinMojang, SkinMs, SkinLegacy, SkinNide, SkinAuth}
|
||||
Public Shared SkinLoaders As New List(Of LoaderTask(Of EqualableList(Of String), String)) From {SkinMs, SkinLegacy, SkinNide, SkinAuth}
|
||||
|
||||
#End Region
|
||||
|
||||
'版本选择按钮
|
||||
Private Sub BtnVersion_Click(sender As Object, e As EventArgs) Handles BtnVersion.Click
|
||||
If McLaunchLoader.State = LoadState.Loading Then Exit Sub
|
||||
FrmMain.PageChange(FormMain.PageType.VersionSelect)
|
||||
End Sub
|
||||
'启动按钮
|
||||
Public Sub LaunchButtonClick() Handles BtnLaunch.Click
|
||||
If McLaunchLoader.State = LoadState.Loading OrElse Not BtnLaunch.IsEnabled Then Exit Sub
|
||||
If McLaunchLoader.State = LoadState.Loading OrElse Not BtnLaunch.IsEnabled OrElse
|
||||
(FrmMain.PageRight IsNot Nothing AndAlso FrmMain.PageRight.PageState <> MyPageRight.PageStates.ContentStay AndAlso FrmMain.PageRight.PageState <> MyPageRight.PageStates.ContentEnter) Then Exit Sub
|
||||
'愚人节处理
|
||||
If IsAprilEnabled AndAlso Not IsAprilGiveup Then
|
||||
ThemeUnlock(12, False, "隐藏主题 滑稽彩 已解锁!")
|
||||
@@ -697,63 +667,13 @@ ExitRefresh:
|
||||
End Try
|
||||
End If
|
||||
End Sub
|
||||
'更多按钮
|
||||
'版本设置按钮
|
||||
Private Sub BtnMore_Click(sender As Object, e As EventArgs) Handles BtnMore.Click
|
||||
If McLaunchLoader.State = LoadState.Loading Then Exit Sub
|
||||
McVersionCurrent.Load()
|
||||
PageVersionLeft.Version = McVersionCurrent
|
||||
FrmMain.PageChange(FormMain.PageType.VersionSetup, 0)
|
||||
End Sub
|
||||
'刷新 UI
|
||||
Public Sub LaunchingPreload()
|
||||
'修改登陆方式
|
||||
Select Case Setup.Get("LoginType")
|
||||
Case McLoginType.Legacy
|
||||
If PageLinkHiper.HiperState = LoadState.Finished Then
|
||||
LabLaunchingMethod.Text = "联机离线登录"
|
||||
Else
|
||||
LabLaunchingMethod.Text = "离线登录"
|
||||
End If
|
||||
Case McLoginType.Mojang
|
||||
LabLaunchingMethod.Text = "Mojang 正版登录"
|
||||
Case McLoginType.Ms
|
||||
LabLaunchingMethod.Text = "微软正版登录"
|
||||
Case McLoginType.Nide
|
||||
LabLaunchingMethod.Text = "统一通行证"
|
||||
Case McLoginType.Auth
|
||||
LabLaunchingMethod.Text = "Authlib-Injector"
|
||||
End Select
|
||||
'初始化页面
|
||||
LabLaunchingName.Text = McVersionCurrent.Name
|
||||
LabLaunchingStage.Text = "初始化"
|
||||
LabLaunchingTitle.Text = If(McLaunchLoader.Input Is Nothing OrElse McLaunchLoader.Input.SaveBatch Is Nothing, "正在启动游戏", "正在导出启动脚本")
|
||||
LabLaunchingProgress.Text = "0.00 %"
|
||||
LabLaunchingProgress.Opacity = 1
|
||||
LabLaunchingDownload.Visibility = Visibility.Visible
|
||||
LabLaunchingProgressLeft.Opacity = 0.6
|
||||
LabLaunchingDownload.Visibility = Visibility.Visible
|
||||
LabLaunchingDownload.Text = "0 B/s"
|
||||
LabLaunchingDownload.Opacity = 0
|
||||
LabLaunchingDownload.Visibility = Visibility.Collapsed
|
||||
LabLaunchingDownloadLeft.Opacity = 0
|
||||
LabLaunchingDownloadLeft.Visibility = Visibility.Collapsed
|
||||
ProgressLaunchingFinished.Width = New GridLength(0, GridUnitType.Star)
|
||||
ProgressLaunchingUnfinished.Width = New GridLength(1, GridUnitType.Star)
|
||||
PanLaunchingHint.Opacity = 0
|
||||
PanLaunchingHint.Visibility = Visibility.Collapsed
|
||||
PanLaunchingInfo.Width = Double.NaN '重置宽度改变动画
|
||||
McLaunchProcess = Nothing
|
||||
McLaunchWatcher = Nothing
|
||||
'获取 “你知道吗” 提示
|
||||
Dim Hint As String = PageOtherTest.GetRandomHint()
|
||||
Try
|
||||
Dim RawHints = ReadFile(Path & "PCL\hints.txt").Split(vbCrLf.ToCharArray).Where(Function(s) Not String.IsNullOrWhiteSpace(s)).ToList
|
||||
If RawHints.Count > 0 Then Hint = RandomOne(RawHints)
|
||||
Catch ex As Exception
|
||||
Log(ex, "获取自定义 你知道吗 提示失败", LogLevel.Hint)
|
||||
End Try
|
||||
LabLaunchingHint.Text = Hint
|
||||
End Sub
|
||||
Private ShowProgress As Double = 0
|
||||
''' <summary>
|
||||
''' 每 0.2s 执行一次,刷新启动的数据 UI 显示。
|
||||
''' </summary>
|
||||
@@ -837,6 +757,7 @@ ExitRefresh:
|
||||
Log(ex, "刷新启动信息失败", LogLevel.Feedback)
|
||||
End Try
|
||||
End Sub
|
||||
Private ShowProgress As Double = 0
|
||||
'尺寸改变动画
|
||||
Private IsWidthAnimating As Boolean = False
|
||||
Private ActualUsedWidth As Double
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
<local:MyPageRight x:Class="PageLaunchRight"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
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:local="clr-namespace:PCL"
|
||||
mc:Ignorable="d">
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
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:local="clr-namespace:PCL"> <!-- 不知道为啥只有这个文件不能在 XAML 设置 PanScroll -->
|
||||
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
|
||||
<StackPanel Name="PanMain" Margin="25,25,25,10" Grid.IsSharedSizeScope="True">
|
||||
<StackPanel x:Name="PanCustom">
|
||||
</StackPanel>
|
||||
<local:MyCard Margin="0,0,0,15" Title="快照版提示" x:Name="PanHint">
|
||||
<StackPanel Margin="25,40,23,12">
|
||||
<TextBlock x:Name="LabHint1" TextWrapping="Wrap" Margin="0,0,0,2" />
|
||||
<TextBlock x:Name="LabHint2" TextWrapping="Wrap" Margin="0,0,0,2" />
|
||||
<local:MyIconButton LogoScale="1.1" 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="10" HorizontalAlignment="Right" VerticalAlignment="Top"
|
||||
x:Name="BtnHintClose" />
|
||||
<StackPanel Margin="25,38,23,15">
|
||||
<TextBlock x:Name="LabHint1" TextWrapping="Wrap" Margin="0,0,0,2" FontSize="13.5" />
|
||||
<TextBlock x:Name="LabHint2" TextWrapping="Wrap" Margin="0,0,0,2" FontSize="13.5" />
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
<local:MyCard Margin="0,0,0,15" Title="启动日志" x:Name="PanLog">
|
||||
|
||||
@@ -2,107 +2,220 @@
|
||||
|
||||
Private Sub Init() Handles Me.Loaded
|
||||
PanBack.ScrollToHome()
|
||||
PanScroll = PanBack '不知道为啥不能在 XAML 设置
|
||||
PanLog.Visibility = If(ModeDebug, Visibility.Visible, Visibility.Collapsed)
|
||||
'刷新主页
|
||||
If ShouldRefresh Then
|
||||
ShouldRefresh = False
|
||||
RefreshCustom()
|
||||
End If
|
||||
'快照版提示
|
||||
#If BETA Then
|
||||
PanHint.Visibility = Visibility.Collapsed
|
||||
#Else
|
||||
PanHint.Title = "快照版提示"
|
||||
PanHint.Visibility = If(ThemeCheckGold(), Visibility.Collapsed, Visibility.Visible)
|
||||
LabHint1.Text = "快照版包含尚未在正式版发布的测试性功能,仅用于赞助者本人尝鲜。所以请不要发给其他人或者用于制作整合包哦!如果发现了 Bug,可以在 更多 → 反馈 中提交!"
|
||||
LabHint2.Text = "你可以通过赞助¥23.33 档位换取解锁码来隐藏这个提示。"
|
||||
LabHint1.Text = "快照版包含尚未正式发布的测试功能,仅用于赞助者本人尝鲜。请不要发给其他人或者用来制作整合包哦!"
|
||||
LabHint2.Text = $"若已累积赞助¥23.33,在爱发电私信发送 {vbLQ}解锁码{vbRQ} 即可永久隐藏此提示。"
|
||||
#End If
|
||||
End Sub
|
||||
|
||||
Private LatestFileContent As String = ""
|
||||
Private LatestFileLink As String = Nothing
|
||||
Private IsRefreshing As Boolean = False
|
||||
Public ShouldRefresh As Boolean = True
|
||||
'暂时关闭快照版提示
|
||||
#If Not BETA Then
|
||||
Private Sub BtnHintClose_Click(sender As Object, e As EventArgs) Handles BtnHintClose.Click
|
||||
AniDispose(PanHint, True)
|
||||
End Sub
|
||||
#End If
|
||||
|
||||
#Region "自定义主页"
|
||||
|
||||
''' <summary>
|
||||
''' 刷新自定义的主页。返回是否成功。
|
||||
''' 刷新自定义主页。
|
||||
''' </summary>
|
||||
Private Sub RefreshCustom()
|
||||
If IsRefreshing Then
|
||||
ShouldRefresh = True
|
||||
Exit Sub
|
||||
End If
|
||||
Private Sub Refresh() Handles Me.Loaded
|
||||
RunInNewThread(Sub()
|
||||
Try
|
||||
SyncLock RefreshLock
|
||||
RefreshReal()
|
||||
End SyncLock
|
||||
Catch ex As Exception
|
||||
Log(ex, "加载 PCL 主页自定义信息失败", LogLevel.Msgbox)
|
||||
End Try
|
||||
End Sub, $"刷新自定义主页 #{GetUuid()}")
|
||||
End Sub
|
||||
Private Sub RefreshReal()
|
||||
Dim Content As String = ""
|
||||
Dim Url As String
|
||||
Select Case Setup.Get("UiCustomType")
|
||||
Case 1
|
||||
'加载本地文件
|
||||
Log("[Page] 主页自定义数据来源:本地文件")
|
||||
Content = ReadFile(Path & "PCL\Custom.xaml") 'ReadFile 会进行存在检测
|
||||
Case 2
|
||||
Url = Setup.Get("UiCustomNet")
|
||||
Download:
|
||||
'加载联网文件
|
||||
If String.IsNullOrWhiteSpace(Url) Then Exit Select
|
||||
If Url = Setup.Get("CacheSavedPageUrl") AndAlso File.Exists(PathTemp & "Cache\Custom.xaml") Then
|
||||
'缓存可用
|
||||
Log("[Page] 主页自定义数据来源:联网缓存文件")
|
||||
Content = ReadFile(PathTemp & "Cache\Custom.xaml")
|
||||
'后台更新缓存
|
||||
OnlineLoader.Start(Url)
|
||||
Else
|
||||
'缓存不可用
|
||||
Log("[Page] 主页自定义数据来源:联网全新下载")
|
||||
Hint("正在加载主页……")
|
||||
RunInUiWait(Sub() LoadContent("")) '在加载结束前清空页面
|
||||
Setup.Set("CacheSavedPageVersion", "")
|
||||
OnlineLoader.Start(Url) '下载完成后将会再次触发更新
|
||||
Exit Sub
|
||||
End If
|
||||
Case 3
|
||||
Select Case Setup.Get("UiCustomPreset")
|
||||
Case 0
|
||||
Log("[Page] 主页预设:你知道吗")
|
||||
Content = "
|
||||
<local:MyCard Title=""你知道吗?"" Margin=""0,0,0,15"">
|
||||
<TextBlock Margin=""25,38,23,15"" FontSize=""13.5"" IsHitTestVisible=""False"" Text=""{hint}"" TextWrapping=""Wrap"" Foreground=""{DynamicResource ColorBrush1}"" />
|
||||
<local:MyIconButton Height=""22"" Width=""22"" Margin=""9"" VerticalAlignment=""Top"" HorizontalAlignment=""Right""
|
||||
EventType=""刷新主页"" EventData=""/""
|
||||
Logo=""M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z"" />
|
||||
</local:MyCard>"
|
||||
Case 1
|
||||
Log("[Page] 主页预设:回声洞")
|
||||
Content = "
|
||||
<local:MyCard Title=""回声洞"" Margin=""0,0,0,15"">
|
||||
<TextBlock Margin=""25,38,23,15"" FontSize=""13.5"" IsHitTestVisible=""False"" Text=""{cave}"" TextWrapping=""Wrap"" Foreground=""{DynamicResource ColorBrush1}"" />
|
||||
<local:MyIconButton Height=""22"" Width=""22"" Margin=""9"" VerticalAlignment=""Top"" HorizontalAlignment=""Right""
|
||||
EventType=""刷新主页"" EventData=""/""
|
||||
Logo=""M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z"" />
|
||||
</local:MyCard>"
|
||||
Case 2
|
||||
Log("[Page] 主页预设:Minecraft 新闻")
|
||||
Url = "http://pcl.mcnews.thestack.top"
|
||||
GoTo Download
|
||||
Case 3
|
||||
Log("[Page] 主页预设:简单主页")
|
||||
Url = "https://gitee.com/mfn233/PCL-Mainpage/raw/main/Custom.xaml"
|
||||
GoTo Download
|
||||
Case 4
|
||||
Log("[Page] 主页预设:每日整合包推荐")
|
||||
Url = "https://pclsub.sodamc.com/"
|
||||
GoTo Download
|
||||
End Select
|
||||
End Select
|
||||
RunInUi(Sub() LoadContent(Content))
|
||||
End Sub
|
||||
Private RefreshLock As New Object
|
||||
|
||||
'联网获取自定义主页文件
|
||||
Private OnlineLoader As New LoaderTask(Of String, Integer)("自定义主页获取", AddressOf OnlineLoaderSub) With {.ReloadTimeout = 10 * 60 * 1000}
|
||||
Private Sub OnlineLoaderSub(Task As LoaderTask(Of String, Integer))
|
||||
Try
|
||||
IsRefreshing = True
|
||||
PanCustom.Children.Clear()
|
||||
RunInNewThread(Sub()
|
||||
Dim FileContent As String = ""
|
||||
Select Case Setup.Get("UiCustomType")
|
||||
Case 0
|
||||
'啥也不干
|
||||
Case 1
|
||||
'加载本地文件
|
||||
FileContent = ReadFile(Path & "PCL\Custom.xaml") 'ReadFile 会进行存在检测
|
||||
Log("[System] 尝试从本地文件读取主页自定义数据(" & FileContent.Length & ")")
|
||||
Case 2
|
||||
'加载联网文件
|
||||
Try
|
||||
Dim Link As String = Setup.Get("UiCustomNet")
|
||||
If Link = LatestFileLink Then
|
||||
FileContent = LatestFileContent
|
||||
Log("[System] 尝试缓存加载主页自定义数据(" & FileContent.Length & ")")
|
||||
ElseIf Not String.IsNullOrWhiteSpace(Link) Then
|
||||
Log("[System] 开始从网络读取主页自定义数据(" & Link & ")")
|
||||
FileContent = NetGetCodeByRequestRetry(Link)
|
||||
Log("[System] 尝试从网络读取主页自定义数据(" & FileContent.Length & ")")
|
||||
End If
|
||||
LatestFileLink = Link
|
||||
LatestFileContent = FileContent
|
||||
Catch ex As Exception
|
||||
Log(ex, "获取 PCL 主页自定义信息失败", LogLevel.Msgbox)
|
||||
LatestFileLink = ""
|
||||
End Try
|
||||
End Select
|
||||
If FileContent = "" Then
|
||||
IsRefreshing = False
|
||||
Exit Sub
|
||||
End If
|
||||
FileContent = "<StackPanel xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" xmlns:local=""clr-namespace:PCL;assembly=Plain Craft Launcher 2"">" & FileContent & "</StackPanel>"
|
||||
FileContent = FileContent.Replace("{path}", Path)
|
||||
RunInUi(Sub()
|
||||
Try
|
||||
Log("[System] 加载主页自定义数据")
|
||||
PanCustom.Children.Add(GetObjectFromXML(FileContent))
|
||||
IsRefreshing = False
|
||||
Catch ex As Exception
|
||||
IsRefreshing = False
|
||||
Log("[System] 自定义信息内容:" & vbCrLf & FileContent)
|
||||
If MyMsgBox(ex.Message, "加载自定义主页失败", "重试", "取消") = 1 Then
|
||||
LatestFileLink = Nothing
|
||||
LatestFileContent = ""
|
||||
RefreshCustom()
|
||||
End If
|
||||
End Try
|
||||
End Sub)
|
||||
End Sub, "主页自定义刷新")
|
||||
'获取版本校验地址
|
||||
Dim VersionAddress As String
|
||||
If Task.Input.Contains(".xaml") Then
|
||||
VersionAddress = Task.Input.Replace(".xaml", ".xaml.ini")
|
||||
Else
|
||||
VersionAddress = Task.Input.Split("?").First
|
||||
If Not VersionAddress.EndsWith("/") Then VersionAddress += "/"
|
||||
VersionAddress += "version"
|
||||
If Task.Input.Contains("?") Then VersionAddress += Task.Input.Split("?").Last
|
||||
End If
|
||||
'校验版本
|
||||
Dim Version As String = ""
|
||||
Dim NeedDownload As Boolean = True
|
||||
Try
|
||||
Version = NetGetCodeByRequestOnce(VersionAddress, Timeout:=10000)
|
||||
If Version.Length > 100 Then Throw New Exception($"获取的自定义主页版本过长({Version.Length} 字符)")
|
||||
Dim CurrentVersion As String = Setup.Get("CacheSavedPageVersion")
|
||||
If Version <> "" AndAlso CurrentVersion <> "" AndAlso Version = CurrentVersion Then
|
||||
Log($"[Page] 当前缓存的自定义主页已为最新,当前版本:{Version},检查源:{VersionAddress}")
|
||||
NeedDownload = False
|
||||
Else
|
||||
Log($"[Page] 需要下载联网自定义主页,当前版本:{Version},检查源:{VersionAddress}")
|
||||
End If
|
||||
Catch exx As Exception
|
||||
Log(exx, $"联网获取自定义主页版本失败", LogLevel.Developer)
|
||||
Log($"[Page] 无法检查联网自定义主页版本,将直接下载,检查源:{VersionAddress}")
|
||||
End Try
|
||||
'实际下载
|
||||
If NeedDownload Then
|
||||
Dim FileContent As String = NetGetCodeByRequestRetry(Task.Input)
|
||||
Log($"[Page] 已联网下载自定义主页,内容长度:{FileContent.Length},来源:{Task.Input}")
|
||||
Setup.Set("CacheSavedPageUrl", Task.Input)
|
||||
Setup.Set("CacheSavedPageVersion", Version)
|
||||
WriteFile(PathTemp & "Cache\Custom.xaml", FileContent)
|
||||
End If
|
||||
'要求刷新
|
||||
Refresh()
|
||||
Catch ex As Exception
|
||||
Log(ex, "加载 PCL 主页自定义信息失败", LogLevel.Msgbox)
|
||||
IsRefreshing = False
|
||||
If Setup.Get("CacheSavedPageVersion") = "" Then
|
||||
Log(ex, $"联网下载自定义主页失败({Task.Input})", LogLevel.Msgbox)
|
||||
Else
|
||||
Log(ex, $"联网下载自定义主页失败({Task.Input})")
|
||||
End If
|
||||
Task.Input = ""
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
Public Sub ForceRefresh(Optional ShowHint As Boolean = True)
|
||||
ShouldRefresh = True
|
||||
LatestFileContent = ""
|
||||
LatestFileLink = Nothing
|
||||
If ShowHint Then Hint("已刷新主页!", HintType.Finish)
|
||||
FrmMain.PageChange(FormMain.PageType.Launch)
|
||||
Init()
|
||||
''' <summary>
|
||||
''' 立即强制刷新自定义主页。
|
||||
''' 必须在 UI 线程调用。
|
||||
''' </summary>
|
||||
Public Sub ForceRefresh()
|
||||
Log("[Page] 要求强制刷新自定义主页")
|
||||
ClearCache()
|
||||
'实际的刷新
|
||||
If FrmMain.PageCurrent.Page = FormMain.PageType.Launch Then
|
||||
PanBack.ScrollToHome()
|
||||
Refresh()
|
||||
Else
|
||||
FrmMain.PageChange(FormMain.PageType.Launch)
|
||||
End If
|
||||
End Sub
|
||||
|
||||
Public Shared Sub StartMigration()
|
||||
MyMsgBox("在迁移过程中,你可能需要设置你的档案信息,此时请注意让年龄大于 18 岁,否则可能导致无法登录!" & vbCrLf &
|
||||
"在迁移完成后,请在上方选择 微软 登录方式而非 Mojang。", "迁移提示", "继续", ForceWait:=True)
|
||||
OpenWebsite("https://www.minecraft.net/zh-hans/account-security")
|
||||
''' <summary>
|
||||
''' 清空自定义主页缓存信息。
|
||||
''' </summary>
|
||||
Private Sub ClearCache()
|
||||
LoadedContentHash = -1
|
||||
OnlineLoader.Input = ""
|
||||
Setup.Set("CacheSavedPageUrl", "")
|
||||
Setup.Set("CacheSavedPageVersion", "")
|
||||
Log("[Page] 已清空自定义主页缓存")
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 从文本内容中加载自定义主页。
|
||||
''' 必须在 UI 线程调用。
|
||||
''' </summary>
|
||||
Private Sub LoadContent(Content As String)
|
||||
SyncLock LoadContentLock
|
||||
'如果加载目标内容一致则不加载
|
||||
Dim Hash = Content.GetHashCode()
|
||||
If Hash = LoadedContentHash Then Exit Sub
|
||||
LoadedContentHash = Hash
|
||||
'实际加载内容
|
||||
PanCustom.Children.Clear()
|
||||
If String.IsNullOrWhiteSpace(Content) Then
|
||||
Log($"[Page] 实例化:清空自定义主页 UI,来源为空")
|
||||
Return
|
||||
End If
|
||||
Content = "<StackPanel xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" xmlns:local=""clr-namespace:PCL;assembly=Plain Craft Launcher 2"">" & Content & "</StackPanel>"
|
||||
Content = HelpArgumentReplace(Content)
|
||||
Log($"[Page] 实例化:加载自定义主页 UI,最终内容长度:{Content.Count}")
|
||||
Try
|
||||
PanCustom.Children.Add(GetObjectFromXML(Content))
|
||||
Catch ex As Exception
|
||||
Log("[Page] 加载失败的自定义主页内容:" & vbCrLf & Content)
|
||||
If MyMsgBox($"自定义主页内容编写有误,请根据下列错误信息进行检查:{vbCrLf}{ex.Message}", "加载自定义主页失败", "重试", "取消") = 1 Then
|
||||
GoTo Refresh '防止 SyncLock 死锁
|
||||
End If
|
||||
End Try
|
||||
End SyncLock
|
||||
Return
|
||||
Refresh:
|
||||
ForceRefresh()
|
||||
End Sub
|
||||
Private LoadedContentHash As Integer = -1
|
||||
Private LoadContentLock As New Object
|
||||
|
||||
#End Region
|
||||
|
||||
End Class
|
||||
@@ -20,8 +20,8 @@
|
||||
<TextBlock Name="TextName" Text="加载中" Margin="8,0" FontSize="14" HorizontalAlignment="Center" TextTrimming="CharacterEllipsis" />
|
||||
<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.Placement="Center" ToolTipService.VerticalOffset="32" ToolTipService.InitialShowDelay="50" ToolTipService.ShowDuration="1000000" 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.Placement="Center" ToolTipService.VerticalOffset="32" ToolTipService.InitialShowDelay="50" ToolTipService.ShowDuration="1000000" 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="BtnEdit" ToolTipService.Placement="Center" ToolTipService.VerticalOffset="32" ToolTipService.HorizontalOffset="1" 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.Placement="Center" ToolTipService.VerticalOffset="32" ToolTipService.HorizontalOffset="1" 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" />
|
||||
</Grid>
|
||||
<local:MySkin HorizontalAlignment="Center" VerticalAlignment="Top" x:Name="Skin" HasCape="False" />
|
||||
</Grid>
|
||||
@@ -17,7 +17,7 @@ Public Class PageLoginLegacy
|
||||
Public Sub Reload(KeepInput As Boolean)
|
||||
If KeepInput AndAlso IsReloaded Then '避免第一次就以 KeepInput 的方式加载,导致文本框里没东西
|
||||
'保留输入,只刷新下拉框列表
|
||||
Dim Input As String = ComboName.Text
|
||||
Dim Input As String = ComboName.Text.Trim
|
||||
ComboName.ItemsSource = If(Setup.Get("LoginLegacyName") = "", Nothing, Setup.Get("LoginLegacyName").ToString.Split("¨"))
|
||||
ComboName.Text = Input
|
||||
Else
|
||||
@@ -26,7 +26,7 @@ Public Class PageLoginLegacy
|
||||
ComboName.ItemsSource = Nothing
|
||||
Else
|
||||
ComboName.ItemsSource = Setup.Get("LoginLegacyName").ToString.Split("¨")
|
||||
ComboName.Text = Setup.Get("LoginLegacyName").ToString.Split("¨")(0)
|
||||
ComboName.Text = Setup.Get("LoginLegacyName").ToString.Split("¨")(0).Trim
|
||||
End If
|
||||
End If
|
||||
IsReloaded = True
|
||||
@@ -50,17 +50,14 @@ Public Class PageLoginLegacy
|
||||
Return IsVaild(GetLoginData())
|
||||
End Function
|
||||
|
||||
Private Sub ComboName_PreviewKeyDown(sender As Object, e As Input.KeyEventArgs) Handles ComboName.PreviewKeyDown
|
||||
If e.Key = Key.Space Then
|
||||
Dim Index = CType(ComboName.Template.FindName("PART_EditableTextBox", ComboName), MyTextBox).CaretIndex
|
||||
If Index = ComboName.Text.Length OrElse Index = 0 Then e.Handled = True
|
||||
End If
|
||||
End Sub
|
||||
Private Sub ComboLegacy_TextChanged(sender As Object, e As TextChangedEventArgs) Handles ComboName.TextChanged
|
||||
If ComboName.Text.EndsWith(" ") Then
|
||||
ComboName.Text = ComboName.Text.Trim()
|
||||
SendKeys.SendWait("{END}")
|
||||
ElseIf ComboName.Text.StartsWith(" ") Then
|
||||
ComboName.Text = ComboName.Text.Trim()
|
||||
SendKeys.SendWait("{HOME}")
|
||||
End If
|
||||
If Setup.Get("LaunchSkinType") = 0 Then
|
||||
PageLaunchLeft.SkinLegacy.Start(IsForceRestart:=True)
|
||||
End If
|
||||
If Setup.Get("LaunchSkinType") = 0 Then PageLaunchLeft.SkinLegacy.Start(IsForceRestart:=True)
|
||||
HintChinese.Visibility = If(RegexCheck(ComboName.Text, "^[0-9A-Za-z_]*$"), Visibility.Collapsed, Visibility.Visible)
|
||||
End Sub
|
||||
Private Sub Skin_Click() Handles Skin.Click
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
<Grid x:Class="PageLoginMojang" Tag="False"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
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:local="clr-namespace:PCL"
|
||||
mc:Ignorable="d" d:DesignWidth="302.4" Margin="0,0,0,-2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="10" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="224" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<local:MyHint Text="Mojang 账号现已无法进入正版服务器,请点击此处开始账号迁移!" Grid.ColumnSpan="2" Margin="0,0,0,15" x:Name="HintMigration" />
|
||||
<local:MyComboBox Grid.Row="1" Grid.Column="1" IsEditable="True" Height="28" x:Name="ComboName" />
|
||||
<TextBlock Grid.Row="1" Text="邮箱" VerticalAlignment="Center" />
|
||||
<PasswordBox Grid.Row="3" Grid.Column="1" x:Name="TextPass" />
|
||||
<TextBlock Text="密码" Grid.Row="3" VerticalAlignment="Center" />
|
||||
<local:MyCheckBox x:Name="CheckRemember" Tag="LoginRemember" Height="20" Margin="-1,12,80,0" Text="记住密码" Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="2" />
|
||||
<local:MyTextButton Margin="0,12,2,0" x:Name="BtnLink" Content="购买正版" Grid.Row="4" HorizontalAlignment="Right" Grid.Column="1" Padding="20,0,0,0" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" FontSize="13" />
|
||||
</Grid>
|
||||
@@ -1,84 +0,0 @@
|
||||
Public Class PageLoginMojang
|
||||
|
||||
Private IsFirstLoad As Boolean = True
|
||||
''' <summary>
|
||||
''' 刷新页面显示的所有信息。
|
||||
''' </summary>
|
||||
Public Sub Reload(KeepInput As Boolean)
|
||||
'记住密码
|
||||
CheckRemember.Checked = Setup.Get("LoginRemember")
|
||||
If KeepInput AndAlso Not IsFirstLoad Then '避免第一次就以 KeepInput 的方式加载,导致文本框里没东西
|
||||
'保留输入,只刷新下拉框列表
|
||||
Dim Input As String = ComboName.Text
|
||||
ComboName.ItemsSource = If(Setup.Get("LoginMojangEmail") = "", Nothing, Setup.Get("LoginMojangEmail").ToString.Split("¨"))
|
||||
ComboName.Text = Input
|
||||
Else
|
||||
'不保留输入,刷新列表后自动选择第一项
|
||||
If Setup.Get("LoginMojangEmail") = "" Then
|
||||
ComboName.ItemsSource = Nothing
|
||||
Else
|
||||
ComboName.ItemsSource = Setup.Get("LoginMojangEmail").ToString.Split("¨")
|
||||
ComboName.Text = Setup.Get("LoginMojangEmail").ToString.Split("¨")(0)
|
||||
If Setup.Get("LoginRemember") Then TextPass.Password = Setup.Get("LoginMojangPass").ToString.Split("¨")(0).Trim
|
||||
End If
|
||||
End If
|
||||
IsFirstLoad = False
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 获取当前页面的登录信息。
|
||||
''' </summary>
|
||||
Public Shared Function GetLoginData() As McLoginServer
|
||||
If FrmLoginMojang Is Nothing Then
|
||||
Return New McLoginServer(McLoginType.Mojang) With {.BaseUrl = "https://authserver.mojang.com", .Token = "Mojang", .UserName = "", .Password = "", .Type = McLoginType.Mojang, .Description = "Mojang 正版"}
|
||||
Else
|
||||
Return New McLoginServer(McLoginType.Mojang) With {.BaseUrl = "https://authserver.mojang.com", .Token = "Mojang", .UserName = If(FrmLoginMojang.ComboName.Text, "").Replace("¨", "").Trim(), .Password = If(FrmLoginMojang.TextPass.Password, "").Replace("¨", "").Trim(), .Type = McLoginType.Mojang, .Description = "Mojang 正版"}
|
||||
End If
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 当前页面的登录信息是否有效。
|
||||
''' </summary>
|
||||
Public Shared Function IsVaild(LoginData As McLoginServer) As String
|
||||
If LoginData.UserName = "" Then Return "邮箱不能为空!"
|
||||
If Not LoginData.UserName.Contains("@") Then Return "邮箱格式错误!"
|
||||
If LoginData.Password = "" Then Return "密码不能为空!"
|
||||
Return ""
|
||||
End Function
|
||||
Public Function IsVaild() As String
|
||||
Return IsVaild(GetLoginData())
|
||||
End Function
|
||||
|
||||
'保存输入信息
|
||||
Private Sub ComboMojangName_TextChanged(sender As Object, e As TextChangedEventArgs) Handles ComboName.TextChanged
|
||||
If sender.Text = "" Then TextPass.Password = ""
|
||||
If AniControlEnabled = 0 Then Setup.Set("CacheMojangAccess", "") '迫使其不进行 Validate
|
||||
End Sub
|
||||
Private Sub TextMojangPass_PasswordChanged(sender As Object, e As RoutedEventArgs) Handles TextPass.PasswordChanged
|
||||
If AniControlEnabled = 0 Then Setup.Set("CacheMojangAccess", "")
|
||||
End Sub
|
||||
Private Sub ComboMojangName_SelectionChanged(sender As MyComboBox, e As SelectionChangedEventArgs) Handles ComboName.SelectionChanged
|
||||
If sender.SelectedIndex = -1 OrElse Not Setup.Get("LoginRemember") Then
|
||||
TextPass.Password = ""
|
||||
Else
|
||||
TextPass.Password = Setup.Get("LoginMojangPass").ToString.Split("¨")(sender.SelectedIndex).Trim
|
||||
End If
|
||||
End Sub
|
||||
Private Sub CheckRememberChange(sender As MyCheckBox, e As Object) Handles CheckRemember.Change
|
||||
If AniControlEnabled = 0 Then Setup.Set(sender.Tag, sender.Checked)
|
||||
End Sub
|
||||
|
||||
'链接处理
|
||||
Private Sub InputChanged() Handles ComboName.TextChanged, TextPass.PasswordChanged
|
||||
BtnLink.Content = If(ComboName.Text = "" AndAlso TextPass.Password = "", "购买正版", "找回密码")
|
||||
End Sub
|
||||
Private Sub BtnMojang_Click(sender As Object, e As EventArgs) Handles BtnLink.Click
|
||||
If BtnLink.Content = "购买正版" Then
|
||||
OpenWebsite("https://www.minecraft.net/zh-hans/store/checkout/minecraft-java-bedrock-edition-pc")
|
||||
Else
|
||||
OpenWebsite("https://www.minecraft.net/zh-hans/password/forgot")
|
||||
End If
|
||||
End Sub
|
||||
Private Sub HintMigration_Click() Handles HintMigration.MouseLeftButtonUp
|
||||
PageLaunchRight.StartMigration()
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -1,29 +0,0 @@
|
||||
<Grid x:Class="PageLoginMojangSkin" Tag="True"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
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:local="clr-namespace:PCL"
|
||||
mc:Ignorable="d" d:DesignWidth="297.6" Margin="0,19,0,5">
|
||||
<Grid Margin="0,56,0,0" Background="{StaticResource ColorBrushSemiTransparent}" Name="PanData">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="34" />
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="34" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="19" />
|
||||
<RowDefinition Height="Auto" MinHeight="30" />
|
||||
<RowDefinition Height="19" />
|
||||
</Grid.RowDefinitions>
|
||||
<local:MyHint Text="Mojang 账号现已无法进入正版服务器,请点击此处开始账号迁移!" Grid.ColumnSpan="3" Margin="0,0,0,15" x:Name="HintMigration" />
|
||||
<StackPanel Grid.Column="1" Grid.Row="2" VerticalAlignment="Center">
|
||||
<TextBlock Name="TextName" Text="加载中" Margin="8,0" FontSize="14" HorizontalAlignment="Center" TextTrimming="CharacterEllipsis" />
|
||||
<TextBlock Name="TextEmail" Text="加载中" Margin="8,4,8,0" FontSize="12" HorizontalAlignment="Center" TextTrimming="CharacterEllipsis" Foreground="{DynamicResource ColorBrushGray3}" />
|
||||
</StackPanel>
|
||||
<local:MyIconButton ToolTip="更改密码" Grid.Row="2" Height="14" Opacity="0" x:Name="BtnEdit" ToolTipService.Placement="Center" ToolTipService.VerticalOffset="32" ToolTipService.InitialShowDelay="50" ToolTipService.ShowDuration="1000000" 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="2" Height="14" Opacity="0" x:Name="BtnExit" ToolTipService.Placement="Center" ToolTipService.VerticalOffset="32" ToolTipService.InitialShowDelay="50" ToolTipService.ShowDuration="1000000" 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" />
|
||||
</Grid>
|
||||
<local:MySkin HorizontalAlignment="Center" VerticalAlignment="Top" x:Name="Skin" HasCape="False" />
|
||||
</Grid>
|
||||
@@ -1,117 +0,0 @@
|
||||
Public Class PageLoginMojangSkin
|
||||
|
||||
Public Sub New()
|
||||
InitializeComponent()
|
||||
Skin.Loader = PageLaunchLeft.SkinMojang
|
||||
End Sub
|
||||
Private Sub PageLoginLegacy_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
|
||||
Skin.Loader.Start()
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 刷新页面显示的所有信息。
|
||||
''' </summary>
|
||||
Public Sub Reload(KeepInput As Boolean)
|
||||
TextName.Text = Setup.Get("CacheMojangName")
|
||||
TextEmail.Text = Setup.Get("CacheMojangUsername")
|
||||
TextEmail.Visibility = If(Setup.Get("UiLauncherEmail"), Visibility.Collapsed, Visibility.Visible)
|
||||
'皮肤在 Loaded 加载
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 获取当前页面的登录信息。
|
||||
''' </summary>
|
||||
Public Shared Function GetLoginData() As McLoginServer
|
||||
Return New McLoginServer(McLoginType.Mojang) With {.BaseUrl = "https://authserver.mojang.com", .Token = "Mojang", .UserName = Setup.Get("CacheMojangUsername"), .Password = Setup.Get("CacheMojangPass"), .Description = "Mojang 正版", .Type = McLoginType.Mojang}
|
||||
End Function
|
||||
|
||||
Private Sub PageLoginMojangSkin_MouseEnter(sender As Object, e As MouseEventArgs) Handles PanData.MouseEnter
|
||||
AniStart({
|
||||
AaOpacity(BtnEdit, 1 - BtnEdit.Opacity, 80),
|
||||
AaHeight(BtnEdit, 25.5 - BtnEdit.Height, 140,, New AniEaseOutFluent),
|
||||
AaHeight(BtnEdit, -1.5, 50, 140, New AniEaseInFluent),
|
||||
AaOpacity(BtnExit, 1 - BtnExit.Opacity, 80),
|
||||
AaHeight(BtnExit, 25.5 - BtnExit.Height, 140,, New AniEaseOutFluent),
|
||||
AaHeight(BtnExit, -1.5, 50, 140, New AniEaseInFluent)
|
||||
}, "PageLoginMojangSkin Button")
|
||||
End Sub
|
||||
Private Sub PageLoginMojangSkin_MouseLeave(sender As Object, e As MouseEventArgs) Handles PanData.MouseLeave
|
||||
AniStart({
|
||||
AaOpacity(BtnEdit, -BtnEdit.Opacity, 120,, New AniEaseOutFluent),
|
||||
AaHeight(BtnEdit, 14 - BtnEdit.Height, 120,, New AniEaseInFluent),
|
||||
AaOpacity(BtnExit, -BtnExit.Opacity, 120,, New AniEaseOutFluent),
|
||||
AaHeight(BtnExit, 14 - BtnExit.Height, 120,, New AniEaseInFluent)
|
||||
}, "PageLoginMojangSkin Button")
|
||||
End Sub
|
||||
|
||||
Private Sub BtnEdit_Click(sender As Object, e As EventArgs) Handles BtnEdit.Click
|
||||
Select Case MyMsgBox("你希望通过哪种途径更改密码?" & vbCrLf & "首次通过个人档案修改密码可能需要验证你的密保问题。", "更改密码", "个人档案", "密保邮箱", "取消")
|
||||
Case 1
|
||||
'个人档案
|
||||
OpenWebsite("https://www.minecraft.net/zh-hans/msaprofile")
|
||||
Case 2
|
||||
'密保邮箱
|
||||
OpenWebsite("https://account.mojang.com/password")
|
||||
Case 3
|
||||
'取消
|
||||
End Select
|
||||
End Sub
|
||||
Private Sub BtnExit_Click() Handles BtnExit.Click
|
||||
Setup.Set("CacheMojangAccess", "")
|
||||
FrmLaunchLeft.RefreshPage(False, True)
|
||||
End Sub
|
||||
|
||||
Private Sub Skin_Click(sender As Object, e As MouseButtonEventArgs) Handles Skin.Click
|
||||
If McLoginLoader.State = LoadState.Failed Then
|
||||
Hint("登录失败,无法更改皮肤!", HintType.Critical)
|
||||
Exit Sub
|
||||
End If
|
||||
Dim SkinInfo As McSkinInfo = McSkinSelect()
|
||||
If Not SkinInfo.IsVaild Then Exit Sub
|
||||
Hint("正在更改皮肤……")
|
||||
RunInNewThread(Async Sub()
|
||||
Try
|
||||
|
||||
If McLoginLoader.State = LoadState.Loading Then McLoginLoader.WaitForExit() '等待登录结束
|
||||
Dim AccessToken As String = Setup.Get("CacheMojangAccess")
|
||||
Dim Uuid As String = Setup.Get("CacheMojangUuid")
|
||||
|
||||
Dim Client As New Net.Http.HttpClient With {.Timeout = New TimeSpan(0, 0, 10)}
|
||||
Client.DefaultRequestHeaders.Authorization = New Net.Http.Headers.AuthenticationHeaderValue("Bearer", AccessToken)
|
||||
Client.DefaultRequestHeaders.Accept.Add(New Net.Http.Headers.MediaTypeWithQualityHeaderValue("*/*"))
|
||||
Client.DefaultRequestHeaders.UserAgent.Add(New Net.Http.Headers.ProductInfoHeaderValue("MojangSharp", "0.1"))
|
||||
Dim Contents As New Net.Http.MultipartFormDataContent From {
|
||||
{New Net.Http.StringContent(If(SkinInfo.IsSlim, "slim", "")), "model"},
|
||||
{New Net.Http.ByteArrayContent(File.ReadAllBytes(SkinInfo.LocalFile)), "file", GetFileNameFromPath(SkinInfo.LocalFile)}
|
||||
}
|
||||
Dim Result As String = Await (Await Client.PutAsync(New Uri("https://api.mojang.com/user/profile/" & Uuid & "/skin"), Contents)).Content.ReadAsStringAsync
|
||||
If RegexCheck(Result, "^[0-9a-f]{59,64}$") Then
|
||||
MySkin.ReloadCache("http://textures.minecraft.net/texture/" & Result, False)
|
||||
ElseIf Result.ToLower.Contains("ip not secured") Then
|
||||
If MyMsgBox("首次操作需要在官网验证密保问题。" & vbCrLf & "验证完成后,你即可使用 PCL 更改皮肤,且无需再次验证。", "验证密保问题", "开始验证", "取消") = 1 Then
|
||||
OpenWebsite("https://www.minecraft.net/zh-hans/profile/skin")
|
||||
End If
|
||||
ElseIf Result.Contains("request requires user authentication") Then
|
||||
Hint("正在重新登录,请稍后再次尝试更改皮肤!")
|
||||
RunInUi(Sub()
|
||||
BtnExit_Click()
|
||||
If String.IsNullOrEmpty(McLoginAble()) Then McLoginLoader.Start()
|
||||
End Sub)
|
||||
ElseIf Result.Contains("""errorMessage""") Then
|
||||
Hint("更改皮肤失败:" & GetJson(Result)("errorMessage"), HintType.Critical)
|
||||
Else
|
||||
Throw New Exception("未知错误(" & Result & ")")
|
||||
End If
|
||||
Catch ex As Exception
|
||||
If ex.GetType.Equals(GetType(Tasks.TaskCanceledException)) Then
|
||||
Hint("更改皮肤失败:与 Mojang 皮肤服务器的连接超时,请检查你的网络是否通畅!", HintType.Critical)
|
||||
Else
|
||||
Log(ex, "更改皮肤失败", LogLevel.Hint)
|
||||
End If
|
||||
End Try
|
||||
End Sub, "Mojang Skin Upload")
|
||||
End Sub
|
||||
Private Sub HintMigration_Click() Handles HintMigration.MouseLeftButtonUp
|
||||
PageLaunchRight.StartMigration()
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -4,13 +4,33 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:PCL"
|
||||
mc:Ignorable="d" d:DesignWidth="302.4" Margin="0,0,0,-2">
|
||||
<StackPanel x:Name="PanEmpty">
|
||||
<Path Data="M535.94 571.76l291.060 0.36v286.020l-291.060-39.24v-247.14M499.94 535.76v314.64l363.060 48.96v-363.060l-363.060-0.54zM197 571.4l218.52 0.36v226.98l-218.52-30.42v-196.92M161 535.4v264.42l290.52 40.32v-304.2l-290.52-0.54zM827 166.76v285.12l-291.060-0.36v-239.76l291.060-45M863 124.64l-363.060 56.16v306.54l363.060 0.54v-363.24zM415.52 231.2v219.96l-218.52-0.18v-189.36l218.52-30.42M451.52 189.8l-290.52 40.5v256.68l290.52 0.36v-297.54z"
|
||||
Fill="{DynamicResource ColorBrush1}" Opacity="0.7"
|
||||
Width="40" Stretch="Uniform"/>
|
||||
<TextBlock Text=" 无账号信息
点击 添加账号 按钮以登录" HorizontalAlignment="Center" Margin="0,15,0,25"
|
||||
Foreground="{DynamicResource ColorBrush1}" Opacity="0.7" />
|
||||
<local:MyButton Text="添加账号" x:Name="BtnLogin" ColorType="Highlight" Height="35" HorizontalAlignment="Center" Width="150" />
|
||||
</StackPanel>
|
||||
mc:Ignorable="d" d:DesignWidth="302.4">
|
||||
<Grid x:Name="PanEmpty" Margin="0,10,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="25" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Path Data="M660.338 528.065c63.61-46.825 105.131-121.964 105.131-206.83 0-141.7-115.29-256.987-256.997-256.987-141.706 0-256.998 115.288-256.998 256.987 0 85.901 42.52 161.887 107.456 208.562-152.1 59.92-260.185 207.961-260.185 381.077 0 21.276 17.253 38.53 38.53 38.53 21.278 0 38.53-17.254 38.53-38.53 0-183.426 149.232-332.671 332.667-332.671 1.589 0 3.113-0.207 4.694-0.244 0.8 0.056 1.553 0.244 2.362 0.244 183.434 0 332.664 149.245 332.664 332.671 0 21.276 17.255 38.53 38.533 38.53 21.277 0 38.53-17.254 38.53-38.53 0-174.885-110.354-324.13-264.917-382.809z m-331.803-206.83c0-99.22 80.72-179.927 179.935-179.927s179.937 80.708 179.937 179.927c0 99.203-80.721 179.91-179.937 179.91s-179.935-80.708-179.935-179.91z"
|
||||
Fill="{DynamicResource ColorBrush2}" Opacity="0.7" Width="40" Stretch="Uniform" Grid.ColumnSpan="2" />
|
||||
<local:MyComboBox x:Name="ComboAccounts" Grid.Row="2" />
|
||||
<local:MyButton Text="登录" x:Name="BtnLogin" ColorType="Highlight"
|
||||
Height="28" Width="60" Margin="10,0,0,0" Grid.Row="2" Grid.Column="1" />
|
||||
<Grid Grid.Row="4" Grid.ColumnSpan="2" Opacity="0.3" Margin="2,30,2,0" HorizontalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="30" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<local:MyTextButton Text="» 购买正版" EventType="打开网页" EventData="https://www.xbox.com/zh-cn/games/store/minecraft-java-bedrock-edition-for-pc/9nxp44l49shj"
|
||||
Grid.Column="0" />
|
||||
<local:MyTextButton Text="» 前往官网" EventType="打开网页" EventData="https://www.minecraft.net/zh-hans"
|
||||
Grid.Column="2" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
@@ -4,19 +4,57 @@
|
||||
''' 刷新页面显示的所有信息。
|
||||
''' </summary>
|
||||
Public Sub Reload(KeepInput As Boolean)
|
||||
Dim IndexBefore = ComboAccounts.SelectedIndex
|
||||
'刷新下拉框列表
|
||||
ComboAccounts.Items.Clear()
|
||||
ComboAccounts.Items.Add(New MyComboBoxItem With {.Content = "添加新账号"})
|
||||
Try
|
||||
Dim MsJson As JObject = GetJson(Setup.Get("LoginMsJson"))
|
||||
For Each Account In MsJson
|
||||
Dim Item As MyListItem = CType(FindResource("ComboBoxItemTemplateWithDelete"), DataTemplate).LoadContent()
|
||||
Item.Tag = Account.Value.ToString
|
||||
Item.Title = Account.Key
|
||||
CType(Item.Buttons(0), MyIconButton).Tag = Account.Key
|
||||
ComboAccounts.Items.Add(Item)
|
||||
Next
|
||||
Catch ex As Exception
|
||||
Log(ex, $"微软登录信息出错,登录信息已被重置({Setup.Get("LoginMsJson")})", LogLevel.Hint)
|
||||
Setup.Set("LoginMsJson", "{}")
|
||||
End Try
|
||||
'如果不保留输入,刷新列表后自动选择第一项
|
||||
ComboAccounts.SelectedIndex = If(KeepInput, Math.Max(0, IndexBefore), 0)
|
||||
End Sub
|
||||
Private Sub ComboAccounts_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles ComboAccounts.SelectionChanged
|
||||
If AniControlEnabled <> 0 OrElse ComboAccounts.SelectedItem Is Nothing OrElse ComboAccounts.ContentPresenter Is Nothing Then Exit Sub
|
||||
If TypeOf ComboAccounts.SelectedItem Is MyListItem Then
|
||||
ComboAccounts.ContentPresenter.Content = CType(ComboAccounts.SelectedItem, MyListItem).Title
|
||||
ElseIf TypeOf ComboAccounts.SelectedItem Is MyComboBoxItem Then
|
||||
ComboAccounts.ContentPresenter.Content = CType(ComboAccounts.SelectedItem, MyComboBoxItem).Content
|
||||
End If
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 获取当前页面的登录信息。
|
||||
''' </summary>
|
||||
Public Shared Function GetLoginData() As McLoginMs
|
||||
Return New McLoginMs With {.OAuthRefreshToken = Setup.Get("CacheMsOAuthRefresh"), .UserName = Setup.Get("CacheMsName")}
|
||||
If FrmLoginMs Is Nothing Then Return New McLoginMs With {.OAuthRefreshToken = Setup.Get("CacheMsOAuthRefresh"), .UserName = Setup.Get("CacheMsName")}
|
||||
Dim Result As McLoginMs = Nothing
|
||||
RunInUiWait(Sub()
|
||||
If FrmLoginMs.ComboAccounts.SelectedIndex = 0 Then
|
||||
Result = New McLoginMs
|
||||
Else
|
||||
Dim Item As MyListItem = FrmLoginMs.ComboAccounts.SelectedItem
|
||||
Result = New McLoginMs With {.OAuthRefreshToken = Item.Tag, .UserName = Item.Title}
|
||||
End If
|
||||
End Sub)
|
||||
Return Result
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 当前页面的登录信息是否有效。
|
||||
''' </summary>
|
||||
Public Shared Function IsVaild(LoginData As McLoginMs) As String
|
||||
If LoginData.OAuthRefreshToken = "" Then
|
||||
Return "请在登录账号后再继续!"
|
||||
Return "请在登录账号后再启动游戏!"
|
||||
Else
|
||||
Return ""
|
||||
End If
|
||||
@@ -26,13 +64,14 @@
|
||||
End Function
|
||||
|
||||
Private Sub BtnLogin_Click(sender As Object, e As EventArgs) Handles BtnLogin.Click
|
||||
ComboAccounts.IsEnabled = False
|
||||
BtnLogin.IsEnabled = False
|
||||
BtnLogin.Text = "登录中 0%"
|
||||
BtnLogin.Text = "0%"
|
||||
RunInNewThread(Sub()
|
||||
Try
|
||||
McLoginMsLoader.Start(GetLoginData(), IsForceRestart:=True)
|
||||
Do While McLoginMsLoader.State = LoadState.Loading
|
||||
RunInUi(Sub() BtnLogin.Text = "登录中 " & Math.Round(McLoginMsLoader.Progress * 100) & "%")
|
||||
RunInUi(Sub() BtnLogin.Text = Math.Round(McLoginMsLoader.Progress * 100) & "%")
|
||||
Thread.Sleep(50)
|
||||
Loop
|
||||
If McLoginMsLoader.State = LoadState.Finished Then
|
||||
@@ -45,7 +84,7 @@
|
||||
Throw New Exception(McLoginMsLoader.Error.Message, McLoginMsLoader.Error)
|
||||
End If
|
||||
Catch ex As ThreadInterruptedException
|
||||
Hint("登录已取消!")
|
||||
Hint("已取消登录!")
|
||||
Catch ex As Exception
|
||||
If ex.Message = "$$" Then
|
||||
ElseIf ex.Message.StartsWith("$") Then
|
||||
@@ -55,8 +94,9 @@
|
||||
End If
|
||||
Finally
|
||||
RunInUi(Sub()
|
||||
ComboAccounts.IsEnabled = True
|
||||
BtnLogin.IsEnabled = True
|
||||
BtnLogin.Text = "添加账号"
|
||||
BtnLogin.Text = "登录"
|
||||
End Sub)
|
||||
End Try
|
||||
End Sub, "Ms Login")
|
||||
|
||||
@@ -4,21 +4,47 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:PCL"
|
||||
mc:Ignorable="d" d:DesignWidth="297.6" Margin="0,19,0,5">
|
||||
<Grid Margin="0,56,0,0" Background="{StaticResource ColorBrushSemiTransparent}" Name="PanData">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="34" />
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="34" />
|
||||
</Grid.ColumnDefinitions>
|
||||
mc:Ignorable="d" d:DesignWidth="297.6" Margin="0,60,0,5">
|
||||
<Grid Background="{StaticResource ColorBrushSemiTransparent}" Name="PanData">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="19" />
|
||||
<RowDefinition Height="75" />
|
||||
<RowDefinition Height="Auto" MinHeight="30" />
|
||||
<RowDefinition Height="13" />
|
||||
<RowDefinition Height="30" />
|
||||
<RowDefinition Height="19" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Column="1" Grid.Row="1" VerticalAlignment="Center" Name="TextName" Text="加载中" Margin="8,0" FontSize="14" HorizontalAlignment="Center" TextTrimming="CharacterEllipsis" />
|
||||
<local:MyIconButton ToolTip="更改密码" Grid.Row="1" Height="14" Opacity="0" x:Name="BtnEdit" ToolTipService.Placement="Center" ToolTipService.VerticalOffset="32" ToolTipService.InitialShowDelay="50" ToolTipService.ShowDuration="1000000" 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.Placement="Center" ToolTipService.VerticalOffset="32" ToolTipService.InitialShowDelay="50" ToolTipService.ShowDuration="1000000" 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" />
|
||||
<TextBlock Grid.Row="1" VerticalAlignment="Center" Name="TextName" Text="加载中" Margin="8,0" FontSize="17" HorizontalAlignment="Center" TextTrimming="CharacterEllipsis" />
|
||||
<Border Grid.Row="3" HorizontalAlignment="Center" x:Name="PanButtons" Opacity="0"
|
||||
CornerRadius="5" Background="{DynamicResource ColorBrush8}" Padding="10,3,9,3">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<local:MyIconButton Height="24" Margin="0,0.5,9,0" x:Name="BtnSkin"
|
||||
ToolTip="皮肤与披风" ToolTipService.Placement="Center" ToolTipService.VerticalOffset="35" ToolTipService.HorizontalOffset="1" ToolTipService.InitialShowDelay="50"
|
||||
LogoScale="1.08" Logo="M764.0003 0.076794a215.433442 215.433442 0 0 0-76.282279 13.950954l-9.06172 3.686123c-49.148314 20.734445-88.518161 30.53851-115.498538 30.53851-27.031573 0-66.478214-9.855261-116.676049-31.050471a217.583681 217.583681 0 0 0-72.186586-16.766743l-9.138515-0.307177-3.839712-0.051196-8.626553 0.307177-4.223683 0.281579A160.755943 160.755943 0 0 0 250.477214 45.385396l-219.32435 212.438467a102.392321 102.392321 0 0 0-11.263156 134.210734l106.360023 144.552359 3.378947 4.351674a102.469115 102.469115 0 0 0 112.810739 31.690423l5.657176-2.175837V883.210559a140.789441 140.789441 0 0 0 140.789441 140.789441h372.170487l5.657175-0.102392a140.789441 140.789441 0 0 0 135.132266-140.687049l-0.025599-318.6705 1.177512 0.716747a102.392321 102.392321 0 0 0 141.941355-44.233483l70.906682-144.39877a102.392321 102.392321 0 0 0-20.657651-118.647101L875.86391 45.385396A160.730345 160.730345 0 0 0 764.0003 0.076794z m-357.349199 111.454041C468.444867 137.615279 520.562558 150.644702 563.157763 150.644702c42.492813 0 94.584906-12.978227 156.199485-38.985876A112.580356 112.580356 0 0 1 764.0003 102.469115c15.154063 0 29.693773 5.887558 40.598555 16.433967L1023.923206 331.495138l-70.906682 144.39877-93.023423-65.684674a38.39712 38.39712 0 0 0-60.53946 31.383247V883.210559a38.39712 38.39712 0 0 1-38.39712 38.39712H388.886034a38.39712 38.39712 0 0 1-38.397121-38.39712V445.176212a38.39712 38.39712 0 0 0-61.691373-30.487314l-80.070795 61.20501L102.392321 331.341549l219.32435-212.438467a58.363623 58.363623 0 0 1 35.581332-16.229183L362.110442 102.469115l6.322726 0.179186a115.191361 115.191361 0 0 1 38.217933 8.882534z">
|
||||
<local:MyIconButton.ContextMenu>
|
||||
<ContextMenu Closed="HidePanel" HorizontalOffset="10" VerticalOffset="18">
|
||||
<local:MyMenuItem Click="BtnSkinEdit_Click" Header="修改皮肤"/>
|
||||
<local:MyMenuItem Click="BtnSkinSave_Click" Header="保存皮肤"/>
|
||||
<local:MyMenuItem Click="BtnSkinRefresh_Click" Header="刷新头像"/>
|
||||
<Separator />
|
||||
<local:MyMenuItem Click="BtnSkinCape_Click" Header="修改披风"/>
|
||||
</ContextMenu>
|
||||
</local:MyIconButton.ContextMenu>
|
||||
</local:MyIconButton>
|
||||
<local:MyIconButton Height="24" Margin="0,0,9,0" x:Name="BtnEdit"
|
||||
ToolTip="修改信息" ToolTipService.Placement="Center" ToolTipService.VerticalOffset="35" ToolTipService.HorizontalOffset="1" ToolTipService.InitialShowDelay="50"
|
||||
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 Click="BtnEditPassword_Click" Header="修改密码" />
|
||||
<local:MyMenuItem Click="BtnEditName_Click" Header="修改玩家名" />
|
||||
</ContextMenu>
|
||||
</local:MyIconButton.ContextMenu>
|
||||
</local:MyIconButton>
|
||||
<local:MyIconButton Height="24" x:Name="BtnExit" Margin="0,0,0,0"
|
||||
ToolTip="切换账号" ToolTipService.Placement="Center" ToolTipService.VerticalOffset="35" ToolTipService.HorizontalOffset="1" ToolTipService.InitialShowDelay="50"
|
||||
LogoScale="1.15" 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" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
<local:MySkin HorizontalAlignment="Center" VerticalAlignment="Top" x:Name="Skin" HasCape="True" />
|
||||
<local:MySkin HorizontalAlignment="Center" VerticalAlignment="Top" x:Name="Skin" HasCape="True" IsHitTestVisible="False" />
|
||||
</Grid>
|
||||
@@ -26,28 +26,29 @@
|
||||
End If
|
||||
End Function
|
||||
|
||||
Private Sub PageLoginMsSkin_MouseEnter(sender As Object, e As MouseEventArgs) Handles PanData.MouseEnter
|
||||
AniStart({
|
||||
AaOpacity(BtnEdit, 1 - BtnEdit.Opacity, 80),
|
||||
AaHeight(BtnEdit, 25.5 - BtnEdit.Height, 140,, New AniEaseOutFluent),
|
||||
AaHeight(BtnEdit, -1.5, 50, 140, New AniEaseInFluent),
|
||||
AaOpacity(BtnExit, 1 - BtnExit.Opacity, 80),
|
||||
AaHeight(BtnExit, 25.5 - BtnExit.Height, 140,, New AniEaseOutFluent),
|
||||
AaHeight(BtnExit, -1.5, 50, 140, New AniEaseInFluent)
|
||||
}, "PageLoginMsSkin Button")
|
||||
#Region "下边栏其他内容"
|
||||
|
||||
'显示/隐藏控制
|
||||
Private Sub ShowPanel(sender As Object, e As MouseEventArgs) Handles PanData.MouseEnter
|
||||
AniStart(AaOpacity(PanButtons, 1 - PanButtons.Opacity, 120), "PageLoginMsSkin Button")
|
||||
End Sub
|
||||
Private Sub PageLoginMsSkin_MouseLeave(sender As Object, e As MouseEventArgs) Handles PanData.MouseLeave
|
||||
AniStart({
|
||||
AaOpacity(BtnEdit, -BtnEdit.Opacity, 120,, New AniEaseOutFluent),
|
||||
AaHeight(BtnEdit, 14 - BtnEdit.Height, 120,, New AniEaseInFluent),
|
||||
AaOpacity(BtnExit, -BtnExit.Opacity, 120,, New AniEaseOutFluent),
|
||||
AaHeight(BtnExit, 14 - BtnExit.Height, 120,, New AniEaseInFluent)
|
||||
}, "PageLoginMsSkin Button")
|
||||
Public Sub HidePanel() Handles PanData.MouseLeave
|
||||
If BtnEdit.ContextMenu.IsOpen OrElse BtnSkin.ContextMenu.IsOpen OrElse PanData.IsMouseOver Then Exit Sub
|
||||
AniStart(AaOpacity(PanButtons, -PanButtons.Opacity, 120), "PageLoginMsSkin Button")
|
||||
End Sub
|
||||
|
||||
'修改账号信息
|
||||
Private Sub BtnEdit_Click(sender As Object, e As EventArgs) Handles BtnEdit.Click
|
||||
OpenWebsite("https://account.microsoft.com/security")
|
||||
BtnEdit.ContextMenu.IsOpen = True
|
||||
End Sub
|
||||
Public Sub BtnEditPassword_Click(sender As Object, e As RoutedEventArgs)
|
||||
OpenWebsite("https://account.live.com/password/Change")
|
||||
End Sub
|
||||
Public Sub BtnEditName_Click(sender As Object, e As RoutedEventArgs)
|
||||
OpenWebsite("https://www.minecraft.net/zh-hans/msaprofile/mygames/editprofile")
|
||||
End Sub
|
||||
|
||||
'退出登录
|
||||
Private Sub BtnExit_Click() Handles BtnExit.Click
|
||||
Setup.Set("CacheMsOAuthRefresh", "")
|
||||
Setup.Set("CacheMsAccess", "")
|
||||
@@ -58,8 +59,18 @@
|
||||
FrmLaunchLeft.RefreshPage(False, True)
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "皮肤/披风"
|
||||
|
||||
'展开
|
||||
Private Sub BtnSkin_Click(sender As Object, e As RoutedEventArgs) Handles BtnSkin.Click
|
||||
BtnSkin.ContextMenu.IsOpen = True
|
||||
End Sub
|
||||
|
||||
'修改皮肤
|
||||
Private IsChanging As Boolean = False
|
||||
Private Sub Skin_Click(sender As Object, e As MouseButtonEventArgs) Handles Skin.Click
|
||||
Public Sub BtnSkinEdit_Click(sender As Object, e As RoutedEventArgs)
|
||||
'检查条件,获取新皮肤
|
||||
If IsChanging Then
|
||||
Hint("正在更改皮肤中,请稍候!")
|
||||
@@ -74,50 +85,68 @@
|
||||
Hint("正在更改皮肤……")
|
||||
IsChanging = True
|
||||
'开始实际获取
|
||||
RunInNewThread(Async Sub()
|
||||
Try
|
||||
RunInNewThread(
|
||||
Async Sub()
|
||||
Try
|
||||
Retry:
|
||||
If McLoginMsLoader.State = LoadState.Loading Then McLoginMsLoader.WaitForExit() '等待登录结束
|
||||
Dim AccessToken As String = Setup.Get("CacheMsAccess")
|
||||
Dim Uuid As String = Setup.Get("CacheMsUuid")
|
||||
If McLoginMsLoader.State = LoadState.Loading Then McLoginMsLoader.WaitForExit() '等待登录结束
|
||||
Dim AccessToken As String = Setup.Get("CacheMsAccess")
|
||||
Dim Uuid As String = Setup.Get("CacheMsUuid")
|
||||
|
||||
Dim Client As New Net.Http.HttpClient With {.Timeout = New TimeSpan(0, 0, 30)}
|
||||
Client.DefaultRequestHeaders.Authorization = New Net.Http.Headers.AuthenticationHeaderValue("Bearer", AccessToken)
|
||||
Client.DefaultRequestHeaders.Accept.Add(New Net.Http.Headers.MediaTypeWithQualityHeaderValue("*/*"))
|
||||
Client.DefaultRequestHeaders.UserAgent.Add(New Net.Http.Headers.ProductInfoHeaderValue("MojangSharp", "0.1"))
|
||||
Dim Contents As New Net.Http.MultipartFormDataContent From {
|
||||
{New Net.Http.StringContent(If(SkinInfo.IsSlim, "slim", "classic")), "variant"},
|
||||
{New Net.Http.ByteArrayContent(File.ReadAllBytes(SkinInfo.LocalFile)), "file", GetFileNameFromPath(SkinInfo.LocalFile)}
|
||||
}
|
||||
Dim Result As String = Await (Await Client.PostAsync(New Uri("https://api.minecraftservices.com/minecraft/profile/skins"), Contents)).Content.ReadAsStringAsync
|
||||
If Result.Contains("request requires user authentication") Then
|
||||
Hint("正在登录,将在登录完成后继续更改皮肤……")
|
||||
McLoginMsLoader.Start(GetLoginData(), IsForceRestart:=True)
|
||||
GoTo Retry
|
||||
ElseIf Result.Contains("""error""") Then
|
||||
Hint("更改皮肤失败:" & GetJson(Result)("error"), HintType.Critical)
|
||||
Exit Sub
|
||||
End If
|
||||
'获取新皮肤地址
|
||||
Log("[Skin] 皮肤修改返回值:" & vbCrLf & Result)
|
||||
Dim ResultJson As JObject = GetJson(Result)
|
||||
For Each Skin As JObject In ResultJson("skins")
|
||||
If Skin("state").ToString = "ACTIVE" Then
|
||||
MySkin.ReloadCache(Skin("url"), True)
|
||||
Exit Sub
|
||||
End If
|
||||
Next
|
||||
Throw New Exception("未知错误(" & Result & ")")
|
||||
Catch ex As Exception
|
||||
If ex.GetType.Equals(GetType(Tasks.TaskCanceledException)) Then
|
||||
Hint("更改皮肤失败:与 Mojang 皮肤服务器的连接超时,请检查你的网络是否通畅!", HintType.Critical)
|
||||
Else
|
||||
Log(ex, "更改皮肤失败", LogLevel.Hint)
|
||||
End If
|
||||
Finally
|
||||
IsChanging = False
|
||||
End Try
|
||||
End Sub, "Ms Skin Upload")
|
||||
Dim Client As New Net.Http.HttpClient With {.Timeout = New TimeSpan(0, 0, 30)}
|
||||
Client.DefaultRequestHeaders.Authorization = New Net.Http.Headers.AuthenticationHeaderValue("Bearer", AccessToken)
|
||||
Client.DefaultRequestHeaders.Accept.Add(New Net.Http.Headers.MediaTypeWithQualityHeaderValue("*/*"))
|
||||
Client.DefaultRequestHeaders.UserAgent.Add(New Net.Http.Headers.ProductInfoHeaderValue("MojangSharp", "0.1"))
|
||||
Dim Contents As New Net.Http.MultipartFormDataContent From {
|
||||
{New Net.Http.StringContent(If(SkinInfo.IsSlim, "slim", "classic")), "variant"},
|
||||
{New Net.Http.ByteArrayContent(File.ReadAllBytes(SkinInfo.LocalFile)), "file", GetFileNameFromPath(SkinInfo.LocalFile)}
|
||||
}
|
||||
Dim Result As String = Await (Await Client.PostAsync(New Uri("https://api.minecraftservices.com/minecraft/profile/skins"), Contents)).Content.ReadAsStringAsync
|
||||
If Result.Contains("request requires user authentication") Then
|
||||
Hint("正在登录,将在登录完成后继续更改皮肤……")
|
||||
McLoginMsLoader.Start(GetLoginData(), IsForceRestart:=True)
|
||||
GoTo Retry
|
||||
ElseIf Result.Contains("""error""") Then
|
||||
Hint("更改皮肤失败:" & GetJson(Result)("error"), HintType.Critical)
|
||||
Exit Sub
|
||||
End If
|
||||
'获取新皮肤地址
|
||||
Log("[Skin] 皮肤修改返回值:" & vbCrLf & Result)
|
||||
Dim ResultJson As JObject = GetJson(Result)
|
||||
For Each Skin As JObject In ResultJson("skins")
|
||||
If Skin("state").ToString = "ACTIVE" Then
|
||||
MySkin.ReloadCache(Skin("url"))
|
||||
Exit Sub
|
||||
End If
|
||||
Next
|
||||
Throw New Exception("未知错误(" & Result & ")")
|
||||
Catch ex As Exception
|
||||
If ex.GetType.Equals(GetType(Tasks.TaskCanceledException)) Then
|
||||
Hint("更改皮肤失败:与 Mojang 皮肤服务器的连接超时,请检查你的网络是否通畅!", HintType.Critical)
|
||||
Else
|
||||
Log(ex, "更改皮肤失败", LogLevel.Hint)
|
||||
End If
|
||||
Finally
|
||||
IsChanging = False
|
||||
End Try
|
||||
End Sub, "Ms Skin Upload")
|
||||
End Sub
|
||||
|
||||
'保存皮肤
|
||||
Public Sub BtnSkinSave_Click(sender As Object, e As RoutedEventArgs)
|
||||
Skin.BtnSkinSave_Click()
|
||||
End Sub
|
||||
|
||||
'刷新头像
|
||||
Public Sub BtnSkinRefresh_Click(sender As Object, e As RoutedEventArgs)
|
||||
Skin.RefreshClick()
|
||||
End Sub
|
||||
|
||||
'修改披风
|
||||
Public Sub BtnSkinCape_Click(sender As Object, e As RoutedEventArgs)
|
||||
Skin.BtnSkinCape_Click()
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
End Class
|
||||
@@ -20,8 +20,8 @@
|
||||
<TextBlock Name="TextName" Text="加载中" Margin="8,0" FontSize="14" HorizontalAlignment="Center" TextTrimming="CharacterEllipsis" />
|
||||
<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.Placement="Center" ToolTipService.VerticalOffset="32" ToolTipService.InitialShowDelay="50" ToolTipService.ShowDuration="1000000" 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.Placement="Center" ToolTipService.VerticalOffset="32" ToolTipService.InitialShowDelay="50" ToolTipService.ShowDuration="1000000" 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="BtnEdit" ToolTipService.Placement="Center" ToolTipService.VerticalOffset="32" ToolTipService.HorizontalOffset="1" 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.Placement="Center" ToolTipService.VerticalOffset="32" ToolTipService.HorizontalOffset="1" 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" />
|
||||
</Grid>
|
||||
<local:MySkin HorizontalAlignment="Center" VerticalAlignment="Top" x:Name="Skin" HasCape="False" />
|
||||
</Grid>
|
||||
@@ -1,9 +1,9 @@
|
||||
<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"
|
||||
mc:Ignorable="d" x:Class="PageLinkFeedback"
|
||||
d:DesignWidth="800" d:DesignHeight="800">
|
||||
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"
|
||||
mc:Ignorable="d" x:Class="PageLinkFeedback"
|
||||
PanScroll="{Binding ElementName=PanBack}">
|
||||
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
|
||||
<StackPanel Name="PanMain" Margin="25,25,25,10" Grid.IsSharedSizeScope="True">
|
||||
<local:MyCard Title="关于" Margin="0,0,0,15">
|
||||
@@ -14,23 +14,23 @@
|
||||
Margin="0,0,0,6" />
|
||||
<local:MyListItem Title="速聚官网" Info="打开速聚的官方网站"
|
||||
Type="Clickable" EventType="打开网页" EventData="https://www.the.bb/zh-CN.html"
|
||||
PaddingRight="20" Logo="pack://application:,,,/images/Blocks/Grass.png" />
|
||||
Logo="pack://application:,,,/images/Blocks/Grass.png" />
|
||||
<local:MyListItem Title="授权协议书" Info="查看速聚与 PCL 间的授权协议书"
|
||||
Type="Clickable" EventType="打开网页" EventData="https://shimo.im/docs/0l3NVXYzeXhb903R"
|
||||
PaddingRight="20" Logo="pack://application:,,,/images/Blocks/Grass.png" />
|
||||
Logo="pack://application:,,,/images/Blocks/Grass.png" />
|
||||
<local:MyListItem Title="用户协议与隐私政策" Info="查看速聚的用户协议与隐私政策文档"
|
||||
Type="Clickable" EventType="打开网页" EventData="https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzkxMTMyODk3Mg==&action=getalbum&album_id=2585385685407514625&scene=173&from_msgid=2247483720&from_itemidx=1&count=3&nolastread=1#wechat_redirect"
|
||||
PaddingRight="20" Logo="pack://application:,,,/images/Blocks/Grass.png" />
|
||||
Logo="pack://application:,,,/images/Blocks/Grass.png" />
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
<local:MyCard Title="发送反馈" Margin="0,0,0,15">
|
||||
<StackPanel Margin="20,40,18,18">
|
||||
<local:MyListItem Title="联机反馈与交流" Info="加入联机模块官方 QQ 群反馈联机异常,或询问联机功能的使用方式"
|
||||
Type="Clickable" PaddingRight="20" Logo="pack://application:,,,/images/Blocks/RedstoneBlock.png"
|
||||
Type="Clickable" Logo="pack://application:,,,/images/Blocks/RedstoneBlock.png"
|
||||
EventType="打开网页" EventData="https://shimo.im/docs/KrkEVXz1K0frowAJ#anchor-7qQG" />
|
||||
<local:MyListItem Title="违法违规举报" Info="投诉、举报联机使用者的各类违法违规行为"
|
||||
Type="Clickable" EventType="打开网页" EventData="https://shimo.im/docs/rGrd8pY8xWkt6ryW#anchor-7x6P"
|
||||
PaddingRight="20" Logo="pack://application:,,,/images/Blocks/RedstoneBlock.png" />
|
||||
Logo="pack://application:,,,/images/Blocks/RedstoneBlock.png" />
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
</StackPanel>
|
||||
|
||||
Loaded 100 of 138 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user