From d886acdeac6ec0bc326817bda4b0633343aaf6fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=99=E8=85=BE=E7=8C=AB=E8=B7=83?= <1043137532@qq.com> Date: Wed, 8 Mar 2023 23:34:10 +0800 Subject: [PATCH] 2.5.1 --- Plain Craft Launcher 2/Application.xaml.vb | 10 +- Plain Craft Launcher 2/Controls/MyCard.vb | 18 +- .../Controls/MyMsg/MyMsgText.xaml.vb | 30 +- .../Controls/MyPageRight.vb | 2 +- Plain Craft Launcher 2/Controls/MyTextBox.vb | 34 +- Plain Craft Launcher 2/FormMain.xaml.vb | 54 ++- .../Modules/Base/ModBase.vb | 60 ++- Plain Craft Launcher 2/Modules/Base/ModNet.vb | 2 +- .../Modules/Base/MyBitmap.vb | 36 +- .../Modules/Minecraft/ModComp.vb | 62 ++-- .../Modules/Minecraft/ModCrash.vb | 349 +++++++++--------- .../Modules/Minecraft/ModDownload.vb | 12 +- .../Modules/Minecraft/ModLaunch.vb | 112 ++++-- .../Modules/Minecraft/ModMinecraft.vb | 138 ++++--- .../Modules/Minecraft/ModModpack.vb | 10 +- .../Modules/Minecraft/ModWatcher.vb | 2 +- Plain Craft Launcher 2/Modules/ModEvent.vb | 5 +- Plain Craft Launcher 2/Modules/ModMusic.vb | 4 +- .../My Project/AssemblyInfo.vb | 4 +- .../Pages/PageDownload/ModDownloadLib.vb | 73 ++-- .../PageDownload/PageDownloadClient.xaml | 2 +- .../PageDownloadCompDetail.xaml.vb | 4 +- .../PageDownload/PageDownloadInstall.xaml | 12 +- .../PageDownload/PageDownloadInstall.xaml.vb | 19 +- .../PageDownload/PageDownloadLeft.xaml.vb | 2 +- .../Pages/PageDownload/PageDownloadMod.xaml | 21 +- .../PageDownload/PageDownloadMod.xaml.vb | 29 +- .../Pages/PageDownload/PageDownloadPack.xaml | 17 +- .../PageDownload/PageDownloadPack.xaml.vb | 21 +- .../Pages/PageLaunch/FormLoginOAuth.xaml.vb | 2 +- .../Pages/PageLaunch/PageLaunchLeft.xaml.vb | 4 +- .../Pages/PageLaunch/PageLaunchRight.xaml.vb | 4 +- .../Pages/PageLaunch/PageLoginLegacy.xaml | 9 +- .../Pages/PageLaunch/PageLoginLegacy.xaml.vb | 12 +- .../PageLaunch/PageLoginMojangSkin.xaml.vb | 2 +- .../Pages/PageLaunch/PageLoginMsSkin.xaml.vb | 2 +- .../Pages/PageLink/PageLinkFeedback.xaml | 6 +- .../Pages/PageLink/PageLinkHiper.xaml.vb | 9 +- .../Pages/PageLink/PageLinkIoi.xaml.vb | 20 +- .../Pages/PageSelectRight.xaml.vb | 15 +- .../Pages/PageSetup/PageSetupLaunch.xaml | 17 +- .../Pages/PageSetup/PageSetupLaunch.xaml.vb | 71 ++-- .../Pages/PageSetup/PageSetupSystem.xaml | 4 +- .../Pages/PageSetup/PageSetupSystem.xaml.vb | 59 --- .../Pages/PageVersion/PageVersionMod.xaml | 2 +- .../Pages/PageVersion/PageVersionMod.xaml.vb | 13 +- .../PageVersion/PageVersionOverall.xaml.vb | 23 +- .../Pages/PageVersion/PageVersionSetup.xaml | 8 +- Plain Craft Launcher 2/Resources/Custom.xaml | 14 +- Plain Craft Launcher 2/Resources/Help.zip | Bin 70164 -> 69754 bytes 50 files changed, 781 insertions(+), 659 deletions(-) diff --git a/Plain Craft Launcher 2/Application.xaml.vb b/Plain Craft Launcher 2/Application.xaml.vb index cb326f9..b5bb048 100644 --- a/Plain Craft Launcher 2/Application.xaml.vb +++ b/Plain Craft Launcher 2/Application.xaml.vb @@ -47,9 +47,9 @@ Public Class Application Directory.CreateDirectory(Path & "PCL\Musics") Try Directory.CreateDirectory(PathTemp) - If Not CheckPermission(PathTemp) Then Throw New Exception("PCL2 没有对 " & PathTemp & " 的访问权限") + If Not CheckPermission(PathTemp) Then Throw New Exception("PCL 没有对 " & PathTemp & " 的访问权限") Catch ex As Exception - MyMsgBox("手动设置的缓存文件夹不可用,PCL2 将使用默认缓存文件夹。" & vbCrLf & "错误原因:" & GetExceptionDetail(ex), "缓存文件夹不可用") + MyMsgBox("手动设置的缓存文件夹不可用,PCL 将使用默认缓存文件夹。" & vbCrLf & "错误原因:" & GetExceptionDetail(ex), "缓存文件夹不可用") Setup.Set("SystemSystemCache", "") PathTemp = IO.Path.GetTempPath() & "PCL\" End Try @@ -59,7 +59,7 @@ Public Class Application #If Not DEBUG Then Dim WindowHwnd As IntPtr = FindWindow(Nothing, "Plain Craft Launcher 2 ") If WindowHwnd <> IntPtr.Zero Then - '将已有的 PCL2 窗口拖出来 + '将已有的 PCL 窗口拖出来 ShowWindowToTop(WindowHwnd) '播放提示音并退出 Beep() @@ -82,7 +82,7 @@ Public Class Application Log("[Start] 程序路径:" & PathWithName) '检测压缩包运行 If Path.Contains(IO.Path.GetTempPath()) OrElse Path.Contains("AppData\Local\Temp\") Then - MyMsgBox("PCL2 正在临时文件夹运行,设置、游戏存档等很可能无法保存,且部分功能会无法使用或出错。" & vbCrLf & "请将 PCL2 从压缩文件中解压,或是更换文件夹后再继续使用!", "环境警告", "我知道了", IsWarn:=True) + MyMsgBox("PCL 正在临时文件夹运行,设置、游戏存档等很可能无法保存,且部分功能会无法使用或出错。" & vbCrLf & "请将 PCL 从压缩文件中解压,或是更换文件夹后再继续使用!", "环境警告", "我知道了", IsWarn:=True) End If '设置初始化 Setup.Load("SystemDebugMode") @@ -104,7 +104,7 @@ Public Class Application #End If AniControlEnabled += 1 Catch ex As Exception - MsgBox(GetExceptionDetail(ex, True), MsgBoxStyle.Critical, "PCL2 初始化错误") + MsgBox(GetExceptionDetail(ex, True), MsgBoxStyle.Critical, "PCL 初始化错误") FormMain.EndProgramForce(Result.Exception) End Try End Sub diff --git a/Plain Craft Launcher 2/Controls/MyCard.vb b/Plain Craft Launcher 2/Controls/MyCard.vb index cfd6e48..613d747 100644 --- a/Plain Craft Launcher 2/Controls/MyCard.vb +++ b/Plain Craft Launcher 2/Controls/MyCard.vb @@ -134,9 +134,23 @@ FrmDownloadInstall.MinecraftSelected(sender, e) End Sub, False)) Case 8 - Stack.Children.Add(CType(Data, CompFile).ToListItem(AddressOf FrmDownloadCompDetail.Save_Click)) + If Distinct(CType(Stack.Tag, List(Of CompFile)), Function(a, b) a.DisplayName = b.DisplayName).Count <> + CType(Stack.Tag, List(Of CompFile)).Count Then + '存在重复的名称(#1344) + Stack.Children.Add(CType(Data, CompFile).ToListItem(AddressOf FrmDownloadCompDetail.Save_Click, BadDisplayName:=True)) + Else + '不存在重复的名称,正常加载 + Stack.Children.Add(CType(Data, CompFile).ToListItem(AddressOf FrmDownloadCompDetail.Save_Click)) + End If Case 9 - Stack.Children.Add(CType(Data, CompFile).ToListItem(AddressOf FrmDownloadCompDetail.Install_Click, AddressOf FrmDownloadCompDetail.Save_Click)) + If Distinct(CType(Stack.Tag, List(Of CompFile)), Function(a, b) a.DisplayName = b.DisplayName).Count <> + CType(Stack.Tag, List(Of CompFile)).Count Then + '存在重复的名称(#1344) + Stack.Children.Add(CType(Data, CompFile).ToListItem(AddressOf FrmDownloadCompDetail.Install_Click, AddressOf FrmDownloadCompDetail.Save_Click, BadDisplayName:=True)) + Else + '不存在重复的名称,正常加载 + Stack.Children.Add(CType(Data, CompFile).ToListItem(AddressOf FrmDownloadCompDetail.Install_Click, AddressOf FrmDownloadCompDetail.Save_Click)) + End If Case 10 Stack.Children.Add(LiteLoaderDownloadListItem(Data, AddressOf LiteLoaderSave_Click, True)) Case 11 diff --git a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml.vb b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml.vb index fce2c8d..f3c6e27 100644 --- a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml.vb +++ b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml.vb @@ -70,21 +70,33 @@ Public Sub Btn1_Click() Handles Btn1.Click If MyConverter.IsExited Then Exit Sub - MyConverter.IsExited = True - MyConverter.Result = 1 - Close() + If MyConverter.Button1Action IsNot Nothing Then + MyConverter.Button1Action() + Else + MyConverter.IsExited = True + MyConverter.Result = 1 + Close() + End If End Sub Public Sub Btn2_Click() Handles Btn2.Click If MyConverter.IsExited Then Exit Sub - MyConverter.IsExited = True - MyConverter.Result = 2 - Close() + If MyConverter.Button2Action IsNot Nothing Then + MyConverter.Button2Action() + Else + MyConverter.IsExited = True + MyConverter.Result = 2 + Close() + End If End Sub Public Sub Btn3_Click() Handles Btn3.Click If MyConverter.IsExited Then Exit Sub - MyConverter.IsExited = True - MyConverter.Result = 3 - Close() + If MyConverter.Button3Action IsNot Nothing Then + MyConverter.Button3Action() + Else + MyConverter.IsExited = True + MyConverter.Result = 3 + Close() + End If End Sub Private Sub Drag(sender As Object, e As MouseButtonEventArgs) Handles PanBorder.MouseLeftButtonDown, LabTitle.MouseLeftButtonDown diff --git a/Plain Craft Launcher 2/Controls/MyPageRight.vb b/Plain Craft Launcher 2/Controls/MyPageRight.vb index ab6c666..32a7ba3 100644 --- a/Plain Craft Launcher 2/Controls/MyPageRight.vb +++ b/Plain Craft Launcher 2/Controls/MyPageRight.vb @@ -65,7 +65,7 @@ Sub() Do While PageState = MyPageRight.PageStates.PageExit OrElse PageState = MyPageRight.PageStates.ContentExit Thread.Sleep(10) '不在退出动画时执行 UI 线程操作,避免退出动画被重置 - Loop + Loop RunInUiWait(Sub() FinishedInvoke(RealLoader)) Thread.Sleep(20) '由于大量初始化控件会导致掉帧,延迟触发 State 改变事件 End Sub diff --git a/Plain Craft Launcher 2/Controls/MyTextBox.vb b/Plain Craft Launcher 2/Controls/MyTextBox.vb index cc0d35b..d0c6f44 100644 --- a/Plain Craft Launcher 2/Controls/MyTextBox.vb +++ b/Plain Craft Launcher 2/Controls/MyTextBox.vb @@ -86,11 +86,11 @@ ValidateResult = ValidateRule.Validate(Text) If IsNothing(ValidateResult) OrElse Not ValidateResult = "" Then Exit For Next - Dim NewResult As Integer = If(ValidateResult = "", 0, 1) + Dim NewResult As Boolean = ValidateResult = "" '根据结果改变样式 - If ShownValidateResult <> NewResult Then + If ShownValidateResult <> If(NewResult, 0, 1) Then If IsLoaded AndAlso labWrong IsNot Nothing Then - ChangeValidateResult(ValidateResult = "", True) + ChangeValidateResult(NewResult, True) Else RunInNewThread(Sub() Thread.Sleep(30) @@ -119,10 +119,17 @@ End If End If End Sub + ''' + ''' 强制显示结果为正常,类似尚未输入过文本的状态。不影响实际的检查结果。 + ''' + Public Sub ForceShowAsSuccess() + IsTextChanged = False + ChangeValidateResult(ValidateResult = "", True) + End Sub Private ShownValidateResult As Integer = -1 Private Sub ChangeValidateResult(NewResult As Boolean, IsLoaded As Boolean) - If IsLoaded AndAlso AniControlEnabled = 0 AndAlso IsTextChanged AndAlso labWrong IsNot Nothing Then - If NewResult Then + If IsLoaded AndAlso AniControlEnabled = 0 AndAlso labWrong IsNot Nothing Then + If NewResult OrElse Not IsTextChanged Then '变为正确 ShownValidateResult = 0 AniStart({ @@ -144,23 +151,6 @@ End If Else ShownValidateResult = 3 - 'AniStop("MyTextBox Validate " & Uuid) - 'If NewResult Then - ' '变为正确 - ' labWrong.Opacity = 0 - ' labWrong.Height = 0 - ' labWrong.Visibility = Visibility.Collapsed - 'Else - ' '变为错误 - ' labWrong.Visibility = Visibility.Visible - ' labWrong.Text = ValidateResult - ' labWrong.Opacity = 1 - ' labWrong.Height = 20.5 - 'End If - 'Dim OriginalEnabled As Integer = AniControlEnabled - 'AniControlEnabled += 1 - 'RefreshColor() - 'AniControlEnabled = OriginalEnabled End If RefreshColor() RaiseEvent ValidateChanged(Me, New EventArgs) diff --git a/Plain Craft Launcher 2/FormMain.xaml.vb b/Plain Craft Launcher 2/FormMain.xaml.vb index 58a9526..c8d25ce 100644 --- a/Plain Craft Launcher 2/FormMain.xaml.vb +++ b/Plain Craft Launcher 2/FormMain.xaml.vb @@ -108,7 +108,7 @@ Public Class FormMain End If If LastVersion < 233 Then 'Release 2.2.3 FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "微软账号支持更换皮肤与披风")) - FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "支持拖拽整合包文件到 PCL2 进行快捷安装")) + FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "支持拖拽整合包文件到 PCL 进行快捷安装")) FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "Mod 下载支持显示对应的 MC 版本与 Mod 加载器,且可以跳转到 MCBBS 介绍页")) FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "支持安装 1.17 OptiFine + Forge")) FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "使用 Mojang 账号登录将会显示迁移提醒")) @@ -126,6 +126,12 @@ Public Class FormMain '3:BUG+ IMP* FEAT- '2:BUG* IMP- '1:BUG- + If LastVersion < 284 Then 'Snapshot 2.5.1 + If LastVersion >= 275 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "在全局启动设置中添加了 启动前执行命令 选项")) + FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "在选择下载 Fabric 时会自动选择 Fabric API")) + FeatureCount += 22 + BugCount += 21 + End If If LastVersion < 283 Then 'Snapshot 2.5.0 FeatureList.Add(New KeyValuePair(Of Integer, String)(5, "支持搜索、下载 Modrinth 中的 Mod 与整合包")) FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "资源搜索页面支持翻页")) @@ -146,10 +152,6 @@ Public Class FormMain If LastVersion = 277 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复设置背景音乐可能导致无法启动 PCL 的 Bug")) BugCount += 2 End If - If LastVersion < 278 Then 'Snapshot 2.4.7 - If LastVersion = 277 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复设置背景音乐可能导致无法启动 PCL 的 Bug")) - BugCount += 2 - End If If LastVersion < 277 Then 'Snapshot 2.4.6 FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "在百宝箱添加了清理 MC 日志、崩溃报告的按钮")) FeatureCount += 8 @@ -284,7 +286,7 @@ Public Class FormMain BugCount += 19 End If If LastVersion < 234 Then 'Snapshot 2.2.3 - FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "支持拖拽整合包文件到 PCL2 进行快捷安装")) + FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "支持拖拽整合包文件到 PCL 进行快捷安装")) FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "支持安装 1.17 OptiFine + Forge")) FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "Mod 详情页面支持跳转 MCBBS 介绍页")) FeatureCount += 10 @@ -329,7 +331,7 @@ Public Class FormMain Dim Content As String = "· " & Join(ContentList, vbCrLf & "· ") '输出更新日志 RunInNewThread(Sub() - If MyMsgBox(Content, "PCL2 已更新至 " & VersionDisplayName, "确定", "完整更新日志") = 2 Then + If MyMsgBox(Content, "PCL 已更新至 " & VersionDisplayName, "确定", "完整更新日志") = 2 Then OpenWebsite("https://afdian.net/a/LTCat?tab=feed") End If End Sub, "UpdateLog Output") @@ -443,15 +445,12 @@ Public Class FormMain '加载池 RunInNewThread(Sub() If Not Setup.Get("SystemEula") Then -Reopen: - Select Case MyMsgBox("在使用 PCL2 前,请同意 PCL2 的用户协议与免责声明。", "协议授权", "同意", "拒绝", "查看用户协议与免责声明") + Select Case MyMsgBox("在使用 PCL 前,请同意 PCL 的用户协议与免责声明。", "协议授权", "同意", "拒绝", "查看用户协议与免责声明", + Button3Action:=Sub() OpenWebsite("https://shimo.im/docs/rGrd8pY8xWkt6ryW")) Case 1 Setup.Set("SystemEula", True) Case 2 EndProgram(False) - Case 3 - OpenWebsite("https://shimo.im/docs/rGrd8pY8xWkt6ryW") - GoTo Reopen End Select End If Try @@ -478,13 +477,13 @@ Reopen: #If Not BETA Then Select Case Setup.Get("SystemCount") Case 1 - MyMsgBox("欢迎使用 PCL2 快照版!" & vbCrLf & + MyMsgBox("欢迎使用 PCL 快照版!" & vbCrLf & "快照版包含尚未在正式版发布的测试性功能,仅用于赞助者本人尝鲜。所以请不要发给其他人或者用于制作整合包哦!" & vbCrLf & "如果你并非通过赞助或赞助者本人邀请进群获得的本程序,那么可能是有人在违规传播,记得提醒他一下啦。", "快照版使用说明") End Select If Setup.Get("SystemCount") >= 99 Then If ThemeUnlock(6, False) Then - MyMsgBox("你已经使用了 99 次 PCL2 啦,感谢你长期以来的支持!" & vbCrLf & + MyMsgBox("你已经使用了 99 次 PCL 啦,感谢你长期以来的支持!" & vbCrLf & "隐藏主题 铁杆粉 已解锁!", "提示") End If End If @@ -521,7 +520,7 @@ Reopen: Dim UnlockedTheme As New List(Of String)(Setup.Get("UiLauncherThemeHide2").ToString.Split("|")) UnlockedTheme.Remove("13") Setup.Set("UiLauncherThemeHide2", Join(UnlockedTheme, "|")) - MyMsgBox("由于新版 PCL2 修改了欧皇彩的解锁方式,你需要重新解锁欧皇彩。" & vbCrLf & + MyMsgBox("由于新版 PCL 修改了欧皇彩的解锁方式,你需要重新解锁欧皇彩。" & vbCrLf & "多谢各位的理解啦!", "重新解锁提醒") End If '重置滑稽彩 @@ -529,7 +528,7 @@ Reopen: Dim UnlockedTheme As New List(Of String)(Setup.Get("UiLauncherThemeHide2").ToString.Split("|")) UnlockedTheme.Remove("12") Setup.Set("UiLauncherThemeHide2", Join(UnlockedTheme, "|")) - MyMsgBox("由于新版 PCL2 修改了滑稽彩的解锁方式,你需要重新解锁滑稽彩。" & vbCrLf & + MyMsgBox("由于新版 PCL 修改了滑稽彩的解锁方式,你需要重新解锁滑稽彩。" & vbCrLf & "多谢各位的理解啦!", "重新解锁提醒") End If '移动自定义皮肤 @@ -838,8 +837,23 @@ Reopen: Log("[System] 接受文件拖拽:" & FilePath, LogLevel.Developer) RunInNewThread(Sub() Dim Extension As String = FilePath.Split(".").Last.ToLower + '自定义主页 + If Extension = "xaml" Then + Log("[System] 文件后缀为 XAML,作为自定义主页加载") + If File.Exists(Path & "PCL\Custom.xaml") Then + If MyMsgBox("已存在一个自定义主页文件,是否要将它覆盖?", "覆盖确认", "覆盖", "取消") = 2 Then + Exit Sub + End If + End If + CopyFile(FilePath, Path & "PCL\Custom.xaml") + RunInUi(Sub() + Setup.Set("UiCustomType", 1) + FrmLaunchRight.ForceRefresh() + End Sub) + Exit Sub + End If 'Mod 安装 - If {"jar", "litemod", "disabled"}.Contains(Extension) Then + If {"jar", "litemod", "disabled"}.Any(Function(t) t = Extension) Then Log("[System] 文件为 jar/litemod 格式,尝试作为 Mod 安装") '获取并检查目标版本 Dim TargetVersion As McVersion = McVersionCurrent @@ -873,13 +887,13 @@ Install: Exit Sub End If '安装整合包 - If {"zip", "rar", "mrpack"}.Contains(Extension) Then '部分压缩包是 zip 格式但后缀为 rar,总之试一试 + If {"zip", "rar", "mrpack"}.Any(Function(t) t = Extension) Then '部分压缩包是 zip 格式但后缀为 rar,总之试一试 Log("[System] 文件为压缩包,尝试作为整合包安装") If ModpackInstall(FilePath, ShowHint:=False) Then Exit Sub End If 'RAR 处理 If Extension = "rar" Then - Hint("PCL2 无法处理 rar 格式的压缩包,请在解压后重新压缩为 zip 格式再试!") + Hint("PCL 无法处理 rar 格式的压缩包,请在解压后重新压缩为 zip 格式再试!") Exit Sub End If '错误报告分析 @@ -895,7 +909,7 @@ Install: Log(ex, "自主错误报告分析失败", LogLevel.Feedback) End Try '未知操作 - Hint("PCL2 无法确定应当执行的文件拖拽操作……") + Hint("PCL 无法确定应当执行的文件拖拽操作……") End Sub, "文件拖拽") End If Catch ex As Exception diff --git a/Plain Craft Launcher 2/Modules/Base/ModBase.vb b/Plain Craft Launcher 2/Modules/Base/ModBase.vb index a98dad5..213a276 100644 --- a/Plain Craft Launcher 2/Modules/Base/ModBase.vb +++ b/Plain Craft Launcher 2/Modules/Base/ModBase.vb @@ -10,12 +10,12 @@ Public Module ModBase #Region "声明" '下列版本信息由更新器自动修改 - Public Const VersionBaseName As String = "2.5.0" '不含分支前缀的显示用版本名 - Public Const VersionStandardCode As String = "2.5.0." & VersionBranchCode '标准格式的四段式版本号 + Public Const VersionBaseName As String = "2.5.1" '不含分支前缀的显示用版本名 + Public Const VersionStandardCode As String = "2.5.1." & VersionBranchCode '标准格式的四段式版本号 #If BETA Then Public Const VersionCode As Integer = 281 'Release #Else - Public Const VersionCode As Integer = 283 'Snapshot + Public Const VersionCode As Integer = 284 'Snapshot #End If '自动生成的版本信息 Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName @@ -778,14 +778,10 @@ Public Module ModBase If Not ToPath.Contains(":\") Then ToPath = Path & ToPath '如果复制同一个文件则跳过 If FromPath = ToPath Then Exit Sub - '读取文件内容 - Dim FileBytes As Byte() - Using ReadStream As New FileStream(FromPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) '支持读取使用中的文件 - ReDim FileBytes(ReadStream.Length - 1) - ReadStream.Read(FileBytes, 0, ReadStream.Length) - End Using - '写入文件内容 - WriteFile(ToPath, FileBytes) + '确保目录存在 + Directory.CreateDirectory(GetPathFromFullPath(ToPath)) + '复制文件 + File.Copy(FromPath, ToPath, True) Catch ex As Exception Throw New Exception("复制文件出错:" & FromPath & " -> " & ToPath, ex) End Try @@ -1222,7 +1218,7 @@ Re: Try GetJson(Content) Catch ex As Exception - Throw New Exception("不是有效的 Json 文件", ex) + Throw New Exception("不是有效的 json 文件", ex) End Try End If Return Nothing @@ -1355,14 +1351,14 @@ Re: Dim Desc As String = Join(DescList, vbCrLf & "→ ") Dim Stack As String = If(StackList.Count > 0, vbCrLf & Join(StackList, vbCrLf), "") - '常见错误 + '常见错误(记得同时修改下面的) Dim CommonReason As String = Nothing If TypeOf InnerEx Is TypeLoadException OrElse TypeOf InnerEx Is MissingMethodException OrElse TypeOf InnerEx Is NotImplementedException OrElse TypeOf InnerEx Is TypeInitializationException Then - CommonReason = "PCL2 的运行环境存在问题。请尝试重新安装 .NET Framework 4.6.2 然后再试。" + CommonReason = "PCL 的运行环境存在问题。请尝试重新安装 .NET Framework 4.6.2 然后再试。" ElseIf TypeOf InnerEx Is UnauthorizedAccessException Then - CommonReason = "PCL2 的权限不足。请尝试右键 PCL2,选择以管理员身份运行。" + CommonReason = "PCL 的权限不足。请尝试右键 PCL,选择以管理员身份运行。" ElseIf TypeOf InnerEx Is OutOfMemoryException Then - CommonReason = "你的电脑运行内存不足,导致 PCL2 无法继续运行。请在关闭一部分不需要的程序后再试。" + CommonReason = "你的电脑运行内存不足,导致 PCL 无法继续运行。请在关闭一部分不需要的程序后再试。" ElseIf {"远程主机强迫关闭了", "远程方已关闭传输流", "未能解析此远程名称", "由于目标计算机积极拒绝", "操作已超时", "操作超时", "服务器超时", "连接超时"}.Any(Function(s) Desc.Contains(s)) Then CommonReason = "你的网络环境不佳,导致难以连接到服务器。请检查网络,多重试几次,或尝试使用 VPN。" @@ -1399,18 +1395,19 @@ Re: Ex = Ex.InnerException Loop DescList = DescList.Distinct.ToList - - '常见错误 Dim Desc As String = Join(DescList, vbCrLf & "→ ") + + '常见错误(记得同时修改上面的) Dim CommonReason As String = Nothing If TypeOf InnerEx Is TypeLoadException OrElse TypeOf InnerEx Is MissingMethodException OrElse TypeOf InnerEx Is NotImplementedException OrElse TypeOf InnerEx Is TypeInitializationException Then - CommonReason = "PCL2 的运行环境存在问题。请尝试重新安装 .NET Framework 4.6.2 然后再试。" + CommonReason = "PCL 的运行环境存在问题。请尝试重新安装 .NET Framework 4.6.2 然后再试。" ElseIf TypeOf InnerEx Is UnauthorizedAccessException Then - CommonReason = "PCL2 的权限不足。请尝试右键 PCL2,选择以管理员身份运行。" + CommonReason = "PCL 的权限不足。请尝试右键 PCL,选择以管理员身份运行。" ElseIf TypeOf InnerEx Is OutOfMemoryException Then - CommonReason = "你的电脑运行内存不足,导致 PCL2 无法继续运行。请在关闭一部分不需要的程序后再试。" - ElseIf {"远程主机强迫关闭了", "远程方已关闭传输流", "操作已超时", "操作超时", "服务器超时", "连接超时"}.Any(Function(s) Desc.Contains(s)) Then - CommonReason = "你的网络环境不佳,导致难以连接到服务器。请重试,或尝试使用 VPN。" + CommonReason = "你的电脑运行内存不足,导致 PCL 无法继续运行。请在关闭一部分不需要的程序后再试。" + ElseIf {"远程主机强迫关闭了", "远程方已关闭传输流", "未能解析此远程名称", "由于目标计算机积极拒绝", + "操作已超时", "操作超时", "服务器超时", "连接超时"}.Any(Function(s) Desc.Contains(s)) Then + CommonReason = "你的网络环境不佳,导致难以连接到服务器。请检查网络,多重试几次,或尝试使用 VPN。" End If '构造输出信息 @@ -1459,7 +1456,7 @@ Re: Try Return JsonConvert.DeserializeObject(Data, New JsonSerializerSettings With {.DateTimeZoneHandling = DateTimeZoneHandling.Local}) Catch ex As Exception - Throw New Exception("格式化 Json 对象失败:" & If(If(Data, "").Length > 10000, Data.Substring(0, 100) & "...", Data)) + Throw New Exception("格式化 json 对象失败:" & If(If(Data, "").Length > 10000, Data.Substring(0, 100) & "...", Data)) End Try End Function @@ -2042,7 +2039,8 @@ NextElement: Catch ex As Exception Log(ex, "无法打开网页(" & Url & ")") ClipboardSet(Url, False) - MyMsgBox("可能由于浏览器未正确配置,PCL2 无法为你打开网页。" & vbCrLf & "网址已经复制到剪贴板,若有需要可以手动粘贴访问。", "无法打开网页") + MyMsgBox("可能由于浏览器未正确配置,PCL 无法为你打开网页。" & vbCrLf & "网址已经复制到剪贴板,若有需要可以手动粘贴访问。" & vbCrLf & + $"网址:{Url}", "无法打开网页") End Try End Sub ''' @@ -2299,7 +2297,7 @@ Retry: File.Create(Path & "PCL\Log1.txt").Dispose() Catch ex As IOException IsInitSuccess = False - Hint("可能同时开启了多个 PCL2,程序可能会出现未知问题!", HintType.Critical) + Hint("可能同时开启了多个 PCL,程序可能会出现未知问题!", HintType.Critical) Log(ex, "日志初始化失败(疑似文件占用问题)") Catch ex As Exception IsInitSuccess = False @@ -2386,14 +2384,14 @@ Retry: If CanFeedback(False) Then If MyMsgBox(Text & vbCrLf & vbCrLf & "是否反馈此问题?如果不反馈,这个问题可能永远无法得到解决!", Title, "反馈", "取消", IsWarn:=True) = 1 Then Feedback(False, True) Else - MyMsgBox(Text & vbCrLf & vbCrLf & "将 PCL2 更新至最新版或许可以解决这个问题……", Title, IsWarn:=True) + MyMsgBox(Text & vbCrLf & vbCrLf & "将 PCL 更新至最新版或许可以解决这个问题……", Title, IsWarn:=True) End If Case LogLevel.Assert Dim Time As Long = GetTimeTick() If CanFeedback(False) Then If MsgBox(Text & vbCrLf & vbCrLf & "是否反馈此问题?如果不反馈,这个问题可能永远无法得到解决!", MsgBoxStyle.Critical + MsgBoxStyle.YesNo, Title) = MsgBoxResult.Yes Then Feedback(False, True) Else - MsgBox(Text & vbCrLf & vbCrLf & "将 PCL2 更新至最新版或许可以解决这个问题……", MsgBoxStyle.Critical, Title) + MsgBox(Text & vbCrLf & vbCrLf & "将 PCL 更新至最新版或许可以解决这个问题……", MsgBoxStyle.Critical, Title) End If If GetTimeTick() - Time < 1500 Then '弹窗无法保留 @@ -2455,14 +2453,14 @@ Retry: If CanFeedback(False) Then If MyMsgBox(ExFull & vbCrLf & vbCrLf & "是否反馈此问题?如果不反馈,这个问题可能永远无法得到解决!", Title, "反馈", "取消", IsWarn:=True) = 1 Then Feedback(False, True) Else - MyMsgBox(ExFull & vbCrLf & vbCrLf & "将 PCL2 更新至最新版或许可以解决这个问题……", Title, IsWarn:=True) + MyMsgBox(ExFull & vbCrLf & vbCrLf & "将 PCL 更新至最新版或许可以解决这个问题……", Title, IsWarn:=True) End If Case LogLevel.Assert Dim Time As Long = GetTimeTick() If CanFeedback(False) Then If MsgBox(ExFull & vbCrLf & vbCrLf & "是否反馈此问题?如果不反馈,这个问题可能永远无法得到解决!", MsgBoxStyle.Critical + MsgBoxStyle.YesNo, Title) = MsgBoxResult.Yes Then Feedback(False, True) Else - MsgBox(ExFull & vbCrLf & vbCrLf & "将 PCL2 更新至最新版或许可以解决这个问题……", MsgBoxStyle.Critical, Title) + MsgBox(ExFull & vbCrLf & vbCrLf & "将 PCL 更新至最新版或许可以解决这个问题……", MsgBoxStyle.Critical, Title) End If If GetTimeTick() - Time < 1500 Then '弹窗无法保留 @@ -2486,7 +2484,7 @@ Retry: End Sub Public Function CanFeedback(ShowHint As Boolean) As Boolean If False.Equals(PageSetupSystem.IsLauncherNewest) Then - If ShowHint Then MyMsgBox("你的 PCL2 不是最新版,因此无法提交反馈。" & vbCrLf & "请先在 设置 → 启动器 中更新启动器,确认该问题在最新版中依然存在,然后再提交反馈。", "无法提交反馈") + If ShowHint Then MyMsgBox("你的 PCL 不是最新版,因此无法提交反馈。" & vbCrLf & "请先在 设置 → 启动器 中更新启动器,确认该问题在最新版中依然存在,然后再提交反馈。", "无法提交反馈") Return False Else Return True diff --git a/Plain Craft Launcher 2/Modules/Base/ModNet.vb b/Plain Craft Launcher 2/Modules/Base/ModNet.vb index b8d33ae..a534f2b 100644 --- a/Plain Craft Launcher 2/Modules/Base/ModNet.vb +++ b/Plain Craft Launcher 2/Modules/Base/ModNet.vb @@ -961,7 +961,7 @@ Capture: Next '是否禁用多线程,以及规定碎片大小 Dim TargetUrl As String = GetSource().Url - If TargetUrl.Contains("pcl2-server") OrElse TargetUrl.Contains("gitcode.net") OrElse TargetUrl.Contains("mcer.cn") Then Return False + If TargetUrl.Contains("pcl2-server") OrElse TargetUrl.Contains("gitcode.net") OrElse TargetUrl.Contains("github.com") Then Return False Dim RealFilePieceLimit = If(TargetUrl.Contains("download.mcbbs.net"), FilePieceLimit * 5, FilePieceLimit) '寻找最大碎片 Dim FilePieceMax As NetThread = Threads diff --git a/Plain Craft Launcher 2/Modules/Base/MyBitmap.vb b/Plain Craft Launcher 2/Modules/Base/MyBitmap.vb index 12e78cc..e9e74f3 100644 --- a/Plain Craft Launcher 2/Modules/Base/MyBitmap.vb +++ b/Plain Craft Launcher 2/Modules/Base/MyBitmap.vb @@ -107,32 +107,28 @@ Public Class MyBitmap ''' ''' 旋转角度(单位为角度)。 Public Function Rotation(angle As Double) As System.Drawing.Bitmap - With Me - Dim img As System.Drawing.Image = Me.Pic - Dim bitSize As Single = img.Width - Dim bmp As New System.Drawing.Bitmap(CInt(bitSize), CInt(bitSize)) - Using g As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmp) - g.TranslateTransform(bitSize / 2, bitSize / 2) - g.RotateTransform(angle) - g.TranslateTransform(-bitSize / 2, -bitSize / 2) - g.DrawImage(img, New System.Drawing.Rectangle(0, 0, img.Width, img.Width)) - End Using - Return bmp - End With + Dim img As System.Drawing.Image = Me.Pic + Dim bitSize As Single = img.Width + Dim bmp As New System.Drawing.Bitmap(CInt(bitSize), CInt(bitSize)) + Using g As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmp) + g.TranslateTransform(bitSize / 2, bitSize / 2) + g.RotateTransform(angle) + g.TranslateTransform(-bitSize / 2, -bitSize / 2) + g.DrawImage(img, New System.Drawing.Rectangle(0, 0, img.Width, img.Width)) + End Using + Return bmp End Function ''' ''' 获取裁切的图片,这个方法不会导致原对象改变且会返回一个新的对象。 ''' Public Function Clip(rect As System.Drawing.Rectangle) As System.Drawing.Bitmap - With Me - Dim img As System.Drawing.Image = Pic - Dim bmp As New System.Drawing.Bitmap(rect.Width, rect.Height) - Using g As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmp) - g.DrawImageUnscaled(img, rect) - End Using - Return bmp - End With + Dim img As System.Drawing.Image = Pic + Dim bmp As New System.Drawing.Bitmap(rect.Width, rect.Height) + Using g As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmp) + g.DrawImageUnscaled(img, rect) + End Using + Return bmp End Function ''' diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb index 5eb980e..e88b429 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb @@ -225,7 +225,7 @@ RawName = Data("name") Description = Data("summary") Website = Data("links")("websiteUrl").ToString.TrimEnd("/") - LastUpdate = Data("dateModified") + LastUpdate = Data("dateReleased") '#1194 DownloadCount = Data("downloadCount") 'Logo If Data("logo").Count > 0 Then @@ -302,6 +302,7 @@ ModLoaders = New List(Of CompModLoaderType) For Each File In If(Data("latestFiles"), {}) Dim NewFile As New CompFile(File, Type) + If Not NewFile.Available Then Continue For ModLoaders.AddRange(NewFile.ModLoaders) Next ModLoaders = ModLoaders.Distinct.OrderBy(Of Integer)(Function(t) t).ToList @@ -414,9 +415,9 @@ Dim NewItem As New MyCompItem With {.Tag = Me} NewItem.LabTitle.Text = TranslatedName NewItem.LabInfo.Text = Description.Replace(vbCr, "").Replace(vbLf, "") - NewItem.LabLeft.Text = If(ModeDebug, If(FromCurseForge, "[CurseForge] ", "[Modrinth] "), "") & - (If(ModLoaders.Count > 0 AndAlso ShowLoaderDesc, "[" & Join(ModLoaders.Select(Function(m) GetStringFromEnum(m)).ToList, "/") & "] ", "") & + NewItem.LabLeft.Text = (If(ModLoaders.Count > 0 AndAlso ShowLoaderDesc, "[" & Join(ModLoaders.Select(Function(m) GetStringFromEnum(m)).ToList, "/") & "] ", "") & If(ShowMcVersionDesc, GameVersionDescription, "")).Replace("] [", " ") & + If(FromCurseForge, "[CurseForge] ", "[Modrinth] ") & Join(Tags, ",") & If(LastUpdate IsNot Nothing, " (" & GetTimeSpanString(LastUpdate - Date.Now) & "更新)", "") If LogoUrl Is Nothing Then @@ -439,6 +440,7 @@ ''' 检查是否与某个 Project 是相同的工程,只是在不同的网站。 ''' Public Function IsLike(Project As CompProject) As Boolean + If Id = Project.Id Then Return True '相同实例 '提取字符串中的字母和数字 Dim GetRaw = Function(Data As String) As String @@ -448,12 +450,17 @@ Next Return Result.ToString.ToLower End Function - '具体的检查 - '精准匹配:相同实例 或 MCMOD 指定的相同内容 - '模糊匹配:支持的 Mod 加载器一致,且 (原名、描述文本或 Slug 的英文部分相同) - If Id = Project.Id OrElse TranslatedName = Project.TranslatedName OrElse '精准匹配 - ((RawName = Project.RawName OrElse Description = Project.Description OrElse GetRaw(Slug) = GetRaw(Project.Slug)) AndAlso - ModLoaders.Count = Project.ModLoaders.Count AndAlso ModLoaders.Except(Project.ModLoaders).Count = 0) Then '模糊匹配 + '来自不同的网站 + If FromCurseForge = Project.FromCurseForge Then Return False + 'Mod 加载器一致 + If ModLoaders.Count <> Project.ModLoaders.Count OrElse ModLoaders.Except(Project.ModLoaders).Count > 0 Then Return False + 'MC 版本一致 + If GameVersions.Count <> Project.GameVersions.Count OrElse GameVersions.Except(Project.GameVersions).Count > 0 Then Return False + 'MCMOD 翻译名 / 原名 / 描述文本 / Slug 的英文部分相同 + If TranslatedName = Project.TranslatedName OrElse + RawName = Project.RawName OrElse Description = Project.Description OrElse + GetRaw(Slug) = GetRaw(Project.Slug) Then + Log($"[Comp] 将 {RawName} ({Slug}) 与 {Project.RawName} ({Project.Slug}) 认定为相似工程") '如果只有一个有 DatabaseEntry,设置给另外一个 If DatabaseEntry Is Nothing AndAlso Project.DatabaseEntry IsNot Nothing Then DatabaseEntry = Project.DatabaseEntry If DatabaseEntry IsNot Nothing AndAlso Project.DatabaseEntry Is Nothing Then Project.DatabaseEntry = DatabaseEntry @@ -809,8 +816,9 @@ Retry: #Region "提取非重复项,存储于 RealResults" - '将 CurseForge 排在 Modrinth 的前面,避免加载结束顺序不同导致排名不同 - RawResults = RawResults.Where(Function(x) x.FromCurseForge).Concat(RawResults.Where(Function(x) Not x.FromCurseForge)).ToList + '将 Modrinth 排在 CurseForge 的前面,避免加载结束顺序不同导致排名不同 + '这样做的话,去重后将优先保留 CurseForge 内容(考虑到 CurseForge 热度更高) + RawResults = RawResults.Where(Function(x) Not x.FromCurseForge).Concat(RawResults.Where(Function(x) x.FromCurseForge)).ToList 'RawResults 去重 RawResults = Distinct(RawResults, Function(a, b) a.IsLike(b)) '已有内容去重 @@ -939,11 +947,18 @@ Retry: End Property '下载信息 - + ''' + ''' 下载信息是否可用。 + ''' + Public ReadOnly Property Available As Boolean + Get + Return FileName IsNot Nothing + End Get + End Property ''' ''' 下载的文件名。 ''' - Public ReadOnly FileName As String + Public ReadOnly FileName As String = Nothing ''' ''' 文件所有可能的下载源。 ''' @@ -1022,8 +1037,11 @@ Retry: ReleaseDate = Data("date_published") Status = If(Data("version_type").ToString = "release", CompFileStatus.Release, If(Data("version_type").ToString = "beta", CompFileStatus.Beta, CompFileStatus.Alpha)) DownloadCount = Data("downloads") - FileName = Data("files")(0)("filename") - DownloadAddress = {Data("files")(0)("url").ToString.Replace(FileName, Net.WebUtility.UrlEncode(FileName))} + '可能为空 + If CType(Data("files"), JArray).Count > 0 Then + FileName = Data("files")(0)("filename") + DownloadAddress = {Data("files")(0)("url").ToString.Replace(FileName, Net.WebUtility.UrlEncode(FileName))} + End If 'Dependencies If Type = CompType.Mod Then RawDependencies = Data("dependencies"). @@ -1058,15 +1076,15 @@ Retry: ''' ''' 将当前文件信息实例化为控件。 ''' - Public Function ToListItem(OnClick As MyListItem.ClickEventHandler, Optional OnSaveClick As MyIconButton.ClickEventHandler = Nothing) As MyListItem + Public Function ToListItem(OnClick As MyListItem.ClickEventHandler, Optional OnSaveClick As MyIconButton.ClickEventHandler = Nothing, + Optional BadDisplayName As Boolean = False) As MyListItem '获取描述信息 Dim Info As String = "" Select Case Type Case CompType.Mod - Info += If(ModLoaders.Count > 0, Join(ModLoaders.Select(Function(m) GetStringFromEnum(m)).ToList, "/"), "") & - If(GameVersions.Count > 1 AndAlso GameVersions.Count < 10, If(ModLoaders.Count > 0, " ", "") & Join(GameVersions, "、").Replace("-snapshot", " 快照"), "") - If Info <> "" Then Info = "适用于 " & Info & "," + Info += If(ModLoaders.Count > 0, + "适用于 " & Join(ModLoaders.Select(Function(m) GetStringFromEnum(m)).ToList, "/") & ",", "") Info += If(ModeDebug AndAlso Dependencies.Count > 0, Dependencies.Count & " 个前置 Mod,", "") Case CompType.ModPack If GameVersions.All(Function(v) v.Contains("w")) Then @@ -1081,7 +1099,8 @@ Retry: '建立控件 Dim NewItem As New MyListItem With { - .Title = DisplayName, .SnapsToDevicePixels = True, .Height = 42, .Type = MyListItem.CheckType.Clickable, .Tag = Me, + .Title = If(BadDisplayName, FileName, DisplayName), + .SnapsToDevicePixels = True, .Height = 42, .Type = MyListItem.CheckType.Clickable, .Tag = Me, .Info = Info } Select Case Status @@ -1153,7 +1172,8 @@ Retry: 'Modrinth ResultJsonArray = NetGetCodeByRequestRetry($"https://api.modrinth.com/v2/project/{ProjectId}/version", Accept:="application/json", IsJson:=True) End If - CompFilesCache(ProjectId) = ResultJsonArray.Select(Function(a) New CompFile(a, TargetProject.Type)).ToList + CompFilesCache(ProjectId) = Distinct(ResultJsonArray.Select(Function(a) New CompFile(a, TargetProject.Type)). + Where(Function(a) a.Available).ToList, Function(a, b) a.Id = b.Id) 'CurseForge 可能会重复返回相同项(#1330) '获取前置 Mod 列表 If TargetProject.Type <> CompType.Mod Then Return CompFilesCache(ProjectId) Dim Deps As List(Of String) = CompFilesCache(ProjectId). diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb index fd5ae5e..75429ec 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb @@ -29,7 +29,7 @@ ''' ''' 将可用于分析的日志存储到 AnalyzeRawFiles。 ''' - ''' 从 PCL2 捕获到的最后 200 行程序输出。 + ''' 从 PCL 捕获到的最后 200 行程序输出。 Public Sub Collect(VersionPathIndie As String, Optional LatestLog As IList(Of String) = Nothing) Log("[Crash] 步骤 1:收集日志文件") @@ -178,7 +178,7 @@ FileName = "游戏崩溃前的输出.txt" OrElse FileName = "rawoutput.log" Then TargetType = AnalyzeFileType.MinecraftLog If DirectFile Is Nothing Then DirectFile = LogFile - ElseIf FileName = "启动器日志.txt" OrElse FileName = "PCL2 启动器日志.txt" OrElse FileName = "log1.txt" Then + ElseIf FileName = "启动器日志.txt" OrElse FileName = "PCL2 启动器日志.txt" OrElse FileName = "PCL 启动器日志.txt" OrElse FileName = "log1.txt" Then If LogFile.Value.Any(Function(s) s.Contains("以下为游戏输出的最后一段内容")) Then TargetType = AnalyzeFileType.MinecraftLog If DirectFile Is Nothing Then DirectFile = LogFile @@ -243,7 +243,7 @@ Log("[Crash] 输出报告:" & SelectedFile.Key & ",作为 Minecraft 或启动器日志") Next '选择一份最佳的来自启动器的游戏日志 - For Each FileName As String In {"rawoutput.log", "启动器日志.txt", "log1.txt", "游戏崩溃前的输出.txt", "PCL2 启动器日志.txt"} + For Each FileName As String In {"rawoutput.log", "启动器日志.txt", "log1.txt", "游戏崩溃前的输出.txt", "PCL2 启动器日志.txt", "PCL 启动器日志.txt"} If FileNameDict.ContainsKey(FileName) Then Dim CurrentLog = FileNameDict(FileName) '截取 “以下为游戏输出的最后一段内容” 后的内容 @@ -253,12 +253,12 @@ LogMc += Line & vbLf ElseIf Line.Contains("以下为游戏输出的最后一段内容") Then HasLauncherMark = True - Log("[Crash] 找到 PCL2 输出的游戏实时日志头") + Log("[Crash] 找到 PCL 输出的游戏实时日志头") End If Next '导入后 500 行 If Not HasLauncherMark Then LogMc += GetHeadTailLines(CurrentLog.Value, 0, 500) - LogMc = LogMc.TrimEnd(vbCrLf.ToCharArray) & vbLf & "[" '路径包含中文且存在编码问题导致找不到或无法加载主类的末端检测 + LogMc = LogMc.TrimEnd(vbCrLf.ToCharArray) Log("[Crash] 导入分析:" & CurrentLog.Key & ",作为启动器日志") Exit For End If @@ -267,7 +267,7 @@ For Each FileName As String In {"latest.log", "latest log.txt", "debug.log", "debug log.txt"} If FileNameDict.ContainsKey(FileName) Then Dim CurrentLog = FileNameDict(FileName) - LogMc += GetHeadTailLines(CurrentLog.Value, 250, 500) & vbLf & "[" + LogMc += GetHeadTailLines(CurrentLog.Value, 250, 500) Log("[Crash] 导入分析:" & CurrentLog.Key & ",作为 Minecraft 日志") Exit For End If @@ -331,7 +331,7 @@ Java版本过高 Java版本不兼容 显卡驱动不支持导致无法设置像素格式 - 路径包含中文且存在编码问题导致找不到或无法加载主类 + 极短的程序输出 Intel驱动不兼容导致EXCEPTION_ACCESS_VIOLATION 'https://bugs.mojang.com/browse/MC-32606 AMD驱动不兼容导致EXCEPTION_ACCESS_VIOLATION 'https://bugs.mojang.com/browse/MC-31618 Nvidia驱动不兼容导致EXCEPTION_ACCESS_VIOLATION @@ -464,6 +464,7 @@ Done: If LogMc.Contains("java.lang.ClassCastException: java.base/jdk") Then AppendReason(CrashReason.使用JDK) If LogMc.Contains("java.lang.ClassCastException: class jdk.") Then AppendReason(CrashReason.使用JDK) If LogMc.Contains("TRANSFORMER/net.optifine/net.optifine.reflect.Reflector.(Reflector.java") Then AppendReason(CrashReason.OptiFine与Forge不兼容) + If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraft.client.renderer.texture.SpriteContents.(net.minecraft.resources.ResourceLocation, ") Then AppendReason(CrashReason.OptiFine与Forge不兼容) If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraft.client.renderer.block.model.BakedQuad.(int[], int, net.minecraft.core.Direction, net.minecraft.client.renderer.texture.TextureAtlasSprite, boolean, boolean)'") Then AppendReason(CrashReason.OptiFine与Forge不兼容) If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraft.server.level.DistanceManager.addRegionTicket(net.minecraft.server.level.TicketType, net.minecraft.world.level.ChunkPos, int, java.lang.Object, boolean)'") Then AppendReason(CrashReason.OptiFine与Forge不兼容) If LogMc.Contains("Open J9 is not supported") OrElse LogMc.Contains("OpenJ9 is incompatible") OrElse LogMc.Contains(".J9VMInternals.") Then AppendReason(CrashReason.使用OpenJ9) @@ -478,8 +479,6 @@ Done: If LogMc.Contains("java.lang.NoSuchMethodError: sun.security.util.ManifestEntryVerifier") Then AppendReason(CrashReason.低版本Forge与高版本Java不兼容) If LogMc.Contains("1282: Invalid operation") Then AppendReason(CrashReason.光影或资源包导致OpenGL1282错误) If LogMc.Contains("signer information does not match signer information of other classes in the same package") Then AppendReason(CrashReason.文件或内容校验失败, If(RegexSeek(LogMc, "(?<=class "")[^']+(?=""'s signer information)"), "").TrimEnd(vbCrLf)) - If LogMc.Contains("An exception was thrown, the game will display an error screen and halt.") Then AppendReason(CrashReason.Forge报错, If(RegexSeek(LogMc, "(?<=the game will display an error screen and halt[\s\S]+?Exception: )[\s\S]+?(?=\n\tat)"), "").TrimEnd(vbCrLf)) - If LogMc.Contains("A potential solution has been determined:") Then AppendReason(CrashReason.Fabric报错并给出解决方案, Join(RegexSearch(If(RegexSeek(LogMc, "(?<=A potential solution has been determined:\n)((\t)+ - [^\n]+\n)+"), ""), "(?<=(\t)+)[^\n]+"), vbLf)) If LogMc.Contains("Maybe try a lower resolution resourcepack?") Then AppendReason(CrashReason.材质过大或显卡配置不足) If LogMc.Contains("java.lang.NoSuchMethodError: net.minecraft.world.server.ChunkManager$ProxyTicketManager.shouldForceTicks(J)Z") AndAlso LogMc.Contains("OptiFine") Then AppendReason(CrashReason.OptiFine导致无法加载世界) If LogMc.Contains("Unsupported class file major version") Then AppendReason(CrashReason.Java版本不兼容) @@ -495,21 +494,24 @@ Done: If LogMc.Contains("DuplicateModsFoundException") Then AppendReason(CrashReason.Mod重复安装, RegexSearch(LogMc, "(?<=\n\t[\w]+ : [A-Z]{1}:[^\n]+(/|\\))[^/\\\n]+?.jar", RegularExpressions.RegexOptions.IgnoreCase)) If LogMc.Contains("Found a duplicate mod") Then AppendReason(CrashReason.Mod重复安装, RegexSearch(If(RegexSeek(LogMc, "Found a duplicate mod[^\n]+"), ""), "[^\\/]+.jar", RegularExpressions.RegexOptions.IgnoreCase)) If LogMc.Contains("ModResolutionException: Duplicate") Then AppendReason(CrashReason.Mod重复安装, RegexSearch(If(RegexSeek(LogMc, "ModResolutionException: Duplicate[^\n]+"), ""), "[^\\/]+.jar", RegularExpressions.RegexOptions.IgnoreCase)) - '找不到或无法加载主类 - If (RegexCheck(LogMc, "^[^\n.]+.\w+.[^\n]+\n\[$") OrElse RegexCheck(LogMc, "^\[[^\]]+\] [^\n.]+.\w+.[^\n]+\n\[")) AndAlso - Not (LogMc.Contains("at net.") OrElse LogMc.Contains("/INFO]")) AndAlso - LogHs Is Nothing AndAlso LogCrash Is Nothing AndAlso LogMc.Length < 500 Then - AppendReason(CrashReason.路径包含中文且存在编码问题导致找不到或无法加载主类) - End If 'Mod 导致的崩溃 If LogMc.Contains("Mixin prepare failed ") OrElse LogMc.Contains("Mixin apply failed ") OrElse LogMc.Contains("mixin.injection.throwables.") OrElse LogMc.Contains(".mixins.json] FAILED during )") Then Dim ModId As String = RegexSeek(LogMc, "(?<=in )[^./ ]+(?=.mixins.json.+failed injection check)") + If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=in mixins.)[^./ ]+(?=.json.+failed injection check)") If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= failed .+ in )[^./ ]+(?=.mixins.json)") + If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= failed .+ in mixins.)[^./ ]+(?=.json)") If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= in config \[)[^./ ]+(?=.mixins.json\] FAILED during )") + If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= in config \[mixins.)[^./ ]+(?=.json\] FAILED during )") If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= in callback )[^./ ]+(?=.mixins.json:)") + If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= in callback mixins.)[^./ ]+(?=.json:)") AppendReason(CrashReason.ModMixin失败, TryAnalyzeModName(If(ModId, "").TrimEnd((vbCrLf & " ").ToCharArray))) End If - If LogMc.Contains("Caught exception from ") Then AppendReason(CrashReason.确定Mod导致游戏崩溃, TryAnalyzeModName(If(RegexSeek(LogMc, "[^\n]+?(?)"), "").TrimEnd((vbCrLf & " ").ToCharArray))) + If LogMc.Contains("Caught exception from ") Then AppendReason(CrashReason.确定Mod导致游戏崩溃, TryAnalyzeModName(RegexSeek(LogMc, "(?<=Caught exception from )[^\n]+?")?.TrimEnd((vbCrLf & " ").ToCharArray))) + '仅当没有其他分析结果时显示 + If CrashReasons.Count = 0 Then + If LogMc.Contains("An exception was thrown, the game will display an error screen and halt.") Then AppendReason(CrashReason.Forge报错, RegexSeek(LogMc, "(?<=the game will display an error screen and halt.[\n\r]+[^\n]+?Exception: )[\s\S]+?(?=\n\tat)")?.Trim(vbCrLf)) + If LogMc.Contains("A potential solution has been determined:") Then AppendReason(CrashReason.Fabric报错并给出解决方案, Join(RegexSearch(If(RegexSeek(LogMc, "(?<=A potential solution has been determined:\n)((\t)+ - [^\n]+\n)+"), ""), "(?<=(\t)+)[^\n]+"), vbLf)) + End If End If '虚拟机日志分析 @@ -552,9 +554,13 @@ Done: '游戏日志分析 If LogMc IsNot Nothing Then - If LogMc.Contains("]: Warnings were found!") Then AppendReason(CrashReason.Fabric报错, If(RegexSeek(LogMc, "(?<=\]: Warnings were found! ?[\n]+)[\w\W]+?(?=[\n]+\[)"), "").Trim(vbCrLf.ToCharArray)) + If Not (LogMc.Contains("at net.") OrElse LogMc.Contains("INFO]")) AndAlso + LogHs Is Nothing AndAlso LogCrash Is Nothing AndAlso LogMc.Length < 100 Then + AppendReason(CrashReason.极短的程序输出, LogMc) + End If 'Mixin 失败可以导致大量 Mod 实例创建失败 If LogMc.Contains("Failed to create mod instance.") Then AppendReason(CrashReason.Mod初始化失败, TryAnalyzeModName(If(RegexSeek(LogMc, "(?<=Failed to create mod instance. ModID: )[^,]+"), If(RegexSeek(LogMc, "(?<=Failed to create mod instance. ModId )[^\n]+(?= for )"), "")).TrimEnd(vbCrLf))) + '注意:Fabric 的 Warnings were found! 不一定是崩溃原因,它可能是单纯的警报 End If '崩溃报告分析 @@ -600,12 +606,12 @@ NextStack: For i = 0 To Math.Min(3, Splited.Count - 1) '最多取前 4 节 Dim Word As String = Splited(i) If Word.Length <= 2 OrElse Word.StartsWith("func_") Then Continue For - If {"com", "org", "net", "asm", "fml", "mod", "jar", "sun", "lib", "map", "gui", "dev", "nio", "api", "dsi", + If {"com", "org", "net", "asm", "fml", "mod", "jar", "sun", "lib", "map", "gui", "dev", "nio", "api", "dsi", "top", "core", "init", "mods", "main", "file", "game", "load", "read", "done", "util", "tile", "item", "base", - "forge", "setup", "block", "model", "mixin", "event", "unimi", - "common", "server", "config", "loader", "launch", "entity", "assist", "client", "modapi", "mojang", "shader", "events", "github", "recipe", + "forge", "setup", "block", "model", "mixin", "event", "unimi", "netty", + "common", "server", "config", "loader", "launch", "entity", "assist", "client", "plugin", "modapi", "mojang", "shader", "events", "github", "recipe", "preinit", "preload", "machine", "reflect", "channel", "general", "handler", "content", - "fastutil", "optifine", "minecraft", "transformers", "universal", "internal", "multipart", "minecraftforge", "override", "blockentity" + "fastutil", "optifine", "scheduler", "minecraft", "transformers", "universal", "internal", "multipart", "minecraftforge", "override", "blockentity" }.Contains(Word.ToLower) Then Continue For PossibleWords.Add(Word.Trim) Next @@ -663,7 +669,7 @@ NextStack: For Each ModString As String In ModNameLines Dim RealModString As String = ModString.ToLower.Replace("_", "") If Not RealModString.Contains(KeyWord.ToLower.Replace("_", "")) Then Continue For - If RealModString.Contains("minecraft.jar") OrElse RealModString.Contains(" forge-") Then Continue For + If RealModString.Contains("minecraft.jar") OrElse RealModString.Contains(" forge-") OrElse RealModString.Contains(" mixin-") Then Continue For HintLines.Add(ModString.Trim(vbCrLf.ToCharArray)) Exit For Next @@ -696,7 +702,7 @@ NextStack: ''' 尝试获取 Mod 名称,若失败则返回原关键字。 ''' Private Function TryAnalyzeModName(Keywords As String) As List(Of String) - Dim RawList As New List(Of String) From {Keywords} + Dim RawList As New List(Of String) From {If(Keywords, "")} If String.IsNullOrEmpty(Keywords) Then Return RawList Return If(AnalyzeModName(RawList), RawList) End Function @@ -709,10 +715,8 @@ NextStack: Public Sub Output(IsHandAnalyze As Boolean, Optional ExtraFiles As List(Of String) = Nothing) '弹窗提示 FrmMain.ShowWindowToTop() -Redo: - Select Case MyMsgBox(GetAnalyzeResult(IsHandAnalyze), If(IsHandAnalyze, "错误报告分析结果", "Minecraft 出现错误"), - "确定", If(IsHandAnalyze OrElse DirectFile Is Nothing, "", "查看日志"), If(IsHandAnalyze, "", "导出错误报告")) - Case 2 + Dim ShowLog As Action = + Sub() If File.Exists(DirectFile.Value.Key) Then ShellOnly(DirectFile.Value.Key) Else @@ -720,7 +724,10 @@ Redo: WriteFile(FilePath, Join(DirectFile.Value.Value, vbCrLf)) ShellOnly(FilePath) End If - GoTo Redo + 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)) Case 3 Dim FileAddress As String = Nothing Try @@ -740,7 +747,7 @@ Redo: Case "LatestLaunch.bat" FileName = "启动脚本.bat" Case "Log1.txt" - FileName = "PCL2 启动器日志.txt" + FileName = "PCL 启动器日志.txt" Case "RawOutput.log" FileName = "游戏崩溃前的输出.txt" End Select @@ -764,157 +771,161 @@ Redo: ''' Private Function GetAnalyzeResult(IsHandAnalyze As Boolean) As String - '非一个结果的处理 - Dim ResultString As String + '没有结果的处理 If CrashReasons.Count = 0 Then If IsHandAnalyze Then - Return "很抱歉,PCL2 无法确定错误原因。" + Return "很抱歉,PCL 无法确定错误原因。" Else Return "很抱歉,你的游戏出现了一些问题……" & vbCrLf & "如果要寻求帮助,请向他人发送错误报告文件,而不是发送这个窗口的截图。" & vbCrLf & "你也可以查看错误报告,其中可能会有出错的原因。" End If - ElseIf CrashReasons.Count >= 2 Then - Hint("错误分析时发现数个可能的原因,窗口中仅显示了第一条,你可以在启动器日志中检查更多可能的原因!", HintType.Finish) End If '根据不同原因判断 - Dim Additional As List(Of String) = CrashReasons.First.Value - Select Case CrashReasons.First.Key - Case CrashReason.Mod文件被解压 - ResultString = "由于 Mod 文件被解压了,导致游戏无法继续运行。\n直接把整个 Mod 文件放进 Mod 文件夹中即可,若解压就会导致游戏出错。\n\n请删除 Mod 文件夹中已被解压的 Mod,然后再启动游戏。" - Case CrashReason.内存不足 - ResultString = "Minecraft 内存不足,导致其无法继续运行。\n这很可能是由于你为游戏分配的内存不足,或是游戏的配置要求过高。\n\n你可以在启动设置中增加为游戏分配的内存,删除配置要求较高的材质、Mod、光影。\n如果这依然不奏效,请在开始游戏前尽量关闭其他软件,或者……换台电脑?\h" - Case CrashReason.使用OpenJ9 - ResultString = "游戏因为使用 Open J9 而崩溃了。\n请在启动设置的 Java 选择一项中改用非 OpenJ9 的 Java,然后再启动游戏。" - Case CrashReason.使用JDK - ResultString = "游戏似乎因为使用 JDK,或 Java 版本过高而崩溃了。\n请在启动设置的 Java 选择一项中改用 JRE 8(Java 8),然后再启动游戏。\n如果你没有安装 JRE 8,你可以从网络中下载、安装一个。" - Case CrashReason.Java版本过高 - ResultString = "游戏似乎因为你所使用的 Java 版本过高而崩溃了。\n请在启动设置的 Java 选择一项中改用较低版本的 Java,然后再启动游戏。\n如果没有,可以从网络中下载、安装一个。" - Case CrashReason.Java版本不兼容 - ResultString = "游戏不兼容你当前使用的 Java。\n如果没有合适的 Java,可以从网络中下载、安装一个。" - Case CrashReason.使用32位Java导致JVM无法分配足够多的内存 - If Environment.Is64BitOperatingSystem Then - ResultString = "你似乎正在使用 32 位 Java,这会导致 Minecraft 无法使用 1GB 以上的内存,进而造成崩溃。\n\n请在启动设置的 Java 选择一项中改用 64 位的 Java 再启动游戏,然后再启动游戏。\n如果你没有安装 64 位的 Java,你可以从网络中下载、安装一个。" - Else - ResultString = "你正在使用 32 位的操作系统,这会导致 Minecraft 无法使用 1GB 以上的内存,进而造成崩溃。\n\n你或许只能重装 64 位的操作系统来解决此问题。\n如果你的电脑内存在 2GB 以内,那或许只能换台电脑了……\h" - End If - Case CrashReason.崩溃日志堆栈分析发现关键字, CrashReason.MC日志堆栈分析发现关键字 - If Additional.Count = 1 Then - ResultString = "你的游戏遇到了一些问题,这可能是某些 Mod 所引起的,PCL2 找到了一个可疑的关键词:" & Additional.First & "。\n\n如果你知道它对应的 Mod,那么有可能就是它引起的错误,你也可以查看错误报告获取详情。\h" - Else - ResultString = "你的游戏遇到了一些问题,这可能是某些 Mod 所引起的,PCL2 找到了以下可疑的关键词:\n - " & Join(Additional, ", ") & "\n\n如果你知道这些关键词对应的 Mod,那么有可能就是它引起的错误,你也可以查看错误报告获取详情。\h" - End If - Case CrashReason.崩溃日志堆栈分析发现Mod名称 - If Additional.Count = 1 Then - ResultString = "名为 " & Additional.First & " 的 Mod 可能导致了游戏出错。\n\e\h" - Else - ResultString = "可能是以下 Mod 导致了游戏出错:\n - " & Join(Additional, "\n - ") & "\n\e\h" - End If - Case CrashReason.确定Mod导致游戏崩溃 - If Additional.Count = 1 Then - ResultString = "名称或 ID 为 " & Additional.First & " 的 Mod 导致了游戏出错。\n\e\h" - Else - ResultString = "以下 Mod 导致了游戏出错:\n - " & Join(Additional, "\n - ") & "\n\e\h" - End If - Case CrashReason.ModMixin失败 - If Additional.Count = 1 Then - ResultString = "名称或 ID 为 " & Additional.First & " 的 Mod 注入失败,导致游戏出错。\n这一般代表着该 Mod 存在 Bug,或与当前环境不兼容。\n\e\h" - Else - ResultString = "以下 Mod 导致了游戏出错:\n - " & Join(Additional, "\n - ") & "\n这一般代表着这些 Mod 存在 Bug,或与当前环境不兼容。\n\e\h" - End If - Case CrashReason.Mod配置文件导致游戏崩溃 - If Additional(1) Is Nothing Then - ResultString = "名称或 ID 为 " & Additional.First & " 的 Mod 导致了游戏出错。\n\e\h" - Else - ResultString = "名称或 ID 为 " & Additional.First & " 的 Mod 导致了游戏出错:\n其配置文件 " & Additional(1) & " 存在异常,无法读取。" - End If - Case CrashReason.Mod初始化失败 - If Additional.Count = 1 Then - ResultString = "名为 " & Additional.First & " 的 Mod 初始化失败,导致游戏无法继续加载。\n\e\h" - Else - ResultString = "以下 Mod 初始化失败,导致游戏无法继续加载:\n - " & Join(Additional, "\n - ") & "\n\e\h" - End If - Case CrashReason.特定方块导致崩溃 - If Additional.Count = 1 Then - ResultString = "游戏似乎因为方块 " & Additional.First & " 出现了问题。\n\n你可以创建一个新世界,并观察游戏的运行情况:\n - 若正常运行,则是该方块导致出错,你或许需要使用一些方式删除此方块。\n - 若仍然出错,问题就可能来自其他原因……\h" - Else - ResultString = "游戏似乎因为世界中的某些方块出现了问题。\n\n你可以创建一个新世界,并观察游戏的运行情况:\n - 若正常运行,则是某些方块导致出错,你或许需要删除该世界。\n - 若仍然出错,问题就可能来自其他原因……\h" - End If - Case CrashReason.Mod重复安装 - If Additional.Count >= 2 Then - ResultString = "你重复安装了多个相同的 Mod:\n - " & Join(Additional, "\n - ") & "\n\n每个 Mod 只能出现一次,请删除重复的 Mod,然后再启动游戏。" - Else - ResultString = "你可能重复安装了多个相同的 Mod,导致游戏无法继续加载。\n\n每个 Mod 只能出现一次,请删除重复的 Mod,然后再启动游戏。\e\h" - End If - Case CrashReason.特定实体导致崩溃 - If Additional.Count = 1 Then - ResultString = "游戏似乎因为实体 " & Additional.First & " 出现了问题。\n\n你可以创建一个新世界,并生成一个该实体,然后观察游戏的运行情况:\n - 若正常运行,则是该实体导致出错,你或许需要使用一些方式删除此实体。\n - 若仍然出错,问题就可能来自其他原因……\h" - Else - ResultString = "游戏似乎因为世界中的某些实体出现了问题。\n\n你可以创建一个新世界,并生成各种实体,观察游戏的运行情况:\n - 若正常运行,则是某些实体导致出错,你或许需要删除该世界。\n - 若仍然出错,问题就可能来自其他原因……\h" - End If - Case CrashReason.OptiFine与Forge不兼容 - ResultString = "由于 OptiFine 与当前版本的 Forge 不兼容,导致了游戏崩溃。\n\n请前往 OptiFine 官网(https://optifine.net/downloads)查看 OptiFine 所兼容的 Forge 版本,并严格按照对应版本重新安装游戏。\n经测试,Forge 版本过高或过低都可能导致崩溃。" - Case CrashReason.低版本Forge与高版本Java不兼容 - ResultString = "由于低版本 Forge 与当前 Java 不兼容,导致了游戏崩溃。\n\n请尝试以下解决方案:\n - 更新 Forge 到 36.2.26 或更高版本\n - 换用版本低于 8.0.320 的 Java" - Case CrashReason.版本Json中存在多个Forge - ResultString = "可能由于使用其他启动器修改了 Forge 版本,当前版本的文件存在异常,导致了游戏崩溃。\n请尝试重新全新安装 Forge,而非使用其他启动器修改 Forge 版本。" - Case CrashReason.玩家手动触发调试崩溃 - ResultString = "* 事实上,你的游戏没有任何问题,这是你自己触发的崩溃。\n* 你难道没有更重要的事要做吗?" - Case CrashReason.路径包含中文且存在编码问题导致找不到或无法加载主类 - ResultString = "由于游戏路径含有中文字符,且 Java 或系统编码存在错误,导致游戏无法运行。\n\n解决这一问题的最简单方法是检查游戏的完整路径,并删除各个文件夹名中的中文字符。\n这包括了游戏的版本名、它所处的 .minecraft 文件夹及其之前的文件夹名。\h" - Case CrashReason.OptiFine导致无法加载世界 'https://www.minecraftforum.net/forums/support/java-edition-support/3051132-exception-ticking-world - ResultString = "你所使用的 OptiFine 可能导致了你的游戏出现问题。\n\n该问题只在特定 OptiFine 版本中出现,你可以尝试更换 OptiFine 的版本。\h" - Case CrashReason.显卡驱动不支持导致无法设置像素格式, CrashReason.Intel驱动不兼容导致EXCEPTION_ACCESS_VIOLATION, CrashReason.AMD驱动不兼容导致EXCEPTION_ACCESS_VIOLATION, CrashReason.Nvidia驱动不兼容导致EXCEPTION_ACCESS_VIOLATION, CrashReason.显卡不支持OpenGL - If LogAll.Contains("hd graphics ") Then - ResultString = "你的显卡驱动存在问题,或未使用独立显卡,导致游戏无法正常运行。\n\n如果你的电脑存在独立显卡,请使用独立显卡而非 Intel 核显启动 PCL2 与 Minecraft。\n如果问题依然存在,请尝试升级你的显卡驱动到最新版本,或回退到出厂版本。\n如果还是不行,还可以尝试使用 8.0.51 或更低版本的 Java。\h" - Else - ResultString = "你的显卡驱动存在问题,导致游戏无法正常运行。\n\n请尝试升级你的显卡驱动到最新版本,或回退到出厂版本,然后再启动游戏。\n如果还是不行,可以尝试使用 8.0.51 或更低版本的 Java。\n如果问题依然存在,那么你可能需要换个更好的显卡……\h" - End If - Case CrashReason.材质过大或显卡配置不足 - ResultString = "你所使用的材质分辨率过高,或显卡配置不足,导致游戏无法继续运行。\n\n如果你正在使用高清材质,请将它移除。\n如果你没有使用材质,那么你可能需要更新显卡驱动,或者换个更好的显卡……\h" - Case CrashReason.光影或资源包导致OpenGL1282错误 - ResultString = "你所使用的光影或材质导致游戏出现了一些问题……\n\n请尝试删除你所添加的这些额外资源。\h" - Case CrashReason.Mod过多导致超出ID限制 - ResultString = "你所安装的 Mod 过多,超出了游戏的 ID 限制,导致了游戏崩溃。\n请尝试安装 JEID 等修复 Mod,或删除部分大型 Mod。" - Case CrashReason.文件或内容校验失败 - ResultString = "部分文件或内容校验失败,导致游戏出现了问题。\n\n请尝试删除游戏(包括 Mod)并重新下载,或尝试在重新下载时使用 VPN。\h" - Case CrashReason.Fabric报错 - If Additional.Count = 1 Then - ResultString = "Fabric 提供了以下错误信息:\n" & Additional.First & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。" - Else - ResultString = "Fabric 可能已经提供了错误信息,请根据错误报告中的日志信息进行对应处理,如果看不懂英文可以使用翻译软件。\n如果没有看到报错信息,可以查看错误报告了解错误具体是如何发生的。\h" - End If - Case CrashReason.Mod加载器报错 - If Additional.Count = 1 Then - ResultString = "Mod 加载器提供了以下错误信息:\n" & Additional.First & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。" - Else - ResultString = "Mod 加载器可能已经提供了错误信息,请根据错误报告中的日志信息进行对应处理,如果看不懂英文可以使用翻译软件。\n如果没有看到报错信息,可以查看错误报告了解错误具体是如何发生的。\h" - End If - Case CrashReason.Fabric报错并给出解决方案 - If Additional.Count = 1 Then - ResultString = "Fabric 提供了以下解决方案:\n" & Additional.First & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。" - Else - ResultString = "Fabric 可能已经提供了解决方案,请根据错误报告中的日志信息进行对应处理,如果看不懂英文可以使用翻译软件。\n如果没有看到报错信息,可以查看错误报告了解错误具体是如何发生的。\h" - End If - Case CrashReason.Forge报错 - If Additional.Count = 1 Then - ResultString = "Forge 提供了以下错误信息:\n" & Additional.First & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。" - Else - ResultString = "Forge 可能已经提供了错误信息,请根据错误报告中的日志信息进行对应处理,如果看不懂英文可以使用翻译软件。\n如果没有看到报错信息,可以查看错误报告了解错误具体是如何发生的。\h" - End If - Case CrashReason.没有可用的分析文件 - ResultString = "你的游戏出现了一些问题,但 PCL2 未能找到相关记录文件,因此无法进行分析。\h" - Case Else - ResultString = "PCL2 获取到了没有详细信息的错误原因(" & CrashReasons.First.Key & "),请向 PCL2 作者提交反馈以获取详情。\h" - End Select + Dim Results As New List(Of String) + For Each Reason In CrashReasons + Dim Additional As List(Of String) = Reason.Value + Select Case Reason.Key + Case CrashReason.Mod文件被解压 + Results.Add("由于 Mod 文件被解压了,导致游戏无法继续运行。\n直接把整个 Mod 文件放进 Mod 文件夹中即可,若解压就会导致游戏出错。\n\n请删除 Mod 文件夹中已被解压的 Mod,然后再启动游戏。") + Case CrashReason.内存不足 + Results.Add("Minecraft 内存不足,导致其无法继续运行。\n这很可能是由于你为游戏分配的内存不足,或是游戏的配置要求过高。\n\n你可以在启动设置中增加为游戏分配的内存,删除配置要求较高的材质、Mod、光影。\n如果这依然不奏效,请在开始游戏前尽量关闭其他软件,或者……换台电脑?\h") + Case CrashReason.使用OpenJ9 + Results.Add("游戏因为使用 Open J9 而崩溃了。\n请在启动设置的 Java 选择一项中改用非 OpenJ9 的 Java,然后再启动游戏。") + Case CrashReason.使用JDK + Results.Add("游戏似乎因为使用 JDK,或 Java 版本过高而崩溃了。\n请在启动设置的 Java 选择一项中改用 JRE 8(Java 8),然后再启动游戏。\n如果你没有安装 JRE 8,你可以从网络中下载、安装一个。") + Case CrashReason.Java版本过高 + Results.Add("游戏似乎因为你所使用的 Java 版本过高而崩溃了。\n请在启动设置的 Java 选择一项中改用较低版本的 Java,然后再启动游戏。\n如果没有,可以从网络中下载、安装一个。") + Case CrashReason.Java版本不兼容 + Results.Add("游戏不兼容你当前使用的 Java。\n如果没有合适的 Java,可以从网络中下载、安装一个。") + Case CrashReason.使用32位Java导致JVM无法分配足够多的内存 + If Environment.Is64BitOperatingSystem Then + Results.Add("你似乎正在使用 32 位 Java,这会导致 Minecraft 无法使用 1GB 以上的内存,进而造成崩溃。\n\n请在启动设置的 Java 选择一项中改用 64 位的 Java 再启动游戏,然后再启动游戏。\n如果你没有安装 64 位的 Java,你可以从网络中下载、安装一个。") + Else + Results.Add("你正在使用 32 位的操作系统,这会导致 Minecraft 无法使用 1GB 以上的内存,进而造成崩溃。\n\n你或许只能重装 64 位的操作系统来解决此问题。\n如果你的电脑内存在 2GB 以内,那或许只能换台电脑了……\h") + End If + Case CrashReason.崩溃日志堆栈分析发现关键字, CrashReason.MC日志堆栈分析发现关键字 + If Additional.Count = 1 Then + Results.Add("你的游戏遇到了一些问题,这可能是某些 Mod 所引起的,PCL 找到了一个可疑的关键词:" & Additional.First & "。\n\n如果你知道它对应的 Mod,那么有可能就是它引起的错误,你也可以查看错误报告获取详情。\h") + Else + Results.Add("你的游戏遇到了一些问题,这可能是某些 Mod 所引起的,PCL 找到了以下可疑的关键词:\n - " & Join(Additional, ", ") & "\n\n如果你知道这些关键词对应的 Mod,那么有可能就是它引起的错误,你也可以查看错误报告获取详情。\h") + End If + Case CrashReason.崩溃日志堆栈分析发现Mod名称 + If Additional.Count = 1 Then + Results.Add("名为 " & Additional.First & " 的 Mod 可能导致了游戏出错。\n\e\h") + Else + Results.Add("可能是以下 Mod 导致了游戏出错:\n - " & Join(Additional, "\n - ") & "\n\e\h") + End If + Case CrashReason.确定Mod导致游戏崩溃 + If Additional.Count = 1 Then + Results.Add("名称或 ID 为 " & Additional.First & " 的 Mod 导致了游戏出错。\n\e\h") + Else + Results.Add("以下 Mod 导致了游戏出错:\n - " & Join(Additional, "\n - ") & "\n\e\h") + End If + Case CrashReason.ModMixin失败 + If Additional.Count = 1 Then + Results.Add("名称或 ID 为 " & Additional.First & " 的 Mod 注入失败,导致游戏出错。\n这一般代表着该 Mod 存在 Bug,或与当前环境不兼容。\n\e\h") + Else + Results.Add("以下 Mod 导致了游戏出错:\n - " & Join(Additional, "\n - ") & "\n这一般代表着这些 Mod 存在 Bug,或与当前环境不兼容。\n\e\h") + End If + Case CrashReason.Mod配置文件导致游戏崩溃 + If Additional(1) Is Nothing Then + Results.Add("名称或 ID 为 " & Additional.First & " 的 Mod 导致了游戏出错。\n\e\h") + Else + Results.Add("名称或 ID 为 " & Additional.First & " 的 Mod 导致了游戏出错:\n其配置文件 " & Additional(1) & " 存在异常,无法读取。") + End If + Case CrashReason.Mod初始化失败 + If Additional.Count = 1 Then + Results.Add("名为 " & Additional.First & " 的 Mod 初始化失败,导致游戏无法继续加载。\n\e\h") + Else + Results.Add("以下 Mod 初始化失败,导致游戏无法继续加载:\n - " & Join(Additional, "\n - ") & "\n\e\h") + End If + Case CrashReason.特定方块导致崩溃 + If Additional.Count = 1 Then + Results.Add("游戏似乎因为方块 " & Additional.First & " 出现了问题。\n\n你可以创建一个新世界,并观察游戏的运行情况:\n - 若正常运行,则是该方块导致出错,你或许需要使用一些方式删除此方块。\n - 若仍然出错,问题就可能来自其他原因……\h") + Else + Results.Add("游戏似乎因为世界中的某些方块出现了问题。\n\n你可以创建一个新世界,并观察游戏的运行情况:\n - 若正常运行,则是某些方块导致出错,你或许需要删除该世界。\n - 若仍然出错,问题就可能来自其他原因……\h") + End If + Case CrashReason.Mod重复安装 + If Additional.Count >= 2 Then + Results.Add("你重复安装了多个相同的 Mod:\n - " & Join(Additional, "\n - ") & "\n\n每个 Mod 只能出现一次,请删除重复的 Mod,然后再启动游戏。") + Else + Results.Add("你可能重复安装了多个相同的 Mod,导致游戏无法继续加载。\n\n每个 Mod 只能出现一次,请删除重复的 Mod,然后再启动游戏。\e\h") + End If + Case CrashReason.特定实体导致崩溃 + If Additional.Count = 1 Then + Results.Add("游戏似乎因为实体 " & Additional.First & " 出现了问题。\n\n你可以创建一个新世界,并生成一个该实体,然后观察游戏的运行情况:\n - 若正常运行,则是该实体导致出错,你或许需要使用一些方式删除此实体。\n - 若仍然出错,问题就可能来自其他原因……\h") + Else + Results.Add("游戏似乎因为世界中的某些实体出现了问题。\n\n你可以创建一个新世界,并生成各种实体,观察游戏的运行情况:\n - 若正常运行,则是某些实体导致出错,你或许需要删除该世界。\n - 若仍然出错,问题就可能来自其他原因……\h") + End If + Case CrashReason.OptiFine与Forge不兼容 + Results.Add("由于 OptiFine 与当前版本的 Forge 不兼容,导致了游戏崩溃。\n\n请前往 OptiFine 官网(https://optifine.net/downloads)查看 OptiFine 所兼容的 Forge 版本,并严格按照对应版本重新安装游戏。") + Case CrashReason.低版本Forge与高版本Java不兼容 + Results.Add("由于低版本 Forge 与当前 Java 不兼容,导致了游戏崩溃。\n\n请尝试以下解决方案:\n - 更新 Forge 到 36.2.26 或更高版本\n - 换用版本低于 8.0.320 的 Java") + Case CrashReason.版本Json中存在多个Forge + Results.Add("可能由于使用其他启动器修改了 Forge 版本,当前版本的文件存在异常,导致了游戏崩溃。\n请尝试重新全新安装 Forge,而非使用其他启动器修改 Forge 版本。") + Case CrashReason.玩家手动触发调试崩溃 + Results.Add("* 事实上,你的游戏没有任何问题,这是你自己触发的崩溃。\n* 你难道没有更重要的事要做吗?") + Case CrashReason.极短的程序输出 + Results.Add($"程序返回了以下信息:\n{Additional.First}\n\h") + Case CrashReason.OptiFine导致无法加载世界 'https://www.minecraftforum.net/forums/support/java-edition-support/3051132-exception-ticking-world + Results.Add("你所使用的 OptiFine 可能导致了你的游戏出现问题。\n\n该问题只在特定 OptiFine 版本中出现,你可以尝试更换 OptiFine 的版本。\h") + Case CrashReason.显卡驱动不支持导致无法设置像素格式, CrashReason.Intel驱动不兼容导致EXCEPTION_ACCESS_VIOLATION, CrashReason.AMD驱动不兼容导致EXCEPTION_ACCESS_VIOLATION, CrashReason.Nvidia驱动不兼容导致EXCEPTION_ACCESS_VIOLATION, CrashReason.显卡不支持OpenGL + If LogAll.Contains("hd graphics ") Then + Results.Add("你的显卡驱动存在问题,或未使用独立显卡,导致游戏无法正常运行。\n\n如果你的电脑存在独立显卡,请使用独立显卡而非 Intel 核显启动 PCL 与 Minecraft。\n如果问题依然存在,请尝试升级你的显卡驱动到最新版本,或回退到出厂版本。\n如果还是不行,还可以尝试使用 8.0.51 或更低版本的 Java。\h") + Else + Results.Add("你的显卡驱动存在问题,导致游戏无法正常运行。\n\n请尝试升级你的显卡驱动到最新版本,或回退到出厂版本,然后再启动游戏。\n如果还是不行,可以尝试使用 8.0.51 或更低版本的 Java。\n如果问题依然存在,那么你可能需要换个更好的显卡……\h") + End If + Case CrashReason.材质过大或显卡配置不足 + Results.Add("你所使用的材质分辨率过高,或显卡配置不足,导致游戏无法继续运行。\n\n如果你正在使用高清材质,请将它移除。\n如果你没有使用材质,那么你可能需要更新显卡驱动,或者换个更好的显卡……\h") + Case CrashReason.光影或资源包导致OpenGL1282错误 + Results.Add("你所使用的光影或材质导致游戏出现了一些问题……\n\n请尝试删除你所添加的这些额外资源。\h") + Case CrashReason.Mod过多导致超出ID限制 + Results.Add("你所安装的 Mod 过多,超出了游戏的 ID 限制,导致了游戏崩溃。\n请尝试安装 JEID 等修复 Mod,或删除部分大型 Mod。") + Case CrashReason.文件或内容校验失败 + Results.Add("部分文件或内容校验失败,导致游戏出现了问题。\n\n请尝试删除游戏(包括 Mod)并重新下载,或尝试在重新下载时使用 VPN。\h") + Case CrashReason.Fabric报错 + If Additional.Count = 1 Then + Results.Add("Fabric 提供了以下错误信息:\n" & Additional.First & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。") + Else + Results.Add("Fabric 可能已经提供了错误信息,请根据错误报告中的日志信息进行对应处理,如果看不懂英文可以使用翻译软件。\n如果没有看到报错信息,可以查看错误报告了解错误具体是如何发生的。\h") + End If + Case CrashReason.Mod加载器报错 + If Additional.Count = 1 Then + Results.Add("Mod 加载器提供了以下错误信息:\n" & Additional.First & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。") + Else + Results.Add("Mod 加载器可能已经提供了错误信息,请根据错误报告中的日志信息进行对应处理,如果看不懂英文可以使用翻译软件。\n如果没有看到报错信息,可以查看错误报告了解错误具体是如何发生的。\h") + End If + Case CrashReason.Fabric报错并给出解决方案 + If Additional.Count = 1 Then + Results.Add("Fabric 提供了以下解决方案:\n" & Additional.First & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。") + Else + Results.Add("Fabric 可能已经提供了解决方案,请根据错误报告中的日志信息进行对应处理,如果看不懂英文可以使用翻译软件。\n如果没有看到报错信息,可以查看错误报告了解错误具体是如何发生的。\h") + End If + Case CrashReason.Forge报错 + If Additional.Count = 1 Then + Results.Add("Forge 提供了以下错误信息:\n" & Additional.First & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。") + Else + Results.Add("Forge 可能已经提供了错误信息,请根据错误报告中的日志信息进行对应处理,如果看不懂英文可以使用翻译软件。\n如果没有看到报错信息,可以查看错误报告了解错误具体是如何发生的。\h") + End If + Case CrashReason.没有可用的分析文件 + Results.Add("你的游戏出现了一些问题,但 PCL 未能找到相关记录文件,因此无法进行分析。\h") + Case Else + Results.Add("PCL 获取到了没有详细信息的错误原因(" & CrashReasons.First.Key & "),请向 PCL 作者提交反馈以获取详情。\h") + End Select + Next - ResultString = ResultString. - Replace("\n", vbCrLf). - Replace("\h", If(IsHandAnalyze, "", vbCrLf & "如果要寻求帮助,请向他人发送错误报告文件,而不是发送这个窗口的截图。")). - Replace("\e", If(IsHandAnalyze, "", vbCrLf & "你可以查看错误报告了解错误具体是如何发生的。")). - Replace(vbCrLf, vbCr).Replace(vbLf, vbCr).Replace(vbCr, vbCrLf) - Return ResultString.Trim(vbCrLf.ToCharArray) + '整理多个原因 + IsHandAnalyze = False + Return Join(Results, "\n\n此外,"). + Replace("\n", vbCrLf). + Replace("\h", ""). + Replace("\e", If(IsHandAnalyze, "", vbCrLf & "你可以查看错误报告了解错误具体是如何发生的。")). + Replace(vbCrLf, vbCr).Replace(vbLf, vbCr).Replace(vbCr, vbCrLf). + Trim(vbCrLf.ToCharArray) & + If(Results.Any(Function(r) r.EndsWith("\h")), + If(IsHandAnalyze, "", vbCrLf & "如果要寻求帮助,请向他人发送错误报告文件,而不是发送这个窗口的截图。"), "") End Function End Class diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb index 576195c..1367161 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb @@ -13,7 +13,7 @@ Loop '检查 Json 是否标准 If Version.JsonObject("downloads") Is Nothing OrElse Version.JsonObject("downloads")("client") Is Nothing OrElse Version.JsonObject("downloads")("client")("url") Is Nothing Then - Throw New Exception("底层版本 " & Version.Name & " 中无 Jar 文件下载信息") + Throw New Exception("底层版本 " & Version.Name & " 中无 jar 文件下载信息") End If '检查文件 Dim Checker As New FileChecker(MinSize:=1024, ActualSize:=If(Version.JsonObject("downloads")("client")("size"), -1), Hash:=Version.JsonObject("downloads")("client")("sha1")) @@ -211,7 +211,7 @@ Try Dim Versions As JArray = Json("versions") If Versions.Count < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Json.ToString & ")") - '添加 PCL2 特供项 + '添加 PCL 特供项 If File.Exists(PathTemp & "Cache\download.json") Then Versions.Merge(GetJson(ReadFile(PathTemp & "Cache\download.json"))) '返回 Loader.Output = New DlClientListResult With {.IsOfficial = True, .SourceName = "Mojang 官方源", .Value = Json} @@ -244,7 +244,7 @@ Try Dim Versions As JArray = Json("versions") If Versions.Count < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Json.ToString & ")") - '添加 PCL2 特供项 + '添加 PCL 特供项 If File.Exists(PathTemp & "Cache\download.json") Then Versions.Merge(GetJson(ReadFile(PathTemp & "Cache\download.json"))) '返回 Loader.Output = New DlClientListResult With {.IsOfficial = False, .SourceName = "BMCLAPI", .Value = Json} @@ -274,10 +274,10 @@ Return Version("url").ToString End If Next - Log("未发现版本 " & Id & " 的 Json 下载地址,版本列表返回为:" & vbCrLf & DlClientListLoader.Output.Value.ToString, LogLevel.Debug) + Log("未发现版本 " & Id & " 的 json 下载地址,版本列表返回为:" & vbCrLf & DlClientListLoader.Output.Value.ToString, LogLevel.Debug) Return Nothing Catch ex As Exception - Log(ex, "获取版本 " & Id & " 的 Json 下载地址失败") + Log(ex, "获取版本 " & Id & " 的 json 下载地址失败") Return Nothing End Try End Function @@ -977,7 +977,7 @@ End Function Public Function DlSourceLauncherOrMetaGet(MojangBase As String, Optional IsStatic As Boolean = True) As String() - If MojangBase Is Nothing Then Throw New Exception("无对应的 Json 下载地址") + If MojangBase Is Nothing Then Throw New Exception("无对应的 json 下载地址") If IsStatic Then Return {MojangBase.Replace("https://launcher.mojang.com", "https://download.mcbbs.net").Replace("https://launchermeta.mojang.com", "https://download.mcbbs.net"), MojangBase, diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb index 0b1a90d..c056fc4 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb @@ -181,12 +181,6 @@ NextInner: RunInUiWait(Sub() Dim LoginInput As McLoginData = McLoginInput() CheckResult = McLoginAble(LoginInput) - If CheckResult = "" AndAlso - LoginInput.Type = McLoginType.Legacy AndAlso '离线登录 - Not RegexCheck(CType(LoginInput, McLoginLegacy).UserName, "^[0-9A-Za-z_]+$") AndAlso - McVersionCurrent.Version.McCodeMain >= 18 Then 'MC 1.18 以上 - CheckResult = "玩家名只能由数字、英文和下划线组成,不能包含中文或特殊字符!" - End If End Sub) If CheckResult <> "" Then Throw New ArgumentException(CheckResult) '求赞助 @@ -194,8 +188,8 @@ NextInner: 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, 1600, 1700, 1800, 1900, 2000 - If MyMsgBox("PCL2 已经为你启动了 " & Setup.Get("SystemLaunchCount") & " 次游戏啦!" & vbCrLf & - "如果觉得 PCL2 还算好用的话,也可以考虑小小地赞助一下作者呢 qwq……" & vbCrLf & + If MyMsgBox("PCL 已经为你启动了 " & Setup.Get("SystemLaunchCount") & " 次游戏啦!" & vbCrLf & + "如果觉得 PCL 还算好用的话,也可以考虑小小地赞助一下作者呢 qwq……" & vbCrLf & "毕竟一个人开发也不容易(小声)……", "求赞助啦……", "这就赞助!", "但是我拒绝") = 1 Then OpenWebsite("https://afdian.net/a/LTCat/plan") @@ -928,7 +922,7 @@ SystemBrowser: Try Result = NetRequestMuity("https://login.live.com/oauth20_token.srf", "POST", Request, "application/x-www-form-urlencoded", 1) Catch ex As Exception - If ex.Message.Contains("must sign in again") OrElse ex.Message.Contains("invalid_grant") Then 'Fix Hex-Dragon/PCL2#269 + If ex.Message.Contains("must sign in again") OrElse ex.Message.Contains("invalid_grant") Then '#269 Return {"Relogin", ""} Else Throw @@ -988,11 +982,11 @@ SystemBrowser: "该账号目前填写的年龄是否在 13 岁以上?", "登录提示", "13 岁以上", "12 岁以下", "我不知道") = 1 Then OpenWebsite("https://account.live.com/editprof.aspx") MyMsgBox("请在打开的网页中修改账号的出生日期(至少改为 18 岁以上)。" & vbCrLf & - "在修改成功后等待一分钟,然后再回到 PCL2,就可以正常登录了!", "登录提示") + "在修改成功后等待一分钟,然后再回到 PCL,就可以正常登录了!", "登录提示") Else OpenWebsite("https://support.microsoft.com/zh-cn/account-billing/如何更改-microsoft-帐户上的出生日期-837badbc-999e-54d2-2617-d19206b9540a") MyMsgBox("请根据打开的网页的说明,修改账号的出生日期(至少改为 18 岁以上)。" & vbCrLf & - "在修改成功后等待一分钟,然后再回到 PCL2,就可以正常登录了!", "登录提示") + "在修改成功后等待一分钟,然后再回到 PCL,就可以正常登录了!", "登录提示") End If Throw New Exception("$$") Else @@ -1373,7 +1367,7 @@ SystemBrowser: '添加 MainClass If Version.JsonObject("mainClass") Is Nothing Then - Throw New Exception("版本 Json 中没有 mainClass 项!") + Throw New Exception("版本 json 中没有 mainClass 项!") Else DataList.Add(Version.JsonObject("mainClass")) End If @@ -1461,7 +1455,7 @@ NextVersion: '添加 MainClass If Version.JsonObject("mainClass") Is Nothing Then - Throw New Exception("版本 Json 中没有 mainClass 项!") + Throw New Exception("版本 json 中没有 mainClass 项!") Else Result += " " & Version.JsonObject("mainClass").ToString End If @@ -1579,7 +1573,7 @@ NextVersion: GameArguments.Add("${natives_directory}", Version.Path & Version.Name & "-natives") GameArguments.Add("${library_directory}", PathMcFolder & "libraries") GameArguments.Add("${libraries_directory}", PathMcFolder & "libraries") - GameArguments.Add("${launcher_name}", "PCL2") + GameArguments.Add("${launcher_name}", "PCL") GameArguments.Add("${launcher_version}", VersionCode) GameArguments.Add("${version_name}", Version.Name) Dim ArgumentInfo As String = Setup.Get("VersionArgumentInfo", Version:=McVersionCurrent) @@ -1837,19 +1831,20 @@ NextVersion: PackFormat = 9 End If Case 19, 99 '99 是快照版 - If McVersionCurrent.Version.McCodeSub <= 2 Then - PackFormat = 9 + If McVersionCurrent.Version.McCodeSub <= 3 Then + PackFormat = 10 Else PackFormat = 12 End If Case Else PackFormat = 13 + 'https://minecraft.fandom.com/zh/wiki/数据包#数据包版本 End Select McLaunchLog("正在构建自定义皮肤资源包,格式为:" & PackFormat) '准备文件 Dim Bit As New MyBitmap(PathImage & "Heads/Logo.png") Bit.Save(PackPicAddress) - WriteFile(MetaFileAddress, "{""pack"":{""pack_format"":" & PackFormat & ",""description"":""PCL2 自定义离线皮肤资源包""}}") + WriteFile(MetaFileAddress, "{""pack"":{""pack_format"":" & PackFormat & ",""description"":""PCL 自定义离线皮肤资源包""}}") Dim Skin As New MyBitmap(PathAppdata & "CustomSkin.png") If (McVersionCurrent.Version.McCodeMain = 6 OrElse McVersionCurrent.Version.McCodeMain = 7) AndAlso Skin.Pic.Height = 64 Then McLaunchLog("该 Minecraft 版本不支持双层皮肤,已进行裁剪") @@ -1861,7 +1856,24 @@ NextVersion: Using ZipAr As New ZipArchive(ZipFile, ZipArchiveMode.Create) ZipAr.CreateEntryFromFile(MetaFileAddress, "pack.mcmeta") ZipAr.CreateEntryFromFile(PackPicAddress, "pack.png") - ZipAr.CreateEntryFromFile(Path & "PCL\CustomSkin_Cliped.png", "assets/minecraft/textures/entity/" & If(Setup.Get("LaunchSkinSlim"), "alex.png", "steve.png")) + '1.19.3+ 使用复杂版本的替换 + Dim IsOldType As Boolean + Select Case McVersionCurrent.Version.McCodeMain + Case Is < 19 + IsOldType = True + Case 19 + IsOldType = McVersionCurrent.Version.McCodeSub <= 2 + Case Else + IsOldType = False + End Select + If IsOldType Then + ZipAr.CreateEntryFromFile(Path & "PCL\CustomSkin_Cliped.png", "assets/minecraft/textures/entity/" & If(Setup.Get("LaunchSkinSlim"), "alex.png", "steve.png")) + Else + For Each SkinName In {"alex", "ari", "efe", "kai", "makena", "noor", "steve", "sunny", "zuri"} + ZipAr.CreateEntryFromFile(Path & "PCL\CustomSkin_Cliped.png", + $"assets/minecraft/textures/entity/player/{If(Setup.Get("LaunchSkinSlim"), "slim", "wide")}/{SkinName}.png") + Next + End If End Using End Using File.Delete(Path & "PCL\CustomSkin_Cliped.png") @@ -1917,8 +1929,10 @@ IgnoreCustomSkin: Private Sub McLaunchCustom(Loader As LoaderTask(Of Integer, Integer)) '获取自定义命令 - Dim CustomCommand As String = Setup.Get("VersionAdvanceRun", Version:=McVersionCurrent) - If CustomCommand <> "" Then CustomCommand = ArgumentReplace(CustomCommand) + Dim CustomCommandGlobal As String = Setup.Get("LaunchAdvanceRun") + If CustomCommandGlobal <> "" Then CustomCommandGlobal = ArgumentReplace(CustomCommandGlobal) + Dim CustomCommandVersion As String = Setup.Get("VersionAdvanceRun", Version:=McVersionCurrent) + If CustomCommandVersion <> "" Then CustomCommandVersion = ArgumentReplace(CustomCommandVersion) '输出 bat Try @@ -1928,7 +1942,8 @@ IgnoreCustomSkin: "echo 游戏正在启动,请稍候。" & vbCrLf & "set APPDATA=""" & PathMcFolder & """" & vbCrLf & "cd /D """ & PathMcFolder & """" & vbCrLf & - CustomCommand & vbCrLf & + CustomCommandGlobal & vbCrLf & + CustomCommandVersion & vbCrLf & """" & McLaunchJavaSelected.PathJava & """ " & McLaunchArgument & vbCrLf & "echo 游戏已退出。" & vbCrLf & "pause" @@ -1947,23 +1962,52 @@ IgnoreCustomSkin: End Try '执行自定义命令 - If CustomCommand <> "" Then - McLaunchLog("正在执行自定义命令:" & CustomCommand) + If CustomCommandGlobal <> "" Then + McLaunchLog("正在执行全局自定义命令:" & CustomCommandGlobal) + Dim CustomProcess As New Process Try - Dim Cmd As New Process - Cmd.StartInfo.FileName = "cmd.exe" - Cmd.StartInfo.Arguments = "/c """ & CustomCommand & """" - Cmd.StartInfo.WorkingDirectory = Path - Cmd.StartInfo.UseShellExecute = False - Cmd.StartInfo.CreateNoWindow = True - Cmd.Start() - If Setup.Get("VersionAdvanceRunWait", Version:=McVersionCurrent) Then - Do Until Cmd.HasExited OrElse Loader.IsAborted + CustomProcess.StartInfo.FileName = "cmd.exe" + CustomProcess.StartInfo.Arguments = "/c """ & CustomCommandGlobal & """" + CustomProcess.StartInfo.WorkingDirectory = Path + CustomProcess.StartInfo.UseShellExecute = False + CustomProcess.StartInfo.CreateNoWindow = True + CustomProcess.Start() + If Setup.Get("LaunchAdvanceRunWait") Then + Do Until CustomProcess.HasExited OrElse Loader.IsAborted Thread.Sleep(10) Loop End If Catch ex As Exception - Log(ex, "执行自定义命令失败", LogLevel.Hint) + Log(ex, "执行全局自定义命令失败", LogLevel.Hint) + Finally + If Not CustomProcess.HasExited AndAlso Loader.IsAborted Then + McLaunchLog("由于取消启动,已强制结束自定义命令 CMD 进程") '#1183 + CustomProcess.Kill() + End If + End Try + End If + If CustomCommandVersion <> "" Then + McLaunchLog("正在执行版本自定义命令:" & CustomCommandVersion) + Dim CustomProcess As New Process + Try + CustomProcess.StartInfo.FileName = "cmd.exe" + CustomProcess.StartInfo.Arguments = "/c """ & CustomCommandVersion & """" + CustomProcess.StartInfo.WorkingDirectory = Path + CustomProcess.StartInfo.UseShellExecute = False + CustomProcess.StartInfo.CreateNoWindow = True + CustomProcess.Start() + If Setup.Get("VersionAdvanceRunWait", Version:=McVersionCurrent) Then + Do Until CustomProcess.HasExited OrElse Loader.IsAborted + Thread.Sleep(10) + Loop + End If + Catch ex As Exception + Log(ex, "执行版本自定义命令失败", LogLevel.Hint) + Finally + If Not CustomProcess.HasExited AndAlso Loader.IsAborted Then + McLaunchLog("由于取消启动,已强制结束自定义命令 CMD 进程") '#1183 + CustomProcess.Kill() + End If End Try End If @@ -2020,7 +2064,7 @@ IgnoreCustomSkin: '输出信息 McLaunchLog("") McLaunchLog("~ 基础参数 ~") - McLaunchLog("PCL2 版本:" & VersionDisplayName & " (" & VersionCode & ")") + McLaunchLog("PCL 版本:" & VersionDisplayName & " (" & VersionCode & ")") McLaunchLog("游戏版本:" & McVersionCurrent.Version.ToString & "(" & McVersionCurrent.Version.McCodeMain & "." & McVersionCurrent.Version.McCodeSub & ")") McLaunchLog("资源版本:" & McAssetsGetIndexName(McVersionCurrent)) McLaunchLog("版本继承:" & If(McVersionCurrent.InheritVersion = "", "无", McVersionCurrent.InheritVersion)) diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb index c629c63..9e8e894 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb @@ -6,7 +6,7 @@ Public Module ModMinecraft '列表初始化 - Public JavaListCacheVersion As Integer = 2 + Public JavaListCacheVersion As Integer = 3 ''' ''' 目前所有可用的 Java。 ''' @@ -46,12 +46,23 @@ Public Module ModMinecraft ''' Private ReadOnly Property PathEnv As String Get - If _PathEnv Is Nothing Then _PathEnv = Environment.GetEnvironmentVariable("Path") + If _PathEnv Is Nothing Then _PathEnv = If(Environment.GetEnvironmentVariable("Path"), "") Return _PathEnv End Get End Property Private _PathEnv As String = Nothing + ''' + ''' JAVA_HOME 环境变量。 + ''' + Private ReadOnly Property PathJavaHome As String + Get + If _PathJavaHome Is Nothing Then _PathJavaHome = If(Environment.GetEnvironmentVariable("JAVA_HOME"), "") + Return _PathJavaHome + End Get + End Property + Private _PathJavaHome As String = Nothing + Public Class JavaEntry '路径 @@ -216,8 +227,9 @@ Public Module ModMinecraft #Region "模糊查找可能可用的 Java" '查找环境变量中的 Java - For Each PathInEnv As String In Split(PathEnv.Replace("\\", "\").Replace("/", "\"), ";") + For Each PathInEnv As String In Split((PathEnv & ";" & PathJavaHome).Replace("\\", "\").Replace("/", "\"), ";") PathInEnv = PathInEnv.Trim(" """.ToCharArray()) + If PathInEnv = "" Then Continue For If Not PathInEnv.EndsWith("\") Then PathInEnv += "\" '粗略检查有效性 If File.Exists(PathInEnv & "javaw.exe") Then JavaPreList(PathInEnv) = False @@ -235,7 +247,7 @@ Public Module ModMinecraft JavaSearchFolder(PathMcFolder, JavaPreList, False, IsFullSearch:=True) End If - '若[不全]为符号链接,则清除符号链接的地址 + '若不全为符号链接,则清除符号链接的地址 Dim JavaWithoutReparse As New Dictionary(Of String, Boolean) For Each Pair In JavaPreList Dim Folder As String = Pair.Key.Replace("\\", "\").Replace("/", "\") @@ -252,14 +264,14 @@ Public Module ModMinecraft Next If JavaWithoutReparse.Count > 0 Then JavaPreList = JavaWithoutReparse - '若不全为 javapath_target,则清除二重引用的地址 + '若不全为特殊引用,则清除特殊引用的地址 Dim JavaWithoutInherit As New Dictionary(Of String, Boolean) For Each Pair In JavaPreList - If Pair.Key.Contains("javapath_target_") Then - Log("[Java] 位于 " & Pair.Key & " 的 Java 包含二重引用") + If Pair.Key.Contains("javapath_target_") OrElse Pair.Key.Contains("javatmp") Then + Log("[Java] 位于 " & Pair.Key & " 的 Java 包含特殊引用") Continue For End If - Log("[Java] 位于 " & Pair.Key & " 的 Java 不含二重引用") + Log("[Java] 位于 " & Pair.Key & " 的 Java 不含特殊引用") JavaWithoutInherit.Add(Pair.Key, Pair.Value) Next If JavaWithoutInherit.Count > 0 Then JavaPreList = JavaWithoutInherit @@ -508,6 +520,9 @@ RetryGet: '指定的 Java 不可用,弹窗要求选择 Log("[Java] 发现用户指定的不兼容 Java:" & UserJava.ToString) + For Each Java In AllowedJavaList + Log($"[Java] 实际可用的 Java:{Java.ToString}") + Next Dim Requirement As String = "" Dim ShowRevision As Boolean = False If (MinVersion Is Nothing OrElse MinVersion.Minor = 0) AndAlso (MaxVersion IsNot Nothing AndAlso MaxVersion.Minor < 999) Then @@ -529,12 +544,12 @@ RetryGet: AllowedJavaList = New List(Of JavaEntry) From {UserJava} Else Select Case MyMsgBox("你在设置中手动指定了使用 Java " & JavaCurrent & ",但当前" & Requirement & "。" & vbCrLf & - "如果强制使用该 Java,可能会导致出现异常。" & vbCrLf & - "你可以将 游戏 Java 设置修改为 自动选择合适的 Java 来避免这一提示。" & vbCrLf & + "如果强制使用该 Java,可能导致游戏崩溃。" & vbCrLf & + "你也可以将 游戏 Java 设置修改为 自动选择合适的 Java。" & vbCrLf & vbCrLf & " - 指定的 Java:" & UserJava.ToString, - "Java 兼容性警告", "让 PCL2 自动选择", "强制使用该 Java", "取消") - Case 1 '让 PCL2 自动选择 + "Java 兼容性警告", "让 PCL 自动选择", "强制使用该 Java", "取消") + Case 1 '让 PCL 自动选择 Case 2 '强制使用指定的 Java Log("[Java] 已强制使用用户指定的不兼容 Java") AllowedJavaList = New List(Of JavaEntry) From {UserJava} @@ -662,8 +677,8 @@ NoUserJava: Public Sub JavaMissing(VersionCode As Integer) Select Case VersionCode Case 7 - MyMsgBox("PCL2 未找到 Java 7。" & vbCrLf & - "请自行百度安装 Java 7,安装后在 PCL2 的 设置 → 启动设置 → 游戏 Java 中通过搜索或导入,确保安装的 Java 已列入 Java 列表。", + MyMsgBox("PCL 未找到 Java 7。" & vbCrLf & + "请自行百度安装 Java 7,安装后在 PCL 的 设置 → 启动设置 → 游戏 Java 中通过搜索或导入,确保安装的 Java 已列入 Java 列表。", "未找到 Java") Case 8 '291 If Is32BitSystem Then @@ -671,17 +686,17 @@ NoUserJava: Else OpenWebsite("https://wwa.lanzoui.com/i2UyMq0jaqb") End If - MyMsgBox("PCL2 未找到版本适宜的 Java 8。" & vbCrLf & - "请在打开的网页中下载安装包并安装,安装后在 PCL2 的 设置 → 启动设置 → 游戏 Java 中通过搜索或导入,确保安装的 Java 已列入 Java 列表。", + MyMsgBox("PCL 未找到版本适宜的 Java 8。" & vbCrLf & + "请在打开的网页中下载安装包并安装,安装后在 PCL 的 设置 → 启动设置 → 游戏 Java 中通过搜索或导入,确保安装的 Java 已列入 Java 列表。", "未找到 Java") Case 16, 17 If Is32BitSystem Then MyMsgBox("该版本的 MC 已不支持 32 位操作系统。你必须增加内存并重装为 64 位系统才能继续。", "系统兼容性提示") Else OpenWebsite("https://www.oracle.com/java/technologies/downloads/#java17") - MyMsgBox("PCL2 未找到 Java 17。" & vbCrLf & + MyMsgBox("PCL 未找到 Java 17。" & vbCrLf & "请在打开的网页中选择 x64 Installer,下载并安装。" & vbCrLf & - "安装后在 PCL2 的 设置 → 启动设置 → 游戏 Java 中通过搜索或导入,确保安装的 Java 已列入 Java 列表。", + "安装后在 PCL 的 设置 → 启动设置 → 游戏 Java 中通过搜索或导入,确保安装的 Java 已列入 Java 列表。", "未找到 Java") End If End Select @@ -812,15 +827,15 @@ NoUserJava: Dim ResultJson As String = "{ ""profiles"": { - ""PCL2"": { + ""PCL"": { ""icon"": ""Grass"", - ""name"": ""PCL2"", + ""name"": ""PCL"", ""lastVersionId"": ""latest-release"", ""type"": ""latest-release"", ""lastUsed"": """ & Date.Now.ToString("yyyy-MM-dd") & "T" & Date.Now.ToString("HH:mm:ss") & ".0000Z"" } }, - ""selectedProfile"": ""PCL2"", + ""selectedProfile"": ""PCL"", ""clientToken"": ""23323323323323323323323323323333"" }" WriteFile(Folder & "launcher_profiles.json", ResultJson, Encoding:=Encoding.GetEncoding("GB18030")) @@ -1136,21 +1151,21 @@ VersionSearchFinish: Public Property JsonText As String Get 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 RunInUi() Then - Log("[Minecraft] 版本 Json 文件为空或读取失败,由于代码在主线程运行,将不再进行重试", LogLevel.Debug) + Log("[Minecraft] 版本 json 文件为空或读取失败,由于代码在主线程运行,将不再进行重试", LogLevel.Debug) Throw New Exception("版本 Json 文件为空或读取失败") 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 _JsonText.Length = 0 Then Throw New Exception("版本 json 文件为空或读取失败") End If End If - If _JsonText.Length < 100 Then Throw New Exception("版本 Json 文件有误,内容为:" & _JsonText) + If _JsonText.Length < 100 Then Throw New Exception("版本 json 文件有误,内容为:" & _JsonText) End If Return _JsonText End Get @@ -1222,10 +1237,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 - Throw New Exception("版本 Json 不规范(" & If(Name, "null") & ")", ex) + Throw New Exception("版本 json 不规范(" & If(Name, "null") & ")", ex) End Try Try '处理 JumpLoader @@ -1317,7 +1332,7 @@ Recheck: CheckPermissionWithException(Path & "PCL\") Catch ex As Exception State = McVersionState.Error - Info = "PCL2 没有对该文件夹的访问权限,请右键以管理员身份运行 PCL2" + Info = "PCL 没有对该文件夹的访问权限,请右键以管理员身份运行 PCL" Log(ex, "没有访问版本文件夹的权限") Return False End Try @@ -1325,7 +1340,7 @@ Recheck: Try Dim JsonObjCheck = JsonObject Catch ex As Exception - Log(ex, "版本 Json 可用性检查失败(" & Path & ")") + Log(ex, "版本 json 可用性检查失败(" & Path & ")") JsonText = "" JsonObject = Nothing Info = ex.Message @@ -1747,11 +1762,7 @@ Reload: '改变当前选择的版本 OnLoaded: - If McVersionList.Count = 0 Then - McVersionCurrent = Nothing - Setup.Set("LaunchVersionSelect", "") - Log("[Minecraft] 未找到可用 Minecraft 版本") - Else + If McVersionList.Any(Function(v) v.Key <> McVersionCardType.Error) Then '不能判断 Count = 0,这在只有错误版本时导致了误判 '尝试读取已储存的选择 Dim SavedSelection As String = ReadIni(Path & "PCL.ini", "Version") If Not SavedSelection = "" Then @@ -1773,6 +1784,10 @@ OnLoaded: Setup.Set("LaunchVersionSelect", McVersionCurrent.Name) Log("[Launch] 自动选择 Minecraft 版本:" & McVersionCurrent.Path) End If + Else + McVersionCurrent = Nothing + Setup.Set("LaunchVersionSelect", "") + Log("[Minecraft] 未找到可用 Minecraft 版本") End If If Setup.Get("SystemDebugDelay") Then Thread.Sleep(RandomInteger(200, 3000)) Catch ex As ThreadInterruptedException @@ -2131,27 +2146,16 @@ NextVersion: ''' ''' 要求玩家选择一个皮肤文件,并进行相关校验。 ''' - Public Function McSkinSelect(MustHaveTwoLayers As Boolean) As McSkinInfo + Public Function McSkinSelect() As McSkinInfo Dim FileName As String = SelectFile("皮肤文件(*.png)|*.png", "选择皮肤文件") '验证有效性 If FileName = "" Then Return New McSkinInfo With {.IsVaild = False} Try Dim Image As New MyBitmap(FileName) - If MustHaveTwoLayers Then - If Image.Pic.Width = 64 AndAlso Image.Pic.Height = 32 Then - Hint("自定义离线皮肤只支持 64x64 像素的双层皮肤!", HintType.Critical) - Return New McSkinInfo With {.IsVaild = False} - End If - If Image.Pic.Width <> 64 OrElse Image.Pic.Height <> 64 Then - Hint("自定义离线皮肤图片大小应为 64x64 像素!", HintType.Critical) - Return New McSkinInfo With {.IsVaild = False} - End If - Else - If Image.Pic.Width <> 64 OrElse Not (Image.Pic.Height = 32 OrElse Image.Pic.Height = 64) Then - Hint("皮肤图片大小应为 64x32 像素或 64x64 像素!", HintType.Critical) - Return New McSkinInfo With {.IsVaild = False} - End If + If Image.Pic.Width <> 64 OrElse Not (Image.Pic.Height = 32 OrElse Image.Pic.Height = 64) Then + Hint("皮肤图片大小应为 64x32 像素或 64x64 像素!", HintType.Critical) + Return New McSkinInfo With {.IsVaild = False} End If Dim FileInfo As New FileInfo(FileName) If FileInfo.Length > 24 * 1024 Then @@ -2403,7 +2407,7 @@ NextVersion: '不能调用 RealVersion.Check(),可能会莫名其妙地触发 CheckPermission 正被另一进程使用,导致误判前置不存在 If Not File.Exists(RealVersion.Path & RealVersion.Name & ".json") Then RealVersion = Version - Log("[Minecraft] 可能缺少前置版本 " & RealVersion.Name & ",找不到对应的 Json 文件", LogLevel.Debug) + Log("[Minecraft] 可能缺少前置版本 " & RealVersion.Name & ",找不到对应的 json 文件", LogLevel.Debug) End If '获取详细下载信息 If RealVersion.JsonObject("downloads") IsNot Nothing AndAlso RealVersion.JsonObject("downloads")("client") IsNot Nothing Then @@ -2545,7 +2549,7 @@ NextVersion: Dim MainJar As NetFile = DlClientJarGet(Version, True) If MainJar IsNot Nothing Then Result.Add(MainJar) Catch ex As Exception - Log(ex, "版本缺失主 Jar 文件所必须的信息", LogLevel.Developer) + Log(ex, "版本缺失主 jar 文件所必须的信息", LogLevel.Developer) End Try If CoreJarOnly Then Return Result @@ -3116,7 +3120,7 @@ NextVersion: If Jar.Entries.Count = 0 Then Throw New FileFormatException("文件内容为空") Catch ex As UnauthorizedAccessException Log(ex, "Mod 文件由于无权限无法打开(" & Path & ")", LogLevel.Developer) - _FileUnavailableReason = New UnauthorizedAccessException("没有读取此文件的权限,请尝试右键以管理员身份运行 PCL2", ex) + _FileUnavailableReason = New UnauthorizedAccessException("没有读取此文件的权限,请尝试右键以管理员身份运行 PCL", ex) Jar = Nothing Catch ex As Exception Log(ex, "Mod 文件无法打开(" & Path & ")", LogLevel.Developer) @@ -3751,27 +3755,15 @@ VersionFindFail: '进行提示 If Version Is Nothing Then Exit Sub Dim Time As Date = Version("releaseTime") - Dim MsgResult As Integer - If (Date.Now - Time).TotalDays > 1 Then - '1 天以上 - MsgResult = MyMsgBox("新版本:" & VersionName & vbCrLf & "更新时间:" & Time.ToString, "Minecraft 更新提示", "下载", "更新日志", "取消") - ElseIf (Date.Now - Time).TotalHours > 3 Then - '1 天 ~ 3 小时 - MsgResult = MyMsgBox("新版本:" & VersionName & vbCrLf & "更新于:" & GetTimeSpanString(Time - Date.Now), "Minecraft 更新提示", "下载", "更新日志", "取消") - Else - '不到 3 小时 - MsgResult = MyMsgBox("新版本:" & VersionName & vbCrLf & "更新于:" & GetTimeSpanString(Time - Date.Now), "Minecraft 更新提示", "下载", "取消") - If MsgResult = 2 Then MsgResult = 3 '把 “取消” 移位 - End If + Dim MsgBoxText As String = $"新版本:{VersionName}{vbCrLf}" & + If((Date.Now - Time).TotalDays > 1, "更新时间:" & Time.ToString, "更新于:" & GetTimeSpanString(Time - Date.Now)) + Dim MsgResult = MyMsgBox(MsgBoxText, "Minecraft 更新提示", "确定", "下载", If((Date.Now - Time).TotalHours > 3, "更新日志", ""), + Button3Action:=Sub() McUpdateLogShow(Version)) '弹窗结果 - Select Case MsgResult - Case 1 - '下载 - McDownloadClient(NetPreDownloadBehaviour.HintWhileExists, VersionName, Version("url").ToString) - Case 2 - '更新日志 - McUpdateLogShow(Version) - End Select + If MsgResult = 2 Then + '下载 + McDownloadClient(NetPreDownloadBehaviour.HintWhileExists, VersionName, Version("url").ToString) + End If Catch ex As Exception Log(ex, "Minecraft 更新提示发送失败(" & If(VersionName, "Nothing") & ")", LogLevel.Feedback) diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb index fe23ef2..399d0b7 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb @@ -60,7 +60,7 @@ Log(ex, "打开整合包文件失败", If(ShowHint, LogLevel.Hint, LogLevel.Normal)) Return False ElseIf File.ToLower.EndsWith(".rar") Then - Log(ex, "PCL2 无法处理 rar 格式的压缩包,请在解压后重新压缩为 zip 格式再试", If(ShowHint, LogLevel.Hint, LogLevel.Normal)) + Log(ex, "PCL 无法处理 rar 格式的压缩包,请在解压后重新压缩为 zip 格式再试", If(ShowHint, LogLevel.Hint, LogLevel.Normal)) Return False Else Log(ex, "打开整合包文件失败,文件可能损坏或为不支持的压缩包格式", If(ShowHint, LogLevel.Hint, LogLevel.Normal)) @@ -263,8 +263,10 @@ Retry: IsResourcePack = False End If '实际的添加 - FileList.Add(ModJson("id"), New CompFile(ModJson, CompType.Mod).ToNetFile( - PathMcFolder & "versions\" & VersionName & "\" & If(IsResourcePack, "resourcepacks", "mods") & "\")) + Dim File As New CompFile(ModJson, CompType.Mod) + If Not File.Available Then Continue For + FileList.Add(ModJson("id"), + File.ToNetFile($"{PathMcFolder}versions\{VersionName}\{If(IsResourcePack, "resourcepacks", "mods")}\")) Task.Progress += 1 / (1 + ModFileList.Count) Next Task.Output = FileList.Values.ToList @@ -358,7 +360,7 @@ Retry: Select Case Entry.Name.ToLower Case "minecraft" MinecraftVersion = Entry.Value.ToString - Case "forge" 'eg. 14.23.5.2859 + Case "forge" 'eg. 14.23.5.2859 / 1.19-41.1.0 ForgeVersion = Entry.Value.ToString Log("[ModPack] 整合包 Forge 版本:" & ForgeVersion) Case "fabric-loader" 'eg. 0.14.14 diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb index 72cb75c..2e28628 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb @@ -206,7 +206,7 @@ 'Log(Text) '关闭与崩溃检测 If Not Text.Contains("[CHAT]") Then - If Text.Contains("Someone is closing me!") Then + If Text.Contains("Someone is closing me!") OrElse Text.Contains("Restarting Minecraft with command") Then '#1258 WatcherLog("识别为关闭的 Log:" & Text) State = MinecraftState.Ended ElseIf Text.Contains("Crash report saved to") OrElse Text.Contains("This crash report has been saved to:") Then diff --git a/Plain Craft Launcher 2/Modules/ModEvent.vb b/Plain Craft Launcher 2/Modules/ModEvent.vb index 5e02778..9c19995 100644 --- a/Plain Craft Launcher 2/Modules/ModEvent.vb +++ b/Plain Craft Launcher 2/Modules/ModEvent.vb @@ -64,13 +64,13 @@ Case "下载文件" Data(0) = Data(0).Replace("\", "/") If Not (Data(0).StartsWith("http://") OrElse Data(0).StartsWith("https://")) Then - MyMsgBox("EventData 必须为以 http:// 或 https:// 开头的网址。" & vbCrLf & "PCL2 不支持其他乱七八糟的协议。", "事件执行失败") + MyMsgBox("EventData 必须为以 http:// 或 https:// 开头的网址。" & vbCrLf & "PCL 不支持其他乱七八糟的协议。", "事件执行失败") Exit Sub End If PageOtherTest.StartCustomDownload(Data(0), GetFileNameFromPath(Data(0))) Case Else - MyMsgBox("未知的事件类型:" & Type & vbCrLf & "请检查事件类型填写是否正确,或者 PCL2 是否为最新版本。", "事件执行失败") + MyMsgBox("未知的事件类型:" & Type & vbCrLf & "请检查事件类型填写是否正确,或者 PCL 是否为最新版本。", "事件执行失败") End Select Catch ex As Exception Log(ex, "事件执行失败", LogLevel.Msgbox) @@ -121,6 +121,7 @@ '确认实际路径 Dim Location As String, WorkingDir As String = Path & "PCL" + HelpTryExtract() If RelativeUrl.Contains(":\") Then '绝对路径 Location = RelativeUrl diff --git a/Plain Craft Launcher 2/Modules/ModMusic.vb b/Plain Craft Launcher 2/Modules/ModMusic.vb index 677edc0..dc64b69 100644 --- a/Plain Craft Launcher 2/Modules/ModMusic.vb +++ b/Plain Craft Launcher 2/Modules/ModMusic.vb @@ -265,9 +265,9 @@ If CurrentWave.PlaybackState = NAudio.Wave.PlaybackState.Stopped AndAlso MusicAllList.Count > 0 Then MusicStartPlay(DequeueNextMusicAddress) Catch ex As Exception If ex.Message.Contains("Got a frame at sample rate") OrElse ex.Message.Contains("does not support changes to") Then - Hint("播放音乐失败(" & GetFileNameFromPath(MusicCurrent) & "):PCL2 不支持播放音频属性在中途发生变化的音乐", HintType.Critical) + Hint("播放音乐失败(" & GetFileNameFromPath(MusicCurrent) & "):PCL 不支持播放音频属性在中途发生变化的音乐", HintType.Critical) ElseIf Not (MusicCurrent.ToLower.EndsWith(".wav") OrElse MusicCurrent.ToLower.EndsWith(".mp3") OrElse MusicCurrent.ToLower.EndsWith(".flac")) Then - Hint("播放音乐失败(" & GetFileNameFromPath(MusicCurrent) & "):PCL2 可能不支持此音乐格式,请将格式转换为 .wav、.mp3 或 .flac 后再试", HintType.Critical) + Hint("播放音乐失败(" & GetFileNameFromPath(MusicCurrent) & "):PCL 可能不支持此音乐格式,请将格式转换为 .wav、.mp3 或 .flac 后再试", HintType.Critical) Else Log(ex, "播放音乐失败(" & GetFileNameFromPath(MusicCurrent) & ")", LogLevel.Hint) End If diff --git a/Plain Craft Launcher 2/My Project/AssemblyInfo.vb b/Plain Craft Launcher 2/My Project/AssemblyInfo.vb index 3b27ac5..aa4a463 100644 --- a/Plain Craft Launcher 2/My Project/AssemblyInfo.vb +++ b/Plain Craft Launcher 2/My Project/AssemblyInfo.vb @@ -51,6 +51,6 @@ Imports System.Runtime.InteropServices ' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 ' 方法是按如下所示使用“*” - - + + diff --git a/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb b/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb index 8d3bb4b..9e40572 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb +++ b/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb @@ -37,7 +37,7 @@ Public Module ModDownloadLib '已有版本检查 If Behaviour <> NetPreDownloadBehaviour.IgnoreCheck AndAlso File.Exists(VersionFolder & Id & ".json") AndAlso File.Exists(VersionFolder & Id & ".jar") Then If Behaviour = NetPreDownloadBehaviour.ExitWhileExistsOrDownloading Then Return Nothing - If MyMsgBox("版本 " & Id & " 已存在,是否重新下载?" & vbCrLf & "这会覆盖版本的 Json 与 Jar 文件,但不会影响版本隔离的文件。", "版本已存在", "继续", "取消") = 1 Then + If MyMsgBox("版本 " & Id & " 已存在,是否重新下载?" & vbCrLf & "这会覆盖版本的 json 与 jar 文件,但不会影响版本隔离的文件。", "版本已存在", "继续", "取消") = 1 Then File.Delete(VersionFolder & Id & ".jar") File.Delete(VersionFolder & Id & ".json") Else @@ -82,14 +82,14 @@ Public Module ModDownloadLib Dim Loaders As New List(Of LoaderBase) '下载版本 Json 文件 - Loaders.Add(New LoaderDownload("下载版本 Json 文件", New List(Of NetFile) From { + Loaders.Add(New LoaderDownload("下载版本 json 文件", New List(Of NetFile) From { New NetFile(DlSourceLauncherOrMetaGet(JsonUrl), VersionFolder & Id & ".json", New FileChecker(CanUseExistsFile:=False, IsJson:=True)) }) With {.ProgressWeight = 2}) '获取支持库文件地址 - Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("分析核心 Jar 文件下载地址", + Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("分析核心 jar 文件下载地址", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionFolder), True)) With {.ProgressWeight = 0.5, .Show = False}) '下载支持库文件 - Loaders.Add(New LoaderDownload("下载核心 Jar 文件", New List(Of NetFile)) With {.ProgressWeight = 5}) + Loaders.Add(New LoaderDownload("下载核心 jar 文件", New List(Of NetFile)) With {.ProgressWeight = 5}) '启动 Dim Loader As New LoaderCombo(Of String)("Minecraft " & Id & " 下载", Loaders) With {.OnStateChanged = AddressOf DownloadStateSave} @@ -115,7 +115,7 @@ Public Module ModDownloadLib '下载版本 Json 文件 If JsonUrl Is Nothing Then - Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("获取原版 Json 文件下载地址", + Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("获取原版 json 文件下载地址", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Dim JsonAddress As String = DlClientListGet(Id) Task.Output = New List(Of NetFile) From {New NetFile(DlSourceLauncherOrMetaGet(JsonAddress), VersionFolder & VersionName & ".json")} @@ -166,7 +166,7 @@ Public Module ModDownloadLib End Function Private Const McDownloadClientLibName As String = "下载原版支持库文件" - Private Const McDownloadClientJsonName As String = "下载原版 Json 文件" + Private Const McDownloadClientJsonName As String = "下载原版 json 文件" #End Region @@ -192,7 +192,7 @@ Public Module ModDownloadLib Else NewItem.Info = Entry("lore").ToString End If - If Entry("url").ToString.Contains("pcl") Then NewItem.Info = "[PCL2 特别提供] " & NewItem.Info + If Entry("url").ToString.Contains("pcl") Then NewItem.Info = "[PCL 特别提供] " & NewItem.Info AddHandler NewItem.Click, OnClick '建立菜单 If IsSaveOnly Then @@ -333,7 +333,7 @@ Public Module ModDownloadLib '已有版本检查 If File.Exists(VersionFolder & Id & ".json") Then - If MyMsgBox("版本 " & Id & " 已存在,是否重新下载?" & vbCrLf & "这会覆盖版本的 Json 文件,但不会影响版本隔离的文件。", "版本已存在", "继续", "取消") = 1 Then + If MyMsgBox("版本 " & Id & " 已存在,是否重新下载?" & vbCrLf & "这会覆盖版本的 json 和 jar 文件,但不会影响版本隔离的文件。", "版本已存在", "继续", "取消") = 1 Then File.Delete(VersionFolder & Id & ".jar") File.Delete(VersionFolder & Id & ".json") Else @@ -770,7 +770,7 @@ Public Module ModDownloadLib '已有版本检查 If File.Exists(VersionFolder & VersionName & ".json") Then - If MyMsgBox("版本 " & VersionName & " 已存在,是否重新下载?" & vbCrLf & "这会覆盖版本的 Json 文件,但不会影响版本隔离的文件。", "版本已存在", "继续", "取消") = 1 Then + If MyMsgBox("版本 " & VersionName & " 已存在,是否重新下载?" & vbCrLf & "这会覆盖版本的 json 和 jar 文件,但不会影响版本隔离的文件。", "版本已存在", "继续", "取消") = 1 Then File.Delete(VersionFolder & VersionName & ".jar") File.Delete(VersionFolder & VersionName & ".json") Else @@ -992,16 +992,16 @@ Public Module ModDownloadLib Public Sub McDownloadForge(DownloadInfo As DlForgeVersionEntry) '老版本提示 If DownloadInfo.Category = "client" Then - If MyMsgBox("该 Forge 版本过于古老,PCL2 暂不支持该版本的自动安装。" & vbCrLf & - "若你仍然希望继续,PCL2 将把安装程序下载到你指定的位置,但不会进行安装。", + If MyMsgBox("该 Forge 版本过于古老,PCL 暂不支持该版本的自动安装。" & vbCrLf & + "若你仍然希望继续,PCL 将把安装程序下载到你指定的位置,但不会进行安装。", "版本过老", "继续", "取消") = 1 Then McDownloadForgeSave(DownloadInfo) End If Exit Sub End If If DownloadInfo.Category = "universal" OrElse DownloadInfo.Inherit.StartsWith("1.5") Then '对该版本自动安装的支持将在之后加入 - If MyMsgBox("该 Forge 版本过于古老,PCL2 暂不支持该版本的自动安装。" & vbCrLf & - "若你仍然希望继续,PCL2 将把安装程序下载到你指定的位置,但不会进行安装。", + If MyMsgBox("该 Forge 版本过于古老,PCL 暂不支持该版本的自动安装。" & vbCrLf & + "若你仍然希望继续,PCL 将把安装程序下载到你指定的位置,但不会进行安装。", "版本过老", "继续", "取消") = 1 Then McDownloadForgeSave(DownloadInfo) End If @@ -1026,7 +1026,7 @@ Public Module ModDownloadLib '已有版本检查 If File.Exists(VersionFolder & Id & ".json") Then - If MyMsgBox("版本 " & Id & " 已存在,是否重新下载?" & vbCrLf & "这会覆盖版本的 Json 文件,但不会影响版本隔离的文件。", "版本已存在", "继续", "取消") = 1 Then + If MyMsgBox("版本 " & Id & " 已存在,是否重新下载?" & vbCrLf & "这会覆盖版本的 json 和 jar 文件,但不会影响版本隔离的文件。", "版本已存在", "继续", "取消") = 1 Then File.Delete(VersionFolder & Id & ".jar") File.Delete(VersionFolder & Id & ".json") Else @@ -1239,6 +1239,11 @@ Public Module ModDownloadLib '参数初始化 McFolder = If(McFolder, PathMcFolder) + If Version.StartsWith("1.") AndAlso Version.Contains("-") Then + '类似 1.19.3-41.2.8 格式,优先使用 Version 中要求的版本而非 Inherit(例如 1.19.3 却使用了 1.19 的 Forge) + Inherit = Version.Split("-").First + Version = Version.Split("-").Last + End If Dim IsCustomFolder As Boolean = McFolder <> PathMcFolder Dim Id As String = Inherit & "-forge-" & Version Dim InstallerAddress As String = PathTemp & "Cache\Code\ForgeInstall-" & Version & "_" & RandomInteger(0, 100000) @@ -1267,20 +1272,20 @@ Public Module ModDownloadLib End If '下载 Forge 主文件 Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("准备 Mod 加载器下载", - Sub(Task As LoaderTask(Of String, List(Of NetFile))) - '启动依赖版本的下载 - If ClientDownloadLoader Is Nothing Then - If IsCustomFolder Then Throw New Exception("如果没有指定原版下载器,则不能指定 MC 安装文件夹") - ClientDownloadLoader = McDownloadClient(NetPreDownloadBehaviour.ExitWhileExistsOrDownloading, Inherit) - End If - '添加主文件 - Dim Files As New List(Of NetFile) From {New NetFile({ - "https://bmclapi2.bangbang93.com/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName, - "http://files.minecraftforge.net/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName, - "https://download.mcbbs.net/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName - }, InstallerAddress, New FileChecker(MinSize:=64 * 1024, Hash:=DownloadInfo.Hash))} - Task.Output = Files - End Sub) With {.ProgressWeight = 0.5, .Show = False}) + Sub(Task As LoaderTask(Of String, List(Of NetFile))) + '启动依赖版本的下载 + If ClientDownloadLoader Is Nothing Then + If IsCustomFolder Then Throw New Exception("如果没有指定原版下载器,则不能指定 MC 安装文件夹") + ClientDownloadLoader = McDownloadClient(NetPreDownloadBehaviour.ExitWhileExistsOrDownloading, Inherit) + End If + '添加主文件 + Dim Files As New List(Of NetFile) From {New NetFile({ + "https://bmclapi2.bangbang93.com/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName, + "http://files.minecraftforge.net/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName, + "https://download.mcbbs.net/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName + }, InstallerAddress, New FileChecker(MinSize:=64 * 1024, Hash:=DownloadInfo.Hash))} + Task.Output = Files + End Sub) With {.ProgressWeight = 0.5, .Show = False}) Loaders.Add(New LoaderDownload("下载 Mod 加载器主文件", New List(Of NetFile)) With {.ProgressWeight = 9}) '安装(仅在新版安装时需要原版 Jar) @@ -1788,7 +1793,7 @@ Public Module ModDownloadLib '若要下载 Forge,则需要在下面两项中完成至少一项 ''' - ''' 欲下载的 Forge 版本名。例如 36.1.4。 + ''' 欲下载的 Forge 版本名。接受例如 36.1.4 / 14.23.5.2859 / 1.19-41.1.0 的输入。 ''' Public ForgeVersion As String = Nothing ''' @@ -2065,30 +2070,30 @@ Public Module ModDownloadLib Dim OutputJson As JObject, MinecraftJson As JObject, OptiFineJson As JObject = Nothing, ForgeJson As JObject = Nothing, LiteLoaderJson As JObject = Nothing, FabricJson As JObject = Nothing #Region "读取文件并检查文件是否合规" Dim MinecraftJsonText As String = ReadFile(MinecraftJsonPath) - If Not MinecraftJsonText.StartsWith("{") Then Throw New Exception("Minecraft Json 有误,地址:" & MinecraftJsonPath & ",前段内容:" & MinecraftJsonText.Substring(0, Math.Min(MinecraftJsonText.Length, 1000))) + If Not MinecraftJsonText.StartsWith("{") Then Throw New Exception("Minecraft json 有误,地址:" & MinecraftJsonPath & ",前段内容:" & MinecraftJsonText.Substring(0, Math.Min(MinecraftJsonText.Length, 1000))) MinecraftJson = GetJson(MinecraftJsonText) If HasOptiFine Then Dim OptiFineJsonText As String = ReadFile(OptiFineJsonPath) - If Not OptiFineJsonText.StartsWith("{") Then Throw New Exception("OptiFine Json 有误,地址:" & OptiFineJsonPath & ",前段内容:" & OptiFineJsonText.Substring(0, Math.Min(OptiFineJsonText.Length, 1000))) + If Not OptiFineJsonText.StartsWith("{") Then Throw New Exception("OptiFine json 有误,地址:" & OptiFineJsonPath & ",前段内容:" & OptiFineJsonText.Substring(0, Math.Min(OptiFineJsonText.Length, 1000))) OptiFineJson = GetJson(OptiFineJsonText) End If If HasForge Then Dim ForgeJsonText As String = ReadFile(ForgeJsonPath) - If Not ForgeJsonText.StartsWith("{") Then Throw New Exception("Forge Json 有误,地址:" & ForgeJsonPath & ",前段内容:" & ForgeJsonText.Substring(0, Math.Min(ForgeJsonText.Length, 1000))) + If Not ForgeJsonText.StartsWith("{") Then Throw New Exception("Forge json 有误,地址:" & ForgeJsonPath & ",前段内容:" & ForgeJsonText.Substring(0, Math.Min(ForgeJsonText.Length, 1000))) ForgeJson = GetJson(ForgeJsonText) End If If HasLiteLoader Then Dim LiteLoaderJsonText As String = ReadFile(LiteLoaderJsonPath) - If Not LiteLoaderJsonText.StartsWith("{") Then Throw New Exception("LiteLoader Json 有误,地址:" & LiteLoaderJsonPath & ",前段内容:" & LiteLoaderJsonText.Substring(0, Math.Min(LiteLoaderJsonText.Length, 1000))) + If Not LiteLoaderJsonText.StartsWith("{") Then Throw New Exception("LiteLoader json 有误,地址:" & LiteLoaderJsonPath & ",前段内容:" & LiteLoaderJsonText.Substring(0, Math.Min(LiteLoaderJsonText.Length, 1000))) LiteLoaderJson = GetJson(LiteLoaderJsonText) End If If HasFabric Then Dim FabricJsonText As String = ReadFile(FabricJsonPath) - If Not FabricJsonText.StartsWith("{") Then Throw New Exception("Fabric Json 有误,地址:" & FabricJsonPath & ",前段内容:" & FabricJsonText.Substring(0, Math.Min(FabricJsonText.Length, 1000))) + If Not FabricJsonText.StartsWith("{") Then Throw New Exception("Fabric json 有误,地址:" & FabricJsonPath & ",前段内容:" & FabricJsonText.Substring(0, Math.Min(FabricJsonText.Length, 1000))) FabricJson = GetJson(FabricJsonText) End If #End Region diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadClient.xaml b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadClient.xaml index ee70967..879883c 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadClient.xaml +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadClient.xaml @@ -9,7 +9,7 @@ @@ -220,13 +221,21 @@ + + + - + - + - + + + + - + @@ -111,10 +111,8 @@ - - diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml.vb b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml.vb index c6f3924..3ac1db5 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml.vb @@ -154,65 +154,6 @@ If AniControlEnabled = 0 Then Hint("部分调试信息将在刷新或启动器重启后切换显示!",, False) End Sub - '清理缓存 - Private IsClearingCache As Boolean = False - Private Sub BtnSystemCacheClear_Click(sender As Object, e As EventArgs) Handles BtnSystemCacheClear.Click - If HasDownloadingTask() Then - Hint("请在所有下载任务完成后再清理缓存!", HintType.Critical) - Exit Sub - End If - If PathTemp = IO.Path.GetTempPath() & "PCL\" Then - If MyMsgBox("你确定要清理缓存吗?" & vbCrLf & "在清理缓存后,PCL2 会被强制关闭,以避免缓存缺失带来的异常。", "清理确认", "确定", "取消") = 2 Then - Exit Sub - End If - Else - If MyMsgBox("你已将缓存文件夹手动修改为:" & PathTemp & vbCrLf & - "清理缓存将删除你所设置的这一文件夹中的所有内容,且无法恢复!" & vbCrLf & - "请在继续前,确认其中没有除了 PCL2 缓存以外的重要文件!" & vbCrLf & vbCrLf & - "在清理缓存后,PCL2 会被强制关闭,以避免缓存缺失带来的异常。", "清理确认", "永久删除该文件夹", "取消", IsWarn:=True) = 2 Then - Exit Sub - End If - End If - If IsClearingCache Then Exit Sub - IsClearingCache = True - Try - Dim TotalSize As ULong = DeleteCacheDirectory(PathTemp) - DeleteDirectory(PathTemp, True) - TotalSize += DeleteCacheDirectory(OsDrive & "ProgramData\PCL\") - DeleteDirectory(OsDrive & "ProgramData\PCL\", True) - If TotalSize <= 0 Then - Hint("没有可清理的缓存!") - Else - MyMsgBox("已清理 " & GetString(TotalSize) & " 缓存!" & vbCrLf & "PCL2 即将自动关闭。", "缓存已清理", ForceWait:=True) - FrmMain.EndProgram(False) - End If - Catch ex As Exception - Log(ex, "清理缓存失败", LogLevel.Hint) - Finally - IsClearingCache = False - End Try - End Sub - Private Function DeleteCacheDirectory(Path As String, Optional TotalSize As ULong = 0) As ULong - If Not Directory.Exists(Path) Then Return TotalSize - Dim Temp As String() - Temp = Directory.GetFiles(Path) - For Each str As String In Temp - Try - Dim Info As New FileInfo(str) - Dim FileActualSize = Math.Ceiling(Info.Length / 4096) * 4096 - Info.Delete() - TotalSize += FileActualSize - Catch ex As Exception - Log(ex, "删除失败的缓存文件(" & str & ")") - End Try - Next - Temp = Directory.GetDirectories(Path) - For Each str As String In Temp - TotalSize += DeleteCacheDirectory(str) - Next - Return TotalSize - End Function - '自动更新 Private Sub ComboSystemActivity_SizeChanged(sender As Object, e As SelectionChangedEventArgs) Handles ComboSystemActivity.SelectionChanged If AniControlEnabled <> 0 Then Exit Sub diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml index 23f7cb0..33bc4e1 100644 --- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml +++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml @@ -39,7 +39,7 @@ - + diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb index 54f4de9..2fe2bcd 100644 --- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb @@ -308,6 +308,7 @@ Public Sub Delete_Click(sender As MyIconButton, e As EventArgs) Try + Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown Dim ListItem As MyListItem = sender.Tag Dim ModEntity As McMod = ListItem.Tag '前置检测警告 @@ -316,7 +317,11 @@ End If '删除 If File.Exists(ModEntity.Path) Then - My.Computer.FileSystem.DeleteFile(ModEntity.Path, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin) + If IsShiftPressed Then + File.Delete(ModEntity.Path) + Else + My.Computer.FileSystem.DeleteFile(ModEntity.Path, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin) + End If Else '在不明原因下删除的文件可能还在列表里,如果玩家又点了一次就会出错,总之加个判断也不碍事 Log("[System] 需要删除的 Mod 文件不存在(" & ModEntity.Path & ")", LogLevel.Hint) @@ -333,7 +338,11 @@ CType(Parent.Parent, MyCard).Title = McModGetTitle(McModLoader.Output) End If '显示提示 - Hint("已将 " & ModEntity.FileName & " 删除到回收站!", HintType.Finish) + If IsShiftPressed Then + Hint("已删除 " & ModEntity.FileName & "!", HintType.Finish) + Else + Hint("已将 " & ModEntity.FileName & " 删除到回收站!", HintType.Finish) + End If LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.UpdateOnly) Catch ex As Exception diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml.vb b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml.vb index 40491f7..72517aa 100644 --- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml.vb @@ -92,7 +92,7 @@ Private Sub BtnDisplayDesc_Click(sender As Object, e As EventArgs) Handles BtnDisplayDesc.Click Try Dim OldInfo As String = ReadIni(PageVersionLeft.Version.Path & "PCL\Setup.ini", "CustomInfo") - Dim NewInfo As String = MyMsgBoxInput("更改描述", "修改版本的描述文本,留空则使用 PCL2 的默认描述。", OldInfo, New ObjectModel.Collection(Of Validate), "默认描述") + Dim NewInfo As String = MyMsgBoxInput("更改描述", "修改版本的描述文本,留空则使用 PCL 的默认描述。", OldInfo, New ObjectModel.Collection(Of Validate), "默认描述") If NewInfo IsNot Nothing AndAlso OldInfo <> NewInfo Then WriteIni(PageVersionLeft.Version.Path & "PCL\Setup.ini", "CustomInfo", NewInfo) PageVersionLeft.Version = New McVersion(PageVersionLeft.Version.Name).Load() Reload() @@ -121,7 +121,7 @@ Try JsonObject = GetJson(ReadFile(PageVersionLeft.Version.Path & PageVersionLeft.Version.Name & ".json")) Catch ex As Exception - Log(ex, "重命名读取 Json 时失败") + Log(ex, "重命名读取 json 时失败") JsonObject = PageVersionLeft.Version.JsonObject End Try '重命名主文件夹 @@ -162,7 +162,7 @@ JsonObject("id") = NewName WriteFile(NewPath & NewName & ".json", JsonObject.ToString) Catch ex As Exception - Log(ex, "重命名 Json 时失败") + Log(ex, "重命名版本 json 失败") End Try End If '刷新与提示 @@ -315,17 +315,20 @@ Private Sub BtnManageDelete_Click(sender As Object, e As EventArgs) Handles BtnManageDelete.Click '修改此代码时,同时修改 PageSelectRight 中的代码 Try + Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown Dim IsHintIndie As Boolean = PageVersionLeft.Version.State <> McVersionState.Error AndAlso PageVersionLeft.Version.PathIndie <> PathMcFolder - Select Case MyMsgBox("你确定要删除版本 " & PageVersionLeft.Version.Name & " 吗?" & + Select Case MyMsgBox($"你确定要{If(IsShiftPressed, "永久", "")}删除版本 {PageVersionLeft.Version.Name} 吗?" & If(IsHintIndie, vbCrLf & "由于该版本开启了版本隔离,删除版本时该版本对应的存档、资源包、Mod 等文件也将被一并删除!", ""), - "版本删除确认", , "取消",, True) + "版本删除确认", , "取消",, IsHintIndie OrElse IsShiftPressed) Case 1 - FileIO.FileSystem.DeleteDirectory(PageVersionLeft.Version.Path, FileIO.UIOption.AllDialogs, FileIO.RecycleOption.SendToRecycleBin) - Hint("版本 " & PageVersionLeft.Version.Name & " 已删除到回收站!", HintType.Finish) + If IsShiftPressed Then + DeleteDirectory(PageVersionLeft.Version.Path) + Hint("版本 " & PageVersionLeft.Version.Name & " 已永久删除!", HintType.Finish) + Else + FileIO.FileSystem.DeleteDirectory(PageVersionLeft.Version.Path, FileIO.UIOption.AllDialogs, FileIO.RecycleOption.SendToRecycleBin) + Hint("版本 " & PageVersionLeft.Version.Name & " 已删除到回收站!", HintType.Finish) + End If Case 2 - ' DeleteDirectory(PageVersionLeft.Version.Path) - ' Hint("版本 " & PageVersionLeft.Version.Name & " 已永久删除!", HintType.Finish) - 'Case 3 Exit Sub End Select LoaderFolderRun(McVersionListLoader, PathMcFolder, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\") diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml index bc5f981..f802b44 100644 --- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml +++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml @@ -47,7 +47,7 @@ - + @@ -190,9 +190,9 @@ - + - + @@ -202,7 +202,7 @@ + ToolTip="该项不会覆盖全局设置:启动时会先执行全局设置的命令,再执行版本设置的命令。 在 MC 启动前执行特定命令或程序,语法与 Windows 的命令提示符一致。 可以使用以下替换标记实现相对路径(路径均以 \ 结尾): · {path}:PCL 的 exe 文件所在的文件夹 · {minecraft}:.minecraft 文件夹 · {verpath}:版本文件夹(.minecraft\versions\版本名\) · {verindie}:开启版本隔离时等同版本文件夹,未开启时等同 .minecraft 文件夹 · {java}:游戏运行时的 Java 文件夹 除此之外,也支持以下替换标记: · {user}:玩家名字 · {login}:玩家的登录方式 · {uuid}:玩家的 UUID · {name}:游戏版本名 · {time}:当前的系统时间 · {version}:游戏对应的原版版本号 例如: · "{verpath}test.exe" :运行版本文件夹下的 test.exe 程序 · "{java}java.exe" -jar "{verpath}test.jar" :用 Java 运行版本文件夹下的 test.jar · notepad "{verindie}option.txt" :使用记事本打开该版本的设置文件 涉及路径的操作最好都打上双引号,以避免路径中的空格导致运行失败。" /> diff --git a/Plain Craft Launcher 2/Resources/Custom.xaml b/Plain Craft Launcher 2/Resources/Custom.xaml index e77ad26..368b824 100644 --- a/Plain Craft Launcher 2/Resources/Custom.xaml +++ b/Plain Craft Launcher 2/Resources/Custom.xaml @@ -1,6 +1,6 @@  - @@ -84,13 +84,13 @@ + Text="此外,PCL 会将文件中的花括号 path 替换为 PCL 可执行文件所在文件夹({path}),可以用于执行特定程序或加载图片。" /> + Text="打开 PCL 日志" EventType="打开文件" EventData="Log1.txt" ToolTip="PCL 的日志文件就在 PCL 文件夹内,且名为 Log1.txt" /> + Text="定位游戏启动脚本" EventType="打开文件" EventData="explorer.exe|/select,LatestLaunch.bat" ToolTip="PCL 会将上一次启动 MC 的脚本保存在 PCL 文件夹内,且名为 LatestLaunch.bat。 点击按钮会打开文件管理器,并且借助参数要求它选中这个文件。" /> @@ -162,7 +162,7 @@ + Text="PCL 中包含一些预置的方块与物品图片,可以直接使用。"/> @@ -203,7 +203,7 @@ - Width、Height:设置宽度与高度 - ToolTip:设置鼠标指向时的提示文本 - Foreground:调整前景颜色(十六进制字符串,例如 #FF010203 代表 ARGB 中的 255,1,2,3。前两位代表不透明度,FF 为全不透明,00 为全透明) - - Background:调整背景颜色(使用类似 Background="{DynamicResource ColorBrush5}" 的格式以调用 PCL2 当前的主题颜色,修改末尾的数字编号以改变颜色浓度) + - Background:调整背景颜色(使用类似 Background="{DynamicResource ColorBrush5}" 的格式以调用 PCL 当前的主题颜色,修改末尾的数字编号以改变颜色浓度) - Margin:调整外边距(格式为 左,上,右,下。例如 Margin="0,0,0,2" 即为 2 单位的下边距) - HorizontalAlignment:设置对齐方式(Left、Center、Right) diff --git a/Plain Craft Launcher 2/Resources/Help.zip b/Plain Craft Launcher 2/Resources/Help.zip index 7a5cd703a83e819b359faae05988a32f964e5b5e..9c757813c98ac0bc764ef30584a12de80624965e 100644 GIT binary patch delta 54685 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 o7qfT8&;S4c delta 52331 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