Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76bf3c40d0 | ||
|
|
d48ab143be | ||
|
|
fad37c9b46 | ||
|
|
b5c9736918 | ||
|
|
30bc43bdb5 | ||
|
|
2dfa810431 | ||
|
|
8e0368180c | ||
|
|
778e5636ce | ||
|
|
cc46b29ba6 | ||
|
|
49fe40662f | ||
|
|
1aaf37f815 | ||
|
|
3606a834c0 | ||
|
|
08faaf1f46 | ||
|
|
7afd002b85 | ||
|
|
4f69e9e2d2 | ||
|
|
140b7effca | ||
|
|
e51f6f0a66 | ||
|
|
b30ad60d99 | ||
|
|
5bf3ce0105 | ||
|
|
ae503a6945 | ||
|
|
dff5b4730e | ||
|
|
4d28be2610 | ||
|
|
58ad193581 | ||
|
|
9fb45610bf | ||
|
|
0d47b82c71 | ||
|
|
007b51b314 | ||
|
|
a74b2d55c7 | ||
|
|
1d946cb675 | ||
|
|
6dea3ffb9c | ||
|
|
e32bc28510 | ||
|
|
9ed5f04f17 | ||
|
|
d390ce05d4 | ||
|
|
6b041cd540 | ||
|
|
c917066e0b | ||
|
|
7b7ad7899a | ||
|
|
545ebd680f | ||
|
|
5ea5dc3164 | ||
|
|
68c1deb2aa | ||
|
|
3d7cffbcde | ||
|
|
2bdda5795a | ||
|
|
26c492b370 | ||
|
|
b6a06f8d8d | ||
|
|
f40a299010 | ||
|
|
a509ac6337 | ||
|
|
cfdda03b35 | ||
|
|
894b36e746 | ||
|
|
e42098171e | ||
|
|
76549b4039 | ||
|
|
93574f60f4 | ||
|
|
0acd917e6b | ||
|
|
05dc7a2650 | ||
|
|
881e79dfdf | ||
|
|
20fcb59e36 | ||
|
|
f919ae49af | ||
|
|
5598ff3239 | ||
|
|
918178d0bd | ||
|
|
1519c9038a | ||
|
|
9577e9946b | ||
|
|
53743495ec | ||
|
|
76adffffad | ||
|
|
7721db131b | ||
|
|
9fe1471f8e | ||
|
|
eca89febf6 | ||
|
|
f9c4e5a752 | ||
|
|
8972aec56a | ||
|
|
0007a0ac72 | ||
|
|
d2ef7d7989 | ||
|
|
3e06ea4502 | ||
|
|
7cbdca12f3 | ||
|
|
cfe925c667 | ||
|
|
c0a966f212 | ||
|
|
d915cd48f6 | ||
|
|
591421393f | ||
|
|
ebfed4243b | ||
|
|
462f985cc3 | ||
|
|
3f332fee8e | ||
|
|
7be16a8906 | ||
|
|
22746c3ca9 | ||
|
|
0bbb048c59 | ||
|
|
2b57e1ec48 | ||
|
|
7cf91d923e | ||
|
|
4ae3e4aa20 | ||
|
|
1bd7b717d0 | ||
|
|
fa9e1be4e2 | ||
|
|
15f82f100c | ||
|
|
cc18004c41 | ||
|
|
227ceccb8f | ||
|
|
83a5370bad | ||
|
|
2348d3c8f3 | ||
|
|
d7e649c44e | ||
|
|
b10fc8c720 | ||
|
|
ecf1fae8ff | ||
|
|
d52a4ec081 | ||
|
|
a7a4838b96 | ||
|
|
c860ad26e2 | ||
|
|
8423ef6371 | ||
|
|
eaebd04bbb | ||
|
|
2b55ed078b | ||
|
|
4358058696 | ||
|
|
d49a8f8717 | ||
|
|
59a05a8c71 | ||
|
|
e0bd274e0d | ||
|
|
4747c3dc68 | ||
|
|
3c71a2cbf8 | ||
|
|
678fc3b40c | ||
|
|
96b0018adf | ||
|
|
536c309967 | ||
|
|
dea21c27c5 | ||
|
|
8b5187bff4 | ||
|
|
236557ab9e | ||
|
|
99b2adb646 |
No files matched your search
@@ -1,5 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 主页预设反馈
|
||||
url: https://github.com/Hex-Dragon/PCL2/discussions/categories/自定义主页
|
||||
about: 提交与预设的主页(设置 → 个性化 → 主页预设)中的具体内容相关的反馈
|
||||
- name: 帮助文档反馈
|
||||
url: https://github.com/LTCatt/PCL2Help/issues
|
||||
about: 提交与 PCL 帮助文档(更多 → 帮助)中的具体内容相关的反馈
|
||||
|
||||
+12
-15
@@ -1,5 +1,4 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
@@ -12,37 +11,35 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [Debug, Release, Snapshot, BETA, ReleaseUpdate]
|
||||
|
||||
name: Build
|
||||
runs-on: windows-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [Debug, Release]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set Describe
|
||||
shell: bash
|
||||
run: |
|
||||
Describe=`git describe --tags --always`
|
||||
echo "Describe=$Describe" >> $GITHUB_ENV
|
||||
|
||||
describe=`git describe --tags --always`
|
||||
echo "describe=$describe" >> $GITHUB_ENV
|
||||
- name: Setup MSBuild.exe
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
with:
|
||||
msbuild-architecture: x64
|
||||
|
||||
- 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\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
|
||||
run: msbuild "Plain Craft Launcher 2\Plain Craft Launcher 2.vbproj" -p:Configuration=${{ matrix.configuration }}
|
||||
|
||||
- name: Upload a Build Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.configuration }} ${{ env.Describe }}
|
||||
name: ${{ matrix.configuration }} ${{ env.describe }}
|
||||
path: Plain Craft Launcher 2\obj\${{ matrix.configuration }}\Plain Craft Launcher 2.exe
|
||||
@@ -17,8 +17,9 @@
|
||||
1、给出适当的署名,且提供指向本存储库的链接。GitHub 上的 Fork 可视为满足此条。
|
||||
2、不得以任何方式暗示开发者为您或您的使用作担保,且需要给出适当的说明,表示这是非官方内容。
|
||||
3、使用本指南文件作为协议,不得附加额外条款。
|
||||
4、该衍生作品不能被允许用于再次分发,这包括制作整合包。
|
||||
4、该衍生作品不能被允许用于再次分发。包括但不限于用于制作整合包。
|
||||
5、该衍生作品的名称必须以 “PCL” 或 “Plain Craft Launcher” 开头,并在其后添加后缀,例如 “PCL 启动修复版”、“PCL PR Collection”。
|
||||
6、该衍生作品不得包含 PCL 的赞助功能,或是其他作用相似的功能。包括但不限于主题功能、回声洞投稿功能等。
|
||||
|
||||
————————————
|
||||
|
||||
|
||||
@@ -99,14 +99,17 @@ Public Class Application
|
||||
'日志初始化
|
||||
LogStart()
|
||||
'添加日志
|
||||
Log($"[Start] 程序版本:{VersionDisplayName} ({VersionCode})")
|
||||
Log($"[Start] 程序版本:{VersionDisplayName} ({VersionCode}{If(CommitHash = "", "", $",#{CommitHash}")})")
|
||||
Log($"[Start] 识别码:{UniqueAddress}{If(ThemeCheckOne(9), ",已解锁反馈主题", "")}")
|
||||
Log($"[Start] 程序路径:{PathWithName}")
|
||||
Log($"[Start] 系统编码:{Encoding.Default} ({Encoding.Default.CodePage}, GBK={IsGBKEncoding})")
|
||||
Log($"[Start] 管理员权限:{IsAdmin()}")
|
||||
'检测压缩包运行
|
||||
'检测异常环境
|
||||
If Path.Contains(IO.Path.GetTempPath()) OrElse Path.Contains("AppData\Local\Temp\") Then
|
||||
MyMsgBox("PCL 正在临时文件夹运行,设置、游戏存档等很可能无法保存,且部分功能会无法使用或出错。" & vbCrLf & "请将 PCL 从压缩文件中解压,或是更换文件夹后再继续使用!", "环境警告", "我知道了", IsWarn:=True)
|
||||
MyMsgBox("请将 PCL 从压缩文件中解压,或是更换文件夹后再继续使用!" & vbCrLf & "程序目前在临时文件夹中运行,设置、游戏存档等可能无法保存,且部分功能将无法使用。", "环境警告", "我知道了", IsWarn:=True)
|
||||
End If
|
||||
If Is32BitSystem Then
|
||||
MyMsgBox("PCL 和新版 Minecraft 均不再支持 32 位系统,部分功能将无法使用。" & vbCrLf & "非常建议重装为 64 位系统后再进行游戏!", "环境警告", "我知道了", IsWarn:=True)
|
||||
End If
|
||||
'设置初始化
|
||||
Setup.Load("SystemDebugMode")
|
||||
@@ -171,9 +174,12 @@ Public Class Application
|
||||
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
|
||||
@@ -199,6 +205,16 @@ Public Class Application
|
||||
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(GetPureAsciiDir())
|
||||
File.WriteAllBytes(GetPureAsciiDir() & "\libwebp.dll", GetResources("libwebp64"))
|
||||
End If
|
||||
Return AssemblyImazenWebp
|
||||
End SyncLock
|
||||
Else
|
||||
Return Nothing
|
||||
End If
|
||||
|
||||
@@ -46,18 +46,18 @@
|
||||
Black
|
||||
Highlight
|
||||
End Enum
|
||||
Private _ColorType As ColorState = ColorState.Black
|
||||
Public Property ColorType As ColorState
|
||||
Get
|
||||
Return _ColorType
|
||||
Return GetValue(ColorTypeProperty)
|
||||
End Get
|
||||
Set(value As ColorState)
|
||||
_ColorType = value
|
||||
If ColorType = value Then Return
|
||||
SetValue(ColorTypeProperty, value)
|
||||
RefreshColor()
|
||||
End Set
|
||||
End Property '颜色类别
|
||||
Public Shared ReadOnly ColorTypeProperty As DependencyProperty =
|
||||
DependencyProperty.Register("ColorType", GetType(ColorState), GetType(MyIconTextButton), New PropertyMetadata())
|
||||
DependencyProperty.Register("ColorType", GetType(ColorState), GetType(MyIconTextButton), New PropertyMetadata(ColorState.Black))
|
||||
|
||||
'点击事件
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@
|
||||
</Border>
|
||||
</Border>
|
||||
</Grid>
|
||||
<StackPanel x:Name="PanHint" IsHitTestVisible="False" UseLayoutRounding="True" SnapsToDevicePixels="True" HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="0,0,0,25" Grid.RowSpan="2" />
|
||||
<StackPanel x:Name="PanHint" IsHitTestVisible="False" UseLayoutRounding="True" SnapsToDevicePixels="True" HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="0,0,0,20" Grid.RowSpan="2" />
|
||||
<StackPanel HorizontalAlignment="Right" VerticalAlignment="Bottom" Grid.Row="1" Margin="15">
|
||||
<local:MyExtraButton x:Name="BtnExtraBack" HorizontalAlignment="Right" VerticalAlignment="Center" ToolTip="返回顶部" Visibility="Collapsed"
|
||||
Logo="M858.496 188.9024 173.1072 188.9024c-30.2848 0-54.8352-24.5504-54.8352-54.8352L118.272 106.6496c0-30.2848 24.5504-54.8352 54.8352-54.8352l685.3888 0c30.2848 0 54.8352 24.5504 54.8352 54.8352l0 27.4176C913.3312 164.352 888.7808 188.9024 858.496 188.9024L858.496 188.9024zM150.6048 550.8608c0 0 300.0064-240.3584 303.0272-243.328 13.9776-13.5936 31.1808-21.8624 48.8192-24.7552 1.7152-0.3072 3.4304-0.5888 5.1456-0.768 2.7392-0.3072 5.4528-0.3584 8.192-0.3328 2.7392-0.0256 5.4272 0.0256 8.1664 0.3328 1.7408 0.1792 3.4304 0.4864 5.1456 0.768 17.664 2.8928 34.8672 11.1616 48.8192 24.7552 3.0464 2.944 303.0016 243.328 303.0016 243.328 32.384 31.5136 29.6192 63.9744-2.7392 95.5136-32.3328 31.5392-75.648 2.9696-108.0064-28.544l-185.8816-147.1232 0 485.8368c0 30.3104-24.5248 54.8608-54.8352 54.8608l-27.392 0c-30.2848 0-54.8352-24.5504-54.8352-54.8608L447.232 470.7072l-185.8304 147.0976c-32.3584 31.5392-75.6992 60.1344-108.032 28.5696C121.0368 614.8352 118.272 582.3744 150.6048 550.8608L150.6048 550.8608zM150.6048 550.8608" />
|
||||
|
||||
@@ -10,6 +10,29 @@ Public Class FormMain
|
||||
Dim FeatureList As New List(Of KeyValuePair(Of Integer, String))
|
||||
'统计更新日志条目
|
||||
#If BETA Then
|
||||
If LastVersion < 340 Then 'Release 2.8.8
|
||||
If LastVersion = 338 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复数个与新正版登录相关的严重 Bug"))
|
||||
FeatureCount += 3
|
||||
BugCount += 7
|
||||
End If
|
||||
If LastVersion < 338 Then 'Release 2.8.7
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "使用新的正版登录方式,以提高安全性"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化安装整合包、检索 Mod 的稳定性"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法加载部分 Mod 的图标的 Bug"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复在 Mod 管理页面删除 Mod 导致报错的 Bug"))
|
||||
FeatureCount += 11
|
||||
BugCount += 21
|
||||
End If
|
||||
If LastVersion < 336 Then 'Release 2.8.6
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "下载 Mod 时会使用 MCIM 国内镜像源"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "Mod 管理页面允许筛选可更新/启用/禁用的 Mod"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "打开 PCL 时会自动安装同目录下的 modpack.zip"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "爱发电域名迁移至 afdian.com"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复 1.20.1+ 离线登录使用正版皮肤时无法保存游戏的 Bug"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复安装的 1.14~1.15 Forge+OptiFine 无法进入世界的 Bug"))
|
||||
FeatureCount += 19
|
||||
BugCount += 24
|
||||
End If
|
||||
If LastVersion < 332 Then 'Release 2.8.3
|
||||
If LastVersion = 330 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复部分玩家无法启动 MC 的 Bug"))
|
||||
End If
|
||||
@@ -124,6 +147,31 @@ Public Class FormMain
|
||||
'3:BUG+ IMP* FEAT-
|
||||
'2:BUG* IMP-
|
||||
'1:BUG-
|
||||
If LastVersion < 339 Then 'Snapshot 2.8.8
|
||||
If LastVersion = 337 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复数个与新正版登录相关的严重 Bug"))
|
||||
FeatureCount += 3
|
||||
BugCount += 7
|
||||
End If
|
||||
If LastVersion < 337 Then 'Snapshot 2.8.7
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "使用新的正版登录方式,以提高安全性"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化安装整合包、检索 Mod 的稳定性"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法加载部分 Mod 的图标的 Bug"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复在 Mod 管理页面删除 Mod 导致报错的 Bug"))
|
||||
FeatureCount += 11
|
||||
BugCount += 21
|
||||
End If
|
||||
If LastVersion < 335 Then 'Snapshot 2.8.6
|
||||
BugCount += 2
|
||||
End If
|
||||
If LastVersion < 334 Then 'Snapshot 2.8.5
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "Mod 管理页面允许筛选可更新/启用/禁用的 Mod"))
|
||||
If LastVersion = 333 Then
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法安装愚人节和预发布版本的 Bug"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法导出错误报告的 Bug"))
|
||||
End If
|
||||
FeatureCount += 6
|
||||
BugCount += 7
|
||||
End If
|
||||
If LastVersion < 333 Then 'Snapshot 2.8.4
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "下载 Mod 时会使用 MCIM 国内镜像源"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "打开 PCL 时会自动安装同目录下的 modpack.zip"))
|
||||
@@ -410,6 +458,10 @@ Public Class FormMain
|
||||
Height = MinHeight + 50
|
||||
Width = MinWidth + 50
|
||||
End Try
|
||||
#If DEBUG Then
|
||||
MinHeight = 50
|
||||
MinWidth = 50
|
||||
#End If
|
||||
Topmost = False
|
||||
If FrmStart IsNot Nothing Then FrmStart.Close(New TimeSpan(0, 0, 0, 0, 400 / AniSpeed))
|
||||
'更改窗口
|
||||
@@ -687,7 +739,7 @@ Public Class FormMain
|
||||
Exit Sub
|
||||
ElseIf e.Key = Key.Escape Then
|
||||
Dim Msg As Object = PanMsg.Children(0)
|
||||
If TypeOf Msg Is MyMsgText AndAlso Msg.Btn3.Visibility = Visibility.Visible Then
|
||||
If TypeOf Msg IsNot MyMsgInput AndAlso TypeOf Msg IsNot MyMsgSelect AndAlso Msg.Btn3.Visibility = Visibility.Visible Then
|
||||
Msg.Btn3_Click()
|
||||
ElseIf Msg.Btn2.Visibility = Visibility.Visible Then
|
||||
Msg.Btn2_Click()
|
||||
@@ -744,7 +796,7 @@ Public Class FormMain
|
||||
Try
|
||||
If PageCurrent = PageType.VersionSetup AndAlso PageCurrentSub = PageSubType.VersionMod Then
|
||||
'Mod 管理自动刷新
|
||||
FrmVersionMod.RefreshList()
|
||||
FrmVersionMod.ReloadModList()
|
||||
ElseIf PageCurrent = PageType.VersionSelect Then
|
||||
'版本选择自动刷新
|
||||
LoaderFolderRun(McVersionListLoader, PathMcFolder, LoaderFolderRunType.RunOnUpdated, MaxDepth:=1, ExtraPath:="versions\")
|
||||
@@ -989,18 +1041,19 @@ Install:
|
||||
''' 把当前窗口拖到最前面。
|
||||
''' </summary>
|
||||
Public Sub ShowWindowToTop()
|
||||
RunInUi(Sub()
|
||||
'这一坨乱七八糟的,别改,改了指不定就炸了,自己电脑还复现不出来
|
||||
Visibility = Visibility.Visible
|
||||
ShowInTaskbar = True
|
||||
WindowState = WindowState.Normal
|
||||
Hidden = False
|
||||
Topmost = True '偶尔 SetForegroundWindow 失效
|
||||
Topmost = False
|
||||
SetForegroundWindow(Handle)
|
||||
Focus()
|
||||
Log("[System] 窗口已置顶,位置:(" & Left & ", " & Top & "), " & Width & " x " & Height)
|
||||
End Sub)
|
||||
RunInUi(
|
||||
Sub()
|
||||
'这一坨乱七八糟的,别改,改了指不定就炸了,自己电脑还复现不出来
|
||||
Visibility = Visibility.Visible
|
||||
ShowInTaskbar = True
|
||||
WindowState = WindowState.Normal
|
||||
Hidden = False
|
||||
Topmost = True '偶尔 SetForegroundWindow 失效
|
||||
Topmost = False
|
||||
SetForegroundWindow(Handle)
|
||||
Focus()
|
||||
Log($"[System] 窗口已置顶,位置:({Left}, {Top}), {Width} x {Height}")
|
||||
End Sub)
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
File renamed without changes.
@@ -1,4 +1,5 @@
|
||||
Imports System.IO.Compression
|
||||
Imports System.Globalization
|
||||
Imports System.IO.Compression
|
||||
Imports System.Runtime.CompilerServices
|
||||
Imports System.Security.Cryptography
|
||||
Imports System.Security.Principal
|
||||
@@ -11,12 +12,13 @@ Public Module ModBase
|
||||
#Region "声明"
|
||||
|
||||
'下列版本信息由更新器自动修改
|
||||
Public Const VersionBaseName As String = "2.8.4" '不含分支前缀的显示用版本名
|
||||
Public Const VersionStandardCode As String = "2.8.4." & VersionBranchCode '标准格式的四段式版本号
|
||||
Public Const VersionBaseName As String = "2.8.8" '不含分支前缀的显示用版本名
|
||||
Public Const VersionStandardCode As String = "2.8.8." & VersionBranchCode '标准格式的四段式版本号
|
||||
Public Const CommitHash As String = "" 'Commit Hash,由 GitHub Workflow 自动替换
|
||||
#If BETA Then
|
||||
Public Const VersionCode As Integer = 332 'Release
|
||||
Public Const VersionCode As Integer = 340 'Release
|
||||
#Else
|
||||
Public Const VersionCode As Integer = 333 'Snapshot
|
||||
Public Const VersionCode As Integer = 339 'Snapshot
|
||||
#End If
|
||||
'自动生成的版本信息
|
||||
Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName
|
||||
@@ -64,7 +66,7 @@ Public Module ModBase
|
||||
''' </summary>
|
||||
Public ApplicationOpenTime As Date = Date.Now
|
||||
''' <summary>
|
||||
''' 设备唯一标识符。
|
||||
''' 识别码。
|
||||
''' </summary>
|
||||
Public UniqueAddress As String = SecretGetUniqueAddress()
|
||||
''' <summary>
|
||||
@@ -696,7 +698,7 @@ Public Module ModBase
|
||||
If Not FileName.Contains(":\") Then FileName = $"{Path}PCL\{FileName}.ini"
|
||||
WriteFile(FileName, FileContent.ToString)
|
||||
Catch ex As Exception
|
||||
Log(ex, $"写入文件失败({FileName} -> {Key}:{Value})")
|
||||
Log(ex, $"写入文件失败({FileName} → {Key}:{Value})", LogLevel.Hint)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
@@ -770,10 +772,10 @@ Public Module ModBase
|
||||
End Try
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 读取文件,如果失败则返回空字符串。
|
||||
''' 读取文件,如果失败则返回空数组。
|
||||
''' </summary>
|
||||
''' <param name="FilePath">文件完整或相对路径。</param>
|
||||
Public Function ReadFile(FilePath As String, Optional Encoding As Encoding = Nothing) As String
|
||||
Public Function ReadFileBytes(FilePath As String, Optional Encoding As Encoding = Nothing) As Byte()
|
||||
Try
|
||||
'还原文件路径
|
||||
If Not FilePath.Contains(":\") Then FilePath = Path & FilePath
|
||||
@@ -783,17 +785,25 @@ Public Module ModBase
|
||||
ReDim FileBytes(ReadStream.Length - 1)
|
||||
ReadStream.Read(FileBytes, 0, ReadStream.Length)
|
||||
End Using
|
||||
ReadFile = If(Encoding Is Nothing, DecodeBytes(FileBytes), Encoding.GetString(FileBytes))
|
||||
Return FileBytes
|
||||
Else
|
||||
Log("[System] 欲读取的文件不存在,已返回空字符串:" & FilePath)
|
||||
Return ""
|
||||
Log("[System] 欲读取的文件不存在,已返回空内容:" & FilePath)
|
||||
Return {}
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "读取文件出错:" & FilePath)
|
||||
Return ""
|
||||
Return {}
|
||||
End Try
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 读取文件,如果失败则返回空字符串。
|
||||
''' </summary>
|
||||
''' <param name="FilePath">文件完整或相对路径。</param>
|
||||
Public Function ReadFile(FilePath As String, Optional Encoding As Encoding = Nothing) As String
|
||||
Dim FileBytes = ReadFileBytes(FilePath)
|
||||
ReadFile = If(Encoding Is Nothing, DecodeBytes(FileBytes), Encoding.GetString(FileBytes))
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 读取流中的所有文本。
|
||||
''' </summary>
|
||||
Public Function ReadFile(Stream As Stream, Optional Encoding As Encoding = Nothing) As String
|
||||
@@ -818,50 +828,39 @@ Public Module ModBase
|
||||
''' <param name="FilePath">文件完整或相对路径。</param>
|
||||
''' <param name="Text">文件内容。</param>
|
||||
''' <param name="Append">是否将文件内容追加到当前文件,而不是覆盖它。</param>
|
||||
Public Function WriteFile(FilePath As String, Text As String, Optional Append As Boolean = False, Optional Encoding As Encoding = Nothing) As Boolean
|
||||
Try
|
||||
'还原文件路径
|
||||
If Not FilePath.Contains(":\") Then FilePath = Path & FilePath
|
||||
'确保目录存在
|
||||
Directory.CreateDirectory(GetPathFromFullPath(FilePath))
|
||||
'写入文件
|
||||
If File.Exists(FilePath) Then
|
||||
'如果文件存在,刷新目前文件
|
||||
Using writer As New StreamWriter(FilePath, Append, If(Encoding, GetEncoding(FilePath)))
|
||||
writer.Write(Text)
|
||||
writer.Flush()
|
||||
writer.Close()
|
||||
End Using
|
||||
Else
|
||||
'如果文件不存在,则新建并写入
|
||||
File.WriteAllText(FilePath, Text, If(Encoding, New UTF8Encoding(False)))
|
||||
End If
|
||||
Return True
|
||||
Catch ex As Exception
|
||||
Log(ex, "写入文件时出错:" & FilePath)
|
||||
Return False
|
||||
End Try
|
||||
End Function
|
||||
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
|
||||
'确保目录存在
|
||||
Directory.CreateDirectory(GetPathFromFullPath(FilePath))
|
||||
'写入文件
|
||||
If File.Exists(FilePath) Then
|
||||
'如果文件存在,刷新目前文件
|
||||
Using writer As New StreamWriter(FilePath, Append, If(Encoding, GetEncoding(ReadFileBytes(FilePath))))
|
||||
writer.Write(Text)
|
||||
writer.Flush()
|
||||
writer.Close()
|
||||
End Using
|
||||
Else
|
||||
'如果文件不存在,则新建并写入
|
||||
File.WriteAllText(FilePath, Text, If(Encoding, New UTF8Encoding(False)))
|
||||
End If
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 写入文件。
|
||||
''' 如果 CanThrow 设置为 False,返回是否写入成功。
|
||||
''' </summary>
|
||||
''' <param name="FilePath">文件完整或相对路径。</param>
|
||||
''' <param name="Content">文件内容。</param>
|
||||
''' <param name="Append">是否将文件内容追加到当前文件,而不是覆盖它。</param>
|
||||
Public Function WriteFile(FilePath As String, Content As Byte(), Optional Append As Boolean = False) As Boolean
|
||||
Try
|
||||
'还原文件路径
|
||||
If Not FilePath.Contains(":\") Then FilePath = Path & FilePath
|
||||
'确保目录存在
|
||||
Directory.CreateDirectory(GetPathFromFullPath(FilePath))
|
||||
'写入文件
|
||||
File.WriteAllBytes(FilePath, Content)
|
||||
Return True
|
||||
Catch ex As Exception
|
||||
Log(ex, "写入文件时出错:" & FilePath)
|
||||
Return False
|
||||
End Try
|
||||
End Function
|
||||
Public Sub WriteFile(FilePath As String, Content As Byte(), Optional Append As Boolean = False)
|
||||
'还原文件路径
|
||||
If Not FilePath.Contains(":\") Then FilePath = Path & FilePath
|
||||
'确保目录存在
|
||||
Directory.CreateDirectory(GetPathFromFullPath(FilePath))
|
||||
'写入文件
|
||||
File.WriteAllBytes(FilePath, Content)
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 将流写入文件。
|
||||
''' </summary>
|
||||
@@ -891,21 +890,11 @@ Public Module ModBase
|
||||
|
||||
'文件编码
|
||||
''' <summary>
|
||||
''' 获取文件编码。
|
||||
''' </summary>
|
||||
''' <param name="FilePath">文件完整或相对路径。</param>
|
||||
Public Function GetEncoding(FilePath As String) As Encoding
|
||||
'还原文件路径
|
||||
If Not FilePath.Contains(":\") Then FilePath = Path & FilePath
|
||||
'获取编码
|
||||
GetEncoding = GetEncoding(File.ReadAllBytes(FilePath))
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 获取 Bytes 的编码。
|
||||
''' 根据字节数组分析其编码。
|
||||
''' </summary>
|
||||
Public Function GetEncoding(Bytes As Byte()) As Encoding
|
||||
Dim Length As Integer = Bytes.Count
|
||||
If Length <= 2 Then Return New UTF8Encoding(False) '不带 BOM 的 UTF8
|
||||
If Length < 3 Then Return New UTF8Encoding(False) '不带 BOM 的 UTF8
|
||||
'根据 BOM 判断编码
|
||||
If Bytes(0) >= &HEF Then
|
||||
'有 BOM 类型
|
||||
@@ -1249,9 +1238,15 @@ Re:
|
||||
Public Function DeleteDirectory(Path As String, Optional IgnoreIssue As Boolean = False) As Integer
|
||||
If Not Directory.Exists(Path) Then Return 0
|
||||
Dim DeletedCount As Integer = 0
|
||||
Dim Temp As String()
|
||||
Temp = Directory.GetFiles(Path)
|
||||
For Each FilePath As String In Temp
|
||||
Dim Files As String()
|
||||
Try
|
||||
Files = Directory.GetFiles(Path)
|
||||
Catch ex As DirectoryNotFoundException '#4549
|
||||
Log(ex, $"疑似为孤立符号链接,尝试直接删除({Path})", LogLevel.Developer)
|
||||
Directory.Delete(Path)
|
||||
Return 0
|
||||
End Try
|
||||
For Each FilePath As String In Files
|
||||
Dim RetriedFile As Boolean = False
|
||||
RetryFile:
|
||||
Try
|
||||
@@ -1270,8 +1265,7 @@ RetryFile:
|
||||
End If
|
||||
End Try
|
||||
Next
|
||||
Temp = Directory.GetDirectories(Path)
|
||||
For Each str As String In Temp
|
||||
For Each str As String In Directory.GetDirectories(Path)
|
||||
DeleteDirectory(str, IgnoreIssue)
|
||||
Next
|
||||
Dim RetriedDir As Boolean = False
|
||||
@@ -1453,14 +1447,14 @@ RetryDir:
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 获取 Json 对象。
|
||||
''' 获取 JSON 对象。
|
||||
''' </summary>
|
||||
Public Function GetJson(Data As String)
|
||||
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 > 10000, Data.Substring(0, 100) & $"...(全长 {Length} 个字符)..." & Right(Data, 100), Data))
|
||||
Throw New Exception("格式化 JSON 失败:" & If(Length > 2000, Data.Substring(0, 500) & $"...(全长 {Length} 个字符)..." & Right(Data, 500), Data))
|
||||
End Try
|
||||
End Function
|
||||
|
||||
@@ -1649,10 +1643,15 @@ RetryDir:
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 输入 And 字符不会报错的 Val。
|
||||
''' 不会报错的 Val。
|
||||
''' 如果输入有误,返回 0。
|
||||
''' </summary>
|
||||
Public Function Val(Str As Object) As Double
|
||||
Return If(TypeOf Str Is String AndAlso Str = "&", 0, Conversion.Val(Str))
|
||||
Try
|
||||
Return If(TypeOf Str Is String AndAlso Str = "&", 0, Conversion.Val(Str))
|
||||
Catch
|
||||
Return 0
|
||||
End Try
|
||||
End Function
|
||||
|
||||
'转义
|
||||
@@ -1841,6 +1840,13 @@ RetryDir:
|
||||
|
||||
#Region "系统"
|
||||
|
||||
''' <summary>
|
||||
''' 指示接取到这个异常的函数进行重试。
|
||||
''' </summary>
|
||||
Public Class RetryException
|
||||
Inherits Exception
|
||||
End Class
|
||||
|
||||
''' <summary>
|
||||
''' 当前程序是否拥有管理员权限。
|
||||
''' </summary>
|
||||
@@ -1859,6 +1865,13 @@ RetryDir:
|
||||
Return NewProcess.ExitCode
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 判断当前系统语言是否为中文。
|
||||
''' </summary>
|
||||
Public Function IsSystemLanguageChinese() As Boolean
|
||||
Return CultureInfo.CurrentCulture.TwoLetterISOLanguageName = "zh" OrElse CultureInfo.CurrentUICulture.TwoLetterISOLanguageName = "zh"
|
||||
End Function
|
||||
|
||||
Private Uuid As Integer = 1
|
||||
Private UuidLock As Object
|
||||
''' <summary>
|
||||
@@ -2424,6 +2437,14 @@ Retry:
|
||||
Return rect.Contains(bounds.TopLeft) OrElse rect.Contains(bounds.BottomRight)
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 控件是否受到 TextTrimming 属性影响,导致内容被截取。
|
||||
''' </summary>
|
||||
<Extension> Public Function IsTextTrimmed(Control As TextBlock) As Boolean
|
||||
Control.Measure(New Size(Double.MaxValue, Double.MaxValue))
|
||||
Return Control.DesiredSize.Width > Control.ActualWidth
|
||||
End Function
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "Debug"
|
||||
@@ -2664,7 +2685,11 @@ Retry:
|
||||
End Sub
|
||||
Public Function CanFeedback(ShowHint As Boolean) As Boolean
|
||||
If False.Equals(PageSetupSystem.IsLauncherNewest) Then
|
||||
If ShowHint Then MyMsgBox("你的 PCL 不是最新版,因此无法提交反馈。" & vbCrLf & "请先在 设置 → 启动器 中更新启动器,确认该问题在最新版中依然存在,然后再提交反馈。", "无法提交反馈")
|
||||
If ShowHint Then
|
||||
If MyMsgBox($"你的 PCL 不是最新版,因此无法提交反馈。{vbCrLf}请在更新后,确认该问题在最新版中依然存在,然后再提交反馈。", "无法提交反馈", "更新", "取消") = 1 Then
|
||||
UpdateCheckByButton()
|
||||
End If
|
||||
End If
|
||||
Return False
|
||||
Else
|
||||
Return True
|
||||
|
||||
@@ -91,17 +91,18 @@
|
||||
_State = value
|
||||
Log("[Loader] 加载器 " & Name & " 状态改变:" & GetStringFromEnum(value))
|
||||
'实现 ILoadingTrigger 接口与 OnStateChanged 回调
|
||||
RunInUi(Sub()
|
||||
Select Case value
|
||||
Case LoadState.Loading
|
||||
LoadingState = MyLoading.MyLoadingState.Run
|
||||
Case LoadState.Failed
|
||||
LoadingState = MyLoading.MyLoadingState.Error
|
||||
Case Else
|
||||
LoadingState = MyLoading.MyLoadingState.Stop
|
||||
End Select
|
||||
RaiseEvent OnStateChangedUi(Me, value, OldState)
|
||||
End Sub)
|
||||
RunInUi(
|
||||
Sub()
|
||||
Select Case value
|
||||
Case LoadState.Loading
|
||||
LoadingState = MyLoading.MyLoadingState.Run
|
||||
Case LoadState.Failed
|
||||
LoadingState = MyLoading.MyLoadingState.Error
|
||||
Case Else
|
||||
LoadingState = MyLoading.MyLoadingState.Stop
|
||||
End Select
|
||||
RaiseEvent OnStateChangedUi(Me, value, OldState)
|
||||
End Sub)
|
||||
If HasOnStateChangedThread Then RunInThread(Sub() RaiseEvent OnStateChangedThread(Me, value, OldState))
|
||||
End Set
|
||||
End Property
|
||||
@@ -287,15 +288,13 @@
|
||||
End SyncLock
|
||||
End Try
|
||||
'检验输入以确定情况
|
||||
If (Not IsForceRestart) AndAlso '不要求强行重启
|
||||
((Input IsNot Nothing AndAlso Input.Equals(Me.Input)) OrElse (Input Is Nothing AndAlso Me.Input Is Nothing)) AndAlso '输入相同
|
||||
(State = LoadState.Loading OrElse State = LoadState.Finished) AndAlso '正在加载或已加载成功
|
||||
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
|
||||
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)
|
||||
@@ -316,7 +315,7 @@
|
||||
Sub()
|
||||
Try
|
||||
IsForceRestarting = IsForceRestart
|
||||
If ModeDebug Then Log("[Loader] 加载线程 " & Name & " (" & Thread.CurrentThread.ManagedThreadId & ") 已启动")
|
||||
If ModeDebug 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)
|
||||
@@ -440,12 +439,13 @@
|
||||
Exit Sub
|
||||
End If
|
||||
End SyncLock
|
||||
RunInThread(Sub()
|
||||
'中断加载器
|
||||
For Each Loader In Loaders
|
||||
Loader.Abort()
|
||||
Next
|
||||
End Sub)
|
||||
RunInThread(
|
||||
Sub()
|
||||
'中断加载器
|
||||
For Each Loader In Loaders
|
||||
Loader.Abort()
|
||||
Next
|
||||
End Sub)
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
@@ -484,7 +484,7 @@
|
||||
Private Sub Update()
|
||||
If State = LoadState.Finished OrElse State = LoadState.Failed OrElse State = LoadState.Aborted Then Exit Sub
|
||||
Dim IsFinished As Boolean = True
|
||||
Dim Ignore As Boolean = False
|
||||
Dim Blocked As Boolean = False
|
||||
Dim Input As Object = Me.Input
|
||||
For Each Loader In Loaders
|
||||
Select Case Loader.State
|
||||
@@ -492,28 +492,30 @@
|
||||
'检查是否需要重启
|
||||
If Loader.GetType.Name.StartsWithF("LoaderTask") Then '类型名后面带有泛型,必须用 StartsWith
|
||||
If CType(Loader, Object).ShouldStart(If(Input IsNot Nothing AndAlso Loader.GetType.GenericTypeArguments.First Is Input.GetType, Input, Nothing), IgnoreReloadTimeout:=True) Then
|
||||
If ModeDebug Then Log("[Loader] 由于输入条件变更,重启已完成的加载器 " & Loader.Name)
|
||||
Log("[Loader] 由于输入条件变更,重启已完成的加载器 " & Loader.Name)
|
||||
GoTo Restart
|
||||
End If
|
||||
'更新下一个加载器的输入
|
||||
Input = CType(Loader, Object).Output
|
||||
End If
|
||||
'如果不让继续启动,且已有加载器正在加载中,就不继续启动
|
||||
If Loader.Block AndAlso Not IsFinished Then Blocked = True
|
||||
Case LoadState.Loading
|
||||
'检查是否需要重启
|
||||
If Loader.GetType.Name.StartsWithF("LoaderTask") Then
|
||||
If CType(Loader, Object).ShouldStart(If(Input IsNot Nothing AndAlso Loader.GetType.GenericTypeArguments.First Is Input.GetType, Input, Nothing), IgnoreReloadTimeout:=True) Then
|
||||
If ModeDebug Then Log("[Loader] 由于输入条件变更,重启进行中的加载器 " & Loader.Name, LogLevel.Developer)
|
||||
Log("[Loader] 由于输入条件变更,重启进行中的加载器 " & Loader.Name, LogLevel.Developer)
|
||||
GoTo Restart
|
||||
End If
|
||||
End If
|
||||
'已经有正在加载中的了,不需要再启动了
|
||||
IsFinished = False
|
||||
Ignore = True
|
||||
Blocked = True
|
||||
Case Else
|
||||
Restart:
|
||||
'未启动,则启动加载器
|
||||
IsFinished = False
|
||||
If Ignore Then Continue For
|
||||
If Blocked Then Continue For
|
||||
If Input IsNot Nothing Then
|
||||
'若输入类型与下一个加载器相同才继续
|
||||
Dim LoaderType As String = Loader.GetType.Name
|
||||
@@ -527,8 +529,8 @@ Restart:
|
||||
Else
|
||||
Loader.Start(IsForceRestart:=IsForceRestarting)
|
||||
End If
|
||||
'如果不让继续启动,那就不启动呗
|
||||
If Loader.Block Then Ignore = True
|
||||
'阻止继续
|
||||
If Loader.Block Then Blocked = True
|
||||
End Select
|
||||
Next
|
||||
If IsFinished Then
|
||||
@@ -566,7 +568,7 @@ Restart:
|
||||
End Class
|
||||
|
||||
'任务栏进度条
|
||||
Public LoaderTaskbar As New Concurrent.ConcurrentBag(Of LoaderBase)
|
||||
Public LoaderTaskbar As New SynchronizedCollection(Of LoaderBase)
|
||||
Public LoaderTaskbarProgress As Double = 0 '平滑后的进度
|
||||
Private LoaderTaskbarProgressLast As Shell.TaskbarItemProgressState = Shell.TaskbarItemProgressState.None
|
||||
|
||||
@@ -582,14 +584,14 @@ Restart:
|
||||
'检查任务是否完成,若完成则移除
|
||||
'外显任务是否已经全部完成
|
||||
Dim IsAllDownloadTaskCompleted As Boolean = True
|
||||
For Each Loader In LoaderTaskbar
|
||||
For Each Loader In LoaderTaskbar.ToList()
|
||||
If Loader.State = LoadState.Loading Then IsAllDownloadTaskCompleted = False
|
||||
Next
|
||||
'若单个任务已中止或全部任务已完成,则刷新并移除
|
||||
For Each Task In LoaderTaskbar.ToList()
|
||||
If IsAllDownloadTaskCompleted OrElse Task.State = LoadState.Aborted OrElse Task.State = LoadState.Waiting Then
|
||||
If FrmSpeedLeft IsNot Nothing Then FrmSpeedLeft.TaskRefresh(Task)
|
||||
LoaderTaskbar.TryTake(Task)
|
||||
LoaderTaskbar.Remove(Task)
|
||||
Log($"[Taskbar] {Task.Name} 已移出任务列表")
|
||||
End If
|
||||
Next
|
||||
|
||||
@@ -140,7 +140,7 @@ Retry:
|
||||
''' </summary>
|
||||
''' <param name="Url">网页的 Url。</param>
|
||||
''' <param name="Encode">网页的编码,通常为 UTF-8。</param>
|
||||
Public Function NetGetCodeByRequestMulty(Url As String, Optional Encode As Encoding = Nothing, Optional Accept As String = "", Optional IsJson As Boolean = False)
|
||||
Public Function NetGetCodeByRequestMultiple(Url As String, Optional Encode As Encoding = Nothing, Optional Accept As String = "", Optional IsJson As Boolean = False)
|
||||
Dim Threads As New List(Of Thread)
|
||||
Dim RequestResult = Nothing
|
||||
Dim RequestEx As Exception = Nothing
|
||||
@@ -268,12 +268,7 @@ RequestFinished:
|
||||
'下载
|
||||
Using Client As New WebClient
|
||||
Try
|
||||
If UseBrowserUserAgent Then
|
||||
Client.Headers(HttpRequestHeader.UserAgent) = "PCL2/" & VersionStandardCode & " Mozilla/5.0 AppleWebKit/537.36 Chrome/63.0.3239.132 Safari/537.36"
|
||||
Else
|
||||
Client.Headers(HttpRequestHeader.UserAgent) = "PCL2/" & VersionStandardCode
|
||||
End If
|
||||
Client.Headers(HttpRequestHeader.Referer) = "http://" & VersionCode & ".pcl2.server/"
|
||||
SecretHeadersSign(Url, Client, UseBrowserUserAgent)
|
||||
Client.DownloadFile(Url, LocalFile)
|
||||
Catch ex As Exception
|
||||
File.Delete(LocalFile)
|
||||
@@ -315,7 +310,7 @@ Retry:
|
||||
Catch ex As ThreadInterruptedException
|
||||
Throw
|
||||
Catch ex As Exception
|
||||
If ex.InnerException IsNot Nothing AndAlso ex.InnerException.Message.Contains("(40") AndAlso DontRetryOnRefused Then RetryCount = 999
|
||||
If ex.InnerException IsNot Nothing AndAlso ex.InnerException.Message.Contains("(40") AndAlso DontRetryOnRefused Then Throw
|
||||
Select Case RetryCount
|
||||
Case 0
|
||||
If ModeDebug Then Log(ex, "[Net] 网络请求第一次失败(" & Url & ")")
|
||||
@@ -334,7 +329,7 @@ Retry:
|
||||
''' <summary>
|
||||
''' 同时发送多个网络请求并要求返回内容。
|
||||
''' </summary>
|
||||
Public Function NetRequestMulty(Url As String, Method As String, Data As Object, ContentType As String, Optional RequestCount As Integer = 4, Optional Headers As Dictionary(Of String, String) = Nothing)
|
||||
Public Function NetRequestMultiple(Url As String, Method As String, Data As Object, ContentType As String, Optional RequestCount As Integer = 4, Optional Headers As Dictionary(Of String, String) = Nothing, Optional MakeLog As Boolean = True)
|
||||
Dim Threads As New List(Of Thread)
|
||||
Dim RequestResult = Nothing
|
||||
Dim RequestEx As Exception = Nothing
|
||||
@@ -343,7 +338,7 @@ Retry:
|
||||
Dim th As New Thread(
|
||||
Sub()
|
||||
Try
|
||||
RequestResult = NetRequestOnce(Url, Method, Data, ContentType, 30000, Headers)
|
||||
RequestResult = NetRequestOnce(Url, Method, Data, ContentType, 30000, Headers, MakeLog)
|
||||
Catch ex As Exception
|
||||
FailCount += 1
|
||||
RequestEx = ex
|
||||
@@ -432,7 +427,11 @@ RequestFinished:
|
||||
End Using
|
||||
Catch
|
||||
End Try
|
||||
ex = New WebException($"网络请求失败({ex.Status},{ex.Message},{Url}){If(String.IsNullOrEmpty(Res), "", vbCrLf & Res)}", ex)
|
||||
If Res = "" Then
|
||||
ex = New WebException($"网络请求失败({ex.Status},{ex.Message},{Url})", ex)
|
||||
Else
|
||||
ex = New ResponsedWebException($"服务器返回错误({ex.Status},{ex.Message},{Url}){vbCrLf}{Res}", Res, ex)
|
||||
End If
|
||||
End If
|
||||
If MakeLog Then Log(ex, "NetRequestOnce 失败", LogLevel.Developer)
|
||||
Throw ex
|
||||
@@ -445,6 +444,17 @@ RequestFinished:
|
||||
If Resp IsNot Nothing Then Resp.Dispose()
|
||||
End Try
|
||||
End Function
|
||||
Public Class ResponsedWebException
|
||||
Inherits WebException
|
||||
''' <summary>
|
||||
''' 远程服务器给予的回复。
|
||||
''' </summary>
|
||||
Public Overloads Property Response As String
|
||||
Public Sub New(Message As String, Response As String, InnerException As Exception)
|
||||
MyBase.New(Message, InnerException)
|
||||
Me.Response = Response
|
||||
End Sub
|
||||
End Class
|
||||
|
||||
''' <summary>
|
||||
''' 最大线程数。
|
||||
@@ -691,8 +701,7 @@ RequestFinished:
|
||||
''' <summary>
|
||||
''' 所属的文件列表任务。
|
||||
''' </summary>
|
||||
Public Tasks As New Concurrent.ConcurrentBag(Of LoaderDownload)
|
||||
|
||||
Public Tasks As New SynchronizedCollection(Of LoaderDownload)
|
||||
''' <summary>
|
||||
''' 所有下载源。
|
||||
''' </summary>
|
||||
@@ -1334,7 +1343,7 @@ Retry:
|
||||
If Check.ActualSize = -1 Then
|
||||
Check.ActualSize = FileSize
|
||||
ElseIf Check.ActualSize <> FileSize Then
|
||||
Throw New Exception("文件大小不一致:任务要求为 " & Check.ActualSize & " B,网络获取结果为 " & FileSize & "B")
|
||||
Throw New Exception($"文件大小不一致:任务要求为 {Check.ActualSize} B,网络获取结果为 {FileSize}B")
|
||||
End If
|
||||
End If
|
||||
'检查文件
|
||||
@@ -1393,7 +1402,7 @@ Retry:
|
||||
''' </summary>
|
||||
Public Sub Abort(CausedByTask As LoaderDownload)
|
||||
'从特定任务中移除,如果它还属于其他任务,则继续下载
|
||||
Tasks.TryTake(CausedByTask)
|
||||
Tasks.Remove(CausedByTask)
|
||||
If Tasks.Any Then Exit Sub
|
||||
'确认中断
|
||||
SyncLock LockState
|
||||
@@ -1644,6 +1653,9 @@ NextElement:
|
||||
'复制已存在的文件
|
||||
For Each FileToken In ExistFiles
|
||||
Dim File As NetFile = FileToken.Key
|
||||
SyncLock LockState
|
||||
If File.State > NetState.WaitForCopy Then Exit Sub
|
||||
End SyncLock
|
||||
Dim LocalPath As String = FileToken.Value
|
||||
Dim RetryCount As Integer = 0
|
||||
Retry:
|
||||
@@ -1872,7 +1884,7 @@ Retry:
|
||||
For Each File As NetFile In WaitingFiles
|
||||
If NetTaskThreadCount >= NetTaskThreadLimit Then Continue While '最大线程数检查
|
||||
Dim NewThread = File.TryBeginThread()
|
||||
If NewThread IsNot Nothing AndAlso NewThread.Source.Url.Contains("bmclapi.") Then Thread.Sleep(40) '减少 BMCLAPI 请求频率,避免 Too Many Requests
|
||||
If NewThread IsNot Nothing AndAlso NewThread.Source.Url.Contains("bmclapi") Then Thread.Sleep(70) '减少 BMCLAPI 请求频率,避免 Too Many Requests
|
||||
Next
|
||||
'为进行中的文件追加线程
|
||||
If Speed >= NetTaskSpeedLimitLow Then Continue While '下载速度足够,无需新增
|
||||
@@ -1892,7 +1904,7 @@ Retry:
|
||||
'新增线程
|
||||
If PreparingCount > DownloadingCount Then Continue For '准备中的线程已多于下载中的线程,不再新增
|
||||
Dim NewThread = File.TryBeginThread()
|
||||
If NewThread IsNot Nothing AndAlso NewThread.Source.Url.Contains("bmclapi.") Then Thread.Sleep(40) '减少 BMCLAPI 请求频率,避免 Too Many Requests
|
||||
If NewThread IsNot Nothing AndAlso NewThread.Source.Url.Contains("bmclapi") Then Thread.Sleep(70) '减少 BMCLAPI 请求频率,避免 Too Many Requests
|
||||
Next
|
||||
End While
|
||||
Catch ex As Exception
|
||||
@@ -1990,7 +2002,7 @@ Retry:
|
||||
''' 是否有正在进行中、需要在下载管理页面显示的下载任务?
|
||||
''' </summary>
|
||||
Public Function HasDownloadingTask(Optional IgnoreCustomDownload As Boolean = False) As Boolean
|
||||
For Each Task In LoaderTaskbar
|
||||
For Each Task In LoaderTaskbar.ToList()
|
||||
If (Task.Show AndAlso Task.State = LoadState.Loading) AndAlso
|
||||
(Not IgnoreCustomDownload OrElse Not Task.Name.ToString.Contains("自定义下载")) Then
|
||||
Return True
|
||||
|
||||
@@ -66,8 +66,20 @@ Public Class MyBitmap
|
||||
Else
|
||||
'使用这种自己接管 FileStream 的方法加载才能解除文件占用
|
||||
Using InputStream As New FileStream(FilePathOrResourceName, FileMode.Open)
|
||||
Pic = New System.Drawing.Bitmap(InputStream)
|
||||
InputStream.Dispose()
|
||||
'判断是否为 WebP 文件头
|
||||
Dim Header(1) As Byte
|
||||
InputStream.Read(Header, 0, 2)
|
||||
InputStream.Seek(0, SeekOrigin.Begin)
|
||||
If Header(0) = 82 AndAlso Header(1) = 73 Then
|
||||
'读取 WebP
|
||||
If Is32BitSystem Then Throw New Exception("不支持在 32 位系统下加载 WebP 图片。")
|
||||
Dim FileBytes(InputStream.Length - 1) As Byte
|
||||
InputStream.Read(FileBytes, 0, FileBytes.Length)
|
||||
Dim Decoder As New Imazen.WebP.SimpleDecoder()
|
||||
Pic = Decoder.DecodeFromBytes(FileBytes, FileBytes.Length)
|
||||
Else
|
||||
Pic = New System.Drawing.Bitmap(InputStream)
|
||||
End If
|
||||
End Using
|
||||
End If
|
||||
Catch ex As Exception
|
||||
|
||||
@@ -247,7 +247,13 @@
|
||||
Website = Data("links")("websiteUrl").ToString.TrimEnd("/")
|
||||
LastUpdate = Data("dateReleased") '#1194
|
||||
DownloadCount = Data("downloadCount")
|
||||
If Data("logo").Count > 0 Then LogoUrl = Data("logo")("thumbnailUrl")
|
||||
If Data("logo").Count > 0 Then
|
||||
If Data("logo")("thumbnailUrl") Is Nothing OrElse Data("logo")("thumbnailUrl") = "" Then
|
||||
LogoUrl = Data("logo")("url")
|
||||
Else
|
||||
LogoUrl = Data("logo")("thumbnailUrl")
|
||||
End If
|
||||
End If
|
||||
'FileIndexes / GameVersions / ModLoaders
|
||||
ModLoaders = New List(Of CompModLoaderType)
|
||||
Dim Files As New List(Of KeyValuePair(Of Integer, List(Of String))) 'FileId, GameVersions
|
||||
@@ -417,7 +423,7 @@
|
||||
Json("DownloadCount") = DownloadCount
|
||||
If ModLoaders IsNot Nothing AndAlso ModLoaders.Any Then Json("ModLoaders") = New JArray(ModLoaders.Select(Function(m) CInt(m)))
|
||||
Json("Tags") = New JArray(Tags)
|
||||
If LogoUrl IsNot Nothing Then Json("LogoUrl") = LogoUrl
|
||||
If Not String.IsNullOrEmpty(LogoUrl) Then Json("LogoUrl") = LogoUrl
|
||||
If GameVersions.Any Then Json("GameVersions") = New JArray(GameVersions)
|
||||
Json("CacheTime") = Date.Now '用于检查缓存时间
|
||||
Return Json
|
||||
@@ -536,7 +542,7 @@
|
||||
Return NewItem
|
||||
End Function
|
||||
Public Function GetControlLogo() As String
|
||||
If LogoUrl Is Nothing Then
|
||||
If String.IsNullOrEmpty(LogoUrl) Then
|
||||
Return PathImage & "Icons/NoIcon.png"
|
||||
Else
|
||||
Return LogoUrl
|
||||
@@ -944,30 +950,30 @@ Retry:
|
||||
Dim CurseForgeFailed As Boolean = False
|
||||
If CurseForgeUrl IsNot Nothing Then
|
||||
CurseForgeThread = RunInNewThread(
|
||||
Sub()
|
||||
Try
|
||||
'获取工程列表
|
||||
Log("[Comp] 开始从 CurseForge 获取工程列表:" & CurseForgeUrl)
|
||||
Dim RequestResult As JObject = DlModRequest(CurseForgeUrl, IsJson:=True)
|
||||
Task.Progress += 0.2
|
||||
Dim ProjectList As New List(Of CompProject)
|
||||
For Each JsonEntry As JObject In RequestResult("data")
|
||||
ProjectList.Add(New CompProject(JsonEntry))
|
||||
Next
|
||||
'更新结果
|
||||
SyncLock ResultsLock
|
||||
RawResults.AddRange(ProjectList)
|
||||
End SyncLock
|
||||
Storage.CurseForgeOffset += ProjectList.Count
|
||||
Storage.CurseForgeTotal = RequestResult("pagination")("totalCount").ToObject(Of Integer)
|
||||
Log($"[Comp] 从 CurseForge 获取到了 {ProjectList.Count} 个工程(已获取 {Storage.CurseForgeOffset} 个,共 {Storage.CurseForgeTotal} 个)")
|
||||
Catch ex As Exception
|
||||
Log(ex, "从 CurseForge 获取工程列表失败")
|
||||
Storage.CurseForgeTotal = -1 'Storage.CurseForgeOffset
|
||||
[Error] = ex
|
||||
CurseForgeFailed = True
|
||||
End Try
|
||||
End Sub, "CurseForge Project Request")
|
||||
Sub()
|
||||
Try
|
||||
'获取工程列表
|
||||
Log("[Comp] 开始从 CurseForge 获取工程列表:" & CurseForgeUrl)
|
||||
Dim RequestResult As JObject = DlModRequest(CurseForgeUrl, IsJson:=True)
|
||||
Task.Progress += 0.2
|
||||
Dim ProjectList As New List(Of CompProject)
|
||||
For Each JsonEntry As JObject In RequestResult("data")
|
||||
ProjectList.Add(New CompProject(JsonEntry))
|
||||
Next
|
||||
'更新结果
|
||||
SyncLock ResultsLock
|
||||
RawResults.AddRange(ProjectList)
|
||||
End SyncLock
|
||||
Storage.CurseForgeOffset += ProjectList.Count
|
||||
Storage.CurseForgeTotal = RequestResult("pagination")("totalCount").ToObject(Of Integer)
|
||||
Log($"[Comp] 从 CurseForge 获取到了 {ProjectList.Count} 个工程(已获取 {Storage.CurseForgeOffset} 个,共 {Storage.CurseForgeTotal} 个)")
|
||||
Catch ex As Exception
|
||||
Log(ex, "从 CurseForge 获取工程列表失败")
|
||||
Storage.CurseForgeTotal = -1 'Storage.CurseForgeOffset
|
||||
[Error] = ex
|
||||
CurseForgeFailed = True
|
||||
End Try
|
||||
End Sub, "CurseForge Project Request")
|
||||
End If
|
||||
|
||||
'启动 Modrinth 线程
|
||||
@@ -975,32 +981,32 @@ Retry:
|
||||
Dim ModrinthFailed As Boolean = False
|
||||
If ModrinthUrl IsNot Nothing Then
|
||||
ModrinthThread = RunInNewThread(
|
||||
Sub()
|
||||
Try
|
||||
Log("[Comp] 开始从 Modrinth 获取工程列表:" & ModrinthUrl)
|
||||
Dim RequestResult As JObject = DlModRequest(ModrinthUrl, IsJson:=True)
|
||||
Task.Progress += 0.2
|
||||
Dim ProjectList As New List(Of CompProject)
|
||||
For Each JsonEntry As JObject In RequestResult("hits")
|
||||
ProjectList.Add(New CompProject(JsonEntry))
|
||||
Sub()
|
||||
Try
|
||||
Log("[Comp] 开始从 Modrinth 获取工程列表:" & ModrinthUrl)
|
||||
Dim RequestResult As JObject = DlModRequest(ModrinthUrl, IsJson:=True)
|
||||
Task.Progress += 0.2
|
||||
Dim ProjectList As New List(Of CompProject)
|
||||
For Each JsonEntry As JObject In RequestResult("hits")
|
||||
ProjectList.Add(New CompProject(JsonEntry))
|
||||
Next
|
||||
'更新结果
|
||||
SyncLock ResultsLock
|
||||
For Each Project In ProjectList
|
||||
If Task.Input.Type = CompType.Mod AndAlso Not Project.ModLoaders.Any() Then Continue For '过滤插件(#2458)
|
||||
RawResults.Add(Project)
|
||||
Next
|
||||
'更新结果
|
||||
SyncLock ResultsLock
|
||||
For Each Project In ProjectList
|
||||
If Task.Input.Type = CompType.Mod AndAlso Not Project.ModLoaders.Any() Then Continue For '过滤插件(#2458)
|
||||
RawResults.Add(Project)
|
||||
Next
|
||||
End SyncLock
|
||||
Storage.ModrinthOffset += ProjectList.Count
|
||||
Storage.ModrinthTotal = RequestResult("total_hits").ToObject(Of Integer)
|
||||
Log($"[Comp] 从 Modrinth 获取到了 {ProjectList.Count} 个工程(已获取 {Storage.ModrinthOffset} 个,共 {Storage.ModrinthTotal} 个)")
|
||||
Catch ex As Exception
|
||||
Log(ex, "从 Modrinth 获取工程列表失败")
|
||||
Storage.ModrinthTotal = -1 'Storage.ModrinthOffset
|
||||
[Error] = ex
|
||||
ModrinthFailed = True
|
||||
End Try
|
||||
End Sub, "Modrinth Project Request")
|
||||
End SyncLock
|
||||
Storage.ModrinthOffset += ProjectList.Count
|
||||
Storage.ModrinthTotal = RequestResult("total_hits").ToObject(Of Integer)
|
||||
Log($"[Comp] 从 Modrinth 获取到了 {ProjectList.Count} 个工程(已获取 {Storage.ModrinthOffset} 个,共 {Storage.ModrinthTotal} 个)")
|
||||
Catch ex As Exception
|
||||
Log(ex, "从 Modrinth 获取工程列表失败")
|
||||
Storage.ModrinthTotal = -1 'Storage.ModrinthOffset
|
||||
[Error] = ex
|
||||
ModrinthFailed = True
|
||||
End Try
|
||||
End Sub, "Modrinth Project Request")
|
||||
End If
|
||||
|
||||
'等待线程结束
|
||||
@@ -1077,7 +1083,7 @@ Retry:
|
||||
If String.IsNullOrEmpty(Task.Input.SearchText) Then
|
||||
'如果没有搜索文本,按下载量将结果排序
|
||||
For Each Result As CompProject In RealResults
|
||||
Scores.Add(Result, Result.DownloadCount * If(Result.FromCurseForge, 1, 30))
|
||||
Scores.Add(Result, Result.DownloadCount * If(Result.FromCurseForge, 1, 10))
|
||||
Next
|
||||
Else
|
||||
'如果有搜索文本,按关联度将结果排序
|
||||
@@ -1085,7 +1091,7 @@ Retry:
|
||||
Dim Entry As New List(Of SearchEntry(Of CompProject))
|
||||
For Each Result As CompProject In RealResults
|
||||
Scores.Add(Result, If(Result.WikiId > 0, 0.2, 0) +
|
||||
Math.Log10(Math.Max(Result.DownloadCount, 1) * If(Result.FromCurseForge, 1, 30)) / 9)
|
||||
Math.Log10(Math.Max(Result.DownloadCount, 1) * If(Result.FromCurseForge, 1, 10)) / 9)
|
||||
Entry.Add(New SearchEntry(Of CompProject) With {.Item = Result, .SearchSource = New List(Of KeyValuePair(Of String, Double)) From {
|
||||
New KeyValuePair(Of String, Double)(If(IsChineseSearch, Result.TranslatedName, Result.RawName), 1),
|
||||
New KeyValuePair(Of String, Double)(Result.Description, 0.05)}})
|
||||
@@ -1470,7 +1476,7 @@ Retry:
|
||||
End If
|
||||
'更新前置 Mod 信息
|
||||
If Deps.Any Then
|
||||
For Each DepProject In Deps.Select(Function(id) CompProjectCache(id))
|
||||
For Each DepProject In Deps.Where(Function(id) CompProjectCache.ContainsKey(id)).Select(Function(id) CompProjectCache(id))
|
||||
For Each File In CompFilesCache(ProjectId)
|
||||
If File.RawDependencies.Contains(DepProject.Id) AndAlso DepProject.Id <> ProjectId Then
|
||||
File.Dependencies.Add(DepProject.Id)
|
||||
@@ -1490,10 +1496,11 @@ Retry:
|
||||
Dim Deps As List(Of String) = Files.SelectMany(Function(f) f.Dependencies).Distinct.ToList()
|
||||
Deps.Sort()
|
||||
If Not Deps.Any() Then Exit Sub
|
||||
Deps = Deps.Where(Function(dep)
|
||||
If Not CompProjectCache.ContainsKey(dep) Then Log($"[Comp] 未找到 ID {dep} 的前置 Mod 信息", LogLevel.Debug)
|
||||
Return CompProjectCache.ContainsKey(dep)
|
||||
End Function).ToList
|
||||
Deps = Deps.Where(
|
||||
Function(dep)
|
||||
If Not CompProjectCache.ContainsKey(dep) Then Log($"[Comp] 未找到 ID {dep} 的前置 Mod 信息", LogLevel.Debug)
|
||||
Return CompProjectCache.ContainsKey(dep)
|
||||
End Function).ToList
|
||||
'添加开头间隔
|
||||
Stack.Children.Add(New TextBlock With {.Text = "前置 Mod", .FontSize = 14, .HorizontalAlignment = HorizontalAlignment.Left, .Margin = New Thickness(6, 2, 0, 5)})
|
||||
'添加前置 Mod 列表
|
||||
|
||||
@@ -844,8 +844,11 @@ NextStack:
|
||||
Public Sub Output(IsHandAnalyze As Boolean, Optional ExtraFiles As List(Of String) = Nothing)
|
||||
'弹窗提示
|
||||
FrmMain.ShowWindowToTop()
|
||||
Dim ShowLog As Action =
|
||||
Select Case MyMsgBox(GetAnalyzeResult(IsHandAnalyze), If(IsHandAnalyze, "错误报告分析结果", "Minecraft 出现错误"),
|
||||
"确定", If(IsHandAnalyze OrElse DirectFile Is Nothing, "", "查看日志"), If(IsHandAnalyze, "", "导出错误报告"),
|
||||
Button2Action:=If(IsHandAnalyze OrElse DirectFile Is Nothing, Nothing,
|
||||
Sub()
|
||||
'弹窗选择:查看日志
|
||||
If File.Exists(DirectFile.Value.Key) Then
|
||||
ShellOnly("notepad", DirectFile.Value.Key)
|
||||
Else
|
||||
@@ -853,11 +856,9 @@ NextStack:
|
||||
WriteFile(FilePath, Join(DirectFile.Value.Value, vbCrLf))
|
||||
ShellOnly(FilePath)
|
||||
End If
|
||||
End Sub
|
||||
Select Case MyMsgBox(GetAnalyzeResult(IsHandAnalyze), If(IsHandAnalyze, "错误报告分析结果", "Minecraft 出现错误"),
|
||||
"确定", If(IsHandAnalyze OrElse DirectFile Is Nothing, "", "查看日志"), If(IsHandAnalyze, "", "导出错误报告"),
|
||||
Button2Action:=If(IsHandAnalyze OrElse DirectFile Is Nothing, Nothing, ShowLog))
|
||||
End Sub))
|
||||
Case 3
|
||||
'弹窗选择:导出错误报告
|
||||
Dim FileAddress As String = Nothing
|
||||
Try
|
||||
'获取文件路径
|
||||
@@ -881,7 +882,7 @@ NextStack:
|
||||
FileName = "游戏崩溃前的输出.txt"
|
||||
End Select
|
||||
If File.Exists(OutputFile) Then
|
||||
Dim FileEncoding As Encoding = GetEncoding(OutputFile)
|
||||
Dim FileEncoding As Encoding = GetEncoding(ReadFileBytes(OutputFile))
|
||||
WriteFile(TempFolder & "Report\" & FileName,
|
||||
SecretFilter(ReadFile(OutputFile, FileEncoding), If(FileName = "启动脚本.bat", "F", "*")),
|
||||
Encoding:=FileEncoding)
|
||||
|
||||
@@ -21,12 +21,7 @@
|
||||
End If
|
||||
'检查文件
|
||||
Dim Checker As New FileChecker(MinSize:=1024, ActualSize:=If(Version.JsonObject("downloads")("client")("size"), -1), Hash:=Version.JsonObject("downloads")("client")("sha1"))
|
||||
If ReturnNothingOnFileUseable Then
|
||||
'是否跳过
|
||||
Dim IsSetupSkip As Boolean = ShouldIgnoreFileCheck(Version)
|
||||
If IsSetupSkip AndAlso File.Exists(Version.Path & Version.Name & ".jar") Then Return Nothing '跳过校验
|
||||
If Checker.Check(Version.Path & Version.Name & ".jar") Is Nothing Then Return Nothing '通过校验
|
||||
End If
|
||||
If ReturnNothingOnFileUseable AndAlso Checker.Check(Version.Path & Version.Name & ".jar") Is Nothing Then Return Nothing '通过校验
|
||||
'返回下载信息
|
||||
Dim JarUrl As String = Version.JsonObject("downloads")("client")("url")
|
||||
Return New NetFile(DlSourceLauncherOrMetaGet(JarUrl), Version.Path & Version.Name & ".jar", Checker)
|
||||
@@ -56,7 +51,7 @@
|
||||
''' <summary>
|
||||
''' 构造补全某 Minecraft 版本的所有文件的加载器列表。失败会抛出异常。
|
||||
''' </summary>
|
||||
Public Function DlClientFix(Version As McVersion, CheckAssetsHash As Boolean, AssetsIndexBehaviour As AssetsIndexExistsBehaviour, SkipAssetsDownloadWhileSetupRequired As Boolean) As List(Of LoaderBase)
|
||||
Public Function DlClientFix(Version As McVersion, CheckAssetsHash As Boolean, AssetsIndexBehaviour As AssetsIndexExistsBehaviour) As List(Of LoaderBase)
|
||||
Dim Loaders As New List(Of LoaderBase)
|
||||
|
||||
#Region "下载支持库文件"
|
||||
@@ -69,9 +64,9 @@
|
||||
#End Region
|
||||
|
||||
#Region "下载资源文件"
|
||||
Dim IsSetupSkip As Boolean = ShouldIgnoreFileCheck(Version)
|
||||
If IsSetupSkip Then Log("[Download] 已跳过 Assets 下载")
|
||||
If (Not SkipAssetsDownloadWhileSetupRequired) OrElse Not IsSetupSkip Then
|
||||
If ShouldIgnoreFileCheck(Version) Then
|
||||
Log("[Download] 已跳过所有 Assets 检查")
|
||||
Else
|
||||
Dim LoadersAssets As New List(Of LoaderBase)
|
||||
'获取资源文件索引地址
|
||||
LoadersAssets.Add(New LoaderTask(Of String, List(Of NetFile))("分析资源文件索引地址",
|
||||
@@ -104,7 +99,7 @@
|
||||
Task.Output = New List(Of NetFile) From {BackAssetsFile}
|
||||
'检查是否需要更新:每天只更新一次
|
||||
If File.Exists(RealAddress) AndAlso Math.Abs((File.GetLastWriteTime(RealAddress).Date - Now.Date).TotalDays) < 1 Then
|
||||
Log("[Download] 无需更新资源文件索引")
|
||||
Log("[Download] 无需更新资源文件索引,取消")
|
||||
Task.Abort()
|
||||
End If
|
||||
End Sub))
|
||||
@@ -121,7 +116,7 @@
|
||||
'获取资源文件地址
|
||||
LoadersAssets.Add(New LoaderTask(Of String, List(Of NetFile))("分析缺失资源文件",
|
||||
Sub(Task As LoaderTask(Of String, List(Of NetFile)))
|
||||
Task.Output = McAssetsFixList(McAssetsGetIndexName(Version), CheckAssetsHash, Task)
|
||||
Task.Output = McAssetsFixList(Version, CheckAssetsHash, Task)
|
||||
End Sub) With {.ProgressWeight = 3})
|
||||
'下载资源文件
|
||||
LoadersAssets.Add(New LoaderDownload("下载资源文件", New List(Of NetFile)) With {.ProgressWeight = 25})
|
||||
@@ -1087,29 +1082,46 @@
|
||||
|
||||
''' <summary>
|
||||
''' 对可能涉及 Mod 镜像源的请求进行处理,返回字符串或 JObject。
|
||||
''' 调用 NetGetCodeByRequestOnce。
|
||||
''' 调用 NetGetCodeByRequest。
|
||||
''' </summary>
|
||||
Public Function DlModRequest(Url As String, Optional IsJson As Boolean = False) As Object
|
||||
Dim McimUrl As String = DlSourceModGet(Url)
|
||||
Dim Urls As New List(Of KeyValuePair(Of String, Integer))
|
||||
If McimUrl <> Url Then
|
||||
Select Case Setup.Get("ToolDownloadMod")
|
||||
'UNDONE: 在 MCIM 源稳定后回调
|
||||
Case 0
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 30))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 60))
|
||||
If ModeDebug Then
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 10))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 20))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 30))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 60))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 60))
|
||||
Else
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 5))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 20))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 30))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 60))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 60))
|
||||
End If
|
||||
Case 1
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 5))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 35))
|
||||
Case Else
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 20))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 30))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 60))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 60))
|
||||
Case Else
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 5))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 30))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 60))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 60))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 60))
|
||||
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)
|
||||
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
|
||||
@@ -1119,28 +1131,46 @@
|
||||
|
||||
''' <summary>
|
||||
''' 对可能涉及 Mod 镜像源的请求进行处理。
|
||||
''' 调用 NetRequestOnce。
|
||||
''' 调用 NetRequest。
|
||||
''' </summary>
|
||||
Public Function DlModRequest(Url As String, Method As String, Data As String, ContentType As String, Optional Headers As Dictionary(Of String, String) = Nothing) As String
|
||||
Public Function DlModRequest(Url As String, Method As String, Data As String, ContentType As String) As String
|
||||
Dim McimUrl As String = DlSourceModGet(Url)
|
||||
Dim Urls As New List(Of KeyValuePair(Of String, Integer))
|
||||
If McimUrl <> Url Then
|
||||
Select Case Setup.Get("ToolDownloadMod")
|
||||
'UNDONE: 在 MCIM 源稳定后回调
|
||||
Case 0
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 30))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 60))
|
||||
If ModeDebug Then
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 10))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 20))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 30))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 60))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 60))
|
||||
Else
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 5))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 20))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 30))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 60))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 60))
|
||||
End If
|
||||
Case 1
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 5))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 35))
|
||||
Case Else
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 20))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 30))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 60))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 60))
|
||||
Case Else
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 5))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 30))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 60))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 60))
|
||||
Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 60))
|
||||
End Select
|
||||
End If
|
||||
Dim Exs As String = ""
|
||||
For Each Source In Urls
|
||||
Try
|
||||
Return NetRequestOnce(Source.Key, Method, Data, ContentType, Timeout:=Source.Value * 1000, Headers:=Headers)
|
||||
Return NetRequestOnce(Source.Key, Method, Data, ContentType, Timeout:=Source.Value * 1000)
|
||||
Catch ex As Exception
|
||||
Exs += ex.Message + vbCrLf
|
||||
End Try
|
||||
|
||||
@@ -216,8 +216,8 @@
|
||||
|
||||
'添加特定的 Java
|
||||
Dim JavaPreList As New Dictionary(Of String, Boolean)
|
||||
If PathMcFolder.Split("\").Count > 3 Then
|
||||
JavaSearchFolder(GetPathFromFullPath(PathMcFolder), JavaPreList, False, True) 'Minecraft 文件夹的父文件夹(如果不是根目录的话)
|
||||
If PathMcFolder.Split("\").Count > 3 AndAlso Not PathMcFolder.Contains("AppData\Roaming") Then
|
||||
JavaSearchFolder(GetPathFromFullPath(PathMcFolder), JavaPreList, False, True) 'Minecraft 文件夹的父文件夹(如果不是根目录或 %APPDATA% 的话)
|
||||
End If
|
||||
JavaSearchFolder(PathMcFolder, JavaPreList, False, True) 'Minecraft 文件夹
|
||||
JavaPreList = JavaPreList.Where(Function(j) Not j.Key.Contains(".minecraft\runtime")).
|
||||
|
||||
@@ -4,6 +4,7 @@ Public Module ModLaunch
|
||||
|
||||
#Region "开始"
|
||||
|
||||
Public CurrentLaunchOptions As McLaunchOptions = Nothing
|
||||
Public Class McLaunchOptions
|
||||
''' <summary>
|
||||
''' 强制指定在启动后进入的服务器 IP。
|
||||
@@ -20,32 +21,34 @@ Public Module ModLaunch
|
||||
''' 默认值:Nothing。使用 McVersionCurrent。
|
||||
''' </summary>
|
||||
Public Version As McVersion = Nothing
|
||||
''' <summary>
|
||||
''' 额外的启动参数。
|
||||
''' </summary>
|
||||
Public ExtraArgs As New List(Of String)
|
||||
End Class
|
||||
''' <summary>
|
||||
''' 尝试启动 Minecraft。必须在 UI 线程调用。
|
||||
''' 返回是否实际开始了启动(如果没有,则一定弹出了错误提示)。
|
||||
''' </summary>
|
||||
Public Function McLaunchStart(Optional Options As McLaunchOptions = Nothing) As Boolean
|
||||
Options = If(Options, New McLaunchOptions)
|
||||
CurrentLaunchOptions = If(Options, New McLaunchOptions)
|
||||
'预检查
|
||||
If Not RunInUi() Then
|
||||
Throw New Exception("McLaunchStart 必须在 UI 线程调用!")
|
||||
End If
|
||||
If Not RunInUi() Then Throw New Exception("McLaunchStart 必须在 UI 线程调用!")
|
||||
If McLaunchLoader.State = LoadState.Loading Then
|
||||
Hint("已有游戏正在启动中!", HintType.Critical)
|
||||
Return False
|
||||
End If
|
||||
'强制切换需要启动的版本
|
||||
If Options.Version IsNot Nothing AndAlso McVersionCurrent <> Options.Version Then
|
||||
McLaunchLog("在启动前切换到版本 " & Options.Version.Name)
|
||||
If CurrentLaunchOptions.Version IsNot Nothing AndAlso McVersionCurrent <> CurrentLaunchOptions.Version Then
|
||||
McLaunchLog("在启动前切换到版本 " & CurrentLaunchOptions.Version.Name)
|
||||
'检查版本
|
||||
Options.Version.Load()
|
||||
If Options.Version.State = McVersionState.Error Then
|
||||
Hint("无法启动 Minecraft:" & Options.Version.Info, HintType.Critical)
|
||||
CurrentLaunchOptions.Version.Load()
|
||||
If CurrentLaunchOptions.Version.State = McVersionState.Error Then
|
||||
Hint("无法启动 Minecraft:" & CurrentLaunchOptions.Version.Info, HintType.Critical)
|
||||
Return False
|
||||
End If
|
||||
'切换版本
|
||||
McVersionCurrent = Options.Version
|
||||
McVersionCurrent = CurrentLaunchOptions.Version
|
||||
Setup.Set("LaunchVersionSelect", McVersionCurrent.Name)
|
||||
FrmLaunchLeft.RefreshButtonsUI()
|
||||
FrmLaunchLeft.RefreshPage(False, False)
|
||||
@@ -95,7 +98,7 @@ Public Module ModLaunch
|
||||
McLaunchPrecheck()
|
||||
McLaunchLog("预检测已通过")
|
||||
Catch ex As Exception
|
||||
Hint(ex.Message, HintType.Critical)
|
||||
If Not ex.Message.StartsWithF("$$") Then Hint(ex.Message, HintType.Critical)
|
||||
Throw
|
||||
End Try
|
||||
'正式加载
|
||||
@@ -103,8 +106,8 @@ Public Module ModLaunch
|
||||
'构造主加载器
|
||||
Dim Loaders As New List(Of LoaderBase) From {
|
||||
New LoaderTask(Of Integer, Integer)("获取 Java", AddressOf McLaunchJava) With {.ProgressWeight = 4, .Block = False},
|
||||
McLoginLoader,
|
||||
New LoaderCombo(Of String)("补全文件", DlClientFix(McVersionCurrent, False, AssetsIndexExistsBehaviour.DownloadInBackground, True)) With {.ProgressWeight = 15, .Show = False},
|
||||
McLoginLoader, '.ProgressWeight = 15, .Block = False
|
||||
New LoaderCombo(Of String)("补全文件", DlClientFix(McVersionCurrent, False, AssetsIndexExistsBehaviour.DownloadInBackground)) With {.ProgressWeight = 15, .Show = False},
|
||||
New LoaderTask(Of String, List(Of McLibToken))("获取启动参数", AddressOf McLaunchArgumentMain) With {.ProgressWeight = 2},
|
||||
New LoaderTask(Of List(Of McLibToken), Integer)("解压文件", AddressOf McLaunchNatives) With {.ProgressWeight = 2},
|
||||
New LoaderTask(Of Integer, Integer)("预启动处理", AddressOf McLaunchPrerun) With {.ProgressWeight = 1},
|
||||
@@ -144,7 +147,7 @@ Public Module ModLaunch
|
||||
Hint(McVersionCurrent.Name & " 启动成功!", HintType.Finish)
|
||||
Case LoadState.Aborted
|
||||
If AbortHint Is Nothing Then
|
||||
Hint(If(Loader.Input.SaveBatch Is Nothing, "已取消启动!", "已取消导出启动脚本!"), HintType.Info)
|
||||
Hint(If(CurrentLaunchOptions?.SaveBatch Is Nothing, "已取消启动!", "已取消导出启动脚本!"), HintType.Info)
|
||||
Else
|
||||
Hint(AbortHint, HintType.Finish)
|
||||
End If
|
||||
@@ -159,7 +162,7 @@ NextInner:
|
||||
If CurrentEx.Message.StartsWithF("$") Then
|
||||
'若有以 $ 开头的错误信息,则以此为准显示提示
|
||||
'若错误信息为 $$,则不提示
|
||||
If Not CurrentEx.Message = "$$" Then MyMsgBox(CurrentEx.Message.TrimStart("$"), If(Loader.Input.SaveBatch Is Nothing, "启动失败", "导出启动脚本失败"))
|
||||
If Not CurrentEx.Message = "$$" Then MyMsgBox(CurrentEx.Message.TrimStart("$"), If(CurrentLaunchOptions?.SaveBatch Is Nothing, "启动失败", "导出启动脚本失败"))
|
||||
Throw
|
||||
ElseIf CurrentEx.InnerException IsNot Nothing Then
|
||||
'检查下一级错误
|
||||
@@ -168,7 +171,9 @@ NextInner:
|
||||
Else
|
||||
'没有特殊处理过的错误信息
|
||||
McLaunchLog("错误:" & GetExceptionDetail(ex))
|
||||
Log(ex, If(Loader.Input.SaveBatch Is Nothing, "Minecraft 启动失败", "导出启动脚本失败"), LogLevel.Msgbox, If(Loader.Input.SaveBatch Is Nothing, "启动失败", "导出启动脚本失败"))
|
||||
Log(ex,
|
||||
If(CurrentLaunchOptions?.SaveBatch Is Nothing, "Minecraft 启动失败", "导出启动脚本失败"), LogLevel.Msgbox,
|
||||
If(CurrentLaunchOptions?.SaveBatch Is Nothing, "启动失败", "导出启动脚本失败"))
|
||||
Throw
|
||||
End If
|
||||
End Try
|
||||
@@ -211,36 +216,45 @@ NextInner:
|
||||
If McVersionCurrent.State = McVersionState.Error Then Throw New Exception("Minecraft 存在问题:" & McVersionCurrent.Info)
|
||||
'检查输入信息
|
||||
Dim CheckResult As String = ""
|
||||
RunInUiWait(Sub()
|
||||
Dim LoginInput As McLoginData = McLoginInput()
|
||||
CheckResult = McLoginAble(LoginInput)
|
||||
End Sub)
|
||||
RunInUiWait(Sub() CheckResult = McLoginAble(McLoginInput()))
|
||||
If CheckResult <> "" Then Throw New ArgumentException(CheckResult)
|
||||
#If BETA Then
|
||||
'求赞助
|
||||
RunInNewThread(Sub()
|
||||
Select Case Setup.Get("SystemLaunchCount")
|
||||
Case 20, 50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1700, 1900, 2100, 2300, 2500
|
||||
If MyMsgBox("PCL 已经为你启动了 " & Setup.Get("SystemLaunchCount") & " 次游戏啦!" & vbCrLf &
|
||||
"如果觉得 PCL 还算好用的话,也可以考虑赞助一下作者……一点心意也行……" & vbCrLf &
|
||||
"毕竟一个人开发也不容易(悲)……",
|
||||
"求赞助啦……", "这就赞助!", "但是我拒绝") = 1 Then
|
||||
OpenWebsite("https://afdian.com/a/LTCat")
|
||||
End If
|
||||
End Select
|
||||
End Sub, "Donate")
|
||||
RunInNewThread(
|
||||
Sub()
|
||||
Select Case Setup.Get("SystemLaunchCount")
|
||||
Case 10, 20, 40, 60, 80, 100, 120, 150, 200, 250, 300, 350, 400, 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000
|
||||
If MyMsgBox("PCL 已经为你启动了 " & Setup.Get("SystemLaunchCount") & " 次游戏啦!" & vbCrLf &
|
||||
"如果觉得 PCL 还算好用的话,也可以考虑赞助一下作者……一点心意也行……" & vbCrLf &
|
||||
"毕竟一个人开发也不容易(悲)……",
|
||||
"求赞助啦……", "这就赞助!", "但是我拒绝") = 1 Then
|
||||
OpenWebsite("https://afdian.com/a/LTCat")
|
||||
End If
|
||||
End Select
|
||||
End Sub, "Donate")
|
||||
#End If
|
||||
'正版购买提示
|
||||
If Not Setup.Get("HintBuy") AndAlso Setup.Get("LoginType") <> McLoginType.Ms Then
|
||||
Select Case Setup.Get("SystemLaunchCount")
|
||||
Case 10, 35, 75, 125, 175, 225, 275, 325, 375, 425, 475, 550, 650, 750, 850, 950, 1050, 1150, 1250, 1350, 1450, 1600, 1800, 2000, 2200, 2400
|
||||
If MyMsgBox("你已经启动了 " & Setup.Get("SystemLaunchCount") & " 次 Minecraft 啦!" & vbCrLf &
|
||||
"如果觉得 Minecraft 还不错,也可以考虑购买正版支持一下,毕竟开发游戏也很不容易……" & vbCrLf &
|
||||
"在你登录一次正版账号后,就不会再出现这个提示了。",
|
||||
"考虑一下正版?", "购买正版游戏", "下次一定") = 1 Then
|
||||
OpenWebsite("https://www.xbox.com/zh-cn/games/store/minecraft-java-bedrock-edition-for-pc/9nxp44l49shj")
|
||||
End If
|
||||
End Select
|
||||
If IsSystemLanguageChinese() Then
|
||||
Select Case Setup.Get("SystemLaunchCount")
|
||||
Case 2, 5, 10, 15, 20, 40, 60, 80, 100, 125, 150, 175, 200, 250, 300, 350, 400, 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000
|
||||
If MyMsgBox("你已经启动了 " & Setup.Get("SystemLaunchCount") & " 次 Minecraft 啦!" & vbCrLf &
|
||||
"如果觉得 Minecraft 还不错,可以购买正版支持一下,毕竟开发游戏也真的很不容易……" & vbCrLf & vbCrLf &
|
||||
"在你登录一次正版账号后,就不会再出现这个提示了!",
|
||||
"考虑一下正版?", "支持正版游戏!", "下次一定") = 1 Then
|
||||
OpenWebsite("https://www.xbox.com/zh-cn/games/store/minecraft-java-bedrock-edition-for-pc/9nxp44l49shj")
|
||||
End If
|
||||
End Select
|
||||
ElseIf Setup.Get("LoginType") = McLoginType.Legacy Then
|
||||
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)
|
||||
CurrentLaunchOptions.ExtraArgs.Add("--demo")
|
||||
Case 3
|
||||
Throw New Exception("$$")
|
||||
End Select
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
|
||||
@@ -487,19 +501,22 @@ NextInner:
|
||||
#Region "分方式登录模块"
|
||||
|
||||
'各个登录方式的主对象与输入构造
|
||||
Public McLoginMsLoader As New LoaderTask(Of McLoginMs, McLoginResult)("Loader Login Ms", AddressOf McLoginMsStart) With {.ReloadTimeout = 300000}
|
||||
Public McLoginMsLoader As New LoaderTask(Of McLoginMs, McLoginResult)("Loader Login Ms", AddressOf McLoginMsStart)
|
||||
Public McLoginLegacyLoader As New LoaderTask(Of McLoginLegacy, McLoginResult)("Loader Login Legacy", AddressOf McLoginLegacyStart)
|
||||
Public McLoginNideLoader As New LoaderTask(Of McLoginServer, McLoginResult)("Loader Login Nide", AddressOf McLoginServerStart) With {.ReloadTimeout = 60000}
|
||||
Public McLoginAuthLoader As New LoaderTask(Of McLoginServer, McLoginResult)("Loader Login Auth", AddressOf McLoginServerStart) With {.ReloadTimeout = 60000}
|
||||
Public McLoginNideLoader As New LoaderTask(Of McLoginServer, McLoginResult)("Loader Login Nide", AddressOf McLoginServerStart) With {.ReloadTimeout = 1000 * 60 * 10}
|
||||
Public McLoginAuthLoader As New LoaderTask(Of McLoginServer, McLoginResult)("Loader Login Auth", AddressOf McLoginServerStart) With {.ReloadTimeout = 1000 * 60 * 10}
|
||||
|
||||
'主加载函数,返回所有需要的登录信息
|
||||
Private McLoginMsRefreshTime As Long = 0 '上次刷新登录的时间
|
||||
Private Sub McLoginMsStart(Data As LoaderTask(Of McLoginMs, McLoginResult))
|
||||
Dim Input As McLoginMs = Data.Input
|
||||
Dim LogUsername As String = Input.UserName
|
||||
McLaunchLog("登录方式:正版(" & If(LogUsername = "", "尚未登录", LogUsername) & ")")
|
||||
Data.Progress = 0.05
|
||||
'检查是否已经登录完成
|
||||
If Input.AccessToken <> "" AndAlso Not Data.IsForceRestarting Then
|
||||
If Not Data.IsForceRestarting AndAlso '不要求强行重启
|
||||
Input.AccessToken <> "" AndAlso '已经登录过了
|
||||
(McLoginMsRefreshTime > 0 AndAlso GetTimeTick() - McLoginMsRefreshTime < 1000 * 60 * 10) Then '完成时间在 10 分钟内
|
||||
Data.Output = New McLoginResult With
|
||||
{.AccessToken = Input.AccessToken, .Name = Input.UserName, .Uuid = Input.Uuid, .Type = "Microsoft", .ClientToken = Input.Uuid, .ProfileJson = Input.ProfileJson}
|
||||
GoTo SkipLogin
|
||||
@@ -509,35 +526,31 @@ NextInner:
|
||||
If Input.OAuthRefreshToken = "" Then
|
||||
'无 RefreshToken
|
||||
Relogin:
|
||||
Dim OAuthCode As String = MsLoginStep1(Data)
|
||||
If OAuthCode = "Cancel" Then Exit Sub
|
||||
If Data.IsAborted Then Throw New ThreadInterruptedException
|
||||
Data.Progress = 0.2
|
||||
OAuthTokens = MsLoginStep2(OAuthCode, False)
|
||||
OAuthTokens = MsLoginStep1New(Data)
|
||||
Else
|
||||
'有 RefreshToken
|
||||
OAuthTokens = MsLoginStep2(Input.OAuthRefreshToken, True)
|
||||
OAuthTokens = MsLoginStep1Refresh(Input.OAuthRefreshToken)
|
||||
If OAuthTokens(0) = "Relogin" Then GoTo Relogin '要求重新打开登录网页认证
|
||||
End If
|
||||
'要求重新打开登录网页认证
|
||||
If OAuthTokens(0) = "Cancel" Then Exit Sub
|
||||
If OAuthTokens(0) = "Relogin" Then GoTo Relogin
|
||||
Data.Progress = 0.35
|
||||
If Data.IsAborted Then Throw New ThreadInterruptedException
|
||||
Data.Progress = 0.25
|
||||
If Data.IsAborted Then Throw New ThreadInterruptedException
|
||||
Dim OAuthAccessToken As String = OAuthTokens(0)
|
||||
Dim OAuthRefreshToken As String = OAuthTokens(1)
|
||||
Dim XBLToken As String = MsLoginStep3(OAuthAccessToken)
|
||||
Data.Progress = 0.5
|
||||
Dim XBLToken As String = MsLoginStep2(OAuthAccessToken)
|
||||
Data.Progress = 0.4
|
||||
If Data.IsAborted Then Throw New ThreadInterruptedException
|
||||
Dim Tokens = MsLoginStep4(XBLToken)
|
||||
Data.Progress = 0.65
|
||||
Dim Tokens = MsLoginStep3(XBLToken)
|
||||
Data.Progress = 0.55
|
||||
If Data.IsAborted Then Throw New ThreadInterruptedException
|
||||
Dim AccessToken As String = MsLoginStep5(Tokens)
|
||||
Data.Progress = 0.8
|
||||
Dim AccessToken As String = MsLoginStep4(Tokens)
|
||||
Data.Progress = 0.7
|
||||
If Data.IsAborted Then Throw New ThreadInterruptedException
|
||||
MsLoginStep6(AccessToken)
|
||||
Data.Progress = 0.9
|
||||
MsLoginStep5(AccessToken)
|
||||
Data.Progress = 0.85
|
||||
If Data.IsAborted Then Throw New ThreadInterruptedException
|
||||
Dim Result = MsLoginStep7(AccessToken)
|
||||
Dim Result = MsLoginStep6(AccessToken)
|
||||
Data.Progress = 0.98
|
||||
'输出登录结果
|
||||
Setup.Set("CacheMsOAuthRefresh", OAuthRefreshToken)
|
||||
Setup.Set("CacheMsAccess", AccessToken)
|
||||
@@ -549,11 +562,11 @@ Relogin:
|
||||
MsJson(Result(1)) = OAuthRefreshToken
|
||||
Setup.Set("LoginMsJson", MsJson.ToString(Newtonsoft.Json.Formatting.None))
|
||||
Data.Output = New McLoginResult With {.AccessToken = AccessToken, .Name = Result(1), .Uuid = Result(0), .Type = "Microsoft", .ClientToken = Result(0), .ProfileJson = Result(2)}
|
||||
'解锁主题
|
||||
SkipLogin:
|
||||
'结束
|
||||
McLoginMsRefreshTime = GetTimeTick()
|
||||
McLaunchLog("微软登录完成")
|
||||
SkipLogin:
|
||||
Setup.Set("HintBuy", True) '关闭正版购买提示
|
||||
Data.Progress = 0.98
|
||||
If ThemeUnlock(10, False) Then MyMsgBox("感谢你对正版游戏的支持!" & vbCrLf & "隐藏主题 跳票红 已解锁!", "提示")
|
||||
End Sub
|
||||
Private Sub McLoginServerStart(Data As LoaderTask(Of McLoginServer, McLoginResult))
|
||||
@@ -670,6 +683,7 @@ LoginFinish:
|
||||
Url:=Data.Input.BaseUrl & "/validate",
|
||||
Method:="POST",
|
||||
Data:=RequestData.ToString(0),
|
||||
Headers:=New Dictionary(Of String, String) From {{"Accept-Language", "zh_CN"}},
|
||||
ContentType:="application/json; charset=utf-8") '没有返回值的
|
||||
'将登录结果输出
|
||||
Data.Output.AccessToken = AccessToken
|
||||
@@ -693,6 +707,7 @@ LoginFinish:
|
||||
""name"":""" & Setup.Get("Cache" & Data.Input.Token & "Name") & """},", "") & "
|
||||
""accessToken"":""" & Setup.Get("Cache" & Data.Input.Token & "Access") & """,
|
||||
""clientToken"":""" & Setup.Get("Cache" & Data.Input.Token & "Client") & """}",
|
||||
Headers:=New Dictionary(Of String, String) From {{"Accept-Language", "zh_CN"}},
|
||||
ContentType:="application/json; charset=utf-8"))
|
||||
'将登录结果输出
|
||||
If LoginJson("selectedProfile") Is Nothing Then Throw New Exception("选择的角色 " & Setup.Get("Cache" & Data.Input.Token & "Name") & " 无效!")
|
||||
@@ -723,6 +738,7 @@ LoginFinish:
|
||||
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"))
|
||||
'检查登录结果
|
||||
If LoginJson("availableProfiles").Count = 0 Then
|
||||
@@ -782,6 +798,16 @@ LoginFinish:
|
||||
Catch ex As Exception
|
||||
Dim AllMessage As String = GetExceptionSummary(ex)
|
||||
Log(ex, "登录失败原始错误信息", LogLevel.Normal)
|
||||
'读取服务器返回的错误
|
||||
If TypeOf ex Is ResponsedWebException Then
|
||||
Dim ErrorMessage As String = Nothing
|
||||
Try
|
||||
ErrorMessage = GetJson(DirectCast(ex, ResponsedWebException).Response)("errorMessage")
|
||||
Catch
|
||||
End Try
|
||||
If Not String.IsNullOrWhiteSpace(ErrorMessage) Then Throw New Exception("$登录失败:" & ErrorMessage)
|
||||
End If
|
||||
'通用关键字检测
|
||||
If AllMessage.Contains("403") Then
|
||||
Select Case Data.Input.Type
|
||||
Case McLoginType.Auth
|
||||
@@ -807,46 +833,48 @@ LoginFinish:
|
||||
End Try
|
||||
End Function
|
||||
|
||||
'微软登录步骤 1:打开网页认证,获取 OAuth Code
|
||||
Private Function MsLoginStep1(Data As LoaderTask(Of McLoginMs, McLoginResult)) As String
|
||||
McLaunchLog("开始微软登录步骤 1")
|
||||
OpenWebsite(FormLoginOAuth.LoginUrl1)
|
||||
Dim Result As String =
|
||||
MyMsgBoxInput("等待网页登录",
|
||||
"登录完成后,网页会变得完全空白。把那个空白网页的网址复制到下面的框中就行!" & vbCrLf &
|
||||
"如果网络环境不佳,它可能一直加载不出来,那就只能试试用 VPN 或加速器了。",
|
||||
ValidateRules:=New ObjectModel.Collection(Of Validate) From {New ValidateRegex("(?<=code\=)[^&]+", "返回网址应以 https://login.live.com/oauth20_desktop.srf?code= 开头")},
|
||||
HintText:="https://login.live.com/oauth20_desktop.srf?code=XXXXXX")
|
||||
If Result Is Nothing Then
|
||||
McLaunchLog("微软登录已在步骤 1 被取消")
|
||||
Throw New ThreadInterruptedException("$$")
|
||||
'微软登录步骤 1,原始登录:获取 DeviceCode 并开启登录网页
|
||||
Private Function MsLoginStep1New(Data As LoaderTask(Of McLoginMs, McLoginResult)) As String()
|
||||
'参考:https://learn.microsoft.com/zh-cn/entra/identity-platform/v2-oauth2-device-code
|
||||
|
||||
'初始请求
|
||||
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"))
|
||||
McLaunchLog("网页登录地址:" & PrepareJson("verification_uri").ToString)
|
||||
|
||||
'弹窗
|
||||
Dim Converter As New MyMsgBoxConverter With {.Content = PrepareJson, .ForceWait = True, .Type = MyMsgBoxType.Login}
|
||||
WaitingMyMsgBox.Add(Converter)
|
||||
While Converter.Result Is Nothing
|
||||
Thread.Sleep(100)
|
||||
End While
|
||||
If TypeOf Converter.Result Is RetryException Then
|
||||
If MyMsgBox($"请在登录时选择 {vbLQ}其他登录方法{vbRQ},然后选择 {vbLQ}使用我的密码{vbRQ}。{vbCrLf}如果没有该选项,请选择 {vbLQ}设置密码{vbRQ},设置完毕后再登录。",
|
||||
"需要使用密码登录", "重新登录", "设置密码", "取消",
|
||||
Button2Action:=Sub() OpenWebsite("https://account.live.com/password/Change")) = 1 Then
|
||||
GoTo Retry
|
||||
Else
|
||||
Throw New Exception("$$")
|
||||
End If
|
||||
ElseIf TypeOf Converter.Result Is Exception Then
|
||||
Throw CType(Converter.Result, Exception)
|
||||
Else
|
||||
Return RegexSeek(Result, "(?<=code\=)[^&]+")
|
||||
Return Converter.Result
|
||||
End If
|
||||
End Function
|
||||
'微软登录步骤 2:从 OAuth Code 或 OAuth RefreshToken 获取 {OAuth AccessToken, OAuth RefreshToken}
|
||||
Private Function MsLoginStep2(Code As String, IsRefresh As Boolean) As String()
|
||||
McLaunchLog("开始微软登录步骤 2(" & If(IsRefresh, "", "非") & "刷新登录)")
|
||||
'微软登录步骤 1,刷新登录:从 OAuth Code 或 OAuth RefreshToken 获取 {OAuth AccessToken, OAuth RefreshToken}
|
||||
Private Function MsLoginStep1Refresh(Code As String) As String()
|
||||
McLaunchLog("开始微软登录步骤 1/6(刷新登录)")
|
||||
|
||||
Dim Request As String
|
||||
If IsRefresh Then
|
||||
Request = "client_id=00000000402b5328" & "&" &
|
||||
"refresh_token=" & Uri.EscapeDataString(Code) & "&" &
|
||||
"grant_type=refresh_token" & "&" &
|
||||
"redirect_uri=" & Uri.EscapeDataString("https://login.live.com/oauth20_desktop.srf") & "&" &
|
||||
"scope=" & Uri.EscapeDataString("service::user.auth.xboxlive.com::MBI_SSL")
|
||||
Else
|
||||
Request = "client_id=00000000402b5328" & "&" &
|
||||
"code=" & Uri.EscapeDataString(Code) & "&" &
|
||||
"grant_type=authorization_code" & "&" &
|
||||
"redirect_uri=" & Uri.EscapeDataString("https://login.live.com/oauth20_desktop.srf") & "&" &
|
||||
"scope=" & Uri.EscapeDataString("service::user.auth.xboxlive.com::MBI_SSL")
|
||||
End If
|
||||
Dim Result As String
|
||||
Try
|
||||
Result = NetRequestMulty("https://login.live.com/oauth20_token.srf", "POST", Request, "application/x-www-form-urlencoded", 2)
|
||||
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.Contains("must sign in again") OrElse ex.Message.Contains("invalid_grant") Then '#269
|
||||
If ex.Message.Contains("must sign in again") OrElse (ex.Message.Contains("refresh_token") AndAlso ex.Message.Contains("is not valid")) Then '#269
|
||||
Return {"Relogin", ""}
|
||||
Else
|
||||
Throw
|
||||
@@ -858,121 +886,28 @@ LoginFinish:
|
||||
Dim RefreshToken As String = ResultJson("refresh_token").ToString
|
||||
Return {AccessToken, RefreshToken}
|
||||
End Function
|
||||
|
||||
''微软登录步骤 1:获取 DeviceCode 并显示验证提示
|
||||
''https://learn.microsoft.com/zh-cn/entra/identity-platform/v2-oauth2-device-code
|
||||
'Private Function MsLoginStep1(Data As LoaderTask(Of McLoginMs, McLoginResult)) As String
|
||||
' McLaunchLog("开始微软登录步骤 1:获取验证信息")
|
||||
' Dim PrepareJson As JObject = GetJson(NetRequestMulty("https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode", "POST",
|
||||
' $"client_id={OAuthClientId}&scope=XboxLive.signin%20offline_access", "application/x-www-form-urlencoded", 2))
|
||||
|
||||
' '从这里开始直到步骤 2 结束都没有 Review
|
||||
' '参考:https://learn.microsoft.com/zh-cn/entra/identity-platform/v2-oauth2-device-code
|
||||
' If MyMsgBox("请在打开的网页里输入 PCL2 提供的设备代码(会自动复制到剪贴板)。" & vbCrLf &
|
||||
' "设备代码有时效性,若操作时间过长需要重新进行登录流程。", "正版验证确认", "继续", "取消") <> 1 Then
|
||||
' Return "Cancel"
|
||||
' End If
|
||||
|
||||
' Dim DeviceCode As String = PrepareJson("device_code")
|
||||
' Dim UserCode As String = PrepareJson("user_code")
|
||||
' Dim VerifyUri As String = PrepareJson("verification_uri")
|
||||
' 'ResultJson("expires_in")
|
||||
' ClipboardSet(UserCode)
|
||||
' OpenWebsite(VerifyUri)
|
||||
|
||||
' Dim MsgBoxValue As String = ""
|
||||
' While MsgBoxValue IsNot "1"
|
||||
' MsgBoxValue = MyMsgBox("本次验证的设备代码为:" & UserCode & vbCrLf & vbCrLf & "你也可以在任意设备上打开下列网址进行验证:" & vbCrLf & VerifyUri & vbCrLf & vbCrLf & "在验证完成后,你可以直接关闭这个弹窗,PCL2 会自动完成接下来的流程。", "正版验证", "关闭", "复制网址", "复制设备代码").ToString
|
||||
' If MsgBoxValue = "2" Then
|
||||
' ClipboardSet(VerifyUri)
|
||||
' Continue While
|
||||
' ElseIf MsgBoxValue = "3" Then
|
||||
' ClipboardSet(UserCode)
|
||||
' Continue While
|
||||
' ElseIf MsgBoxValue = "1" Then
|
||||
' Exit While
|
||||
' End If
|
||||
' End While
|
||||
|
||||
' Return DeviceCode
|
||||
'End Function
|
||||
|
||||
''微软登录步骤 2:从 OAuth Code 或 OAuth RefreshToken 获取 {OAuth AccessToken, OAuth RefreshToken}
|
||||
'Private Function MsLoginStep2(Code As String, IsRefresh As Boolean, Optional ExpiresIn As String = "900") As String()
|
||||
' McLaunchLog("开始微软登录步骤 2(" & If(IsRefresh, "", "非") & "刷新登录)")
|
||||
|
||||
' Dim Request As String
|
||||
' If IsRefresh Then
|
||||
' Request = "grant_type=refresh_token" & "&" &
|
||||
' "client_id=" & OAuthClientId & "&" &
|
||||
' "device_code=" & Code & "&" &
|
||||
' "refresh_token=" & Uri.EscapeDataString(Code) & "&" &
|
||||
' "scope=XboxLive.signin%20offline_access"
|
||||
' Else
|
||||
' Request = "grant_type=urn:ietf:params:oauth:grant-type:device_code" & "&" &
|
||||
' "client_id=" & OAuthClientId & "&" &
|
||||
' "device_code=" & Code & "&" &
|
||||
' "scope=XboxLive.signin%20offline_access"
|
||||
' End If
|
||||
' Dim Result As String
|
||||
' Dim stopwatch As Stopwatch = Stopwatch.StartNew()
|
||||
|
||||
' While stopwatch.Elapsed < TimeSpan.FromSeconds(ExpiresIn)
|
||||
' Try
|
||||
' Result = NetRequestMulty("https://login.microsoftonline.com/consumers/oauth2/v2.0/token", "POST", Request, "application/x-www-form-urlencoded", 2)
|
||||
' Catch ex As Exception
|
||||
' If ex.Message.Contains("must sign in again") OrElse ex.Message.Contains("invalid_grant") Then '#269
|
||||
' Return {"Relogin", ""}
|
||||
' ElseIf ex.Message.Contains("authorization_declined") Then
|
||||
' Hint("你拒绝了 PCL2 的访问权限申请,验证过程被中断!")
|
||||
' Return {"Cancel", ""}
|
||||
' ElseIf ex.Message.Contains("expired_token") Then
|
||||
' Hint("Token 已过期,请尝试重新验证!")
|
||||
' Exit While
|
||||
' ElseIf ex.Message.Contains("AADSTS70016") Then
|
||||
' Continue While
|
||||
' Else
|
||||
' Throw
|
||||
' End If
|
||||
' End Try
|
||||
|
||||
' If Result IsNot Nothing Then
|
||||
' Dim ResultJson As JObject = GetJson(Result)
|
||||
' Dim AccessToken As String = ResultJson("access_token").ToString
|
||||
' Dim RefreshToken As String = ResultJson("refresh_token").ToString
|
||||
' Return {AccessToken, RefreshToken}
|
||||
' End If
|
||||
|
||||
' Thread.Sleep(1000)
|
||||
' End While
|
||||
|
||||
' Hint("验证超时,请尝试重新验证!")
|
||||
' Return {"Cancel", ""}
|
||||
|
||||
'End Function
|
||||
|
||||
'微软登录步骤 3:从 OAuth AccessToken 获取 XBLToken
|
||||
Private Function MsLoginStep3(AccessToken As String) As String
|
||||
McLaunchLog("开始微软登录步骤 3")
|
||||
'微软登录步骤 2:从 OAuth AccessToken 获取 XBLToken
|
||||
Private Function MsLoginStep2(AccessToken As String) As String
|
||||
McLaunchLog("开始微软登录步骤 2/6")
|
||||
|
||||
Dim Request As String = "{
|
||||
""Properties"": {
|
||||
""AuthMethod"": ""RPS"",
|
||||
""SiteName"": ""user.auth.xboxlive.com"",
|
||||
""RpsTicket"": """ & AccessToken & """
|
||||
""RpsTicket"": """ & If(AccessToken.StartsWithF("d="), "", "d=") & AccessToken & """
|
||||
},
|
||||
""RelyingParty"": ""http://auth.xboxlive.com"",
|
||||
""TokenType"": ""JWT""
|
||||
}" 'TODO: 新版登录改为 ""RpsTicket"": ""d=" & AccessToken & """
|
||||
Dim Result As String = NetRequestMulty("https://user.auth.xboxlive.com/user/authenticate", "POST", Request, "application/json", 3)
|
||||
}"
|
||||
Dim Result As String = NetRequestMultiple("https://user.auth.xboxlive.com/user/authenticate", "POST", Request, "application/json", 3)
|
||||
|
||||
Dim ResultJson As JObject = GetJson(Result)
|
||||
Dim XBLToken As String = ResultJson("Token").ToString
|
||||
Return XBLToken
|
||||
End Function
|
||||
'微软登录步骤 4:从 XBLToken 获取 {XSTSToken, UHS}
|
||||
Private Function MsLoginStep4(XBLToken As String) As String()
|
||||
McLaunchLog("开始微软登录步骤 4")
|
||||
'微软登录步骤 3:从 XBLToken 获取 {XSTSToken, UHS}
|
||||
Private Function MsLoginStep3(XBLToken As String) As String()
|
||||
McLaunchLog("开始微软登录步骤 3/6")
|
||||
|
||||
Dim Request As String = "{
|
||||
""Properties"": {
|
||||
@@ -986,7 +921,7 @@ LoginFinish:
|
||||
}"
|
||||
Dim Result As String
|
||||
Try
|
||||
Result = NetRequestMulty("https://xsts.auth.xboxlive.com/xsts/authorize", "POST", Request, "application/json", 3)
|
||||
Result = NetRequestMultiple("https://xsts.auth.xboxlive.com/xsts/authorize", "POST", Request, "application/json", 3)
|
||||
Catch ex As Net.WebException
|
||||
'参考 https://github.com/PrismarineJS/prismarine-auth/blob/master/src/common/Constants.js
|
||||
If ex.Message.Contains("2148916227") Then
|
||||
@@ -1022,14 +957,14 @@ LoginFinish:
|
||||
Dim UHS As String = ResultJson("DisplayClaims")("xui")(0)("uhs").ToString
|
||||
Return {XSTSToken, UHS}
|
||||
End Function
|
||||
'微软登录步骤 5:从 {XSTSToken, UHS} 获取 Minecraft AccessToken
|
||||
Private Function MsLoginStep5(Tokens As String()) As String
|
||||
McLaunchLog("开始微软登录步骤 5")
|
||||
'微软登录步骤 4:从 {XSTSToken, UHS} 获取 Minecraft AccessToken
|
||||
Private Function MsLoginStep4(Tokens As String()) As String
|
||||
McLaunchLog("开始微软登录步骤 4/6")
|
||||
|
||||
Dim Request As String = New JObject(New JProperty("identityToken", $"XBL3.0 x={Tokens(1)};{Tokens(0)}")).ToString(0)
|
||||
Dim Result As String
|
||||
Try
|
||||
Result = NetRequestMulty("https://api.minecraftservices.com/authentication/login_with_xbox", "POST", Request, "application/json", 2)
|
||||
Result = NetRequestRetry("https://api.minecraftservices.com/authentication/login_with_xbox", "POST", Request, "application/json")
|
||||
Catch ex As Net.WebException
|
||||
Dim Message As String = GetExceptionSummary(ex)
|
||||
If Message.Contains("(429)") Then
|
||||
@@ -1047,11 +982,11 @@ LoginFinish:
|
||||
Dim AccessToken As String = ResultJson("access_token").ToString
|
||||
Return AccessToken
|
||||
End Function
|
||||
'微软登录步骤 6:验证微软账号是否持有 MC,这也会刷新 XGP
|
||||
Private Sub MsLoginStep6(AccessToken As String)
|
||||
McLaunchLog("开始微软登录步骤 6")
|
||||
'微软登录步骤 5:验证微软账号是否持有 MC,这也会刷新 XGP
|
||||
Private Sub MsLoginStep5(AccessToken As String)
|
||||
McLaunchLog("开始微软登录步骤 5/6")
|
||||
|
||||
Dim Result As String = NetRequestMulty("https://api.minecraftservices.com/entitlements/mcstore", "GET", "", "application/json", 2, New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}})
|
||||
Dim Result As String = NetRequestMultiple("https://api.minecraftservices.com/entitlements/mcstore", "GET", "", "application/json", 2, New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}})
|
||||
Try
|
||||
Dim ResultJson As JObject = GetJson(Result)
|
||||
If Not (ResultJson.ContainsKey("items") AndAlso ResultJson("items").Any) Then
|
||||
@@ -1066,13 +1001,13 @@ LoginFinish:
|
||||
Throw
|
||||
End Try
|
||||
End Sub
|
||||
'微软登录步骤 7:从 Minecraft AccessToken 获取 {UUID, UserName, ProfileJson}
|
||||
Private Function MsLoginStep7(AccessToken As String) As String()
|
||||
McLaunchLog("开始微软登录步骤 7")
|
||||
'微软登录步骤 6:从 Minecraft AccessToken 获取 {UUID, UserName, ProfileJson}
|
||||
Private Function MsLoginStep6(AccessToken As String) As String()
|
||||
McLaunchLog("开始微软登录步骤 6/6")
|
||||
|
||||
Dim Result As String
|
||||
Try
|
||||
Result = NetRequestMulty("https://api.minecraftservices.com/minecraft/profile", "GET", "", "application/json", 2, New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}})
|
||||
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)
|
||||
If Message.Contains("(429)") Then
|
||||
@@ -1327,33 +1262,42 @@ LoginFinish:
|
||||
''' 释放 Java Wrapper 并返回完整文件路径。
|
||||
''' </summary>
|
||||
Public Function ExtractJavaWrapper() As String
|
||||
Dim BaseDir As String = GetJavaWrapperDir()
|
||||
Dim BaseDir As String = GetPureASCIIDir()
|
||||
Dim WrapperPath As String = BaseDir & "\JavaWrapper.jar"
|
||||
Log("[Java] 选定的 Java Wrapper 路径:" & WrapperPath)
|
||||
SyncLock ExtractJavaWrapperLock '避免 OptiFine 和 Forge 安装时同时释放 Java Wrapper 导致冲突
|
||||
Dim IsWrapperWritten As Boolean = WriteFile(WrapperPath, GetResources("JavaWrapper"))
|
||||
If Not IsWrapperWritten AndAlso File.Exists(WrapperPath) Then
|
||||
'以下为 #4243 的修复,因为未知原因 Java Wrapper 可能变为只读文件
|
||||
Log("[Java] Java Wrapper 文件释放失败,但文件已存在,将在删除后尝试重新生成", LogLevel.Debug)
|
||||
Try
|
||||
File.Delete(WrapperPath)
|
||||
IsWrapperWritten = WriteFile(WrapperPath, GetResources("JavaWrapper"))
|
||||
Catch ex As Exception
|
||||
Log(ex, "Java Wrapper 文件重新释放失败,将尝试更换文件名重新生成")
|
||||
WrapperPath = BaseDir & "\JavaWrapper2.jar"
|
||||
IsWrapperWritten = WriteFile(WrapperPath, GetResources("JavaWrapper"))
|
||||
End Try
|
||||
End If
|
||||
If Not IsWrapperWritten Then Throw New FileNotFoundException("释放 Java Wrapper 失败,请查看 PCL 日志查找详细信息")
|
||||
Try
|
||||
WriteFile(WrapperPath, GetResources("JavaWrapper"))
|
||||
Catch ex As Exception
|
||||
If File.Exists(WrapperPath) Then
|
||||
'因为未知原因 Java Wrapper 可能变为只读文件(#4243)
|
||||
Log(ex, "Java Wrapper 文件释放失败,但文件已存在,将在删除后尝试重新生成", LogLevel.Developer)
|
||||
Try
|
||||
File.Delete(WrapperPath)
|
||||
WriteFile(WrapperPath, GetResources("JavaWrapper"))
|
||||
Catch ex2 As Exception
|
||||
Log(ex2, "Java Wrapper 文件重新释放失败,将尝试更换文件名重新生成", LogLevel.Developer)
|
||||
WrapperPath = BaseDir & "\JavaWrapper2.jar"
|
||||
Try
|
||||
WriteFile(WrapperPath, GetResources("JavaWrapper"))
|
||||
Catch ex3 As Exception
|
||||
Throw New FileNotFoundException("释放 Java Wrapper 最终尝试失败", ex3)
|
||||
End Try
|
||||
End Try
|
||||
Else
|
||||
Throw New FileNotFoundException("释放 Java Wrapper 失败", ex)
|
||||
End If
|
||||
End Try
|
||||
End SyncLock
|
||||
Return WrapperPath
|
||||
End Function
|
||||
Private ExtractJavaWrapperLock As New Object
|
||||
|
||||
''' <summary>
|
||||
''' 获取 Java Wrapper 所在的文件夹,不以 \ 结尾。
|
||||
''' 获取一个可用于临时存放文件的,不含任何特殊字符的文件夹路径。
|
||||
''' 返回值不以 \ 结尾。
|
||||
''' </summary>
|
||||
Public Function GetJavaWrapperDir() As String
|
||||
Public Function GetPureASCIIDir() As String
|
||||
If (Path & "PCL").IsASCII() Then
|
||||
Return Path & "PCL"
|
||||
ElseIf PathAppdata.IsASCII() Then
|
||||
@@ -1405,8 +1349,12 @@ LoginFinish:
|
||||
Arguments = Arguments.Replace(" -Dos.name=Windows 10", " -Dos.name=""Windows 10""")
|
||||
'全屏
|
||||
If Setup.Get("LaunchArgumentWindowType") = 0 Then Arguments += " --fullscreen"
|
||||
'由 Option 传入的额外参数
|
||||
For Each Arg In CurrentLaunchOptions.ExtraArgs
|
||||
Arguments += " " & Arg.Trim
|
||||
Next
|
||||
'进服
|
||||
Dim Server As String = If(String.IsNullOrEmpty(McLaunchLoader.Input.ServerIp), Setup.Get("VersionServerEnter", McVersionCurrent), McLaunchLoader.Input.ServerIp)
|
||||
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
|
||||
@@ -1470,7 +1418,7 @@ LoginFinish:
|
||||
|
||||
'添加 Java Wrapper 作为主 Jar
|
||||
If McLaunchJavaSelected.VersionCode >= 9 Then DataList.Add("--add-exports cpw.mods.bootstraplauncher/cpw.mods.bootstraplauncher=ALL-UNNAMED")
|
||||
DataList.Add("-Doolloo.jlw.tmpdir=""" & GetJavaWrapperDir() & """")
|
||||
DataList.Add("-Doolloo.jlw.tmpdir=""" & GetPureASCIIDir() & """")
|
||||
DataList.Add("-jar """ & ExtractJavaWrapper() & """")
|
||||
|
||||
'添加 MainClass
|
||||
@@ -1537,7 +1485,7 @@ NextVersion:
|
||||
|
||||
'添加 Java Wrapper 作为主 Jar
|
||||
If McLaunchJavaSelected.VersionCode >= 9 Then DataList.Add("--add-exports cpw.mods.bootstraplauncher/cpw.mods.bootstraplauncher=ALL-UNNAMED")
|
||||
DataList.Add("-Doolloo.jlw.tmpdir=""" & GetJavaWrapperDir() & """")
|
||||
DataList.Add("-Doolloo.jlw.tmpdir=""" & GetPureASCIIDir() & """")
|
||||
DataList.Add("-jar """ & ExtractJavaWrapper() & """")
|
||||
|
||||
'将 "-XXX" 与后面 "XXX" 合并到一起
|
||||
@@ -2106,18 +2054,18 @@ IgnoreCustomSkin:
|
||||
"""" & McLaunchJavaSelected.PathJava & """ " & McLaunchArgument & vbCrLf &
|
||||
"echo 游戏已退出。" & vbCrLf &
|
||||
"pause"
|
||||
WriteFile(If(McLaunchLoader.Input.SaveBatch, Path & "PCL\LatestLaunch.bat"), SecretFilter(CmdString, "F"),
|
||||
WriteFile(If(CurrentLaunchOptions.SaveBatch, Path & "PCL\LatestLaunch.bat"), SecretFilter(CmdString, "F"),
|
||||
Encoding:=If(Encoding.Default.Equals(Encoding.UTF8), Encoding.UTF8, Encoding.GetEncoding("GB18030")))
|
||||
If McLaunchLoader.Input.SaveBatch IsNot Nothing Then
|
||||
If CurrentLaunchOptions.SaveBatch IsNot Nothing Then
|
||||
McLaunchLog("导出启动脚本完成,强制结束启动过程")
|
||||
AbortHint = "导出启动脚本成功!"
|
||||
OpenExplorer("/select,""" & McLaunchLoader.Input.SaveBatch & """")
|
||||
OpenExplorer("/select,""" & CurrentLaunchOptions.SaveBatch & """")
|
||||
Loader.Parent.Abort()
|
||||
Exit Sub '导出脚本完成
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "输出启动脚本失败")
|
||||
If McLaunchLoader.Input.SaveBatch IsNot Nothing Then Throw ex '直接触发启动失败
|
||||
If CurrentLaunchOptions.SaveBatch IsNot Nothing Then Throw ex '直接触发启动失败
|
||||
End Try
|
||||
|
||||
'执行自定义命令
|
||||
|
||||
@@ -475,22 +475,27 @@ VersionSearchFinish:
|
||||
''' </summary>
|
||||
Public Property JsonText As String
|
||||
Get
|
||||
'快速检查 JSON 是否以 { 开头、} 结尾;忽略空白字符
|
||||
Dim FastJsonCheck =
|
||||
Function(Json As String) As Boolean
|
||||
Dim TrimedJson As String = Json.Trim()
|
||||
Return TrimedJson.StartsWithF("{") AndAlso TrimedJson.EndsWithF("}")
|
||||
End Function
|
||||
If _JsonText Is Nothing Then
|
||||
If Not File.Exists(Path & Name & ".json") Then Throw New Exception("未找到版本 json 文件:" & Path & Name & ".json")
|
||||
If Not File.Exists(Path & Name & ".json") Then Throw New Exception($"未找到版本 JSON 文件:{Path}{Name}.json")
|
||||
_JsonText = ReadFile(Path & Name & ".json")
|
||||
'如果 ReadFile 失败会返回空字符串;这可能是由于文件被临时占用,故延时后重试
|
||||
If _JsonText.Length = 0 Then
|
||||
If Not FastJsonCheck(_JsonText) Then
|
||||
If RunInUi() Then
|
||||
Log("[Minecraft] 版本 json 文件为空或读取失败,由于代码在主线程运行,将不再进行重试", LogLevel.Debug)
|
||||
Throw New Exception("版本 Json 文件为空或读取失败")
|
||||
Log("[Minecraft] 版本 JSON 文件为空或有误,由于代码在主线程运行,将不再进行重试", LogLevel.Debug)
|
||||
GetJson(_JsonText) '触发异常
|
||||
Else
|
||||
Log("[Minecraft] 版本 json 文件为空或读取失败,将在 2s 后重试读取(" & Path & Name & ".json)", LogLevel.Debug)
|
||||
Log("[Minecraft] 版本 JSON 文件为空或有误,将在 2s 后重试读取(" & Path & Name & ".json)", LogLevel.Debug)
|
||||
Thread.Sleep(2000)
|
||||
_JsonText = ReadFile(Path & Name & ".json")
|
||||
If _JsonText.Length = 0 Then Throw New Exception("版本 json 文件为空或读取失败")
|
||||
If Not FastJsonCheck(_JsonText) Then GetJson(_JsonText) '触发异常
|
||||
End If
|
||||
End If
|
||||
If _JsonText.Length < 100 Then Throw New Exception("版本 json 文件有误,内容为:" & _JsonText)
|
||||
End If
|
||||
Return _JsonText
|
||||
End Get
|
||||
@@ -562,11 +567,10 @@ Recheck:
|
||||
GoTo Recheck
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "合并版本依赖项 json 失败(" & If(InheritVersion, "null").ToString & ")")
|
||||
Log(ex, "合并版本依赖项 JSON 失败(" & If(InheritVersion, "null").ToString & ")")
|
||||
End Try
|
||||
Catch ex As Exception
|
||||
Log($"[Minecraft] 传入的版本 json 文件内容(共 {Text.Length} 字符,最多输出前 5000 字符):{vbCrLf}{Text.Substring(0, 5000)}")
|
||||
Throw New Exception("版本 json 不规范(" & If(Name, "null") & ")", ex)
|
||||
Throw New Exception("初始化版本 JSON 时失败(" & If(Name, "null") & ")", ex)
|
||||
End Try
|
||||
Try
|
||||
'处理 JumpLoader
|
||||
@@ -1030,21 +1034,21 @@ ExitDataLoad:
|
||||
Public Function GetMcFoolName(Name As String) As String
|
||||
Name = Name.ToLower
|
||||
If Name.StartsWithF("2.0") Then
|
||||
Return "这个秘密计划了两年的更新将游戏推向了一个新高度!"
|
||||
ElseIf Name.StartsWithF("20w14inf") OrElse Name = "20w14∞" Then
|
||||
Return "我们加入了 20 亿个新的维度,让无限的想象变成了现实!"
|
||||
Return "2013 | 这个秘密计划了两年的更新将游戏推向了一个新高度!"
|
||||
ElseIf Name = "15w14a" Then
|
||||
Return "作为一款全年龄向的游戏,我们需要和平,需要爱与拥抱。"
|
||||
Return "2015 | 作为一款全年龄向的游戏,我们需要和平,需要爱与拥抱。"
|
||||
ElseIf Name = "1.rv-pre1" Then
|
||||
Return "是时候将现代科技带入 Minecraft 了!"
|
||||
Return "2016 | 是时候将现代科技带入 Minecraft 了!"
|
||||
ElseIf Name = "3d shareware v1.34" Then
|
||||
Return "我们从地下室的废墟里找到了这个开发于 1994 年的杰作!"
|
||||
Return "2019 | 我们从地下室的废墟里找到了这个开发于 1994 年的杰作!"
|
||||
ElseIf Name.StartsWithF("20w14inf") OrElse Name = "20w14∞" Then
|
||||
Return "2020 | 我们加入了 20 亿个新的维度,让无限的想象变成了现实!"
|
||||
ElseIf Name = "22w13oneblockatatime" Then
|
||||
Return "一次一个方块更新!迎接全新的挖掘、合成与骑乘玩法吧!"
|
||||
Return "2022 | 一次一个方块更新!迎接全新的挖掘、合成与骑乘玩法吧!"
|
||||
ElseIf Name = "23w13a_or_b" Then
|
||||
Return "研究表明:玩家喜欢作出选择——越多越好!"
|
||||
Return "2023 | 研究表明:玩家喜欢作出选择——越多越好!"
|
||||
ElseIf Name = "24w14potato" Then
|
||||
Return "毒马铃薯一直都被大家忽视和低估,于是我们超级加强了它!"
|
||||
Return "2024 | 毒马铃薯一直都被大家忽视和低估,于是我们超级加强了它!"
|
||||
Else
|
||||
Return ""
|
||||
End If
|
||||
@@ -1946,17 +1950,13 @@ OnLoaded:
|
||||
End Try
|
||||
If CoreJarOnly Then Return Result
|
||||
|
||||
'是否跳过校验
|
||||
Dim IsSetupSkip As Boolean = ShouldIgnoreFileCheck(Version)
|
||||
|
||||
'Library 文件
|
||||
Result.AddRange(McLibFixFromLibToken(McLibListGet(Version, False), JumpLoaderFolder:=Version.PathIndie & ".jumploader\", AllowUnsameFile:=IsSetupSkip))
|
||||
Result.AddRange(McLibFixFromLibToken(McLibListGet(Version, False), JumpLoaderFolder:=Version.PathIndie & ".jumploader\"))
|
||||
|
||||
'统一通行证文件
|
||||
If Setup.Get("VersionServerLogin", Version:=Version) = 3 Then
|
||||
Dim TargetFile = PathAppdata & "nide8auth.jar"
|
||||
If Not (IsSetupSkip AndAlso File.Exists(TargetFile)) Then
|
||||
Dim DownloadInfo As JObject = Nothing
|
||||
Dim DownloadInfo As JObject = Nothing
|
||||
'获取下载信息
|
||||
Try
|
||||
Log("[Minecraft] 开始获取统一通行证下载信息")
|
||||
@@ -1966,14 +1966,13 @@ OnLoaded:
|
||||
Catch ex As Exception
|
||||
Log(ex, "获取统一通行证下载信息失败")
|
||||
End Try
|
||||
'校验文件
|
||||
If DownloadInfo IsNot Nothing Then
|
||||
Dim Checker As New FileChecker(Hash:=DownloadInfo("jarHash").ToString)
|
||||
If (IsSetupSkip AndAlso File.Exists(TargetFile)) OrElse Checker.Check(TargetFile) IsNot Nothing Then
|
||||
'开始下载
|
||||
Log("[Minecraft] 统一通行证需要更新:Hash - " & Checker.Hash, LogLevel.Developer)
|
||||
Result.Add(New NetFile({"https://login.mc-user.com:233/index/jar"}, TargetFile, Checker))
|
||||
End If
|
||||
'校验文件
|
||||
If DownloadInfo IsNot Nothing Then
|
||||
Dim Checker As New FileChecker(Hash:=DownloadInfo("jarHash").ToString)
|
||||
If Checker.Check(TargetFile) IsNot Nothing Then
|
||||
'开始下载
|
||||
Log("[Minecraft] 统一通行证需要更新:Hash - " & Checker.Hash, LogLevel.Developer)
|
||||
Result.Add(New NetFile({"https://login.mc-user.com:233/index/jar"}, TargetFile, Checker))
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
@@ -1982,45 +1981,59 @@ OnLoaded:
|
||||
If Setup.Get("VersionServerLogin", Version:=Version) = 4 OrElse
|
||||
(PageLinkHiper.HiperState = LoadState.Finished AndAlso Setup.Get("LoginType") = McLoginType.Legacy) Then 'HiPer 登录转接
|
||||
Dim TargetFile = PathAppdata & "authlib-injector.jar"
|
||||
If Not (IsSetupSkip AndAlso File.Exists(TargetFile)) Then
|
||||
Dim DownloadInfo As JObject = Nothing
|
||||
'获取下载信息
|
||||
Try
|
||||
Log("[Minecraft] 开始获取 Authlib-Injector 下载信息")
|
||||
DownloadInfo = GetJson(NetGetCodeByDownload({"https://bmclapi2.bangbang93.com/mirrors/authlib-injector/artifact/latest.json"}, IsJson:=True))
|
||||
Catch ex As Exception
|
||||
Log(ex, "获取 Authlib-Injector 下载信息失败")
|
||||
End Try
|
||||
'校验文件
|
||||
If DownloadInfo IsNot Nothing Then
|
||||
Dim Checker As New FileChecker(Hash:=DownloadInfo("checksums")("sha256").ToString)
|
||||
If (IsSetupSkip AndAlso File.Exists(TargetFile)) OrElse Checker.Check(TargetFile) IsNot Nothing Then
|
||||
'开始下载
|
||||
Dim DownloadAddress As String = DownloadInfo("download_url")
|
||||
Log("[Minecraft] Authlib-Injector 需要更新:" & DownloadAddress, LogLevel.Developer)
|
||||
Result.Add(New NetFile({DownloadAddress}, TargetFile, New FileChecker(Hash:=DownloadInfo("checksums")("sha256").ToString)))
|
||||
End If
|
||||
Dim DownloadInfo As JObject = Nothing
|
||||
'获取下载信息
|
||||
Try
|
||||
Log("[Minecraft] 开始获取 Authlib-Injector 下载信息")
|
||||
DownloadInfo = GetJson(NetGetCodeByDownload({
|
||||
"https://authlib-injector.yushi.moe/artifact/latest.json",
|
||||
"https://bmclapi2.bangbang93.com/mirrors/authlib-injector/artifact/latest.json"
|
||||
}, IsJson:=True))
|
||||
Catch ex As Exception
|
||||
Log(ex, "获取 Authlib-Injector 下载信息失败")
|
||||
End Try
|
||||
'校验文件
|
||||
If DownloadInfo IsNot Nothing Then
|
||||
Dim Checker As New FileChecker(Hash:=DownloadInfo("checksums")("sha256").ToString)
|
||||
If Checker.Check(TargetFile) IsNot Nothing Then
|
||||
'开始下载
|
||||
Dim DownloadAddress As String = DownloadInfo("download_url").ToString.
|
||||
Replace("bmclapi2.bangbang93.com/mirrors/authlib-injector", "authlib-injector.yushi.moe")
|
||||
Log("[Minecraft] Authlib-Injector 需要更新:" & DownloadAddress, LogLevel.Developer)
|
||||
Result.Add(New NetFile({
|
||||
DownloadAddress,
|
||||
DownloadAddress.Replace("authlib-injector.yushi.moe", "bmclapi2.bangbang93.com/mirrors/authlib-injector")
|
||||
}, TargetFile, New FileChecker(Hash:=DownloadInfo("checksums")("sha256").ToString)))
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
|
||||
'跳过校验
|
||||
If ShouldIgnoreFileCheck(Version) Then
|
||||
Log("[Minecraft] 用户要求尽量忽略文件检查,这可能会保留有误的文件")
|
||||
Result = Result.Where(
|
||||
Function(f)
|
||||
If File.Exists(f.LocalPath) Then
|
||||
Log("[Minecraft] 跳过下载的支持库文件:" & f.LocalPath, LogLevel.Debug)
|
||||
Return False
|
||||
Else
|
||||
Return True
|
||||
End If
|
||||
End Function).ToList
|
||||
End If
|
||||
|
||||
Return Result
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 将 McLibToken 列表转换为 NetFile。无需下载的文件会被自动过滤。
|
||||
''' </summary>
|
||||
Public Function McLibFixFromLibToken(Libs As List(Of McLibToken), Optional CustomMcFolder As String = Nothing, Optional JumpLoaderFolder As String = Nothing, Optional AllowUnsameFile As Boolean = False) As List(Of NetFile)
|
||||
Public Function McLibFixFromLibToken(Libs As List(Of McLibToken), Optional CustomMcFolder As String = Nothing, Optional JumpLoaderFolder As String = Nothing) As List(Of NetFile)
|
||||
CustomMcFolder = If(CustomMcFolder, PathMcFolder)
|
||||
Dim Result As New List(Of NetFile)
|
||||
'获取
|
||||
For Each Token As McLibToken In Libs
|
||||
'检查文件
|
||||
Dim Checker As FileChecker
|
||||
If AllowUnsameFile Then '只要文件存在则通过检查,用于放宽完整性校验的情况
|
||||
Checker = New FileChecker(MinSize:=1)
|
||||
Else
|
||||
Checker = New FileChecker(ActualSize:=If(Token.Size = 0, -1, Token.Size), Hash:=Token.SHA1)
|
||||
End If
|
||||
Dim Checker As New FileChecker(ActualSize:=If(Token.Size = 0, -1, Token.Size), Hash:=Token.SHA1)
|
||||
If Checker.Check(Token.LocalPath) Is Nothing Then Continue For
|
||||
'文件不符合,添加下载
|
||||
Dim Urls As New List(Of String)
|
||||
@@ -2159,77 +2172,50 @@ OnLoaded:
|
||||
''' </summary>
|
||||
Public Size As Long
|
||||
''' <summary>
|
||||
''' 是否为 Virtual 资源文件。
|
||||
''' </summary>
|
||||
Public IsVirtual As Boolean
|
||||
''' <summary>
|
||||
''' 文件的 Hash 校验码。
|
||||
''' </summary>
|
||||
Public Hash As String
|
||||
|
||||
Public Overrides Function ToString() As String
|
||||
Return If(IsVirtual, "[Virtual] ", "") & GetString(Size) & " | " & LocalPath
|
||||
Return GetString(Size) & " | " & LocalPath
|
||||
End Function
|
||||
End Structure
|
||||
''' <summary>
|
||||
''' 获取 Minecraft 的资源文件列表。失败会抛出异常。
|
||||
''' </summary>
|
||||
''' <param name="Name">版本的资源名称,如“1.13.1”。</param>
|
||||
Private Function McAssetsListGet(Name As String) As List(Of McAssetsToken)
|
||||
Private Function McAssetsListGet(Version As McVersion) As List(Of McAssetsToken)
|
||||
Dim IndexName = McAssetsGetIndexName(Version)
|
||||
Try
|
||||
|
||||
'初始化
|
||||
If Not File.Exists(PathMcFolder & "assets\indexes\" & Name & ".json") Then Throw New FileNotFoundException("Assets 索引文件未找到", PathMcFolder & "assets\indexes\" & Name & ".json")
|
||||
McAssetsListGet = New List(Of McAssetsToken)
|
||||
Dim Json = GetJson(ReadFile(PathMcFolder & "assets\indexes\" & Name & ".json"))
|
||||
If Not File.Exists($"{PathMcFolder}assets\indexes\{IndexName}.json") Then Throw New FileNotFoundException("未找到 Asset Index", PathMcFolder & "assets\indexes\" & IndexName & ".json")
|
||||
Dim Result As New List(Of McAssetsToken)
|
||||
Dim Json As JObject = GetJson(ReadFile($"{PathMcFolder}assets\indexes\{IndexName}.json"))
|
||||
|
||||
'确认 Virtual 与 Map 状态
|
||||
Dim IsVirtual As Boolean = False
|
||||
If Json("virtual") IsNot Nothing AndAlso Json("virtual").ToString Then IsVirtual = True
|
||||
If Json("map_to_resources") IsNot Nothing AndAlso Json("map_to_resources").ToString Then
|
||||
IsVirtual = True
|
||||
'刷新 resources 文件夹符号链接(#2182)
|
||||
'Dim Info As FileSystemInfo = New FileInfo(PathMcFolder & "resources\pack.mcmeta")
|
||||
'If Info.Attributes.HasFlag(FileAttributes.ReparsePoint) Then
|
||||
If Not File.Exists(PathMcFolder & "resources\pack.mcmeta") Then
|
||||
Log("[Minecraft] 尝试刷新 resources 文件夹符号链接", LogLevel.Debug)
|
||||
Try
|
||||
DeleteDirectory(PathMcFolder & "resources\", True)
|
||||
Directory.CreateDirectory(PathMcFolder & "assets\virtual\legacy\")
|
||||
Dim Result = ShellAndGetOutput("cmd", $"/C mklink /D /J ""{PathMcFolder}resources"" ""{PathMcFolder}assets\virtual\legacy""")
|
||||
Log($"[Minecraft] 符号链接创建结果:{Result}")
|
||||
If Not Result.Contains("<<===>>") Then Throw New Exception($"非预期的结果({Result})")
|
||||
Catch ex As Exception
|
||||
Log(ex, "创建资源文件夹链接失败,游戏可能会没有声音", LogLevel.Msgbox)
|
||||
End Try
|
||||
'读取列表
|
||||
For Each File As JProperty In Json("objects").Children
|
||||
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("/", "\")
|
||||
ElseIf Json("virtual") IsNot Nothing AndAlso Json("virtual").ToObject(Of Boolean) Then
|
||||
'Virtual
|
||||
LocalPath = PathMcFolder & "assets\virtual\legacy\" & File.Name.Replace("/", "\")
|
||||
Else
|
||||
'正常
|
||||
LocalPath = PathMcFolder & "assets\objects\" & Left(File.Value("hash").ToString, 2) & "\" & File.Value("hash").ToString
|
||||
End If
|
||||
End If
|
||||
|
||||
'加载列表
|
||||
If IsVirtual Then
|
||||
For Each File As JProperty In Json("objects").Children
|
||||
McAssetsListGet.Add(New McAssetsToken With {
|
||||
.IsVirtual = True,
|
||||
.LocalPath = PathMcFolder & "assets\virtual\legacy\" & File.Name.Replace("/", "\"),
|
||||
.SourcePath = File.Name,
|
||||
.Hash = File.Value("hash").ToString,
|
||||
.Size = File.Value("size").ToString
|
||||
})
|
||||
Next
|
||||
Else
|
||||
For Each File As JProperty In Json("objects").Children
|
||||
McAssetsListGet.Add(New McAssetsToken With {
|
||||
.IsVirtual = False,
|
||||
.LocalPath = PathMcFolder & "assets\objects\" & Left(File.Value("hash").ToString, 2) & "\" & File.Value("hash").ToString,
|
||||
.SourcePath = File.Name,
|
||||
.Hash = File.Value("hash").ToString,
|
||||
.Size = File.Value("size").ToString
|
||||
})
|
||||
Next
|
||||
End If
|
||||
Result.Add(New McAssetsToken With {
|
||||
.LocalPath = LocalPath,
|
||||
.SourcePath = File.Name,
|
||||
.Hash = File.Value("hash").ToString,
|
||||
.Size = File.Value("size").ToString
|
||||
})
|
||||
Next
|
||||
Return Result
|
||||
|
||||
Catch ex As Exception
|
||||
Log(ex, "获取资源文件列表失败:" & Name)
|
||||
Log(ex, "获取资源文件列表失败:" & IndexName)
|
||||
Throw
|
||||
End Try
|
||||
End Function
|
||||
@@ -2238,12 +2224,12 @@ OnLoaded:
|
||||
''' <summary>
|
||||
''' 获取版本缺失的资源文件所对应的 NetTaskFile。
|
||||
''' </summary>
|
||||
Public Function McAssetsFixList(IndexAddress As String, CheckHash As Boolean, Optional ByRef ProgressFeed As LoaderBase = Nothing) As List(Of NetFile)
|
||||
Public Function McAssetsFixList(Version As McVersion, CheckHash As Boolean, Optional ByRef ProgressFeed As LoaderBase = Nothing) As List(Of NetFile)
|
||||
Dim Result As New List(Of NetFile)
|
||||
|
||||
Dim AssetsList As List(Of McAssetsToken)
|
||||
Try
|
||||
AssetsList = McAssetsListGet(IndexAddress)
|
||||
AssetsList = McAssetsListGet(Version)
|
||||
Dim Token As McAssetsToken
|
||||
If ProgressFeed IsNot Nothing Then ProgressFeed.Progress = 0.04
|
||||
For i = 0 To AssetsList.Count - 1
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
Imports System.IO.Compression
|
||||
|
||||
Public Module ModMod
|
||||
Private Const LocalModCacheVersion As Integer = 7
|
||||
|
||||
Public Class McMod
|
||||
|
||||
@@ -47,7 +48,7 @@ Public Module ModMod
|
||||
Get
|
||||
Load()
|
||||
If Not IsFileAvailable Then
|
||||
Return McModState.Unavaliable
|
||||
Return McModState.Unavailable
|
||||
ElseIf Path.EndsWithF(".disabled", True) OrElse Path.EndsWithF(".old", True) Then
|
||||
Return McModState.Disabled
|
||||
Else
|
||||
@@ -58,7 +59,7 @@ Public Module ModMod
|
||||
Public Enum McModState As Integer
|
||||
Fine = 0
|
||||
Disabled = 1
|
||||
Unavaliable = 2
|
||||
Unavailable = 2
|
||||
End Enum
|
||||
|
||||
#End Region
|
||||
@@ -707,7 +708,7 @@ Finished:
|
||||
End If
|
||||
'读取文件
|
||||
Dim data As New List(Of Byte)
|
||||
For Each b As Byte In File.ReadAllBytes(Path)
|
||||
For Each b As Byte In ReadFileBytes(Path)
|
||||
If b = 9 OrElse b = 10 OrElse b = 13 OrElse b = 32 Then Continue For
|
||||
data.Add(b)
|
||||
Next
|
||||
@@ -814,7 +815,7 @@ Finished:
|
||||
|
||||
'等待 Mod 更新完成
|
||||
If PageVersionMod.UpdatingVersions.Contains(Loader.Input) Then
|
||||
Log($"[Mod] 等待 Mod 更新完成后才能继续加载 Mod 列表")
|
||||
Log($"[Mod] 等待 Mod 更新完成后才能继续加载 Mod 列表:" & Loader.Input)
|
||||
Try
|
||||
RunInUiWait(Sub() If FrmVersionMod IsNot Nothing Then FrmVersionMod.Load.Text = "正在更新 Mod")
|
||||
Do Until Not PageVersionMod.UpdatingVersions.Contains(Loader.Input)
|
||||
@@ -852,7 +853,7 @@ Finished:
|
||||
Dim Cache As New JObject
|
||||
Try
|
||||
Dim CacheContent As String = ReadFile(CachePath)
|
||||
If CacheContent <> "" Then
|
||||
If Not String.IsNullOrWhiteSpace(CacheContent) Then
|
||||
Cache = GetJson(CacheContent)
|
||||
If Not Cache.ContainsKey("version") OrElse Cache("version").ToObject(Of Integer) <> LocalModCacheVersion Then
|
||||
Log($"[Mod] 本地 Mod 信息缓存版本已过期,将弃用这些缓存信息", LogLevel.Debug)
|
||||
@@ -861,6 +862,7 @@ Finished:
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "读取本地 Mod 信息缓存失败,已重置")
|
||||
Cache = New JObject
|
||||
End Try
|
||||
Cache("version") = LocalModCacheVersion
|
||||
|
||||
@@ -886,7 +888,7 @@ Finished:
|
||||
End If
|
||||
ModList.Add(ModEntry)
|
||||
'读取 Comp 缓存
|
||||
If ModEntry.State = McMod.McModState.Unavaliable Then Continue For
|
||||
If ModEntry.State = McMod.McModState.Unavailable Then Continue For
|
||||
Dim CacheKey = ModEntry.ModrinthHash & PageVersionLeft.Version.Version.McName & GetTargetModLoaders().Join("")
|
||||
If Cache.ContainsKey(CacheKey) Then
|
||||
ModEntry.FromJson(Cache(CacheKey))
|
||||
@@ -901,8 +903,8 @@ Finished:
|
||||
'排序
|
||||
ModList = Sort(ModList,
|
||||
Function(Left As McMod, Right As McMod) As Boolean
|
||||
If (Left.State = McMod.McModState.Unavaliable) <> (Right.State = McMod.McModState.Unavaliable) Then
|
||||
Return Left.State = McMod.McModState.Unavaliable
|
||||
If (Left.State = McMod.McModState.Unavailable) <> (Right.State = McMod.McModState.Unavailable) Then
|
||||
Return Left.State = McMod.McModState.Unavailable
|
||||
Else
|
||||
Return Not Right.FileName.CompareTo(Left.FileName)
|
||||
End If
|
||||
@@ -924,7 +926,6 @@ Finished:
|
||||
End Try
|
||||
End Sub
|
||||
'联网加载 Mod 详情
|
||||
Private Const LocalModCacheVersion As Integer = 6
|
||||
Public McModDetailLoader As New LoaderTask(Of KeyValuePair(Of List(Of McMod), JObject), Integer)("Mod List Detail Loader", AddressOf McModDetailLoad)
|
||||
Private Sub McModDetailLoad(Loader As LoaderTask(Of KeyValuePair(Of List(Of McMod), JObject), Integer))
|
||||
Dim Mods As List(Of McMod) = Loader.Input.Key
|
||||
@@ -944,7 +945,7 @@ Finished:
|
||||
'McVersions = McVersions.Distinct().ToList()
|
||||
'开始网络获取
|
||||
Log($"[Mod] 目标加载器:{ModLoaders.Join("/")},版本:{McVersion}")
|
||||
Dim EndedThreadCount As Integer = 0, SucceedThreadCount As Integer = 0
|
||||
Dim EndedThreadCount As Integer = 0, IsFailed As Boolean = False
|
||||
Dim MainThread As Thread = Thread.CurrentThread
|
||||
'从 Modrinth 获取信息
|
||||
RunInNewThread(
|
||||
@@ -979,7 +980,6 @@ Finished:
|
||||
For Each ProjectJson In ModrinthProject
|
||||
Dim Project As New CompProject(ProjectJson)
|
||||
For Each Entry In ModrinthMapping(Project.Id)
|
||||
If Entry.Comp IsNot Nothing AndAlso Entry.Comp.FromCurseForge Then Project.LogoUrl = Entry.Comp.LogoUrl 'Modrinth 的部分 Logo 不是图片,如果可能,使用 CurseForge 的 Logo
|
||||
Entry.Comp = Project
|
||||
Next
|
||||
Next
|
||||
@@ -1005,9 +1005,9 @@ Finished:
|
||||
End If
|
||||
Next
|
||||
Log($"[Mod] 从 Modrinth 获取本地 Mod 信息结束")
|
||||
SucceedThreadCount += 1
|
||||
Catch ex As Exception
|
||||
Log(ex, "从 Modrinth 获取本地 Mod 信息失败")
|
||||
IsFailed = True
|
||||
Finally
|
||||
EndedThreadCount += 1
|
||||
End Try
|
||||
@@ -1023,7 +1023,7 @@ Finished:
|
||||
If Loader.IsAbortedWithThread(MainThread) Then Exit Sub
|
||||
Next
|
||||
Dim CurseForgeRaw = CType(CType(GetJson(DlModRequest("https://api.curseforge.com/v1/fingerprints/432", "POST",
|
||||
$"{{""fingerprints"": [{CurseForgeHashes.Join(",")}]}}", "application/json")), JObject)("data")("exactMatches"), JContainer)
|
||||
$"{{""fingerprints"": [{CurseForgeHashes.Join(",")}]}}", "application/json")), JObject)("data")("exactMatches"), JContainer)
|
||||
Log($"[Mod] 从 CurseForge 获取到 {CurseForgeRaw.Count} 个本地 Mod 的对应信息")
|
||||
'步骤 2:尝试读取工程信息缓存,构建其他 Mod 的对应关系
|
||||
If Not CurseForgeRaw.Any() Then Exit Sub
|
||||
@@ -1046,7 +1046,7 @@ Finished:
|
||||
'步骤 3:获取工程信息
|
||||
If Not CurseForgeMapping.Any() Then Exit Sub
|
||||
Dim CurseForgeProject = CType(GetJson(DlModRequest("https://api.curseforge.com/v1/mods", "POST",
|
||||
$"{{""modIds"": [{CurseForgeMapping.Keys.Join(",")}]}}", "application/json")), JObject)("data")
|
||||
$"{{""modIds"": [{CurseForgeMapping.Keys.Join(",")}]}}", "application/json")), JObject)("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
|
||||
@@ -1055,7 +1055,6 @@ Finished:
|
||||
Dim Project As New CompProject(ProjectJson)
|
||||
For Each Entry In CurseForgeMapping(Project.Id) '倒查防止 CurseForge 返回的内容有漏
|
||||
If Entry.Comp IsNot Nothing AndAlso Not Entry.Comp.FromCurseForge Then
|
||||
Entry.Comp.LogoUrl = Project.LogoUrl 'Modrinth 部分 Logo 加载不出来
|
||||
Entry.Comp = Entry.Comp '再次触发修改事件
|
||||
Continue For
|
||||
End If
|
||||
@@ -1066,10 +1065,9 @@ Finished:
|
||||
Dim NewestVersion As String = Nothing
|
||||
Dim NewestFileIds As New List(Of Integer)
|
||||
For Each IndexEntry In ProjectJson("latestFilesIndexes")
|
||||
If IndexEntry("modLoader") Is Nothing OrElse Not IndexEntry("modLoader").HasValues OrElse '镜像源会返回一个值为 null 的键
|
||||
ModLoaders.Single <> IndexEntry("modLoader").ToObject(Of Integer) Then Continue For 'ModLoader 唯一且匹配
|
||||
If IndexEntry("modLoader") Is Nothing OrElse ModLoaders.Single <> IndexEntry("modLoader").ToObject(Of Integer) Then Continue For 'ModLoader 唯一且匹配
|
||||
Dim IndexVersion As String = IndexEntry("gameVersion")
|
||||
If McVersion <> IndexVersion Then Continue For 'MC 版本匹配
|
||||
If IndexVersion <> McVersion Then Continue For 'MC 版本匹配
|
||||
'由于 latestFilesIndexes 是按时间从新到老排序的,所以只需取第一个;如果需要检查多个 releaseType 下的文件,将 > -1 改为 = 1,但这应当并不会获取到更新的文件
|
||||
If NewestVersion IsNot Nothing AndAlso VersionSortInteger(NewestVersion, IndexVersion) > -1 Then Continue For '只保留最新 MC 版本
|
||||
If NewestVersion <> IndexVersion Then
|
||||
@@ -1114,9 +1112,9 @@ Finished:
|
||||
End If
|
||||
Next
|
||||
Log($"[Mod] 从 CurseForge 获取 Mod 更新信息结束")
|
||||
SucceedThreadCount += 1
|
||||
Catch ex As Exception
|
||||
Log(ex, "从 CurseForge 获取本地 Mod 信息失败")
|
||||
IsFailed = True
|
||||
Finally
|
||||
EndedThreadCount += 1
|
||||
End Try
|
||||
@@ -1130,13 +1128,13 @@ Finished:
|
||||
Mods = Mods.Where(Function(m) m.Comp IsNot Nothing).ToList()
|
||||
Log($"[Mod] 联网获取本地 Mod 信息完成,为 {Mods.Count} 个 Mod 更新缓存")
|
||||
If Not Mods.Any() Then Exit Sub
|
||||
For Each Entry In Mods 'TODO: Bookshelf 的 Logo 会在两个网站间横跳
|
||||
Entry.CompLoaded = SucceedThreadCount = 2
|
||||
For Each Entry In Mods
|
||||
Entry.CompLoaded = Not IsFailed
|
||||
Cache(Entry.ModrinthHash & McVersion & ModLoaders.Join("")) = Entry.ToJson()
|
||||
Next
|
||||
WriteFile(PathTemp & "Cache\LocalMod.json", Cache.ToString(If(ModeDebug, Newtonsoft.Json.Formatting.Indented, Newtonsoft.Json.Formatting.None)))
|
||||
'刷新下边栏(#4377)
|
||||
RunInUi(Sub() FrmVersionMod?.RefreshBottomBar())
|
||||
'刷新边栏
|
||||
RunInUi(Sub() FrmVersionMod?.RefreshBars())
|
||||
End Sub
|
||||
Private Function GetTargetModLoaders() As List(Of CompModLoaderType)
|
||||
Dim ModLoaders As New List(Of CompModLoaderType)
|
||||
|
||||
@@ -260,7 +260,7 @@ Retry:
|
||||
'获取 Mod 下载信息
|
||||
ModDownloadLoaders.Add(New LoaderTask(Of Integer, JArray)("获取 Mod 下载信息",
|
||||
Sub(Task As LoaderTask(Of Integer, JArray))
|
||||
Task.Output = GetJson(NetRequestRetry("https://api.curseforge.com/v1/mods/files", "POST", "{""fileIds"": [" & Join(ModList, ",") & "]}", "application/json"))("data")
|
||||
Task.Output = GetJson(DlModRequest("https://api.curseforge.com/v1/mods/files", "POST", "{""fileIds"": [" & Join(ModList, ",") & "]}", "application/json"))("data")
|
||||
'如果文件已被删除,则 API 会跳过那一项
|
||||
If ModList.Count > Task.Output.Count Then Throw New Exception("整合包所需要的部分 Mod 版本已被 Mod 作者删除,因此无法完成整合包安装,请联系整合包作者更新整合包中的 Mod 版本")
|
||||
End Sub) With {.ProgressWeight = ModList.Count / 10}) '每 10 Mod 需要 1s
|
||||
|
||||
@@ -88,26 +88,27 @@
|
||||
AddHandler GameProcess.ErrorDataReceived, AddressOf LogReceived
|
||||
|
||||
'初始化时钟
|
||||
RunInNewThread(Sub()
|
||||
Try
|
||||
Do Until State = MinecraftState.Ended OrElse State = MinecraftState.Crashed OrElse State = MinecraftState.Canceled OrElse Loader.State = LoadState.Aborted
|
||||
TimerWindow()
|
||||
TimerLog()
|
||||
'设置窗口标题
|
||||
For i = 1 To 3
|
||||
If IsWindowFinished AndAlso IsWindowAppeared AndAlso WindowTitle <> "" AndAlso State = MinecraftState.Running AndAlso Not GameProcess.HasExited Then
|
||||
Dim RealTitle As String = WindowTitle.Replace("{date}", Date.Now.ToString("yyyy/M/d")).Replace("{time}", Date.Now.ToString("HH:mm:ss"))
|
||||
SetWindowText(WindowHandle, RealTitle.ToCharArray)
|
||||
End If
|
||||
Thread.Sleep(64)
|
||||
Next
|
||||
Loop
|
||||
WatcherLog("Minecraft 日志监控已退出")
|
||||
Catch ex As Exception
|
||||
Log(ex, "Minecraft 日志监控主循环出错", LogLevel.Feedback)
|
||||
State = MinecraftState.Ended
|
||||
End Try
|
||||
End Sub, "Minecraft Watcher PID " & PID)
|
||||
RunInNewThread(
|
||||
Sub()
|
||||
Try
|
||||
Do Until State = MinecraftState.Ended OrElse State = MinecraftState.Crashed OrElse State = MinecraftState.Canceled OrElse Loader.State = LoadState.Aborted
|
||||
TimerWindow()
|
||||
TimerLog()
|
||||
'设置窗口标题
|
||||
For i = 1 To 3
|
||||
If IsWindowFinished AndAlso IsWindowAppeared AndAlso WindowTitle <> "" AndAlso State = MinecraftState.Running AndAlso Not GameProcess.HasExited Then
|
||||
Dim RealTitle As String = WindowTitle.Replace("{date}", Date.Now.ToString("yyyy/M/d")).Replace("{time}", Date.Now.ToString("HH:mm:ss"))
|
||||
SetWindowText(WindowHandle, RealTitle.ToCharArray)
|
||||
End If
|
||||
Thread.Sleep(64)
|
||||
Next
|
||||
Loop
|
||||
WatcherLog("Minecraft 日志监控已退出")
|
||||
Catch ex As Exception
|
||||
Log(ex, "Minecraft 日志监控主循环出错", LogLevel.Feedback)
|
||||
State = MinecraftState.Ended
|
||||
End Try
|
||||
End Sub, "Minecraft Watcher PID " & PID)
|
||||
End Sub
|
||||
|
||||
'状态
|
||||
@@ -349,22 +350,21 @@
|
||||
WatcherLog("Minecraft 已崩溃,将在 2 秒后开始崩溃分析")
|
||||
Hint("检测到 Minecraft 出现错误,错误分析已开始……")
|
||||
FeedbackInfo()
|
||||
RunInNewThread(Sub()
|
||||
Try
|
||||
Thread.Sleep(2000)
|
||||
WatcherLog("崩溃分析开始")
|
||||
Dim Analyzer As New CrashAnalyzer(PID)
|
||||
Analyzer.Collect(Version.PathIndie, LatestLog.ToList)
|
||||
Analyzer.Prepare()
|
||||
Analyzer.Analyze(Version)
|
||||
Analyzer.Output(False, New List(Of String) From
|
||||
{Version.Path & Version.Name & ".json",
|
||||
Path & "PCL\Log1.txt",
|
||||
Path & "PCL\LatestLaunch.bat"})
|
||||
Catch ex As Exception
|
||||
Log(ex, "崩溃分析失败", LogLevel.Feedback)
|
||||
End Try
|
||||
End Sub, "Crash Analyzer")
|
||||
RunInNewThread(
|
||||
Sub()
|
||||
Try
|
||||
Thread.Sleep(2000)
|
||||
WatcherLog("崩溃分析开始")
|
||||
Dim Analyzer As New CrashAnalyzer(PID)
|
||||
Analyzer.Collect(Version.PathIndie, LatestLog.ToList)
|
||||
Analyzer.Prepare()
|
||||
Analyzer.Analyze(Version)
|
||||
Analyzer.Output(False, New List(Of String) From
|
||||
{Version.Path & Version.Name & ".json", Path & "PCL\Log1.txt", Path & "PCL\LatestLaunch.bat"})
|
||||
Catch ex As Exception
|
||||
Log(ex, "崩溃分析失败", LogLevel.Feedback)
|
||||
End Try
|
||||
End Sub, "Crash Analyzer")
|
||||
End Sub
|
||||
|
||||
'强制关闭
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
'网络图片
|
||||
If File.Exists(FileAddress) Then
|
||||
PathLogo.Source = New MyBitmap(FileAddress)
|
||||
ElseIf _Logo.EndsWithF(".webp", True) Then 'Modrinth 林业 Mod 使用了不支持的 WebP 格式 Logo
|
||||
Log($"[Comp] 发现不支持的 WebP 格式图标,已更改为默认图标:{_Logo}")
|
||||
PathLogo.Source = New MyBitmap(PathImage & "Icons/NoIcon.png")
|
||||
Else
|
||||
PathLogo.Source = New MyBitmap(PathImage & "Icons/NoIcon.png")
|
||||
RunInNewThread(Sub() LogoLoader(FileAddress), "Comp Logo Loader " & Uuid & "#", ThreadPriority.BelowNormal)
|
||||
|
||||
@@ -27,27 +27,24 @@
|
||||
<Image x:Name="PathLogo" RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
</Border>
|
||||
<!-- 标题 -->
|
||||
<StackPanel Grid.Column="3" Grid.ColumnSpan="2" Grid.Row="1" VerticalAlignment="Bottom" Margin="0,1,0,0" Orientation="Horizontal"
|
||||
SnapsToDevicePixels="False" UseLayoutRounding="False">
|
||||
<TextBlock x:Name="LabTitle" TextTrimming="WordEllipsis" FontSize="14" IsHitTestVisible="False" />
|
||||
<TextBlock x:Name="LabTitleRaw" TextTrimming="WordEllipsis" FontSize="12" IsHitTestVisible="False"
|
||||
Foreground="{StaticResource ColorBrushGray1}" Opacity="0.4" VerticalAlignment="Bottom" Visibility="Collapsed" />
|
||||
<!-- 更新按钮 -->
|
||||
<local:MyIconButton Width="21" Height="21" Margin="-2,-1.6,0,0" x:Name="BtnUpdate" Theme="Black" Opacity="0.4" Visibility="Collapsed"
|
||||
ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="100" ToolTipService.VerticalOffset="-9"
|
||||
Logo="M640 768H384l-32-32V509H213L190 454l298-298h45l298 298L810 509h-138v226z m-224-64h192V477l32-32h93L512 223 290 445H384l32 32zM352 831h320v64h-320z" />
|
||||
</StackPanel>
|
||||
<!--<Grid Grid.Column="3" Grid.ColumnSpan="2" Grid.Row="1" VerticalAlignment="Bottom" Margin="0,1,0,0" IsHitTestVisible="False" SnapsToDevicePixels="False" UseLayoutRounding="False">
|
||||
<Grid Grid.Column="3" Grid.ColumnSpan="2" Grid.Row="1" VerticalAlignment="Bottom" Margin="0,1,0,0"
|
||||
SnapsToDevicePixels="False" UseLayoutRounding="False" x:Name="PanTitle">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="Auto" x:Name="ColumnSubtitle" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="1*" x:Name="ColumnExtend" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Name="LabTitle" TextTrimming="WordEllipsis" FontSize="14" Grid.Column="0" />
|
||||
<TextBlock x:Name="LabTitleRaw" TextTrimming="WordEllipsis" FontSize="12" Grid.Column="1"
|
||||
Foreground="{StaticResource ColorBrushGray1}" Opacity="0.4" VerticalAlignment="Bottom" Visibility="Collapsed" />
|
||||
<local:MyIconTextButton Grid.Column="2" Text="更新" />
|
||||
</Grid>-->
|
||||
<TextBlock x:Name="LabTitle" TextTrimming="WordEllipsis" FontSize="14" IsHitTestVisible="False" />
|
||||
<TextBlock x:Name="LabSubtitle" Grid.Column="1" HorizontalAlignment="Left"
|
||||
TextTrimming="CharacterEllipsis" FontSize="12" IsHitTestVisible="False"
|
||||
Foreground="{StaticResource ColorBrushGray1}" Opacity="0.4" VerticalAlignment="Bottom" Visibility="Collapsed" />
|
||||
<!-- 更新按钮 -->
|
||||
<local:MyIconButton Grid.Column="2"
|
||||
Width="21" Height="21" Margin="-2,-1.6,0,0" x:Name="BtnUpdate" Theme="Black" Opacity="0.4" Visibility="Collapsed"
|
||||
ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="100" ToolTipService.VerticalOffset="-9"
|
||||
Logo="M640 768H384l-32-32V509H213L190 454l298-298h45l298 298L810 509h-138v226z m-224-64h192V477l32-32h93L512 223 290 445H384l32 32zM352 831h320v64h-320z" />
|
||||
</Grid>
|
||||
<!-- Tag 与 详情 -->
|
||||
<StackPanel x:Name="PanTags" Grid.Row="2" Grid.Column="3" Orientation="Horizontal" VerticalAlignment="Bottom" Margin="-1,0,1,0" Visibility="Collapsed" IsHitTestVisible="False">
|
||||
<!--<Border Background="{DynamicResource ColorBrush6}" Padding="3,1" CornerRadius="3" Margin="0,0,4,0" SnapsToDevicePixels="True" UseLayoutRounding="False">
|
||||
|
||||
@@ -14,15 +14,13 @@ Public Class MyLocalModItem
|
||||
Set(value As String)
|
||||
If _Logo = value OrElse value Is Nothing Then Exit Property
|
||||
_Logo = value
|
||||
If ModeDebug AndAlso Not _Logo = PathImage & "Icons/NoIcon.png" Then Log($"[LocalModItem] Mod {Title} 的图标:{value}")
|
||||
Dim FileAddress = PathTemp & "CompLogo\" & GetHash(_Logo) & ".png"
|
||||
Try
|
||||
If _Logo.StartsWithF("http", True) Then
|
||||
'网络图片
|
||||
If File.Exists(FileAddress) Then
|
||||
PathLogo.Source = New MyBitmap(FileAddress)
|
||||
ElseIf _Logo.EndsWithF(".webp", True) Then 'Modrinth 林业 Mod 使用了不支持的 WebP 格式 Logo
|
||||
Log($"[LocalModItem] 发现不支持的 WebP 格式图标,已更改为默认图标:{_Logo}")
|
||||
PathLogo.Source = New MyBitmap(PathImage & "Icons/NoIcon.png")
|
||||
Else
|
||||
PathLogo.Source = New MyBitmap(PathImage & "Icons/NoIcon.png")
|
||||
RunInNewThread(Sub() LogoLoader(FileAddress), "Comp Logo Loader " & Uuid & "#", ThreadPriority.BelowNormal)
|
||||
@@ -55,8 +53,8 @@ RetryStart:
|
||||
Try
|
||||
'CurseForge 图片使用缩略图
|
||||
Dim Url As String = _Logo
|
||||
If Url.Contains("/256/256/") AndAlso GetPixelSize(1) <= 1.25 AndAlso Not Retried Then
|
||||
Url = Url.Replace("/256/256/", "/64/64/") '#3075:部分 Mod 不存在 64x64 图标,所以重试时不再缩小
|
||||
If Url.Contains("/256/256/") AndAlso GetPixelSize(1) <= 1.25 AndAlso Not Retried Then '#3075:部分 Mod 不存在 64x64 图标,所以重试时不再缩小
|
||||
Url = Url.Replace("/256/256/", "/64/64/")
|
||||
End If
|
||||
'下载图片
|
||||
NetDownload(Url, LocalFileAddress & DownloadEnd, True)
|
||||
@@ -104,7 +102,7 @@ RetryStart:
|
||||
LabTitle.TextDecorations = Nothing
|
||||
Case McMod.McModState.Disabled
|
||||
LabTitle.TextDecorations = TextDecorations.Strikethrough
|
||||
Case McMod.McModState.Unavaliable
|
||||
Case McMod.McModState.Unavailable
|
||||
LabTitle.TextDecorations = TextDecorations.Strikethrough
|
||||
value &= " [错误]"
|
||||
End Select
|
||||
@@ -117,12 +115,12 @@ RetryStart:
|
||||
'副标题
|
||||
Public Property SubTitle As String
|
||||
Get
|
||||
Return If(LabTitleRaw?.Text, "")
|
||||
Return If(LabSubtitle?.Text, "")
|
||||
End Get
|
||||
Set(value As String)
|
||||
If LabTitleRaw.Text = value Then Exit Property
|
||||
LabTitleRaw.Text = value
|
||||
LabTitleRaw.Visibility = If(value = "", Visibility.Collapsed, Visibility.Visible)
|
||||
If LabSubtitle.Text = value Then Exit Property
|
||||
LabSubtitle.Text = value
|
||||
LabSubtitle.Visibility = If(value = "", Visibility.Collapsed, Visibility.Visible)
|
||||
End Set
|
||||
End Property
|
||||
|
||||
@@ -427,7 +425,7 @@ RetryStart:
|
||||
NewDescription = GetFileNameWithoutExtentionFromPath(Entry.Path)
|
||||
Case McMod.McModState.Disabled
|
||||
NewDescription = GetFileNameWithoutExtentionFromPath(Entry.Path.Replace(".disabled", "").Replace(".old", ""))
|
||||
Case Else 'McMod.McModState.Unavaliable
|
||||
Case Else 'McMod.McModState.Unavailable
|
||||
NewDescription = GetFileNameFromPath(Entry.Path)
|
||||
End Select
|
||||
If Entry.Comp IsNot Nothing Then
|
||||
@@ -458,7 +456,7 @@ RetryStart:
|
||||
'<Image x:Name="ImgState" RenderOptions.BitmapScalingMode="HighQuality" Width="16" Height="16" Margin="0,0,-3,-1"
|
||||
' Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" IsHitTestVisible="False"
|
||||
' HorizontalAlignment="Right" VerticalAlignment="Bottom"
|
||||
' Source="/Images/Icons/Unavaliable.png" />
|
||||
' Source="/Images/Icons/Unavailable.png" />
|
||||
End If
|
||||
ImgState.Source = New MyBitmap(PathImage & $"Icons/{Entry.State}.png")
|
||||
End If
|
||||
@@ -535,4 +533,17 @@ RetryStart:
|
||||
FrmVersionMod.UpdateMods({Entry})
|
||||
End Sub
|
||||
|
||||
'自适应(#4465)
|
||||
Private Sub PanTitle_SizeChanged(sender As Object, e As SizeChangedEventArgs) Handles PanTitle.SizeChanged
|
||||
If ColumnExtend.Width.IsStar AndAlso ColumnExtend.ActualWidth < 0.5 Then
|
||||
'压缩 Subtitle
|
||||
ColumnSubtitle.Width = New GridLength(1, GridUnitType.Star)
|
||||
ColumnExtend.Width = New GridLength(0, GridUnitType.Pixel)
|
||||
ElseIf Not ColumnExtend.Width.IsStar AndAlso Not LabSubtitle.IsTextTrimmed Then
|
||||
'向右展开 Subtitle
|
||||
ColumnSubtitle.Width = GridLength.Auto
|
||||
ColumnExtend.Width = New GridLength(1, GridUnitType.Star)
|
||||
End If
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -69,14 +69,14 @@ Public Module ModMain
|
||||
Dim Percent As Double = 0.3
|
||||
Select Case CurrentHint.Type
|
||||
Case HintType.Info
|
||||
TargetColor0 = New MyColor(37, 155, 252)
|
||||
TargetColor1 = New MyColor(10, 142, 252)
|
||||
TargetColor0 = New MyColor(215, 37, 155, 252)
|
||||
TargetColor1 = New MyColor(215, 10, 142, 252)
|
||||
Case HintType.Finish
|
||||
TargetColor0 = New MyColor(33, 177, 33)
|
||||
TargetColor1 = New MyColor(29, 160, 29)
|
||||
TargetColor0 = New MyColor(215, 33, 177, 33)
|
||||
TargetColor1 = New MyColor(215, 29, 160, 29)
|
||||
Case Else 'HintType.Critical
|
||||
TargetColor0 = New MyColor(255, 53, 11)
|
||||
TargetColor1 = New MyColor(255, 43, 0)
|
||||
TargetColor0 = New MyColor(215, 255, 53, 11)
|
||||
TargetColor1 = New MyColor(215, 255, 43, 0)
|
||||
End Select
|
||||
If Not IsNothing(DoubleStack) Then
|
||||
'有重复提示,且该提示的进入动画已播放
|
||||
@@ -176,15 +176,17 @@ EndHint:
|
||||
Public Title As String
|
||||
Public Text As String
|
||||
''' <summary>
|
||||
''' 输入框模式:文本框的文本;选择模式:需要放进去的 List(Of MyListItem)。
|
||||
''' 输入模式:文本框的文本。
|
||||
''' 选择模式:需要放进去的 List(Of MyListItem)。
|
||||
''' 登录模式:登录步骤 1 中返回的 JSON。
|
||||
''' </summary>
|
||||
Public Content As Object
|
||||
''' <summary>
|
||||
''' 仅输入框模式:输入验证规则。
|
||||
''' 输入模式:输入验证规则。
|
||||
''' </summary>
|
||||
Public ValidateRules As ObjectModel.Collection(Of Validate)
|
||||
''' <summary>
|
||||
''' 仅输入框模式:提示文本。
|
||||
''' 输入模式:提示文本。
|
||||
''' </summary>
|
||||
Public HintText As String = ""
|
||||
''' <summary>
|
||||
@@ -214,7 +216,9 @@ EndHint:
|
||||
''' </summary>
|
||||
Public IsExited As Boolean = False
|
||||
''' <summary>
|
||||
''' 点击的按钮编号或输入的文本。若输入弹窗点击了非第一个按钮,则为 Nothing。
|
||||
''' 输入模式:输入的文本。若点击了 非 第一个按钮,则为 Nothing。
|
||||
''' 选择模式:点击的按钮编号,从 1 开始。
|
||||
''' 登录模式:字符串数组 {AccessToken, RefreshToken} 或一个 Exception。
|
||||
''' </summary>
|
||||
Public Result As Object
|
||||
End Class
|
||||
@@ -222,6 +226,7 @@ EndHint:
|
||||
Text
|
||||
[Select]
|
||||
Input
|
||||
Login
|
||||
End Enum
|
||||
|
||||
''' <summary>
|
||||
@@ -352,6 +357,8 @@ EndHint:
|
||||
FrmMain.PanMsg.Children.Add(New MyMsgSelect(WaitingMyMsgBox(0)))
|
||||
Case MyMsgBoxType.Text
|
||||
FrmMain.PanMsg.Children.Add(New MyMsgText(WaitingMyMsgBox(0)))
|
||||
Case MyMsgBoxType.Login
|
||||
FrmMain.PanMsg.Children.Add(New MyMsgLogin(WaitingMyMsgBox(0)))
|
||||
End Select
|
||||
WaitingMyMsgBox.RemoveAt(0)
|
||||
Else
|
||||
|
||||
@@ -280,7 +280,7 @@
|
||||
'当前音乐已播放结束,继续下一曲
|
||||
If CurrentWave.PlaybackState = NAudio.Wave.PlaybackState.Stopped AndAlso MusicAllList.Any Then MusicStartPlay(DequeueNextMusicAddress)
|
||||
Catch ex As Exception
|
||||
Log("[Music] 播放音乐失败的文件完整路径:" & MusicCurrent)
|
||||
Log(ex, "播放音乐出现内部错误(" & MusicCurrent & ")", LogLevel.Developer)
|
||||
If TypeOf ex Is NAudio.MmException AndAlso (ex.Message.Contains("NoDriver") OrElse ex.Message.Contains("BadDeviceId")) Then
|
||||
Hint("由于音频设备变更,音乐播放功能在重启 PCL 后才能恢复!", HintType.Critical)
|
||||
Thread.Sleep(1000000000)
|
||||
|
||||
@@ -8,10 +8,12 @@ Friend Module ModSecret
|
||||
|
||||
#Region "杂项"
|
||||
|
||||
'在开源内容的注册表键与普通内容的注册表键隔离
|
||||
'在开源版的注册表与常规版的注册表隔离,以防数据冲突
|
||||
Public Const RegFolder As String = "PCLDebug"
|
||||
'用于微软登录的 ClientId
|
||||
Public Const OAuthClientId As String = ""
|
||||
'CurseForge API Key
|
||||
Public Const CurseForgeAPIKey As String = ""
|
||||
|
||||
Friend Sub SecretOnApplicationStart()
|
||||
'提升 UI 线程优先级
|
||||
@@ -44,7 +46,7 @@ Friend Module ModSecret
|
||||
End If
|
||||
'开源版本提示
|
||||
MyMsgBox($"该版本中无法使用以下特性:
|
||||
- CurseForge API 调用:需要你自行申请 API Key,然后添加到 SecretHeadersSign 方法中
|
||||
- CurseForge API 调用:需要你自行申请 API Key,然后添加到 ModSecret.vb 的开头
|
||||
- 正版登录:需要你自行申请 Client ID,然后添加到 ModSecret.vb 的开头
|
||||
- 更新与联网通知:避免滥用隐患
|
||||
- 主题切换:这是需要赞助解锁的纪念性质的功能,别让赞助者太伤心啦……
|
||||
@@ -98,26 +100,30 @@ Friend Module ModSecret
|
||||
''' <summary>
|
||||
''' 设置 Headers 的 UA、Referer。
|
||||
''' </summary>
|
||||
Friend Sub SecretHeadersSign(Url As String, ByRef Client As CookieWebClient, Optional UseBrowserUserAgent As Boolean = False)
|
||||
If UseBrowserUserAgent Then
|
||||
Friend Sub SecretHeadersSign(Url As String, ByRef Client As WebClient, Optional UseBrowserUserAgent As Boolean = False)
|
||||
If Url.Contains("modrinth.com") Then '根据 #4334,不添加 PCL 的 UA 反而能正常访问
|
||||
Client.Headers("User-Agent") = "Mozilla/5.0 AppleWebKit/537.36 Chrome/63.0.3239.132 Safari/537.36"
|
||||
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
|
||||
End If
|
||||
Client.Headers("Referer") = "http://" & VersionCode & ".pcl2.open.server/"
|
||||
'如果你有 CurseForge API Key,请添加到 Headers 中,以恢复对 CurseForge 的访问
|
||||
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 UseBrowserUserAgent Then
|
||||
If Url.Contains("modrinth.com") Then '根据 #4334,不添加 PCL 的 UA 反而能正常访问
|
||||
Request.UserAgent = "Mozilla/5.0 AppleWebKit/537.36 Chrome/63.0.3239.132 Safari/537.36"
|
||||
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 & ".pcl2.open.server/"
|
||||
'如果你有 CurseForge API Key,请添加到 Headers 中,以恢复对 CurseForge 的访问
|
||||
Request.Headers("x-api-key") = CurseForgeAPIKey
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
@@ -51,6 +51,6 @@ Imports System.Runtime.InteropServices
|
||||
' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
|
||||
' 方法是按如下所示使用“*”
|
||||
|
||||
<Assembly: AssemblyVersion("2.8.4.0")>
|
||||
<Assembly: AssemblyFileVersion("2.8.4.0")>
|
||||
<Assembly: AssemblyVersion("2.8.8.0")>
|
||||
<Assembly: AssemblyFileVersion("2.8.8.0")>
|
||||
<Assembly: NeutralResourcesLanguage("")>
|
||||
@@ -100,6 +100,16 @@ Namespace My.Resources
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 查找 System.Byte[] 类型的本地化资源。
|
||||
'''</summary>
|
||||
Friend ReadOnly Property Imazen_WebP() As Byte()
|
||||
Get
|
||||
Dim obj As Object = ResourceManager.GetObject("Imazen_WebP", resourceCulture)
|
||||
Return CType(obj,Byte())
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 查找 System.Byte[] 类型的本地化资源。
|
||||
'''</summary>
|
||||
@@ -120,6 +130,16 @@ Namespace My.Resources
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 查找 System.Byte[] 类型的本地化资源。
|
||||
'''</summary>
|
||||
Friend ReadOnly Property libwebp64() As Byte()
|
||||
Get
|
||||
Dim obj As Object = ResourceManager.GetObject("libwebp64", resourceCulture)
|
||||
Return CType(obj,Byte())
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 查找 System.Byte[] 类型的本地化资源。
|
||||
'''</summary>
|
||||
|
||||
@@ -130,12 +130,18 @@
|
||||
<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">
|
||||
<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>
|
||||
<data name="ModData" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\ModData.txt;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
|
||||
@@ -28,7 +28,7 @@ Public Module ModDownloadLib
|
||||
Dim VersionFolder As String = PathMcFolder & "versions\" & Id & "\"
|
||||
|
||||
'重复任务检查
|
||||
For Each OngoingLoader In LoaderTaskbar
|
||||
For Each OngoingLoader In LoaderTaskbar.ToList()
|
||||
If OngoingLoader.Name <> $"Minecraft {Id} 下载" Then Continue For
|
||||
If Behaviour = NetPreDownloadBehaviour.ExitWhileExistsOrDownloading Then Return OngoingLoader
|
||||
Hint("该版本正在下载中!", HintType.Critical)
|
||||
@@ -71,7 +71,7 @@ Public Module ModDownloadLib
|
||||
VersionFolder = VersionFolder & Id & "\"
|
||||
|
||||
'重复任务检查
|
||||
For Each OngoingLoader In LoaderTaskbar
|
||||
For Each OngoingLoader In LoaderTaskbar.ToList()
|
||||
If OngoingLoader.Name <> $"Minecraft {Id} 下载" Then Continue For
|
||||
If Behaviour = NetPreDownloadBehaviour.ExitWhileExistsOrDownloading Then Exit Sub
|
||||
Hint("该版本正在下载中!", HintType.Critical)
|
||||
@@ -155,7 +155,7 @@ Public Module ModDownloadLib
|
||||
LoadersAssets.Add(New LoaderDownload("下载资源文件索引(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 3, .Show = False})
|
||||
LoadersAssets.Add(New LoaderTask(Of String, List(Of NetFile))("分析所需资源文件(副加载器)",
|
||||
Sub(Task As LoaderTask(Of String, List(Of NetFile)))
|
||||
Task.Output = McAssetsFixList(McAssetsGetIndexName(New McVersion(VersionFolder)), True, Task)
|
||||
Task.Output = McAssetsFixList(New McVersion(VersionFolder), True, Task)
|
||||
End Sub) With {.ProgressWeight = 3, .Show = False})
|
||||
LoadersAssets.Add(New LoaderDownload("下载资源文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 14, .Show = False})
|
||||
Loaders.Add(New LoaderCombo(Of String)("下载原版资源文件", LoadersAssets) With {.Block = False, .ProgressWeight = 21})
|
||||
@@ -317,7 +317,7 @@ Public Module ModDownloadLib
|
||||
If Not Target.Contains("\") Then Exit Sub
|
||||
|
||||
'重复任务检查
|
||||
For Each OngoingLoader In LoaderTaskbar
|
||||
For Each OngoingLoader In LoaderTaskbar.ToList()
|
||||
If OngoingLoader.Name <> $"OptiFine {DownloadInfo.NameDisplay} 下载" Then Continue For
|
||||
Hint("该版本正在下载中!", HintType.Critical)
|
||||
Exit Sub
|
||||
@@ -359,7 +359,7 @@ Public Module ModDownloadLib
|
||||
'添加 Java Wrapper 作为主 Jar
|
||||
Dim Arguments As String
|
||||
If UseJavaWrapper Then
|
||||
Arguments = $"-Doolloo.jlw.tmpdir=""{GetJavaWrapperDir()}"" -Duser.home=""{BaseMcFolderHome}"" -cp ""{Target}"" -jar ""{ExtractJavaWrapper()}"" optifine.Installer"
|
||||
Arguments = $"-Doolloo.jlw.tmpdir=""{GetPureASCIIDir()}"" -Duser.home=""{BaseMcFolderHome}"" -cp ""{Target}"" -jar ""{ExtractJavaWrapper()}"" optifine.Installer"
|
||||
Else
|
||||
Arguments = $"-Duser.home=""{BaseMcFolderHome}"" -cp ""{Target}"" optifine.Installer"
|
||||
End If
|
||||
@@ -744,7 +744,7 @@ Retry:
|
||||
Dim VersionFolder As String = PathMcFolder & "versions\" & VersionName & "\"
|
||||
|
||||
'重复任务检查
|
||||
For Each OngoingLoader In LoaderTaskbar
|
||||
For Each OngoingLoader In LoaderTaskbar.ToList()
|
||||
If OngoingLoader.Name <> $"LiteLoader {Id} 下载" Then Continue For
|
||||
Hint("该版本正在下载中!", HintType.Critical)
|
||||
Exit Sub
|
||||
@@ -778,7 +778,7 @@ Retry:
|
||||
If Not Target.Contains("\") Then Exit Sub
|
||||
|
||||
'重复任务检查
|
||||
For Each OngoingLoader In LoaderTaskbar
|
||||
For Each OngoingLoader In LoaderTaskbar.ToList()
|
||||
If OngoingLoader.Name <> $"LiteLoader {Id} 下载" Then Continue For
|
||||
Hint("该版本正在下载中!", HintType.Critical)
|
||||
Exit Sub
|
||||
@@ -960,7 +960,7 @@ Retry:
|
||||
If Not Target.Contains("\") Then Exit Sub
|
||||
|
||||
'重复任务检查
|
||||
For Each OngoingLoader In LoaderTaskbar
|
||||
For Each OngoingLoader In LoaderTaskbar.ToList()
|
||||
If OngoingLoader.Name <> $"{DisplayName} 下载" Then Continue For
|
||||
Hint("该版本正在下载中!", HintType.Critical)
|
||||
Exit Sub
|
||||
@@ -1026,7 +1026,7 @@ Retry:
|
||||
'添加 Java Wrapper 作为主 Jar
|
||||
Dim Arguments As String
|
||||
If UseJavaWrapper Then
|
||||
Arguments = $"-Doolloo.jlw.tmpdir=""{GetJavaWrapperDir()}"" -cp ""{PathTemp}Cache\forge_installer.jar;{Target}"" -jar ""{ExtractJavaWrapper()}"" com.bangbang93.ForgeInstaller ""{McFolder}"
|
||||
Arguments = $"-Doolloo.jlw.tmpdir=""{GetPureASCIIDir()}"" -cp ""{PathTemp}Cache\forge_installer.jar;{Target}"" -jar ""{ExtractJavaWrapper()}"" com.bangbang93.ForgeInstaller ""{McFolder}"
|
||||
Else
|
||||
Arguments = $"-cp ""{PathTemp}Cache\forge_installer.jar;{Target}"" com.bangbang93.ForgeInstaller ""{McFolder}"
|
||||
End If
|
||||
@@ -1714,7 +1714,7 @@ Sub(Task As LoaderTask(Of List(Of NetFile), Boolean))
|
||||
If Not Target.Contains("\") Then Exit Sub
|
||||
|
||||
'重复任务检查
|
||||
For Each OngoingLoader In LoaderTaskbar
|
||||
For Each OngoingLoader In LoaderTaskbar.ToList()
|
||||
If OngoingLoader.Name <> $"Fabric {Version} 安装器下载" Then Continue For
|
||||
Hint("该版本正在下载中!", HintType.Critical)
|
||||
Exit Sub
|
||||
@@ -2021,7 +2021,7 @@ Sub(Task As LoaderTask(Of List(Of NetFile), Boolean))
|
||||
If Request.LiteLoaderEntry IsNot Nothing Then LiteLoaderFolder = TempMcFolder & "versions\" & Request.MinecraftName & "-LiteLoader"
|
||||
|
||||
'判断 OptiFine 是否作为 Mod 进行下载
|
||||
Dim MinecraftCode As Integer = If(Request.MinecraftName.Contains("."), Request.MinecraftName.Split(".")(1), 99)
|
||||
Dim MinecraftCode As Integer = If(Request.MinecraftName.Contains("."), Val(Request.MinecraftName.Split(".")(1)), 0)
|
||||
Dim OptiFineAsMod As Boolean = Request.OptiFineEntry IsNot Nothing AndAlso '1. 选择了 OptiFine
|
||||
(Request.FabricVersion IsNot Nothing OrElse '2. 选择了 Fabric...
|
||||
(Request.ForgeEntry IsNot Nothing AndAlso MinecraftCode >= 14 AndAlso MinecraftCode <= 15)) '...或者 Forge 1.14~15(#4134)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<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.19+ 的 Mod 加载器,你需要先安装它才能安装各种 NeoForge 模组。
本页面提供 NeoForge 安装器下载,在下载后你需要手动打开安装器进行安装。" TextWrapping="Wrap" />
|
||||
<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" />
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<Window x:Class="FormLoginOAuth"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:PCL"
|
||||
mc:Ignorable="d"
|
||||
Height="720" Width="580" MinHeight="720" MinWidth="580" ResizeMode="CanResize" WindowStartupLocation="CenterScreen" Title="Microsoft 账户登录">
|
||||
<Grid>
|
||||
<Grid.Background>
|
||||
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
|
||||
<GradientStop Color="White" Offset="0"/>
|
||||
<GradientStop Color="{DynamicResource ColorObject8}" Offset="1"/>
|
||||
</LinearGradientBrush>
|
||||
</Grid.Background>
|
||||
<StackPanel x:Name="PanLoading" Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<local:MyLoading x:Name="Loading" Text="正在加载登录页面" HorizontalAlignment="Center" />
|
||||
<TextBlock Text="如果网络环境不佳,加载可能非常缓慢甚至一直加载不出来" Margin="0,25,0,0"
|
||||
HorizontalAlignment="Center" Foreground="{DynamicResource ColorBrush1}" Opacity="0.7" />
|
||||
<TextBlock Text="此时请尝试使用 VPN 或加速器,然后重试"
|
||||
HorizontalAlignment="Center" Foreground="{DynamicResource ColorBrush1}" Opacity="0.7" />
|
||||
</StackPanel>
|
||||
<WebBrowser x:Name="Browser1" Visibility="Collapsed" Tag="1" Margin="0,0,0,25" />
|
||||
<WebBrowser x:Name="Browser2" Visibility="Collapsed" Tag="2" Margin="0,0,0,25" />
|
||||
<WebBrowser x:Name="Browser3" Visibility="Collapsed" Tag="3" Margin="0,0,0,25" />
|
||||
<local:MyTextButton Text="如果遇到问题,可以点击这里使用系统浏览器登录" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,5" x:Name="HintSwitch" />
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -1,100 +0,0 @@
|
||||
Imports System.Windows.Navigation
|
||||
|
||||
Public Class FormLoginOAuth
|
||||
|
||||
Public Event OnLoginSuccess(code As String)
|
||||
Public Event OnLoginCanceled(IsSwitch As Boolean)
|
||||
|
||||
'跳转事件
|
||||
Private IsLoginSuccessed As Boolean = False
|
||||
Private IsSwitch As Boolean = False
|
||||
Private Sub Browser_Navigating(sender As WebBrowser, e As NavigatingCancelEventArgs) Handles Browser1.Navigating, Browser2.Navigating, Browser3.Navigating
|
||||
Dim Url As String = e.Uri.AbsoluteUri
|
||||
If ModeDebug Then Log("[Login] 登录浏览器 " & sender.Tag & " 导向:" & Url)
|
||||
If Url.Contains("code=") Then
|
||||
Dim Code As String = RegexSeek(Url, "(?<=code\=)[^&]+")
|
||||
Log("[Login] 抓取到 OAuth 返回码:" & Code)
|
||||
IsLoginSuccessed = True
|
||||
RaiseEvent OnLoginSuccess(Code)
|
||||
ElseIf Url.Contains("github.") Then
|
||||
Hint("PCL 不支持使用 GitHub 登录微软账号!", HintType.Critical)
|
||||
Close()
|
||||
End If
|
||||
End Sub
|
||||
|
||||
'已导航事件
|
||||
Private IsFirstLoaded As Boolean = False
|
||||
Private IsFirstLoadedLock As New Object
|
||||
Private Sub Browser_Navigated(sender As WebBrowser, e As NavigationEventArgs) Handles Browser1.Navigated, Browser2.Navigated, Browser3.Navigated
|
||||
SyncLock IsFirstLoadedLock
|
||||
If IsFirstLoaded Then Exit Sub
|
||||
IsFirstLoaded = True
|
||||
End SyncLock
|
||||
RunInThread(Sub()
|
||||
Thread.Sleep(1000)
|
||||
RunInUi(Sub()
|
||||
sender.Visibility = Visibility.Visible
|
||||
PanLoading.Visibility = Visibility.Collapsed
|
||||
Log("[Login] 已将登录窗口切换至浏览器")
|
||||
End Sub)
|
||||
End Sub)
|
||||
End Sub
|
||||
|
||||
'窗体的进出事件
|
||||
Public Shared LoginUrl1 As String = "https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?prompt=login&client_id=00000000402b5328&response_type=code&scope=service%3A%3Auser.auth.xboxlive.com%3A%3AMBI_SSL&redirect_uri=https:%2F%2Flogin.live.com%2Foauth20_desktop.srf"
|
||||
Public Shared LoginUrl2 As String = "https://login.live.com/oauth20_authorize.srf?prompt=login&client_id=00000000402b5328&response_type=code&scope=service%3A%3Auser.auth.xboxlive.com%3A%3AMBI_SSL&redirect_uri=https:%2F%2Flogin.live.com%2Foauth20_desktop.srf"
|
||||
Private Sub FrmLoginOAuth_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
|
||||
Browser1.Navigate(LoginUrl1)
|
||||
RunInThread(Sub()
|
||||
'估计是由于时间戳原因,同时导航会导致误报密码失败
|
||||
Thread.Sleep(2500)
|
||||
RunInUi(Sub()
|
||||
Try '防止在 Dispose 之后调用
|
||||
If Not IsFirstLoaded Then Browser2.Navigate(LoginUrl1)
|
||||
Catch
|
||||
End Try
|
||||
End Sub)
|
||||
Thread.Sleep(2500)
|
||||
RunInUi(Sub()
|
||||
Try
|
||||
If Not IsFirstLoaded Then Browser3.Navigate(LoginUrl2)
|
||||
Catch
|
||||
End Try
|
||||
End Sub)
|
||||
End Sub)
|
||||
End Sub
|
||||
Private Sub FormLoginOAuth_Closed() Handles Me.Closed
|
||||
RunInThread(Sub()
|
||||
Thread.Sleep(1000) '释放会卡一下,所以稍等一下……
|
||||
RunInUiWait(Sub()
|
||||
Try
|
||||
Browser1.Dispose()
|
||||
Catch ex As Exception
|
||||
Log(ex, "释放微软登录浏览器 1 失败")
|
||||
End Try
|
||||
End Sub)
|
||||
Thread.Sleep(200)
|
||||
RunInUiWait(Sub()
|
||||
Try
|
||||
Browser2.Dispose()
|
||||
Catch ex As Exception
|
||||
Log(ex, "释放微软登录浏览器 2 失败")
|
||||
End Try
|
||||
End Sub)
|
||||
Thread.Sleep(200)
|
||||
RunInUiWait(Sub()
|
||||
Try
|
||||
Browser3.Dispose()
|
||||
Catch ex As Exception
|
||||
Log(ex, "释放微软登录浏览器 3 失败")
|
||||
End Try
|
||||
End Sub)
|
||||
End Sub)
|
||||
If Not IsLoginSuccessed Then RaiseEvent OnLoginCanceled(IsSwitch)
|
||||
FrmMain.Focus()
|
||||
End Sub
|
||||
Private Sub HintSwitch_Click() Handles HintSwitch.Click
|
||||
IsSwitch = True
|
||||
Close()
|
||||
End Sub
|
||||
End Class
|
||||
@@ -0,0 +1,40 @@
|
||||
<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">
|
||||
<Grid.RenderTransform>
|
||||
<TransformGroup>
|
||||
<RotateTransform x:Name="TransformRotate" Angle="-4" />
|
||||
<TranslateTransform x:Name="TransformPos" X="0" Y="40" />
|
||||
</TransformGroup>
|
||||
</Grid.RenderTransform>
|
||||
<Border Name="PanBorder" CornerRadius="7" Background="#FBFBFB">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect Color="{DynamicResource ColorObject1}" BlurRadius="20" ShadowDepth="4" RenderingBias="Performance" Opacity="0.8" x:Name="EffectShadow" />
|
||||
</Border.Effect>
|
||||
<Grid Name="PanMain" VerticalAlignment="Top" Margin="22,22,22,23">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="2" />
|
||||
<RowDefinition Height="13" />
|
||||
<RowDefinition Height="1*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</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"
|
||||
VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" DeltaMuity="0.7">
|
||||
<TextBlock VerticalAlignment="Top" TextTrimming="None" TextWrapping="Wrap" Name="LabCaption" FontSize="15" LineHeight="18"
|
||||
Foreground="#FF5C5C5C" FontWeight="Normal" Padding="1" />
|
||||
</my: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" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
@@ -0,0 +1,145 @@
|
||||
Public Class MyMsgLogin
|
||||
Private Data As JObject
|
||||
Private UserCode As String '需要用户在网页上输入的设备代码
|
||||
Private DeviceCode As String '用于轮询的设备代码
|
||||
Private Website As String '验证网页的网址
|
||||
|
||||
#Region "弹窗"
|
||||
|
||||
Private ReadOnly MyConverter As MyMsgBoxConverter
|
||||
Private ReadOnly Uuid As Integer = GetUuid()
|
||||
|
||||
Public Sub New(Converter As MyMsgBoxConverter)
|
||||
Try
|
||||
InitializeComponent()
|
||||
Btn1.Name = Btn1.Name & GetUuid()
|
||||
Btn2.Name = Btn2.Name & GetUuid()
|
||||
Btn3.Name = Btn3.Name & GetUuid()
|
||||
MyConverter = Converter
|
||||
ShapeLine.StrokeThickness = GetWPFSize(1)
|
||||
Data = Converter.Content
|
||||
Init()
|
||||
Catch ex As Exception
|
||||
Log(ex, "登录弹窗初始化失败", LogLevel.Hint)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
Private Sub Load(sender As Object, e As EventArgs) Handles MyBase.Loaded
|
||||
Try
|
||||
'动画
|
||||
Opacity = 0
|
||||
AniStart(AaColor(FrmMain.PanMsg, Grid.BackgroundProperty, If(MyConverter.IsWarn, New MyColor(140, 80, 0, 0), New MyColor(90, 0, 0, 0)) - FrmMain.PanMsg.Background, 200), "PanMsg Background")
|
||||
AniStart({
|
||||
AaOpacity(Me, 1, 120, 60),
|
||||
AaDouble(Sub(i) TransformPos.Y += i, -TransformPos.Y, 300, 60, New AniEaseOutBack(AniEasePower.Weak)),
|
||||
AaDouble(Sub(i) TransformRotate.Angle += i, -TransformRotate.Angle, 300, 60, New AniEaseOutFluent(AniEasePower.Weak))
|
||||
}, "MyMsgBox " & Uuid)
|
||||
'记录日志
|
||||
Log("[Control] 登录弹窗:" & LabTitle.Text & vbCrLf & LabCaption.Text)
|
||||
Catch ex As Exception
|
||||
Log(ex, "登录弹窗加载失败", LogLevel.Hint)
|
||||
End Try
|
||||
End Sub
|
||||
Private Sub Close()
|
||||
'动画
|
||||
AniStart({
|
||||
AaCode(
|
||||
Sub()
|
||||
If Not WaitingMyMsgBox.Any() Then
|
||||
AniStart(AaColor(FrmMain.PanMsg, Grid.BackgroundProperty, New MyColor(0, 0, 0, 0) - FrmMain.PanMsg.Background, 200, Ease:=New AniEaseOutFluent(AniEasePower.Weak)))
|
||||
End If
|
||||
End Sub, 30),
|
||||
AaOpacity(Me, -Opacity, 80, 20),
|
||||
AaDouble(Sub(i) TransformPos.Y += i, 20 - TransformPos.Y, 150, 0, New AniEaseOutFluent),
|
||||
AaDouble(Sub(i) TransformRotate.Angle += i, 6 - TransformRotate.Angle, 150, 0, New AniEaseInFluent(AniEasePower.Weak)),
|
||||
AaCode(Sub() CType(Parent, Grid).Children.Remove(Me), , True)
|
||||
}, "MyMsgBox " & Uuid)
|
||||
End Sub
|
||||
|
||||
'实现回车和 Esc 的接口(#4857)
|
||||
Public Sub Btn1_Click() Handles Btn1.Click
|
||||
End Sub
|
||||
Public Sub Btn3_Click() Handles Btn3.Click
|
||||
Finished(New ThreadInterruptedException)
|
||||
End Sub
|
||||
|
||||
Private Sub Drag(sender As Object, e As MouseButtonEventArgs) Handles PanBorder.MouseLeftButtonDown, LabTitle.MouseLeftButtonDown
|
||||
On Error Resume Next
|
||||
If e.GetPosition(ShapeLine).Y <= 2 Then FrmMain.DragMove()
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
Private Sub Finished(Result As Object)
|
||||
If MyConverter.IsExited Then Return
|
||||
MyConverter.IsExited = True
|
||||
MyConverter.Result = Result
|
||||
RunInUi(AddressOf Close)
|
||||
Thread.Sleep(200)
|
||||
FrmMain.ShowWindowToTop()
|
||||
End Sub
|
||||
|
||||
Private Sub Init()
|
||||
UserCode = Data("user_code")
|
||||
DeviceCode = Data("device_code")
|
||||
Website = Data("verification_uri")
|
||||
'设置 UI
|
||||
LabTitle.Text = "登录 Minecraft"
|
||||
LabCaption.Text =
|
||||
$"登录网页将自动开启,请在网页中输入 {UserCode}(已自动复制)。" & vbCrLf & vbCrLf &
|
||||
$"如果网络环境不佳,网页可能一直加载不出来,届时请使用 VPN 并重试。" & vbCrLf &
|
||||
$"你也可以用其他设备打开 {Website} 并输入上述代码。"
|
||||
Btn1.EventData = Website
|
||||
Btn2.EventData = UserCode
|
||||
'启动工作线程
|
||||
RunInNewThread(AddressOf WorkThread, "MyMsgLogin")
|
||||
End Sub
|
||||
|
||||
Private Sub WorkThread()
|
||||
Thread.Sleep(3000)
|
||||
If MyConverter.IsExited Then Exit Sub
|
||||
OpenWebsite(Website)
|
||||
ClipboardSet(UserCode)
|
||||
Thread.Sleep((Data("interval").ToObject(Of Integer) - 1) * 1000)
|
||||
'轮询
|
||||
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 ResultJson As JObject = GetJson(Result)
|
||||
McLaunchLog($"令牌过期时间:{ResultJson("expires_in")} 秒")
|
||||
Hint("网页登录成功!", HintType.Finish)
|
||||
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("AADSTS70000") Then '可能不能判 “invalid_grant”,见 #269
|
||||
Finished(New RetryException)
|
||||
Return
|
||||
ElseIf ex.Message.Contains("authorization_pending") Then
|
||||
Thread.Sleep(2000)
|
||||
ElseIf UnknownFailureCount <= 2 Then
|
||||
UnknownFailureCount += 1
|
||||
Log(ex, $"登录轮询第 {UnknownFailureCount} 次失败")
|
||||
Thread.Sleep(2000)
|
||||
Else
|
||||
Finished(New Exception("登录轮询失败", ex))
|
||||
Return
|
||||
End If
|
||||
End Try
|
||||
Loop
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -130,7 +130,7 @@
|
||||
'初始化页面
|
||||
LabLaunchingName.Text = McVersionCurrent.Name
|
||||
LabLaunchingStage.Text = "初始化"
|
||||
LabLaunchingTitle.Text = If(McLaunchLoader.Input Is Nothing OrElse McLaunchLoader.Input.SaveBatch Is Nothing, "正在启动游戏", "正在导出启动脚本")
|
||||
LabLaunchingTitle.Text = If(CurrentLaunchOptions?.SaveBatch Is Nothing, "正在启动游戏", "正在导出启动脚本")
|
||||
LabLaunchingProgress.Text = "0.00 %"
|
||||
LabLaunchingProgress.Opacity = 1
|
||||
LabLaunchingDownload.Visibility = Visibility.Visible
|
||||
@@ -733,7 +733,7 @@ ExitRefresh:
|
||||
If ActualProgress <= ShowProgress Then ShowProgress = ActualProgress '原来或处理后变得比实际进度高,直接回退
|
||||
If IsLaunched Then ShowProgress = 1 '如果已经完成了,就不卖关子了
|
||||
'文本
|
||||
LabLaunchingTitle.Text = If(IsLaunched, "已启动游戏", If(McLaunchLoader.Input.SaveBatch Is Nothing, "正在启动游戏", "正在导出启动脚本"))
|
||||
LabLaunchingTitle.Text = If(IsLaunched, "已启动游戏", If(CurrentLaunchOptions.SaveBatch Is Nothing, "正在启动游戏", "正在导出启动脚本"))
|
||||
LabLaunchingProgress.Text = StrFillNum(ShowProgress * 100, 2) & " %"
|
||||
Dim HasLaunchDownloader As Boolean = False
|
||||
Try
|
||||
|
||||
@@ -102,6 +102,18 @@ Download:
|
||||
Log("[Page] 主页预设:Minecraft 皮肤推荐")
|
||||
Url = "https://forgepixel.com/pcl_sub_file"
|
||||
GoTo Download
|
||||
Case 6
|
||||
Log("[Page] 主页预设:OpenBMCLAPI 仪表盘 Lite")
|
||||
Url = "https://pcl-bmcl.milulu.xyz/"
|
||||
GoTo Download
|
||||
Case 7
|
||||
Log("[Page] 主页预设:主页市场")
|
||||
Url = "https://homepage-market.pages.dev/Custom.xaml"
|
||||
GoTo Download
|
||||
Case 8
|
||||
Log("[Page] 主页预设:更新日志")
|
||||
Url = "https://updatehomepage.pages.dev/UpdateHomepage.xaml"
|
||||
GoTo Download
|
||||
End Select
|
||||
End Select
|
||||
RunInUi(Sub() LoadContent(Content))
|
||||
@@ -128,7 +140,7 @@ Download:
|
||||
Dim NeedDownload As Boolean = True
|
||||
Try
|
||||
Version = NetGetCodeByRequestOnce(VersionAddress, Timeout:=10000)
|
||||
If Version.Length > 100 Then Throw New Exception($"获取的自定义主页版本过长({Version.Length} 字符)")
|
||||
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}")
|
||||
|
||||
@@ -99,7 +99,7 @@ Retry:
|
||||
Client.DefaultRequestHeaders.UserAgent.Add(New Net.Http.Headers.ProductInfoHeaderValue("MojangSharp", "0.1"))
|
||||
Dim Contents As New Net.Http.MultipartFormDataContent From {
|
||||
{New Net.Http.StringContent(If(SkinInfo.IsSlim, "slim", "classic")), "variant"},
|
||||
{New Net.Http.ByteArrayContent(File.ReadAllBytes(SkinInfo.LocalFile)), "file", GetFileNameFromPath(SkinInfo.LocalFile)}
|
||||
{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
|
||||
If Result.Contains("request requires user authentication") Then
|
||||
|
||||
@@ -104,92 +104,108 @@
|
||||
</local:MyCard>
|
||||
<local:MyCard Margin="0,0,0,15" Title="赞助者">
|
||||
<StackPanel Margin="25,40,10,20">
|
||||
<TextBlock HorizontalAlignment="Left" Text="以下是 2024 年 7 月的高级赞助者们!感谢各位对 PCL 和伟大的咕咕咕事业的支持!" Margin="0,0,25,5.5" TextTrimming="None" TextWrapping="WrapWithOverflow" />
|
||||
<TextBlock HorizontalAlignment="Left" Text="以下是 2024 年 9 月的高级赞助者们!感谢各位对 PCL 和伟大的咕咕咕事业的支持!" Margin="0,0,25,5.5" TextTrimming="None" TextWrapping="WrapWithOverflow" />
|
||||
<WrapPanel Margin="0,0,0,10">
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="nemonemo" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="矢坕" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="梁景瑜" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="GhostZiLie" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="靈曦蠡醯" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="zzdr" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="井下啊~" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="zkQkWyQkz" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="fill" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="法老" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="aleafsautumn" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="聪明的凉生" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="干嘛1337" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="dlxw4" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="古神" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="朝仓理香子" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="Raper_Sulin" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="vv" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="QuinnL" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="pigeno" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="whx61" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="peoprem" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="慕言" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="白雾云-白少" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="S0CK" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="1111" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="怕做苦力的苦力怕" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="慕柒茗桜" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="DsxiaKsxia" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="lucca" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="时酱" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="666a" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="Rigilkent" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="亡灵引渡" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="初音末来" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="king" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="zzhm" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="半自动M9716" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="MMIAO780" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="7930" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="Jedro" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="寂灭星云" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="天上北辰星" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="黄为很高" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="a酱" />
|
||||
<TextBlock Width="240" Margin="0,1.5" Text="Pedlar Mayweather" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="Daddy江" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="汣魰" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="lawaass" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="scz" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="tiredl" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="Jeffy0314" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="青云一梦" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="摸鱼大湿" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="PIGEON" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="Redio" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="无垠星海" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="Z10" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="嗯哼" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="_Ejvy" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="LG.神梦" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="叫爱丽丝的墨鱼" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="芙芙雪糕" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="屑萨你" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="qianlan" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="dndjkdkdms" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="时空乂猎人" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="芒果" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="小比登T^T" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="北川兔兔" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="q_q!!!" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="eric" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="Min" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="JTOPfujo》" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="CZALIZI" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="outsider" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="Kimmo" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="超爱居居巧" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="quin22" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="贤者摸鱼" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="chsero" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="幽梦瞳鸭" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="Thankscrazy" />
|
||||
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="花卉" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="hmmmfffll" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="夕阳下的余光" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="扶风°" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="唠叨的土豆" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="ulifx" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="DXX" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="小曲" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="0飞霜墨染0" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="Aminos1337" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="死亡旋风大魔王" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="风见翎" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="水阔鱼" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="无名氏" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="绘梨衣のGodzilla" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="521HERTZ" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="momotsuki" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="浩东流" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="破片南瓜" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="playalone" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="木丶九九" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="闫雁" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="雨季丶看花落丬" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="greenClemente" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="nfd" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="求阙" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="苏苏" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="冰魄凝华Iris" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="TNT" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="jokeresin" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="冻冻莓" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="木吉" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="寰辰" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="大智" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="晚吟" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="e_2" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="墨辞" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="CongYiXu1259" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="绀香十九" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="花十一" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="摸摸鱼" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="不想起名" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="Corazon" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="断幕" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="wuying" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="隐”" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="华子" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="Hikari" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="小辉joyb_" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="bluck" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="风人过" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="锦锋" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="初辟鸿蒙" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="YLF零度" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="ADB114514" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="晓乐" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="Raze" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="jj" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="社÷老登" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="仝圮" />
|
||||
<TextBlock Width="240" Margin="0,1.5" Text="FSL Launcher - 小黄菌" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="华燃王小灭" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="Charlotte_Caius" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="木了丰" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="-夜-光-" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="蓝色骷髅" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="易游" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="祈绫参_UPE" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="卖梦的人" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="rking" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="柠果籽" />
|
||||
<TextBlock Width="240" Margin="0,1.5" Text="真我 · 人之律者爱莉希雅" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="KevinREL" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="LEIBI" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="GNFDF" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="朱重八" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="TONYAF" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="Reshiram???" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="克罗44" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="社恐的波奇酱" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="Xylophone233" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="陌上如玉" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="lili66" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="小派呀" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="日想" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="墨处择" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="刘畅" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="做好事不留名的好心人" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="好将军" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="为生活而拼搏" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="韬光养晦" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="知芍" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="awa" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="CaTy" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="zhifeiwen" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="磁爆步兵" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="双开门冰箱AK15" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="泠羽" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="lupeihao" />
|
||||
<TextBlock Width="120" Margin="0,1.5" Text="木子" />
|
||||
</WrapPanel>
|
||||
<Grid Height="35">
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -238,6 +254,8 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="60" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="60" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Java Launch Wrapper" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2022 00ll00
Licensed under the MIT License." TextWrapping="Wrap" />
|
||||
@@ -274,10 +292,18 @@
|
||||
<local:MyButton Grid.Row="9" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://iconmonstr.com/license/" />
|
||||
|
||||
<TextBlock Grid.Row="10" Grid.Column="0" Text="Iconfont" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="10" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © ALIMAMA MUX, powered by alimama THX." TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="11" Grid.Column="2" MinWidth="140" Text="查看原网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<TextBlock Grid.Row="10" Grid.Column="0" Text="libwebp wrapper for .NET" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="10" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2012 Imazen
Licensed under the MIT License." TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="11" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/imazen/libwebp-net" />
|
||||
<local:MyButton Grid.Row="11" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/imazen/libwebp-net/blob/master/LICENSE.txt" />
|
||||
|
||||
<TextBlock Grid.Row="12" Grid.Column="0" Text="Iconfont" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="12" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © ALIMAMA MUX, powered by alimama THX." TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="13" Grid.Column="2" MinWidth="140" Text="查看原网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://www.iconfont.cn/" />
|
||||
|
||||
</Grid>
|
||||
</local:MyCard>
|
||||
</StackPanel>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
End Sub
|
||||
|
||||
Private Sub BtnAboutBmclapi_Click(sender As Object, e As EventArgs) Handles BtnAboutBmclapi.Click
|
||||
OpenWebsite("https://afdian.net/a/bangbang93")
|
||||
OpenWebsite("https://afdian.com/a/bangbang93")
|
||||
End Sub
|
||||
Private Sub BtnAboutWiki_Click(sender As Object, e As EventArgs) Handles BtnAboutWiki.Click
|
||||
OpenWebsite("https://www.mcmod.cn")
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
{"LaunchArgumentWindowHeight", New SetupEntry(480)},
|
||||
{"LaunchArgumentWindowType", New SetupEntry(1)},
|
||||
{"LaunchArgumentRam", New SetupEntry(False, Source:=SetupSource.Registry)},
|
||||
{"LaunchAdvanceJvm", New SetupEntry("-XX:+UseG1GC -XX:-UseAdaptiveSizePolicy -XX:-OmitStackTraceInFastThrow -Dfml.ignoreInvalidMinecraftCertificates=True -Dfml.ignorePatchDiscrepancies=True -Dlog4j2.formatMsgNoLookups=true")},
|
||||
{"LaunchAdvanceJvm", New SetupEntry("-XX:+UseG1GC -XX:-UseAdaptiveSizePolicy -XX:-OmitStackTraceInFastThrow -Djdk.lang.Process.allowAmbiguousCommands=true -Dfml.ignoreInvalidMinecraftCertificates=True -Dfml.ignorePatchDiscrepancies=True -Dlog4j2.formatMsgNoLookups=true")},
|
||||
{"LaunchAdvanceGame", New SetupEntry("")},
|
||||
{"LaunchAdvanceRun", New SetupEntry("")},
|
||||
{"LaunchAdvanceRunWait", New SetupEntry(True)},
|
||||
@@ -107,7 +107,7 @@
|
||||
{"ToolDownloadTranslate", New SetupEntry(0, Source:=SetupSource.Registry)},
|
||||
{"ToolDownloadKeepModpack", New SetupEntry(False, Source:=SetupSource.Registry)},
|
||||
{"ToolDownloadIgnoreQuilt", New SetupEntry(True, Source:=SetupSource.Registry)},
|
||||
{"ToolDownloadCert", New SetupEntry(True, Source:=SetupSource.Registry)},
|
||||
{"ToolDownloadCert", New SetupEntry(False, Source:=SetupSource.Registry)},
|
||||
{"ToolDownloadMod", New SetupEntry(1, Source:=SetupSource.Registry)},
|
||||
{"ToolUpdateAlpha", New SetupEntry(0, Source:=SetupSource.Registry, Encoded:=True)},
|
||||
{"ToolUpdateRelease", New SetupEntry(False, Source:=SetupSource.Registry)},
|
||||
|
||||
@@ -321,6 +321,11 @@
|
||||
''' 获取当前设置的 RAM 值。单位为 GB。
|
||||
''' </summary>
|
||||
Public Shared Function GetRam(Version As McVersion, UseVersionJavaSetup As Boolean, Optional Is32BitJava As Boolean? = Nothing) As Double
|
||||
|
||||
'------------------------------------------
|
||||
' 修改下方代码时需要一并修改 PageVersionSetup
|
||||
'------------------------------------------
|
||||
|
||||
Dim RamGive As Double
|
||||
If Setup.Get("LaunchRamType") = 0 Then
|
||||
'自动配置
|
||||
@@ -507,7 +512,7 @@ PreFin:
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "启动参数"
|
||||
#Region "其他选项"
|
||||
|
||||
Private Sub WindowTypeUIRefresh() Handles ComboArgumentWindowType.SelectionChanged
|
||||
If ComboArgumentWindowType Is Nothing Then Exit Sub
|
||||
@@ -545,6 +550,19 @@ PreFin:
|
||||
End If
|
||||
End Sub
|
||||
|
||||
'版本隔离警告
|
||||
Private Sub ComboArgumentIndie_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles ComboArgumentIndie.SelectionChanged
|
||||
If AniControlEnabled <> 0 Then Exit Sub
|
||||
If MyMsgBox("调整版本隔离后,你可能得把游戏存档、Mod 等文件手动迁移到新的游戏文件夹中。" & vbCrLf &
|
||||
"如果修改后发现存档消失,把这项设置改回来就能恢复。" & vbCrLf &
|
||||
"如果你不会迁移存档,不建议修改这项设置!",
|
||||
"警告", "我知道我在做什么", "取消", IsWarn:=True) = 2 Then
|
||||
AniControlEnabled += 1
|
||||
ComboArgumentIndie.SelectedItem = e.RemovedItems(0)
|
||||
AniControlEnabled -= 1
|
||||
End If
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "高级设置"
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
ToolTip="下载 Mod 时,若该 Mod 支持 Forge 和 Fabric,则显示为支持任意 Mod 加载器。
例如,开启时 JEI 显示支持全版本,关闭后 JEI 就会显示仅支持 Forge / Fabric 全版本。" />
|
||||
<local:MyCheckBox Margin="0,2,0,4" Text="验证 SSL 证书" Grid.Row="12" Height="22" Grid.ColumnSpan="2"
|
||||
x:Name="CheckDownloadCert" Tag="ToolDownloadCert"
|
||||
ToolTip="验证证书会提高安全性,但疑似有概率导致正版登录失败(#3018)。
如果在正版登录时因为 [根据验证过程,远程证书无效] 失败,可以尝试关闭此项。" />
|
||||
ToolTip="开启验证会提高安全性、降低盗号风险(见 #2767),但也可能导致正版登录失败(见 #3018)。" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
|
||||
@@ -170,26 +170,24 @@
|
||||
End Sub
|
||||
|
||||
'自动更新
|
||||
Private Sub ComboSystemActivity_SizeChanged(sender As Object, e As SelectionChangedEventArgs) Handles ComboSystemActivity.SelectionChanged
|
||||
Private Sub ComboSystemActivity_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles ComboSystemActivity.SelectionChanged
|
||||
If AniControlEnabled <> 0 Then Exit Sub
|
||||
If ComboSystemActivity.SelectedIndex = 2 Then
|
||||
If MyMsgBox("若选择此项,即使在将来出现严重问题时,你也无法获取相关通知。" & vbCrLf &
|
||||
"例如,如果发现某个版本游戏存在严重 Bug,你可能就会因为无法得到通知而导致无法预知的后果。" & vbCrLf & vbCrLf &
|
||||
"一般选择 仅在有重要通知时显示公告 就可以让你尽量不受打扰了。" & vbCrLf &
|
||||
"除非你在制作服务器整合包,或时常手动更新启动器,否则极度不推荐选择此项!", "警告", "我知道我在做什么", "取消", IsWarn:=True) = 2 Then
|
||||
ComboSystemActivity.SelectedItem = e.RemovedItems(0)
|
||||
End If
|
||||
If ComboSystemActivity.SelectedIndex <> 2 Then Exit Sub
|
||||
If MyMsgBox("若选择此项,即使在将来出现严重问题时,你也无法获取相关通知。" & vbCrLf &
|
||||
"例如,如果发现某个版本游戏存在严重 Bug,你可能就会因为无法得到通知而导致无法预知的后果。" & vbCrLf & vbCrLf &
|
||||
"一般选择 仅在有重要通知时显示公告 就可以让你尽量不受打扰了。" & vbCrLf &
|
||||
"除非你在制作服务器整合包,或时常手动更新启动器,否则极度不推荐选择此项!", "警告", "我知道我在做什么", "取消", IsWarn:=True) = 2 Then
|
||||
ComboSystemActivity.SelectedItem = e.RemovedItems(0)
|
||||
End If
|
||||
End Sub
|
||||
Private Sub ComboSystemUpdate_SizeChanged(sender As Object, e As SelectionChangedEventArgs) Handles ComboSystemUpdate.SelectionChanged
|
||||
Private Sub ComboSystemUpdate_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles ComboSystemUpdate.SelectionChanged
|
||||
If AniControlEnabled <> 0 Then Exit Sub
|
||||
If ComboSystemUpdate.SelectedIndex = 3 Then
|
||||
If MyMsgBox("若选择此项,即使在启动器将来出现严重问题时,你也无法获取更新并获得修复。" & vbCrLf &
|
||||
"例如,如果官方修改了登录方式,从而导致现有启动器无法登录,你可能就会因为无法更新而无法开始游戏。" & vbCrLf & vbCrLf &
|
||||
"一般选择 仅在有重大漏洞更新时显示提示 就可以让你尽量不受打扰了。" & vbCrLf &
|
||||
"除非你在制作服务器整合包,或时常手动更新启动器,否则极度不推荐选择此项!", "警告", "我知道我在做什么", "取消", IsWarn:=True) = 2 Then
|
||||
ComboSystemUpdate.SelectedItem = e.RemovedItems(0)
|
||||
End If
|
||||
If ComboSystemUpdate.SelectedIndex <> 3 Then Exit Sub
|
||||
If MyMsgBox("若选择此项,即使在启动器将来出现严重问题时,你也无法获取更新并获得修复。" & vbCrLf &
|
||||
"例如,如果官方修改了登录方式,从而导致现有启动器无法登录,你可能就会因为无法更新而无法开始游戏。" & vbCrLf & vbCrLf &
|
||||
"一般选择 仅在有重大漏洞更新时显示提示 就可以让你尽量不受打扰了。" & vbCrLf &
|
||||
"除非你在制作服务器整合包,或时常手动更新启动器,否则极度不推荐选择此项!", "警告", "我知道我在做什么", "取消", IsWarn:=True) = 2 Then
|
||||
ComboSystemUpdate.SelectedItem = e.RemovedItems(0)
|
||||
End If
|
||||
End Sub
|
||||
Private Sub BtnSystemUpdate_Click(sender As Object, e As EventArgs) Handles BtnSystemUpdate.Click
|
||||
|
||||
@@ -242,6 +242,9 @@
|
||||
<local:MyComboBoxItem Content="简单主页(作者:MFn233)" />
|
||||
<local:MyComboBoxItem Content="每日整合包推荐(作者:wkea)" />
|
||||
<local:MyComboBoxItem Content="Minecraft 皮肤推荐(作者:wkea)" />
|
||||
<local:MyComboBoxItem Content="OpenBMCLAPI 仪表盘 Lite(作者:Silverteal、Mxmilu666)" />
|
||||
<local:MyComboBoxItem Content="PCL 主页市场(作者:凌云)" />
|
||||
<local:MyComboBoxItem Content="PCL 更新日志(作者:Joker2184)" />
|
||||
</local:MyComboBox>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
@@ -451,7 +451,7 @@ Refresh:
|
||||
End If
|
||||
End Sub
|
||||
Private Sub LabLauncherTheme8Copy_MouseRightButtonUp() Handles LabLauncherTheme8Copy.MouseRightButtonUp
|
||||
OpenWebsite("https://afdian.net/a/LTCat")
|
||||
OpenWebsite("https://afdian.com/a/LTCat")
|
||||
End Sub
|
||||
Private Sub LabLauncherTheme9Copy_MouseRightButtonUp() Handles LabLauncherTheme9Copy.MouseRightButtonUp
|
||||
PageOtherLeft.TryFeedback()
|
||||
@@ -662,9 +662,9 @@ Refresh:
|
||||
|
||||
#End Region
|
||||
|
||||
'公开预览版
|
||||
'赞助
|
||||
Private Sub BtnLauncherDonate_Click(sender As Object, e As EventArgs) Handles BtnLauncherDonate.Click
|
||||
OpenWebsite("https://afdian.net/a/LTCat")
|
||||
OpenWebsite("https://afdian.com/a/LTCat")
|
||||
End Sub
|
||||
|
||||
'滑动条
|
||||
|
||||
@@ -92,15 +92,12 @@ Public Class PageSpeedLeft
|
||||
Card.Children.Add(GetObjectFromXML("<Path xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" Stretch=""Uniform"" Tag=""Failed"" Data=""F1 M2.5,0 L0,2.5 7.5,10 0,17.5 2.5,20 10,12.5 17.5,20 20,17.5 12.5,10 20,2.5 17.5,0 10,7.5 2.5,0Z"" Height=""15"" Width=""15"" HorizontalAlignment=""Center"" Grid.Column=""0"" Grid.Row=""0"" Fill=""{DynamicResource ColorBrush3}"" Margin=""0,1,0,0"" VerticalAlignment=""Top""/>"))
|
||||
Dim Tb As TextBlock = GetObjectFromXML("<TextBlock xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" TextWrapping=""Wrap"" HorizontalAlignment=""Left"" ToolTip=""单击复制错误详情"" Grid.Column=""1"" Grid.Row=""0"" Margin=""0,0,0,5"" />")
|
||||
Tb.Text = GetExceptionDetail(Loader.Error)
|
||||
AddHandler Tb.MouseLeftButtonDown, Sub(sender As TextBlock, e As EventArgs)
|
||||
ClipboardSet(sender.Text, False)
|
||||
Hint("已复制错误详情!", HintType.Finish)
|
||||
End Sub
|
||||
AddHandler Tb.MouseLeftButtonDown,
|
||||
Sub(sender As TextBlock, e As EventArgs)
|
||||
ClipboardSet(sender.Text, False)
|
||||
Hint("已复制错误详情!", HintType.Finish)
|
||||
End Sub
|
||||
Card.Children.Add(Tb)
|
||||
''删除取消按钮
|
||||
'For Each Element As FrameworkElement In CType(Card.Parent, Panel).Children
|
||||
' If Element.Name = "BtnCancel" Then AniDispose(CType(Element, MyIconButton), True)
|
||||
'Next
|
||||
#End Region
|
||||
Case LoadState.Finished
|
||||
#Region "完成,销毁卡片并返回"
|
||||
@@ -195,11 +192,10 @@ Public Class PageSpeedLeft
|
||||
Sub(sender As MyIconButton, e As EventArgs)
|
||||
AniDispose(sender, False)
|
||||
AniDispose(Card, True, Sub() If FrmSpeedRight.PanMain.Children.Count = 0 AndAlso FrmMain.PageCurrent = FormMain.PageType.DownloadManager Then FrmMain.PageBack())
|
||||
Dim LoaderName = If(Loader Is Nothing, "null", Loader.Name)
|
||||
RightCards.Remove(LoaderName)
|
||||
LoaderTaskbar.TryTake(Loader)
|
||||
Log($"[Taskbar] 关闭下载管理卡片:{LoaderName},且移出任务列表")
|
||||
RunInThread(Sub() Loader?.Abort())
|
||||
RightCards.Remove(Loader.Name)
|
||||
LoaderTaskbar.Remove(Loader)
|
||||
Log($"[Taskbar] 关闭下载管理卡片:{Loader.Name},且移出任务列表")
|
||||
RunInThread(Sub() Loader.Abort())
|
||||
End Sub
|
||||
'如果已经失败,再刷新一次,修改成失败的控件
|
||||
If Loader.State = LoadState.Failed Then
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
Catch ex As Exception
|
||||
Log(ex, "强制刷新时清理本地 Mod 信息缓存失败")
|
||||
End Try
|
||||
If FrmVersionMod IsNot Nothing Then FrmVersionMod.RefreshList(True) '无需 Else,还没加载刷个鬼的新
|
||||
If FrmVersionMod IsNot Nothing Then FrmVersionMod.ReloadModList(True) '无需 Else,还没加载刷个鬼的新
|
||||
ItemMod.Checked = True
|
||||
Hint("正在刷新……", Log:=False)
|
||||
End Sub
|
||||
|
||||
@@ -23,8 +23,15 @@
|
||||
<local:MyButton x:Name="BtnManageCheck" Grid.Column="3" MinWidth="140" Text="检查 Mod" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ToolTip="快速检查 Mod 的版本要求、重复、前置缺失等问题。
结果仅供参考,由于部分 Mod 标注的信息有误,可能并不准确。" Visibility="Collapsed" />
|
||||
</Grid>
|
||||
</local:MyCard>
|
||||
<local:MyCard x:Name="PanListBack" VerticalAlignment="Top" Opacity="0" Margin="0,0,0,15" Title=" " MinHeight="40">
|
||||
<StackPanel Margin="20,38,18,18" Name="PanList" VerticalAlignment="Top" />
|
||||
<local:MyCard x:Name="PanListBack" VerticalAlignment="Top" Opacity="0" Margin="0,0,0,14" Title=" " MinHeight="55">
|
||||
<StackPanel x:Name="PanFilter" Margin="15,13,0,0" Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Height="28">
|
||||
<local:MyRadioButton Tag="0" ColorType="Highlight" VerticalAlignment="Center" Margin="2,0" Text="全部" x:Name="BtnFilterAll" Checked="True" />
|
||||
<local:MyRadioButton Tag="1" ColorType="Highlight" VerticalAlignment="Center" Margin="2,0" Text="已启用" x:Name="BtnFilterEnabled" />
|
||||
<local:MyRadioButton Tag="2" ColorType="Highlight" VerticalAlignment="Center" Margin="2,0" Text="已禁用" x:Name="BtnFilterDisabled" />
|
||||
<local:MyRadioButton Tag="3" ColorType="Highlight" VerticalAlignment="Center" Margin="2,0" Text="可更新" x:Name="BtnFilterCanUpdate" />
|
||||
<local:MyRadioButton Tag="4" ColorType="Highlight" VerticalAlignment="Center" Margin="2,0" Text="错误" x:Name="BtnFilterError" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="20,48,18,22" Name="PanList" VerticalAlignment="Top" />
|
||||
</local:MyCard>
|
||||
</StackPanel>
|
||||
</local:MyScrollViewer>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
If FrmMain.PageLast.Page <> FormMain.PageType.CompDetail Then PanBack.ScrollToHome()
|
||||
AniControlEnabled += 1
|
||||
SelectedMods.Clear()
|
||||
RefreshList()
|
||||
ReloadModList()
|
||||
ChangeAllSelected(False)
|
||||
AniControlEnabled -= 1
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
If IsLoad Then Exit Sub
|
||||
IsLoad = True
|
||||
|
||||
'调整按钮边距(这玩意儿没法从 XAML 改)
|
||||
For Each Btn As MyRadioButton In PanFilter.Children
|
||||
Btn.LabText.Margin = New Thickness(-2, 0, 8, 0)
|
||||
Next
|
||||
|
||||
#If DEBUG Then
|
||||
BtnManageCheck.Visibility = Visibility.Visible
|
||||
#End If
|
||||
@@ -24,9 +29,10 @@
|
||||
''' <summary>
|
||||
''' 刷新 Mod 列表。
|
||||
''' </summary>
|
||||
Public Sub RefreshList(Optional ForceReload As Boolean = False)
|
||||
Public Sub ReloadModList(Optional ForceReload As Boolean = False)
|
||||
If LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", If(ForceReload, LoaderFolderRunType.ForceRun, LoaderFolderRunType.RunOnUpdated)) Then
|
||||
Log("[System] 已刷新 Mod 列表")
|
||||
Filter = FilterType.All
|
||||
PanBack.ScrollToHome()
|
||||
SearchBox.Text = ""
|
||||
End If
|
||||
@@ -53,10 +59,9 @@
|
||||
''' 将加载器结果的 Mod 列表加载为 UI。
|
||||
''' </summary>
|
||||
Private Sub LoadUIFromLoaderOutput()
|
||||
Dim Mods As List(Of McMod) = McModLoader.Output
|
||||
Try
|
||||
'判断应该显示哪一个页面
|
||||
If Mods.Any() Then
|
||||
If McModLoader.Output.Any() Then
|
||||
PanBack.Visibility = Visibility.Visible
|
||||
PanEmpty.Visibility = Visibility.Collapsed
|
||||
Else
|
||||
@@ -64,13 +69,15 @@
|
||||
PanBack.Visibility = Visibility.Collapsed
|
||||
Exit Sub
|
||||
End If
|
||||
'输出结果
|
||||
'修改缓存
|
||||
ModItems.Clear()
|
||||
For Each ModEntity As McMod In Mods
|
||||
For Each ModEntity As McMod In McModLoader.Output
|
||||
ModItems(ModEntity.RawFileName) = McModListItem(ModEntity)
|
||||
Next
|
||||
'显示结果
|
||||
Filter = FilterType.All
|
||||
SearchBox.Text = "" '这会触发结果刷新,所以需要在 ModItems 更新之后,详见 #3124 的视频
|
||||
RefreshResult(Mods)
|
||||
RefreshUI()
|
||||
Catch ex As Exception
|
||||
Log(ex, "加载 Mod 列表 UI 失败", LogLevel.Feedback)
|
||||
End Try
|
||||
@@ -114,7 +121,7 @@
|
||||
ToolTipService.SetVerticalOffset(BtnED, 30)
|
||||
ToolTipService.SetHorizontalOffset(BtnED, 2)
|
||||
AddHandler BtnED.Click, AddressOf ED_Click
|
||||
If sender.Entry.State = McMod.McModState.Unavaliable Then
|
||||
If sender.Entry.State = McMod.McModState.Unavailable Then
|
||||
sender.Buttons = {BtnCont, BtnOpen, BtnDelete}
|
||||
Else
|
||||
sender.Buttons = {BtnCont, BtnOpen, BtnED, BtnDelete}
|
||||
@@ -122,38 +129,134 @@
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 刷新结果显示。
|
||||
''' 刷新整个 UI。
|
||||
''' </summary>
|
||||
Private Sub RefreshResult(Mods As List(Of McMod))
|
||||
PanList.Children.Clear()
|
||||
For Each TargetMod In Mods
|
||||
PanList.Children.Add(ModItems(TargetMod.RawFileName))
|
||||
Next
|
||||
RefreshTitle()
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 刷新卡片标题。
|
||||
''' </summary>
|
||||
Private Sub RefreshTitle()
|
||||
Dim Mods = PanList.Children.Cast(Of MyLocalModItem).Select(Function(i) i.Entry).ToList
|
||||
Dim Counter = {0, 0, 0}
|
||||
For Each ModEntity As McMod In Mods
|
||||
Counter(ModEntity.State) += 1
|
||||
Next
|
||||
Dim TypeList As New List(Of String)
|
||||
If Counter(McMod.McModState.Disabled) > 0 Then TypeList.Add("禁用 " & Counter(McMod.McModState.Disabled))
|
||||
If Counter(McMod.McModState.Unavaliable) > 0 Then TypeList.Add("错误 " & Counter(McMod.McModState.Unavaliable))
|
||||
If Counter(McMod.McModState.Fine) > 0 Then TypeList.Insert(0, If(TypeList.Any, "启用 ", "") & Counter(McMod.McModState.Fine))
|
||||
If Not IsSearching Then
|
||||
PanListBack.Title = "Mod 列表 (" & Join(TypeList, ",") & ")"
|
||||
ElseIf TypeList.Any() Then
|
||||
PanListBack.Title = "搜索结果 (" & Join(TypeList, ",") & ")"
|
||||
Public Sub RefreshUI()
|
||||
If PanList Is Nothing Then Exit Sub
|
||||
Dim ShowMods = GetShowingMods(True).ToList()
|
||||
'重新列出列表
|
||||
AniControlEnabled += 1
|
||||
If ShowMods.Any() Then
|
||||
PanList.Visibility = Visibility.Visible
|
||||
PanList.Children.Clear()
|
||||
For Each TargetMod In ShowMods
|
||||
Dim Item As MyLocalModItem = ModItems(TargetMod.RawFileName)
|
||||
Item.Checked = SelectedMods.Contains(TargetMod.RawFileName) '更新选中状态
|
||||
PanList.Children.Add(Item)
|
||||
Next
|
||||
Else
|
||||
PanListBack.Title = "无搜索结果"
|
||||
PanList.Visibility = Visibility.Collapsed
|
||||
End If
|
||||
PanList.Visibility = If(Mods.Any(), Visibility.Visible, Visibility.Collapsed)
|
||||
AniControlEnabled -= 1
|
||||
SelectedMods = SelectedMods.Where(Function(m) ShowMods.Any(Function(s) s.RawFileName = m)).ToList '取消选中已经不显示的 Mod
|
||||
RefreshBars()
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 刷新顶栏和底栏显示。
|
||||
''' </summary>
|
||||
Public Sub RefreshBars()
|
||||
'-----------------
|
||||
' 顶部栏
|
||||
'-----------------
|
||||
|
||||
'计数
|
||||
Dim AnyCount As Integer = 0
|
||||
Dim EnabledCount As Integer = 0
|
||||
Dim DisabledCount As Integer = 0
|
||||
Dim UpdateCount As Integer = 0
|
||||
Dim UnavalialeCount As Integer = 0
|
||||
For Each ModItem In GetShowingMods(False)
|
||||
AnyCount += 1
|
||||
If ModItem.CanUpdate Then UpdateCount += 1
|
||||
If ModItem.State.Equals(McMod.McModState.Fine) Then EnabledCount += 1
|
||||
If ModItem.State.Equals(McMod.McModState.Disabled) Then DisabledCount += 1
|
||||
If ModItem.State.Equals(McMod.McModState.Unavailable) Then UnavalialeCount += 1
|
||||
Next
|
||||
'显示
|
||||
BtnFilterAll.Text = If(IsSearching, "搜索结果", "全部") & $" ({AnyCount})"
|
||||
BtnFilterCanUpdate.Text = $"可更新 ({UpdateCount})"
|
||||
BtnFilterCanUpdate.Visibility = If(Filter = FilterType.CanUpdate OrElse UpdateCount > 0, Visibility.Visible, Visibility.Collapsed)
|
||||
BtnFilterEnabled.Text = $"启用 ({EnabledCount})"
|
||||
BtnFilterEnabled.Visibility = If(Filter = FilterType.Enabled OrElse (EnabledCount > 0 AndAlso EnabledCount < AnyCount), Visibility.Visible, Visibility.Collapsed)
|
||||
BtnFilterDisabled.Text = $"禁用 ({DisabledCount})"
|
||||
BtnFilterDisabled.Visibility = If(Filter = FilterType.Disabled OrElse DisabledCount > 0, Visibility.Visible, Visibility.Collapsed)
|
||||
BtnFilterError.Text = $"错误 ({UnavalialeCount})"
|
||||
BtnFilterError.Visibility = If(Filter = FilterType.Unavailable OrElse UnavalialeCount > 0, Visibility.Visible, Visibility.Collapsed)
|
||||
|
||||
'-----------------
|
||||
' 底部栏
|
||||
'-----------------
|
||||
|
||||
'计数
|
||||
Dim NewCount As Integer = SelectedMods.Count
|
||||
Dim Selected = NewCount > 0
|
||||
If Selected Then LabSelect.Text = $"已选择 {NewCount} 个文件" '取消所有选择时不更新数字
|
||||
'按钮可用性
|
||||
If Selected Then
|
||||
Dim HasUpdate As Boolean = False
|
||||
Dim HasEnabled As Boolean = False
|
||||
Dim HasDisabled As Boolean = False
|
||||
For Each ModEntity In McModLoader.Output
|
||||
If SelectedMods.Contains(ModEntity.RawFileName) Then
|
||||
If ModEntity.CanUpdate Then HasUpdate = True
|
||||
If ModEntity.State = McMod.McModState.Fine Then
|
||||
HasEnabled = True
|
||||
ElseIf ModEntity.State = McMod.McModState.Disabled Then
|
||||
HasDisabled = True
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
BtnSelectDisable.IsEnabled = HasEnabled
|
||||
BtnSelectEnable.IsEnabled = HasDisabled
|
||||
BtnSelectUpdate.IsEnabled = HasUpdate
|
||||
End If
|
||||
'更新显示状态
|
||||
If AniControlEnabled = 0 Then
|
||||
PanListBack.Margin = New Thickness(0, 0, 0, If(Selected, 95, 15))
|
||||
If Selected Then
|
||||
'仅在数量增加时播放出现/跳跃动画
|
||||
If BottomBarShownCount >= NewCount Then
|
||||
BottomBarShownCount = NewCount
|
||||
Return
|
||||
Else
|
||||
BottomBarShownCount = NewCount
|
||||
End If
|
||||
'出现/跳跃动画
|
||||
CardSelect.Visibility = Visibility.Visible
|
||||
AniStart({
|
||||
AaOpacity(CardSelect, 1 - CardSelect.Opacity, 60),
|
||||
AaTranslateY(CardSelect, -27 - TransSelect.Y, 120, Ease:=New AniEaseOutFluent(AniEasePower.Weak)),
|
||||
AaTranslateY(CardSelect, 3, 150, 120, Ease:=New AniEaseInoutFluent(AniEasePower.Weak)),
|
||||
AaTranslateY(CardSelect, -1, 90, 270, Ease:=New AniEaseInoutFluent(AniEasePower.Weak))
|
||||
}, "Mod Sidebar")
|
||||
Else
|
||||
'不重复播放隐藏动画
|
||||
If BottomBarShownCount = 0 Then Return
|
||||
BottomBarShownCount = 0
|
||||
'隐藏动画
|
||||
AniStart({
|
||||
AaOpacity(CardSelect, -CardSelect.Opacity, 90),
|
||||
AaTranslateY(CardSelect, -10 - TransSelect.Y, 90, Ease:=New AniEaseInFluent(AniEasePower.Weak)),
|
||||
AaCode(Sub() CardSelect.Visibility = Visibility.Collapsed, After:=True)
|
||||
}, "Mod Sidebar")
|
||||
End If
|
||||
Else
|
||||
AniStop("Mod Sidebar")
|
||||
BottomBarShownCount = NewCount
|
||||
If Selected Then
|
||||
CardSelect.Visibility = Visibility.Visible
|
||||
CardSelect.Opacity = 1
|
||||
TransSelect.Y = -25
|
||||
Else
|
||||
CardSelect.Visibility = Visibility.Collapsed
|
||||
CardSelect.Opacity = 0
|
||||
TransSelect.Y = -10
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
Private BottomBarShownCount As Integer = 0
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "管理"
|
||||
@@ -192,11 +295,7 @@
|
||||
''' 全选。
|
||||
''' </summary>
|
||||
Private Sub BtnManageSelectAll_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnManageSelectAll.Click
|
||||
If SelectedMods.Count < PanList.Children.Count Then
|
||||
ChangeAllSelected(True)
|
||||
Else
|
||||
ChangeAllSelected(False)
|
||||
End If
|
||||
ChangeAllSelected(SelectedMods.Count < GetShowingMods(True).Count)
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
@@ -223,91 +322,19 @@
|
||||
Else
|
||||
SelectedMods.Remove(SelectedKey)
|
||||
End If
|
||||
'更新下边栏 UI
|
||||
RefreshBottomBar()
|
||||
End Sub
|
||||
|
||||
'改变下边栏状态
|
||||
Private ShownCount As Integer = 0
|
||||
Public Sub RefreshBottomBar()
|
||||
'计数
|
||||
Dim NewCount As Integer = SelectedMods.Count
|
||||
Dim Selected = NewCount > 0
|
||||
If Selected Then LabSelect.Text = $"已选择 {NewCount} 个文件" '取消所有选择时不更新数字
|
||||
'按钮可用性
|
||||
If Selected Then
|
||||
Dim HasUpdate As Boolean = False
|
||||
Dim HasEnabled As Boolean = False
|
||||
Dim HasDisabled As Boolean = False
|
||||
For Each ModEntity In McModLoader.Output
|
||||
If SelectedMods.Contains(ModEntity.RawFileName) Then
|
||||
If ModEntity.CanUpdate Then HasUpdate = True
|
||||
If ModEntity.State = McMod.McModState.Fine Then
|
||||
HasEnabled = True
|
||||
ElseIf ModEntity.State = McMod.McModState.Disabled Then
|
||||
HasDisabled = True
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
BtnSelectDisable.IsEnabled = HasEnabled
|
||||
BtnSelectEnable.IsEnabled = HasDisabled
|
||||
BtnSelectUpdate.IsEnabled = HasUpdate
|
||||
End If
|
||||
'更新显示状态
|
||||
If AniControlEnabled = 0 Then
|
||||
If Selected Then
|
||||
'仅在数量增加时播放出现/跳跃动画
|
||||
If ShownCount >= NewCount Then
|
||||
ShownCount = NewCount
|
||||
Return
|
||||
Else
|
||||
ShownCount = NewCount
|
||||
End If
|
||||
'出现/跳跃动画
|
||||
CardSelect.Visibility = Visibility.Visible
|
||||
AniStart({
|
||||
AaOpacity(CardSelect, 1 - CardSelect.Opacity, 60),
|
||||
AaTranslateY(CardSelect, -27 - TransSelect.Y, 120, Ease:=New AniEaseOutFluent(AniEasePower.Weak)),
|
||||
AaTranslateY(CardSelect, 3, 150, 120, Ease:=New AniEaseInoutFluent(AniEasePower.Weak)),
|
||||
AaTranslateY(CardSelect, -1, 90, 270, Ease:=New AniEaseInoutFluent(AniEasePower.Weak))
|
||||
}, "Mod Sidebar")
|
||||
Else
|
||||
'不重复播放隐藏动画
|
||||
If ShownCount = 0 Then Return
|
||||
ShownCount = 0
|
||||
'隐藏动画
|
||||
AniStart({
|
||||
AaOpacity(CardSelect, -CardSelect.Opacity, 90),
|
||||
AaTranslateY(CardSelect, -10 - TransSelect.Y, 90, Ease:=New AniEaseInFluent(AniEasePower.Weak)),
|
||||
AaCode(Sub() CardSelect.Visibility = Visibility.Collapsed, After:=True)
|
||||
}, "Mod Sidebar")
|
||||
End If
|
||||
Else
|
||||
AniStop("Mod Sidebar")
|
||||
ShownCount = NewCount
|
||||
If Selected Then
|
||||
CardSelect.Visibility = Visibility.Visible
|
||||
CardSelect.Opacity = 1
|
||||
TransSelect.Y = -25
|
||||
Else
|
||||
CardSelect.Visibility = Visibility.Collapsed
|
||||
CardSelect.Opacity = 0
|
||||
TransSelect.Y = -10
|
||||
End If
|
||||
End If
|
||||
RefreshBars()
|
||||
End Sub
|
||||
|
||||
'切换所有项的选择状态
|
||||
Private Sub ChangeAllSelected(Value As Boolean)
|
||||
AniControlEnabled += 1
|
||||
SelectedMods.Clear()
|
||||
For Each Item As MyLocalModItem In PanList.Children
|
||||
For Each Item As MyLocalModItem In GetShowingMods(True).Select(Function(m) ModItems(m.RawFileName))
|
||||
Item.Checked = Value
|
||||
If Value Then SelectedMods.Add(Item.Entry.RawFileName)
|
||||
Next
|
||||
AniControlEnabled -= 1
|
||||
'更新下边栏 UI
|
||||
RefreshBottomBar()
|
||||
RefreshBars()
|
||||
End Sub
|
||||
Private Sub UnselectedAllWithAnimation() Handles Load.StateChanged, Me.PageExit
|
||||
Dim CacheAniControlEnabled = AniControlEnabled
|
||||
@@ -321,6 +348,75 @@
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "筛选"
|
||||
|
||||
Private _Filter As FilterType = FilterType.All
|
||||
Private Property Filter As FilterType
|
||||
Get
|
||||
Return _Filter
|
||||
End Get
|
||||
Set(value As FilterType)
|
||||
If _Filter = value Then Return
|
||||
_Filter = value
|
||||
Select Case value
|
||||
Case FilterType.All
|
||||
BtnFilterAll.Checked = True
|
||||
Case FilterType.Enabled
|
||||
BtnFilterEnabled.Checked = True
|
||||
Case FilterType.Disabled
|
||||
BtnFilterDisabled.Checked = True
|
||||
Case FilterType.CanUpdate
|
||||
BtnFilterCanUpdate.Checked = True
|
||||
Case Else
|
||||
BtnFilterError.Checked = True
|
||||
End Select
|
||||
RefreshUI()
|
||||
End Set
|
||||
End Property
|
||||
Private Enum FilterType As Integer
|
||||
All = 0
|
||||
Enabled = 1
|
||||
Disabled = 2
|
||||
CanUpdate = 3
|
||||
Unavailable = 4
|
||||
End Enum
|
||||
|
||||
''' <summary>
|
||||
''' 获取所有应该显示在 UI 中的 Mod。
|
||||
''' </summary>
|
||||
Private Function GetShowingMods(ApplyFilter As Boolean) As IEnumerable(Of McMod)
|
||||
If McModLoader.Output Is Nothing Then Return New List(Of McMod)
|
||||
Return If(IsSearching, SearchResult, McModLoader.Output).Where(Function(m) Not ApplyFilter OrElse CanPassFilter(m))
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 检查该 Mod 项是否符合当前筛选的类别。
|
||||
''' </summary>
|
||||
Private Function CanPassFilter(CheckingMod As McMod) As Boolean
|
||||
Select Case Filter
|
||||
Case FilterType.All
|
||||
Return True
|
||||
Case FilterType.Enabled
|
||||
Return CheckingMod.State = McMod.McModState.Fine
|
||||
Case FilterType.Disabled
|
||||
Return CheckingMod.State = McMod.McModState.Disabled
|
||||
Case FilterType.CanUpdate
|
||||
Return CheckingMod.CanUpdate
|
||||
Case FilterType.Unavailable
|
||||
Return CheckingMod.State = McMod.McModState.Unavailable
|
||||
Case Else
|
||||
Return False
|
||||
End Select
|
||||
End Function
|
||||
|
||||
'点击筛选项触发的改变
|
||||
Private Sub ChangeFilter(sender As MyRadioButton, raiseByMouse As Boolean) Handles BtnFilterAll.Check, BtnFilterCanUpdate.Check, BtnFilterDisabled.Check, BtnFilterEnabled.Check, BtnFilterError.Check
|
||||
Filter = sender.Tag
|
||||
RefreshUI()
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "下边栏"
|
||||
|
||||
'启用 / 禁用
|
||||
@@ -362,7 +458,7 @@
|
||||
FileSystem.Rename(ModEntity.Path, NewPath)
|
||||
Catch ex As FileNotFoundException
|
||||
Log(ex, $"未找到需要重命名的 Mod({If(ModEntity.Path, "null")})", LogLevel.Feedback)
|
||||
RefreshList(True)
|
||||
ReloadModList(True)
|
||||
Return
|
||||
Catch ex As Exception
|
||||
Log(ex, $"重命名 Mod 失败({If(ModEntity.Path, "null")})")
|
||||
@@ -374,6 +470,11 @@
|
||||
Dim IndexOfLoader As Integer = McModLoader.Output.IndexOf(ModEntity)
|
||||
McModLoader.Output.RemoveAt(IndexOfLoader)
|
||||
McModLoader.Output.Insert(IndexOfLoader, NewModEntity)
|
||||
If SearchResult IsNot Nothing Then
|
||||
Dim IndexOfResult As Integer = SearchResult.IndexOf(ModEntity)
|
||||
SearchResult.Remove(ModEntity)
|
||||
SearchResult.Insert(IndexOfResult, NewModEntity)
|
||||
End If
|
||||
'更改 UI 中的列表
|
||||
Dim NewItem As MyLocalModItem = McModListItem(NewModEntity)
|
||||
ModItems(ModEntity.RawFileName) = NewItem
|
||||
@@ -382,18 +483,20 @@
|
||||
PanList.Children.RemoveAt(IndexOfUi)
|
||||
PanList.Children.Insert(IndexOfUi, NewItem)
|
||||
Next
|
||||
RefreshTitle() '改变数量显示
|
||||
If Not IsSuccessful Then
|
||||
Hint("由于文件被占用,Mod 的状态切换失败,请尝试关闭正在运行的游戏后再试!", HintType.Critical)
|
||||
RefreshList(True)
|
||||
If IsSuccessful Then
|
||||
RefreshBars()
|
||||
Else
|
||||
RefreshBottomBar()
|
||||
Hint("由于文件被占用,Mod 的状态切换失败,请尝试关闭正在运行的游戏后再试!", HintType.Critical)
|
||||
ReloadModList(True)
|
||||
End If
|
||||
End Sub
|
||||
|
||||
'更新
|
||||
Private Sub BtnSelectUpdate_Click() Handles BtnSelectUpdate.Click
|
||||
UpdateMods(McModLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawFileName) AndAlso m.CanUpdate))
|
||||
Dim UpdateList As List(Of McMod) = McModLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawFileName) AndAlso m.CanUpdate).ToList()
|
||||
If Not UpdateList.Any() Then Return
|
||||
UpdateMods(UpdateList)
|
||||
ChangeAllSelected(False)
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 记录正在进行 Mod 更新的 mods 文件夹路径。
|
||||
@@ -476,7 +579,14 @@
|
||||
'结果提示
|
||||
Select Case Loader.State
|
||||
Case LoadState.Finished
|
||||
Hint(If(FinishedFileNames.Count > 1, $"已成功更新 {FinishedFileNames.Count} 个 Mod!", $"已成功更新:{FinishedFileNames.Single}"), HintType.Finish)
|
||||
Select Case FinishedFileNames.Count
|
||||
Case 0 '一般是由于 Mod 文件被占用,然后玩家主动取消
|
||||
Log($"[Mod] 没有 Mod 被成功更新")
|
||||
Case 1
|
||||
Hint($"已成功更新 {FinishedFileNames.Single}!", HintType.Finish)
|
||||
Case Else
|
||||
Hint($"已成功更新 {FinishedFileNames.Count} 个 Mod!", HintType.Finish)
|
||||
End Select
|
||||
Case LoadState.Failed
|
||||
Hint("Mod 更新失败:" & GetExceptionSummary(Loader.Error), HintType.Critical)
|
||||
Case LoadState.Aborted
|
||||
@@ -484,6 +594,7 @@
|
||||
Case Else
|
||||
Exit Sub
|
||||
End Select
|
||||
Log($"[Mod] 已从正在进行 Mod 更新的文件夹列表移除:{PathMods}")
|
||||
UpdatingVersions.Remove(PathMods)
|
||||
'清理缓存
|
||||
RunInNewThread(
|
||||
@@ -498,13 +609,13 @@
|
||||
End Sub, "Clean Mod Update Cache", ThreadPriority.BelowNormal)
|
||||
End Sub
|
||||
'启动加载器
|
||||
Log($"[Mod] 开始更新 {ModList.Count} 个 Mod")
|
||||
Log($"[Mod] 开始更新 {ModList.Count} 个 Mod:{PathMods}")
|
||||
UpdatingVersions.Add(PathMods)
|
||||
Loader.Start()
|
||||
LoaderTaskbarAdd(Loader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
FrmMain.BtnExtraDownload.Ribble()
|
||||
RefreshList(True)
|
||||
ReloadModList(True)
|
||||
Catch ex As Exception
|
||||
Log(ex, "初始化 Mod 更新失败")
|
||||
End Try
|
||||
@@ -513,6 +624,7 @@
|
||||
'删除
|
||||
Private Sub BtnSelectDelete_Click() Handles BtnSelectDelete.Click
|
||||
DeleteMods(McModLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawFileName)))
|
||||
ChangeAllSelected(False)
|
||||
End Sub
|
||||
Private Sub DeleteMods(ModList As IEnumerable(Of McMod))
|
||||
Try
|
||||
@@ -538,7 +650,7 @@
|
||||
End If
|
||||
Catch ex As OperationCanceledException
|
||||
Log(ex, "删除 Mod 被主动取消")
|
||||
RefreshList(True)
|
||||
ReloadModList(True)
|
||||
Return
|
||||
Catch ex As Exception
|
||||
Log(ex, $"删除 Mod 失败({ModEntity.Path})", LogLevel.Msgbox)
|
||||
@@ -548,18 +660,19 @@
|
||||
SelectedMods.Remove(ModEntity.RawFileName)
|
||||
'更改 Loader 和 UI 中的列表
|
||||
McModLoader.Output.Remove(ModEntity)
|
||||
SearchResult?.Remove(ModEntity)
|
||||
ModItems.Remove(ModEntity.RawFileName)
|
||||
Dim IndexOfUi As Integer = PanList.Children.IndexOf(PanList.Children.OfType(Of MyLocalModItem).FirstOrDefault(Function(i) i.Entry.Equals(ModEntity)))
|
||||
If IndexOfUi >= 0 Then PanList.Children.RemoveAt(IndexOfUi)
|
||||
Next
|
||||
RefreshTitle()
|
||||
RefreshBars()
|
||||
If Not IsSuccessful Then
|
||||
Hint("由于文件被占用,Mod 删除失败,请尝试关闭正在运行的游戏后再试!", HintType.Critical)
|
||||
RefreshList(True)
|
||||
ReloadModList(True)
|
||||
ElseIf PanList.Children.Count = 0 Then
|
||||
RefreshList(True) '删除了全部文件
|
||||
ReloadModList(True) '删除了全部文件
|
||||
Else
|
||||
RefreshBottomBar()
|
||||
RefreshBars()
|
||||
End If
|
||||
'显示结果提示
|
||||
If Not IsSuccessful Then Exit Sub
|
||||
@@ -578,10 +691,10 @@
|
||||
End If
|
||||
Catch ex As OperationCanceledException
|
||||
Log(ex, "删除 Mod 被主动取消")
|
||||
RefreshList(True)
|
||||
ReloadModList(True)
|
||||
Catch ex As Exception
|
||||
Log(ex, "删除 Mod 出现未知错误", LogLevel.Feedback)
|
||||
RefreshList(True)
|
||||
ReloadModList(True)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
@@ -600,7 +713,7 @@
|
||||
|
||||
Dim ModEntry As McMod = CType(If(TypeOf sender Is MyIconButton, sender.Tag, sender), MyLocalModItem).Entry
|
||||
'加载失败信息
|
||||
If ModEntry.State = McMod.McModState.Unavaliable Then
|
||||
If ModEntry.State = McMod.McModState.Unavailable Then
|
||||
MyMsgBox("无法读取此 Mod 的信息。" & vbCrLf & vbCrLf & "详细的错误信息:" & GetExceptionDetail(ModEntry.FileUnavailableReason), "Mod 读取失败")
|
||||
Return
|
||||
End If
|
||||
@@ -694,8 +807,8 @@
|
||||
Return Not String.IsNullOrWhiteSpace(SearchBox.Text)
|
||||
End Get
|
||||
End Property
|
||||
Private SearchResult As List(Of McMod)
|
||||
Public Sub SearchRun() Handles SearchBox.TextChanged
|
||||
ChangeAllSelected(False)
|
||||
If IsSearching Then
|
||||
'构造请求
|
||||
Dim QueryList As New List(Of SearchEntry(Of McMod))
|
||||
@@ -718,12 +831,9 @@
|
||||
QueryList.Add(New SearchEntry(Of McMod) With {.Item = Entry, .SearchSource = SearchSource})
|
||||
Next
|
||||
'进行搜索
|
||||
Dim SearchResult = Search(QueryList, SearchBox.Text, MaxBlurCount:=6, MinBlurSimilarity:=0.35)
|
||||
RefreshResult(SearchResult.Select(Function(r) r.Item).ToList)
|
||||
Else
|
||||
'退出搜索状态
|
||||
RefreshResult(McModLoader.Output)
|
||||
SearchResult = Search(QueryList, SearchBox.Text, MaxBlurCount:=6, MinBlurSimilarity:=0.35).Select(Function(r) r.Item).ToList
|
||||
End If
|
||||
RefreshUI()
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
@@ -283,6 +283,15 @@
|
||||
'补全文件
|
||||
Private Sub BtnManageCheck_Click(sender As Object, e As EventArgs) Handles BtnManageCheck.Click
|
||||
Try
|
||||
'忽略文件检查提示
|
||||
If ShouldIgnoreFileCheck(PageVersionLeft.Version) Then
|
||||
If Setup.Get("LaunchAdvanceAssets") Then
|
||||
Hint("请先关闭 [设置 → 高级启动选项 → 关闭文件校验],然后再尝试补全文件!", HintType.Info)
|
||||
Else
|
||||
Hint("请先关闭 [版本设置 → 设置 → 高级启动选项 → 关闭文件校验],然后再尝试补全文件!", HintType.Info)
|
||||
End If
|
||||
Exit Sub
|
||||
End If
|
||||
'重复任务检查
|
||||
For Each OngoingLoader In LoaderTaskbar
|
||||
If OngoingLoader.Name <> PageVersionLeft.Version.Name & " 文件补全" Then Continue For
|
||||
@@ -290,7 +299,7 @@
|
||||
Exit Sub
|
||||
Next
|
||||
'启动
|
||||
Dim Loader As New LoaderCombo(Of String)(PageVersionLeft.Version.Name & " 文件补全", DlClientFix(PageVersionLeft.Version, True, AssetsIndexExistsBehaviour.AlwaysDownload, False))
|
||||
Dim Loader As New LoaderCombo(Of String)(PageVersionLeft.Version.Name & " 文件补全", DlClientFix(PageVersionLeft.Version, True, AssetsIndexExistsBehaviour.AlwaysDownload))
|
||||
Loader.OnStateChanged =
|
||||
Sub()
|
||||
Select Case Loader.State
|
||||
|
||||
@@ -282,6 +282,11 @@
|
||||
If Setup.Get("VersionRamType", Version:=Version) = 2 Then
|
||||
Return PageSetupLaunch.GetRam(Version, True, Is32BitJava)
|
||||
End If
|
||||
|
||||
'------------------------------------------
|
||||
' 修改下方代码时需要一并修改 PageSetupLaunch
|
||||
'------------------------------------------
|
||||
|
||||
'使用当前版本的设置
|
||||
Dim RamGive As Double
|
||||
If Setup.Get("VersionRamType", Version:=Version) = 0 Then
|
||||
@@ -297,19 +302,19 @@
|
||||
'可安装 Mod 的版本
|
||||
Dim ModDir As New DirectoryInfo(Version.PathIndie & "mods\")
|
||||
Dim ModCount As Integer = If(ModDir.Exists, ModDir.GetFiles.Length, 0)
|
||||
RamMininum = 0.4 + ModCount / 150
|
||||
RamTarget1 = 1.5 + ModCount / 100
|
||||
RamTarget2 = 3 + ModCount / 60
|
||||
RamTarget3 = 6 + ModCount / 30
|
||||
RamMininum = 0.5 + ModCount / 150
|
||||
RamTarget1 = 1.5 + ModCount / 90
|
||||
RamTarget2 = 2.7 + ModCount / 50
|
||||
RamTarget3 = 4.5 + ModCount / 25
|
||||
ElseIf Version IsNot Nothing AndAlso Version.Version.HasOptiFine Then
|
||||
'OptiFine 版本
|
||||
RamMininum = 0.3
|
||||
RamMininum = 0.5
|
||||
RamTarget1 = 1.5
|
||||
RamTarget2 = 3
|
||||
RamTarget3 = 6
|
||||
RamTarget3 = 5
|
||||
Else
|
||||
'普通版本
|
||||
RamMininum = 0.3
|
||||
RamMininum = 0.5
|
||||
RamTarget1 = 1.5
|
||||
RamTarget2 = 2.5
|
||||
RamTarget3 = 4
|
||||
@@ -317,27 +322,23 @@
|
||||
Dim RamDelta As Double
|
||||
'预分配内存,阶段一,0 ~ T1,100%
|
||||
RamDelta = RamTarget1
|
||||
RamAvailable = Math.Max(0, RamAvailable - 0.1)
|
||||
RamGive += Math.Min(RamAvailable * 1, RamDelta)
|
||||
RamAvailable -= RamDelta / 1 + 0.1
|
||||
RamGive += Math.Min(RamAvailable, RamDelta)
|
||||
RamAvailable -= RamDelta
|
||||
If RamAvailable < 0.1 Then GoTo PreFin
|
||||
'预分配内存,阶段二,T1 ~ T2,80%
|
||||
'预分配内存,阶段二,T1 ~ T2,70%
|
||||
RamDelta = RamTarget2 - RamTarget1
|
||||
RamAvailable = Math.Max(0, RamAvailable - 0.1)
|
||||
RamGive += Math.Min(RamAvailable * 0.8, RamDelta)
|
||||
RamAvailable -= RamDelta / 0.8 + 0.1
|
||||
RamGive += Math.Min(RamAvailable * 0.7, RamDelta)
|
||||
RamAvailable -= RamDelta / 0.7
|
||||
If RamAvailable < 0.1 Then GoTo PreFin
|
||||
'预分配内存,阶段三,T2 ~ T3,60%
|
||||
'预分配内存,阶段三,T2 ~ T3,40%
|
||||
RamDelta = RamTarget3 - RamTarget2
|
||||
RamAvailable = Math.Max(0, RamAvailable - 0.2)
|
||||
RamGive += Math.Min(RamAvailable * 0.6, RamDelta)
|
||||
RamAvailable -= RamDelta / 0.6 + 0.2
|
||||
If RamAvailable < 0.1 Then GoTo PreFin
|
||||
'预分配内存,阶段四,T3 ~ T3 * 2,40%
|
||||
RamDelta = RamTarget3
|
||||
RamAvailable = Math.Max(0, RamAvailable - 0.3)
|
||||
RamGive += Math.Min(RamAvailable * 0.4, RamDelta)
|
||||
RamAvailable -= RamDelta / 0.4 + 0.3
|
||||
RamAvailable -= RamDelta / 0.4
|
||||
If RamAvailable < 0.1 Then GoTo PreFin
|
||||
'预分配内存,阶段四,T3 ~ T3 * 2,15%
|
||||
RamDelta = RamTarget3
|
||||
RamGive += Math.Min(RamAvailable * 0.15, RamDelta)
|
||||
RamAvailable -= RamDelta / 0.15
|
||||
If RamAvailable < 0.1 Then GoTo PreFin
|
||||
PreFin:
|
||||
'不低于最低值
|
||||
@@ -484,6 +485,23 @@ PreFin:
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "其他设置"
|
||||
|
||||
'版本隔离警告
|
||||
Private Sub ComboArgumentIndie_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles ComboArgumentIndie.SelectionChanged
|
||||
If AniControlEnabled <> 0 Then Exit Sub
|
||||
If MyMsgBox("调整版本隔离后,你可能得把游戏存档、Mod 等文件手动迁移到新的游戏文件夹中。" & vbCrLf &
|
||||
"如果修改后发现存档消失,把这项设置改回来就能恢复。" & vbCrLf &
|
||||
"如果你不会迁移存档,不建议修改这项设置!",
|
||||
"警告", "我知道我在做什么", "取消", IsWarn:=True) = 2 Then
|
||||
AniControlEnabled += 1
|
||||
ComboArgumentIndie.SelectedItem = e.RemovedItems(0)
|
||||
AniControlEnabled -= 1
|
||||
End If
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "高级设置"
|
||||
|
||||
Private Sub TextAdvanceRun_TextChanged(sender As Object, e As TextChangedEventArgs) Handles TextAdvanceRun.TextChanged
|
||||
|
||||
@@ -140,6 +140,11 @@
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Imazen.WebP, Version=10.0.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>Resources\Imazen.WebP.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="NAudio, Version=1.7.3.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<Private>False</Private>
|
||||
@@ -160,6 +165,7 @@
|
||||
<Reference Include="System.Management" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System" />
|
||||
@@ -250,8 +256,8 @@
|
||||
<Compile Include="Pages\PageDownload\PageDownloadPack.xaml.vb">
|
||||
<DependentUpon>PageDownloadPack.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Pages\PageLaunch\FormLoginOAuth.xaml.vb">
|
||||
<DependentUpon>FormLoginOAuth.xaml</DependentUpon>
|
||||
<Compile Include="Pages\PageLaunch\MyMsgLogin.xaml.vb">
|
||||
<DependentUpon>MyMsgLogin.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Pages\PageLaunch\PageLoginAuth.xaml.vb">
|
||||
<DependentUpon>PageLoginAuth.xaml</DependentUpon>
|
||||
@@ -505,9 +511,9 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Pages\PageLaunch\FormLoginOAuth.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Page Include="Pages\PageLaunch\MyMsgLogin.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Pages\PageLaunch\PageLoginAuth.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
@@ -881,26 +887,6 @@
|
||||
<ItemGroup>
|
||||
<WCFMetadata Include="Connected Services\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<COMReference Include="NATUPNPLib">
|
||||
<Guid>{1C565858-F302-471E-B409-F180AA4ABEC6}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<COMReference Include="NetFwTypeLib">
|
||||
<Guid>{58FBCF7C-E7A9-467C-80B3-FC65E8FCCA08}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\Blocks\OptiFabric.png" />
|
||||
</ItemGroup>
|
||||
@@ -921,7 +907,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\Icons\Disabled.png" />
|
||||
<Resource Include="Images\Icons\Unavaliable.png" />
|
||||
<Resource Include="Images\Icons\Unavailable.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\Heads\MCBBS.png" />
|
||||
@@ -935,5 +921,9 @@
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\Heads\z0z0r4.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Resources\Imazen.WebP.dll" />
|
||||
<None Include="Resources\libwebp64.dll" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
|
||||
</Project>
|
||||
Binary file not shown.
Binary file not shown.
@@ -3234,7 +3234,7 @@ memory-cleaner-mod|内存自动清理*
|
||||
ore-flowers|矿物花卉*
|
||||
aqua-creepers|水下苦力怕 (Aqua Creepers!)
|
||||
extrastorage|更多存储 (ExtraStorage)
|
||||
thermal-locomotion@thermal-integration|热力运输*
|
||||
thermal-locomotion|热力运输*
|
||||
external-tweaker
|
||||
morebiomesxl
|
||||
notes@|笔记*
|
||||
@@ -3821,7 +3821,7 @@ cat-jammies
|
||||
rhino@|犀牛*
|
||||
fastsuite|配方性能优化 (FastSuite)
|
||||
twerkitmeal|跳舞生长一切 (Twerk To Grow All The Things)
|
||||
thermal-integration@thermal-locomotion|热力集成*
|
||||
thermal-integration|热力集成*
|
||||
piggybanks|存钱罐*
|
||||
in-control@
|
||||
mystical-agriculture-refabricated|神秘农业:Fabric版 (Mystical Agriculture: Refabricated)
|
||||
|
||||
Binary file not shown.
Reference in new issue
Block a user