Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
efd480f06c | ||
|
|
a067271995 | ||
|
|
f85361d009 | ||
|
|
4a5cc3f95b | ||
|
|
4c0f525cf3 | ||
|
|
8d2eb85aec | ||
|
|
e5dccf3a13 | ||
|
|
8ba86ffed8 | ||
|
|
76f4e761b1 | ||
|
|
e503a39baf | ||
|
|
5ed4a9bbbf | ||
|
|
382deaa197 | ||
|
|
00966bec6a | ||
|
|
a41579b1da | ||
|
|
86ed0e9ae0 | ||
|
|
ab697054c8 | ||
|
|
0ba2fd1edb | ||
|
|
49984a2913 | ||
|
|
6f6744dfd2 | ||
|
|
2210da6fda | ||
|
|
08e5e64ca4 | ||
|
|
ee385912f8 | ||
|
|
47c3c163ed | ||
|
|
45b6582f30 | ||
|
|
6f9262ef4f | ||
|
|
46fd4c1e8d | ||
|
|
c3d79e1f18 | ||
|
|
f63e9a896c | ||
|
|
9c9d98084a | ||
|
|
cfbde4ad1c | ||
|
|
46b96d6408 | ||
|
|
6f3bad680c | ||
|
|
ad5207470d | ||
|
|
b936608ec7 | ||
|
|
dc7f01dfdd | ||
|
|
3528bf5a3d | ||
|
|
f211df1ea2 | ||
|
|
9a3df4adec | ||
|
|
b09750ffff | ||
|
|
2f74640de5 | ||
|
|
78a5536bc7 | ||
|
|
54c45ff54b | ||
|
|
fe39495f2e | ||
|
|
e2c24a7d44 | ||
|
|
37b069c572 | ||
|
|
5622e50a54 | ||
|
|
86d1d387a5 | ||
|
|
7f2f7df05f | ||
|
|
b285ffa401 | ||
|
|
8e095ddaed | ||
|
|
afc08cc243 | ||
|
|
bac349a54c | ||
|
|
02c766ac08 | ||
|
|
273cee69e5 | ||
|
|
adf52d56b7 | ||
|
|
1d0947181f | ||
|
|
f67ef9c2c5 | ||
|
|
28640dcdf5 | ||
|
|
88e87011af | ||
|
|
c22f925807 | ||
|
|
5785ffdfb3 | ||
|
|
59857c3391 | ||
|
|
c90d96e65d | ||
|
|
838cb3c842 | ||
|
|
d133138984 | ||
|
|
128b8001ff | ||
|
|
c7e0dd3816 | ||
|
|
8e0f53c881 | ||
|
|
68483876f3 | ||
|
|
10a5a30968 | ||
|
|
fdc56a2a4b | ||
|
|
490626a293 | ||
|
|
ce91fe6245 | ||
|
|
5a841de3ba | ||
|
|
fc910b1955 | ||
|
|
340e7457f8 | ||
|
|
b76266a69d | ||
|
|
5fc0693892 | ||
|
|
73d9eed9a9 | ||
|
|
28ef67ee0f | ||
|
|
add4153067 | ||
|
|
3e474b89ef | ||
|
|
b5cfebe8aa | ||
|
|
4486e7ea2c | ||
|
|
4d91d0b27e | ||
|
|
14e14b9810 | ||
|
|
d6575df6ab | ||
|
|
eed6c92f5a | ||
|
|
ae8de397ae | ||
|
|
2de22b780c | ||
|
|
4d954ae45d | ||
|
|
3d63ceba57 | ||
|
|
8b234d8e4f | ||
|
|
d6a5a8b83e | ||
|
|
885b3eada6 | ||
|
|
deb902904b | ||
|
|
1458ce95ce | ||
|
|
d8efb76bf0 | ||
|
|
a843b22f38 | ||
|
|
08e4414f34 | ||
|
|
ff225e9f37 | ||
|
|
96b6ef294b | ||
|
|
f271d7212c | ||
|
|
ad583c29c2 | ||
|
|
f98dcd8fef | ||
|
|
9b49235cda | ||
|
|
f090a63c54 | ||
|
|
c332a4dc94 | ||
|
|
6770e55214 | ||
|
|
dd028bb6d8 | ||
|
|
5126b257f8 | ||
|
|
35398052bb | ||
|
|
17031e1a65 | ||
|
|
5aad129121 | ||
|
|
e40a41a761 | ||
|
|
5ae813825c | ||
|
|
48684f8f85 | ||
|
|
3f5392d76d | ||
|
|
9af03d499a | ||
|
|
065b69b346 | ||
|
|
c708aca3fb | ||
|
|
e188f7392c | ||
|
|
d6043c7d32 | ||
|
|
a38e20870b | ||
|
|
72a2b5da8c | ||
|
|
f3017b6b62 | ||
|
|
5274ae8499 | ||
|
|
83e34b84fa | ||
|
|
65812b8754 | ||
|
|
6616afde6d | ||
|
|
7cd5747f4f | ||
|
|
70ec0b66f9 | ||
|
|
74e5eb82f5 | ||
|
|
fdf5407ea3 | ||
|
|
5e31a1d937 | ||
|
|
81619cbfc7 | ||
|
|
300d1ecf66 | ||
|
|
6dbb5c98cc | ||
|
|
da8a50abd3 | ||
|
|
7ecf306445 | ||
|
|
3adf47aa2c | ||
|
|
70a2f28073 | ||
|
|
9c9216c02c | ||
|
|
f2ae0fbaa9 | ||
|
|
36c03d1654 | ||
|
|
ebd20195be | ||
|
|
5a94854809 | ||
|
|
d2e210320b | ||
|
|
73d705aa5b | ||
|
|
9a0ad08498 | ||
|
|
443d306d31 | ||
|
|
acaff10bf0 | ||
|
|
f3680f0b78 | ||
|
|
74657d54c7 | ||
|
|
cd736be212 | ||
|
|
52ddb97938 | ||
|
|
31f0b81652 | ||
|
|
ca729067c1 | ||
|
|
855ff55495 | ||
|
|
a174c4856a |
No files matched your search
@@ -12,7 +12,7 @@ body:
|
||||
required: false
|
||||
- label: "**我已尝试使用 HMCL 启动,HMCL 没有出现问题。** 如果 HMCL 也无法启动就不是 PCL 导致的问题,请 **不要** 提交反馈。[下载 HMCL](https://hmcl.huangyuhui.net/download)"
|
||||
required: true
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一 Bug 未被提交过。"
|
||||
- label: "我已在 [Issues 页面](https://github.com/Meloong-Git/PCL/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Meloong-Git/PCL/discussions/1930) 中搜索,确认了这一 Bug 未被提交过。"
|
||||
required: true
|
||||
- type: textarea
|
||||
id: "yml-2"
|
||||
|
||||
@@ -12,7 +12,7 @@ body:
|
||||
required: false
|
||||
- label: "我知晓大多数此类问题都是网络环境不佳导致的,但我确实认为我的问题可能是 PCL 导致的,和网络环境无关。"
|
||||
required: true
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一 Bug 未被提交过。"
|
||||
- label: "我已在 [Issues 页面](https://github.com/Meloong-Git/PCL/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Meloong-Git/PCL/discussions/1930) 中搜索,确认了这一 Bug 未被提交过。"
|
||||
required: true
|
||||
- type: textarea
|
||||
id: "yml-2"
|
||||
|
||||
@@ -8,7 +8,7 @@ body:
|
||||
label: "检查项"
|
||||
description: "请逐个检查下列项目,并勾选确认。"
|
||||
options:
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一 Bug 未被提交过。"
|
||||
- label: "我已在 [Issues 页面](https://github.com/Meloong-Git/PCL/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Meloong-Git/PCL/discussions/1930) 中搜索,确认了这一 Bug 未被提交过。"
|
||||
required: true
|
||||
- type: textarea
|
||||
id: "yml-2"
|
||||
|
||||
@@ -8,9 +8,9 @@ body:
|
||||
label: "检查项"
|
||||
description: "请逐个检查下列项目,并勾选确认。"
|
||||
options:
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一建议未被提交过。"
|
||||
- label: "我已在 [Issues 页面](https://github.com/Meloong-Git/PCL/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Meloong-Git/PCL/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/),确认了这一建议未在投票列表中。"
|
||||
- label: "我已查看 [功能投票页面](https://github.com/Meloong-Git/PCL/discussions/categories/%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8/),确认了这一建议未在投票列表中。"
|
||||
required: true
|
||||
- type: textarea
|
||||
id: "yml-2"
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 主页预设反馈
|
||||
url: https://github.com/Hex-Dragon/PCL2/discussions/categories/自定义主页
|
||||
url: https://github.com/Meloong-Git/PCL/discussions/categories/自定义主页
|
||||
about: 提交与预设的主页(设置 → 个性化 → 主页预设)中的具体内容相关的反馈
|
||||
- name: 帮助文档反馈
|
||||
url: https://github.com/LTCatt/PCL2Help/issues
|
||||
about: 提交与 PCL 帮助文档(更多 → 帮助)中的具体内容相关的反馈
|
||||
- name: 提问
|
||||
url: https://github.com/Hex-Dragon/PCL2/discussions/new?category=%E6%8F%90%E9%97%AE
|
||||
url: https://github.com/Meloong-Git/PCL/discussions/new?category=%E6%8F%90%E9%97%AE
|
||||
about: 我想问一些 PCL 相关的问题……
|
||||
- name: 讨论
|
||||
url: https://github.com/Hex-Dragon/PCL2/discussions/new?category=%E8%AE%A8%E8%AE%BA
|
||||
url: https://github.com/Meloong-Git/PCL/discussions/new?category=%E8%AE%A8%E8%AE%BA
|
||||
about: 我想讨论一些 PCL 相关的事情……
|
||||
@@ -8,9 +8,9 @@ body:
|
||||
label: "检查项"
|
||||
description: "请逐个检查下列项目,并勾选确认。"
|
||||
options:
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一提案未被提交过。"
|
||||
- label: "我已在 [Issues 页面](https://github.com/Meloong-Git/PCL/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Meloong-Git/PCL/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/),确认了这一提案未在投票列表中。"
|
||||
- label: "我已查看 [功能投票页面](https://github.com/Meloong-Git/PCL/discussions/categories/%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8/),确认了这一提案未在投票列表中。"
|
||||
required: true
|
||||
- label: "我知晓还没做的新功能真的太多了,忙不过来,所以新功能提案几乎不会被处理,也不建议再提交新功能提案 qwq……"
|
||||
required: true
|
||||
|
||||
@@ -38,7 +38,6 @@ jobs:
|
||||
|
||||
- name: Update Help
|
||||
run: |
|
||||
$workpath = [System.Environment]::CurrentDirectory
|
||||
Set-Location ..
|
||||
git clone https://github.com/LTCatt/PCL2Help.git -b master --single-branch --depth 1
|
||||
Set-Location PCL2Help
|
||||
@@ -52,8 +51,8 @@ jobs:
|
||||
|
||||
- name: Replace
|
||||
run: |
|
||||
(gc "Plain Craft Launcher 2\Modules\ModSecret.vb") -replace 'Public Const OAuthClientId As String = ""', 'Public Const OAuthClientId As String = "${{ secrets.CLIENT_ID }}"' | Out-File "Plain Craft Launcher 2\Modules\ModSecret.vb"
|
||||
(gc "Plain Craft Launcher 2\Modules\ModSecret.vb") -replace 'Public Const CurseForgeAPIKey As String = ""', 'Public Const CurseForgeAPIKey As String = "${{ secrets.CURSEFORGE_API_KEY }}"' | Out-File "Plain Craft Launcher 2\Modules\ModSecret.vb"
|
||||
(gc "Plain Craft Launcher 2\Modules\ModSecret.vb") -replace 'Public OAuthClientId As String = If(Environment.GetEnvironmentVariable("PCL_MS_CLIENT_ID"), "")', 'Public OAuthClientId As String = If(Environment.GetEnvironmentVariable("PCL_MS_CLIENT_ID"), "${{ secrets.CLIENT_ID }}")' | Out-File "Plain Craft Launcher 2\Modules\ModSecret.vb"
|
||||
(gc "Plain Craft Launcher 2\Modules\ModSecret.vb") -replace 'Public CurseForgeAPIKey As String = If(Environment.GetEnvironmentVariable("PCL_CURSEFORGE_API_KEY"), "")', 'Public CurseForgeAPIKey As String = If(Environment.GetEnvironmentVariable("PCL_CURSEFORGE_API_KEY"), "${{ secrets.CURSEFORGE_API_KEY }}")' | Out-File "Plain Craft Launcher 2\Modules\ModSecret.vb"
|
||||
(gc "Plain Craft Launcher 2\Modules\Base\ModBase.vb") -replace 'Public Const CommitHash As String = ""', 'Public Const CommitHash As String = "${{ github.sha }}"' | Out-File "Plain Craft Launcher 2\Modules\Base\ModBase.vb"
|
||||
|
||||
- name: Build
|
||||
|
||||
@@ -44,6 +44,6 @@
|
||||
这些规则主要是为了阻止恶意的使用和 “山寨版” 的出现,常规、善意的使用都没啥问题的,放心吧!
|
||||
|
||||
如果你只是参考了一小段代码,署个名就行啦,不用担心。
|
||||
如果对具体细节有疑问,欢迎在 https://github.com/Hex-Dragon/PCL2/discussions/new?category=%E6%8F%90%E9%97%AE 发帖询问!
|
||||
如果对具体细节有疑问,欢迎在 https://github.com/Meloong-Git/PCL/discussions/new?category=%E6%8F%90%E9%97%AE 发帖询问!
|
||||
|
||||
多谢大家啦!
|
||||
@@ -30,6 +30,10 @@
|
||||
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0"/>
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="13.0.0.0"/>
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
@@ -1,6 +1,7 @@
|
||||
<Application x:Class="Application"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:behaviors="clr-namespace:PCL.Controls.Behaviors"
|
||||
xmlns:local="clr-namespace:PCL"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
StartupUri="FormMain.xaml"
|
||||
@@ -302,6 +303,7 @@
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="behaviors:ClipboardInterceptor.EnableSafeClipboard" Value="True" />
|
||||
</Style>
|
||||
<Style TargetType="PasswordBox">
|
||||
<Setter Property="FontSize" Value="13" />
|
||||
@@ -343,6 +345,7 @@
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="behaviors:ClipboardInterceptor.EnableSafeClipboard" Value="True" />
|
||||
</Style>
|
||||
|
||||
<!-- ComboBox -->
|
||||
|
||||
@@ -19,6 +19,9 @@ Public Class Application
|
||||
'开始
|
||||
Private Sub Application_Startup(sender As Object, e As StartupEventArgs) Handles Me.Startup
|
||||
Try
|
||||
'动态 DLL 调用(必须尽量在前面,否则模块加载 CacheCow 等 DLL 就可能导致崩溃)
|
||||
AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf AssemblyResolve
|
||||
'开始
|
||||
SecretOnApplicationStart()
|
||||
'检查参数调用
|
||||
If e.Args.Length > 0 Then
|
||||
@@ -67,15 +70,14 @@ Public Class Application
|
||||
If Not CheckPermission(PathTemp) Then Throw New Exception("PCL 没有对 " & PathTemp & " 的访问权限")
|
||||
Catch ex As Exception
|
||||
If PathTemp = IO.Path.GetTempPath() & "PCL\" Then
|
||||
MyMsgBox("PCL 无法访问缓存文件夹,可能导致程序出错或无法正常使用!" & vbCrLf & "错误原因:" & GetExceptionDetail(ex), "缓存文件夹不可用")
|
||||
MyMsgBox("PCL 无法访问缓存文件夹,可能导致程序出错或无法正常使用!" & vbCrLf & vbCrLf & "错误原因:" & ex.GetDetail(), "缓存文件夹不可用")
|
||||
Else
|
||||
MyMsgBox("手动设置的缓存文件夹不可用,PCL 将使用默认缓存文件夹。" & vbCrLf & "错误原因:" & GetExceptionDetail(ex), "缓存文件夹不可用")
|
||||
MyMsgBox("手动设置的缓存文件夹不可用,PCL 将使用默认缓存文件夹。" & vbCrLf & vbCrLf & "错误原因:" & ex.GetDetail(), "缓存文件夹不可用")
|
||||
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
|
||||
@@ -104,28 +106,34 @@ WaitRetry:
|
||||
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))
|
||||
'设置网络配置默认值
|
||||
ServicePointManager.Expect100Continue = False
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 Or SecurityProtocolType.Tls Or SecurityProtocolType.Tls11 Or SecurityProtocolType.Tls12
|
||||
ServicePointManager.DefaultConnectionLimit = 10000
|
||||
ServicePointManager.UseNagleAlgorithm = False
|
||||
ServicePointManager.EnableDnsRoundRobin = True
|
||||
ServicePointManager.ReusePort = True
|
||||
'设置初始窗口
|
||||
If Setup.Get("UiLauncherLogo") Then
|
||||
FrmStart = New SplashScreen("Images\icon.ico")
|
||||
FrmStart.Show(False, True)
|
||||
End If
|
||||
'动态 DLL 调用
|
||||
AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf AssemblyResolve
|
||||
'日志初始化
|
||||
LogStart()
|
||||
'添加日志
|
||||
Log($"[Start] 程序版本:{VersionDisplayName} ({VersionCode}{If(CommitHash = "", "", $",#{CommitHash}")})")
|
||||
#If RELEASE Then
|
||||
Log($"[Start] 识别码:{UniqueAddress}{If(ThemeCheckOne(9), ",正式版", "")}")
|
||||
Log($"[Start] 识别码:{Identify}{If(ThemeCheckOne(9), ",正式版", "")}")
|
||||
#Else
|
||||
Log($"[Start] 识别码:{UniqueAddress}{If(ThemeCheckOne(9), ",已解锁反馈主题", "")}")
|
||||
Log($"[Start] 识别码:{Identify}{If(ThemeCheckOne(9), ",已解锁反馈主题", "")}")
|
||||
#End If
|
||||
Log($"[Start] 程序路径:{PathWithName}")
|
||||
Log($"[Start] 系统编码:{Encoding.Default.HeaderName} ({Encoding.Default.CodePage}, GBK={IsGBKEncoding})")
|
||||
Log($"[Start] 管理员权限:{IsAdmin()}")
|
||||
'检测异常环境
|
||||
If Path.Contains(IO.Path.GetTempPath()) OrElse Path.Contains("AppData\Local\Temp\") Then
|
||||
MyMsgBox("请将 PCL 从压缩包中解压之后再使用!" & vbCrLf & "在当前环境下运行可能会导致丢失游戏存档或设置,部分功能也可能无法使用!", "环境警告", "我知道了", IsWarn:=True)
|
||||
MyMsgBox("请将 PCL 从压缩包中解压后再使用!" & vbCrLf & "如果不会解压,可以在网上寻找教程。", "尚未解压", "我知道了", IsWarn:=True)
|
||||
FormMain.EndProgramForce(ProcessReturnValues.Cancel)
|
||||
End If
|
||||
If Is32BitSystem Then
|
||||
MyMsgBox("PCL 和新版 Minecraft 均不再支持 32 位系统,部分功能将无法使用。" & vbCrLf & "非常建议重装为 64 位系统后再进行游戏!", "环境警告", "我知道了", IsWarn:=True)
|
||||
@@ -136,10 +144,6 @@ WaitRetry:
|
||||
Setup.Load("ToolDownloadThread")
|
||||
Setup.Load("ToolDownloadCert")
|
||||
Setup.Load("ToolDownloadSpeed")
|
||||
'网络配置初始化
|
||||
ServicePointManager.Expect100Continue = True
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 Or SecurityProtocolType.Tls Or SecurityProtocolType.Tls11 Or SecurityProtocolType.Tls12
|
||||
ServicePointManager.DefaultConnectionLimit = 1024
|
||||
'计时
|
||||
Log("[Start] 第一阶段加载用时:" & GetTimeTick() - ApplicationStartTick & " ms")
|
||||
ApplicationStartTick = GetTimeTick()
|
||||
@@ -154,7 +158,7 @@ WaitRetry:
|
||||
FilePath = PathWithName
|
||||
Catch
|
||||
End Try
|
||||
MsgBox(GetExceptionDetail(ex, True) & vbCrLf & "PCL 所在路径:" & If(String.IsNullOrEmpty(FilePath), "获取失败", FilePath), MsgBoxStyle.Critical, "PCL 初始化错误")
|
||||
MsgBox(ex.GetDetail(True) & vbCrLf & "PCL 所在路径:" & If(String.IsNullOrEmpty(FilePath), "获取失败", FilePath), MsgBoxStyle.Critical, "PCL 初始化错误")
|
||||
FormMain.EndProgramForce(ProcessReturnValues.Exception)
|
||||
End Try
|
||||
End Sub
|
||||
@@ -167,10 +171,20 @@ WaitRetry:
|
||||
'异常
|
||||
Private Sub Application_DispatcherUnhandledException(sender As Object, e As DispatcherUnhandledExceptionEventArgs) Handles Me.DispatcherUnhandledException
|
||||
On Error Resume Next
|
||||
'触发页面的 Dispatcher
|
||||
If FrmMain?.PageLeft IsNot Nothing AndAlso TypeOf FrmMain.PageLeft Is IDispatcherUnhandledException Then
|
||||
CType(FrmMain.PageLeft, IDispatcherUnhandledException).DispatcherUnhandledException(sender, e)
|
||||
If e.Handled Then Return
|
||||
End If
|
||||
If FrmMain?.PageRight IsNot Nothing AndAlso TypeOf FrmMain.PageRight Is IDispatcherUnhandledException Then
|
||||
CType(FrmMain.PageRight, IDispatcherUnhandledException).DispatcherUnhandledException(sender, e)
|
||||
If e.Handled Then Return
|
||||
End If
|
||||
'正常处理
|
||||
e.Handled = True
|
||||
If IsProgramEnded Then Return
|
||||
FeedbackInfo()
|
||||
Dim Detail As String = GetExceptionDetail(e.Exception, True)
|
||||
Dim Detail As String = e.Exception.GetDetail(True)
|
||||
If Detail.Contains("System.Windows.Threading.Dispatcher.Invoke") OrElse Detail.Contains("MS.Internal.AppModel.ITaskbarList.HrInit") OrElse Detail.Contains("未能加载文件或程序集") OrElse
|
||||
Detail.Contains(".NET Framework") Then ' “自动错误判断” 的结果分析
|
||||
OpenWebsite("https://dotnet.microsoft.com/zh-cn/download/dotnet-framework/thank-you/net462-offline-installer")
|
||||
@@ -181,53 +195,29 @@ WaitRetry:
|
||||
End Sub
|
||||
|
||||
'动态 DLL 调用
|
||||
Private Shared AssemblyNAudio As Assembly
|
||||
Private Shared AssemblyJson As Assembly
|
||||
Private Shared AssemblyDialog As Assembly
|
||||
Private Shared AssemblyImazenWebp As Assembly
|
||||
Private Shared ReadOnly AssemblyNAudioLock As New Object
|
||||
Private Shared ReadOnly AssemblyJsonLock As New Object
|
||||
Private Shared ReadOnly AssemblyDialogLock As New Object
|
||||
Private Shared ReadOnly AssemblyImazenWebpLock As New Object
|
||||
Private Declare Function SetDllDirectory Lib "kernel32" Alias "SetDllDirectoryA" (lpPathName As String) As Boolean
|
||||
Public Shared Function AssemblyResolve(sender As Object, args As ResolveEventArgs) As Assembly
|
||||
If args.Name.StartsWithF("NAudio") Then
|
||||
SyncLock AssemblyNAudioLock
|
||||
If AssemblyNAudio Is Nothing Then
|
||||
Log("[Start] 加载 DLL:NAudio")
|
||||
AssemblyNAudio = Assembly.Load(GetResources("NAudio"))
|
||||
End If
|
||||
Return AssemblyNAudio
|
||||
End SyncLock
|
||||
ElseIf args.Name.StartsWithF("Newtonsoft.Json") Then
|
||||
SyncLock AssemblyJsonLock
|
||||
If AssemblyJson Is Nothing Then
|
||||
Log("[Start] 加载 DLL:Json")
|
||||
AssemblyJson = Assembly.Load(GetResources("Json"))
|
||||
End If
|
||||
Return AssemblyJson
|
||||
End SyncLock
|
||||
ElseIf args.Name.StartsWithF("Ookii.Dialogs.Wpf") Then
|
||||
SyncLock AssemblyDialogLock
|
||||
If AssemblyDialog Is Nothing Then
|
||||
Log("[Start] 加载 DLL:Dialogs")
|
||||
AssemblyDialog = Assembly.Load(GetResources("Dialogs"))
|
||||
End If
|
||||
Return AssemblyDialog
|
||||
End SyncLock
|
||||
ElseIf args.Name.StartsWithF("Imazen.WebP") Then
|
||||
SyncLock AssemblyImazenWebpLock
|
||||
If AssemblyImazenWebp Is Nothing Then
|
||||
Log("[Start] 加载 DLL:Imazen.WebP")
|
||||
AssemblyImazenWebp = Assembly.Load(GetResources("Imazen_WebP"))
|
||||
SetDllDirectory(PathPure.TrimEnd("\"))
|
||||
Public Shared Function AssemblyResolve(sender As Object, Args As ResolveEventArgs) As Assembly
|
||||
'缓存
|
||||
Static Prefixes As String() = {"NAudio", "Newtonsoft.Json", "Ookii.Dialogs.Wpf", "Imazen.WebP", "CacheCow.Common", "CacheCow.Client.FileStore", "CacheCow.Client", "System.Net.Http.Formatting"}
|
||||
Static Locks As New Dictionary(Of String, Object)(StringComparer.Ordinal)
|
||||
Static LoadedAssembly As New Dictionary(Of String, Assembly)(StringComparer.Ordinal)
|
||||
'查找对应的 DLL
|
||||
Dim Prefix As String = Prefixes.FirstOrDefault(Function(p) Args.Name.StartsWithF(p))
|
||||
If Prefix Is Nothing Then Return Nothing
|
||||
'加载 DLL
|
||||
If Not Locks.ContainsKey(Prefix) Then Locks(Prefix) = New Object()
|
||||
SyncLock Locks(Prefix)
|
||||
If Not LoadedAssembly.ContainsKey(Prefix) Then
|
||||
Log($"[Start] 加载 DLL:{Prefix}")
|
||||
LoadedAssembly(Prefix) = Assembly.Load(GetResources(Prefix))
|
||||
'WebP 特判
|
||||
If Prefix = "Imazen.WebP" Then
|
||||
SetDllDirectory(PathPure.TrimEnd("\"c))
|
||||
WriteFile(PathPure & "libwebp.dll", GetResources("libwebp64"))
|
||||
End If
|
||||
Return AssemblyImazenWebp
|
||||
End SyncLock
|
||||
Else
|
||||
Return Nothing
|
||||
End If
|
||||
End If
|
||||
Return LoadedAssembly(Prefix)
|
||||
End SyncLock
|
||||
End Function
|
||||
|
||||
'切换窗口
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
' Author: uye (owner of the MaaAssistantArknights team)
|
||||
' Original Source: MaaAssistantArknights project - https://github.com/MaaAssistantArknights/MaaAssistantArknights
|
||||
' License: Apache License 2.0 (this file only)
|
||||
'
|
||||
' This file is originally developed for MaaAssistantArknights and licensed under AGPL v3.0.
|
||||
' As the original author and copyright holder,
|
||||
' I hereby re-license this file under the Apache License 2.0 for use in the Plain Craft Launcher project.
|
||||
'
|
||||
' This file is contributed under the spirit of good faith and open cooperation.
|
||||
' It does not implement any core launcher functions of PCL.
|
||||
'
|
||||
' Description:
|
||||
' Provides a WPF clipboard handling fix to avoid OpenClipboard exceptions
|
||||
' in TextBox, RichTextBox, and DataGrid under Windows focus issues or clipboard hooks.
|
||||
'
|
||||
' Date: 2025-07-03
|
||||
|
||||
Imports System.Windows
|
||||
Imports System.Windows.Controls
|
||||
Imports System.Windows.Input
|
||||
Imports System.Windows.Documents
|
||||
Imports System.Linq
|
||||
|
||||
Namespace Controls.Behaviors
|
||||
Public NotInheritable Class ClipboardInterceptor
|
||||
Private Sub New()
|
||||
End Sub
|
||||
|
||||
Public Shared ReadOnly EnableSafeClipboardProperty As DependencyProperty =
|
||||
DependencyProperty.RegisterAttached("EnableSafeClipboard", GetType(Boolean), GetType(ClipboardInterceptor),
|
||||
New PropertyMetadata(False, AddressOf OnEnableSafeClipboardChanged))
|
||||
|
||||
Public Shared Sub SetEnableSafeClipboard(element As DependencyObject, value As Boolean)
|
||||
element.SetValue(EnableSafeClipboardProperty, value)
|
||||
End Sub
|
||||
|
||||
Public Shared Function GetEnableSafeClipboard(element As DependencyObject) As Boolean
|
||||
Return CBool(element.GetValue(EnableSafeClipboardProperty))
|
||||
End Function
|
||||
|
||||
Private Shared Sub OnEnableSafeClipboardChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
|
||||
If TypeOf d Is TextBox AndAlso CBool(e.NewValue) Then
|
||||
AddCommandBindingsToTextBox(DirectCast(d, TextBox))
|
||||
ElseIf TypeOf d Is RichTextBox AndAlso CBool(e.NewValue) Then
|
||||
AddCommandBindingsToRichTextBox(DirectCast(d, RichTextBox))
|
||||
ElseIf TypeOf d Is DataGrid AndAlso CBool(e.NewValue) Then
|
||||
AddCommandBindingsToDataGrid(DirectCast(d, DataGrid))
|
||||
End If
|
||||
End Sub
|
||||
|
||||
Private Shared Sub AddCommandBindingsToTextBox(tb As TextBox)
|
||||
tb.CommandBindings.Add(New CommandBinding(ApplicationCommands.Copy, AddressOf OnCopyTextBox))
|
||||
tb.CommandBindings.Add(New CommandBinding(ApplicationCommands.Cut, AddressOf OnCutTextBox))
|
||||
tb.CommandBindings.Add(New CommandBinding(ApplicationCommands.Paste, AddressOf OnPasteTextBox))
|
||||
End Sub
|
||||
|
||||
Private Shared Sub AddCommandBindingsToRichTextBox(rtb As RichTextBox)
|
||||
rtb.CommandBindings.Add(New CommandBinding(ApplicationCommands.Copy, AddressOf OnCopyRichTextBox))
|
||||
rtb.CommandBindings.Add(New CommandBinding(ApplicationCommands.Cut, AddressOf OnCutRichTextBox))
|
||||
rtb.CommandBindings.Add(New CommandBinding(ApplicationCommands.Paste, AddressOf OnPasteRichTextBox))
|
||||
End Sub
|
||||
|
||||
Private Shared Sub AddCommandBindingsToDataGrid(dg As DataGrid)
|
||||
dg.CommandBindings.Add(New CommandBinding(ApplicationCommands.Copy, AddressOf OnCopyDataGrid))
|
||||
End Sub
|
||||
|
||||
Private Shared Sub OnCopyTextBox(sender As Object, e As ExecutedRoutedEventArgs)
|
||||
Dim tb = TryCast(sender, TextBox)
|
||||
If tb Is Nothing OrElse tb.SelectionLength <= 0 Then Return
|
||||
|
||||
Try
|
||||
Forms.Clipboard.Clear()
|
||||
Forms.Clipboard.SetDataObject(tb.SelectedText, True)
|
||||
Catch
|
||||
End Try
|
||||
|
||||
e.Handled = True
|
||||
End Sub
|
||||
|
||||
Private Shared Sub OnCutTextBox(sender As Object, e As ExecutedRoutedEventArgs)
|
||||
Dim tb = TryCast(sender, TextBox)
|
||||
If tb Is Nothing OrElse tb.SelectionLength <= 0 Then Return
|
||||
|
||||
Try
|
||||
Forms.Clipboard.Clear()
|
||||
Forms.Clipboard.SetDataObject(tb.SelectedText, True)
|
||||
Catch
|
||||
End Try
|
||||
|
||||
tb.SelectedText = String.Empty
|
||||
e.Handled = True
|
||||
End Sub
|
||||
|
||||
Private Shared Sub OnPasteTextBox(sender As Object, e As ExecutedRoutedEventArgs)
|
||||
Dim tb = TryCast(sender, TextBox)
|
||||
If tb Is Nothing Then Return
|
||||
|
||||
If Forms.Clipboard.ContainsText() Then
|
||||
Dim pasteText = Forms.Clipboard.GetText()
|
||||
Dim start = tb.SelectionStart
|
||||
|
||||
tb.SelectedText = pasteText
|
||||
tb.CaretIndex = start + pasteText.Length
|
||||
tb.SelectionLength = 0
|
||||
End If
|
||||
|
||||
e.Handled = True
|
||||
End Sub
|
||||
|
||||
Private Shared Sub OnCopyRichTextBox(sender As Object, e As ExecutedRoutedEventArgs)
|
||||
Dim rtb = TryCast(sender, RichTextBox)
|
||||
If rtb Is Nothing Then Return
|
||||
|
||||
Dim textRange = New TextRange(rtb.Selection.Start, rtb.Selection.End)
|
||||
If String.IsNullOrEmpty(textRange.Text) Then Return
|
||||
|
||||
Try
|
||||
Forms.Clipboard.Clear()
|
||||
Forms.Clipboard.SetDataObject(textRange.Text, True)
|
||||
Catch
|
||||
End Try
|
||||
|
||||
e.Handled = True
|
||||
End Sub
|
||||
|
||||
Private Shared Sub OnCutRichTextBox(sender As Object, e As ExecutedRoutedEventArgs)
|
||||
Dim rtb = TryCast(sender, RichTextBox)
|
||||
If rtb Is Nothing Then Return
|
||||
|
||||
Dim selection = New TextRange(rtb.Selection.Start, rtb.Selection.End)
|
||||
If String.IsNullOrEmpty(selection.Text) Then Return
|
||||
|
||||
Try
|
||||
Forms.Clipboard.Clear()
|
||||
Forms.Clipboard.SetDataObject(selection.Text, True)
|
||||
Catch
|
||||
End Try
|
||||
|
||||
selection.Text = String.Empty
|
||||
e.Handled = True
|
||||
End Sub
|
||||
|
||||
Private Shared Sub OnPasteRichTextBox(sender As Object, e As ExecutedRoutedEventArgs)
|
||||
Dim rtb = TryCast(sender, RichTextBox)
|
||||
If rtb Is Nothing Then Return
|
||||
|
||||
If Not Forms.Clipboard.ContainsText() Then Return
|
||||
|
||||
Dim pasteText = Forms.Clipboard.GetText()
|
||||
Dim selection = rtb.Selection
|
||||
|
||||
selection.Text = pasteText
|
||||
|
||||
Dim caretPos = selection.End
|
||||
rtb.CaretPosition = caretPos
|
||||
rtb.Selection.Select(caretPos, caretPos)
|
||||
|
||||
e.Handled = True
|
||||
End Sub
|
||||
|
||||
Private Shared Sub OnCopyDataGrid(sender As Object, e As ExecutedRoutedEventArgs)
|
||||
Dim dg = TryCast(sender, DataGrid)
|
||||
If dg Is Nothing OrElse dg.SelectedCells Is Nothing OrElse dg.SelectedCells.Count = 0 Then Return
|
||||
|
||||
Dim sb = New System.Text.StringBuilder()
|
||||
Dim rowGroups = dg.SelectedCells.GroupBy(Function(c) c.Item)
|
||||
|
||||
For Each row In rowGroups
|
||||
Dim rowText = String.Join(vbTab, row.Select(Function(cell)
|
||||
Dim tb = TryCast(cell.Column.GetCellContent(cell.Item), TextBlock)
|
||||
Return If(tb IsNot Nothing, tb.Text, "")
|
||||
End Function))
|
||||
sb.AppendLine(rowText)
|
||||
Next
|
||||
|
||||
Dim sbStr = sb.ToString().TrimEnd(ControlChars.Cr, ControlChars.Lf)
|
||||
|
||||
Try
|
||||
Forms.Clipboard.Clear()
|
||||
Forms.Clipboard.SetDataObject(sbStr, True)
|
||||
Catch
|
||||
End Try
|
||||
|
||||
e.Handled = True
|
||||
End Sub
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -0,0 +1,23 @@
|
||||
Imports System.Windows.Threading
|
||||
|
||||
''' <summary>
|
||||
''' 指示页面可以通过按下 F5 或以其他方式刷新。
|
||||
''' </summary>
|
||||
Public Interface IRefreshable
|
||||
Sub Refresh()
|
||||
End Interface
|
||||
|
||||
''' <summary>
|
||||
''' 指示页面可以处理未捕获的异常。
|
||||
''' </summary>
|
||||
Public Interface IDispatcherUnhandledException
|
||||
Sub DispatcherUnhandledException(sender As Object, e As DispatcherUnhandledExceptionEventArgs)
|
||||
End Interface
|
||||
|
||||
''' <summary>
|
||||
''' 指示该控件是可勾选的。
|
||||
''' </summary>
|
||||
Public Interface IMyRadio
|
||||
Event Check(sender As Object, e As RouteEventArgs)
|
||||
Event Changed(sender As Object, e As RouteEventArgs)
|
||||
End Interface
|
||||
@@ -1,4 +0,0 @@
|
||||
Public Interface IMyRadio
|
||||
Event Check(sender As Object, e As RouteEventArgs)
|
||||
Event Changed(sender As Object, e As RouteEventArgs)
|
||||
End Interface
|
||||
@@ -140,39 +140,14 @@ Public Class MyButton
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
'实现自定义事件
|
||||
'鼠标点击判定(务必放在点击事件之后,以使得 Button_MouseUp 先于 Button_MouseLeave 执行)
|
||||
Private IsMouseDown As Boolean = False
|
||||
Private Sub Button_MouseUp(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonUp
|
||||
If Not IsMouseDown Then Return
|
||||
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
|
||||
End If
|
||||
ModEvent.TryStartEvent(EventType, EventData)
|
||||
RaiseCustomEvent() '自定义事件
|
||||
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(MyButton), 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(MyButton), New PropertyMetadata(Nothing))
|
||||
|
||||
'鼠标点击判定(务必放在点击事件之后,以使得 Button_MouseUp 先于 Button_MouseLeave 执行)
|
||||
Private IsMouseDown As Boolean = False
|
||||
Private Sub Button_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonDown
|
||||
IsMouseDown = True
|
||||
Focus()
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
MainGrid.Children.Add(MainSwap)
|
||||
End If
|
||||
'改变默认的折叠
|
||||
If IsSwaped AndAlso SwapControl IsNot Nothing Then
|
||||
If IsSwapped AndAlso SwapControl IsNot Nothing Then
|
||||
MainSwap.RenderTransform = New RotateTransform(If(SwapLogoRight, 270, 0))
|
||||
'取消由于高度变化被迫触发的高度动画
|
||||
Dim RawUseAnimation As Boolean = UseAnimation
|
||||
@@ -123,14 +123,6 @@
|
||||
End Select
|
||||
'控件转换
|
||||
Select Case Type
|
||||
Case 5
|
||||
Dim LoadingPickaxe As New MyLoading With {.Text = "正在获取版本列表", .Margin = New Thickness(5)}
|
||||
Dim Loader = New LoaderTask(Of String, List(Of DlForgeVersionEntry))("DlForgeVersion Main", AddressOf DlForgeVersionMain)
|
||||
LoadingPickaxe.State = Loader
|
||||
Loader.Start(Stack.Tag)
|
||||
AddHandler LoadingPickaxe.StateChanged, AddressOf FrmDownloadForge.Forge_StateChanged
|
||||
AddHandler LoadingPickaxe.Click, AddressOf FrmDownloadForge.Forge_Click
|
||||
Stack.Children.Add(LoadingPickaxe)
|
||||
Case 6
|
||||
ForgeDownloadListItemPreload(Stack, Stack.Tag, AddressOf ForgeSave_Click, True)
|
||||
Case 8
|
||||
@@ -140,14 +132,13 @@
|
||||
For Each Data As Object In Stack.Tag
|
||||
Select Case Type
|
||||
Case 0
|
||||
Stack.Children.Add(PageSelectRight.McVersionListItem(Data))
|
||||
Dim VersionItem As MyListItem = CType(Data, McVersion).ToListItem
|
||||
VersionItem.ContentHandler = AddressOf PageSelectRight.McVersionListContent
|
||||
Stack.Children.Add(VersionItem)
|
||||
Case 2
|
||||
Stack.Children.Add(McDownloadListItem(Data, AddressOf McDownloadMenuSave, True))
|
||||
Case 3
|
||||
Stack.Children.Add(OptiFineDownloadListItem(Data, AddressOf OptiFineSave_Click, True))
|
||||
Case 4
|
||||
Stack.Children.Add(LiteLoaderDownloadListItem(Data, AddressOf FrmDownloadLiteLoader.DownloadStart, False))
|
||||
Case 5
|
||||
Case 6
|
||||
Stack.Children.Add(ForgeDownloadListItem(Data, AddressOf ForgeSave_Click, True))
|
||||
Case 7
|
||||
@@ -224,7 +215,7 @@
|
||||
Private ActualUsedHeight As Double '回滚实际高度(例如 NaN)
|
||||
Private Sub MySizeChanged(sender As Object, e As SizeChangedEventArgs) Handles Me.SizeChanged
|
||||
If Not UseAnimation Then Return
|
||||
Dim DeltaHeight As Double = If(IsSwaped, SwapedHeight, e.NewSize.Height) - e.PreviousSize.Height
|
||||
Dim DeltaHeight As Double = If(IsSwapped, SwapedHeight, e.NewSize.Height) - e.PreviousSize.Height
|
||||
'卡片的进入时动画已被页面通用切换动画替代
|
||||
If e.PreviousSize.Height = 0 OrElse IsHeightAnimating OrElse Math.Abs(DeltaHeight) < 1 OrElse ActualHeight = 0 Then Return
|
||||
StartHeightAnimation(DeltaHeight, e.PreviousSize.Height, False)
|
||||
@@ -269,18 +260,18 @@
|
||||
Sub()
|
||||
IsHeightAnimating = False
|
||||
Height = ActualUsedHeight
|
||||
If IsSwaped AndAlso SwapControl IsNot Nothing Then SwapControl.Visibility = Visibility.Collapsed
|
||||
If IsSwapped AndAlso SwapControl IsNot Nothing Then SwapControl.Visibility = Visibility.Collapsed
|
||||
End Sub,, True))
|
||||
AniStart(AnimList, "MyCard Height " & Uuid)
|
||||
IsHeightAnimating = True
|
||||
ActualUsedHeight = If(IsSwaped, SwapedHeight, Height)
|
||||
ActualUsedHeight = If(IsSwapped, SwapedHeight, Height)
|
||||
Height = PreviousHeight
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 通知 MyCard,控件内容已改变,需要中断动画并更新高度。
|
||||
''' </summary>
|
||||
Public Sub TriggerForceResize()
|
||||
Height = If(IsSwaped, SwapedHeight, Double.NaN)
|
||||
Height = If(IsSwapped, SwapedHeight, Double.NaN)
|
||||
AniStop("MyCard Height " & Uuid)
|
||||
IsHeightAnimating = False
|
||||
End Sub
|
||||
@@ -300,59 +291,78 @@
|
||||
''' <summary>
|
||||
''' 是否已被折叠。
|
||||
''' </summary>
|
||||
Public Property IsSwaped As Boolean
|
||||
Public Property IsSwapped As Boolean
|
||||
Get
|
||||
Return _IsSwaped
|
||||
Return _IsSwapped
|
||||
End Get
|
||||
Set(value As Boolean)
|
||||
If _IsSwaped = value Then Return
|
||||
_IsSwaped = value
|
||||
If _IsSwapped = value Then Return
|
||||
_IsSwapped = value
|
||||
If SwapControl Is Nothing Then Return
|
||||
'展开
|
||||
If Not IsSwaped AndAlso TypeOf SwapControl Is StackPanel Then StackInstall(SwapControl, SwapType, Title)
|
||||
If Not IsSwapped AndAlso TypeOf SwapControl Is StackPanel Then StackInstall(SwapControl, SwapType, Title)
|
||||
'若尚未加载,会在 Loaded 事件中触发无动画的折叠,不需要在这里进行
|
||||
If Not IsLoaded Then Return
|
||||
'更新高度
|
||||
SwapControl.Visibility = Visibility.Visible
|
||||
TriggerForceResize()
|
||||
'改变箭头
|
||||
AniStart(AaRotateTransform(MainSwap, If(_IsSwaped, If(SwapLogoRight, 270, 0), 180) - CType(MainSwap.RenderTransform, RotateTransform).Angle, 250,, New AniEaseOutFluent(AniEasePower.ExtraStrong)), "MyCard Swap " & Uuid, True)
|
||||
AniStart(AaRotateTransform(MainSwap, If(_IsSwapped, If(SwapLogoRight, 270, 0), 180) - CType(MainSwap.RenderTransform, RotateTransform).Angle, 250,, New AniEaseOutFluent(AniEasePower.ExtraStrong)), "MyCard Swap " & Uuid, True)
|
||||
End Set
|
||||
End Property
|
||||
Private _IsSwaped As Boolean = False
|
||||
Private _IsSwapped As Boolean = False
|
||||
<Obsolete("IsSwaped 存在拼写错误,请换用 IsSwapped 属性。")>
|
||||
Public Property IsSwaped As Boolean
|
||||
Get
|
||||
Return GetValue(IsSwapedProperty)
|
||||
End Get
|
||||
Set(value As Boolean)
|
||||
SetValue(IsSwapedProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
Public Shared ReadOnly IsSwapedProperty As DependencyProperty = DependencyProperty.Register("IsSwaped", GetType(Boolean), GetType(MyCard), New PropertyMetadata(False,
|
||||
Sub(sender, e) If sender IsNot Nothing AndAlso TypeOf sender Is MyCard Then CType(sender, MyCard).IsSwapped = CType(e.NewValue, Boolean)))
|
||||
|
||||
Public Property SwapLogoRight As Boolean = False
|
||||
Private IsMouseDown As Boolean = False
|
||||
Private IsSwapMouseDown As Boolean = False '用于触发卡片展开/折叠的 MouseDown
|
||||
Private IsCustomMouseDown As Boolean = False '用于触发自定义事件的 MouseDown
|
||||
Public Event PreviewSwap(sender As Object, e As RouteEventArgs)
|
||||
Public Event Swap(sender As Object, e As RouteEventArgs)
|
||||
Public Const SwapedHeight As Integer = 40
|
||||
Private Sub MyCard_MouseLeftButtonDown(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonDown
|
||||
Dim Pos As Double = Mouse.GetPosition(Me).Y
|
||||
If Not IsSwaped AndAlso
|
||||
(SwapControl Is Nothing OrElse Pos > If(IsSwaped, SwapedHeight, SwapedHeight - 6) OrElse (Pos = 0 AndAlso Not IsMouseDirectlyOver)) Then Return '检测点击位置;或已经不在可视树上的误判
|
||||
IsMouseDown = True
|
||||
If Not IsSwapped AndAlso
|
||||
(Pos > If(IsSwapped, SwapedHeight, SwapedHeight - 6) OrElse (Pos = 0 AndAlso Not IsMouseDirectlyOver)) Then Return
|
||||
IsCustomMouseDown = True
|
||||
If Not IsSwapped AndAlso
|
||||
(SwapControl Is Nothing OrElse Pos > If(IsSwapped, SwapedHeight, SwapedHeight - 6) OrElse (Pos = 0 AndAlso Not IsMouseDirectlyOver)) Then Return '检测点击位置;或已经不在可视树上的误判
|
||||
IsSwapMouseDown = True
|
||||
End Sub
|
||||
Private Sub MyCard_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonUp
|
||||
If Not IsMouseDown Then Return
|
||||
IsMouseDown = False
|
||||
Private Sub MyCard_MouseLeftButtonUp() Handles Me.MouseLeftButtonUp
|
||||
If Not IsCustomMouseDown Then Return
|
||||
IsCustomMouseDown = False
|
||||
RaiseCustomEvent() '触发自定义事件
|
||||
|
||||
If Not IsSwapMouseDown Then Return
|
||||
IsSwapMouseDown = False
|
||||
|
||||
Dim Pos As Double = Mouse.GetPosition(Me).Y
|
||||
If Not IsSwaped AndAlso
|
||||
(SwapControl Is Nothing OrElse Pos > If(IsSwaped, SwapedHeight, SwapedHeight - 6) OrElse (Pos = 0 AndAlso Not IsMouseDirectlyOver)) Then Return '检测点击位置;或已经不在可视树上的误判
|
||||
If Not IsSwapped AndAlso
|
||||
(SwapControl Is Nothing OrElse Pos > If(IsSwapped, SwapedHeight, SwapedHeight - 6) OrElse (Pos = 0 AndAlso Not IsMouseDirectlyOver)) Then Return '检测点击位置;或已经不在可视树上的误判
|
||||
|
||||
Dim ee = New RouteEventArgs(True)
|
||||
RaiseEvent PreviewSwap(Me, ee)
|
||||
If ee.Handled Then
|
||||
IsMouseDown = False
|
||||
Dim e = New RouteEventArgs(True)
|
||||
RaiseEvent PreviewSwap(Me, e)
|
||||
If e.Handled Then
|
||||
IsSwapMouseDown = False
|
||||
Return
|
||||
End If
|
||||
|
||||
IsSwaped = Not IsSwaped
|
||||
Log("[Control] " & If(IsSwaped, "折叠卡片", "展开卡片") & If(Title Is Nothing, "", ":" & Title))
|
||||
RaiseEvent Swap(Me, ee)
|
||||
IsSwapped = Not IsSwapped
|
||||
Log("[Control] " & If(IsSwapped, "折叠卡片", "展开卡片") & If(Title Is Nothing, "", ":" & Title))
|
||||
RaiseEvent Swap(Me, e)
|
||||
End Sub
|
||||
Private Sub MyCard_MouseLeave_Swap(sender As Object, e As MouseEventArgs) Handles Me.MouseLeave
|
||||
IsMouseDown = False
|
||||
IsSwapMouseDown = False
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
@@ -12,9 +12,6 @@ Public Class MyCheckBox
|
||||
''' <param name="user">是否为用户手动改变的勾选状态。</param>
|
||||
Public Event Change(sender As Object, user As Boolean)
|
||||
Public Event PreviewChange(sender As Object, e As RouteEventArgs)
|
||||
Public Sub RaiseChange()
|
||||
RaiseEvent Change(Me, False)
|
||||
End Sub '使外部程序引发本控件的 Change 事件
|
||||
|
||||
'自定义属性
|
||||
Public Property Checked As Boolean
|
||||
@@ -59,6 +56,7 @@ Public Class MyCheckBox
|
||||
|
||||
'更改动画
|
||||
SyncUI()
|
||||
RaiseCustomEvent()
|
||||
Catch ex As Exception
|
||||
Log(ex, "设置 Checked 失败")
|
||||
End Try
|
||||
|
||||
@@ -117,15 +117,14 @@
|
||||
Private IsTextChanging As Boolean = False
|
||||
Private Sub MyComboBox_TextChanged(sender As Object, e As TextChangedEventArgs) Handles Me.TextChanged
|
||||
If IsTextChanging OrElse Not IsEditable Then Return
|
||||
If SelectedItem IsNot Nothing AndAlso Text <> SelectedItem.ToString Then
|
||||
Dim RawText As String = Text
|
||||
Dim RawSelectionStart As Integer = TextBox.SelectionStart
|
||||
IsTextChanging = True
|
||||
SelectedItem = Nothing
|
||||
Text = RawText
|
||||
TextBox.SelectionStart = RawSelectionStart
|
||||
IsTextChanging = False
|
||||
End If
|
||||
If SelectedItem Is Nothing OrElse Text = SelectedItem.ToString Then Return
|
||||
Dim RawText As String = Text
|
||||
Dim RawSelectionStart As Integer = TextBox.SelectionStart
|
||||
IsTextChanging = True
|
||||
SelectedItem = Nothing
|
||||
Text = RawText
|
||||
TextBox.SelectionStart = RawSelectionStart
|
||||
IsTextChanging = False
|
||||
End Sub
|
||||
|
||||
Public ReadOnly Property ContentPresenter As ContentPresenter
|
||||
@@ -134,4 +133,8 @@
|
||||
End Get
|
||||
End Property
|
||||
|
||||
Private Sub MyComboBox_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles Me.SelectionChanged
|
||||
If IsLoaded AndAlso AniControlEnabled = 0 Then RaiseCustomEvent()
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -53,25 +53,26 @@
|
||||
Set(value As Boolean)
|
||||
If _Show = value Then Return
|
||||
_Show = value
|
||||
RunInUi(Sub()
|
||||
If value Then
|
||||
'有了
|
||||
Visibility = Visibility.Visible
|
||||
AniStart({
|
||||
AaScaleTransform(Me, 0.3 - CType(RenderTransform, ScaleTransform).ScaleX, 500, 60, New AniEaseOutFluent(AniEasePower.Weak)),
|
||||
AaScaleTransform(Me, 0.7, 500, 60, New AniEaseOutBack(AniEasePower.Weak)),
|
||||
AaHeight(Me, 50 - Height, 200,, New AniEaseOutFluent(AniEasePower.Weak))
|
||||
}, "MyExtraButton MainScale " & Uuid)
|
||||
Else
|
||||
'没了
|
||||
AniStart({
|
||||
AaScaleTransform(Me, -CType(RenderTransform, ScaleTransform).ScaleX, 100,, New AniEaseInFluent(AniEasePower.Weak)),
|
||||
AaHeight(Me, -Height, 400, 100, New AniEaseOutFluent()),
|
||||
AaCode(Sub() Visibility = Visibility.Collapsed,, True)
|
||||
}, "MyExtraButton MainScale " & Uuid)
|
||||
End If
|
||||
IsHitTestVisible = value '防止缩放动画中依然可以点进去
|
||||
End Sub)
|
||||
RunInUi(
|
||||
Sub()
|
||||
If value Then
|
||||
'有了
|
||||
Visibility = Visibility.Visible
|
||||
AniStart({
|
||||
AaScaleTransform(Me, 0.3 - CType(RenderTransform, ScaleTransform).ScaleX, 500, 60, New AniEaseOutFluent(AniEasePower.Weak)),
|
||||
AaScaleTransform(Me, 0.7, 500, 60, New AniEaseOutBack(AniEasePower.Weak)),
|
||||
AaHeight(Me, 50 - Height, 200,, New AniEaseOutFluent(AniEasePower.Weak))
|
||||
}, "MyExtraButton MainScale " & Uuid)
|
||||
Else
|
||||
'没了
|
||||
AniStart({
|
||||
AaScaleTransform(Me, -CType(RenderTransform, ScaleTransform).ScaleX, 100,, New AniEaseInFluent(AniEasePower.Weak)),
|
||||
AaHeight(Me, -Height, 400, 100, New AniEaseOutFluent()),
|
||||
AaCode(Sub() Visibility = Visibility.Collapsed,, True)
|
||||
}, "MyExtraButton MainScale " & Uuid)
|
||||
End If
|
||||
IsHitTestVisible = value '防止缩放动画中依然可以点进去
|
||||
End Sub)
|
||||
End Set
|
||||
End Property
|
||||
Public Delegate Function ShowCheckDelegate() As Boolean
|
||||
@@ -137,6 +138,7 @@
|
||||
AaScaleTransform(PanScale, 1 - CType(PanScale.RenderTransform, ScaleTransform).ScaleX, 300,, New AniEaseOutBack)
|
||||
}, "MyExtraButton Scale " & Uuid)
|
||||
End If
|
||||
If IsLeftMouseHeld Then RaiseCustomEvent()
|
||||
IsLeftMouseHeld = False
|
||||
RefreshColor() '直接刷新颜色以判断是否已触发 MouseLeave
|
||||
End Sub
|
||||
@@ -199,16 +201,17 @@
|
||||
''' 发出一圈波浪效果提示。
|
||||
''' </summary>
|
||||
Public Sub Ribble()
|
||||
RunInUi(Sub()
|
||||
Dim Shape As New Border With {.CornerRadius = New CornerRadius(1000), .BorderThickness = New Thickness(0.001), .Opacity = 0.5, .RenderTransformOrigin = New Point(0.5, 0.5), .RenderTransform = New ScaleTransform()}
|
||||
Shape.SetResourceReference(Border.BackgroundProperty, "ColorBrush5")
|
||||
PanScale.Children.Insert(0, Shape)
|
||||
AniStart({
|
||||
AaScaleTransform(Shape, 13, 1000, Ease:=New AniEaseInoutFluent(AniEasePower.Strong, 0.3)),
|
||||
AaOpacity(Shape, -Shape.Opacity, 1000),
|
||||
AaCode(Sub() PanScale.Children.Remove(Shape), After:=True)
|
||||
}, "ExtraButton Ribble " & GetUuid())
|
||||
End Sub)
|
||||
RunInUi(
|
||||
Sub()
|
||||
Dim Shape As New Border With {.CornerRadius = New CornerRadius(1000), .BorderThickness = New Thickness(0.001), .Opacity = 0.5, .RenderTransformOrigin = New Point(0.5, 0.5), .RenderTransform = New ScaleTransform()}
|
||||
Shape.SetResourceReference(Border.BackgroundProperty, "ColorBrush5")
|
||||
PanScale.Children.Insert(0, Shape)
|
||||
AniStart({
|
||||
AaScaleTransform(Shape, 13, 1000, Ease:=New AniEaseInoutFluent(AniEasePower.Strong, 0.3)),
|
||||
AaOpacity(Shape, -Shape.Opacity, 1000),
|
||||
AaCode(Sub() PanScale.Children.Remove(Shape), After:=True)
|
||||
}, "ExtraButton Ribble " & GetUuid())
|
||||
End Sub)
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -81,12 +81,12 @@ Public Class MyExtraTextButton
|
||||
|
||||
'触发点击事件
|
||||
Private Sub Button_LeftMouseUp(sender As Object, e As MouseButtonEventArgs) Handles PanClick.MouseLeftButtonUp
|
||||
If IsLeftMouseHeld Then
|
||||
Log("[Control] 按下附加图标按钮:" & Text)
|
||||
RaiseEvent Click(sender, e)
|
||||
e.Handled = True
|
||||
Button_LeftMouseUp()
|
||||
End If
|
||||
If Not IsLeftMouseHeld Then Return
|
||||
Log("[Control] 按下附加图标按钮:" & Text)
|
||||
RaiseEvent Click(sender, e)
|
||||
e.Handled = True
|
||||
RaiseCustomEvent()
|
||||
Button_LeftMouseUp()
|
||||
End Sub
|
||||
|
||||
'鼠标点击判定(务必放在点击事件之后,以使得 Button_MouseUp 先于 Button_MouseLeave 执行)
|
||||
|
||||
@@ -113,7 +113,7 @@ Public Class MyHint
|
||||
IsMouseDown = False
|
||||
Log("[Control] 按下提示条" & If(String.IsNullOrEmpty(Name), "", ":" & Name))
|
||||
e.Handled = True
|
||||
ModEvent.TryStartEvent(EventType, EventData)
|
||||
RaiseCustomEvent() '自定义事件
|
||||
End Sub
|
||||
Private Sub MyHint_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonDown
|
||||
IsMouseDown = True
|
||||
@@ -121,24 +121,6 @@ Public Class MyHint
|
||||
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
|
||||
|
||||
@@ -48,36 +48,16 @@
|
||||
End Set
|
||||
End Property
|
||||
|
||||
'触发点击事件
|
||||
'鼠标点击判定(务必放在点击事件之后,以使得 Button_MouseUp 先于 Button_MouseLeave 执行)
|
||||
Private IsMouseDown As Boolean = False
|
||||
Private Sub Button_MouseUp(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonUp
|
||||
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)
|
||||
RaiseCustomEvent() '自定义事件
|
||||
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
|
||||
Private Sub Button_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonDown
|
||||
IsMouseDown = True
|
||||
Focus()
|
||||
@@ -102,7 +82,6 @@
|
||||
RefreshAnim() '直接刷新颜色以判断是否已触发 MouseLeave
|
||||
End Sub
|
||||
|
||||
'自定义事件
|
||||
'务必放在 IsMouseDown 更新之后
|
||||
Private Const AnimationColorIn As Integer = 120
|
||||
Private Const AnimationColorOut As Integer = 150
|
||||
|
||||
@@ -8,9 +8,6 @@ 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 事件
|
||||
|
||||
'自定义属性
|
||||
|
||||
@@ -76,7 +73,7 @@ Public Class MyIconTextButton
|
||||
Log("[Control] 按下带图标按钮:" & Text)
|
||||
IsMouseDown = False
|
||||
RaiseEvent Click(Me, New RouteEventArgs(True))
|
||||
ModEvent.TryStartEvent(EventType, EventData)
|
||||
RaiseCustomEvent() '自定义事件
|
||||
RefreshColor()
|
||||
End Sub
|
||||
Private Sub MyIconTextButton_MouseDown() Handles Me.MouseLeftButtonDown
|
||||
@@ -87,24 +84,6 @@ Public Class MyIconTextButton
|
||||
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))
|
||||
|
||||
'动画
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
''' <summary>
|
||||
''' 与 Image 的 Source 类似。
|
||||
''' 若输入以 http 开头的字符串,则会尝试下载图片然后显示,图片会保存为本地缓存。
|
||||
''' 若输入以 http 开头的字符串,则会尝试下载图片然后显示;若 EnableCache 设为 True,图片还会保存为本地缓存。
|
||||
''' 支持 WebP 格式的图片。
|
||||
''' </summary>
|
||||
Public Shadows Property Source As String '覆写 Image 的 Source 属性
|
||||
@@ -46,7 +46,7 @@
|
||||
Sub(sender, e) If sender IsNot Nothing Then CType(sender, MyImage).Source = e.NewValue.ToString())))
|
||||
|
||||
''' <summary>
|
||||
''' 当 Source 首次下载失败时,会从该备用地址加载图片。
|
||||
''' 若 Source 是一个网络图片,该地址将作为第二图片源。
|
||||
''' </summary>
|
||||
Public Property FallbackSource As String
|
||||
Get
|
||||
@@ -111,73 +111,47 @@
|
||||
Return
|
||||
End If
|
||||
'从缓存加载网络图片
|
||||
Dim Url As String = Source
|
||||
Dim Retried As Boolean = False
|
||||
Dim TempPath As String = GetTempPath(Url)
|
||||
Dim TempFile As New FileInfo(TempPath)
|
||||
Dim EnableCache As Boolean = Me.EnableCache
|
||||
Dim TempPath As String = GetTempPath(Source) & If(EnableCache, "", GetUuid()) '不启用缓存时加上随机字符串,避免冲突
|
||||
Dim TempFile As New FileInfo(TempPath)
|
||||
If EnableCache AndAlso TempFile.Exists Then
|
||||
ActualSource = TempPath
|
||||
If (Date.Now - TempFile.LastWriteTime) < FileCacheExpiredTime Then Return '无需刷新缓存
|
||||
End If
|
||||
RunInNewThread(
|
||||
Sub()
|
||||
Dim TempDownloadingPath As String = Nothing
|
||||
Dim IsLocalFallback As Boolean = Not String.IsNullOrEmpty(FallbackSource) AndAlso Not FallbackSource.StartsWithF("http")
|
||||
Try
|
||||
RetryStart:
|
||||
'下载
|
||||
ActualSource = LoadingSource '显示加载中图片
|
||||
TempDownloadingPath = TempPath & RandomInteger(0, 10000000)
|
||||
Directory.CreateDirectory(GetPathFromFullPath(TempPath)) '重新实现下载,以避免携带 Header(#5072)
|
||||
Using Client As New Net.WebClient
|
||||
Client.DownloadFile(Url, TempDownloadingPath)
|
||||
End Using
|
||||
If Url <> Source AndAlso Url <> FallbackSource Then
|
||||
'已经更换了地址
|
||||
File.Delete(TempDownloadingPath)
|
||||
ElseIf EnableCache Then
|
||||
ActualSource = LoadingSource '显示加载中的占位图片
|
||||
NetDownloadByLoader(
|
||||
If(FallbackSource?.StartsWithF("http"), {Source, FallbackSource}, {Source}),
|
||||
TempPath, SimulateBrowserHeaders:=True)
|
||||
If EnableCache Then
|
||||
'保存缓存并显示
|
||||
If File.Exists(TempPath) Then File.Delete(TempPath)
|
||||
FileSystem.Rename(TempDownloadingPath, TempPath)
|
||||
RunInUi(Sub() ActualSource = TempPath)
|
||||
Else
|
||||
'直接显示
|
||||
RunInUiWait(Sub() ActualSource = TempDownloadingPath)
|
||||
File.Delete(TempDownloadingPath)
|
||||
RunInUiWait(Sub() ActualSource = TempPath)
|
||||
File.Delete(TempPath)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Try
|
||||
If TempPath IsNot Nothing Then File.Delete(TempPath)
|
||||
If TempDownloadingPath IsNot Nothing Then File.Delete(TempDownloadingPath)
|
||||
Catch
|
||||
End Try
|
||||
If Not Retried Then
|
||||
'更换备用地址
|
||||
Log(ex, $"下载图片可重试地失败({Url})", LogLevel.Developer)
|
||||
Retried = True
|
||||
Url = If(FallbackSource, Source)
|
||||
'空
|
||||
If Url Is Nothing Then
|
||||
ActualSource = Nothing
|
||||
'加载本地备用图片
|
||||
If IsLocalFallback Then
|
||||
Try
|
||||
RunInUiWait(Sub() ActualSource = FallbackSource)
|
||||
Log(ex, $"下载图片失败,使用本地备用图片({Source})")
|
||||
Return
|
||||
End If
|
||||
'本地图片
|
||||
If Not Url.StartsWithF("http") Then
|
||||
ActualSource = Url
|
||||
Return
|
||||
End If
|
||||
'从缓存加载网络图片
|
||||
TempPath = GetTempPath(Url)
|
||||
TempFile = New FileInfo(TempPath)
|
||||
If EnableCache AndAlso TempFile.Exists() Then
|
||||
ActualSource = TempPath
|
||||
If (Date.Now - TempFile.CreationTime) < FileCacheExpiredTime Then Return '无需刷新缓存
|
||||
End If
|
||||
'下载
|
||||
If Source = Url Then Thread.Sleep(1000) '延迟 1s 重试
|
||||
GoTo RetryStart
|
||||
Catch exx As Exception
|
||||
Log(ex, $"下载图片失败({Source})", LogLevel.Hint)
|
||||
Log(exx, $"加载备用图片失败({FallbackSource})", LogLevel.Hint)
|
||||
End Try
|
||||
Else
|
||||
Log(ex, $"下载图片失败({Url})", LogLevel.Hint)
|
||||
Log(ex, $"下载图片失败({Source},备用:{FallbackSource})", LogLevel.Hint)
|
||||
End If
|
||||
End Try
|
||||
End Sub, "MyImage PicLoader " & GetUuid() & "#", ThreadPriority.BelowNormal)
|
||||
|
||||
@@ -506,8 +506,8 @@ Public Class MyListItem
|
||||
RaiseEvent Click(sender, e)
|
||||
If e.Handled Then Return
|
||||
'触发自定义事件
|
||||
If Not String.IsNullOrEmpty(EventType) Then
|
||||
ModEvent.TryStartEvent(EventType, EventData)
|
||||
If CustomEventService.GetEventType(sender) <> CustomEvent.EventType.None Then
|
||||
RaiseCustomEvent()
|
||||
e.Handled = True
|
||||
End If
|
||||
If e.Handled Then Return
|
||||
@@ -537,26 +537,6 @@ Public Class MyListItem
|
||||
If ButtonStack IsNot Nothing Then ButtonStack.IsHitTestVisible = True
|
||||
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(MyListItem), 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(MyListItem), New PropertyMetadata(Nothing))
|
||||
|
||||
#End Region
|
||||
|
||||
Private StateLast As String
|
||||
@@ -657,13 +637,14 @@ Public Class MyListItem
|
||||
SetResourceReference(ForegroundProperty, "ColorBrush1")
|
||||
End If
|
||||
ColumnPaddingRight.Width = New GridLength(MinPaddingRight)
|
||||
If EventType = "打开帮助" AndAlso Not (Title <> "" AndAlso Info <> "") Then '#3266
|
||||
If CustomEventService.GetEventType(Me) = CustomEvent.EventType.打开帮助 AndAlso Not (Title <> "" AndAlso Info <> "") Then '#3266
|
||||
Try
|
||||
Dim Unused = New HelpEntry(GetEventAbsoluteUrls(EventData, EventType)(0)).SetToListItem(Me)
|
||||
Dim Entry As New HelpEntry(CustomEvent.GetAbsoluteUrls(CustomEventService.GetEventData(Me), CustomEventService.GetEventType(Me))(0))
|
||||
Entry.SetToListItem(Me)
|
||||
Catch ex As Exception
|
||||
Log(ex, "设置帮助 MyListItem 失败", LogLevel.Msgbox)
|
||||
EventType = Nothing
|
||||
EventData = Nothing
|
||||
CustomEventService.SetEventType(Me, CustomEvent.EventType.None)
|
||||
CustomEventService.SetEventData(Me, "")
|
||||
End Try
|
||||
End If
|
||||
End Sub
|
||||
|
||||
@@ -79,11 +79,7 @@ Public Class MyLoading
|
||||
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
|
||||
LabText.Text = If(Ex.IsNetworkRelated(), "网络环境不佳,请稍后再试,或使用 VPN 改善网络环境", StrTrim(Ex.Message))
|
||||
End If
|
||||
Else
|
||||
LabText.Text = TextError
|
||||
@@ -187,30 +183,29 @@ Public Class MyLoading
|
||||
IsLooping = True
|
||||
ErrorAnimationWaiting = True
|
||||
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)),
|
||||
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
|
||||
End Sub),
|
||||
AaOpacity(PathLeft, -1, 100, 50),
|
||||
AaX(PathLeft, -5, 180,, New AniEaseOutFluent),
|
||||
AaY(PathLeft, -6, 180,, New AniEaseOutFluent),
|
||||
AaOpacity(PathRight, -1, 100, 50),
|
||||
AaX(PathRight, 5, 180,, New AniEaseOutFluent),
|
||||
AaY(PathRight, -6, 180,, New AniEaseOutFluent),
|
||||
AaCode(Sub()
|
||||
IsLooping = False
|
||||
AniLoop()
|
||||
End Sub,, True)
|
||||
}, "MyLoader Loop " & Uuid & "/" & GetUuid())
|
||||
If ShowProgress Then
|
||||
|
||||
End If
|
||||
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)),
|
||||
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
|
||||
End Sub),
|
||||
AaOpacity(PathLeft, -1, 100, 50),
|
||||
AaX(PathLeft, -5, 180,, New AniEaseOutFluent),
|
||||
AaY(PathLeft, -6, 180,, New AniEaseOutFluent),
|
||||
AaOpacity(PathRight, -1, 100, 50),
|
||||
AaX(PathRight, 5, 180,, New AniEaseOutFluent),
|
||||
AaY(PathRight, -6, 180,, New AniEaseOutFluent),
|
||||
AaCode(
|
||||
Sub()
|
||||
IsLooping = False
|
||||
AniLoop()
|
||||
End Sub,, True)
|
||||
}, "MyLoader Loop " & Uuid & "/" & GetUuid())
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
|
||||
@@ -54,4 +54,7 @@
|
||||
End If
|
||||
End Sub
|
||||
|
||||
Private Sub MyMenuItem_Click(sender As Object, e As RoutedEventArgs) Handles Me.Click
|
||||
RaiseCustomEvent()
|
||||
End Sub
|
||||
End Class
|
||||
@@ -3,15 +3,15 @@
|
||||
Private Uuid As Integer = GetUuid()
|
||||
|
||||
'执行逐个进入动画的控件
|
||||
Public Property AnimatedControl As FrameworkElement
|
||||
Public Property AnimatedControl As String '需要在 Loaded 之后才能获取到控件,所以不能用 Binding 直接绑定(#6664)
|
||||
Get
|
||||
Return GetValue(AnimatedControlProperty)
|
||||
End Get
|
||||
Set(value As FrameworkElement)
|
||||
Set(value As String)
|
||||
SetValue(AnimatedControlProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
Public Shared ReadOnly AnimatedControlProperty As DependencyProperty = DependencyProperty.Register("AnimatedControl", GetType(FrameworkElement), GetType(MyPageLeft), New PropertyMetadata(Nothing))
|
||||
Public Shared ReadOnly AnimatedControlProperty As DependencyProperty = DependencyProperty.Register("AnimatedControl", GetType(String), GetType(MyPageLeft), New PropertyMetadata(Nothing))
|
||||
|
||||
Public Sub TriggerShowAnimation()
|
||||
If AnimatedControl Is Nothing Then
|
||||
@@ -84,7 +84,7 @@
|
||||
'遍历获取所有需要生成动画的控件
|
||||
Private Function GetAllAnimControls(Optional IgnoreInvisibility As Boolean = False) As List(Of FrameworkElement)
|
||||
Dim AllControls As New List(Of FrameworkElement)
|
||||
GetAllAnimControls(AnimatedControl, AllControls, IgnoreInvisibility)
|
||||
GetAllAnimControls(FindName(AnimatedControl), AllControls, IgnoreInvisibility)
|
||||
Return AllControls
|
||||
End Function
|
||||
Private Sub GetAllAnimControls(Element As FrameworkElement, ByRef AllControls As List(Of FrameworkElement), IgnoreInvisibility As Boolean)
|
||||
@@ -105,7 +105,3 @@
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
|
||||
Public Interface IRefreshable
|
||||
Sub Refresh()
|
||||
End Interface
|
||||
@@ -3,16 +3,16 @@
|
||||
Public PageUuid As Integer = GetUuid()
|
||||
|
||||
'“返回顶部” 按钮检测的滚动区域
|
||||
Public Property PanScroll As MyScrollViewer
|
||||
Public Property PanScroll As String '需要在 Loaded 之后才能获取到控件,所以不能用 Binding 直接绑定(#6664)
|
||||
Get
|
||||
Return GetValue(PanScrollProperty)
|
||||
End Get
|
||||
Set(value As MyScrollViewer)
|
||||
Set(value As String)
|
||||
SetValue(PanScrollProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
Private Shared ReadOnly PanScrollProperty =
|
||||
DependencyProperty.Register("PanScroll", GetType(MyScrollViewer), GetType(MyPageRight), New PropertyMetadata(Nothing))
|
||||
DependencyProperty.Register("PanScroll", GetType(String), GetType(MyPageRight), New PropertyMetadata(Nothing))
|
||||
|
||||
'当前状态
|
||||
Public Enum PageStates
|
||||
@@ -130,7 +130,7 @@
|
||||
Select Case PageState
|
||||
Case PageStates.Empty
|
||||
If PageLoader Is Nothing OrElse PageLoader.State = LoadState.Finished OrElse PageLoader.State = LoadState.Waiting OrElse PageLoader.State = LoadState.Aborted Then
|
||||
'如果加载器在进入页面时不启动(例如 HiPer 联机),那么在此时就会有 State = Waiting
|
||||
'如果加载器在进入页面时不启动(例如联机),那么在此时就会有 State = Waiting
|
||||
PageState = PageStates.ContentEnter
|
||||
TriggerEnterAnimation(PanAlways, If(PanContent, Child))
|
||||
ElseIf PageLoader.State = LoadState.Loading Then
|
||||
@@ -152,7 +152,8 @@
|
||||
PageState = PageStates.LoaderEnter
|
||||
TriggerEnterAnimation(PanLoader)
|
||||
End If
|
||||
Case PageStates.ContentEnter '重复调用 PageOnEnter,直接忽略
|
||||
Case PageStates.ContentEnter
|
||||
'重复调用 PageOnEnter,直接忽略
|
||||
Case Else
|
||||
Throw New Exception("在状态为 " & GetStringFromEnum(PageState) & " 时触发了 PageOnEnter 事件。")
|
||||
End Select
|
||||
@@ -207,8 +208,8 @@
|
||||
Public Sub PageOnContentExit()
|
||||
If ModeDebug Then Log("[UI] 已触发 PageOnContentExit")
|
||||
If PageLoader IsNot Nothing AndAlso PageLoader.State = LoadState.Loading Then
|
||||
Throw New Exception("在调用 PageOnContentExit 时,加载器不能为 Loading 状态")
|
||||
'Loading 的加载器可能触发进一步变化,难以预测会触发子页面的动画还是加载器完成的动画
|
||||
Throw New Exception("在调用 PageOnContentExit 时,加载器不能为 Loading 状态")
|
||||
End If
|
||||
Select Case PageState
|
||||
Case PageStates.ContentEnter, PageStates.ContentStay
|
||||
@@ -219,8 +220,12 @@
|
||||
Case PageStates.LoaderEnter, PageStates.LoaderStayForce, PageStates.LoaderStay
|
||||
PageState = PageStates.ContentExit
|
||||
TriggerExitAnimation(PanLoader)
|
||||
Case PageStates.LoaderWait, PageStates.Empty
|
||||
Case PageStates.LoaderWait
|
||||
PageOnEnter()
|
||||
Case PageStates.Empty
|
||||
'可能是在离开页面后,其他后台线程调用了本函数,此时不应触发 PageOnEnter()
|
||||
'例如:在加入联机房间后,切换到其他页面,然后联机房间被解散
|
||||
If IsLoaded Then PageOnEnter()
|
||||
End Select
|
||||
End Sub
|
||||
|
||||
|
||||
@@ -89,14 +89,15 @@ Public Class MyRadioBox
|
||||
End If
|
||||
End Select
|
||||
|
||||
'触发事件
|
||||
If IsChanged Then
|
||||
'触发事件
|
||||
If Checked Then RaiseEvent Check(Me, New RouteEventArgs(user))
|
||||
RaiseEvent Changed(Me, New RouteEventArgs(user))
|
||||
RaiseCustomEvent()
|
||||
'更改动画
|
||||
SyncUI()
|
||||
End If
|
||||
|
||||
'更改动画
|
||||
SyncUI()
|
||||
Catch ex As Exception
|
||||
Log(ex, "单选框勾选改变错误", LogLevel.Hint)
|
||||
End Try
|
||||
|
||||
@@ -7,10 +7,6 @@ Public Class MyRadioButton
|
||||
|
||||
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 事件
|
||||
|
||||
'自定义属性
|
||||
|
||||
@@ -46,23 +42,22 @@ Public Class MyRadioButton
|
||||
''' 手动设置 Checked 属性。
|
||||
''' </summary>
|
||||
''' <param name="value">新的 Checked 属性。</param>
|
||||
''' <param name="user">是否由用户引发。</param>
|
||||
''' <param name="raiseByMouse">是否由用户引发。</param>
|
||||
''' <param name="anime">是否执行动画。</param>
|
||||
Public Sub SetChecked(value As Boolean, user As Boolean, anime As Boolean)
|
||||
Public Sub SetChecked(value As Boolean, raiseByMouse As Boolean, anime As Boolean)
|
||||
Try
|
||||
|
||||
'自定义属性基础
|
||||
|
||||
Dim IsChanged As Boolean = False
|
||||
If IsLoaded AndAlso Not value = _Checked Then RaiseEvent Change(Me, user)
|
||||
If Not value = _Checked Then
|
||||
If _Checked <> value Then
|
||||
_Checked = value
|
||||
IsChanged = True
|
||||
End If
|
||||
|
||||
'保证只有一个单选框选中
|
||||
|
||||
If IsNothing(Parent) Then Return
|
||||
If Parent Is Nothing Then Return
|
||||
Dim RadioboxList As New List(Of MyRadioButton)
|
||||
Dim CheckedCount As Integer = 0
|
||||
'收集控件列表与选中个数
|
||||
@@ -105,7 +100,8 @@ Public Class MyRadioButton
|
||||
RefreshColor(Nothing, anime)
|
||||
|
||||
'触发事件
|
||||
If Checked Then RaiseEvent Check(Me, user)
|
||||
If Checked Then RaiseEvent Check(Me, raiseByMouse)
|
||||
RaiseCustomEvent()
|
||||
|
||||
Catch ex As Exception
|
||||
Log(ex, "单选按钮勾选改变错误", LogLevel.Hint)
|
||||
|
||||
@@ -39,4 +39,7 @@
|
||||
TextBox.Focus()
|
||||
End Sub
|
||||
|
||||
Private Sub MySearchBox_KeyUp(sender As Object, e As KeyEventArgs) Handles Me.KeyUp
|
||||
If e.Key = Key.Enter Then RaiseCustomEvent()
|
||||
End Sub
|
||||
End Class
|
||||
@@ -264,7 +264,7 @@
|
||||
If IsLoaded AndAlso AniControlEnabled = 0 Then '防止默认属性变更触发动画
|
||||
'有动画
|
||||
AniStart({
|
||||
AaColor(Me, BorderBrushProperty, ForeColorName, AnimationTime),
|
||||
AaColor(Me, BorderBrushProperty, ForeColorName, AnimationTime),
|
||||
AaColor(Me, BackgroundProperty, BackColorName, AnimationTime)
|
||||
}, "MyTextBox Color " & Uuid)
|
||||
Else
|
||||
@@ -291,4 +291,8 @@
|
||||
End If
|
||||
End Sub
|
||||
|
||||
'在按下回车时触发自定义事件
|
||||
Private Sub MyTextBox_KeyUp(sender As Object, e As KeyEventArgs) Handles Me.KeyUp
|
||||
If e.Key = Key.Enter Then RaiseCustomEvent()
|
||||
End Sub
|
||||
End Class
|
||||
@@ -22,15 +22,15 @@
|
||||
End Set
|
||||
End Property
|
||||
Public Shared ReadOnly TextProperty As DependencyProperty =
|
||||
DependencyProperty.Register("Text", GetType(String), GetType(MyTextButton), New PropertyMetadata("", Sub(sender As MyTextButton, e As DependencyPropertyChangedEventArgs)
|
||||
If Not e.OldValue = e.NewValue Then
|
||||
AniStart({
|
||||
AaOpacity(sender, -sender.Opacity, 50),
|
||||
AaCode(Sub() sender.Content = e.NewValue,, True),
|
||||
AaOpacity(sender, 1, 170)
|
||||
}, "MyTextButton Text " & sender.Uuid)
|
||||
End If
|
||||
End Sub))
|
||||
DependencyProperty.Register("Text", GetType(String), GetType(MyTextButton), New PropertyMetadata("",
|
||||
Sub(sender As MyTextButton, e As DependencyPropertyChangedEventArgs)
|
||||
If e.OldValue = e.NewValue Then Return
|
||||
AniStart({
|
||||
AaOpacity(sender, -sender.Opacity, 50),
|
||||
AaCode(Sub() sender.Content = e.NewValue,, True),
|
||||
AaOpacity(sender, 1, 170)
|
||||
}, "MyTextButton Text " & sender.Uuid)
|
||||
End Sub))
|
||||
|
||||
'鼠标事件
|
||||
|
||||
@@ -43,13 +43,12 @@
|
||||
IsMouseDown = False
|
||||
End Sub
|
||||
Private Sub MyTextButton_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles Me.PreviewMouseLeftButtonUp
|
||||
If IsMouseDown Then
|
||||
IsMouseDown = False
|
||||
Log("[Control] 按下文本按钮:" & Text)
|
||||
RaiseEvent Click(Me, Nothing)
|
||||
ModEvent.TryStartEvent(EventType, EventData)
|
||||
e.Handled = True
|
||||
End If
|
||||
If Not IsMouseDown Then Return
|
||||
IsMouseDown = False
|
||||
Log("[Control] 按下文本按钮:" & Text)
|
||||
RaiseEvent Click(Me, Nothing)
|
||||
RaiseCustomEvent()
|
||||
e.Handled = True
|
||||
End Sub
|
||||
|
||||
'指向动画
|
||||
@@ -57,7 +56,7 @@
|
||||
Private Const AnimationTimeIn As Integer = 100
|
||||
Private Const AnimationTimeOut As Integer = 200
|
||||
Private ColorName As String
|
||||
Private Sub RefreshColor() Handles Me.MouseEnter, Me.MouseLeave, Me.IsEnabledChanged, Me.MouseLeftButtonDown, Me.MouseLeftButtonUp
|
||||
Private Sub RefreshColor() Handles Me.MouseEnter, Me.MouseLeave, Me.IsEnabledChanged, Me.MouseLeftButtonDown, Me.MouseLeftButtonUp
|
||||
'判断当前颜色
|
||||
Dim ForeName As String
|
||||
Dim Time As Integer
|
||||
@@ -85,26 +84,4 @@
|
||||
End If
|
||||
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(MyTextButton), 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(MyTextButton), New PropertyMetadata(Nothing))
|
||||
|
||||
End Class
|
||||
@@ -1,4 +1,5 @@
|
||||
Imports System.ComponentModel
|
||||
Imports System.Windows.Interop
|
||||
|
||||
Public Class FormMain
|
||||
|
||||
@@ -10,6 +11,39 @@ Public Class FormMain
|
||||
Dim FeatureList As New List(Of KeyValuePair(Of Integer, String))
|
||||
'统计更新日志条目
|
||||
#If BETA Then
|
||||
If LastVersion < 372 Then 'Release 2.10.9
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:如果版本设置了自定义描述,会在标题后面以淡灰色显示其版本号"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "新增:支持为一个控件设置多个自定义事件"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "新增:修改变量、弹出提示自定义事件"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "新增:添加大量替换标记,允许在更多设置和 XAML 中使用更多替换标记"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:无法安装 MMC 整合包"))
|
||||
FeatureCount += 33
|
||||
BugCount += 11
|
||||
End If
|
||||
If LastVersion < 369 Then 'Release 2.10.8
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "新增:允许在版本设置中设置禁止更新 Mod,以防整合包玩家误操作"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:网络与下载稳定性优化"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "优化:若整合包需要 PCL 不兼容的加载器,允许选择跳过它的安装"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "删除:由于已不再需要,删除手动安装包下载功能"))
|
||||
FeatureCount += 21
|
||||
BugCount += 32
|
||||
End If
|
||||
If LastVersion < 367 Then 'Release 2.10.6
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:启用 MCIM 社区资源镜像源,以缓解社区资源难以下载的问题"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:正版登录出错时无法给出正确的错误信息"))
|
||||
FeatureCount += 9
|
||||
BugCount += 9
|
||||
End If
|
||||
If LastVersion < 365 Then 'Release 2.10.5
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:下载资源时,会单独记忆每种资源上次下载到的文件夹,以防混淆"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:网络底层框架与下载稳定性"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:无法启动一部分 LabyMod 和 GTNH 客户端"))
|
||||
FeatureCount += 22
|
||||
BugCount += 26
|
||||
End If
|
||||
If LastVersion < 361 Then 'Release 2.10.3
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复:无法安装部分使用老版本 PCL 导出的整合包"))
|
||||
End If
|
||||
If LastVersion < 359 Then 'Release 2.10.2
|
||||
If LastVersion >= 357 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:下载资源包、光影包时能自动跳转到对应的文件夹"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:调整界面样式与动画,让整体视觉更干净,操作体验更顺滑"))
|
||||
@@ -79,6 +113,66 @@ Public Class FormMain
|
||||
'3:BUG+ IMP* FEAT-
|
||||
'2:BUG* IMP-
|
||||
'1:BUG-
|
||||
If LastVersion < 374 Then 'Snapshot 2.11.1
|
||||
If LastVersion >= 373 Then
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:使用离线登录也可以直接加入联机房间了"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:会从所有共享节点中自动选择负载最低的进行中继连接"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:若复制了邀请码,则可以直接快速加入房间"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:关闭 PCL 时总是会提示是否退出联机,防止在关闭 PCL 时无意地关闭或退出了房间"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "新增:允许自定义要连接的节点"))
|
||||
End If
|
||||
FeatureCount += 9
|
||||
BugCount += 7
|
||||
End If
|
||||
If LastVersion < 373 Then 'Snapshot 2.11.0
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(5, "新增:联机功能!"))
|
||||
FeatureCount += 7
|
||||
BugCount += 10
|
||||
End If
|
||||
If LastVersion < 371 Then 'Snapshot 2.10.9
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:如果版本设置了自定义描述,会在标题后面以淡灰色显示其版本号"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "新增:支持为一个控件设置多个自定义事件"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "新增:修改变量、弹出提示自定义事件"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "新增:添加大量替换标记,允许在更多设置和 XAML 中使用更多替换标记"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:无法安装 MMC 整合包"))
|
||||
FeatureCount += 33
|
||||
BugCount += 11
|
||||
End If
|
||||
If LastVersion < 370 Then 'Snapshot 2.10.8
|
||||
If LastVersion >= 368 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:网络与下载稳定性优化"))
|
||||
FeatureCount += 3
|
||||
BugCount += 4
|
||||
End If
|
||||
If LastVersion < 368 Then 'Snapshot 2.10.7
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "新增:允许在版本设置中设置禁止更新 Mod,以防整合包玩家误操作"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:网络与下载稳定性优化"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "优化:若整合包需要 PCL 不兼容的加载器,允许选择跳过它的安装"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "删除:由于已不再需要,删除手动安装包下载功能"))
|
||||
FeatureCount += 19
|
||||
BugCount += 28
|
||||
End If
|
||||
If LastVersion < 366 Then 'Snapshot 2.10.6
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:启用 MCIM 社区资源镜像源,以缓解社区资源难以下载的问题"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:正版登录出错时无法给出正确的错误信息"))
|
||||
FeatureCount += 9
|
||||
BugCount += 9
|
||||
End If
|
||||
If LastVersion < 364 Then 'Snapshot 2.10.5
|
||||
If LastVersion >= 363 Then
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:无法添加新的正版账号"))
|
||||
BugCount += 1
|
||||
End If
|
||||
End If
|
||||
If LastVersion < 363 Then 'Snapshot 2.10.4
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:下载资源时,会单独记忆每种资源上次下载到的文件夹,以防混淆"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:网络底层框架与下载稳定性"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:无法启动一部分 LabyMod 和 GTNH 客户端"))
|
||||
FeatureCount += 22
|
||||
BugCount += 26
|
||||
End If
|
||||
If LastVersion < 362 Then 'Snapshot 2.10.3
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复:无法安装部分使用老版本 PCL 导出的整合包"))
|
||||
End If
|
||||
If LastVersion < 360 Then 'Snapshot 2.10.2
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复:无法从 CurseForge 下载 Mod 等资源,或是安装整合包"))
|
||||
If LastVersion >= 358 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:无法加载正版皮肤的头像"))
|
||||
@@ -219,7 +313,7 @@ Public Class FormMain
|
||||
If Not Setup.IsUnset("LaunchArgumentIndie") Then
|
||||
Log("[Start] 从老 PCL 迁移版本隔离")
|
||||
Setup.Set("LaunchArgumentIndieV2", Setup.Get("LaunchArgumentIndie"))
|
||||
ElseIf Not Setup.IsUnset("WindowHeight") Then
|
||||
ElseIf Not Setup.IsUnset("LaunchVersionSelect") Then
|
||||
Log("[Start] 从老 PCL 升级,但此前未调整版本隔离,使用老的版本隔离默认值")
|
||||
Setup.Set("LaunchArgumentIndieV2", Setup.GetDefault("LaunchArgumentIndie"))
|
||||
Else
|
||||
@@ -236,13 +330,18 @@ Public Class FormMain
|
||||
'加载 UI
|
||||
InitializeComponent()
|
||||
Opacity = 0
|
||||
''开启管理员权限下的文件拖拽,但下列代码也没用(#2531)
|
||||
'If IsAdmin() Then
|
||||
' Log("[Start] PCL 正以管理员权限运行")
|
||||
' ChangeWindowMessageFilter(&H233, 1)
|
||||
' ChangeWindowMessageFilter(&H4A, 1)
|
||||
' ChangeWindowMessageFilter(&H49, 1)
|
||||
'End If
|
||||
'开启管理员权限下的文件拖拽
|
||||
If IsAdmin() Then
|
||||
Static Helper As New DragHelper
|
||||
AddHandler SourceInitialized,
|
||||
Sub()
|
||||
Dim WpfHelper As New WindowInteropHelper(Me)
|
||||
Helper.HwndIntPtrSource = HwndSource.FromHwnd(WpfHelper.Handle)
|
||||
Helper.AddHook()
|
||||
End Sub
|
||||
AddHandler Closing, Sub() Helper.RemoveDragHook()
|
||||
AddHandler Helper.DragDrop, Sub() FileDrag(Helper.DropFilePaths)
|
||||
End If
|
||||
'切换到首页
|
||||
If Not IsNothing(FrmLaunchLeft.Parent) Then FrmLaunchLeft.SetValue(ContentPresenter.ContentProperty, Nothing)
|
||||
If Not IsNothing(FrmLaunchRight.Parent) Then FrmLaunchRight.SetValue(ContentPresenter.ContentProperty, Nothing)
|
||||
@@ -253,7 +352,7 @@ Public Class FormMain
|
||||
FrmLaunchRight.PageState = MyPageRight.PageStates.ContentStay
|
||||
'模式提醒
|
||||
#If DEBUG Then
|
||||
Hint("[开发者模式] PCL 正以开发者模式运行,这可能会造成严重的性能下降,请务必立即向开发者反馈此问题!", HintType.Critical)
|
||||
Hint("[开发者模式] PCL 正以开发者模式运行,这可能会造成严重的性能下降,请务必立即向开发者反馈此问题!", HintType.Red)
|
||||
#End If
|
||||
If ModeDebug Then Hint("[调试模式] PCL 正以调试模式运行,这可能会导致性能下降,若无必要请不要开启!")
|
||||
'尽早执行的加载池
|
||||
@@ -333,11 +432,16 @@ Public Class FormMain
|
||||
RunInNewThread(
|
||||
Sub()
|
||||
'EULA 提示
|
||||
If Not Setup.Get("SystemEula") Then
|
||||
Select Case MyMsgBox("在使用 PCL 前,请同意 PCL 的用户协议与免责声明。", "协议授权", "同意", "拒绝", "查看用户协议与免责声明",
|
||||
Const EulaVersion As Integer = 2
|
||||
If Setup.Get("SystemEulaVersion") < EulaVersion Then
|
||||
Select Case MyMsgBox(
|
||||
If(Setup.Get("SystemEulaVersion") = 0,
|
||||
"在使用 PCL 前,请先阅读用户协议与免责声明。",
|
||||
$"PCL 的用户协议与免责声明已更新。{vbCrLf}请阅读更新后的用户协议与免责声明。"),
|
||||
"协议授权", "同意", "拒绝", "查看用户协议与免责声明",
|
||||
Button3Action:=Sub() OpenWebsite("https://shimo.im/docs/rGrd8pY8xWkt6ryW"))
|
||||
Case 1
|
||||
Setup.Set("SystemEula", True)
|
||||
Setup.Set("SystemEulaVersion", EulaVersion)
|
||||
Case 2
|
||||
EndProgram(False)
|
||||
End Select
|
||||
@@ -359,7 +463,9 @@ Public Class FormMain
|
||||
Catch ex As Exception
|
||||
Log(ex, "清理自动更新文件失败")
|
||||
End Try
|
||||
End Sub, "Start Loader", ThreadPriority.Lowest)
|
||||
'上报
|
||||
Telemetry("启动")
|
||||
End Sub, "初始化", ThreadPriority.Lowest)
|
||||
|
||||
Log("[Start] 第三阶段加载用时:" & GetTimeTick() - ApplicationStartTick & " ms")
|
||||
End Sub
|
||||
@@ -400,6 +506,8 @@ Public Class FormMain
|
||||
Log("[Start] 最高版本号从 " & LowerVersionCode & " 升高到 " & VersionCode)
|
||||
End If
|
||||
#End If
|
||||
'迁移 EULA 版本
|
||||
If Setup.Get("SystemEula") AndAlso Setup.Get("SystemEulaVersion") = 0 Then Setup.Set("SystemEulaVersion", 1)
|
||||
'被移除的窗口设置选项
|
||||
If Setup.Get("LaunchArgumentWindowType") = 5 Then Setup.Set("LaunchArgumentWindowType", 1)
|
||||
'修改主题设置项名称
|
||||
@@ -478,21 +586,19 @@ Public Class FormMain
|
||||
''' </summary>
|
||||
''' <param name="SendWarning">是否在还有下载任务未完成时发出警告。</param>
|
||||
Public Sub EndProgram(SendWarning As Boolean)
|
||||
'发出警告
|
||||
If SendWarning AndAlso HasDownloadingTask() Then
|
||||
If MyMsgBox("还有下载任务尚未完成,是否确定退出?", "提示", "确定", "取消") = 1 Then
|
||||
'强行结束下载任务
|
||||
RunInNewThread(
|
||||
Sub()
|
||||
Log("[System] 正在强行停止任务")
|
||||
For Each Task As LoaderBase In LoaderTaskbar.ToList()
|
||||
Task.Abort()
|
||||
Next
|
||||
End Sub, "强行停止下载任务")
|
||||
Else
|
||||
Return
|
||||
End If
|
||||
'强行结束下载任务?
|
||||
If HasDownloadingTask() Then
|
||||
If SendWarning AndAlso MyMsgBox("还有下载任务尚未完成,是否确定退出?", "提示", "确定", "取消") = 2 Then Return
|
||||
RunInNewThread(
|
||||
Sub()
|
||||
Log("[System] 正在强行停止任务")
|
||||
For Each Task As LoaderBase In LoaderTaskbar.ToList()
|
||||
Task.Abort()
|
||||
Next
|
||||
End Sub, "强行停止下载任务")
|
||||
End If
|
||||
'关闭联机?
|
||||
If FrmLinkMain?.TryExit(Not SendWarning, True) Then Return
|
||||
'关闭
|
||||
RunInUiWait(
|
||||
Sub()
|
||||
@@ -534,9 +640,9 @@ Public Class FormMain
|
||||
If ReturnCode = ProcessReturnValues.Exception Then
|
||||
If Not IsLogShown Then
|
||||
FeedbackInfo()
|
||||
Log("请在 https://github.com/Hex-Dragon/PCL2/issues 提交错误报告,以便于作者解决此问题!")
|
||||
Log("请在 https://github.com/Meloong-Git/PCL/issues 提交错误报告,以便于作者解决此问题!")
|
||||
IsLogShown = True
|
||||
ShellOnly(Path & "PCL\Log1.txt")
|
||||
StartProcess(Path & "PCL\Log1.txt")
|
||||
End If
|
||||
Thread.Sleep(500) '防止 PCL 在记事本打开前就被掐掉
|
||||
End If
|
||||
@@ -585,11 +691,12 @@ Public Class FormMain
|
||||
'按键事件
|
||||
Private Sub FormMain_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
|
||||
If e.IsRepeat Then Return
|
||||
'调用弹窗:回车选择第一个,Esc 选择最后一个
|
||||
If PanMsg.Children.Count > 0 Then
|
||||
'修复按下 Alt 后误认为弹出系统菜单导致的冻结
|
||||
If e.SystemKey = Key.LeftAlt OrElse e.SystemKey = Key.RightAlt Then e.Handled = True
|
||||
'在有弹窗时:回车选择第一个,Esc 选择最后一个
|
||||
If PanMsg.Children.Any Then
|
||||
If e.Key = Key.Enter Then
|
||||
CType(PanMsg.Children(0), Object).Btn1_Click()
|
||||
Return
|
||||
ElseIf e.Key = Key.Escape Then
|
||||
Dim Msg As Object = PanMsg.Children(0)
|
||||
If TypeOf Msg IsNot MyMsgInput AndAlso TypeOf Msg IsNot MyMsgSelect AndAlso Msg.Btn3.Visibility = Visibility.Visible Then
|
||||
@@ -599,9 +706,14 @@ Public Class FormMain
|
||||
Else
|
||||
Msg.Btn1_Click()
|
||||
End If
|
||||
Return
|
||||
End If
|
||||
Return
|
||||
End If
|
||||
|
||||
'==========================
|
||||
' 在没有弹窗时:继续检查……
|
||||
'==========================
|
||||
|
||||
'按 ESC 返回上一级
|
||||
If e.Key = Key.Escape Then TriggerPageBack()
|
||||
'更改隐藏版本可见性
|
||||
@@ -614,9 +726,9 @@ Public Class FormMain
|
||||
If e.Key = Key.F12 Then
|
||||
PageSetupUI.HiddenForceShow = Not PageSetupUI.HiddenForceShow
|
||||
If PageSetupUI.HiddenForceShow Then
|
||||
Hint("功能隐藏设置已暂时关闭!", HintType.Finish)
|
||||
Hint("功能隐藏设置已暂时关闭!", HintType.Green)
|
||||
Else
|
||||
Hint("功能隐藏设置已重新开启!", HintType.Finish)
|
||||
Hint("功能隐藏设置已重新开启!", HintType.Green)
|
||||
End If
|
||||
PageSetupUI.HiddenRefresh()
|
||||
Return
|
||||
@@ -635,8 +747,6 @@ Public Class FormMain
|
||||
FrmLaunchLeft.LaunchButtonClick()
|
||||
End If
|
||||
End If
|
||||
'修复按下 Alt 后误认为弹出系统菜单导致的冻结
|
||||
If e.SystemKey = Key.LeftAlt OrElse e.SystemKey = Key.RightAlt Then e.Handled = True
|
||||
End Sub
|
||||
Private Sub FormMain_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseDown
|
||||
'鼠标侧键返回上一级
|
||||
@@ -664,6 +774,21 @@ Public Class FormMain
|
||||
Catch ex As Exception
|
||||
Log(ex, "切回窗口时出错", LogLevel.Feedback)
|
||||
End Try
|
||||
'读取剪贴板,自动加入联机房间
|
||||
If PageLinkMain.LinkState <> PageLinkMain.LinkStates.Waiting Then Return '已启动联机
|
||||
Dim Code = ClipboardGetText() : If Code Is Nothing Then Return '剪贴板无文本
|
||||
If Setup.Get("LinkLastAutoJoinInviteCode") = Code Then Return
|
||||
If PageLinkMain.ValidateCodeFormat(Code) IsNot Nothing Then Return '不是邀请码
|
||||
Setup.Set("LinkLastAutoJoinInviteCode", Code)
|
||||
RunInThread(
|
||||
Sub()
|
||||
If MyMsgBox("嘿,是否使用复制的邀请码加入房间?", "加入联机房间", "加入", "取消") = 2 Then Return '防止弹窗阻碍主线程,所以必须放在工作线程
|
||||
RunInUi(
|
||||
Sub()
|
||||
PageLinkMain.Join(Code)
|
||||
ClipboardSet(Nothing, False)
|
||||
End Sub)
|
||||
End Sub)
|
||||
End Sub
|
||||
|
||||
'文件拖放
|
||||
@@ -701,6 +826,7 @@ Public Class FormMain
|
||||
End Sub
|
||||
Private Sub FrmMain_Drop(sender As Object, e As DragEventArgs) Handles Me.Drop
|
||||
Try
|
||||
ShowWindowToTop()
|
||||
If e.Data.GetDataPresent(DataFormats.Text) Then
|
||||
'获取文本
|
||||
Try
|
||||
@@ -713,12 +839,12 @@ Public Class FormMain
|
||||
Dim AuthlibServer As String = Net.WebUtility.UrlDecode(Str.Substring("authlib-injector:yggdrasil-server:".Length))
|
||||
Log("[System] Authlib 拖拽:" & AuthlibServer)
|
||||
If Not String.IsNullOrEmpty(New ValidateHttp().Validate(AuthlibServer)) Then
|
||||
Hint($"输入的 Authlib 验证服务器不符合网址格式({AuthlibServer})!", HintType.Critical)
|
||||
Hint($"输入的 Authlib 验证服务器不符合网址格式({AuthlibServer})!", HintType.Red)
|
||||
Return
|
||||
End If
|
||||
Dim TargetVersion = If(PageCurrent = PageType.VersionSetup, PageVersionLeft.Version, McVersionCurrent)
|
||||
If TargetVersion Is Nothing Then
|
||||
Hint("请先下载游戏,再设置第三方登录!", HintType.Critical)
|
||||
Hint("请先下载游戏,再设置第三方登录!", HintType.Red)
|
||||
Return
|
||||
End If
|
||||
If AuthlibServer = "https://littleskin.cn/api/yggdrasil" Then
|
||||
@@ -765,7 +891,7 @@ Public Class FormMain
|
||||
'获取文件并检查
|
||||
Dim FilePathRaw = e.Data.GetData(DataFormats.FileDrop)
|
||||
If FilePathRaw Is Nothing Then '#2690
|
||||
Hint("请将文件解压后再拖入!", HintType.Critical)
|
||||
Hint("请将文件解压后再拖入!", HintType.Red)
|
||||
Return
|
||||
End If
|
||||
e.Handled = True
|
||||
@@ -783,10 +909,10 @@ Public Class FormMain
|
||||
Log("[System] 接受文件拖拽:" & FilePath & If(FilePathList.Any, $" 等 {FilePathList.Count} 个文件", ""), LogLevel.Developer)
|
||||
'基础检查
|
||||
If Directory.Exists(FilePathList.First) AndAlso Not File.Exists(FilePathList.First) Then
|
||||
Hint("请拖入一个文件,而非文件夹!", HintType.Critical)
|
||||
Hint("请拖入一个文件,而非文件夹!", HintType.Red)
|
||||
Return
|
||||
ElseIf Not File.Exists(FilePathList.First) Then
|
||||
Hint("拖入的文件不存在:" & FilePathList.First, HintType.Critical)
|
||||
Hint("拖入的文件不存在:" & FilePathList.First, HintType.Red)
|
||||
Return
|
||||
End If
|
||||
'多文件拖拽
|
||||
@@ -794,7 +920,7 @@ Public Class FormMain
|
||||
'必须要求全部为 jar 文件
|
||||
For Each File In FilePathList
|
||||
If Not {"jar", "litemod", "disabled", "old"}.Contains(File.AfterLast(".").ToLower) Then
|
||||
Hint("一次请只拖入一个文件!", HintType.Critical)
|
||||
Hint("一次请只拖入一个文件!", HintType.Red)
|
||||
Return
|
||||
End If
|
||||
Next
|
||||
@@ -813,7 +939,7 @@ Public Class FormMain
|
||||
Sub()
|
||||
Setup.Set("UiCustomType", 1)
|
||||
FrmLaunchRight.ForceRefresh()
|
||||
Hint("已加载主页自定义文件!", HintType.Finish)
|
||||
Hint("已加载主页自定义文件!", HintType.Green)
|
||||
End Sub)
|
||||
Return
|
||||
End If
|
||||
@@ -839,7 +965,7 @@ Public Class FormMain
|
||||
'错误报告分析
|
||||
Try
|
||||
Log("[System] 尝试进行错误报告分析")
|
||||
Dim Analyzer As New CrashAnalyzer(GetUuid())
|
||||
Dim Analyzer As New CrashAnalyzer
|
||||
Analyzer.Import(FilePath)
|
||||
If Not Analyzer.Prepare() Then Exit Try
|
||||
Analyzer.Analyze()
|
||||
@@ -923,9 +1049,9 @@ Public Class FormMain
|
||||
#Region "切换页面"
|
||||
|
||||
'页面种类与属性
|
||||
'注意,这一枚举在 “切换页面” EventType 中调用,应视作公开 API 的一部分
|
||||
''' <summary>
|
||||
''' 页面种类。
|
||||
''' 该枚举在自定义事件中使用,是公开 API 的一部分。
|
||||
''' </summary>
|
||||
Public Enum PageType
|
||||
''' <summary>
|
||||
@@ -971,30 +1097,21 @@ Public Class FormMain
|
||||
End Enum
|
||||
''' <summary>
|
||||
''' 次要页面种类。其数值必须与 StackPanel 中的下标一致。
|
||||
''' 该枚举在自定义事件中使用,是公开 API 的一部分。
|
||||
''' </summary>
|
||||
Public Enum PageSubType
|
||||
[Default] = 0
|
||||
DownloadInstall = 1
|
||||
DownloadClient = 4
|
||||
DownloadOptiFine = 5
|
||||
DownloadForge = 6
|
||||
DownloadNeoForge = 7
|
||||
DownloadFabric = 8
|
||||
DownloadLiteLoader = 9
|
||||
DownloadMod = 11
|
||||
DownloadPack = 12
|
||||
DownloadDataPack = 13
|
||||
DownloadResourcePack = 14
|
||||
DownloadShader = 15
|
||||
DownloadInstall = 0
|
||||
DownloadMod = 2
|
||||
DownloadPack = 3
|
||||
DownloadDataPack = 4
|
||||
DownloadResourcePack = 5
|
||||
DownloadShader = 6
|
||||
SetupLaunch = 0
|
||||
SetupUI = 1
|
||||
SetupSystem = 2
|
||||
SetupLink = 3
|
||||
LinkHiper = 1
|
||||
LinkIoi = 2
|
||||
LinkSetup = 4
|
||||
LinkHelp = 5
|
||||
LinkFeedback = 6
|
||||
SetupLink = 1
|
||||
SetupUI = 2
|
||||
SetupSystem = 3
|
||||
LinkMain = 0
|
||||
OtherHelp = 0
|
||||
OtherAbout = 1
|
||||
OtherTest = 2
|
||||
@@ -1222,8 +1339,8 @@ Public Class FormMain
|
||||
'PageGet 方法会在未设置 SubType 时指定默认值,并建立相关页面的实例
|
||||
PageChangeAnim(FrmDownloadLeft, FrmDownloadLeft.PageGet(SubType))
|
||||
Case PageType.Link '联机
|
||||
If FrmLinkLeft Is Nothing Then FrmLinkLeft = New PageLinkLeft
|
||||
PageChangeAnim(FrmLinkLeft, FrmLinkLeft.PageGet(SubType))
|
||||
If FrmLinkMain Is Nothing Then FrmLinkMain = New PageLinkMain
|
||||
PageChangeAnim(New MyPageLeft, FrmLinkMain)
|
||||
Case PageType.Setup '设置
|
||||
If FrmSetupLeft Is Nothing Then FrmSetupLeft = New PageSetupLeft
|
||||
PageChangeAnim(FrmSetupLeft, FrmSetupLeft.PageGet(SubType))
|
||||
@@ -1419,7 +1536,7 @@ Public Class FormMain
|
||||
'投降
|
||||
Public Sub AprilGiveup() Handles BtnExtraApril.Click
|
||||
If IsAprilEnabled AndAlso Not IsAprilGiveup Then
|
||||
Hint("=D", HintType.Finish)
|
||||
Hint("=D", HintType.Green)
|
||||
IsAprilGiveup = True
|
||||
FrmLaunchLeft.AprilScaleTrans.ScaleX = 1
|
||||
FrmLaunchLeft.AprilScaleTrans.ScaleY = 1
|
||||
@@ -1437,7 +1554,7 @@ Public Class FormMain
|
||||
For Each Watcher In McWatcherList
|
||||
Watcher.Kill()
|
||||
Next
|
||||
Hint("已关闭运行中的 Minecraft!", HintType.Finish)
|
||||
Hint("已关闭运行中的 Minecraft!", HintType.Green)
|
||||
Catch ex As Exception
|
||||
Log(ex, "强制关闭所有 Minecraft 失败", LogLevel.Feedback)
|
||||
End Try
|
||||
@@ -1463,7 +1580,8 @@ Public Class FormMain
|
||||
End Function
|
||||
Private Function BtnExtraBack_GetRealChild() As MyScrollViewer
|
||||
If PanMainRight.Child Is Nothing OrElse TypeOf PanMainRight.Child IsNot MyPageRight Then Return Nothing
|
||||
Return CType(PanMainRight.Child, MyPageRight).PanScroll
|
||||
Dim Page As MyPageRight = PanMainRight.Child
|
||||
Return Page.FindName(Page.PanScroll)
|
||||
End Function
|
||||
|
||||
#End Region
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -467,7 +467,7 @@ Public Module ModAnimation
|
||||
''' 按照 WPF 方式旋转控件的动画。
|
||||
''' </summary>
|
||||
''' <param name="Obj">动画的对象。它必须已经拥有了单一的 ScaleTransform 值。</param>
|
||||
''' <param name="Value">大小改变的百分比(如-0.6)。</param>
|
||||
''' <param name="Value">旋转角度(度)。</param>
|
||||
''' <param name="Time">动画长度(毫秒)。</param>
|
||||
''' <param name="Delay">动画延迟执行的时间(毫秒)。</param>
|
||||
''' <param name="Ease">插值器类型。</param>
|
||||
@@ -792,44 +792,46 @@ Public Module ModAnimation
|
||||
AniFPSTimer = AniLastTick
|
||||
AniRunning = True '标记动画执行开始
|
||||
|
||||
RunInNewThread(Sub()
|
||||
Try
|
||||
Log("[Animation] 动画线程开始")
|
||||
Do While True
|
||||
'两帧之间的间隔时间
|
||||
Dim DeltaTime As Long = MathClamp(GetTimeTick() - AniLastTick, 0, 100000)
|
||||
If DeltaTime < 3 Then GoTo Sleeper
|
||||
AniLastTick = GetTimeTick()
|
||||
'记录 FPS
|
||||
If ModeDebug Then
|
||||
If MathClamp(AniLastTick - AniFPSTimer, 0, 100000) >= 500 Then
|
||||
AniFPS = AniFPSCounter
|
||||
AniFPSCounter = 0
|
||||
AniFPSTimer = AniLastTick
|
||||
End If
|
||||
AniFPSCounter += 2
|
||||
End If
|
||||
'执行动画
|
||||
RunInUiWait(Sub()
|
||||
AniCount = 0
|
||||
AniTimer(DeltaTime * AniSpeed)
|
||||
'#If DEBUG Then
|
||||
' FrmMain.Title = "F " & AniFPS & ", A " & AniCount & ", R " & NetManage.FileRemain
|
||||
'#Else
|
||||
' If ModeDebug Then FrmMain.Title = "FPS " & AniFPS & ", 动画 " & AniCount & ", 下载中 " & NetManage.FileRemain
|
||||
'#End If
|
||||
If RandomInteger(0, 64 * If(ModeDebug, 5, 30)) = 0 AndAlso ((AniFPS < 62 AndAlso AniFPS > 0) OrElse AniCount > 4 OrElse NetManager.FileRemain <> 0) Then
|
||||
Log("[Report] FPS " & AniFPS & ", 动画 " & AniCount & ", 下载中 " & NetManager.FileRemain & "(" & GetString(NetManager.Speed) & "/s)")
|
||||
End If
|
||||
End Sub)
|
||||
RunInNewThread(
|
||||
Sub()
|
||||
Try
|
||||
Log("[Animation] 动画线程开始")
|
||||
Do While True
|
||||
'两帧之间的间隔时间
|
||||
Dim DeltaTime As Long = MathClamp(GetTimeTick() - AniLastTick, 0, 100000)
|
||||
If DeltaTime < 3 Then GoTo Sleeper
|
||||
AniLastTick = GetTimeTick()
|
||||
'记录 FPS
|
||||
If ModeDebug Then
|
||||
If MathClamp(AniLastTick - AniFPSTimer, 0, 100000) >= 500 Then
|
||||
AniFPS = AniFPSCounter
|
||||
AniFPSCounter = 0
|
||||
AniFPSTimer = AniLastTick
|
||||
End If
|
||||
AniFPSCounter += 2
|
||||
End If
|
||||
'执行动画
|
||||
RunInUiWait(
|
||||
Sub()
|
||||
AniCount = 0
|
||||
AniTimer(DeltaTime * AniSpeed)
|
||||
'#If DEBUG Then
|
||||
' FrmMain.Title = "F " & AniFPS & ", A " & AniCount & ", R " & NetManage.FileRemain
|
||||
'#Else
|
||||
' If ModeDebug Then FrmMain.Title = "FPS " & AniFPS & ", 动画 " & AniCount & ", 下载中 " & NetManage.FileRemain
|
||||
'#End If
|
||||
If RandomInteger(0, 64 * If(ModeDebug, 5, 30)) = 0 AndAlso ((AniFPS < 62 AndAlso AniFPS > 0) OrElse AniCount > 4 OrElse NetManager.FileRemain <> 0) Then
|
||||
Log("[Report] FPS " & AniFPS & ", 动画 " & AniCount & ", 下载中 " & NetManager.FileRemain & "(" & GetString(NetManager.Speed) & "/s)")
|
||||
End If
|
||||
End Sub)
|
||||
Sleeper:
|
||||
'控制 FPS
|
||||
Thread.Sleep(1)
|
||||
Loop
|
||||
Catch ex As Exception
|
||||
Log(ex, "动画帧执行失败", LogLevel.Critical)
|
||||
End Try
|
||||
End Sub, "Animation", ThreadPriority.AboveNormal)
|
||||
'控制 FPS
|
||||
Thread.Sleep(1)
|
||||
Loop
|
||||
Catch ex As Exception
|
||||
Log(ex, "动画帧执行失败", LogLevel.Critical)
|
||||
End Try
|
||||
End Sub, "Animation", ThreadPriority.AboveNormal)
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
@@ -996,7 +998,7 @@ NextAni:
|
||||
Case AniType.ScaleTransform
|
||||
Dim Obj As FrameworkElement = Ani.Obj
|
||||
If TypeOf Obj.RenderTransform IsNot ScaleTransform Then
|
||||
Obj.RenderTransformOrigin = New Point(0.5, 0.5)
|
||||
If Obj.RenderTransformOrigin = New Point(0, 0) Then Obj.RenderTransformOrigin = New Point(0.5, 0.5)
|
||||
Obj.RenderTransform = New ScaleTransform(1, 1)
|
||||
End If
|
||||
Dim Delta As Double = MathPercent(0, Ani.Value, Ani.Ease.GetDelta(Ani.TimeFinished / Ani.TimeTotal, Ani.TimePercent))
|
||||
@@ -1006,7 +1008,7 @@ NextAni:
|
||||
Case AniType.RotateTransform
|
||||
Dim Obj As FrameworkElement = Ani.Obj
|
||||
If TypeOf Obj.RenderTransform IsNot RotateTransform Then
|
||||
Obj.RenderTransformOrigin = New Point(0.5, 0.5)
|
||||
If Obj.RenderTransformOrigin = New Point(0, 0) Then Obj.RenderTransformOrigin = New Point(0.5, 0.5)
|
||||
Obj.RenderTransform = New RotateTransform(0)
|
||||
End If
|
||||
Dim Delta As Double = MathPercent(0, Ani.Value, Ani.Ease.GetDelta(Ani.TimeFinished / Ani.TimeTotal, Ani.TimePercent))
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
Imports System.Globalization
|
||||
Imports System.IO.Compression
|
||||
Imports System.Reflection
|
||||
Imports System.Net.Sockets
|
||||
Imports System.Runtime.CompilerServices
|
||||
Imports System.Security.Cryptography
|
||||
Imports System.Security.Principal
|
||||
Imports System.Text.RegularExpressions
|
||||
Imports System.Threading.Tasks
|
||||
Imports System.Xaml
|
||||
Imports Newtonsoft.Json
|
||||
|
||||
@@ -13,13 +14,13 @@ Public Module ModBase
|
||||
#Region "声明"
|
||||
|
||||
'下列版本信息由更新器自动修改
|
||||
Public Const VersionBaseName As String = "2.10.2" '不含分支前缀的显示用版本名
|
||||
Public Const VersionStandardCode As String = "2.10.2." & VersionBranchCode '标准格式的四段式版本号
|
||||
Public Const VersionBaseName As String = "2.11.1" '不含分支前缀的显示用版本名
|
||||
Public Const VersionStandardCode As String = "2.11.1." & VersionBranchCode '标准格式的四段式版本号
|
||||
Public Const CommitHash As String = "" 'Commit Hash,由 GitHub Workflow 自动替换
|
||||
#If BETA Then
|
||||
Public Const VersionCode As Integer = 359 'Release
|
||||
Public Const VersionCode As Integer = 372 'Release
|
||||
#Else
|
||||
Public Const VersionCode As Integer = 360 'Snapshot
|
||||
Public Const VersionCode As Integer = 374 'Snapshot
|
||||
#End If
|
||||
'自动生成的版本信息
|
||||
Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName
|
||||
@@ -69,7 +70,7 @@ Public Module ModBase
|
||||
''' <summary>
|
||||
''' 识别码。
|
||||
''' </summary>
|
||||
Public UniqueAddress As String = SecretGetUniqueAddress()
|
||||
Public Identify As String = SecretGetIdentify()
|
||||
''' <summary>
|
||||
''' 程序是否已结束。
|
||||
''' </summary>
|
||||
@@ -498,6 +499,7 @@ Public Module ModBase
|
||||
RealNum += Digit * Scale
|
||||
Scale *= FromRadix
|
||||
Next
|
||||
If RealNum = 0 Then Return "0"
|
||||
'转换为指定进制
|
||||
Dim Result = ""
|
||||
While RealNum > 0
|
||||
@@ -761,21 +763,9 @@ Public Module ModBase
|
||||
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 获取不包含路径与扩展名的文件名。不包含文件名将会抛出异常。
|
||||
''' </summary>
|
||||
Public Function GetFileNameWithoutExtentionFromPath(FilePath As String) As String
|
||||
Dim Name As String = GetFileNameFromPath(FilePath)
|
||||
If Name.Contains(".") Then
|
||||
Return Name.Substring(0, Name.LastIndexOfF("."))
|
||||
Else
|
||||
Return Name
|
||||
End If
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 从文件夹路径获取文件夹名。
|
||||
''' </summary>
|
||||
Public Function GetFolderNameFromPath(FolderPath As String) As String
|
||||
@@ -791,8 +781,8 @@ Public Module ModBase
|
||||
Public Sub CopyFile(FromPath As String, ToPath As String)
|
||||
Try
|
||||
'还原文件路径
|
||||
If Not FromPath.Contains(":\") Then FromPath = Path & FromPath
|
||||
If Not ToPath.Contains(":\") Then ToPath = Path & ToPath
|
||||
FromPath = If(FromPath.Contains(":\"), FromPath, Path & FromPath)
|
||||
ToPath = If(ToPath.Contains(":\"), ToPath, Path & ToPath)
|
||||
'如果复制同一个文件则跳过
|
||||
If FromPath = ToPath Then Return
|
||||
'确保目录存在
|
||||
@@ -861,7 +851,7 @@ Public Module ModBase
|
||||
''' <param name="Append">是否将文件内容追加到当前文件,而不是覆盖它。</param>
|
||||
Public Sub WriteFile(FilePath As String, Text As String, Optional Append As Boolean = False, Optional Encoding As Encoding = Nothing)
|
||||
'处理相对路径
|
||||
If Not FilePath.Contains(":\") Then FilePath = Path & FilePath
|
||||
FilePath = If(FilePath.Contains(":\"), FilePath, Path & FilePath)
|
||||
'确保目录存在
|
||||
Directory.CreateDirectory(GetPathFromFullPath(FilePath))
|
||||
'写入文件
|
||||
@@ -886,7 +876,7 @@ Public Module ModBase
|
||||
''' <param name="Append">是否将文件内容追加到当前文件,而不是覆盖它。</param>
|
||||
Public Sub WriteFile(FilePath As String, Content As Byte(), Optional Append As Boolean = False)
|
||||
'处理相对路径
|
||||
If Not FilePath.Contains(":\") Then FilePath = Path & FilePath
|
||||
FilePath = If(FilePath.Contains(":\"), FilePath, Path & FilePath)
|
||||
'确保目录存在
|
||||
Directory.CreateDirectory(GetPathFromFullPath(FilePath))
|
||||
'写入文件
|
||||
@@ -1283,7 +1273,7 @@ Re:
|
||||
Return Nothing
|
||||
Catch ex As Exception
|
||||
Log(ex, "检查文件出错")
|
||||
Return GetExceptionSummary(ex)
|
||||
Return ex.GetBrief()
|
||||
End Try
|
||||
End Function
|
||||
End Class
|
||||
@@ -1309,16 +1299,15 @@ Re:
|
||||
Else
|
||||
'以 zip 方式解压
|
||||
Using Archive = ZipFile.Open(CompressFilePath, ZipArchiveMode.Read, If(Encode, Encoding.GetEncoding("GB18030")))
|
||||
Dim TotalCount As Integer = Archive.Entries.Count
|
||||
Dim TotalCount As Long = Archive.Entries.Count
|
||||
For Each Entry As ZipArchiveEntry In Archive.Entries
|
||||
If ProgressIncrementHandler IsNot Nothing Then ProgressIncrementHandler(1 / TotalCount)
|
||||
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
|
||||
If ProgressIncrementHandler IsNot Nothing AndAlso TotalCount > 0 Then ProgressIncrementHandler(1 / TotalCount)
|
||||
Dim DestinationFullPath As String = IO.Path.Combine(DestDirectory, Entry.FullName)
|
||||
If Not IO.Path.GetFullPath(DestinationFullPath).StartsWithF(IO.Path.GetFullPath(DestDirectory), True) Then Continue For 'ZipSlip 漏洞修复
|
||||
If DestinationFullPath.EndsWithF("\") OrElse DestinationFullPath.EndsWithF("/") Then Continue For '不创建空文件夹
|
||||
Dim DestinationDirectory As String = GetPathFromFullPath(DestinationFullPath)
|
||||
Directory.CreateDirectory(DestinationDirectory)
|
||||
Entry.ExtractToFile(DestinationFullPath.Replace(DestinationDirectory, ShortenPath(DestinationDirectory)), True) '#7329
|
||||
Next
|
||||
End Using
|
||||
End If
|
||||
@@ -1329,11 +1318,20 @@ Re:
|
||||
''' </summary>
|
||||
Public Function DeleteDirectory(Path As String, Optional IgnoreIssue As Boolean = False) As Integer
|
||||
If Not Directory.Exists(Path) Then Return 0
|
||||
If Not Path.EndsWithF("\") Then Path &= "\"
|
||||
If Path.EndsWith(":\") OrElse
|
||||
Path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) & "\" OrElse
|
||||
Path = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) & "\" OrElse
|
||||
Path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) & "\" Then '#7030
|
||||
Throw New UnauthorizedAccessException("这是重要的系统文件夹,无法删除:" & Path)
|
||||
End If
|
||||
'实际删除
|
||||
Dim DeletedCount As Integer = 0
|
||||
Dim Files As String()
|
||||
Try
|
||||
Files = Directory.GetFiles(Path)
|
||||
Catch ex As DirectoryNotFoundException '#4549
|
||||
If Not Directory.Exists(Path) Then Return 0 '可能已被其他线程删除
|
||||
Log(ex, $"疑似为孤立符号链接,尝试直接删除({Path})", LogLevel.Developer)
|
||||
Directory.Delete(Path)
|
||||
Return 0
|
||||
@@ -1364,6 +1362,7 @@ RetryFile:
|
||||
RetryDir:
|
||||
Try
|
||||
Directory.Delete(Path, True)
|
||||
Catch ex As DirectoryNotFoundException
|
||||
Catch ex As Exception
|
||||
If Not RetriedDir AndAlso Not RunInUi() Then
|
||||
RetriedDir = True
|
||||
@@ -1387,31 +1386,34 @@ RetryDir:
|
||||
ToPath = ToPath.Replace("/", "\")
|
||||
If Not ToPath.EndsWithF("\") Then ToPath &= "\"
|
||||
Dim AllFiles = EnumerateFiles(FromPath).ToList
|
||||
Dim FileCount As Integer = AllFiles.Count
|
||||
Dim FileCount As Long = AllFiles.Count
|
||||
For Each File In AllFiles
|
||||
CopyFile(File.FullName, File.FullName.Replace(FromPath, ToPath))
|
||||
If ProgressIncrementHandler IsNot Nothing Then ProgressIncrementHandler(1 / FileCount)
|
||||
If ProgressIncrementHandler IsNot Nothing AndAlso FileCount > 0 Then ProgressIncrementHandler(1 / FileCount)
|
||||
Next
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 遍历文件夹中的所有文件。
|
||||
''' </summary>
|
||||
Public Function EnumerateFiles(Directory As String) As IEnumerable(Of FileInfo)
|
||||
Dim Info As New DirectoryInfo(ShortenPath(Directory))
|
||||
Dim Info As New DirectoryInfo(Directory)
|
||||
If Not Info.Exists Then Return New List(Of FileInfo)
|
||||
Return Info.EnumerateFiles("*", SearchOption.AllDirectories)
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 若路径长度大于指定值,则将长路径转换为短路径。
|
||||
''' 如果路径不存在,则返回原始路径。
|
||||
''' </summary>
|
||||
Public Function ShortenPath(LongPath As String, Optional ShortenThreshold As Integer = 247) As String
|
||||
If LongPath.Length <= ShortenThreshold Then Return LongPath
|
||||
Public Function ShortenPath(LongPath As String) As String
|
||||
If LongPath.Length <= 200 OrElse LongPath.StartsWithF("http", True) Then Return LongPath
|
||||
If Not Directory.Exists(LongPath) AndAlso Not File.Exists(LongPath) Then Return LongPath
|
||||
Dim ShortPath As New StringBuilder(260)
|
||||
GetShortPathName(LongPath, ShortPath, 260)
|
||||
Return ShortPath.ToString
|
||||
GetShortPathName(LongPath.Replace("/", "\"), ShortPath, 260) '第一个参数也会在调用后被修改,使用 Replace 后不会影响原始字符串
|
||||
ShortenPath = ShortPath.ToString
|
||||
If String.IsNullOrEmpty(ShortenPath) Then Return LongPath
|
||||
End Function
|
||||
Private Declare Function GetShortPathName Lib "kernel32" Alias "GetShortPathNameA" (ByVal lpszLongPath As String, ByVal lpszShortPath As StringBuilder, ByVal cchBuffer As Integer) As Integer
|
||||
Private Declare Function GetShortPathName Lib "kernel32" Alias "GetShortPathNameA" (lpszLongPath As String, lpszShortPath As StringBuilder, cchBuffer As Integer) As Integer
|
||||
|
||||
#End Region
|
||||
|
||||
@@ -1420,10 +1422,10 @@ RetryDir:
|
||||
Public vbRQ As Char = Convert.ToChar(8221)
|
||||
|
||||
''' <summary>
|
||||
''' 提取 Exception 的具体描述与堆栈。
|
||||
''' 提取 Exception 与 Inner Exception 的详细描述与堆栈信息。返回内容总是多于一行。
|
||||
''' </summary>
|
||||
''' <param name="ShowAllStacks">是否必须显示所有堆栈。通常用于判定堆栈信息。</param>
|
||||
Public Function GetExceptionDetail(Ex As Exception, Optional ShowAllStacks As Boolean = False) As String
|
||||
<Extension> Public Function GetDetail(Ex As Exception, Optional ShowAllStacks As Boolean = False) As String
|
||||
If Ex Is Nothing Then Return "无可用错误信息!"
|
||||
|
||||
'获取最底层的异常
|
||||
@@ -1449,32 +1451,18 @@ RetryDir:
|
||||
IsInner = True
|
||||
Loop
|
||||
|
||||
'常见错误(记得同时修改下面的)
|
||||
Dim CommonReason As String = Nothing
|
||||
If TypeOf InnerEx Is TypeLoadException OrElse TypeOf InnerEx Is BadImageFormatException OrElse TypeOf InnerEx Is MissingMethodException OrElse TypeOf InnerEx Is NotImplementedException OrElse TypeOf InnerEx Is TypeInitializationException Then
|
||||
CommonReason = "PCL 的运行环境存在问题。请尝试重新安装 .NET Framework 4.6.2 然后再试。若无法安装,请先卸载较新版本的 .NET Framework,然后再尝试安装。"
|
||||
ElseIf TypeOf InnerEx Is UnauthorizedAccessException Then
|
||||
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) DescList.Any(Function(l) l.Contains(s))) Then
|
||||
CommonReason = "你的网络环境不佳,导致难以连接到服务器。请稍后重试,或使用 VPN 以改善网络环境。"
|
||||
End If
|
||||
|
||||
'构造输出信息
|
||||
If CommonReason Is Nothing Then
|
||||
Dim UsualReason As String = AnalyzeUsualReason(InnerEx, Ex, DescList)
|
||||
If UsualReason Is Nothing Then
|
||||
Return DescList.Join(vbCrLf)
|
||||
Else
|
||||
Return CommonReason & vbCrLf & vbCrLf & "————————————" & vbCrLf & "详细错误信息:" & vbCrLf & DescList.Join(vbCrLf)
|
||||
Return UsualReason & vbCrLf & vbCrLf & "————————————" & vbCrLf & "详细错误信息:" & vbCrLf & DescList.Join(vbCrLf)
|
||||
End If
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 提取 Exception 描述,汇总到一行。
|
||||
''' 提取 Exception 与 Inner Exception 的描述,汇总到一行。
|
||||
''' </summary>
|
||||
Public Function GetExceptionSummary(Ex As Exception) As String
|
||||
<Extension> Public Function GetBrief(Ex As Exception) As String
|
||||
If Ex Is Nothing Then Return "无可用错误信息!"
|
||||
|
||||
'获取最底层的异常
|
||||
@@ -1492,29 +1480,32 @@ RetryDir:
|
||||
DescList = DescList.Distinct.ToList
|
||||
Dim Desc As String = Join(DescList, vbCrLf & "→ ")
|
||||
|
||||
'常见错误(记得同时修改上面的)
|
||||
Dim CommonReason As String = Nothing
|
||||
If TypeOf InnerEx Is TypeLoadException OrElse TypeOf InnerEx Is BadImageFormatException OrElse TypeOf InnerEx Is MissingMethodException OrElse TypeOf InnerEx Is NotImplementedException OrElse TypeOf InnerEx Is TypeInitializationException Then
|
||||
CommonReason = "PCL 的运行环境存在问题。请尝试重新安装 .NET Framework 4.6.2 然后再试。若无法安装,请先卸载较新版本的 .NET Framework,然后再尝试安装。"
|
||||
ElseIf TypeOf InnerEx Is UnauthorizedAccessException Then
|
||||
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 以改善网络环境。"
|
||||
End If
|
||||
|
||||
'构造输出信息
|
||||
If CommonReason IsNot Nothing Then
|
||||
Return CommonReason & "详细错误:" & DescList.First
|
||||
Dim UsualReason As String = AnalyzeUsualReason(InnerEx, Ex, DescList)
|
||||
If UsualReason IsNot Nothing Then
|
||||
Return UsualReason & "详细错误:" & DescList.First
|
||||
Else
|
||||
DescList.Reverse() '让最深层错误在最左边
|
||||
Return Join(DescList, " ← ")
|
||||
End If
|
||||
End Function
|
||||
Private Function AnalyzeUsualReason(InnerEx As Exception, OuterEx As Exception, DescList As List(Of String)) As String
|
||||
If TypeOf InnerEx Is TypeLoadException OrElse TypeOf InnerEx Is BadImageFormatException OrElse TypeOf InnerEx Is MissingMethodException OrElse TypeOf InnerEx Is NotImplementedException OrElse TypeOf InnerEx Is TypeInitializationException Then
|
||||
Return "PCL 的运行环境存在问题。请尝试重新安装 .NET Framework 4.6.2 然后再试。若无法安装,请先卸载较新版本的 .NET Framework,然后再尝试安装。"
|
||||
ElseIf TypeOf InnerEx Is UnauthorizedAccessException Then
|
||||
Return "PCL 的权限不足。请尝试右键 PCL 选择以管理员身份运行。"
|
||||
ElseIf TypeOf InnerEx Is OutOfMemoryException Then
|
||||
Return "你的电脑运行内存不足,导致 PCL 无法继续运行。请在关闭一部分不需要的程序后再试。"
|
||||
ElseIf TypeOf InnerEx Is Runtime.InteropServices.COMException Then
|
||||
Return "由于操作系统或显卡存在问题,导致出现错误。请尝试重启 PCL。"
|
||||
ElseIf TypeOf InnerEx Is SocketException AndAlso DescList.Any(Function(l) l.Contains("WSAStartup")) Then
|
||||
Return "请尝试卸载中国移动云盘,然后再试。"
|
||||
ElseIf OuterEx.IsNetworkRelated() Then
|
||||
Return "你的网络环境不佳,请稍后再试,或使用 VPN 改善网络环境。"
|
||||
Else
|
||||
Return Nothing
|
||||
End If
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 返回一个枚举对应的字符串。
|
||||
@@ -1524,6 +1515,19 @@ RetryDir:
|
||||
Return [Enum].GetName(EnumData.GetType, EnumData)
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 将字符串转换为枚举类型,支持数字或原文两种格式,若为空字符串则返回 0。
|
||||
''' 若转换失败会抛出异常。
|
||||
''' </summary>
|
||||
<Extension> Public Function ParseToEnum(Of T)(Str As String) As T
|
||||
If String.IsNullOrEmpty(Str) Then
|
||||
Return CType(0, Object)
|
||||
ElseIf IsNumeric(Str) Then
|
||||
Return CType(CInt(Str), Object)
|
||||
Else
|
||||
Return [Enum].Parse(GetType(T), Str, True)
|
||||
End If
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 将文件大小转化为适合的文本形式,如“1.28 M”。
|
||||
''' </summary>
|
||||
''' <param name="FileSize">以字节为单位的大小表示。</param>
|
||||
@@ -1551,12 +1555,12 @@ RetryDir:
|
||||
''' <summary>
|
||||
''' 获取 JSON 对象。
|
||||
''' </summary>
|
||||
Public Function GetJson(Data As String)
|
||||
Public Function GetJson(Data As String) As JToken
|
||||
Try
|
||||
Return JsonConvert.DeserializeObject(Data, New JsonSerializerSettings With {.DateTimeZoneHandling = DateTimeZoneHandling.Local})
|
||||
Catch ex As Exception
|
||||
Dim Length As Integer = If(Data, "").Length
|
||||
Throw New Exception("格式化 JSON 失败:" & If(Length > 2000, Data.Substring(0, 500) & $"...(全长 {Length} 个字符)..." & Right(Data, 500), Data))
|
||||
Throw New Exception("格式化 JSON 失败:" & If(Length > 2000, Data.Substring(0, 500) & $"...(全长 {Length} 个字符)..." & Right(Data, 500), Data), ex)
|
||||
End Try
|
||||
End Function
|
||||
|
||||
@@ -1724,6 +1728,7 @@ RetryDir:
|
||||
''' </summary>
|
||||
<Extension> <MethodImpl(MethodImplOptions.AggressiveInlining)>
|
||||
Public Function StartsWithF(Str As String, Prefix As String, Optional IgnoreCase As Boolean = False) As Boolean
|
||||
If Str Is Nothing Then Return False
|
||||
Return Str.StartsWith(Prefix, If(IgnoreCase, StringComparison.OrdinalIgnoreCase, StringComparison.Ordinal))
|
||||
End Function
|
||||
''' <summary>
|
||||
@@ -1731,6 +1736,7 @@ RetryDir:
|
||||
''' </summary>
|
||||
<Extension> <MethodImpl(MethodImplOptions.AggressiveInlining)>
|
||||
Public Function EndsWithF(Str As String, Suffix As String, Optional IgnoreCase As Boolean = False) As Boolean
|
||||
If Str Is Nothing Then Return False
|
||||
Return Str.EndsWith(Suffix, If(IgnoreCase, StringComparison.OrdinalIgnoreCase, StringComparison.Ordinal))
|
||||
End Function
|
||||
''' <summary>
|
||||
@@ -1738,6 +1744,7 @@ RetryDir:
|
||||
''' </summary>
|
||||
<Extension> <MethodImpl(MethodImplOptions.AggressiveInlining)>
|
||||
Public Function ContainsF(Str As String, SubStr As String, Optional IgnoreCase As Boolean = False) As Boolean
|
||||
If Str Is Nothing Then Return False
|
||||
Return Str.IndexOf(SubStr, If(IgnoreCase, StringComparison.OrdinalIgnoreCase, StringComparison.Ordinal)) >= 0
|
||||
End Function
|
||||
''' <summary>
|
||||
@@ -1774,6 +1781,7 @@ RetryDir:
|
||||
''' 如果输入有误,返回 0。
|
||||
''' </summary>
|
||||
Public Function Val(Str As Object) As Double
|
||||
If Str Is Nothing Then Return 0
|
||||
Try
|
||||
Return If(TypeOf Str Is String AndAlso Str = "&", 0, Conversion.Val(Str))
|
||||
Catch
|
||||
@@ -2029,8 +2037,8 @@ RetryDir:
|
||||
'构造函数
|
||||
Public Sub New()
|
||||
End Sub
|
||||
Public Sub New(data As IEnumerable(Of KeyValuePair(Of TKey, TValue)))
|
||||
For Each DataItem In data
|
||||
Public Sub New(Data As IEnumerable(Of KeyValuePair(Of TKey, TValue)))
|
||||
For Each DataItem In Data
|
||||
_Dictionary.Add(DataItem.Key, DataItem.Value)
|
||||
Next
|
||||
End Sub
|
||||
@@ -2131,6 +2139,42 @@ RetryDir:
|
||||
End Function
|
||||
End Class
|
||||
|
||||
''' <summary>
|
||||
''' 将二维数组转换为字典。
|
||||
''' </summary>
|
||||
<Extension> Public Function ToDictionary(Of T)(arr As T(,)) As Dictionary(Of T, T)
|
||||
Dim result As New Dictionary(Of T, T)
|
||||
If arr.Length = 0 Then Return result
|
||||
If arr.GetLength(1) <> 2 Then Throw New ArgumentException("数组必须为两列,第一列为 Key,第二列为 Value。")
|
||||
For i As Integer = 0 To arr.GetLength(0) - 1
|
||||
result(arr(i, 0)) = arr(i, 1)
|
||||
Next
|
||||
Return result
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 为 Task 设置超时,在超时时抛出 TimeoutException。
|
||||
''' </summary>
|
||||
<Extension> Public Function GetResultWithTimeout(Of T)(TargetTask As Task(Of T), TokenSource As CancellationTokenSource, TimeoutMs As Integer) As T
|
||||
Dim DelayTask = Task.Delay(TimeoutMs)
|
||||
If Task.WhenAny(TargetTask, DelayTask).GetAwaiter().GetResult() Is DelayTask Then
|
||||
TokenSource.Cancel()
|
||||
Throw New TimeoutException($"任务超时({TimeoutMs} ms)")
|
||||
End If
|
||||
Return TargetTask.GetAwaiter().GetResult()
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 为 Task 设置超时,在超时时抛出 TimeoutException。
|
||||
''' </summary>
|
||||
<Extension> Public Sub GetResultWithTimeout(TargetTask As Task, TokenSource As CancellationTokenSource, TimeoutMs As Integer)
|
||||
Dim DelayTask = Task.Delay(TimeoutMs)
|
||||
If Task.WhenAny(TargetTask, DelayTask).GetAwaiter().GetResult() Is DelayTask Then
|
||||
TokenSource.Cancel()
|
||||
Throw New TimeoutException($"任务超时({TimeoutMs} ms)")
|
||||
End If
|
||||
TargetTask.GetAwaiter().GetResult()
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 可用于临时存放文件的,不含任何特殊字符的文件夹路径,以“\”结尾。
|
||||
''' </summary>
|
||||
@@ -2173,16 +2217,16 @@ RetryDir:
|
||||
''' 返回程序的返回代码,如果运行失败将抛出异常。
|
||||
''' </summary>
|
||||
Public Function RunAsAdmin(Argument As String) As Integer
|
||||
Dim NewProcess = Process.Start(New ProcessStartInfo(PathWithName) With {.Verb = "runas", .Arguments = Argument})
|
||||
Dim NewProcess = StartProcess(New ProcessStartInfo(PathWithName) With {.Verb = "runas", .Arguments = Argument})
|
||||
NewProcess.WaitForExit()
|
||||
Return NewProcess.ExitCode
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 判断当前系统语言是否为 zh-CN。
|
||||
''' 判断当前系统语言是否为中文。
|
||||
''' </summary>
|
||||
Public Function IsSystemLanguageChinese() As Boolean
|
||||
Return CultureInfo.CurrentCulture.Name = "zh-CN" OrElse CultureInfo.CurrentUICulture.Name = "zh-CN"
|
||||
Return CultureInfo.CurrentCulture.Name.StartsWithF("zh-") OrElse CultureInfo.CurrentUICulture.Name.StartsWithF("zh-")
|
||||
End Function
|
||||
|
||||
Private Uuid As Integer = 1
|
||||
@@ -2225,6 +2269,9 @@ NextElement:
|
||||
Next i
|
||||
Return ResultArray
|
||||
End Function
|
||||
<Extension> Public Function Any(Arr As UIElementCollection) As Boolean
|
||||
Return Arr?.Count > 0
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 获取格式类似于“11:08:52.037”的当前时间的字符串。
|
||||
@@ -2329,46 +2376,21 @@ NextElement:
|
||||
End Class
|
||||
|
||||
''' <summary>
|
||||
''' 前台运行文件。
|
||||
''' 启动进程并返回进程实例,若失败则抛出异常。
|
||||
''' 会自动进行长路径处理。
|
||||
''' </summary>
|
||||
''' <param name="FileName">文件名。可以为“notepad”等缩写。</param>
|
||||
''' <param name="Arguments">运行参数。</param>
|
||||
Public Sub ShellOnly(FileName As String, Optional Arguments As String = "")
|
||||
Try
|
||||
FileName = ShortenPath(FileName)
|
||||
Using Program As New Process
|
||||
Program.StartInfo.Arguments = Arguments
|
||||
Program.StartInfo.FileName = FileName
|
||||
Log("[System] 执行外部命令:" & FileName & " " & Arguments)
|
||||
Program.Start()
|
||||
End Using
|
||||
Catch ex As Exception
|
||||
Log(ex, "打开文件或程序失败:" & FileName, LogLevel.Msgbox)
|
||||
End Try
|
||||
End Sub
|
||||
Public Function StartProcess(FileName As String, Optional Arguments As String = "") As Process
|
||||
Return StartProcess(New ProcessStartInfo With {.FileName = FileName, .Arguments = Arguments})
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 前台运行文件并返回返回值。
|
||||
''' 启动进程并返回进程实例,若失败则抛出异常。
|
||||
''' 会自动进行长路径处理。
|
||||
''' </summary>
|
||||
''' <param name="FileName">文件名。可以为“notepad”等缩写。</param>
|
||||
''' <param name="Arguments">运行参数。</param>
|
||||
''' <param name="Timeout">等待该程序结束的最长时间(毫秒)。超时会返回 Result.Timeout。</param>
|
||||
Public Function ShellAndGetExitCode(FileName As String, Optional Arguments As String = "", Optional Timeout As Integer = 1000000) As ProcessReturnValues
|
||||
Try
|
||||
Using Program As New Process
|
||||
Program.StartInfo.Arguments = Arguments
|
||||
Program.StartInfo.FileName = FileName
|
||||
Log("[System] 执行外部命令并等待返回码:" & FileName & " " & Arguments)
|
||||
Program.Start()
|
||||
If Program.WaitForExit(Timeout) Then
|
||||
Return Program.ExitCode
|
||||
Else
|
||||
Return ProcessReturnValues.Timeout
|
||||
End If
|
||||
End Using
|
||||
Catch ex As Exception
|
||||
Log(ex, "执行命令失败:" & FileName, LogLevel.Msgbox)
|
||||
Return ProcessReturnValues.Fail
|
||||
End Try
|
||||
Public Function StartProcess(StartInfo As ProcessStartInfo) As Process
|
||||
StartInfo.FileName = ShortenPath(StartInfo.FileName)
|
||||
If Not String.IsNullOrEmpty(StartInfo.WorkingDirectory) Then StartInfo.WorkingDirectory = ShortenPath(StartInfo.WorkingDirectory)
|
||||
Log("[System] 启动进程:" & StartInfo.FileName & " " & StartInfo.Arguments)
|
||||
Return Process.Start(StartInfo)
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 静默运行文件并返回输出流字符串。执行失败会抛出异常。
|
||||
@@ -2376,15 +2398,18 @@ NextElement:
|
||||
''' <param name="FileName">文件名。可以为“notepad”等缩写。</param>
|
||||
''' <param name="Arguments">运行参数。</param>
|
||||
''' <param name="Timeout">等待该程序结束的最长时间(毫秒)。超时会抛出错误。</param>
|
||||
Public Function ShellAndGetOutput(FileName As String, Optional Arguments As String = "", Optional Timeout As Integer = 1000000, Optional WorkingDirectory As String = Nothing) As String
|
||||
Public Function StartProcessAndGetOutput(FileName As String, Optional Arguments As String = "", Optional Timeout As Integer = 1000000,
|
||||
Optional WorkingDirectory As String = Nothing, Optional Encoding As Encoding = Nothing, Optional PrintLog As Boolean = True) As String
|
||||
Dim Info = New ProcessStartInfo With {
|
||||
.Arguments = Arguments,
|
||||
.FileName = FileName,
|
||||
.FileName = ShortenPath(FileName),
|
||||
.UseShellExecute = False,
|
||||
.CreateNoWindow = True,
|
||||
.RedirectStandardError = True,
|
||||
.RedirectStandardOutput = True,
|
||||
.WorkingDirectory = ShortenPath(If(WorkingDirectory, Path.TrimEnd("\"c)))
|
||||
.WorkingDirectory = ShortenPath(If(WorkingDirectory, Path.TrimEnd("\"c))),
|
||||
.StandardErrorEncoding = Encoding,
|
||||
.StandardOutputEncoding = Encoding
|
||||
}
|
||||
If WorkingDirectory IsNot Nothing Then
|
||||
If Info.EnvironmentVariables.ContainsKey("appdata") Then
|
||||
@@ -2393,8 +2418,8 @@ NextElement:
|
||||
Info.EnvironmentVariables.Add("appdata", WorkingDirectory)
|
||||
End If
|
||||
End If
|
||||
Log("[System] 执行外部命令并等待返回结果:" & FileName & " " & Arguments)
|
||||
Using Program As New Process() With {.StartInfo = Info}
|
||||
If PrintLog Then 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)
|
||||
@@ -2462,7 +2487,7 @@ NextElement:
|
||||
''' </summary>
|
||||
Public Sub RunInThread(Action As Action)
|
||||
If RunInUi() Then
|
||||
RunInNewThread(Action, "Runtime Invoke " & GetUuid() & "#")
|
||||
RunInNewThread(Action, "Invoke " & GetUuid())
|
||||
Else
|
||||
Action()
|
||||
End If
|
||||
@@ -2496,17 +2521,14 @@ NextElement:
|
||||
''' <summary>
|
||||
''' 尝试从字典中获取某项,如果该项不存在,则返回默认值。
|
||||
''' </summary>
|
||||
<Extension> Public Function GetOrDefault(Of TKey, TValue)(Dict As Dictionary(Of TKey, TValue), Key As TKey, Optional DefaultValue As TValue = Nothing) As TValue
|
||||
If Dict.ContainsKey(Key) Then
|
||||
Return Dict(Key)
|
||||
Else
|
||||
Return DefaultValue
|
||||
End If
|
||||
<Extension> Public Function GetOrDefault(Of TKey, TValue)(Dict As IDictionary(Of TKey, TValue), Key As TKey, Optional DefaultValue As TValue = Nothing) As TValue
|
||||
Dim Result As TValue
|
||||
Return If(Dict.TryGetValue(Key, Result), Result, DefaultValue)
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 将某项添加到以列表作为值的字典中。
|
||||
''' </summary>
|
||||
<Extension> Public Sub AddToList(Of TKey, TValue)(Dict As Dictionary(Of TKey, List(Of TValue)), Key As TKey, Value As TValue)
|
||||
<Extension> Public Sub AddToList(Of TKey, TValue)(Dict As IDictionary(Of TKey, List(Of TValue)), Key As TKey, Value As TValue)
|
||||
If Dict.ContainsKey(Key) Then
|
||||
Dict(Key).Add(Value)
|
||||
Else
|
||||
@@ -2550,11 +2572,11 @@ NextElement:
|
||||
''' </summary>
|
||||
Public Sub OpenWebsite(Url As String)
|
||||
Try
|
||||
If Not Url.StartsWithF("http", True) AndAlso Not Url.StartsWithF("minecraft://", True) Then
|
||||
If Not Url.StartsWithF("http", True) AndAlso Not Url.StartsWithF("minecraft://", True) AndAlso Not Url.StartsWithF("minecraft-preview://", True) Then
|
||||
Throw New Exception(Url & " 不是一个有效的网址,它必须以 http 开头!")
|
||||
End If
|
||||
Log("[System] 正在打开网页:" & Url)
|
||||
Process.Start(Url)
|
||||
StartProcess(Url)
|
||||
Catch ex As Exception
|
||||
Log(ex, "无法打开网页(" & Url & ")")
|
||||
ClipboardSet(Url, False)
|
||||
@@ -2568,12 +2590,12 @@ NextElement:
|
||||
''' </summary>
|
||||
Public Sub OpenExplorer(Location As String)
|
||||
Try
|
||||
Location = ShortenPath(Location.Replace("/", "\").Trim(" "c, """"c))
|
||||
Location = Location.Replace("/", "\").Trim(" "c, """"c)
|
||||
Log("[System] 正在打开资源管理器:" & Location)
|
||||
If Location.EndsWith("\") Then
|
||||
ShellOnly(Location)
|
||||
StartProcess(Location)
|
||||
Else
|
||||
ShellOnly("explorer", $"/select,""{Location}""")
|
||||
StartProcess("explorer", $"/select,""{Location}""")
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "打开资源管理器失败,请尝试关闭安全软件(如 360 安全卫士)", LogLevel.Msgbox)
|
||||
@@ -2592,7 +2614,7 @@ Retry:
|
||||
RunInUi(
|
||||
Sub()
|
||||
My.Computer.Clipboard.Clear()
|
||||
My.Computer.Clipboard.SetText(Text)
|
||||
If Not String.IsNullOrEmpty(Text) Then My.Computer.Clipboard.SetText(Text)
|
||||
End Sub)
|
||||
Catch ex As Exception
|
||||
RetryCount += 1
|
||||
@@ -2603,9 +2625,24 @@ Retry:
|
||||
Log(ex, "可能由于剪贴板被其他程序占用,文本复制失败", LogLevel.Hint)
|
||||
End If
|
||||
End Try
|
||||
If ShowSuccessHint Then Hint("已成功复制!", HintType.Finish)
|
||||
If ShowSuccessHint Then Hint("已成功复制!", HintType.Green)
|
||||
End Sub)
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 获取剪贴板文本。将在 UI 线程运行,且不会抛出异常。
|
||||
''' </summary>
|
||||
Public Function ClipboardGetText() As String
|
||||
Dim Result As String = Nothing
|
||||
RunInUiWait(
|
||||
Sub()
|
||||
Try
|
||||
If My.Computer.Clipboard.ContainsText() Then Result = My.Computer.Clipboard.GetText()
|
||||
Catch ex As Exception
|
||||
Log(ex, "获取剪贴板文本失败")
|
||||
End Try
|
||||
End Sub)
|
||||
Return Result
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 以 Byte() 形式获取程序中的资源。
|
||||
@@ -2625,10 +2662,6 @@ Retry:
|
||||
''' 相对增减控件的左边距。
|
||||
''' </summary>
|
||||
Public Sub DeltaLeft(control As FrameworkElement, newValue As Double)
|
||||
'安全性检查
|
||||
DebugAssert(Not Double.IsNaN(newValue))
|
||||
DebugAssert(Not Double.IsInfinity(newValue))
|
||||
|
||||
If TypeOf control Is Window Then
|
||||
'窗口改变
|
||||
CType(control, Window).Left += newValue
|
||||
@@ -2640,8 +2673,6 @@ Retry:
|
||||
Case HorizontalAlignment.Right
|
||||
control.Margin = New Thickness(control.Margin.Left, control.Margin.Top, control.Margin.Right - newValue, control.Margin.Bottom)
|
||||
'control.Margin = New Thickness(control.Margin.Left, control.Margin.Top, CType(control.Parent, Object).ActualWidth - control.ActualWidth - newValue, control.Margin.Bottom)
|
||||
Case Else
|
||||
DebugAssert(False)
|
||||
End Select
|
||||
End If
|
||||
End Sub
|
||||
@@ -2649,17 +2680,12 @@ Retry:
|
||||
''' 设置控件的左边距。(仅针对置左控件)
|
||||
''' </summary>
|
||||
Public Sub SetLeft(control As FrameworkElement, newValue As Double)
|
||||
DebugAssert(control.HorizontalAlignment = HorizontalAlignment.Left)
|
||||
control.Margin = New Thickness(newValue, control.Margin.Top, control.Margin.Right, control.Margin.Bottom)
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 相对增减控件的上边距。
|
||||
''' </summary>
|
||||
Public Sub DeltaTop(control As FrameworkElement, newValue As Double)
|
||||
'安全性检查
|
||||
DebugAssert(Not Double.IsNaN(newValue))
|
||||
DebugAssert(Not Double.IsInfinity(newValue))
|
||||
|
||||
If TypeOf control Is Window Then
|
||||
'窗口改变
|
||||
CType(control, Window).Top += newValue
|
||||
@@ -2671,11 +2697,8 @@ Retry:
|
||||
Case VerticalAlignment.Bottom
|
||||
control.Margin = New Thickness(control.Margin.Left, control.Margin.Top, control.Margin.Right, control.Margin.Bottom - newValue)
|
||||
'control.Margin = New Thickness(control.Margin.Left, control.Margin.Top, CType(control.Parent, Object).ActualWidth - control.ActualWidth - newValue, control.Margin.Bottom)
|
||||
Case Else
|
||||
DebugAssert(False)
|
||||
End Select
|
||||
End If
|
||||
|
||||
'If Double.IsNaN(newValue) OrElse Double.IsInfinity(newValue) Then Return '安全性检查
|
||||
'Select Case control.VerticalAlignment
|
||||
' Case VerticalAlignment.Top, VerticalAlignment.Stretch, VerticalAlignment.Center
|
||||
@@ -2689,7 +2712,6 @@ Retry:
|
||||
''' 设置控件的顶边距。(仅针对置上控件)
|
||||
''' </summary>
|
||||
Public Sub SetTop(control As FrameworkElement, newValue As Double)
|
||||
DebugAssert(control.VerticalAlignment = VerticalAlignment.Top)
|
||||
control.Margin = New Thickness(control.Margin.Left, newValue, control.Margin.Right, control.Margin.Bottom)
|
||||
End Sub
|
||||
|
||||
@@ -2755,16 +2777,21 @@ Retry:
|
||||
''' 将 XML 转换为对应 UI 对象。
|
||||
''' </summary>
|
||||
Public Function GetObjectFromXML(Str As String) As Object
|
||||
Str = Str. '兼容旧版自定义事件写法
|
||||
Replace("EventType=""", "local:CustomEventService.EventType=""").
|
||||
Replace("EventData=""", "local:CustomEventService.EventData=""").
|
||||
Replace("Property=""EventType""", "Property=""local:CustomEventService.EventType""").
|
||||
Replace("Property=""EventData""", "Property=""local:CustomEventService.EventData""")
|
||||
Using Stream As New MemoryStream(Encoding.UTF8.GetBytes(Str))
|
||||
'类型检查
|
||||
Using Reader As New XamlXmlReader(Stream)
|
||||
While Reader.Read()
|
||||
For Each BlackListType In {GetType(WebBrowser), GetType(Frame), GetType(MediaElement), GetType(ObjectDataProvider), GetType(XamlReader), GetType(Window), GetType(XmlDataProvider)}
|
||||
If Reader.Type IsNot Nothing AndAlso BlackListType.IsAssignableFrom(Reader.Type.UnderlyingType) Then Throw New UnauthorizedAccessException($"不允许使用 {BlackListType.Name} 类型。")
|
||||
If Reader.Value IsNot Nothing AndAlso Reader.Value = BlackListType.Name Then Throw New UnauthorizedAccessException($"不允许使用 {BlackListType.Name} 值。")
|
||||
If Reader.Type IsNot Nothing AndAlso BlackListType.IsAssignableFrom(Reader.Type.UnderlyingType) Then Throw New UnauthorizedAccessException($"基于安全考虑,不允许使用 {BlackListType.Name} 类型。")
|
||||
If Reader.Value IsNot Nothing AndAlso Reader.Value = BlackListType.Name Then Throw New UnauthorizedAccessException($"基于安全考虑,不允许使用 {BlackListType.Name} 值。")
|
||||
Next
|
||||
For Each BlackListMember In {"Code", "FactoryMethod", "Static"}
|
||||
If Reader.Member IsNot Nothing AndAlso Reader.Member.Name = BlackListMember Then Throw New UnauthorizedAccessException($"不允许使用 {BlackListMember} 成员。")
|
||||
If Reader.Member IsNot Nothing AndAlso Reader.Member.Name = BlackListMember Then Throw New UnauthorizedAccessException($"基于安全考虑,不允许使用 {BlackListMember} 成员。")
|
||||
Next
|
||||
End While
|
||||
End Using
|
||||
@@ -2870,7 +2897,7 @@ Retry:
|
||||
File.Create(Path & "PCL\Log1.txt").Dispose()
|
||||
Catch ex As IOException
|
||||
IsInitSuccess = False
|
||||
Hint("可能同时开启了多个 PCL,程序可能会出现未知问题!", HintType.Critical)
|
||||
Hint("可能同时开启了多个 PCL,程序可能会出现未知问题!", HintType.Red)
|
||||
Log(ex, "日志初始化失败(疑似文件占用问题)")
|
||||
Catch ex As Exception
|
||||
IsInitSuccess = False
|
||||
@@ -2922,7 +2949,8 @@ Retry:
|
||||
'处理错误会导致再次调用 Log() 导致无限循环
|
||||
|
||||
'输出日志
|
||||
Dim AppendText As String = "[" & GetTimeNow() & "] " & Text & vbCrLf '减轻同步锁占用
|
||||
Dim AppendText As String = $"[{GetTimeNow()}] <{If(Thread.CurrentThread.Name = "", "主线程", Thread.CurrentThread.Name)}> {Text}{vbCrLf}" '减轻同步锁占用
|
||||
AppendText = FilterUserName(FilterAccessToken(AppendText, "*"), "*")
|
||||
If ModeDebug Then
|
||||
SyncLock LogListLock
|
||||
LogList.Append(AppendText)
|
||||
@@ -2942,16 +2970,16 @@ Retry:
|
||||
Select Case Level
|
||||
#If DEBUG Then
|
||||
Case LogLevel.Developer
|
||||
Hint("[开发者模式] " & Text, HintType.Info, False)
|
||||
Hint("[开发者模式] " & Text, HintType.Blue, False)
|
||||
Case LogLevel.Debug
|
||||
Hint("[调试模式] " & Text, HintType.Info, False)
|
||||
Hint("[调试模式] " & Text, HintType.Blue, False)
|
||||
#Else
|
||||
Case LogLevel.Developer
|
||||
Case LogLevel.Debug
|
||||
If ModeDebug Then Hint("[调试模式] " & Text, HintType.Info, False)
|
||||
If ModeDebug Then Hint("[调试模式] " & Text, HintType.Blue, False)
|
||||
#End If
|
||||
Case LogLevel.Hint
|
||||
Hint(Text, HintType.Critical, False)
|
||||
Hint(Text, HintType.Red, False)
|
||||
Case LogLevel.Msgbox
|
||||
MyMsgBox(Text, Title, IsWarn:=True)
|
||||
Case LogLevel.Feedback
|
||||
@@ -2973,6 +3001,13 @@ Retry:
|
||||
End If
|
||||
End Select
|
||||
|
||||
'上报
|
||||
If Level >= LogLevel.Hint Then
|
||||
Telemetry("PCL 错误",
|
||||
"Level", GetStringFromEnum(Level),
|
||||
"Title", FilterUserName(FilterAccessToken(Title, "*"), "*"),
|
||||
"Exception", FilterUserName(FilterAccessToken(Text, "*"), "*"))
|
||||
End If
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 输出错误信息。
|
||||
@@ -2983,10 +3018,11 @@ Retry:
|
||||
If TypeOf Ex Is ThreadInterruptedException Then Return
|
||||
|
||||
'获取错误信息
|
||||
Dim ExFull As String = Desc & ":" & GetExceptionDetail(Ex)
|
||||
Dim ExFull As String = $"{Desc}:{Ex.GetDetail()}"
|
||||
|
||||
'输出日志
|
||||
Dim AppendText As String = "[" & GetTimeNow() & "] " & Desc & ":" & GetExceptionDetail(Ex, True) & vbCrLf '减轻同步锁占用
|
||||
Dim AppendText As String = $"[{GetTimeNow()}] <{If(Thread.CurrentThread.Name = "", "主线程", Thread.CurrentThread.Name)}> {Desc}:{Ex.GetDetail(True)}{vbCrLf}" '减轻同步锁占用
|
||||
AppendText = FilterUserName(FilterAccessToken(AppendText, "*"), "*")
|
||||
If ModeDebug Then
|
||||
SyncLock LogListLock
|
||||
LogList.Append(AppendText)
|
||||
@@ -3004,20 +3040,20 @@ Retry:
|
||||
Case LogLevel.Normal
|
||||
#If DEBUG Then
|
||||
Case LogLevel.Developer
|
||||
Dim ExLine As String = Desc & ":" & GetExceptionSummary(Ex)
|
||||
Hint("[开发者模式] " & ExLine, HintType.Info, False)
|
||||
Dim ExLine As String = Desc & ":" & Ex.GetBrief()
|
||||
Hint("[开发者模式] " & ExLine, HintType.Blue, False)
|
||||
Case LogLevel.Debug
|
||||
Dim ExLine As String = Desc & ":" & GetExceptionSummary(Ex)
|
||||
Hint("[调试模式] " & ExLine, HintType.Info, False)
|
||||
Dim ExLine As String = Desc & ":" & Ex.GetBrief()
|
||||
Hint("[调试模式] " & ExLine, HintType.Blue, False)
|
||||
#Else
|
||||
Case LogLevel.Developer
|
||||
Case LogLevel.Debug
|
||||
Dim ExLine As String = Desc & ":" & GetExceptionSummary(Ex)
|
||||
If ModeDebug Then Hint("[调试模式] " & ExLine, HintType.Info, False)
|
||||
Dim ExLine As String = Desc & ":" & Ex.GetBrief()
|
||||
If ModeDebug Then Hint("[调试模式] " & ExLine, HintType.Blue, False)
|
||||
#End If
|
||||
Case LogLevel.Hint
|
||||
Dim ExLine As String = Desc & ":" & GetExceptionSummary(Ex)
|
||||
Hint(ExLine, HintType.Critical, False)
|
||||
Dim ExLine As String = Desc & ":" & Ex.GetBrief()
|
||||
Hint(ExLine, HintType.Red, False)
|
||||
Case LogLevel.Msgbox
|
||||
MyMsgBox(ExFull, Title, IsWarn:=True)
|
||||
Case LogLevel.Feedback
|
||||
@@ -3039,6 +3075,13 @@ Retry:
|
||||
End If
|
||||
End Select
|
||||
|
||||
'上报
|
||||
If Level >= LogLevel.Hint Then
|
||||
Telemetry("PCL 异常",
|
||||
"Level", GetStringFromEnum(Level),
|
||||
"Title", FilterUserName(FilterAccessToken(Title, "*"), "*"),
|
||||
"Exception", FilterUserName(FilterAccessToken(ExFull, "*"), "*"))
|
||||
End If
|
||||
End Sub
|
||||
|
||||
'反馈
|
||||
@@ -3048,7 +3091,7 @@ Retry:
|
||||
If ForceOpenLog OrElse (ShowMsgbox AndAlso MyMsgBox("若你在汇报一个 Bug,请点击 打开文件夹 按钮,并上传 Log(1~5).txt 中包含错误信息的文件。" & vbCrLf & "游戏崩溃一般与启动器无关,请不要因为游戏崩溃而提交反馈。", "反馈提交提醒", "打开文件夹", "不需要") = 1) Then
|
||||
OpenExplorer(Path & "PCL\Log1.txt")
|
||||
End If
|
||||
OpenWebsite("https://github.com/Hex-Dragon/PCL2/issues/")
|
||||
OpenWebsite("https://github.com/Meloong-Git/PCL/issues/")
|
||||
End Sub
|
||||
Public Function CanFeedback(ShowHint As Boolean) As Boolean
|
||||
If False.Equals(PageSetupSystem.IsLauncherNewest) Then
|
||||
@@ -3075,9 +3118,24 @@ Retry:
|
||||
"文件位置:" & Path)
|
||||
End Sub
|
||||
|
||||
'断言
|
||||
Public Sub DebugAssert(Exp As Boolean)
|
||||
If Not Exp Then Throw New Exception("断言命中")
|
||||
'遥测
|
||||
Public Sub Telemetry([Event] As String, ParamArray Datas As String())
|
||||
If Not Setup.Get("SystemSystemTelemetry") Then Return '用户关闭了遥测
|
||||
If Not ClsBaseUrl.StartsWithF("http") Then Return '开源版没有设置遥测地址
|
||||
If VersionBranchName = "Debug" Then Return '开发版本不上传遥测
|
||||
RunInNewThread(
|
||||
Sub()
|
||||
Try
|
||||
Log($"[System] 匿名数据上报:{[Event]}")
|
||||
Dim Url As String = $"{ClsBaseUrl}&Event={WebUtility.UrlEncode([Event])}"
|
||||
For i = 0 To Datas.Length - 1 Step 2
|
||||
Url &= "&" & WebUtility.UrlEncode(Datas(i)) & "=" & WebUtility.UrlEncode(Datas(i + 1).Replace(vbCrLf, vbLf))
|
||||
Next
|
||||
NetRequestByClient(Url, MakeLog:=False)
|
||||
Catch ex As Exception
|
||||
Log(ex, "匿名数据上报失败")
|
||||
End Try
|
||||
End Sub, "Telemetry", ThreadPriority.Lowest)
|
||||
End Sub
|
||||
|
||||
'获取当前的堆栈信息
|
||||
@@ -3102,7 +3160,7 @@ Retry:
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 取随机整数。
|
||||
''' 取随机整数(包含)。
|
||||
''' </summary>
|
||||
Public Function RandomInteger(min As Integer, max As Integer) As Integer
|
||||
Return Math.Floor((max - min + 1) * Random.NextDouble) + min
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
Implements ILoadingTrigger
|
||||
|
||||
Public ReadOnly Property IsLoader As Boolean = True Implements ILoadingTrigger.IsLoader
|
||||
Protected Shared LogBlackList As String() = {"EasyTier CLI"}
|
||||
|
||||
'基础属性
|
||||
''' <summary>
|
||||
@@ -89,7 +90,7 @@
|
||||
Dim OldState = _State
|
||||
If value = LoadState.Finished AndAlso Setup.Get("SystemDebugDelay") Then Thread.Sleep(RandomInteger(100, 2000))
|
||||
_State = value
|
||||
Log("[Loader] 加载器 " & Name & " 状态改变:" & GetStringFromEnum(value))
|
||||
If Not LogBlackList.Contains(Name) Then Log($"[Loader] 加载器 {Name} 状态改变:{GetStringFromEnum(value)}")
|
||||
'实现 ILoadingTrigger 接口与 OnStateChanged 回调
|
||||
RunInUi(
|
||||
Sub()
|
||||
@@ -170,6 +171,7 @@
|
||||
'状态变化
|
||||
Public MustOverride Sub Start(Optional Input As Object = Nothing, Optional IsForceRestart As Boolean = False)
|
||||
Public MustOverride Sub Abort()
|
||||
Public MustOverride Sub Failed(Ex As Exception)
|
||||
|
||||
'等待结束
|
||||
Public Const WaitForExitTimeoutMessage As String = "等待加载器执行超时。"
|
||||
@@ -290,11 +292,13 @@
|
||||
'检验输入以确定情况
|
||||
If IsForceRestart Then Return True '强制要求重启
|
||||
If ((Input Is Nothing) <> (Me.Input Is Nothing)) OrElse (Input IsNot Nothing AndAlso Not Input.Equals(Me.Input)) Then Return True '输入不同
|
||||
If (State = LoadState.Loading OrElse State = LoadState.Finished) AndAlso '正在加载或已结束
|
||||
(IgnoreReloadTimeout OrElse ReloadTimeout = -1 OrElse LastFinishedTime = 0 OrElse GetTimeTick() - LastFinishedTime < ReloadTimeout) Then '没有超时
|
||||
Return False '则不重试
|
||||
|
||||
If (State = LoadState.Loading OrElse State = LoadState.Finished) AndAlso '正在加载或已结束…
|
||||
(IgnoreReloadTimeout OrElse ReloadTimeout = -1 OrElse
|
||||
LastFinishedTime = 0 OrElse GetTimeTick() - LastFinishedTime < ReloadTimeout) Then '…且没有超时…
|
||||
Return False '…则不重试
|
||||
Else
|
||||
Return True '需要开始
|
||||
Return True '否则需要重启
|
||||
End If
|
||||
End Function
|
||||
Public Overrides Sub Start(Optional Input As Object = Nothing, Optional IsForceRestart As Boolean = False)
|
||||
@@ -315,13 +319,13 @@
|
||||
Sub()
|
||||
Try
|
||||
IsForceRestarting = IsForceRestart
|
||||
If ModeDebug Then Log($"[Loader] 加载线程 {Name} ({Thread.CurrentThread.ManagedThreadId}) 已{If(IsForceRestarting, "强制", "")}启动")
|
||||
If ModeDebug AndAlso Not LogBlackList.Contains(Name) Then Log($"[Loader] 加载线程 {Name} ({Thread.CurrentThread.ManagedThreadId}) 已{If(IsForceRestarting, "强制", "")}启动")
|
||||
LoadDelegate(Me)
|
||||
If IsAborted Then
|
||||
Log($"[Loader] 加载线程 {Name} ({Thread.CurrentThread.ManagedThreadId}) 已中断但线程正常运行至结束,输出被弃用(最新线程:{If(LastRunningThread Is Nothing, -1, LastRunningThread.ManagedThreadId)})", LogLevel.Developer)
|
||||
Return
|
||||
End If
|
||||
If ModeDebug Then Log($"[Loader] 加载线程 {Name} ({Thread.CurrentThread.ManagedThreadId}) 已完成")
|
||||
If ModeDebug AndAlso Not LogBlackList.Contains(Name) Then Log($"[Loader] 加载线程 {Name} ({Thread.CurrentThread.ManagedThreadId}) 已完成")
|
||||
RaisePreviewFinish()
|
||||
State = LoadState.Finished
|
||||
LastFinishedTime = GetTimeTick() '未中断,本次输出有效
|
||||
@@ -334,25 +338,33 @@
|
||||
'如果线程是在没有 IsAborted 时手动引发了 ThreadInterruptedException,则代表没有重启线程,这通常代表用户手动取消,应当改为 Aborted
|
||||
If Not IsAborted Then State = LoadState.Aborted
|
||||
Catch ex As Exception
|
||||
If IsAborted Then Return
|
||||
Log(ex, $"加载线程 {Name} ({Thread.CurrentThread.ManagedThreadId}) 出错,已完成 {Math.Round(Progress * 100)}%", LogLevel.Developer)
|
||||
[Error] = ex
|
||||
State = LoadState.Failed
|
||||
Failed(ex)
|
||||
End Try
|
||||
End Sub) With {.Name = Name, .Priority = ThreadPriority}
|
||||
End Sub) With {.Name = "L/" & Name, .Priority = ThreadPriority}
|
||||
LastRunningThread.Start() '不能使用 RunInNewThread,否则在函数返回前线程就会运行完,导致误判 IsAborted
|
||||
End Sub
|
||||
Public Overrides Sub Abort()
|
||||
If State <> LoadState.Loading Then Return
|
||||
Public Overrides Sub Failed(ex As Exception)
|
||||
[Error] = ex
|
||||
SyncLock LockState
|
||||
If IsAborted OrElse State >= LoadState.Finished Then Return
|
||||
State = LoadState.Failed
|
||||
End SyncLock
|
||||
Log(ex, $"加载线程 {Name} ({Thread.CurrentThread.ManagedThreadId}) 出错,已完成 {Math.Round(Progress * 100)}%", LogLevel.Developer)
|
||||
TriggerThreadAbort()
|
||||
End Sub
|
||||
Public Overrides Sub Abort()
|
||||
SyncLock LockState
|
||||
If State <> LoadState.Loading Then Return
|
||||
State = LoadState.Aborted
|
||||
End SyncLock
|
||||
TriggerThreadAbort()
|
||||
End Sub
|
||||
Private Sub TriggerThreadAbort()
|
||||
If LastRunningThread Is Nothing Then Return
|
||||
If ModeDebug Then Log($"[Loader] 加载线程 {Name} ({LastRunningThread.ManagedThreadId}) 已中断")
|
||||
If LastRunningThread.IsAlive Then LastRunningThread.Interrupt()
|
||||
If LastRunningThread.IsAlive Then
|
||||
LastRunningThread.Interrupt()
|
||||
If ModeDebug Then Log($"[Loader] 加载线程 {Name} ({LastRunningThread.ManagedThreadId}) 已中断")
|
||||
End If
|
||||
LastRunningThread = Nothing
|
||||
End Sub
|
||||
|
||||
@@ -451,6 +463,17 @@
|
||||
Next
|
||||
End Sub)
|
||||
End Sub
|
||||
Public Overrides Sub Failed(Ex As Exception)
|
||||
[Error] = Ex '先设置错误再调整状态,防止父加载器获取不到异常
|
||||
SyncLock LockState
|
||||
If State >= LoadState.Finished Then Return
|
||||
State = LoadState.Failed
|
||||
End SyncLock
|
||||
For Each Loader In Loaders
|
||||
Loader.Abort()
|
||||
Next
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 子任务状态变更。
|
||||
@@ -468,18 +491,9 @@
|
||||
Case LoadState.Aborted
|
||||
'被中断,这个任务也中断
|
||||
Abort()
|
||||
Case Else
|
||||
Case LoadState.Failed
|
||||
'完蛋,出错了
|
||||
SyncLock LockState
|
||||
If State >= LoadState.Finished Then Return
|
||||
[Error] = New Exception(Loader.Name & "失败", Loader.Error)
|
||||
State = Loader.State
|
||||
End SyncLock
|
||||
For Each Loader In Loaders
|
||||
Loader.Abort()
|
||||
Next
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
Return
|
||||
Failed(New Exception(Loader.Name & "失败", Loader.Error))
|
||||
End Select
|
||||
End Sub
|
||||
''' <summary>
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -113,11 +113,11 @@ Public Class ValidateInteger
|
||||
Me.Max = Max
|
||||
End Sub
|
||||
Public Overrides Function Validate(Str As String) As String
|
||||
Dim Valed As Integer
|
||||
If Not Integer.TryParse(Str, Valed) Then Return "请输入一个整数!"
|
||||
If Str.Length > 9 Then Return "请输入一个大小合理的数字!"
|
||||
Dim Valed As Integer = Val(Str)
|
||||
If Valed.ToString <> Str Then Return "请输入一个整数!"
|
||||
If Val(Str) > Max Then Return "不可超过 " & Max & "!"
|
||||
If Val(Str) < Min Then Return "不可低于 " & Min & "!"
|
||||
If Valed > Max Then Return "不可超过 " & Max & "!"
|
||||
If Valed < Min Then Return "不可低于 " & Min & "!"
|
||||
Return ""
|
||||
End Function
|
||||
End Class
|
||||
@@ -148,7 +148,7 @@ End Class
|
||||
''' </summary>
|
||||
Public Class ValidateExcept
|
||||
Inherits Validate
|
||||
Public Property Excepts As ObjectModel.Collection(Of String) = New ObjectModel.Collection(Of String)
|
||||
Public Property Excepts As New ObjectModel.Collection(Of String)
|
||||
Public Property ErrorMessage As String
|
||||
Public Sub New()
|
||||
ErrorMessage = "输入内容不能包含 %"
|
||||
@@ -157,12 +157,15 @@ Public Class ValidateExcept
|
||||
Me.Excepts = Excepts
|
||||
Me.ErrorMessage = ErrorMessage
|
||||
End Sub
|
||||
Public Sub New(Excepts As IEnumerable, Optional ErrorMessage As String = "输入内容不能包含 %")
|
||||
Me.Excepts = New ObjectModel.Collection(Of String)
|
||||
Me.ErrorMessage = ErrorMessage
|
||||
For Each Data As String In Excepts
|
||||
Me.Excepts.Add(Data)
|
||||
Next
|
||||
Public Sub New(Excepts As IEnumerable(Of String), Optional ErrorMessage As String = "输入内容不能包含 %")
|
||||
Me.New(
|
||||
New ObjectModel.Collection(Of String)(New List(Of String)(Excepts)),
|
||||
ErrorMessage)
|
||||
End Sub
|
||||
Public Sub New(Excepts As IEnumerable(Of Char), Optional ErrorMessage As String = "输入内容不能包含 %")
|
||||
Me.New(
|
||||
New ObjectModel.Collection(Of String)(Excepts.Select(Function(c) c.ToString).ToList),
|
||||
ErrorMessage)
|
||||
End Sub
|
||||
Public Overrides Function Validate(Str As String) As String
|
||||
For Each Ch As String In Excepts
|
||||
@@ -181,7 +184,7 @@ End Class
|
||||
''' </summary>
|
||||
Public Class ValidateExceptSame
|
||||
Inherits Validate
|
||||
Public Property Excepts As ObjectModel.Collection(Of String) = New ObjectModel.Collection(Of String)
|
||||
Public Property Excepts As New ObjectModel.Collection(Of String)
|
||||
Public Property ErrorMessage As String
|
||||
Public Property IgnoreCase As Boolean = False
|
||||
Public Sub New()
|
||||
@@ -191,13 +194,15 @@ Public Class ValidateExceptSame
|
||||
Me.ErrorMessage = ErrorMessage
|
||||
Me.IgnoreCase = IgnoreCase
|
||||
End Sub
|
||||
Public Sub New(Excepts As IEnumerable, Optional ErrorMessage As String = "输入内容不能为 %", Optional IgnoreCase As Boolean = False)
|
||||
Me.Excepts = New ObjectModel.Collection(Of String)
|
||||
For Each Data As String In Excepts
|
||||
Me.Excepts.Add(Data)
|
||||
Next
|
||||
Me.ErrorMessage = ErrorMessage
|
||||
Me.IgnoreCase = IgnoreCase
|
||||
Public Sub New(Excepts As IEnumerable(Of String), Optional ErrorMessage As String = "输入内容不能为 %", Optional IgnoreCase As Boolean = False)
|
||||
Me.New(
|
||||
New ObjectModel.Collection(Of String)(New List(Of String)(Excepts)),
|
||||
ErrorMessage, IgnoreCase)
|
||||
End Sub
|
||||
Public Sub New(Except As String, Optional ErrorMessage As String = "输入内容不能为 %", Optional IgnoreCase As Boolean = False)
|
||||
Me.New(
|
||||
New ObjectModel.Collection(Of String) From {Except},
|
||||
ErrorMessage, IgnoreCase)
|
||||
End Sub
|
||||
Public Overrides Function Validate(Str As String) As String
|
||||
If Str Is Nothing Then Return ErrorMessage.Replace("%", "null")
|
||||
|
||||
@@ -347,7 +347,7 @@
|
||||
Case 4475 : Tags.Add("冒险")
|
||||
Case 4476 : Tags.Add("探索")
|
||||
Case 4477 : Tags.Add("小游戏")
|
||||
Case 4471 : Tags.Add("科幻")
|
||||
Case 4474 : Tags.Add("科幻")
|
||||
Case 4736 : Tags.Add("空岛")
|
||||
Case 5128 : Tags.Add("原版改良")
|
||||
Case 4487 : Tags.Add("FTB")
|
||||
@@ -1076,7 +1076,7 @@ Retry:
|
||||
Try
|
||||
'获取工程列表
|
||||
Log("[Comp] 开始从 CurseForge 获取工程列表:" & CurseForgeUrl)
|
||||
Dim RequestResult As JObject = DlModRequest(CurseForgeUrl, IsJson:=True)
|
||||
Dim RequestResult As JObject = DlModRequest(CurseForgeUrl)
|
||||
Task.Progress += 0.2
|
||||
Dim ProjectList As New List(Of CompProject)
|
||||
For Each JsonEntry As JObject In RequestResult("data")
|
||||
@@ -1107,7 +1107,7 @@ Retry:
|
||||
Sub()
|
||||
Try
|
||||
Log("[Comp] 开始从 Modrinth 获取工程列表:" & ModrinthUrl)
|
||||
Dim RequestResult As JObject = DlModRequest(ModrinthUrl, IsJson:=True)
|
||||
Dim RequestResult As JObject = DlModRequest(ModrinthUrl)
|
||||
Task.Progress += 0.2
|
||||
Dim ProjectList As New List(Of CompProject)
|
||||
For Each JsonEntry As JObject In RequestResult("hits")
|
||||
@@ -1137,6 +1137,11 @@ Retry:
|
||||
If ModrinthThread IsNot Nothing Then ModrinthThread.Join()
|
||||
If Task.IsAborted Then Return
|
||||
|
||||
'筛除不是 Forge 的 Mod
|
||||
If IsOldForgeRequest Then
|
||||
RawResults = RawResults.Where(Function(p) Not p.ModLoaders.Any() OrElse p.ModLoaders.Contains(CompModLoaderType.Forge)).ToList
|
||||
End If
|
||||
|
||||
'确保存在结果
|
||||
Storage.ErrorMessage = Nothing
|
||||
If Not RawResults.Any() Then
|
||||
@@ -1156,9 +1161,9 @@ Retry:
|
||||
ElseIf [Error] IsNot Nothing Then
|
||||
'有结果但是有错误
|
||||
If CurseForgeFailed Then
|
||||
Storage.ErrorMessage = $"无法连接到 CurseForge,所以目前仅显示了来自 Modrinth 的内容,搜索结果可能不全。{vbCrLf}请稍后重试,或使用 VPN 以改善网络环境。"
|
||||
Storage.ErrorMessage = $"无法连接到 CurseForge,所以目前仅显示了来自 Modrinth 的内容,搜索结果可能不全。{vbCrLf}请稍后再试,或使用 VPN 改善网络环境。"
|
||||
Else
|
||||
Storage.ErrorMessage = $"无法连接到 Modrinth,所以目前仅显示了来自 CurseForge 的内容,搜索结果可能不全。{vbCrLf}请稍后重试,或使用 VPN 以改善网络环境。"
|
||||
Storage.ErrorMessage = $"无法连接到 Modrinth,所以目前仅显示了来自 CurseForge 的内容,搜索结果可能不全。{vbCrLf}请稍后再试,或使用 VPN 改善网络环境。"
|
||||
End If
|
||||
End If
|
||||
|
||||
@@ -1167,11 +1172,6 @@ Retry:
|
||||
ModrinthThread?.Interrupt()
|
||||
End Try
|
||||
|
||||
'筛除不是 Forge 的 Mod
|
||||
If IsOldForgeRequest Then
|
||||
RawResults = RawResults.Where(Function(p) Not p.ModLoaders.Any() OrElse p.ModLoaders.Contains(CompModLoaderType.Forge)).ToList
|
||||
End If
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "提取非重复项,存储于 RealResults"
|
||||
@@ -1211,11 +1211,11 @@ Retry:
|
||||
Function(Project As CompProject) As Double
|
||||
Select Case Request.Type
|
||||
Case CompType.Mod, CompType.ModPack
|
||||
Return If(Project.FromCurseForge, 1, 7)
|
||||
Return If(Project.FromCurseForge, 1, 5)
|
||||
Case CompType.DataPack
|
||||
Return If(Project.FromCurseForge, 10, 1)
|
||||
Case CompType.ResourcePack, CompType.Shader
|
||||
Return If(Project.FromCurseForge, 1, 5)
|
||||
Return If(Project.FromCurseForge, 1, 4)
|
||||
Case Else
|
||||
Return 1
|
||||
End Select
|
||||
@@ -1354,7 +1354,7 @@ Retry:
|
||||
''' </summary>
|
||||
''' <param name="LocalAddress">目标本地文件夹,或完整的文件路径。会自动判断类型。</param>
|
||||
Public Function ToNetFile(LocalAddress As String) As NetFile
|
||||
Return New NetFile(DownloadUrls, LocalAddress & If(LocalAddress.EndsWithF("\"), FileName, ""), New FileChecker(Hash:=Hash), UseBrowserUserAgent:=True)
|
||||
Return New NetFile(DownloadUrls, LocalAddress & If(LocalAddress.EndsWithF("\"), FileName, ""), New FileChecker(Hash:=Hash), SimulateBrowserHeaders:=True)
|
||||
End Function
|
||||
|
||||
'实例化
|
||||
@@ -1395,10 +1395,9 @@ Retry:
|
||||
If Hash Is Nothing Then Hash = CType(Data("hashes"), JArray).ToList.FirstOrDefault(Function(s) s("algo").ToObject(Of Integer) = 2)?("value")
|
||||
'DownloadAddress
|
||||
Dim Url = Data("downloadUrl").ToString
|
||||
If Url = "" Then Url = $"https://media.forgecdn.net/files/{CInt(Id.ToString.Substring(0, 4))}/{CInt(Id.ToString.Substring(4))}/{FileName}"
|
||||
Url = Url.Replace(FileName, Net.WebUtility.UrlEncode(FileName)) '对文件名进行编码
|
||||
DownloadUrls = HandleCurseForgeDownloadUrls(Url) '对脑残 CurseForge 的下载地址进行多种修正
|
||||
DownloadUrls.AddRange(DownloadUrls.Select(Function(u) DlSourceModGet(u)).ToList) '添加镜像源,这个写法是为了让镜像源排在后面
|
||||
If Url = "" Then Url = $"https://edge.forgecdn.net/files/{CInt(Id.ToString.Substring(0, 4))}/{CInt(Id.ToString.Substring(4))}/{FileName}"
|
||||
DownloadUrls = HandleCurseForgeDownloadUrls(Url.Replace(FileName, WebUtility.UrlEncode(FileName))) '对脑残 CurseForge 的下载地址进行多种修正
|
||||
DownloadUrls.Add(DlSourceModGet(Url)) '添加镜像源;注意 MCIM 源不支持 URL 编码后的文件名,必须传入 URL 编码前的文件名
|
||||
DownloadUrls = DownloadUrls.Distinct.ToList '最终去重
|
||||
'Dependencies
|
||||
If Data.ContainsKey("dependencies") Then
|
||||
@@ -1492,10 +1491,9 @@ Retry:
|
||||
Return {
|
||||
Url.Replace("-service.overwolf.wtf", ".forgecdn.net").Replace("://edge.", "://mediafilez.").Replace("://media.", "://mediafilez."),
|
||||
Url.Replace("://edge.", "://mediafilez.").Replace("://media.", "://mediafilez."),
|
||||
Url,
|
||||
Url.Replace("-service.overwolf.wtf", ".forgecdn.net").Replace("://edge.", "://media."),
|
||||
Url.Replace("-service.overwolf.wtf", ".forgecdn.net"),
|
||||
Url.Replace("://edge.", "://media.")
|
||||
Url.Replace("://media.", "://edge."),
|
||||
Url
|
||||
}.Distinct.ToList
|
||||
End Function
|
||||
|
||||
@@ -1591,27 +1589,40 @@ Retry:
|
||||
If CompProjectCache.ContainsKey(ProjectId) Then '存在缓存
|
||||
TargetProject = CompProjectCache(ProjectId)
|
||||
ElseIf FromCurseForge Then 'CurseForge
|
||||
TargetProject = New CompProject(DlModRequest("https://api.curseforge.com/v1/mods/" & ProjectId, IsJson:=True)("data"))
|
||||
TargetProject = New CompProject(DlModRequest("https://api.curseforge.com/v1/mods/" & ProjectId)("data"))
|
||||
Else 'Modrinth
|
||||
TargetProject = New CompProject(DlModRequest("https://api.modrinth.com/v2/project/" & ProjectId, IsJson:=True))
|
||||
TargetProject = New CompProject(DlModRequest("https://api.modrinth.com/v2/project/" & ProjectId))
|
||||
End If
|
||||
'获取工程对象的文件列表
|
||||
If Not CompFilesCache.ContainsKey(ProjectId) Then '有缓存也不能直接返回,这时候前置可能没获取(#5173)
|
||||
Log("[Comp] 开始获取文件列表:" & ProjectId)
|
||||
Dim ResultJsonArray As JArray
|
||||
Dim ResultJsonArray As JArray = Nothing
|
||||
If FromCurseForge Then
|
||||
'CurseForge
|
||||
'HMCL 一次性请求了 10000 个文件,虽然不知道会不会出问题但先这样吧……(#5522)
|
||||
ResultJsonArray = DlModRequest($"https://api.curseforge.com/v1/mods/{ProjectId}/files?pageSize=10000", IsJson:=True)("data")
|
||||
'之前只请求一部分文件的方法备份如下:
|
||||
'If TargetProject.Type = CompType.Mod Then 'Mod 使用每个版本最新的文件
|
||||
' ResultJsonArray = GetJson(DlModRequest("https://api.curseforge.com/v1/mods/files", "POST", "{""fileIds"": [" & Join(TargetProject.CurseForgeFileIds, ",") & "]}", "application/json"))("data")
|
||||
'Else '否则使用全部文件
|
||||
' ResultJsonArray = DlModRequest($"https://api.curseforge.com/v1/mods/{ProjectId}/files?pageSize=999", IsJson:=True)("data")
|
||||
'End If
|
||||
For RetryCount As Integer = 0 To 2
|
||||
Dim ResultJson As JObject = DlModRequest($"https://api.curseforge.com/v1/mods/{ProjectId}/files?pageSize=" &
|
||||
(10000 + RetryCount)) '每次重试多请求一个文件,以避免触发 CDN 缓存
|
||||
'HMCL 一次性请求了 10000 个文件,虽然不知道会不会出问题但先这样吧……(#5522)
|
||||
'之前只请求一部分文件的方法备份如下:
|
||||
'If TargetProject.Type = CompType.Mod Then 'Mod 使用每个版本最新的文件
|
||||
' ResultJsonArray = DlModRequest("https://api.curseforge.com/v1/mods/files", HttpMethod.Post, "{""fileIds"": [" & Join(TargetProject.CurseForgeFileIds, ",") & "]}", "application/json")("data")
|
||||
'Else '否则使用全部文件
|
||||
' ResultJsonArray = DlModRequest($"https://api.curseforge.com/v1/mods/{ProjectId}/files?pageSize=999")("data")
|
||||
'End If
|
||||
If ResultJson("pagination")("resultCount").ToObject(Of Integer) = ResultJson("pagination")("totalCount").ToObject(Of Integer) Then
|
||||
ResultJsonArray = ResultJson("data")
|
||||
Exit For
|
||||
ElseIf RetryCount < 2 Then
|
||||
Log($"[Comp] CurseForge 返回的文件列表存在缺失,即将进行第 {RetryCount + 1} 次重试", LogLevel.Debug) '#6224
|
||||
Log($"[Comp] 返回的原始内容如下:{vbCrLf}{ResultJson}")
|
||||
Else
|
||||
Log($"[Comp] CurseForge 返回的文件列表存在缺失,返回的原始内容如下:{vbCrLf}{ResultJson}")
|
||||
Throw New Exception("CurseForge 返回的文件列表存在缺失")
|
||||
End If
|
||||
Next
|
||||
Else
|
||||
'Modrinth
|
||||
ResultJsonArray = DlModRequest($"https://api.modrinth.com/v2/project/{ProjectId}/version", IsJson:=True)
|
||||
ResultJsonArray = DlModRequest($"https://api.modrinth.com/v2/project/{ProjectId}/version")
|
||||
End If
|
||||
CompFilesCache(ProjectId) = ResultJsonArray.Select(Function(a) New CompFile(a, TargetProject.Type)).
|
||||
Where(Function(a) a.Available).ToList.
|
||||
@@ -1625,10 +1636,10 @@ Retry:
|
||||
Log($"[Comp] {ProjectId} 文件列表中还需要获取信息的前置:{Join(UndoneDeps, ",")}")
|
||||
Dim Projects As JArray
|
||||
If TargetProject.FromCurseForge Then
|
||||
Projects = GetJson(DlModRequest("https://api.curseforge.com/v1/mods",
|
||||
"POST", "{""modIds"": [" & Join(UndoneDeps, ",") & "]}", "application/json"))("data")
|
||||
Projects = DlModRequest("https://api.curseforge.com/v1/mods",
|
||||
HttpMethod.Post, "{""modIds"": [" & Join(UndoneDeps, ",") & "]}", "application/json")("data")
|
||||
Else
|
||||
Projects = DlModRequest($"https://api.modrinth.com/v2/projects?ids=[""{Join(UndoneDeps, """,""")}""]", IsJson:=True)
|
||||
Projects = DlModRequest($"https://api.modrinth.com/v2/projects?ids=[""{Join(UndoneDeps, """,""")}""]")
|
||||
End If
|
||||
For Each Project In Projects
|
||||
Dim Unused As New CompProject(Project) '在 New 的时候会添加缓存以便之后读取
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
'构造函数
|
||||
Private TempFolder As String
|
||||
Public Sub New(UUID As Integer)
|
||||
Private TargetVersion As McVersion
|
||||
Public Sub New(Optional TargetVersion As McVersion = Nothing)
|
||||
Me.TargetVersion = TargetVersion
|
||||
'构建文件结构
|
||||
TempFolder = RequestTaskTempFolder()
|
||||
Directory.CreateDirectory(TempFolder & "Temp\")
|
||||
@@ -339,6 +341,7 @@ Extracted:
|
||||
''' 导致崩溃的原因枚举。
|
||||
''' </summary>
|
||||
Private Enum CrashReason
|
||||
Java虚拟机参数有误
|
||||
Mod文件被解压
|
||||
MixinBootstrap缺失
|
||||
内存不足
|
||||
@@ -482,10 +485,12 @@ Done:
|
||||
'崩溃报告分析,高优先级
|
||||
If LogCrash IsNot Nothing Then
|
||||
If LogCrash.Contains("Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass") Then AppendReason(CrashReason.Java版本过高)
|
||||
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)}) '#7071
|
||||
End If
|
||||
|
||||
'游戏日志分析
|
||||
If LogMc IsNot Nothing Then
|
||||
If LogMc.Contains("Unrecognized option:") Then AppendReason(CrashReason.Java虚拟机参数有误)
|
||||
If LogMc.Contains("Found multiple arguments for option fml.forgeVersion, but you asked for only one") Then AppendReason(CrashReason.版本Json中存在多个Forge)
|
||||
If LogMc.Contains("The driver does not appear to support OpenGL") Then AppendReason(CrashReason.显卡不支持OpenGL)
|
||||
If LogMc.Contains("java.lang.ClassCastException: java.base/jdk") Then AppendReason(CrashReason.使用JDK)
|
||||
@@ -508,13 +513,13 @@ Done:
|
||||
If LogMc.Contains("Couldn't set pixel format") Then AppendReason(CrashReason.显卡驱动不支持导致无法设置像素格式)
|
||||
If LogMc.Contains("java.lang.OutOfMemoryError") OrElse LogMc.Contains("an out of memory error") 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("java.lang.NoSuchMethodError: sun.security.util.ManifestEntryVerifier") OrElse LogMc.Contains("java.lang.NoSuchMethodError: 'void 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("com.electronwill.nightconfig.core.io.ParsingException: Not enough data available") Then AppendReason(CrashReason.NightConfig的Bug)
|
||||
If LogMc.Contains("com.electronwill.nightconfig.core.io.ParsingException: Not enough data available") AndAlso Not CrashReasons.ContainsKey(CrashReason.Mod配置文件导致游戏崩溃) Then AppendReason(CrashReason.NightConfig的Bug)
|
||||
If LogMc.Contains("Cannot find launch target fmlclient, unable to launch") Then AppendReason(CrashReason.Forge安装不完整)
|
||||
If LogMc.Contains("Invalid paths argument, contained no existing paths") AndAlso LogMc.Contains("libraries\net\minecraftforge\fmlcore") Then AppendReason(CrashReason.Forge安装不完整)
|
||||
If LogMc.Contains("Invalid module name: '' is not a Java identifier") Then AppendReason(CrashReason.Mod名称包含特殊字符)
|
||||
@@ -537,8 +542,8 @@ Done:
|
||||
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("Found duplicate mods") Then AppendReason(CrashReason.Mod重复安装, RegexSearch(LogMc, "(?<=Mod ID: ')\w+?(?=' from mod files:)").Distinct.ToList)
|
||||
If LogMc.Contains("ModResolutionException: Duplicate") Then AppendReason(CrashReason.Mod重复安装, RegexSearch(If(RegexSeek(LogMc, "ModResolutionException: Duplicate[^\n]+"), ""), "[^\\/]+.jar", RegularExpressions.RegexOptions.IgnoreCase))
|
||||
If LogMc.Contains("Incompatible mods found!") Then '#5006
|
||||
AppendReason(CrashReason.Mod互不兼容, If(RegexSeek(LogMc, "(?<=Incompatible mods found![\s\S]+: )[\s\S]+?(?=\tat )"), ""))
|
||||
If LogMc.Contains("Incompatible mods found!") Then '#5006, #5115
|
||||
AppendReason(CrashReason.Mod互不兼容, If(RegexSeek(LogMc, "(?<=Incompatible mods found![\s\S]+: )[\s\S]+?(?=\tat )"), "").BeforeFirst("更多信息:").Replace("Some of your mods are incompatible with the game or each other!", "").Trim((vbCrLf & " ").ToCharArray))
|
||||
End If
|
||||
If LogMc.Contains("Missing or unsupported mandatory dependencies:") Then
|
||||
AppendReason(CrashReason.Mod缺少前置或MC版本错误,
|
||||
@@ -654,7 +659,7 @@ Done:
|
||||
End If
|
||||
'Mod 解析错误(常见于 Fabric 前置校验失败)
|
||||
If LogMc.Contains("Mod resolution failed") Then AppendReason(CrashReason.Mod加载器报错)
|
||||
'Mixin 失败可以导致大量 Mod 实例创建失败
|
||||
'Mixin 失败可以导致大量 Mod 实例创建失败,因此需要放到低优先级中
|
||||
If LogMc.Contains("Failed to create mod instance.") Then AppendReason(CrashReason.Mod初始化失败, TryAnalyzeModName(If(RegexSeek(LogMc, "(?<=Failed to create mod instance. ModID: )[^,]+"), If(RegexSeek(LogMc, "(?<=Failed to create mod instance. ModId )[^\n]+(?= for )"), "")).TrimEnd(vbCrLf)))
|
||||
'注意:Fabric 的 Warnings were found! 不一定是崩溃原因,它可能是单纯的警报
|
||||
End If
|
||||
@@ -713,7 +718,7 @@ NextStack:
|
||||
"preinit", "preload", "machine", "reflect", "channel", "general", "handler", "content", "systems", "modules", "service",
|
||||
"fastutil", "optifine", "internal", "platform", "override", "fabricmc", "neoforge",
|
||||
"injection", "listeners", "scheduler", "minecraft", "universal", "multipart", "neoforged", "microsoft",
|
||||
"transformer", "transformers", "minecraftforge", "blockentity", "spongepowered", "electronwill"
|
||||
"transformer", "transformers", "minecraftforge", "blockentity", "spongepowered", "electronwill", "concurrent"
|
||||
}.Contains(Word.ToLower) Then Continue For
|
||||
PossibleWords.Add(Word.Trim)
|
||||
Next
|
||||
@@ -856,20 +861,21 @@ NextStack:
|
||||
''' <summary>
|
||||
''' 弹出崩溃弹窗,并指导导出崩溃报告。
|
||||
''' </summary>
|
||||
Public Sub Output(IsHandAnalyze As Boolean, Optional ExtraFiles As List(Of String) = Nothing)
|
||||
Public Sub Output(IsManualAnalyze As Boolean, Optional ExtraFiles As List(Of String) = Nothing)
|
||||
'弹窗提示
|
||||
FrmMain.ShowWindowToTop()
|
||||
Select Case MyMsgBox(GetAnalyzeResult(IsHandAnalyze), If(IsHandAnalyze, "错误报告分析结果", "Minecraft 出现错误"),
|
||||
"确定", If(IsHandAnalyze OrElse DirectFile Is Nothing, "", "查看日志"), If(IsHandAnalyze, "", "导出错误报告"),
|
||||
Button2Action:=If(IsHandAnalyze OrElse DirectFile Is Nothing, Nothing,
|
||||
Dim Detail As String = GetAnalyzeResult(IsManualAnalyze)
|
||||
Select Case MyMsgBox(Detail, If(IsManualAnalyze, "错误报告分析结果", "Minecraft 出现错误"),
|
||||
"确定", If(IsManualAnalyze OrElse DirectFile Is Nothing, "", "查看日志"), If(IsManualAnalyze, "", "导出错误报告"),
|
||||
Button2Action:=If(IsManualAnalyze OrElse DirectFile Is Nothing, Nothing,
|
||||
Sub()
|
||||
'弹窗选择:查看日志
|
||||
If File.Exists(DirectFile.Value.Key) Then
|
||||
ShellOnly(DirectFile.Value.Key)
|
||||
StartProcess(DirectFile.Value.Key)
|
||||
Else
|
||||
Dim FilePath As String = PathTemp & "Crash.txt"
|
||||
WriteFile(FilePath, Join(DirectFile.Value.Value, vbCrLf))
|
||||
ShellOnly(FilePath)
|
||||
StartProcess(FilePath)
|
||||
End If
|
||||
End Sub))
|
||||
Case 3
|
||||
@@ -902,8 +908,7 @@ NextStack:
|
||||
If File.Exists(OutputFile) Then
|
||||
If FileEncoding Is Nothing Then FileEncoding = GetEncoding(ReadFileBytes(OutputFile))
|
||||
Dim FileContent As String = ReadFile(OutputFile, FileEncoding)
|
||||
FileContent = FilterAccessToken(FileContent, If(FileName = "启动脚本.bat", "F", "*"))
|
||||
FileContent = FilterUserName(FileContent, "*")
|
||||
FileContent = FilterUserName(FilterAccessToken(FileContent, If(FileName = "启动脚本.bat", "F", "*")), "*")
|
||||
WriteFile(TempFolder & "Report\" & FileName, FileContent, Encoding:=FileEncoding)
|
||||
Log($"[Crash] 导出文件:{FileName},编码:{FileEncoding.HeaderName}")
|
||||
End If
|
||||
@@ -911,13 +916,18 @@ NextStack:
|
||||
'导出报告
|
||||
Compression.ZipFile.CreateFromDirectory(TempFolder & "Report\", FileAddress)
|
||||
DeleteDirectory(TempFolder & "Report\")
|
||||
Hint("错误报告已导出!", HintType.Finish)
|
||||
Hint("错误报告已导出!", HintType.Green)
|
||||
Catch ex As Exception
|
||||
Log(ex, "导出错误报告失败", LogLevel.Feedback)
|
||||
Return
|
||||
End Try
|
||||
OpenExplorer(FileAddress)
|
||||
End Select
|
||||
'上报
|
||||
If IsManualAnalyze Then Return
|
||||
Telemetry("Minecraft 崩溃",
|
||||
"Version", If(TargetVersion Is Nothing, "null", TargetVersion.Version.ToString),
|
||||
"Detail", FilterUserName(Detail, "*"))
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 获取崩溃分析的结果描述。
|
||||
@@ -938,6 +948,8 @@ NextStack:
|
||||
For Each Reason In CrashReasons
|
||||
Dim Additional As List(Of String) = Reason.Value
|
||||
Select Case Reason.Key
|
||||
Case CrashReason.Java虚拟机参数有误
|
||||
Results.Add("由于 Java 参数有误,导致游戏无法继续运行。\n请检查高级选项中的 Java 虚拟机参数和游戏参数设置是否有误。")
|
||||
Case CrashReason.Mod文件被解压
|
||||
Results.Add("由于 Mod 文件被解压了,导致游戏无法继续运行。\n直接把整个 Mod 文件放进 Mod 文件夹中即可,若解压就会导致游戏出错。\n\n请删除 Mod 文件夹中已被解压的 Mod,然后再启动游戏。")
|
||||
Case CrashReason.内存不足
|
||||
@@ -1018,9 +1030,17 @@ NextStack:
|
||||
End If
|
||||
Case CrashReason.特定实体导致崩溃
|
||||
If Additional.Count = 1 Then
|
||||
Results.Add("游戏似乎因为实体 " & Additional.First & " 出现了问题。\n\n你可以创建一个新世界,并生成一个该实体,然后观察游戏的运行情况:\n - 若正常运行,则是该实体导致出错,你或许需要使用一些方式删除此实体。\n - 若仍然出错,问题就可能来自其他原因……\h")
|
||||
If Additional.First.Contains("minecraft:player") Then
|
||||
If Additional.First.Contains(" ") Then
|
||||
Results.Add($"游戏因为位于 {Additional.First.AfterFirst(" ")} 的玩家实体导致了崩溃。\h")
|
||||
Else
|
||||
Results.Add($"游戏因为玩家实体导致了崩溃。\h")
|
||||
End If
|
||||
Else
|
||||
Results.Add($"游戏因为实体 {Additional.First} 导致了崩溃。\h")
|
||||
End If
|
||||
Else
|
||||
Results.Add("游戏似乎因为世界中的某些实体出现了问题。\n\n你可以创建一个新世界,并生成各种实体,观察游戏的运行情况:\n - 若正常运行,则是某些实体导致出错,你或许需要删除该世界。\n - 若仍然出错,问题就可能来自其他原因……\h")
|
||||
Results.Add($"游戏因为下列实体导致了崩溃:{Additional.Join("、")}\h")
|
||||
End If
|
||||
Case CrashReason.OptiFine与Forge不兼容
|
||||
Results.Add("由于 OptiFine 与当前版本的 Forge 不兼容,导致了游戏崩溃。\n\n请前往 OptiFine 官网(https://optifine.net/downloads)查看 OptiFine 所兼容的 Forge 版本,并严格按照对应版本重新安装游戏。")
|
||||
@@ -1102,7 +1122,7 @@ NextStack:
|
||||
If(Not Results.Any(Function(r) r.EndsWithF("\h")) OrElse IsHandAnalyze, "",
|
||||
vbCrLf & "如果要寻求帮助,请把错误报告文件发给对方,而不是发送这个窗口的照片或者截图。" &
|
||||
If(If(PageSetupSystem.IsLauncherNewest(), True), "",
|
||||
vbCrLf & vbCrLf & "此外,你正在使用老版本 PCL,更新 PCL 或许也能解决这个问题。" & vbCrLf & "你可以点击 设置 → 启动器 → 检查更新 来更新 PCL。"))
|
||||
vbCrLf & vbCrLf & "此外,你正在使用老版本 PCL,更新 PCL 或许也能解决这个问题。" & vbCrLf & "你可以点击 [设置 → 其他 → 启动器 → 检查更新] 更新 PCL。"))
|
||||
End Function
|
||||
|
||||
End Class
|
||||
@@ -73,8 +73,10 @@
|
||||
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
|
||||
If IndexFile Is Nothing Then
|
||||
Task.Output = New List(Of NetFile)
|
||||
Log("[Download] 未找到版本 " & Version.Name & " 的合适的资源索引下载地址,游戏 assets 可能缺失", LogLevel.Debug)
|
||||
ElseIf 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}
|
||||
@@ -93,6 +95,11 @@
|
||||
LoadersAssetsUpdate.Add(New LoaderTask(Of String, List(Of NetFile))("后台分析资源文件索引地址",
|
||||
Sub(Task As LoaderTask(Of String, List(Of NetFile)))
|
||||
Dim BackAssetsFile As NetFile = DlClientAssetIndexGet(Version)
|
||||
If BackAssetsFile Is Nothing Then
|
||||
Log("[Download] 未找到版本 " & Version.Name & " 的合适的资源索引下载地址,游戏 assets 可能缺失", LogLevel.Debug)
|
||||
Task.Abort()
|
||||
Throw New ThreadInterruptedException
|
||||
End If
|
||||
RealAddress = BackAssetsFile.LocalPath
|
||||
TempAddress = PathTemp & "Cache\" & BackAssetsFile.LocalName
|
||||
BackAssetsFile.LocalPath = TempAddress
|
||||
@@ -198,7 +205,7 @@
|
||||
Private IsNewClientVersionHinted As Boolean = False
|
||||
Private Sub DlClientListMojangMain(Loader As LoaderTask(Of String, DlClientListResult))
|
||||
Dim StartTime As Long = GetTimeTick()
|
||||
Dim Json As JObject = NetGetCodeByRequestRetry("https://launchermeta.mojang.com/mc/game/version_manifest.json", IsJson:=True)
|
||||
Dim Json As JObject = GetJson(NetRequestByClientRetry("https://launchermeta.mojang.com/mc/game/version_manifest.json", RequireJson:=True))
|
||||
Try
|
||||
Dim Versions As JArray = Json("versions")
|
||||
If Versions.Count < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Json.ToString & ")")
|
||||
@@ -235,7 +242,7 @@
|
||||
''' </summary>
|
||||
Public DlClientListBmclapiLoader As New LoaderTask(Of String, DlClientListResult)("DlClientList Bmclapi", AddressOf DlClientListBmclapiMain)
|
||||
Private Sub DlClientListBmclapiMain(Loader As LoaderTask(Of String, DlClientListResult))
|
||||
Dim Json As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/mc/game/version_manifest.json", IsJson:=True)
|
||||
Dim Json As JObject = GetJson(NetRequestByClientRetry("https://bmclapi2.bangbang93.com/mc/game/version_manifest.json", RequireJson:=True))
|
||||
Try
|
||||
Dim Versions As JArray = Json("versions")
|
||||
If Versions.Count < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Json.ToString & ")")
|
||||
@@ -377,7 +384,7 @@
|
||||
''' </summary>
|
||||
Public DlOptiFineListOfficialLoader As New LoaderTask(Of Integer, DlOptiFineListResult)("DlOptiFineList Official", AddressOf DlOptiFineListOfficialMain)
|
||||
Private Sub DlOptiFineListOfficialMain(Loader As LoaderTask(Of Integer, DlOptiFineListResult))
|
||||
Dim Result As String = NetGetCodeByClient("https://optifine.net/downloads", Encoding.Default)
|
||||
Dim Result As String = NetRequestByClientRetry("https://optifine.net/downloads", Encoding:=Encoding.Default)
|
||||
If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
|
||||
Try
|
||||
'获取所有版本信息
|
||||
@@ -413,7 +420,7 @@
|
||||
''' </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", IsJson:=True)
|
||||
Dim Json As JArray = GetJson(NetRequestByClientRetry("https://bmclapi2.bangbang93.com/optifine/versionList", RequireJson:=True))
|
||||
Try
|
||||
Dim Versions As New List(Of DlOptiFineListEntry)
|
||||
For Each Token As JObject In Json
|
||||
@@ -483,7 +490,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("https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.2.4.html", Encoding.Default, "text/html", UseBrowserUserAgent:=True)
|
||||
Dim Result As String = NetRequestByClientRetry("https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.2.4.html", Encoding:=Encoding.Default, Accept:="text/html", SimulateBrowserHeaders:=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)")
|
||||
@@ -497,7 +504,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)
|
||||
Dim Result As String = NetRequestByClientRetry("https://bmclapi2.bangbang93.com/forge/minecraft", Encoding:=Encoding.Default)
|
||||
If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
|
||||
'获取所有版本信息
|
||||
Dim Names As List(Of String) = RegexSearch(Result, "[0-9.]+(_pre[0-9]?)?")
|
||||
@@ -594,7 +601,7 @@
|
||||
VersionName = Version
|
||||
Me.Version = New Version(Version)
|
||||
Me.Inherit = Inherit
|
||||
FileVersion = Version & If(Branch Is Nothing, "", "-" & Branch)
|
||||
FileVersion = Version & If(Branch Is Nothing, "", "-" & Branch)
|
||||
End Sub
|
||||
End Class
|
||||
|
||||
@@ -629,11 +636,11 @@
|
||||
Public Sub DlForgeVersionOfficialMain(Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)))
|
||||
Dim Result As String
|
||||
Try
|
||||
Result = NetGetCodeByLoader("https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_" &
|
||||
Result = NetRequestByLoader("https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_" &
|
||||
Loader.Input.Replace("-", "_") & '兼容 Forge 1.7.10-pre4,#4057
|
||||
".html", UseBrowserUserAgent:=True)
|
||||
".html", SimulateBrowserHeaders:=True)
|
||||
Catch ex As Exception
|
||||
If GetExceptionSummary(ex).Contains("(404)") Then
|
||||
If ex.GetBrief().Contains("(404)") Then
|
||||
Throw New Exception("不可用")
|
||||
Else
|
||||
Throw
|
||||
@@ -700,9 +707,8 @@
|
||||
''' 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.Replace("-", "_"), '兼容 Forge 1.7.10-pre4,#4057
|
||||
IsJson:=True)
|
||||
Dim Json As JArray = GetJson(NetRequestByClientRetry("https://bmclapi2.bangbang93.com/forge/minecraft/" &
|
||||
Loader.Input.Replace("-", "_"), RequireJson:=True)) '兼容 Forge 1.7.10-pre4,#4057
|
||||
Dim Versions As New List(Of DlForgeVersionEntry)
|
||||
Try
|
||||
Dim Recommended As String = McDownloadForgeRecommendedGet(Loader.Input)
|
||||
@@ -836,8 +842,8 @@
|
||||
Public DlNeoForgeListOfficialLoader As New LoaderTask(Of Integer, DlNeoForgeListResult)("DlNeoForgeList Official", AddressOf DlNeoForgeListOfficialMain)
|
||||
Private Sub DlNeoForgeListOfficialMain(Loader As LoaderTask(Of Integer, DlNeoForgeListResult))
|
||||
'获取版本列表 JSON
|
||||
Dim ResultLatest As String = NetGetCodeByLoader("https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/neoforge", UseBrowserUserAgent:=True, IsJson:=True)
|
||||
Dim ResultLegacy As String = NetGetCodeByLoader("https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/forge", UseBrowserUserAgent:=True, IsJson:=True)
|
||||
Dim ResultLatest As String = NetRequestByClientRetry("https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/neoforge", RequireJson:=True)
|
||||
Dim ResultLegacy As String = NetRequestByClientRetry("https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/forge", RequireJson:=True)
|
||||
If ResultLatest.Length < 100 OrElse ResultLegacy.Length < 100 Then Throw New Exception("获取到的版本列表长度不足(" & ResultLatest & ")")
|
||||
'解析
|
||||
Try
|
||||
@@ -854,8 +860,8 @@
|
||||
Public DlNeoForgeListBmclapiLoader As New LoaderTask(Of Integer, DlNeoForgeListResult)("DlNeoForgeList Bmclapi", AddressOf DlNeoForgeListBmclapiMain)
|
||||
Public Sub DlNeoForgeListBmclapiMain(Loader As LoaderTask(Of Integer, DlNeoForgeListResult))
|
||||
'获取版本列表 JSON
|
||||
Dim ResultLatest As String = NetGetCodeByLoader("https://bmclapi2.bangbang93.com/neoforge/meta/api/maven/details/releases/net/neoforged/neoforge", UseBrowserUserAgent:=True, IsJson:=True)
|
||||
Dim ResultLegacy As String = NetGetCodeByLoader("https://bmclapi2.bangbang93.com/neoforge/meta/api/maven/details/releases/net/neoforged/forge", UseBrowserUserAgent:=True, IsJson:=True)
|
||||
Dim ResultLatest As String = NetRequestByClientRetry("https://bmclapi2.bangbang93.com/neoforge/meta/api/maven/details/releases/net/neoforged/neoforge", RequireJson:=True)
|
||||
Dim ResultLegacy As String = NetRequestByClientRetry("https://bmclapi2.bangbang93.com/neoforge/meta/api/maven/details/releases/net/neoforged/forge", RequireJson:=True)
|
||||
If ResultLatest.Length < 100 OrElse ResultLegacy.Length < 100 Then Throw New Exception("获取到的版本列表长度不足(" & ResultLatest & ")")
|
||||
'解析
|
||||
Try
|
||||
@@ -960,7 +966,7 @@
|
||||
''' </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("https://dl.liteloader.com/versions/versions.json", IsJson:=True)
|
||||
Dim Result As JObject = GetJson(NetRequestByClientRetry("https://dl.liteloader.com/versions/versions.json", RequireJson:=True))
|
||||
Try
|
||||
Dim Json As JObject = Result("versions")
|
||||
Dim Versions As New List(Of DlLiteLoaderListEntry)
|
||||
@@ -988,7 +994,7 @@
|
||||
''' </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", IsJson:=True)
|
||||
Dim Result As JObject = GetJson(NetRequestByClientRetry("https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json", RequireJson:=True))
|
||||
Try
|
||||
Dim Json As JObject = Result("versions")
|
||||
Dim Versions As New List(Of DlLiteLoaderListEntry)
|
||||
@@ -1059,7 +1065,7 @@
|
||||
''' </summary>
|
||||
Public DlFabricListOfficialLoader As New LoaderTask(Of Integer, DlFabricListResult)("DlFabricList Official", AddressOf DlFabricListOfficialMain)
|
||||
Private Sub DlFabricListOfficialMain(Loader As LoaderTask(Of Integer, DlFabricListResult))
|
||||
Dim Result As JObject = NetGetCodeByRequestRetry("https://meta.fabricmc.net/v2/versions", IsJson:=True)
|
||||
Dim Result As JObject = GetJson(NetRequestByClientRetry("https://meta.fabricmc.net/v2/versions", RequireJson:=True))
|
||||
Try
|
||||
Dim Output = New DlFabricListResult With {.IsOfficial = True, .SourceName = "Fabric 官方源", .Value = Result}
|
||||
If Output.Value("game") Is Nothing OrElse Output.Value("loader") Is Nothing OrElse Output.Value("installer") Is Nothing Then Throw New Exception("获取到的列表缺乏必要项")
|
||||
@@ -1074,7 +1080,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", IsJson:=True)
|
||||
Dim Result As JObject = GetJson(NetRequestByClientRetry("https://bmclapi2.bangbang93.com/fabric-meta/v2/versions", RequireJson:=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("获取到的列表缺乏必要项")
|
||||
@@ -1101,72 +1107,37 @@
|
||||
#Region "DlMod | Mod 镜像源请求"
|
||||
|
||||
''' <summary>
|
||||
''' 对可能涉及 Mod 镜像源的请求进行处理,返回字符串或 JObject。
|
||||
''' 调用 NetGetCodeByRequest,会进行重试。
|
||||
''' </summary>
|
||||
Public Function DlModRequest(Url As String, Optional IsJson As Boolean = False) As Object
|
||||
Dim Urls As New List(Of KeyValuePair(Of String, Integer))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 5))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 20))
|
||||
'Dim McimUrl As String = DlSourceModGet(Url)
|
||||
'If McimUrl <> Url Then
|
||||
' Select Case Setup.Get("ToolDownloadMod")
|
||||
' Case 0
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 5))
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 10))
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(Url, 15))
|
||||
' Case 1
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(Url, 5))
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 5))
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(Url, 15))
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 10))
|
||||
' Case Else
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(Url, 5))
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(Url, 15))
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 10))
|
||||
' End Select
|
||||
'End If
|
||||
Dim Exs As String = ""
|
||||
For Each Source In Urls
|
||||
Try
|
||||
Return NetGetCodeByRequestOnce(Source.Key, Encode:=Encoding.UTF8, Timeout:=Source.Value * 1000, IsJson:=IsJson, UseBrowserUserAgent:=True)
|
||||
Catch ex As Exception
|
||||
Exs += ex.Message + vbCrLf
|
||||
End Try
|
||||
Next
|
||||
Throw New Exception(Exs)
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 对可能涉及 Mod 镜像源的请求进行处理。
|
||||
''' 对可能涉及 Mod 镜像源的请求进行处理,返回 JToken。
|
||||
''' 调用 NetRequest,会进行重试。
|
||||
''' </summary>
|
||||
Public Function DlModRequest(Url As String, Method As String, Data As String, ContentType As String) As String
|
||||
Public Function DlModRequest(Url As String, Optional Method As HttpMethod = Nothing,
|
||||
Optional Content As String = Nothing, Optional ContentType As String = Nothing) As JToken
|
||||
Dim Urls As New List(Of KeyValuePair(Of String, Integer))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 5))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 20))
|
||||
'Dim McimUrl As String = DlSourceModGet(Url)
|
||||
'If McimUrl <> Url Then
|
||||
' Select Case Setup.Get("ToolDownloadMod")
|
||||
' Case 0
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 5))
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 10))
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(Url, 15))
|
||||
' Case 1
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(Url, 5))
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 5))
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(Url, 15))
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 10))
|
||||
' Case Else
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(Url, 5))
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(Url, 15))
|
||||
' Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 10))
|
||||
' End Select
|
||||
'End If
|
||||
Dim McimUrl As String = DlSourceModGet(Url)
|
||||
If McimUrl <> Url Then
|
||||
Select Case Setup.Get("ToolDownloadMod")
|
||||
Case 0
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 10))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 10))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 30))
|
||||
Case 1
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 10)) '至少 10s,要不然有时候远端服务器来不及完成
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 10))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 30))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 30))
|
||||
Case Else
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 10))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 30))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 30))
|
||||
End Select
|
||||
Else
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 10))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 30))
|
||||
End If
|
||||
Dim Exs As String = ""
|
||||
For Each Source In Urls
|
||||
Try
|
||||
Return NetRequestOnce(Source.Key, Method, Data, ContentType, Timeout:=Source.Value * 1000)
|
||||
Return GetJson(NetRequestByClient(Source.Key, Method, Content, ContentType, Timeout:=Source.Value * 1000, Encoding:=Encoding.UTF8, RequireJson:=True))
|
||||
Catch ex As Exception
|
||||
Exs += ex.Message + vbCrLf
|
||||
End Try
|
||||
@@ -1269,7 +1240,14 @@
|
||||
|
||||
'Mod 下载源
|
||||
Public Function DlSourceModGet(Original As String) As String
|
||||
Return Original
|
||||
Return Original.
|
||||
Replace("api.modrinth.com", "mod.mcimirror.top/modrinth").
|
||||
Replace("staging-api.modrinth.com", "mod.mcimirror.top/modrinth").
|
||||
Replace("cdn.modrinth.com", "mod.mcimirror.top").
|
||||
Replace("api.curseforge.com", "mod.mcimirror.top/curseforge").
|
||||
Replace("edge.forgecdn.net", "mod.mcimirror.top").
|
||||
Replace("mediafilez.forgecdn.net", "mod.mcimirror.top").
|
||||
Replace("media.forgecdn.net", "mod.mcimirror.top")
|
||||
End Function
|
||||
|
||||
'Loader 自动切换
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
'路径
|
||||
''' <summary>
|
||||
''' Java.exe 文件的完整路径。
|
||||
''' java.exe 文件的完整路径。
|
||||
''' </summary>
|
||||
Public ReadOnly Property PathJava As String
|
||||
Get
|
||||
@@ -18,15 +18,7 @@
|
||||
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 文件所在文件夹的路径,以 \ 结尾。
|
||||
''' java.exe 文件所在文件夹的路径,以 \ 结尾。
|
||||
''' </summary>
|
||||
Public PathFolder As String
|
||||
''' <summary>
|
||||
@@ -102,21 +94,15 @@
|
||||
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
|
||||
If File.Exists(PathFolder & "pdf-bookmark") Then
|
||||
Throw New Exception("不兼容 PDF Bookmark 的 Java") '#5326
|
||||
End If
|
||||
If Not File.Exists(PathJava) Then Throw New FileNotFoundException("未找到 java.exe 文件", PathJava)
|
||||
If File.Exists(PathFolder & "pdf-bookmark") Then Throw New Exception("不兼容 PDF Bookmark 的 Java") '#5326
|
||||
IsJre = Not File.Exists(PathFolder & "javac.exe")
|
||||
'运行 -version
|
||||
Output = ShellAndGetOutput(PathFolder & "java.exe", "-version", 15000).ToLower
|
||||
Output = StartProcessAndGetOutput(PathJava, "-version", 15000).ToLower
|
||||
If Output = "" Then Throw New ApplicationException("尝试运行该 Java 失败")
|
||||
If ModeDebug Then Log("[Java] Java 检查输出:" & PathFolder & "java.exe" & vbCrLf & Output)
|
||||
If ModeDebug Then Log("[Java] Java 检查输出:" & PathJava & vbCrLf & Output)
|
||||
If Output.Contains("/lib/ext exists") Then Throw New ApplicationException("无法运行该 Java,请在删除 Java 文件夹中的 /lib/ext 文件夹后再试")
|
||||
If Output.Contains("a fatal error") Then Throw New ApplicationException("无法运行该 Java,该 Java 或系统存在问题")
|
||||
'获取详细信息
|
||||
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"
|
||||
@@ -135,17 +121,17 @@
|
||||
End If
|
||||
Is64Bit = Output.Contains("64-bit")
|
||||
If Version.Minor <= 4 OrElse Version.Minor >= 100 Then Throw New ApplicationException("分析详细信息失败,获取的版本为 " & Version.ToString)
|
||||
'基于 #3649,在 64 位系统上禁用 32 位 Java
|
||||
'#3649:在 64 位系统上禁用 32 位 Java
|
||||
If Not Is64Bit AndAlso Not Is32BitSystem Then Throw New Exception("该 Java 为 32 位版本,请安装 64 位的 Java")
|
||||
'基于 #2249 发现 JRE 17 似乎也导致了 Forge 安装失败,干脆禁用更多版本的 JRE
|
||||
'#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)
|
||||
Log("[Java] 检查失败的 Java 输出:" & If(PathJava, "Nothing") & vbCrLf & If(Output, "无程序输出"))
|
||||
Throw New Exception("检查 Java 失败(" & If(PathJava, "Nothing") & ")", ex)
|
||||
End Try
|
||||
IsChecked = True
|
||||
End Sub
|
||||
@@ -459,10 +445,12 @@ NoUserJava:
|
||||
''' 将 Java 按照适用性排序。
|
||||
''' </summary>
|
||||
Public Function JavaSorter(Left As JavaEntry, Right As JavaEntry) As Boolean
|
||||
Dim PathInfo As New DirectoryInfo(Path)
|
||||
Dim PathMcInfo As New DirectoryInfo(PathMcFolder)
|
||||
'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
|
||||
ProgramPathParent = If(PathInfo.Parent, PathInfo).FullName
|
||||
If PathMcFolder <> "" Then MinecraftPathParent = If(PathMcInfo.Parent, PathMcInfo).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
|
||||
@@ -529,6 +517,8 @@ NoUserJava:
|
||||
If Disk.DriveType = DriveType.Network Then Continue For '跳过网络驱动器(#3705)
|
||||
JavaSearchFolder(Disk.Name, JavaPreList, False)
|
||||
Next
|
||||
'查找 .jdks 文件夹中的 Java
|
||||
JavaSearchFolder(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) & "\.jdks\", JavaPreList, False, True)
|
||||
'查找 APPDATA 文件夹中的 Java
|
||||
JavaSearchFolder(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\", JavaPreList, False)
|
||||
JavaSearchFolder(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) & "\", JavaPreList, False)
|
||||
@@ -656,7 +646,7 @@ Wait:
|
||||
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(ShortenPath(OriginalPath)), Results, Source, IsFullSearch)
|
||||
JavaSearchFolder(New DirectoryInfo(OriginalPath), Results, Source, IsFullSearch)
|
||||
Catch ex As UnauthorizedAccessException
|
||||
Log("[Java] 遍历查找 Java 时遭遇无权限的文件夹:" & OriginalPath)
|
||||
Catch ex As Exception
|
||||
@@ -677,14 +667,14 @@ Wait:
|
||||
'若该目录有 Java,则加入结果
|
||||
If File.Exists(Path & "javaw.exe") Then Results(Path) = Source
|
||||
'查找其下的所有文件夹
|
||||
'不应使用网易的 Java:https://github.com/Hex-Dragon/PCL2/issues/1279#issuecomment-2761489121
|
||||
Dim Keywords = {"java", "jdk", "env", "环境", "run", "软件", "jre", "mc", "dragon",
|
||||
'不应使用网易的 Java:https://github.com/Meloong-Git/PCL/issues/1279#issuecomment-2761489121
|
||||
Dim Keywords = {"java", "jdk", "env", "环境", "run", "软件", "jre", "mc", "dragon", "bin",
|
||||
"soft", "cache", "temp", "corretto", "roaming", "users", "craft", "program", "世界", "net",
|
||||
"游戏", "oracle", "game", "file", "data", "jvm", "服务", "server", "客户", "client", "整合",
|
||||
"应用", "运行", "前置", "mojang", "官启", "新建文件夹", "eclipse", "microsoft", "hotspot",
|
||||
"runtime", "x86", "x64", "forge", "原版", "optifine", "官方", "启动", "hmcl", "mod", "高清",
|
||||
"download", "launch", "程序", "path", "version", "baka", "pcl", "zulu", "local", "packages",
|
||||
"4297127d64ec6", "1.", "启动"}
|
||||
"4297127d64ec6", "1.", "启动", "jbr"}
|
||||
For Each FolderInfo As DirectoryInfo In OriginalPath.EnumerateDirectories
|
||||
If FolderInfo.Attributes.HasFlag(FileAttributes.ReparsePoint) Then Continue For '跳过符号链接
|
||||
Dim SearchEntry = GetFolderNameFromPath(FolderInfo.Name).ToLower '用于搜索的字符串
|
||||
@@ -745,7 +735,7 @@ Wait:
|
||||
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 = NetGetCodeByLoader(DlVersionListOrder(
|
||||
Dim IndexFileStr As String = NetRequestByLoader(DlVersionListOrder(
|
||||
{"https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"},
|
||||
{"https://bmclapi2.bangbang93.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"}
|
||||
), IsJson:=True)
|
||||
@@ -757,7 +747,7 @@ Wait:
|
||||
Dim Address As String = TargetEntry.Value("manifest")("url")
|
||||
Log($"[Java] 准备下载 Java {TargetEntry.Value("version")("name")}({TargetEntry.Key}):{Address}")
|
||||
'获取文件列表
|
||||
Dim ListFileStr As String = NetGetCodeByLoader(DlSourceOrder({Address}, {Address.Replace("piston-meta.mojang.com", "bmclapi2.bangbang93.com")}), IsJson:=True)
|
||||
Dim ListFileStr As String = NetRequestByLoader(DlSourceOrder({Address}, {Address.Replace("piston-meta.mojang.com", "bmclapi2.bangbang93.com")}), 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")
|
||||
|
||||
@@ -35,7 +35,7 @@ Public Module ModLaunch
|
||||
'预检查
|
||||
If Not RunInUi() Then Throw New Exception("McLaunchStart 必须在 UI 线程调用!")
|
||||
If McLaunchLoader.State = LoadState.Loading Then
|
||||
Hint("已有游戏正在启动中!", HintType.Critical)
|
||||
Hint("已有游戏正在启动中!", HintType.Red)
|
||||
Return False
|
||||
End If
|
||||
'强制切换需要启动的版本
|
||||
@@ -44,7 +44,7 @@ Public Module ModLaunch
|
||||
'检查版本
|
||||
CurrentLaunchOptions.Version.Load()
|
||||
If CurrentLaunchOptions.Version.State = McVersionState.Error Then
|
||||
Hint("无法启动 Minecraft:" & CurrentLaunchOptions.Version.Info, HintType.Critical)
|
||||
Hint("无法启动 Minecraft:" & CurrentLaunchOptions.Version.Info, HintType.Red)
|
||||
Return False
|
||||
End If
|
||||
'切换版本
|
||||
@@ -66,7 +66,7 @@ Public Module ModLaunch
|
||||
''' </summary>
|
||||
Public Sub McLaunchLog(Text As String)
|
||||
Text = FilterUserName(FilterAccessToken(Text, "*"), "*")
|
||||
RunInUi(Sub() FrmLaunchRight.LabLog.Text += vbCrLf & "[" & GetTimeNow() & "] " & Text)
|
||||
RunInUi(Sub() FrmLaunchRight.LabLog.Text += $"{vbCrLf}[{GetTimeNow()}] {Text}")
|
||||
Log("[Launch] " & Text)
|
||||
End Sub
|
||||
|
||||
@@ -98,9 +98,11 @@ Public Module ModLaunch
|
||||
McLaunchPrecheck()
|
||||
McLaunchLog("预检测已通过")
|
||||
Catch ex As Exception
|
||||
If Not ex.Message.StartsWithF("$$") Then Hint(ex.Message, HintType.Critical)
|
||||
If Not ex.Message.StartsWithF("$$") Then Hint(ex.Message, HintType.Red)
|
||||
Throw
|
||||
End Try
|
||||
Dim IsSavingBatch As Boolean = CurrentLaunchOptions?.SaveBatch IsNot Nothing
|
||||
|
||||
'正式加载
|
||||
Try
|
||||
'构造主加载器
|
||||
@@ -144,12 +146,18 @@ Public Module ModLaunch
|
||||
'成功与失败处理
|
||||
Select Case LaunchLoader.State
|
||||
Case LoadState.Finished
|
||||
Hint(McVersionCurrent.Name & " 启动成功!", HintType.Finish)
|
||||
Hint(McVersionCurrent.Name & " 启动成功!", HintType.Green)
|
||||
'上报
|
||||
If Not IsSavingBatch Then
|
||||
Telemetry("Minecraft 启动成功",
|
||||
"Version", McVersionCurrent.Version.ToString,
|
||||
"LoginType", GetStringFromEnum(CType(Setup.Get("LoginType"), McLoginType)))
|
||||
End If
|
||||
Case LoadState.Aborted
|
||||
If AbortHint Is Nothing Then
|
||||
Hint(If(CurrentLaunchOptions?.SaveBatch Is Nothing, "已取消启动!", "已取消导出启动脚本!"), HintType.Info)
|
||||
Hint(If(IsSavingBatch, "已取消导出启动脚本!", "已取消启动!"), HintType.Blue)
|
||||
Else
|
||||
Hint(AbortHint, HintType.Finish)
|
||||
Hint(AbortHint, HintType.Green)
|
||||
End If
|
||||
Case LoadState.Failed
|
||||
Throw LaunchLoader.Error
|
||||
@@ -162,7 +170,7 @@ NextInner:
|
||||
If CurrentEx.Message.StartsWithF("$") Then
|
||||
'若有以 $ 开头的错误信息,则以此为准显示提示
|
||||
'若错误信息为 $$,则不提示
|
||||
If Not CurrentEx.Message = "$$" Then MyMsgBox(CurrentEx.Message.TrimStart("$"), If(CurrentLaunchOptions?.SaveBatch Is Nothing, "启动失败", "导出启动脚本失败"))
|
||||
If Not CurrentEx.Message = "$$" Then MyMsgBox(CurrentEx.Message.TrimStart("$"), If(IsSavingBatch, "导出启动脚本失败", "启动失败"))
|
||||
Throw
|
||||
ElseIf CurrentEx.InnerException IsNot Nothing Then
|
||||
'检查下一级错误
|
||||
@@ -170,10 +178,17 @@ NextInner:
|
||||
GoTo NextInner
|
||||
Else
|
||||
'没有特殊处理过的错误信息
|
||||
McLaunchLog("错误:" & GetExceptionDetail(ex))
|
||||
McLaunchLog("错误:" & ex.GetDetail())
|
||||
Log(ex,
|
||||
If(CurrentLaunchOptions?.SaveBatch Is Nothing, "Minecraft 启动失败", "导出启动脚本失败"), LogLevel.Msgbox,
|
||||
If(CurrentLaunchOptions?.SaveBatch Is Nothing, "启动失败", "导出启动脚本失败"))
|
||||
If(IsSavingBatch, "导出启动脚本失败", "Minecraft 启动失败"), LogLevel.Msgbox,
|
||||
If(IsSavingBatch, "导出启动脚本失败", "启动失败"))
|
||||
'上报
|
||||
If Not IsSavingBatch Then
|
||||
Telemetry("Minecraft 启动失败",
|
||||
"Version", McVersionCurrent.Version.ToString,
|
||||
"LoginType", GetStringFromEnum(CType(Setup.Get("LoginType"), McLoginType)),
|
||||
"Exception", FilterUserName(FilterAccessToken(ex.GetDetail(), "*"), "*"))
|
||||
End If
|
||||
Throw
|
||||
End If
|
||||
End Try
|
||||
@@ -255,7 +270,7 @@ NextInner:
|
||||
Select Case MyMsgBox("你必须先登录正版账号,才能进行离线登录!", "正版验证", "购买正版", "试玩", "返回",
|
||||
Button1Action:=Sub() OpenWebsite("https://www.xbox.com/zh-cn/games/store/minecraft-java-bedrock-edition-for-pc/9nxp44l49shj"))
|
||||
Case 2
|
||||
Hint("游戏将以试玩模式启动!", HintType.Critical)
|
||||
Hint("游戏将以试玩模式启动!", HintType.Red)
|
||||
CurrentLaunchOptions.ExtraArgs.Add("--demo")
|
||||
Case 3
|
||||
Throw New Exception("$$")
|
||||
@@ -594,12 +609,8 @@ SkipLogin:
|
||||
McLoginRequestValidate(Data)
|
||||
GoTo LoginFinish
|
||||
Catch ex As Exception
|
||||
Dim AllMessage = GetExceptionDetail(ex)
|
||||
McLaunchLog("验证登录失败:" & AllMessage)
|
||||
If (AllMessage.Contains("超时") OrElse AllMessage.Contains("imeout")) AndAlso Not AllMessage.Contains("403") Then
|
||||
McLaunchLog("已触发超时登录失败")
|
||||
Throw New Exception("$登录失败:你的网络环境不佳,导致难以连接到海外服务器。" & vbCrLf & "请稍后重试,或使用加速器或 VPN 以改善网络环境。")
|
||||
End If
|
||||
Log(ex, "验证登录失败")
|
||||
If IsNetworkRelated(ex) Then Throw New Exception("$登录失败:你的网络环境不佳,导致难以连接到海外服务器。" & vbCrLf & "请稍后再试,或使用加速器、VPN 改善网络环境。")
|
||||
End Try
|
||||
Data.Progress = 0.25
|
||||
'尝试刷新登录
|
||||
@@ -609,7 +620,7 @@ Refresh:
|
||||
McLoginRequestRefresh(Data, NeedRefresh)
|
||||
GoTo LoginFinish
|
||||
Catch ex As Exception
|
||||
McLaunchLog("刷新登录失败:" & GetExceptionDetail(ex))
|
||||
McLaunchLog("刷新登录失败:" & ex.GetDetail())
|
||||
If WasRefreshed Then Throw New Exception("二轮刷新登录失败", ex)
|
||||
End Try
|
||||
Data.Progress = If(NeedRefresh, 0.85, 0.45)
|
||||
@@ -619,7 +630,7 @@ Refresh:
|
||||
If Data.IsAborted Then Throw New ThreadInterruptedException
|
||||
NeedRefresh = McLoginRequestLogin(Data)
|
||||
Catch ex As Exception
|
||||
McLaunchLog("登录失败:" & GetExceptionDetail(ex))
|
||||
McLaunchLog("登录失败:" & ex.GetDetail())
|
||||
Throw
|
||||
End Try
|
||||
If NeedRefresh Then
|
||||
@@ -684,11 +695,10 @@ LoginFinish:
|
||||
'发送登录请求
|
||||
Dim RequestData As New JObject(
|
||||
New JProperty("accessToken", AccessToken), New JProperty("clientToken", ClientToken), New JProperty("requestUser", True))
|
||||
NetRequestRetry(
|
||||
Url:=Data.Input.BaseUrl & "/validate",
|
||||
Method:="POST",
|
||||
Data:=RequestData.ToString(0),
|
||||
Headers:=New Dictionary(Of String, String) From {{"Accept-Language", "zh-CN"}},
|
||||
NetRequestByClientRetry(
|
||||
Data.Input.BaseUrl & "/validate", HttpMethod.Post,
|
||||
Content:=RequestData.ToString(Newtonsoft.Json.Formatting.None),
|
||||
Headers:={{"Accept-Language", "zh-CN"}},
|
||||
ContentType:="application/json; charset=utf-8") '没有返回值的
|
||||
'将登录结果输出
|
||||
Data.Output.AccessToken = AccessToken
|
||||
@@ -701,10 +711,9 @@ LoginFinish:
|
||||
End Sub
|
||||
Private Sub McLoginRequestRefresh(ByRef Data As LoaderTask(Of McLoginServer, McLoginResult), RequestUser As Boolean)
|
||||
McLaunchLog("刷新登录开始(Refresh, " & Data.Input.Token & ")")
|
||||
Dim LoginJson As JObject = GetJson(NetRequestRetry(
|
||||
Url:=Data.Input.BaseUrl & "/refresh",
|
||||
Method:="POST",
|
||||
Data:=New JObject(
|
||||
Dim LoginJson As JObject = GetJson(NetRequestByClientRetry(
|
||||
Data.Input.BaseUrl & "/refresh", HttpMethod.Post,
|
||||
Content:=New JObject(
|
||||
New JProperty("selectedProfile", New JObject(
|
||||
New JProperty("name", Setup.Get($"Cache{Data.Input.Token}Name")),
|
||||
New JProperty("id", Setup.Get($"Cache{Data.Input.Token}Uuid"))
|
||||
@@ -712,8 +721,8 @@ LoginFinish:
|
||||
New JProperty("accessToken", Setup.Get($"Cache{Data.Input.Token}Access")),
|
||||
New JProperty("requestUser", True)
|
||||
).ToString(Newtonsoft.Json.Formatting.None),
|
||||
Headers:=New Dictionary(Of String, String) From {{"Accept-Language", "zh-CN"}},
|
||||
ContentType:="application/json; charset=utf-8"))
|
||||
Headers:={{"Accept-Language", "zh-CN"}},
|
||||
ContentType:="application/json; charset=utf-8", RequireJson:=True))
|
||||
'将登录结果输出
|
||||
If LoginJson("selectedProfile") Is Nothing Then Throw New Exception("选择的角色 " & Setup.Get("Cache" & Data.Input.Token & "Name") & " 无效!")
|
||||
Data.Output.AccessToken = LoginJson("accessToken").ToString
|
||||
@@ -739,18 +748,17 @@ LoginFinish:
|
||||
New JProperty("username", Data.Input.UserName),
|
||||
New JProperty("password", Data.Input.Password),
|
||||
New JProperty("requestUser", True))
|
||||
Dim LoginJson As JObject = GetJson(NetRequestRetry(
|
||||
Url:=Data.Input.BaseUrl & "/authenticate",
|
||||
Method:="POST",
|
||||
Data:=RequestData.ToString(0),
|
||||
Headers:=New Dictionary(Of String, String) From {{"Accept-Language", "zh-CN"}},
|
||||
ContentType:="application/json; charset=utf-8"))
|
||||
Dim LoginJson As JObject = GetJson(NetRequestByClientRetry(
|
||||
Data.Input.BaseUrl & "/authenticate", HttpMethod.Post,
|
||||
Content:=RequestData.ToString(Newtonsoft.Json.Formatting.None),
|
||||
Headers:={{"Accept-Language", "zh-CN"}},
|
||||
ContentType:="application/json; charset=utf-8", RequireJson:=True))
|
||||
'检查登录结果
|
||||
If LoginJson("availableProfiles").Count = 0 Then
|
||||
If Data.Input.ForceReselectProfile Then Hint("你还没有创建角色,无法更换!", HintType.Critical)
|
||||
If Data.Input.ForceReselectProfile Then Hint("你还没有创建角色,无法更换!", HintType.Red)
|
||||
Throw New Exception("$你还没有创建角色,请在创建角色后再试!")
|
||||
ElseIf Data.Input.ForceReselectProfile AndAlso LoginJson("availableProfiles").Count = 1 Then
|
||||
Hint("你的账户中只有一个角色,无法更换!", HintType.Critical)
|
||||
Hint("你的账户中只有一个角色,无法更换!", HintType.Red)
|
||||
End If
|
||||
Dim SelectedName As String = Nothing
|
||||
Dim SelectedId As String = Nothing
|
||||
@@ -802,13 +810,14 @@ LoginFinish:
|
||||
McLaunchLog("登录成功(Login, " & Data.Input.Token & ")")
|
||||
Return NeedRefresh
|
||||
Catch ex As Exception
|
||||
Dim AllMessage As String = GetExceptionSummary(ex)
|
||||
Dim AllMessage As String = ex.GetBrief()
|
||||
Log(ex, "登录失败原始错误信息", LogLevel.Normal)
|
||||
'读取服务器返回的错误
|
||||
If TypeOf ex Is ResponsedWebException Then
|
||||
Dim ErrorMessage As String = Nothing
|
||||
Try
|
||||
ErrorMessage = GetJson(DirectCast(ex, ResponsedWebException).Response)("errorMessage")
|
||||
Dim Response = DirectCast(ex, ResponsedWebException).Response
|
||||
If ErrorMessage.ContainsF("errorMessage", True) Then ErrorMessage = GetJson(Response)("errorMessage")
|
||||
Catch
|
||||
End Try
|
||||
If Not String.IsNullOrWhiteSpace(ErrorMessage) Then
|
||||
@@ -826,7 +835,7 @@ LoginFinish:
|
||||
End If
|
||||
End If
|
||||
'通用关键字检测
|
||||
If AllMessage.Contains("403") Then
|
||||
If AllMessage.Contains("(403)") Then
|
||||
Select Case Data.Input.Type
|
||||
Case McLoginType.Auth
|
||||
Throw New Exception("$登录失败,以下为可能的原因:" & vbCrLf &
|
||||
@@ -840,8 +849,8 @@ LoginFinish:
|
||||
If(Data.Input.UserName.Contains("@"), "", " - 登录账号应为邮箱或统一通行证账号,而非游戏角色 ID。" & vbCrLf) &
|
||||
" - 只注册了账号,但没有加入对应服务器。")
|
||||
End Select
|
||||
ElseIf AllMessage.Contains("超时") OrElse AllMessage.Contains("imeout") OrElse AllMessage.Contains("网络请求失败") Then
|
||||
Throw New Exception("$登录失败:你的网络环境不佳,导致难以连接到海外服务器。" & vbCrLf & "请稍后重试,或使用加速器或 VPN 以改善网络环境。")
|
||||
ElseIf ex.IsNetworkRelated() Then
|
||||
Throw New Exception("$登录失败:你的网络环境不佳,导致难以连接到海外服务器。" & vbCrLf & "请稍后再试,或使用加速器、VPN 改善网络环境。")
|
||||
ElseIf ex.Message.StartsWithF("$") Then
|
||||
Throw
|
||||
Else
|
||||
@@ -858,8 +867,9 @@ LoginFinish:
|
||||
'初始请求
|
||||
Retry:
|
||||
McLaunchLog("开始微软登录步骤 1/6(原始登录)")
|
||||
Dim PrepareJson As JObject = GetJson(NetRequestRetry("https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode", "POST",
|
||||
$"client_id={OAuthClientId}&tenant=/consumers&scope=XboxLive.signin%20offline_access", "application/x-www-form-urlencoded"))
|
||||
Dim PrepareJson As JObject = GetJson(NetRequestByClientRetry("https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode", HttpMethod.Post,
|
||||
Content:=$"client_id={OAuthClientId}&tenant=/consumers&scope=XboxLive.signin%20offline_access",
|
||||
ContentType:="application/x-www-form-urlencoded", RequireJson:=True))
|
||||
McLaunchLog("网页登录地址:" & PrepareJson("verification_uri").ToString)
|
||||
|
||||
'弹窗
|
||||
@@ -888,13 +898,23 @@ Retry:
|
||||
|
||||
Dim Result As String
|
||||
Try
|
||||
Result = NetRequestMultiple("https://login.live.com/oauth20_token.srf", "POST",
|
||||
$"client_id={OAuthClientId}&refresh_token={Uri.EscapeDataString(Code)}&grant_type=refresh_token&scope=XboxLive.signin%20offline_access",
|
||||
"application/x-www-form-urlencoded", 2)
|
||||
Catch ex As Exception
|
||||
If ex.Message.ContainsF("must sign in again", True) OrElse ex.Message.ContainsF("password expired", True) OrElse
|
||||
(ex.Message.Contains("refresh_token") AndAlso ex.Message.Contains("is not valid")) Then '#269
|
||||
Result = NetRequestByClientMultiple("https://login.live.com/oauth20_token.srf", HttpMethod.Post,
|
||||
Content:=$"client_id={OAuthClientId}&refresh_token={Uri.EscapeDataString(Code)}&grant_type=refresh_token&scope=XboxLive.signin%20offline_access",
|
||||
ContentType:="application/x-www-form-urlencoded",
|
||||
Headers:={{"Accept-Language", "en-US,en;q=0.5"}, {"X-Requested-With", "XMLHttpRequest"}},
|
||||
ThreadCount:=2)
|
||||
Catch ex As ResponsedWebException
|
||||
'修改错误列表时,同时检查 MyMsgLogin.xaml.vb 中的对应代码
|
||||
Dim Response = ex.Response
|
||||
If Response.ContainsF("must sign in again", True) OrElse Response.ContainsF("password expired", True) OrElse
|
||||
(Response.Contains("refresh_token") AndAlso Response.Contains("is not valid")) Then '#269
|
||||
Return {"Relogin", ""}
|
||||
ElseIf Response.Contains("Account security interrupt") Then
|
||||
MyMsgBox("该账号由于安全问题无法登陆,请前往微软账户页获取更多信息。", "登录失败", "我知道了", IsWarn:=True)
|
||||
Throw New Exception("$$")
|
||||
ElseIf Response.Contains("service abuse") Then
|
||||
MyMsgBox("非常抱歉,该账号已被微软封禁,无法登录。", "登录失败", "我知道了", IsWarn:=True)
|
||||
Throw New Exception("$$")
|
||||
Else
|
||||
Throw
|
||||
End If
|
||||
@@ -918,8 +938,8 @@ Retry:
|
||||
""RelyingParty"": ""http://auth.xboxlive.com"",
|
||||
""TokenType"": ""JWT""
|
||||
}"
|
||||
Dim Result As String = NetRequestMultiple("https://user.auth.xboxlive.com/user/authenticate", "POST", Request, "application/json", 3)
|
||||
|
||||
Dim Result As String = NetRequestByClientMultiple("https://user.auth.xboxlive.com/user/authenticate", HttpMethod.Post,
|
||||
Content:=Request, ContentType:="application/json", RequireJson:=True)
|
||||
Dim ResultJson As JObject = GetJson(Result)
|
||||
Dim XBLToken As String = ResultJson("Token").ToString
|
||||
Return XBLToken
|
||||
@@ -940,21 +960,22 @@ Retry:
|
||||
}"
|
||||
Dim Result As String
|
||||
Try
|
||||
Result = NetRequestMultiple("https://xsts.auth.xboxlive.com/xsts/authorize", "POST", Request, "application/json", 3)
|
||||
Catch ex As Net.WebException
|
||||
Result = NetRequestByClientMultiple("https://xsts.auth.xboxlive.com/xsts/authorize", HttpMethod.Post,
|
||||
Content:=Request, ContentType:="application/json", RequireJson:=True)
|
||||
Catch ex As ResponsedWebException
|
||||
'参考 https://github.com/PrismarineJS/prismarine-auth/blob/master/src/common/Constants.js
|
||||
If ex.Message.Contains("2148916227") Then
|
||||
If ex.Response.Contains("2148916227") Then
|
||||
MyMsgBox("该账号似乎已被微软封禁,无法登录。", "登录失败", "我知道了", IsWarn:=True)
|
||||
Throw New Exception("$$")
|
||||
ElseIf ex.Message.Contains("2148916233") Then
|
||||
ElseIf ex.Response.Contains("2148916233") Then
|
||||
If MyMsgBox("你尚未注册 Xbox 账户,请在注册后再登录。", "登录提示", "注册", "取消") = 1 Then
|
||||
OpenWebsite("https://signup.live.com/signup")
|
||||
End If
|
||||
Throw New Exception("$$")
|
||||
ElseIf ex.Message.Contains("2148916235") Then
|
||||
MyMsgBox($"你的网络所在的国家或地区无法登录微软账号。{vbCrLf}请使用加速器或 VPN。", "登录失败", "我知道了")
|
||||
ElseIf ex.Response.Contains("2148916235") Then
|
||||
MyMsgBox($"你的网络所在的国家或地区无法登录微软账号。{vbCrLf}请使用加速器或 VPN,然后再试。", "登录失败", "我知道了")
|
||||
Throw New Exception("$$")
|
||||
ElseIf ex.Message.Contains("2148916238") Then
|
||||
ElseIf ex.Response.Contains("2148916238") Then
|
||||
If MyMsgBox("该账号年龄不足,你需要先修改出生日期,然后才能登录。" & vbCrLf &
|
||||
"该账号目前填写的年龄是否在 13 岁以上?", "登录提示", "13 岁以上", "12 岁以下", "我不知道") = 1 Then
|
||||
OpenWebsite("https://account.live.com/editprof.aspx")
|
||||
@@ -972,9 +993,10 @@ Retry:
|
||||
End Try
|
||||
|
||||
Dim ResultJson As JObject = GetJson(Result)
|
||||
Dim XSTSToken As String = ResultJson("Token").ToString
|
||||
Dim UHS As String = ResultJson("DisplayClaims")("xui")(0)("uhs").ToString
|
||||
Return {XSTSToken, UHS}
|
||||
Return {
|
||||
ResultJson("Token"), 'XSTSToken
|
||||
ResultJson("DisplayClaims")("xui")(0)("uhs") 'UHS
|
||||
}
|
||||
End Function
|
||||
'微软登录步骤 4:从 {XSTSToken, UHS} 获取 Minecraft AccessToken
|
||||
Private Function MsLoginStep4(Tokens As String()) As String
|
||||
@@ -983,29 +1005,29 @@ Retry:
|
||||
Dim Request As String = New JObject(New JProperty("identityToken", $"XBL3.0 x={Tokens(1)};{Tokens(0)}")).ToString(0)
|
||||
Dim Result As String
|
||||
Try
|
||||
Result = NetRequestRetry("https://api.minecraftservices.com/authentication/login_with_xbox", "POST", Request, "application/json")
|
||||
Result = NetRequestByClientRetry("https://api.minecraftservices.com/authentication/login_with_xbox", HttpMethod.Post,
|
||||
Content:=Request, ContentType:="application/json", RequireJson:=True)
|
||||
Catch ex As Net.WebException
|
||||
Dim Message As String = GetExceptionSummary(ex)
|
||||
Dim Message As String = ex.GetBrief()
|
||||
If Message.Contains("(429)") Then
|
||||
Log(ex, "微软登录第 5 步汇报 429")
|
||||
Log(ex, "微软登录第 4 步汇报 429")
|
||||
Throw New Exception("$登录尝试太过频繁,请等待几分钟后再试!")
|
||||
ElseIf Message.Contains("(403)") Then
|
||||
Log(ex, "微软登录第 5 步汇报 403")
|
||||
Log(ex, "微软登录第 4 步汇报 403")
|
||||
Throw New Exception("$当前 IP 的登录尝试异常。" & vbCrLf & "如果你使用了 VPN 或加速器,请把它们关掉或更换节点后再试!")
|
||||
Else
|
||||
Throw
|
||||
End If
|
||||
End Try
|
||||
|
||||
Dim ResultJson As JObject = GetJson(Result)
|
||||
Dim AccessToken As String = ResultJson("access_token").ToString
|
||||
Return AccessToken
|
||||
Return GetJson(Result)("access_token").ToString
|
||||
End Function
|
||||
'微软登录步骤 5:验证微软账号是否持有 MC,这也会刷新 XGP
|
||||
Private Sub MsLoginStep5(AccessToken As String)
|
||||
McLaunchLog("开始微软登录步骤 5/6")
|
||||
|
||||
Dim Result As String = NetRequestMultiple("https://api.minecraftservices.com/entitlements/mcstore", "GET", "", "application/json", 2, New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}})
|
||||
Dim Result As String = NetRequestByClientMultiple("https://api.minecraftservices.com/entitlements/mcstore",
|
||||
ContentType:="application/json", ThreadCount:=2, Headers:={{"Authorization", "Bearer " & AccessToken}}, RequireJson:=True)
|
||||
Try
|
||||
Dim ResultJson As JObject = GetJson(Result)
|
||||
If Not (ResultJson.ContainsKey("items") AndAlso ResultJson("items").Any) Then
|
||||
@@ -1016,7 +1038,7 @@ Retry:
|
||||
Throw New Exception("$$")
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "微软登录第 6 步异常:" & Result)
|
||||
Log(ex, "微软登录第 5 步异常:" & Result)
|
||||
Throw
|
||||
End Try
|
||||
End Sub
|
||||
@@ -1026,14 +1048,15 @@ Retry:
|
||||
|
||||
Dim Result As String
|
||||
Try
|
||||
Result = NetRequestMultiple("https://api.minecraftservices.com/minecraft/profile", "GET", "", "application/json", 2, New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}})
|
||||
Catch ex As Net.WebException
|
||||
Dim Message As String = GetExceptionSummary(ex)
|
||||
Result = NetRequestByClientMultiple("https://api.minecraftservices.com/minecraft/profile",
|
||||
ContentType:="application/json", ThreadCount:=2, Headers:={{"Authorization", "Bearer " & AccessToken}}, RequireJson:=True)
|
||||
Catch ex As WebException
|
||||
Dim Message As String = ex.GetBrief()
|
||||
If Message.Contains("(429)") Then
|
||||
Log(ex, "微软登录第 7 步汇报 429")
|
||||
Log(ex, "微软登录第 6 步汇报 429")
|
||||
Throw New Exception("$登录尝试太过频繁,请等待几分钟后再试!")
|
||||
ElseIf Message.Contains("(404)") Then
|
||||
Log(ex, "微软登录第 7 步汇报 404")
|
||||
Log(ex, "微软登录第 6 步汇报 404")
|
||||
RunInNewThread(
|
||||
Sub()
|
||||
Select Case MyMsgBox("请先创建 Minecraft 玩家档案,然后再重新登录。", "登录失败", "创建档案", "取消")
|
||||
@@ -1099,7 +1122,7 @@ Retry:
|
||||
If Len(Uuid) = 32 Then Return Uuid
|
||||
'从官网获取
|
||||
Try
|
||||
Dim GotJson As JObject = NetGetCodeByRequestRetry("https://api.mojang.com/users/profiles/minecraft/" & Name, IsJson:=True)
|
||||
Dim GotJson As JObject = GetJson(NetRequestByClientRetry("https://api.mojang.com/users/profiles/minecraft/" & Name, RequireJson:=True))
|
||||
If GotJson Is Nothing Then Throw New FileNotFoundException("正版玩家档案不存在(" & Name & ")")
|
||||
Uuid = If(GotJson("id"), "")
|
||||
Catch ex As Exception
|
||||
@@ -1205,6 +1228,12 @@ Retry:
|
||||
End If
|
||||
End If
|
||||
|
||||
'LiteLoader 检测
|
||||
If McVersionCurrent.Version.HasLiteLoader AndAlso McVersionCurrent.Version.IsStandardVersion Then '不管非标准版本
|
||||
'最高 Java 8
|
||||
MaxVer = If(New Version(1, 8, 999, 999) < MaxVer, New Version(1, 8, 999, 999), MaxVer)
|
||||
End If
|
||||
|
||||
'统一通行证检测
|
||||
If Setup.Get("LoginType") = McLoginType.Nide Then
|
||||
'至少 Java 8u101
|
||||
@@ -1275,7 +1304,7 @@ Retry:
|
||||
If McLaunchJavaSelected IsNot Nothing Then
|
||||
McLaunchLog("选择的 Java:" & McLaunchJavaSelected.ToString)
|
||||
Else
|
||||
Hint("没有可用的 Java,已取消启动!", HintType.Critical)
|
||||
Hint("没有可用的 Java,已取消启动!", HintType.Red)
|
||||
Throw New Exception("$$")
|
||||
End If
|
||||
|
||||
@@ -1349,23 +1378,13 @@ Retry:
|
||||
McLaunchLog("新版 Game 参数获取成功")
|
||||
End If
|
||||
'编码参数(#4700、#5892、#5909)
|
||||
If McLaunchJavaSelected.VersionCode >= 18 Then 'Dfile.encoding 需要放在 Dstdout.encoding 后面(#6934)
|
||||
If Not Arguments.Contains("-Dfile.encoding=") Then Arguments = "-Dfile.encoding=COMPAT " & Arguments
|
||||
End If
|
||||
If McLaunchJavaSelected.VersionCode > 8 Then
|
||||
If Not Arguments.Contains("-Dstdout.encoding=") Then Arguments = "-Dstdout.encoding=UTF-8 " & Arguments
|
||||
If Not Arguments.Contains("-Dstderr.encoding=") Then Arguments = "-Dstderr.encoding=UTF-8 " & Arguments
|
||||
End If
|
||||
If McLaunchJavaSelected.VersionCode >= 18 Then
|
||||
If Not Arguments.Contains("-Dfile.encoding=") Then Arguments = "-Dfile.encoding=COMPAT " & Arguments
|
||||
End If
|
||||
'替换参数
|
||||
Dim ReplaceArguments = McLaunchArgumentsReplace(McVersionCurrent, Loader)
|
||||
If String.IsNullOrWhiteSpace(ReplaceArguments("${version_type}")) Then
|
||||
'若自定义信息为空,则去掉该部分
|
||||
Arguments = Arguments.Replace(" --versionType ${version_type}", "")
|
||||
ReplaceArguments("${version_type}") = """"""
|
||||
End If
|
||||
For Each entry As KeyValuePair(Of String, String) In ReplaceArguments
|
||||
Arguments = Arguments.Replace(entry.Key, If(entry.Value.Contains(" ") OrElse entry.Value.Contains(":\"), """" & entry.Value & """", entry.Value))
|
||||
Next
|
||||
'MJSB
|
||||
Arguments = Arguments.Replace(" -Dos.name=Windows 10", " -Dos.name=""Windows 10""")
|
||||
'全屏
|
||||
@@ -1374,31 +1393,50 @@ Retry:
|
||||
For Each Arg In CurrentLaunchOptions.ExtraArgs
|
||||
Arguments += " " & Arg.Trim
|
||||
Next
|
||||
'自定义
|
||||
Dim ArgumentGame As String = Setup.Get("VersionAdvanceGame", Version:=McVersionCurrent)
|
||||
Arguments += " " & ArgumentReplace(If(ArgumentGame = "", Setup.Get("LaunchAdvanceGame"), ArgumentGame))
|
||||
'进行参数替换
|
||||
Dim ReplaceArguments = McLaunchArgumentsReplace(McVersionCurrent, Loader)
|
||||
If String.IsNullOrWhiteSpace(ReplaceArguments("${version_type}")) Then
|
||||
'若自定义信息为空,则去掉该部分
|
||||
Arguments = Arguments.Replace(" --versionType ${version_type}", "")
|
||||
ReplaceArguments("${version_type}") = """"""
|
||||
End If
|
||||
Dim FinalArguments As String = ""
|
||||
For Each Argument In Arguments.Split(" ")
|
||||
For Each Entry As KeyValuePair(Of String, String) In ReplaceArguments
|
||||
Argument = Argument.Replace(Entry.Key, Entry.Value)
|
||||
Next
|
||||
If (Argument.Contains(" ") OrElse Argument.Contains(":\")) AndAlso Not Argument.EndsWithF("""") Then Argument = $"""{Argument}"""
|
||||
FinalArguments += Argument & " "
|
||||
Next
|
||||
FinalArguments = FinalArguments.TrimEnd
|
||||
'替换 Module Path 为短路径(Forge 会在版本 JSON 硬编码这些路径)
|
||||
FinalArguments = FinalArguments.RegexReplaceEach("(?<= (-p|--module-path) "")[^""]+(?="" )",
|
||||
Function(ModulePath) ModulePath.Value.RegexReplaceEach("[A-Z]{1}:\\[^;]+", Function(m) ShortenPath(m.Value)))
|
||||
'进服
|
||||
Dim Server As String = If(String.IsNullOrEmpty(CurrentLaunchOptions.ServerIp), Setup.Get("VersionServerEnter", McVersionCurrent), CurrentLaunchOptions.ServerIp)
|
||||
If Server.Length > 0 Then
|
||||
If McVersionCurrent.ReleaseTime > New Date(2023, 4, 4) Then
|
||||
'QuickPlay
|
||||
Arguments += $" --quickPlayMultiplayer ""{Server}"""
|
||||
FinalArguments += $" --quickPlayMultiplayer ""{Server}"""
|
||||
Else
|
||||
'老版本
|
||||
If Server.Contains(":") Then
|
||||
'包含端口号
|
||||
Arguments += " --server " & Server.Split(":")(0) & " --port " & Server.Split(":")(1)
|
||||
FinalArguments += $" --server {Server.Split(":")(0)} --port {Server.Split(":")(1)}"
|
||||
Else
|
||||
'不包含端口号
|
||||
Arguments += " --server " & Server & " --port 25565"
|
||||
FinalArguments += $" --server {Server} --port 25565"
|
||||
End If
|
||||
If McVersionCurrent.Version.HasOptiFine Then Hint("OptiFine 与自动进入服务器可能不兼容,有概率导致材质丢失甚至游戏崩溃!", HintType.Critical)
|
||||
If McVersionCurrent.Version.HasOptiFine Then Hint("OptiFine 与自动进入服务器可能不兼容,有概率导致材质丢失甚至游戏崩溃!", HintType.Red)
|
||||
End If
|
||||
End If
|
||||
'自定义
|
||||
Dim ArgumentGame As String = Setup.Get("VersionAdvanceGame", Version:=McVersionCurrent)
|
||||
Arguments += " " & If(ArgumentGame = "", Setup.Get("LaunchAdvanceGame"), ArgumentGame)
|
||||
'输出
|
||||
McLaunchLog("Minecraft 启动参数:")
|
||||
McLaunchLog(Arguments)
|
||||
McLaunchArgument = Arguments
|
||||
McLaunchLog(FinalArguments)
|
||||
McLaunchArgument = FinalArguments
|
||||
End Sub
|
||||
|
||||
'Jvm 部分(第一段)
|
||||
@@ -1412,7 +1450,7 @@ Retry:
|
||||
If ArgumentJvm = "" Then ArgumentJvm = Setup.Get("LaunchAdvanceJvm")
|
||||
If Not ArgumentJvm.Contains("-Dlog4j2.formatMsgNoLookups=true") Then ArgumentJvm += " -Dlog4j2.formatMsgNoLookups=true"
|
||||
ArgumentJvm = ArgumentJvm.Replace(" -XX:MaxDirectMemorySize=256M", "") '#3511 的清理
|
||||
DataList.Insert(0, ArgumentJvm) '可变 JVM 参数
|
||||
DataList.Insert(0, ArgumentReplace(ArgumentJvm)) '可变 JVM 参数
|
||||
DataList.Add("-Xmn" & Math.Floor(PageVersionSetup.GetRam(McVersionCurrent, Not McLaunchJavaSelected.Is64Bit) * 1024 * 0.15) & "m")
|
||||
DataList.Add("-Xmx" & Math.Floor(PageVersionSetup.GetRam(McVersionCurrent, Not McLaunchJavaSelected.Is64Bit) * 1024) & "m")
|
||||
DataList.Add("""-Djava.library.path=" & GetNativesFolder() & """")
|
||||
@@ -1424,11 +1462,10 @@ Retry:
|
||||
End If
|
||||
'Authlib-Injector
|
||||
If McLoginLoader.Output.Type = "Auth" Then
|
||||
Dim Server As String = If(McLoginLoader.Input.Type = McLoginType.Legacy,
|
||||
"http://hiperauth.tech/api/yggdrasil-hiper/", 'HiPer 登录
|
||||
Setup.Get("VersionServerAuthServer", McVersionCurrent))
|
||||
If McLaunchJavaSelected.VersionCode >= 6 Then DataList.Add("-Djavax.net.ssl.trustStoreType=WINDOWS-ROOT") '信任系统根证书(5252#)
|
||||
Dim Server As String = Setup.Get("VersionServerAuthServer", McVersionCurrent)
|
||||
Try
|
||||
Dim Response As String = NetGetCodeByRequestRetry(Server, Encoding.UTF8)
|
||||
Dim Response As String = NetRequestByClientRetry(Server, Encoding:=Encoding.UTF8)
|
||||
DataList.Insert(0, "-javaagent:""" & PathPure & "authlib-injector.jar""=" & Server &
|
||||
" -Dauthlibinjector.side=client" &
|
||||
" -Dauthlibinjector.yggdrasil.prefetched=" & Convert.ToBase64String(Encoding.UTF8.GetBytes(Response)))
|
||||
@@ -1451,6 +1488,9 @@ Retry:
|
||||
DataList.Add(Version.JsonObject("mainClass"))
|
||||
End If
|
||||
|
||||
'处理 DataList 的换行符
|
||||
DataList = DataList.Select(Function(d) d.Replace(vbCrLf, vbCr).Replace(vbLf, vbCr).Replace(vbCr, " ").Trim).ToList
|
||||
|
||||
Return Join(DataList, " ")
|
||||
End Function
|
||||
Private Function McLaunchArgumentsJvmNew(Version As McVersion) As String
|
||||
@@ -1493,11 +1533,10 @@ NextVersion:
|
||||
End If
|
||||
'Authlib-Injector
|
||||
If McLoginLoader.Output.Type = "Auth" Then
|
||||
Dim Server As String = If(McLoginLoader.Input.Type = McLoginType.Legacy,
|
||||
"http://hiperauth.tech/api/yggdrasil-hiper/", 'HiPer 登录
|
||||
Setup.Get("VersionServerAuthServer", Version:=McVersionCurrent))
|
||||
If McLaunchJavaSelected.VersionCode >= 6 Then DataList.Add("-Djavax.net.ssl.trustStoreType=WINDOWS-ROOT") '信任系统根证书(5252#)
|
||||
Dim Server As String = Setup.Get("VersionServerAuthServer", Version:=McVersionCurrent)
|
||||
Try
|
||||
Dim Response As String = NetGetCodeByRequestRetry(Server, Encoding.UTF8)
|
||||
Dim Response As String = NetRequestByClientRetry(Server, Encoding:=Encoding.UTF8)
|
||||
DataList.Insert(0, "-javaagent:""" & PathPure & "authlib-injector.jar""=" & Server &
|
||||
" -Dauthlibinjector.side=client" &
|
||||
" -Dauthlibinjector.yggdrasil.prefetched=" & Convert.ToBase64String(Encoding.UTF8.GetBytes(Response)))
|
||||
@@ -1517,7 +1556,7 @@ NextVersion:
|
||||
'如果不合并,会导致 Forge 1.17 启动无效,它有两个 --add-exports,进一步导致其中一个在后面被去重
|
||||
Dim DeDuplicateDataList As New List(Of String)
|
||||
For i = 0 To DataList.Count - 1
|
||||
Dim CurrentEntry As String = DataList(i)
|
||||
Dim CurrentEntry As String = DataList(i).Replace(vbCrLf, vbCr).Replace(vbLf, vbCr).Replace(vbCr, " ").Trim
|
||||
If DataList(i).StartsWithF("-") Then
|
||||
Do While i < DataList.Count - 1
|
||||
If DataList(i + 1).StartsWithF("-") Then
|
||||
@@ -1531,11 +1570,9 @@ NextVersion:
|
||||
DeDuplicateDataList.Add(CurrentEntry.Trim.Replace("McEmu= ", "McEmu="))
|
||||
Next
|
||||
|
||||
'#3511 的清理
|
||||
DeDuplicateDataList.Remove("-XX:MaxDirectMemorySize=256M")
|
||||
|
||||
'去重
|
||||
Dim Result As String = Join(DeDuplicateDataList.Distinct.ToList, " ")
|
||||
DeDuplicateDataList.Remove("-XX:MaxDirectMemorySize=256M") '#3511 的清理
|
||||
Dim Result As String = DeDuplicateDataList.Distinct.Join(" ")
|
||||
|
||||
'添加 MainClass
|
||||
If Version.JsonObject("mainClass") Is Nothing Then
|
||||
@@ -1556,7 +1593,7 @@ NextVersion:
|
||||
If Not BasicString.Contains("--height") Then BasicString += " --height ${resolution_height} --width ${resolution_width}"
|
||||
DataList.Add(BasicString)
|
||||
|
||||
Dim Result As String = Join(DataList, " ")
|
||||
Dim Result As String = DataList.Join(" ")
|
||||
|
||||
'特别改变 OptiFineTweaker
|
||||
If (Version.Version.HasForge OrElse Version.Version.HasLiteLoader) AndAlso Version.Version.HasOptiFine Then
|
||||
@@ -1661,7 +1698,7 @@ NextVersion:
|
||||
GameArguments.Add("${launcher_version}", VersionCode)
|
||||
GameArguments.Add("${version_name}", Version.Name)
|
||||
Dim ArgumentInfo As String = Setup.Get("VersionArgumentInfo", Version:=McVersionCurrent)
|
||||
GameArguments.Add("${version_type}", If(ArgumentInfo = "", Setup.Get("LaunchArgumentInfo"), ArgumentInfo))
|
||||
GameArguments.Add("${version_type}", ArgumentReplace(If(ArgumentInfo = "", Setup.Get("LaunchArgumentInfo"), ArgumentInfo)))
|
||||
GameArguments.Add("${game_directory}", ShortenPath(Left(McVersionCurrent.PathIndie, McVersionCurrent.PathIndie.Count - 1)))
|
||||
GameArguments.Add("${assets_root}", ShortenPath(PathMcFolder & "assets"))
|
||||
GameArguments.Add("${user_properties}", "{}")
|
||||
@@ -1671,6 +1708,7 @@ NextVersion:
|
||||
GameArguments.Add("${access_token}", McLoginLoader.Output.AccessToken)
|
||||
GameArguments.Add("${auth_session}", McLoginLoader.Output.AccessToken)
|
||||
GameArguments.Add("${user_type}", "msa") '#1221
|
||||
GameArguments.Add("${primary_jar}", ShortenPath(Version.Path & Version.Name & ".jar")) '#6942
|
||||
|
||||
'窗口尺寸参数
|
||||
Dim GameSize As Size
|
||||
@@ -1759,7 +1797,7 @@ NextVersion:
|
||||
File.Delete(FilePath)
|
||||
Catch ex As UnauthorizedAccessException
|
||||
McLaunchLog("删除原 dll 访问被拒绝,这通常代表有一个 MC 正在运行,跳过解压:" & FilePath)
|
||||
McLaunchLog("实际的错误信息:" & GetExceptionSummary(ex))
|
||||
McLaunchLog("实际的错误信息:" & ex.GetDetail())
|
||||
Exit For
|
||||
End Try
|
||||
End If
|
||||
@@ -1779,7 +1817,7 @@ NextVersion:
|
||||
File.Delete(FileName)
|
||||
Catch ex As UnauthorizedAccessException
|
||||
McLaunchLog("删除多余文件访问被拒绝,跳过删除步骤")
|
||||
McLaunchLog("实际的错误信息:" & GetExceptionSummary(ex))
|
||||
McLaunchLog("实际的错误信息:" & ex.GetDetail())
|
||||
Return
|
||||
End Try
|
||||
Next
|
||||
@@ -1790,10 +1828,14 @@ NextVersion:
|
||||
''' </summary>
|
||||
Private Function GetNativesFolder() As String
|
||||
Dim Result As String = McVersionCurrent.Path & McVersionCurrent.Name & "-natives"
|
||||
If IsGBKEncoding OrElse Result.IsASCII() Then Return Result
|
||||
Result = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\.minecraft\bin\natives"
|
||||
If Result.IsASCII() Then Return Result
|
||||
Return OsDrive & "ProgramData\PCL\natives"
|
||||
If Not (IsGBKEncoding OrElse Result.IsASCII()) Then
|
||||
Result = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\.minecraft\bin\natives"
|
||||
If Not Result.IsASCII() Then
|
||||
Result = OsDrive & "ProgramData\PCL\natives"
|
||||
End If
|
||||
End If
|
||||
Directory.CreateDirectory(Result) '提前创建,这样 ShortenPath 才有结果(否则在长路径下首次启动 Forge 会崩溃)
|
||||
Return ShortenPath(Result)
|
||||
End Function
|
||||
|
||||
#End Region
|
||||
@@ -1805,7 +1847,7 @@ NextVersion:
|
||||
'要求 Java 使用高性能显卡
|
||||
If Setup.Get("LaunchAdvanceGraphicCard") Then
|
||||
Try
|
||||
SetGPUPreference(McLaunchJavaSelected.PathJavaw)
|
||||
SetGPUPreference(McLaunchJavaSelected.PathJava)
|
||||
SetGPUPreference(PathWithName)
|
||||
Catch ex As Exception
|
||||
If IsAdmin() Then
|
||||
@@ -1813,7 +1855,7 @@ NextVersion:
|
||||
Else
|
||||
Log(ex, "直接调整显卡设置失败,将以管理员权限重启 PCL 再次尝试")
|
||||
Try
|
||||
If RunAsAdmin($"--gpu ""{McLaunchJavaSelected.PathJavaw}""") = ProcessReturnValues.TaskDone Then
|
||||
If RunAsAdmin($"--gpu ""{McLaunchJavaSelected.PathJava}""") = ProcessReturnValues.TaskDone Then
|
||||
McLaunchLog("以管理员权限重启 PCL 并调整显卡设置成功")
|
||||
Else
|
||||
Throw New Exception("调整过程中出现异常")
|
||||
@@ -1944,7 +1986,7 @@ NextVersion:
|
||||
McLoginLoader.Input.Type = McLoginType.Legacy AndAlso '离线登录
|
||||
(Setup.Get("LaunchSkinType") = 2 OrElse '强制 Alex
|
||||
(Setup.Get("LaunchSkinType") = 4 AndAlso Setup.Get("LaunchSkinSlim"))) Then '或选用 Alex 皮肤
|
||||
Hint("此 Minecraft 版本尚不支持 Alex 皮肤,你的皮肤可能会显示为 Steve!", HintType.Critical)
|
||||
Hint("此 Minecraft 版本尚不支持 Alex 皮肤,你的皮肤可能会显示为 Steve!", HintType.Red)
|
||||
End If
|
||||
|
||||
'离线皮肤资源包
|
||||
@@ -2088,9 +2130,9 @@ IgnoreCustomSkin:
|
||||
|
||||
'获取自定义命令
|
||||
Dim CustomCommandGlobal As String = Setup.Get("LaunchAdvanceRun")
|
||||
If CustomCommandGlobal <> "" Then CustomCommandGlobal = ArgumentReplace(CustomCommandGlobal, True)
|
||||
If CustomCommandGlobal <> "" Then CustomCommandGlobal = ArgumentReplace(CustomCommandGlobal)
|
||||
Dim CustomCommandVersion As String = Setup.Get("VersionAdvanceRun", Version:=McVersionCurrent)
|
||||
If CustomCommandVersion <> "" Then CustomCommandVersion = ArgumentReplace(CustomCommandVersion, True)
|
||||
If CustomCommandVersion <> "" Then CustomCommandVersion = ArgumentReplace(CustomCommandVersion)
|
||||
|
||||
'输出 bat
|
||||
Try
|
||||
@@ -2122,14 +2164,15 @@ IgnoreCustomSkin:
|
||||
'执行自定义命令
|
||||
If CustomCommandGlobal <> "" Then
|
||||
McLaunchLog("正在执行全局自定义命令:" & CustomCommandGlobal)
|
||||
Dim CustomProcess As New Process
|
||||
Dim CustomProcess As Process = Nothing
|
||||
Try
|
||||
CustomProcess.StartInfo.FileName = "cmd.exe"
|
||||
CustomProcess.StartInfo.Arguments = "/c """ & CustomCommandGlobal & """"
|
||||
CustomProcess.StartInfo.WorkingDirectory = ShortenPath(PathMcFolder)
|
||||
CustomProcess.StartInfo.UseShellExecute = False
|
||||
CustomProcess.StartInfo.CreateNoWindow = True
|
||||
CustomProcess.Start()
|
||||
CustomProcess = StartProcess(New ProcessStartInfo With {
|
||||
.FileName = "cmd.exe",
|
||||
.Arguments = $"/c ""{CustomCommandGlobal}""",
|
||||
.WorkingDirectory = PathMcFolder,
|
||||
.UseShellExecute = False,
|
||||
.CreateNoWindow = True
|
||||
})
|
||||
If Setup.Get("LaunchAdvanceRunWait") Then
|
||||
Do Until CustomProcess.HasExited OrElse Loader.IsAborted
|
||||
Thread.Sleep(10)
|
||||
@@ -2138,7 +2181,7 @@ IgnoreCustomSkin:
|
||||
Catch ex As Exception
|
||||
Log(ex, "执行全局自定义命令失败", LogLevel.Hint)
|
||||
Finally
|
||||
If Not CustomProcess.HasExited AndAlso Loader.IsAborted Then
|
||||
If CustomProcess IsNot Nothing AndAlso Not CustomProcess.HasExited AndAlso Loader.IsAborted Then
|
||||
McLaunchLog("由于取消启动,已强制结束自定义命令 CMD 进程") '#1183
|
||||
CustomProcess.Kill()
|
||||
End If
|
||||
@@ -2146,14 +2189,15 @@ IgnoreCustomSkin:
|
||||
End If
|
||||
If CustomCommandVersion <> "" Then
|
||||
McLaunchLog("正在执行版本自定义命令:" & CustomCommandVersion)
|
||||
Dim CustomProcess As New Process
|
||||
Dim CustomProcess As Process = Nothing
|
||||
Try
|
||||
CustomProcess.StartInfo.FileName = "cmd.exe"
|
||||
CustomProcess.StartInfo.Arguments = "/c """ & CustomCommandVersion & """"
|
||||
CustomProcess.StartInfo.WorkingDirectory = ShortenPath(PathMcFolder)
|
||||
CustomProcess.StartInfo.UseShellExecute = False
|
||||
CustomProcess.StartInfo.CreateNoWindow = True
|
||||
CustomProcess.Start()
|
||||
CustomProcess = StartProcess(New ProcessStartInfo With {
|
||||
.FileName = "cmd.exe",
|
||||
.Arguments = $"/c ""{CustomCommandVersion}""",
|
||||
.WorkingDirectory = PathMcFolder,
|
||||
.UseShellExecute = False,
|
||||
.CreateNoWindow = True
|
||||
})
|
||||
If Setup.Get("VersionAdvanceRunWait", Version:=McVersionCurrent) Then
|
||||
Do Until CustomProcess.HasExited OrElse Loader.IsAborted
|
||||
Thread.Sleep(10)
|
||||
@@ -2162,7 +2206,7 @@ IgnoreCustomSkin:
|
||||
Catch ex As Exception
|
||||
Log(ex, "执行版本自定义命令失败", LogLevel.Hint)
|
||||
Finally
|
||||
If Not CustomProcess.HasExited AndAlso Loader.IsAborted Then
|
||||
If CustomProcess IsNot Nothing AndAlso Not CustomProcess.HasExited AndAlso Loader.IsAborted Then
|
||||
McLaunchLog("由于取消启动,已强制结束自定义命令 CMD 进程") '#1183
|
||||
CustomProcess.Kill()
|
||||
End If
|
||||
@@ -2173,8 +2217,7 @@ IgnoreCustomSkin:
|
||||
Private Sub McLaunchRun(Loader As LoaderTask(Of Integer, Process))
|
||||
|
||||
'启动信息
|
||||
Dim GameProcess = New Process()
|
||||
Dim StartInfo As New ProcessStartInfo(McLaunchJavaSelected.PathJavaw)
|
||||
Dim StartInfo As New ProcessStartInfo(McLaunchJavaSelected.PathJava) '使用 javaw.exe 会导致 #6263
|
||||
|
||||
'设置环境变量
|
||||
Dim Paths As New List(Of String)(StartInfo.EnvironmentVariables("Path").Split(";"))
|
||||
@@ -2183,17 +2226,16 @@ IgnoreCustomSkin:
|
||||
StartInfo.EnvironmentVariables("appdata") = ShortenPath(PathMcFolder)
|
||||
|
||||
'设置其他参数
|
||||
StartInfo.WorkingDirectory = ShortenPath(McVersionCurrent.PathIndie)
|
||||
StartInfo.WorkingDirectory = McVersionCurrent.PathIndie
|
||||
StartInfo.UseShellExecute = False
|
||||
StartInfo.RedirectStandardOutput = True
|
||||
StartInfo.RedirectStandardError = True
|
||||
StartInfo.CreateNoWindow = False
|
||||
StartInfo.CreateNoWindow = True
|
||||
StartInfo.Arguments = McLaunchArgument
|
||||
GameProcess.StartInfo = StartInfo
|
||||
|
||||
'开始进程
|
||||
GameProcess.Start()
|
||||
McLaunchLog("已启动游戏进程:" & McLaunchJavaSelected.PathJavaw)
|
||||
Dim GameProcess = StartProcess(StartInfo)
|
||||
McLaunchLog("已启动游戏进程:" & McLaunchJavaSelected.PathJava)
|
||||
If Loader.IsAborted Then
|
||||
McLaunchLog("由于取消启动,已强制结束游戏进程") '#1631
|
||||
GameProcess.Kill()
|
||||
@@ -2246,7 +2288,7 @@ IgnoreCustomSkin:
|
||||
'获取窗口标题
|
||||
Dim WindowTitle As String = Setup.Get("VersionArgumentTitle", Version:=McVersionCurrent)
|
||||
If WindowTitle = "" Then WindowTitle = Setup.Get("LaunchArgumentTitle")
|
||||
WindowTitle = ArgumentReplace(WindowTitle, False)
|
||||
WindowTitle = ArgumentReplace(WindowTitle, ReplaceTime:=False)
|
||||
|
||||
'初始化等待
|
||||
Dim Watcher As New Watcher(Loader, McVersionCurrent, WindowTitle)
|
||||
@@ -2295,47 +2337,6 @@ IgnoreCustomSkin:
|
||||
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 在启动结束时,对 PCL 约定的替换标记进行处理。
|
||||
''' </summary>
|
||||
Private Function ArgumentReplace(Raw As String, ReplaceTimeAndDate As Boolean) As String
|
||||
If Raw Is Nothing Then Return Nothing
|
||||
'路径替换
|
||||
Raw = Raw.Replace("{minecraft}", PathMcFolder)
|
||||
Raw = Raw.Replace("{verpath}", McVersionCurrent.Path)
|
||||
Raw = Raw.Replace("{verindie}", McVersionCurrent.PathIndie)
|
||||
Raw = Raw.Replace("{java}", McLaunchJavaSelected.PathFolder)
|
||||
'普通替换
|
||||
Raw = Raw.Replace("{user}", McLoginLoader.Output.Name)
|
||||
Raw = Raw.Replace("{uuid}", McLoginLoader.Output.Uuid)
|
||||
If ReplaceTimeAndDate Then '设置窗口标题时需要动态替换日期和时间
|
||||
Raw = Raw.Replace("{date}", Date.Now.ToString("yyyy/M/d"))
|
||||
Raw = Raw.Replace("{time}", Date.Now.ToString("HH:mm:ss"))
|
||||
End If
|
||||
Select Case McLoginLoader.Input.Type
|
||||
Case McLoginType.Legacy
|
||||
If PageLinkHiper.HiperState = LoadState.Finished Then
|
||||
Raw = Raw.Replace("{login}", "联机离线")
|
||||
Else
|
||||
Raw = Raw.Replace("{login}", "离线")
|
||||
End If
|
||||
Case McLoginType.Ms
|
||||
Raw = Raw.Replace("{login}", "正版")
|
||||
Case McLoginType.Nide
|
||||
Raw = Raw.Replace("{login}", "统一通行证")
|
||||
Case McLoginType.Auth
|
||||
Raw = Raw.Replace("{login}", "Authlib-Injector")
|
||||
End Select
|
||||
Raw = Raw.Replace("{name}", McVersionCurrent.Name)
|
||||
If {"unknown", "old", "pending"}.Contains(McVersionCurrent.Version.McName.ToLower) Then
|
||||
Raw = Raw.Replace("{version}", McVersionCurrent.Name)
|
||||
Else
|
||||
Raw = Raw.Replace("{version}", McVersionCurrent.Version.McName)
|
||||
End If
|
||||
Raw = Raw.Replace("{path}", Path)
|
||||
Return Raw
|
||||
End Function
|
||||
|
||||
#End Region
|
||||
|
||||
End Module
|
||||
@@ -68,7 +68,7 @@ Public Module ModMinecraft
|
||||
For Each Folder As String In Setup.Get("LaunchFolders").Split("|")
|
||||
If Folder = "" Then Continue For
|
||||
If Not Folder.Contains(">") OrElse Not Folder.EndsWithF("\") Then
|
||||
Hint("无效的 Minecraft 文件夹:" & Folder, HintType.Critical)
|
||||
Hint("无效的 Minecraft 文件夹:" & Folder, HintType.Red)
|
||||
Continue For
|
||||
End If
|
||||
Dim Name As String = Folder.Split(">")(0)
|
||||
@@ -86,7 +86,7 @@ Public Module ModMinecraft
|
||||
Next
|
||||
If Not Renamed Then CacheMcFolderList.Add(New McFolder With {.Name = Name, .Path = Path, .Type = McFolderType.Custom})
|
||||
Catch ex As Exception
|
||||
MyMsgBox("失效的 Minecraft 文件夹:" & vbCrLf & Path & vbCrLf & vbCrLf & GetExceptionSummary(ex), "Minecraft 文件夹失效", IsWarn:=True)
|
||||
MyMsgBox("失效的 Minecraft 文件夹:" & vbCrLf & Path & vbCrLf & vbCrLf & ex.GetBrief(), "Minecraft 文件夹失效", IsWarn:=True)
|
||||
Log(ex, $"无法访问 Minecraft 文件夹 {Path}")
|
||||
End Try
|
||||
Next
|
||||
@@ -169,8 +169,8 @@ Public Module ModMinecraft
|
||||
_McVersionCurrent = value '由于有可能是 Nothing,导致无法初始化,才得这样弄一圈
|
||||
_McVersionLast = value
|
||||
If value Is Nothing Then Return
|
||||
'重置缓存的 Mod 文件夹
|
||||
PageDownloadCompDetail.CachedFolder = Nothing
|
||||
'重置缓存的下载文件夹
|
||||
PageDownloadCompDetail.CachedFolder.Clear()
|
||||
'统一通行证重判
|
||||
If AniControlEnabled = 0 AndAlso
|
||||
Setup.Get("VersionServerNide", Version:=value) <> Setup.Get("CacheNideServer") AndAlso
|
||||
@@ -261,6 +261,12 @@ Public Module ModMinecraft
|
||||
''' 显示的描述文本。
|
||||
''' </summary>
|
||||
Public Info As String = "该版本未被加载,请向作者反馈此问题"
|
||||
Public ReadOnly Property HasCustomInfo As Boolean
|
||||
Get
|
||||
Return ReadIni(Path & "PCL\Setup.ini", "CustomInfo") <> ""
|
||||
End Get
|
||||
End Property
|
||||
|
||||
''' <summary>
|
||||
''' 该版本的列表检查原始结果,不受自定义影响。
|
||||
''' </summary>
|
||||
@@ -440,6 +446,33 @@ VersionSearchFinish:
|
||||
''' </summary>
|
||||
Public ReleaseTime As New Date(1970, 1, 1, 15, 0, 0)
|
||||
|
||||
''' <summary>
|
||||
''' 获取该版本的 JSON 路径。
|
||||
''' </summary>
|
||||
Public Function GetJsonPath() As String
|
||||
Dim JsonPath As String = Path & Name & ".json"
|
||||
If Not File.Exists(JsonPath) Then
|
||||
'尝试寻找 JSON 文件
|
||||
JsonPath = Nothing
|
||||
For Each JsonCandidatePath In Directory.GetFiles(Path, "*.json")
|
||||
Try
|
||||
Dim JsonCandidate As JObject = GetJson(ReadFile(JsonCandidatePath))
|
||||
If Not JsonCandidate.ContainsKey("mainClass") Then Continue For
|
||||
If Not JsonCandidate.ContainsKey("type") Then Continue For
|
||||
If Not JsonCandidate.ContainsKey("id") Then Continue For
|
||||
Catch
|
||||
End Try
|
||||
JsonPath = JsonCandidatePath
|
||||
Exit For
|
||||
Next
|
||||
If JsonPath Is Nothing Then
|
||||
Throw New Exception($"未找到版本 JSON 文件:{Path}{Name}.json")
|
||||
Else
|
||||
Log("[Minecraft] 未找到同名版本 JSON,自动换用 " & JsonPath, LogLevel.Debug)
|
||||
End If
|
||||
End If
|
||||
Return JsonPath
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 该版本的 Json 文本。
|
||||
''' </summary>
|
||||
@@ -452,17 +485,7 @@ VersionSearchFinish:
|
||||
Return TrimedJson.StartsWithF("{") AndAlso TrimedJson.EndsWithF("}")
|
||||
End Function
|
||||
If _JsonText Is Nothing Then
|
||||
Dim JsonPath As String = Path & Name & ".json"
|
||||
If Not File.Exists(JsonPath) Then
|
||||
'如果文件夹下只有一个 JSON 文件,则将其作为版本 JSON
|
||||
Dim JsonFiles As String() = Directory.GetFiles(Path, "*.json")
|
||||
If JsonFiles.Count = 1 Then
|
||||
JsonPath = JsonFiles(0)
|
||||
Log("[Minecraft] 未找到同名版本 JSON,自动换用 " & JsonPath, LogLevel.Debug)
|
||||
Else
|
||||
Throw New Exception($"未找到版本 JSON 文件:{Path}{Name}.json")
|
||||
End If
|
||||
End If
|
||||
Dim JsonPath As String = GetJsonPath()
|
||||
_JsonText = ReadFile(JsonPath)
|
||||
'如果 ReadFile 失败会返回空字符串;这可能是由于文件被临时占用,故延时后重试
|
||||
If Not FastJsonCheck(_JsonText) Then
|
||||
@@ -540,9 +563,15 @@ Recheck:
|
||||
'继续循环
|
||||
If Inherit.InheritVersion = InheritVersion Then Throw New Exception("版本依赖项出现嵌套:" & InheritVersion)
|
||||
InheritVersion = Inherit.InheritVersion
|
||||
'合并
|
||||
'合并 Libraries 项:子版本放在前面,父版本放在后面(5978#:如果多个 jar 包中含有相同的类,Java 8 和之前的版本按照 -cp 指定的顺序选择第一个)
|
||||
Dim CurrentLib As JArray = _JsonObject("libraries").DeepClone()
|
||||
For Each LibToken In Inherit.JsonObject("libraries")
|
||||
CurrentLib.Add(LibToken)
|
||||
Next
|
||||
'合并其他项:子版本优先于父版本
|
||||
Inherit.JsonObject.Merge(_JsonObject)
|
||||
_JsonObject = Inherit.JsonObject
|
||||
_JsonObject("libraries") = CurrentLib
|
||||
GoTo Recheck
|
||||
End If
|
||||
Catch ex As Exception
|
||||
@@ -580,20 +609,18 @@ Recheck:
|
||||
Get
|
||||
If Not JsonVersionInited Then
|
||||
JsonVersionInited = True
|
||||
If File.Exists(Path & Name & ".jar") Then
|
||||
Try
|
||||
Using JarArchive As New ZipArchive(New FileStream(Path & Name & ".jar", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
Dim VersionJson As ZipArchiveEntry = JarArchive.GetEntry("version.json")
|
||||
If VersionJson IsNot Nothing Then
|
||||
Using VersionJsonStream As New StreamReader(VersionJson.Open)
|
||||
_JsonVersion = GetJson(VersionJsonStream.ReadToEnd)
|
||||
End Using
|
||||
End If
|
||||
Try
|
||||
If Not File.Exists($"{Path}{Name}.jar") Then Exit Try
|
||||
Using JarArchive As New ZipArchive(New FileStream(Path & Name & ".jar", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
Dim VersionJson As ZipArchiveEntry = JarArchive.GetEntry("version.json")
|
||||
If VersionJson Is Nothing Then Exit Try
|
||||
Using VersionJsonStream As New StreamReader(VersionJson.Open)
|
||||
_JsonVersion = GetJson(VersionJsonStream.ReadToEnd)
|
||||
End Using
|
||||
Catch ex As Exception
|
||||
Log(ex, "从版本 jar 中读取 version.json 失败")
|
||||
End Try
|
||||
End If
|
||||
End Using
|
||||
Catch ex As Exception
|
||||
Log(ex, $"从版本 jar 中读取 version.json 失败({Path}{Name}.jar)")
|
||||
End Try
|
||||
End If
|
||||
Return _JsonVersion
|
||||
End Get
|
||||
@@ -674,7 +701,7 @@ Recheck:
|
||||
Catch ex As Exception
|
||||
Log(ex, "依赖版本检查出错(" & Name & ")")
|
||||
State = McVersionState.Error
|
||||
Info = "未知错误:" & GetExceptionSummary(ex)
|
||||
Info = "未知错误:" & ex.GetBrief()
|
||||
Return False
|
||||
End Try
|
||||
|
||||
@@ -763,7 +790,17 @@ ExitDataLoad:
|
||||
End If
|
||||
'确定版本描述
|
||||
Dim CustomInfo As String = ReadIni(Path & "PCL\Setup.ini", "CustomInfo")
|
||||
Info = If(CustomInfo <> "", CustomInfo, GetDefaultDescription())
|
||||
If CustomInfo <> "" Then
|
||||
Info = CustomInfo
|
||||
Else
|
||||
Info = GetVersionDescription()
|
||||
If State = McVersionState.Fool Then
|
||||
Info = GetMcFoolName(Version.McName)
|
||||
ElseIf State <> McVersionState.Error Then
|
||||
If Setup.Get("VersionServerLogin", Version:=Me) = 3 Then Info += ", 统一通行证验证"
|
||||
If Setup.Get("VersionServerLogin", Version:=Me) = 4 Then Info += ", Authlib 验证"
|
||||
End If
|
||||
End If
|
||||
'确定版本收藏状态
|
||||
IsStar = ReadIni(Path & "PCL\Setup.ini", "IsStar", False)
|
||||
'确定版本显示种类
|
||||
@@ -787,7 +824,7 @@ ExitDataLoad:
|
||||
WriteIni(Path & "PCL\Setup.ini", "VersionOriginalSub", Version.McCodeSub)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Info = "未知错误:" & GetExceptionSummary(ex)
|
||||
Info = "未知错误:" & ex.GetBrief()
|
||||
Logo = PathImage & "Blocks/RedstoneBlock.png"
|
||||
State = McVersionState.Error
|
||||
Log(ex, "加载版本失败(" & Name & ")", LogLevel.Feedback)
|
||||
@@ -796,11 +833,13 @@ ExitDataLoad:
|
||||
End Try
|
||||
Return Me
|
||||
End Function
|
||||
Public IsLoaded As Boolean = False
|
||||
''' <summary>
|
||||
''' 获取版本的默认描述。
|
||||
''' 获取对该版本的简短描述。
|
||||
''' 例如 “快照 16w01a”、“原版 1.12.2”、“愚人节版本 2.0”。
|
||||
''' </summary>
|
||||
Public Function GetDefaultDescription() As String
|
||||
Dim Info As String = ""
|
||||
Public Function GetVersionDescription() As String
|
||||
Dim Info As String
|
||||
Select Case State
|
||||
Case McVersionState.Snapshot
|
||||
If Version.McName.ContainsF("pre", True) Then
|
||||
@@ -817,21 +856,38 @@ ExitDataLoad:
|
||||
Case McVersionState.Original, McVersionState.Forge, McVersionState.NeoForge, McVersionState.Fabric, McVersionState.OptiFine, McVersionState.LiteLoader
|
||||
Info = Version.ToString
|
||||
Case McVersionState.Fool
|
||||
Info = GetMcFoolName(Version.McName)
|
||||
Info = "愚人节版本 " & Version.McName
|
||||
Case McVersionState.Error
|
||||
Return Me.Info '已有错误信息
|
||||
Case Else
|
||||
Info = "发生了未知错误,请向作者反馈此问题"
|
||||
End Select
|
||||
If Not State = McVersionState.Error Then
|
||||
If Setup.Get("VersionServerLogin", Version:=Me) = 3 Then Info += ", 统一通行证验证"
|
||||
If Setup.Get("VersionServerLogin", Version:=Me) = 4 Then Info += ", Authlib 验证"
|
||||
End If
|
||||
Return Info
|
||||
End Function
|
||||
|
||||
Public IsLoaded As Boolean = False
|
||||
Public Function ToListItem() As MyListItem
|
||||
Dim NewItem As New MyListItem With {.Info = Info, .Height = 42, .Tag = Me, .SnapsToDevicePixels = True, .Type = MyListItem.CheckType.Clickable}
|
||||
'标题
|
||||
NewItem.Inlines.Clear()
|
||||
NewItem.Inlines.Add(New Run(Name))
|
||||
If HasCustomInfo Then '如果版本设置了自定义描述,在标题后面以淡灰色显示其版本号
|
||||
NewItem.Inlines.Add(New Run(" | " & GetVersionDescription()) With {.Foreground = New MyColor(215, 215, 215), .FontSize = 12})
|
||||
End If
|
||||
'Logo
|
||||
Try
|
||||
If Logo.EndsWith("PCL\Logo.png") Then
|
||||
NewItem.Logo = Path & "PCL\Logo.png" '修复老版本中,存储的自定义 Logo 使用完整路径,导致移动后无法加载的 Bug
|
||||
Else
|
||||
NewItem.Logo = Logo
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "加载版本图标失败", LogLevel.Hint)
|
||||
NewItem.Logo = "pack://application:,,,/images/Blocks/RedstoneBlock.png"
|
||||
End Try
|
||||
Return NewItem
|
||||
End Function
|
||||
|
||||
'运算符支持
|
||||
Public Overrides Function Equals(obj As Object) As Boolean
|
||||
Dim version = TryCast(obj, McVersion)
|
||||
Return version IsNot Nothing AndAlso Path = version.Path
|
||||
@@ -844,7 +900,6 @@ ExitDataLoad:
|
||||
Public Shared Operator <>(a As McVersion, b As McVersion) As Boolean
|
||||
Return Not (a = b)
|
||||
End Operator
|
||||
|
||||
End Class
|
||||
Public Enum McVersionState
|
||||
[Error]
|
||||
@@ -968,7 +1023,7 @@ ExitDataLoad:
|
||||
If ToString = "" Then
|
||||
Return "原版 " & McName
|
||||
Else
|
||||
Return McName & ToString & If(ModeDebug, " (" & SortCode & "#)", "")
|
||||
Return McName & ToString
|
||||
End If
|
||||
End Function
|
||||
|
||||
@@ -1165,6 +1220,7 @@ OnLoaded:
|
||||
If Setup.Get("SystemDebugDelay") Then Thread.Sleep(RandomInteger(200, 3000))
|
||||
Catch ex As ThreadInterruptedException
|
||||
Catch ex As Exception
|
||||
If Loader.IsAborted Then Return '#5617
|
||||
WriteIni(Path & "PCL.ini", "VersionCache", "") '要求下次重新加载
|
||||
Log(ex, "加载 .minecraft 版本列表失败", LogLevel.Feedback)
|
||||
End Try
|
||||
@@ -1538,19 +1594,19 @@ OnLoaded:
|
||||
''' 要求玩家选择一个皮肤文件,并进行相关校验。
|
||||
''' </summary>
|
||||
Public Function McSkinSelect() As McSkinInfo
|
||||
Dim FileName As String = SelectFile("皮肤文件(*.png;*.jpg;*.webp)|*.png;*.jpg;*.webp", "选择皮肤文件")
|
||||
Dim FileName As String = SelectFile("皮肤文件(*.png;*.jpg;*.jpeg;*.webp)|*.png;*.jpg;*.jpeg;*.webp", "选择皮肤文件")
|
||||
|
||||
'验证有效性
|
||||
If FileName = "" Then Return New McSkinInfo With {.IsVaild = False}
|
||||
Try
|
||||
Dim Image As New MyBitmap(FileName)
|
||||
If Image.Pic.Width <> 64 OrElse Not (Image.Pic.Height = 32 OrElse Image.Pic.Height = 64) Then
|
||||
Hint("皮肤图片大小应为 64x32 像素或 64x64 像素!", HintType.Critical)
|
||||
Hint("皮肤图片大小应为 64x32 像素或 64x64 像素!", HintType.Red)
|
||||
Return New McSkinInfo With {.IsVaild = False}
|
||||
End If
|
||||
Dim FileInfo As New FileInfo(FileName)
|
||||
If FileInfo.Length > 24 * 1024 Then
|
||||
Hint("皮肤文件大小需小于 24 KB,而所选文件大小为 " & Math.Round(FileInfo.Length / 1024, 2) & " KB", HintType.Critical)
|
||||
Hint("皮肤文件大小需小于 24 KB,而所选文件大小为 " & Math.Round(FileInfo.Length / 1024, 2) & " KB", HintType.Red)
|
||||
Return New McSkinInfo With {.IsVaild = False}
|
||||
End If
|
||||
Catch ex As Exception
|
||||
@@ -1571,11 +1627,11 @@ OnLoaded:
|
||||
''' <summary>
|
||||
''' 获取 Uuid 对应的皮肤文件地址,失败将抛出异常。
|
||||
''' </summary>
|
||||
Public Function McSkinGetAddress(Uuid As String, Type As String) As String
|
||||
If Uuid = "" Then Throw New Exception("Uuid 为空。")
|
||||
If Uuid.StartsWithF("00000") Then Throw New Exception("离线 Uuid 无正版皮肤文件。")
|
||||
Public Function McSkinGetAddress(UUID As String, Type As String) As String
|
||||
If UUID = "" Then Throw New Exception("UUID 为空。")
|
||||
If UUID.StartsWithF("00000") AndAlso Type <> "Auth" Then Throw New Exception("离线 UUID 无正版皮肤文件:" & UUID)
|
||||
'尝试读取缓存
|
||||
Dim CacheSkinAddress As String = ReadIni(PathTemp & "Cache\Skin\Index" & Type & ".ini", Uuid)
|
||||
Dim CacheSkinAddress As String = ReadIni(PathTemp & "Cache\Skin\Index" & Type & ".ini", UUID)
|
||||
If Not CacheSkinAddress = "" Then Return CacheSkinAddress
|
||||
'获取皮肤地址
|
||||
Dim Url As String
|
||||
@@ -1589,7 +1645,7 @@ OnLoaded:
|
||||
Case Else
|
||||
Throw New ArgumentException("皮肤地址种类无效:" & If(Type, "null"))
|
||||
End Select
|
||||
Dim SkinString = NetGetCodeByRequestRetry(Url & Uuid)
|
||||
Dim SkinString = NetRequestByClientRetry(Url & UUID, RequireJson:=True)
|
||||
If SkinString = "" Then Throw New Exception("皮肤返回值为空,可能是未设置自定义皮肤的用户")
|
||||
'处理皮肤地址
|
||||
Dim SkinValue As String
|
||||
@@ -1610,11 +1666,12 @@ OnLoaded:
|
||||
If SkinJson("textures") Is Nothing OrElse SkinJson("textures")("skin") Is Nothing OrElse SkinJson("textures")("skin")("url") Is Nothing Then
|
||||
Throw New Exception("用户未设置自定义皮肤")
|
||||
Else
|
||||
SkinValue = SkinJson("textures")("skin")("url").ToString.Replace("http:", "https:")
|
||||
Dim SkinUrl As String = SkinJson("textures")("skin")("url").ToString
|
||||
SkinValue = If(SkinUrl.Contains("minecraft.net/"), SkinUrl.Replace("http://", "https://"), SkinUrl)
|
||||
End If
|
||||
'保存缓存
|
||||
WriteIni(PathTemp & "Cache\Skin\Index" & Type & ".ini", Uuid, SkinValue)
|
||||
Log("[Skin] UUID " & Uuid & " 对应的皮肤文件为 " & SkinValue)
|
||||
WriteIni(PathTemp & "Cache\Skin\Index" & Type & ".ini", UUID, SkinValue)
|
||||
Log("[Skin] UUID " & UUID & " 对应的皮肤文件为 " & SkinValue)
|
||||
Return SkinValue
|
||||
End Function
|
||||
|
||||
@@ -1627,9 +1684,9 @@ OnLoaded:
|
||||
Dim FileAddress As String = PathTemp & "Cache\Skin\" & GetHash(Address) & ".png"
|
||||
SyncLock McSkinDownloadLock
|
||||
If Not File.Exists(FileAddress) Then
|
||||
NetDownloadByClient(Address, FileAddress & NetDownloadEnd)
|
||||
NetDownloadByClient(Address, FileAddress & ".PCLDownloading")
|
||||
File.Delete(FileAddress)
|
||||
FileSystem.Rename(FileAddress & NetDownloadEnd, FileAddress)
|
||||
FileSystem.Rename(FileAddress & ".PCLDownloading", FileAddress)
|
||||
Log("[Minecraft] 皮肤下载成功:" & FileAddress)
|
||||
End If
|
||||
Return FileAddress
|
||||
@@ -1772,61 +1829,60 @@ OnLoaded:
|
||||
|
||||
'获取当前支持库列表
|
||||
Log("[Minecraft] 获取支持库列表:" & Version.Name)
|
||||
McLibListGet = McLibListGetWithJson(Version.JsonObject)
|
||||
If Not IncludeVersionJar Then Return McLibListGet
|
||||
Dim Result = McLibListGetWithJson(Version.JsonObject)
|
||||
|
||||
'需要添加原版 Jar
|
||||
Dim RealVersion As McVersion
|
||||
Dim RequiredJar As String = Version.JsonObject("jar")?.ToString
|
||||
If Version.IsHmclFormatJson OrElse RequiredJar Is Nothing Then
|
||||
'HMCL 项直接使用自身的 Jar
|
||||
'根据 Inherit 获取最深层版本
|
||||
Dim OriginalVersion As McVersion = Version
|
||||
'1.17+ 的 Forge 不寻找 Inherit
|
||||
If Not ((Version.Version.HasForge OrElse Version.Version.HasNeoForge) AndAlso Version.Version.McCodeMain >= 17) Then
|
||||
Do Until OriginalVersion.InheritVersion = ""
|
||||
If OriginalVersion.InheritVersion = OriginalVersion.Name Then Exit Do
|
||||
OriginalVersion = New McVersion(PathMcFolder & "versions\" & OriginalVersion.InheritVersion & "\")
|
||||
Loop
|
||||
If IncludeVersionJar Then
|
||||
Dim RealVersion As McVersion
|
||||
Dim RequiredJar As String = Version.JsonObject("jar")?.ToString
|
||||
If Version.IsHmclFormatJson OrElse RequiredJar Is Nothing Then
|
||||
'HMCL 项直接使用自身的 Jar
|
||||
'根据 Inherit 获取最深层版本
|
||||
Dim OriginalVersion As McVersion = Version
|
||||
'1.17+ 的 Forge 不寻找 Inherit
|
||||
If Not ((Version.Version.HasForge OrElse Version.Version.HasNeoForge) AndAlso Version.Version.McCodeMain >= 17) Then
|
||||
Do Until OriginalVersion.InheritVersion = ""
|
||||
If OriginalVersion.InheritVersion = OriginalVersion.Name Then Exit Do
|
||||
OriginalVersion = New McVersion(PathMcFolder & "versions\" & OriginalVersion.InheritVersion & "\")
|
||||
Loop
|
||||
End If
|
||||
'需要新建对象,否则后面的 Check 会导致 McVersionCurrent 的 State 变回 Original
|
||||
'复现:启动一个 Snapshot 版本
|
||||
RealVersion = New McVersion(OriginalVersion.Path)
|
||||
Else
|
||||
'Json 已提供 Jar 字段,使用该字段的信息
|
||||
RealVersion = New McVersion(RequiredJar)
|
||||
End If
|
||||
'需要新建对象,否则后面的 Check 会导致 McVersionCurrent 的 State 变回 Original
|
||||
'复现:启动一个 Snapshot 版本
|
||||
RealVersion = New McVersion(OriginalVersion.Path)
|
||||
Else
|
||||
'Json 已提供 Jar 字段,使用该字段的信息
|
||||
RealVersion = New McVersion(RequiredJar)
|
||||
Dim ClientUrl As String, ClientSHA1 As String
|
||||
'判断需求的版本是否存在
|
||||
'不能调用 RealVersion.Check(),可能会莫名其妙地触发 CheckPermission 正被另一进程使用,导致误判前置不存在
|
||||
If Not File.Exists(RealVersion.Path & RealVersion.Name & ".json") Then
|
||||
RealVersion = Version
|
||||
Log("[Minecraft] 可能缺少前置版本 " & RealVersion.Name & ",找不到对应的 json 文件", LogLevel.Debug)
|
||||
End If
|
||||
'获取详细下载信息
|
||||
If RealVersion.JsonObject("downloads") IsNot Nothing AndAlso RealVersion.JsonObject("downloads")("client") IsNot Nothing Then
|
||||
ClientUrl = RealVersion.JsonObject("downloads")("client")("url")
|
||||
ClientSHA1 = RealVersion.JsonObject("downloads")("client")("sha1")
|
||||
Else
|
||||
ClientUrl = Nothing
|
||||
ClientSHA1 = Nothing
|
||||
End If
|
||||
'把所需的原版 Jar 添加进去
|
||||
Result.Add(New McLibToken With {.LocalPath = RealVersion.Path & RealVersion.Name & ".jar", .Size = 0, .IsNatives = False, .Url = ClientUrl, .SHA1 = ClientSHA1})
|
||||
End If
|
||||
Dim ClientUrl As String, ClientSHA1 As String
|
||||
'判断需求的版本是否存在
|
||||
'不能调用 RealVersion.Check(),可能会莫名其妙地触发 CheckPermission 正被另一进程使用,导致误判前置不存在
|
||||
If Not File.Exists(RealVersion.Path & RealVersion.Name & ".json") Then
|
||||
RealVersion = Version
|
||||
Log("[Minecraft] 可能缺少前置版本 " & RealVersion.Name & ",找不到对应的 json 文件", LogLevel.Debug)
|
||||
End If
|
||||
'获取详细下载信息
|
||||
If RealVersion.JsonObject("downloads") IsNot Nothing AndAlso RealVersion.JsonObject("downloads")("client") IsNot Nothing Then
|
||||
ClientUrl = RealVersion.JsonObject("downloads")("client")("url")
|
||||
ClientSHA1 = RealVersion.JsonObject("downloads")("client")("sha1")
|
||||
Else
|
||||
ClientUrl = Nothing
|
||||
ClientSHA1 = Nothing
|
||||
End If
|
||||
'把所需的原版 Jar 添加进去
|
||||
McLibListGet.Add(New McLibToken With {.LocalPath = RealVersion.Path & RealVersion.Name & ".jar", .Size = 0, .IsNatives = False, .Url = ClientUrl, .SHA1 = ClientSHA1})
|
||||
|
||||
Return Result
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 获取 Minecraft 某一版本忽视继承的支持库列表,即结果中没有继承项。
|
||||
''' </summary>
|
||||
Public Function McLibListGetWithJson(JsonObject As JObject, Optional KeepSameNameDifferentVersionResult As Boolean = False, Optional CustomMcFolder As String = Nothing) As List(Of McLibToken)
|
||||
CustomMcFolder = If(CustomMcFolder, PathMcFolder)
|
||||
Dim BasicArray As New List(Of McLibToken)
|
||||
|
||||
'添加基础 Json 项
|
||||
Dim AllLibs As JArray = JsonObject("libraries")
|
||||
|
||||
'转换为 LibToken
|
||||
For Each Library As JObject In AllLibs.Children
|
||||
Dim BasicArray As New List(Of McLibToken)
|
||||
For Each Library As JObject In CType(JsonObject("libraries"), JArray).Children
|
||||
|
||||
'清理 null 项(BakaXL 会把没有的项序列化为 null,但会被 Newtonsoft 转换为 JValue,导致 Is Nothing = false;这导致了 #409)
|
||||
For i = Library.Properties.Count - 1 To 0 Step -1
|
||||
@@ -1870,17 +1926,29 @@ OnLoaded:
|
||||
.OriginalName = Library("name"),
|
||||
.Url = If(RootUrl, Library("downloads")("classifiers")("natives-windows")("url")),
|
||||
.LocalPath = If(Library("downloads")("classifiers")("natives-windows")("path") Is Nothing,
|
||||
McLibGet(Library("name"), CustomMcFolder:=CustomMcFolder).Replace(".jar", "-" & Library("natives")("windows").ToString & ".jar").Replace("${arch}", If(Environment.Is64BitOperatingSystem, "64", "32")),
|
||||
CustomMcFolder & "libraries\" & Library("downloads")("classifiers")("natives-windows")("path").ToString.Replace("/", "\")),
|
||||
McLibGet(Library("name"), CustomMcFolder:=CustomMcFolder).
|
||||
Replace(".jar", "-" & Library("natives")("windows").ToString & ".jar").
|
||||
Replace("${arch}", If(Environment.Is64BitOperatingSystem, "64", "32")),
|
||||
$"{CustomMcFolder}libraries\{Library("downloads")("classifiers")("natives-windows")("path").ToString.Replace("/", "\")}"),
|
||||
.Size = Val(Library("downloads")("classifiers")("natives-windows")("size").ToString),
|
||||
.IsNatives = True,
|
||||
.SHA1 = Library("downloads")("classifiers")("natives-windows")("sha1").ToString})
|
||||
Else
|
||||
BasicArray.Add(New McLibToken With {.OriginalName = Library("name"), .Url = RootUrl, .LocalPath = McLibGet(Library("name"), CustomMcFolder:=CustomMcFolder).Replace(".jar", "-" & Library("natives")("windows").ToString & ".jar").Replace("${arch}", If(Environment.Is64BitOperatingSystem, "64", "32")), .Size = 0, .IsNatives = True, .SHA1 = Nothing})
|
||||
BasicArray.Add(
|
||||
New McLibToken With {.OriginalName = Library("name"), .Url = RootUrl, .Size = 0, .IsNatives = True, .SHA1 = Nothing,
|
||||
.LocalPath = McLibGet(Library("name"), CustomMcFolder:=CustomMcFolder).
|
||||
Replace(".jar", $"-{Library("natives")("windows")}.jar").
|
||||
Replace("${arch}", If(Environment.Is64BitOperatingSystem, "64", "32"))
|
||||
})
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "处理实际支持库列表失败(有 Natives," & If(Library("name"), "Nothing").ToString & ")")
|
||||
BasicArray.Add(New McLibToken With {.OriginalName = Library("name"), .Url = RootUrl, .LocalPath = McLibGet(Library("name"), CustomMcFolder:=CustomMcFolder).Replace(".jar", "-" & Library("natives")("windows").ToString & ".jar").Replace("${arch}", If(Environment.Is64BitOperatingSystem, "64", "32")), .Size = 0, .IsNatives = True, .SHA1 = Nothing})
|
||||
BasicArray.Add(
|
||||
New McLibToken With {.OriginalName = Library("name"), .Url = RootUrl, .Size = 0, .IsNatives = True, .SHA1 = Nothing,
|
||||
.LocalPath = McLibGet(Library("name"), CustomMcFolder:=CustomMcFolder).
|
||||
Replace(".jar", $"-{Library("natives")("windows")}.jar").
|
||||
Replace("${arch}", If(Environment.Is64BitOperatingSystem, "64", "32"))
|
||||
})
|
||||
End Try
|
||||
End If
|
||||
|
||||
@@ -1945,8 +2013,7 @@ OnLoaded:
|
||||
Try
|
||||
Log("[Minecraft] 开始获取统一通行证下载信息")
|
||||
'测试链接:https://auth.mc-user.com:233/00000000000000000000000000000000/
|
||||
DownloadInfo = GetJson(NetGetCodeByLoader({
|
||||
"https://auth.mc-user.com:233/" & Setup.Get("VersionServerNide", Version:=Version)}, IsJson:=True))
|
||||
DownloadInfo = GetJson(NetRequestByClientRetry("https://auth.mc-user.com:233/" & Setup.Get("VersionServerNide", Version:=Version), RequireJson:=True))
|
||||
Catch ex As Exception
|
||||
Log(ex, "获取统一通行证下载信息失败")
|
||||
End Try
|
||||
@@ -1963,17 +2030,20 @@ OnLoaded:
|
||||
|
||||
'Authlib-Injector 文件
|
||||
If Setup.Get("VersionServerLogin", Version:=Version) = 4 Then
|
||||
Dim TargetFile = PathPure & "\authlib-injector.jar"
|
||||
Dim TargetFile = PathPure & "authlib-injector.jar"
|
||||
Dim DownloadInfo As JObject = Nothing
|
||||
'获取下载信息
|
||||
Try
|
||||
Log("[Minecraft] 开始获取 Authlib-Injector 下载信息")
|
||||
DownloadInfo = GetJson(NetGetCodeByLoader({
|
||||
DownloadInfo = GetJson(NetRequestByClientRetry(
|
||||
"https://authlib-injector.yushi.moe/artifact/latest.json",
|
||||
"https://bmclapi2.bangbang93.com/mirrors/authlib-injector/artifact/latest.json"
|
||||
}, IsJson:=True))
|
||||
BackupUrl:="https://bmclapi2.bangbang93.com/mirrors/authlib-injector/artifact/latest.json", RequireJson:=True))
|
||||
Catch ex As Exception
|
||||
Log(ex, "获取 Authlib-Injector 下载信息失败")
|
||||
If File.Exists(TargetFile) Then
|
||||
Log(ex, "获取 Authlib-Injector 下载信息失败")
|
||||
Else
|
||||
Throw New Exception("获取 Authlib-Injector 下载信息失败", ex)
|
||||
End If
|
||||
End Try
|
||||
'校验文件
|
||||
If DownloadInfo IsNot Nothing Then
|
||||
@@ -2016,12 +2086,16 @@ OnLoaded:
|
||||
'获取
|
||||
For Each Token As McLibToken In Libs
|
||||
'检查文件
|
||||
Dim Checker As New FileChecker(ActualSize:=If(Token.Size = 0, -1, Token.Size), Hash:=Token.SHA1)
|
||||
Dim Checker As FileChecker
|
||||
If Token.Name.ContainsF("labymod") Then
|
||||
Checker = New FileChecker '不检查 LabyMod 的文件,它们提供的文件校验信息是错的(#3225)
|
||||
Else
|
||||
Checker = New FileChecker(ActualSize:=If(Token.Size = 0, -1, Token.Size), Hash:=Token.SHA1)
|
||||
End If
|
||||
If Checker.Check(Token.LocalPath) Is Nothing Then Continue For
|
||||
'文件不符合,添加下载
|
||||
Dim Urls As New List(Of String)
|
||||
If Token.Url Is Nothing AndAlso Token.Name = "net.minecraftforge:forge:universal" Then
|
||||
'特判修复 Forge 部分 universal 文件缺失 URL(#5455)
|
||||
If Token.Url Is Nothing AndAlso Token.Name = "net.minecraftforge:forge:universal" Then '特判修复 Forge 部分 universal 文件缺失 URL(#5455)
|
||||
Token.Url = "https://maven.minecraftforge.net" & Token.LocalPath.Replace(CustomMcFolder & "libraries", "").Replace("\", "/")
|
||||
End If
|
||||
If Token.Url IsNot Nothing Then
|
||||
@@ -2068,19 +2142,12 @@ OnLoaded:
|
||||
''' <param name="WithHead">是否包含 Lib 文件夹头部,若不包含,则会类似以 com\xxx\ 开头。</param>
|
||||
Public Function McLibGet(Original As String, Optional WithHead As Boolean = True, Optional IgnoreLiteLoader As Boolean = False, Optional CustomMcFolder As String = Nothing) As String
|
||||
CustomMcFolder = If(CustomMcFolder, PathMcFolder)
|
||||
'有时候原始的有四段,例如 net.minecraftforge:forge:1.21.4-54.0.34:client
|
||||
'文件夹名应该忽略最后一段(client),但文件名需要保留
|
||||
'参见 #7306(以及 #5376 也是因为这里没有处理好导致的)
|
||||
Dim Splited = Original.Split(":")
|
||||
McLibGet = If(WithHead, CustomMcFolder & "libraries\", "") &
|
||||
Splited(0).Replace(".", "\") & "\" & Splited(1) & "\" & Splited(2) & "\" & Splited(1) & "-" & Splited(2) & ".jar"
|
||||
'判断 OptiFine 是否应该使用 installer
|
||||
If McLibGet.Contains("optifine\OptiFine\1.") AndAlso Splited(2).Split(".").Count > 1 Then
|
||||
Dim MajorVersion As Integer = Val(Splited(2).Split(".")(1).BeforeFirst("_"))
|
||||
Dim MinorVersion As Integer = If(Splited(2).Split(".").Count > 2, Val(Splited(2).Split(".")(2).BeforeFirst("_")), 0)
|
||||
If (MajorVersion = 12 OrElse (MajorVersion = 20 AndAlso MinorVersion >= 4) OrElse MajorVersion >= 21) AndAlso '仅在 1.12 (无法追溯) 和 1.20.4+ (#5376) 遇到此问题
|
||||
File.Exists($"{CustomMcFolder}libraries\{Splited(0).Replace(".", "\")}\{Splited(1)}\{Splited(2)}\{Splited(1)}-{Splited(2)}-installer.jar") Then
|
||||
McLaunchLog("已将 " & Original & " 替换为对应的 Installer 文件")
|
||||
McLibGet = McLibGet.Replace(".jar", "-installer.jar")
|
||||
End If
|
||||
End If
|
||||
$"{Splited(0).Replace(".", "\")}\{Splited(1)}\{Splited(2)}\{Splited.Skip(1).Join("-")}.jar"
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
@@ -2193,16 +2260,16 @@ OnLoaded:
|
||||
Dim LocalPath As String
|
||||
If Json("map_to_resources") IsNot Nothing AndAlso Json("map_to_resources").ToObject(Of Boolean) Then
|
||||
'Remap
|
||||
LocalPath = Version.PathIndie & "resources\" & File.Name.Replace("/", "\")
|
||||
LocalPath = $"{Version.PathIndie}resources\{File.Name}"
|
||||
ElseIf Json("virtual") IsNot Nothing AndAlso Json("virtual").ToObject(Of Boolean) Then
|
||||
'Virtual
|
||||
LocalPath = PathMcFolder & "assets\virtual\legacy\" & File.Name.Replace("/", "\")
|
||||
LocalPath = $"{PathMcFolder}assets\virtual\legacy\{File.Name}"
|
||||
Else
|
||||
'正常
|
||||
LocalPath = PathMcFolder & "assets\objects\" & Left(File.Value("hash").ToString, 2) & "\" & File.Value("hash").ToString
|
||||
LocalPath = $"{PathMcFolder}assets\objects\{Left(File.Value("hash").ToString, 2)}\{File.Value("hash")}"
|
||||
End If
|
||||
Result.Add(New McAssetsToken With {
|
||||
.LocalPath = LocalPath,
|
||||
.LocalPath = LocalPath.Replace("/", "\"),
|
||||
.SourcePath = File.Name,
|
||||
.Hash = File.Value("hash").ToString,
|
||||
.Size = File.Value("size").ToString
|
||||
@@ -2392,7 +2459,12 @@ NextEntry:
|
||||
Public Function FilterUserName(Raw As String, FilterChar As Char) As String
|
||||
If Raw.Contains(":\Users\") Then
|
||||
For Each Token In RegexSearch(Raw, "(?<=:\\Users\\)[^\\]+")
|
||||
Raw = Raw.Replace(Token, New String(FilterChar, Token.Count))
|
||||
Raw = Raw.Replace("\Users\" & Token, "\Users\" & New String(FilterChar, Token.Count))
|
||||
Next
|
||||
End If
|
||||
If Raw.Contains(":/Users/") Then
|
||||
For Each Token In RegexSearch(Raw, "(?<=:/Users/)[^/]+")
|
||||
Raw = Raw.Replace("/Users/" & Token, "/Users/" & New String(FilterChar, Token.Count))
|
||||
Next
|
||||
End If
|
||||
Return Raw
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Imports System.IO.Compression
|
||||
|
||||
Public Module ModMod
|
||||
Private Const LocalModCacheVersion As Integer = 7
|
||||
Private Const LocalModCacheVersion As Integer = 8
|
||||
|
||||
Public Class McMod
|
||||
|
||||
@@ -73,7 +73,7 @@ Public Module ModMod
|
||||
Get
|
||||
If _Name Is Nothing Then Load()
|
||||
If _Name Is Nothing Then _Name = _ModId
|
||||
If _Name Is Nothing Then _Name = GetFileNameWithoutExtentionFromPath(Path)
|
||||
If _Name Is Nothing Then _Name = IO.Path.GetFileNameWithoutExtension(Path)
|
||||
Return _Name
|
||||
End Get
|
||||
Set(value As String)
|
||||
@@ -688,7 +688,7 @@ Finished:
|
||||
''' </summary>
|
||||
Public ReadOnly Property CanUpdate As Boolean
|
||||
Get
|
||||
Return Not Setup.Get("UiHiddenFunctionModUpdate") AndAlso ChangelogUrls.Any()
|
||||
Return Not Setup.Get("UiHiddenFunctionModUpdate") AndAlso Not Setup.Get("VersionAdvanceDisableModUpdate", Version:=PageVersionLeft.Version) AndAlso ChangelogUrls.Any()
|
||||
End Get
|
||||
End Property
|
||||
|
||||
@@ -946,8 +946,8 @@ Finished:
|
||||
Try
|
||||
'步骤 1:获取 Hash 与对应的工程 ID
|
||||
Dim ModrinthHashes = Mods.Select(Function(m) m.ModrinthHash).ToList()
|
||||
Dim ModrinthVersion = CType(GetJson(DlModRequest("https://api.modrinth.com/v2/version_files", "POST",
|
||||
$"{{""hashes"": [""{ModrinthHashes.Join(""",""")}""], ""algorithm"": ""sha1""}}", "application/json")), JObject)
|
||||
Dim ModrinthVersion As JObject = DlModRequest("https://api.modrinth.com/v2/version_files", HttpMethod.Post,
|
||||
$"{{""hashes"": [""{ModrinthHashes.Join(""",""")}""], ""algorithm"": ""sha1""}}", "application/json")
|
||||
Log($"[Mod] 从 Modrinth 获取到 {ModrinthVersion.Count} 个本地 Mod 的对应信息")
|
||||
'步骤 2:尝试读取工程信息缓存,构建其他 Mod 的对应关系
|
||||
If ModrinthVersion.Count = 0 Then Return
|
||||
@@ -967,9 +967,8 @@ Finished:
|
||||
Log($"[Mod] 需要从 Modrinth 获取 {ModrinthMapping.Count} 个本地 Mod 的工程信息")
|
||||
'步骤 3:获取工程信息
|
||||
If Not ModrinthMapping.Any() Then Return
|
||||
Dim ModrinthProject = CType(GetJson(DlModRequest(
|
||||
$"https://api.modrinth.com/v2/projects?ids=[""{ModrinthMapping.Keys.Join(""",""")}""]",
|
||||
"GET", "", "application/json")), JArray)
|
||||
Dim ModrinthProject As JArray = DlModRequest(
|
||||
$"https://api.modrinth.com/v2/projects?ids=[""{ModrinthMapping.Keys.Join(""",""")}""]")
|
||||
For Each ProjectJson In ModrinthProject
|
||||
Dim Project As New CompProject(ProjectJson)
|
||||
For Each Entry In ModrinthMapping(Project.Id)
|
||||
@@ -978,9 +977,9 @@ Finished:
|
||||
Next
|
||||
Log($"[Mod] 已从 Modrinth 获取本地 Mod 信息,继续获取更新信息")
|
||||
'步骤 4:获取更新信息
|
||||
Dim ModrinthUpdate = CType(GetJson(DlModRequest("https://api.modrinth.com/v2/version_files/update", "POST",
|
||||
Dim ModrinthUpdate As JObject = DlModRequest("https://api.modrinth.com/v2/version_files/update", HttpMethod.Post,
|
||||
$"{{""hashes"": [""{ModrinthMapping.SelectMany(Function(l) l.Value.Select(Function(m) m.ModrinthHash)).Join(""",""")}""], ""algorithm"": ""sha1"",
|
||||
""loaders"": [""{ModLoaders.Join(""",""").ToLower}""],""game_versions"": [""{McVersion}""]}}", "application/json")), JObject)
|
||||
""loaders"": [""{ModLoaders.Join(""",""").ToLower}""],""game_versions"": [""{McVersion}""]}}", "application/json")
|
||||
For Each Entry In Mods
|
||||
If Not ModrinthUpdate.ContainsKey(Entry.ModrinthHash) OrElse Entry.CompFile Is Nothing Then Continue For
|
||||
Dim UpdateFile As New CompFile(ModrinthUpdate(Entry.ModrinthHash), CompType.Mod)
|
||||
@@ -1015,8 +1014,8 @@ Finished:
|
||||
CurseForgeHashes.Add(Entry.CurseForgeHash)
|
||||
If Loader.IsAbortedWithThread(CurrentTaskThread) Then Return
|
||||
Next
|
||||
Dim CurseForgeRaw = CType(CType(GetJson(DlModRequest("https://api.curseforge.com/v1/fingerprints/432", "POST",
|
||||
$"{{""fingerprints"": [{CurseForgeHashes.Join(",")}]}}", "application/json")), JObject)("data")("exactMatches"), JContainer)
|
||||
Dim CurseForgeRaw As JContainer = DlModRequest("https://api.curseforge.com/v1/fingerprints/432", HttpMethod.Post,
|
||||
$"{{""fingerprints"": [{CurseForgeHashes.Join(",")}]}}", "application/json")("data")("exactMatches")
|
||||
Log($"[Mod] 从 CurseForge 获取到 {CurseForgeRaw.Count} 个本地 Mod 的对应信息")
|
||||
'步骤 2:尝试读取工程信息缓存,构建其他 Mod 的对应关系
|
||||
If Not CurseForgeRaw.Any() Then Return
|
||||
@@ -1038,8 +1037,8 @@ Finished:
|
||||
Log($"[Mod] 需要从 CurseForge 获取 {CurseForgeMapping.Count} 个本地 Mod 的工程信息")
|
||||
'步骤 3:获取工程信息
|
||||
If Not CurseForgeMapping.Any() Then Return
|
||||
Dim CurseForgeProject = CType(GetJson(DlModRequest("https://api.curseforge.com/v1/mods", "POST",
|
||||
$"{{""modIds"": [{CurseForgeMapping.Keys.Join(",")}]}}", "application/json")), JObject)("data")
|
||||
Dim CurseForgeProject = DlModRequest("https://api.curseforge.com/v1/mods", HttpMethod.Post,
|
||||
$"{{""modIds"": [{CurseForgeMapping.Keys.Join(",")}]}}", "application/json")("data")
|
||||
Dim UpdateFileIds As New Dictionary(Of Integer, List(Of McMod)) 'FileId -> 本地 Mod 文件列表
|
||||
Dim FileIdToProjectSlug As New Dictionary(Of Integer, String)
|
||||
For Each ProjectJson In CurseForgeProject
|
||||
@@ -1079,8 +1078,8 @@ Finished:
|
||||
Log($"[Mod] 已从 CurseForge 获取本地 Mod 信息,需要获取 {UpdateFileIds.Count} 个用于检查更新的文件信息")
|
||||
'步骤 4:获取更新文件信息
|
||||
If Not UpdateFileIds.Any() Then Return
|
||||
Dim CurseForgeFiles = CType(GetJson(DlModRequest("https://api.curseforge.com/v1/mods/files", "POST",
|
||||
$"{{""fileIds"": [{UpdateFileIds.Keys.Join(",")}]}}", "application/json")), JObject)("data")
|
||||
Dim CurseForgeFiles = DlModRequest("https://api.curseforge.com/v1/mods/files", HttpMethod.Post,
|
||||
$"{{""fileIds"": [{UpdateFileIds.Keys.Join(",")}]}}", "application/json")("data")
|
||||
Dim UpdateFiles As New Dictionary(Of McMod, CompFile)
|
||||
For Each FileJson In CurseForgeFiles
|
||||
Dim File As New CompFile(FileJson, CompType.Mod)
|
||||
@@ -1114,8 +1113,8 @@ Finished:
|
||||
End Sub, "Mod List Detail Loader CurseForge")
|
||||
'等待线程结束
|
||||
Do Until EndedThreadCount = 2
|
||||
If Loader.IsAborted Then Return
|
||||
Thread.Sleep(10)
|
||||
If Loader.IsAborted Then Return
|
||||
Loop
|
||||
'保存缓存
|
||||
Mods = Mods.Where(Function(m) m.Comp IsNot Nothing).ToList()
|
||||
@@ -1127,6 +1126,7 @@ Finished:
|
||||
Next
|
||||
WriteFile(PathTemp & "Cache\LocalMod.json", Cache.ToString(If(ModeDebug, Newtonsoft.Json.Formatting.Indented, Newtonsoft.Json.Formatting.None)))
|
||||
'刷新边栏
|
||||
If Loader.IsAborted Then Return
|
||||
If FrmVersionMod?.Filter = PageVersionMod.FilterType.CanUpdate Then
|
||||
RunInUi(Sub() FrmVersionMod?.RefreshUI()) '同步 “可更新” 列表 (#4677)
|
||||
Else
|
||||
@@ -1213,7 +1213,7 @@ Finished:
|
||||
If ReqVersionHead = ReqVersionTail Then
|
||||
VersionRequire = "应为 " & ReqVersionHead
|
||||
ElseIf ReqVersionHead.Contains(".") AndAlso ReqVersionTail.Contains(".") Then
|
||||
VersionRequire = "应为 " & ReqVersionHead & " 至 " & ReqVersionTail
|
||||
VersionRequire = $"应为 {ReqVersionHead} 至 {ReqVersionTail}"
|
||||
ElseIf ReqVersionHead.Contains(".") Then
|
||||
If ReqVersionHeadCanEqual Then
|
||||
VersionRequire = "最低应为 " & ReqVersionHead
|
||||
@@ -1231,7 +1231,7 @@ Finished:
|
||||
End If
|
||||
'检查前置 Mod 是否存在,并获取其版本
|
||||
If Not CurrentDependencies.ContainsKey(ReqId) Then
|
||||
Result.Add("缺少前置 Mod:" & ReqId & If(VersionRequire = "", "", ",其版本" & VersionRequire) & "。" & vbCrLf & " - " & ModEntity.FileName)
|
||||
Result.Add($"缺少前置 Mod:{ReqId}{If(VersionRequire = "", "", ",其版本" & VersionRequire)}。{vbCrLf} - {ModEntity.FileName}")
|
||||
Continue For
|
||||
End If
|
||||
Dim CurrentVersion As String = If(CurrentDependencies(ReqId)(0), "0.0")
|
||||
@@ -1240,7 +1240,7 @@ Finished:
|
||||
If ReqVersionHead.Contains(".") Then
|
||||
If VersionSortInteger(ReqVersionHead, CurrentVersion) > If(ReqVersionHeadCanEqual, 0, -1) Then
|
||||
Result.Add(ReqId.Substring(0, 1).ToUpper & ReqId.Substring(1) & " 版本过低,其版本" & VersionRequire & ",而当前版本为 " & CurrentVersion & "。" & vbCrLf &
|
||||
" - " & ModEntity.FileName & If(ReqId <> "minecraft" AndAlso ReqId <> "forge", vbCrLf & " - 前置:" & CurrentDependencies(ReqId)(1), ""))
|
||||
" - " & ModEntity.FileName & If(ReqId <> "minecraft" AndAlso ReqId <> "forge", $"{vbCrLf} - 前置:{CurrentDependencies(ReqId)(1)}", ""))
|
||||
Continue For
|
||||
End If
|
||||
End If
|
||||
@@ -1248,26 +1248,26 @@ Finished:
|
||||
If ReqVersionTail.Contains(".") Then
|
||||
If VersionSortInteger(CurrentVersion, ReqVersionTail) > If(ReqVersionTailCanEqual, 0, -1) Then
|
||||
Result.Add(ReqId.Substring(0, 1).ToUpper & ReqId.Substring(1) & " 版本过高,其版本" & VersionRequire & ",而当前版本为 " & CurrentVersion & "。" & vbCrLf &
|
||||
" - " & ModEntity.FileName & If(ReqId <> "minecraft" AndAlso ReqId <> "forge", vbCrLf & " - 前置:" & CurrentDependencies(ReqId)(1), ""))
|
||||
" - " & ModEntity.FileName & If(ReqId <> "minecraft" AndAlso ReqId <> "forge", $"{vbCrLf} - 前置:{CurrentDependencies(ReqId)(1)}", ""))
|
||||
Continue For
|
||||
End If
|
||||
End If
|
||||
Else
|
||||
If Not CurrentDependencies.ContainsKey(Dependency.Key) Then
|
||||
Result.Add("缺少前置 Mod:" & Dependency.Key & "。" & vbCrLf & " - " & ModEntity.FileName)
|
||||
Result.Add($"缺少前置 Mod:{Dependency.Key}。{vbCrLf} - {ModEntity.FileName}")
|
||||
Continue For
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
Catch ex As Exception
|
||||
Result.Add("检查 Mod 时出错:" & GetExceptionSummary(ex) & vbCrLf & " - " & ModEntity.FileName)
|
||||
Result.Add($"检查 Mod 时出错:{ex.GetBrief()}{vbCrLf} - {ModEntity.FileName}")
|
||||
Log(ex, "检查 Mod 时出错")
|
||||
End Try
|
||||
Next
|
||||
If Not Result.Any() Then
|
||||
Log("[Minecraft] Mod 检查未发现异常")
|
||||
Else
|
||||
Log("[Minecraft] Mod 检查异常结果:" & vbCrLf & Join(Result, vbCrLf))
|
||||
Log($"[Minecraft] Mod 检查异常结果:{vbCrLf}{Join(Result, vbCrLf)}")
|
||||
End If
|
||||
Return Result
|
||||
End Function
|
||||
|
||||
@@ -31,7 +31,7 @@ Public Module ModModpack
|
||||
Try
|
||||
'字符校验
|
||||
Dim TargetFolder As String = $"{PathMcFolder}versions\{VersionName}\"
|
||||
If TargetFolder.Contains("!") OrElse TargetFolder.Contains(";") Then Hint("游戏路径中不能含有感叹号或分号:" & TargetFolder, HintType.Critical) : Throw New CancelledException
|
||||
If TargetFolder.Contains("!") OrElse TargetFolder.Contains(";") Then Hint("游戏路径中不能含有感叹号或分号:" & TargetFolder, HintType.Red) : Throw New CancelledException
|
||||
'获取整合包种类与关键 Json
|
||||
Dim PackType As Integer = -1
|
||||
Try
|
||||
@@ -72,7 +72,7 @@ Public Module ModModpack
|
||||
If FullNames(1) = "modpack.zip" OrElse FullNames(1) = "modpack.mrpack" Then PackType = 9 : Exit Try '带启动器的压缩包
|
||||
Next
|
||||
Catch ex As Exception
|
||||
If GetExceptionDetail(ex, True).Contains("Error.WinIOError") Then
|
||||
If ex.GetDetail(True).Contains("Error.WinIOError") Then
|
||||
Throw New Exception("打开整合包文件失败", ex)
|
||||
ElseIf File.EndsWithF(".rar", True) Then
|
||||
Throw New Exception("PCL 无法处理 rar 格式的压缩包,请在解压后重新压缩为 zip 格式再试", ex)
|
||||
@@ -158,6 +158,7 @@ Retry:
|
||||
If File.Exists(OverridesIni) Then
|
||||
WriteIni(OverridesIni, "VersionArgumentIndie", 1) '开启版本隔离
|
||||
WriteIni(OverridesIni, "VersionArgumentIndieV2", True)
|
||||
WriteIni(OverridesIni, "IsStar", False)
|
||||
CopyFile(OverridesIni, VersionIni) '覆写已有的 ini
|
||||
Else
|
||||
WriteIni(VersionIni, "VersionArgumentIndie", 1) '开启版本隔离
|
||||
@@ -165,6 +166,15 @@ Retry:
|
||||
End If
|
||||
IniClearCache(VersionIni) '重置缓存,避免被安装过程中写入的 ini 覆盖
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 弹窗提示整合包中存在不兼容的加载器。
|
||||
''' 如果取消,则抛出 CancelledException。
|
||||
''' </summary>
|
||||
''' <exception cref="CancelledException" />
|
||||
Private Sub NotifyIncompatibleLoader(LoaderName As String)
|
||||
If MyMsgBox($"整合包中存在不兼容的加载器:{LoaderName}{vbCrLf}如果你知道如何手动安装它,可以先选择跳过,然后在整合包下载结束后手动安装。",
|
||||
"不兼容的加载器", "取消", $"不安装 {LoaderName} 并继续") = 1 Then Throw New CancelledException
|
||||
End Sub
|
||||
|
||||
#Region "不同类型整合包的安装方法"
|
||||
|
||||
@@ -210,7 +220,8 @@ Retry:
|
||||
Log("[ModPack] 整合包 Fabric 版本:" & Id)
|
||||
FabricVersion = Id.Replace("fabric-", "")
|
||||
Else
|
||||
Log("[ModPack] 未知 Mod 加载器:" & Id)
|
||||
'ElseIf Id.StartsWithF("quilt-") Then
|
||||
NotifyIncompatibleLoader(Id)
|
||||
End If
|
||||
Next
|
||||
'解压
|
||||
@@ -244,7 +255,7 @@ Retry:
|
||||
'获取 Mod 下载信息
|
||||
ModDownloadLoaders.Add(New LoaderTask(Of Integer, JArray)("获取 Mod 下载信息",
|
||||
Sub(Task As LoaderTask(Of Integer, JArray))
|
||||
Task.Output = GetJson(DlModRequest("https://api.curseforge.com/v1/mods/files", "POST", "{""fileIds"": [" & Join(ModList, ",") & "]}", "application/json"))("data")
|
||||
Task.Output = DlModRequest("https://api.curseforge.com/v1/mods/files", HttpMethod.Post, "{""fileIds"": [" & Join(ModList, ",") & "]}", "application/json")("data")
|
||||
'如果文件已被删除,则 API 会跳过那一项
|
||||
If ModList.Count > Task.Output.Count Then Throw New Exception("整合包中的部分 Mod 版本已被 Mod 作者删除,所以没法继续安装了,请向整合包作者反馈该问题")
|
||||
End Sub) With {.ProgressWeight = ModList.Count / 10}) '每 10 Mod 需要 1s
|
||||
@@ -258,8 +269,7 @@ Retry:
|
||||
If FileList.ContainsKey(Id) Then Continue For
|
||||
'可选 Mod 提示
|
||||
If ModOptionalList.Contains(Id) Then
|
||||
If MyMsgBox("是否要下载整合包中的可选文件 " & ModJson("displayName").ToString & "?",
|
||||
"下载可选文件", "是", "否") = 2 Then
|
||||
If MyMsgBox("是否要下载整合包中的可选文件 " & ModJson("displayName").ToString & "?", "下载可选文件", "是", "否") = 2 Then
|
||||
Continue For
|
||||
End If
|
||||
End If
|
||||
@@ -325,7 +335,7 @@ Retry:
|
||||
File.Delete(Target)
|
||||
End If
|
||||
Next
|
||||
If File.Exists(FileAddress) AndAlso GetFileNameWithoutExtentionFromPath(FileAddress) = "modpack" Then
|
||||
If File.Exists(FileAddress) AndAlso IO.Path.GetFileNameWithoutExtension(FileAddress) = "modpack" Then
|
||||
Log("[ModPack] 删除安装整合包文件:" & FileAddress)
|
||||
File.Delete(FileAddress)
|
||||
End If
|
||||
@@ -334,7 +344,7 @@ Retry:
|
||||
'重复任务检查
|
||||
Dim LoaderName As String = "CurseForge 整合包安装:" & VersionName & " "
|
||||
If LoaderTaskbar.Any(Function(l) l.Name = LoaderName) Then
|
||||
Hint("该整合包正在安装中!", HintType.Critical)
|
||||
Hint("该整合包正在安装中!", HintType.Red)
|
||||
Throw New CancelledException
|
||||
End If
|
||||
|
||||
@@ -376,12 +386,9 @@ Retry:
|
||||
Case "fabric-loader" 'eg. 0.14.14
|
||||
FabricVersion = Entry.Value.ToString
|
||||
Log("[ModPack] 整合包 Fabric 版本:" & FabricVersion)
|
||||
Case "quilt-loader" 'eg. 1.0.0
|
||||
Hint("PCL 暂不支持安装需要 Quilt 的整合包!", HintType.Critical)
|
||||
Throw New CancelledException
|
||||
Case Else
|
||||
Hint($"无法安装整合包,其中出现了未知的 Mod 加载器 {Entry.Value}!", HintType.Critical)
|
||||
Throw New CancelledException
|
||||
'Case "quilt-loader" 'eg. 1.0.0
|
||||
NotifyIncompatibleLoader(Entry.Name)
|
||||
End Select
|
||||
Next
|
||||
'获取版本名
|
||||
@@ -423,7 +430,7 @@ Retry:
|
||||
End Select
|
||||
End If
|
||||
'添加下载文件
|
||||
Dim Urls = File("downloads").Select(Function(t) t.ToString.Replace("://edge.forgecdn", "://media.forgecdn")).ToList
|
||||
Dim Urls = File("downloads").SelectMany(Function(t) CompFile.HandleCurseForgeDownloadUrls(t.ToString)).ToList
|
||||
Urls.AddRange(Urls.Select(Function(u) DlSourceModGet(u)).ToList)
|
||||
Urls = Urls.Distinct.ToList()
|
||||
Dim TargetPath As String = $"{PathMcFolder}versions\{VersionName}\{File("path")}"
|
||||
@@ -469,7 +476,7 @@ Retry:
|
||||
File.Delete(Target)
|
||||
End If
|
||||
Next
|
||||
If File.Exists(FileAddress) AndAlso GetFileNameWithoutExtentionFromPath(FileAddress) = "modpack" Then
|
||||
If File.Exists(FileAddress) AndAlso IO.Path.GetFileNameWithoutExtension(FileAddress) = "modpack" Then
|
||||
Log("[ModPack] 删除安装整合包文件:" & FileAddress)
|
||||
File.Delete(FileAddress)
|
||||
End If
|
||||
@@ -478,7 +485,7 @@ Retry:
|
||||
'重复任务检查
|
||||
Dim LoaderName As String = $"Modrinth 整合包安装:{VersionName} "
|
||||
If LoaderTaskbar.Any(Function(l) l.Name = LoaderName) Then
|
||||
Hint("该整合包正在安装中!", HintType.Critical)
|
||||
Hint("该整合包正在安装中!", HintType.Red)
|
||||
Throw New CancelledException
|
||||
End If
|
||||
|
||||
@@ -533,7 +540,7 @@ Retry:
|
||||
'重复任务检查
|
||||
Dim LoaderName As String = "HMCL 整合包安装:" & VersionName & " "
|
||||
If LoaderTaskbar.Any(Function(l) l.Name = LoaderName) Then
|
||||
Hint("该整合包正在安装中!", HintType.Critical)
|
||||
Hint("该整合包正在安装中!", HintType.Red)
|
||||
Throw New CancelledException
|
||||
End If
|
||||
'启动
|
||||
@@ -632,8 +639,9 @@ Retry:
|
||||
'构造版本安装请求
|
||||
If PackJson("components") Is Nothing Then Throw New Exception("该 MMC 整合包未提供游戏版本信息,无法安装!")
|
||||
Dim Request As New McInstallRequest With {.TargetVersionName = VersionName, .TargetVersionFolder = $"{PathMcFolder}versions\{VersionName}\"}
|
||||
For Each Component In PackJson("components")
|
||||
Select Case If(Component("uid"), "").ToString
|
||||
For Each Component As JObject In PackJson("components")
|
||||
If Not Component.ContainsKey("uid") Then Continue For
|
||||
Select Case Component("uid").ToString
|
||||
Case "org.lwjgl"
|
||||
Log("[ModPack] 已跳过 LWJGL 项")
|
||||
Case "net.minecraft"
|
||||
@@ -644,9 +652,9 @@ Retry:
|
||||
Request.NeoForgeVersion = 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)
|
||||
Throw New CancelledException
|
||||
Case Else
|
||||
'Case "org.quiltmc.quilt-loader" 'eg. 1.0.0
|
||||
NotifyIncompatibleLoader(Component("uid"))
|
||||
End Select
|
||||
Next
|
||||
'构造加载器
|
||||
@@ -659,7 +667,7 @@ Retry:
|
||||
'重复任务检查
|
||||
Dim LoaderName As String = "MMC 整合包安装:" & VersionName & " "
|
||||
If LoaderTaskbar.Any(Function(l) l.Name = LoaderName) Then
|
||||
Hint("该整合包正在安装中!", HintType.Critical)
|
||||
Hint("该整合包正在安装中!", HintType.Red)
|
||||
Throw New CancelledException
|
||||
End If
|
||||
|
||||
@@ -717,7 +725,7 @@ Retry:
|
||||
Next
|
||||
If Not Addons.ContainsKey("game") Then Throw New Exception("该 MCBBS 整合包未提供游戏版本信息,无法安装!")
|
||||
If Addons.ContainsKey("quilt") Then
|
||||
Hint("PCL 暂不支持安装需要 Quilt 的整合包!", HintType.Critical)
|
||||
Hint("PCL 暂不支持安装需要 Quilt 的整合包!", HintType.Red)
|
||||
Throw New CancelledException
|
||||
End If
|
||||
Dim Request As New McInstallRequest With {
|
||||
@@ -738,7 +746,7 @@ Retry:
|
||||
'重复任务检查
|
||||
Dim LoaderName As String = "MCBBS 整合包安装:" & VersionName & " "
|
||||
If LoaderTaskbar.Any(Function(l) l.Name = LoaderName) Then
|
||||
Hint("该整合包正在安装中!", HintType.Critical)
|
||||
Hint("该整合包正在安装中!", HintType.Red)
|
||||
Throw New CancelledException
|
||||
End If
|
||||
|
||||
@@ -758,7 +766,7 @@ Retry:
|
||||
MyMsgBox("接下来请选择一个空文件夹,它会被安装到这个文件夹里。", "安装", "继续", ForceWait:=True)
|
||||
Dim TargetFolder As String = SelectFolder("选择安装目标(必须是一个空文件夹)")
|
||||
If String.IsNullOrEmpty(TargetFolder) Then Throw New CancelledException
|
||||
If Directory.GetFileSystemEntries(TargetFolder).Length > 0 Then Hint("请选择一个空文件夹作为安装目标!", HintType.Critical) : Throw New CancelledException
|
||||
If Directory.GetFileSystemEntries(TargetFolder).Length > 0 Then Hint("请选择一个空文件夹作为安装目标!", HintType.Red) : Throw New CancelledException
|
||||
'解压
|
||||
Dim Loader As New LoaderCombo(Of String)("解压压缩包", {
|
||||
New LoaderTask(Of String, Integer)("解压压缩包",
|
||||
@@ -787,7 +795,7 @@ Retry:
|
||||
Log("[Modpack] 找到压缩包中附带的启动器:" & Launcher)
|
||||
If MyMsgBox($"整合包里似乎自带了启动器,是否换用它继续安装?{vbCrLf}即将打开:{Launcher}", "换用整合包启动器?", "换用", "不换用") = 1 Then
|
||||
OpenExplorer(TargetFolder)
|
||||
ShellOnly(Launcher, "--wait") '要求等待已有的 PCL 退出
|
||||
StartProcess(Launcher, "--wait") '要求等待已有的 PCL 退出
|
||||
Log("[Modpack] 为换用整合包中的启动器启动,强制结束程序")
|
||||
FrmMain.EndProgram(False)
|
||||
Return
|
||||
@@ -835,8 +843,8 @@ Retry:
|
||||
MyMsgBox("接下来请选择一个空文件夹,它会被安装到这个文件夹里。", "安装", "继续", ForceWait:=True)
|
||||
Dim TargetFolder As String = SelectFolder("选择安装目标(必须是一个空文件夹)")
|
||||
If String.IsNullOrEmpty(TargetFolder) Then Throw New CancelledException
|
||||
If TargetFolder.Contains("!") OrElse TargetFolder.Contains(";") Then Hint("Minecraft 文件夹路径中不能含有感叹号或分号!", HintType.Critical) : Throw New CancelledException
|
||||
If Directory.GetFileSystemEntries(TargetFolder).Length > 0 Then Hint("请选择一个空文件夹作为安装目标!", HintType.Critical) : Throw New CancelledException
|
||||
If TargetFolder.Contains("!") OrElse TargetFolder.Contains(";") Then Hint("Minecraft 文件夹路径中不能含有感叹号或分号!", HintType.Red) : Throw New CancelledException
|
||||
If Directory.GetFileSystemEntries(TargetFolder).Length > 0 Then Hint("请选择一个空文件夹作为安装目标!", HintType.Red) : Throw New CancelledException
|
||||
'解压
|
||||
Dim Loader As New LoaderCombo(Of String)("解压压缩包", {
|
||||
New LoaderTask(Of String, Integer)("解压压缩包",
|
||||
|
||||
@@ -292,8 +292,8 @@
|
||||
End Sub, "MinecraftWindowMaximize")
|
||||
End If
|
||||
ElseIf Not IsWindowAppeared Then
|
||||
'已找到 FML 窗口
|
||||
WatcherLog("FML 窗口已加载:" & MinecraftWindowName & "(" & MinecraftWindowHandle.ToInt64 & ")")
|
||||
'已找到 Mod Loader 窗口
|
||||
WatcherLog("Mod Loader 窗口已加载:" & MinecraftWindowName & "(" & MinecraftWindowHandle.ToInt64 & ")")
|
||||
End If
|
||||
IsWindowAppeared = True
|
||||
Catch ex As Exception
|
||||
@@ -349,14 +349,14 @@
|
||||
State = MinecraftState.Crashed
|
||||
'崩溃分析
|
||||
WatcherLog("Minecraft 已崩溃,将在 2 秒后开始崩溃分析")
|
||||
Hint("检测到 Minecraft 出现错误,错误分析已开始……")
|
||||
Hint("检测到 Minecraft 出现错误,错误分析已开始……", HintType.Red)
|
||||
FeedbackInfo()
|
||||
RunInNewThread(
|
||||
Sub()
|
||||
Try
|
||||
Thread.Sleep(2000)
|
||||
WatcherLog("崩溃分析开始")
|
||||
Dim Analyzer As New CrashAnalyzer(PID)
|
||||
Dim Analyzer As New CrashAnalyzer(Version)
|
||||
Analyzer.Collect(Version.PathIndie, LatestLog.ToList)
|
||||
Analyzer.Prepare()
|
||||
Analyzer.Analyze(Version)
|
||||
|
||||
@@ -1,125 +1,305 @@
|
||||
Public Module ModEvent
|
||||
|
||||
#Region "附加属性"
|
||||
|
||||
Public Sub TryStartEvent(Type As String, Data As String)
|
||||
If String.IsNullOrWhiteSpace(Type) Then Return
|
||||
Dim RealData As String() = {""}
|
||||
If Data IsNot Nothing Then RealData = Data.Split("|")
|
||||
StartEvent(Type, RealData)
|
||||
''' <summary>
|
||||
''' 用于在 XAML 中初始化列表对象。
|
||||
''' 附加属性无法在 XAML 中为每个对象初始化独立的列表对象,因此需要一个包装类,然后在 XAML 中显式初始化。
|
||||
''' </summary>
|
||||
<Markup.ContentProperty("Events")>
|
||||
Public Class CustomEventCollection
|
||||
Implements IEnumerable(Of CustomEvent)
|
||||
Dim _Events As New List(Of CustomEvent)
|
||||
Public ReadOnly Property Events As List(Of CustomEvent)
|
||||
Get
|
||||
Return _Events
|
||||
End Get
|
||||
End Property
|
||||
Public Function GetEnumerator() As IEnumerator(Of CustomEvent) Implements IEnumerable(Of CustomEvent).GetEnumerator
|
||||
Return DirectCast(Events, IEnumerable(Of CustomEvent)).GetEnumerator()
|
||||
End Function
|
||||
Private Function IEnumerable_GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
|
||||
Return DirectCast(Events, IEnumerable).GetEnumerator()
|
||||
End Function
|
||||
End Class
|
||||
|
||||
''' <summary>
|
||||
''' 提供自定义事件的附加属性。
|
||||
''' </summary>
|
||||
Public Class CustomEventService
|
||||
|
||||
'Events
|
||||
Public Shared ReadOnly EventsProperty As DependencyProperty =
|
||||
DependencyProperty.RegisterAttached("Events", GetType(CustomEventCollection), GetType(CustomEventService), New PropertyMetadata(Nothing))
|
||||
<AttachedPropertyBrowsableForType(GetType(DependencyObject))>
|
||||
Public Shared Sub SetEvents(d As DependencyObject, value As CustomEventCollection)
|
||||
d.SetValue(EventsProperty, value)
|
||||
End Sub
|
||||
Public Sub StartEvent(Type As String, Data As String())
|
||||
<AttachedPropertyBrowsableForType(GetType(DependencyObject))>
|
||||
Public Shared Function GetEvents(d As DependencyObject) As CustomEventCollection
|
||||
If d.GetValue(EventsProperty) Is Nothing Then d.SetValue(EventsProperty, New CustomEventCollection)
|
||||
Return d.GetValue(EventsProperty)
|
||||
End Function
|
||||
|
||||
'EventType
|
||||
Public Shared ReadOnly EventTypeProperty As DependencyProperty =
|
||||
DependencyProperty.RegisterAttached("EventType", GetType(CustomEvent.EventType), GetType(CustomEventService), New PropertyMetadata(Nothing))
|
||||
<AttachedPropertyBrowsableForType(GetType(DependencyObject))>
|
||||
Public Shared Sub SetEventType(d As DependencyObject, value As CustomEvent.EventType)
|
||||
d.SetValue(EventTypeProperty, value)
|
||||
End Sub
|
||||
<AttachedPropertyBrowsableForType(GetType(DependencyObject))>
|
||||
Public Shared Function GetEventType(d As DependencyObject) As CustomEvent.EventType
|
||||
Return d.GetValue(EventTypeProperty)
|
||||
End Function
|
||||
|
||||
'EventData
|
||||
Public Shared ReadOnly EventDataProperty As DependencyProperty =
|
||||
DependencyProperty.RegisterAttached("EventData", GetType(String), GetType(CustomEventService), New PropertyMetadata(Nothing))
|
||||
<AttachedPropertyBrowsableForType(GetType(DependencyObject))>
|
||||
Public Shared Sub SetEventData(d As DependencyObject, value As String)
|
||||
d.SetValue(EventDataProperty, value)
|
||||
End Sub
|
||||
<AttachedPropertyBrowsableForType(GetType(DependencyObject))>
|
||||
Public Shared Function GetEventData(d As DependencyObject) As String
|
||||
Return d.GetValue(EventDataProperty)
|
||||
End Function
|
||||
|
||||
End Class
|
||||
|
||||
Partial Public Module ModMain
|
||||
|
||||
''' <summary>
|
||||
''' 触发该控件上的自定义事件。
|
||||
''' 事件会在新线程中执行。
|
||||
''' </summary>
|
||||
<Runtime.CompilerServices.Extension>
|
||||
Public Sub RaiseCustomEvent(Control As DependencyObject)
|
||||
'收集事件列表
|
||||
Dim Events = CustomEventService.GetEvents(Control).ToList
|
||||
Dim EventType = CustomEventService.GetEventType(Control)
|
||||
If EventType <> CustomEvent.EventType.None Then Events.Add(New CustomEvent(EventType, CustomEventService.GetEventData(Control)))
|
||||
'执行事件
|
||||
If Not Events.Any Then Return
|
||||
RunInNewThread(
|
||||
Sub()
|
||||
For Each e In Events
|
||||
e.Raise()
|
||||
Next
|
||||
End Sub, "执行自定义事件 " & GetUuid())
|
||||
End Sub
|
||||
|
||||
End Module
|
||||
|
||||
#End Region
|
||||
|
||||
''' <summary>
|
||||
''' 自定义事件。
|
||||
''' </summary>
|
||||
Public Class CustomEvent
|
||||
|
||||
#Region "属性与触发"
|
||||
|
||||
Public Property Type As EventType = EventType.None
|
||||
Public Property Data As String = Nothing
|
||||
Public Sub New()
|
||||
End Sub
|
||||
Public Sub New(Type As EventType, Data As String)
|
||||
Me.Type = Type
|
||||
Me.Data = Data
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 在当前线程中触发该自定义事件。
|
||||
''' </summary>
|
||||
Public Sub Raise()
|
||||
Raise(Type, Data)
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
Public Enum EventType
|
||||
None = 0
|
||||
打开网页
|
||||
打开文件
|
||||
打开帮助
|
||||
执行命令
|
||||
启动游戏
|
||||
复制文本
|
||||
刷新主页
|
||||
刷新页面
|
||||
刷新帮助
|
||||
今日人品
|
||||
内存优化
|
||||
清理垃圾
|
||||
弹出窗口
|
||||
弹出提示
|
||||
切换页面
|
||||
导入整合包
|
||||
安装整合包
|
||||
下载文件
|
||||
修改设置
|
||||
写入设置
|
||||
修改变量
|
||||
写入变量
|
||||
加入房间
|
||||
End Enum
|
||||
|
||||
''' <summary>
|
||||
''' 在当前线程中触发一个自定义事件。
|
||||
''' </summary>
|
||||
Public Shared Sub Raise(Type As EventType, Arg As String)
|
||||
If Type = EventType.None Then Return
|
||||
Log($"[Control] 执行自定义事件:{Type}, {Arg}")
|
||||
Try
|
||||
Log("[Control] 执行自定义事件:" & Type & ", " & Join(Data, ", "))
|
||||
Dim Args As String() = If(Arg?.Split("|"), {""})
|
||||
Select Case Type
|
||||
|
||||
Case "打开网页"
|
||||
Data(0) = Data(0).Replace("\", "/")
|
||||
If Not Data(0).Contains("://") OrElse Data(0).StartsWithF("file", True) Then '为了支持更多协议(#2200)
|
||||
Case EventType.打开网页
|
||||
Arg = Arg.Replace("\", "/")
|
||||
If Not Arg.Contains("://") OrElse Arg.StartsWithF("file", True) Then '为了支持更多协议(#2200)
|
||||
MyMsgBox("EventData 必须为一个网址。" & vbCrLf & "如果想要启动程序,请将 EventType 改为 打开文件。", "事件执行失败")
|
||||
Return
|
||||
End If
|
||||
Hint("正在开启中,请稍候……")
|
||||
OpenWebsite(Data(0))
|
||||
Hint("正在开启中,请稍候:" & Arg)
|
||||
RunInThread(Sub() OpenWebsite(Arg))
|
||||
|
||||
Case "打开文件", "打开帮助", "执行命令"
|
||||
Case EventType.打开文件, EventType.打开帮助, EventType.执行命令
|
||||
RunInThread(
|
||||
Sub()
|
||||
Try
|
||||
'确认实际路径
|
||||
Dim ActualPaths = GetEventAbsoluteUrls(Data(0), Type)
|
||||
Dim ActualPaths = GetAbsoluteUrls(Args(0), Type)
|
||||
Dim Location = ActualPaths(0), WorkingDir = ActualPaths(1)
|
||||
Log($"[Control] 打开类自定义事件实际路径:{Location},工作目录:{WorkingDir}")
|
||||
'执行
|
||||
If Type = "打开帮助" Then
|
||||
If Type = EventType.打开帮助 Then
|
||||
PageOtherHelp.EnterHelpPage(Location)
|
||||
Else
|
||||
If Not Setup.Get("HintCustomCommand") Then
|
||||
Select Case MyMsgBox(
|
||||
"即将执行:" & Location & If(Data.Length >= 2, " " & Data(1), "") & vbCrLf &
|
||||
"请在确认该操作没有安全隐患后继续。", "执行确认", "继续", "继续且今后不再要求确认", "取消")
|
||||
Case 2
|
||||
Setup.Set("HintCustomCommand", True)
|
||||
Case 3
|
||||
Return
|
||||
End Select
|
||||
End If
|
||||
If Not EventSafetyConfirm("即将执行:" & Location & If(Args.Length >= 2, " " & Args(1), "")) Then Return
|
||||
Dim Info As New ProcessStartInfo With {
|
||||
.Arguments = If(Data.Length >= 2, Data(1), ""),
|
||||
.Arguments = If(Args.Length >= 2, Args(1), ""),
|
||||
.FileName = Location,
|
||||
.WorkingDirectory = ShortenPath(WorkingDir)
|
||||
.WorkingDirectory = WorkingDir
|
||||
}
|
||||
Process.Start(Info)
|
||||
StartProcess(Info)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "执行打开类自定义事件失败", LogLevel.Msgbox)
|
||||
End Try
|
||||
End Sub)
|
||||
|
||||
Case "启动游戏"
|
||||
If Data(0) = "\current" Then
|
||||
Case EventType.启动游戏
|
||||
If Args(0) = "\current" Then
|
||||
If McVersionCurrent Is Nothing Then
|
||||
Hint("请先选择一个 Minecraft 版本!", HintType.Critical)
|
||||
Hint("请先选择一个 Minecraft 版本!", HintType.Red)
|
||||
Return
|
||||
Else
|
||||
Data(0) = McVersionCurrent.Name
|
||||
Args(0) = McVersionCurrent.Name
|
||||
End If
|
||||
End If
|
||||
If McLaunchStart(New McLaunchOptions With
|
||||
{.ServerIp = If(Data.Length >= 2, Data(1), Nothing), .Version = New McVersion(Data(0))}) Then
|
||||
Hint("正在启动 " & Data(0) & "……")
|
||||
RunInUi(
|
||||
Sub()
|
||||
If McLaunchStart(New McLaunchOptions With
|
||||
{.ServerIp = If(Args.Length >= 2, Args(1), Nothing), .Version = New McVersion(Args(0))}) Then
|
||||
Hint($"正在启动 {Args(0)}……")
|
||||
End If
|
||||
End Sub)
|
||||
|
||||
Case EventType.复制文本
|
||||
ClipboardSet(Arg)
|
||||
|
||||
Case EventType.刷新主页
|
||||
RunInUi(
|
||||
Sub()
|
||||
FrmLaunchRight.ForceRefresh()
|
||||
If String.IsNullOrEmpty(Arg) Then Hint("已刷新!", HintType.Green)
|
||||
End Sub)
|
||||
|
||||
Case EventType.刷新页面
|
||||
If TypeOf FrmMain.PageRight Is IRefreshable Then
|
||||
RunInUiWait(Sub() CType(FrmMain.PageRight, IRefreshable).Refresh())
|
||||
If String.IsNullOrEmpty(Arg) Then Hint("已刷新!", HintType.Green)
|
||||
Else
|
||||
Hint("当前页面不支持刷新操作!", HintType.Red)
|
||||
End If
|
||||
|
||||
Case "复制文本"
|
||||
ClipboardSet(Join(Data, "|"))
|
||||
Case EventType.刷新帮助
|
||||
RunInUiWait(Sub() PageOtherLeft.RefreshHelp())
|
||||
If String.IsNullOrEmpty(Arg) Then Hint("已刷新!", HintType.Green)
|
||||
|
||||
Case "刷新主页"
|
||||
FrmLaunchRight.ForceRefresh()
|
||||
If Data(0) = "" Then Hint("已刷新主页!", HintType.Finish)
|
||||
|
||||
Case "刷新帮助"
|
||||
PageOtherLeft.RefreshHelp()
|
||||
|
||||
Case "今日人品"
|
||||
Case EventType.今日人品
|
||||
PageOtherTest.Jrrp()
|
||||
|
||||
Case "内存优化"
|
||||
Case EventType.内存优化
|
||||
RunInThread(Sub() PageOtherTest.MemoryOptimize(True))
|
||||
|
||||
Case "清理垃圾"
|
||||
Case EventType.清理垃圾
|
||||
RunInThread(Sub() PageOtherTest.RubbishClear())
|
||||
|
||||
Case "弹出窗口"
|
||||
MyMsgBox(Data(1).Replace("\n", vbCrLf), Data(0).Replace("\n", vbCrLf))
|
||||
Case EventType.弹出窗口
|
||||
If Args.Length = 1 Then Throw New Exception($"EventType {Type} 需要至少 2 个以 | 分割的参数,例如 弹窗标题|弹窗内容")
|
||||
MyMsgBox(Args(1).Replace("\n", vbCrLf), Args(0).Replace("\n", vbCrLf), If(Args.Length > 2, Args(2), "确定"))
|
||||
|
||||
Case "切换页面"
|
||||
FrmMain.PageChange(Val(Data(0)), Val(Data(1)))
|
||||
Case EventType.弹出提示
|
||||
Hint(Args(0).Replace("\n", vbCrLf), If(Args.Length = 1, HintType.Blue, Args(1).ParseToEnum(Of HintType)))
|
||||
|
||||
Case "导入整合包", "安装整合包"
|
||||
Case EventType.切换页面
|
||||
RunInUi(Sub() FrmMain.PageChange(
|
||||
Args(0).ParseToEnum(Of FormMain.PageType),
|
||||
If(Args.Length = 1, FormMain.PageSubType.Default, Args(1).ParseToEnum(Of FormMain.PageSubType))))
|
||||
|
||||
Case EventType.导入整合包, EventType.安装整合包
|
||||
RunInUi(Sub() ModpackInstall())
|
||||
|
||||
Case "下载文件"
|
||||
Data(0) = Data(0).Replace("\", "/")
|
||||
If Not (Data(0).StartsWithF("http://", True) OrElse Data(0).StartsWithF("https://", True)) Then
|
||||
Case EventType.下载文件
|
||||
Args(0) = Args(0).Replace("\", "/")
|
||||
If Not (Args(0).StartsWithF("http://", True) OrElse Args(0).StartsWithF("https://", True)) Then
|
||||
MyMsgBox("EventData 必须为以 http:// 或 https:// 开头的网址。" & vbCrLf & "PCL 不支持其他乱七八糟的下载协议。", "事件执行失败")
|
||||
Return
|
||||
End If
|
||||
Try
|
||||
Select Case Data.Length
|
||||
Case 1
|
||||
PageOtherTest.StartCustomDownload(Data(0), GetFileNameFromPath(Data(0)))
|
||||
Case 2
|
||||
PageOtherTest.StartCustomDownload(Data(0), Data(1))
|
||||
Case Else
|
||||
PageOtherTest.StartCustomDownload(Data(0), Data(1), Data(2))
|
||||
End Select
|
||||
Catch
|
||||
PageOtherTest.StartCustomDownload(Data(0), "未知")
|
||||
End Try
|
||||
If Not EventSafetyConfirm("即将从该网址下载文件:" & vbCrLf & Args(0)) Then Return
|
||||
RunInUi(
|
||||
Sub()
|
||||
Try
|
||||
Select Case Args.Length
|
||||
Case 1
|
||||
PageOtherTest.StartCustomDownload(Args(0), GetFileNameFromPath(Args(0)))
|
||||
Case 2
|
||||
PageOtherTest.StartCustomDownload(Args(0), Args(1))
|
||||
Case Else
|
||||
PageOtherTest.StartCustomDownload(Args(0), Args(1), Args(2))
|
||||
End Select
|
||||
Catch
|
||||
PageOtherTest.StartCustomDownload(Args(0), "未知")
|
||||
End Try
|
||||
End Sub)
|
||||
|
||||
Case EventType.修改设置, EventType.写入设置
|
||||
If Args.Length = 1 Then Throw New Exception($"EventType {Type} 需要至少 2 个以 | 分割的参数,例如 UiLauncherTransparent|400")
|
||||
Setup.SetSafe(Args(0), Args(1), Version:=McVersionCurrent)
|
||||
If Args.Length = 2 Then Hint($"已写入设置:{Args(0)} → {Args(1)}", HintType.Green)
|
||||
|
||||
Case EventType.修改变量, EventType.写入变量
|
||||
If Args.Length = 1 Then Throw New Exception($"EventType {Type} 需要至少 2 个以 | 分割的参数,例如 VariableName|SomeValue")
|
||||
WriteReg("CustomEvent" & Args(0), Args(1))
|
||||
If Args.Length = 2 Then Hint($"已写入变量:{Args(0)} → {Args(1)}", HintType.Green)
|
||||
|
||||
Case EventType.加入房间
|
||||
RunInUi(
|
||||
Sub()
|
||||
If String.IsNullOrWhiteSpace(Arg) Then
|
||||
PageLinkMain.Join()
|
||||
Else
|
||||
If Not EventSafetyConfirm("即将加入第三方联机房间。") Then Return
|
||||
PageLinkMain.Join(Arg)
|
||||
End If
|
||||
End Sub)
|
||||
|
||||
Case Else
|
||||
MyMsgBox("未知的事件类型:" & Type & vbCrLf & "请检查事件类型填写是否正确,或者 PCL 是否为最新版本。", "事件执行失败")
|
||||
End Select
|
||||
Catch ex As Exception
|
||||
Log(ex, "事件执行失败", LogLevel.Msgbox)
|
||||
Log(ex, $"事件执行失败({Type}, {Arg})", LogLevel.Msgbox)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
@@ -127,7 +307,7 @@
|
||||
''' 返回自定义事件的绝对 Url。实际返回 {绝对 Url, WorkingDir}。
|
||||
''' 失败会抛出异常。
|
||||
''' </summary>
|
||||
Public Function GetEventAbsoluteUrls(RelativeUrl As String, EventType As String) As String()
|
||||
Public Shared Function GetAbsoluteUrls(RelativeUrl As String, Type As EventType) As String()
|
||||
|
||||
'网页确认
|
||||
If RelativeUrl.StartsWithF("http", True) Then
|
||||
@@ -168,25 +348,25 @@
|
||||
If RelativeUrl.Contains(":\") Then
|
||||
'绝对路径
|
||||
Location = RelativeUrl
|
||||
Log("[Control] 自定义事件中由绝对路径" & EventType & ":" & Location)
|
||||
Log("[Control] 自定义事件中由绝对路径" & Type & ":" & Location)
|
||||
ElseIf File.Exists(Path & "PCL\" & RelativeUrl) Then
|
||||
'相对 PCL 文件夹的路径
|
||||
Location = Path & "PCL\" & RelativeUrl
|
||||
Log("[Control] 自定义事件中由相对 PCL 文件夹的路径" & EventType & ":" & Location)
|
||||
Log("[Control] 自定义事件中由相对 PCL 文件夹的路径" & Type & ":" & Location)
|
||||
ElseIf File.Exists(Path & "PCL\Help\" & RelativeUrl) Then
|
||||
'相对 PCL 本地帮助文件夹的路径
|
||||
Location = Path & "PCL\Help\" & RelativeUrl
|
||||
WorkingDir = Path & "PCL\Help\"
|
||||
Log("[Control] 自定义事件中由相对 PCL 本地帮助文件夹的路径" & EventType & ":" & Location)
|
||||
ElseIf EventType = "打开帮助" AndAlso File.Exists(PathTemp & "Help\" & RelativeUrl) Then
|
||||
Log("[Control] 自定义事件中由相对 PCL 本地帮助文件夹的路径" & Type & ":" & Location)
|
||||
ElseIf Type = EventType.打开帮助 AndAlso File.Exists(PathTemp & "Help\" & RelativeUrl) Then
|
||||
'相对 PCL 自带帮助文件夹的路径
|
||||
Location = PathTemp & "Help\" & RelativeUrl
|
||||
WorkingDir = PathTemp & "Help\"
|
||||
Log("[Control] 自定义事件中由相对 PCL 自带帮助文件夹的路径" & EventType & ":" & Location)
|
||||
ElseIf EventType = "打开文件" OrElse EventType = "执行命令" Then
|
||||
Log("[Control] 自定义事件中由相对 PCL 自带帮助文件夹的路径" & Type & ":" & Location)
|
||||
ElseIf Type = EventType.打开文件 OrElse Type = EventType.执行命令 Then
|
||||
'直接使用原有路径启动程序
|
||||
Location = RelativeUrl
|
||||
Log("[Control] 自定义事件中直接" & EventType & ":" & Location)
|
||||
Log("[Control] 自定义事件中直接" & Type & ":" & Location)
|
||||
Else
|
||||
'打开帮助,但是格式不对劲
|
||||
Throw New FileNotFoundException("未找到 EventData 指向的本地 xaml 文件:" & RelativeUrl, RelativeUrl)
|
||||
@@ -195,4 +375,20 @@
|
||||
Return {Location, WorkingDir}
|
||||
End Function
|
||||
|
||||
End Module
|
||||
''' <summary>
|
||||
''' 弹出安全确认弹窗。返回是否继续执行。
|
||||
''' </summary>
|
||||
Private Shared Function EventSafetyConfirm(Message As String) As Boolean
|
||||
If Setup.Get("HintCustomCommand") Then Return True
|
||||
Select Case MyMsgBox(Message & vbCrLf & "请在确认没有安全隐患后再继续。", "执行确认", "继续", "继续且今后不再要求确认", "取消")
|
||||
Case 1
|
||||
Return True
|
||||
Case 2
|
||||
Setup.Set("HintCustomCommand", True)
|
||||
Return True
|
||||
Case Else
|
||||
Return False
|
||||
End Select
|
||||
End Function
|
||||
|
||||
End Class
|
||||
@@ -7,23 +7,12 @@ Public Module ModMain
|
||||
|
||||
''' <summary>
|
||||
''' 提示信息的种类。
|
||||
''' 该枚举在自定义事件中使用,是公开 API 的一部分。
|
||||
''' </summary>
|
||||
Public Enum HintType
|
||||
''' <summary>
|
||||
''' 信息,通常是蓝色的“i”。
|
||||
''' </summary>
|
||||
''' <remarks></remarks>
|
||||
Info
|
||||
''' <summary>
|
||||
''' 已完成,通常是绿色的“√”。
|
||||
''' </summary>
|
||||
''' <remarks></remarks>
|
||||
Finish
|
||||
''' <summary>
|
||||
''' 错误,通常是红色的“×”。
|
||||
''' </summary>
|
||||
''' <remarks></remarks>
|
||||
Critical
|
||||
Blue
|
||||
Green
|
||||
Red
|
||||
End Enum
|
||||
Private Structure HintMessage
|
||||
Public Text As String
|
||||
@@ -38,7 +27,7 @@ Public Module ModMain
|
||||
''' <summary>
|
||||
''' 在窗口左下角弹出提示文本。
|
||||
''' </summary>
|
||||
Public Sub Hint(Text As String, Optional Type As HintType = HintType.Info, Optional Log As Boolean = True)
|
||||
Public Sub Hint(Text As String, Optional Type As HintType = HintType.Blue, Optional Log As Boolean = True)
|
||||
If HintWaiting Is Nothing Then HintWaiting = New SafeList(Of HintMessage)
|
||||
HintWaiting.Add(New HintMessage With {.Text = If(Text, ""), .Type = Type, .Log = Log})
|
||||
End Sub
|
||||
@@ -68,13 +57,13 @@ Public Module ModMain
|
||||
Dim TargetColor0, TargetColor1 As MyColor
|
||||
Dim Percent As Double = 0.3
|
||||
Select Case CurrentHint.Type
|
||||
Case HintType.Info
|
||||
Case HintType.Blue
|
||||
TargetColor0 = New MyColor(215, 37, 155, 252)
|
||||
TargetColor1 = New MyColor(215, 10, 142, 252)
|
||||
Case HintType.Finish
|
||||
Case HintType.Green
|
||||
TargetColor0 = New MyColor(215, 33, 177, 33)
|
||||
TargetColor1 = New MyColor(215, 29, 160, 29)
|
||||
Case Else 'HintType.Critical
|
||||
Case Else 'HintType.Red
|
||||
TargetColor0 = New MyColor(215, 255, 53, 11)
|
||||
TargetColor1 = New MyColor(215, 255, 43, 0)
|
||||
End Select
|
||||
@@ -248,10 +237,6 @@ EndHint:
|
||||
'将弹窗列入队列
|
||||
Dim Converter As New MyMsgBoxConverter With {.Type = MyMsgBoxType.Text, .Button1 = Button1, .Button2 = Button2, .Button3 = Button3, .Text = Caption, .IsWarn = IsWarn, .Title = Title, .HighLight = HighLight, .ForceWait = True, .Button1Action = Button1Action, .Button2Action = Button2Action, .Button3Action = Button3Action}
|
||||
WaitingMyMsgBox.Add(Converter)
|
||||
If RunInUi() Then
|
||||
'若为 UI 线程,立即执行弹窗刻, 避免快速(连点器)点击时多次弹窗
|
||||
MyMsgBoxTick()
|
||||
End If
|
||||
If Button2.Length > 0 OrElse ForceWait Then
|
||||
'若有多个按钮则开始等待
|
||||
If FrmMain Is Nothing OrElse FrmMain.PanMsg Is Nothing AndAlso RunInUi() Then
|
||||
@@ -275,6 +260,7 @@ EndHint:
|
||||
Else
|
||||
Try
|
||||
FrmMain.DragStop()
|
||||
If RunInUi() Then MyMsgBoxTick()
|
||||
ComponentDispatcher.PushModal()
|
||||
Dispatcher.PushFrame(Converter.WaitFrame)
|
||||
Finally
|
||||
@@ -306,6 +292,7 @@ EndHint:
|
||||
'虽然我也不知道这是啥但是能用就成了 :)
|
||||
Try
|
||||
If FrmMain IsNot Nothing Then FrmMain.DragStop()
|
||||
If RunInUi() Then MyMsgBoxTick()
|
||||
ComponentDispatcher.PushModal()
|
||||
Dispatcher.PushFrame(Converter.WaitFrame)
|
||||
Finally
|
||||
@@ -328,6 +315,7 @@ EndHint:
|
||||
'虽然我也不知道这是啥但是能用就成了 :)
|
||||
Try
|
||||
If FrmMain IsNot Nothing Then FrmMain.DragStop()
|
||||
If RunInUi() Then MyMsgBoxTick()
|
||||
ComponentDispatcher.PushModal()
|
||||
Dispatcher.PushFrame(Converter.WaitFrame)
|
||||
Finally
|
||||
@@ -350,7 +338,7 @@ EndHint:
|
||||
ElseIf WaitingMyMsgBox.Any Then
|
||||
'没有弹窗,显示一个等待的弹窗
|
||||
FrmMain.PanMsg.Visibility = Visibility.Visible
|
||||
Select Case CType(WaitingMyMsgBox(0), MyMsgBoxConverter).Type
|
||||
Select Case WaitingMyMsgBox(0).Type
|
||||
Case MyMsgBoxType.Input
|
||||
FrmMain.PanMsg.Children.Add(New MyMsgInput(WaitingMyMsgBox(0)))
|
||||
Case MyMsgBoxType.Select
|
||||
@@ -363,7 +351,7 @@ EndHint:
|
||||
WaitingMyMsgBox.RemoveAt(0)
|
||||
Else
|
||||
'没有弹窗,没有等待的弹窗
|
||||
If Not FrmMain.PanMsg.Visibility = Visibility.Collapsed Then FrmMain.PanMsg.Visibility = Visibility.Collapsed
|
||||
If FrmMain.PanMsg.Visibility <> Visibility.Collapsed Then FrmMain.PanMsg.Visibility = Visibility.Collapsed
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "处理等待中的弹窗失败", LogLevel.Feedback)
|
||||
@@ -388,21 +376,11 @@ EndHint:
|
||||
Public FrmSpeedRight As PageSpeedRight
|
||||
|
||||
'联机页面声明
|
||||
Public FrmLinkLeft As PageLinkLeft
|
||||
Public FrmLinkIoi As PageLinkIoi
|
||||
Public FrmLinkHiper As PageLinkHiper
|
||||
Public FrmLinkHelp As PageOtherHelpDetail
|
||||
Public FrmLinkFeedback As PageLinkFeedback
|
||||
Public FrmLinkMain As PageLinkMain
|
||||
|
||||
'下载页面声明
|
||||
Public FrmDownloadLeft As PageDownloadLeft
|
||||
Public FrmDownloadInstall As PageDownloadInstall
|
||||
Public FrmDownloadClient As PageDownloadClient
|
||||
Public FrmDownloadOptiFine As PageDownloadOptiFine
|
||||
Public FrmDownloadLiteLoader As PageDownloadLiteLoader
|
||||
Public FrmDownloadForge As PageDownloadForge
|
||||
Public FrmDownloadNeoForge As PageDownloadNeoForge
|
||||
Public FrmDownloadFabric As PageDownloadFabric
|
||||
Public FrmDownloadMod As PageDownloadMod
|
||||
Public FrmDownloadPack As PageDownloadPack
|
||||
Public FrmDownloadDataPack As PageDownloadDataPack
|
||||
@@ -496,7 +474,7 @@ EndHint:
|
||||
''' 是否为 “执行事件”。
|
||||
''' </summary>
|
||||
Public IsEvent As Boolean
|
||||
Public EventType As String
|
||||
Public EventType As CustomEvent.EventType
|
||||
Public EventData As String
|
||||
''' <summary>
|
||||
''' 若非执行事件,其对应的 .xaml 本地文件内容。
|
||||
@@ -510,7 +488,7 @@ EndHint:
|
||||
''' </summary>
|
||||
Public Sub New(FilePath As String)
|
||||
RawPath = FilePath
|
||||
Dim JsonData As JObject = GetJson(HelpArgumentReplace(ReadFile(FilePath)))
|
||||
Dim JsonData As JObject = GetJson(ArgumentReplace(ReadFile(FilePath), AddressOf EscapeXML))
|
||||
If JsonData Is Nothing Then Throw New FileNotFoundException("未找到帮助文件:" & FilePath, FilePath)
|
||||
'加载常规信息
|
||||
If JsonData("Title") IsNot Nothing Then
|
||||
@@ -530,8 +508,7 @@ EndHint:
|
||||
Next
|
||||
'加载事件信息
|
||||
If If(JsonData("IsEvent"), False) Then
|
||||
EventType = JsonData("EventType")
|
||||
If EventType Is Nothing Then Throw New ArgumentException("未找到 EventType 项")
|
||||
EventType = [Enum].Parse(GetType(CustomEvent.EventType), JsonData("EventType").ToString)
|
||||
EventData = If(JsonData("EventData"), "")
|
||||
IsEvent = True
|
||||
Else
|
||||
@@ -556,7 +533,7 @@ EndHint:
|
||||
Public Function SetToListItem(Item As MyListItem) As MyListItem
|
||||
Dim Logo As String
|
||||
If IsEvent Then
|
||||
If EventType = "弹出窗口" Then
|
||||
If EventType = CustomEvent.EventType.弹出窗口 Then
|
||||
Logo = PathImage & "Blocks/GrassPath.png"
|
||||
Else
|
||||
Logo = PathImage & "Blocks/CommandBlock.png"
|
||||
@@ -573,9 +550,9 @@ EndHint:
|
||||
.Height = 42
|
||||
.Type = MyListItem.CheckType.Clickable
|
||||
.Tag = Me
|
||||
.EventType = Nothing
|
||||
.EventData = Nothing
|
||||
End With
|
||||
CustomEventService.SetEventType(Item, CustomEvent.EventType.None) '清空自定义事件属性,它们会被下面的点击事件处理
|
||||
CustomEventService.SetEventData(Item, Nothing)
|
||||
'项目的点击事件
|
||||
AddHandler Item.Click, Sub(sender, e) PageOtherHelp.OnItemClick(sender.Tag)
|
||||
Return Item
|
||||
@@ -675,15 +652,6 @@ NextFile:
|
||||
Log("[Help] 已解压内置帮助文件,目前状态:" & File.Exists(PathTemp & "Help\启动器\备份设置.xaml"), LogLevel.Debug)
|
||||
End If
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 对帮助文件约定的替换标记进行处理,如果遇到需要转义的字符会进行转义。
|
||||
''' </summary>
|
||||
Public Function HelpArgumentReplace(Xaml As String) As String
|
||||
Dim Result = Xaml.Replace("{path}", EscapeXML(Path))
|
||||
Result = Result.RegexReplaceEach("\{hint\}", Function() EscapeXML(PageOtherTest.GetRandomHint()))
|
||||
Result = Result.RegexReplaceEach("\{cave\}", Function() EscapeXML(PageOtherTest.GetRandomCave()))
|
||||
Return Result
|
||||
End Function
|
||||
|
||||
#End Region
|
||||
|
||||
@@ -842,6 +810,79 @@ NextFile:
|
||||
End Using
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 对替换标记进行处理。会对替换内容使用 EscapeHandler 进行转义。
|
||||
''' </summary>
|
||||
Public Function ArgumentReplace(Text As String, Optional EscapeHandler As Func(Of String, String) = Nothing, Optional ReplaceTime As Boolean = True) As String
|
||||
'预处理
|
||||
If Text Is Nothing Then Return Nothing
|
||||
Dim Replacer =
|
||||
Function(s As String) As String
|
||||
If s Is Nothing Then Return ""
|
||||
If EscapeHandler Is Nothing Then Return s
|
||||
If s.Contains(":\") Then s = ShortenPath(s)
|
||||
Return EscapeHandler(s)
|
||||
End Function
|
||||
'基础
|
||||
Text = Text.Replace("{pcl_version}", Replacer(VersionBaseName))
|
||||
Text = Text.Replace("{pcl_version_code}", Replacer(VersionCode))
|
||||
Text = Text.Replace("{pcl_version_branch}", Replacer(VersionBranchName))
|
||||
Text = Text.Replace("{pcl_branch}", Replacer(VersionBranchMain))
|
||||
Text = Text.Replace("{identify}", Replacer(Identify))
|
||||
Text = Text.Replace("{path}", Replacer(Path))
|
||||
Text = Text.Replace("{path_with_name}", Replacer(PathWithName))
|
||||
Text = Text.Replace("{path_temp}", Replacer(PathTemp))
|
||||
'时间
|
||||
If ReplaceTime Then '在窗口标题中,时间会被后续动态替换,所以此时不应该替换
|
||||
Text = Text.Replace("{date}", Replacer(Date.Now.ToString("yyyy/M/d")))
|
||||
Text = Text.Replace("{time}", Replacer(Date.Now.ToString("HH:mm:ss")))
|
||||
End If
|
||||
'Minecraft
|
||||
Text = Text.Replace("{java}", Replacer(McLaunchJavaSelected?.PathFolder))
|
||||
Text = Text.Replace("{minecraft}", Replacer(PathMcFolder))
|
||||
If McVersionCurrent IsNot Nothing Then
|
||||
Text = Text.Replace("{version_path}", Replacer(McVersionCurrent.Path)) : Text = Text.Replace("{verpath}", Replacer(McVersionCurrent.Path))
|
||||
Text = Text.Replace("{version_indie}", Replacer(McVersionCurrent.PathIndie)) : Text = Text.Replace("{verindie}", Replacer(McVersionCurrent.PathIndie))
|
||||
Text = Text.Replace("{name}", Replacer(McVersionCurrent.Name))
|
||||
If {"unknown", "old", "pending"}.Contains(McVersionCurrent.Version.McName.ToLower) Then
|
||||
Text = Text.Replace("{version}", Replacer(McVersionCurrent.Name))
|
||||
Else
|
||||
Text = Text.Replace("{version}", Replacer(McVersionCurrent.Version.McName))
|
||||
End If
|
||||
Else
|
||||
Text = Text.Replace("{version_path}", Replacer(Nothing)) : Text = Text.Replace("{verpath}", Replacer(Nothing))
|
||||
Text = Text.Replace("{version_indie}", Replacer(Nothing)) : Text = Text.Replace("{verindie}", Replacer(Nothing))
|
||||
Text = Text.Replace("{name}", Replacer(Nothing))
|
||||
Text = Text.Replace("{version}", Replacer(Nothing))
|
||||
End If
|
||||
'登录信息
|
||||
If McLoginLoader.State = LoadState.Finished Then
|
||||
Text = Text.Replace("{user}", Replacer(McLoginLoader.Output.Name))
|
||||
Text = Text.Replace("{uuid}", Replacer(McLoginLoader.Output.Uuid?.ToLower))
|
||||
Select Case McLoginLoader.Input.Type
|
||||
Case McLoginType.Legacy
|
||||
Text = Text.Replace("{login}", Replacer("离线"))
|
||||
Case McLoginType.Ms
|
||||
Text = Text.Replace("{login}", Replacer("正版"))
|
||||
Case McLoginType.Nide
|
||||
Text = Text.Replace("{login}", Replacer("统一通行证"))
|
||||
Case McLoginType.Auth
|
||||
Text = Text.Replace("{login}", Replacer("Authlib-Injector"))
|
||||
End Select
|
||||
Else
|
||||
Text = Text.Replace("{user}", Replacer(Nothing))
|
||||
Text = Text.Replace("{uuid}", Replacer(Nothing))
|
||||
Text = Text.Replace("{login}", Replacer(Nothing))
|
||||
End If
|
||||
'高级
|
||||
Text = Text.RegexReplaceEach("\{hint\}", Function() Replacer(PageOtherTest.GetRandomHint()))
|
||||
Text = Text.RegexReplaceEach("\{cave\}", Function() Replacer(PageOtherTest.GetRandomCave()))
|
||||
Text = Text.RegexReplaceEach("\{setup:([a-zA-Z0-9]+)\}", Function(m) Replacer(Setup.GetSafe(m.Groups(1).Value, McVersionCurrent)))
|
||||
Text = Text.RegexReplaceEach("\{varible:([^:\}]+)(?::([^\}]+))?\}", Function(m) Replacer(ReadReg("CustomEvent" & m.Groups(1).Value, m.Groups(2).Value)))
|
||||
Text = Text.RegexReplaceEach("\{variable:([^:\}]+)(?::([^\}]+))?\}", Function(m) Replacer(ReadReg("CustomEvent" & m.Groups(1).Value, m.Groups(2).Value)))
|
||||
Return Text
|
||||
End Function
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "任务缓存"
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
If MusicState = MusicStates.Pause Then
|
||||
FrmMain.BtnExtraMusic.Logo = Logo.IconPlay
|
||||
FrmMain.BtnExtraMusic.LogoScale = 0.8
|
||||
ToolTipText = "已暂停:" & GetFileNameWithoutExtentionFromPath(MusicCurrent)
|
||||
ToolTipText = "已暂停:" & IO.Path.GetFileNameWithoutExtension(MusicCurrent)
|
||||
If MusicAllList.Count > 1 Then
|
||||
ToolTipText += vbCrLf & "左键恢复播放,右键播放下一曲。"
|
||||
Else
|
||||
@@ -88,7 +88,7 @@
|
||||
Else
|
||||
FrmMain.BtnExtraMusic.Logo = Logo.IconMusic
|
||||
FrmMain.BtnExtraMusic.LogoScale = 1
|
||||
ToolTipText = "正在播放:" & GetFileNameWithoutExtentionFromPath(MusicCurrent)
|
||||
ToolTipText = "正在播放:" & IO.Path.GetFileNameWithoutExtension(MusicCurrent)
|
||||
If MusicAllList.Count > 1 Then
|
||||
ToolTipText += vbCrLf & "左键暂停,右键播放下一曲。"
|
||||
Else
|
||||
@@ -111,7 +111,7 @@
|
||||
''' </summary>
|
||||
Public Sub MusicControlPause()
|
||||
If MusicNAudio Is Nothing Then
|
||||
Hint("音乐播放尚未开始!", HintType.Critical)
|
||||
Hint("音乐播放尚未开始!", HintType.Red)
|
||||
Else
|
||||
Select Case MusicState
|
||||
Case MusicStates.Pause
|
||||
@@ -131,14 +131,14 @@
|
||||
Public Sub MusicControlNext()
|
||||
If MusicAllList.Count = 1 Then
|
||||
MusicStartPlay(MusicCurrent)
|
||||
Hint("重新播放:" & GetFileNameFromPath(MusicCurrent), HintType.Finish)
|
||||
Hint("重新播放:" & GetFileNameFromPath(MusicCurrent), HintType.Green)
|
||||
Else
|
||||
Dim Address As String = DequeueNextMusicAddress()
|
||||
If Address Is Nothing Then
|
||||
Hint("没有可以播放的音乐!", HintType.Critical)
|
||||
Hint("没有可以播放的音乐!", HintType.Red)
|
||||
Else
|
||||
MusicStartPlay(Address)
|
||||
Hint("正在播放:" & GetFileNameFromPath(Address), HintType.Finish)
|
||||
Hint("正在播放:" & GetFileNameFromPath(Address), HintType.Green)
|
||||
End If
|
||||
End If
|
||||
MusicRefreshUI()
|
||||
@@ -182,19 +182,19 @@
|
||||
MusicListInit(True)
|
||||
If Not MusicAllList.Any() Then
|
||||
If MusicNAudio Is Nothing Then
|
||||
If ShowHint Then Hint("未检测到可用的背景音乐!", HintType.Critical)
|
||||
If ShowHint Then Hint("未检测到可用的背景音乐!", HintType.Red)
|
||||
Else
|
||||
MusicNAudio = Nothing
|
||||
If ShowHint Then Hint("背景音乐已清除!", HintType.Finish)
|
||||
If ShowHint Then Hint("背景音乐已清除!", HintType.Green)
|
||||
End If
|
||||
Else
|
||||
Dim Address As String = DequeueNextMusicAddress()
|
||||
If Address Is Nothing Then
|
||||
If ShowHint Then Hint("没有可以播放的音乐!", HintType.Critical)
|
||||
If ShowHint Then Hint("没有可以播放的音乐!", HintType.Red)
|
||||
Else
|
||||
Try
|
||||
MusicStartPlay(Address, IsFirstLoad)
|
||||
If ShowHint Then Hint("背景音乐已刷新:" & GetFileNameFromPath(Address), HintType.Finish, False)
|
||||
If ShowHint Then Hint("背景音乐已刷新:" & GetFileNameFromPath(Address), HintType.Green, False)
|
||||
Catch
|
||||
End Try
|
||||
End If
|
||||
@@ -247,7 +247,7 @@
|
||||
Log("[Music] 已恢复播放")
|
||||
Try
|
||||
MusicNAudio?.Play()
|
||||
Catch 'https://github.com/Hex-Dragon/PCL2/pull/5415#issuecomment-2751135223
|
||||
Catch 'https://github.com/Meloong-Git/PCL/pull/5415#issuecomment-2751135223
|
||||
MusicNAudio?.Stop()
|
||||
MusicNAudio?.Play()
|
||||
End Try
|
||||
@@ -303,18 +303,18 @@
|
||||
Catch ex As Exception
|
||||
Log(ex, "播放音乐出现内部错误(" & MusicCurrent & ")", LogLevel.Developer)
|
||||
If TypeOf ex Is NAudio.MmException AndAlso ex.Message.Contains("AlreadyAllocated") Then
|
||||
Hint("你的音频设备正被其他程序占用。请在关闭占用的程序后重启 PCL,才能恢复音乐播放功能!", HintType.Critical)
|
||||
Hint("你的音频设备正被其他程序占用。请在关闭占用的程序后重启 PCL,才能恢复音乐播放功能!", HintType.Red)
|
||||
Thread.Sleep(1000000000)
|
||||
End If
|
||||
If TypeOf ex Is NAudio.MmException AndAlso (ex.Message.Contains("NoDriver") OrElse ex.Message.Contains("BadDeviceId")) Then
|
||||
Hint("由于音频设备变更,音乐播放功能在重启 PCL 后才能恢复!", HintType.Critical)
|
||||
Hint("由于音频设备变更,音乐播放功能在重启 PCL 后才能恢复!", HintType.Red)
|
||||
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)
|
||||
Hint("播放音乐失败(" & GetFileNameFromPath(MusicCurrent) & "):PCL 不支持播放音频属性在中途发生变化的音乐", HintType.Red)
|
||||
ElseIf Not (MusicCurrent.EndsWithF(".wav", True) OrElse MusicCurrent.EndsWithF(".mp3", True) OrElse MusicCurrent.EndsWithF(".flac", True)) OrElse
|
||||
ex.Message.Contains("0xC00D36C4") Then '#5096:不支持给定的 URL 的字节流类型。 (异常来自 HRESULT:0xC00D36C4)
|
||||
Hint("播放音乐失败(" & GetFileNameFromPath(MusicCurrent) & "):PCL 可能不支持此音乐格式,请将格式转换为 .wav、.mp3 或 .flac 后再试", HintType.Critical)
|
||||
Hint("播放音乐失败(" & GetFileNameFromPath(MusicCurrent) & "):PCL 可能不支持此音乐格式,请将格式转换为 .wav、.mp3 或 .flac 后再试", HintType.Red)
|
||||
Else
|
||||
Log(ex, "播放音乐失败(" & GetFileNameFromPath(MusicCurrent) & ")", LogLevel.Hint)
|
||||
End If
|
||||
|
||||
@@ -6,12 +6,16 @@ Friend Module ModSecret
|
||||
|
||||
#Region "杂项"
|
||||
|
||||
'标注 PCL 的不同分支,仅用于替换标记
|
||||
Public Const VersionBranchMain As String = "OpenSource"
|
||||
'在开源版的注册表与常规版的注册表隔离,以防数据冲突
|
||||
Public Const RegFolder As String = "PCLDebug"
|
||||
'用于微软登录的 ClientId
|
||||
Public Const OAuthClientId As String = ""
|
||||
Public OAuthClientId As String = If(Environment.GetEnvironmentVariable("PCL_MS_CLIENT_ID"), "")
|
||||
'CurseForge API Key
|
||||
Public Const CurseForgeAPIKey As String = ""
|
||||
Public CurseForgeAPIKey As String = If(Environment.GetEnvironmentVariable("PCL_CURSEFORGE_API_KEY"), "")
|
||||
'用于匿名数据收集的腾讯云日志服务上报 URL,形如 https://{region}.cls.tencentcs.com/track?topic_id={topic_id}
|
||||
Public Const ClsBaseUrl As String = ""
|
||||
|
||||
Friend Sub SecretOnApplicationStart()
|
||||
'提升 UI 线程优先级
|
||||
@@ -54,13 +58,13 @@ Friend Module ModSecret
|
||||
''' <summary>
|
||||
''' 获取设备标识码。
|
||||
''' </summary>
|
||||
Friend Function SecretGetUniqueAddress() As String
|
||||
Friend Function SecretGetIdentify() As String
|
||||
Return "0000-0000-0000-0000"
|
||||
End Function
|
||||
|
||||
Friend Sub SecretLaunchJvmArgs(ByRef DataList As List(Of String))
|
||||
Dim DataJvmCustom As String = Setup.Get("VersionAdvanceJvm", Version:=McVersionCurrent)
|
||||
DataList.Insert(0, If(DataJvmCustom = "", Setup.Get("LaunchAdvanceJvm"), DataJvmCustom)) '可变 JVM 参数
|
||||
DataList.Insert(0, ArgumentReplace(If(DataJvmCustom = "", Setup.Get("LaunchAdvanceJvm"), DataJvmCustom))) '可变 JVM 参数
|
||||
McLaunchLog("当前剩余内存:" & Math.Round(My.Computer.Info.AvailablePhysicalMemory / 1024 / 1024 / 1024 * 10) / 10 & "G")
|
||||
DataList.Add("-Xmn" & Math.Floor(PageVersionSetup.GetRam(McVersionCurrent) * 1024 * 0.15) & "m")
|
||||
DataList.Add("-Xmx" & Math.Floor(PageVersionSetup.GetRam(McVersionCurrent) * 1024) & "m")
|
||||
@@ -71,37 +75,25 @@ Friend Module ModSecret
|
||||
|
||||
#Region "网络鉴权"
|
||||
|
||||
Friend Function SecretCdnSign(UrlWithMark As String)
|
||||
Friend Function SecretCdnSign(UrlWithMark As String) As String
|
||||
If Not UrlWithMark.EndsWithF("{CDN}") Then Return UrlWithMark
|
||||
Return UrlWithMark.Replace("{CDN}", "").Replace(" ", "%20")
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 设置 Headers 的 UA、Referer。
|
||||
''' </summary>
|
||||
Friend Sub SecretHeadersSign(Url As String, ByRef Client As WebClient, Optional UseBrowserUserAgent As Boolean = False)
|
||||
If Url.Contains("baidupcs.com") OrElse Url.Contains("baidu.com") Then
|
||||
Client.Headers("User-Agent") = "LogStatistic" '#4951
|
||||
ElseIf UseBrowserUserAgent Then
|
||||
Client.Headers("User-Agent") = "PCL2/" & VersionStandardCode & " Mozilla/5.0 AppleWebKit/537.36 Chrome/63.0.3239.132 Safari/537.36"
|
||||
Else
|
||||
Client.Headers("User-Agent") = "PCL2/" & VersionStandardCode
|
||||
Friend Sub SecretHeadersSign(Url As String, ByRef Req As HttpRequestMessage, Optional SimulateBrowserHeaders As Boolean = False)
|
||||
If Not Req.Headers.UserAgent.Any Then
|
||||
If Url.Contains("baidupcs.com") OrElse Url.Contains("baidu.com") Then
|
||||
Req.Headers.Add("User-Agent", "LogStatistic") '#4951
|
||||
ElseIf SimulateBrowserHeaders Then
|
||||
Req.Headers.Add("User-Agent", $"PCL2/{VersionStandardCode} Mozilla/5.0 AppleWebKit/537.36 Chrome/63.0.3239.132 Safari/537.36")
|
||||
Else
|
||||
Req.Headers.Add("User-Agent", $"PCL2/{VersionStandardCode}")
|
||||
End If
|
||||
End If
|
||||
Client.Headers("Referer") = "http://" & VersionCode & ".open.pcl2.server/"
|
||||
If Url.Contains("api.curseforge.com") Then Client.Headers("x-api-key") = CurseForgeAPIKey
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 设置 Headers 的 UA、Referer。
|
||||
''' </summary>
|
||||
Friend Sub SecretHeadersSign(Url As String, ByRef Request As HttpWebRequest, Optional UseBrowserUserAgent As Boolean = False)
|
||||
If Url.Contains("baidupcs.com") OrElse Url.Contains("baidu.com") Then
|
||||
Request.UserAgent = "LogStatistic" '#4951
|
||||
ElseIf UseBrowserUserAgent Then
|
||||
Request.UserAgent = "PCL2/" & VersionStandardCode & " Mozilla/5.0 AppleWebKit/537.36 Chrome/63.0.3239.132 Safari/537.36"
|
||||
Else
|
||||
Request.UserAgent = "PCL2/" & VersionStandardCode
|
||||
End If
|
||||
Request.Referer = "http://" & VersionCode & ".open.pcl2.server/"
|
||||
If Url.Contains("api.curseforge.com") Then Request.Headers("x-api-key") = CurseForgeAPIKey
|
||||
If Not SimulateBrowserHeaders Then Req.Headers.Add("Referer", $"http://{VersionCode}.open.pcl2.server/")
|
||||
If Url.Contains("api.curseforge.com") Then Req.Headers.Add("x-api-key", CurseForgeAPIKey)
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
@@ -225,11 +217,17 @@ Friend Module ModSecret
|
||||
If Not FrmMain.IsLoaded Then Return
|
||||
'顶部条背景
|
||||
Dim Brush = New LinearGradientBrush With {.EndPoint = New Point(1, 0), .StartPoint = New Point(0, 0)}
|
||||
Brush.GradientStops.Add(New GradientStop With {.Offset = 0, .Color = New MyColor().FromHSL2(ColorHue - 21, ColorSat, 53 + ColorLightAdjust)})
|
||||
Brush.GradientStops.Add(New GradientStop With {.Offset = 0.33, .Color = New MyColor().FromHSL2(ColorHue - 7, ColorSat, 47 + ColorLightAdjust)})
|
||||
Brush.GradientStops.Add(New GradientStop With {.Offset = 0.67, .Color = New MyColor().FromHSL2(ColorHue + 7, ColorSat, 47 + ColorLightAdjust)})
|
||||
Brush.GradientStops.Add(New GradientStop With {.Offset = 1, .Color = New MyColor().FromHSL2(ColorHue + 21, ColorSat, 53 + ColorLightAdjust)})
|
||||
If TypeOf ColorHueTopbarDelta Is Integer Then
|
||||
Brush.GradientStops.Add(New GradientStop With {.Offset = 0, .Color = New MyColor().FromHSL2(ColorHue - ColorHueTopbarDelta, ColorSat, 48 + ColorLightAdjust)})
|
||||
Brush.GradientStops.Add(New GradientStop With {.Offset = 0.5, .Color = New MyColor().FromHSL2(ColorHue, ColorSat, 54 + ColorLightAdjust)})
|
||||
Brush.GradientStops.Add(New GradientStop With {.Offset = 1, .Color = New MyColor().FromHSL2(ColorHue + ColorHueTopbarDelta, ColorSat, 48 + ColorLightAdjust)})
|
||||
Else
|
||||
Brush.GradientStops.Add(New GradientStop With {.Offset = 0, .Color = New MyColor().FromHSL2(ColorHue + ColorHueTopbarDelta(0), ColorSat, 48 + ColorLightAdjust)})
|
||||
Brush.GradientStops.Add(New GradientStop With {.Offset = 0.5, .Color = New MyColor().FromHSL2(ColorHue + ColorHueTopbarDelta(1), ColorSat, 54 + ColorLightAdjust)})
|
||||
Brush.GradientStops.Add(New GradientStop With {.Offset = 1, .Color = New MyColor().FromHSL2(ColorHue + ColorHueTopbarDelta(2), ColorSat, 48 + ColorLightAdjust)})
|
||||
End If
|
||||
FrmMain.PanTitle.Background = Brush
|
||||
FrmMain.PanTitle.Background.Freeze()
|
||||
'主页面背景
|
||||
If Setup.Get("UiBackgroundColorful") Then
|
||||
Brush = New LinearGradientBrush With {.EndPoint = New Point(0.1, 1), .StartPoint = New Point(0.9, 0)}
|
||||
@@ -283,9 +281,16 @@ Friend Module ModSecret
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "联网通知"
|
||||
#Region "联网配置"
|
||||
|
||||
Public ServerLoader As New LoaderTask(Of Integer, Integer)("PCL 服务", Sub() Log("[Server] 该版本中不包含更新通知功能……"), Priority:=ThreadPriority.BelowNormal)
|
||||
''' <summary>
|
||||
''' 联网获取的配置信息。
|
||||
''' 若获取失败或仍在获取中,可能为 Nothing。
|
||||
''' </summary>
|
||||
Public ServerConfig As JObject
|
||||
|
||||
Public ServerLoader As New LoaderTask(Of Integer, Integer)("PCL 配置更新", Sub() Log("[Server] 该版本中不包含更新通知功能……"), Priority:=ThreadPriority.BelowNormal) With
|
||||
{.ReloadTimeout = 1000 * 60 * 60} '超时 1 小时
|
||||
|
||||
#End Region
|
||||
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
Imports System.Net
|
||||
|
||||
Public Class CookieWebClient
|
||||
Inherits WebClient
|
||||
|
||||
Public Sub New(container As CookieContainer, Headers As Dictionary(Of String, String))
|
||||
Me.New(container)
|
||||
For Each keyVal In Headers
|
||||
Me.Headers(keyVal.Key) = keyVal.Value
|
||||
Next
|
||||
End Sub
|
||||
|
||||
Public Sub New()
|
||||
Me.New(New CookieContainer())
|
||||
End Sub
|
||||
|
||||
Public Sub New(container As CookieContainer)
|
||||
Me.container = container
|
||||
End Sub
|
||||
|
||||
Private Shadows ReadOnly container As New CookieContainer()
|
||||
|
||||
''' <summary>
|
||||
''' 以毫秒为单位的超时。
|
||||
''' </summary>
|
||||
Public Timeout As Integer = 600000
|
||||
|
||||
Protected Overrides Function GetWebRequest(address As Uri) As WebRequest
|
||||
Dim r As WebRequest = MyBase.GetWebRequest(address)
|
||||
Dim request = TryCast(r, HttpWebRequest)
|
||||
|
||||
If request IsNot Nothing Then
|
||||
request.CookieContainer = container
|
||||
request.Timeout = Timeout
|
||||
End If
|
||||
|
||||
Return r
|
||||
End Function
|
||||
|
||||
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(request As WebRequest) As WebResponse
|
||||
Dim response As WebResponse = MyBase.GetWebResponse(request)
|
||||
ReadCookies(response)
|
||||
Return response
|
||||
End Function
|
||||
|
||||
Private Sub ReadCookies(r As WebResponse)
|
||||
Dim response = TryCast(r, HttpWebResponse)
|
||||
If response IsNot Nothing Then
|
||||
Dim cookies As CookieCollection = response.Cookies
|
||||
container.Add(cookies)
|
||||
End If
|
||||
End Sub
|
||||
End Class
|
||||
@@ -0,0 +1,151 @@
|
||||
'来源:https://blog.csdn.net/simpleman2000/article/details/140952294
|
||||
|
||||
Imports System.ComponentModel
|
||||
Imports System.Runtime.InteropServices
|
||||
Imports System.Windows.Interop
|
||||
|
||||
Public Class DragHelper
|
||||
|
||||
Public Event DragDrop As EventHandler
|
||||
|
||||
Public Property DropFilePaths As String()
|
||||
Get
|
||||
Return _DropFilePathsBackingField
|
||||
End Get
|
||||
Private Set(value As String())
|
||||
_DropFilePathsBackingField = value
|
||||
End Set
|
||||
End Property
|
||||
Private _DropFilePathsBackingField As String()
|
||||
|
||||
Public Property DropPoint As POINT
|
||||
Get
|
||||
Return _DropPointBackingField
|
||||
End Get
|
||||
Private Set(value As POINT)
|
||||
_DropPointBackingField = value
|
||||
End Set
|
||||
End Property
|
||||
Private _DropPointBackingField As POINT
|
||||
|
||||
Public Property HwndIntPtrSource As HwndSource
|
||||
|
||||
Public Sub AddHook()
|
||||
RemoveDragHook()
|
||||
HwndIntPtrSource.AddHook(AddressOf WndProc)
|
||||
Dim handle As IntPtr = HwndIntPtrSource.Handle
|
||||
If IsUserAnAdmin() Then RevokeDragDrop(handle)
|
||||
DragAcceptFiles(handle, True)
|
||||
ChangeMessageFilter(handle)
|
||||
End Sub
|
||||
|
||||
Public Sub RemoveDragHook()
|
||||
HwndIntPtrSource.RemoveHook(AddressOf WndProc)
|
||||
DragAcceptFiles(HwndIntPtrSource.Handle, False)
|
||||
End Sub
|
||||
|
||||
Private Function WndProc(hwnd As IntPtr, msg As Integer, wParam As IntPtr, lParam As IntPtr, ByRef handled As Boolean) As IntPtr
|
||||
Dim filePaths As String() = Nothing
|
||||
Dim point As New POINT()
|
||||
|
||||
If TryGetDropInfo(msg, wParam, filePaths, point) Then
|
||||
DropPoint = point
|
||||
DropFilePaths = filePaths
|
||||
RaiseEvent DragDrop(Me, EventArgs.Empty)
|
||||
handled = True
|
||||
End If
|
||||
Return IntPtr.Zero
|
||||
End Function
|
||||
|
||||
<DllImport("user32.dll", SetLastError:=True)>
|
||||
Private Shared Function ChangeWindowMessageFilterEx(hWnd As IntPtr, msg As UInteger, action As UInteger, ByRef pChangeFilterStruct As CHANGEFILTERSTRUCT) As Boolean
|
||||
End Function
|
||||
|
||||
<DllImport("user32.dll", SetLastError:=True)>
|
||||
Private Shared Function ChangeWindowMessageFilter(msg As UInteger, flags As UInteger) As Boolean
|
||||
End Function
|
||||
|
||||
<DllImport("shell32.dll")>
|
||||
Private Shared Sub DragAcceptFiles(hWnd As IntPtr, fAccept As Boolean)
|
||||
End Sub
|
||||
|
||||
<DllImport("shell32.dll", CharSet:=CharSet.Unicode)>
|
||||
Private Shared Function DragQueryFile(hWnd As IntPtr, iFile As UInteger, lpszFile As StringBuilder, cch As Integer) As UInteger
|
||||
End Function
|
||||
|
||||
<DllImport("shell32.dll")>
|
||||
Private Shared Function DragQueryPoint(hDrop As IntPtr, ByRef lppt As POINT) As Boolean
|
||||
End Function
|
||||
|
||||
<DllImport("shell32.dll")>
|
||||
Private Shared Sub DragFinish(hDrop As IntPtr)
|
||||
End Sub
|
||||
|
||||
<DllImport("ole32.dll")>
|
||||
Private Shared Function RevokeDragDrop(hWnd As IntPtr) As Integer
|
||||
End Function
|
||||
|
||||
<DllImport("shell32.dll")>
|
||||
Private Shared Function IsUserAnAdmin() As Boolean
|
||||
End Function
|
||||
|
||||
<StructLayout(LayoutKind.Sequential)>
|
||||
Public Structure POINT
|
||||
Public X As Integer
|
||||
Public Y As Integer
|
||||
End Structure
|
||||
|
||||
<StructLayout(LayoutKind.Sequential)>
|
||||
Private Structure CHANGEFILTERSTRUCT
|
||||
Public cbSize As UInteger
|
||||
Public ExtStatus As UInteger
|
||||
End Structure
|
||||
|
||||
Private Const WM_COPYGLOBALDATA As UInteger = &H49
|
||||
Private Const WM_COPYDATA As UInteger = &H4A
|
||||
Private Const WM_DROPFILES As UInteger = &H233
|
||||
Private Const MSGFLT_ALLOW As UInteger = 1
|
||||
Private Const MSGFLT_ADD As UInteger = 1
|
||||
Private Const MAX_PATH As Integer = 260
|
||||
|
||||
Private Shared Sub ChangeMessageFilter(handle As IntPtr)
|
||||
Dim ver As Version = Environment.OSVersion.Version
|
||||
Dim isVistaOrHigher As Boolean = ver >= New Version(6, 0)
|
||||
Dim isNt61OrHiger As Boolean = ver >= New Version(6, 1)
|
||||
|
||||
If isVistaOrHigher Then
|
||||
Dim status As New CHANGEFILTERSTRUCT With {.cbSize = 8}
|
||||
For Each msg As UInteger In New UInteger() {WM_DROPFILES, WM_COPYGLOBALDATA, WM_COPYDATA}
|
||||
Dim [error] As Boolean = False
|
||||
If isNt61OrHiger Then
|
||||
[error] = Not ChangeWindowMessageFilterEx(handle, msg, MSGFLT_ALLOW, status)
|
||||
Else
|
||||
[error] = Not ChangeWindowMessageFilter(msg, MSGFLT_ADD)
|
||||
End If
|
||||
|
||||
If [error] Then Throw New Win32Exception(Marshal.GetLastWin32Error())
|
||||
Next
|
||||
End If
|
||||
End Sub
|
||||
|
||||
Private Shared Function TryGetDropInfo(msg As Integer, wParam As IntPtr, ByRef dropFilePaths As String(), ByRef dropPoint As POINT) As Boolean
|
||||
dropFilePaths = Nothing
|
||||
dropPoint = New POINT()
|
||||
|
||||
If msg <> WM_DROPFILES Then Return False
|
||||
|
||||
Dim fileCount As UInteger = DragQueryFile(wParam, UInteger.MaxValue, Nothing, 0)
|
||||
ReDim dropFilePaths(CInt(fileCount) - 1)
|
||||
|
||||
For i As UInteger = 0 To CInt(fileCount) - 1
|
||||
Dim sb As New StringBuilder(MAX_PATH)
|
||||
Dim result As UInteger = DragQueryFile(wParam, i, sb, sb.Capacity)
|
||||
If result > 0 Then dropFilePaths(i) = sb.ToString()
|
||||
Next
|
||||
|
||||
DragQueryPoint(wParam, dropPoint)
|
||||
DragFinish(wParam)
|
||||
Return True
|
||||
End Function
|
||||
|
||||
End Class
|
||||
@@ -51,6 +51,6 @@ Imports System.Runtime.InteropServices
|
||||
' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
|
||||
' 方法是按如下所示使用“*”
|
||||
|
||||
<Assembly: AssemblyVersion("2.10.2.0")>
|
||||
<Assembly: AssemblyFileVersion("2.10.2.0")>
|
||||
<Assembly: AssemblyVersion("2.11.1.0")>
|
||||
<Assembly: AssemblyFileVersion("2.11.1.0")>
|
||||
<Assembly: NeutralResourcesLanguage("")>
|
||||
+55
-15
@@ -63,9 +63,9 @@ Namespace My.Resources
|
||||
'''<summary>
|
||||
''' 查找 System.Byte[] 类型的本地化资源。
|
||||
'''</summary>
|
||||
Friend ReadOnly Property Custom() As Byte()
|
||||
Friend ReadOnly Property CacheCow_Client() As Byte()
|
||||
Get
|
||||
Dim obj As Object = ResourceManager.GetObject("Custom", resourceCulture)
|
||||
Dim obj As Object = ResourceManager.GetObject("CacheCow.Client", resourceCulture)
|
||||
Return CType(obj,Byte())
|
||||
End Get
|
||||
End Property
|
||||
@@ -73,9 +73,29 @@ Namespace My.Resources
|
||||
'''<summary>
|
||||
''' 查找 System.Byte[] 类型的本地化资源。
|
||||
'''</summary>
|
||||
Friend ReadOnly Property Dialogs() As Byte()
|
||||
Friend ReadOnly Property CacheCow_Client_FileStore() As Byte()
|
||||
Get
|
||||
Dim obj As Object = ResourceManager.GetObject("Dialogs", resourceCulture)
|
||||
Dim obj As Object = ResourceManager.GetObject("CacheCow.Client.FileStore", resourceCulture)
|
||||
Return CType(obj,Byte())
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 查找 System.Byte[] 类型的本地化资源。
|
||||
'''</summary>
|
||||
Friend ReadOnly Property CacheCow_Common() As Byte()
|
||||
Get
|
||||
Dim obj As Object = ResourceManager.GetObject("CacheCow.Common", resourceCulture)
|
||||
Return CType(obj,Byte())
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 查找 System.Byte[] 类型的本地化资源。
|
||||
'''</summary>
|
||||
Friend ReadOnly Property Custom() As Byte()
|
||||
Get
|
||||
Dim obj As Object = ResourceManager.GetObject("Custom", resourceCulture)
|
||||
Return CType(obj,Byte())
|
||||
End Get
|
||||
End Property
|
||||
@@ -105,7 +125,7 @@ Namespace My.Resources
|
||||
'''</summary>
|
||||
Friend ReadOnly Property Imazen_WebP() As Byte()
|
||||
Get
|
||||
Dim obj As Object = ResourceManager.GetObject("Imazen_WebP", resourceCulture)
|
||||
Dim obj As Object = ResourceManager.GetObject("Imazen.WebP", resourceCulture)
|
||||
Return CType(obj,Byte())
|
||||
End Get
|
||||
End Property
|
||||
@@ -120,16 +140,6 @@ Namespace My.Resources
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 查找 System.Byte[] 类型的本地化资源。
|
||||
'''</summary>
|
||||
Friend ReadOnly Property Json() As Byte()
|
||||
Get
|
||||
Dim obj As Object = ResourceManager.GetObject("Json", resourceCulture)
|
||||
Return CType(obj,Byte())
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 查找 System.Byte[] 类型的本地化资源。
|
||||
'''</summary>
|
||||
@@ -160,6 +170,36 @@ Namespace My.Resources
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 查找 System.Byte[] 类型的本地化资源。
|
||||
'''</summary>
|
||||
Friend ReadOnly Property Newtonsoft_Json() As Byte()
|
||||
Get
|
||||
Dim obj As Object = ResourceManager.GetObject("Newtonsoft.Json", resourceCulture)
|
||||
Return CType(obj,Byte())
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 查找 System.Byte[] 类型的本地化资源。
|
||||
'''</summary>
|
||||
Friend ReadOnly Property Ookii_Dialogs_Wpf() As Byte()
|
||||
Get
|
||||
Dim obj As Object = ResourceManager.GetObject("Ookii.Dialogs.Wpf", resourceCulture)
|
||||
Return CType(obj,Byte())
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 查找 System.Byte[] 类型的本地化资源。
|
||||
'''</summary>
|
||||
Friend ReadOnly Property System_Net_Http_Formatting() As Byte()
|
||||
Get
|
||||
Dim obj As Object = ResourceManager.GetObject("System.Net.Http.Formatting", resourceCulture)
|
||||
Return CType(obj,Byte())
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 查找 System.Byte[] 类型的本地化资源。
|
||||
'''</summary>
|
||||
|
||||
@@ -118,27 +118,30 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="CacheCow.Client" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\CacheCow.Client.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="CacheCow.Client.FileStore" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\CacheCow.Client.FileStore.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="CacheCow.Common" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\CacheCow.Common.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="Custom" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\resources\custom.xaml;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="Dialogs" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\resources\ookii.dialogs.wpf.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="ForgeInstaller" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\forge-installer.jar;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="Help" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Help.zip;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="Imazen_WebP" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<data name="Imazen.WebP" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Imazen.WebP.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="JavaWrapper" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\java-wrapper.jar;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="Json" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Newtonsoft.Json.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="libwebp64" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\libwebp64.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
@@ -148,6 +151,15 @@
|
||||
<data name="NAudio" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\NAudio.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="Newtonsoft.Json" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Newtonsoft.Json.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="Ookii.Dialogs.Wpf" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\resources\ookii.dialogs.wpf.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="System.Net.Http.Formatting" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\System.Net.Http.Formatting.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="Transformer" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\transformer.jar;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
Dim Titles As New List(Of String)
|
||||
If FrmMain.PageCurrent.Page = FormMain.PageType.CompDetail Then
|
||||
For Each Card As MyCard In FrmDownloadCompDetail.PanResults.Children
|
||||
If Card.Title <> "" AndAlso Not Card.IsSwaped Then Titles.Add(Card.Title)
|
||||
If Card.Title <> "" AndAlso Not Card.IsSwapped Then Titles.Add(Card.Title)
|
||||
Next
|
||||
Log("[Comp] 记录当前已展开的卡片:" & String.Join("、", Titles))
|
||||
FrmMain.PageCurrent.Additional(1) = Titles
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<TextBlock VerticalAlignment="Center" Grid.Row="2" HorizontalAlignment="Left" Text="版本" Margin="0,0,18,0" />
|
||||
<local:MyComboBox x:Name="TextSearchVersion" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" IsEditable="True" MaxDropDownHeight="320">
|
||||
<local:MyComboBoxItem Content="全部 (也可自行输入)" IsSelected="True" />
|
||||
<local:MyComboBoxItem Content="1.21.5" />
|
||||
<local:MyComboBoxItem Content="1.21.10" />
|
||||
<local:MyComboBoxItem Content="1.21.1" />
|
||||
<local:MyComboBoxItem Content="1.20.6" />
|
||||
<local:MyComboBoxItem Content="1.20.1" />
|
||||
@@ -71,7 +71,7 @@
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
<StackPanel x:Name="PanContent">
|
||||
<local:MyHint x:Name="HintError" HasBorder="True" Text="无法连接到 Modrinth,所以目前仅显示了来自 CurseForge 的内容,搜索结果可能不全。
请稍后重试,或使用 VPN 以改善网络环境。" Theme="Yellow" Margin="0,0,0,15" />
|
||||
<local:MyHint x:Name="HintError" HasBorder="True" Text="无法连接到 Modrinth,所以目前仅显示了来自 CurseForge 的内容,搜索结果可能不全。
请稍后再试,或使用 VPN 改善网络环境。" Theme="Yellow" Margin="0,0,0,15" />
|
||||
<local:MyCard x:Name="CardProjects" UseAnimation="False">
|
||||
<StackPanel x:Name="PanProjects" Margin="12" />
|
||||
</local:MyCard>
|
||||
|
||||
@@ -68,6 +68,10 @@ Public Class PageComp
|
||||
''' 在切换到页面时,应自动将筛选项设置为与该目标 MC 版本和加载器相同。
|
||||
''' </summary>
|
||||
Public Shared TargetVersion As McVersion = Nothing
|
||||
''' <summary>
|
||||
''' 在切换到该页面时自动设置的搜索框内的内容。
|
||||
''' </summary>
|
||||
Public Shared TargetName As String = Nothing
|
||||
|
||||
'在点击 MyCompItem 时会获取 Loader 的输入,以使资源详情页面可以应用相同的筛选项
|
||||
Public Loader As New LoaderTask(Of CompProjectRequest, Integer)("社区资源获取:XXX", AddressOf CompProjectsGet, AddressOf LoaderInput) With {.ReloadTimeout = 60 * 1000}
|
||||
@@ -94,6 +98,10 @@ Public Class PageComp
|
||||
ComboSearchLoader.SelectedItem = GetTargetItemByName("NeoForge")
|
||||
End If
|
||||
TargetVersion = Nothing
|
||||
If TargetName IsNot Nothing Then
|
||||
TextSearchName.Text = TargetName
|
||||
TargetName = Nothing
|
||||
End If
|
||||
'如果已经完成请求,则重新开始
|
||||
If IsLoaderInited Then StartNewSearch()
|
||||
ScrollToHome()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
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}">
|
||||
PanScroll="PanBack">
|
||||
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
|
||||
<Grid Margin="25,25,25,10">
|
||||
<Grid.RowDefinitions>
|
||||
|
||||
@@ -73,11 +73,14 @@
|
||||
'筛选类型相同的结果(Modrinth 会返回 Mod、服务端插件、数据包混合的列表)
|
||||
Private Function GetResults() As List(Of CompFile)
|
||||
Dim Results As List(Of CompFile) = CompFileLoader.Output
|
||||
If PageType = CompType.Any Then
|
||||
Results = Results.Where(Function(r) r.Type <> CompType.Plugin).ToList
|
||||
Else
|
||||
Results = Results.Where(Function(r) r.Type = PageType).ToList
|
||||
End If
|
||||
Select Case PageType
|
||||
Case CompType.Any
|
||||
Results = Results.Where(Function(r) r.Type <> CompType.Plugin).ToList
|
||||
Case CompType.Shader, CompType.ResourcePack
|
||||
'不筛选光影和资源包,否则原版光影会因为是资源包格式而被过滤(#6473)
|
||||
Case Else
|
||||
Results = Results.Where(Function(r) r.Type = PageType).ToList
|
||||
End Select
|
||||
Return Results
|
||||
End Function
|
||||
Private Sub Load_OnFinish()
|
||||
@@ -202,7 +205,7 @@
|
||||
CType(FrmMain.PageCurrent.Additional(1), List(Of String)).Contains(NewCard.Title)) Then
|
||||
MyCard.StackInstall(NewStack, If(PageType = CompType.ModPack, 9, 8), Pair.Key) '9 是安装,8 是另存为
|
||||
Else
|
||||
NewCard.IsSwaped = True
|
||||
NewCard.IsSwapped = True
|
||||
End If
|
||||
'增加提示
|
||||
If Pair.Key = "其他版本" Then
|
||||
@@ -211,7 +214,7 @@
|
||||
Next
|
||||
'如果只有一张卡片,展开第一张卡片
|
||||
If PanResults.Children.Count = 1 Then
|
||||
CType(PanResults.Children(0), MyCard).IsSwaped = False
|
||||
CType(PanResults.Children(0), MyCard).IsSwapped = False
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "可视化工程下载列表出错", LogLevel.Feedback)
|
||||
@@ -287,9 +290,9 @@
|
||||
Sub(MyLoader)
|
||||
Select Case MyLoader.State
|
||||
Case LoadState.Failed
|
||||
Hint(MyLoader.Name & "失败:" & GetExceptionSummary(MyLoader.Error), HintType.Critical)
|
||||
Hint(MyLoader.Name & "失败:" & MyLoader.Error.GetBrief(), HintType.Red)
|
||||
Case LoadState.Aborted
|
||||
Hint(MyLoader.Name & "已取消!", HintType.Info)
|
||||
Hint(MyLoader.Name & "已取消!", HintType.Blue)
|
||||
Case LoadState.Loading
|
||||
Return '不重新加载版本列表
|
||||
End Select
|
||||
@@ -305,7 +308,7 @@
|
||||
End Try
|
||||
End Sub
|
||||
'资源下载;整合包另存为
|
||||
Public Shared CachedFolder As String = Nothing '仅在本次缓存的下载文件夹
|
||||
Public Shared CachedFolder As New Dictionary(Of CompType, String) '仅在本次缓存的下载文件夹
|
||||
Public Sub Save_Click(sender As Object, e As EventArgs)
|
||||
Dim File As CompFile = If(TypeOf sender Is MyListItem, sender, sender.Parent).Tag
|
||||
RunInNewThread(
|
||||
@@ -323,7 +326,7 @@
|
||||
Dim DefaultFolder As String = Nothing
|
||||
If File.Type <> CompType.ModPack Then
|
||||
Dim SubFolder As String = Nothing
|
||||
Select Case Project.Type
|
||||
Select Case File.Type
|
||||
Case CompType.Mod : SubFolder = "mods\"
|
||||
Case CompType.ResourcePack : SubFolder = "resourcepacks\"
|
||||
Case CompType.Shader : SubFolder = "shaderpacks\"
|
||||
@@ -343,7 +346,7 @@
|
||||
Function(Version)
|
||||
If Not Version.IsLoaded Then Version.Load()
|
||||
'只对 Mod 和数据包进行版本检测
|
||||
If Project.Type = CompType.Mod OrElse Project.Type = CompType.DataPack Then
|
||||
If File.Type = CompType.Mod OrElse File.Type = CompType.DataPack Then
|
||||
If File.GameVersions.Any(Function(v) v.Contains(".")) AndAlso
|
||||
Not File.GameVersions.Any(Function(v) v.Contains(".") AndAlso v = Version.Version.McName) Then Return False
|
||||
End If
|
||||
@@ -356,8 +359,8 @@
|
||||
Return False
|
||||
End Function
|
||||
'获取常规资源默认下载位置
|
||||
If CachedFolder IsNot Nothing Then
|
||||
DefaultFolder = CachedFolder
|
||||
If CachedFolder.ContainsKey(File.Type) Then
|
||||
DefaultFolder = CachedFolder(File.Type)
|
||||
Log($"[Comp] 使用上次下载时的文件夹作为默认下载位置:{DefaultFolder}")
|
||||
ElseIf McVersionCurrent IsNot Nothing AndAlso IsVersionSuitable(McVersionCurrent) Then
|
||||
DefaultFolder = $"{McVersionCurrent.PathIndie}{SubFolder}"
|
||||
@@ -421,8 +424,8 @@
|
||||
If(File.FileName.EndsWith(".mrpack"), "*.mrpack", "*.zip")), DefaultFolder)
|
||||
If Not Target.Contains("\") Then Return
|
||||
'构造步骤加载器
|
||||
Dim LoaderName As String = Desc & "下载:" & GetFileNameWithoutExtentionFromPath(Target) & " "
|
||||
If Target <> DefaultFolder AndAlso File.Type = CompType.Mod Then CachedFolder = GetPathFromFullPath(Target)
|
||||
Dim LoaderName As String = Desc & "下载:" & IO.Path.GetFileNameWithoutExtension(Target) & " "
|
||||
If Target <> DefaultFolder Then CachedFolder(File.Type) = GetPathFromFullPath(Target)
|
||||
Dim Loaders As New List(Of LoaderBase)
|
||||
Loaders.Add(New LoaderDownload("下载文件", New List(Of NetFile) From {File.ToNetFile(Target)}) With {.ProgressWeight = 6, .Block = True})
|
||||
'启动
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
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="PageDownloadDataPack"
|
||||
PanScroll="{Binding ElementName=Content}">
|
||||
PanScroll="Content">
|
||||
<local:PageComp x:Name="Content" PageType="DataPack" TypeName="数据包" TypeNameSpaced="数据包">
|
||||
<local:MyComboBoxItem Tag="" Content="全部" IsSelected="True" />
|
||||
<local:MyComboBoxItem Content="世界元素" Tag="/worldgen" />
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
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=Content}">
|
||||
PanScroll="Content">
|
||||
<local:PageComp x:Name="Content" PageType="Mod" TypeName="Mod" TypeNameSpaced=" Mod ">
|
||||
<local:MyComboBoxItem Content="全部" Tag="" IsSelected="True" />
|
||||
<local:MyComboBoxItem Content="世界元素" Tag="406/worldgen" />
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
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=Content}">
|
||||
PanScroll="Content">
|
||||
<local:PageComp x:Name="Content" PageType="ModPack" TypeName="整合包" TypeNameSpaced="整合包">
|
||||
<local:MyComboBoxItem Tag="" Content="全部" IsSelected="True" />
|
||||
<local:MyComboBoxItem Tag="4484/" Content="多人" />
|
||||
@@ -17,7 +17,7 @@
|
||||
<local:MyComboBoxItem Tag="/kitchen-sink" Content="水槽包" />
|
||||
<local:MyComboBoxItem Tag="4476/" Content="探索" />
|
||||
<local:MyComboBoxItem Tag="4477/" Content="小游戏" />
|
||||
<local:MyComboBoxItem Tag="4471/" Content="科幻" />
|
||||
<local:MyComboBoxItem Tag="4474/" Content="科幻" />
|
||||
<local:MyComboBoxItem Tag="4736/" Content="空岛" />
|
||||
<local:MyComboBoxItem Tag="5128/" Content="原版改良" />
|
||||
<local:MyComboBoxItem Tag="4487/" Content="FTB" ToolTip="由 FTB 官方发布" ToolTipService.HorizontalOffset="70" />
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
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="PageDownloadResourcePack"
|
||||
PanScroll="{Binding ElementName=Content}">
|
||||
PanScroll="Content">
|
||||
<local:PageComp x:Name="Content" PageType="ResourcePack" TypeName="资源包" TypeNameSpaced="资源包">
|
||||
<local:MyComboBoxItem Tag="" Content="全部" IsSelected="True" />
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
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="PageDownloadShader"
|
||||
PanScroll="{Binding ElementName=Content}">
|
||||
PanScroll="Content">
|
||||
<local:PageComp x:Name="Content" PageType="Shader" TypeName="光影包" TypeNameSpaced="光影包">
|
||||
<local:MyComboBoxItem Tag="" Content="全部" IsSelected="True" />
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
Imports System.IO.Compression
|
||||
Imports System.Net.Http
|
||||
|
||||
Public Module ModDownloadLib
|
||||
|
||||
@@ -32,7 +31,7 @@ Public Module ModDownloadLib
|
||||
For Each OngoingLoader In LoaderTaskbar.ToList()
|
||||
If OngoingLoader.Name <> $"Minecraft {Id} 下载" Then Continue For
|
||||
If Behaviour = NetPreDownloadBehaviour.ExitWhileExistsOrDownloading Then Return OngoingLoader
|
||||
Hint("该版本正在下载中!", HintType.Critical)
|
||||
Hint("该版本正在下载中!", HintType.Red)
|
||||
Return OngoingLoader
|
||||
Next
|
||||
|
||||
@@ -100,7 +99,8 @@ Public Module ModDownloadLib
|
||||
Sub(Task As LoaderTask(Of String, List(Of NetFile)))
|
||||
Try
|
||||
Dim Version As New McVersion(VersionFolder)
|
||||
Task.Output = New List(Of NetFile) From {DlClientAssetIndexGet(Version)}
|
||||
Dim AssetIndex = DlClientAssetIndexGet(Version)
|
||||
Task.Output = If(AssetIndex Is Nothing, New List(Of NetFile), New List(Of NetFile) From {AssetIndex})
|
||||
Catch ex As Exception
|
||||
Throw New Exception("分析资源文件索引地址失败", ex)
|
||||
End Try
|
||||
@@ -223,7 +223,7 @@ Public Module ModDownloadLib
|
||||
'重复任务检查
|
||||
For Each OngoingLoader In LoaderTaskbar.ToList()
|
||||
If OngoingLoader.Name <> $"Minecraft {Id} 服务端下载" Then Continue For
|
||||
Hint("该服务端正在下载中!", HintType.Critical)
|
||||
Hint("该服务端正在下载中!", HintType.Red)
|
||||
Return
|
||||
Next
|
||||
|
||||
@@ -241,7 +241,7 @@ Public Module ModDownloadLib
|
||||
File.Delete(VersionFolder & Id & ".json")
|
||||
If Not New DirectoryInfo(VersionFolder).GetFileSystemInfos.Any() Then Directory.Delete(VersionFolder)
|
||||
Task.Output = New List(Of NetFile)
|
||||
Hint($"Mojang 没有给 Minecraft {Id} 提供官方服务端下载,没法下,撤退!", HintType.Critical)
|
||||
Hint($"Mojang 没有给 Minecraft {Id} 提供官方服务端下载,没法下,撤退!", HintType.Red)
|
||||
Thread.Sleep(2000) '等玩家把上一个提示看完
|
||||
Task.Abort()
|
||||
Return
|
||||
@@ -300,7 +300,7 @@ pause"
|
||||
'重复任务检查
|
||||
For Each OngoingLoader In LoaderTaskbar.ToList()
|
||||
If OngoingLoader.Name <> $"Minecraft {Id} 下载" Then Continue For
|
||||
Hint("该版本正在下载中!", HintType.Critical)
|
||||
Hint("该版本正在下载中!", HintType.Red)
|
||||
Return
|
||||
Next
|
||||
|
||||
@@ -401,7 +401,7 @@ pause"
|
||||
'重复任务检查
|
||||
For Each OngoingLoader In LoaderTaskbar.ToList()
|
||||
If OngoingLoader.Name <> $"OptiFine {DownloadInfo.NameDisplay} 下载" Then Continue For
|
||||
Hint("该版本正在下载中!", HintType.Critical)
|
||||
Hint("该版本正在下载中!", HintType.Red)
|
||||
Return
|
||||
Next
|
||||
|
||||
@@ -449,7 +449,7 @@ pause"
|
||||
'开始启动
|
||||
SyncLock InstallSyncLock
|
||||
Dim Info = New ProcessStartInfo With {
|
||||
.FileName = Java.PathJavaw,
|
||||
.FileName = Java.PathJava,
|
||||
.Arguments = Arguments,
|
||||
.UseShellExecute = False,
|
||||
.CreateNoWindow = True,
|
||||
@@ -570,7 +570,8 @@ pause"
|
||||
'官方源
|
||||
Dim PageData As String
|
||||
Try
|
||||
PageData = NetGetCodeByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, New UTF8Encoding(False), 15000, "text/html", True)
|
||||
PageData = NetRequestByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile,
|
||||
Encoding:=New UTF8Encoding(False), Timeout:=15000, Accept:="text/html", SimulateBrowserHeaders:=True)
|
||||
Task.Progress = 0.8
|
||||
Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0))
|
||||
Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources.Last)
|
||||
@@ -733,7 +734,8 @@ Retry:
|
||||
'官方源
|
||||
Dim PageData As String
|
||||
Try
|
||||
PageData = NetGetCodeByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, New UTF8Encoding(False), 15000, "text/html", True)
|
||||
PageData = NetRequestByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile,
|
||||
Encoding:=New UTF8Encoding(False), Timeout:=15000, Accept:="text/html", SimulateBrowserHeaders:=True)
|
||||
Task.Progress = 0.8
|
||||
Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0))
|
||||
Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources.Last)
|
||||
@@ -830,7 +832,7 @@ Retry:
|
||||
'重复任务检查
|
||||
For Each OngoingLoader In LoaderTaskbar.ToList()
|
||||
If OngoingLoader.Name <> $"LiteLoader {Id} 下载" Then Continue For
|
||||
Hint("该版本正在下载中!", HintType.Critical)
|
||||
Hint("该版本正在下载中!", HintType.Red)
|
||||
Return
|
||||
Next
|
||||
|
||||
@@ -864,7 +866,7 @@ Retry:
|
||||
'重复任务检查
|
||||
For Each OngoingLoader In LoaderTaskbar.ToList()
|
||||
If OngoingLoader.Name <> $"LiteLoader {Id} 下载" Then Continue For
|
||||
Hint("该版本正在下载中!", HintType.Critical)
|
||||
Hint("该版本正在下载中!", HintType.Red)
|
||||
Return
|
||||
Next
|
||||
|
||||
@@ -1046,7 +1048,7 @@ Retry:
|
||||
'重复任务检查
|
||||
For Each OngoingLoader In LoaderTaskbar.ToList()
|
||||
If OngoingLoader.Name <> $"{DisplayName} 下载" Then Continue For
|
||||
Hint("该版本正在下载中!", HintType.Critical)
|
||||
Hint("该版本正在下载中!", HintType.Red)
|
||||
Return
|
||||
Next
|
||||
|
||||
@@ -1118,7 +1120,7 @@ Retry:
|
||||
'开始启动
|
||||
SyncLock InstallSyncLock
|
||||
Dim Info = New ProcessStartInfo With {
|
||||
.FileName = Java.PathJavaw,
|
||||
.FileName = Java.PathJava,
|
||||
.Arguments = Arguments,
|
||||
.UseShellExecute = False,
|
||||
.CreateNoWindow = True,
|
||||
@@ -1340,10 +1342,10 @@ Retry:
|
||||
If Json("data") IsNot Nothing AndAlso Json("data")("MOJMAPS") IsNot Nothing Then
|
||||
'下载原版 Json 文件
|
||||
Task.Progress = 0.4
|
||||
Dim RawJson As JObject = GetJson(NetGetCodeByLoader(DlSourceLauncherOrMetaGet(DlClientListGet(Inherit)), IsJson:=True))
|
||||
Dim RawJson As JObject = GetJson(NetRequestByLoader(DlSourceLauncherOrMetaGet(DlClientListGet(Inherit)), IsJson:=True))
|
||||
'[net.minecraft:client:1.17.1-20210706.113038:mappings@txt] 或 @tsrg]
|
||||
Dim OriginalName As String = Json("data")("MOJMAPS")("client").ToString.Trim("[]".ToCharArray()).BeforeFirst("@")
|
||||
Dim Address = McLibGet(OriginalName).Replace(".jar", "-mappings." & Json("data")("MOJMAPS")("client").ToString.Trim("[]".ToCharArray()).Split("@")(1))
|
||||
Dim Address = McLibGet(OriginalName).Replace(".jar", "." & Json("data")("MOJMAPS")("client").ToString.Trim("[]".ToCharArray()).Split("@")(1))
|
||||
Dim ClientMappings As JToken = RawJson("downloads")("client_mappings")
|
||||
Libs.Add(New McLibToken With {
|
||||
.IsNatives = False, .LocalPath = Address, .OriginalName = OriginalName,
|
||||
@@ -1641,30 +1643,31 @@ Retry:
|
||||
Public Sub McDownloadForgeRecommendedRefresh()
|
||||
If IsForgeRecommendedRefreshed Then Return
|
||||
IsForgeRecommendedRefreshed = True
|
||||
RunInNewThread(Sub()
|
||||
Try
|
||||
Log("[Download] 刷新 Forge 推荐版本缓存开始")
|
||||
Dim Result As String = NetGetCodeByLoader("https://bmclapi2.bangbang93.com/forge/promos")
|
||||
If Result.Length < 1000 Then Throw New Exception("获取的结果过短(" & Result & ")")
|
||||
Dim ResultJson As JContainer = GetJson(Result)
|
||||
'获取所有推荐版本列表
|
||||
Dim RecommendedList As New List(Of String)
|
||||
For Each Version As JObject In ResultJson
|
||||
If Version("name") Is Nothing OrElse Version("build") Is Nothing Then Continue For
|
||||
Dim Name As String = Version("name")
|
||||
If Not Name.EndsWithF("-recommended") Then Continue For
|
||||
'内容为:"1.15.2":"31.2.0"
|
||||
RecommendedList.Add("""" & Name.Replace("-recommended", """:""" & Version("build")("version").ToString & """"))
|
||||
Next
|
||||
If RecommendedList.Count < 5 Then Throw New Exception("获取的推荐版本数过少(" & Result & ")")
|
||||
'保存
|
||||
Dim CacheJson As String = "{" & Join(RecommendedList, ",") & "}"
|
||||
WriteFile(PathTemp & "Cache\ForgeRecommendedList.json", CacheJson)
|
||||
Log("[Download] 刷新 Forge 推荐版本缓存成功")
|
||||
Catch ex As Exception
|
||||
Log(ex, "刷新 Forge 推荐版本缓存失败")
|
||||
End Try
|
||||
End Sub, "ForgeRecommendedRefresh")
|
||||
RunInNewThread(
|
||||
Sub()
|
||||
Try
|
||||
Log("[Download] 刷新 Forge 推荐版本缓存开始")
|
||||
Dim Result As String = NetRequestByClientRetry("https://bmclapi2.bangbang93.com/forge/promos", RequireJson:=True)
|
||||
If Result.Length < 1000 Then Throw New Exception("获取的结果过短(" & Result & ")")
|
||||
Dim ResultJson As JContainer = GetJson(Result)
|
||||
'获取所有推荐版本列表
|
||||
Dim RecommendedList As New List(Of String)
|
||||
For Each Version As JObject In ResultJson
|
||||
If Version("name") Is Nothing OrElse Version("build") Is Nothing Then Continue For
|
||||
Dim Name As String = Version("name")
|
||||
If Not Name.EndsWithF("-recommended") Then Continue For
|
||||
'内容为:"1.15.2":"31.2.0"
|
||||
RecommendedList.Add("""" & Name.Replace("-recommended", """:""" & Version("build")("version").ToString & """"))
|
||||
Next
|
||||
If RecommendedList.Count < 5 Then Throw New Exception("获取的推荐版本数过少(" & Result & ")")
|
||||
'保存
|
||||
Dim CacheJson As String = "{" & Join(RecommendedList, ",") & "}"
|
||||
WriteFile(PathTemp & "Cache\ForgeRecommendedList.json", CacheJson)
|
||||
Log("[Download] 刷新 Forge 推荐版本缓存成功")
|
||||
Catch ex As Exception
|
||||
Log(ex, "刷新 Forge 推荐版本缓存失败")
|
||||
End Try
|
||||
End Sub, "ForgeRecommendedRefresh")
|
||||
End Sub
|
||||
Private IsForgeRecommendedRefreshed As Boolean = False
|
||||
|
||||
@@ -1800,7 +1803,7 @@ Retry:
|
||||
'重复任务检查
|
||||
For Each OngoingLoader In LoaderTaskbar.ToList()
|
||||
If OngoingLoader.Name <> $"Fabric {Version} 安装器下载" Then Continue For
|
||||
Hint("该版本正在下载中!", HintType.Critical)
|
||||
Hint("该版本正在下载中!", HintType.Red)
|
||||
Return
|
||||
Next
|
||||
|
||||
@@ -1983,28 +1986,28 @@ Retry:
|
||||
''' 在加载器状态改变后显示一条提示。
|
||||
''' 不会进行任何其他操作。
|
||||
''' </summary>
|
||||
Public Sub LoaderStateChangedHintOnly(Loader)
|
||||
Public Sub LoaderStateChangedHintOnly(Loader As LoaderBase)
|
||||
Select Case Loader.State
|
||||
Case LoadState.Finished
|
||||
Hint(Loader.Name & "成功!", HintType.Finish)
|
||||
Hint(Loader.Name & "成功!", HintType.Green)
|
||||
Case LoadState.Failed
|
||||
Hint(Loader.Name & "失败:" & GetExceptionSummary(Loader.Error), HintType.Critical)
|
||||
Hint(Loader.Name & "失败:" & Loader.Error.GetBrief(), HintType.Red)
|
||||
Case LoadState.Aborted
|
||||
Hint(Loader.Name & "已取消!", HintType.Info)
|
||||
Hint(Loader.Name & "已取消!", HintType.Blue)
|
||||
End Select
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 安装加载器状态改变后进行提示和重载文件夹列表的方法。
|
||||
''' </summary>
|
||||
Public Sub McInstallState(Loader)
|
||||
Public Sub McInstallState(Loader As LoaderBase)
|
||||
Select Case Loader.State
|
||||
Case LoadState.Finished
|
||||
WriteIni(PathMcFolder & "PCL.ini", "VersionCache", "") '清空缓存(合并安装会先生成文件夹,这会在刷新时误判为可以使用缓存)
|
||||
Hint(Loader.Name & "成功!", HintType.Finish)
|
||||
Hint(Loader.Name & "成功!", HintType.Green)
|
||||
Case LoadState.Failed
|
||||
Hint(Loader.Name & "失败:" & GetExceptionSummary(Loader.Error), HintType.Critical)
|
||||
Hint(Loader.Name & "失败:" & Loader.Error.GetBrief(), HintType.Red)
|
||||
Case LoadState.Aborted
|
||||
Hint(Loader.Name & "已取消!", HintType.Info)
|
||||
Hint(Loader.Name & "已取消!", HintType.Blue)
|
||||
Case LoadState.Loading
|
||||
Return '不重新加载版本列表
|
||||
End Select
|
||||
@@ -2105,7 +2108,7 @@ Retry:
|
||||
|
||||
'重复版本检查
|
||||
If File.Exists($"{VersionFolder}{Request.TargetVersionName}.json") Then
|
||||
Hint("版本 " & Request.TargetVersionName & " 已经存在!", HintType.Critical)
|
||||
Hint("版本 " & Request.TargetVersionName & " 已经存在!", HintType.Red)
|
||||
Throw New CancelledException
|
||||
End If
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<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"
|
||||
PanScroll="{Binding ElementName=PanBack}">
|
||||
<Grid>
|
||||
<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="自动安装提示" x:Name="CardTip">
|
||||
<StackPanel Margin="25,40,15,20">
|
||||
<TextBlock Margin="0,0,0,2" Text="本页只提供需要在下载后手动进行安装的游戏 jar 与 json 下载。" TextWrapping="Wrap" />
|
||||
<TextBlock Margin="0,0,0,11" Text="如果你想要进行自动安装,在游戏安装页选择游戏版本,然后直接安装即可。" TextWrapping="Wrap" />
|
||||
<Grid Height="35">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<local:MyButton x:Name="BtnWeb" Grid.Column="0" MinWidth="140" Text="打开官网" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" />
|
||||
<local:MyButton x:Name="BtnInstall" Grid.Column="1" MinWidth="140" Text="转到游戏安装页" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</local:MyCard>-->
|
||||
<StackPanel Name="PanMain" Margin="20,20,20,5" Grid.IsSharedSizeScope="True" />
|
||||
</StackPanel>
|
||||
</local:MyScrollViewer>
|
||||
<local:MyCard HorizontalAlignment="Center" VerticalAlignment="Center" Margin="40,0" SnapsToDevicePixels="True" x:Name="PanLoad" UseAnimation="False">
|
||||
<local:MyLoading Text="正在获取版本列表" Margin="20,20,20,17" x:Name="Load" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</local:MyCard>
|
||||
</Grid>
|
||||
</local:MyPageRight>
|
||||
@@ -1,111 +0,0 @@
|
||||
Public Class PageDownloadClient
|
||||
|
||||
Private Sub LoaderInit() Handles Me.Initialized
|
||||
PageLoaderInit(Load, PanLoad, PanBack, Nothing, DlClientListLoader, AddressOf Load_OnFinish)
|
||||
End Sub
|
||||
Private Sub Init() Handles Me.Loaded
|
||||
PanBack.ScrollToHome()
|
||||
End Sub
|
||||
|
||||
Private Sub Load_OnFinish()
|
||||
'结果数据化
|
||||
Try
|
||||
'归类
|
||||
Dim Dict As New Dictionary(Of String, List(Of JObject)) From {
|
||||
{"正式版", New List(Of JObject)}, {"预览版", New List(Of JObject)}, {"远古版", New List(Of JObject)}, {"愚人节版", New List(Of JObject)}
|
||||
}
|
||||
Dim Versions As JArray = DlClientListLoader.Output.Value("versions")
|
||||
For Each Version As JObject In Versions
|
||||
'确定分类
|
||||
Dim Type As String = Version("type")
|
||||
Select Case Type
|
||||
Case "release"
|
||||
Type = "正式版"
|
||||
Case "snapshot"
|
||||
Type = "预览版"
|
||||
'Mojang 误分类
|
||||
If Version("id").ToString.StartsWith("1.") AndAlso
|
||||
Not Version("id").ToString.ToLower.Contains("combat") AndAlso
|
||||
Not Version("id").ToString.ToLower.Contains("rc") AndAlso
|
||||
Not Version("id").ToString.ToLower.Contains("experimental") AndAlso
|
||||
Not Version("id").ToString.ToLower.Contains("pre") Then
|
||||
Type = "正式版"
|
||||
Version("type") = "release"
|
||||
End If
|
||||
'愚人节版本
|
||||
Select Case Version("id").ToString.ToLower
|
||||
Case "20w14infinite", "20w14∞"
|
||||
Type = "愚人节版"
|
||||
Version("id") = "20w14∞"
|
||||
Version("type") = "special"
|
||||
Version.Add("lore", GetMcFoolName(Version("id")))
|
||||
Case "3d shareware v1.34", "1.rv-pre1", "15w14a", "2.0", "22w13oneblockatatime", "23w13a_or_b", "24w14potato", "25w14craftmine"
|
||||
Type = "愚人节版"
|
||||
Version("type") = "special"
|
||||
Version.Add("lore", GetMcFoolName(Version("id")))
|
||||
Case Else '4/1 自动视作愚人节版
|
||||
Dim ReleaseDate = Version("releaseTime").Value(Of Date).ToUniversalTime().AddHours(2)
|
||||
If ReleaseDate.Month = 4 AndAlso ReleaseDate.Day = 1 Then
|
||||
Type = "愚人节版"
|
||||
Version("type") = "special"
|
||||
End If
|
||||
End Select
|
||||
Case "special"
|
||||
'已被处理的愚人节版
|
||||
Type = "愚人节版"
|
||||
Case Else
|
||||
Type = "远古版"
|
||||
End Select
|
||||
'加入辞典
|
||||
Dict(Type).Add(Version)
|
||||
Next
|
||||
'排序
|
||||
For i = 0 To Dict.Keys.Count - 1
|
||||
Dict(Dict.Keys(i)) = Dict.Values(i).OrderByDescending(Function(v) v("releaseTime").Value(Of Date)).ToList
|
||||
Next
|
||||
'清空当前
|
||||
PanMain.Children.Clear()
|
||||
'添加最新版本
|
||||
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")
|
||||
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")
|
||||
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}
|
||||
MyCard.StackInstall(PanInfo, 2)
|
||||
CardInfo.Children.Add(PanInfo)
|
||||
PanMain.Children.Add(CardInfo)
|
||||
'添加其他版本
|
||||
For Each Pair As KeyValuePair(Of String, List(Of JObject)) In Dict
|
||||
If Not Pair.Value.Any() Then Continue For
|
||||
'增加卡片
|
||||
Dim NewCard As New MyCard With {.Title = Pair.Key & " (" & 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}
|
||||
NewCard.Children.Add(NewStack)
|
||||
NewCard.SwapControl = NewStack
|
||||
NewCard.IsSwaped = True
|
||||
PanMain.Children.Add(NewCard)
|
||||
Next
|
||||
Catch ex As Exception
|
||||
Log(ex, "可视化 MC 版本列表出错", LogLevel.Feedback)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
Public Sub DownloadStart(sender As MyListItem, e As Object)
|
||||
McDownloadClient(NetPreDownloadBehaviour.HintWhileExists, sender.Title, sender.Tag("url").ToString)
|
||||
End Sub
|
||||
|
||||
''介绍栏
|
||||
'Private Sub BtnWeb_Click(sender As Object, e As EventArgs) Handles BtnWeb.Click
|
||||
' OpenWebsite("https://www.minecraft.net/zh-hans")
|
||||
'End Sub
|
||||
'Private Sub BtnInstall_Click(sender As Object, e As EventArgs) Handles BtnInstall.Click
|
||||
' FrmMain.PageChange(FormMain.PageType.Download, FormMain.PageSubType.DownloadInstall)
|
||||
'End Sub
|
||||
|
||||
End Class
|
||||
@@ -1,28 +0,0 @@
|
||||
<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"
|
||||
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">
|
||||
<StackPanel Margin="25,40,15,20">
|
||||
<TextBlock Margin="0,0,0,11" Text="Fabric Loader 是新版 Minecraft 下的轻量化 Mod 加载器,你需要先安装它才能安装各种 Fabric 模组。
本页面提供 Fabric 安装器下载,在下载后你需要手动打开安装器进行安装。" TextWrapping="Wrap" />
|
||||
<Grid Height="35">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<local:MyButton x:Name="BtnWeb" Grid.Column="0" MinWidth="140" Text="打开官网" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
<local:MyCard x:Name="CardVersions" Margin="20,15,20,20" Title="版本列表">
|
||||
<StackPanel x:Name="PanVersions" Margin="20,40,18,20" />
|
||||
</local:MyCard>
|
||||
<local:MyCard HorizontalAlignment="Center" SnapsToDevicePixels="True" x:Name="PanLoad" UseAnimation="False" Margin="40,50">
|
||||
<local:MyLoading Text="正在获取 Fabric 列表" Margin="20,20,20,17" x:Name="Load" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</local:MyCard>
|
||||
</StackPanel>
|
||||
</local:MyScrollViewer>
|
||||
</local:MyPageRight>
|
||||
@@ -1,32 +0,0 @@
|
||||
Public Class PageDownloadFabric
|
||||
|
||||
Private Sub LoaderInit() Handles Me.Initialized
|
||||
PageLoaderInit(Load, PanLoad, CardVersions, CardTip, DlFabricListLoader, AddressOf Load_OnFinish)
|
||||
End Sub
|
||||
Private Sub Init() Handles Me.Loaded
|
||||
PanBack.ScrollToHome()
|
||||
End Sub
|
||||
|
||||
Private Sub Load_OnFinish()
|
||||
'结果数据化
|
||||
Try
|
||||
Dim Versions As JArray = DlFabricListLoader.Output.Value("installer")
|
||||
PanVersions.Children.Clear()
|
||||
For Each Version In Versions
|
||||
PanVersions.Children.Add(FabricDownloadListItem(Version, AddressOf Fabric_Selected))
|
||||
Next
|
||||
CardVersions.Title = "版本列表 (" & Versions.Count & ")"
|
||||
Catch ex As Exception
|
||||
Log(ex, "可视化 Fabric 版本列表出错", LogLevel.Feedback)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
Private Sub Fabric_Selected(sender As MyListItem, e As EventArgs)
|
||||
McDownloadFabricLoaderSave(sender.Tag)
|
||||
End Sub
|
||||
|
||||
Private Sub BtnWeb_Click(sender As Object, e As EventArgs) Handles BtnWeb.Click
|
||||
OpenWebsite("https://www.fabricmc.net")
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -1,26 +0,0 @@
|
||||
<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"
|
||||
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">
|
||||
<StackPanel Margin="25,40,15,20">
|
||||
<TextBlock Name="LabConnect" Margin="0,0,0,11" Text="Forge 是一个 Mod 加载器,你需要先安装 Forge 才能安装各种 Forge 模组。" TextWrapping="Wrap" />
|
||||
<Grid Height="35">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<local:MyButton x:Name="BtnWeb" Grid.Column="0" MinWidth="140" Text="打开官网" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
<StackPanel Name="PanMain" Margin="20,15,20,5" Grid.IsSharedSizeScope="True" />
|
||||
<local:MyCard HorizontalAlignment="Center" Margin="40,50" SnapsToDevicePixels="True" x:Name="PanLoad" UseAnimation="False">
|
||||
<local:MyLoading Text="正在获取 Forge 列表" Margin="20,20,20,17" x:Name="Load" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</local:MyCard>
|
||||
</StackPanel>
|
||||
</local:MyScrollViewer>
|
||||
</local:MyPageRight>
|
||||
@@ -1,61 +0,0 @@
|
||||
Public Class PageDownloadForge
|
||||
|
||||
Private Sub LoaderInit() Handles Me.Initialized
|
||||
PageLoaderInit(Load, PanLoad, PanMain, CardTip, DlForgeListLoader, AddressOf Load_OnFinish)
|
||||
End Sub
|
||||
Private Sub Init() Handles Me.Loaded
|
||||
PanBack.ScrollToHome()
|
||||
End Sub
|
||||
|
||||
Private Sub Load_OnFinish()
|
||||
'结果数据化
|
||||
Try
|
||||
'清空当前
|
||||
PanMain.Children.Clear()
|
||||
'转化为 UI
|
||||
For Each Version As String In DlForgeListLoader.Output.Value.Sort(AddressOf VersionSortBoolean)
|
||||
'增加卡片
|
||||
Dim NewCard As New MyCard With {.Title = Version.Replace("_p", " P"), .Margin = New Thickness(0, 0, 0, 15), .SwapType = 5}
|
||||
Dim NewStack As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0), .Tag = Version}
|
||||
NewCard.Children.Add(NewStack)
|
||||
NewCard.SwapControl = NewStack
|
||||
NewCard.IsSwaped = True
|
||||
PanMain.Children.Add(NewCard)
|
||||
Next
|
||||
''非官方源警示
|
||||
'If Setup.Get("ToolDownloadOutOfDate") AndAlso Not DlForgeListLoader.Output.IsOfficial Then
|
||||
' Dim CardWarn As New MyCard With {.Title = "过期提示", .Margin = New Thickness(0, 0, 0, 15)}
|
||||
' CardWarn.Children.Add(New TextBlock With {
|
||||
' .Margin = New Thickness(25, MyCard.SwapedHeight, 15, 15), .VerticalAlignment = VerticalAlignment.Top, .HorizontalAlignment = HorizontalAlignment.Left, .TextTrimming = TextTrimming.None, .TextWrapping = TextWrapping.Wrap,
|
||||
' .Text = "获取官方源失败,正在使用 " & DlForgeListLoader.Output.SourceName & " 镜像源,版本列表可能并非最新。" & vbCrLf & "官方源错误原因:" & If(DlForgeListLoader.Output.OfficialError, New Exception("连接服务器超时")).Message})
|
||||
' PanMain.Children.Insert(0, CardWarn)
|
||||
'End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "可视化 Forge 版本列表出错", LogLevel.Feedback)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
'Forge 版本列表加载
|
||||
Public Sub Forge_Click(sender As MyLoading, e As MouseButtonEventArgs)
|
||||
If sender.State.LoadingState = MyLoading.MyLoadingState.Error Then
|
||||
CType(sender.State, LoaderTask(Of String, List(Of DlForgeVersionEntry))).Start(IsForceRestart:=True)
|
||||
End If
|
||||
End Sub
|
||||
Public Sub Forge_StateChanged(sender As MyLoading, newState As MyLoading.MyLoadingState, oldState As MyLoading.MyLoadingState)
|
||||
If newState <> MyLoading.MyLoadingState.Stop Then Return
|
||||
|
||||
Dim Card As MyCard = CType(sender.Parent, FrameworkElement).Parent
|
||||
Dim Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)) = sender.State
|
||||
'载入列表
|
||||
Card.SwapControl.Children.Clear()
|
||||
Card.SwapControl.Tag = Loader.Output
|
||||
Card.SwapType = 6
|
||||
Card.StackInstall()
|
||||
End Sub
|
||||
|
||||
'介绍栏
|
||||
Private Sub BtnWeb_Click(sender As Object, e As EventArgs) Handles BtnWeb.Click
|
||||
OpenWebsite("https://files.minecraftforge.net")
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -3,7 +3,7 @@
|
||||
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}">
|
||||
PanScroll="PanBack">
|
||||
<Grid>
|
||||
<Grid Name="PanAllBack">
|
||||
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
|
||||
@@ -27,7 +27,7 @@
|
||||
<local:MyHint Text="必须安装 OptiFabric 才能正常使用 OptiFine!" Margin="0,10,0,0" x:Name="HintOptiFabric" Theme="Red" />
|
||||
<local:MyHint Text="安装结束后,请在 Mod 下载中搜索 OptiFabric Origins 并下载,否则 OptiFine 会无法使用!" Margin="0,10,0,0" x:Name="HintOptiFabricOld" Theme="Yellow" />
|
||||
<local:MyHint Text="OptiFine 与一部分 Mod 的兼容性不佳,请谨慎安装。" Margin="0,10,0,0" x:Name="HintModOptiFine" Theme="Yellow" />
|
||||
<local:MyCard Title="Forge" Height="40" Margin="0,12,0,0" x:Name="CardForge" IsSwaped="True" CanSwap="True" SwapLogoRight="True">
|
||||
<local:MyCard Title="Forge" Height="40" Margin="0,12,0,0" x:Name="CardForge" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
|
||||
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanForge" />
|
||||
<Grid x:Name="PanForgeInfo" Height="18" Margin="132,11,15,0" VerticalAlignment="Top" Tag="True">
|
||||
<Grid.RenderTransform>
|
||||
@@ -45,7 +45,7 @@
|
||||
Data="F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z" />
|
||||
</Grid>
|
||||
</local:MyCard>
|
||||
<local:MyCard Title="NeoForge" Height="40" Margin="0,12,0,0" x:Name="CardNeoForge" IsSwaped="True" CanSwap="True" SwapLogoRight="True">
|
||||
<local:MyCard Title="NeoForge" Height="40" Margin="0,12,0,0" x:Name="CardNeoForge" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
|
||||
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanNeoForge" />
|
||||
<Grid x:Name="PanNeoForgeInfo" Height="18" Margin="132,11,15,0" VerticalAlignment="Top" Tag="True">
|
||||
<Grid.RenderTransform>
|
||||
@@ -63,7 +63,7 @@
|
||||
Data="F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z" />
|
||||
</Grid>
|
||||
</local:MyCard>
|
||||
<local:MyCard Title="Fabric" Height="40" Margin="0,12,0,0" x:Name="CardFabric" IsSwaped="True" CanSwap="True" SwapLogoRight="True">
|
||||
<local:MyCard Title="Fabric" Height="40" Margin="0,12,0,0" x:Name="CardFabric" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
|
||||
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanFabric" />
|
||||
<Grid x:Name="PanFabricInfo" Height="18" Margin="132,11,15,0" VerticalAlignment="Top" Tag="True">
|
||||
<Grid.RenderTransform>
|
||||
@@ -81,7 +81,7 @@
|
||||
Data="F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z" />
|
||||
</Grid>
|
||||
</local:MyCard>
|
||||
<local:MyCard Title="Fabric API" Height="40" Margin="0,12,0,0" x:Name="CardFabricApi" IsSwaped="True" CanSwap="True" SwapLogoRight="True">
|
||||
<local:MyCard Title="Fabric API" Height="40" Margin="0,12,0,0" x:Name="CardFabricApi" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
|
||||
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanFabricApi" />
|
||||
<Grid x:Name="PanFabricApiInfo" Height="18" Margin="132,11,15,0" VerticalAlignment="Top" Tag="True">
|
||||
<Grid.RenderTransform>
|
||||
@@ -99,7 +99,7 @@
|
||||
Data="F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z" />
|
||||
</Grid>
|
||||
</local:MyCard>
|
||||
<local:MyCard Title="OptiFine" Height="40" Margin="0,12,0,0" x:Name="CardOptiFine" IsSwaped="True" CanSwap="True" SwapLogoRight="True">
|
||||
<local:MyCard Title="OptiFine" Height="40" Margin="0,12,0,0" x:Name="CardOptiFine" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
|
||||
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top">
|
||||
<StackPanel Name="PanOptiFine" />
|
||||
</StackPanel>
|
||||
@@ -119,7 +119,7 @@
|
||||
Data="F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z" />
|
||||
</Grid>
|
||||
</local:MyCard>
|
||||
<local:MyCard Title="OptiFabric" Height="40" Margin="0,12,0,0" x:Name="CardOptiFabric" IsSwaped="True" CanSwap="True" SwapLogoRight="True">
|
||||
<local:MyCard Title="OptiFabric" Height="40" Margin="0,12,0,0" x:Name="CardOptiFabric" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
|
||||
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanOptiFabric" />
|
||||
<Grid x:Name="PanOptiFabricInfo" Height="18" Margin="132,11,15,0" VerticalAlignment="Top" Tag="True">
|
||||
<Grid.RenderTransform>
|
||||
@@ -137,7 +137,7 @@
|
||||
Data="F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z" />
|
||||
</Grid>
|
||||
</local:MyCard>
|
||||
<local:MyCard Title="LiteLoader" Height="40" Margin="0,12,0,0" x:Name="CardLiteLoader" IsSwaped="True" CanSwap="True" SwapLogoRight="True">
|
||||
<local:MyCard Title="LiteLoader" Height="40" Margin="0,12,0,0" x:Name="CardLiteLoader" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
|
||||
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanLiteLoader" />
|
||||
<Grid x:Name="PanLiteLoaderInfo" Height="18" Margin="132,11,15,0" VerticalAlignment="Top" Tag="True">
|
||||
<Grid.RenderTransform>
|
||||
|
||||
@@ -54,18 +54,13 @@
|
||||
|
||||
DisabledPageAnimControls.Remove(BtnStart)
|
||||
BtnStart.Show = True
|
||||
CardOptiFine.IsSwaped = True
|
||||
CardLiteLoader.IsSwaped = True
|
||||
CardForge.IsSwaped = True
|
||||
CardNeoForge.IsSwaped = True
|
||||
CardFabric.IsSwaped = True
|
||||
CardFabricApi.IsSwaped = True
|
||||
CardOptiFabric.IsSwaped = True
|
||||
|
||||
If Not Setup.Get("HintInstallBack") Then
|
||||
Setup.Set("HintInstallBack", True)
|
||||
Hint("点击 Minecraft 项即可返回游戏主版本选择页面!")
|
||||
End If
|
||||
CardOptiFine.IsSwapped = True
|
||||
CardLiteLoader.IsSwapped = True
|
||||
CardForge.IsSwapped = True
|
||||
CardNeoForge.IsSwapped = True
|
||||
CardFabric.IsSwapped = True
|
||||
CardFabricApi.IsSwapped = True
|
||||
CardOptiFabric.IsSwapped = True
|
||||
|
||||
'如果在选择页面按了刷新键,选择页的东西可能会由于动画被隐藏,但不会由于加载结束而再次显示,因此这里需要手动恢复
|
||||
For Each Control In GetAllAnimControls(PanSelect)
|
||||
@@ -318,8 +313,8 @@
|
||||
'OptiFine
|
||||
Dim OptiFineError As String = LoadOptiFineGetError()
|
||||
CardOptiFine.MainSwap.Visibility = If(OptiFineError Is Nothing, Visibility.Visible, Visibility.Collapsed)
|
||||
If OptiFineError IsNot Nothing Then CardOptiFine.IsSwaped = True '例如在同时展开卡片时选择了不兼容项则强制折叠
|
||||
SetOptiFineInfoShow(CardOptiFine.IsSwaped)
|
||||
If OptiFineError IsNot Nothing Then CardOptiFine.IsSwapped = True '例如在同时展开卡片时选择了不兼容项则强制折叠
|
||||
SetOptiFineInfoShow(CardOptiFine.IsSwapped)
|
||||
If SelectedOptiFine Is Nothing Then
|
||||
BtnOptiFineClear.Visibility = Visibility.Collapsed
|
||||
ImgOptiFine.Visibility = Visibility.Collapsed
|
||||
@@ -338,8 +333,8 @@
|
||||
CardLiteLoader.Visibility = Visibility.Visible
|
||||
Dim LiteLoaderError As String = LoadLiteLoaderGetError()
|
||||
CardLiteLoader.MainSwap.Visibility = If(LiteLoaderError Is Nothing, Visibility.Visible, Visibility.Collapsed)
|
||||
If LiteLoaderError IsNot Nothing Then CardLiteLoader.IsSwaped = True '例如在同时展开卡片时选择了不兼容项则强制折叠
|
||||
SetLiteLoaderInfoShow(CardLiteLoader.IsSwaped)
|
||||
If LiteLoaderError IsNot Nothing Then CardLiteLoader.IsSwapped = True '例如在同时展开卡片时选择了不兼容项则强制折叠
|
||||
SetLiteLoaderInfoShow(CardLiteLoader.IsSwapped)
|
||||
If SelectedLiteLoader Is Nothing Then
|
||||
BtnLiteLoaderClear.Visibility = Visibility.Collapsed
|
||||
ImgLiteLoader.Visibility = Visibility.Collapsed
|
||||
@@ -355,8 +350,8 @@
|
||||
'Forge
|
||||
Dim ForgeError As String = LoadForgeGetError()
|
||||
CardForge.MainSwap.Visibility = If(ForgeError Is Nothing, Visibility.Visible, Visibility.Collapsed)
|
||||
If ForgeError IsNot Nothing Then CardForge.IsSwaped = True
|
||||
SetForgeInfoShow(CardForge.IsSwaped)
|
||||
If ForgeError IsNot Nothing Then CardForge.IsSwapped = True
|
||||
SetForgeInfoShow(CardForge.IsSwapped)
|
||||
If SelectedForge Is Nothing Then
|
||||
BtnForgeClear.Visibility = Visibility.Collapsed
|
||||
ImgForge.Visibility = Visibility.Collapsed
|
||||
@@ -375,8 +370,8 @@
|
||||
CardNeoForge.Visibility = Visibility.Visible
|
||||
Dim NeoForgeError As String = LoadNeoForgeGetError()
|
||||
CardNeoForge.MainSwap.Visibility = If(NeoForgeError Is Nothing, Visibility.Visible, Visibility.Collapsed)
|
||||
If NeoForgeError IsNot Nothing Then CardNeoForge.IsSwaped = True
|
||||
SetNeoForgeInfoShow(CardNeoForge.IsSwaped)
|
||||
If NeoForgeError IsNot Nothing Then CardNeoForge.IsSwapped = True
|
||||
SetNeoForgeInfoShow(CardNeoForge.IsSwapped)
|
||||
If SelectedNeoForge Is Nothing Then
|
||||
BtnNeoForgeClear.Visibility = Visibility.Collapsed
|
||||
ImgNeoForge.Visibility = Visibility.Collapsed
|
||||
@@ -396,8 +391,8 @@
|
||||
CardFabric.Visibility = Visibility.Visible
|
||||
Dim FabricError As String = LoadFabricGetError()
|
||||
CardFabric.MainSwap.Visibility = If(FabricError Is Nothing, Visibility.Visible, Visibility.Collapsed)
|
||||
If FabricError IsNot Nothing Then CardFabric.IsSwaped = True
|
||||
SetFabricInfoShow(CardFabric.IsSwaped)
|
||||
If FabricError IsNot Nothing Then CardFabric.IsSwapped = True
|
||||
SetFabricInfoShow(CardFabric.IsSwapped)
|
||||
If SelectedFabric Is Nothing Then
|
||||
BtnFabricClear.Visibility = Visibility.Collapsed
|
||||
ImgFabric.Visibility = Visibility.Collapsed
|
||||
@@ -417,8 +412,8 @@
|
||||
CardFabricApi.Visibility = Visibility.Visible
|
||||
Dim FabricApiError As String = LoadFabricApiGetError()
|
||||
CardFabricApi.MainSwap.Visibility = If(FabricApiError Is Nothing, Visibility.Visible, Visibility.Collapsed)
|
||||
If FabricApiError IsNot Nothing OrElse SelectedFabric Is Nothing Then CardFabricApi.IsSwaped = True
|
||||
SetFabricApiInfoShow(CardFabricApi.IsSwaped)
|
||||
If FabricApiError IsNot Nothing OrElse SelectedFabric Is Nothing Then CardFabricApi.IsSwapped = True
|
||||
SetFabricApiInfoShow(CardFabricApi.IsSwapped)
|
||||
If SelectedFabricApi Is Nothing Then
|
||||
BtnFabricApiClear.Visibility = Visibility.Collapsed
|
||||
ImgFabricApi.Visibility = Visibility.Collapsed
|
||||
@@ -438,8 +433,8 @@
|
||||
CardOptiFabric.Visibility = Visibility.Visible
|
||||
Dim OptiFabricError As String = LoadOptiFabricGetError()
|
||||
CardOptiFabric.MainSwap.Visibility = If(OptiFabricError Is Nothing, Visibility.Visible, Visibility.Collapsed)
|
||||
If OptiFabricError IsNot Nothing OrElse SelectedFabric Is Nothing Then CardOptiFabric.IsSwaped = True
|
||||
SetOptiFabricInfoShow(CardOptiFabric.IsSwaped)
|
||||
If OptiFabricError IsNot Nothing OrElse SelectedFabric Is Nothing Then CardOptiFabric.IsSwapped = True
|
||||
SetOptiFabricInfoShow(CardOptiFabric.IsSwapped)
|
||||
If SelectedOptiFabric Is Nothing Then
|
||||
BtnOptiFabricClear.Visibility = Visibility.Collapsed
|
||||
ImgOptiFabric.Visibility = Visibility.Collapsed
|
||||
@@ -618,16 +613,16 @@
|
||||
PanMinecraft.Children.Clear()
|
||||
'添加最新版本
|
||||
Dim CardInfo As New MyCard With {.Title = "最新版本", .Margin = New Thickness(0, 15, 0, 15), .SwapType = 2}
|
||||
Dim TopestVersions As New List(Of JObject)
|
||||
Dim PinnedVersions As New List(Of JObject)
|
||||
Dim Release As JObject = Dict("正式版")(0).DeepClone()
|
||||
Release("lore") = "最新正式版,发布于 " & Release("releaseTime").Value(Of Date).ToString("yyyy'/'MM'/'dd HH':'mm")
|
||||
TopestVersions.Add(Release)
|
||||
PinnedVersions.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")
|
||||
TopestVersions.Add(Snapshot)
|
||||
PinnedVersions.Add(Snapshot)
|
||||
End If
|
||||
Dim PanInfo As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0), .Tag = TopestVersions}
|
||||
Dim PanInfo As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0), .Tag = PinnedVersions}
|
||||
MyCard.StackInstall(PanInfo, 7)
|
||||
CardInfo.Children.Add(PanInfo)
|
||||
PanMinecraft.Children.Insert(0, CardInfo)
|
||||
@@ -635,11 +630,11 @@
|
||||
For Each Pair As KeyValuePair(Of String, List(Of JObject)) In Dict
|
||||
If Not Pair.Value.Any() Then Continue For
|
||||
'增加卡片
|
||||
Dim NewCard As New MyCard With {.Title = Pair.Key & " (" & Pair.Value.Count & ")", .Margin = New Thickness(0, 0, 0, 15), .SwapType = 7}
|
||||
Dim NewCard As New MyCard With {.Title = Pair.Key, .Margin = New Thickness(0, 0, 0, 15), .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}
|
||||
NewCard.Children.Add(NewStack)
|
||||
NewCard.SwapControl = NewStack
|
||||
NewCard.IsSwaped = True
|
||||
NewCard.IsSwapped = True
|
||||
PanMinecraft.Children.Add(NewCard)
|
||||
Next
|
||||
'自动选择版本
|
||||
@@ -676,6 +671,11 @@
|
||||
VersionSortInteger(SelectedMinecraftId, "1.13") >= 0 AndAlso VersionSortInteger("1.14.3", SelectedMinecraftId) >= 0 Then
|
||||
Return "与 Forge 不兼容"
|
||||
End If
|
||||
'检查 Fabric 1.20.5+:全部不兼容
|
||||
If SelectedFabric IsNot Nothing AndAlso
|
||||
VersionSortInteger(SelectedMinecraftId, "1.20.4") > 0 Then
|
||||
Return "与 Fabric 不兼容"
|
||||
End If
|
||||
'检查 Forge 版本
|
||||
Dim HasAny As Boolean = False
|
||||
Dim HasRequiredVersion As Boolean = False
|
||||
@@ -750,14 +750,14 @@
|
||||
OptiFabric_Loaded()
|
||||
Forge_Loaded()
|
||||
NeoForge_Loaded()
|
||||
CardOptiFine.IsSwaped = True
|
||||
CardOptiFine.IsSwapped = True
|
||||
SelectReload()
|
||||
End Sub
|
||||
Private Sub OptiFine_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnOptiFineClear.MouseLeftButtonUp
|
||||
SelectedOptiFine = Nothing
|
||||
SelectedOptiFabric = Nothing
|
||||
AutoSelectedOptiFabric = False
|
||||
CardOptiFine.IsSwaped = True
|
||||
CardOptiFine.IsSwapped = True
|
||||
e.Handled = True
|
||||
Forge_Loaded()
|
||||
NeoForge_Loaded()
|
||||
@@ -811,12 +811,12 @@
|
||||
'选择与清除
|
||||
Private Sub LiteLoader_Selected(sender As MyListItem, e As EventArgs)
|
||||
SelectedLiteLoader = sender.Tag
|
||||
CardLiteLoader.IsSwaped = True
|
||||
CardLiteLoader.IsSwapped = True
|
||||
SelectReload()
|
||||
End Sub
|
||||
Private Sub LiteLoader_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnLiteLoaderClear.MouseLeftButtonUp
|
||||
SelectedLiteLoader = Nothing
|
||||
CardLiteLoader.IsSwaped = True
|
||||
CardLiteLoader.IsSwapped = True
|
||||
e.Handled = True
|
||||
SelectReload()
|
||||
End Sub
|
||||
@@ -897,14 +897,14 @@
|
||||
'选择与清除
|
||||
Private Sub Forge_Selected(sender As MyListItem, e As EventArgs)
|
||||
SelectedForge = sender.Tag
|
||||
CardForge.IsSwaped = True
|
||||
CardForge.IsSwapped = True
|
||||
If SelectedOptiFine IsNot Nothing AndAlso Not IsOptiFineSuitForForge(SelectedOptiFine, SelectedForge) Then SelectedOptiFine = Nothing
|
||||
OptiFine_Loaded()
|
||||
SelectReload()
|
||||
End Sub
|
||||
Private Sub Forge_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnForgeClear.MouseLeftButtonUp
|
||||
SelectedForge = Nothing
|
||||
CardForge.IsSwaped = True
|
||||
CardForge.IsSwapped = True
|
||||
e.Handled = True
|
||||
OptiFine_Loaded()
|
||||
SelectReload()
|
||||
@@ -959,13 +959,13 @@
|
||||
'选择与清除
|
||||
Private Sub NeoForge_Selected(sender As MyListItem, e As EventArgs)
|
||||
SelectedNeoForge = sender.Tag
|
||||
CardNeoForge.IsSwaped = True
|
||||
CardNeoForge.IsSwapped = True
|
||||
OptiFine_Loaded()
|
||||
SelectReload()
|
||||
End Sub
|
||||
Private Sub NeoForge_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnNeoForgeClear.MouseLeftButtonUp
|
||||
SelectedNeoForge = Nothing
|
||||
CardNeoForge.IsSwaped = True
|
||||
CardNeoForge.IsSwapped = True
|
||||
e.Handled = True
|
||||
OptiFine_Loaded()
|
||||
SelectReload()
|
||||
@@ -981,6 +981,11 @@
|
||||
Private Function LoadFabricGetError() As String
|
||||
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
|
||||
'检查 OptiFine 1.20.5+:没有 OptiFabric 故全部不兼容
|
||||
If SelectedOptiFine IsNot Nothing AndAlso
|
||||
VersionSortInteger(SelectedMinecraftId, "1.20.4") > 0 Then
|
||||
Return "与 OptiFine 不兼容"
|
||||
End If
|
||||
For Each Version As JObject In DlFabricListLoader.Output.Value("game")
|
||||
If Version("version").ToString = SelectedMinecraftId.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3") Then
|
||||
If SelectedForge IsNot Nothing Then Return "与 Forge 不兼容"
|
||||
@@ -1020,7 +1025,7 @@
|
||||
SelectedFabric = sender.Tag("version").ToString
|
||||
FabricApi_Loaded()
|
||||
OptiFabric_Loaded()
|
||||
CardFabric.IsSwaped = True
|
||||
CardFabric.IsSwapped = True
|
||||
SelectReload()
|
||||
End Sub
|
||||
Private Sub Fabric_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnFabricClear.MouseLeftButtonUp
|
||||
@@ -1029,7 +1034,7 @@
|
||||
AutoSelectedFabricApi = False
|
||||
SelectedOptiFabric = Nothing
|
||||
AutoSelectedOptiFabric = False
|
||||
CardFabric.IsSwaped = True
|
||||
CardFabric.IsSwapped = True
|
||||
e.Handled = True
|
||||
SelectReload()
|
||||
End Sub
|
||||
@@ -1141,12 +1146,12 @@
|
||||
'选择与清除
|
||||
Private Sub FabricApi_Selected(sender As MyListItem, e As EventArgs)
|
||||
SelectedFabricApi = sender.Tag
|
||||
CardFabricApi.IsSwaped = True
|
||||
CardFabricApi.IsSwapped = True
|
||||
SelectReload()
|
||||
End Sub
|
||||
Private Sub FabricApi_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnFabricApiClear.MouseLeftButtonUp
|
||||
SelectedFabricApi = Nothing
|
||||
CardFabricApi.IsSwaped = True
|
||||
CardFabricApi.IsSwapped = True
|
||||
e.Handled = True
|
||||
SelectReload()
|
||||
End Sub
|
||||
@@ -1233,12 +1238,12 @@
|
||||
'选择与清除
|
||||
Private Sub OptiFabric_Selected(sender As MyListItem, e As EventArgs)
|
||||
SelectedOptiFabric = sender.Tag
|
||||
CardOptiFabric.IsSwaped = True
|
||||
CardOptiFabric.IsSwapped = True
|
||||
SelectReload()
|
||||
End Sub
|
||||
Private Sub OptiFabric_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnOptiFabricClear.MouseLeftButtonUp
|
||||
SelectedOptiFabric = Nothing
|
||||
CardOptiFabric.IsSwaped = True
|
||||
CardOptiFabric.IsSwapped = True
|
||||
e.Handled = True
|
||||
SelectReload()
|
||||
End Sub
|
||||
|
||||
@@ -1,114 +1,59 @@
|
||||
<local:MyPageLeft x:Class="PageDownloadLeft"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:PCL" AnimatedControl="{Binding ElementName=PanItem, Mode=OneWay}">
|
||||
xmlns:local="clr-namespace:PCL" AnimatedControl="PanItem">
|
||||
<local:MyScrollViewer PanningMode="VerticalOnly" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" Style="{StaticResource ScrollViewerFullMargin}">
|
||||
<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" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="游戏下载"
|
||||
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 x:Name="ItemInstall" Checked="True" IsScaleAnimationEnabled="False" Type="RadioBox" Tag="0" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="原版游戏"
|
||||
Logo="m351.9,801.7c-8.6,0-17-3-23.7-8.4L18.9,614.7c-12.5-7.2-19.7-20.5-18.8-34.9V221c-.7-15,6.6-28.8,19.2-36L325.2,8.3c6.6-5.3,15.1-8.3,24-8.3s17.3,2.9,23.9,8.2l308.3,178c.2.1.5.3.8.5h0c12,6.7,19.4,19.8,19.4,34.1,0,13.9-7.1,26-18.7,32.5l-290.1,167.5v276l234-135.1v-168.9c0-20.8,17-37.8,37.8-37.8s37.8,17,37.8,37.8v191.7c0,16.4-10.9,31.2-26.5,36.1l-299.4,172.8c-6.7,5.4-15.1,8.3-23.7,8.3h-.9Zm-34.8-101.9v-276l-241.4-139.4v276l241.4,139.4Zm35.3-342.8l236.8-136.7-240-138.6L112.4,218.3l240,138.6Z">
|
||||
<local:MyListItem.Buttons>
|
||||
<x:Array Type="{x:Type local:MyIconButton}">
|
||||
<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" />
|
||||
<local:MyIconButton Tag="0" 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" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="手动安装包"
|
||||
Logo="M1000 241c-2-5-4-11-6-17-0-6-4-12-8-17-7-9-18-15-31-15-6 0-13 1-19 5l-15 15-17 17-21 21L749 381c-14 14-38 14-52 0l-54-54c-14-14-14-38 0-52l132-132 14-14 20-20 16-16c3-5 5-11 5-19 0-12-5-24-16-32-5-4-11-6-18-7-5-2-10-4-16-5-36-12-76-19-117-19-7 0-14 0-22 0-188 11-337 168-337 360 0 16 0 32 3 48L56 663c-73 74-73 193 0 267l33 33c73 73 193 73 267 0l240-241c21 4 42 5 64 5 190 0 346-146 360-333 0-8 0-17 0-27 0-44-6-86-21-125zM658 639c-28 0-54-4-79-11-4 7-10 14-16 21l-258 258c-43 43-115 43-158 0l-29-29c-43-44-43-115 0-158l212-212 46-46c5-5 12-11 19-15-8-26-12-54-12-83v-1c0-152 123-275 276-275 18 0 37 1 55 5L597 207c-52 52-52 137 0 189l29 29c52 52 137 52 189 0L930 312c2 16 4 33 4 50 0 152-123 276-276 276z" />
|
||||
<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" 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">-->
|
||||
<TextBlock Text="社区资源" Margin="13,20,5,4" Opacity="0.6" FontSize="12" />
|
||||
<local:MyListItem x:Name="ItemMod" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="2" 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="2" 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="3" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="整合包"
|
||||
LogoScale="0.98" Logo="M511 995a128 128 0 0 1-57-13L70 791a126 126 0 0 1-70-113V311a126 126 0 0 1 15-60V248c1-2 3-5 5-8a127 127 0 0 1 49-42L454 13a128 128 0 0 1 112 0l383 190a126 126 0 0 1 72 113v360a126 126 0 0 1-70 115L568 984c-17 7-37 11-57 11z m42-470v370l360-178c14-7 23-21 23-38v-335L554 524zM85 330v347a42 42 0 0 0 23 38l360 178V523L85 330zM135 260l375 189 137-65L286 188 135 260z m245-118l363 197 150-71-365-180a42 42 0 0 0-37 0l-111 53z">
|
||||
<local:MyListItem.Buttons>
|
||||
<x:Array Type="{x:Type local:MyIconButton}">
|
||||
<local:MyIconButton Tag="3" 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="ItemDataPack" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="4" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="数据包"
|
||||
LogoScale="0.96" Logo="M445 545c18 0 33 14 33 33v149a182 182 0 1 1-182-182z m282 0a182 182 0 1 1-182 182v-149c0-18 14-33 33-33zM412 611H296a116 116 0 1 0 116 116V611z m315 0H611v116a116 116 0 1 0 116-116zM296 113a182 182 0 0 1 182 182v149a33 33 0 0 1-33 33H296a182 182 0 0 1 0-365z m431 0a182 182 0 0 1 0 365h-149a33 33 0 0 1-33-33V296A182 182 0 0 1 727 113zM296 180a116 116 0 0 0 0 232H412V296c0-62-48-112-109-115z m431 0a116 116 0 0 0-116 116V412h116c62 0 112-48 115-109l0-6a116 116 0 0 0-116-116z">
|
||||
<local:MyListItem.Buttons>
|
||||
<x:Array Type="{x:Type local:MyIconButton}">
|
||||
<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" 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 x:Name="ItemResourcePack" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="5" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="资源包"
|
||||
LogoScale="0.81" Logo="M884.4 130.6H140.6c-41.9 0-76 34.1-76 76v613.3c0 41.9 34.1 76 76 76h743.8c41.9 0 76-34.1 76-76V206.6c0-41.9-34.1-76-76-76z m-743.8 72h743.8c2.2 0 4 1.8 4 4v371.3L796 461.7c-21.7-27.3-54.1-43.7-88.8-45-34.7-1.3-68.1 12.7-91.7 38.3L454 630c-2.8 3-7.2 3.5-10.5 1.3l-103-70.3c-31.5-21.5-72.8-22.5-105.3-2.7l-98.6 60.1V206.6c0-2.2 1.8-4 4-4z m743.8 621.3H140.6c-2.2 0-4-1.8-4-4V702.7l136.1-83c8.4-5.1 19.1-4.8 27.2 0.7l103 70.3c15.9 10.8 35.1 15.6 54.2 13.4 19-2.2 36.7-11.2 49.8-25.3l161.6-175c9.5-10.3 22.3-15.7 36.2-15.1 13.9 0.5 26.3 6.8 35.1 17.8l148.7 187v126.3c-0.1 2.3-1.9 4.1-4.1 4.1z M231.8 400.6a69.6 69.6 0 1 0 98.4-98.4 69.6 69.6 0 1 0-98.4 98.4Z">
|
||||
<local:MyListItem.Buttons>
|
||||
<x:Array Type="{x:Type local:MyIconButton}">
|
||||
<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" 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 x:Name="ItemShader" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="6" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="光影包"
|
||||
LogoScale="1.04" Logo="M512 0c25 0 42 17 42 42v85c0 25-17 42-42 42s-42-17-42-42V42c0-25 17-42 42-42zM512 853c25 0 42 17 42 42v85c0 25-17 42-42 42s-42-17-42-42v-85c0-25 17-42 42-42zM209 149c-17-17-42-17-59 0-17 17-17 42 0 59l59 59c17 17 42 17 59 0 17-17 17-42 0-59L209 149zM755 755c17-17 42-17 59 0l59 59c17 17 17 42 0 59s-42 17-59 0l-59-59c-17-17-17-46 0-59zM42 469c-25 0-42 17-42 42s17 42 42 42h85c25 0 42-17 42-42s-17-42-42-42H42zM853 512c0-25 17-42 42-42h85c25 0 42 17 42 42s-17 42-42 42h-85c-25 0-42-17-42-42zM149 814c-17 17-17 42 0 59 17 17 42 17 59 0l59-59c17-17 17-42 0-59-17-17-42-17-59 0l-59 59zM755 268c-17-17-17-42 0-59l59-59c17-17 42-17 59 0s17 42 0 59l-59 59c-17 17-46 17-59 0z M512 213c-166 0-298 132-298 298s132 298 298 298 298-132 298-298-132-298-298-298z m0 512c-119 0-213-93-213-213s93-213 213-213 213 93 213 213-93 213-213 213z">
|
||||
<local:MyListItem.Buttons>
|
||||
<x:Array Type="{x:Type local:MyIconButton}">
|
||||
<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="ItemNeoForge" Visibility="Collapsed" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="7" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="NeoForge"
|
||||
LogoScale="0.95" Logo="M544.6 921.2l-23.7 32.6c14.1 10.2 33.3 10.2 47.5 0l-23.7-32.6zM114.6 608.5l-23.9 32.5 0.2 0.1 23.6-32.6z m-12.0-38.0l38.3 12.7v-0.2l-38.3-12.4z m49.2-152.6l-38.4-12.4 38.4 12.4zM250.2 114.4l-37.6-14.6a40.2 40.2 0 0 0-0.7 2.1l38.4 12.4z m4.3-6.4l27.0 29.9 0.2-0.2-27.2-29.7z m11.7-4.5V63.0v40.4z m11.7 4.5l28.0-29.0a40.5 40.5 0 0 0-0.8-0.8l-27.2 29.8z m4.4 7.3l-38.6 11.7a40.5 40.5 0 0 0 0.2 0.7l38.3-12.4zM381.0 417.7l-38.4 12.5a40.3 40.3 0 0 0 38.4 27.8v-40.3z m327.0 0v40.3c17.5 0 32.9-11.2 38.4-27.8L708.1 417.7z m98.5-303.3l-37.6-14.6a38.4 38.4 0 0 0-0.7 2.1l38.4 12.4z m4.3-6.4l27.1 29.9 0.1-0.2-27.2-29.7z m11.7-4.5v0z m11.7 4.5l28.0-29.0a38.4 38.4 0 0 0-0.8-0.8l-27.2 29.8z m4.3 7.3l-38.6 11.7a47.5 47.5 0 0 0 0.2 0.7l38.4-12.4z m98.5 303.3l-38.4 12.4 0.1 0.5 38.2-12.9z m51.2 151.9l38.7-11.3a34.5 34.5 0 0 0-0.5-1.6l-38.1 12.9z m-37.8 5.2l-430.0 312.6 47.5 65.3 429.9-312.6-47.5-65.3z m-382.5 312.6L138.3 575.8l-47.5 65.3L520.9 953.9l47.3-65.3zM138.5 576a6.4 6.4 0 0 1 2.3 3.2l-76.9 24.5c4.7 14.9 14.1 28.0 26.7 37.3l47.8-65.0z m2.3 3.2a6.4 6.4 0 0 1 0 4.0L64.1 557.9a74.3 74.3 0 0 0-0.2 45.8l76.9-24.5z m0 3.7l49.2-152.6-76.8-24.8L64.0 558.2l76.8 24.8z m49.2-152.5l98.5-303.3-76.8-25.0L113.3 405.3l76.8 25.0z m97.7-301.3a23.4 23.4 0 0 1-6.1 8.9L227.6 78.0a57.3 57.3 0 0 0-15.0 21.8l75.3 29.1z m-5.9 8.7a23.0 23.0 0 0 1-15.5 6.0V63.0a57.7 57.7 0 0 0-38.9 15.1l54.4 59.6z m-15.5 6.0a23.0 23.0 0 0 1-15.5-6.0l54.4-59.6A57.7 57.7 0 0 0 266.4 63.0v80.8z m-16.3-6.8a23.4 23.4 0 0 1-6.1-10.0l77.3-23.4a57.3 57.3 0 0 0-15.0-24.5l-56.1 58.0z m-5.8-9.2l98.4 302.5 76.8-25.0-98.5-302.5-76.8 25.0z m136.8 330.4H708.1V377.5H381.2v80.7z m365.4-27.8l98.5-303.3-76.8-25.0L669.6 405.3l76.8 25.0z m97.7-301.3a23.4 23.4 0 0 1-6.1 8.9l-54.1-59.9a57.3 57.3 0 0 0-15.0 21.8l75.3 29.1z m-5.9 8.7a23.0 23.0 0 0 1-15.5 6.0V63.0a57.7 57.7 0 0 0-38.9 15.1l54.4 59.6z m-15.5 6.0a23.0 23.0 0 0 1-15.5-6.0l54.4-59.6a57.7 57.7 0 0 0-38.9-15.1v80.8z m-16.3-6.8a23.4 23.4 0 0 1-6.1-10.0l77.3-23.4a57.4 57.4 0 0 0-14.9-24.5l-56.1 58.0z m-5.8-9.2l98.5 303.3 76.8-25.0-98.5-303.3-76.8 25.0z m98.6 303.8l51.2 151.9 76.5-25.8-51.2-151.9-76.5 25.8z m50.7 150.2a6.5 6.5 0 0 1 0.1-4.0l75.6 28.1c5.5-14.9 6.2-31.3 1.6-46.7l-77.5 22.6z m0.1-4.0a6.4 6.4 0 0 1 2.4-3.2l43.8 67.8c13.4-8.7 23.6-21.5 29.2-36.4l-75.6-28.1z">
|
||||
<local:MyListItem.Buttons>
|
||||
<x:Array Type="{x:Type local:MyIconButton}">
|
||||
<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="ItemFabric" Visibility="Collapsed" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="8" 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="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>
|
||||
<local:MyListItem x:Name="ItemLiteLoader" Visibility="Collapsed" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="9" 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="9" 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,30,5,4" Opacity="0.6" FontSize="12" />
|
||||
<local:MyListItem x:Name="ItemMod" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="11" 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="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>
|
||||
<local:MyListItem x:Name="ItemPack" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="12" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="整合包"
|
||||
LogoScale="0.98" Logo="M511 995a128 128 0 0 1-57-13L70 791a126 126 0 0 1-70-113V311a126 126 0 0 1 15-60V248c1-2 3-5 5-8a127 127 0 0 1 49-42L454 13a128 128 0 0 1 112 0l383 190a126 126 0 0 1 72 113v360a126 126 0 0 1-70 115L568 984c-17 7-37 11-57 11z m42-470v370l360-178c14-7 23-21 23-38v-335L554 524zM85 330v347a42 42 0 0 0 23 38l360 178V523L85 330zM135 260l375 189 137-65L286 188 135 260z m245-118l363 197 150-71-365-180a42 42 0 0 0-37 0l-111 53z">
|
||||
<local:MyListItem.Buttons>
|
||||
<x:Array Type="{x:Type local:MyIconButton}">
|
||||
<local:MyIconButton Tag="12" 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="ItemDataPack" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="13" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="数据包"
|
||||
LogoScale="0.96" Logo="M445 545c18 0 33 14 33 33v149a182 182 0 1 1-182-182z m282 0a182 182 0 1 1-182 182v-149c0-18 14-33 33-33zM412 611H296a116 116 0 1 0 116 116V611z m315 0H611v116a116 116 0 1 0 116-116zM296 113a182 182 0 0 1 182 182v149a33 33 0 0 1-33 33H296a182 182 0 0 1 0-365z m431 0a182 182 0 0 1 0 365h-149a33 33 0 0 1-33-33V296A182 182 0 0 1 727 113zM296 180a116 116 0 0 0 0 232H412V296c0-62-48-112-109-115z m431 0a116 116 0 0 0-116 116V412h116c62 0 112-48 115-109l0-6a116 116 0 0 0-116-116z">
|
||||
<local:MyListItem.Buttons>
|
||||
<x:Array Type="{x:Type local:MyIconButton}">
|
||||
<local:MyIconButton Tag="13" 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="ItemResourcePack" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="14" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="资源包"
|
||||
LogoScale="0.81" Logo="M884.4 130.6H140.6c-41.9 0-76 34.1-76 76v613.3c0 41.9 34.1 76 76 76h743.8c41.9 0 76-34.1 76-76V206.6c0-41.9-34.1-76-76-76z m-743.8 72h743.8c2.2 0 4 1.8 4 4v371.3L796 461.7c-21.7-27.3-54.1-43.7-88.8-45-34.7-1.3-68.1 12.7-91.7 38.3L454 630c-2.8 3-7.2 3.5-10.5 1.3l-103-70.3c-31.5-21.5-72.8-22.5-105.3-2.7l-98.6 60.1V206.6c0-2.2 1.8-4 4-4z m743.8 621.3H140.6c-2.2 0-4-1.8-4-4V702.7l136.1-83c8.4-5.1 19.1-4.8 27.2 0.7l103 70.3c15.9 10.8 35.1 15.6 54.2 13.4 19-2.2 36.7-11.2 49.8-25.3l161.6-175c9.5-10.3 22.3-15.7 36.2-15.1 13.9 0.5 26.3 6.8 35.1 17.8l148.7 187v126.3c-0.1 2.3-1.9 4.1-4.1 4.1z M231.8 400.6a69.6 69.6 0 1 0 98.4-98.4 69.6 69.6 0 1 0-98.4 98.4Z">
|
||||
<local:MyListItem.Buttons>
|
||||
<x:Array Type="{x:Type local:MyIconButton}">
|
||||
<local:MyIconButton Tag="14" 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="ItemShader" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="15" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="光影包"
|
||||
LogoScale="1.04" Logo="M512 0c25 0 42 17 42 42v85c0 25-17 42-42 42s-42-17-42-42V42c0-25 17-42 42-42zM512 853c25 0 42 17 42 42v85c0 25-17 42-42 42s-42-17-42-42v-85c0-25 17-42 42-42zM209 149c-17-17-42-17-59 0-17 17-17 42 0 59l59 59c17 17 42 17 59 0 17-17 17-42 0-59L209 149zM755 755c17-17 42-17 59 0l59 59c17 17 17 42 0 59s-42 17-59 0l-59-59c-17-17-17-46 0-59zM42 469c-25 0-42 17-42 42s17 42 42 42h85c25 0 42-17 42-42s-17-42-42-42H42zM853 512c0-25 17-42 42-42h85c25 0 42 17 42 42s-17 42-42 42h-85c-25 0-42-17-42-42zM149 814c-17 17-17 42 0 59 17 17 42 17 59 0l59-59c17-17 17-42 0-59-17-17-42-17-59 0l-59 59zM755 268c-17-17-17-42 0-59l59-59c17-17 42-17 59 0s17 42 0 59l-59 59c-17 17-46 17-59 0z M512 213c-166 0-298 132-298 298s132 298 298 298 298-132 298-298-132-298-298-298z m0 512c-119 0-213-93-213-213s93-213 213-213 213 93 213 213-93 213-213 213z">
|
||||
<local:MyListItem.Buttons>
|
||||
<x:Array Type="{x:Type local:MyIconButton}">
|
||||
<local:MyIconButton Tag="15" 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>
|
||||
</StackPanel>
|
||||
</local:MyScrollViewer>
|
||||
</local:MyPageLeft>
|
||||
@@ -11,7 +11,7 @@
|
||||
''' <summary>
|
||||
''' 勾选事件改变页面。
|
||||
''' </summary>
|
||||
Private Sub PageCheck(sender As MyListItem, e As RouteEventArgs) Handles ItemInstall.Check, ItemClient.Check, ItemOptiFine.Check, ItemForge.Check, ItemNeoForge.Check, ItemLiteLoader.Check, ItemMod.Check, ItemFabric.Check, ItemPack.Check, ItemResourcePack.Check, ItemShader.Check, ItemDataPack.Check
|
||||
Private Sub PageCheck(sender As MyListItem, e As RouteEventArgs) Handles ItemInstall.Check, ItemMod.Check, ItemPack.Check, ItemResourcePack.Check, ItemShader.Check, ItemDataPack.Check
|
||||
'尚未初始化控件属性时,sender.Tag 为 Nothing,会导致切换到页面 0
|
||||
'若使用 IsLoaded,则会导致模拟点击不被执行(模拟点击切换页面时,控件的 IsLoaded 为 False)
|
||||
If sender.Tag IsNot Nothing Then PageChange(Val(sender.Tag))
|
||||
@@ -23,24 +23,6 @@
|
||||
Case FormMain.PageSubType.DownloadInstall
|
||||
If FrmDownloadInstall Is Nothing Then FrmDownloadInstall = New PageDownloadInstall
|
||||
Return FrmDownloadInstall
|
||||
Case FormMain.PageSubType.DownloadClient
|
||||
If FrmDownloadClient Is Nothing Then FrmDownloadClient = New PageDownloadClient
|
||||
Return FrmDownloadClient
|
||||
Case FormMain.PageSubType.DownloadOptiFine
|
||||
If FrmDownloadOptiFine Is Nothing Then FrmDownloadOptiFine = New PageDownloadOptiFine
|
||||
Return FrmDownloadOptiFine
|
||||
Case FormMain.PageSubType.DownloadForge
|
||||
If FrmDownloadForge Is Nothing Then FrmDownloadForge = New PageDownloadForge
|
||||
Return FrmDownloadForge
|
||||
Case FormMain.PageSubType.DownloadNeoForge
|
||||
If FrmDownloadNeoForge Is Nothing Then FrmDownloadNeoForge = New PageDownloadNeoForge
|
||||
Return FrmDownloadNeoForge
|
||||
Case FormMain.PageSubType.DownloadLiteLoader
|
||||
If FrmDownloadLiteLoader Is Nothing Then FrmDownloadLiteLoader = New PageDownloadLiteLoader
|
||||
Return FrmDownloadLiteLoader
|
||||
Case FormMain.PageSubType.DownloadFabric
|
||||
If FrmDownloadFabric Is Nothing Then FrmDownloadFabric = New PageDownloadFabric
|
||||
Return FrmDownloadFabric
|
||||
Case FormMain.PageSubType.DownloadMod
|
||||
If FrmDownloadMod Is Nothing Then FrmDownloadMod = New PageDownloadMod
|
||||
Return FrmDownloadMod
|
||||
@@ -163,24 +145,6 @@
|
||||
FrmDownloadDataPack.PageLoaderRestart()
|
||||
End If
|
||||
ItemDataPack.Checked = True
|
||||
Case FormMain.PageSubType.DownloadClient
|
||||
DlClientListLoader.Start(IsForceRestart:=True)
|
||||
ItemClient.Checked = True
|
||||
Case FormMain.PageSubType.DownloadOptiFine
|
||||
DlOptiFineListLoader.Start(IsForceRestart:=True)
|
||||
ItemOptiFine.Checked = True
|
||||
Case FormMain.PageSubType.DownloadForge
|
||||
DlForgeListLoader.Start(IsForceRestart:=True)
|
||||
ItemForge.Checked = True
|
||||
Case FormMain.PageSubType.DownloadNeoForge
|
||||
DlNeoForgeListLoader.Start(IsForceRestart:=True)
|
||||
ItemNeoForge.Checked = True
|
||||
Case FormMain.PageSubType.DownloadLiteLoader
|
||||
DlLiteLoaderListLoader.Start(IsForceRestart:=True)
|
||||
ItemLiteLoader.Checked = True
|
||||
Case FormMain.PageSubType.DownloadFabric
|
||||
DlFabricListLoader.Start(IsForceRestart:=True)
|
||||
ItemFabric.Checked = True
|
||||
End Select
|
||||
Hint("正在刷新……", Log:=False)
|
||||
End Sub
|
||||
@@ -191,55 +155,4 @@
|
||||
FrmDownloadInstall.ExitSelectPage()
|
||||
End Sub
|
||||
|
||||
'展开手动安装
|
||||
Private Sub ItemHand_Click(sender As Object, e As RouteEventArgs) Handles ItemHand.Changed
|
||||
If ItemHand.Checked = False Then Return
|
||||
e.Handled = True
|
||||
AniControlEnabled += 1
|
||||
If Not Setup.Get("HintHandInstall") Then
|
||||
Setup.Set("HintHandInstall", True)
|
||||
If MyMsgBox("手动安装包功能提供了 OptiFine、Forge 等组件的 .jar 安装文件下载,但无法自动安装。" & vbCrLf &
|
||||
"在自动安装页面先选择 MC 版本,然后就可以选择 OptiFine、Forge 等组件,让 PCL 自动进行安装了。", "自动安装提示", "返回自动安装", "继续下载手动安装包") = 1 Then
|
||||
FrmMain.PageChange(New FormMain.PageStackData With {.Page = FormMain.PageType.Download}, FormMain.PageSubType.DownloadInstall)
|
||||
AniControlEnabled -= 1
|
||||
Return
|
||||
End If
|
||||
End If
|
||||
ItemHand.Visibility = Visibility.Collapsed
|
||||
LabGame.Visibility = Visibility.Collapsed
|
||||
LabHand.Visibility = Visibility.Visible
|
||||
ItemClient.Visibility = Visibility.Visible
|
||||
ItemOptiFine.Visibility = Visibility.Visible
|
||||
ItemFabric.Visibility = Visibility.Visible
|
||||
ItemForge.Visibility = Visibility.Visible
|
||||
ItemNeoForge.Visibility = Visibility.Visible
|
||||
ItemLiteLoader.Visibility = Visibility.Visible
|
||||
RunInThread(
|
||||
Sub()
|
||||
Thread.Sleep(20)
|
||||
RunInUiWait(Sub() ItemClient.SetChecked(True, True, True))
|
||||
AniControlEnabled -= 1
|
||||
End Sub)
|
||||
End Sub
|
||||
'折叠手动安装
|
||||
Private Sub LabHand_Click(sender As Object, e As MouseButtonEventArgs) Handles LabHand.MouseLeftButtonUp
|
||||
e.Handled = True
|
||||
AniControlEnabled += 1
|
||||
ItemHand.Visibility = Visibility.Visible
|
||||
LabGame.Visibility = Visibility.Visible
|
||||
LabHand.Visibility = Visibility.Collapsed
|
||||
ItemClient.Visibility = Visibility.Collapsed
|
||||
ItemOptiFine.Visibility = Visibility.Collapsed
|
||||
ItemNeoForge.Visibility = Visibility.Collapsed
|
||||
ItemFabric.Visibility = Visibility.Collapsed
|
||||
ItemForge.Visibility = Visibility.Collapsed
|
||||
ItemLiteLoader.Visibility = Visibility.Collapsed
|
||||
RunInThread(
|
||||
Sub()
|
||||
Thread.Sleep(20)
|
||||
RunInUiWait(Sub() ItemInstall.SetChecked(True, True, True))
|
||||
AniControlEnabled -= 1
|
||||
End Sub)
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -1,26 +0,0 @@
|
||||
<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"
|
||||
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">
|
||||
<StackPanel Margin="25,40,15,20">
|
||||
<TextBlock Name="LabConnect" Margin="0,0,0,11" Text="与 Forge 类似,LiteLoader 可以用于加载老版本 Minecraft 中的 LiteLoader 模组。" TextWrapping="Wrap" />
|
||||
<Grid Height="35">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<local:MyButton x:Name="BtnWeb" Grid.Column="0" MinWidth="140" Text="打开官网" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
<StackPanel Name="PanMain" Margin="20,15,20,5" Grid.IsSharedSizeScope="True" />
|
||||
<local:MyCard HorizontalAlignment="Center" Margin="40,50" SnapsToDevicePixels="True" x:Name="PanLoad" UseAnimation="False">
|
||||
<local:MyLoading Text="正在获取 LiteLoader 列表" Margin="20,20,20,17" x:Name="Load" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</local:MyCard>
|
||||
</StackPanel>
|
||||
</local:MyScrollViewer>
|
||||
</local:MyPageRight>
|
||||
@@ -1,52 +0,0 @@
|
||||
Public Class PageDownloadLiteLoader
|
||||
|
||||
Private Sub LoaderInit() Handles Me.Initialized
|
||||
PageLoaderInit(Load, PanLoad, PanMain, CardTip, DlLiteLoaderListLoader, AddressOf Load_OnFinish)
|
||||
End Sub
|
||||
Private Sub Init() Handles Me.Loaded
|
||||
PanBack.ScrollToHome()
|
||||
End Sub
|
||||
|
||||
Private Sub Load_OnFinish()
|
||||
'结果数据化
|
||||
Try
|
||||
'归类
|
||||
Dim Dict As New Dictionary(Of String, List(Of DlLiteLoaderListEntry))
|
||||
For VersionCode As Integer = 30 To 0 Step -1
|
||||
Dict.Add("1." & VersionCode, New List(Of DlLiteLoaderListEntry))
|
||||
Next
|
||||
Dict.Add("未知版本", New List(Of DlLiteLoaderListEntry))
|
||||
For Each Version As DlLiteLoaderListEntry In DlLiteLoaderListLoader.Output.Value
|
||||
Dim MainVersion As String = "1." & Version.Inherit.Split(".")(1)
|
||||
If Dict.ContainsKey(MainVersion) Then
|
||||
Dict(MainVersion).Add(Version)
|
||||
Else
|
||||
Dict("未知版本").Add(Version)
|
||||
End If
|
||||
Next
|
||||
'清空当前
|
||||
PanMain.Children.Clear()
|
||||
'转化为 UI
|
||||
For Each Pair As KeyValuePair(Of String, List(Of DlLiteLoaderListEntry)) In Dict
|
||||
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}
|
||||
NewCard.Children.Add(NewStack)
|
||||
NewCard.SwapControl = NewStack
|
||||
NewCard.IsSwaped = True
|
||||
PanMain.Children.Add(NewCard)
|
||||
Next
|
||||
Catch ex As Exception
|
||||
Log(ex, "可视化 LiteLoader 版本列表出错", LogLevel.Feedback)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
Public Sub DownloadStart(sender As MyListItem, e As Object)
|
||||
McDownloadLiteLoader(sender.Tag)
|
||||
End Sub
|
||||
Private Sub BtnWeb_Click(sender As Object, e As EventArgs) Handles BtnWeb.Click
|
||||
OpenWebsite("https://www.liteloader.com")
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -1,27 +0,0 @@
|
||||
<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="PageDownloadNeoForge"
|
||||
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="NeoForge 简介" x:Name="CardTip">
|
||||
<StackPanel Margin="25,40,15,20">
|
||||
<TextBlock Margin="0,0,0,11" Text="NeoForge 是 Minecraft 1.20.1+ 的 Mod 加载器,你需要先安装它才能安装各种 NeoForge 模组,它也兼容一些 Forge 模组。
本页面提供 NeoForge 安装器下载,在下载后你需要手动打开安装器进行安装。" TextWrapping="Wrap" />
|
||||
<Grid Height="35">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<local:MyButton x:Name="BtnWeb" Grid.Column="0" MinWidth="140" Text="打开官网" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight"
|
||||
EventType="打开网页" EventData="https://neoforged.net/" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
<StackPanel Name="PanMain" Margin="20,15,20,5" Grid.IsSharedSizeScope="True" />
|
||||
<local:MyCard HorizontalAlignment="Center" SnapsToDevicePixels="True" x:Name="PanLoad" UseAnimation="False" Margin="40,50">
|
||||
<local:MyLoading Text="正在获取 NeoForge 列表" Margin="20,20,20,17" x:Name="Load" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</local:MyCard>
|
||||
</StackPanel>
|
||||
</local:MyScrollViewer>
|
||||
</local:MyPageRight>
|
||||
@@ -1,34 +0,0 @@
|
||||
Public Class PageDownloadNeoForge
|
||||
|
||||
Private Sub LoaderInit() Handles Me.Initialized
|
||||
PageLoaderInit(Load, PanLoad, PanMain, CardTip, DlNeoForgeListLoader, AddressOf Load_OnFinish)
|
||||
End Sub
|
||||
Private Sub Init() Handles Me.Loaded
|
||||
PanBack.ScrollToHome()
|
||||
End Sub
|
||||
|
||||
Private Sub Load_OnFinish()
|
||||
'结果数据化
|
||||
Try
|
||||
'归类
|
||||
Dim Dict = DlNeoForgeListLoader.Output.Value.GroupBy(Function(d) d.Inherit).OrderByDescending(Function(g) g.Key).
|
||||
ToDictionary(Function(g) g.Key, Function(g) g.ToList())
|
||||
'清空当前
|
||||
PanMain.Children.Clear()
|
||||
'转化为 UI
|
||||
For Each Pair As KeyValuePair(Of String, List(Of DlNeoForgeListEntry)) In Dict
|
||||
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 = 13}
|
||||
Dim NewStack As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0), .Tag = Pair.Value}
|
||||
NewCard.Children.Add(NewStack)
|
||||
NewCard.SwapControl = NewStack
|
||||
NewCard.IsSwaped = True
|
||||
PanMain.Children.Add(NewCard)
|
||||
Next
|
||||
Catch ex As Exception
|
||||
Log(ex, "可视化 NeoForge 版本列表出错", LogLevel.Feedback)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -1,26 +0,0 @@
|
||||
<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"
|
||||
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">
|
||||
<StackPanel Margin="25,40,15,20">
|
||||
<TextBlock Name="LabConnect" Margin="0,0,0,11" Text="OptiFine 又称为高清修复,以允许安装光影、使用高清材质、提高游戏性能,但与 Mod 的兼容性不佳。" TextWrapping="Wrap" />
|
||||
<Grid Height="35">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<local:MyButton x:Name="BtnWeb" Grid.Column="0" MinWidth="140" Text="打开官网" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
<StackPanel Name="PanMain" Margin="20,15,20,5" Grid.IsSharedSizeScope="True" />
|
||||
<local:MyCard HorizontalAlignment="Center" VerticalAlignment="Center" SnapsToDevicePixels="True" x:Name="PanLoad" UseAnimation="False" Margin="40,50">
|
||||
<local:MyLoading Text="正在获取 OptiFine 列表" Margin="20,20,20,17" x:Name="Load" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</local:MyCard>
|
||||
</StackPanel>
|
||||
</local:MyScrollViewer>
|
||||
</local:MyPageRight>
|
||||
@@ -1,53 +0,0 @@
|
||||
Public Class PageDownloadOptiFine
|
||||
|
||||
Private Sub LoaderInit() Handles Me.Initialized
|
||||
PageLoaderInit(Load, PanLoad, PanMain, CardTip, DlOptiFineListLoader, AddressOf Load_OnFinish)
|
||||
End Sub
|
||||
Private Sub Init() Handles Me.Loaded
|
||||
PanBack.ScrollToHome()
|
||||
End Sub
|
||||
|
||||
Private Sub Load_OnFinish()
|
||||
'结果数据化
|
||||
Try
|
||||
'归类
|
||||
Dim Dict As New Dictionary(Of String, List(Of DlOptiFineListEntry))
|
||||
Dict.Add("快照版本", New List(Of DlOptiFineListEntry))
|
||||
For VersionCode As Integer = 50 To 0 Step -1
|
||||
Dict.Add("1." & VersionCode, New List(Of DlOptiFineListEntry))
|
||||
Next
|
||||
For Each Version As DlOptiFineListEntry In DlOptiFineListLoader.Output.Value
|
||||
If Version.Inherit.StartsWith("1.") Then
|
||||
Dim MainVersion As String = "1." & Version.NameDisplay.Split(".")(1).Split(" ")(0)
|
||||
If Dict.ContainsKey(MainVersion) Then
|
||||
Dict(MainVersion).Add(Version)
|
||||
Else
|
||||
Dict("快照版本").Add(Version)
|
||||
End If
|
||||
Else
|
||||
Dict("快照版本").Add(Version)
|
||||
End If
|
||||
Next
|
||||
'清空当前
|
||||
PanMain.Children.Clear()
|
||||
'转化为 UI
|
||||
For Each Pair As KeyValuePair(Of String, List(Of DlOptiFineListEntry)) In Dict
|
||||
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}
|
||||
NewCard.Children.Add(NewStack)
|
||||
NewCard.SwapControl = NewStack
|
||||
NewCard.IsSwaped = True
|
||||
PanMain.Children.Add(NewCard)
|
||||
Next
|
||||
Catch ex As Exception
|
||||
Log(ex, "可视化 OptiFine 版本列表出错", LogLevel.Feedback)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
Private Sub BtnWeb_Click(sender As Object, e As EventArgs) Handles BtnWeb.Click
|
||||
OpenWebsite("https://www.optifine.net/")
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -1,8 +1,8 @@
|
||||
<Grid x:Class="MyMsgLogin"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:my="clr-namespace:PCL"
|
||||
RenderTransformOrigin="0,0.5" UseLayoutRounding="True" SnapsToDevicePixels="True" MinWidth="400" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="25">
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:PCL"
|
||||
RenderTransformOrigin="0,0.5" UseLayoutRounding="True" SnapsToDevicePixels="True" MinWidth="400" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="25">
|
||||
<Grid.RenderTransform>
|
||||
<TransformGroup>
|
||||
<RotateTransform x:Name="TransformRotate" Angle="-4" />
|
||||
@@ -23,17 +23,17 @@
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" FontSize="23" TextTrimming="None" Foreground="{DynamicResource ColorBrush2}" HorizontalAlignment="Left" Name="LabTitle" Margin="7,-1,70,9" Text="测试标题文本" VerticalAlignment="Top" SnapsToDevicePixels="False" UseLayoutRounding="False" />
|
||||
<Rectangle x:Name="ShapeLine" Grid.Row="1" Height="2" Fill="{Binding Foreground, ElementName=LabTitle}" />
|
||||
<my:MyScrollViewer Grid.Row="3" VerticalAlignment="Top" x:Name="PanCaption" Margin="0,0,0,17" Padding="7,0,15,0"
|
||||
<local:MyScrollViewer Grid.Row="3" VerticalAlignment="Top" x:Name="PanCaption" Margin="0,0,0,17" Padding="7,0,15,0"
|
||||
VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" DeltaMult="0.7">
|
||||
<TextBlock VerticalAlignment="Top" TextTrimming="None" TextWrapping="Wrap" Name="LabCaption" FontSize="15" LineHeight="18"
|
||||
Foreground="#FF5C5C5C" FontWeight="Normal" Padding="1" />
|
||||
</my:MyScrollViewer>
|
||||
</local:MyScrollViewer>
|
||||
<StackPanel Grid.Row="4" Name="PanBtn" VerticalAlignment="Top" HorizontalAlignment="Right" Margin="150,0,8,0" Orientation="Horizontal">
|
||||
<my:MyButton ColorType="Normal" x:FieldModifier="public" Text="重新打开网页" x:Name="Btn1" Margin="12,0,0,0" TextPadding="7" SnapsToDevicePixels="False" Padding="5,0" UseLayoutRounding="False"
|
||||
EventType="打开网页" />
|
||||
<my:MyButton ColorType="Normal" x:FieldModifier="public" Text="复制代码" x:Name="Btn2" Margin="12,0,0,0" TextPadding="7" SnapsToDevicePixels="False" Padding="5,0" UseLayoutRounding="False"
|
||||
EventType="复制文本" />
|
||||
<my:MyButton ColorType="Normal" x:FieldModifier="public" Text="取消" x:Name="Btn3" Margin="12,0,0,0" TextPadding="7" SnapsToDevicePixels="False" Padding="5,0" UseLayoutRounding="False" />
|
||||
<local:MyButton ColorType="Normal" x:FieldModifier="public" Text="重新打开网页" x:Name="Btn1" Margin="12,0,0,0" TextPadding="7" SnapsToDevicePixels="False" Padding="5,0" UseLayoutRounding="False"
|
||||
local:CustomEventService.EventType="打开网页" />
|
||||
<local:MyButton ColorType="Normal" x:FieldModifier="public" Text="复制代码" x:Name="Btn2" Margin="12,0,0,0" TextPadding="7" SnapsToDevicePixels="False" Padding="5,0" UseLayoutRounding="False"
|
||||
local:CustomEventService.EventType="复制文本" />
|
||||
<local:MyButton ColorType="Normal" x:FieldModifier="public" Text="取消" x:Name="Btn3" Margin="12,0,0,0" TextPadding="7" SnapsToDevicePixels="False" Padding="5,0" UseLayoutRounding="False" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
@@ -87,16 +87,16 @@
|
||||
LabTitle.Text = "登录 Minecraft"
|
||||
LabCaption.Text =
|
||||
$"登录网页将自动开启,请在网页中输入 {UserCode}(已自动复制)。" & vbCrLf & vbCrLf &
|
||||
$"如果网络环境不佳,网页可能一直加载不出来,届时请使用使用加速器或 VPN 以改善网络环境。" & vbCrLf &
|
||||
$"如果网络环境不佳,网页可能一直加载不出来,届时请使用加速器或 VPN 改善网络环境。" & vbCrLf &
|
||||
$"你也可以用其他设备打开 {Website} 并输入上述代码。"
|
||||
Btn1.EventData = Website
|
||||
Btn2.EventData = UserCode
|
||||
CustomEventService.SetEventData(Btn1, Website)
|
||||
CustomEventService.SetEventData(Btn2, UserCode)
|
||||
'启动工作线程
|
||||
RunInNewThread(AddressOf WorkThread, "MyMsgLogin")
|
||||
End Sub
|
||||
|
||||
Private Sub WorkThread()
|
||||
Thread.Sleep(3000)
|
||||
Thread.Sleep(2000)
|
||||
If MyConverter.IsExited Then Return
|
||||
OpenWebsite(Website)
|
||||
ClipboardSet(UserCode)
|
||||
@@ -105,35 +105,47 @@
|
||||
Dim UnknownFailureCount As Integer = 0
|
||||
Do While Not MyConverter.IsExited
|
||||
Try
|
||||
Dim Result = NetRequestOnce(
|
||||
"https://login.microsoftonline.com/consumers/oauth2/v2.0/token", "POST",
|
||||
"grant_type=urn:ietf:params:oauth:grant-type:device_code" & "&" &
|
||||
"client_id=" & OAuthClientId & "&" &
|
||||
"device_code=" & DeviceCode & "&" &
|
||||
"scope=XboxLive.signin%20offline_access",
|
||||
"application/x-www-form-urlencoded", 5000 + UnknownFailureCount * 5000, MakeLog:=False)
|
||||
Dim Result = NetRequestByClient(
|
||||
"https://login.microsoftonline.com/consumers/oauth2/v2.0/token", HttpMethod.Post,
|
||||
Content:=
|
||||
"grant_type=urn:ietf:params:oauth:grant-type:device_code" & "&" &
|
||||
"client_id=" & OAuthClientId & "&" &
|
||||
"device_code=" & DeviceCode & "&" &
|
||||
"scope=XboxLive.signin%20offline_access",
|
||||
ContentType:="application/x-www-form-urlencoded",
|
||||
Timeout:=5000 + UnknownFailureCount * 5000,
|
||||
MakeLog:=False, RequireJson:=True)
|
||||
'获取结果
|
||||
Dim ResultJson As JObject = GetJson(Result)
|
||||
McLaunchLog($"令牌过期时间:{ResultJson("expires_in")} 秒")
|
||||
Hint("网页登录成功!", HintType.Finish)
|
||||
Hint("网页登录成功!", HintType.Green)
|
||||
Finished({ResultJson("access_token").ToString, ResultJson("refresh_token").ToString})
|
||||
Return
|
||||
Catch ex As Exception
|
||||
If ex.Message.Contains("authorization_declined") Then
|
||||
Finished(New Exception("$你拒绝了 PCL 申请的权限……"))
|
||||
Return
|
||||
ElseIf ex.Message.Contains("expired_token") Then
|
||||
Finished(New Exception("$登录用时太长啦,重新试试吧!"))
|
||||
Return
|
||||
ElseIf ex.Message.Contains("service abuse") Then
|
||||
Finished(New Exception("$非常抱歉,该账号已被微软封禁,无法登录。"))
|
||||
Return
|
||||
ElseIf ex.Message.Contains("AADSTS70000") Then '可能不能判 “invalid_grant”,见 #269
|
||||
Finished(New RestartException)
|
||||
Return
|
||||
ElseIf ex.Message.Contains("authorization_pending") Then
|
||||
Thread.Sleep(2000)
|
||||
ElseIf UnknownFailureCount <= 2 Then
|
||||
'修改错误列表时,同时修改 ModLaunch.MsLoginStep1Refresh 中的对应代码
|
||||
If TypeOf ex Is ResponsedWebException Then
|
||||
Dim Response = CType(ex, ResponsedWebException).Response
|
||||
If Response.Contains("authorization_declined") Then
|
||||
Finished(New Exception("$你拒绝了 PCL 申请的权限……"))
|
||||
Return
|
||||
ElseIf Response.Contains("expired_token") Then
|
||||
Finished(New Exception("$登录用时太长啦,重新试试吧!"))
|
||||
Return
|
||||
ElseIf Response.Contains("Account security interrupt") Then
|
||||
Finished(New Exception("$该账号由于安全问题无法登陆,请前往微软账户页获取更多信息。"))
|
||||
Return
|
||||
ElseIf Response.Contains("service abuse") Then
|
||||
Finished(New Exception("$非常抱歉,该账号已被微软封禁,无法登录。"))
|
||||
Return
|
||||
ElseIf Response.Contains("AADSTS70000") Then '可能不能判 “invalid_grant”,见 #269
|
||||
Finished(New RestartException)
|
||||
Return
|
||||
ElseIf Response.Contains("authorization_pending") Then
|
||||
Thread.Sleep(2000)
|
||||
Continue Do
|
||||
End If
|
||||
End If
|
||||
If UnknownFailureCount <= 2 Then
|
||||
UnknownFailureCount += 1
|
||||
Log(ex, $"登录轮询第 {UnknownFailureCount} 次失败")
|
||||
Thread.Sleep(2000)
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
Public Shared Sub Save(Loader As LoaderTask(Of EqualableList(Of String), String))
|
||||
Dim Address = Loader.Output
|
||||
If Not Loader.State = LoadState.Finished Then
|
||||
Hint("皮肤正在获取中,请稍候!", HintType.Critical)
|
||||
Hint("皮肤正在获取中,请稍候!", HintType.Red)
|
||||
If Not Loader.State = LoadState.Loading Then Loader.Start()
|
||||
Return
|
||||
End If
|
||||
@@ -61,7 +61,7 @@
|
||||
Else
|
||||
CopyFile(Address, FileAddress)
|
||||
End If
|
||||
Hint("皮肤保存成功!", HintType.Finish)
|
||||
Hint("皮肤保存成功!", HintType.Green)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "保存皮肤失败", LogLevel.Hint)
|
||||
@@ -143,7 +143,7 @@
|
||||
Next
|
||||
If FrmLaunchLeft IsNot Nothing AndAlso HasLoaderRunning Then
|
||||
'由于 Abort 不是实时的,暂时不会释放文件,会导致删除报错,故只能取消执行
|
||||
Hint("有正在获取中的皮肤,请稍后再试!", HintType.Info)
|
||||
Hint("有正在获取中的皮肤,请稍后再试!", HintType.Blue)
|
||||
Else
|
||||
RunInThread(
|
||||
Sub()
|
||||
@@ -161,7 +161,7 @@
|
||||
For Each SkinLoader In If(sender IsNot Nothing, {sender}, {PageLaunchLeft.SkinLegacy, PageLaunchLeft.SkinMs})
|
||||
SkinLoader.WaitForExit(IsForceRestart:=True)
|
||||
Next
|
||||
Hint("已刷新头像!", HintType.Finish)
|
||||
Hint("已刷新头像!", HintType.Green)
|
||||
Catch ex As Exception
|
||||
Log(ex, "刷新皮肤缓存失败", LogLevel.Msgbox)
|
||||
End Try
|
||||
@@ -184,7 +184,7 @@
|
||||
SkinLoader.WaitForExit(IsForceRestart:=True)
|
||||
Next
|
||||
'完成提示
|
||||
Hint("更改皮肤成功!", HintType.Finish)
|
||||
Hint("更改皮肤成功!", HintType.Green)
|
||||
Catch ex As Exception
|
||||
Log(ex, "更改正版皮肤后刷新皮肤失败", LogLevel.Feedback)
|
||||
End Try
|
||||
@@ -212,7 +212,7 @@
|
||||
Return
|
||||
End If
|
||||
If McLoginMsLoader.State = LoadState.Failed Then
|
||||
Hint("登录失败,无法更改披风!", HintType.Critical)
|
||||
Hint("登录失败,无法更改披风!", HintType.Red)
|
||||
Return
|
||||
End If
|
||||
Hint("正在获取披风列表,请稍候……")
|
||||
@@ -225,7 +225,7 @@ Retry:
|
||||
'获取登录信息
|
||||
If McLoginMsLoader.State <> LoadState.Finished Then McLoginMsLoader.WaitForExit(PageLoginMsSkin.GetLoginData())
|
||||
If McLoginMsLoader.State <> LoadState.Finished Then
|
||||
Hint("登录失败,无法更改披风!", HintType.Critical)
|
||||
Hint("登录失败,无法更改披风!", HintType.Red)
|
||||
Return
|
||||
End If
|
||||
Dim AccessToken As String = McLoginMsLoader.Output.AccessToken
|
||||
@@ -244,7 +244,7 @@ Retry:
|
||||
{"Cherry Blossom", "樱花披风"}, {"15th Anniversary", "15 周年纪念披风"}, {"Purple Heart", "紫色心形披风"},
|
||||
{"Follower's", "追随者披风"}, {"MCC 15th Year", "MCC 15 周年披风"}, {"Minecraft Experience", "村民救援披风"},
|
||||
{"Mojang Office", "Mojang 办公室披风"}, {"Home", "家园披风"}, {"Menace", "入侵披风"}, {"Yearn", "渴望披风"},
|
||||
{"Common", "普通披风"}, {"Pan", "薄煎饼披风"}, {"Founder's", "创始人披风"}
|
||||
{"Common", "普通披风"}, {"Pan", "薄煎饼披风"}, {"Founder's", "创始人披风"}, {"Copper", "铜披风"}
|
||||
}
|
||||
Dim SelectionControl As New List(Of IMyRadio) From {New MyRadioBox With {.Text = "无披风"}}
|
||||
For Each Cape In SkinData("capes")
|
||||
@@ -259,15 +259,16 @@ Retry:
|
||||
End Sub)
|
||||
If SelId Is Nothing Then Return
|
||||
'发送请求
|
||||
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}})
|
||||
Dim Result As String = NetRequestByClientRetry("https://api.minecraftservices.com/minecraft/profile/capes/active",
|
||||
If(SelId = 0, HttpMethod.Delete, HttpMethod.Put),
|
||||
Content:=If(SelId = 0, "", New JObject(New JProperty("capeId", SkinData("capes")(SelId - 1)("id"))).ToString(0)),
|
||||
ContentType:="application/json",
|
||||
Headers:={{"Authorization", "Bearer " & AccessToken}})
|
||||
If Result.Contains("""errorMessage""") Then
|
||||
Hint("更改披风失败:" & GetJson(Result)("errorMessage"), HintType.Critical)
|
||||
Hint("更改披风失败:" & GetJson(Result)("errorMessage").ToString, HintType.Red)
|
||||
Return
|
||||
Else
|
||||
Hint("更改披风成功!", HintType.Finish)
|
||||
Hint("更改披风成功!", HintType.Green)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "更改披风失败", LogLevel.Hint)
|
||||
|
||||
@@ -110,11 +110,7 @@
|
||||
'修改登陆方式
|
||||
Select Case Setup.Get("LoginType")
|
||||
Case McLoginType.Legacy
|
||||
If PageLinkHiper.HiperState = LoadState.Finished Then
|
||||
LabLaunchingMethod.Text = "联机离线登录"
|
||||
Else
|
||||
LabLaunchingMethod.Text = "离线登录"
|
||||
End If
|
||||
LabLaunchingMethod.Text = "离线登录"
|
||||
Case McLoginType.Ms
|
||||
LabLaunchingMethod.Text = "正版登录"
|
||||
Case McLoginType.Nide
|
||||
@@ -243,19 +239,21 @@
|
||||
If Not IsNothing(PageNew) AndAlso Not IsNothing(PageNew.Parent) Then PageNew.SetValue(ContentPresenter.ContentProperty, Nothing)
|
||||
If Anim Then
|
||||
'动画
|
||||
Dispatcher.Invoke(Sub()
|
||||
'执行动画
|
||||
AniStart({
|
||||
AaOpacity(PanLogin, -PanLogin.Opacity, 100,, New AniEaseOutFluent),
|
||||
AaCode(Sub()
|
||||
AniControlEnabled += 1
|
||||
PanLogin.Children.Clear()
|
||||
PanLogin.Children.Add(PageNew)
|
||||
AniControlEnabled -= 1
|
||||
End Sub, 100),
|
||||
AaOpacity(PanLogin, 1, 100, 120, New AniEaseInFluent)
|
||||
}, "FrmLogin PageChange")
|
||||
End Sub, Threading.DispatcherPriority.Render)
|
||||
Dispatcher.Invoke(
|
||||
Sub()
|
||||
'执行动画
|
||||
AniStart({
|
||||
AaOpacity(PanLogin, -PanLogin.Opacity, 100,, New AniEaseOutFluent),
|
||||
AaCode(
|
||||
Sub()
|
||||
AniControlEnabled += 1
|
||||
PanLogin.Children.Clear()
|
||||
PanLogin.Children.Add(PageNew)
|
||||
AniControlEnabled -= 1
|
||||
End Sub, 100),
|
||||
AaOpacity(PanLogin, 1, 100, 120, New AniEaseInFluent)
|
||||
}, "FrmLogin PageChange")
|
||||
End Sub, Threading.DispatcherPriority.Render)
|
||||
Else
|
||||
'无动画
|
||||
AniControlEnabled += 1
|
||||
@@ -390,14 +388,14 @@ UnknownType:
|
||||
Result = McSkinDownload(Result)
|
||||
If Data.IsAborted Then Throw New ThreadInterruptedException("当前任务已取消:" & UserName)
|
||||
Data.Output = Result
|
||||
Catch ex As ThreadInterruptedException
|
||||
Data.Output = ""
|
||||
Return
|
||||
Catch ex As Exception
|
||||
If ex.GetType.Name = "ThreadInterruptedException" Then
|
||||
Data.Output = ""
|
||||
Return
|
||||
ElseIf GetExceptionSummary(ex).Contains("429") Then
|
||||
If ex.GetBrief.Contains("(429)") Then
|
||||
Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(UserName)) & ".png"
|
||||
Log("[Minecraft] 获取正版皮肤失败(" & UserName & "):获取皮肤太过频繁,请 5 分钟后再试!", LogLevel.Hint)
|
||||
ElseIf GetExceptionSummary(ex).Contains("未设置自定义皮肤") Then
|
||||
ElseIf ex.GetBrief.Contains("未设置自定义皮肤") Then
|
||||
Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(UserName)) & ".png"
|
||||
Log("[Minecraft] 用户未设置自定义皮肤,跳过皮肤加载")
|
||||
Else
|
||||
@@ -460,11 +458,11 @@ UseDefault:
|
||||
If Data.IsAborted Then Throw New ThreadInterruptedException("当前任务已取消:" & ID)
|
||||
Data.Output = Result
|
||||
End If
|
||||
Catch ex As ThreadInterruptedException
|
||||
Data.Output = ""
|
||||
Return
|
||||
Catch ex As Exception
|
||||
If ex.GetType.Name = "ThreadInterruptedException" Then
|
||||
Data.Output = ""
|
||||
Return
|
||||
ElseIf GetExceptionSummary(ex).Contains("429") Then
|
||||
If ex.GetBrief.Contains("(429)") Then
|
||||
Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(ID)) & ".png"
|
||||
Log("获取离线登录使用的正版皮肤失败(" & ID & "):获取皮肤太过频繁,请 5 分钟后再试!")
|
||||
Else
|
||||
@@ -512,14 +510,14 @@ UseDefault:
|
||||
Result = McSkinDownload(Result)
|
||||
If Data.IsAborted Then Throw New ThreadInterruptedException("当前任务已取消:" & UserName)
|
||||
Data.Output = Result
|
||||
Catch ex As ThreadInterruptedException
|
||||
Data.Output = ""
|
||||
Return
|
||||
Catch ex As Exception
|
||||
If ex.GetType.Name = "ThreadInterruptedException" Then
|
||||
Data.Output = ""
|
||||
Return
|
||||
ElseIf GetExceptionSummary(ex).Contains("429") Then
|
||||
If ex.GetBrief.Contains("(429)") Then
|
||||
Data.Output = PathImage & "Skins/Steve.png"
|
||||
Log("[Minecraft] 获取统一通行证皮肤失败(" & UserName & "):获取皮肤太过频繁,请 5 分钟后再试!", LogLevel.Hint)
|
||||
ElseIf GetExceptionSummary(ex).Contains("未设置自定义皮肤") Then
|
||||
ElseIf ex.GetBrief.Contains("未设置自定义皮肤") Then
|
||||
Data.Output = PathImage & "Skins/Steve.png"
|
||||
Log("[Minecraft] 用户未设置自定义皮肤,跳过皮肤加载")
|
||||
Else
|
||||
@@ -548,26 +546,26 @@ Finish:
|
||||
RunInUi(Sub() If FrmLoginAuthSkin IsNot Nothing AndAlso FrmLoginAuthSkin.Skin IsNot Nothing Then FrmLoginAuthSkin.Skin.Clear())
|
||||
'获取 Url
|
||||
Dim UserName As String = Data.Input(0)
|
||||
Dim Uuid As String = Data.Input(1)
|
||||
Dim UUID As String = Data.Input(1)
|
||||
If UserName = "" Then
|
||||
Data.Output = PathImage & "Skins/Steve.png"
|
||||
Log("[Minecraft] 获取 Authlib-Injector 皮肤失败,ID 为空")
|
||||
GoTo Finish
|
||||
End If
|
||||
Try
|
||||
Dim Result As String = McSkinGetAddress(Uuid, "Auth")
|
||||
Dim Result As String = McSkinGetAddress(UUID, "Auth")
|
||||
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 ThreadInterruptedException
|
||||
Data.Output = ""
|
||||
Return
|
||||
Catch ex As Exception
|
||||
If ex.GetType.Name = "ThreadInterruptedException" Then
|
||||
Data.Output = ""
|
||||
Return
|
||||
ElseIf GetExceptionSummary(ex).Contains("429") Then
|
||||
If ex.GetBrief.Contains("(429)") Then
|
||||
Data.Output = PathImage & "Skins/Steve.png"
|
||||
Log("[Minecraft] 获取 Authlib-Injector 皮肤失败(" & UserName & "):获取皮肤太过频繁,请 5 分钟后再试!", LogLevel.Hint)
|
||||
ElseIf GetExceptionSummary(ex).Contains("未设置自定义皮肤") Then
|
||||
ElseIf ex.GetBrief.Contains("未设置自定义皮肤") Then
|
||||
Data.Output = PathImage & "Skins/Steve.png"
|
||||
Log("[Minecraft] 用户未设置自定义皮肤,跳过皮肤加载")
|
||||
Else
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:PCL"> <!-- 不知道为啥只有这个文件不能在 XAML 设置 PanScroll -->
|
||||
xmlns:local="clr-namespace:PCL" PanScroll="PanBack">
|
||||
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
|
||||
<StackPanel Name="PanMain" Margin="25,25,25,10" Grid.IsSharedSizeScope="True">
|
||||
<StackPanel x:Name="PanCustom">
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
Public Class PageLaunchRight
|
||||
Implements IRefreshable
|
||||
Imports System.Windows.Threading
|
||||
|
||||
Public Class PageLaunchRight
|
||||
Implements IRefreshable, IDispatcherUnhandledException
|
||||
|
||||
Private Sub Init() Handles Me.Loaded
|
||||
PanBack.ScrollToHome()
|
||||
PanScroll = PanBack '不知道为啥不能在 XAML 设置
|
||||
PanLog.Visibility = If(ModeDebug, Visibility.Visible, Visibility.Collapsed)
|
||||
'快照版提示
|
||||
#If BETA Then
|
||||
@@ -28,7 +29,7 @@
|
||||
''' 刷新主页。
|
||||
''' </summary>
|
||||
Private Sub Refresh() Handles Me.Loaded
|
||||
RunInNewThread(
|
||||
RunInThread(
|
||||
Sub()
|
||||
Try
|
||||
SyncLock RefreshLock
|
||||
@@ -37,37 +38,20 @@
|
||||
Catch ex As Exception
|
||||
Log(ex, "加载 PCL 主页自定义信息失败", If(ModeDebug, LogLevel.Msgbox, LogLevel.Hint))
|
||||
End Try
|
||||
End Sub, $"刷新主页 #{GetUuid()}")
|
||||
End Sub)
|
||||
End Sub
|
||||
Private Sub RefreshReal()
|
||||
Dim Content As String = ""
|
||||
Dim Url As String
|
||||
Dim Content As String = Nothing, Url As String = Nothing
|
||||
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) '下载完成后将会再次触发更新
|
||||
Return
|
||||
End If
|
||||
Case 3
|
||||
'预设
|
||||
Select Case Setup.Get("UiCustomPreset")
|
||||
Case 0
|
||||
Log("[Page] 主页预设:你知道吗")
|
||||
@@ -89,59 +73,73 @@ Download:
|
||||
</local:MyCard>"
|
||||
Case 2
|
||||
Log("[Page] 主页预设:Minecraft 新闻")
|
||||
Url = "http://pcl.mcnews.thestack.top"
|
||||
GoTo Download
|
||||
Url = "https://mcnews.meloong.com"
|
||||
Case 3
|
||||
Log("[Page] 主页预设:简单主页")
|
||||
Url = "https://raw.gitcode.com/mfn233/PCL-Mainpage/raw/main/Custom.xaml"
|
||||
GoTo Download
|
||||
Url = "https://pclhomeplazaoss.lingyunawa.top:26994/d/Homepages/MFn233/Custom.xaml"
|
||||
Case 4
|
||||
Log("[Page] 主页预设:每日整合包推荐")
|
||||
Url = "https://pclsub.sodamc.com/"
|
||||
GoTo Download
|
||||
Case 5
|
||||
Log("[Page] 主页预设:Minecraft 皮肤推荐")
|
||||
Url = "https://forgepixel.com/pcl_sub_file"
|
||||
GoTo Download
|
||||
Case 6
|
||||
Log("[Page] 主页预设:OpenBMCLAPI 仪表盘 Lite")
|
||||
Url = "https://pcl-bmcl.milu.ink/"
|
||||
GoTo Download
|
||||
Case 7
|
||||
Log("[Page] 主页预设:主页市场")
|
||||
Url = "https://pclhomeplazaoss.lingyunawa.top:26994/d/Homepages/JingHai-Lingyun/Custom.xaml"
|
||||
GoTo Download
|
||||
Case 8
|
||||
Log("[Page] 主页预设:更新日志")
|
||||
Url = "https://pclhomeplazaoss.lingyunawa.top:26994/d/Homepages/Joker2184/UpdateHomepage.xaml"
|
||||
GoTo Download
|
||||
Case 9
|
||||
Log("[Page] 主页预设:PCL 新功能说明书")
|
||||
Url = "https://raw.gitcode.com/WForst-Breeze/WhatsNewPCL/raw/main/Custom.xaml"
|
||||
GoTo Download
|
||||
Case 10
|
||||
Log("[Page] 主页预设:OpenMCIM Dashboard")
|
||||
Url = "https://files.mcimirror.top/PCL"
|
||||
GoTo Download
|
||||
Case 11
|
||||
Log("[Page] 主页预设:杂志主页")
|
||||
Url = "https://pclhomeplazaoss.lingyunawa.top:26994/d/Homepages/Ext1nguisher/Custom.xaml"
|
||||
GoTo Download
|
||||
Case 12
|
||||
Log("[Page] 主页预设:PCL GitHub 仪表盘")
|
||||
Url = "https://raw.gitcode.com/Deep-Dark-Forest/PCL2-GitHub-Dashboard-Homepage/raw/main/custom.xaml"
|
||||
GoTo Download
|
||||
Url = "https://ddf.pcl-community.org/Custom.xaml"
|
||||
Case 13
|
||||
Log("[Page] 主页预设:PCL 更新摘要")
|
||||
Url = "https://raw.gitcode.com/ENC_Euphony/PCL-AI-Summary-HomePage/raw/master/Custom.xaml"
|
||||
End Select
|
||||
End Select
|
||||
'联网下载
|
||||
If Not String.IsNullOrWhiteSpace(Url) Then
|
||||
If Url = Setup.Get("CacheSavedPageUrl") AndAlso File.Exists(PathTemp & "Cache\Custom.xaml") Then
|
||||
'缓存可用
|
||||
Log("[Page] 主页自定义数据来源:联网缓存文件")
|
||||
Content = ReadFile(PathTemp & "Cache\Custom.xaml")
|
||||
'后台更新缓存
|
||||
OnlineLoader.Start(New Tuple(Of String, Boolean)(Url, False))
|
||||
Else
|
||||
'缓存不可用
|
||||
Log("[Page] 主页自定义数据来源:联网全新下载")
|
||||
Hint("正在加载主页……")
|
||||
RunInUiWait(Sub() LoadContent(Nothing)) '在加载结束前清空页面
|
||||
Setup.Set("CacheSavedPageVersion", "")
|
||||
OnlineLoader.Start(New Tuple(Of String, Boolean)(Url, True)) '下载完成后将会再次触发更新
|
||||
Return
|
||||
End If
|
||||
End If
|
||||
'同步到 UI
|
||||
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))
|
||||
Dim Address As String = Task.Input '#3721 中连续触发两次导致内容变化
|
||||
Private OnlineLoader As New LoaderTask(Of Tuple(Of String, Boolean), Integer)("下载主页", AddressOf OnlineLoaderSub) With {.ReloadTimeout = 10 * 60 * 1000}
|
||||
Private Sub OnlineLoaderSub(Task As LoaderTask(Of Tuple(Of String, Boolean), Integer))
|
||||
Dim Address As String = Task.Input.Item1 '#3721 中连续触发两次导致内容变化
|
||||
Dim ShouldRefresh As Boolean = Task.Input.Item2
|
||||
Try
|
||||
'替换自定义变量与设置
|
||||
Address = ArgumentReplace(Address, AddressOf WebUtility.HtmlEncode)
|
||||
'获取版本校验地址
|
||||
Dim VersionAddress As String
|
||||
If Address.Contains(".xaml") Then
|
||||
@@ -154,31 +152,27 @@ Download:
|
||||
End If
|
||||
'校验版本
|
||||
Dim Version As String = ""
|
||||
Dim NeedDownload As Boolean = True
|
||||
Try
|
||||
Version = NetGetCodeByRequestOnce(VersionAddress, Timeout:=10000)
|
||||
Version = NetRequestByClientRetry(VersionAddress)
|
||||
If Version.Length > 1000 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}")
|
||||
Return
|
||||
End If
|
||||
Log($"[Page] 需要下载联网主页,当前版本:{Version},检查源:{VersionAddress}")
|
||||
Catch exx As Exception
|
||||
Log(exx, $"联网获取主页版本失败", LogLevel.Developer)
|
||||
Log($"[Page] 无法检查联网主页版本,将直接下载,检查源:{VersionAddress}")
|
||||
End Try
|
||||
'实际下载
|
||||
If NeedDownload Then
|
||||
Dim FileContent As String = NetGetCodeByRequestRetry(Address)
|
||||
Log($"[Page] 已联网下载主页,内容长度:{FileContent.Length},来源:{Address}")
|
||||
Setup.Set("CacheSavedPageUrl", Address)
|
||||
Setup.Set("CacheSavedPageVersion", Version)
|
||||
WriteFile(PathTemp & "Cache\Custom.xaml", FileContent)
|
||||
End If
|
||||
'要求刷新
|
||||
RunInUi(AddressOf Refresh) '不直接调用 Refresh,以防止死循环(#6245)
|
||||
Dim FileContent As String = NetRequestByClientRetry(Address)
|
||||
Log($"[Page] 已联网下载主页,内容长度:{FileContent.Length},来源:{Address}")
|
||||
Setup.Set("CacheSavedPageUrl", Address)
|
||||
Setup.Set("CacheSavedPageVersion", Version)
|
||||
WriteFile(PathTemp & "Cache\Custom.xaml", FileContent)
|
||||
'若内容变更则要求刷新
|
||||
If LoadedContentHash <> FileContent.GetHashCode() AndAlso ShouldRefresh Then Refresh()
|
||||
Catch ex As Exception
|
||||
Log(ex, $"下载主页失败({Address})", If(ModeDebug, LogLevel.Msgbox, LogLevel.Hint))
|
||||
End Try
|
||||
@@ -205,7 +199,7 @@ Download:
|
||||
''' </summary>
|
||||
Private Sub ClearCache()
|
||||
LoadedContentHash = -1
|
||||
OnlineLoader.Input = ""
|
||||
OnlineLoader.Input = New Tuple(Of String, Boolean)("", True)
|
||||
Setup.Set("CacheSavedPageUrl", "")
|
||||
Setup.Set("CacheSavedPageVersion", "")
|
||||
Log("[Page] 已清空主页缓存")
|
||||
@@ -216,45 +210,59 @@ Download:
|
||||
''' 必须在 UI 线程调用。
|
||||
''' </summary>
|
||||
Private Sub LoadContent(Content As String)
|
||||
SyncLock LoadContentLock
|
||||
'如果加载目标内容一致则不加载
|
||||
Dim Hash = Content.GetHashCode()
|
||||
If Hash = LoadedContentHash Then Return
|
||||
LoadedContentHash = Hash
|
||||
'实际加载内容
|
||||
PanCustom.Children.Clear()
|
||||
If String.IsNullOrWhiteSpace(Content) Then
|
||||
Log($"[Page] 实例化:清空主页 UI,来源为空")
|
||||
Return
|
||||
End If
|
||||
Dim LoadStartTime As Date = Date.Now
|
||||
Try
|
||||
Try
|
||||
SyncLock LoadContentLock
|
||||
'如果加载目标内容一致则不加载
|
||||
Dim Hash = If(Content, "").GetHashCode()
|
||||
If Hash = LoadedContentHash Then Return
|
||||
LoadedContentHash = Hash
|
||||
'实际加载内容
|
||||
PanCustom.Children.Clear()
|
||||
If String.IsNullOrWhiteSpace(Content) Then
|
||||
Log($"[Page] 实例化:清空主页 UI,来源为空")
|
||||
Return
|
||||
End If
|
||||
Dim LoadStartTime As Date = Date.Now
|
||||
'修改时应同时修改 PageOtherHelpDetail.Init
|
||||
Content = HelpArgumentReplace(Content)
|
||||
If Content.Contains("xmlns") Then Content = Content.RegexReplace("xmlns[^""']*(""|')[^""']*(""|')", "").Replace("xmlns", "") '禁止声明命名空间
|
||||
Content = ArgumentReplace(Content, AddressOf EscapeXML)
|
||||
Do While Content.Contains("xmlns")
|
||||
Content = Content.RegexReplace("xmlns[^""']*(""|')[^""']*(""|')", "").Replace("xmlns", "") '禁止声明命名空间
|
||||
Loop
|
||||
Content = "<StackPanel xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:sys=""clr-namespace:System;assembly=mscorlib"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" xmlns:local=""clr-namespace:PCL;assembly=Plain Craft Launcher 2"">" & Content & "</StackPanel>"
|
||||
Log($"[Page] 实例化:加载主页 UI 开始,最终内容长度:{Content.Count}")
|
||||
PanCustom.Children.Add(GetObjectFromXML(Content))
|
||||
Catch ex As Exception
|
||||
If ModeDebug Then
|
||||
Log(ex, "加载失败的主页内容:" & vbCrLf & Content)
|
||||
If MyMsgBox(If(TypeOf ex Is UnauthorizedAccessException, ex.Message, $"主页内容编写有误,请根据下列错误信息进行检查:{vbCrLf}{GetExceptionSummary(ex)}"),
|
||||
"加载主页界面失败", "重试", "取消") = 1 Then
|
||||
GoTo Refresh '防止 SyncLock 死锁
|
||||
End If
|
||||
Else
|
||||
Log(ex, "加载主页界面失败", LogLevel.Hint)
|
||||
End If
|
||||
Return
|
||||
End Try
|
||||
Dim LoadCostTime = (Date.Now - LoadStartTime).Milliseconds
|
||||
Log($"[Page] 实例化:加载主页 UI 完成,耗时 {LoadCostTime}ms")
|
||||
If LoadCostTime > 3000 Then Hint($"主页加载过于缓慢(花费了 {Math.Round(LoadCostTime / 1000, 1)} 秒),请向主页作者反馈此问题,或暂时停止使用该主页")
|
||||
End SyncLock
|
||||
Return
|
||||
Refresh:
|
||||
ForceRefresh()
|
||||
'加载计时
|
||||
Dim LoadCostTime = (Date.Now - LoadStartTime).Milliseconds
|
||||
Log($"[Page] 实例化:加载主页 UI 完成,耗时 {LoadCostTime}ms")
|
||||
If LoadCostTime > 3000 Then Hint($"主页加载过于缓慢(花费了 {Math.Round(LoadCostTime / 1000, 1)} 秒),请向主页作者反馈此问题,或暂时停止使用该主页")
|
||||
End SyncLock
|
||||
Catch ex As Exception
|
||||
Log(ex, "加载失败的主页内容:" & vbCrLf & Content)
|
||||
OnLoadContentFailed(ex)
|
||||
End Try
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 加载主页失败时调用。
|
||||
''' </summary>
|
||||
Private Sub OnLoadContentFailed(ex As Exception)
|
||||
If ModeDebug OrElse Setup.Get("UiCustomType") = 1 Then
|
||||
Log(ex, "加载主页失败")
|
||||
If MyMsgBox(If(TypeOf ex Is UnauthorizedAccessException, ex.Message, $"主页内容编写有误,请根据下列错误信息进行检查:{vbCrLf}{ex.GetBrief}"),
|
||||
"加载主页失败", "重试", "取消") = 1 Then ForceRefresh()
|
||||
Else
|
||||
Log(ex, "加载主页失败", LogLevel.Hint)
|
||||
End If
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 捕获主页在 Measure 和 Arrange 阶段抛出的异常。
|
||||
''' </summary>
|
||||
Private Sub DispatcherUnhandledException(sender As Object, e As DispatcherUnhandledExceptionEventArgs) Implements IDispatcherUnhandledException.DispatcherUnhandledException
|
||||
If TypeOf e.Exception IsNot Markup.XamlParseException Then Return
|
||||
e.Handled = True
|
||||
LoadContent(Nothing)
|
||||
OnLoadContentFailed(e.Exception)
|
||||
End Sub
|
||||
|
||||
Private LoadedContentHash As Integer = -1
|
||||
Private LoadContentLock As New Object
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
If CType(McLoginLoader.Input, McLoginServer).ForceReselectProfile Then
|
||||
Hint("正在尝试更换,请稍候!")
|
||||
Else
|
||||
Hint("正在登录中,请稍后再更换角色!", HintType.Critical)
|
||||
Hint("正在登录中,请稍后再更换角色!", HintType.Red)
|
||||
End If
|
||||
Return
|
||||
End If
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:PCL"
|
||||
mc:Ignorable="d" d:DesignWidth="297.6">
|
||||
<StackPanel Margin="0,0,0,5">
|
||||
<StackPanel Margin="0,0,0,5" x:Name="PanMain">
|
||||
<local:MyHint x:Name="HintChinese" Theme="Red" Text="玩家名如果包含英文、数字、下划线以外的内容,可能会无法进入 Minecraft 1.18 以后的世界!" Margin="0,0,0,15" Visibility="Collapsed" />
|
||||
<local:MySkin HorizontalAlignment="Center" x:Name="Skin" HasCape="False" Margin="0,0,0,15" />
|
||||
<local:MyComboBox IsEditable="True" x:Name="ComboName" Height="28" IsTextSearchEnabled="False" HintText="游戏用户名" />
|
||||
|
||||
@@ -64,10 +64,11 @@ Public Class PageLoginLegacy
|
||||
Private Sub ComboLegacy_TextChanged(sender As Object, e As TextChangedEventArgs) Handles ComboName.TextChanged
|
||||
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)
|
||||
RunInUi(Sub() PanMain.InvalidateMeasure(), True) '由于 WPF 的 Bug,它不一定会自动更新 HintChinese 的大小(#6627)
|
||||
End Sub
|
||||
Private Sub Skin_Click() Handles Skin.Click
|
||||
If (Setup.Get("UiHiddenPageSetup") OrElse Setup.Get("UiHiddenSetupLaunch")) AndAlso Not PageSetupUI.HiddenForceShow Then
|
||||
Hint("启动设置已被禁用!", HintType.Critical)
|
||||
Hint("启动设置已被禁用!", HintType.Red)
|
||||
Else
|
||||
FrmMain.PageChange(FormMain.PageType.Setup, FormMain.PageSubType.SetupLaunch) '切换到皮肤设置页面
|
||||
End If
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
<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"
|
||||
<local:MyTextButton Text="» 购买正版" local:CustomEventService.EventType="打开网页" local:CustomEventService.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"
|
||||
<local:MyTextButton Text="» 前往官网" local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://www.minecraft.net/zh-hans"
|
||||
Grid.Column="2" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
Catch ex As Exception
|
||||
If ex.Message = "$$" Then
|
||||
ElseIf ex.Message.StartsWith("$") Then
|
||||
Hint(ex.Message.TrimStart("$"), HintType.Critical)
|
||||
Hint(ex.Message.TrimStart("$"), HintType.Red)
|
||||
ElseIf TypeOf ex Is Security.Authentication.AuthenticationException AndAlso ex.Message.ContainsF("SSL/TLS") Then
|
||||
Log(ex, "正版登录验证失败,请考虑在 [设置 → 其他] 中关闭 [在正版登录时验证 SSL 证书],然后再试。" & vbCrLf & vbCrLf & "原始错误信息:", LogLevel.Msgbox)
|
||||
Else
|
||||
|
||||
@@ -23,10 +23,11 @@
|
||||
<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="刷新头像"/>
|
||||
<local:MyMenuItem Click="BtnSkinSave_Click" Header="保存皮肤文件"/>
|
||||
<Separator />
|
||||
<local:MyMenuItem Click="BtnSkinCape_Click" Header="修改披风"/>
|
||||
<Separator />
|
||||
<local:MyMenuItem Click="BtnSkinRefresh_Click" Header="刷新"/>
|
||||
</ContextMenu>
|
||||
</local:MyIconButton.ContextMenu>
|
||||
</local:MyIconButton>
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
Return
|
||||
End If
|
||||
If McLoginLoader.State = LoadState.Failed Then
|
||||
Hint("登录失败,无法更改皮肤!", HintType.Critical)
|
||||
Hint("登录失败,无法更改皮肤!", HintType.Red)
|
||||
Return
|
||||
End If
|
||||
Dim SkinInfo As McSkinInfo = McSkinSelect()
|
||||
@@ -86,28 +86,27 @@
|
||||
IsChanging = True
|
||||
'开始实际获取
|
||||
RunInNewThread(
|
||||
Async Sub()
|
||||
Sub()
|
||||
Try
|
||||
Retry:
|
||||
If McLoginMsLoader.State = LoadState.Loading Then McLoginMsLoader.WaitForExit() '等待登录结束
|
||||
Do While McLoginMsLoader.State = LoadState.Loading '等待登录结束
|
||||
Thread.Sleep(10)
|
||||
Loop
|
||||
If McLoginMsLoader.State = LoadState.Failed Then Throw New Exception("登录失败", McLoginMsLoader.Error)
|
||||
Dim AccessToken As String = Setup.Get("CacheMsV2Access")
|
||||
Dim Uuid As String = Setup.Get("CacheMsV2Uuid")
|
||||
|
||||
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(ReadFileBytes(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
|
||||
Dim Result As String = NetRequestByClientRetry("https://api.minecraftservices.com/minecraft/profile/skins", HttpMethod.Post,
|
||||
Content:=New Net.Http.MultipartFormDataContent From {
|
||||
{New Net.Http.StringContent(If(SkinInfo.IsSlim, "slim", "classic")), "variant"},
|
||||
{New Net.Http.ByteArrayContent(ReadFileBytes(SkinInfo.LocalFile)), "file", GetFileNameFromPath(SkinInfo.LocalFile)}
|
||||
},
|
||||
Headers:={{"Authorization", "Bearer " & AccessToken}, {"Accept", "*/*"}, {"User-Agent", "MojangSharp/0.1"}})
|
||||
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)
|
||||
Hint("更改皮肤失败:" & GetJson(Result)("error").ToString, HintType.Red)
|
||||
Return
|
||||
End If
|
||||
'获取新皮肤地址
|
||||
@@ -122,8 +121,8 @@ Retry:
|
||||
Next
|
||||
Throw New Exception("未知错误(" & Result & ")")
|
||||
Catch ex As Exception
|
||||
If ex.GetType.Equals(GetType(Tasks.TaskCanceledException)) Then
|
||||
Hint("更改皮肤失败:与 Mojang 皮肤服务器的连接超时,请检查你的网络是否通畅!", HintType.Critical)
|
||||
If TypeOf ex Is OperationCanceledException OrElse TypeOf ex Is TimeoutException Then
|
||||
Hint("更改皮肤失败:连接皮肤服务器超时,请稍后再试,或使用 VPN 改善网络环境", HintType.Red)
|
||||
Else
|
||||
Log(ex, "更改皮肤失败", LogLevel.Hint)
|
||||
End If
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
<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"
|
||||
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">
|
||||
<StackPanel Margin="20,40,18,18">
|
||||
<TextBlock TextWrapping="Wrap" Text="多人联机服务由速聚授权提供,PCL 仅为速聚提供多人联机服务入口,使用中遇到的任何问题由速聚负责处理。你在使用多人联机服务过程中所遇到的任何问题与纠纷(包括其付费业务)均与 PCL 无关,应与速聚协商解决。"
|
||||
Margin="0,0,0,3" />
|
||||
<TextBlock TextWrapping="Wrap" Text="PCL 作者并不会从速聚的付费业务中获得任何分成、奖金等报酬,请不要瞎说。"
|
||||
Margin="0,0,0,6" />
|
||||
<local:MyListItem Title="速聚官网" Info="打开速聚的官方网站"
|
||||
Type="Clickable" EventType="打开网页" EventData="https://www.the.bb/zh-CN.html"
|
||||
Logo="pack://application:,,,/images/Blocks/Grass.png" />
|
||||
<local:MyListItem Title="授权协议书" Info="查看速聚与 PCL 间的授权协议书"
|
||||
Type="Clickable" EventType="打开网页" EventData="https://shimo.im/docs/0l3NVXYzeXhb903R"
|
||||
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"
|
||||
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" 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"
|
||||
Logo="pack://application:,,,/images/Blocks/RedstoneBlock.png" />
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
</StackPanel>
|
||||
</local:MyScrollViewer>
|
||||
</local:MyPageRight>
|
||||
Loaded 100 of 157 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user