diff --git a/Plain Craft Launcher 2/Application.xaml.vb b/Plain Craft Launcher 2/Application.xaml.vb index b9b88a1..32909e7 100644 --- a/Plain Craft Launcher 2/Application.xaml.vb +++ b/Plain Craft Launcher 2/Application.xaml.vb @@ -213,6 +213,7 @@ Public Class Application Dim MsJson As JObject = GetJson(Setup.Get("LoginMsJson")) MsJson.Remove(sender.Tag) Setup.Set("LoginMsJson", MsJson.ToString(Newtonsoft.Json.Formatting.None)) + If FrmLoginMs.ComboAccounts.SelectedItem Is sender.Parent Then FrmLoginMs.ComboAccounts.SelectedIndex = 0 FrmLoginMs.ComboAccounts.Items.Remove(sender.Parent) Case McLoginType.Legacy '离线 diff --git a/Plain Craft Launcher 2/Controls/MyCard.vb b/Plain Craft Launcher 2/Controls/MyCard.vb index 3f6d66a..11b7669 100644 --- a/Plain Craft Launcher 2/Controls/MyCard.vb +++ b/Plain Craft Launcher 2/Controls/MyCard.vb @@ -13,8 +13,26 @@ MainBorder.Child = value End Set End Property - Public MainTextBlock As TextBlock - Public MainSwap As Shapes.Path + Private _MainTextBlock As TextBlock + Public Property MainTextBlock As TextBlock + Get + Init() '当父级触发 Loaded 时,本卡片可能尚未触发 Loaded(该事件从父级向子级调用),因此这会是 null。手动触发以确保控件已加载。 + Return _MainTextBlock + End Get + Set(value As TextBlock) + _MainTextBlock = value + End Set + End Property + Private _MainSwap As Shapes.Path + Public Property MainSwap As Shapes.Path + Get + Init() + Return _MainSwap + End Get + Set(value As Shapes.Path) + _MainSwap = value + End Set + End Property '属性 Public Uuid As Integer = GetUuid() @@ -24,7 +42,7 @@ End Get Set(value As String) SetValue(TitleProperty, value) - If MainTextBlock IsNot Nothing Then MainTextBlock.Text = value + If _MainTextBlock IsNot Nothing Then MainTextBlock.Text = value End Set End Property Public Shared ReadOnly TitleProperty As DependencyProperty = DependencyProperty.Register("Title", GetType(String), GetType(MyCard), New PropertyMetadata("")) @@ -40,10 +58,11 @@ Children.Add(MainGrid) End Sub Private IsLoad As Boolean = False - Private Sub MyCard_Loaded() Handles Me.Loaded + Private Sub Init() Handles Me.Loaded If IsLoad Then Exit Sub + IsLoad = True '初次加载限定 - If Not Title = "" AndAlso IsNothing(MainTextBlock) Then + If Title <> "" AndAlso MainTextBlock Is Nothing Then MainTextBlock = New TextBlock With {.HorizontalAlignment = HorizontalAlignment.Left, .VerticalAlignment = VerticalAlignment.Top, .Margin = New Thickness(15, 12, 0, 0), .FontWeight = FontWeights.Bold, .FontSize = 13, .IsHitTestVisible = False} MainTextBlock.SetResourceReference(TextBlock.ForegroundProperty, "ColorBrush1") MainTextBlock.SetBinding(TextBlock.TextProperty, New Binding("Title") With {.Source = Me, .Mode = BindingMode.OneWay}) @@ -55,7 +74,6 @@ MainSwap.SetResourceReference(Shapes.Path.FillProperty, "ColorBrush1") MainGrid.Children.Add(MainSwap) End If - IsLoad = True '改变默认的折叠 If IsSwaped AndAlso SwapControl IsNot Nothing Then MainSwap.RenderTransform = New RotateTransform(If(SwapLogoRight, 270, 0)) diff --git a/Plain Craft Launcher 2/Controls/MyListItem.xaml.vb b/Plain Craft Launcher 2/Controls/MyListItem.xaml.vb index 68866e6..60706f9 100644 --- a/Plain Craft Launcher 2/Controls/MyListItem.xaml.vb +++ b/Plain Craft Launcher 2/Controls/MyListItem.xaml.vb @@ -656,9 +656,9 @@ SetResourceReference(ForegroundProperty, "ColorBrush1") End If ColumnPaddingRight.Width = New GridLength(MinPaddingRight) - If EventType = "打开帮助" Then + If EventType = "打开帮助" AndAlso Not (Title <> "" AndAlso Info <> "") Then '#3266 Try - Dim NewElem = New HelpEntry(GetEventAbsoluteUrls(EventData, EventType)(0)).SetToListItem(Me) + Dim Unused = New HelpEntry(GetEventAbsoluteUrls(EventData, EventType)(0)).SetToListItem(Me) Catch ex As Exception Log(ex, "设置帮助 MyListItem 失败", LogLevel.Msgbox) End Try diff --git a/Plain Craft Launcher 2/FormMain.xaml.vb b/Plain Craft Launcher 2/FormMain.xaml.vb index 1278084..2910887 100644 --- a/Plain Craft Launcher 2/FormMain.xaml.vb +++ b/Plain Craft Launcher 2/FormMain.xaml.vb @@ -108,13 +108,19 @@ Public Class FormMain '3:BUG+ IMP* FEAT- '2:BUG* IMP- '1:BUG- + If LastVersion < 325 Then 'Snapshot 2.7.4 + FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "会自动隐藏明显不可用的自动安装选项")) + FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化正版登录流程和 MC 性能")) + FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复正版登录时弹出脚本错误提示的 Bug")) + FeatureCount += 17 + BugCount += 19 + End If If LastVersion < 324 Then 'Snapshot 2.7.3 FeatureCount += 4 BugCount += 3 End If If LastVersion < 322 Then 'Snapshot 2.7.2 FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "添加 启动游戏前进行内存优化 设置")) - FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化 MC 性能")) FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复安装 OptiFine 有概率失败的 Bug")) FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复启动 Fabric 1.20.5+ 时无法正确选择 Java 的 Bug")) FeatureCount += 18 @@ -376,44 +382,46 @@ Public Class FormMain AaOpacity(Me, Setup.Get("UiLauncherTransparent") / 1000 + 0.4, 250, 100), AaDouble(Sub(i) TransformPos.Y += i, -TransformPos.Y, 600, 100, New AniEaseOutBack(AniEasePower.Weak)), AaDouble(Sub(i) TransformRotate.Angle += i, -TransformRotate.Angle, 500, 100, New AniEaseOutBack(AniEasePower.Weak)), - AaCode(Sub() - PanBack.RenderTransform = Nothing - IsWindowLoadFinished = True - Log($"[System] DPI:{DPI},系统版本:{OsVersion},PCL 位置:{PathWithName}") - End Sub, , True) + AaCode( + Sub() + PanBack.RenderTransform = Nothing + IsWindowLoadFinished = True + Log($"[System] DPI:{DPI},系统版本:{OsVersion},PCL 位置:{PathWithName}") + End Sub, , True) }, "Form Show") 'Timer 启动 AniStart() TimerMainStart() '加载池 - RunInNewThread(Sub() - 'EULA 提示 - If Not Setup.Get("SystemEula") Then - Select Case MyMsgBox("在使用 PCL 前,请同意 PCL 的用户协议与免责声明。", "协议授权", "同意", "拒绝", "查看用户协议与免责声明", + RunInNewThread( + Sub() + 'EULA 提示 + If Not Setup.Get("SystemEula") Then + Select Case MyMsgBox("在使用 PCL 前,请同意 PCL 的用户协议与免责声明。", "协议授权", "同意", "拒绝", "查看用户协议与免责声明", Button3Action:=Sub() OpenWebsite("https://shimo.im/docs/rGrd8pY8xWkt6ryW")) - Case 1 - Setup.Set("SystemEula", True) - Case 2 - EndProgram(False) - End Select - End If - '启动加载器池 - Try - JavaListInit() '延后到同意协议后再执行,避免在初次启动时进行进程操作 - Thread.Sleep(200) - DlClientListMojangLoader.Start(1) - RunCountSub() - ServerLoader.Start(1) - 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 - End Sub, "Start Loader", ThreadPriority.Lowest) + Case 1 + Setup.Set("SystemEula", True) + Case 2 + EndProgram(False) + End Select + End If + '启动加载器池 + Try + JavaListInit() '延后到同意协议后再执行,避免在初次启动时进行进程操作 + Thread.Sleep(200) + DlClientListMojangLoader.Start(1) + RunCountSub() + ServerLoader.Start(1) + 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 + End Sub, "Start Loader", ThreadPriority.Lowest) Log("[Start] 第三阶段加载用时:" & GetTimeTick() - ApplicationStartTick & " ms") End Sub @@ -521,12 +529,13 @@ Public Class FormMain If SendWarning AndAlso HasDownloadingTask() Then If MyMsgBox("还有下载任务尚未完成,是否确定退出?", "提示", "确定", "取消") = 1 Then '强行结束下载任务 - RunInNewThread(Sub() - Log("[System] 正在强行停止任务") - For Each Task As LoaderBase In LoaderTaskbar.ToArray - Task.Abort() - Next - End Sub, "强行停止下载任务") + RunInNewThread( + Sub() + Log("[System] 正在强行停止任务") + For Each Task As LoaderBase In LoaderTaskbar.ToArray + Task.Abort() + Next + End Sub, "强行停止下载任务") Else Exit Sub End If @@ -542,17 +551,19 @@ Public Class FormMain PanBack.RenderTransform = New TransformGroup() With {.Children = New TransformCollection({TransformRotate, TransformPos, TransformScale})} AniStart({ AaOpacity(Me, -Opacity, 140, 40, New AniEaseOutFluent(AniEasePower.Weak)), - AaDouble(Sub(i) - TransformScale.ScaleX += i - TransformScale.ScaleY += i - End Sub, 0.88 - TransformScale.ScaleX, 180), + AaDouble( + Sub(i) + TransformScale.ScaleX += i + TransformScale.ScaleY += i + End Sub, 0.88 - TransformScale.ScaleX, 180), AaDouble(Sub(i) TransformPos.Y += i, 20 - TransformPos.Y, 180, 0, New AniEaseOutFluent(AniEasePower.Weak)), AaDouble(Sub(i) TransformRotate.Angle += i, 0.6 - TransformRotate.Angle, 180, 0, New AniEaseInoutFluent(AniEasePower.Weak)), - AaCode(Sub() - IsHitTestVisible = False - Top = -10000 - ShowInTaskbar = False - End Sub, 210), + AaCode( + Sub() + IsHitTestVisible = False + Top = -10000 + ShowInTaskbar = False + End Sub, 210), AaCode(AddressOf EndProgramForce, 230) }, "Form Close") Else @@ -800,14 +811,14 @@ Public Class FormMain If FilePathList.Count > 1 Then '必须要求全部为 jar 文件 For Each File In FilePathList - If Not {"jar", "litemod", "disabled", "old"}.Contains(File.Split(".").Last.ToLower) Then + If Not {"jar", "litemod", "disabled", "old"}.Contains(File.After(".").ToLower) Then Hint("一次请只拖入一个文件!", HintType.Critical) Exit Sub End If Next End If '自定义主页 - Dim Extension As String = FilePath.Split(".").Last.ToLower + Dim Extension As String = FilePath.After(".").ToLower If Extension = "xaml" Then Log("[System] 文件后缀为 XAML,作为自定义主页加载") If File.Exists(Path & "PCL\Custom.xaml") Then diff --git a/Plain Craft Launcher 2/Modules/Base/ModBase.vb b/Plain Craft Launcher 2/Modules/Base/ModBase.vb index 5166a20..e07e1eb 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.7.3" '不含分支前缀的显示用版本名 - Public Const VersionStandardCode As String = "2.7.3." & VersionBranchCode '标准格式的四段式版本号 + Public Const VersionBaseName As String = "2.7.4" '不含分支前缀的显示用版本名 + Public Const VersionStandardCode As String = "2.7.4." & VersionBranchCode '标准格式的四段式版本号 #If BETA Then Public Const VersionCode As Integer = 323 'Release #Else - Public Const VersionCode As Integer = 324 'Snapshot + Public Const VersionCode As Integer = 325 'Snapshot #End If '自动生成的版本信息 Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName @@ -1451,7 +1451,8 @@ 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)) + Dim Length As Integer = If(Data, "").Length + Throw New Exception("格式化 json 对象失败:" & If(Length > 10000, Data.Substring(0, 100) & $"...(全长 {Length} 个字符)..." & Right(Data, 100), Data)) End Try End Function @@ -1544,6 +1545,51 @@ Re: Return Input.All(Function(c) AscW(c) < 128) End Function + ''' + ''' 获取在子字符串之前的部分。 + ''' 会裁切尽可能多的内容,但如果未找到子字符串则不裁切。 + ''' + Public Function Before(Str As String, Text As String, Optional IgnoreCase As Boolean = False) As String + Dim Pos As Integer = If(String.IsNullOrEmpty(Text), -1, Str.IndexOfF(Text, IgnoreCase)) + If Pos >= 0 Then + Return Str.Substring(0, Pos) + Else + Return Str + End If + End Function + ''' + ''' 获取在子字符串之后的部分。 + ''' 会裁切尽可能多的内容,但如果未找到子字符串则不裁切。 + ''' + Public Function After(Str As String, Text As String, Optional IgnoreCase As Boolean = False) As String + Dim Pos As Integer = If(String.IsNullOrEmpty(Text), -1, Str.LastIndexOfF(Text, IgnoreCase)) + If Pos >= 0 Then + Return Str.Substring(Pos + Text.Length) + Else + Return Str + End If + End Function + ''' + ''' 获取处于两个子字符串之间的部分。 + ''' 会裁切尽可能多的内容:匹配开始使用 LastIndexOf,匹配结束使用 IndexOf,但如果未找到子字符串则不裁切。 + ''' + Public Function Between(Str As String, Before As String, After As String, Optional IgnoreCase As Boolean = False) As String + Dim StartPos As Integer = If(String.IsNullOrEmpty(Before), -1, Str.LastIndexOfF(Before, IgnoreCase)) + If StartPos >= 0 Then + StartPos += Before.Length + Else + StartPos = 0 + End If + Dim EndPos As Integer = If(String.IsNullOrEmpty(After), -1, Str.IndexOfF(After, StartPos, IgnoreCase)) + If EndPos >= 0 Then + Return Str.Substring(StartPos, EndPos - StartPos) + ElseIf StartPos > 0 Then + Return Str.Substring(StartPos) + Else + Return Str + End If + End Function + ''' ''' 高速的 StartsWith。 ''' @@ -1573,12 +1619,26 @@ Re: Return Str.IndexOf(SubStr, If(IgnoreCase, StringComparison.OrdinalIgnoreCase, StringComparison.Ordinal)) End Function ''' + ''' 高速的 IndexOf。 + ''' + + Public Function IndexOfF(Str As String, SubStr As String, StartIndex As Integer, Optional IgnoreCase As Boolean = False) As Integer + Return Str.IndexOf(SubStr, StartIndex, If(IgnoreCase, StringComparison.OrdinalIgnoreCase, StringComparison.Ordinal)) + End Function + ''' ''' 高速的 LastIndexOf。 ''' Public Function LastIndexOfF(Str As String, SubStr As String, Optional IgnoreCase As Boolean = False) As Integer Return Str.LastIndexOf(SubStr, If(IgnoreCase, StringComparison.OrdinalIgnoreCase, StringComparison.Ordinal)) End Function + ''' + ''' 高速的 LastIndexOf。 + ''' + + Public Function LastIndexOfF(Str As String, SubStr As String, StartIndex As Integer, Optional IgnoreCase As Boolean = False) As Integer + Return Str.LastIndexOf(SubStr, StartIndex, If(IgnoreCase, StringComparison.OrdinalIgnoreCase, StringComparison.Ordinal)) + End Function ''' ''' 输入 And 字符不会报错的 Val。 diff --git a/Plain Craft Launcher 2/Modules/Base/ModLoader.vb b/Plain Craft Launcher 2/Modules/Base/ModLoader.vb index 9c1c573..18ed602 100644 --- a/Plain Craft Launcher 2/Modules/Base/ModLoader.vb +++ b/Plain Craft Launcher 2/Modules/Base/ModLoader.vb @@ -266,7 +266,7 @@ Public Output As OutputType = Nothing '获取输入 - Public Function StartGetInput(Optional Input As InputType = Nothing, Optional InputDelegate As Func(Of Object) = Nothing) As InputType + Public Function StartGetInput(Optional Input As InputType = Nothing, Optional InputDelegate As Func(Of Object) = Nothing) As InputType 'InputDelegate 参数存在匿名调用 If InputDelegate Is Nothing Then InputDelegate = Me.InputDelegate Dim NewInput As InputType = Nothing '若 InputType 不能为 Nothing,则会导致 Input Is Nothing 永远失败,因此需要额外判断 If (Input Is Nothing OrElse (NewInput IsNot Nothing AndAlso Input.Equals(NewInput))) AndAlso InputDelegate IsNot Nothing Then diff --git a/Plain Craft Launcher 2/Modules/Base/ModNet.vb b/Plain Craft Launcher 2/Modules/Base/ModNet.vb index 470597a..de97191 100644 --- a/Plain Craft Launcher 2/Modules/Base/ModNet.vb +++ b/Plain Craft Launcher 2/Modules/Base/ModNet.vb @@ -781,7 +781,7 @@ RequestFinished: ''' ''' 为不需要分割的小文件进行临时存储。 ''' - Private SmailFileCache As List(Of Byte) + Private SmailFileCache As Queue(Of Byte) ''' ''' 文件的已下载大小。 @@ -1101,7 +1101,7 @@ NotSupportRange: '创建缓存文件 If IsNoSplit Then Info.Temp = Nothing - SmailFileCache = New List(Of Byte) + SmailFileCache = New Queue(Of Byte) Else Info.Temp = PathTemp & "Download\" & Uuid & "_" & Info.Uuid & "_" & RandomInteger(0, 999999) & ".tmp" ResultStream = New FileStream(Info.Temp, FileMode.Create, FileAccess.Write, FileShare.Read) @@ -1152,9 +1152,15 @@ NotSupportRange: Info.DownloadDone += RealDataCount If IsNoSplit Then If HttpData.Count = RealDataCount Then - SmailFileCache.AddRange(HttpData) + 'SmailFileCache.AddRange(HttpData) + For Each B In HttpData + SmailFileCache.Enqueue(B) + Next Else - SmailFileCache.AddRange(HttpData.ToList.GetRange(0, RealDataCount)) + 'SmailFileCache.AddRange(HttpData.ToList.GetRange(0, RealDataCount)) + For i = 0 To RealDataCount - 1 + SmailFileCache.Enqueue(HttpData(i)) + Next End If Else ResultStream.Write(HttpData, 0, RealDataCount) @@ -1456,7 +1462,9 @@ Retry: Public Overrides Property Progress As Double Get If State >= LoadState.Finished Then Return 1 - If Not Files.Any() Then Return 0 '必须返回 0,否则在获取列表的时候会错觉已经下载完了 + SyncLock FilesLock + If Not Files.Any() Then Return 0 '必须返回 0,否则在获取列表的时候会错觉已经下载完了 + End SyncLock Return _Progress End Get Set(value As Double) diff --git a/Plain Craft Launcher 2/Modules/Base/ModValidate.vb b/Plain Craft Launcher 2/Modules/Base/ModValidate.vb index a89dcc3..154e950 100644 --- a/Plain Craft Launcher 2/Modules/Base/ModValidate.vb +++ b/Plain Craft Launcher 2/Modules/Base/ModValidate.vb @@ -149,7 +149,7 @@ Public Class ValidateExcept End Sub Public Overrides Function Validate(Str As String) As String For Each Ch As String In Excepts - If Str.IndexOfF(Ch, StringComparison.OrdinalIgnoreCase) >= 0 Then + If Str.IndexOfF(Ch, True) >= 0 Then If IsNothing(ErrorMessage) Then ErrorMessage = "" Return ErrorMessage.Replace("%", Ch) End If diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb index d7b0f10..39dbf53 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb @@ -265,7 +265,7 @@ Next CurseForgeFileIds = Files.Select(Function(f) f.Key).Distinct.ToList GameVersions = Files.SelectMany(Function(f) f.Value).Where(Function(v) v.StartsWithF("1.")). - Select(Function(v) CInt(Val(v.Split(".")(1).Split("-").First))).Where(Function(v) v > 0). + Select(Function(v) CInt(Val(v.Split(".")(1).Before("-")))).Where(Function(v) v > 0). Distinct.OrderByDescending(Function(v) v).ToList ModLoaders = ModLoaders.Distinct.OrderBy(Of Integer)(Function(t) t).ToList 'Type @@ -342,7 +342,7 @@ '搜索结果的键为 versions,获取特定工程的键为 game_versions GameVersions = If(CType(If(Data("game_versions"), Data("versions")), JArray), New JArray). Select(Function(v) v.ToString).Where(Function(v) v.StartsWithF("1.")). - Select(Of Integer)(Function(v) Val(v.Split(".")(1).Split("-").First)).Where(Function(v) v > 0). + Select(Of Integer)(Function(v) Val(v.Split(".")(1).Before("-"))).Where(Function(v) v > 0). Distinct.OrderByDescending(Function(v) v).ToList 'Type Select Case Data("project_type").ToString @@ -576,9 +576,9 @@ '有中文翻译 '尝试将文本分为三段:Title (EnglishName) - Suffix '检查时注意 Carpet:它没有中文译名,但有 Suffix - Title = TranslatedName.Split(" (").First.Split(" - ").First + Title = TranslatedName.Before(" (").Before(" - ") Dim Suffix As String = "" - If TranslatedName.Split(")").Last.Contains(" - ") Then Suffix = TranslatedName.Split(")").Last.Split(" - ").Last + If TranslatedName.After(")").Contains(" - ") Then Suffix = TranslatedName.After(")").After(" - ") Dim EnglishName As String = TranslatedName If Suffix <> "" Then EnglishName = EnglishName.Replace(" - " & Suffix, "") EnglishName = EnglishName.Replace(Title, "").Trim("("c, ")"c, " "c) @@ -751,7 +751,7 @@ NoSubtitle: Case CompType.ResourcePack 'FUTURE: Res End Select - Address += "&categoryId=" & If(Tag = "", "0", Tag.Split("/").First) + Address += "&categoryId=" & If(Tag = "", "0", Tag.Before("/")) If ModLoader <> CompModLoaderType.Any Then Address += "&modLoaderType=" & CType(ModLoader, Integer) If Not String.IsNullOrEmpty(GameVersion) Then Address += "&gameVersion=" & GameVersion If Not String.IsNullOrEmpty(SearchText) Then Address += "&searchFilter=" & Net.WebUtility.UrlEncode(SearchText) @@ -772,7 +772,7 @@ NoSubtitle: 'facets=[["categories:'game-mechanics'"],["categories:'forge'"],["versions:1.19.3"],["project_type:mod"]] Dim Facets As New List(Of String) Facets.Add($"[""project_type:{GetStringFromEnum(Type).ToLower}""]") - If Not String.IsNullOrEmpty(Tag) Then Facets.Add($"[""categories:'{Tag.Split("/").Last}'""]") + If Not String.IsNullOrEmpty(Tag) Then Facets.Add($"[""categories:'{Tag.After("/")}'""]") If ModLoader <> CompModLoaderType.Any Then Facets.Add($"[""categories:'{GetStringFromEnum(ModLoader).ToLower}'""]") If Not String.IsNullOrEmpty(GameVersion) Then Facets.Add($"[""versions:'{GameVersion}'""]") Address += "&facets=[" & String.Join(",", Facets) & "]" @@ -880,7 +880,7 @@ NoSubtitle: If Not SearchResults(i).AbsoluteRight AndAlso i >= Math.Min(2, SearchResults.Count - 1) Then Exit For '把 3 个结果拼合以提高准确度 If SearchResults(i).Item.CurseForgeSlug IsNot Nothing Then SearchResult += SearchResults(i).Item.CurseForgeSlug.Replace("-", " ").Replace("/", " ") & " " If SearchResults(i).Item.ModrinthSlug IsNot Nothing Then SearchResult += SearchResults(i).Item.ModrinthSlug.Replace("-", " ").Replace("/", " ") & " " - SearchResult += SearchResults(i).Item.ChineseName.Split(" (").Last.TrimEnd(") ").Split(" - ").First. + SearchResult += SearchResults(i).Item.ChineseName.After(" (").TrimEnd(") ").Before(" - "). Replace(":", "").Replace("(", "").Replace(")", "").ToLower.Replace("/", " ") & " " Next Log("[Comp] 中文搜索原始关键词:" & SearchResult, LogLevel.Developer) @@ -1133,7 +1133,7 @@ Retry: '描述性信息 ''' - ''' 文件描述名(并非文件名,是自定义的字段)。 + ''' 文件描述名(并非文件名,是自定义的字段)。对很多 Mod,这会给出 Mod 版本号。 ''' Public DisplayName As String ''' @@ -1305,7 +1305,7 @@ Retry: 'GameVersions Dim RawVersions As List(Of String) = Data("game_versions").Select(Function(t) t.ToString.Trim.ToLower).ToList GameVersions = RawVersions.Where(Function(v) v.StartsWithF("1.") OrElse v.StartsWithF("b1.")). - Select(Function(v) If(v.Contains("-"), v.Split("-").First & " 快照", If(v.StartsWithF("b1."), "远古版本", v))).ToList + Select(Function(v) If(v.Contains("-"), v.Before("-") & " 快照", If(v.StartsWithF("b1."), "远古版本", v))).ToList If GameVersions.Count > 1 Then GameVersions = Sort(GameVersions, AddressOf VersionSortBoolean).ToList If Type = CompType.ModPack Then GameVersions = New List(Of String) From {GameVersions(0)} @@ -1403,7 +1403,7 @@ Retry: '辅助函数 Public Overrides Function ToString() As String - Return $"{Id}: {DisplayName}" + Return $"{Id}: {FileName}" End Function End Class diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb index 9ab3bb2..ef52547 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb @@ -312,16 +312,30 @@ Extracted: End Function ''' - ''' 输出字符串的前后某些行,并统一行尾为 vbLf (\r)、删除空行。 + ''' 输出字符串的前后某些行,并统一行尾为 vbLf (正则 \n)、删除空行和重复行。 ''' Private Function GetHeadTailLines(Raw As String(), HeadLines As Integer, TailLines As Integer) As String - If Raw.Length <= HeadLines + TailLines Then Return Join(Raw, vbLf) + If Raw.Length <= HeadLines + TailLines Then Return Join(Raw.Distinct, vbLf) + Dim Lines As New List(Of String) + Dim RealHeadLines As Integer = 0, ViewedLines As Integer + For ViewedLines = 0 To Raw.Length - 1 + If Lines.Contains(Raw(ViewedLines)) Then Continue For + RealHeadLines += 1 + Lines.Add(Raw(ViewedLines)) + If RealHeadLines >= HeadLines Then Exit For + Next + Dim RealTailLines = 0 + For i = Raw.Length - 1 To ViewedLines Step -1 + If Lines.Contains(Raw(i)) Then Continue For + RealTailLines += 1 + Lines.Insert(RealHeadLines, Raw(i)) + If RealTailLines >= TailLines Then Exit For + Next Dim Result As New StringBuilder - For i = 0 To Raw.Count - 1 - If i < HeadLines OrElse Raw.Count - i < TailLines Then - If Raw(i) = "" Then Continue For - Result.Append(Raw(i) & vbLf) - End If + For Each Line In Lines + If Line = "" Then Continue For + Result.Append(Line) + Result.Append(vbLf) Next Return Result.ToString End Function @@ -353,6 +367,7 @@ Extracted: 光影或资源包导致OpenGL1282错误 文件或内容校验失败 确定Mod导致游戏崩溃 + 怀疑Mod导致游戏崩溃 Mod配置文件导致游戏崩溃 ModMixin失败 Mod加载器报错 @@ -398,23 +413,21 @@ Extracted: '崩溃日志 If LogCrash IsNot Nothing Then Log("[Crash] 开始进行崩溃日志堆栈分析") - Dim StackLogs As String = LogCrash.Split("System Details").First - Keywords.AddRange(AnalyzeStackKeyword(StackLogs)) + Keywords.AddRange(AnalyzeStackKeyword(LogCrash.Before("System Details"))) End If 'Minecraft 日志 If LogMc IsNot Nothing Then - Dim Fatals = RegexSearch(LogMc, "/FATAL] [\w\W]+?(?=[\n]+\[)") + Dim Fatals As List(Of String) = RegexSearch(LogMc, "/FATAL] .+?(?=[\n]+\[)") + If LogMc.Contains("Unreported exception thrown!") Then Fatals.Add(LogMc.Between("Unreported exception thrown!", "at oolloo.jlw.Wrapper")) Log("[Crash] 开始进行 Minecraft 日志堆栈分析,发现 " & Fatals.Count & " 个报错项") - If Fatals.Any Then - For Each Fatal In Fatals - Keywords.AddRange(AnalyzeStackKeyword(Fatal)) - Next - End If + For Each Fatal In Fatals + Keywords.AddRange(AnalyzeStackKeyword(Fatal)) + Next 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 + Dim StackLogs As String = LogHs.Between("T H R E A D", "Registers:") Keywords.AddRange(AnalyzeStackKeyword(StackLogs)) End If 'Mod 名称分析 @@ -502,7 +515,7 @@ Done: If LogMc.Contains("Extracted mod jars found, loading will NOT continue") Then AppendReason(CrashReason.Mod文件被解压) If LogMc.Contains("java.lang.ClassNotFoundException: org.spongepowered.asm.launch.MixinTweaker") Then AppendReason(CrashReason.MixinBootstrap缺失) If LogMc.Contains("Couldn't set pixel format") Then AppendReason(CrashReason.显卡驱动不支持导致无法设置像素格式) - If LogMc.Contains("java.lang.OutOfMemoryError") Then AppendReason(CrashReason.内存不足) + If LogMc.Contains("java.lang.OutOfMemoryError") OrElse LogMc.Contains("an out of memory error") Then AppendReason(CrashReason.内存不足) If LogMc.Contains("java.lang.RuntimeException: Shaders Mod detected. Please remove it, OptiFine has built-in support for shaders.") Then AppendReason(CrashReason.ShadersMod与Optifine同时安装) If LogMc.Contains("java.lang.NoSuchMethodError: sun.security.util.ManifestEntryVerifier") Then AppendReason(CrashReason.低版本Forge与高版本Java不兼容) If LogMc.Contains("1282: Invalid operation") Then AppendReason(CrashReason.光影或资源包导致OpenGL1282错误) @@ -530,6 +543,7 @@ Done: 'Mod 重复安装 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("Found duplicate mods") Then AppendReason(CrashReason.Mod重复安装, RegexSearch(LogMc, "(?<=Mod ID: ')\w+?(?=' from mod files:)").Distinct.ToList) If LogMc.Contains("ModResolutionException: Duplicate") Then AppendReason(CrashReason.Mod重复安装, RegexSearch(If(RegexSeek(LogMc, "ModResolutionException: Duplicate[^\n]+"), ""), "[^\\/]+.jar", RegularExpressions.RegexOptions.IgnoreCase)) 'Mod 缺少前置 If LogMc.Contains("Missing or unsupported mandatory dependencies:") Then @@ -556,10 +570,10 @@ Done: If LogCrash.Contains("java.lang.OutOfMemoryError") Then AppendReason(CrashReason.内存不足) If LogCrash.Contains("Pixel format not accelerated") Then AppendReason(CrashReason.显卡驱动不支持导致无法设置像素格式) If LogCrash.Contains("Manually triggered debug crash") Then AppendReason(CrashReason.玩家手动触发调试崩溃) + If LogCrash.Contains("has mods that were not found") AndAlso RegexCheck(LogCrash, "The Mod File [^\n]+optifine\\OptiFine[^\n]+ has mods that were not found") Then AppendReason(CrashReason.OptiFine与Forge不兼容) 'Mod 导致的崩溃 If LogCrash.Contains("-- MOD ") Then - Dim LogLeft = LogCrash.Split("-- MOD").Last - If LogLeft.Contains("Failure message: MISSING") Then + If LogCrash.Between("-- MOD ", "Failure message:").ContainsF(".jar", True) Then AppendReason(CrashReason.确定Mod导致游戏崩溃, If(RegexSeek(LogCrash, "(?<=Mod File: ).+"), "").TrimEnd((vbCrLf & " ").ToCharArray)) Else AppendReason(CrashReason.Mod加载器报错, If(RegexSeek(LogCrash, "(?<=Failure message: )[\w\W]+?(?=\tMod)"), "").Replace(vbTab, " ").TrimEnd((vbCrLf & " ").ToCharArray)) @@ -584,19 +598,9 @@ Done: 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("A potential solution has been determined, this may resolve your problem:") Then AppendReason(CrashReason.Fabric报错并给出解决方案, Join(RegexSearch(If(RegexSeek(LogMc, "(?<=A potential solution has been determined, this may resolve your problem:\n)((\t)+ - [^\n]+\n)+"), ""), "(?<=(\t)+)[^\n]+"), vbLf)) If LogMc.Contains("确定了一种可能的解决方法,这样做可能会解决你的问题:") Then AppendReason(CrashReason.Fabric报错并给出解决方案, Join(RegexSearch(If(RegexSeek(LogMc, "(?<=确定了一种可能的解决方法,这样做可能会解决你的问题:\n)((\t)+ - [^\n]+\n)+"), ""), "(?<=(\t)+)[^\n]+"), vbLf)) - End If - - End Sub - ''' - ''' 进行精准日志匹配。匹配优先级低于堆栈分析的崩溃。 - ''' - Private Sub AnalyzeCrit3() - - '游戏日志分析 - 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 + LogMc.Contains("mixin.injection.throwables.") OrElse LogMc.Contains(".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)") @@ -614,6 +618,24 @@ Done: '在 #3104 的情况下,这一句导致 OptiFabric 的 Mixin 失败错判为 Fabric Loader 加载失败 If LogMc.Contains("due to errors, provided by ") Then AppendReason(CrashReason.确定Mod导致游戏崩溃, TryAnalyzeModName(If(RegexSeek(LogMc, "(?<=due to errors, provided by ')[^']+"), "").TrimEnd((vbCrLf & " ").ToCharArray))) End If + End If + + '崩溃报告分析 + If LogCrash IsNot Nothing Then + If LogCrash.Contains("Suspected Mod") Then + Dim Suspects = RegexSearch(LogCrash.Between("Suspected Mod", "Stacktrace"), "(?<=\n\t[^(\t]+\()[^)\n]+") + If Suspects.Any Then AppendReason(CrashReason.怀疑Mod导致游戏崩溃, TryAnalyzeModName(Suspects)) + End If + End If + + End Sub + ''' + ''' 进行精准日志匹配。匹配优先级低于堆栈分析的崩溃。 + ''' + Private Sub AnalyzeCrit3() + + '游戏日志分析 + If LogMc IsNot Nothing Then '极短的程序输出 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) @@ -634,9 +656,13 @@ Done: ''' 从堆栈中提取 Mod ID 关键字。若失败则返回空列表。 ''' Private Function AnalyzeStackKeyword(ErrorStack As String) As List(Of String) + ErrorStack = vbLf & If(ErrorStack, "") & vbLf '进行正则匹配 - Dim StackSearchResults As List(Of String) = RegexSearch(If(ErrorStack, "") & vbCrLf, "(?<=\n[^{]+)[a-zA-Z]+\w+\.[a-zA-Z]+[\w\.]+(?=\.[\w\.$]+\.)") + Dim StackSearchResults As New List(Of String) + StackSearchResults.AddRange(RegexSearch(ErrorStack, "(?<=\n[^{]+)[a-zA-Z_]+\w+\.[a-zA-Z_]+[\w\.]+(?=\.[\w\.$]+\.)")) + StackSearchResults.AddRange(RegexSearch(ErrorStack, "(?<=at [^(]+?\.\w+\$\w+\$)[\w\$]+?(?=\$\w+\()").Select(Function(s) s.Replace("$", "."))) 'Mixin 堆栈:xxx.xxx.xxxx$xxxx$xxx + StackSearchResults = StackSearchResults.Distinct.ToList '检查堆栈开头 Dim PossibleStacks As New List(Of String) @@ -649,7 +675,7 @@ Done: "MojangTricksIntelDriversForPerformance_javaw"} If Stack.StartsWithF(IgnoreStack) Then GoTo NextStack Next - PossibleStacks.Add(Stack.Trim) '.Split("$").First) + PossibleStacks.Add(Stack.Trim) NextStack: Next PossibleStacks = PossibleStacks.Distinct.ToList @@ -666,13 +692,13 @@ NextStack: For i = 0 To Math.Min(3, Splited.Count - 1) '最多取前 4 节 Dim Word As String = Splited(i) If Word.Length <= 2 OrElse Word.StartsWithF("func_") Then Continue For - 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", "oshi", - "forge", "setup", "block", "model", "mixin", "event", "unimi", "netty", - "gitlab", "common", "server", "config", "loader", "launch", "entity", "assist", "client", "plugin", "modapi", "mojang", "shader", "events", "github", "recipe", - "preinit", "preload", "machine", "reflect", "channel", "general", "handler", "content", "systems", "modules", - "fastutil", "optifine", "internal", "platform", "override", - "listeners", "scheduler", "minecraft", "transformers", "universal", "multipart", "minecraftforge", "blockentity" + If {"com", "org", "net", "asm", "fml", "mod", "jar", "sun", "lib", "map", "gui", "dev", "nio", "api", "dsi", "top", "mcp", + "core", "init", "mods", "main", "file", "game", "load", "read", "done", "util", "tile", "item", "base", "oshi", "impl", "data", "pool", "task", + "forge", "setup", "block", "model", "mixin", "event", "unimi", "netty", "world", + "gitlab", "common", "server", "config", "loader", "launch", "entity", "assist", "client", "plugin", "modapi", "mojang", "shader", "events", "github", "recipe", "render", "packet", "events", + "preinit", "preload", "machine", "reflect", "channel", "general", "handler", "content", "systems", "modules", "service", + "fastutil", "optifine", "internal", "platform", "override", "fabricmc", + "injection", "listeners", "scheduler", "minecraft", "transformer", "transformers", "universal", "multipart", "minecraftforge", "blockentity", "spongepowered", "electronwill" }.Contains(Word.ToLower) Then Continue For PossibleWords.Add(Word.Trim) Next @@ -712,7 +738,7 @@ NextStack: Details = Details.Replace("Fabric Mods", "¨") Log("[Crash] 崩溃报告中检测到 Fabric Mod 信息格式") End If - Details = Details.Split("¨").Last + Details = Details.After("¨") '[Forge] 获取所有包含 .jar 的行 '[Fabric] 获取所有包含 Mod 信息的行 @@ -795,13 +821,20 @@ NextStack: End If End Function ''' - ''' 尝试获取 Mod 名称,若失败则返回原关键字。 + ''' 尝试从关键字获取 Mod 名称,若失败则返回原关键字。 ''' - Private Function TryAnalyzeModName(Keywords As String) As List(Of String) - Dim RawList As New List(Of String) From {If(Keywords, "")} - If String.IsNullOrEmpty(Keywords) Then Return RawList + Private Function TryAnalyzeModName(Keyword As String) As List(Of String) + Dim RawList As New List(Of String) From {If(Keyword, "")} + If String.IsNullOrEmpty(Keyword) Then Return RawList Return If(AnalyzeModName(RawList), RawList) End Function + ''' + ''' 尝试从关键字获取 Mod 名称,若失败则返回原关键字。 + ''' + Private Function TryAnalyzeModName(Keywords As List(Of String)) As List(Of String) + If Not Keywords.Any Then Return Keywords + Return If(AnalyzeModName(Keywords), Keywords) + End Function '4:根据原因输出信息 Private OutputFiles As New List(Of String) @@ -814,7 +847,7 @@ NextStack: Dim ShowLog As Action = Sub() If File.Exists(DirectFile.Value.Key) Then - ShellOnly(DirectFile.Value.Key) + ShellOnly("notepad", DirectFile.Value.Key) Else Dim FilePath As String = PathTemp & "Crash.txt" WriteFile(FilePath, Join(DirectFile.Value.Value, vbCrLf)) @@ -911,39 +944,39 @@ NextStack: End If Case CrashReason.堆栈分析发现关键字 If Additional.Count = 1 Then - Results.Add("你的游戏遇到了一些问题,这可能是某些 Mod 所引起的,PCL 找到了一个可疑的关键词:" & Additional.First & "。\n\n如果你知道它对应的 Mod,那么有可能就是它引起的错误,你也可以查看错误报告获取详情。\h") + Results.Add("你的游戏遇到了一些问题,PCL 为此找到了一个可疑的关键词:" & Additional.First & "。\n\n如果你知道某个关键词对应的 Mod,那么有可能就是它引起的错误,你也可以查看错误报告获取详情。\h") Else - Results.Add("你的游戏遇到了一些问题,这可能是某些 Mod 所引起的,PCL 找到了以下可疑的关键词:\n - " & Join(Additional, ", ") & "\n\n如果你知道这些关键词对应的 Mod,那么有可能就是它引起的错误,你也可以查看错误报告获取详情。\h") + Results.Add("你的游戏遇到了一些问题,PCL 为此找到了以下可疑的关键词:\n - " & Join(Additional, ", ") & "\n\n如果你知道某个关键词对应的 Mod,那么有可能就是它引起的错误,你也可以查看错误报告获取详情。\h") End If - Case CrashReason.堆栈分析发现Mod名称 + Case CrashReason.堆栈分析发现Mod名称, CrashReason.怀疑Mod导致游戏崩溃 If Additional.Count = 1 Then - Results.Add("名为 " & Additional.First & " 的 Mod 可能导致了游戏出错。\n\e\h") + Results.Add("PCL 怀疑名为 " & Additional.First & " 的 Mod 导致了游戏出错,但不能完全确定。\n你可以尝试禁用此 Mod,然后观察游戏是否还会崩溃。\n\e\h") Else - Results.Add("可能是以下 Mod 导致了游戏出错:\n - " & Join(Additional, "\n - ") & "\n\e\h") + Results.Add("PCL 怀疑以下 Mod 导致了游戏出错,但不能完全确定:\n - " & Join(Additional, "\n - ") & "\n\n你可以尝试依次禁用上述 Mod,然后观察游戏是否还会崩溃。\n\e\h") End If Case CrashReason.确定Mod导致游戏崩溃 If Additional.Count = 1 Then - Results.Add("名称或 ID 为 " & Additional.First & " 的 Mod 导致了游戏出错。\n\e\h") + Results.Add("名为 " & Additional.First & " 的 Mod 导致了游戏出错。\n你可以尝试禁用此 Mod,然后观察游戏是否还会崩溃。\n\e\h") Else - Results.Add("以下 Mod 导致了游戏出错:\n - " & Join(Additional, "\n - ") & "\n\e\h") + Results.Add("以下 Mod 导致了游戏出错:\n - " & Join(Additional, "\n - ") & "\n\n你可以尝试依次禁用上述 Mod,然后观察游戏是否还会崩溃。\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") + Results.Add("名为 " & Additional.First & " 的 Mod 注入失败,导致游戏出错。\n这一般代表着它与其他 Mod 或当前环境不兼容,或是它存在 Bug。\n你可以尝试禁用此 Mod,然后观察游戏是否还会崩溃。\n\e\h") Else - Results.Add("以下 Mod 导致了游戏出错:\n - " & Join(Additional, "\n - ") & "\n这一般代表着这些 Mod 存在 Bug,或与当前环境不兼容。\n\e\h") + Results.Add("以下 Mod 导致了游戏出错:\n - " & Join(Additional, "\n - ") & "\n这一般代表着它们与其他 Mod 或当前环境不兼容,或是它存在 Bug。\n你可以尝试依次禁用上述 Mod,然后观察游戏是否还会崩溃。\n\e\h") End If Case CrashReason.Mod配置文件导致游戏崩溃 If Additional(1) Is Nothing Then - Results.Add("名称或 ID 为 " & Additional.First & " 的 Mod 导致了游戏出错。\n\e\h") + Results.Add("名为 " & Additional.First & " 的 Mod 导致了游戏出错。\n\e\h") Else - Results.Add("名称或 ID 为 " & Additional.First & " 的 Mod 导致了游戏出错:\n其配置文件 " & Additional(1) & " 存在异常,无法读取。") + Results.Add("名为 " & Additional.First & " 的 Mod 导致了游戏出错:\n其配置文件 " & Additional(1) & " 存在异常,无法读取。") End If Case CrashReason.Mod初始化失败 If Additional.Count = 1 Then - Results.Add("名为 " & Additional.First & " 的 Mod 初始化失败,导致游戏无法继续加载。\n\e\h") + Results.Add("名为 " & Additional.First & " 的 Mod 初始化失败,导致游戏无法继续加载。\n你可以尝试禁用此 Mod,然后观察游戏是否还会崩溃。\n\e\h") Else - Results.Add("以下 Mod 初始化失败,导致游戏无法继续加载:\n - " & Join(Additional, "\n - ") & "\n\e\h") + Results.Add("以下 Mod 初始化失败,导致游戏无法继续加载:\n - " & Join(Additional, "\n - ") & "\n\n你可以尝试依次禁用上述 Mod,然后观察游戏是否还会崩溃。\n\e\h") End If Case CrashReason.特定方块导致崩溃 If Additional.Count = 1 Then @@ -1028,8 +1061,6 @@ NextStack: End Select Next - '整理多个原因 - IsHandAnalyze = False Return Join(Results, "\n\n此外,"). Replace("\n", vbCrLf). Replace("\h", ""). @@ -1039,7 +1070,7 @@ NextStack: If(Not Results.Any(Function(r) r.EndsWithF("\h")) OrElse IsHandAnalyze, "", vbCrLf & "如果要寻求帮助,请向他人发送错误报告文件,而不是发送这个窗口的截图。" & If(If(PageSetupSystem.IsLauncherNewest(), True), "", - vbCrLf & vbCrLf & "此外,你正在使用老版本 PCL,更新到最新版可能会修复这个问题。" & vbCrLf & "你可以点击 设置 → 启动器 → 检查更新 来更新 PCL。")) + vbCrLf & vbCrLf & "此外,你正在使用老版本 PCL,更新 PCL 或许也能解决这个问题。" & vbCrLf & "你可以点击 设置 → 启动器 → 检查更新 来更新 PCL。")) End Function End Class diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb index e220fca..7767f6b 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb @@ -1,5 +1,5 @@ Public Module ModJava - Public JavaListCacheVersion As Integer = 5 + Public JavaListCacheVersion As Integer = 6 ''' ''' 目前所有可用的 Java。 @@ -131,7 +131,9 @@ Version = New Version(1, Version.Major, Version.Build, Version.Revision) End If Is64Bit = Output.Contains("64-bit") - If Version.Minor <= 4 OrElse Version.Minor >= 25 Then Throw New Exception("分析详细信息失败,获取的版本为 " & Version.ToString) + If Version.Minor <= 4 OrElse Version.Minor >= 25 Then Throw New ApplicationException("分析详细信息失败,获取的版本为 " & Version.ToString) + '基于 #3649,在 64 位系统上禁用 32 位 Java + If Not Is64Bit AndAlso Not Is32BitSystem Then Throw New Exception("该 Java 为 32 位版本,请安装 64 位的 Java") '基于 #2249 发现 JRE 17 似乎也导致了 Forge 安装失败,干脆禁用更多版本的 JRE If IsJre AndAlso VersionCode >= 16 Then Throw New Exception("由于高版本 JRE 对游戏的兼容性很差,因此不再允许使用。你可以使用对应版本的 JDK,而非 JRE!") Catch ex As ApplicationException @@ -197,10 +199,15 @@ Setup.Set("LaunchArgumentJavaAll", "[]") End Try End Sub + + ''' + ''' 防止多个需要 Java 的部分同时要求下载 Java(#3797)。 + ''' + Public JavaLock As New Object ''' ''' 根据要求返回最适合的 Java,若找不到则返回 Nothing。 ''' 最小与最大版本在与输入相同时也会通过。 - ''' 必须在工作线程调用。 + ''' 必须在工作线程调用,且必须包括 SyncLock JavaLock。 ''' Public Function JavaSelect(CancelException As String, Optional MinVersion As Version = Nothing, Optional MaxVersion As Version = Nothing, Optional RelatedVersion As McVersion = Nothing) As JavaEntry @@ -460,8 +467,8 @@ NoUserJava: If Not Left.IsJre AndAlso Right.IsJre Then Return False '4. Java 大版本 If Left.VersionCode <> Right.VersionCode Then - ' Java 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21... - Dim Weight = {0, 1, 2, 3, 4, 5, 6, 14, 30, 10, 12, 15, 13, 9, 8, 7, 11, 31, 29, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28} + ' Java 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23... + Dim Weight = {0, 1, 2, 3, 4, 5, 6, 14, 30, 10, 12, 15, 13, 9, 8, 7, 11, 31, 29, 16, 17, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18} Return Weight.ElementAtOrDefault(Left.VersionCode) >= Weight.ElementAtOrDefault(Right.VersionCode) End If '5. 最次级版本号更接近 51 @@ -601,22 +608,23 @@ NoUserJava: '启动检查线程 Dim CheckThreads As New List(Of Thread) For Each Entry In JavaEntries - Dim CheckThread As New Thread(Sub() - Try - Entry.Check() - If ModeDebug Then Log("[Java] - " & Entry.ToString) - SyncLock ListLock - JavaCheckList.Add(Entry) - End SyncLock - Catch ex As ThreadInterruptedException - Catch ex As Exception - If Entry.IsUserImport Then - Log(ex, "位于 " & Entry.PathFolder & " 的 Java 存在异常,将被自动移除", LogLevel.Hint) - Else - Log(ex, "位于 " & Entry.PathFolder & " 的 Java 存在异常") - End If - End Try - End Sub) + Dim CheckThread As New Thread( + Sub() + Try + Entry.Check() + If ModeDebug Then Log("[Java] - " & Entry.ToString) + SyncLock ListLock + JavaCheckList.Add(Entry) + End SyncLock + Catch ex As ThreadInterruptedException + Catch ex As Exception + If Entry.IsUserImport Then + Log(ex, "位于 " & Entry.PathFolder & " 的 Java 存在异常,将被自动移除", LogLevel.Hint) + Else + Log(ex, "位于 " & Entry.PathFolder & " 的 Java 存在异常") + End If + End Try + End Sub) CheckThreads.Add(CheckThread) CheckThread.Start() Next diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb index 5cc6182..48a8228 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb @@ -358,7 +358,7 @@ NextInner: Case McLoginType.Ms If Setup.Get("CacheMsName") <> "" Then Return Setup.Get("CacheMsName") Case McLoginType.Legacy - If Setup.Get("LoginLegacyName") <> "" Then Return Setup.Get("LoginLegacyName").ToString.Split("¨")(0) + If Setup.Get("LoginLegacyName") <> "" Then Return Setup.Get("LoginLegacyName").ToString.Before("¨") Case McLoginType.Nide If Setup.Get("CacheNideName") <> "" Then Return Setup.Get("CacheNideName") Case McLoginType.Auth @@ -368,7 +368,7 @@ NextInner: If Setup.Get("CacheMsName") <> "" Then Return Setup.Get("CacheMsName") If Setup.Get("CacheNideName") <> "" Then Return Setup.Get("CacheNideName") If Setup.Get("CacheAuthName") <> "" Then Return Setup.Get("CacheAuthName") - If Setup.Get("LoginLegacyName") <> "" Then Return Setup.Get("LoginLegacyName").ToString.Split("¨")(0) + If Setup.Get("LoginLegacyName") <> "" Then Return Setup.Get("LoginLegacyName").ToString.Before("¨") Return Nothing End Function ''' @@ -524,7 +524,10 @@ Relogin: Dim AccessToken As String = MsLoginStep5(Tokens) Data.Progress = 0.8 If Data.IsAborted Then Throw New ThreadInterruptedException - Dim Result = MsLoginStep6(AccessToken) + MsLoginStep6(AccessToken) + Data.Progress = 0.9 + If Data.IsAborted Then Throw New ThreadInterruptedException + Dim Result = MsLoginStep7(AccessToken) '输出登录结果 Setup.Set("CacheMsOAuthRefresh", OAuthRefreshToken) Setup.Set("CacheMsAccess", AccessToken) @@ -540,7 +543,7 @@ Relogin: SkipLogin: McLaunchLog("微软登录完成") Setup.Set("HintBuy", True) '关闭正版购买提示 - Data.Progress = 0.95 + Data.Progress = 0.98 If ThemeUnlock(10, False) Then MyMsgBox("感谢你对正版游戏的支持!" & vbCrLf & "隐藏主题 跳票红 已解锁!", "提示") End Sub Private Sub McLoginServerStart(Data As LoaderTask(Of McLoginServer, McLoginResult)) @@ -797,14 +800,14 @@ LoginFinish: '微软登录步骤 1:打开网页认证,获取 OAuth Code Private Function MsLoginStep1(Data As LoaderTask(Of McLoginMs, McLoginResult)) As String McLaunchLog("开始微软登录步骤 1") - If OsVersion <= New Version(10, 0, 17763, 0) Then + If True OrElse OsVersion <= New Version(10, 0, 17763, 0) Then '由于 #3849,WebBrowser 已经无法正常加载微软登录网页了 SystemBrowser: 'Windows 7 或老版 Windows 10 登录 OpenWebsite(FormLoginOAuth.LoginUrl1) Dim Result As String = MyMsgBoxInput("等待网页登录", - "在登录成功后,网页会变成一片空白,请将该空白页面的网址复制到下面的框中。" & vbCrLf & - "如果网络环境不佳,登录网页可能一直加载不出来,此时请尝试使用 VPN 或加速器,然后再试。", + "登录完成后,网页会变得完全空白,把那个空白网页的网址复制到下面的框中就行了!" & vbCrLf & + "如果网络环境不佳,它可能一直加载不出来,那就只能试试用 VPN 或加速器了。", ValidateRules:=New ObjectModel.Collection(Of Validate) From {New ValidateRegex("(?<=code\=)[^&]+", "返回网址应以 https://login.live.com/oauth20_desktop.srf?code= 开头")}, HintText:="https://login.live.com/oauth20_desktop.srf?code=XXXXXX") If Result Is Nothing Then @@ -813,7 +816,7 @@ SystemBrowser: Else Return RegexSeek(Result, "(?<=code\=)[^&]+") End If - Hint("网页登录已完成,你现在可以关闭浏览器上的那个网页了", HintType.Finish) + Hint("网页登录成功,你可以关闭浏览器啦!", HintType.Finish) Else 'Windows 10 登录 Dim ReturnCode As String = Nothing @@ -926,12 +929,18 @@ SystemBrowser: Try Result = NetRequestMulty("https://xsts.auth.xboxlive.com/xsts/authorize", "POST", Request, "application/json", 3) Catch ex As Net.WebException - If ex.Message.Contains("2148916233") Then - If MyMsgBox("该微软账号尚未购买 Minecraft Java 版,或尚未注册 Xbox 账户。" & vbCrLf & - "如果你确定该账号完成了上述步骤,请先在游戏官网登录一次,然后再在启动器登录。", "登录提示", "打开游戏官网", "取消") = 1 Then - OpenWebsite("https://www.minecraft.net/") + '参考 https://github.com/PrismarineJS/prismarine-auth/blob/master/src/common/Constants.js + If ex.Message.Contains("2148916227") Then + MyMsgBox("该账号似乎已被微软封禁,无法登录。", "登录失败", "我知道了", IsWarn:=True) + Throw New Exception("$$") + ElseIf ex.Message.Contains("2148916233") Then + If MyMsgBox("你尚未注册 Xbox 账户,请在注册后再登录。", "登录提示", "注册", "取消") = 1 Then + OpenWebsite("https://signup.live.com/signup") End If Throw New Exception("$$") + ElseIf ex.Message.Contains("2148916235") Then + MyMsgBox($"你的网络所在的国家或地区无法登录微软账号。{vbCrLf}请尝试使用加速器或 VPN。", "登录失败", "我知道了") + Throw New Exception("$$") ElseIf ex.Message.Contains("2148916238") Then If MyMsgBox("该账号年龄不足,你需要先修改出生日期,然后才能登录。" & vbCrLf & "该账号目前填写的年龄是否在 13 岁以上?", "登录提示", "13 岁以上", "12 岁以下", "我不知道") = 1 Then @@ -979,30 +988,46 @@ SystemBrowser: Dim AccessToken As String = ResultJson("access_token").ToString Return AccessToken End Function - '微软登录步骤 6:从 Minecraft AccessToken 获取 {UUID, UserName, ProfileJson} - Private Function MsLoginStep6(AccessToken As String) As String() + '微软登录步骤 6:验证微软账号是否持有 MC,这也会刷新 XGP + Private Sub MsLoginStep6(AccessToken As String) McLaunchLog("开始微软登录步骤 6") + Dim Result As String = NetRequestMulty("https://api.minecraftservices.com/entitlements/mcstore", "GET", "", "application/json", 2, New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}}) + Try + Dim ResultJson As JObject = GetJson(Result) + If Not (ResultJson.ContainsKey("items") AndAlso ResultJson("items").Any) Then + Select Case MyMsgBox("你尚未购买正版 Minecraft,或者 Xbox Game Pass 已到期。", "登录失败", "购买 Minecraft", "取消") + Case 1 + OpenWebsite("https://www.xbox.com/zh-cn/games/store/minecraft-java-bedrock-edition-for-pc/9nxp44l49shj") + End Select + Throw New Exception("$$") + End If + Catch ex As Exception + Log(ex, "微软登录第 6 步异常:" & Result) + Throw + End Try + End Sub + '微软登录步骤 7:从 Minecraft AccessToken 获取 {UUID, UserName, ProfileJson} + Private Function MsLoginStep7(AccessToken As String) As String() + McLaunchLog("开始微软登录步骤 7") + Dim Result As String Try - NetRequestMulty("https://api.minecraftservices.com/entitlements/mcstore", "GET", "", "application/json", 2, New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}}) Result = NetRequestMulty("https://api.minecraftservices.com/minecraft/profile", "GET", "", "application/json", 2, New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}}) Catch ex As Net.WebException Dim Message As String = GetExceptionSummary(ex) If Message.Contains("(429)") Then - Log(ex, "微软登录第 6 步汇报 429") + Log(ex, "微软登录第 7 步汇报 429") Throw New Exception("$登录尝试太过频繁,请等待几分钟后再试!") ElseIf Message.Contains("(404)") Then - Log(ex, "微软登录第 6 步汇报 404") - RunInNewThread(Sub() - Select Case MyMsgBox("你可能没有创建 Minecraft 玩家档案、Xbox Game Pass 已到期,或者还没有购买 Minecraft。" & vbCrLf & - "如果您确认您已购买 Minecraft 或您的 Xbox Game Pass 未过期,请创建档案,之后即可使用 PCL 登录。", "登录失败", "创建档案", "购买 Minecraft", "取消") - Case 1 - OpenWebsite("https://www.minecraft.net/zh-hans/msaprofile/mygames/editprofile") - Case 2 - OpenWebsite("https://www.xbox.com/zh-cn/games/store/minecraft-java-bedrock-edition-for-pc/9nxp44l49shj") - End Select - End Sub, "Login Failed: Create Profile") + Log(ex, "微软登录第 7 步汇报 404") + RunInNewThread( + Sub() + Select Case MyMsgBox("请先创建 Minecraft 玩家档案,然后再重新登录。", "登录失败", "创建档案", "取消") + Case 1 + OpenWebsite("https://www.minecraft.net/zh-hans/msaprofile/mygames/editprofile") + End Select + End Sub, "Login Failed: Create Profile") Throw New Exception("$$") Else Throw @@ -1169,65 +1194,68 @@ SystemBrowser: MinVer = If(New Version(1, 8, 0, 101) > MinVer, New Version(1, 8, 0, 101), MinVer) End If - '选择 Java - McLaunchLog("Java 版本需求:最低 " & MinVer.ToString & ",最高 " & MaxVer.ToString) - McLaunchJavaSelected = JavaSelect("$$", MinVer, MaxVer, McVersionCurrent) - If Task.IsAborted Then Exit Sub - If McLaunchJavaSelected IsNot Nothing Then - McLaunchLog("选择的 Java:" & McLaunchJavaSelected.ToString) - Exit Sub - End If + SyncLock JavaLock - '无合适的 Java - If Task.IsAborted Then Exit Sub '中断加载会导致 JavaSelect 异常地返回空值,误判找不到 Java - McLaunchLog("无合适的 Java,需要确认是否自动下载") - Dim JavaCode As String - If MinVer >= New Version(1, 21, 0, 0) Then - JavaCode = 21 - If Not JavaDownloadConfirm("Java 21") Then Throw New Exception("$$") - ElseIf MinVer >= New Version(1, 9, 0, 0) Then - JavaCode = 17 - If Not JavaDownloadConfirm("Java 17") Then Throw New Exception("$$") - ElseIf MaxVer < New Version(1, 8, 0, 0) Then - JavaCode = 7 - If Not JavaDownloadConfirm("Java 7") Then Throw New Exception("$$") - ElseIf MinVer > New Version(1, 8, 0, 100) AndAlso MaxVer < New Version(1, 8, 0, 321) Then - JavaCode = "8u101" - If Not JavaDownloadConfirm("Java 8.0.101 ~ 8.0.320", True) Then Throw New Exception("$$") - ElseIf MinVer > New Version(1, 8, 0, 100) Then - JavaCode = "8u101" - If Not JavaDownloadConfirm("Java 8.0.101 或更高版本的 Java 8", True) Then Throw New Exception("$$") - ElseIf MaxVer < New Version(1, 8, 0, 321) Then - JavaCode = 8 - If Not JavaDownloadConfirm("Java 8.0.320 或更低版本的 Java 8") Then Throw New Exception("$$") - Else - JavaCode = 8 - If Not JavaDownloadConfirm("Java 8") Then Throw New Exception("$$") - End If + '选择 Java + McLaunchLog("Java 版本需求:最低 " & MinVer.ToString & ",最高 " & MaxVer.ToString) + McLaunchJavaSelected = JavaSelect("$$", MinVer, MaxVer, McVersionCurrent) + If Task.IsAborted Then Exit Sub + If McLaunchJavaSelected IsNot Nothing Then + McLaunchLog("选择的 Java:" & McLaunchJavaSelected.ToString) + Exit Sub + End If - '开始自动下载 - Dim JavaLoader = JavaFixLoaders(JavaCode) - Try - JavaLoader.Start(JavaCode, IsForceRestart:=True) - Do While JavaLoader.State = LoadState.Loading AndAlso Not Task.IsAborted - Task.Progress = JavaLoader.Progress - Thread.Sleep(10) - Loop - Finally - JavaLoader.Abort() '确保取消时中止 Java 下载 - End Try + '无合适的 Java + If Task.IsAborted Then Exit Sub '中断加载会导致 JavaSelect 异常地返回空值,误判找不到 Java + McLaunchLog("无合适的 Java,需要确认是否自动下载") + Dim JavaCode As String + If MinVer >= New Version(1, 21, 0, 0) Then + JavaCode = 21 + If Not JavaDownloadConfirm("Java 21") Then Throw New Exception("$$") + ElseIf MinVer >= New Version(1, 9, 0, 0) Then + JavaCode = 17 + If Not JavaDownloadConfirm("Java 17") Then Throw New Exception("$$") + ElseIf MaxVer < New Version(1, 8, 0, 0) Then + JavaCode = 7 + If Not JavaDownloadConfirm("Java 7", True) Then Throw New Exception("$$") + ElseIf MinVer > New Version(1, 8, 0, 100) AndAlso MaxVer < New Version(1, 8, 0, 321) Then + JavaCode = "8u101" + If Not JavaDownloadConfirm("Java 8.0.101 ~ 8.0.320", True) Then Throw New Exception("$$") + ElseIf MinVer > New Version(1, 8, 0, 100) Then + JavaCode = "8u101" + If Not JavaDownloadConfirm("Java 8.0.101 或更高版本的 Java 8", True) Then Throw New Exception("$$") + ElseIf MaxVer < New Version(1, 8, 0, 321) Then + JavaCode = 8 + If Not JavaDownloadConfirm("Java 8.0.320 或更低版本的 Java 8") Then Throw New Exception("$$") + Else + JavaCode = 8 + If Not JavaDownloadConfirm("Java 8") Then Throw New Exception("$$") + End If - '检查下载结果 - If JavaSearchLoader.State <> LoadState.Loading Then JavaSearchLoader.State = LoadState.Waiting '2872# - McLaunchJavaSelected = JavaSelect("$$", MinVer, MaxVer, McVersionCurrent) - If Task.IsAborted Then Exit Sub - If McLaunchJavaSelected IsNot Nothing Then - McLaunchLog("选择的 Java:" & McLaunchJavaSelected.ToString) - Else - Hint("没有可用的 Java,已取消启动!", HintType.Critical) - Throw New Exception("$$") - End If + '开始自动下载 + Dim JavaLoader = JavaFixLoaders(JavaCode) + Try + JavaLoader.Start(JavaCode, IsForceRestart:=True) + Do While JavaLoader.State = LoadState.Loading AndAlso Not Task.IsAborted + Task.Progress = JavaLoader.Progress + Thread.Sleep(10) + Loop + Finally + JavaLoader.Abort() '确保取消时中止 Java 下载 + End Try + '检查下载结果 + If JavaSearchLoader.State <> LoadState.Loading Then JavaSearchLoader.State = LoadState.Waiting '2872# + McLaunchJavaSelected = JavaSelect("$$", MinVer, MaxVer, McVersionCurrent) + If Task.IsAborted Then Exit Sub + If McLaunchJavaSelected IsNot Nothing Then + McLaunchLog("选择的 Java:" & McLaunchJavaSelected.ToString) + Else + Hint("没有可用的 Java,已取消启动!", HintType.Critical) + Throw New Exception("$$") + End If + + End SyncLock End Sub #End Region @@ -1342,7 +1370,7 @@ SystemBrowser: If Not ArgumentJvm.Contains("-Dlog4j2.formatMsgNoLookups=true") Then ArgumentJvm += " -Dlog4j2.formatMsgNoLookups=true" ArgumentJvm = ArgumentJvm.Replace(" -XX:MaxDirectMemorySize=256M", "") '#3511 的清理 DataList.Insert(0, ArgumentJvm) '可变 JVM 参数 - DataList.Add("-Xmn" & Math.Floor(PageVersionSetup.GetRam(McVersionCurrent, Not McLaunchJavaSelected.Is64Bit) * 1024 * 0.5) & "m") + DataList.Add("-Xmn" & Math.Floor(PageVersionSetup.GetRam(McVersionCurrent, Not McLaunchJavaSelected.Is64Bit) * 1024 * 0.15) & "m") DataList.Add("-Xmx" & Math.Floor(PageVersionSetup.GetRam(McVersionCurrent, Not McLaunchJavaSelected.Is64Bit) * 1024) & "m") DataList.Add("""-Djava.library.path=" & GetNativesFolder() & """") DataList.Add("-cp ${classpath}") '把支持库添加进启动参数表 @@ -1610,7 +1638,8 @@ NextVersion: GameSize = New Size(875 - 2, 540 - 2) End Select GameSize.Height -= 29.5 * DPI / 96 '标题栏高度 - If McVersionCurrent.Version.McCodeMain <= 12 AndAlso McLaunchJavaSelected.VersionCode <= 8 Then '修复 #3463:1.12.2-,JRE 8 下窗口大小为设置大小的 DPI% 倍 + If McVersionCurrent.Version.McCodeMain <= 12 AndAlso McLaunchJavaSelected.VersionCode <= 8 AndAlso + Not McVersionCurrent.Version.HasOptiFine AndAlso Not McVersionCurrent.Version.HasForge Then '修复 #3463:1.12.2-,JRE 8 下窗口大小为设置大小的 DPI% 倍 GameSize.Width /= DPI / 96 GameSize.Height /= DPI / 96 End If @@ -1809,13 +1838,14 @@ NextVersion: '1.11 ~ 12:zh_cn 时正常,zh_CN 时虽然显示了中文但语言设置会错误地显示选择英文 '1.13+ :zh_cn 时正常,zh_CN 时自动切换为英文 Dim CurrentLang As String = ReadIni(SetupFileAddress, "lang", "none") - Dim RequiredLang As String = If(CurrentLang = "none", If(Setup.Get("ToolHelpChinese"), "zh_cn", "en_us"), CurrentLang.ToLower) + Dim RequiredLang As String = If(CurrentLang = "none" OrElse Not Directory.Exists(McVersionCurrent.PathIndie & "saves"), '#3844,整合包可能已经自带了 options.txt + If(Setup.Get("ToolHelpChinese"), "zh_cn", "en_us"), CurrentLang.ToLower) If McVersionCurrent.Version.McCodeMain < 12 Then '注意老版本(包含 MC 1.1)的 McCodeMain 可能为 -1 '将最后两位改为大写,前面的部分保留 RequiredLang = RequiredLang.Substring(0, RequiredLang.Length - 2) & RequiredLang.Substring(RequiredLang.Length - 2).ToUpper End If If CurrentLang = RequiredLang Then - McLaunchLog($"当前语言为 {CurrentLang},无需修改") + McLaunchLog($"需要的语言为 {RequiredLang},当前语言为 {CurrentLang},无需修改") Else WriteIni(SetupFileAddress, "lang", "-") '触发缓存更改,避免删除后重新下载残留缓存 WriteIni(SetupFileAddress, "lang", RequiredLang) diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb index b84ee9f..458a42a 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb @@ -1,4 +1,6 @@ -Public Module ModMinecraft +Imports System.IO.Compression + +Public Module ModMinecraft #Region "文件夹" @@ -149,7 +151,7 @@ #Region "版本处理" - Public Const McVersionCacheVersion As Integer = 27 + Public Const McVersionCacheVersion As Integer = 29 Private _McVersionCurrent As McVersion Private _McVersionLast = 0 '为 0 以保证与 Nothing 不相同,使得 UI 显示可以正常初始化 @@ -391,6 +393,29 @@ _Version.McName = JsonObject("jar").ToString GoTo VersionSearchFinish End If + '从 jar 文件的 version.json 中获取版本号 + If File.Exists(Path & Name & ".jar") Then + Try + Using JarArchive As New ZipArchive(New FileStream(Path & Name & ".jar", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + Dim VersionJson As ZipArchiveEntry = JarArchive.GetEntry("version.json") + If VersionJson IsNot Nothing Then + Using VersionJsonStream As New StreamReader(VersionJson.Open) + Dim VersionJsonObj As JObject = GetJson(VersionJsonStream.ReadToEnd) + If VersionJsonObj("id") IsNot Nothing Then + Dim VersionId As String = VersionJsonObj("id").ToString + If VersionId.Length < 32 Then '因为 wiki 说这玩意儿可能是个 hash,虽然我没发现 + _Version.McName = VersionId + Log("[Minecraft] 从版本 jar 中的 version.json 获取到版本号:" & VersionId) + GoTo VersionSearchFinish + End If + End If + End Using + End If + End Using + Catch ex As Exception + Log(ex, "从版本 jar 中的 version.json 获取版本号失败") + End Try + End If '非准确的版本判断警告 Log("[Minecraft] 无法完全确认 MC 版本号的版本:" & Name) '从文件夹名中获取 @@ -1034,7 +1059,7 @@ ExitDataLoad: Dim Path As String = Loader.Input Try '初始化 - McVersionList.Clear() + McVersionList = New Dictionary(Of McVersionCardType, List(Of McVersion)) '检测缓存是否需要更新 Dim FolderList As New List(Of String) @@ -1075,10 +1100,10 @@ Reload: '改变当前选择的版本 OnLoaded: - If McVersionList.Any(Function(v) v.Key <> McVersionCardType.Error) Then '不能判断 Count = 0,这在只有错误版本时导致了误判 + If McVersionList.Any(Function(v) v.Key <> McVersionCardType.Error) Then '尝试读取已储存的选择 Dim SavedSelection As String = ReadIni(Path & "PCL.ini", "Version") - If Not SavedSelection = "" Then + If SavedSelection <> "" Then For Each Card As KeyValuePair(Of McVersionCardType, List(Of McVersion)) In McVersionList For Each Version As McVersion In Card.Value If Version.Name = SavedSelection AndAlso Not Version.State = McVersionState.Error Then @@ -1190,7 +1215,7 @@ OnLoaded: End Try Next - ResultVersionList.Add(CardType, VersionList) + If VersionList.Any Then ResultVersionList.Add(CardType, VersionList) Next Return ResultVersionList Catch ex As Exception @@ -1200,10 +1225,14 @@ OnLoaded: End Function Private Function McVersionListLoadNoCache(Path As String) As Dictionary(Of McVersionCardType, List(Of McVersion)) Dim VersionList As New List(Of McVersion) - Dim ResultVersionList As New Dictionary(Of McVersionCardType, List(Of McVersion)) + #Region "循环加载每个版本的信息" Dim Dirs As DirectoryInfo() = New DirectoryInfo(Path & "versions").GetDirectories For Each Folder As DirectoryInfo In Dirs + If Not Folder.EnumerateFiles.Any Then + Log("[Minecraft] 跳过空文件夹:" & Folder.FullName) + Continue For + End If If (Folder.Name = "cache" OrElse Folder.Name = "BLClient" OrElse Folder.Name = "PCL") AndAlso Not File.Exists(Folder.FullName & "\" & Folder.Name & ".json") Then Log("[Minecraft] 跳过可能不是版本文件夹的项目:" & Folder.FullName) Continue For @@ -1223,6 +1252,9 @@ OnLoaded: Version.Load() Next #End Region + + Dim ResultVersionList As New Dictionary(Of McVersionCardType, List(Of McVersion)) + #Region "将版本分类到各个卡片" Try @@ -1236,7 +1268,7 @@ OnLoaded: Next If StaredVersions.Any Then VersionListOriginal.Add(McVersionCardType.Star, StaredVersions) - '预先筛选出愚人节的版本 + '预先筛选出愚人节和错误的版本 McVersionFilter(VersionList, VersionListOriginal, {McVersionState.Error}, McVersionCardType.Error) McVersionFilter(VersionList, VersionListOriginal, {McVersionState.Fool}, McVersionCardType.Fool) @@ -1347,9 +1379,7 @@ OnLoaded: Next If Not IsNothing(Snapshot) Then OldList.Remove(Snapshot) '按版本号排序 - Dim NewList As List(Of McVersion) = Sort(OldList, Function(Left As McVersion, Right As McVersion) - Return Left.Version.McCodeMain > Right.Version.McCodeMain - End Function) + Dim NewList As List(Of McVersion) = Sort(OldList, Function(Left, Right) Left.Version.McCodeMain > Right.Version.McCodeMain) '回设 If Not IsNothing(Snapshot) Then NewList.Insert(0, Snapshot) ResultVersionList(McVersionCardType.OriginalLike) = NewList @@ -1357,43 +1387,45 @@ OnLoaded: '不常用版本:按发布时间新旧排序,如果不可用则按名称排序 If ResultVersionList.ContainsKey(McVersionCardType.Rubbish) Then - ResultVersionList(McVersionCardType.Rubbish) = Sort(ResultVersionList(McVersionCardType.Rubbish), Function(Left As McVersion, Right As McVersion) - Dim LeftYear As Integer = Left.ReleaseTime.Year '+ If(Left.State = McVersionState.Original OrElse Left.Version.HasOptiFine, 100, 0) - Dim RightYear As Integer = Right.ReleaseTime.Year '+ If(Right.State = McVersionState.Original OrElse Left.Version.HasOptiFine, 100, 0) - If LeftYear > 2000 AndAlso RightYear > 2000 Then - If LeftYear <> RightYear Then - Return LeftYear > RightYear - Else - Return Left.ReleaseTime > Right.ReleaseTime - End If - ElseIf LeftYear > 2000 AndAlso RightYear < 2000 Then - Return True - ElseIf LeftYear < 2000 AndAlso RightYear > 2000 Then - Return False - Else - Return Left.Name > Right.Name - End If - End Function) + ResultVersionList(McVersionCardType.Rubbish) = Sort(ResultVersionList(McVersionCardType.Rubbish), + Function(Left As McVersion, Right As McVersion) + Dim LeftYear As Integer = Left.ReleaseTime.Year '+ If(Left.State = McVersionState.Original OrElse Left.Version.HasOptiFine, 100, 0) + Dim RightYear As Integer = Right.ReleaseTime.Year '+ If(Right.State = McVersionState.Original OrElse Left.Version.HasOptiFine, 100, 0) + If LeftYear > 2000 AndAlso RightYear > 2000 Then + If LeftYear <> RightYear Then + Return LeftYear > RightYear + Else + Return Left.ReleaseTime > Right.ReleaseTime + End If + ElseIf LeftYear > 2000 AndAlso RightYear < 2000 Then + Return True + ElseIf LeftYear < 2000 AndAlso RightYear > 2000 Then + Return False + Else + Return Left.Name > Right.Name + End If + End Function) End If 'API 版本:优先按版本排序,此后【先放 Fabric,再放 Forge(按版本号从高到低排序),最后放 LiteLoader(按名称排序)】 If ResultVersionList.ContainsKey(McVersionCardType.API) Then - ResultVersionList(McVersionCardType.API) = Sort(ResultVersionList(McVersionCardType.API), Function(Left As McVersion, Right As McVersion) - Dim Basic = VersionSortInteger(Left.Version.McName, Right.Version.McName) - If Basic <> 0 Then - Return Basic > 0 - Else - If Left.Version.HasFabric Xor Right.Version.HasFabric Then - Return Left.Version.HasFabric - ElseIf Left.Version.HasForge Xor Right.Version.HasForge Then - Return Left.Version.HasForge - ElseIf Not Left.Version.SortCode <> Right.Version.SortCode Then - Return Left.Version.SortCode > Right.Version.SortCode - Else - Return Left.Name > Right.Name - End If - End If - End Function) + ResultVersionList(McVersionCardType.API) = Sort(ResultVersionList(McVersionCardType.API), + Function(Left As McVersion, Right As McVersion) + Dim Basic = VersionSortInteger(Left.Version.McName, Right.Version.McName) + If Basic <> 0 Then + Return Basic > 0 + Else + If Left.Version.HasFabric Xor Right.Version.HasFabric Then + Return Left.Version.HasFabric + ElseIf Left.Version.HasForge Xor Right.Version.HasForge Then + Return Left.Version.HasForge + ElseIf Not Left.Version.SortCode <> Right.Version.SortCode Then + Return Left.Version.SortCode > Right.Version.SortCode + Else + Return Left.Name > Right.Name + End If + End If + End Function) End If #End Region @@ -1417,17 +1449,7 @@ OnLoaded: ''' 需要筛选出的版本类型。-2 代表隐藏的版本。 ''' 卡片的名称。 Private Sub McVersionFilter(ByRef VersionList As List(Of McVersion), ByRef Target As Dictionary(Of McVersionCardType, List(Of McVersion)), Formula As McVersionState(), CardType As McVersionCardType) - '将某种版本筛选出来 - Dim KeepList As New List(Of McVersion) - For Each Version As McVersion In VersionList - For Each Type As Integer In Formula - If Type = Version.State Then - KeepList.Add(Version) - GoTo NextVersion - End If - Next -NextVersion: - Next + Dim KeepList = VersionList.Where(Function(v) Formula.Contains(v.State)).ToList '加入版本列表,并从剩余中删除 If KeepList.Any Then Target.Add(CardType, KeepList) @@ -1443,13 +1465,8 @@ NextVersion: ''' 需要筛选出的版本类型。-2 代表隐藏的版本。 ''' 传入需要增加入的列表。 Private Sub McVersionFilter(ByRef VersionList As List(Of McVersion), Formula As McVersionState(), ByRef KeepList As List(Of McVersion)) - For Each Version As McVersion In VersionList - For Each Type As McVersionState In Formula - If Type <> Version.State Then Continue For - KeepList.Add(Version) : Exit For - Next -NextVersion: - Next + KeepList.AddRange(VersionList.Where(Function(v) Formula.Contains(v.State))) + '加入版本列表,并从剩余中删除 If KeepList.Any Then For Each Version As McVersion In KeepList VersionList.Remove(Version) diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb index 040e9d5..348d3cb 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb @@ -887,11 +887,11 @@ Finished: ModList.Add(ModEntry) '读取 Comp 缓存 If ModEntry.State = McMod.McModState.Unavaliable Then Continue For - Dim Hash = ModEntry.ModrinthHash - If Cache.ContainsKey(Hash) Then - ModEntry.FromJson(Cache(Hash)) + Dim CacheKey = ModEntry.ModrinthHash & PageVersionLeft.Version.Version.McName + If Cache.ContainsKey(CacheKey) Then + ModEntry.FromJson(Cache(CacheKey)) '如果缓存中的信息在 6 小时以内更新过,则无需重新获取 - If ModEntry.CompLoaded AndAlso Date.Now - Cache(Hash)("Comp")("CacheTime").ToObject(Of Date) < New TimeSpan(6, 0, 0) Then Continue For + If ModEntry.CompLoaded AndAlso Date.Now - Cache(CacheKey)("Comp")("CacheTime").ToObject(Of Date) < New TimeSpan(6, 0, 0) Then Continue For End If ModUpdateList.Add(ModEntry) Next @@ -925,7 +925,7 @@ Finished: End Sub '联网加载 Mod 详情 - Private Const LocalModCacheVersion As Integer = 3 + Private Const LocalModCacheVersion As Integer = 4 Public McModDetailLoader As New LoaderTask(Of KeyValuePair(Of List(Of McMod), JObject), Integer)("Mod List Detail Loader", AddressOf McModDetailLoad) Private Sub McModDetailLoad(Loader As LoaderTask(Of KeyValuePair(Of List(Of McMod), JObject), Integer)) Dim Mods As List(Of McMod) = Loader.Input.Key @@ -937,7 +937,7 @@ Finished: If TargetMcVersion.HasFabric Then ModLoaders.Add(CompModLoaderType.Fabric) If TargetMcVersion.HasLiteLoader Then ModLoaders.Add(CompModLoaderType.LiteLoader) If Not ModLoaders.Any() Then ModLoaders.AddRange({CompModLoaderType.Forge, CompModLoaderType.Fabric, CompModLoaderType.LiteLoader, CompModLoaderType.Quilt}) - Dim McVersions As New List(Of String) From {TargetMcVersion.McName} + Dim McVersion = TargetMcVersion.McName '暂不向下扩展检查的 MC 小版本 '例如:Mod 在更新 1.16.5 后,对早期的 1.16.2 版本发布了修补补丁,这会导致 PCL 将 1.16.5 版本的 Mod 降级到 1.16.2 'If TargetMcVersion.McCodeMain > 0 AndAlso TargetMcVersion.McCodeMain < 99 Then @@ -948,7 +948,7 @@ Finished: 'End If 'McVersions = McVersions.Distinct().ToList() '开始网络获取 - Log($"[Mod] 目标加载器:{ModLoaders.Join("/")},版本:{McVersions.Join("/")}") + Log($"[Mod] 目标加载器:{ModLoaders.Join("/")},版本:{McVersion}") Dim EndedThreadCount As Integer = 0, SucceedThreadCount As Integer = 0 Dim MainThread As Thread = Thread.CurrentThread '从 Modrinth 获取信息 @@ -991,8 +991,8 @@ Finished: Log($"[Mod] 已从 Modrinth 获取本地 Mod 信息,继续获取更新信息") '步骤 4:获取更新信息 Dim ModrinthUpdate = CType(GetJson(NetRequestRetry("https://api.modrinth.com/v2/version_files/update", "POST", - $"{{""hashes"": [""{ModrinthHashes.Join(""",""")}""], ""algorithm"": ""sha1"", - ""loaders"": [""{ModLoaders.Join(""",""").ToLower}""],""game_versions"": [""{McVersions.Join(""",""")}""]}}", "application/json")), JObject) + $"{{""hashes"": [""{ModrinthMapping.SelectMany(Function(l) l.Value.Select(Function(m) m.ModrinthHash)).Join(""",""")}""], ""algorithm"": ""sha1"", + ""loaders"": [""{ModLoaders.Join(""",""").ToLower}""],""game_versions"": [""{McVersion}""]}}", "application/json")), JObject) For Each Entry In Mods If Not ModrinthUpdate.ContainsKey(Entry.ModrinthHash) OrElse Entry.CompFile Is Nothing Then Continue For Dim UpdateFile As New CompFile(ModrinthUpdate(Entry.ModrinthHash), CompType.Mod) @@ -1001,11 +1001,11 @@ Finished: If Entry.CompFile.ReleaseDate >= UpdateFile.ReleaseDate OrElse Entry.CompFile.Hash = UpdateFile.Hash Then Continue For '设置更新日志与更新文件 If Entry.UpdateFile IsNot Nothing AndAlso UpdateFile.Hash = Entry.UpdateFile.Hash Then '合并 - Entry.ChangelogUrls.Add($"https://modrinth.com/mod/{ModrinthUpdate(Entry.ModrinthHash)("project_id")}/changelog?{McVersions.Select(Function(v) $"g={v}&").Join("")}") + Entry.ChangelogUrls.Add($"https://modrinth.com/mod/{ModrinthUpdate(Entry.ModrinthHash)("project_id")}/changelog?g={McVersion}") UpdateFile.DownloadUrls.AddRange(Entry.UpdateFile.DownloadUrls) '合并下载源 Entry.UpdateFile = UpdateFile '优先使用 Modrinth 的文件 ElseIf Entry.UpdateFile Is Nothing OrElse UpdateFile.ReleaseDate >= Entry.UpdateFile.ReleaseDate Then '替换 - Entry.ChangelogUrls = New List(Of String) From {$"https://modrinth.com/mod/{ModrinthUpdate(Entry.ModrinthHash)("project_id")}/changelog?{McVersions.Select(Function(v) $"g={v}&").Join("")}"} + Entry.ChangelogUrls = New List(Of String) From {$"https://modrinth.com/mod/{ModrinthUpdate(Entry.ModrinthHash)("project_id")}/changelog?g={McVersion}"} Entry.UpdateFile = UpdateFile End If Next @@ -1073,7 +1073,7 @@ Finished: For Each IndexEntry In ProjectJson("latestFilesIndexes") If ModLoaders.Single <> IndexEntry("modLoader")?.ToObject(Of Integer) Then Continue For 'ModLoader 唯一且匹配 Dim IndexVersion As String = IndexEntry("gameVersion") - If Not McVersions.Contains(IndexVersion) Then Continue For 'MC 版本匹配 + If McVersion <> IndexVersion Then Continue For 'MC 版本匹配 '由于 latestFilesIndexes 是按时间从新到老排序的,所以只需取第一个;如果需要检查多个 releaseType 下的文件,将 > -1 改为 = 1,但这应当并不会获取到更新的文件 If NewestVersion IsNot Nothing AndAlso VersionSortInteger(NewestVersion, IndexVersion) > -1 Then Continue For '只保留最新 MC 版本 If NewestVersion <> IndexVersion Then @@ -1136,7 +1136,7 @@ Finished: If Not Mods.Any() Then Exit Sub For Each Entry In Mods 'TODO: Bookshelf 的 Logo 会在两个网站间横跳 Entry.CompLoaded = SucceedThreadCount = 2 - Cache(Entry.ModrinthHash) = Entry.ToJson() + Cache(Entry.ModrinthHash & McVersion) = Entry.ToJson() Next WriteFile(PathTemp & "Cache\LocalMod.json", Cache.ToString(If(ModeDebug, Newtonsoft.Json.Formatting.Indented, Newtonsoft.Json.Formatting.None))) End Sub diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb index c4e5c55..9d16fd2 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb @@ -348,6 +348,7 @@ '崩溃分析 WatcherLog("Minecraft 已崩溃,将在 2 秒后开始崩溃分析") Hint("检测到 Minecraft 出现错误,错误分析已开始……") + FeedbackInfo() RunInNewThread(Sub() Try Thread.Sleep(2000) diff --git a/Plain Craft Launcher 2/Modules/Minecraft/MyLocalModItem.xaml.vb b/Plain Craft Launcher 2/Modules/Minecraft/MyLocalModItem.xaml.vb index 7b48d21..016f81a 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/MyLocalModItem.xaml.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/MyLocalModItem.xaml.vb @@ -195,6 +195,7 @@ RetryStart: Private Shared Swiping As Boolean = False Private Shared SwipToState As Boolean '被滑动到的目标应将 Checked 改为此值 Private Sub Button_MouseSwipeStart(sender As Object, e As Object) Handles Me.MouseLeftButtonDown + If Parent Is Nothing Then Exit Sub 'Mod 可能已被删除(#3824) '开始滑动 Dim Index = CType(Parent, StackPanel).Children.IndexOf(Me) SwipeStart = Index @@ -204,6 +205,7 @@ RetryStart: FrmVersionMod.CardSelect.IsHitTestVisible = False '暂时禁用下边栏 End Sub Private Sub Button_MouseSwipe(sender As Object, e As Object) Handles Me.MouseEnter, Me.MouseLeave, Me.MouseLeftButtonUp + If Parent Is Nothing Then Exit Sub 'Mod 可能已被删除(#3824) '结束滑动 If Mouse.LeftButton <> MouseButtonState.Pressed OrElse Not Swiping Then Swiping = False diff --git a/Plain Craft Launcher 2/Modules/ModEvent.vb b/Plain Craft Launcher 2/Modules/ModEvent.vb index dddef07..aa09e79 100644 --- a/Plain Craft Launcher 2/Modules/ModEvent.vb +++ b/Plain Craft Launcher 2/Modules/ModEvent.vb @@ -73,8 +73,8 @@ Case "切换页面" FrmMain.PageChange(Val(Data(0)), Val(Data(1))) - Case "导入整合包" - RunInThread(Sub() ModpackInstall()) + Case "导入整合包", "安装整合包" + RunInUi(Sub() ModpackInstall()) Case "下载文件" Data(0) = Data(0).Replace("\", "/") @@ -101,8 +101,7 @@ '网页确认 If RelativeUrl.StartsWithF("http", True) Then If RunInUi() Then - Throw New Exception("MyListItem 在界面初始化时就需要获取帮助标题等信息,这会导致程序在网络请求时卡死。" & vbCrLf & - "因此,请换用 MyListItem 以外的控件(例如 MyButton)作为联网帮助页面的入口!") + Throw New Exception("能打开联网帮助页面的 MyListItem 必须手动设置 Title、Info 属性!") End If '获取文件名 Dim RawFileName As String diff --git a/Plain Craft Launcher 2/Modules/ModMusic.vb b/Plain Craft Launcher 2/Modules/ModMusic.vb index 860a967..8377aef 100644 --- a/Plain Craft Launcher 2/Modules/ModMusic.vb +++ b/Plain Craft Launcher 2/Modules/ModMusic.vb @@ -1,6 +1,4 @@ -Imports System.Security.Cryptography - -Public Module ModMusic +Public Module ModMusic #Region "播放列表" @@ -199,13 +197,15 @@ Public Module ModMusic ''' Public Function MusicPause() As Boolean If MusicState = MusicStates.Play Then - RunInThread(Sub() - MusicNAudio.Pause() - MusicRefreshUI() - Log("[Music] 已暂停播放") - End Sub) + RunInThread( + Sub() + Log("[Music] 已暂停播放") + MusicNAudio?.Pause() + MusicRefreshUI() + End Sub) Return True Else + Log($"[Music] 无需暂停播放,当前状态为 {MusicState}") Return False End If End Function @@ -214,13 +214,15 @@ Public Module ModMusic ''' Public Function MusicResume() As Boolean If MusicState = MusicStates.Play OrElse Not MusicAllList.Any() Then + Log($"[Music] 无需继续播放,当前状态为 {MusicState}") Return False Else - RunInThread(Sub() - MusicNAudio.Play() - MusicRefreshUI() - Log("[Music] 已恢复播放") - End Sub) + RunInThread( + Sub() + Log("[Music] 已恢复播放") + MusicNAudio?.Play() + MusicRefreshUI() + End Sub) Return True End If End Function diff --git a/Plain Craft Launcher 2/My Project/AssemblyInfo.vb b/Plain Craft Launcher 2/My Project/AssemblyInfo.vb index 699891c..885e140 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 7be4561..3b02dc6 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb +++ b/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb @@ -384,24 +384,27 @@ Public Module ModDownloadLib End Sub 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 - If Not JavaDownloadConfirm("Java 8 或更高版本") Then Throw New Exception("由于未找到 Java,已取消安装。") - '开始自动下载 - Dim JavaLoader = JavaFixLoaders(17) - Try - JavaLoader.Start(17, IsForceRestart:=True) - Do While JavaLoader.State = LoadState.Loading AndAlso Not Task.IsAborted - Thread.Sleep(10) - Loop - Finally - JavaLoader.Abort() '确保取消时中止 Java 下载 - End Try - '检查下载结果 + Dim Java As JavaEntry + SyncLock JavaLock Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0)) - If Task.IsAborted Then Exit Sub - If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。") - End If + If Java Is Nothing Then + If Not JavaDownloadConfirm("Java 8 或更高版本") Then Throw New Exception("由于未找到 Java,已取消安装。") + '开始自动下载 + Dim JavaLoader = JavaFixLoaders(17) + Try + JavaLoader.Start(17, IsForceRestart:=True) + Do While JavaLoader.State = LoadState.Loading AndAlso Not Task.IsAborted + Thread.Sleep(10) + Loop + Finally + JavaLoader.Abort() '确保取消时中止 Java 下载 + End Try + '检查下载结果 + Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0)) + If Task.IsAborted Then Exit Sub + If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。") + End If + End SyncLock '添加 Java Wrapper 作为主 Jar Dim Arguments As String If UseJavaWrapper Then @@ -1111,24 +1114,27 @@ Retry: 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, 60)) - If Java Is Nothing Then - If Not JavaDownloadConfirm("Java 8 或更高版本") Then Throw New Exception("由于未找到 Java,已取消安装。") - '开始自动下载 - Dim JavaLoader = JavaFixLoaders(17) - Try - JavaLoader.Start(17, IsForceRestart:=True) - Do While JavaLoader.State = LoadState.Loading AndAlso Not Task.IsAborted - Thread.Sleep(10) - Loop - Finally - JavaLoader.Abort() '确保取消时中止 Java 下载 - End Try - '检查下载结果 + Dim Java As JavaEntry + SyncLock JavaLock Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 60)) - If Task.IsAborted Then Exit Sub - If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。") - End If + If Java Is Nothing Then + If Not JavaDownloadConfirm("Java 8 或更高版本") Then Throw New Exception("由于未找到 Java,已取消安装。") + '开始自动下载 + Dim JavaLoader = JavaFixLoaders(17) + Try + JavaLoader.Start(17, IsForceRestart:=True) + Do While JavaLoader.State = LoadState.Loading AndAlso Not Task.IsAborted + Thread.Sleep(10) + Loop + Finally + JavaLoader.Abort() '确保取消时中止 Java 下载 + End Try + '检查下载结果 + Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 60)) + If Task.IsAborted Then Exit Sub + If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。") + End If + End SyncLock '添加 Java Wrapper 作为主 Jar Dim Arguments As String If UseJavaWrapper Then @@ -1275,8 +1281,8 @@ Retry: McFolder = If(McFolder, PathMcFolder) If Version.StartsWithF("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 + Inherit = Version.Before("-") + Version = Version.After("-") End If Dim IsCustomFolder As Boolean = McFolder <> PathMcFolder Dim Id As String = Inherit & "-forge-" & Version @@ -1322,7 +1328,7 @@ Retry: Loaders.Add(New LoaderDownload("下载 Mod 加载器主文件", New List(Of NetFile)) With {.ProgressWeight = 9}) '安装(仅在新版安装时需要原版 Jar) - If Version.Split(".")(0) >= 20 Then + If Version.Before(".") >= 20 Then Log("[Download] 检测为新版 Forge:" & Version) Dim Libs As List(Of McLibToken) = Nothing Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("分析 Mod 加载器支持库文件", @@ -1344,7 +1350,7 @@ Retry: Task.Progress = 0.4 Dim RawJson As JObject = GetJson(NetGetCodeByDownload(DlSourceLauncherOrMetaGet(DlClientListGet(Inherit)), IsJson:=True)) '[net.minecraft:client:1.17.1-20210706.113038:mappings@txt] 或 @tsrg] - Dim OriginalName As String = Json("data")("MOJMAPS")("client").ToString.Trim("[]".ToCharArray()).Split("@")(0) + Dim OriginalName As String = Json("data")("MOJMAPS")("client").ToString.Trim("[]".ToCharArray()).Before("@") Dim Address = McLibGet(OriginalName).Replace(".jar", "-mappings." & Json("data")("MOJMAPS")("client").ToString.Trim("[]".ToCharArray()).Split("@")(1)) Libs.Add(New McLibToken With {.IsJumpLoader = False, .IsNatives = False, .LocalPath = Address, .OriginalName = OriginalName, .Url = RawJson("downloads")("client_mappings")("url"), .Size = RawJson("downloads")("client_mappings")("size"), .SHA1 = RawJson("downloads")("client_mappings")("sha1")}) @@ -1788,7 +1794,7 @@ Retry: Public Function FabricApiDownloadListItem(Entry As CompFile, OnClick As MyListItem.ClickEventHandler) As MyListItem '建立控件 Dim NewItem As New MyListItem With { - .Title = Entry.DisplayName.Split("]")(1).Replace("Fabric API ", "").Replace(" build ", ".").Split("+").First.Trim, .SnapsToDevicePixels = True, .Height = 42, .Type = MyListItem.CheckType.Clickable, .Tag = Entry, + .Title = Entry.DisplayName.Split("]")(1).Replace("Fabric API ", "").Replace(" build ", ".").Before("+").Trim, .SnapsToDevicePixels = True, .Height = 42, .Type = MyListItem.CheckType.Clickable, .Tag = Entry, .Info = Entry.StatusDescription & ",发布于 " & Entry.ReleaseDate.ToString("yyyy'/'MM'/'dd HH':'mm"), .Logo = PathImage & "Blocks/Fabric.png" } @@ -1978,7 +1984,7 @@ Retry: If Directory.Exists(TempMcFolder) Then DeleteDirectory(TempMcFolder) Dim OptiFineFolder As String = Nothing If Request.OptiFineVersion IsNot Nothing Then - If Request.OptiFineVersion.Contains("_HD_U_") Then Request.OptiFineVersion = "HD_U_" & Request.OptiFineVersion.Split("_HD_U_").Last '#735 + If Request.OptiFineVersion.Contains("_HD_U_") Then Request.OptiFineVersion = "HD_U_" & Request.OptiFineVersion.After("_HD_U_") '#735 Request.OptiFineEntry = New DlOptiFineListEntry With { .NameDisplay = Request.MinecraftName & " " & Request.OptiFineVersion.Replace("HD_U_", "").Replace("_", "").Replace("pre", " pre"), .Inherit = Request.MinecraftName, @@ -2062,7 +2068,7 @@ Retry: End Sub) With {.ProgressWeight = 2, .Block = True}) '补全文件 If Not DontFixLibraries AndAlso - (Request.OptiFineEntry IsNot Nothing OrElse (Request.ForgeVersion IsNot Nothing AndAlso Request.ForgeVersion.Split(".")(0) >= 20) OrElse Request.FabricVersion IsNot Nothing OrElse Request.LiteLoaderEntry IsNot Nothing) Then + (Request.OptiFineEntry IsNot Nothing OrElse (Request.ForgeVersion IsNot Nothing AndAlso Request.ForgeVersion.Before(".") >= 20) OrElse Request.FabricVersion IsNot Nothing OrElse Request.LiteLoaderEntry IsNot Nothing) Then Dim LoadersLib As New List(Of LoaderBase) LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析游戏支持库文件(副加载器)", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(OutputFolder))) With {.ProgressWeight = 1, .Show = False}) LoadersLib.Add(New LoaderDownload("下载游戏支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 7, .Show = False}) diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml.vb b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml.vb index 56959a5..1fa91e3 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml.vb @@ -275,7 +275,7 @@ If Project.TranslatedName = Project.RawName Then FileName = File.FileName Else - Dim ChineseName As String = Project.TranslatedName.Split(" (").First.Split(" - ").First. + Dim ChineseName As String = Project.TranslatedName.Before(" (").Before(" - "). Replace("\", "\").Replace("/", "/").Replace("|", "|").Replace(":", ":").Replace("<", "<").Replace(">", ">").Replace("*", "*").Replace("?", "?").Replace("""", "").Replace(": ", ":") Select Case Setup.Get("ToolDownloadTranslate") Case 0 @@ -299,7 +299,7 @@ If(File.FileName.EndsWith(".mrpack"), "*.mrpack", "*.zip")), DefaultFolder) If Not Target.Contains("\") Then Exit Sub '构造步骤加载器 - Dim LoaderName As String = Desc & "下载:" & File.DisplayName & " " + Dim LoaderName As String = Desc & "下载:" & GetFileNameWithoutExtentionFromPath(Target) & " " If Target <> DefaultFolder AndAlso Project.Type = CompType.Mod Then CachedFolder = GetPathFromFullPath(Target) Dim Loaders As New List(Of LoaderBase) Loaders.Add(New LoaderDownload("下载文件", New List(Of NetFile) From {File.ToNetFile(Target)}) With {.ProgressWeight = 6, .Block = True}) diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml index f7333fe..b6b6dc5 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml @@ -20,6 +20,7 @@ + @@ -60,26 +61,6 @@ - - - - - - - - - - - - - - - - - - - @@ -134,6 +115,26 @@ Data="F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z" /> + + + + + + + + + + + + + + + + + + + diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb index edfd0af..d7ef0bc 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb @@ -305,20 +305,25 @@ LabOptiFine.Foreground = ColorGray1 End If 'LiteLoader - Dim LiteLoaderError As String = LoadLiteLoaderGetError() - CardLiteLoader.MainSwap.Visibility = If(LiteLoaderError Is Nothing, Visibility.Visible, Visibility.Collapsed) - If LiteLoaderError IsNot Nothing Then CardLiteLoader.IsSwaped = True '例如在同时展开卡片时选择了不兼容项则强制折叠 - SetLiteLoaderInfoShow(CardLiteLoader.IsSwaped) - If SelectedLiteLoader Is Nothing Then - BtnLiteLoaderClear.Visibility = Visibility.Collapsed - ImgLiteLoader.Visibility = Visibility.Collapsed - LabLiteLoader.Text = If(LiteLoaderError, "点击选择") - LabLiteLoader.Foreground = ColorGray4 + If Not SelectedMinecraftId.Contains("1.") OrElse Val(SelectedMinecraftId.Split(".")(1)) > 12 Then + CardLiteLoader.Visibility = Visibility.Collapsed Else - BtnLiteLoaderClear.Visibility = Visibility.Visible - ImgLiteLoader.Visibility = Visibility.Visible - LabLiteLoader.Text = SelectedLiteLoader.Inherit - LabLiteLoader.Foreground = ColorGray1 + CardLiteLoader.Visibility = Visibility.Visible + Dim LiteLoaderError As String = LoadLiteLoaderGetError() + CardLiteLoader.MainSwap.Visibility = If(LiteLoaderError Is Nothing, Visibility.Visible, Visibility.Collapsed) + If LiteLoaderError IsNot Nothing Then CardLiteLoader.IsSwaped = True '例如在同时展开卡片时选择了不兼容项则强制折叠 + SetLiteLoaderInfoShow(CardLiteLoader.IsSwaped) + If SelectedLiteLoader Is Nothing Then + BtnLiteLoaderClear.Visibility = Visibility.Collapsed + ImgLiteLoader.Visibility = Visibility.Collapsed + LabLiteLoader.Text = If(LiteLoaderError, "点击选择") + LabLiteLoader.Foreground = ColorGray4 + Else + BtnLiteLoaderClear.Visibility = Visibility.Visible + ImgLiteLoader.Visibility = Visibility.Visible + LabLiteLoader.Text = SelectedLiteLoader.Inherit + LabLiteLoader.Foreground = ColorGray1 + End If End If 'Forge Dim ForgeError As String = LoadForgeGetError() @@ -337,60 +342,85 @@ LabForge.Foreground = ColorGray1 End If 'Fabric - Dim FabricError As String = LoadFabricGetError() - CardFabric.MainSwap.Visibility = If(FabricError Is Nothing, Visibility.Visible, Visibility.Collapsed) - If FabricError IsNot Nothing Then CardFabric.IsSwaped = True - SetFabricInfoShow(CardFabric.IsSwaped) - If SelectedFabric Is Nothing Then - BtnFabricClear.Visibility = Visibility.Collapsed - ImgFabric.Visibility = Visibility.Collapsed - LabFabric.Text = If(FabricError, "点击选择") - LabFabric.Foreground = ColorGray4 + If SelectedMinecraftId.Contains("1.") AndAlso Val(SelectedMinecraftId.Split(".")(1)) <= 13 Then + CardFabric.Visibility = Visibility.Collapsed Else - BtnFabricClear.Visibility = Visibility.Visible - ImgFabric.Visibility = Visibility.Visible - LabFabric.Text = SelectedFabric.Replace("+build", "") - LabFabric.Foreground = ColorGray1 + CardFabric.Visibility = Visibility.Visible + Dim FabricError As String = LoadFabricGetError() + CardFabric.MainSwap.Visibility = If(FabricError Is Nothing, Visibility.Visible, Visibility.Collapsed) + If FabricError IsNot Nothing Then CardFabric.IsSwaped = True + SetFabricInfoShow(CardFabric.IsSwaped) + If SelectedFabric Is Nothing Then + BtnFabricClear.Visibility = Visibility.Collapsed + ImgFabric.Visibility = Visibility.Collapsed + LabFabric.Text = If(FabricError, "点击选择") + LabFabric.Foreground = ColorGray4 + Else + BtnFabricClear.Visibility = Visibility.Visible + ImgFabric.Visibility = Visibility.Visible + LabFabric.Text = SelectedFabric.Replace("+build", "") + LabFabric.Foreground = ColorGray1 + End If End If 'FabricApi - Dim FabricApiError As String = LoadFabricApiGetError() - CardFabricApi.MainSwap.Visibility = If(FabricApiError Is Nothing, Visibility.Visible, Visibility.Collapsed) - If FabricApiError IsNot Nothing OrElse SelectedFabric Is Nothing Then CardFabricApi.IsSwaped = True - SetFabricApiInfoShow(CardFabricApi.IsSwaped) - If SelectedFabricApi Is Nothing Then - BtnFabricApiClear.Visibility = Visibility.Collapsed - ImgFabricApi.Visibility = Visibility.Collapsed - LabFabricApi.Text = If(FabricApiError, "点击选择") - LabFabricApi.Foreground = ColorGray4 + If SelectedFabric Is Nothing Then + CardFabricApi.Visibility = Visibility.Collapsed Else - BtnFabricApiClear.Visibility = Visibility.Visible - ImgFabricApi.Visibility = Visibility.Visible - LabFabricApi.Text = SelectedFabricApi.DisplayName.Split("]")(1).Replace("Fabric API ", "").Replace(" build ", ".").Split("+").First.Trim - LabFabricApi.Foreground = ColorGray1 + CardFabricApi.Visibility = Visibility.Visible + Dim FabricApiError As String = LoadFabricApiGetError() + CardFabricApi.MainSwap.Visibility = If(FabricApiError Is Nothing, Visibility.Visible, Visibility.Collapsed) + If FabricApiError IsNot Nothing OrElse SelectedFabric Is Nothing Then CardFabricApi.IsSwaped = True + SetFabricApiInfoShow(CardFabricApi.IsSwaped) + If SelectedFabricApi Is Nothing Then + BtnFabricApiClear.Visibility = Visibility.Collapsed + ImgFabricApi.Visibility = Visibility.Collapsed + LabFabricApi.Text = If(FabricApiError, "点击选择") + LabFabricApi.Foreground = ColorGray4 + Else + BtnFabricApiClear.Visibility = Visibility.Visible + ImgFabricApi.Visibility = Visibility.Visible + LabFabricApi.Text = SelectedFabricApi.DisplayName.Split("]")(1).Replace("Fabric API ", "").Replace(" build ", ".").Split("+").First.Trim + LabFabricApi.Foreground = ColorGray1 + End If End If 'OptiFabric - Dim OptiFabricError As String = LoadOptiFabricGetError() - CardOptiFabric.MainSwap.Visibility = If(OptiFabricError Is Nothing, Visibility.Visible, Visibility.Collapsed) - If OptiFabricError IsNot Nothing OrElse SelectedFabric Is Nothing Then CardOptiFabric.IsSwaped = True - SetOptiFabricInfoShow(CardOptiFabric.IsSwaped) - If SelectedOptiFabric Is Nothing Then - BtnOptiFabricClear.Visibility = Visibility.Collapsed - ImgOptiFabric.Visibility = Visibility.Collapsed - LabOptiFabric.Text = If(OptiFabricError, "点击选择") - LabOptiFabric.Foreground = ColorGray4 + If SelectedFabric Is Nothing OrElse SelectedOptiFine Is Nothing Then + CardOptiFabric.Visibility = Visibility.Collapsed Else - BtnOptiFabricClear.Visibility = Visibility.Visible - ImgOptiFabric.Visibility = Visibility.Visible - LabOptiFabric.Text = SelectedOptiFabric.DisplayName.ToLower.Replace("optifabric-", "").Replace(".jar", "").Trim.TrimStart("v") - LabOptiFabric.Foreground = ColorGray1 + CardOptiFabric.Visibility = Visibility.Visible + Dim OptiFabricError As String = LoadOptiFabricGetError() + CardOptiFabric.MainSwap.Visibility = If(OptiFabricError Is Nothing, Visibility.Visible, Visibility.Collapsed) + If OptiFabricError IsNot Nothing OrElse SelectedFabric Is Nothing Then CardOptiFabric.IsSwaped = True + SetOptiFabricInfoShow(CardOptiFabric.IsSwaped) + If SelectedOptiFabric Is Nothing Then + BtnOptiFabricClear.Visibility = Visibility.Collapsed + ImgOptiFabric.Visibility = Visibility.Collapsed + LabOptiFabric.Text = If(OptiFabricError, "点击选择") + LabOptiFabric.Foreground = ColorGray4 + Else + BtnOptiFabricClear.Visibility = Visibility.Visible + ImgOptiFabric.Visibility = Visibility.Visible + LabOptiFabric.Text = SelectedOptiFabric.DisplayName.ToLower.Replace("optifabric-", "").Replace(".jar", "").Trim.TrimStart("v") + LabOptiFabric.Foreground = ColorGray1 + End If End If '主警告 - HintFabricAPI.Visibility = If(SelectedFabric IsNot Nothing AndAlso SelectedFabricApi Is Nothing, Visibility.Visible, Visibility.Collapsed) + If SelectedFabric IsNot Nothing AndAlso SelectedFabricApi Is Nothing Then + HintFabricAPI.Visibility = Visibility.Visible + Else + HintFabricAPI.Visibility = Visibility.Collapsed + End If If SelectedFabric IsNot Nothing AndAlso SelectedOptiFine IsNot Nothing AndAlso SelectedOptiFabric Is Nothing Then HintOptiFabric.Visibility = Visibility.Visible Else HintOptiFabric.Visibility = Visibility.Collapsed End If + If SelectedMinecraftId.Contains("1.") AndAlso Val(SelectedMinecraftId.Split(".")(1)) >= 16 AndAlso SelectedOptiFine IsNot Nothing AndAlso + (SelectedForge IsNot Nothing OrElse SelectedFabric IsNot Nothing) Then + HintModOptiFine.Visibility = Visibility.Visible + Else + HintModOptiFine.Visibility = Visibility.Collapsed + End If '结束 IsReloading = False End Sub @@ -632,7 +662,7 @@ Return (OptiFine.RequiredForgeVersion.Contains(".") AndAlso 'XX.X.XXX VersionSortInteger(Forge.Version, OptiFine.RequiredForgeVersion) >= 0) OrElse (Not OptiFine.RequiredForgeVersion.Contains(".") AndAlso '#XXXX - VersionSortInteger(Forge.Version.Split(".").Last, OptiFine.RequiredForgeVersion) >= 0) + Forge.Version.After(".") >= OptiFine.RequiredForgeVersion) End Function '限制展开 @@ -909,12 +939,12 @@ If DisplayName.StartsWith("[" & MinecraftVersion & "]") Then Return True If Not DisplayName.Contains("/") OrElse Not DisplayName.Contains("]") Then Return False '直接的判断(例如 1.18.1/22w03a) - For Each Part As String In DisplayName.Split("]")(0).TrimStart("[").Split("/") + For Each Part As String In DisplayName.Before("]").TrimStart("[").Split("/") If Part = MinecraftVersion Then Return True Next '将版本名分割语素(例如 1.16.4/5) - Dim Lefts = RegexSearch(DisplayName.Split("]")(0), "[a-z/]+|[0-9/]+") - Dim Rights = RegexSearch(MinecraftVersion.Split("]")(0), "[a-z/]+|[0-9/]+") + Dim Lefts = RegexSearch(DisplayName.Before("]"), "[a-z/]+|[0-9/]+") + Dim Rights = RegexSearch(MinecraftVersion.Before("]"), "[a-z/]+|[0-9/]+") '对每段进行判断 Dim i As Integer = 0 While True diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/FormLoginOAuth.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/FormLoginOAuth.xaml.vb index 86fd7a5..35cd847 100644 --- a/Plain Craft Launcher 2/Pages/PageLaunch/FormLoginOAuth.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageLaunch/FormLoginOAuth.xaml.vb @@ -93,7 +93,7 @@ Public Class FormLoginOAuth If Not IsLoginSuccessed Then RaiseEvent OnLoginCanceled(IsSwitch) FrmMain.Focus() End Sub - Private Sub HintSwitch_Click(sender As Object, e As EventArgs) Handles HintSwitch.Click + Private Sub HintSwitch_Click() Handles HintSwitch.Click IsSwitch = True Close() End Sub diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml.vb index 1cc46fe..c7da13c 100644 --- a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml.vb @@ -400,7 +400,7 @@ Finish: ElseIf Setup.Get("LoginLegacyName") = "" Then Return New EqualableList(Of String) From {0, ""} Else - Return New EqualableList(Of String) From {0, If(Setup.Get("LoginLegacyName").ToString.Split("¨")(0), "")} + Return New EqualableList(Of String) From {0, If(Setup.Get("LoginLegacyName").ToString.Before("¨"), "")} End If Case 3 Return New EqualableList(Of String) From {3, Setup.Get("LaunchSkinID")} diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb index 64d755f..8da1fc5 100644 --- a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb @@ -97,6 +97,10 @@ Download: Log("[Page] 主页预设:每日整合包推荐") Url = "https://pclsub.sodamc.com/" GoTo Download + Case 5 + Log("[Page] 主页预设:Minecraft 皮肤推荐") + Url = "https://forgepixel.com/pcl_sub_file" + GoTo Download End Select End Select RunInUi(Sub() LoadContent(Content)) @@ -106,16 +110,17 @@ Download: '联网获取自定义主页文件 Private OnlineLoader As New LoaderTask(Of String, Integer)("自定义主页获取", AddressOf OnlineLoaderSub) With {.ReloadTimeout = 10 * 60 * 1000} Private Sub OnlineLoaderSub(Task As LoaderTask(Of String, Integer)) + Dim Address As String = Task.Input '#3721 中连续触发两次导致内容变化 Try '获取版本校验地址 Dim VersionAddress As String - If Task.Input.Contains(".xaml") Then - VersionAddress = Task.Input.Replace(".xaml", ".xaml.ini") + If Address.Contains(".xaml") Then + VersionAddress = Address.Replace(".xaml", ".xaml.ini") Else - VersionAddress = Task.Input.Split("?").First + VersionAddress = Address.Before("?") If Not VersionAddress.EndsWith("/") Then VersionAddress += "/" VersionAddress += "version" - If Task.Input.Contains("?") Then VersionAddress += Task.Input.Split("?").Last + If Address.Contains("?") Then VersionAddress += Address.After("?") End If '校验版本 Dim Version As String = "" @@ -136,9 +141,9 @@ Download: End Try '实际下载 If NeedDownload Then - Dim FileContent As String = NetGetCodeByRequestRetry(Task.Input) - Log($"[Page] 已联网下载自定义主页,内容长度:{FileContent.Length},来源:{Task.Input}") - Setup.Set("CacheSavedPageUrl", Task.Input) + Dim FileContent As String = NetGetCodeByRequestRetry(Address) + Log($"[Page] 已联网下载自定义主页,内容长度:{FileContent.Length},来源:{Address}") + Setup.Set("CacheSavedPageUrl", Address) Setup.Set("CacheSavedPageVersion", Version) WriteFile(PathTemp & "Cache\Custom.xaml", FileContent) End If @@ -146,11 +151,11 @@ Download: Refresh() Catch ex As Exception If Setup.Get("CacheSavedPageVersion") = "" Then - Log(ex, $"联网下载自定义主页失败({Task.Input})", LogLevel.Msgbox) + Log(ex, $"联网下载自定义主页失败({Address})", LogLevel.Msgbox) Else - Log(ex, $"联网下载自定义主页失败({Task.Input})") + Log(ex, $"联网下载自定义主页失败({Address})") End If - Task.Input = "" + Address = "" End Try End Sub @@ -208,6 +213,7 @@ Download: GoTo Refresh '防止 SyncLock 死锁 End If End Try + Log($"[Page] 实例化:加载自定义主页 UI 完成") End SyncLock Return Refresh: diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginAuth.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginAuth.xaml.vb index 7445bd6..b5422ae 100644 --- a/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginAuth.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginAuth.xaml.vb @@ -17,8 +17,8 @@ ComboName.ItemsSource = Nothing Else ComboName.ItemsSource = Setup.Get("LoginAuthEmail").ToString.Split("¨") - ComboName.Text = Setup.Get("LoginAuthEmail").ToString.Split("¨")(0) - If Setup.Get("LoginRemember") Then TextPass.Password = Setup.Get("LoginAuthPass").ToString.Split("¨")(0).Trim + ComboName.Text = Setup.Get("LoginAuthEmail").ToString.Before("¨") + If Setup.Get("LoginRemember") Then TextPass.Password = Setup.Get("LoginAuthPass").ToString.Before("¨").Trim End If End If IsFirstLoad = False diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginLegacy.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginLegacy.xaml.vb index 7b3f408..0c47882 100644 --- a/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginLegacy.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginLegacy.xaml.vb @@ -26,7 +26,7 @@ Public Class PageLoginLegacy ComboName.ItemsSource = Nothing Else ComboName.ItemsSource = Setup.Get("LoginLegacyName").ToString.Split("¨") - ComboName.Text = Setup.Get("LoginLegacyName").ToString.Split("¨")(0).Trim + ComboName.Text = Setup.Get("LoginLegacyName").ToString.Before("¨").Trim End If End If IsReloaded = True diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMs.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMs.xaml.vb index dc6b007..4bf92e2 100644 --- a/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMs.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMs.xaml.vb @@ -67,39 +67,43 @@ ComboAccounts.IsEnabled = False BtnLogin.IsEnabled = False BtnLogin.Text = "0%" - RunInNewThread(Sub() - Try - McLoginMsLoader.Start(GetLoginData(), IsForceRestart:=True) - Do While McLoginMsLoader.State = LoadState.Loading - RunInUi(Sub() BtnLogin.Text = Math.Round(McLoginMsLoader.Progress * 100) & "%") - Thread.Sleep(50) - Loop - If McLoginMsLoader.State = LoadState.Finished Then - RunInUi(Sub() FrmLaunchLeft.RefreshPage(False, True)) - ElseIf McLoginMsLoader.State = LoadState.Aborted Then - Throw New ThreadInterruptedException - ElseIf McLoginMsLoader.Error Is Nothing Then - Throw New Exception("未知错误!") - Else - Throw New Exception(McLoginMsLoader.Error.Message, McLoginMsLoader.Error) - End If - Catch ex As ThreadInterruptedException - Hint("已取消登录!") - Catch ex As Exception - If ex.Message = "$$" Then - ElseIf ex.Message.StartsWith("$") Then - Hint(ex.Message.TrimStart("$"), HintType.Critical) - Else - Log(ex, "微软登录尝试失败", LogLevel.Msgbox) - End If - Finally - RunInUi(Sub() - ComboAccounts.IsEnabled = True - BtnLogin.IsEnabled = True - BtnLogin.Text = "登录" - End Sub) - End Try - End Sub, "Ms Login") + RunInNewThread( + Sub() + Try + McLoginMsLoader.Start(GetLoginData(), IsForceRestart:=True) + Do While McLoginMsLoader.State = LoadState.Loading + RunInUi(Sub() BtnLogin.Text = Math.Round(McLoginMsLoader.Progress * 100) & "%") + Thread.Sleep(50) + Loop + If McLoginMsLoader.State = LoadState.Finished Then + RunInUi(Sub() FrmLaunchLeft.RefreshPage(False, True)) + ElseIf McLoginMsLoader.State = LoadState.Aborted Then + Throw New ThreadInterruptedException + ElseIf McLoginMsLoader.Error Is Nothing Then + Throw New Exception("未知错误!") + Else + Throw New Exception(McLoginMsLoader.Error.Message, McLoginMsLoader.Error) + End If + Catch ex As ThreadInterruptedException + Hint("已取消登录!") + Catch ex As Exception + If ex.Message = "$$" Then + ElseIf ex.Message.StartsWith("$") Then + Hint(ex.Message.TrimStart("$"), HintType.Critical) + ElseIf TypeOf ex Is Security.Authentication.AuthenticationException AndAlso ex.Message.ContainsF("SSL/TLS") Then + Log(ex, "正版登录验证失败,请尝试在 [设置 → 启动器] 中关闭 [验证 SSL 证书] 然后再试。" & vbCrLf & vbCrLf & "原始错误信息:", LogLevel.Msgbox) + Else + Log(ex, "正版登录尝试失败", LogLevel.Msgbox) + End If + Finally + RunInUi( + Sub() + ComboAccounts.IsEnabled = True + BtnLogin.IsEnabled = True + BtnLogin.Text = "登录" + End Sub) + End Try + End Sub, "Ms Login") End Sub End Class diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginNide.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginNide.xaml.vb index 70a971b..0e327d0 100644 --- a/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginNide.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginNide.xaml.vb @@ -17,8 +17,8 @@ ComboName.ItemsSource = Nothing Else ComboName.ItemsSource = Setup.Get("LoginNideEmail").ToString.Split("¨") - ComboName.Text = Setup.Get("LoginNideEmail").ToString.Split("¨")(0) - If Setup.Get("LoginRemember") Then TextPass.Password = Setup.Get("LoginNidePass").ToString.Split("¨")(0).Trim + ComboName.Text = Setup.Get("LoginNideEmail").ToString.Before("¨") + If Setup.Get("LoginRemember") Then TextPass.Password = Setup.Get("LoginNidePass").ToString.Before("¨").Trim End If End If IsFirstLoad = False diff --git a/Plain Craft Launcher 2/Pages/PageSelectLeft.xaml.vb b/Plain Craft Launcher 2/Pages/PageSelectLeft.xaml.vb index ac127bf..1ef40df 100644 --- a/Plain Craft Launcher 2/Pages/PageSelectLeft.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageSelectLeft.xaml.vb @@ -295,7 +295,7 @@ For i = 0 To Folders.Count - 1 If Folders(i) = "" Then Exit For If Folders(i).ToString.EndsWith(Folder.Path) Then - Name = Folders(i).ToString.Split(">")(0) + Name = Folders(i).ToString.Before(">") Folders.RemoveAt(i) Exit For End If @@ -320,7 +320,7 @@ For i = 0 To Folders.Count - 1 If Folders(i) = "" Then Exit For If Folders(i).ToString.EndsWith(Folder.Path) Then - 'Name = Folders(i).ToString.Split(">")(0) + 'Name = Folders(i).ToString.Before(">") Folders.RemoveAt(i) Exit For End If diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml index f4ff7e8..a1957fe 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml +++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml @@ -81,7 +81,7 @@ - + diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml index f235841..325c1f6 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml +++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml @@ -58,7 +58,7 @@ ToolTip="下载 Mod 时,若该 Mod 支持 Forge 和 Fabric,则显示为支持任意 Mod 加载器。 例如,开启时 JEI 显示支持全版本,关闭后 JEI 就会显示仅支持 Forge / Fabric 全版本。" /> + ToolTip="验证证书会提高安全性,但疑似有概率导致正版登录失败(#3018)。 如果在正版登录时因为 [根据验证过程,远程证书无效] 失败,可以尝试关闭此项。" /> diff --git a/Plain Craft Launcher 2/Pages/PageSpeedLeft.xaml.vb b/Plain Craft Launcher 2/Pages/PageSpeedLeft.xaml.vb index db5f4f2..18c7270 100644 --- a/Plain Craft Launcher 2/Pages/PageSpeedLeft.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageSpeedLeft.xaml.vb @@ -1,4 +1,6 @@ -Public Class PageSpeedLeft +Imports System.Threading.Tasks + +Public Class PageSpeedLeft Private Const WatcherInterval As Integer = 300 '初始化 @@ -59,8 +61,8 @@ If FrmSpeedRight Is Nothing OrElse FrmSpeedRight.PanMain Is Nothing Then Exit Sub Try SyncLock LoaderTaskbarLock - For i = 0 To LoaderTaskbar.Count - 1 - TaskRefresh(LoaderTaskbar(i)) + For Each Loader In LoaderTaskbar.ToList + TaskRefresh(Loader) Next End SyncLock Catch ex As Exception @@ -110,7 +112,7 @@ #Region "进度不同,更新卡片" Try If Card.Children.Count < LoaderList.Count * 2 Then - Log("刷新下载管理卡片失败:卡片中仅有 " & Card.Children.Count & " 个子项", LogLevel.Debug) + Log($"[Watcher] 刷新下载管理卡片 {Loader.Name} 失败:卡片中仅有 {Card.Children.Count} 个子项,要求至少有 {LoaderList.Count * 2} 个子项", LogLevel.Debug) Exit Try End If Dim Row As Integer = 0 @@ -137,7 +139,7 @@ Row += 1 Next Catch ex As Exception - Log(ex, "刷新下载管理卡片失败", LogLevel.Feedback) + Log(ex, $"刷新下载管理卡片 {Loader.Name} 失败", LogLevel.Feedback) End Try #End Region End Select @@ -191,13 +193,18 @@ '添加取消按钮 Dim Cancel As New MyIconButton With {.Name = "BtnCancel", .Logo = "F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z", .Height = 20, .Margin = New Thickness(0, 10, 10, 0), .LogoScale = 1.1, .HorizontalAlignment = HorizontalAlignment.Right, .VerticalAlignment = VerticalAlignment.Top} Card.Children.Add(Cancel) - AddHandler Cancel.Click, Sub(sender As MyIconButton, e As EventArgs) - AniDispose(sender, False) - AniDispose(Card, True, Sub() If FrmSpeedRight.PanMain.Children.Count = 0 AndAlso FrmMain.PageCurrent = FormMain.PageType.DownloadManager Then FrmMain.PageBack()) - RightCards.Remove(Loader.Name) - Log($"[Watcher] 关闭下载管理卡片:{Loader.Name}") - RunInThread(Sub() Loader.Abort()) - End Sub + AddHandler Cancel.Click, + Sub(sender As MyIconButton, e As EventArgs) + AniDispose(sender, False) + AniDispose(Card, True, Sub() If FrmSpeedRight.PanMain.Children.Count = 0 AndAlso FrmMain.PageCurrent = FormMain.PageType.DownloadManager Then FrmMain.PageBack()) + RightCards.Remove(Loader.Name) + SyncLock LoaderTaskbarLock + LoaderTaskbar.Remove(Loader) + Log($"[Taskbar] 由于下载管理卡片关闭,{Loader.Name} 已移出任务列表") + End SyncLock + Log($"[Watcher] 关闭下载管理卡片:{Loader.Name}") + RunInThread(Sub() Loader.Abort()) + End Sub '如果已经失败,再刷新一次,修改成失败的控件 If Loader.State = LoadState.Failed Then Card.Tag = Nothing '避免重复导致刷新无效