diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index f2c4927c..fa9c3a7b 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -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
diff --git a/Plain Craft Launcher 2/Application.xaml.vb b/Plain Craft Launcher 2/Application.xaml.vb
index 2ee8be9e..cb4d0301 100644
--- a/Plain Craft Launcher 2/Application.xaml.vb
+++ b/Plain Craft Launcher 2/Application.xaml.vb
@@ -96,23 +96,20 @@ Public Class Application
End If
'动态 DLL 调用
AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf AssemblyResolve
- SetDllDirectory(Path & "PCL\")
- If Is32BitSystem Then
- File.WriteAllBytes(Path & "PCL\libwebp.dll", GetResources("libwebp32"))
- Else
- File.WriteAllBytes(Path & "PCL\libwebp.dll", GetResources("libwebp64"))
- End If
'日志初始化
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")
@@ -177,11 +174,12 @@ Public Class Application
Private Shared AssemblyNAudio As Assembly
Private Shared AssemblyJson As Assembly
Private Shared AssemblyDialog As Assembly
- Private Shared AssemblyWebp 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 AssemblyWebpLock 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
@@ -208,12 +206,14 @@ Public Class Application
Return AssemblyDialog
End SyncLock
ElseIf args.Name.StartsWithF("Imazen.WebP") Then
- SyncLock AssemblyWebpLock
- If AssemblyWebp Is Nothing Then
+ SyncLock AssemblyImazenWebpLock
+ If AssemblyImazenWebp Is Nothing Then
Log("[Start] 加载 DLL:Imazen.WebP")
- AssemblyWebp = Assembly.Load(GetResources("WebP"))
+ AssemblyImazenWebp = Assembly.Load(GetResources("Imazen_WebP"))
+ SetDllDirectory(PathTemp)
+ File.WriteAllBytes(PathTemp & "libwebp.dll", GetResources("libwebp64"))
End If
- Return AssemblyWebp
+ Return AssemblyImazenWebp
End SyncLock
Else
Return Nothing
diff --git a/Plain Craft Launcher 2/Controls/MyIconTextButton.xaml.vb b/Plain Craft Launcher 2/Controls/MyIconTextButton.xaml.vb
index 48a3f1b8..a0940290 100644
--- a/Plain Craft Launcher 2/Controls/MyIconTextButton.xaml.vb
+++ b/Plain Craft Launcher 2/Controls/MyIconTextButton.xaml.vb
@@ -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))
'点击事件
diff --git a/Plain Craft Launcher 2/FormMain.xaml b/Plain Craft Launcher 2/FormMain.xaml
index 2dd9671a..edebd06e 100644
--- a/Plain Craft Launcher 2/FormMain.xaml
+++ b/Plain Craft Launcher 2/FormMain.xaml
@@ -166,7 +166,7 @@
-
+
diff --git a/Plain Craft Launcher 2/FormMain.xaml.vb b/Plain Craft Launcher 2/FormMain.xaml.vb
index e57bc968..a1f9b156 100644
--- a/Plain Craft Launcher 2/FormMain.xaml.vb
+++ b/Plain Craft Launcher 2/FormMain.xaml.vb
@@ -134,6 +134,14 @@ Public Class FormMain
'3:BUG+ IMP* FEAT-
'2:BUG* IMP-
'1:BUG-
+ 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
@@ -432,6 +440,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))
'更改窗口
@@ -1011,18 +1023,19 @@ Install:
''' 把当前窗口拖到最前面。
'''
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
diff --git a/Plain Craft Launcher 2/Images/Icons/Unavaliable.png b/Plain Craft Launcher 2/Images/Icons/Unavailable.png
similarity index 100%
rename from Plain Craft Launcher 2/Images/Icons/Unavaliable.png
rename to Plain Craft Launcher 2/Images/Icons/Unavailable.png
diff --git a/Plain Craft Launcher 2/Modules/Base/ModBase.vb b/Plain Craft Launcher 2/Modules/Base/ModBase.vb
index b57f3a45..74cba6a0 100644
--- a/Plain Craft Launcher 2/Modules/Base/ModBase.vb
+++ b/Plain Craft Launcher 2/Modules/Base/ModBase.vb
@@ -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.6" '不含分支前缀的显示用版本名
- Public Const VersionStandardCode As String = "2.8.6." & VersionBranchCode '标准格式的四段式版本号
+ Public Const VersionBaseName As String = "2.8.7" '不含分支前缀的显示用版本名
+ Public Const VersionStandardCode As String = "2.8.7." & VersionBranchCode '标准格式的四段式版本号
+ Public Const CommitHash As String = "" 'Commit Hash,由 GitHub Workflow 自动替换
#If BETA Then
Public Const VersionCode As Integer = 336 'Release
#Else
- Public Const VersionCode As Integer = 335 'Snapshot
+ Public Const VersionCode As Integer = 337 'Snapshot
#End If
'自动生成的版本信息
Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName
@@ -64,7 +66,7 @@ Public Module ModBase
'''
Public ApplicationOpenTime As Date = Date.Now
'''
- ''' 设备唯一标识符。
+ ''' 识别码。
'''
Public UniqueAddress As String = SecretGetUniqueAddress()
'''
@@ -1240,9 +1242,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
@@ -1261,8 +1269,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
@@ -1444,14 +1451,14 @@ RetryDir:
End Function
'''
- ''' 获取 Json 对象。
+ ''' 获取 JSON 对象。
'''
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
@@ -1855,6 +1862,13 @@ RetryDir:
Return NewProcess.ExitCode
End Function
+ '''
+ ''' 判断当前系统语言是否为中文。
+ '''
+ 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
'''
@@ -2660,7 +2674,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
diff --git a/Plain Craft Launcher 2/Modules/Base/ModLoader.vb b/Plain Craft Launcher 2/Modules/Base/ModLoader.vb
index 05596a29..0e71e588 100644
--- a/Plain Craft Launcher 2/Modules/Base/ModLoader.vb
+++ b/Plain Craft Launcher 2/Modules/Base/ModLoader.vb
@@ -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
@@ -566,7 +567,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 +583,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
diff --git a/Plain Craft Launcher 2/Modules/Base/ModNet.vb b/Plain Craft Launcher 2/Modules/Base/ModNet.vb
index b2775a38..9fa91361 100644
--- a/Plain Craft Launcher 2/Modules/Base/ModNet.vb
+++ b/Plain Craft Launcher 2/Modules/Base/ModNet.vb
@@ -329,7 +329,7 @@ Retry:
'''
''' 同时发送多个网络请求并要求返回内容。
'''
- 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 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, Optional MakeLog As Boolean = True)
Dim Threads As New List(Of Thread)
Dim RequestResult = Nothing
Dim RequestEx As Exception = Nothing
@@ -338,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
@@ -1232,7 +1232,7 @@ SourceBreak:
'根据情况判断,是否在多线程下禁用下载源(连续错误过多,或不支持断点续传)
If ex.Message.Contains("该下载源不支持") OrElse ex.Message.Contains("未能解析") OrElse ex.Message.Contains("(404)") OrElse
ex.Message.Contains("(502)") OrElse ex.Message.Contains("无返回数据") OrElse ex.Message.Contains("空间不足") OrElse ex.Message.Contains("获取到的分段大小不一致") OrElse
- (ex.Message.Contains("(403)") AndAlso Not Info.Source.Url.ContainsF("bmclapi")) OrElse 'BMCLAPI 的部分源在高频率请求下会返回 403,所以不应因此禁用下载源
+ ((ex.Message.Contains("(403)") OrElse ex.Message.Contains("(429)")) AndAlso Not Info.Source.Url.ContainsF("bmclapi")) OrElse 'BMCLAPI 的部分源在高频率请求下会返回 403、429,所以不应因此禁用下载源
(Info.Source.FailCount >= MathClamp(NetTaskThreadLimit, 5, 30) AndAlso DownloadDone < 1) OrElse
Info.Source.FailCount > NetTaskThreadLimit + 2 Then
Dim IsThisFail As Boolean = False
@@ -1344,7 +1344,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
'检查文件
@@ -1654,6 +1654,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:
@@ -1882,7 +1885,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 '下载速度足够,无需新增
@@ -1902,7 +1905,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
@@ -2000,7 +2003,7 @@ Retry:
''' 是否有正在进行中、需要在下载管理页面显示的下载任务?
'''
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
diff --git a/Plain Craft Launcher 2/Modules/Base/MyBitmap.vb b/Plain Craft Launcher 2/Modules/Base/MyBitmap.vb
index 0b074e6d..e60d27a8 100644
--- a/Plain Craft Launcher 2/Modules/Base/MyBitmap.vb
+++ b/Plain Craft Launcher 2/Modules/Base/MyBitmap.vb
@@ -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
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb
index 591f6746..73614ed2 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb
@@ -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
'等待线程结束
@@ -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 列表
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb
index 867b417b..50f77a3e 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb
@@ -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 @@
'''
''' 构造补全某 Minecraft 版本的所有文件的加载器列表。失败会抛出异常。
'''
- 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))("分析资源文件索引地址",
@@ -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 @@
'''
''' 对可能涉及 Mod 镜像源的请求进行处理,返回字符串或 JObject。
- ''' 调用 NetGetCodeByRequestOnce。
+ ''' 调用 NetGetCodeByRequest。
'''
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: 受 #4811 影响
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))
+ 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 @@
'''
''' 对可能涉及 Mod 镜像源的请求进行处理。
- ''' 调用 NetRequestOnce。
+ ''' 调用 NetRequest。
'''
- 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: 受 #4811 影响
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))
+ 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
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb
index 26cd470a..452816c8 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb
@@ -4,6 +4,7 @@ Public Module ModLaunch
#Region "开始"
+ Public CurrentLaunchOptions As McLaunchOptions = Nothing
Public Class McLaunchOptions
'''
''' 强制指定在启动后进入的服务器 IP。
@@ -20,32 +21,34 @@ Public Module ModLaunch
''' 默认值:Nothing。使用 McVersionCurrent。
'''
Public Version As McVersion = Nothing
+ '''
+ ''' 额外的启动参数。
+ '''
+ Public ExtraArgs As New List(Of String)
End Class
'''
''' 尝试启动 Minecraft。必须在 UI 线程调用。
''' 返回是否实际开始了启动(如果没有,则一定弹出了错误提示)。
'''
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
'正式加载
@@ -104,7 +107,7 @@ 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},
+ 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")
-#End If
- '正版购买提示
- If Not Setup.Get("HintBuy") AndAlso Setup.Get("LoginType") <> McLoginType.Ms Then
+ RunInNewThread(
+ Sub()
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")
+ 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
+ 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,58 +501,56 @@ 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
End If
'尝试登录
Dim OAuthTokens As String()
- Dim ClientId As String = ""
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, ClientId)
+ OAuthTokens = MsLoginStep1New(Data)
Else
'有 RefreshToken
- OAuthTokens = MsLoginStep2(Input.OAuthRefreshToken, True, ClientId)
+ 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)
@@ -550,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))
@@ -821,179 +833,61 @@ 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("$$")
- Else
- Return RegexSeek(Result, "(?<=code\=)[^&]+")
- End If
- End Function
+ '微软登录步骤 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
+
+ '初始请求
+ McLaunchLog("开始微软登录步骤 1/6(原始登录)")
+ Dim PrepareJson As JObject = GetJson(NetRequestMulty("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", 2))
+ McLaunchLog("网页登录地址:" & PrepareJson("verification_uri").ToString)
- '微软登录步骤 2:从 OAuth Code 或 OAuth RefreshToken 获取 {OAuth AccessToken, OAuth RefreshToken}
- Private Function MsLoginStep2(Code As String, IsRefresh As Boolean, ClientId As String, 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=" & ClientId & "&" &
- "device_code=" & Code & "&" &
- "refresh_token=" & Uri.EscapeDataString(Code) & "&" &
- "scope=XboxLive.signin%20offline_access"
+ '弹窗
+ 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 Exception Then
+ Throw CType(Converter.Result, Exception)
Else
- Request = "grant_type=urn:ietf:params:oauth:grant-type:device_code" & "&" &
- "client_id=" & ClientId & "&" &
- "device_code=" & Code & "&" &
- "scope=XboxLive.signin%20offline_access"
+ Return Converter.Result
End If
- Dim Result As String
- Dim stopwatch As Stopwatch = Stopwatch.StartNew()
+ End Function
- 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
+ '微软登录步骤 1,刷新登录:从 OAuth Code 或 OAuth RefreshToken 获取 {OAuth AccessToken, OAuth RefreshToken}
+ Private Function MsLoginStep1Refresh(Code As String) As String()
+ McLaunchLog("开始微软登录步骤 1/6(刷新登录)")
- 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}
+ Dim Result As String
+ Try
+ Result = NetRequestMulty("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
+ Return {"Relogin", ""}
+ Else
+ Throw
End If
+ End Try
- Thread.Sleep(1000)
- End While
-
- Hint("验证超时,请尝试重新验证!")
- Return {"Cancel", ""}
-
+ 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 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""
@@ -1004,9 +898,9 @@ LoginFinish:
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"": {
@@ -1056,9 +950,9 @@ 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
@@ -1081,9 +975,9 @@ 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}})
Try
@@ -1100,9 +994,9 @@ 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
@@ -1447,8 +1341,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
@@ -2148,18 +2046,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
'执行自定义命令
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb
index abc4a6bd..957e2e2d 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb
@@ -475,22 +475,27 @@ VersionSearchFinish:
'''
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
'''
''' 将 McLibToken 列表转换为 NetFile。无需下载的文件会被自动过滤。
'''
- 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:
'''
Public Size As Long
'''
- ''' 是否为 Virtual 资源文件。
- '''
- Public IsVirtual As Boolean
- '''
''' 文件的 Hash 校验码。
'''
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
'''
''' 获取 Minecraft 的资源文件列表。失败会抛出异常。
'''
- ''' 版本的资源名称,如“1.13.1”。
- 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"))
-
- '确认 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
- End If
- End If
+ 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"))
- '加载列表
- 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
+ '读取列表
+ 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
+ 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:
'''
''' 获取版本缺失的资源文件所对应的 NetTaskFile。
'''
- 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
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb
index 0c3ea340..1eb713b2 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb
@@ -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
@@ -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
@@ -1131,7 +1129,7 @@ Finished:
Log($"[Mod] 联网获取本地 Mod 信息完成,为 {Mods.Count} 个 Mod 更新缓存")
If Not Mods.Any() Then Exit Sub
For Each Entry In Mods
- Entry.CompLoaded = SucceedThreadCount = 2
+ 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)))
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb
index 9d16fd27..ce900eae 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb
@@ -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
'强制关闭
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/MyCompItem.xaml.vb b/Plain Craft Launcher 2/Modules/Minecraft/MyCompItem.xaml.vb
index ee52b361..f1fce69b 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/MyCompItem.xaml.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/MyCompItem.xaml.vb
@@ -55,12 +55,6 @@ RetryStart:
End If
'下载图片
NetDownload(Url, LocalFileAddress & DownloadEnd, True)
- If Url.EndsWithF("webp") Then
- Log($"[Comp] Webp 格式转换:{LocalFileAddress}")
- Dim dec = New Imazen.WebP.SimpleDecoder()
- Dim picFile = File.ReadAllBytes(LocalFileAddress & DownloadEnd)
- dec.DecodeFromBytes(picFile, picFile.Length).Save(LocalFileAddress & DownloadEnd)
- End If
Dim LoadError As Exception = Nothing
RunInUiWait(
Sub()
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/MyLocalModItem.xaml b/Plain Craft Launcher 2/Modules/Minecraft/MyLocalModItem.xaml
index a64f249c..ce687e89 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/MyLocalModItem.xaml
+++ b/Plain Craft Launcher 2/Modules/Minecraft/MyLocalModItem.xaml
@@ -27,27 +27,24 @@
-
+
+
+
+
+
+
+
-
-
-
-
+