From 007b51b31464dce63b03869edde4a7403782db57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=99=E8=85=BE=E7=8C=AB=E8=B7=83?= <1043137532@qq.com> Date: Sat, 31 Aug 2024 02:39:45 +0800 Subject: [PATCH] 2.8.5 --- Plain Craft Launcher 2/FormMain.xaml.vb | 11 +- .../Modules/Base/ModBase.vb | 116 +++-- Plain Craft Launcher 2/Modules/Base/ModNet.vb | 26 +- .../Modules/Minecraft/ModComp.vb | 4 +- .../Modules/Minecraft/ModCrash.vb | 13 +- .../Modules/Minecraft/ModJava.vb | 4 +- .../Modules/Minecraft/ModLaunch.vb | 67 +-- .../Modules/Minecraft/ModMod.vb | 8 +- Plain Craft Launcher 2/Modules/ModSecret.vb | 10 +- .../My Project/AssemblyInfo.vb | 4 +- .../Pages/PageDownload/ModDownloadLib.vb | 2 +- .../PageDownload/PageDownloadNeoForge.xaml | 2 +- .../Pages/PageLaunch/PageLaunchRight.xaml.vb | 4 + .../Pages/PageLaunch/PageLoginMsSkin.xaml.vb | 2 +- .../Pages/PageSetup/ModSetup.vb | 2 +- .../Pages/PageSetup/PageSetupSystem.xaml | 2 +- .../Pages/PageSetup/PageSetupUI.xaml | 1 + .../Pages/PageVersion/PageVersionLeft.xaml.vb | 2 +- .../Pages/PageVersion/PageVersionMod.xaml | 23 +- .../Pages/PageVersion/PageVersionMod.xaml.vb | 445 +++++++++--------- Plain Craft Launcher 2/Resources/ModData.txt | 4 +- 21 files changed, 385 insertions(+), 367 deletions(-) diff --git a/Plain Craft Launcher 2/FormMain.xaml.vb b/Plain Craft Launcher 2/FormMain.xaml.vb index 4dedbccd..ee69501b 100644 --- a/Plain Craft Launcher 2/FormMain.xaml.vb +++ b/Plain Craft Launcher 2/FormMain.xaml.vb @@ -124,6 +124,15 @@ Public Class FormMain '3:BUG+ IMP* FEAT- '2:BUG* IMP- '1:BUG- + If LastVersion < 334 Then 'Snapshot 2.8.5 + FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "Mod 管理页面允许筛选可更新/启用/禁用的 Mod")) + If LastVersion = 333 Then + FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法安装愚人节和预发布版本的 Bug")) + FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法导出错误报告的 Bug")) + End If + FeatureCount += 6 + BugCount += 7 + End If If LastVersion < 333 Then 'Snapshot 2.8.4 FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "下载 Mod 时会使用 MCIM 国内镜像源")) FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "打开 PCL 时会自动安装同目录下的 modpack.zip")) @@ -744,7 +753,7 @@ Public Class FormMain Try If PageCurrent = PageType.VersionSetup AndAlso PageCurrentSub = PageSubType.VersionMod Then 'Mod 管理自动刷新 - FrmVersionMod.RefreshList() + FrmVersionMod.ReloadModList() ElseIf PageCurrent = PageType.VersionSelect Then '版本选择自动刷新 LoaderFolderRun(McVersionListLoader, PathMcFolder, LoaderFolderRunType.RunOnUpdated, MaxDepth:=1, ExtraPath:="versions\") diff --git a/Plain Craft Launcher 2/Modules/Base/ModBase.vb b/Plain Craft Launcher 2/Modules/Base/ModBase.vb index f41e5b14..00f7e1db 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.8.4" '不含分支前缀的显示用版本名 - Public Const VersionStandardCode As String = "2.8.4." & VersionBranchCode '标准格式的四段式版本号 + Public Const VersionBaseName As String = "2.8.5" '不含分支前缀的显示用版本名 + Public Const VersionStandardCode As String = "2.8.5." & VersionBranchCode '标准格式的四段式版本号 #If BETA Then Public Const VersionCode As Integer = 332 'Release #Else - Public Const VersionCode As Integer = 333 'Snapshot + Public Const VersionCode As Integer = 334 'Snapshot #End If '自动生成的版本信息 Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName @@ -696,7 +696,7 @@ Public Module ModBase If Not FileName.Contains(":\") Then FileName = $"{Path}PCL\{FileName}.ini" WriteFile(FileName, FileContent.ToString) Catch ex As Exception - Log(ex, $"写入文件失败({FileName} -> {Key}:{Value})") + Log(ex, $"写入文件失败({FileName} → {Key}:{Value})", LogLevel.Hint) End Try End Sub @@ -770,10 +770,10 @@ Public Module ModBase End Try End Sub ''' - ''' 读取文件,如果失败则返回空字符串。 + ''' 读取文件,如果失败则返回空数组。 ''' ''' 文件完整或相对路径。 - Public Function ReadFile(FilePath As String, Optional Encoding As Encoding = Nothing) As String + Public Function ReadFileBytes(FilePath As String, Optional Encoding As Encoding = Nothing) As Byte() Try '还原文件路径 If Not FilePath.Contains(":\") Then FilePath = Path & FilePath @@ -783,17 +783,25 @@ Public Module ModBase ReDim FileBytes(ReadStream.Length - 1) ReadStream.Read(FileBytes, 0, ReadStream.Length) End Using - ReadFile = If(Encoding Is Nothing, DecodeBytes(FileBytes), Encoding.GetString(FileBytes)) + Return FileBytes Else - Log("[System] 欲读取的文件不存在,已返回空字符串:" & FilePath) - Return "" + Log("[System] 欲读取的文件不存在,已返回空内容:" & FilePath) + Return {} End If Catch ex As Exception Log(ex, "读取文件出错:" & FilePath) - Return "" + Return {} End Try End Function ''' + ''' 读取文件,如果失败则返回空字符串。 + ''' + ''' 文件完整或相对路径。 + Public Function ReadFile(FilePath As String, Optional Encoding As Encoding = Nothing) As String + Dim FileBytes = ReadFileBytes(FilePath) + ReadFile = If(Encoding Is Nothing, DecodeBytes(FileBytes), Encoding.GetString(FileBytes)) + End Function + ''' ''' 读取流中的所有文本。 ''' Public Function ReadFile(Stream As Stream, Optional Encoding As Encoding = Nothing) As String @@ -818,50 +826,39 @@ Public Module ModBase ''' 文件完整或相对路径。 ''' 文件内容。 ''' 是否将文件内容追加到当前文件,而不是覆盖它。 - Public Function WriteFile(FilePath As String, Text As String, Optional Append As Boolean = False, Optional Encoding As Encoding = Nothing) As Boolean - Try - '还原文件路径 - If Not FilePath.Contains(":\") Then FilePath = Path & FilePath - '确保目录存在 - Directory.CreateDirectory(GetPathFromFullPath(FilePath)) - '写入文件 - If File.Exists(FilePath) Then - '如果文件存在,刷新目前文件 - Using writer As New StreamWriter(FilePath, Append, If(Encoding, GetEncoding(FilePath))) - writer.Write(Text) - writer.Flush() - writer.Close() - End Using - Else - '如果文件不存在,则新建并写入 - File.WriteAllText(FilePath, Text, If(Encoding, New UTF8Encoding(False))) - End If - Return True - Catch ex As Exception - Log(ex, "写入文件时出错:" & FilePath) - Return False - End Try - End Function + Public Sub WriteFile(FilePath As String, Text As String, Optional Append As Boolean = False, Optional Encoding As Encoding = Nothing) + '还原文件路径 + If Not FilePath.Contains(":\") Then FilePath = Path & FilePath + '确保目录存在 + Directory.CreateDirectory(GetPathFromFullPath(FilePath)) + '写入文件 + If File.Exists(FilePath) Then + '如果文件存在,刷新目前文件 + Using writer As New StreamWriter(FilePath, Append, If(Encoding, GetEncoding(ReadFileBytes(FilePath)))) + writer.Write(Text) + writer.Flush() + writer.Close() + End Using + Else + '如果文件不存在,则新建并写入 + File.WriteAllText(FilePath, Text, If(Encoding, New UTF8Encoding(False))) + End If + End Sub ''' ''' 写入文件。 + ''' 如果 CanThrow 设置为 False,返回是否写入成功。 ''' ''' 文件完整或相对路径。 ''' 文件内容。 ''' 是否将文件内容追加到当前文件,而不是覆盖它。 - Public Function WriteFile(FilePath As String, Content As Byte(), Optional Append As Boolean = False) As Boolean - Try - '还原文件路径 - If Not FilePath.Contains(":\") Then FilePath = Path & FilePath - '确保目录存在 - Directory.CreateDirectory(GetPathFromFullPath(FilePath)) - '写入文件 - File.WriteAllBytes(FilePath, Content) - Return True - Catch ex As Exception - Log(ex, "写入文件时出错:" & FilePath) - Return False - End Try - End Function + Public Sub WriteFile(FilePath As String, Content As Byte(), Optional Append As Boolean = False) + '还原文件路径 + If Not FilePath.Contains(":\") Then FilePath = Path & FilePath + '确保目录存在 + Directory.CreateDirectory(GetPathFromFullPath(FilePath)) + '写入文件 + File.WriteAllBytes(FilePath, Content) + End Sub ''' ''' 将流写入文件。 ''' @@ -891,21 +888,11 @@ Public Module ModBase '文件编码 ''' - ''' 获取文件编码。 - ''' - ''' 文件完整或相对路径。 - Public Function GetEncoding(FilePath As String) As Encoding - '还原文件路径 - If Not FilePath.Contains(":\") Then FilePath = Path & FilePath - '获取编码 - GetEncoding = GetEncoding(File.ReadAllBytes(FilePath)) - End Function - ''' - ''' 获取 Bytes 的编码。 + ''' 根据字节数组分析其编码。 ''' Public Function GetEncoding(Bytes As Byte()) As Encoding Dim Length As Integer = Bytes.Count - If Length <= 2 Then Return New UTF8Encoding(False) '不带 BOM 的 UTF8 + If Length < 3 Then Return New UTF8Encoding(False) '不带 BOM 的 UTF8 '根据 BOM 判断编码 If Bytes(0) >= &HEF Then '有 BOM 类型 @@ -1649,10 +1636,15 @@ RetryDir: End Function ''' - ''' 输入 And 字符不会报错的 Val。 + ''' 不会报错的 Val。 + ''' 如果输入有误,返回 0。 ''' Public Function Val(Str As Object) As Double - Return If(TypeOf Str Is String AndAlso Str = "&", 0, Conversion.Val(Str)) + Try + Return If(TypeOf Str Is String AndAlso Str = "&", 0, Conversion.Val(Str)) + Catch + Return 0 + End Try End Function '转义 diff --git a/Plain Craft Launcher 2/Modules/Base/ModNet.vb b/Plain Craft Launcher 2/Modules/Base/ModNet.vb index e93c699e..b2775a38 100644 --- a/Plain Craft Launcher 2/Modules/Base/ModNet.vb +++ b/Plain Craft Launcher 2/Modules/Base/ModNet.vb @@ -268,12 +268,7 @@ RequestFinished: '下载 Using Client As New WebClient Try - If UseBrowserUserAgent Then - Client.Headers(HttpRequestHeader.UserAgent) = "PCL2/" & VersionStandardCode & " Mozilla/5.0 AppleWebKit/537.36 Chrome/63.0.3239.132 Safari/537.36" - Else - Client.Headers(HttpRequestHeader.UserAgent) = "PCL2/" & VersionStandardCode - End If - Client.Headers(HttpRequestHeader.Referer) = "http://" & VersionCode & ".pcl2.server/" + SecretHeadersSign(Url, Client, UseBrowserUserAgent) Client.DownloadFile(Url, LocalFile) Catch ex As Exception File.Delete(LocalFile) @@ -315,7 +310,7 @@ Retry: Catch ex As ThreadInterruptedException Throw Catch ex As Exception - If ex.InnerException IsNot Nothing AndAlso ex.InnerException.Message.Contains("(40") AndAlso DontRetryOnRefused Then RetryCount = 999 + If ex.InnerException IsNot Nothing AndAlso ex.InnerException.Message.Contains("(40") AndAlso DontRetryOnRefused Then Throw Select Case RetryCount Case 0 If ModeDebug Then Log(ex, "[Net] 网络请求第一次失败(" & Url & ")") @@ -432,7 +427,11 @@ RequestFinished: End Using Catch End Try - ex = New WebException($"网络请求失败({ex.Status},{ex.Message},{Url}){If(String.IsNullOrEmpty(Res), "", vbCrLf & Res)}", ex) + If Res = "" Then + ex = New WebException($"网络请求失败({ex.Status},{ex.Message},{Url})", ex) + Else + ex = New ResponsedWebException($"服务器返回错误({ex.Status},{ex.Message},{Url}){vbCrLf}{Res}", Res, ex) + End If End If If MakeLog Then Log(ex, "NetRequestOnce 失败", LogLevel.Developer) Throw ex @@ -445,6 +444,17 @@ RequestFinished: If Resp IsNot Nothing Then Resp.Dispose() End Try End Function + Public Class ResponsedWebException + Inherits WebException + ''' + ''' 远程服务器给予的回复。 + ''' + Public Overloads Property Response As String + Public Sub New(Message As String, Response As String, InnerException As Exception) + MyBase.New(Message, InnerException) + Me.Response = Response + End Sub + End Class ''' ''' 最大线程数。 diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb index a893e44a..591f6746 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb @@ -1077,7 +1077,7 @@ Retry: If String.IsNullOrEmpty(Task.Input.SearchText) Then '如果没有搜索文本,按下载量将结果排序 For Each Result As CompProject In RealResults - Scores.Add(Result, Result.DownloadCount * If(Result.FromCurseForge, 1, 30)) + Scores.Add(Result, Result.DownloadCount * If(Result.FromCurseForge, 1, 10)) Next Else '如果有搜索文本,按关联度将结果排序 @@ -1085,7 +1085,7 @@ Retry: Dim Entry As New List(Of SearchEntry(Of CompProject)) For Each Result As CompProject In RealResults Scores.Add(Result, If(Result.WikiId > 0, 0.2, 0) + - Math.Log10(Math.Max(Result.DownloadCount, 1) * If(Result.FromCurseForge, 1, 30)) / 9) + Math.Log10(Math.Max(Result.DownloadCount, 1) * If(Result.FromCurseForge, 1, 10)) / 9) Entry.Add(New SearchEntry(Of CompProject) With {.Item = Result, .SearchSource = New List(Of KeyValuePair(Of String, Double)) From { New KeyValuePair(Of String, Double)(If(IsChineseSearch, Result.TranslatedName, Result.RawName), 1), New KeyValuePair(Of String, Double)(Result.Description, 0.05)}}) diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb index 0228a494..f1f070e0 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb @@ -844,8 +844,11 @@ NextStack: Public Sub Output(IsHandAnalyze As Boolean, Optional ExtraFiles As List(Of String) = Nothing) '弹窗提示 FrmMain.ShowWindowToTop() - Dim ShowLog As Action = + Select Case MyMsgBox(GetAnalyzeResult(IsHandAnalyze), If(IsHandAnalyze, "错误报告分析结果", "Minecraft 出现错误"), + "确定", If(IsHandAnalyze OrElse DirectFile Is Nothing, "", "查看日志"), If(IsHandAnalyze, "", "导出错误报告"), + Button2Action:=If(IsHandAnalyze OrElse DirectFile Is Nothing, Nothing, Sub() + '弹窗选择:查看日志 If File.Exists(DirectFile.Value.Key) Then ShellOnly("notepad", DirectFile.Value.Key) Else @@ -853,11 +856,9 @@ NextStack: WriteFile(FilePath, Join(DirectFile.Value.Value, vbCrLf)) ShellOnly(FilePath) End If - End Sub - Select Case MyMsgBox(GetAnalyzeResult(IsHandAnalyze), If(IsHandAnalyze, "错误报告分析结果", "Minecraft 出现错误"), - "确定", If(IsHandAnalyze OrElse DirectFile Is Nothing, "", "查看日志"), If(IsHandAnalyze, "", "导出错误报告"), - Button2Action:=If(IsHandAnalyze OrElse DirectFile Is Nothing, Nothing, ShowLog)) + End Sub)) Case 3 + '弹窗选择:导出错误报告 Dim FileAddress As String = Nothing Try '获取文件路径 @@ -881,7 +882,7 @@ NextStack: FileName = "游戏崩溃前的输出.txt" End Select If File.Exists(OutputFile) Then - Dim FileEncoding As Encoding = GetEncoding(OutputFile) + Dim FileEncoding As Encoding = GetEncoding(ReadFileBytes(OutputFile)) WriteFile(TempFolder & "Report\" & FileName, SecretFilter(ReadFile(OutputFile, FileEncoding), If(FileName = "启动脚本.bat", "F", "*")), Encoding:=FileEncoding) diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb index 58527603..af527eff 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb @@ -216,8 +216,8 @@ '添加特定的 Java Dim JavaPreList As New Dictionary(Of String, Boolean) - If PathMcFolder.Split("\").Count > 3 Then - JavaSearchFolder(GetPathFromFullPath(PathMcFolder), JavaPreList, False, True) 'Minecraft 文件夹的父文件夹(如果不是根目录的话) + If PathMcFolder.Split("\").Count > 3 AndAlso Not PathMcFolder.Contains("AppData\Roaming") Then + JavaSearchFolder(GetPathFromFullPath(PathMcFolder), JavaPreList, False, True) 'Minecraft 文件夹的父文件夹(如果不是根目录或 %APPDATA% 的话) End If JavaSearchFolder(PathMcFolder, JavaPreList, False, True) 'Minecraft 文件夹 JavaPreList = JavaPreList.Where(Function(j) Not j.Key.Contains(".minecraft\runtime")). diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb index db51edb2..c614d63c 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb @@ -1,5 +1,4 @@ Imports System.IO.Compression -Imports Newtonsoft.Json Public Module ModLaunch @@ -671,6 +670,7 @@ LoginFinish: Url:=Data.Input.BaseUrl & "/validate", Method:="POST", Data:=RequestData.ToString(0), + Headers:=New Dictionary(Of String, String) From {{"Accept-Language", "zh_CN"}}, ContentType:="application/json; charset=utf-8") '没有返回值的 '将登录结果输出 Data.Output.AccessToken = AccessToken @@ -694,6 +694,7 @@ LoginFinish: ""name"":""" & Setup.Get("Cache" & Data.Input.Token & "Name") & """},", "") & " ""accessToken"":""" & Setup.Get("Cache" & Data.Input.Token & "Access") & """, ""clientToken"":""" & Setup.Get("Cache" & Data.Input.Token & "Client") & """}", + Headers:=New Dictionary(Of String, String) From {{"Accept-Language", "zh_CN"}}, ContentType:="application/json; charset=utf-8")) '将登录结果输出 If LoginJson("selectedProfile") Is Nothing Then Throw New Exception("选择的角色 " & Setup.Get("Cache" & Data.Input.Token & "Name") & " 无效!") @@ -711,16 +712,6 @@ LoginFinish: Setup.Set("Cache" & Data.Input.Token & "Pass", Data.Input.Password) McLaunchLog("刷新登录成功(Refresh, " & Data.Input.Token & ")") End Sub - - Public Class AuthErrorResponse - Public Property [error] As String - Public Property errorMessage As String - End Class - Private Function ParseErrorResponse(ex As Exception) As AuthErrorResponse - Dim SplitEx As String = ex.ToString().Split(vbCrLf)(1).Split("-")(0).ToString().Trim() - Dim JsonEx As AuthErrorResponse = JsonConvert.DeserializeObject(Of AuthErrorResponse)(SplitEx) - Return JsonEx - End Function Private Function McLoginRequestLogin(ByRef Data As LoaderTask(Of McLoginServer, McLoginResult)) As Boolean Try Dim NeedRefresh As Boolean = False @@ -730,13 +721,11 @@ LoginFinish: New JProperty("username", Data.Input.UserName), New JProperty("password", Data.Input.Password), New JProperty("requestUser", True)) - Dim LHeaders As New Dictionary(Of String, String) - LHeaders.Add("Accept-Language", "zh_CN") Dim LoginJson As JObject = GetJson(NetRequestRetry( Url:=Data.Input.BaseUrl & "/authenticate", Method:="POST", Data:=RequestData.ToString(0), - Headers:=LHeaders, + Headers:=New Dictionary(Of String, String) From {{"Accept-Language", "zh_CN"}}, ContentType:="application/json; charset=utf-8")) '检查登录结果 If LoginJson("availableProfiles").Count = 0 Then @@ -796,13 +785,19 @@ LoginFinish: Catch ex As Exception Dim AllMessage As String = GetExceptionSummary(ex) Log(ex, "登录失败原始错误信息", LogLevel.Normal) + '读取服务器返回的错误 + If TypeOf ex Is ResponsedWebException Then + Dim ErrorMessage As String = Nothing + Try + ErrorMessage = GetJson(DirectCast(ex, ResponsedWebException).Response)("errorMessage") + Catch + End Try + If Not String.IsNullOrWhiteSpace(ErrorMessage) Then Throw New Exception("$登录失败:" & ErrorMessage) + End If + '通用关键字检测 If AllMessage.Contains("403") Then Select Case Data.Input.Type Case McLoginType.Auth - Dim ErrorResponse As AuthErrorResponse = ParseErrorResponse(ex) - If (ErrorResponse.errorMessage <> "") Then - Throw New Exception("$登录失败,以下为认证服务器提供的信息:" & vbCrLf & $" - {ErrorResponse.errorMessage}") - End If Throw New Exception("$登录失败,以下为可能的原因:" & vbCrLf & " - 输入的账号或密码错误。" & vbCrLf & " - 登录尝试过于频繁,导致被暂时屏蔽。请不要操作,等待 10 分钟后再试。" & vbCrLf & @@ -1349,20 +1344,28 @@ LoginFinish: Dim WrapperPath As String = BaseDir & "\JavaWrapper.jar" Log("[Java] 选定的 Java Wrapper 路径:" & WrapperPath) SyncLock ExtractJavaWrapperLock '避免 OptiFine 和 Forge 安装时同时释放 Java Wrapper 导致冲突 - Dim IsWrapperWritten As Boolean = WriteFile(WrapperPath, GetResources("JavaWrapper")) - If Not IsWrapperWritten AndAlso File.Exists(WrapperPath) Then - '以下为 #4243 的修复,因为未知原因 Java Wrapper 可能变为只读文件 - Log("[Java] Java Wrapper 文件释放失败,但文件已存在,将在删除后尝试重新生成", LogLevel.Debug) - Try - File.Delete(WrapperPath) - IsWrapperWritten = WriteFile(WrapperPath, GetResources("JavaWrapper")) - Catch ex As Exception - Log(ex, "Java Wrapper 文件重新释放失败,将尝试更换文件名重新生成") - WrapperPath = BaseDir & "\JavaWrapper2.jar" - IsWrapperWritten = WriteFile(WrapperPath, GetResources("JavaWrapper")) - End Try - End If - If Not IsWrapperWritten Then Throw New FileNotFoundException("释放 Java Wrapper 失败,请查看 PCL 日志查找详细信息") + Try + WriteFile(WrapperPath, GetResources("JavaWrapper")) + Catch ex As Exception + If File.Exists(WrapperPath) Then + '因为未知原因 Java Wrapper 可能变为只读文件(#4243) + Log(ex, "Java Wrapper 文件释放失败,但文件已存在,将在删除后尝试重新生成", LogLevel.Developer) + Try + File.Delete(WrapperPath) + WriteFile(WrapperPath, GetResources("JavaWrapper")) + Catch ex2 As Exception + Log(ex2, "Java Wrapper 文件重新释放失败,将尝试更换文件名重新生成", LogLevel.Developer) + WrapperPath = BaseDir & "\JavaWrapper2.jar" + Try + WriteFile(WrapperPath, GetResources("JavaWrapper")) + Catch ex3 As Exception + Throw New FileNotFoundException("释放 Java Wrapper 最终尝试失败", ex3) + End Try + End Try + Else + Throw New FileNotFoundException("释放 Java Wrapper 失败", ex) + End If + End Try End SyncLock Return WrapperPath End Function diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb index f21c764f..0c3ea340 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb @@ -707,7 +707,7 @@ Finished: End If '读取文件 Dim data As New List(Of Byte) - For Each b As Byte In File.ReadAllBytes(Path) + For Each b As Byte In ReadFileBytes(Path) If b = 9 OrElse b = 10 OrElse b = 13 OrElse b = 32 Then Continue For data.Add(b) Next @@ -1130,13 +1130,13 @@ Finished: Mods = Mods.Where(Function(m) m.Comp IsNot Nothing).ToList() Log($"[Mod] 联网获取本地 Mod 信息完成,为 {Mods.Count} 个 Mod 更新缓存") If Not Mods.Any() Then Exit Sub - For Each Entry In Mods 'TODO: Bookshelf 的 Logo 会在两个网站间横跳 + For Each Entry In Mods Entry.CompLoaded = SucceedThreadCount = 2 Cache(Entry.ModrinthHash & McVersion & ModLoaders.Join("")) = Entry.ToJson() Next WriteFile(PathTemp & "Cache\LocalMod.json", Cache.ToString(If(ModeDebug, Newtonsoft.Json.Formatting.Indented, Newtonsoft.Json.Formatting.None))) - '刷新下边栏(#4377) - RunInUi(Sub() FrmVersionMod?.RefreshBottomBar()) + '刷新边栏 + RunInUi(Sub() FrmVersionMod?.RefreshBars()) End Sub Private Function GetTargetModLoaders() As List(Of CompModLoaderType) Dim ModLoaders As New List(Of CompModLoaderType) diff --git a/Plain Craft Launcher 2/Modules/ModSecret.vb b/Plain Craft Launcher 2/Modules/ModSecret.vb index 593a0e99..00b26c0e 100644 --- a/Plain Craft Launcher 2/Modules/ModSecret.vb +++ b/Plain Craft Launcher 2/Modules/ModSecret.vb @@ -98,8 +98,10 @@ Friend Module ModSecret ''' ''' 设置 Headers 的 UA、Referer。 ''' - Friend Sub SecretHeadersSign(Url As String, ByRef Client As CookieWebClient, Optional UseBrowserUserAgent As Boolean = False) - If UseBrowserUserAgent Then + Friend Sub SecretHeadersSign(Url As String, ByRef Client As WebClient, Optional UseBrowserUserAgent As Boolean = False) + If Url.Contains("modrinth.com") Then '根据 #4334,不添加 PCL 的 UA 反而能正常访问 + Client.Headers("User-Agent") = "Mozilla/5.0 AppleWebKit/537.36 Chrome/63.0.3239.132 Safari/537.36" + ElseIf UseBrowserUserAgent Then Client.Headers("User-Agent") = "PCL2/" & VersionStandardCode & " Mozilla/5.0 AppleWebKit/537.36 Chrome/63.0.3239.132 Safari/537.36" Else Client.Headers("User-Agent") = "PCL2/" & VersionStandardCode @@ -111,7 +113,9 @@ Friend Module ModSecret ''' 设置 Headers 的 UA、Referer。 ''' Friend Sub SecretHeadersSign(Url As String, ByRef Request As HttpWebRequest, Optional UseBrowserUserAgent As Boolean = False) - If UseBrowserUserAgent Then + If Url.Contains("modrinth.com") Then '根据 #4334,不添加 PCL 的 UA 反而能正常访问 + Request.UserAgent = "Mozilla/5.0 AppleWebKit/537.36 Chrome/63.0.3239.132 Safari/537.36" + ElseIf UseBrowserUserAgent Then Request.UserAgent = "PCL2/" & VersionStandardCode & " Mozilla/5.0 AppleWebKit/537.36 Chrome/63.0.3239.132 Safari/537.36" Else Request.UserAgent = "PCL2/" & VersionStandardCode diff --git a/Plain Craft Launcher 2/My Project/AssemblyInfo.vb b/Plain Craft Launcher 2/My Project/AssemblyInfo.vb index 6fc9adbe..1f07bf58 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 7302f5dc..1cd46b99 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb +++ b/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb @@ -2021,7 +2021,7 @@ Sub(Task As LoaderTask(Of List(Of NetFile), Boolean)) If Request.LiteLoaderEntry IsNot Nothing Then LiteLoaderFolder = TempMcFolder & "versions\" & Request.MinecraftName & "-LiteLoader" '判断 OptiFine 是否作为 Mod 进行下载 - Dim MinecraftCode As Integer = If(Request.MinecraftName.Contains("."), Request.MinecraftName.Split(".")(1), 99) + Dim MinecraftCode As Integer = If(Request.MinecraftName.Contains("."), Val(Request.MinecraftName.Split(".")(1)), 0) Dim OptiFineAsMod As Boolean = Request.OptiFineEntry IsNot Nothing AndAlso '1. 选择了 OptiFine (Request.FabricVersion IsNot Nothing OrElse '2. 选择了 Fabric... (Request.ForgeEntry IsNot Nothing AndAlso MinecraftCode >= 14 AndAlso MinecraftCode <= 15)) '...或者 Forge 1.14~15(#4134) diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadNeoForge.xaml b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadNeoForge.xaml index 236af697..886c3437 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadNeoForge.xaml +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadNeoForge.xaml @@ -8,7 +8,7 @@ - + diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb index 5efe384c..09943790 100644 --- a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb @@ -102,6 +102,10 @@ Download: Log("[Page] 主页预设:Minecraft 皮肤推荐") Url = "https://forgepixel.com/pcl_sub_file" GoTo Download + Case 6 + Log("[Page] 主页预设:OpenBMCLAPI 仪表盘 Lite") + Url = "https://pcl-bmcl.milulu.xyz/" + GoTo Download End Select End Select RunInUi(Sub() LoadContent(Content)) diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMsSkin.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMsSkin.xaml.vb index 15a35813..b3f47ae0 100644 --- a/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMsSkin.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMsSkin.xaml.vb @@ -99,7 +99,7 @@ Retry: Client.DefaultRequestHeaders.UserAgent.Add(New Net.Http.Headers.ProductInfoHeaderValue("MojangSharp", "0.1")) Dim Contents As New Net.Http.MultipartFormDataContent From { {New Net.Http.StringContent(If(SkinInfo.IsSlim, "slim", "classic")), "variant"}, - {New Net.Http.ByteArrayContent(File.ReadAllBytes(SkinInfo.LocalFile)), "file", GetFileNameFromPath(SkinInfo.LocalFile)} + {New Net.Http.ByteArrayContent(ReadFileBytes(SkinInfo.LocalFile)), "file", GetFileNameFromPath(SkinInfo.LocalFile)} } Dim Result As String = Await (Await Client.PostAsync(New Uri("https://api.minecraftservices.com/minecraft/profile/skins"), Contents)).Content.ReadAsStringAsync If Result.Contains("request requires user authentication") Then diff --git a/Plain Craft Launcher 2/Pages/PageSetup/ModSetup.vb b/Plain Craft Launcher 2/Pages/PageSetup/ModSetup.vb index 5caddaed..1e335a0a 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/ModSetup.vb +++ b/Plain Craft Launcher 2/Pages/PageSetup/ModSetup.vb @@ -107,7 +107,7 @@ {"ToolDownloadTranslate", New SetupEntry(0, Source:=SetupSource.Registry)}, {"ToolDownloadKeepModpack", New SetupEntry(False, Source:=SetupSource.Registry)}, {"ToolDownloadIgnoreQuilt", New SetupEntry(True, Source:=SetupSource.Registry)}, - {"ToolDownloadCert", New SetupEntry(True, Source:=SetupSource.Registry)}, + {"ToolDownloadCert", New SetupEntry(False, Source:=SetupSource.Registry)}, {"ToolDownloadMod", New SetupEntry(1, Source:=SetupSource.Registry)}, {"ToolUpdateAlpha", New SetupEntry(0, Source:=SetupSource.Registry, Encoded:=True)}, {"ToolUpdateRelease", New SetupEntry(False, Source:=SetupSource.Registry)}, diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml index 4f95fe3c..20ac75c5 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml +++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml @@ -66,7 +66,7 @@ ToolTip="下载 Mod 时,若该 Mod 支持 Forge 和 Fabric,则显示为支持任意 Mod 加载器。 例如,开启时 JEI 显示支持全版本,关闭后 JEI 就会显示仅支持 Forge / Fabric 全版本。" /> + ToolTip="开启验证会提高安全性、降低盗号风险(见 #2767),但也可能导致正版登录失败(见 #3018)。" /> diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml index 7f6436ba..4f6e7f79 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml +++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml @@ -242,6 +242,7 @@ + diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionLeft.xaml.vb b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionLeft.xaml.vb index 9f33fed5..99e211fa 100644 --- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionLeft.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionLeft.xaml.vb @@ -96,7 +96,7 @@ Catch ex As Exception Log(ex, "强制刷新时清理本地 Mod 信息缓存失败") End Try - If FrmVersionMod IsNot Nothing Then FrmVersionMod.RefreshList(True) '无需 Else,还没加载刷个鬼的新 + If FrmVersionMod IsNot Nothing Then FrmVersionMod.ReloadModList(True) '无需 Else,还没加载刷个鬼的新 ItemMod.Checked = True Hint("正在刷新……", Log:=False) End Sub diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml index d5b72852..854458ef 100644 --- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml +++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml @@ -7,8 +7,7 @@ - - + @@ -24,19 +23,15 @@ - - - - - - - - - - - + + + + + + + - + diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb index edd7e93b..0bf1a50c 100644 --- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb @@ -4,16 +4,11 @@ Private IsLoad As Boolean = False Public Sub PageOther_Loaded() Handles Me.Loaded - BtnTypeAll.Tag = ViewType.All - BtnTypeEnabled.Tag = ViewType.Enabled - BtnTypeDisabled.Tag = ViewType.Disabled - BtnTypeCanUpdate.Tag = ViewType.CanUpdate - BtnTypeError.Tag = ViewType.InError If FrmMain.PageLast.Page <> FormMain.PageType.CompDetail Then PanBack.ScrollToHome() AniControlEnabled += 1 SelectedMods.Clear() - RefreshList() + ReloadModList() ChangeAllSelected(False) AniControlEnabled -= 1 @@ -21,6 +16,11 @@ If IsLoad Then Exit Sub IsLoad = True + '调整按钮边距(这玩意儿没法从 XAML 改) + For Each Btn As MyRadioButton In PanFilter.Children + Btn.LabText.Margin = New Thickness(-2, 0, 8, 0) + Next + #If DEBUG Then BtnManageCheck.Visibility = Visibility.Visible #End If @@ -29,11 +29,10 @@ ''' ''' 刷新 Mod 列表。 ''' - Public Sub RefreshList(Optional ForceReload As Boolean = False) + Public Sub ReloadModList(Optional ForceReload As Boolean = False) If LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", If(ForceReload, LoaderFolderRunType.ForceRun, LoaderFolderRunType.RunOnUpdated)) Then Log("[System] 已刷新 Mod 列表") - ViewModType = ViewType.All - BtnTypeAll.Checked = True + Filter = FilterType.All PanBack.ScrollToHome() SearchBox.Text = "" End If @@ -60,10 +59,9 @@ ''' 将加载器结果的 Mod 列表加载为 UI。 ''' Private Sub LoadUIFromLoaderOutput() - Dim Mods As List(Of McMod) = McModLoader.Output Try '判断应该显示哪一个页面 - If Mods.Any() Then + If McModLoader.Output.Any() Then PanBack.Visibility = Visibility.Visible PanEmpty.Visibility = Visibility.Collapsed Else @@ -71,13 +69,15 @@ PanBack.Visibility = Visibility.Collapsed Exit Sub End If - '输出结果 + '修改缓存 ModItems.Clear() - For Each ModEntity As McMod In Mods + For Each ModEntity As McMod In McModLoader.Output ModItems(ModEntity.RawFileName) = McModListItem(ModEntity) Next + '显示结果 + Filter = FilterType.All SearchBox.Text = "" '这会触发结果刷新,所以需要在 ModItems 更新之后,详见 #3124 的视频 - RefreshResult(Mods) + RefreshUI() Catch ex As Exception Log(ex, "加载 Mod 列表 UI 失败", LogLevel.Feedback) End Try @@ -129,85 +129,137 @@ End Sub ''' - ''' 刷新结果显示。 + ''' 刷新整个 UI。 ''' - Private Sub RefreshResult(Mods As List(Of McMod)) + Public Sub RefreshUI() If PanList Is Nothing Then Exit Sub - Dim ShowMods As List(Of McMod) = New List(Of McMod) - Select Case ViewModType - Case ViewType.All - ShowMods = Mods - Case ViewType.Enabled - For Each Item In Mods - If Item.State.Equals(McMod.McModState.Fine) Then ShowMods.Add(Item) - Next - Case ViewType.Disabled - For Each Item In Mods - If Item.State.Equals(McMod.McModState.Disabled) Then ShowMods.Add(Item) - Next - Case ViewType.CanUpdate - For Each Item In Mods - If Item.CanUpdate Then ShowMods.Add(Item) - Next - Case ViewType.InError - For Each Item In Mods - If Item.State.Equals(McMod.McModState.Unavaliable) Then ShowMods.Add(Item) - Next - End Select - PanList.Children.Clear() - For Each TargetMod In ShowMods - PanList.Children.Add(ModItems(TargetMod.RawFileName)) - Next - Dim ModEnabled As Integer = 0 - Dim ModDisabled As Integer = 0 - Dim ModCanUpdate As Integer = 0 - Dim ModError As Integer = 0 - For Each ModItem In ModItems - If ModItem.Value.Entry.CanUpdate Then - ModCanUpdate += 1 - End If - If ModItem.Value.Entry.State.Equals(McMod.McModState.Fine) Then - ModEnabled += 1 - End If - If ModItem.Value.Entry.State.Equals(McMod.McModState.Disabled) Then - ModDisabled += 1 - End If - If ModItem.Value.Entry.State.Equals(McMod.McModState.Unavaliable) Then - ModError += 1 - End If - Next - BtnTypeAll.Text = $"全部 ({ModEnabled + ModDisabled + ModError}) " - BtnTypeCanUpdate.Text = $"可更新 ({ModCanUpdate}) " - BtnTypeEnabled.Text = $"已启用 ({ModEnabled}) " - BtnTypeDisabled.Text = $"已禁用 ({ModDisabled}) " - BtnTypeError.Text = $"错误 ({ModError}) " - RefreshTitle() + Dim ShowMods = GetShowingMods(True).ToList() + '重新列出列表 + AniControlEnabled += 1 + If ShowMods.Any() Then + PanList.Visibility = Visibility.Visible + PanList.Children.Clear() + For Each TargetMod In ShowMods + Dim Item As MyLocalModItem = ModItems(TargetMod.RawFileName) + Item.Checked = SelectedMods.Contains(TargetMod.RawFileName) '更新选中状态 + PanList.Children.Add(Item) + Next + Else + PanList.Visibility = Visibility.Collapsed + End If + AniControlEnabled -= 1 + SelectedMods = SelectedMods.Where(Function(m) ShowMods.Any(Function(s) s.RawFileName = m)).ToList '取消选中已经不显示的 Mod + RefreshBars() End Sub + ''' - ''' 刷新卡片标题。 + ''' 刷新顶栏和底栏显示。 ''' - Private Sub RefreshTitle() - If Not IsSearching Then - PanListBack.Title = "Mod 列表 - " - ElseIf PanList.Children.Count > 0 Then - PanListBack.Title = "搜索结果 - " + Public Sub RefreshBars() + '----------------- + ' 顶部栏 + '----------------- + + '计数 + Dim AnyCount As Integer = 0 + Dim EnabledCount As Integer = 0 + Dim DisabledCount As Integer = 0 + Dim UpdateCount As Integer = 0 + Dim UnavalialeCount As Integer = 0 + For Each ModItem In GetShowingMods(False) + AnyCount += 1 + If ModItem.CanUpdate Then UpdateCount += 1 + If ModItem.State.Equals(McMod.McModState.Fine) Then EnabledCount += 1 + If ModItem.State.Equals(McMod.McModState.Disabled) Then DisabledCount += 1 + If ModItem.State.Equals(McMod.McModState.Unavaliable) Then UnavalialeCount += 1 + Next + '显示 + BtnFilterAll.Text = If(IsSearching, "搜索结果", "全部") & $" ({AnyCount})" + BtnFilterCanUpdate.Text = $"可更新 ({UpdateCount})" + BtnFilterCanUpdate.Visibility = If(Filter = FilterType.CanUpdate OrElse + UpdateCount > 0, Visibility.Visible, Visibility.Collapsed) + BtnFilterEnabled.Text = $"启用 ({EnabledCount})" + BtnFilterEnabled.Visibility = If(Filter = FilterType.Enabled OrElse Filter = FilterType.Disabled OrElse + EnabledCount > 0 AndAlso EnabledCount <> AnyCount, Visibility.Visible, Visibility.Collapsed) + BtnFilterDisabled.Text = $"禁用 ({DisabledCount})" + BtnFilterDisabled.Visibility = If(Filter = FilterType.Enabled OrElse Filter = FilterType.Disabled OrElse + DisabledCount > 0, Visibility.Visible, Visibility.Collapsed) + BtnFilterError.Text = $"错误 ({UnavalialeCount})" + BtnFilterError.Visibility = If(Filter = FilterType.Unavaliable OrElse + UnavalialeCount > 0, Visibility.Visible, Visibility.Collapsed) + + '----------------- + ' 底部栏 + '----------------- + + '计数 + Dim NewCount As Integer = SelectedMods.Count + Dim Selected = NewCount > 0 + If Selected Then LabSelect.Text = $"已选择 {NewCount} 个文件" '取消所有选择时不更新数字 + '按钮可用性 + If Selected Then + Dim HasUpdate As Boolean = False + Dim HasEnabled As Boolean = False + Dim HasDisabled As Boolean = False + For Each ModEntity In McModLoader.Output + If SelectedMods.Contains(ModEntity.RawFileName) Then + If ModEntity.CanUpdate Then HasUpdate = True + If ModEntity.State = McMod.McModState.Fine Then + HasEnabled = True + ElseIf ModEntity.State = McMod.McModState.Disabled Then + HasDisabled = True + End If + End If + Next + BtnSelectDisable.IsEnabled = HasEnabled + BtnSelectEnable.IsEnabled = HasDisabled + BtnSelectUpdate.IsEnabled = HasUpdate + End If + '更新显示状态 + If AniControlEnabled = 0 Then + PanListBack.Margin = New Thickness(0, 0, 0, If(Selected, 95, 15)) + If Selected Then + '仅在数量增加时播放出现/跳跃动画 + If BottomBarShownCount >= NewCount Then + BottomBarShownCount = NewCount + Return + Else + BottomBarShownCount = NewCount + End If + '出现/跳跃动画 + CardSelect.Visibility = Visibility.Visible + AniStart({ + AaOpacity(CardSelect, 1 - CardSelect.Opacity, 60), + AaTranslateY(CardSelect, -27 - TransSelect.Y, 120, Ease:=New AniEaseOutFluent(AniEasePower.Weak)), + AaTranslateY(CardSelect, 3, 150, 120, Ease:=New AniEaseInoutFluent(AniEasePower.Weak)), + AaTranslateY(CardSelect, -1, 90, 270, Ease:=New AniEaseInoutFluent(AniEasePower.Weak)) + }, "Mod Sidebar") + Else + '不重复播放隐藏动画 + If BottomBarShownCount = 0 Then Return + BottomBarShownCount = 0 + '隐藏动画 + AniStart({ + AaOpacity(CardSelect, -CardSelect.Opacity, 90), + AaTranslateY(CardSelect, -10 - TransSelect.Y, 90, Ease:=New AniEaseInFluent(AniEasePower.Weak)), + AaCode(Sub() CardSelect.Visibility = Visibility.Collapsed, After:=True) + }, "Mod Sidebar") + End If Else - PanListBack.Title = "无搜索结果 - " + AniStop("Mod Sidebar") + BottomBarShownCount = NewCount + If Selected Then + CardSelect.Visibility = Visibility.Visible + CardSelect.Opacity = 1 + TransSelect.Y = -25 + Else + CardSelect.Visibility = Visibility.Collapsed + CardSelect.Opacity = 0 + TransSelect.Y = -10 + End If End If - Select Case ViewModType - Case ViewType.All - PanListBack.Title += BtnTypeAll.Text - Case ViewType.Enabled - PanListBack.Title += BtnTypeEnabled.Text - Case ViewType.Disabled - PanListBack.Title += BtnTypeDisabled.Text - Case ViewType.CanUpdate - PanListBack.Title += BtnTypeCanUpdate.Text - Case ViewType.InError - PanListBack.Title += BtnTypeError.Text - End Select - PanList.Visibility = If(PanList.Children.Count > 0, Visibility.Visible, Visibility.Collapsed) End Sub + Private BottomBarShownCount As Integer = 0 #End Region @@ -247,15 +299,7 @@ ''' 全选。 ''' Private Sub BtnManageSelectAll_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnManageSelectAll.Click - Dim CurrentSelected As Integer = 0 - For Each Item In PanList.Children.OfType(Of MyLocalModItem).ToList - If Item.Checked Then CurrentSelected += 1 - Next - If CurrentSelected < PanList.Children.Count Then - ChangeCurrentSelected(True) - Else - ChangeCurrentSelected(False) - End If + ChangeAllSelected(SelectedMods.Count < GetShowingMods(True).Count) End Sub ''' @@ -282,110 +326,20 @@ Else SelectedMods.Remove(SelectedKey) End If - '更新下边栏 UI - RefreshBottomBar() - End Sub - - '改变下边栏状态 - Private ShownCount As Integer = 0 - Public Sub RefreshBottomBar() - '计数 - Dim NewCount As Integer = SelectedMods.Count - Dim Selected = NewCount > 0 - If Selected Then LabSelect.Text = $"已选择 {NewCount} 个文件" '取消所有选择时不更新数字 - '按钮可用性 - If Selected Then - Dim HasUpdate As Boolean = False - Dim HasEnabled As Boolean = False - Dim HasDisabled As Boolean = False - For Each ModEntity In McModLoader.Output - If SelectedMods.Contains(ModEntity.RawFileName) Then - If ModEntity.CanUpdate Then HasUpdate = True - If ModEntity.State = McMod.McModState.Fine Then - HasEnabled = True - ElseIf ModEntity.State = McMod.McModState.Disabled Then - HasDisabled = True - End If - End If - Next - BtnSelectDisable.IsEnabled = HasEnabled - BtnSelectEnable.IsEnabled = HasDisabled - BtnSelectUpdate.IsEnabled = HasUpdate - End If - '更新显示状态 - If AniControlEnabled = 0 Then - If Selected Then - PanListBack.Margin = New Thickness(0, 0, 0, 95) - '仅在数量增加时播放出现/跳跃动画 - If ShownCount >= NewCount Then - ShownCount = NewCount - Return - Else - ShownCount = NewCount - End If - '出现/跳跃动画 - CardSelect.Visibility = Visibility.Visible - AniStart({ - AaOpacity(CardSelect, 1 - CardSelect.Opacity, 60), - AaTranslateY(CardSelect, -27 - TransSelect.Y, 120, Ease:=New AniEaseOutFluent(AniEasePower.Weak)), - AaTranslateY(CardSelect, 3, 150, 120, Ease:=New AniEaseInoutFluent(AniEasePower.Weak)), - AaTranslateY(CardSelect, -1, 90, 270, Ease:=New AniEaseInoutFluent(AniEasePower.Weak)) - }, "Mod Sidebar") - Else - PanListBack.Margin = New Thickness(0, 0, 0, 15) - '不重复播放隐藏动画 - If ShownCount = 0 Then Return - ShownCount = 0 - '隐藏动画 - AniStart({ - AaOpacity(CardSelect, -CardSelect.Opacity, 90), - AaTranslateY(CardSelect, -10 - TransSelect.Y, 90, Ease:=New AniEaseInFluent(AniEasePower.Weak)), - AaCode(Sub() CardSelect.Visibility = Visibility.Collapsed, After:=True) - }, "Mod Sidebar") - End If - Else - AniStop("Mod Sidebar") - ShownCount = NewCount - If Selected Then - CardSelect.Visibility = Visibility.Visible - CardSelect.Opacity = 1 - TransSelect.Y = -25 - Else - CardSelect.Visibility = Visibility.Collapsed - CardSelect.Opacity = 0 - TransSelect.Y = -10 - End If - End If + RefreshBars() End Sub '切换所有项的选择状态 Private Sub ChangeAllSelected(Value As Boolean) AniControlEnabled += 1 SelectedMods.Clear() - For Each Item As MyLocalModItem In ModItems.Values.ToList + For Each Item As MyLocalModItem In GetShowingMods(True).Select(Function(m) ModItems(m.RawFileName)) Item.Checked = Value If Value Then SelectedMods.Add(Item.Entry.RawFileName) Next AniControlEnabled -= 1 - '更新下边栏 UI - RefreshBottomBar() + RefreshBars() End Sub - - Private Sub ChangeCurrentSelected(Value As Boolean) - AniControlEnabled += 1 - For Each Item As MyLocalModItem In PanList.Children - Item.Checked = Value - If Value Then - If Not SelectedMods.Contains(Item.Entry.RawFileName) Then SelectedMods.Add(Item.Entry.RawFileName) - Else - If SelectedMods.Contains(Item.Entry.RawFileName) Then SelectedMods.Remove(Item.Entry.RawFileName) - End If - Next - AniControlEnabled -= 1 - '更新下边栏 UI - RefreshBottomBar() - End Sub - Private Sub UnselectedAllWithAnimation() Handles Load.StateChanged, Me.PageExit Dim CacheAniControlEnabled = AniControlEnabled AniControlEnabled = 0 @@ -393,26 +347,76 @@ AniControlEnabled += CacheAniControlEnabled End Sub Private Sub PageVersionMod_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown - If My.Computer.Keyboard.CtrlKeyDown AndAlso e.Key = Key.A Then ChangeCurrentSelected(True) + If My.Computer.Keyboard.CtrlKeyDown AndAlso e.Key = Key.A Then ChangeAllSelected(True) End Sub - Private ViewModType As ViewType = ViewType.All +#End Region + +#Region "筛选" - Private Enum ViewType As Integer - All - Enabled - Disabled - CanUpdate - InError + Private _Filter As FilterType = FilterType.All + Private Property Filter As FilterType + Get + Return _Filter + End Get + Set(value As FilterType) + If _Filter = value Then Return + _Filter = value + Select Case value + Case FilterType.All + BtnFilterAll.Checked = True + Case FilterType.Enabled + BtnFilterEnabled.Checked = True + Case FilterType.Disabled + BtnFilterDisabled.Checked = True + Case FilterType.CanUpdate + BtnFilterCanUpdate.Checked = True + Case Else + BtnFilterError.Checked = True + End Select + RefreshUI() + End Set + End Property + Private Enum FilterType As Integer + All = 0 + Enabled = 1 + Disabled = 2 + CanUpdate = 3 + Unavaliable = 4 End Enum - Private Sub ChangeViewType(sender As MyRadioButton, raiseByMouse As Boolean) Handles BtnTypeAll.Check, BtnTypeCanUpdate.Check, BtnTypeDisabled.Check, BtnTypeEnabled.Check, BtnTypeError.Check - ViewModType = sender.Tag - If IsSearching Then - SearchRun() - Else - RefreshResult(McModLoader.Output) - End If + ''' + ''' 获取所有应该显示在 UI 中的 Mod。 + ''' + Private Function GetShowingMods(ApplyFilter As Boolean) As IEnumerable(Of McMod) + If McModLoader.Output Is Nothing Then Return New List(Of McMod) + Return If(IsSearching, SearchResult, McModLoader.Output).Where(Function(m) Not ApplyFilter OrElse CanPassFilter(m)) + End Function + + ''' + ''' 检查该 Mod 项是否符合当前筛选的类别。 + ''' + Private Function CanPassFilter(CheckingMod As McMod) As Boolean + Select Case Filter + Case FilterType.All + Return True + Case FilterType.Enabled + Return CheckingMod.State = McMod.McModState.Fine + Case FilterType.Disabled + Return CheckingMod.State = McMod.McModState.Disabled + Case FilterType.CanUpdate + Return CheckingMod.CanUpdate + Case FilterType.Unavaliable + Return CheckingMod.State = McMod.McModState.Unavaliable + Case Else + Return False + End Select + End Function + + '点击筛选项触发的改变 + Private Sub ChangeFilter(sender As MyRadioButton, raiseByMouse As Boolean) Handles BtnFilterAll.Check, BtnFilterCanUpdate.Check, BtnFilterDisabled.Check, BtnFilterEnabled.Check, BtnFilterError.Check + Filter = sender.Tag + RefreshUI() End Sub #End Region @@ -424,7 +428,6 @@ EDMods(McModLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawFileName)), Not sender.Equals(BtnSelectDisable)) ChangeAllSelected(False) - RefreshResult(McModLoader.Output) End Sub Private Sub EDMods(ModList As IEnumerable(Of McMod), IsEnable As Boolean) Dim IsSuccessful As Boolean = True @@ -459,7 +462,7 @@ FileSystem.Rename(ModEntity.Path, NewPath) Catch ex As FileNotFoundException Log(ex, $"未找到需要重命名的 Mod({If(ModEntity.Path, "null")})", LogLevel.Feedback) - RefreshList(True) + ReloadModList(True) Return Catch ex As Exception Log(ex, $"重命名 Mod 失败({If(ModEntity.Path, "null")})") @@ -479,12 +482,11 @@ PanList.Children.RemoveAt(IndexOfUi) PanList.Children.Insert(IndexOfUi, NewItem) Next - RefreshTitle() '改变数量显示 - If Not IsSuccessful Then - Hint("由于文件被占用,Mod 的状态切换失败,请尝试关闭正在运行的游戏后再试!", HintType.Critical) - RefreshList(True) + If IsSuccessful Then + RefreshBars() Else - RefreshBottomBar() + Hint("由于文件被占用,Mod 的状态切换失败,请尝试关闭正在运行的游戏后再试!", HintType.Critical) + ReloadModList(True) End If End Sub @@ -601,7 +603,7 @@ LoaderTaskbarAdd(Loader) FrmMain.BtnExtraDownload.ShowRefresh() FrmMain.BtnExtraDownload.Ribble() - RefreshList(True) + ReloadModList(True) Catch ex As Exception Log(ex, "初始化 Mod 更新失败") End Try @@ -635,7 +637,7 @@ End If Catch ex As OperationCanceledException Log(ex, "删除 Mod 被主动取消") - RefreshList(True) + ReloadModList(True) Return Catch ex As Exception Log(ex, $"删除 Mod 失败({ModEntity.Path})", LogLevel.Msgbox) @@ -649,14 +651,14 @@ Dim IndexOfUi As Integer = PanList.Children.IndexOf(PanList.Children.OfType(Of MyLocalModItem).FirstOrDefault(Function(i) i.Entry.Equals(ModEntity))) If IndexOfUi >= 0 Then PanList.Children.RemoveAt(IndexOfUi) Next - RefreshTitle() + RefreshBars() If Not IsSuccessful Then Hint("由于文件被占用,Mod 删除失败,请尝试关闭正在运行的游戏后再试!", HintType.Critical) - RefreshList(True) + ReloadModList(True) ElseIf PanList.Children.Count = 0 Then - RefreshList(True) '删除了全部文件 + ReloadModList(True) '删除了全部文件 Else - RefreshBottomBar() + RefreshBars() End If '显示结果提示 If Not IsSuccessful Then Exit Sub @@ -675,10 +677,10 @@ End If Catch ex As OperationCanceledException Log(ex, "删除 Mod 被主动取消") - RefreshList(True) + ReloadModList(True) Catch ex As Exception Log(ex, "删除 Mod 出现未知错误", LogLevel.Feedback) - RefreshList(True) + ReloadModList(True) End Try End Sub @@ -791,8 +793,8 @@ Return Not String.IsNullOrWhiteSpace(SearchBox.Text) End Get End Property + Private SearchResult As List(Of McMod) Public Sub SearchRun() Handles SearchBox.TextChanged - ChangeAllSelected(False) If IsSearching Then '构造请求 Dim QueryList As New List(Of SearchEntry(Of McMod)) @@ -815,12 +817,9 @@ QueryList.Add(New SearchEntry(Of McMod) With {.Item = Entry, .SearchSource = SearchSource}) Next '进行搜索 - Dim SearchResult = Search(QueryList, SearchBox.Text, MaxBlurCount:=6, MinBlurSimilarity:=0.35) - RefreshResult(SearchResult.Select(Function(r) r.Item).ToList) - Else - '退出搜索状态 - RefreshResult(McModLoader.Output) + SearchResult = Search(QueryList, SearchBox.Text, MaxBlurCount:=6, MinBlurSimilarity:=0.35).Select(Function(r) r.Item).ToList End If + RefreshUI() End Sub #End Region diff --git a/Plain Craft Launcher 2/Resources/ModData.txt b/Plain Craft Launcher 2/Resources/ModData.txt index 91cb6ced..89b8ba92 100644 --- a/Plain Craft Launcher 2/Resources/ModData.txt +++ b/Plain Craft Launcher 2/Resources/ModData.txt @@ -3234,7 +3234,7 @@ memory-cleaner-mod|内存自动清理* ore-flowers|矿物花卉* aqua-creepers|水下苦力怕 (Aqua Creepers!) extrastorage|更多存储 (ExtraStorage) -thermal-locomotion@thermal-integration|热力运输* +thermal-locomotion|热力运输* external-tweaker morebiomesxl notes@|笔记* @@ -3821,7 +3821,7 @@ cat-jammies rhino@|犀牛* fastsuite|配方性能优化 (FastSuite) twerkitmeal|跳舞生长一切 (Twerk To Grow All The Things) -thermal-integration@thermal-locomotion|热力集成* +thermal-integration|热力集成* piggybanks|存钱罐* in-control@ mystical-agriculture-refabricated|神秘农业:Fabric版 (Mystical Agriculture: Refabricated)