diff --git a/Plain Craft Launcher 2/Application.xaml b/Plain Craft Launcher 2/Application.xaml index 8fc582a..7a25967 100644 --- a/Plain Craft Launcher 2/Application.xaml +++ b/Plain Craft Launcher 2/Application.xaml @@ -133,7 +133,7 @@ - + @@ -147,7 +147,7 @@ - + diff --git a/Plain Craft Launcher 2/Application.xaml.vb b/Plain Craft Launcher 2/Application.xaml.vb index 05e70a1..a0ffcef 100644 --- a/Plain Craft Launcher 2/Application.xaml.vb +++ b/Plain Craft Launcher 2/Application.xaml.vb @@ -247,4 +247,12 @@ Public Class Application End Select End Sub + Public Shared ShowingTooltips As New List(Of Border) + Private Sub TooltipLoaded(sender As Border, e As EventArgs) + ShowingTooltips.Add(sender) + End Sub + Private Sub TooltipUnloaded(sender As Border, e As RoutedEventArgs) + ShowingTooltips.Remove(sender) + End Sub + End Class diff --git a/Plain Craft Launcher 2/Controls/MyCard.vb b/Plain Craft Launcher 2/Controls/MyCard.vb index f938186..db2fe82 100644 --- a/Plain Craft Launcher 2/Controls/MyCard.vb +++ b/Plain Craft Launcher 2/Controls/MyCard.vb @@ -41,10 +41,6 @@ End Sub Private IsLoad As Boolean = False Private Sub MyCard_Loaded() Handles Me.Loaded - '卡片的进入时动画已被页面通用切换动画替代 - 'If LoadAnimation AndAlso IsLoad Then - ' StartHeightAnimation(Math.Min(50, ActualHeight), ActualHeight - Math.Min(50, ActualHeight), True) - 'End If If IsLoad Then Exit Sub '初次加载限定 If Not Title = "" AndAlso IsNothing(MainTextBlock) Then diff --git a/Plain Craft Launcher 2/Controls/MyExtraButton.xaml b/Plain Craft Launcher 2/Controls/MyExtraButton.xaml index a5b796f..f8a0d88 100644 --- a/Plain Craft Launcher 2/Controls/MyExtraButton.xaml +++ b/Plain Craft Launcher 2/Controls/MyExtraButton.xaml @@ -16,9 +16,9 @@ - + - + diff --git a/Plain Craft Launcher 2/Controls/MyExtraButton.xaml.vb b/Plain Craft Launcher 2/Controls/MyExtraButton.xaml.vb index a31e0d0..d6da198 100644 --- a/Plain Craft Launcher 2/Controls/MyExtraButton.xaml.vb +++ b/Plain Craft Launcher 2/Controls/MyExtraButton.xaml.vb @@ -17,7 +17,7 @@ PanProgress.Visibility = Visibility.Collapsed Else PanProgress.Visibility = Visibility.Visible - RectProgress.Rect = New Rect(0, 0, 40, 40 * (1 - value)) + RectProgress.Rect = New Rect(0, 40 * (1 - value), 40, 40 * value) End If End Set End Property @@ -179,7 +179,7 @@ AniStop("MyExtraButton Color " & Uuid) If IsMouseOver Then - PanColor.SetResourceReference(BackgroundProperty, "ColorBrush2") + PanColor.SetResourceReference(BackgroundProperty, "ColorBrush4") Else PanColor.SetResourceReference(BackgroundProperty, "ColorBrush3") End If diff --git a/Plain Craft Launcher 2/Controls/MyPageLeft.vb b/Plain Craft Launcher 2/Controls/MyPageLeft.vb index c43e56e..b09897b 100644 --- a/Plain Craft Launcher 2/Controls/MyPageLeft.vb +++ b/Plain Craft Launcher 2/Controls/MyPageLeft.vb @@ -29,21 +29,28 @@ '逐个进入动画 Dim AniList As New List(Of AniData) Dim Id As Integer = 0, Delay As Integer = 0 - Dim Controls = GetAllAnimControls() - For Each Element As FrameworkElement In Controls - Element.Opacity = 0 - Element.RenderTransform = New TranslateTransform(-25, 0) - AniList.Add(AaOpacity(Element, If(TypeOf Element Is TextBlock, 0.6, 1), 200, Delay, New AniEaseOutFluent(AniEasePower.Weak))) - AniList.Add(AaTranslateX(Element, 5, 200, Delay, New AniEaseOutFluent)) - AniList.Add(AaTranslateX(Element, 20, 300, Delay, New AniEaseOutBack(AniEasePower.Weak))) - If TypeOf Element Is MyListItem Then - AniList.Add(AaCode(Sub() - CType(Element, MyListItem).IsMouseOverAnimationEnabled = True - CType(Element, MyListItem).RefreshColor(Me, New EventArgs) - End Sub, Delay + 280)) + For Each Element As FrameworkElement In GetAllAnimControls(True) + If Element.Visibility = Visibility.Collapsed Then + '还原之前的隐藏动画可能导致的改变(#2436) + Element.Opacity = 1 + Element.RenderTransform = New TranslateTransform(0, 0) + If TypeOf Element Is MyListItem Then CType(Element, MyListItem).IsMouseOverAnimationEnabled = True + Else + Element.Opacity = 0 + Element.RenderTransform = New TranslateTransform(-25, 0) + If TypeOf Element Is MyListItem Then CType(Element, MyListItem).IsMouseOverAnimationEnabled = False + AniList.Add(AaOpacity(Element, If(TypeOf Element Is TextBlock, 0.6, 1), 200, Delay, New AniEaseOutFluent(AniEasePower.Weak))) + AniList.Add(AaTranslateX(Element, 5, 200, Delay, New AniEaseOutFluent)) + AniList.Add(AaTranslateX(Element, 20, 300, Delay, New AniEaseOutBack(AniEasePower.Weak))) + If TypeOf Element Is MyListItem Then + AniList.Add(AaCode(Sub() + CType(Element, MyListItem).IsMouseOverAnimationEnabled = True + CType(Element, MyListItem).RefreshColor(Me, New EventArgs) + End Sub, Delay + 280)) + End If + Delay += Math.Max(8, 20 - Id) * 2.5 + Id += 1 End If - Delay += Math.Max(8, 20 - Id) * 2.5 - Id += 1 Next AniStart(AniList, "PageLeft PageChange " & Uuid) End If @@ -74,23 +81,22 @@ End Sub '遍历获取所有需要生成动画的控件 - Private Function GetAllAnimControls() As List(Of FrameworkElement) + Private Function GetAllAnimControls(Optional IgnoreInvisibility As Boolean = False) As List(Of FrameworkElement) Dim AllControls As New List(Of FrameworkElement) - GetAllAnimControls(AnimatedControl, AllControls) + GetAllAnimControls(AnimatedControl, AllControls, IgnoreInvisibility) Return AllControls End Function - Private Sub GetAllAnimControls(Element As FrameworkElement, ByRef AllControls As List(Of FrameworkElement)) - If Element.Visibility = Visibility.Collapsed Then Exit Sub + Private Sub GetAllAnimControls(Element As FrameworkElement, ByRef AllControls As List(Of FrameworkElement), IgnoreInvisibility As Boolean) + If Not IgnoreInvisibility AndAlso Element.Visibility = Visibility.Collapsed Then Exit Sub If TypeOf Element Is MyTextButton Then AllControls.Add(Element) ElseIf TypeOf Element Is MyListItem Then - CType(Element, MyListItem).IsMouseOverAnimationEnabled = False AllControls.Add(Element) ElseIf TypeOf Element Is ContentControl Then - GetAllAnimControls(CType(Element, ContentControl).Content, AllControls) + GetAllAnimControls(CType(Element, ContentControl).Content, AllControls, IgnoreInvisibility) ElseIf TypeOf Element Is Panel Then For Each Element2 As FrameworkElement In CType(Element, Panel).Children - GetAllAnimControls(Element2, AllControls) + GetAllAnimControls(Element2, AllControls, IgnoreInvisibility) Next Else AllControls.Add(Element) diff --git a/Plain Craft Launcher 2/Controls/MyScrollViewer.vb b/Plain Craft Launcher 2/Controls/MyScrollViewer.vb index 666d9fa..1187052 100644 --- a/Plain Craft Launcher 2/Controls/MyScrollViewer.vb +++ b/Plain Craft Launcher 2/Controls/MyScrollViewer.vb @@ -15,8 +15,12 @@ '如果当前是在对有滚动条的下拉框或文本框执行,则不接管操作 Exit Sub End If - PerformVerticalOffsetDelta(-e.Delta) e.Handled = True + PerformVerticalOffsetDelta(-e.Delta) + '关闭 Tooltip (#2552) + For Each TooltipBorder In Application.ShowingTooltips + AniStart(AaOpacity(TooltipBorder, -1, 100), $"Hide Tooltip {GetUuid()}") + Next End Sub Public Sub PerformVerticalOffsetDelta(Delta As Double) AniStart(AaDouble(Sub(AnimDelta As Double) diff --git a/Plain Craft Launcher 2/FormMain.xaml.vb b/Plain Craft Launcher 2/FormMain.xaml.vb index bd16da5..37908b8 100644 --- a/Plain Craft Launcher 2/FormMain.xaml.vb +++ b/Plain Craft Launcher 2/FormMain.xaml.vb @@ -10,6 +10,11 @@ Public Class FormMain Dim FeatureList As New List(Of KeyValuePair(Of Integer, String)) '统计更新日志条目 #If BETA Then + If LastVersion < 304 Then 'Release 2.6.8 + FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复开始或结束游戏时可能报错的 Bug")) + FeatureCount += 20 + BugCount += 25 + End If If LastVersion < 302 Then 'Release 2.6.7 FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "再次修复网络条件差时可能无法下载 MC 的 Bug")) BugCount += 1 @@ -128,6 +133,11 @@ Public Class FormMain '3:BUG+ IMP* FEAT- '2:BUG* IMP- '1:BUG- + If LastVersion < 305 Then 'Snapshot 2.6.8 + FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复开始或结束游戏时可能报错的 Bug")) + FeatureCount += 20 + BugCount += 25 + End If If LastVersion < 303 Then 'Snapshot 2.6.7 FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "再次修复网络条件差时可能无法下载 MC 的 Bug")) BugCount += 1 @@ -331,6 +341,7 @@ Public Class FormMain End Sub '窗口加载 + Private Declare Auto Function ChangeWindowMessageFilterEx Lib "user32.dll" (ByVal hwnd As IntPtr, ByVal message As UInt32, ByVal action As UInt32, ByVal changeInfo As UInt32) As Boolean Private IsWindowLoadFinished As Boolean = False ''' ''' 是否为联机提权后自动重启。 @@ -442,6 +453,7 @@ Public Class FormMain TimerMainStartRun() '加载池 RunInNewThread(Sub() + 'EULA 提示 If Not Setup.Get("SystemEula") Then Select Case MyMsgBox("在使用 PCL 前,请同意 PCL 的用户协议与免责声明。", "协议授权", "同意", "拒绝", "查看用户协议与免责声明", Button3Action:=Sub() OpenWebsite("https://shimo.im/docs/rGrd8pY8xWkt6ryW")) @@ -451,6 +463,7 @@ Public Class FormMain EndProgram(False) End Select End If + '启动加载器池 Try JavaListInit() '延后到同意协议后再执行,避免在初次启动时进行进程操作 Thread.Sleep(200) @@ -460,11 +473,22 @@ Public Class FormMain Catch ex As Exception Log(ex, "初始化加载池运行失败", LogLevel.Feedback) End Try + '清理自动更新文件 Try If File.Exists(Path & "PCL\Plain Craft Launcher 2.exe") Then File.Delete(Path & "PCL\Plain Craft Launcher 2.exe") Catch ex As Exception Log(ex, "清理自动更新文件失败") End Try + '开启管理员权限下的文件拖拽(#2531) + Try + If IsAdmin() Then + Log("[System] PCL 正以管理员权限运行") + ChangeWindowMessageFilterEx(Handle, &H233, 1, Nothing) + ChangeWindowMessageFilterEx(Handle, &H49, 1, Nothing) + End If + Catch ex As Exception + Log(ex, "开启管理员权限下的文件拖拽失败") + End Try End Sub, "Start Loader", ThreadPriority.Lowest) Log("[Start] 第三阶段加载用时:" & GetTimeTick() - ApplicationStartTick & " ms") diff --git a/Plain Craft Launcher 2/Modules/Base/ModBase.vb b/Plain Craft Launcher 2/Modules/Base/ModBase.vb index b7f340b..e19f368 100644 --- a/Plain Craft Launcher 2/Modules/Base/ModBase.vb +++ b/Plain Craft Launcher 2/Modules/Base/ModBase.vb @@ -11,12 +11,12 @@ Public Module ModBase #Region "声明" '下列版本信息由更新器自动修改 - Public Const VersionBaseName As String = "2.6.7" '不含分支前缀的显示用版本名 - Public Const VersionStandardCode As String = "2.6.7." & VersionBranchCode '标准格式的四段式版本号 + Public Const VersionBaseName As String = "2.6.8" '不含分支前缀的显示用版本名 + Public Const VersionStandardCode As String = "2.6.8." & VersionBranchCode '标准格式的四段式版本号 #If BETA Then - Public Const VersionCode As Integer = 302 'Release + Public Const VersionCode As Integer = 304 'Release #Else - Public Const VersionCode As Integer = 303 'Snapshot + Public Const VersionCode As Integer = 305 'Snapshot #End If '自动生成的版本信息 Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName @@ -1242,23 +1242,7 @@ Re: Public Function ExtractFile(CompressFilePath As String, DestDirectory As String, Optional Encode As Encoding = Nothing) As Boolean Try Directory.CreateDirectory(DestDirectory) - If CompressFilePath.EndsWith(".zip") OrElse CompressFilePath.EndsWith(".jar") OrElse CompressFilePath.EndsWith(".mrpack") Then - '以 zip 方式解压 - Using stream As New FileStream(CompressFilePath, FileMode.Open) - Using archive As New ZipArchive(stream, ZipArchiveMode.Read, False, If(Encode, Encoding.GetEncoding("GB18030"))) - For Each entry As ZipArchiveEntry In archive.Entries - Dim destinationPath As String = IO.Path.Combine(DestDirectory, entry.FullName) - If destinationPath.EndsWith("\") OrElse destinationPath.EndsWith("/") Then - Directory.CreateDirectory(destinationPath) - Else - If File.Exists(destinationPath) Then File.Delete(destinationPath) - entry.ExtractToFile(destinationPath, True) - End If - Next - End Using - End Using - Return True - ElseIf CompressFilePath.EndsWith(".gz") Then + If CompressFilePath.EndsWith(".gz") Then '以 gz 方式解压 Dim stream As New GZipStream(New FileStream(CompressFilePath, FileMode.Open, FileAccess.ReadWrite), CompressionMode.Decompress) Dim decompressedFile As New FileStream(DestDirectory & GetFileNameFromPath(CompressFilePath).ToLower.Replace(".tar", "").Replace(".gz", ""), FileMode.OpenOrCreate, FileAccess.Write) @@ -1271,7 +1255,19 @@ Re: stream.Close() Return True Else - Return False + '以 zip 方式解压 + Using Archive = ZipFile.Open(CompressFilePath, ZipArchiveMode.Read, If(Encode, Encoding.GetEncoding("GB18030"))) + For Each Entry As ZipArchiveEntry In Archive.Entries + Dim DestinationPath As String = IO.Path.Combine(DestDirectory, Entry.FullName) + If DestinationPath.EndsWith("\") OrElse DestinationPath.EndsWith("/") Then + Continue For '不创建空文件夹 + Else + Directory.CreateDirectory(GetPathFromFullPath(DestinationPath)) + Entry.ExtractToFile(DestinationPath, True) + End If + Next + End Using + Return True End If Catch ex As Exception Log(ex, "尝试解压文件失败") @@ -1301,7 +1297,7 @@ Re: Next Temp = Directory.GetDirectories(Path) For Each str As String In Temp - DeleteDirectory(str) + DeleteDirectory(str, IgnoreIssue) Next Try Directory.Delete(Path, True) @@ -1578,7 +1574,7 @@ Re: ''' 为字符串进行 XML 转义。 ''' Public Function EscapeXML(Str As String) As String - Return Str.Replace("&", "&").Replace("<", "<").Replace(">", ">").Replace("'", "'").Replace("""", """) + Return Str.Replace("&", "&").Replace("<", "<").Replace(">", ">").Replace("'", "'").Replace("""", """).Replace(vbCrLf, " ") End Function '正则 diff --git a/Plain Craft Launcher 2/Modules/Base/ModLoader.vb b/Plain Craft Launcher 2/Modules/Base/ModLoader.vb index 0d8deed..6a173c1 100644 --- a/Plain Craft Launcher 2/Modules/Base/ModLoader.vb +++ b/Plain Craft Launcher 2/Modules/Base/ModLoader.vb @@ -437,10 +437,6 @@ For Each Loader In Loaders Loader.Abort() Next - '从列表删除 - SyncLock LoaderTaskbarLock - If LoaderTaskbar.Contains(Me) Then LoaderTaskbar.Remove(Me) - End SyncLock End Sub) End Sub @@ -571,6 +567,7 @@ Restart: If FrmSpeedLeft IsNot Nothing Then FrmSpeedLeft.TaskRemove(Loader) SyncLock LoaderTaskbarLock LoaderTaskbar.Add(Loader) + Log($"[Taskbar] {Loader.Name} 已加入任务列表") End SyncLock End Sub Public Sub LoaderTaskbarProgressRefresh() @@ -585,16 +582,13 @@ Restart: If Loader.Show AndAlso Loader.State = LoadState.Loading Then IsAllDownloadTaskCompleted = False Next '若单个任务已中止或全部任务已完成,则刷新并移除 - Dim RemovedTask As New List(Of LoaderBase) - For i = 0 To LoaderTaskbar.Count - 1 - If (IsAllDownloadTaskCompleted OrElse LoaderTaskbar(i).State = LoadState.Aborted) AndAlso LoaderTaskbar(i).Show Then - If FrmSpeedLeft IsNot Nothing Then FrmSpeedLeft.TaskRefresh(LoaderTaskbar(i)) - RemovedTask.Add(LoaderTaskbar(i)) + For Each Task In LoaderTaskbar.ToList() + If (IsAllDownloadTaskCompleted OrElse Task.State = LoadState.Aborted OrElse Task.State = LoadState.Waiting) AndAlso Task.Show Then + If FrmSpeedLeft IsNot Nothing Then FrmSpeedLeft.TaskRefresh(Task) + LoaderTaskbar.Remove(Task) + Log($"[Taskbar] {Task.Name} 已移出任务列表") End If Next - For Each Task In RemovedTask - LoaderTaskbar.Remove(Task) - Next End SyncLock '更新平滑后的进度 If NewProgress <= 0 OrElse NewProgress >= 1 OrElse LoaderTaskbarProgress > NewProgress Then @@ -625,8 +619,9 @@ Restart: Try Dim Total As Double = 0, Count As Integer = 0 SyncLock LoaderTaskbarLock - For i = 0 To LoaderTaskbar.Count - 1 - Total += LoaderTaskbar(i).Progress + For Each Task In LoaderTaskbar.ToList + If Not Task.Show Then Continue For + Total += Task.Progress Count += 1 '避免多线程影响导致计数出错 Next End SyncLock diff --git a/Plain Craft Launcher 2/Modules/Base/MyBitmap.vb b/Plain Craft Launcher 2/Modules/Base/MyBitmap.vb index 02da732..1c25959 100644 --- a/Plain Craft Launcher 2/Modules/Base/MyBitmap.vb +++ b/Plain Craft Launcher 2/Modules/Base/MyBitmap.vb @@ -28,7 +28,7 @@ Public Class MyBitmap Public Shared Widening Operator CType(Image As MyBitmap) As ImageSource Dim Bitmap = Image.Pic Dim rect = New System.Drawing.Rectangle(0, 0, Bitmap.Width, Bitmap.Height) - Dim bitmapData = Bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb) + Dim bitmapData = Bitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb) Try Dim size = rect.Width * rect.Height * 4 Return BitmapSource.Create(Bitmap.Width, Bitmap.Height, Bitmap.HorizontalResolution, Bitmap.VerticalResolution, PixelFormats.Bgra32, Nothing, bitmapData.Scan0, size, bitmapData.Stride) @@ -104,45 +104,27 @@ Public Class MyBitmap End Using End Sub - ''' - ''' 获取旋转的图片,这个方法不会导致原对象改变且会返回一个新的对象。图片长宽必须相等。 - ''' - ''' 旋转角度(单位为角度)。 - Public Function Rotation(angle As Double) As System.Drawing.Bitmap - Dim img As System.Drawing.Image = Me.Pic - Dim bitSize As Single = img.Width - Dim bmp As New System.Drawing.Bitmap(CInt(bitSize), CInt(bitSize), img.PixelFormat) - bmp.SetResolution(img.HorizontalResolution, img.VerticalResolution) - Using g As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmp) - g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor - 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 - Dim img As System.Drawing.Image = Pic - Dim bmp As New System.Drawing.Bitmap(rect.Width, rect.Height, img.PixelFormat) - bmp.SetResolution(img.HorizontalResolution, img.VerticalResolution) + Public Function Clip(X As Integer, Y As Integer, Width As Integer, Height As Integer) As MyBitmap + Dim bmp As New System.Drawing.Bitmap(Width, Height, Pic.PixelFormat) + bmp.SetResolution(Pic.HorizontalResolution, Pic.VerticalResolution) Using g As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmp) g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor - g.DrawImageUnscaled(img, rect.X, rect.Y) + g.TranslateTransform(-X, -Y) + g.DrawImage(Pic, New System.Drawing.Rectangle(0, 0, Pic.Width, Pic.Height)) End Using Return bmp End Function ''' - ''' 获取左右翻转的图片,这个方法不会导致原对象改变。 + ''' 获取旋转或翻转后的图片,这个方法不会导致原对象改变且会返回一个新的对象。 ''' - Public Function LeftRightFilp() As System.Drawing.Bitmap + Public Function RotateFlip(Type As System.Drawing.RotateFlipType) As MyBitmap Dim bmp As New System.Drawing.Bitmap(Pic) - bmp.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipX) + bmp.SetResolution(Pic.HorizontalResolution, Pic.VerticalResolution) + bmp.RotateFlip(Type) Return bmp End Function @@ -152,7 +134,7 @@ Public Class MyBitmap Public Sub Save(FilePath As String) Dim encoder As BitmapEncoder = New PngBitmapEncoder() encoder.Frames.Add(BitmapFrame.Create(Me)) - Using fileStream = New System.IO.FileStream(FilePath, FileMode.Create) + Using fileStream As New FileStream(FilePath, FileMode.Create) encoder.Save(fileStream) End Using End Sub diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb index 251d059..409aee8 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb @@ -434,7 +434,7 @@ Dim NormalNameList = New List(Of String) For Each Name In NameLists Dim LowerName As String = Name.ToLower - If Name.ToUpper = Name Then + If Name.ToUpper = Name AndAlso Name <> "FPS" AndAlso Name <> "HUD" Then '缩写 ExNameList.Add(Name) ElseIf (LowerName.Contains("forge") OrElse LowerName.Contains("fabric") OrElse LowerName.Contains("quilt")) AndAlso @@ -487,8 +487,10 @@ NoExName: '将 “Forge” 等提示改为 “Forge 版” If (Ex.ToLower.Contains("forge") OrElse Ex.ToLower.Contains("fabric") OrElse Ex.ToLower.Contains("quilt")) AndAlso Not Ex.Contains("版") AndAlso Ex.ToLower.Replace("forge", "").Replace("fabric", "").Replace("quilt", "").Length <= 3 Then - Ex = Ex.Replace("Edition", "").Trim.Capitalize & " 版" + Ex = Ex.Replace("Edition", "").Replace("edition", "").Trim.Capitalize & " 版" End If + '将 “forge” 等词语的首字母大写 + Ex = Ex.Replace("forge", "Forge").Replace("neo", "Neo").Replace("fabric", "Fabric").Replace("quilt", "Quilt") ExName &= " | " & Ex.Trim Next If ExName = "" Then GoTo NoExName @@ -933,7 +935,10 @@ Retry: Next '更新结果 SyncLock ResultsLock - RawResults.AddRange(ProjectList) + For Each Project In ProjectList + If Task.Input.Type = CompType.Mod AndAlso Not Project.ModLoaders.Any() Then Continue For '过滤插件(#2458) + RawResults.Add(Project) + Next End SyncLock Storage.ModrinthOffset += ProjectList.Count Storage.ModrinthTotal = RequestResult("total_hits").ToObject(Of Integer) @@ -988,7 +993,7 @@ Retry: Not Storage.Results.Any(Function(b) r.IsLike(b))).ToList '加入列表 RealResults.AddRange(RawResults) - Log($"[Comp] 去重后累计新增结果 {RealResults.Count} 个") + Log($"[Comp] 去重、筛选后累计新增结果 {RealResults.Count} 个") #End Region diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb index 9b5ac13..03672de 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb @@ -127,7 +127,7 @@ End Try '导入其中的日志文件 - For Each TargetFile As FileInfo In New DirectoryInfo(TempFolder & "Temp\").EnumerateFiles + For Each TargetFile As FileInfo In New DirectoryInfo(TempFolder & "Temp\").EnumerateFiles.ToList() Try If Not TargetFile.Exists OrElse TargetFile.Length = 0 Then Continue For Dim Ext As String = TargetFile.Extension.ToLower @@ -137,6 +137,8 @@ Else AnalyzeRawFiles.Add(New KeyValuePair(Of String, String())(TargetFile.FullName, ReadFile(TargetFile.FullName, Encoding.UTF8).Split(vbCrLf.ToCharArray))) End If + Else + File.Delete(TargetFile.FullName) End If Catch ex As Exception Log(ex, "导入单个日志文件失败") @@ -235,6 +237,7 @@ End If Case AnalyzeFileType.MinecraftLog LogMc = "" + LogMcDebug = "" '创建文件名词典 Dim FileNameDict As New Dictionary(Of String, KeyValuePair(Of String, String())) For Each SelectedFile In SelectedFiles @@ -272,11 +275,21 @@ Exit For End If Next + '查找 Debug Log + For Each FileName As String In {"debug.log", "debug log.txt"} + If FileNameDict.ContainsKey(FileName) Then + Dim CurrentLog = FileNameDict(FileName) + LogMcDebug += GetHeadTailLines(CurrentLog.Value, 1000, 0) + Log("[Crash] 导入分析:" & CurrentLog.Key & ",作为 Minecraft Debug 日志") + Exit For + End If + Next '检查错误 If LogMc = "" Then LogMc = Nothing Throw New Exception("无法找到匹配的 Minecraft Log") End If + If LogMcDebug = "" Then LogMcDebug = Nothing Case AnalyzeFileType.ExtraLog '全部丢过去 For Each SelectedFile In SelectedFiles @@ -294,7 +307,7 @@ If ResultCount = 0 Then Log("[Crash] 步骤 2:准备日志文本完成,没有任何可供分析的日志") Else - Log(("[Crash] 步骤 2:准备日志文本完成,找到" & If(LogMc Is Nothing, "", "游戏日志、") & If(LogHs Is Nothing, "", "虚拟机日志、") & If(LogCrash Is Nothing, "", "崩溃日志、")).TrimEnd("、") & "用作分析") + Log(("[Crash] 步骤 2:准备日志文本完成,找到" & If(LogMc Is Nothing, "", "游戏日志、") & If(LogMcDebug Is Nothing, "", "游戏 Debug 日志、") & If(LogHs Is Nothing, "", "虚拟机日志、") & If(LogCrash Is Nothing, "", "崩溃日志、")).TrimEnd("、") & "用作分析") End If Return ResultCount @@ -315,7 +328,7 @@ End Function '3:根据文本分析崩溃原因 - Private LogMc As String = Nothing, LogHs As String = Nothing, LogCrash As String = Nothing + Private LogMc As String = Nothing, LogMcDebug As String = Nothing, LogHs As String = Nothing, LogCrash As String = Nothing Private LogAll As String '可能导致崩溃的原因与附加信息 Private CrashReasons As New Dictionary(Of CrashReason, List(Of String)) @@ -324,6 +337,7 @@ ''' Private Enum CrashReason Mod文件被解压 + MixinBootstrap缺失 内存不足 使用JDK 显卡不支持OpenGL @@ -344,9 +358,8 @@ ModMixin失败 Mod加载器报错 Mod初始化失败 - 崩溃日志堆栈分析发现关键字 - 崩溃日志堆栈分析发现Mod名称 - MC日志堆栈分析发现关键字 + 堆栈分析发现关键字 + 堆栈分析发现Mod名称 OptiFine导致无法加载世界 'https://www.minecraftforum.net/forums/support/java-edition-support/3051132-exception-ticking-world 特定方块导致崩溃 特定实体导致崩溃 @@ -372,7 +385,6 @@ ''' Public Sub Analyze(Optional Version As McVersion = Nothing) Log("[Crash] 步骤 3:分析崩溃原因") - LogAll = (If(LogMc, "") & If(LogHs, "") & If(LogCrash, "")).ToLower '1. 精准日志匹配,中/高优先级 AnalyzeCrit1() @@ -381,37 +393,41 @@ If CrashReasons.Count > 0 Then GoTo Done '2. 堆栈分析 - If LogAll.Contains("forge") OrElse LogAll.Contains("fabric") OrElse LogAll.Contains("liteloader") Then - '来源崩溃日志的分析 + LogAll = (If(LogMc, "") & If(LogMcDebug, "") & If(LogHs, "") & If(LogCrash, "")) + If LogAll.Contains("orge") OrElse LogAll.Contains("abric") OrElse LogAll.Contains("uilt") OrElse LogAll.Contains("iteloader") Then + Dim Keywords As New List(Of String) + '崩溃日志 If LogCrash IsNot Nothing Then Log("[Crash] 开始进行崩溃日志堆栈分析") Dim StackLogs As String = LogCrash.Split("System Details").First - Dim Keywords = AnalyzeStackKeyword(StackLogs) - If Keywords.Count > 0 Then - Dim Names = AnalyzeModName(Keywords) - If Names Is Nothing Then - AppendReason(CrashReason.崩溃日志堆栈分析发现关键字, Keywords) - Else - AppendReason(CrashReason.崩溃日志堆栈分析发现Mod名称, Names) - End If - GoTo Done - End If + Keywords.AddRange(AnalyzeStackKeyword(StackLogs)) End If - '来自 Minecraft 日志的分析 + 'Minecraft 日志 If LogMc IsNot Nothing Then Dim Fatals = RegexSearch(LogMc, "/FATAL] [\w\W]+?(?=[\n]+\[)") Log("[Crash] 开始进行 Minecraft 日志堆栈分析,发现 " & Fatals.Count & " 个报错项") If Fatals.Count > 0 Then - Dim Keywords As New List(Of String) For Each Fatal In Fatals Keywords.AddRange(AnalyzeStackKeyword(Fatal)) Next - If Keywords.Count > 0 Then - AppendReason(CrashReason.MC日志堆栈分析发现关键字, Keywords.Distinct.ToList) - GoTo Done - End If End If End If + '虚拟机日志 + If LogHs IsNot Nothing Then + Log("[Crash] 开始进行虚拟机堆栈分析") + Dim StackLogs As String = LogHs.Split("Registers:").First.Split("T H R E A D").Last + Keywords.AddRange(AnalyzeStackKeyword(StackLogs)) + End If + 'Mod 名称分析 + If Keywords.Count > 0 Then + Dim Names = AnalyzeModName(Keywords) + If Names Is Nothing Then + AppendReason(CrashReason.堆栈分析发现关键字, Keywords) + Else + AppendReason(CrashReason.堆栈分析发现Mod名称, Names) + End If + GoTo Done + End If Else Log("[Crash] 可能并未安装 Mod,不进行堆栈分析") End If @@ -565,22 +581,6 @@ Done: '游戏日志分析 If LogMc IsNot Nothing Then - 'Mixin 崩溃 - If LogMc.Contains("Mixin prepare failed ") OrElse LogMc.Contains("Mixin apply failed ") OrElse LogMc.Contains("MixinApplyError") OrElse - LogMc.Contains("mixin.injection.throwables.") OrElse LogMc.Contains(".mixins.json] FAILED during )") Then - Dim ModId As String = RegexSeek(LogMc, "(?<=in )[^./ ]+(?=.mixins.json.+failed injection check)") - If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=in mixins.)[^./ ]+(?=.json.+failed injection check)") - If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= failed .+ in )[^./ ]+(?=.mixins.json)") - If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= failed .+ in mixins.)[^./ ]+(?=.json)") - If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= in config \[)[^./ ]+(?=.mixins.json\] FAILED during )") - If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= in config \[mixins.)[^./ ]+(?=.json\] FAILED during )") - If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=from mod )[^./ ]+(?=\] from)") - If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=for mod )[^./ ]+(?= failed)") - '兜底名称判断 - If ModId Is Nothing Then ModId = RegexSeek(LogMc, "[^./ \]]+(?=.mixins.json") - If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=mixins.)[^./ \]]+(?=.json") - AppendReason(CrashReason.ModMixin失败, TryAnalyzeModName(If(ModId, "").TrimEnd((vbCrLf & " ").ToCharArray))) - End If '常规信息 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)) @@ -594,6 +594,24 @@ Done: '游戏日志分析 If LogMc IsNot Nothing Then + 'Mixin 崩溃 + If LogMc.Contains("Mixin prepare failed ") OrElse LogMc.Contains("Mixin apply failed ") OrElse LogMc.Contains("MixinApplyError") OrElse + LogMc.Contains("mixin.injection.throwables.") OrElse LogMc.Contains(".mixins.json] FAILED during )") Then + Dim ModId As String = RegexSeek(LogMc, "(?<=in )[^./ ]+(?=.mixins.json.+failed injection check)") + If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=in mixins.)[^./ ]+(?=.json.+failed injection check)") + If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= failed .+ in )[^./ ]+(?=.mixins.json)") + If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= failed .+ in mixins.)[^./ ]+(?=.json)") + If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= failed mixins.)[^./ ]+(?=.json:)") + If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= in config \[)[^./ ]+(?=.mixins.json\] FAILED during )") + If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= in config \[mixins.)[^./ ]+(?=.json\] FAILED during )") + If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=from mod )[^./ ]+(?=\] from)") + If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=for mod )[^./ ]+(?= failed)") + '兜底名称判断 + If ModId Is Nothing Then ModId = RegexSeek(LogMc, "[^./ \]]+(?=.mixins.json)") + If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=mixins.)[^./ \]]+(?=.json)") + AppendReason(CrashReason.ModMixin失败, TryAnalyzeModName(If(ModId, "").TrimEnd((vbCrLf & " ").ToCharArray))) + End If + '极短的程序输出 If Not (LogMc.Contains("at net.") OrElse LogMc.Contains("INFO]")) AndAlso LogHs Is Nothing AndAlso LogCrash Is Nothing AndAlso LogMc.Length < 100 Then AppendReason(CrashReason.极短的程序输出, LogMc) End If @@ -667,7 +685,7 @@ NextStack: End Function ''' - ''' 根据 Mod 关键词与详细信息(崩溃报告第二部分)与,获取实际的 Mod 名称。 + ''' 根据 Mod 关键词尝试获取实际的 Mod 名称。 ''' 若失败则返回 Nothing。 ''' Private Function AnalyzeModName(Keywords As List(Of String)) As List(Of String) @@ -682,60 +700,95 @@ NextStack: Next Keywords = RealKeywords - '获取崩溃报告对应部分 - If LogCrash Is Nothing Then Return Nothing - If Not LogCrash.Contains("A detailed walkthrough of the error") Then Return Nothing - Dim Details As String = LogCrash.Replace("A detailed walkthrough of the error", "¨") - Dim IsFabricDetail As Boolean = Details.Contains("Fabric Mods") '是否为 Fabric 信息格式 - If IsFabricDetail Then - Details = Details.Replace("Fabric Mods", "¨") - Log("[Crash] 检测到 Fabric Mod 信息格式") - End If - Details = Details.Split("¨").Last - - '[Forge] 获取所有包含 .jar 的行 - '[Fabric] 获取所有包含 Mod 信息的行 - Dim ModNameLines As New List(Of String) - For Each Line In Details.Split(vbLf) - If Line.ToLower.Contains(".jar") OrElse - (IsFabricDetail AndAlso Line.StartsWith(vbTab & vbTab) AndAlso Not RegexCheck(Line, "\t\tfabric[\w-]*: Fabric")) Then ModNameLines.Add(Line) - Next - Log("[Crash] 找到 " & ModNameLines.Count & " 个可能的 Mod 项目行") - - '获取 Mod ID 与关键词的匹配行 - Dim HintLines As New List(Of String) - For Each KeyWord As String In Keywords - For Each ModString As String In ModNameLines - Dim RealModString As String = ModString.ToLower.Replace("_", "") - If Not RealModString.Contains(KeyWord.ToLower.Replace("_", "")) Then Continue For - If RealModString.Contains("minecraft.jar") OrElse RealModString.Contains(" forge-") OrElse RealModString.Contains(" mixin-") Then Continue For - HintLines.Add(ModString.Trim(vbCrLf.ToCharArray)) - Exit For - Next - Next - HintLines = HintLines.Distinct.ToList - Log("[Crash] 找到 " & HintLines.Count & " 个可能的崩溃 Mod 匹配行") - For Each ModName As String In HintLines - Log("[Crash] - " & ModName) - Next - - '从 Mod 匹配行中提取 .jar 文件的名称 - For Each Line As String In HintLines - Dim Name As String + '从崩溃报告获取 Mod 信息 + If LogCrash IsNot Nothing AndAlso LogCrash.Contains("A detailed walkthrough of the error") Then + Dim Details As String = LogCrash.Replace("A detailed walkthrough of the error", "¨") + Dim IsFabricDetail As Boolean = Details.Contains("Fabric Mods") '是否为 Fabric 信息格式 If IsFabricDetail Then - Name = RegexSeek(Line, "(?<=: )[^\n]+(?= [^\n]+)") - Else - Name = RegexSeek(Line, "(?<=\()[^\t]+.jar(?=\))|(?<=(\t\t)|(\| ))[^\t\|]+.jar", RegularExpressions.RegexOptions.IgnoreCase) + Details = Details.Replace("Fabric Mods", "¨") + Log("[Crash] 崩溃报告中检测到 Fabric Mod 信息格式") End If - If Name IsNot Nothing Then ModFileNames.Add(Name) - Next - ModFileNames = ModFileNames.Distinct.ToList - Log("[Crash] 找到 " & ModFileNames.Count & " 个可能的崩溃 Mod 文件名") - For Each ModFileName As String In ModFileNames - Log("[Crash] - " & ModFileName) - Next + Details = Details.Split("¨").Last - Return If(ModFileNames.Count = 0, Nothing, ModFileNames) + '[Forge] 获取所有包含 .jar 的行 + '[Fabric] 获取所有包含 Mod 信息的行 + Dim ModNameLines As New List(Of String) + For Each Line In Details.Split(vbLf) + If Line.ToLower.Contains(".jar") OrElse + (IsFabricDetail AndAlso Line.StartsWith(vbTab & vbTab) AndAlso Not RegexCheck(Line, "\t\tfabric[\w-]*: Fabric")) Then ModNameLines.Add(Line) + Next + Log("[Crash] 崩溃报告中找到 " & ModNameLines.Count & " 个可能的 Mod 项目行") + + '获取 Mod ID 与关键词的匹配行 + Dim HintLines As New List(Of String) + For Each KeyWord As String In Keywords + For Each ModString As String In ModNameLines + Dim RealModString As String = ModString.ToLower.Replace("_", "") + If Not RealModString.Contains(KeyWord.ToLower.Replace("_", "")) Then Continue For + If RealModString.Contains("minecraft.jar") OrElse RealModString.Contains(" forge-") OrElse RealModString.Contains(" mixin-") Then Continue For + HintLines.Add(ModString.Trim(vbCrLf.ToCharArray)) + Exit For + Next + Next + HintLines = HintLines.Distinct.ToList + Log("[Crash] 崩溃报告中找到 " & HintLines.Count & " 个可能的崩溃 Mod 匹配行") + For Each ModLine As String In HintLines + Log("[Crash] - " & ModLine) + Next + + '从 Mod 匹配行中提取 .jar 文件的名称 + For Each Line As String In HintLines + Dim Name As String + If IsFabricDetail Then + Name = RegexSeek(Line, "(?<=: )[^\n]+(?= [^\n]+)") + Else + Name = RegexSeek(Line, "(?<=\()[^\t]+.jar(?=\))|(?<=(\t\t)|(\| ))[^\t\|]+.jar", RegularExpressions.RegexOptions.IgnoreCase) + End If + If Name IsNot Nothing Then ModFileNames.Add(Name) + Next + + End If + + '从 debug.log 获取 Mod 信息 + If LogMcDebug IsNot Nothing Then + + 'Forge: Found valid mod file YungsBetterStrongholds-1.20-Forge-4.0.1.jar with {betterstrongholds} mods - versions {1.20-Forge-4.0.1} + Dim ModNameLines As List(Of String) = RegexSearch(LogMcDebug, "(?<=valid mod file ).*", RegularExpressions.RegexOptions.Multiline) + Log("[Crash] Debug 信息中找到 " & ModNameLines.Count & " 个可能的 Mod 项目行") + + '获取 Mod ID 与关键词的匹配行 + Dim HintLines As New List(Of String) + For Each KeyWord As String In Keywords + For Each ModString As String In ModNameLines + If ModString.Contains($"{{{KeyWord}}}") Then HintLines.Add(ModString) + Next + Next + HintLines = HintLines.Distinct.ToList + Log("[Crash] Debug 信息中找到 " & HintLines.Count & " 个可能的崩溃 Mod 匹配行") + For Each ModLine As String In HintLines + Log("[Crash] - " & ModLine) + Next + + '从 Mod 匹配行中提取 .jar 文件的名称 + For Each Line As String In HintLines + Dim Name As String + Name = RegexSeek(Line, ".*(?= with)") + If Name IsNot Nothing Then ModFileNames.Add(Name) + Next + + End If + + '输出 + ModFileNames = ModFileNames.Distinct.ToList + If ModFileNames.Count = 0 Then + Return Nothing + Else + Log("[Crash] 找到 " & ModFileNames.Count & " 个可能的崩溃 Mod 文件名") + For Each ModFileName As String In ModFileNames + Log("[Crash] - " & ModFileName) + Next + Return ModFileNames + End If End Function ''' ''' 尝试获取 Mod 名称,若失败则返回原关键字。 @@ -841,26 +894,26 @@ NextStack: Case CrashReason.Mod名称包含特殊字符 Results.Add("由于有 Mod 的名称包含特殊字符,导致游戏崩溃。\n请尝试修改 Mod 文件名,让它只包含英文字母、数字、减号(-)、下划线(_)和小数点,然后再启动游戏。") Case CrashReason.MixinBootstrap缺失 - Results.Add("由于缺失 MixinBootstrap,导致游戏崩溃。\n请尝试安装 MixinBootstrap 解决。若安装后崩溃,尝试在该文件名前添加英文“!”后再试") + Results.Add("由于缺失 MixinBootstrap,导致游戏崩溃。\n请尝试安装 MixinBootstrap。若安装后依然崩溃,可以尝试在文件名前添加英文感叹号。") Case CrashReason.使用32位Java导致JVM无法分配足够多的内存 If Environment.Is64BitOperatingSystem Then Results.Add("你似乎正在使用 32 位 Java,这会导致 Minecraft 无法使用所需的内存,进而造成崩溃。\n\n请在启动设置的 Java 选择一项中改用 64 位的 Java 再启动游戏,然后再启动游戏。\n如果你没有安装 64 位的 Java,你可以从网络中下载、安装一个。") Else Results.Add("你正在使用 32 位的操作系统,这会导致 Minecraft 无法使用所需的内存,进而造成崩溃。\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缺少前置或MC版本错误 If Additional.Count > 0 Then Results.Add("由于未满足 Mod 的依赖项,导致游戏退出。\n未满足的依赖项:\n - " & Join(Additional, "\n - ") & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。") Else Results.Add("由于未满足 Mod 的依赖项,导致游戏退出。\n请根据错误报告中的日志信息进行对应处理,如果看不懂英文可以使用翻译软件。\h") End If - Case CrashReason.崩溃日志堆栈分析发现Mod名称 + Case CrashReason.堆栈分析发现关键字 + If Additional.Count = 1 Then + Results.Add("你的游戏遇到了一些问题,这可能是某些 Mod 所引起的,PCL 找到了一个可疑的关键词:" & Additional.First & "。\n\n如果你知道它对应的 Mod,那么有可能就是它引起的错误,你也可以查看错误报告获取详情。\h") + Else + Results.Add("你的游戏遇到了一些问题,这可能是某些 Mod 所引起的,PCL 找到了以下可疑的关键词:\n - " & Join(Additional, ", ") & "\n\n如果你知道这些关键词对应的 Mod,那么有可能就是它引起的错误,你也可以查看错误报告获取详情。\h") + End If + Case CrashReason.堆栈分析发现Mod名称 If Additional.Count = 1 Then Results.Add("名为 " & Additional.First & " 的 Mod 可能导致了游戏出错。\n\e\h") Else diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb index 7ca5ca2..a16140e 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb @@ -1141,14 +1141,11 @@ SystemBrowser: '1.15:Java 8 - 15 MinVer = New Version(1, 8, 0, 0) : MaxVer = New Version(1, 15, 999, 999) ElseIf VersionSortBoolean(McVersionCurrent.Version.ForgeVersion, "34.0.0") AndAlso VersionSortBoolean("36.2.25", McVersionCurrent.Version.ForgeVersion) Then - '1.16.X,Forge 34.X ~ 36.2.25:最高 Java 8u320 + '1.16,Forge 34.X ~ 36.2.25:最高 Java 8u320 MaxVer = New Version(1, 8, 0, 320) - ElseIf McVersionCurrent.Version.McCodeMain >= 18 AndAlso McVersionCurrent.Version.McCodeMain < 99 AndAlso McVersionCurrent.Version.HasOptiFine Then '#305 - '1.18+:若安装了 OptiFine,最高 Java 18 + ElseIf McVersionCurrent.Version.McCodeMain >= 18 AndAlso McVersionCurrent.Version.McCodeMain < 19 AndAlso McVersionCurrent.Version.HasOptiFine Then '#305 + '1.18:若安装了 OptiFine,最高 Java 18 MaxVer = New Version(1, 18, 999, 999) - ElseIf McVersionCurrent.Version.McCodeMain >= 19 AndAlso McVersionCurrent.Version.McCodeMain < 99 Then - '1.19+:最高 Java 19 - MaxVer = New Version(1, 19, 999, 999) End If End If @@ -1753,6 +1750,15 @@ NextVersion: '更新 options.txt Dim SetupFileAddress As String = McVersionCurrent.PathIndie & "options.txt" + If Not File.Exists(SetupFileAddress) Then + 'Yosbr Mod 兼容(#2385):https://www.curseforge.com/minecraft/mc-mods/yosbr + Dim YosbrFileAddress As String = McVersionCurrent.PathIndie & "config\yosbr\options.txt" + If File.Exists(YosbrFileAddress) Then + McLaunchLog("将修改 Yosbr Mod 中的 options.txt") + SetupFileAddress = YosbrFileAddress + WriteIni(SetupFileAddress, "lang", "none") '忽略默认语言 + End If + End If Try '语言 '1.0- :没有语言选项 @@ -1773,11 +1779,11 @@ NextVersion: WriteIni(SetupFileAddress, "lang", RequiredLang) McLaunchLog($"已将语言从 {CurrentLang} 修改为 {RequiredLang}") End If - '如果是初次设置,一并修改 forceUnicodeFont - If Setup.Get("ToolHelpChinese") AndAlso (CurrentLang = "none" OrElse Not Directory.Exists(McVersionCurrent.PathIndie & "saves")) Then - WriteIni(SetupFileAddress, "forceUnicodeFont", "true") - McLaunchLog("已开启 forceUnicodeFont") - End If + ''如果是初次设置,一并修改 forceUnicodeFont + 'If Setup.Get("ToolHelpChinese") AndAlso (CurrentLang = "none" OrElse Not Directory.Exists(McVersionCurrent.PathIndie & "saves")) Then + ' WriteIni(SetupFileAddress, "forceUnicodeFont", "true") + ' McLaunchLog("已开启 forceUnicodeFont") + 'End If '窗口 Select Case Setup.Get("LaunchArgumentWindowType") Case 0 '全屏 @@ -1861,7 +1867,7 @@ NextVersion: 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 版本不支持双层皮肤,已进行裁剪") - Skin = Skin.Clip(New System.Drawing.Rectangle(0, 0, 64, 32)) + Skin = Skin.Clip(0, 0, 64, 32) End If Skin.Save(Path & "PCL\CustomSkin_Cliped.png") '构建压缩文件 diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb index aeefbe9..8711ed0 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb @@ -1,4 +1,6 @@ -Public Module ModModpack +Imports System.IO.Compression + +Public Module ModModpack '触发整合包安装的外部接口 ''' @@ -109,7 +111,7 @@ '整合包缓存清理 Private IsInstallCacheCleared As Boolean = False Private IsInstallCacheClearing As Boolean = False - Private Sub UnpackFiles(InstallTemp As String, FileAddress As String) + Private Sub UnpackFiles(InstallTemp As String, FileAddress As String, Loader As LoaderBase) '清理缓存文件夹 If Not IsInstallCacheCleared Then IsInstallCacheCleared = True @@ -135,9 +137,8 @@ Try Retry: '完全不知道为啥会出现文件正在被另一进程使用的问题,总之多试试 - Directory.CreateDirectory(InstallTemp) DeleteDirectory(InstallTemp) - Compression.ZipFile.ExtractToDirectory(FileAddress, InstallTemp, Encode) + ExtractFile(FileAddress, InstallTemp, Encode) Catch ex As Exception Log(ex, "第 " & RetryCount & " 次解压尝试失败") If TypeOf ex Is ArgumentException Then @@ -146,6 +147,7 @@ Retry: End If If RetryCount < 5 Then Thread.Sleep(RetryCount * 2000) + If Loader IsNot Nothing AndAlso Loader.LoadingState <> MyLoading.MyLoadingState.Run Then Exit Sub RetryCount += 1 GoTo Retry Else @@ -215,13 +217,15 @@ Retry: If OverrideHome <> "" Then InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件", Sub(Task As LoaderTask(Of String, Integer)) - UnpackFiles(InstallTemp, FileAddress) + UnpackFiles(InstallTemp, FileAddress, Task) Task.Progress = 0.5 + Dim OverridePath As String = InstallTemp & ArchiveBaseFolder & OverrideHome '复制结果 - If Directory.Exists(InstallTemp & ArchiveBaseFolder & OverrideHome) Then - CopyDirectory(InstallTemp & ArchiveBaseFolder & OverrideHome, PathMcFolder & "versions\" & VersionName) + If Directory.Exists(OverridePath) Then + CopyDirectory(OverridePath, PathMcFolder & "versions\" & VersionName) + Log($"[ModPack] 整合包 override 复制:{OverridePath} -> {PathMcFolder & "versions\" & VersionName}") Else - Log("[ModPack] 整合包中未找到 override 目录,已跳过") + Log($"[ModPack] 整合包中未找到 override 文件夹:{OverridePath}") End If Task.Progress = 0.9 '开启版本隔离 @@ -393,7 +397,7 @@ Retry: Dim InstallLoaders As New List(Of LoaderBase) InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件", Sub(Task As LoaderTask(Of String, Integer)) - UnpackFiles(InstallTemp, FileAddress) + UnpackFiles(InstallTemp, FileAddress, Task) Task.Progress = 0.5 '复制 overrides 文件夹和 client-overrides 文件夹 If Directory.Exists(InstallTemp & ArchiveBaseFolder & "overrides") Then @@ -425,7 +429,7 @@ Retry: End Select End If '添加下载文件 - FileList.Add(New NetFile(File("downloads").Select(Function(t) t.ToString).ToArray, + FileList.Add(New NetFile(File("downloads").Select(Function(t) t.ToString.Replace("://edge.forgecdn", "://media.forgecdn")).ToArray, '修复 #2390 PathMcFolder & "versions\" & VersionName & "\" & File("path").ToString, New FileChecker(ActualSize:=File("fileSize").ToObject(Of Long), Hash:=File("hashes")("sha1").ToString))) Next @@ -520,7 +524,7 @@ Retry: Dim InstallLoaders As New List(Of LoaderBase) InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件", Sub(Task As LoaderTask(Of String, Integer)) - UnpackFiles(InstallTemp, FileAddress) + UnpackFiles(InstallTemp, FileAddress, Task) Task.Progress = 0.5 '复制结果 If Directory.Exists(InstallTemp & ArchiveBaseFolder & "minecraft") Then @@ -619,7 +623,7 @@ Retry: Dim InstallLoaders As New List(Of LoaderBase) InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件", Sub(Task As LoaderTask(Of String, Integer)) - UnpackFiles(InstallTemp, FileAddress) + UnpackFiles(InstallTemp, FileAddress, Task) Task.Progress = 0.5 '复制结果 If Directory.Exists(InstallTemp & ArchiveBaseFolder & ".minecraft") Then @@ -714,7 +718,7 @@ Retry: Dim InstallLoaders As New List(Of LoaderBase) InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件", Sub(Task As LoaderTask(Of String, Integer)) - UnpackFiles(InstallTemp, FileAddress) + UnpackFiles(InstallTemp, FileAddress, Task) Task.Progress = 0.5 '复制结果 If Directory.Exists(InstallTemp & ArchiveBaseFolder & "overrides") Then @@ -805,7 +809,7 @@ Retry: If String.IsNullOrWhiteSpace(NewName) Then Exit Sub '解压 Hint("正在解压压缩包……") - UnpackFiles(TargetFolder, FileAddress) + UnpackFiles(TargetFolder, FileAddress, Nothing) '加入文件夹列表 PageSelectLeft.AddFolder(TargetFolder, NewName, False) Hint("已加入游戏文件夹列表:" & NewName, HintType.Finish) diff --git a/Plain Craft Launcher 2/Modules/ModMusic.vb b/Plain Craft Launcher 2/Modules/ModMusic.vb index ae5a191..1f8f7d7 100644 --- a/Plain Craft Launcher 2/Modules/ModMusic.vb +++ b/Plain Craft Launcher 2/Modules/ModMusic.vb @@ -25,7 +25,7 @@ For Each File In My.Computer.FileSystem.GetFiles(Path & "PCL\Musics\", FileIO.SearchOption.SearchAllSubDirectories, "*.*") '文件夹可能会被加入 .ini 文件夹配置文件、一些乱七八糟的 .jpg 文件啥的 Dim Extend As String = File.Split(".").Last.ToLower - If Not (Extend = "ini" OrElse Extend = "jpg" OrElse Extend = "txt" OrElse Extend = "cfg" OrElse Extend = "lrc" OrElse Extend = "png") Then + If Not (Extend = "ini" OrElse Extend = "jpg" OrElse Extend = "txt" OrElse Extend = "cfg" OrElse Extend = "lrc" OrElse Extend = "db" OrElse Extend = "png") Then MusicAllList.Add(File) End If Next @@ -212,7 +212,7 @@ ''' 继续音乐播放,返回是否成功切换了状态。 ''' Public Function MusicResume() As Boolean - If MusicState = MusicStates.Play Then + If MusicState = MusicStates.Play OrElse Not MusicAllList.Any() Then Return False Else RunInThread(Sub() diff --git a/Plain Craft Launcher 2/My Project/AssemblyInfo.vb b/Plain Craft Launcher 2/My Project/AssemblyInfo.vb index 065e07a..91a059b 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 6b50e8f..ffeb4bc 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb +++ b/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb @@ -196,7 +196,7 @@ Public Module ModDownloadLib Else NewItem.Info = Entry("lore").ToString End If - If Entry("url").ToString.Contains("pcl") Then NewItem.Info = "[PCL 特别提供] " & NewItem.Info + If Entry("url").ToString.Contains("pcl") Then NewItem.Info = "[PCL 特供下载] " & NewItem.Info AddHandler NewItem.Click, OnClick '建立菜单 If IsSaveOnly Then @@ -299,8 +299,8 @@ Public Module ModDownloadLib WikiName = "Java版Infdev_20100618" ElseIf Id = "c0.30_01c" OrElse Id = "c0.30_survival" OrElse Id.Contains("生存测试") Then WikiName = "Java版Classic_0.30(生存模式)" - ElseIf Id = "c0.31" Then - WikiName = "Java版Indev_0.31(2010年1月30日)" + ElseIf Id.StartsWith("c0.31") Then + WikiName = "Java版Indev_0.31_20100130" ElseIf Id.StartsWith("c") Then WikiName = "Java版" & Id.Replace("c", "Classic_") ElseIf Id.StartsWith("rd-") Then @@ -382,7 +382,7 @@ Public Module ModDownloadLib Log(ex, "开始 OptiFine 下载失败", LogLevel.Feedback) End Try End Sub - Private Sub McDownloadOptiFineInstall(BaseMcFolderHome As String, Target As String, Task As LoaderTask(Of List(Of NetFile), Boolean)) + Private Sub McDownloadOptiFineInstall(BaseMcFolderHome As String, Target As String, Task As LoaderTask(Of List(Of NetFile), Boolean), UseJavaWrapper As Boolean) '选择 Java Dim Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0)) If Java Is Nothing Then @@ -399,7 +399,12 @@ Public Module ModDownloadLib If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。") End If '添加 Java Wrapper 作为主 Jar - Dim Arguments = "-Doolloo.jlw.tmpdir=""" & GetJavaWrapperDir() & """ -Duser.home=""" & BaseMcFolderHome & """ -cp """ & Target & """ -jar """ & ExtractJavaWrapper() & """ optifine.Installer" + Dim Arguments As String + If UseJavaWrapper Then + Arguments = $"-Doolloo.jlw.tmpdir=""{GetJavaWrapperDir()}"" -Duser.home=""{BaseMcFolderHome}"" -cp ""{Target}"" -jar ""{ExtractJavaWrapper()}"" optifine.Installer" + Else + Arguments = $"-Duser.home=""{BaseMcFolderHome}"" -cp ""{Target}"" optifine.Installer" + End If If Java.VersionCode >= 9 Then Arguments = "--add-exports cpw.mods.bootstraplauncher/cpw.mods.bootstraplauncher=ALL-UNNAMED " & Arguments '开始启动 SyncLock InstallSyncLock @@ -504,67 +509,66 @@ Public Module ModDownloadLib '获取下载地址 Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("获取 OptiFine 主文件下载地址", - 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, DownloadInfo.Inherit) - End If - Task.Progress = 0.1 - Dim Sources As New List(Of String) - 'BMCLAPI 源 - If DownloadInfo.IsPreview Then - Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/")) - Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/")) - Else - Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "")) - Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "")) - End If - '官方源 - Dim PageData As String - Try - PageData = NetGetCodeByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, New UTF8Encoding(False), 15000, "text/html") - Task.Progress = 0.8 - Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0)) - Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources(0)) - Catch ex As Exception - Log(ex, "获取 OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址失败") - End Try - Task.Progress = 0.9 - 'OptiFine 中文镜像源 - Sources.Add("https://optifine.cn/download/" & DownloadInfo.NameFile) - '构造文件请求 - Task.Output = New List(Of NetFile) From {New NetFile(Sources.ToArray, Target, New FileChecker(MinSize:=64 * 1024))} - End Sub) With {.ProgressWeight = 8}) + 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, DownloadInfo.Inherit) + End If + Task.Progress = 0.1 + Dim Sources As New List(Of String) + 'BMCLAPI 源 + If DownloadInfo.IsPreview Then + Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/")) + Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/")) + Else + Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "")) + Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "")) + End If + '官方源 + Dim PageData As String + Try + PageData = NetGetCodeByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, New UTF8Encoding(False), 15000, "text/html") + Task.Progress = 0.8 + Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0)) + Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources(0)) + Catch ex As Exception + Log(ex, "获取 OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址失败") + End Try + 'OptiFine 中文镜像源 + Sources.Add("https://optifine.cn/download/" & DownloadInfo.NameFile) + '构造文件请求 + Task.Output = New List(Of NetFile) From {New NetFile(Sources.ToArray, Target, New FileChecker(MinSize:=64 * 1024))} + End Sub) With {.ProgressWeight = 8}) Loaders.Add(New LoaderDownload("下载 OptiFine 主文件", New List(Of NetFile)) With {.ProgressWeight = 8}) Loaders.Add(New LoaderTask(Of List(Of NetFile), Boolean)("等待原版下载", - Sub(Task As LoaderTask(Of List(Of NetFile), Boolean)) - '等待原版文件下载完成 - If ClientDownloadLoader Is Nothing Then Exit Sub - Dim TargetLoaders As List(Of LoaderBase) = - ClientDownloadLoader.GetLoaderList.Where(Function(l) l.Name = McDownloadClientLibName OrElse l.Name = McDownloadClientJsonName). - Where(Function(l) l.State <> LoadState.Finished).ToList - If TargetLoaders.Count > 0 Then Log("[Download] OptiFine 安装正在等待原版文件下载完成") - Do While TargetLoaders.Count > 0 AndAlso Not Task.IsAborted - TargetLoaders = TargetLoaders.Where(Function(l) l.State <> LoadState.Finished).ToList - Thread.Sleep(50) - Loop - If Task.IsAborted Then Exit Sub - '拷贝原版文件 - If Not IsCustomFolder Then Exit Sub - SyncLock VanillaSyncLock - Dim ClientName As String = GetFolderNameFromPath(ClientFolder) - Directory.CreateDirectory(McFolder & "versions\" & DownloadInfo.Inherit) - If Not File.Exists(McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".json") Then - CopyFile(ClientFolder & ClientName & ".json", + Sub(Task As LoaderTask(Of List(Of NetFile), Boolean)) + '等待原版文件下载完成 + If ClientDownloadLoader Is Nothing Then Exit Sub + Dim TargetLoaders As List(Of LoaderBase) = + ClientDownloadLoader.GetLoaderList.Where(Function(l) l.Name = McDownloadClientLibName OrElse l.Name = McDownloadClientJsonName). + Where(Function(l) l.State <> LoadState.Finished).ToList + If TargetLoaders.Count > 0 Then Log("[Download] OptiFine 安装正在等待原版文件下载完成") + Do While TargetLoaders.Count > 0 AndAlso Not Task.IsAborted + TargetLoaders = TargetLoaders.Where(Function(l) l.State <> LoadState.Finished).ToList + Thread.Sleep(50) + Loop + If Task.IsAborted Then Exit Sub + '拷贝原版文件 + If Not IsCustomFolder Then Exit Sub + SyncLock VanillaSyncLock + Dim ClientName As String = GetFolderNameFromPath(ClientFolder) + Directory.CreateDirectory(McFolder & "versions\" & DownloadInfo.Inherit) + If Not File.Exists(McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".json") Then + CopyFile(ClientFolder & ClientName & ".json", McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".json") - End If - If Not File.Exists(McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".jar") Then - CopyFile(ClientFolder & ClientName & ".jar", + End If + If Not File.Exists(McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".jar") Then + CopyFile(ClientFolder & ClientName & ".jar", McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".jar") - End If - End SyncLock - End Sub) With {.ProgressWeight = 0.1, .Show = False}) + End If + End SyncLock + End Sub) With {.ProgressWeight = 0.1, .Show = False}) '安装(新旧方式均需要原版 Jar 和 Json) If IsNewVersion Then @@ -586,7 +590,19 @@ Public Module ModDownloadLib BaseMcFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".jar") Task.Progress = 0.06 '进行安装 - McDownloadOptiFineInstall(BaseMcFolderHome, Target, Task) + Dim UseJavaWrapper As Boolean = True +Retry: + Try + McDownloadOptiFineInstall(BaseMcFolderHome, Target, Task, UseJavaWrapper) + Catch ex As Exception + If UseJavaWrapper Then + Log(ex, "使用 JavaWrapper 安装 OptiFine 失败,将不使用 JavaWrapper 并重试") + UseJavaWrapper = False + GoTo Retry + Else + Throw New Exception("运行 OptiFine 安装器失败", ex) + End If + End Try Task.Progress = 0.96 '复制文件 File.Delete(BaseMcFolder & "launcher_profiles.json") @@ -596,7 +612,7 @@ Public Module ModDownloadLib File.Delete(Target) DeleteDirectory(BaseMcFolderHome) Catch ex As Exception - Throw New Exception("安装新版 OptiFine 版本失败", ex) + Throw New Exception("安装 OptiFine(方式 A)失败", ex) End Try End Sub) With {.ProgressWeight = 8}) Else @@ -645,7 +661,7 @@ Public Module ModDownloadLib End If WriteFile(VersionFolder & Id & ".json", Json) Catch ex As Exception - Throw New Exception("安装旧版 OptiFine 版本失败", ex) + Throw New Exception("安装 OptiFine(方式 B)失败", ex) End Try End Sub) With {.ProgressWeight = 1}) End If @@ -1110,7 +1126,7 @@ Public Module ModDownloadLib End Try End Sub - Private Sub ForgeInjector(Target As String, Task As LoaderTask(Of Boolean, Boolean), McFolder As String) + Private Sub ForgeInjector(Target As String, Task As LoaderTask(Of Boolean, Boolean), McFolder As String, UseJavaWrapper As Boolean) '选择 Java Dim Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0)) If Java Is Nothing Then @@ -1127,7 +1143,12 @@ Public Module ModDownloadLib If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。") End If '添加 Java Wrapper 作为主 Jar - Dim Arguments = "-Doolloo.jlw.tmpdir=""" & GetJavaWrapperDir() & """ -cp """ & PathTemp & "Cache\forge_installer.jar;" & Target & """ -jar """ & ExtractJavaWrapper() & """ com.bangbang93.ForgeInstaller """ & McFolder + Dim Arguments As String + If UseJavaWrapper Then + Arguments = $"-Doolloo.jlw.tmpdir=""{GetJavaWrapperDir()}"" -cp ""{PathTemp}Cache\forge_installer.jar;{Target}"" -jar ""{ExtractJavaWrapper()}"" com.bangbang93.ForgeInstaller ""{McFolder}" + Else + Arguments = $"-cp ""{PathTemp}Cache\forge_installer.jar;{Target}"" com.bangbang93.ForgeInstaller ""{McFolder}" + End If If Java.VersionCode >= 9 Then Arguments = "--add-exports cpw.mods.bootstraplauncher/cpw.mods.bootstraplauncher=ALL-UNNAMED " & Arguments '开始启动 SyncLock InstallSyncLock @@ -1415,12 +1436,25 @@ Public Module ModDownloadLib '释放 launcher_installer.json McFolderLauncherProfilesJsonCreate(McFolder) Task.Progress = 0.05 - '释放 Forge 注入器 - WriteFile(PathTemp & "Cache\forge_installer.jar", GetResources("ForgeInstaller")) - Task.Progress = 0.06 - '运行注入器 - ForgeInjector(InstallerAddress, Task, McFolder) - Task.Progress = 0.97 + '运行 Forge 安装器 + Dim UseJavaWrapper As Boolean = True +Retry: + Try + '释放 Forge 注入器 + WriteFile(PathTemp & "Cache\forge_installer.jar", GetResources("ForgeInstaller")) + Task.Progress = 0.06 + '运行注入器 + ForgeInjector(InstallerAddress, Task, McFolder, UseJavaWrapper) + Task.Progress = 0.97 + Catch ex As Exception + If UseJavaWrapper Then + Log(ex, "使用 JavaWrapper 安装 Forge 失败,将不使用 JavaWrapper 并重试") + UseJavaWrapper = False + GoTo Retry + Else + Throw New Exception("运行 Forge 安装器失败", ex) + End If + End Try '拷贝新增的版本 Json Dim DeltaList = New DirectoryInfo(McFolder & "versions\").EnumerateDirectories.SkipWhile(Function(Info As DirectoryInfo) As Boolean Return OldList.Contains(Info.FullName) @@ -1470,7 +1504,7 @@ Public Module ModDownloadLib Task.Progress = 0.6 '解压支持库文件 Installer.Dispose() - ZipFile.ExtractToDirectory(InstallerAddress, InstallerAddress & "_unrar\") + ExtractFile(InstallerAddress, InstallerAddress & "_unrar\") CopyDirectory(InstallerAddress & "_unrar\maven\", McFolder & "libraries\") DeleteDirectory(InstallerAddress & "_unrar\") Else diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb index 01e94ba..0138f2d 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb @@ -848,7 +848,7 @@ If LoadFabric.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……" If LoadFabric.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadFabric.State, Object).Error.Message For Each Version As JObject In DlFabricListLoader.Output.Value("game") - If Version("version").ToString = SelectedMinecraftId.Replace("∞", "infinite") Then + If Version("version").ToString = SelectedMinecraftId.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3") Then If SelectedForge IsNot Nothing Then Return "与 Forge 不兼容" 'If SelectedOptiFine IsNot Nothing Then Return "与 OptiFine 不兼容" Return Nothing @@ -908,7 +908,7 @@ Public Shared Function IsSuitableFabricApi(DisplayName As String, MinecraftVersion As String) As Boolean Try If DisplayName Is Nothing OrElse MinecraftVersion Is Nothing Then Return False - DisplayName = DisplayName.ToLower : MinecraftVersion = MinecraftVersion.ToLower + DisplayName = DisplayName.ToLower : MinecraftVersion = MinecraftVersion.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3").ToLower If DisplayName.StartsWith("[" & MinecraftVersion & "]") Then Return True If Not DisplayName.Contains("/") OrElse Not DisplayName.Contains("]") Then Return False '直接的判断(例如 1.18.1/22w03a) diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/MySkin.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/MySkin.xaml.vb index 5f1849a..e26a226 100644 --- a/Plain Craft Launcher 2/Pages/PageLaunch/MySkin.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageLaunch/MySkin.xaml.vb @@ -104,7 +104,7 @@ (Image.Pic.GetPixel(1, 1) <> Image.Pic.GetPixel(Scale * 41, Scale * 9) AndAlso '或是头部颜色和透明区均不一样 Image.Pic.GetPixel(Image.Pic.Width - 1, Image.Pic.Height - 1) <> Image.Pic.GetPixel(Scale * 41, Scale * 9) AndAlso Image.Pic.GetPixel(Image.Pic.Width - 2, Image.Pic.Height / 2 - 2) <> Image.Pic.GetPixel(Scale * 41, Scale * 9)) Then - ImgFore.Source = New CroppedBitmap(Image, New Int32Rect(Scale * 40, Scale * 8, Scale * 8, Scale * 8)) + ImgFore.Source = Image.Clip(Scale * 40, Scale * 8, Scale * 8, Scale * 8) Else ImgFore.Source = Nothing End If @@ -112,7 +112,7 @@ ImgFore.Source = Nothing End If '脸层 - ImgBack.Source = New CroppedBitmap(Image, New Int32Rect(Scale * 8, Scale * 8, Scale * 8, Scale * 8)) + ImgBack.Source = Image.Clip(Scale * 8, Scale * 8, Scale * 8, Scale * 8) Log("[Skin] 载入头像成功:" & Loader.Name) Catch ex As Exception Log(ex, "载入头像失败(" & If(Address, "null") & "," & Loader.Name & ")", LogLevel.Hint) diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml.vb index 0a1cbb3..ce9c4a9 100644 --- a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml.vb @@ -673,14 +673,7 @@ ExitRefresh: McLaunchProcess = Nothing McLaunchWatcher = Nothing '获取 “你知道吗” 提示 - Dim Hint As String = PageOtherTest.GetRandomHint() - Try - Dim RawHints = ReadFile(Path & "PCL\hints.txt").Split(vbCrLf.ToCharArray).Where(Function(s) Not String.IsNullOrWhiteSpace(s)).ToList - If RawHints.Count > 0 Then Hint = RandomOne(RawHints) - Catch ex As Exception - Log(ex, "获取自定义 你知道吗 提示失败", LogLevel.Hint) - End Try - LabLaunchingHint.Text = Hint + LabLaunchingHint.Text = PageOtherTest.GetRandomHint() End Sub Private ShowProgress As Double = 0 ''' diff --git a/Plain Craft Launcher 2/Pages/PageLink/PageLinkIoi.xaml.vb b/Plain Craft Launcher 2/Pages/PageLink/PageLinkIoi.xaml.vb index 85ee5c3..8d7a317 100644 --- a/Plain Craft Launcher 2/Pages/PageLink/PageLinkIoi.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageLink/PageLinkIoi.xaml.vb @@ -133,7 +133,7 @@ StartDownload: Log("[IOI] 解压联机模块以完成下载") If File.Exists(PathTemp & "ioi.exe") Then File.Delete(PathTemp & "ioi.exe") If File.Exists(PathIoi) Then File.Delete(PathIoi) - Compression.ZipFile.ExtractToDirectory(PathTemp & "联机模块.zip", PathTemp) + ExtractFile(PathTemp & "联机模块.zip", PathTemp) File.Delete(PathTemp & "联机模块.zip") CopyFile(PathTemp & "ioi.exe", PathIoi) File.Delete(PathTemp & "ioi.exe") diff --git a/Plain Craft Launcher 2/Pages/PageOther/PageOtherHelpDetail.xaml.vb b/Plain Craft Launcher 2/Pages/PageOther/PageOtherHelpDetail.xaml.vb index 203bd0f..79aecfd 100644 --- a/Plain Craft Launcher 2/Pages/PageOther/PageOtherHelpDetail.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageOther/PageOtherHelpDetail.xaml.vb @@ -14,7 +14,7 @@ If String.IsNullOrEmpty(Entry.XamlContent) Then Throw New Exception("帮助 xaml 文件为空") Me.Entry = Entry PanCustom.Children.Clear() - FileContent = FileContent.Replace("{path}", Path) + FileContent = HelpArgumentReplace(FileContent) PanCustom.Children.Add(GetObjectFromXML(FileContent)) Return True Catch ex As Exception diff --git a/Plain Craft Launcher 2/Pages/PageSelectRight.xaml.vb b/Plain Craft Launcher 2/Pages/PageSelectRight.xaml.vb index ea70ccd..215b136 100644 --- a/Plain Craft Launcher 2/Pages/PageSelectRight.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageSelectRight.xaml.vb @@ -219,6 +219,7 @@ If(IsHintIndie, vbCrLf & "由于该版本开启了版本隔离,删除版本时该版本对应的存档、资源包、Mod 等文件也将被一并删除!", ""), "版本删除确认", , "取消",, True) Case 1 + IniClearCache(Version.Path & "PCL\Setup.ini") If IsShiftPressed Then DeleteDirectory(Version.Path) Hint("版本 " & Version.Name & " 已永久删除!", HintType.Finish) diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml index 16966f9..d188d32 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml +++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml @@ -74,7 +74,7 @@ - + diff --git a/Plain Craft Launcher 2/Pages/PageSpeedLeft.xaml.vb b/Plain Craft Launcher 2/Pages/PageSpeedLeft.xaml.vb index b3874fe..3e961fd 100644 --- a/Plain Craft Launcher 2/Pages/PageSpeedLeft.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageSpeedLeft.xaml.vb @@ -195,7 +195,6 @@ AniDispose(sender, False) AniDispose(Card, True, Sub() If FrmSpeedRight.PanMain.Children.Count = 0 AndAlso FrmMain.PageCurrent = FormMain.PageType.DownloadManager Then FrmMain.PageBack()) RightCards.Remove(Loader.Name) - LoaderTaskbar.Remove(Loader) Log($"[Watcher] 关闭下载管理卡片:{Loader.Name}") RunInThread(Sub() Loader.Abort()) End Sub diff --git a/Plain Craft Launcher 2/Resources/Custom.xaml b/Plain Craft Launcher 2/Resources/Custom.xaml index 1dcaf68..3502147 100644 --- a/Plain Craft Launcher 2/Resources/Custom.xaml +++ b/Plain Craft Launcher 2/Resources/Custom.xaml @@ -4,102 +4,102 @@ 你也可以使用 Ctrl + F 快速查找。例如,若需要改变文本颜色,则搜索 “颜色” 即可。 --> - + - + - - - + + - + - + - - + + - + - - - - + - + - - - - + - + - - - + - + - - - - + + - - + + @@ -133,128 +133,176 @@ - - - + + - - - + + - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - + - + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - + - + - + - - + + - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file +local:MyIconTextButton(带图标的按钮): + - Text:设置显示的文本 + - Logo:设置显示的图标 + - LogoScale:设置图标的缩放,默认为 1 + - ColorType:设置颜色主题(Highlight 为当前启动器的主题颜色,默认为黑色) + - EventType、EventData:触发特定事件 + +local:MyIconButton(图标按钮): + - Logo:设置显示的图标 + - LogoScale:设置图标的缩放,默认为 1 + - Theme:设置颜色主题,Color(默认)为当前启动器的主题颜色,也可设置为 White、Black、Red + - EventType、EventData:触发特定事件 + +--> diff --git a/Plain Craft Launcher 2/Resources/Help.zip b/Plain Craft Launcher 2/Resources/Help.zip index 481cade..bd5a837 100644 Binary files a/Plain Craft Launcher 2/Resources/Help.zip and b/Plain Craft Launcher 2/Resources/Help.zip differ