diff --git a/.github/ISSUE_TEMPLATE/bug1.yml b/.github/ISSUE_TEMPLATE/bug1.yml index 68cd2baec..af235f15c 100644 --- a/.github/ISSUE_TEMPLATE/bug1.yml +++ b/.github/ISSUE_TEMPLATE/bug1.yml @@ -12,7 +12,7 @@ body: required: false - label: "**我已尝试使用 HMCL 启动,HMCL 没有出现问题。** 如果 HMCL 也无法启动就不是 PCL 导致的问题,请 **不要** 提交反馈。[下载 HMCL](https://hmcl.huangyuhui.net/download)" required: true - - label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一 Bug 未被提交过。" + - label: "我已在 [Issues 页面](https://github.com/Meloong-Git/PCL/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Meloong-Git/PCL/discussions/1930) 中搜索,确认了这一 Bug 未被提交过。" required: true - type: textarea id: "yml-2" diff --git a/.github/ISSUE_TEMPLATE/bug2.yml b/.github/ISSUE_TEMPLATE/bug2.yml index de636f075..0490f6138 100644 --- a/.github/ISSUE_TEMPLATE/bug2.yml +++ b/.github/ISSUE_TEMPLATE/bug2.yml @@ -12,7 +12,7 @@ body: required: false - label: "我知晓大多数此类问题都是网络环境不佳导致的,但我确实认为我的问题可能是 PCL 导致的,和网络环境无关。" required: true - - label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一 Bug 未被提交过。" + - label: "我已在 [Issues 页面](https://github.com/Meloong-Git/PCL/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Meloong-Git/PCL/discussions/1930) 中搜索,确认了这一 Bug 未被提交过。" required: true - type: textarea id: "yml-2" diff --git a/.github/ISSUE_TEMPLATE/bug9.yml b/.github/ISSUE_TEMPLATE/bug9.yml index be20097db..73980a7b6 100644 --- a/.github/ISSUE_TEMPLATE/bug9.yml +++ b/.github/ISSUE_TEMPLATE/bug9.yml @@ -8,7 +8,7 @@ body: label: "检查项" description: "请逐个检查下列项目,并勾选确认。" options: - - label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一 Bug 未被提交过。" + - label: "我已在 [Issues 页面](https://github.com/Meloong-Git/PCL/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Meloong-Git/PCL/discussions/1930) 中搜索,确认了这一 Bug 未被提交过。" required: true - type: textarea id: "yml-2" diff --git a/.github/ISSUE_TEMPLATE/ch.yml b/.github/ISSUE_TEMPLATE/ch.yml index 0ddd69304..fc3ee5d6b 100644 --- a/.github/ISSUE_TEMPLATE/ch.yml +++ b/.github/ISSUE_TEMPLATE/ch.yml @@ -8,9 +8,9 @@ body: label: "检查项" description: "请逐个检查下列项目,并勾选确认。" options: - - label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一建议未被提交过。" + - label: "我已在 [Issues 页面](https://github.com/Meloong-Git/PCL/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Meloong-Git/PCL/discussions/1930) 中搜索,确认了这一建议未被提交过。" required: true - - label: "我已查看 [功能投票页面](https://github.com/Hex-Dragon/PCL2/discussions/categories/%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8/),确认了这一建议未在投票列表中。" + - label: "我已查看 [功能投票页面](https://github.com/Meloong-Git/PCL/discussions/categories/%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8/),确认了这一建议未在投票列表中。" required: true - type: textarea id: "yml-2" diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 082755ead..3a6eed802 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,14 +1,14 @@ blank_issues_enabled: false contact_links: - name: 主页预设反馈 - url: https://github.com/Hex-Dragon/PCL2/discussions/categories/自定义主页 + url: https://github.com/Meloong-Git/PCL/discussions/categories/自定义主页 about: 提交与预设的主页(设置 → 个性化 → 主页预设)中的具体内容相关的反馈 - name: 帮助文档反馈 url: https://github.com/LTCatt/PCL2Help/issues about: 提交与 PCL 帮助文档(更多 → 帮助)中的具体内容相关的反馈 - name: 提问 - url: https://github.com/Hex-Dragon/PCL2/discussions/new?category=%E6%8F%90%E9%97%AE + url: https://github.com/Meloong-Git/PCL/discussions/new?category=%E6%8F%90%E9%97%AE about: 我想问一些 PCL 相关的问题…… - name: 讨论 - url: https://github.com/Hex-Dragon/PCL2/discussions/new?category=%E8%AE%A8%E8%AE%BA + url: https://github.com/Meloong-Git/PCL/discussions/new?category=%E8%AE%A8%E8%AE%BA about: 我想讨论一些 PCL 相关的事情…… diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml index e27452b52..f434a0aa9 100644 --- a/.github/ISSUE_TEMPLATE/feature.yml +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -8,9 +8,9 @@ body: label: "检查项" description: "请逐个检查下列项目,并勾选确认。" options: - - label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一提案未被提交过。" + - label: "我已在 [Issues 页面](https://github.com/Meloong-Git/PCL/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Meloong-Git/PCL/discussions/1930) 中搜索,确认了这一提案未被提交过。" required: true - - label: "我已查看 [功能投票页面](https://github.com/Hex-Dragon/PCL2/discussions/categories/%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8/),确认了这一提案未在投票列表中。" + - label: "我已查看 [功能投票页面](https://github.com/Meloong-Git/PCL/discussions/categories/%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8/),确认了这一提案未在投票列表中。" required: true - label: "我知晓还没做的新功能真的太多了,忙不过来,所以新功能提案几乎不会被处理,也不建议再提交新功能提案 qwq……" required: true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fbebe6ab4..5165cdbb9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,7 +38,6 @@ jobs: - name: Update Help run: | - $workpath = [System.Environment]::CurrentDirectory Set-Location .. git clone https://github.com/LTCatt/PCL2Help.git -b master --single-branch --depth 1 Set-Location PCL2Help diff --git a/LICENCE b/LICENCE index f9e821b6b..c09c9009d 100644 --- a/LICENCE +++ b/LICENCE @@ -44,6 +44,6 @@ 这些规则主要是为了阻止恶意的使用和 “山寨版” 的出现,常规、善意的使用都没啥问题的,放心吧! 如果你只是参考了一小段代码,署个名就行啦,不用担心。 -如果对具体细节有疑问,欢迎在 https://github.com/Hex-Dragon/PCL2/discussions/new?category=%E6%8F%90%E9%97%AE 发帖询问! +如果对具体细节有疑问,欢迎在 https://github.com/Meloong-Git/PCL/discussions/new?category=%E6%8F%90%E9%97%AE 发帖询问! 多谢大家啦! diff --git a/Plain Craft Launcher 2/App.config b/Plain Craft Launcher 2/App.config index 8d134eb86..d46435013 100644 --- a/Plain Craft Launcher 2/App.config +++ b/Plain Craft Launcher 2/App.config @@ -32,4 +32,4 @@ - + \ No newline at end of file diff --git a/Plain Craft Launcher 2/FormMain.xaml.vb b/Plain Craft Launcher 2/FormMain.xaml.vb index ff4de2dc0..14a2c121e 100644 --- a/Plain Craft Launcher 2/FormMain.xaml.vb +++ b/Plain Craft Launcher 2/FormMain.xaml.vb @@ -1,4 +1,5 @@ Imports System.ComponentModel +Imports System.Windows.Interop Public Class FormMain @@ -10,6 +11,16 @@ Public Class FormMain Dim FeatureList As New List(Of KeyValuePair(Of Integer, String)) '统计更新日志条目 #If BETA Then + If LastVersion < 361 Then 'Release 2.10.3 + FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复:无法安装部分使用老版本 PCL 导出的整合包")) + End If + If LastVersion < 359 Then 'Release 2.10.2 + If LastVersion >= 357 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:下载资源包、光影包时能自动跳转到对应的文件夹")) + FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:调整界面样式与动画,让整体视觉更干净,操作体验更顺滑")) + FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复:无法从 CurseForge 下载 Mod 等资源,或是安装整合包")) + FeatureCount += 28 + BugCount += 28 + End If If LastVersion < 357 Then 'Release 2.10.0 FeatureList.Add(New KeyValuePair(Of Integer, String)(5, "新增:下载资源包、光影包、数据包")) FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "新增:允许设置文件下载源")) @@ -72,6 +83,15 @@ Public Class FormMain '3:BUG+ IMP* FEAT- '2:BUG* IMP- '1:BUG- + If LastVersion < 362 Then 'Snapshot 2.10.3 + FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复:无法安装部分使用老版本 PCL 导出的整合包")) + End If + If LastVersion < 360 Then 'Snapshot 2.10.2 + FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复:无法从 CurseForge 下载 Mod 等资源,或是安装整合包")) + If LastVersion >= 358 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:无法加载正版皮肤的头像")) + FeatureCount += 3 + BugCount += 5 + End If If LastVersion < 358 Then 'Snapshot 2.10.1 If LastVersion >= 356 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:下载资源包、光影包时能自动跳转到对应的文件夹")) FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:调整界面样式与动画,让整体视觉更干净,操作体验更顺滑")) @@ -184,6 +204,8 @@ Public Class FormMain End Sub, "UpdateLog Output") End Sub + Private ReadOnly Helper As New DragFileHelper() + '窗口加载 Private IsWindowLoadFinished As Boolean = False Public Sub New() @@ -222,7 +244,11 @@ Public Class FormMain [AddHandler](DragDrop.DragOverEvent, New DragEventHandler(AddressOf HandleDrag), handledEventsToo:=True) '加载 UI InitializeComponent() + + + Opacity = 0 + '旧代码 ''开启管理员权限下的文件拖拽,但下列代码也没用(#2531) 'If IsAdmin() Then ' Log("[Start] PCL 正以管理员权限运行") @@ -230,6 +256,24 @@ Public Class FormMain ' ChangeWindowMessageFilter(&H4A, 1) ' ChangeWindowMessageFilter(&H49, 1) 'End If + + '开启管理员权限下的文件拖拽 + If IsAdmin() Then + AddHandler Me.SourceInitialized, + Sub(sender, e) + Dim wpfHelper As New WindowInteropHelper(Me) + Helper.HwndIntPtrSource = HwndSource.FromHwnd(wpfHelper.Handle) + Helper.AddHook() + End Sub + AddHandler Me.Closing, + Sub(sender, e) + Helper.RemoveDragHook() + End Sub + AddHandler Helper.DragDrop, + Sub(sender, e) + Me.FileDrag(Helper.DropFilePaths) + End Sub + End If '切换到首页 If Not IsNothing(FrmLaunchLeft.Parent) Then FrmLaunchLeft.SetValue(ContentPresenter.ContentProperty, Nothing) If Not IsNothing(FrmLaunchRight.Parent) Then FrmLaunchRight.SetValue(ContentPresenter.ContentProperty, Nothing) @@ -521,7 +565,7 @@ Public Class FormMain If ReturnCode = ProcessReturnValues.Exception Then If Not IsLogShown Then FeedbackInfo() - Log("请在 https://github.com/Hex-Dragon/PCL2/issues 提交错误报告,以便于作者解决此问题!") + Log("请在 https://github.com/Meloong-Git/PCL/issues 提交错误报告,以便于作者解决此问题!") IsLogShown = True ShellOnly(Path & "PCL\Log1.txt") End If @@ -1461,4 +1505,4 @@ Public Class FormMain lastMouseArg = e End Sub -End Class +End Class \ No newline at end of file diff --git a/Plain Craft Launcher 2/Modules/Base/ModBase.vb b/Plain Craft Launcher 2/Modules/Base/ModBase.vb index b9cfe3095..407855a07 100644 --- a/Plain Craft Launcher 2/Modules/Base/ModBase.vb +++ b/Plain Craft Launcher 2/Modules/Base/ModBase.vb @@ -13,13 +13,13 @@ Public Module ModBase #Region "声明" '下列版本信息由更新器自动修改 - Public Const VersionBaseName As String = "2.10.1" '不含分支前缀的显示用版本名 - Public Const VersionStandardCode As String = "2.10.1." & VersionBranchCode '标准格式的四段式版本号 + Public Const VersionBaseName As String = "2.10.3" '不含分支前缀的显示用版本名 + Public Const VersionStandardCode As String = "2.10.3." & VersionBranchCode '标准格式的四段式版本号 Public Const CommitHash As String = "" 'Commit Hash,由 GitHub Workflow 自动替换 #If BETA Then - Public Const VersionCode As Integer = 357 'Release + Public Const VersionCode As Integer = 361 'Release #Else - Public Const VersionCode As Integer = 358 'Snapshot + Public Const VersionCode As Integer = 362 'Snapshot #End If '自动生成的版本信息 Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName diff --git a/Plain Craft Launcher 2/Modules/Base/ModDragHelper.vb b/Plain Craft Launcher 2/Modules/Base/ModDragHelper.vb new file mode 100644 index 000000000..8db1bf5c2 --- /dev/null +++ b/Plain Craft Launcher 2/Modules/Base/ModDragHelper.vb @@ -0,0 +1,152 @@ +Imports System +Imports System.ComponentModel +Imports System.Runtime.InteropServices +Imports System.Text +Imports System.Windows.Interop + + +Public Class DragFileHelper + + Public Event DragDrop As EventHandler + + Public Property DropFilePaths As String() + Get + Return _DropFilePathsBackingField + End Get + Private Set(value As String()) + _DropFilePathsBackingField = value + End Set + End Property + Private _DropFilePathsBackingField As String() + + Public Property DropPoint As POINT + Get + Return _DropPointBackingField + End Get + Private Set(value As POINT) + _DropPointBackingField = value + End Set + End Property + Private _DropPointBackingField As POINT + + Public Property HwndIntPtrSource As HwndSource + + Public Sub AddHook() + Me.RemoveDragHook() + Me.HwndIntPtrSource.AddHook(AddressOf WndProc) + Dim handle As IntPtr = Me.HwndIntPtrSource.Handle + If IsUserAnAdmin() Then RevokeDragDrop(handle) + DragAcceptFiles(handle, True) + ChangeMessageFilter(handle) + End Sub + + Public Sub RemoveDragHook() + Me.HwndIntPtrSource.RemoveHook(AddressOf WndProc) + DragAcceptFiles(Me.HwndIntPtrSource.Handle, False) + End Sub + + Private Function WndProc(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr, ByRef handled As Boolean) As IntPtr + Dim filePaths As String() = Nothing + Dim point As POINT = New POINT() + + If TryGetDropInfo(msg, wParam, filePaths, point) Then + DropPoint = point + DropFilePaths = filePaths + RaiseEvent DragDrop(Me, EventArgs.Empty) + handled = True + End If + Return IntPtr.Zero + End Function + + + Private Shared Function ChangeWindowMessageFilterEx(ByVal hWnd As IntPtr, ByVal msg As UInteger, ByVal action As UInteger, ByRef pChangeFilterStruct As CHANGEFILTERSTRUCT) As Boolean + End Function + + + Private Shared Function ChangeWindowMessageFilter(ByVal msg As UInteger, ByVal flags As UInteger) As Boolean + End Function + + + Private Shared Sub DragAcceptFiles(ByVal hWnd As IntPtr, ByVal fAccept As Boolean) + End Sub + + + Private Shared Function DragQueryFile(ByVal hWnd As IntPtr, ByVal iFile As UInteger, ByVal lpszFile As StringBuilder, ByVal cch As Integer) As UInteger + End Function + + + Private Shared Function DragQueryPoint(ByVal hDrop As IntPtr, ByRef lppt As POINT) As Boolean + End Function + + + Private Shared Sub DragFinish(ByVal hDrop As IntPtr) + End Sub + + + Private Shared Function RevokeDragDrop(ByVal hWnd As IntPtr) As Integer + End Function + + + Private Shared Function IsUserAnAdmin() As Boolean + End Function + + + Public Structure POINT + Public X As Integer + Public Y As Integer + End Structure + + + Private Structure CHANGEFILTERSTRUCT + Public cbSize As UInteger + Public ExtStatus As UInteger + End Structure + + Private Const WM_COPYGLOBALDATA As UInteger = &H49 + Private Const WM_COPYDATA As UInteger = &H4A + Private Const WM_DROPFILES As UInteger = &H233 + Private Const MSGFLT_ALLOW As UInteger = 1 + Private Const MSGFLT_ADD As UInteger = 1 + Private Const MAX_PATH As Integer = 260 + + Private Shared Sub ChangeMessageFilter(ByVal handle As IntPtr) + Dim ver As Version = Environment.OSVersion.Version + Dim isVistaOrHigher As Boolean = ver >= New Version(6, 0) + Dim isNt61OrHiger As Boolean = ver >= New Version(6, 1) + + If isVistaOrHigher Then + Dim status As CHANGEFILTERSTRUCT = New CHANGEFILTERSTRUCT With {.cbSize = 8} + For Each msg As UInteger In New UInteger() {WM_DROPFILES, WM_COPYGLOBALDATA, WM_COPYDATA} + Dim [error] As Boolean = False + If isNt61OrHiger Then + [error] = Not ChangeWindowMessageFilterEx(handle, msg, MSGFLT_ALLOW, status) + Else + [error] = Not ChangeWindowMessageFilter(msg, MSGFLT_ADD) + End If + + If [error] Then Throw New Win32Exception(Marshal.GetLastWin32Error()) + Next + End If + End Sub + + Private Shared Function TryGetDropInfo(ByVal msg As Integer, ByVal wParam As IntPtr, ByRef dropFilePaths As String(), ByRef dropPoint As POINT) As Boolean + dropFilePaths = Nothing + dropPoint = New POINT() + + If msg <> WM_DROPFILES Then Return False + + Dim fileCount As UInteger = DragQueryFile(wParam, UInteger.MaxValue, Nothing, 0) + ReDim dropFilePaths(CInt(fileCount) - 1) + + For i As UInteger = 0 To fileCount - 1 + Dim sb As New StringBuilder(MAX_PATH) + Dim result As UInteger = DragQueryFile(wParam, i, sb, sb.Capacity) + If result > 0 Then dropFilePaths(CInt(i)) = sb.ToString() + Next + + DragQueryPoint(wParam, dropPoint) + DragFinish(wParam) + Return True + End Function + +End Class \ No newline at end of file diff --git a/Plain Craft Launcher 2/Modules/Base/ModNet.vb b/Plain Craft Launcher 2/Modules/Base/ModNet.vb index f93903a5b..f7d1b3cb1 100644 --- a/Plain Craft Launcher 2/Modules/Base/ModNet.vb +++ b/Plain Craft Launcher 2/Modules/Base/ModNet.vb @@ -1,9 +1,216 @@ -Public Module ModNet +Imports System.Drawing +Imports System.Net.Http +Imports System.Net.Http.Headers +Imports System.Threading.Tasks +Imports Newtonsoft.Json + +Public Module ModNet Public Const NetDownloadEnd As String = ".PCLDownloading" + Public ReadOnly HttpProxy As New HttpWebProxyFactory() + Private ClientFactory As New HttpClientFactory() + Private Request As New HttpRequest() + Public Class HttpRequest + Public Sub New() + End Sub + Public Function GetRequestMessage(RequestUrl As String, + Optional Method As String = "GET", + Optional Headers As Dictionary(Of String, String) = Nothing, + Optional Data As Byte() = Nothing, + Optional UseBrowserUA As Boolean = False, + Optional RequireHeaderSign As Boolean = False, + Optional RequireCdnSign As Boolean = False) As HttpRequestMessage + + Dim RequestMethod As HttpMethod = GetHttpMethod(Method) + If RequireCdnSign Then RequestUrl = SecretCdnSign(RequestUrl) + + Dim RequestMessage As New HttpRequestMessage(RequestMethod, RequestUrl) + '基本参数验证 + If Data IsNot Nothing AndAlso Not {HttpMethod.Get, HttpMethod.Head}.Contains(RequestMethod) Then + RequestMessage.Content = New ByteArrayContent(Data) + RequestMessage.Content.Headers.ContentLength = Data.Length + End If + + If Headers IsNot Nothing Then + For Each Header In Headers + If Header.Key.ContainsF("Content") AndAlso RequestMessage.Content IsNot Nothing Then + 'FixContentHeader 往下挪一行就 NullReference 了 +FixContentHeader: + If Data Is Nothing OrElse RequestMessage.Content Is Nothing Then Continue For + If RequestMessage.Content.Headers.Contains(Header.Key) Then Continue For + If Not RequestMessage.Content.Headers.Contains(Header.Key) Then + If Header.Key.ToLower() = "Content-Type" Then RequestMessage.Content.Headers.ContentType = New MediaTypeHeaderValue(Header.Value) + RequestMessage.Content.Headers.TryAddWithoutValidation(Header.Key, Header.Value) + End If + Else + '不知道为什么 Content-Type 标头会通过字符串验证然后加入不应该被加入的 HttpRequestMessage Headers + '这会导致 HttpRequestMessage 内部的标头验证失败,并抛出一个 Exception 导致网络请求失败 + If Header.Key.ContainsF("Content") Then GoTo FixContentHeader + If RequestMessage.Headers.Contains(Header.Key) Then Continue For + RequestMessage.Headers.Add(Header.Key, Header.Value) + End If + Next + End If + + If RequireHeaderSign Then + SecretHeadersSign(RequestUrl, requestMessage, UseBrowserUA) + End If + + Return RequestMessage + + End Function + + ' 发送请求 + Public Function SendRequest(Req As HttpRequestMessage, + Optional RequireDecode As Boolean = True, + Optional Encode As Encoding = Nothing, + Optional Timeout As Integer = 25000, + Optional RequireReturnRespObj As Boolean = False) + Using CTS As New CancellationTokenSource(Timeout) + Dim Client = ClientFactory.GetHttpClient() + Dim Resp As HttpResponseMessage = Client.SendAsync(Req, HttpCompletionOption.ResponseHeadersRead, CTS.Token).GetAwaiter().GetResult() + + If RequireReturnRespObj Then Return Resp + + Resp.EnsureSuccessStatusCode() + CTS.CancelAfter(Timeout) + Dim ResultStream = Resp.Content.ReadAsStreamAsync().GetAwaiter().GetResult() + Using DataStream As New MemoryStream() + ResultStream.CopyTo(DataStream) + Dim Bytes = DataStream.ToArray() + Resp.Dispose() + Req.Dispose() + + If RequireDecode Then + If Encode Is Nothing Then Encode = Encoding.UTF8 + Return Encode.GetString(Bytes) + Else + Return Bytes + End If + End Using + End Using + + + End Function + + ' 获取HttpMethod + Private Function GetHttpMethod(Method As String) As HttpMethod + Select Case Method?.Trim().ToUpperInvariant() + Case "HEAD" + Return HttpMethod.Head + Case "POST" + Return HttpMethod.Post + Case "PUT" + Return HttpMethod.Put + Case "DELETE" + Return HttpMethod.Delete + Case Else + Return HttpMethod.Get + End Select + End Function + + + End Class + + Public Class HttpWebProxyFactory + Implements IWebProxy + Public DisableHttpProxy As Boolean = True + Public ProxyAddress As String + Private CurrentProxy As WebProxy + Public RequiredReloadProxy As Boolean + Public RefreshSyncLock As New Object + Public SystemProxy As New WebProxy() + Public Sub New() + End Sub + + Public Property Credentials As ICredentials Implements IWebProxy.Credentials + Get + Return AuthCredentials + End Get + Set(value As ICredentials) + AuthCredentials = value + End Set + End Property + Private AuthCredentials As ICredentials + + Public Function GetProxy(destination As Uri) As Uri Implements IWebProxy.GetProxy + Return GetProxy(destination.AbsoluteUri).Address + End Function + Public Function GetProxy(destination As String) As WebProxy + If CurrentProxy IsNot Nothing AndAlso Not RequiredReloadProxy Then Return CurrentProxy + If RequiredReloadProxy OrElse CurrentProxy Is Nothing Then + If ProxyAddress IsNot Nothing OrElse + Not String.IsNullOrWhiteSpace(ProxyAddress) Then + SyncLock RefreshSyncLock + CurrentProxy = New WebProxy(ProxyAddress, True) + If AuthCredentials IsNot Nothing Then CurrentProxy.Credentials = AuthCredentials + Return CurrentProxy + End SyncLock + Else + SyncLock RefreshSyncLock + CurrentProxy = New WebProxy() + If AuthCredentials IsNot Nothing Then CurrentProxy.Credentials = AuthCredentials + Return CurrentProxy + End SyncLock + End If + End If + Return Nothing + End Function + Public Function IsBypassed(host As Uri) As Boolean Implements IWebProxy.IsBypassed + If DisableHttpProxy Then Return True + If CurrentProxy Is Nothing Then + SyncLock RefreshSyncLock + CurrentProxy = GetProxy(host.AbsoluteUri) + End SyncLock + End If + If CurrentProxy.IsBypassed(host) Then Return True + Return False + End Function + End Class + + Public Class HttpClientFactory + Private PreviousClient As HttpClient + Private CurrentClient As HttpClient + Private HttpClientLock As New Object + Public ClientHandler As New HttpClientHandler() With { + .UseProxy = True, + .Proxy = HttpProxy, + .AutomaticDecompression = DecompressionMethods.Deflate Or DecompressionMethods.GZip Or DecompressionMethods.None + } + Public Sub New() + HttpClientReload() + End Sub + Public Function GetHttpClient(Optional Handler As HttpClientHandler = Nothing) As HttpClient + SyncLock HttpClientLock + If Handler IsNot Nothing Then + ClientHandler = Handler + CurrentClient.Dispose() + CurrentClient = New HttpClient(Handler) + Return CurrentClient + End If + If CurrentClient IsNot Nothing Then Return CurrentClient + CurrentClient = New HttpClient(ClientHandler) With {.Timeout = Timeout.InfiniteTimeSpan} + Return CurrentClient + End SyncLock + End Function + Public Sub HttpClientReload() + RunInNewThread( + Sub() + While True + Thread.Sleep(TimeSpan.FromHours(6)) + If PreviousClient IsNot Nothing Then PreviousClient.Dispose() + PreviousClient = CurrentClient + CurrentClient = GetHttpClient() + End While + End Sub + ) + End Sub + + End Class ''' ''' 测试 Ping。失败则返回 -1。 ''' + ''' Public Function Ping(Ip As String, Optional Timeout As Integer = 10000, Optional MakeLog As Boolean = True) As Integer Dim PingResult As NetworkInformation.PingReply Try @@ -62,30 +269,22 @@ Retry: End Try End Function Public Function NetGetCodeByClient(Url As String, Encoding As Encoding, Timeout As Integer, Accept As String, Optional UseBrowserUserAgent As Boolean = False) As String - Url = SecretCdnSign(Url) Log("[Net] 获取客户端网络结果:" & Url & ",最大超时 " & Timeout) - Dim Request As CookieWebClient - Dim res As HttpWebResponse = Nothing - Dim HttpStream As Stream = Nothing Try - Request = New CookieWebClient With { - .Encoding = Encoding, - .Timeout = Timeout + Dim Headers As New Dictionary(Of String, String) From { + {"Accept", Accept} } - Request.Headers("Accept") = Accept - Request.Headers("Accept-Language") = "en-US,en;q=0.5" - Request.Headers("X-Requested-With") = "XMLHttpRequest" - SecretHeadersSign(Url, Request, UseBrowserUserAgent) - Return Request.DownloadString(Url) + Using Req As HttpRequestMessage = Request.GetRequestMessage(Url, Headers:=Headers, RequireHeaderSign:=True, RequireCdnSign:=True, UseBrowserUA:=UseBrowserUserAgent) + Req.Headers.AcceptLanguage.Add(New StringWithQualityHeaderValue("en-US,en;q=0.5")) + Req.Headers.TryAddWithoutValidation("X-Request-With","XMLHttpRequest") + Dim Resp As String = Request.SendRequest(Req, Encode:=Encoding, Timeout:=Timeout) + + Return Resp + End Using + Catch ex As TaskCanceledException + Throw New TaskCanceledException("连接服务器超时(" & Url & ")", ex) Catch ex As Exception - If ex.GetType.Equals(GetType(WebException)) AndAlso CType(ex, WebException).Status = WebExceptionStatus.Timeout Then - Throw New TimeoutException("连接服务器超时(" & Url & ")", ex) - Else - Throw New WebException("获取结果失败," & ex.Message & "(" & Url & ")", ex) - End If - Finally - If Not IsNothing(HttpStream) Then HttpStream.Dispose() - If Not IsNothing(res) Then res.Dispose() + Throw New WebException("获取结果失败," & ex.Message & "(" & Url & ")", ex) End Try End Function @@ -183,35 +382,23 @@ RequestFinished: End Function Public Function NetGetCodeByRequestOnce(Url As String, Optional Encode As Encoding = Nothing, Optional Timeout As Integer = 30000, Optional IsJson As Boolean = False, Optional Accept As String = "", Optional UseBrowserUserAgent As Boolean = False) If RunInUi() AndAlso Not Url.Contains("//127.") Then Throw New Exception("在 UI 线程执行了网络请求") - Url = SecretCdnSign(Url) Log($"[Net] 获取网络结果:{Url},超时 {Timeout}ms{If(IsJson, ",要求 json", "")}") - Dim Request As HttpWebRequest = WebRequest.Create(Url) - Dim Result As New List(Of Byte) Try - If Url.StartsWithF("https", True) Then Request.ProtocolVersion = HttpVersion.Version11 - Request.Timeout = Timeout - Request.Accept = Accept - SecretHeadersSign(Url, Request, UseBrowserUserAgent) - Using res As HttpWebResponse = Request.GetResponse() - Using HttpStream As Stream = res.GetResponseStream() - HttpStream.ReadTimeout = Timeout - Dim HttpData As Byte() = New Byte(16384) {} - Using Reader As New StreamReader(HttpStream, If(Encode, Encoding.UTF8)) - Dim ResultString As String = Reader.ReadToEnd - Return If(IsJson, GetJson(ResultString), ResultString) - End Using - End Using + Dim Headers As New Dictionary(Of String, String) From { + {"Accept", If(Accept = "", "*/*", Accept)} + } + Using Req As HttpRequestMessage = Request.GetRequestMessage(Url, Headers:=Headers, UseBrowserUA:=UseBrowserUserAgent, RequireHeaderSign:=True, RequireCdnSign:=True) + Dim Resp As String = Request.SendRequest(Req, Encode:=Encode, Timeout:=Timeout) + Return If(IsJson, GetJson(Resp), Resp) End Using + Catch ex As ThreadInterruptedException Throw - Catch ex As Exception - If TypeOf ex Is WebException AndAlso CType(ex, WebException).Status = WebExceptionStatus.Timeout Then - Throw New TimeoutException($"获取结果失败({CType(ex, WebException).Status},{ex.Message},{Url})", ex) - Else - Throw New WebException($"获取结果失败({If(TypeOf ex Is WebException, CType(ex, WebException).Status & ",", "")}{ex.Message},{Url})", ex) - End If - Finally - Request.Abort() + + Catch ex As TaskCanceledException + Throw New TimeoutException($"获取结果失败({ex.Message},{Url})", ex) + Catch ex As HttpRequestException + Throw New WebException($"获取结果失败({ex.Message},{Url})", ex) End Try End Function @@ -265,7 +452,7 @@ RequestFinished: '下载 Using Client As New WebClient Try - SecretHeadersSign(Url, Client, UseBrowserUserAgent) + 'SecretHeadersSign(Url, Client, UseBrowserUserAgent) Client.DownloadFile(Url, LocalFile) Catch ex As Exception File.Delete(LocalFile) @@ -314,7 +501,7 @@ RequestFinished: ''' 请求的内容。 ''' 请求的套接字类型。 ''' 当返回 40x 时不重试。 - Public Function NetRequestRetry(Url As String, Method As String, Data As Object, ContentType As String, Optional DontRetryOnRefused As Boolean = True, Optional Headers As Dictionary(Of String, String) = Nothing) As String + Public Function NetRequestRetry(Url As String, Method As String, Data As Object, ContentType As String, Optional DontRetryOnRefused As Boolean = True, Optional Headers As Dictionary(Of String, String) = Nothing, Optional RequireReturnRespObj As Boolean = False) As String Dim RetryCount As Integer = 0 Dim RetryException As Exception = Nothing Dim StartTime As Long = GetTimeTick() @@ -322,15 +509,15 @@ RequestFinished: Retry: Select Case RetryCount Case 0 '正常尝试 - Return NetRequestOnce(Url, Method, Data, ContentType, 15000, Headers) + Return NetRequestOnce(Url, Method, Data, ContentType, 15000, Headers, RequiredReturnRespObj:=RequireReturnRespObj) Case 1 '慢速重试 Thread.Sleep(500) - Return NetRequestOnce(Url, Method, Data, ContentType, 25000, Headers) + Return NetRequestOnce(Url, Method, Data, ContentType, 25000, Headers, RequiredReturnRespObj:=RequireReturnRespObj) Case Else '快速重试 If GetTimeTick() - StartTime > 5500 Then '若前两次加载耗费 5 秒以上,才进行重试 Thread.Sleep(500) - Return NetRequestOnce(Url, Method, Data, ContentType, 4000, Headers) + Return NetRequestOnce(Url, Method, Data, ContentType, 4000, Headers,RequiredReturnRespObj:=RequireReturnRespObj) Else Throw RetryException End If @@ -397,79 +584,35 @@ RequestFinished: ''' ''' 发送一次网络请求并获取返回内容。 ''' - Public Function NetRequestOnce(Url As String, Method As String, Data As Object, ContentType As String, Optional Timeout As Integer = 25000, Optional Headers As Dictionary(Of String, String) = Nothing, Optional MakeLog As Boolean = True, Optional UseBrowserUserAgent As Boolean = False) As String + Public Function NetRequestOnce(Url As String, Method As String, Data As Object, ContentType As String, Optional Timeout As Integer = 25000, Optional Headers As Dictionary(Of String, String) = Nothing, Optional MakeLog As Boolean = True, Optional UseBrowserUserAgent As Boolean = False, Optional RequiredReturnRespObj As Boolean = False) As Object If RunInUi() AndAlso Not Url.Contains("//127.") Then Throw New Exception("在 UI 线程执行了网络请求") - Url = SecretCdnSign(Url) If MakeLog Then Log("[Net] 发起网络请求(" & Method & "," & Url & "),最大超时 " & Timeout) - Dim DataStream As Stream = Nothing - Dim Resp As WebResponse = Nothing - Dim Req As HttpWebRequest Try - Req = WebRequest.Create(Url) - Req.Method = Method - Dim SendData As Byte() - If TypeOf Data Is Byte() Then - SendData = Data - Else - SendData = New UTF8Encoding(False).GetBytes(Data.ToString) - End If - If Headers IsNot Nothing Then - For Each Pair In Headers - Req.Headers.Add(Pair.Key, Pair.Value) - Next - End If - Req.ContentType = ContentType - Req.Timeout = Timeout - SecretHeadersSign(Url, Req, UseBrowserUserAgent) - If Url.StartsWithF("https", True) Then Req.ProtocolVersion = HttpVersion.Version11 - If Method = "POST" OrElse Method = "PUT" Then - Req.ContentLength = SendData.Length - DataStream = Req.GetRequestStream() - DataStream.WriteTimeout = Timeout - DataStream.ReadTimeout = Timeout - DataStream.Write(SendData, 0, SendData.Length) - DataStream.Close() - End If - Resp = Req.GetResponse() - DataStream = Resp.GetResponseStream() - DataStream.WriteTimeout = Timeout - DataStream.ReadTimeout = Timeout - Using Reader As New StreamReader(DataStream) - Return Reader.ReadToEnd() - End Using - Catch ex As ThreadInterruptedException - Throw - Catch ex As WebException - If ex.Status = WebExceptionStatus.Timeout Then - ex = New WebException($"连接服务器超时,请检查你的网络环境是否良好({ex.Message},{Url})", ex) - Else - '获取请求失败的返回 - Dim Res As String = "" - Try - If ex.Response Is Nothing Then Exit Try - DataStream = ex.Response.GetResponseStream() - DataStream.WriteTimeout = Timeout - DataStream.ReadTimeout = Timeout - Using Reader As New StreamReader(DataStream) - Res = Reader.ReadToEnd() - End Using - Catch - End Try - If Res = "" Then - ex = New WebException($"网络请求失败({ex.Status},{ex.Message},{Url})", ex) + Dim ReqHeaders As New Dictionary(Of String, String) + If ContentType IsNot Nothing AndAlso Data IsNot Nothing Then + ' 可能指定了 Content-Type 但是 Headers 是 Nothing + If Headers IsNot Nothing Then + If Not Headers.ContainsKey("Content-Type") Then Headers.Add("Content-Type", ContentType) Else - ex = New ResponsedWebException($"服务器返回错误({ex.Status},{ex.Message},{Url}){vbCrLf}{Res}", Res, ex) + If Not ReqHeaders.ContainsKey("Content-Type") Then ReqHeaders.Add("Content-Type", ContentType) End If End If + Dim ReqData As Byte() = If(TypeOf Data Is String, Encoding.UTF8.GetBytes(Data), Data) + + + Dim Req As HttpRequestMessage = Request.GetRequestMessage(Url, Method, If(Headers, ReqHeaders), If(Data IsNot Nothing, ReqData, Nothing), UseBrowserUserAgent, True, True) + Return Request.SendRequest(Req, If(RequiredReturnRespObj, False, True), Timeout:=Timeout, RequireReturnRespObj:=RequiredReturnRespObj) + Catch ex As ThreadInterruptedException + Throw + Catch ex As TaskCanceledException + Return New WebException($"连接服务器超时,请检查你的网络环境是否良好({ex.Message},{Url})", ex) + Catch ex As HttpRequestException + If MakeLog Then Log(ex, "NetRequestOnce 失败", LogLevel.Developer) + Throw New WebException(ex.Message, ex) + Catch ex As Exception + ex = New WebException("网络请求失败(" & Url & ")", ex) If MakeLog Then Log(ex, "NetRequestOnce 失败", LogLevel.Developer) Throw ex - Catch ex As Exception - ex = New WebException("网络请求失败(" & Url & ")", ex) - If MakeLog Then Log(ex, "NetRequestOnce 失败", LogLevel.Developer) - Throw ex - Finally - If DataStream IsNot Nothing Then DataStream.Dispose() - If Resp IsNot Nothing Then Resp.Dispose() End Try End Function Public Class ResponsedWebException @@ -818,6 +961,7 @@ RequestFinished: Get Return IsUnknownSize OrElse FileSize < FilePieceLimit End Get + End Property ''' ''' 为不需要分割的小文件进行临时存储。 @@ -1055,8 +1199,9 @@ StartThread: ''' Private Sub Thread(Info As NetThread) If ModeDebug OrElse Info.DownloadStart = 0 Then Log("[Download] " & LocalName & " " & Info.Uuid & "#:开始,起始点 " & Info.DownloadStart & "," & Info.Source.Url) - Dim HttpRequest As HttpWebRequest + Dim HttpRequest As HttpRequestMessage Dim ResultStream As Stream = Nothing + Dim HttpResponse As HttpResponseMessage '部分下载源真的特别慢,并且只需要一个请求,例如 Ping 为 20s,如果增长太慢,就会造成类似 2.5s 5s 7.5s 10s 12.5s... 的极大延迟 '延迟过长会导致某些特别慢的链接迟迟不被掐死 Dim Timeout As Integer = Math.Min(Math.Max(ConnectAverage, 6000) * (1 + Info.Source.FailCount), 30000) @@ -1064,18 +1209,16 @@ StartThread: Try Dim HttpDataCount As Integer = 0 If SourcesOnce.Contains(Info.Source) AndAlso Not Info.Equals(Info.Source.Thread) Then GoTo SourceBreak - '请求头 - HttpRequest = WebRequest.Create(Info.Source.Url) - If Info.Source.Url.StartsWithF("https", True) Then HttpRequest.ProtocolVersion = HttpVersion.Version11 - 'HttpRequest.Proxy = Nothing 'new WebProxy(Ip, Port) - HttpRequest.Timeout = Timeout - HttpRequest.AddRange(Info.DownloadStart) - SecretHeadersSign(Info.Source.Url, HttpRequest, UseBrowserUserAgent) + HttpRequest = Request.GetRequestMessage(Info.Source.Url, + RequireHeaderSign:=True) + '可能出现结束减去起始等于或者小于 0 的情况,导致下载抛出 ArgumentOutOfRangeException + If Not Info.IsFirstThread Then HttpRequest.Headers.Range = New RangeHeaderValue(Info.DownloadStart, Nothing) + HttpResponse = Request.SendRequest(HttpRequest, False, Timeout:=Timeout, RequireReturnRespObj:=True) Dim ContentLength As Long = 0 - Using HttpResponse As HttpWebResponse = HttpRequest.GetResponse() + Using HttpResponse If State = NetState.Error Then GoTo SourceBreak '快速中断 - If ModeDebug AndAlso HttpResponse.ResponseUri.OriginalString <> Info.Source.Url Then - Log($"[Download] {LocalName} {Info.Uuid}#:重定向至 {HttpResponse.ResponseUri.OriginalString}") + If ModeDebug AndAlso HttpResponse.RequestMessage.RequestUri.ToString <> Info.Source.Url Then + Log($"[Download] {LocalName} {Info.Uuid}#:重定向至 {HttpResponse.RequestMessage.RequestUri.ToString}") End If ''从响应头获取文件名 'If Info.IsFirstThread Then @@ -1087,7 +1230,8 @@ StartThread: ' End If 'End If '文件大小校验 - ContentLength = HttpResponse.ContentLength + ContentLength = HttpResponse.Content.Headers.ContentLength.GetValueOrDefault(-1) + If ContentLength = -1 Then If FileSize > 1 Then If Info.DownloadStart = 0 Then @@ -1136,11 +1280,12 @@ NotSupportRange: SourcesOnce.Add(Info.Source) End If End SyncLock + Throw New WebException($"该下载源不支持分段下载:Range 起始于 {Info.DownloadStart},预期 ContentLength 为 {FileSize - Info.DownloadStart},返回 ContentLength 为 {ContentLength},总文件大小 {FileSize}") ElseIf Not FileSize - Info.DownloadStart = ContentLength Then Throw New WebException($"获取到的分段大小不一致:Range 起始于 {Info.DownloadStart},预期 ContentLength 为 {FileSize - Info.DownloadStart},返回 ContentLength 为 {ContentLength},总文件大小 {FileSize}") End If - 'Log($"[Download] {LocalName} {Info.Uuid}#:通过大小检查,文件大小 {FileSize},起始点 {Info.DownloadStart},ContentLength {ContentLength}") + Log($"[Download] {LocalName} {Info.Uuid}#:通过大小检查,文件大小 {FileSize},起始点 {Info.DownloadStart},ContentLength {ContentLength}") Info.State = NetState.Get SyncLock LockState If State < NetState.Get Then State = NetState.Get @@ -1154,8 +1299,7 @@ NotSupportRange: ResultStream = New FileStream(Info.Temp, FileMode.Create, FileAccess.Write, FileShare.Read) End If '开始下载 - Using HttpStream = HttpResponse.GetResponseStream() - HttpStream.ReadTimeout = Timeout + Using HttpStream = HttpResponse.Content.ReadAsStreamAsync().GetAwaiter().GetResult() If Setup.Get("SystemDebugDelay") Then Threading.Thread.Sleep(RandomInteger(50, 3000)) Dim HttpData As Byte() = New Byte(16384) {} HttpDataCount = HttpStream.Read(HttpData, 0, 16384) @@ -1345,7 +1489,7 @@ Wrong: If State < NetState.Merge Then State = NetState.Merge Else - Return + Exit Sub End If End SyncLock Dim RetryCount As Integer = 0 @@ -1355,7 +1499,8 @@ Retry: SyncLock LockChain '创建文件夹 If File.Exists(LocalPath) Then File.Delete(LocalPath) - Directory.CreateDirectory(GetPathFromFullPath(LocalPath)) + Dim Info As New FileInfo(LocalPath) + Info.Directory.Create() '合并文件 If IsNoSplit Then '仅有一个线程,从缓存中输出 @@ -1434,7 +1579,7 @@ Retry: ''' Private Sub Fail(Optional RaiseEx As Exception = Nothing) SyncLock LockState - If State >= NetState.Finish Then Return + If State >= NetState.Finish Then Exit Sub If RaiseEx IsNot Nothing Then Ex.Add(RaiseEx) '凉凉 State = NetState.Error @@ -1450,10 +1595,10 @@ Retry: Public Sub Abort(CausedByTask As LoaderDownload) '从特定任务中移除,如果它还属于其他任务,则继续下载 Tasks.Remove(CausedByTask) - If Tasks.Any Then Return + If Tasks.Any Then Exit Sub '确认中断 SyncLock LockState - If State >= NetState.Finish Then Return + If State >= NetState.Finish Then Exit Sub State = NetState.Error End SyncLock InterruptAndDelete() @@ -1473,7 +1618,7 @@ Retry: ''' Public Sub Finish(Optional PrintLog As Boolean = True) SyncLock LockState - If State >= NetState.Finish Then Return + If State >= NetState.Finish Then Exit Sub State = NetState.Finish End SyncLock SyncLock NetManager.LockRemain @@ -1600,7 +1745,7 @@ NextElement: '输入检测 If Not Files.Any() Then OnFinish() - Return + Exit Sub End If For Each File As NetFile In Files If File Is Nothing Then Throw New ArgumentException("存在空文件请求!") @@ -1657,14 +1802,13 @@ NextElement: End Sub Private Sub StartCopy(Files As List(Of NetFile), FolderList As List(Of String)) Try - If ModeDebug Then Log($"[Download] 检查线程分配文件数:{Files.Count},线程名:{Thread.CurrentThread.Name}") + If ModeDebug Then Log("[Download] 检查线程分配文件数:" & Files.Count & ",线程名:" & Thread.CurrentThread.Name) '试图从已存在的 Minecraft 文件夹中寻找目标文件 Dim ExistFiles As New List(Of KeyValuePair(Of NetFile, String)) '{NetFile, Target As String} For Each File As NetFile In Files Dim ExistFilePath As String = Nothing '判断是否有已存在的文件 - If File.Check IsNot Nothing AndAlso McFolderList IsNot Nothing AndAlso PathMcFolder IsNot Nothing AndAlso - File.Check.CanUseExistsFile AndAlso File.LocalPath.StartsWithF(PathMcFolder) Then + If File.Check IsNot Nothing AndAlso McFolderList IsNot Nothing AndAlso PathMcFolder IsNot Nothing AndAlso File.Check.CanUseExistsFile AndAlso File.LocalPath.StartsWithF(PathMcFolder) Then Dim Relative = File.LocalPath.Replace(PathMcFolder, "") For Each Folder In FolderList Dim Target = Folder & Relative @@ -1690,7 +1834,7 @@ NextElement: For Each FileToken In ExistFiles Dim File As NetFile = FileToken.Key SyncLock LockState - If File.State > NetState.WaitForCopy Then Return + If File.State > NetState.WaitForCopy Then Exit Sub End SyncLock Dim LocalPath As String = FileToken.Value Dim RetryCount As Integer = 0 @@ -1701,7 +1845,7 @@ Retry: File.Finish(False) Catch ex As Exception RetryCount += 1 - Log(ex, $"复制已存在的文件失败,重试第 {RetryCount} 次({LocalPath} -> {File.LocalPath})") + Log(ex, String.Format("复制已存在的文件失败,重试第 {2} 次({0} -> {1})", LocalPath, File.LocalPath, RetryCount)) If RetryCount < 3 Then Thread.Sleep(200) GoTo Retry @@ -1719,14 +1863,14 @@ Retry: '要求全部文件完成 SyncLock FileRemainLock FileRemain -= 1 - If FileRemain > 0 Then Return + If FileRemain > 0 Then Exit Sub End SyncLock OnFinish() End Sub Public Sub OnFinish() RaisePreviewFinish() SyncLock LockState - If State > LoadState.Loading Then Return + If State > LoadState.Loading Then Exit Sub State = LoadState.Finished End SyncLock End Sub @@ -1739,7 +1883,7 @@ Retry: End Sub Public Sub OnFail(ExList As List(Of Exception)) SyncLock LockState - If State > LoadState.Loading Then Return + If State > LoadState.Loading Then Exit Sub If ExList Is Nothing OrElse Not ExList.Any() Then ExList = New List(Of Exception) From {New Exception("未知错误!")} '寻找第一个不是 404 的下载源 Dim UsefulExs = ExList.Where(Function(e) Not e.Message.Contains("(404)")).ToList @@ -1768,7 +1912,7 @@ Retry: End Sub Public Overrides Sub Abort() SyncLock LockState - If State >= LoadState.Finished Then Return + If State >= LoadState.Finished Then Exit Sub State = LoadState.Aborted End SyncLock Log("[Download] " & Name & " 已取消!") @@ -1885,7 +2029,7 @@ Retry: ''' 启动监控线程,用于新增下载线程。 ''' Private Sub StartManager() - If IsManagerStarted Then Return + If IsManagerStarted Then Exit Sub IsManagerStarted = True Dim ThreadStarter = Sub(Id As Integer) '0 或 1 diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb index e5c5d6436..727013a92 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb @@ -78,8 +78,8 @@ If SplitedLine.Count >= 2 Then Entry.ChineseName = SplitedLine(1) If Entry.ChineseName.Contains("*") Then '处理 * - Entry.ChineseName = Entry.ChineseName.Replace("*", " (" & - String.Join(" ", If(Entry.CurseForgeSlug, Entry.ModrinthSlug).Split("-").Select(Function(w) w.Substring(0, 1).ToUpper & w.Substring(1, w.Length - 1))) & ")") + Entry.ChineseName = Entry.ChineseName.Replace("*", + $" ({If(Entry.CurseForgeSlug, Entry.ModrinthSlug).Replace("-", " ").Capitalize})") End If End If _CompDatabase.Add(Entry) @@ -1137,6 +1137,11 @@ Retry: If ModrinthThread IsNot Nothing Then ModrinthThread.Join() If Task.IsAborted Then Return + '筛除不是 Forge 的 Mod + If IsOldForgeRequest Then + RawResults = RawResults.Where(Function(p) Not p.ModLoaders.Any() OrElse p.ModLoaders.Contains(CompModLoaderType.Forge)).ToList + End If + '确保存在结果 Storage.ErrorMessage = Nothing If Not RawResults.Any() Then @@ -1167,11 +1172,6 @@ Retry: ModrinthThread?.Interrupt() End Try - '筛除不是 Forge 的 Mod - If IsOldForgeRequest Then - RawResults = RawResults.Where(Function(p) Not p.ModLoaders.Any() OrElse p.ModLoaders.Contains(CompModLoaderType.Forge)).ToList - End If - #End Region #Region "提取非重复项,存储于 RealResults" @@ -1490,9 +1490,11 @@ Retry: ''' Public Shared Function HandleCurseForgeDownloadUrls(Url As String) As List(Of String) Return { - Url.Replace("-service.overwolf.wtf", ".forgecdn.net").Replace("://edge", "://media"), + Url.Replace("-service.overwolf.wtf", ".forgecdn.net").Replace("://edge.", "://mediafilez.").Replace("://media.", "://mediafilez."), + Url.Replace("://edge.", "://mediafilez.").Replace("://media.", "://mediafilez."), + Url.Replace("-service.overwolf.wtf", ".forgecdn.net").Replace("://edge.", "://media."), Url.Replace("-service.overwolf.wtf", ".forgecdn.net"), - Url.Replace("://edge", "://media"), + Url.Replace("://edge.", "://media."), Url }.Distinct.ToList End Function diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb index b1718f52b..33fe597b5 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb @@ -708,11 +708,12 @@ NextStack: 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", "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", + "forge", "setup", "block", "model", "mixin", "event", "unimi", "netty", "world", "lwjgl", "gitlab", "common", "server", "config", "mixins", "compat", "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", "neoforge", - "injection", "listeners", "scheduler", "minecraft", "transformer", "transformers", "neoforged", "universal", "multipart", "minecraftforge", "blockentity", "spongepowered", "electronwill" + "injection", "listeners", "scheduler", "minecraft", "universal", "multipart", "neoforged", "microsoft", + "transformer", "transformers", "minecraftforge", "blockentity", "spongepowered", "electronwill" }.Contains(Word.ToLower) Then Continue For PossibleWords.Add(Word.Trim) Next diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb index 9c41a4a36..bf45fec96 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb @@ -117,6 +117,7 @@ If Output = "" Then Throw New ApplicationException("尝试运行该 Java 失败") If ModeDebug Then Log("[Java] Java 检查输出:" & PathFolder & "java.exe" & vbCrLf & Output) If Output.Contains("/lib/ext exists") Then Throw New ApplicationException("无法运行该 Java,请在删除 Java 文件夹中的 /lib/ext 文件夹后再试") + If Output.Contains("a fatal error") Then Throw New ApplicationException("由于操作系统或 JVM 存在问题,无法运行该 Java。") '获取详细信息 Dim VersionString = If(RegexSeek(Output, "(?<=version "")[^""]+"), If(RegexSeek(Output, "(?<=openjdk )[0-9]+"), "")).Replace("_", ".").Split("-").First If VersionString.Split(".").Count > 4 Then VersionString = VersionString.Replace(".0.", ".") '#3493,VersionString = "21.0.2.0.2" @@ -529,6 +530,8 @@ NoUserJava: If Disk.DriveType = DriveType.Network Then Continue For '跳过网络驱动器(#3705) JavaSearchFolder(Disk.Name, JavaPreList, False) Next + '查找 ~/.jdks 文件夹中的 Java + JavaSearchFolder(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) & "\.jdks\", JavaPreList, False) '查找 APPDATA 文件夹中的 Java JavaSearchFolder(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\", JavaPreList, False) JavaSearchFolder(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) & "\", JavaPreList, False) @@ -677,7 +680,7 @@ Wait: '若该目录有 Java,则加入结果 If File.Exists(Path & "javaw.exe") Then Results(Path) = Source '查找其下的所有文件夹 - '不应使用网易的 Java:https://github.com/Hex-Dragon/PCL2/issues/1279#issuecomment-2761489121 + '不应使用网易的 Java:https://github.com/HMeloong-Git/PCL/issues/1279#issuecomment-2761489121 Dim Keywords = {"java", "jdk", "env", "环境", "run", "软件", "jre", "mc", "dragon", "soft", "cache", "temp", "corretto", "roaming", "users", "craft", "program", "世界", "net", "游戏", "oracle", "game", "file", "data", "jvm", "服务", "server", "客户", "client", "整合", diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb index 14edb8a09..00469b8c9 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb @@ -1424,6 +1424,7 @@ Retry: End If 'Authlib-Injector If McLoginLoader.Output.Type = "Auth" Then + If (McLaunchJavaSelected.VersionCode >= 6) Then DataList.Add("-Djavax.net.ssl.trustStoreType=WINDOWS-ROOT") Dim Server As String = If(McLoginLoader.Input.Type = McLoginType.Legacy, "http://hiperauth.tech/api/yggdrasil-hiper/", 'HiPer 登录 Setup.Get("VersionServerAuthServer", McVersionCurrent)) @@ -1493,6 +1494,7 @@ NextVersion: End If 'Authlib-Injector If McLoginLoader.Output.Type = "Auth" Then + If (McLaunchJavaSelected.VersionCode >= 6) Then DataList.Add("-Djavax.net.ssl.trustStoreType=WINDOWS-ROOT") Dim Server As String = If(McLoginLoader.Input.Type = McLoginType.Legacy, "http://hiperauth.tech/api/yggdrasil-hiper/", 'HiPer 登录 Setup.Get("VersionServerAuthServer", Version:=McVersionCurrent)) diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb index efc596e4e..5ef12ee0b 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb @@ -1596,7 +1596,7 @@ OnLoaded: Try For Each SkinProperty In GetJson(SkinString)("properties") If SkinProperty("name") = "textures" Then - SkinValue = SkinProperty("value").Replace("http:", "https:") + SkinValue = SkinProperty("value") Exit Try End If Next @@ -1610,7 +1610,8 @@ OnLoaded: If SkinJson("textures") Is Nothing OrElse SkinJson("textures")("skin") Is Nothing OrElse SkinJson("textures")("skin")("url") Is Nothing Then Throw New Exception("用户未设置自定义皮肤") Else - SkinValue = SkinJson("textures")("skin")("url").ToString + Dim SkinUrl As String = SkinJson("textures")("skin")("url").ToString + SkinValue = If(SkinUrl.Contains("minecraft.net"),SkinUrl.Replace("http:", "https:"),SkinUrl) End If '保存缓存 WriteIni(PathTemp & "Cache\Skin\Index" & Type & ".ini", Uuid, SkinValue) diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb index 4bc5bc54c..bb9b3d5c0 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb @@ -939,7 +939,7 @@ Finished: '开始网络获取 Log($"[Mod] 目标加载器:{ModLoaders.Join("/")},版本:{McVersion}") Dim EndedThreadCount As Integer = 0, IsFailed As Boolean = False - Dim MainThread As Thread = Thread.CurrentThread + Dim CurrentTaskThread As Thread = Thread.CurrentThread '从 Modrinth 获取信息 RunInNewThread( Sub() @@ -963,7 +963,7 @@ Finished: Dim File As New CompFile(ModrinthVersion(Entry.ModrinthHash), CompType.Mod) If Entry.CompFile Is Nothing OrElse Entry.CompFile.ReleaseDate < File.ReleaseDate Then Entry.CompFile = File Next - If Loader.IsAbortedWithThread(MainThread) Then Return + If Loader.IsAbortedWithThread(CurrentTaskThread) Then Return Log($"[Mod] 需要从 Modrinth 获取 {ModrinthMapping.Count} 个本地 Mod 的工程信息") '步骤 3:获取工程信息 If Not ModrinthMapping.Any() Then Return @@ -1013,7 +1013,7 @@ Finished: Dim CurseForgeHashes As New List(Of UInteger) For Each Entry In Mods CurseForgeHashes.Add(Entry.CurseForgeHash) - If Loader.IsAbortedWithThread(MainThread) Then Return + If Loader.IsAbortedWithThread(CurrentTaskThread) Then Return Next Dim CurseForgeRaw = CType(CType(GetJson(DlModRequest("https://api.curseforge.com/v1/fingerprints/432", "POST", $"{{""fingerprints"": [{CurseForgeHashes.Join(",")}]}}", "application/json")), JObject)("data")("exactMatches"), JContainer) @@ -1034,7 +1034,7 @@ Finished: If Entry.CompFile Is Nothing OrElse Entry.CompFile.ReleaseDate < File.ReleaseDate Then Entry.CompFile = File Next Next - If Loader.IsAbortedWithThread(MainThread) Then Return + If Loader.IsAbortedWithThread(CurrentTaskThread) Then Return Log($"[Mod] 需要从 CurseForge 获取 {CurseForgeMapping.Count} 个本地 Mod 的工程信息") '步骤 3:获取工程信息 If Not CurseForgeMapping.Any() Then Return diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb index 44613bfac..ce0c443c0 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb @@ -258,8 +258,7 @@ Retry: If FileList.ContainsKey(Id) Then Continue For '可选 Mod 提示 If ModOptionalList.Contains(Id) Then - If MyMsgBox("是否要下载整合包中的可选文件 " & ModJson("displayName").ToString & "?", - "下载可选文件", "是", "否") = 2 Then + If MyMsgBox("是否要下载整合包中的可选文件 " & ModJson("displayName").ToString & "?", "下载可选文件", "是", "否") = 2 Then Continue For End If End If @@ -423,7 +422,7 @@ Retry: End Select End If '添加下载文件 - Dim Urls = File("downloads").Select(Function(t) t.ToString.Replace("://edge.forgecdn", "://media.forgecdn")).ToList + Dim Urls = File("downloads").SelectMany(Function(t) CompFile.HandleCurseForgeDownloadUrls(t.ToString)).ToList Urls.AddRange(Urls.Select(Function(u) DlSourceModGet(u)).ToList) Urls = Urls.Distinct.ToList() Dim TargetPath As String = $"{PathMcFolder}versions\{VersionName}\{File("path")}" diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb index fb8f52e8f..54ce14b63 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb @@ -271,7 +271,7 @@ If MinecraftWindow Is Nothing Then Return Dim MinecraftWindowName = MinecraftWindow.Value.Value, MinecraftWindowHandle = MinecraftWindow.Value.Key '已找到窗口 - If Not MinecraftWindowName.StartsWithF("FML") Then + If Not MinecraftWindowName.StartsWithF("FML") AndAlso Not MinecraftWindowName.StartsWithF("Quilt Loader") Then '已找到 Minecraft 窗口 WindowHandle = MinecraftWindowHandle WatcherLog($"Minecraft 窗口已加载:{MinecraftWindowName}({MinecraftWindowHandle.ToInt64})") @@ -292,8 +292,8 @@ End Sub, "MinecraftWindowMaximize") End If ElseIf Not IsWindowAppeared Then - '已找到 FML 窗口 - WatcherLog("FML 窗口已加载:" & MinecraftWindowName & "(" & MinecraftWindowHandle.ToInt64 & ")") + '已找到 Mod Loader 窗口 + WatcherLog("Mod Loader 窗口已加载:" & MinecraftWindowName & "(" & MinecraftWindowHandle.ToInt64 & ")") End If IsWindowAppeared = True Catch ex As Exception diff --git a/Plain Craft Launcher 2/Modules/ModMusic.vb b/Plain Craft Launcher 2/Modules/ModMusic.vb index 060091ab2..dc129ec67 100644 --- a/Plain Craft Launcher 2/Modules/ModMusic.vb +++ b/Plain Craft Launcher 2/Modules/ModMusic.vb @@ -247,7 +247,7 @@ Log("[Music] 已恢复播放") Try MusicNAudio?.Play() - Catch 'https://github.com/Hex-Dragon/PCL2/pull/5415#issuecomment-2751135223 + Catch 'https://github.com/Meloong-Git/PCL/pull/5415#issuecomment-2751135223 MusicNAudio?.Stop() MusicNAudio?.Play() End Try diff --git a/Plain Craft Launcher 2/Modules/ModSecret.vb b/Plain Craft Launcher 2/Modules/ModSecret.vb index f62028e36..3c74d220e 100644 --- a/Plain Craft Launcher 2/Modules/ModSecret.vb +++ b/Plain Craft Launcher 2/Modules/ModSecret.vb @@ -1,5 +1,6 @@ '由于包含加解密等安全信息,本文件中的部分代码已被删除 +Imports System.Net.Http Imports System.Security.Cryptography Friend Module ModSecret @@ -9,9 +10,10 @@ Friend Module ModSecret '在开源版的注册表与常规版的注册表隔离,以防数据冲突 Public Const RegFolder As String = "PCLDebug" '用于微软登录的 ClientId - Public Const OAuthClientId As String = "" + Public OAuthClientId As String = If(Environment.GetEnvironmentVariable("PCL_MS_CLIENT_ID"), "") 'CurseForge API Key - Public Const CurseForgeAPIKey As String = "" + Public CurseForgeAPIKey As String = If(Environment.GetEnvironmentVariable("PCL_CURSEFORGE_API_KEY"), "") + Friend Sub SecretOnApplicationStart() '提升 UI 线程优先级 @@ -78,32 +80,19 @@ Friend Module ModSecret ''' ''' 设置 Headers 的 UA、Referer。 ''' - Friend Sub SecretHeadersSign(Url As String, ByRef Client As WebClient, Optional UseBrowserUserAgent As Boolean = False) - If Url.Contains("baidupcs.com") OrElse Url.Contains("baidu.com") Then - Client.Headers("User-Agent") = "LogStatistic" '#4951 - 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 - End If - Client.Headers("Referer") = "http://" & VersionCode & ".open.pcl2.server/" - If Url.Contains("api.curseforge.com") Then Client.Headers("x-api-key") = CurseForgeAPIKey - End Sub - ''' - ''' 设置 Headers 的 UA、Referer。 - ''' - Friend Sub SecretHeadersSign(Url As String, ByRef Request As HttpWebRequest, Optional UseBrowserUserAgent As Boolean = False) + Friend Sub SecretHeadersSign(Url As String, ByRef Req As HttpRequestMessage, Optional UseBrowserUserAgent As Boolean = False) If Url.Contains("baidupcs.com") OrElse Url.Contains("baidu.com") Then - Request.UserAgent = "LogStatistic" '#4951 + Req.Headers.Add("User-Agent", "LogStatistic") '#4951 ElseIf UseBrowserUserAgent Then - Request.UserAgent = "PCL2/" & VersionStandardCode & " Mozilla/5.0 AppleWebKit/537.36 Chrome/63.0.3239.132 Safari/537.36" + Req.Headers.Add("User-Agent", $"PCL2/{VersionStandardCode} Mozilla/5.0 AppleWebKit/537.36 Chrome/63.0.3239.132 Safari/537.36") Else - Request.UserAgent = "PCL2/" & VersionStandardCode + Req.Headers.Add("User-Agent", $"PCL2/{VersionStandardCode}") End If - Request.Referer = "http://" & VersionCode & ".open.pcl2.server/" - If Url.Contains("api.curseforge.com") Then Request.Headers("x-api-key") = CurseForgeAPIKey + Req.Headers.Add("Referer", $"http://{VersionCode}.open.pcl2.server/") + If Url.Contains("api.curseforge.com") Then Req.Headers.Add("x-api-key", CurseForgeAPIKey) End Sub + #End Region #Region "字符串加解密" @@ -225,31 +214,17 @@ Friend Module ModSecret If Not FrmMain.IsLoaded Then Return '顶部条背景 Dim Brush = New LinearGradientBrush With {.EndPoint = New Point(1, 0), .StartPoint = New Point(0, 0)} - If ThemeNow = 5 Then - Brush.GradientStops.Add(New GradientStop With {.Offset = 0, .Color = New MyColor().FromHSL2(ColorHue, ColorSat, 25)}) - Brush.GradientStops.Add(New GradientStop With {.Offset = 0.5, .Color = New MyColor().FromHSL2(ColorHue, ColorSat, 15)}) - Brush.GradientStops.Add(New GradientStop With {.Offset = 1, .Color = New MyColor().FromHSL2(ColorHue, ColorSat, 25)}) - FrmMain.PanTitle.Background = Brush - FrmMain.PanTitle.Background.Freeze() - ElseIf Not (ThemeNow = 12 OrElse ThemeDontClick = 2) Then - If TypeOf ColorHueTopbarDelta Is Integer Then - Brush.GradientStops.Add(New GradientStop With {.Offset = 0, .Color = New MyColor().FromHSL2(ColorHue - ColorHueTopbarDelta, ColorSat, 48 + ColorLightAdjust)}) - Brush.GradientStops.Add(New GradientStop With {.Offset = 0.5, .Color = New MyColor().FromHSL2(ColorHue, ColorSat, 54 + ColorLightAdjust)}) - Brush.GradientStops.Add(New GradientStop With {.Offset = 1, .Color = New MyColor().FromHSL2(ColorHue + ColorHueTopbarDelta, ColorSat, 48 + ColorLightAdjust)}) - Else - Brush.GradientStops.Add(New GradientStop With {.Offset = 0, .Color = New MyColor().FromHSL2(ColorHue + ColorHueTopbarDelta(0), ColorSat, 48 + ColorLightAdjust)}) - Brush.GradientStops.Add(New GradientStop With {.Offset = 0.5, .Color = New MyColor().FromHSL2(ColorHue + ColorHueTopbarDelta(1), ColorSat, 54 + ColorLightAdjust)}) - Brush.GradientStops.Add(New GradientStop With {.Offset = 1, .Color = New MyColor().FromHSL2(ColorHue + ColorHueTopbarDelta(2), ColorSat, 48 + ColorLightAdjust)}) - End If - FrmMain.PanTitle.Background = Brush - FrmMain.PanTitle.Background.Freeze() + If TypeOf ColorHueTopbarDelta Is Integer Then + Brush.GradientStops.Add(New GradientStop With {.Offset = 0, .Color = New MyColor().FromHSL2(ColorHue - ColorHueTopbarDelta, ColorSat, 48 + ColorLightAdjust)}) + Brush.GradientStops.Add(New GradientStop With {.Offset = 0.5, .Color = New MyColor().FromHSL2(ColorHue, ColorSat, 54 + ColorLightAdjust)}) + Brush.GradientStops.Add(New GradientStop With {.Offset = 1, .Color = New MyColor().FromHSL2(ColorHue + ColorHueTopbarDelta, ColorSat, 48 + ColorLightAdjust)}) Else - Brush.GradientStops.Add(New GradientStop With {.Offset = 0, .Color = New MyColor().FromHSL2(ColorHue - 21, ColorSat, 53 + ColorLightAdjust)}) - Brush.GradientStops.Add(New GradientStop With {.Offset = 0.33, .Color = New MyColor().FromHSL2(ColorHue - 7, ColorSat, 47 + ColorLightAdjust)}) - Brush.GradientStops.Add(New GradientStop With {.Offset = 0.67, .Color = New MyColor().FromHSL2(ColorHue + 7, ColorSat, 47 + ColorLightAdjust)}) - Brush.GradientStops.Add(New GradientStop With {.Offset = 1, .Color = New MyColor().FromHSL2(ColorHue + 21, ColorSat, 53 + ColorLightAdjust)}) - FrmMain.PanTitle.Background = Brush + Brush.GradientStops.Add(New GradientStop With {.Offset = 0, .Color = New MyColor().FromHSL2(ColorHue + ColorHueTopbarDelta(0), ColorSat, 48 + ColorLightAdjust)}) + Brush.GradientStops.Add(New GradientStop With {.Offset = 0.5, .Color = New MyColor().FromHSL2(ColorHue + ColorHueTopbarDelta(1), ColorSat, 54 + ColorLightAdjust)}) + Brush.GradientStops.Add(New GradientStop With {.Offset = 1, .Color = New MyColor().FromHSL2(ColorHue + ColorHueTopbarDelta(2), ColorSat, 48 + ColorLightAdjust)}) End If + FrmMain.PanTitle.Background = Brush + FrmMain.PanTitle.Background.Freeze() '主页面背景 If Setup.Get("UiBackgroundColorful") Then Brush = New LinearGradientBrush With {.EndPoint = New Point(0.1, 1), .StartPoint = New Point(0.9, 0)} diff --git a/Plain Craft Launcher 2/My Project/AssemblyInfo.vb b/Plain Craft Launcher 2/My Project/AssemblyInfo.vb index 6c5cb3ed4..8d8a259b4 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/Comp/PageComp.xaml b/Plain Craft Launcher 2/Pages/PageDownload/Comp/PageComp.xaml index e66570e2d..2e3c8c5b3 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/Comp/PageComp.xaml +++ b/Plain Craft Launcher 2/Pages/PageDownload/Comp/PageComp.xaml @@ -34,7 +34,7 @@ - + @@ -92,4 +92,4 @@ - \ No newline at end of file + diff --git a/Plain Craft Launcher 2/Pages/PageDownload/Comp/PageComp.xaml.vb b/Plain Craft Launcher 2/Pages/PageDownload/Comp/PageComp.xaml.vb index 3ad75d4a9..18be6d0f8 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/Comp/PageComp.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageDownload/Comp/PageComp.xaml.vb @@ -68,6 +68,10 @@ Public Class PageComp ''' 在切换到页面时,应自动将筛选项设置为与该目标 MC 版本和加载器相同。 ''' Public Shared TargetVersion As McVersion = Nothing + ''' + ''' 在切换到该页面时自动设置的搜索框内的内容。 + ''' + Public Shared TargetName As String = Nothing '在点击 MyCompItem 时会获取 Loader 的输入,以使资源详情页面可以应用相同的筛选项 Public Loader As New LoaderTask(Of CompProjectRequest, Integer)("社区资源获取:XXX", AddressOf CompProjectsGet, AddressOf LoaderInput) With {.ReloadTimeout = 60 * 1000} @@ -94,6 +98,11 @@ Public Class PageComp ComboSearchLoader.SelectedItem = GetTargetItemByName("NeoForge") End If TargetVersion = Nothing + If TargetName IsNot Nothing Then + '设置搜索框内容 + TextSearchName.Text = TargetName + TargetName = Nothing + End If '如果已经完成请求,则重新开始 If IsLoaderInited Then StartNewSearch() ScrollToHome() diff --git a/Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadCompDetail.xaml.vb b/Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadCompDetail.xaml.vb index 60a8ad385..3f1b0c9b4 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadCompDetail.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadCompDetail.xaml.vb @@ -75,6 +75,8 @@ Dim Results As List(Of CompFile) = CompFileLoader.Output If PageType = CompType.Any Then Results = Results.Where(Function(r) r.Type <> CompType.Plugin).ToList + ElseIf PageType = CompType.Shader OrElse PageType = CompType.ResourcePack Then + Results = Results.ToList Else Results = Results.Where(Function(r) r.Type = PageType).ToList End If @@ -305,7 +307,7 @@ End Try End Sub '资源下载;整合包另存为 - Public Shared CachedFolder As String = Nothing '仅在本次缓存的下载文件夹 + Public Shared CachedFolder As New Dictionary(Of CompType, String) '仅在本次缓存的下载文件夹 Public Sub Save_Click(sender As Object, e As EventArgs) Dim File As CompFile = If(TypeOf sender Is MyListItem, sender, sender.Parent).Tag RunInNewThread( @@ -356,8 +358,8 @@ Return False End Function '获取常规资源默认下载位置 - If CachedFolder IsNot Nothing Then - DefaultFolder = CachedFolder + If CachedFolder.ContainsKey(Project.Type) AndAlso Not String.IsNullOrEmpty(CachedFolder(Project.Type)) Then + DefaultFolder = CachedFolder(Project.Type) Log($"[Comp] 使用上次下载时的文件夹作为默认下载位置:{DefaultFolder}") ElseIf McVersionCurrent IsNot Nothing AndAlso IsVersionSuitable(McVersionCurrent) Then DefaultFolder = $"{McVersionCurrent.PathIndie}{SubFolder}" @@ -422,7 +424,13 @@ If Not Target.Contains("\") Then Return '构造步骤加载器 Dim LoaderName As String = Desc & "下载:" & GetFileNameWithoutExtentionFromPath(Target) & " " - If Target <> DefaultFolder AndAlso File.Type = CompType.Mod Then CachedFolder = GetPathFromFullPath(Target) + If Target <> DefaultFolder Then + If CachedFolder.ContainsKey(Project.Type) Then + CachedFolder(Project.Type) = GetPathFromFullPath(Target) + Else + CachedFolder.Add(Project.Type, GetPathFromFullPath(Target)) + End If + End If 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/Comp/PageDownloadShader.xaml b/Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadShader.xaml index 1fc2b76f3..123ce1b45 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadShader.xaml +++ b/Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadShader.xaml @@ -27,7 +27,7 @@ - + diff --git a/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb b/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb index 036519375..5829841be 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb +++ b/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb @@ -2163,13 +2163,17 @@ Retry: '迁移文件 If Directory.Exists(TempMcFolder & "libraries") Then CopyDirectory(TempMcFolder & "libraries", PathMcFolder & "libraries") Task.Progress = 0.8 + '创建 Mod 和资源包文件夹 Dim ModsFolder = New McVersion(VersionFolder).PathIndie & "mods\" '版本隔离信息在此时被决定 If Directory.Exists(ModsTempFolder) Then CopyDirectory(ModsTempFolder, ModsFolder) ElseIf Modable Then Directory.CreateDirectory(ModsFolder) - Log("[Download] 自动创建 mods 文件夹:" & ModsFolder) + Log("[Download] 自动创建 Mod 文件夹:" & ModsFolder) End If + Dim ResourcepacksFolder = New McVersion(VersionFolder).PathIndie & "resourcepacks\" + Directory.CreateDirectory(ResourcepacksFolder) + Log("[Download] 自动创建资源包文件夹:" & ResourcepacksFolder) End Sub) With {.ProgressWeight = 2, .Block = True}) '补全文件 If Request.OptiFineEntry IsNot Nothing OrElse (Request.ForgeVersion IsNot Nothing AndAlso Request.ForgeVersion.BeforeFirst(".") >= 20) OrElse Request.NeoForgeVersion IsNot Nothing OrElse Request.FabricVersion IsNot Nothing OrElse Request.LiteLoaderEntry IsNot Nothing Then diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml index 35c1a4ef2..5578905ab 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml @@ -27,7 +27,8 @@ - + + diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb index dfd504249..0e347f716 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb @@ -476,6 +476,11 @@ Else HintModOptiFine.Visibility = Visibility.Collapsed End If + If SelectedFabric IsNot Nothing AndAlso VersionSortInteger(SelectedMinecraftId, "1.21.4") > 0 Then + HintModOptiFineHigh.Visibility = Visibility.Visible + Else + HintModOptiFineHigh.Visibility = Visibility.Collapsed + End If '结束 IsReloading = False End Sub @@ -671,6 +676,8 @@ If SelectedNeoForge IsNot Nothing Then Return "与 NeoForge 不兼容" If LoadOptiFine Is Nothing OrElse LoadOptiFine.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "加载中……" If LoadOptiFine.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadOptiFine.State, Object).Error.Message + '检查 Fabric 1.20.5+:没有 OptiFabric 故全部不兼容 + If SelectedFabric IsNot Nothing AndAlso VersionSortInteger(SelectedMinecraftId, "1.20.4") > 0 Then Return "与 Fabric 不兼容" '检查 Forge 1.13 - 1.14.3:全部不兼容 If SelectedForge IsNot Nothing AndAlso VersionSortInteger(SelectedMinecraftId, "1.13") >= 0 AndAlso VersionSortInteger("1.14.3", SelectedMinecraftId) >= 0 Then @@ -981,6 +988,8 @@ Private Function LoadFabricGetError() As String If LoadFabric Is Nothing OrElse LoadFabric.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "加载中……" If LoadFabric.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadFabric.State, Object).Error.Message + '检查 Fabric 1.20.5+:没有 OptiFabric 故全部不兼容 + If SelectedOptiFine IsNot Nothing AndAlso VersionSortInteger(SelectedMinecraftId, "1.20.4") > 0 Then Return "与 OptiFine 不兼容" For Each Version As JObject In DlFabricListLoader.Output.Value("game") If Version("version").ToString = SelectedMinecraftId.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3") Then If SelectedForge IsNot Nothing Then Return "与 Forge 不兼容" diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/MyMsgLogin.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/MyMsgLogin.xaml.vb index ebe28ffe3..533ce72e3 100644 --- a/Plain Craft Launcher 2/Pages/PageLaunch/MyMsgLogin.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageLaunch/MyMsgLogin.xaml.vb @@ -87,7 +87,7 @@ LabTitle.Text = "登录 Minecraft" LabCaption.Text = $"登录网页将自动开启,请在网页中输入 {UserCode}(已自动复制)。" & vbCrLf & vbCrLf & - $"如果网络环境不佳,网页可能一直加载不出来,届时请使用使用加速器或 VPN 以改善网络环境。" & vbCrLf & + $"如果网络环境不佳,网页可能一直加载不出来,届时请使用加速器或 VPN 以改善网络环境。" & vbCrLf & $"你也可以用其他设备打开 {Website} 并输入上述代码。" Btn1.EventData = Website Btn2.EventData = UserCode @@ -125,6 +125,9 @@ ElseIf ex.Message.Contains("expired_token") Then Finished(New Exception("$登录用时太长啦,重新试试吧!")) Return + Else If ex.Message.Contains("Account security interrupt") Then + Finished(New Exception("$非常抱歉,该账号由于安全问题无法登陆,请前往 Microsoft 账户页获取更多信息。")) + Return ElseIf ex.Message.Contains("service abuse") Then Finished(New Exception("$非常抱歉,该账号已被微软封禁,无法登录。")) Return diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb index 42a670979..f9a2be3b1 100644 --- a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb @@ -127,6 +127,10 @@ Download: Log("[Page] 主页预设:杂志主页") Url = "https://pclhomeplazaoss.lingyunawa.top:26994/d/Homepages/Ext1nguisher/Custom.xaml" GoTo Download + Case 12 + Log("[Page] 主页预设:PCL GitHub 仪表盘") + Url = "https://raw.gitcode.com/Deep-Dark-Forest/PCL2-GitHub-Dashboard-Homepage/raw/main/custom.xaml" + GoTo Download End Select End Select RunInUi(Sub() LoadContent(Content)) diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMsSkin.xaml b/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMsSkin.xaml index f8acc1ab2..170f534a0 100644 --- a/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMsSkin.xaml +++ b/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMsSkin.xaml @@ -25,7 +25,6 @@ - diff --git a/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml b/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml index 921b39bc4..eb864b754 100644 --- a/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml +++ b/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml @@ -1,498 +1,468 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Plain Craft Launcher 2/Pages/PageOther/PageOtherLeft.xaml.vb b/Plain Craft Launcher 2/Pages/PageOther/PageOtherLeft.xaml.vb index 4686ce299..534af4120 100644 --- a/Plain Craft Launcher 2/Pages/PageOther/PageOtherLeft.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageOther/PageOtherLeft.xaml.vb @@ -137,7 +137,7 @@ Case 1 Feedback(True, False) Case 2 - OpenWebsite("https://github.com/Hex-Dragon/PCL2/issues/") + OpenWebsite("https://github.com/Meloong-Git/PCL/issues/") End Select End Sub Private Sub TryVote(sender As Object, e As RouteEventArgs) Handles ItemVote.Changed @@ -148,7 +148,7 @@ Public Shared Sub TryVote() If MyMsgBox("是否要打开新功能投票网页?" & vbCrLf & "如果无法打开该网页,请使用 VPN 以改善网络环境。", "新功能投票", "打开", "取消") = 2 Then Return - OpenWebsite("https://github.com/Hex-Dragon/PCL2/discussions/categories/%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8?discussions_q=category%3A%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8+sort%3Adate_created") + OpenWebsite("https://github.com/Meloong-Git/PCL/discussions/categories/%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8?discussions_q=category%3A%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8+sort%3Adate_created") End Sub End Class diff --git a/Plain Craft Launcher 2/Pages/PageSetup/ModSetup.vb b/Plain Craft Launcher 2/Pages/PageSetup/ModSetup.vb index 28fcd3dff..5d8b96840 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/ModSetup.vb +++ b/Plain Craft Launcher 2/Pages/PageSetup/ModSetup.vb @@ -610,7 +610,7 @@ FrmSetupUI.HintCustomWarn.Visibility = If(Setup.Get("HintCustomWarn"), Visibility.Collapsed, Visibility.Visible) FrmSetupUI.HintCustom.Text = $"从指定网址联网获取主页内容。服主也可以用于动态更新服务器公告。{vbCrLf}如果你制作了稳定运行的联网主页,可以点击这条提示投稿,若合格即可加入预设!" FrmSetupUI.HintCustom.EventType = "打开网页" - FrmSetupUI.HintCustom.EventData = "https://github.com/Hex-Dragon/PCL2/discussions/2528" + FrmSetupUI.HintCustom.EventData = "https://github.com/Meloong-Git/PCL/discussions/2528" Case 3 '预设 FrmSetupUI.PanCustomPreset.Visibility = Visibility.Visible FrmSetupUI.PanCustomLocal.Visibility = Visibility.Collapsed diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLeft.xaml.vb b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLeft.xaml.vb index 20238ce37..5744a66bf 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLeft.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLeft.xaml.vb @@ -144,25 +144,25 @@ Public Sub Reset(sender As Object, e As EventArgs) Select Case Val(sender.Tag) Case FormMain.PageSubType.SetupLaunch - If MyMsgBox("是否要初始化启动页的所有设置?该操作不可撤销。", "初始化确认",, "取消", IsWarn:=True) = 1 Then + If MyMsgBox("是否要初始化 启动 页面的所有设置?该操作不可撤销。", "初始化确认",, "取消", IsWarn:=True) = 1 Then If IsNothing(FrmSetupLaunch) Then FrmSetupLaunch = New PageSetupLaunch FrmSetupLaunch.Reset() ItemLaunch.Checked = True End If Case FormMain.PageSubType.SetupUI - If MyMsgBox("是否要初始化个性化页的所有设置?该操作不可撤销。" & vbCrLf & "(背景图片与音乐、主页等外部文件不会被删除)", "初始化确认",, "取消", IsWarn:=True) = 1 Then + If MyMsgBox("是否要初始化 个性化 页面的所有设置?该操作不可撤销。" & vbCrLf & "(背景图片与音乐、主页等外部文件不会被删除)", "初始化确认",, "取消", IsWarn:=True) = 1 Then If IsNothing(FrmSetupUI) Then FrmSetupUI = New PageSetupUI FrmSetupUI.Reset() ItemUI.Checked = True End If Case FormMain.PageSubType.SetupSystem - If MyMsgBox("是否要初始化启动器页的所有设置?该操作不可撤销。", "初始化确认",, "取消", IsWarn:=True) = 1 Then + If MyMsgBox("是否要初始化 其他 页面的所有设置?该操作不可撤销。", "初始化确认",, "取消", IsWarn:=True) = 1 Then If IsNothing(FrmSetupSystem) Then FrmSetupSystem = New PageSetupSystem FrmSetupSystem.Reset() ItemSystem.Checked = True End If Case FormMain.PageSubType.SetupLink - If MyMsgBox("是否要初始化联机页的所有设置?该操作不可撤销。", "初始化确认",, "取消", IsWarn:=True) = 1 Then + If MyMsgBox("是否要初始化 联机 页面的所有设置?该操作不可撤销。", "初始化确认",, "取消", IsWarn:=True) = 1 Then If IsNothing(FrmSetupLink) Then FrmSetupLink = New PageSetupLink FrmSetupLink.Reset() ItemLink.Checked = True diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml index 272a85100..95daf1219 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml +++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml @@ -251,6 +251,7 @@ + diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml.vb b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml.vb index 647675fa1..a4cb54da0 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml.vb @@ -1,4 +1,6 @@ -Public Class PageSetupUI +Imports NAudio.Wave + +Public Class PageSetupUI Public Shadows IsLoaded As Boolean = False @@ -274,7 +276,7 @@ '顶部栏 Private Sub BtnLogoChange_Click(sender As Object, e As EventArgs) Handles BtnLogoChange.Click - Dim FileName As String = SelectFile("常用图片文件(*.png;*.jpg;*.gif;*.webp)|*.png;*.jpg;*.gif;*.webp", "选择图片") + Dim FileName As String = SelectFile("常用图片文件(*.png;*.jpg;*.gif;*.webp;*.jpeg)|*.png;*.jpg;*.gif;*.webp;*.jpeg", "选择图片") If FileName = "" Then Return Try '拷贝文件 @@ -357,7 +359,15 @@ Refresh: PanMusicVolume.Visibility = Visibility.Visible PanMusicDetail.Visibility = Visibility.Visible BtnMusicClear.Visibility = Visibility.Visible - CardMusic.Title = "背景音乐(" & EnumerateFiles(Path & "PCL\Musics\").Count & " 首)" + Dim validMusicCount As Integer = 0 + For Each File In EnumerateFiles(Path & "PCL\Musics\") + Try + If {".ini", ".jpg", ".txt", ".cfg", ".lrc", ".db", ".png"}.Contains(File.Extension.ToLower) Then Continue For + validMusicCount += 1 + Catch ex As Exception + End Try + Next + CardMusic.Title = "背景音乐(" & validMusicCount & " 首)" Else PanMusicVolume.Visibility = Visibility.Collapsed PanMusicDetail.Visibility = Visibility.Collapsed diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb index ae4bf3a05..3fe78d083 100644 --- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb @@ -17,6 +17,7 @@ If IsLoad Then Return IsLoad = True + AddHandler FrmMain.KeyDown, AddressOf FrmMain_KeyDown '调整按钮边距(这玩意儿没法从 XAML 改) For Each Btn As MyRadioButton In PanFilter.Children Btn.LabText.Margin = New Thickness(-2, 0, 8, 0) @@ -381,6 +382,7 @@ Install: ''' Private Sub BtnManageDownload_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnManageDownload.Click, BtnHintDownload.Click PageComp.TargetVersion = PageVersionLeft.Version '将当前版本设置为筛选器 + PageComp.TargetName = SearchBox.Text '将当前搜索框内容设置为筛选器 FrmMain.PageChange(FormMain.PageType.Download, FormMain.PageSubType.DownloadMod) End Sub @@ -423,7 +425,8 @@ Install: ChangeAllSelected(False) AniControlEnabled += CacheAniControlEnabled End Sub - Private Sub PageVersionMod_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown + Private Sub FrmMain_KeyDown(sender As Object, e As KeyEventArgs) '监听自己的事件的话进入页面后不点击右侧控件就没办法监听到事件 (#4311) + If FrmMain.PageRight IsNot Me Then Return If My.Computer.Keyboard.CtrlKeyDown AndAlso e.Key = Key.A Then ChangeAllSelected(True) End Sub diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml.vb b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml.vb index 91a8e0c58..67afc3835 100644 --- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml.vb @@ -130,27 +130,34 @@ '清理 ini 缓存 IniClearCache(PageVersionLeft.Version.PathIndie & "options.txt") IniClearCache(PageVersionLeft.Version.Path & "PCL\Setup.ini") - '遍历重命名所有文件与文件夹 - For Each Entry As DirectoryInfo In New DirectoryInfo(NewPath).EnumerateDirectories - If Not Entry.Name.Contains(OldName) Then Continue For + '重命名 JAR 文件、JSON 文件与 natives 文件夹 + If Directory.Exists(NewPath & OldName & "-natives") Then If IsCaseChangedOnly Then - My.Computer.FileSystem.RenameDirectory(Entry.FullName, Entry.Name & "_temp") - My.Computer.FileSystem.RenameDirectory(Entry.FullName & "_temp", Entry.Name.Replace(OldName, NewName)) + My.Computer.FileSystem.RenameDirectory(NewPath & OldName & "-natives", OldName & "natives" & "_temp") + My.Computer.FileSystem.RenameDirectory(NewPath & OldName & "-natives" & "_temp", NewName & "-natives") Else - DeleteDirectory(NewPath & Entry.Name.Replace(OldName, NewName)) - My.Computer.FileSystem.RenameDirectory(Entry.FullName, Entry.Name.Replace(OldName, NewName)) + DeleteDirectory(NewPath & NewName & "-natives") + My.Computer.FileSystem.RenameDirectory(NewPath & OldName & "-natives", NewName & "-natives") End If - Next - For Each Entry As FileInfo In New DirectoryInfo(NewPath).EnumerateFiles - If Not Entry.Name.Contains(OldName) Then Continue For + End If + If File.Exists(NewPath & OldName & ".jar") Then If IsCaseChangedOnly Then - My.Computer.FileSystem.RenameFile(Entry.FullName, Entry.Name & "_temp") - My.Computer.FileSystem.RenameFile(Entry.FullName & "_temp", Entry.Name.Replace(OldName, NewName)) + My.Computer.FileSystem.RenameFile(NewPath & OldName & ".jar", OldName & "_temp" & ".jar") + My.Computer.FileSystem.RenameFile(NewPath & OldName & "_temp" & ".jar", NewName & ".jar") Else - If File.Exists(NewPath & Entry.Name.Replace(OldName, NewName)) Then File.Delete(NewPath & Entry.Name.Replace(OldName, NewName)) - My.Computer.FileSystem.RenameFile(Entry.FullName, Entry.Name.Replace(OldName, NewName)) + File.Delete(NewPath & NewName & ".jar") + My.Computer.FileSystem.RenameFile(NewPath & OldName & ".jar", NewName & ".jar") End If - Next + End If + If File.Exists(NewPath & OldName & ".json") Then + If IsCaseChangedOnly Then + My.Computer.FileSystem.RenameFile(NewPath & OldName & ".json", OldName & "_temp" & ".json") + My.Computer.FileSystem.RenameFile(NewPath & OldName & "_temp" & ".json", NewName & ".json") + Else + File.Delete(NewPath & NewName & ".json") + My.Computer.FileSystem.RenameFile(NewPath & OldName & ".json", NewName & ".json") + End If + End If '替换版本设置文件中的路径 If File.Exists(NewPath & "PCL\Setup.ini") Then WriteFile(NewPath & "PCL\Setup.ini", ReadFile(NewPath & "PCL\Setup.ini").Replace(OldPath, NewPath)) @@ -185,7 +192,7 @@ '选择 自定义 时修改图片 Try If ComboDisplayLogo.SelectedItem Is ItemDisplayLogoCustom Then - Dim FileName As String = SelectFile("常用图片文件(*.png;*.jpg;*.gif)|*.png;*.jpg;*.gif", "选择图片") + Dim FileName As String = SelectFile("常用图片文件(*.png;*.jpg;*.gif;*.jpeg)|*.png;*.jpg;*.gif;*.jpeg", "选择图片") If FileName = "" Then Reload() '还原选项 Return @@ -331,7 +338,7 @@ DeleteDirectory(PageVersionLeft.Version.Path) Hint("版本 " & PageVersionLeft.Version.Name & " 已永久删除!", HintType.Finish) Else - FileIO.FileSystem.DeleteDirectory(PageVersionLeft.Version.Path, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin) + FileIO.FileSystem.DeleteDirectory(PageVersionLeft.Version.Path, FileIO.UIOption.AllDialogs, FileIO.RecycleOption.SendToRecycleBin) Hint("版本 " & PageVersionLeft.Version.Name & " 已删除到回收站!", HintType.Finish) End If Case 2 diff --git a/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj b/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj index 8d3a3e60a..aa02536aa 100644 --- a/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj +++ b/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj @@ -195,9 +195,7 @@ MyIconTextButton.xaml - - - + @@ -501,7 +499,6 @@ FormMain.xaml Code - MSBuild:Compile Designer @@ -949,7 +946,6 @@ - diff --git a/Plain Craft Launcher 2/Resources/Custom.xaml b/Plain Craft Launcher 2/Resources/Custom.xaml index 77bbd84b0..28d3524d8 100644 --- a/Plain Craft Launcher 2/Resources/Custom.xaml +++ b/Plain Craft Launcher 2/Resources/Custom.xaml @@ -113,7 +113,7 @@ + Text="查看 PCL 源代码" EventType="打开网页" EventData="https://github.com/Meloong-Git/PCL/blob/main/Plain%20Craft%20Launcher%202/FormMain.xaml.vb"/> + Text="打开 GitHub 讨论页" EventType="打开网页" EventData="https://github.com/Meloong-Git/PCL/discussions/2528" /> diff --git a/README.md b/README.md index 99e807658..8b989a827 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Plain Craft Launcher -[![Stars](https://img.shields.io/github/stars/Hex-Dragon/PCL2?style=flat&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEiIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggZD0iTTggLjI1YS43NS43NSAwIDAgMSAuNjczLjQxOGwxLjg4MiAzLjgxNSA0LjIxLjYxMmEuNzUuNzUgMCAwIDEgLjQxNiAxLjI3OWwtMy4wNDYgMi45Ny43MTkgNC4xOTJhLjc1MS43NTEgMCAwIDEtMS4wODguNzkxTDggMTIuMzQ3bC0zLjc2NiAxLjk4YS43NS43NSAwIDAgMS0xLjA4OC0uNzlsLjcyLTQuMTk0TC44MTggNi4zNzRhLjc1Ljc1IDAgMCAxIC40MTYtMS4yOGw0LjIxLS42MTFMNy4zMjcuNjY4QS43NS43NSAwIDAgMSA4IC4yNVoiIGZpbGw9IiNlYWM1NGYiLz48L3N2Zz4=&logoSize=auto&label=Stars&labelColor=444444&color=eac54f)](https://github.com/Hex-Dragon/PCL2/) -[![Issues](https://img.shields.io/github/issues/Hex-Dragon/PCL2?style=flat&label=Issues&labelColor=444444&color=1F883D)](https://github.com/Hex-Dragon/PCL2/issues) +[![Stars](https://img.shields.io/github/stars/Meloong-Git/PCL?style=flat&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEiIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggZD0iTTggLjI1YS43NS43NSAwIDAgMSAuNjczLjQxOGwxLjg4MiAzLjgxNSA0LjIxLjYxMmEuNzUuNzUgMCAwIDEgLjQxNiAxLjI3OWwtMy4wNDYgMi45Ny43MTkgNC4xOTJhLjc1MS43NTEgMCAwIDEtMS4wODguNzkxTDggMTIuMzQ3bC0zLjc2NiAxLjk4YS43NS43NSAwIDAgMS0xLjA4OC0uNzlsLjcyLTQuMTk0TC44MTggNi4zNzRhLjc1Ljc1IDAgMCAxIC40MTYtMS4yOGw0LjIxLS42MTFMNy4zMjcuNjY4QS43NS43NSAwIDAgMSA4IC4yNVoiIGZpbGw9IiNlYWM1NGYiLz48L3N2Zz4=&logoSize=auto&label=Stars&labelColor=444444&color=eac54f)](https://github.com/Meloong-Git/PCL/) +[![Issues](https://img.shields.io/github/issues/Meloong-Git/PCL?style=flat&label=Issues&labelColor=444444&color=1F883D)](https://github.com/Meloong-Git/PCL/issues) [![哔哩哔哩](https://img.shields.io/badge/动态-BiliBili-00A4DB?style=flat&labelColor=444444&logoSize=auto)](https://space.bilibili.com/11343203/dynamic) [![爱发电](https://img.shields.io/badge/赞助-爱发电-946ce6?style=flat&labelColor=444444&logoSize=auto)](https://afdian.com/@LTCat) @@ -13,5 +13,5 @@ ### 相关资源 - [PCL 下载](https://afdian.com/p/0164034c016c11ebafcb52540025c377):下载可免费使用的正式版 PCL! -- [PCL 功能投票](https://github.com/Hex-Dragon/PCL2/discussions/2):来参加投票吧,开发者会优先处理票数较高的项目! +- [PCL 功能投票](https://github.com/Meloong-Git/PCL/discussions/2):来参加投票吧,开发者会优先处理票数较高的项目! - [帮助文档库](https://github.com/LTCatt/PCL2Help):PCL 内置帮助文档的存储库 diff --git "a/\346\234\200\346\226\260\346\255\243\345\274\217\347\211\210.zip" "b/\346\234\200\346\226\260\346\255\243\345\274\217\347\211\210.zip" index 27e8e3164..3e019854b 100644 Binary files "a/\346\234\200\346\226\260\346\255\243\345\274\217\347\211\210.zip" and "b/\346\234\200\346\226\260\346\255\243\345\274\217\347\211\210.zip" differ