From 74657d54c7ee1b7e7782d9c15bf23f292b387b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=84=E5=A5=9A=E6=A2=A6=E7=81=B5?= Date: Sun, 13 Apr 2025 15:21:57 +0800 Subject: [PATCH 1/9] =?UTF-8?q?pref(ModNet):=E4=BD=BF=E7=94=A8=20HttpClien?= =?UTF-8?q?t=20=E6=9B=BF=E4=BB=A3=E8=BF=87=E6=97=B6=E7=9A=84=20WebRequest?= =?UTF-8?q?=20=E5=92=8C=20WebClient?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plain Craft Launcher 2/Modules/Base/ModNet.vb | 28 +++++++++---------- Plain Craft Launcher 2/Modules/ModSecret.vb | 13 +++++---- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/Plain Craft Launcher 2/Modules/Base/ModNet.vb b/Plain Craft Launcher 2/Modules/Base/ModNet.vb index ba0e019..6680548 100644 --- a/Plain Craft Launcher 2/Modules/Base/ModNet.vb +++ b/Plain Craft Launcher 2/Modules/Base/ModNet.vb @@ -1,9 +1,13 @@ -Public Module ModNet - Public Const NetDownloadEnd As String = ".PCLDownloading" +Imports System.Net.Http +Public Module ModNet + Public Const NetDownloadEnd As String = ".PCLDownloading" + Public ReadOnly Client As New HttpClient() + Public ReadOnly ClientHandler As New HttpClientHandler() With {.AllowAutoRedirect = False} ''' ''' 测试 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 @@ -61,21 +65,17 @@ Retry: End Select 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 + Public Async Function NetGetCodeByClient(Url As String, Encoding As Encoding, Timeout As Integer, Accept As String, Optional UseBrowserUserAgent As Boolean = False) As Tasks.Task(Of 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 - } - Request.Headers("Accept") = Accept - Request.Headers("Accept-Language") = "en-US,en;q=0.5" - Request.Headers("X-Requested-With") = "XMLHttpRequest" - SecretHeadersSign(Url, Request, UseBrowserUserAgent) + SecretHeadersSign(Url, Client, UseBrowserUserAgent) + Client.DefaultRequestHeaders.Accept.Add(New Headers.MediaTypeWithQualityHeaderValue(Accept)) + Client.DefaultRequestHeaders.AcceptLanguage.Add(New Headers.StringWithQualityHeaderValue("en-US,en;q=0.5")) + Client.DefaultRequestHeaders.Add("X-Requested-With", "XMLHttpRequest") + Dim Result = Await Client.GetAsync(Url) + Return Request.DownloadString(Url) Catch ex As Exception If ex.GetType.Equals(GetType(WebException)) AndAlso CType(ex, WebException).Status = WebExceptionStatus.Timeout Then diff --git a/Plain Craft Launcher 2/Modules/ModSecret.vb b/Plain Craft Launcher 2/Modules/ModSecret.vb index 3c38897..1c55798 100644 --- a/Plain Craft Launcher 2/Modules/ModSecret.vb +++ b/Plain Craft Launcher 2/Modules/ModSecret.vb @@ -1,6 +1,7 @@ '由于包含加解密等安全信息,本文件中的部分代码已被删除 Imports System.Security.Cryptography +Imports System.Net.Http Friend Module ModSecret @@ -98,16 +99,16 @@ Friend Module ModSecret ''' ''' 设置 Headers 的 UA、Referer。 ''' - Friend Sub SecretHeadersSign(Url As String, ByRef Client As WebClient, Optional UseBrowserUserAgent As Boolean = False) + Friend Sub SecretHeadersSign(Url As String, ByRef Client As HttpClient, Optional UseBrowserUserAgent As Boolean = False) If Url.Contains("baidupcs.com") OrElse Url.Contains("baidu.com") Then - Client.Headers("User-Agent") = "LogStatistic" '#4951 + Client.DefaultRequestHeaders.Add("User-Agent", "LogStatistic") ElseIf UseBrowserUserAgent Then - Client.Headers("User-Agent") = "PCL2/" & VersionStandardCode & " Mozilla/5.0 AppleWebKit/537.36 Chrome/63.0.3239.132 Safari/537.36" + Client.DefaultRequestHeaders.Add("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 + Client.DefaultRequestHeaders.Add("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 + Client.DefaultRequestHeaders.Add("Referer", $"http://{VersionCode}.open.pcl2.server/") + If Url.Contains("api.curseforge.com") Then Client.DefaultRequestHeaders.Add("x-api-key", CurseForgeAPIKey) End Sub ''' ''' 设置 Headers 的 UA、Referer。 From 5a948548093c17ac57121c80081d8581865e56f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=84=E5=A5=9A=E6=A2=A6=E7=81=B5?= Date: Mon, 2 Jun 2025 00:08:22 +0800 Subject: [PATCH 2/9] =?UTF-8?q?Refactor(ModNet):=20=E4=BD=BF=E7=94=A8=20Ht?= =?UTF-8?q?tpClient=20=E4=BB=A3=E6=9B=BF=E8=BF=87=E5=A4=B1=E5=87=BD?= =?UTF-8?q?=E6=95=B0=20Feat(ModNet):=20=E6=94=AF=E6=8C=81=20GZip=20?= =?UTF-8?q?=E7=AD=89=E5=8E=8B=E7=BC=A9=E6=A0=BC=E5=BC=8F=E5=93=8D=E5=BA=94?= =?UTF-8?q?=E7=9A=84=E8=AF=BB=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CO-authored-by: qiuyue712 --- Plain Craft Launcher 2.sln | 12 + Plain Craft Launcher 2/App.config | 4 +- Plain Craft Launcher 2/Modules/Base/ModNet.vb | 441 ++++++++++++------ Plain Craft Launcher 2/Modules/ModSecret.vb | 29 +- .../My Project/Settings.Designer.vb | 38 +- .../Plain Craft Launcher 2.vbproj | 55 ++- 6 files changed, 386 insertions(+), 193 deletions(-) diff --git a/Plain Craft Launcher 2.sln b/Plain Craft Launcher 2.sln index b10e79d..40c68b7 100644 --- a/Plain Craft Launcher 2.sln +++ b/Plain Craft Launcher 2.sln @@ -8,19 +8,31 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Beta|Any CPU = Beta|Any CPU + Beta|x64 = Beta|x64 Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 ReleaseUpdate|Any CPU = ReleaseUpdate|Any CPU + ReleaseUpdate|x64 = ReleaseUpdate|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Beta|Any CPU.ActiveCfg = BETA|Any CPU {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Beta|Any CPU.Build.0 = BETA|Any CPU + {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Beta|x64.ActiveCfg = BETA|x64 + {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Beta|x64.Build.0 = BETA|x64 {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Debug|x64.ActiveCfg = Debug|x64 + {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Debug|x64.Build.0 = Debug|x64 {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Release|Any CPU.ActiveCfg = Release|Any CPU {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Release|Any CPU.Build.0 = Release|Any CPU + {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Release|x64.ActiveCfg = Release|x64 + {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Release|x64.Build.0 = Release|x64 {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.ReleaseUpdate|Any CPU.ActiveCfg = ReleaseUpdate|Any CPU {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.ReleaseUpdate|Any CPU.Build.0 = ReleaseUpdate|Any CPU + {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.ReleaseUpdate|x64.ActiveCfg = ReleaseUpdate|x64 + {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.ReleaseUpdate|x64.Build.0 = ReleaseUpdate|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Plain Craft Launcher 2/App.config b/Plain Craft Launcher 2/App.config index 8d134eb..0f0f321 100644 --- a/Plain Craft Launcher 2/App.config +++ b/Plain Craft Launcher 2/App.config @@ -9,9 +9,9 @@ - + - + diff --git a/Plain Craft Launcher 2/Modules/Base/ModNet.vb b/Plain Craft Launcher 2/Modules/Base/ModNet.vb index bac7cd8..cdc61e6 100644 --- a/Plain Craft Launcher 2/Modules/Base/ModNet.vb +++ b/Plain Craft Launcher 2/Modules/Base/ModNet.vb @@ -1,9 +1,217 @@ -Imports System.Net.Http +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 Client As New HttpClient() - Public ReadOnly ClientHandler As New HttpClientHandler() With {.AllowAutoRedirect = False} + 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 Watcher As New Stopwatch() + Watcher.Start() + 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 + Watcher.Stop() + Log($"[Test] GetRequestMessage 执行用时:{Watcher.ElapsedMilliseconds} 毫秒") + 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) + Dim Watcher As New Stopwatch() + Watcher.Start() + 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() + Watcher.Stop() + Log($"[Test] SendRequest 执行用时:{Watcher.ElapsedMilliseconds} 毫秒") + 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。 ''' @@ -65,27 +273,21 @@ Retry: End Select End Try End Function - Public Async Function NetGetCodeByClient(Url As String, Encoding As Encoding, Timeout As Integer, Accept As String, Optional UseBrowserUserAgent As Boolean = False) As Tasks.Task(Of String) - Url = SecretCdnSign(Url) + Public Function NetGetCodeByClient(Url As String, Encoding As Encoding, Timeout As Integer, Accept As String, Optional UseBrowserUserAgent As Boolean = False) As String Log("[Net] 获取客户端网络结果:" & Url & ",最大超时 " & Timeout) - Try - SecretHeadersSign(Url, Client, UseBrowserUserAgent) - Client.DefaultRequestHeaders.Accept.Add(New Headers.MediaTypeWithQualityHeaderValue(Accept)) - Client.DefaultRequestHeaders.AcceptLanguage.Add(New Headers.StringWithQualityHeaderValue("en-US,en;q=0.5")) - Client.DefaultRequestHeaders.Add("X-Requested-With", "XMLHttpRequest") - Dim Result = Await Client.GetAsync(Url) + Dim Headers As New Dictionary(Of String, String) From { + {"Accept", Accept} + } + Using Req As HttpRequestMessage = Request.GetRequestMessage(Url, Headers:=Headers, RequireHeaderSign:=True, RequireCdnSign:=True, UseBrowserUA:=UseBrowserUserAgent) + Dim Resp As String = Request.SendRequest(Req, Encode:=Encoding, Timeout:=Timeout) - Return Request.DownloadString(Url) + 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 +385,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 +455,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 +504,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 +512,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 +587,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 +964,7 @@ RequestFinished: Get Return IsUnknownSize OrElse FileSize < FilePieceLimit End Get + End Property ''' ''' 为不需要分割的小文件进行临时存储。 @@ -1055,8 +1202,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 +1212,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.DownloadEnd - Info.DownloadEnd <= 0 Then HttpRequest.Headers.Range = New RangeHeaderValue(Info.DownloadStart, Info.DownloadEnd) + 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 +1233,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 @@ -1108,7 +1255,7 @@ StartThread: Throw New Exception($"文件大小不足,获取结果为 {ContentLength},要求至少为 {Check.MinSize}。") End If If ContentLength <> Check.ActualSize AndAlso Check.ActualSize > 0 Then - Throw New Exception($"文件大小不一致,获取结果为 {ContentLength},要求必须为 {Check.ActualSize}。") + 'Throw New Exception($"文件大小不一致,获取结果为 {ContentLength},要求必须为 {Check.ActualSize}。") End If End If FileSize = ContentLength : IsUnknownSize = False @@ -1136,9 +1283,10 @@ 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}") + '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}") Info.State = NetState.Get @@ -1154,8 +1302,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 +1492,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 +1502,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 +1582,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 +1598,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 +1621,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 +1748,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 +1805,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 +1837,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 +1848,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 +1866,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 +1886,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 +1915,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 +2032,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/ModSecret.vb b/Plain Craft Launcher 2/Modules/ModSecret.vb index 99ba462..dcd37cb 100644 --- a/Plain Craft Launcher 2/Modules/ModSecret.vb +++ b/Plain Craft Launcher 2/Modules/ModSecret.vb @@ -1,7 +1,7 @@ '由于包含加解密等安全信息,本文件中的部分代码已被删除 -Imports System.Security.Cryptography Imports System.Net.Http +Imports System.Security.Cryptography Friend Module ModSecret @@ -79,32 +79,19 @@ Friend Module ModSecret ''' ''' 设置 Headers 的 UA、Referer。 ''' - Friend Sub SecretHeadersSign(Url As String, ByRef Client As HttpClient, 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 - Client.DefaultRequestHeaders.Add("User-Agent", "LogStatistic") + Req.Headers.Add("User-Agent", "LogStatistic") '#4951 ElseIf UseBrowserUserAgent Then - Client.DefaultRequestHeaders.Add("User-Agent", $"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 - Client.DefaultRequestHeaders.Add("User-Agent", $"PCL2/{VersionStandardCode}") + Req.Headers.Add("User-Agent", $"PCL2/{VersionStandardCode}") End If - Client.DefaultRequestHeaders.Add("Referer", $"http://{VersionCode}.open.pcl2.server/") - If Url.Contains("api.curseforge.com") Then Client.DefaultRequestHeaders.Add("x-api-key", CurseForgeAPIKey) - End Sub - ''' - ''' 设置 Headers 的 UA、Referer。 - ''' - Friend Sub SecretHeadersSign(Url As String, ByRef Request As HttpWebRequest, Optional UseBrowserUserAgent As Boolean = False) - If Url.Contains("baidupcs.com") OrElse Url.Contains("baidu.com") Then - Request.UserAgent = "LogStatistic" '#4951 - 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 - 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 "字符串加解密" diff --git a/Plain Craft Launcher 2/My Project/Settings.Designer.vb b/Plain Craft Launcher 2/My Project/Settings.Designer.vb index 4fa2aee..502e28c 100644 --- a/Plain Craft Launcher 2/My Project/Settings.Designer.vb +++ b/Plain Craft Launcher 2/My Project/Settings.Designer.vb @@ -12,15 +12,16 @@ Option Strict On Option Explicit On - - _ -Partial Friend NotInheritable Class MySettings - Inherits Global.System.Configuration.ApplicationSettingsBase - - Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings()),MySettings) +Namespace My + _ + Partial Friend NotInheritable Class MySettings + Inherits Global.System.Configuration.ApplicationSettingsBase + + Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings()),MySettings) + #Region "My.Settings 自动保存功能" #If _MyType = "WindowsForms" Then Private Shared addedHandler As Boolean @@ -35,10 +36,10 @@ Partial Friend NotInheritable Class MySettings End Sub #End If #End Region - - Public Shared ReadOnly Property [Default]() As MySettings - Get - + + Public Shared ReadOnly Property [Default]() As MySettings + Get + #If _MyType = "WindowsForms" Then If Not addedHandler Then SyncLock addedHandlerLockObject @@ -49,10 +50,11 @@ Partial Friend NotInheritable Class MySettings End SyncLock End If #End If - Return defaultInstance - End Get - End Property -End Class + Return defaultInstance + End Get + End Property + End Class +End Namespace Namespace My @@ -62,9 +64,9 @@ Namespace My Friend Module MySettingsProperty _ - Friend ReadOnly Property Settings() As Global.PCL.MySettings + Friend ReadOnly Property Settings() As Global.PCL.My.MySettings Get - Return Global.PCL.MySettings.Default + Return Global.PCL.My.MySettings.Default End Get End Property End Module diff --git a/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj b/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj index 8d3a3e6..9b2d8d8 100644 --- a/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj +++ b/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj @@ -139,6 +139,56 @@ BasicDesignGuidelineRules.ruleset false + + true + bin\x64\Debug\ + Plain Craft Launcher 2.xml + 1 + 41999,42016,42017,42018,42019,42020,42021,42022,42032,42036,42314 + full + x64 + MinimumRecommendedRules.ruleset + + + bin\x64\Release\ + RELEASE + Plain Craft Launcher 2.xml + true + 1 + 41999,42016,42017,42018,42019,42020,42021,42022,42032,42036,42314 + x64 + BasicDesignGuidelineRules.ruleset + + + bin\x64\Snapshot\ + SNAPSHOT + Plain Craft Launcher 2.xml + true + 1 + 41999,42016,42017,42018,42019,42020,42021,42022,42032,42036,42314 + x64 + BasicDesignGuidelineRules.ruleset + + + bin\x64\BETA\ + BETA + Plain Craft Launcher 2.xml + true + 1 + 41999,42016,42017,42018,42019,42020,42021,42022,42032,42036,42314 + x64 + BasicDesignGuidelineRules.ruleset + + + bin\x64\ReleaseUpdate\ + RELEASE,BY_UPDATE + Plain Craft Launcher 2.xml + true + 1 + 41999,42016,42017,42018,42019,42020,42021,42022,42032,42036,42314 + x64 + BasicDesignGuidelineRules.ruleset + False @@ -195,9 +245,6 @@ MyIconTextButton.xaml - - - @@ -501,7 +548,6 @@ FormMain.xaml Code - MSBuild:Compile Designer @@ -949,7 +995,6 @@ - From 9c9216c02cd802cd376d52a5e46673cfb66b181b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=84=E5=A5=9A=E6=A2=A6=E7=81=B5?= Date: Mon, 2 Jun 2025 13:20:21 +0800 Subject: [PATCH 3/9] =?UTF-8?q?revert:=20=E9=94=99=E8=AF=AF=E7=9A=84?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=9B=B4=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plain Craft Launcher 2/Modules/Base/ModNet.vb | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Plain Craft Launcher 2/Modules/Base/ModNet.vb b/Plain Craft Launcher 2/Modules/Base/ModNet.vb index cdc61e6..6e3ab5c 100644 --- a/Plain Craft Launcher 2/Modules/Base/ModNet.vb +++ b/Plain Craft Launcher 2/Modules/Base/ModNet.vb @@ -19,8 +19,7 @@ Public Module ModNet Optional UseBrowserUA As Boolean = False, Optional RequireHeaderSign As Boolean = False, Optional RequireCdnSign As Boolean = False) As HttpRequestMessage - Dim Watcher As New Stopwatch() - Watcher.Start() + Dim RequestMethod As HttpMethod = GetHttpMethod(Method) If RequireCdnSign Then RequestUrl = SecretCdnSign(RequestUrl) @@ -55,8 +54,7 @@ FixContentHeader: If RequireHeaderSign Then SecretHeadersSign(RequestUrl, requestMessage, UseBrowserUA) End If - Watcher.Stop() - Log($"[Test] GetRequestMessage 执行用时:{Watcher.ElapsedMilliseconds} 毫秒") + Return RequestMessage End Function @@ -67,8 +65,6 @@ FixContentHeader: Optional Encode As Encoding = Nothing, Optional Timeout As Integer = 25000, Optional RequireReturnRespObj As Boolean = False) - Dim Watcher As New Stopwatch() - Watcher.Start() Using CTS As New CancellationTokenSource(Timeout) Dim Client = ClientFactory.GetHttpClient() Dim Resp As HttpResponseMessage = Client.SendAsync(Req, HttpCompletionOption.ResponseHeadersRead, CTS.Token).GetAwaiter().GetResult() @@ -83,8 +79,7 @@ FixContentHeader: Dim Bytes = DataStream.ToArray() Resp.Dispose() Req.Dispose() - Watcher.Stop() - Log($"[Test] SendRequest 执行用时:{Watcher.ElapsedMilliseconds} 毫秒") + If RequireDecode Then If Encode Is Nothing Then Encode = Encoding.UTF8 Return Encode.GetString(Bytes) From 70a2f28073d3e361320b84b9f0e7cba08e71e924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=84=E5=A5=9A=E6=A2=A6=E7=81=B5?= Date: Mon, 2 Jun 2025 13:27:23 +0800 Subject: [PATCH 4/9] =?UTF-8?q?revert:=20=E9=94=99=E8=AF=AF=E7=9A=84?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=9B=B4=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plain Craft Launcher 2.sln | 16 +- Plain Craft Launcher 2/App.config | 66 +++--- .../My Project/Resources.Designer.vb | 204 +++++------------- .../Plain Craft Launcher 2.vbproj | 55 +---- 4 files changed, 92 insertions(+), 249 deletions(-) diff --git a/Plain Craft Launcher 2.sln b/Plain Craft Launcher 2.sln index 40c68b7..e48dc61 100644 --- a/Plain Craft Launcher 2.sln +++ b/Plain Craft Launcher 2.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.2.32616.157 @@ -8,31 +8,19 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Beta|Any CPU = Beta|Any CPU - Beta|x64 = Beta|x64 Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 ReleaseUpdate|Any CPU = ReleaseUpdate|Any CPU - ReleaseUpdate|x64 = ReleaseUpdate|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Beta|Any CPU.ActiveCfg = BETA|Any CPU {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Beta|Any CPU.Build.0 = BETA|Any CPU - {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Beta|x64.ActiveCfg = BETA|x64 - {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Beta|x64.Build.0 = BETA|x64 {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Debug|x64.ActiveCfg = Debug|x64 - {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Debug|x64.Build.0 = Debug|x64 {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Release|Any CPU.ActiveCfg = Release|Any CPU {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Release|Any CPU.Build.0 = Release|Any CPU - {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Release|x64.ActiveCfg = Release|x64 - {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Release|x64.Build.0 = Release|x64 {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.ReleaseUpdate|Any CPU.ActiveCfg = ReleaseUpdate|Any CPU {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.ReleaseUpdate|Any CPU.Build.0 = ReleaseUpdate|Any CPU - {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.ReleaseUpdate|x64.ActiveCfg = ReleaseUpdate|x64 - {4499F2D2-18EF-4238-B1F0-7BD547AFF925}.ReleaseUpdate|x64.Build.0 = ReleaseUpdate|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -46,4 +34,4 @@ Global GlobalSection(Performance) = preSolution HasPerformanceSessions = true EndGlobalSection -EndGlobal +EndGlobal \ No newline at end of file diff --git a/Plain Craft Launcher 2/App.config b/Plain Craft Launcher 2/App.config index 0f0f321..e9bc7c7 100644 --- a/Plain Craft Launcher 2/App.config +++ b/Plain Craft Launcher 2/App.config @@ -1,35 +1,37 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Plain Craft Launcher 2/My Project/Resources.Designer.vb b/Plain Craft Launcher 2/My Project/Resources.Designer.vb index 81e998d..02b1744 100644 --- a/Plain Craft Launcher 2/My Project/Resources.Designer.vb +++ b/Plain Craft Launcher 2/My Project/Resources.Designer.vb @@ -1,4 +1,4 @@ -'------------------------------------------------------------------------------ +'------------------------------------------------------------------------------ ' ' 此代码由工具生成。 ' 运行时版本:4.0.30319.42000 @@ -11,163 +11,61 @@ Option Strict On Option Explicit On -Imports System -Namespace My.Resources + + _ +Partial Friend NotInheritable Class MySettings + Inherits Global.System.Configuration.ApplicationSettingsBase - '此类是由 StronglyTypedResourceBuilder - '类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 - '若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen - '(以 /str 作为命令选项),或重新生成 VS 项目。 - ''' - ''' 一个强类型的资源类,用于查找本地化的字符串等。 - ''' - _ - Friend Module Resources - - Private resourceMan As Global.System.Resources.ResourceManager - - Private resourceCulture As Global.System.Globalization.CultureInfo - - ''' - ''' 返回此类使用的缓存的 ResourceManager 实例。 - ''' - _ - Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager - Get - If Object.ReferenceEquals(resourceMan, Nothing) Then - Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("PCL.Resources", GetType(Resources).Assembly) - resourceMan = temp + Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings()),MySettings) + +#Region "My.Settings 自动保存功能" +#If _MyType = "WindowsForms" Then + Private Shared addedHandler As Boolean + + Private Shared addedHandlerLockObject As New Object + + _ + Private Shared Sub AutoSaveSettings(sender As Global.System.Object, e As Global.System.EventArgs) + If My.Application.SaveMySettingsOnExit Then + My.Settings.Save() + End If + End Sub +#End If +#End Region + + Public Shared ReadOnly Property [Default]() As MySettings + Get + +#If _MyType = "WindowsForms" Then + If Not addedHandler Then + SyncLock addedHandlerLockObject + If Not addedHandler Then + AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings + addedHandler = True + End If + End SyncLock End If - Return resourceMan - End Get - End Property +#End If + Return defaultInstance + End Get + End Property +End Class + +Namespace My + + _ + Friend Module MySettingsProperty - ''' - ''' 重写当前线程的 CurrentUICulture 属性,对 - ''' 使用此强类型资源类的所有资源查找执行重写。 - ''' - _ - Friend Property Culture() As Global.System.Globalization.CultureInfo + _ + Friend ReadOnly Property Settings() As Global.PCL.MySettings Get - Return resourceCulture - End Get - Set - resourceCulture = value - End Set - End Property - - ''' - ''' 查找 System.Byte[] 类型的本地化资源。 - ''' - Friend ReadOnly Property Custom() As Byte() - Get - Dim obj As Object = ResourceManager.GetObject("Custom", resourceCulture) - Return CType(obj,Byte()) - End Get - End Property - - ''' - ''' 查找 System.Byte[] 类型的本地化资源。 - ''' - Friend ReadOnly Property Dialogs() As Byte() - Get - Dim obj As Object = ResourceManager.GetObject("Dialogs", resourceCulture) - Return CType(obj,Byte()) - End Get - End Property - - ''' - ''' 查找 System.Byte[] 类型的本地化资源。 - ''' - Friend ReadOnly Property ForgeInstaller() As Byte() - Get - Dim obj As Object = ResourceManager.GetObject("ForgeInstaller", resourceCulture) - Return CType(obj,Byte()) - End Get - End Property - - ''' - ''' 查找 System.Byte[] 类型的本地化资源。 - ''' - Friend ReadOnly Property Help() As Byte() - Get - Dim obj As Object = ResourceManager.GetObject("Help", resourceCulture) - Return CType(obj,Byte()) - End Get - End Property - - ''' - ''' 查找 System.Byte[] 类型的本地化资源。 - ''' - Friend ReadOnly Property Imazen_WebP() As Byte() - Get - Dim obj As Object = ResourceManager.GetObject("Imazen_WebP", resourceCulture) - Return CType(obj,Byte()) - End Get - End Property - - ''' - ''' 查找 System.Byte[] 类型的本地化资源。 - ''' - Friend ReadOnly Property JavaWrapper() As Byte() - Get - Dim obj As Object = ResourceManager.GetObject("JavaWrapper", resourceCulture) - Return CType(obj,Byte()) - End Get - End Property - - ''' - ''' 查找 System.Byte[] 类型的本地化资源。 - ''' - Friend ReadOnly Property Json() As Byte() - Get - Dim obj As Object = ResourceManager.GetObject("Json", resourceCulture) - Return CType(obj,Byte()) - End Get - End Property - - ''' - ''' 查找 System.Byte[] 类型的本地化资源。 - ''' - Friend ReadOnly Property libwebp64() As Byte() - Get - Dim obj As Object = ResourceManager.GetObject("libwebp64", resourceCulture) - Return CType(obj,Byte()) - End Get - End Property - - ''' - ''' 查找 System.Byte[] 类型的本地化资源。 - ''' - Friend ReadOnly Property ModData() As Byte() - Get - Dim obj As Object = ResourceManager.GetObject("ModData", resourceCulture) - Return CType(obj,Byte()) - End Get - End Property - - ''' - ''' 查找 System.Byte[] 类型的本地化资源。 - ''' - Friend ReadOnly Property NAudio() As Byte() - Get - Dim obj As Object = ResourceManager.GetObject("NAudio", resourceCulture) - Return CType(obj,Byte()) - End Get - End Property - - ''' - ''' 查找 System.Byte[] 类型的本地化资源。 - ''' - Friend ReadOnly Property Transformer() As Byte() - Get - Dim obj As Object = ResourceManager.GetObject("Transformer", resourceCulture) - Return CType(obj,Byte()) + Return Global.PCL.MySettings.Default End Get End Property End Module -End Namespace +End Namespace \ No newline at end of file diff --git a/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj b/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj index 9b2d8d8..8d3a3e6 100644 --- a/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj +++ b/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj @@ -139,56 +139,6 @@ BasicDesignGuidelineRules.ruleset false - - true - bin\x64\Debug\ - Plain Craft Launcher 2.xml - 1 - 41999,42016,42017,42018,42019,42020,42021,42022,42032,42036,42314 - full - x64 - MinimumRecommendedRules.ruleset - - - bin\x64\Release\ - RELEASE - Plain Craft Launcher 2.xml - true - 1 - 41999,42016,42017,42018,42019,42020,42021,42022,42032,42036,42314 - x64 - BasicDesignGuidelineRules.ruleset - - - bin\x64\Snapshot\ - SNAPSHOT - Plain Craft Launcher 2.xml - true - 1 - 41999,42016,42017,42018,42019,42020,42021,42022,42032,42036,42314 - x64 - BasicDesignGuidelineRules.ruleset - - - bin\x64\BETA\ - BETA - Plain Craft Launcher 2.xml - true - 1 - 41999,42016,42017,42018,42019,42020,42021,42022,42032,42036,42314 - x64 - BasicDesignGuidelineRules.ruleset - - - bin\x64\ReleaseUpdate\ - RELEASE,BY_UPDATE - Plain Craft Launcher 2.xml - true - 1 - 41999,42016,42017,42018,42019,42020,42021,42022,42032,42036,42314 - x64 - BasicDesignGuidelineRules.ruleset - False @@ -245,6 +195,9 @@ MyIconTextButton.xaml + + + @@ -548,6 +501,7 @@ FormMain.xaml Code + MSBuild:Compile Designer @@ -995,6 +949,7 @@ + From 3adf47aa2c466e9a8b9bbeb51eebae7b5304f1a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=84=E5=A5=9A=E6=A2=A6=E7=81=B5?= Date: Mon, 2 Jun 2025 13:40:43 +0800 Subject: [PATCH 5/9] =?UTF-8?q?fix:=20=E7=BC=96=E8=AF=91=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CO-Authored-by: jiangyanyue --- Plain Craft Launcher 2.sln | 4 +- Plain Craft Launcher 2/App.config | 64 ++-- .../Reference/SystemDropShadowChrome.vb | 299 ++++++++++++++++++ .../My Project/Resources.Designer.vb | 204 +++++++++--- .../My Project/Settings.Designer.vb | 38 ++- .../Resources/Language/en_US.xaml | 10 + .../Resources/Language/zh_CN.xaml | 10 + 7 files changed, 523 insertions(+), 106 deletions(-) create mode 100644 Plain Craft Launcher 2/Modules/Reference/SystemDropShadowChrome.vb create mode 100644 Plain Craft Launcher 2/Resources/Language/en_US.xaml create mode 100644 Plain Craft Launcher 2/Resources/Language/zh_CN.xaml diff --git a/Plain Craft Launcher 2.sln b/Plain Craft Launcher 2.sln index e48dc61..b10e79d 100644 --- a/Plain Craft Launcher 2.sln +++ b/Plain Craft Launcher 2.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.2.32616.157 @@ -34,4 +34,4 @@ Global GlobalSection(Performance) = preSolution HasPerformanceSessions = true EndGlobalSection -EndGlobal \ No newline at end of file +EndGlobal diff --git a/Plain Craft Launcher 2/App.config b/Plain Craft Launcher 2/App.config index e9bc7c7..8d134eb 100644 --- a/Plain Craft Launcher 2/App.config +++ b/Plain Craft Launcher 2/App.config @@ -1,37 +1,35 @@ - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/Plain Craft Launcher 2/Modules/Reference/SystemDropShadowChrome.vb b/Plain Craft Launcher 2/Modules/Reference/SystemDropShadowChrome.vb new file mode 100644 index 0000000..6e103cf --- /dev/null +++ b/Plain Craft Launcher 2/Modules/Reference/SystemDropShadowChrome.vb @@ -0,0 +1,299 @@ +Public NotInheritable Class SystemDropShadowChrome + Inherits Decorator + + '源码来源于:https://referencesource.microsoft.com/#PresentationFramework.Aero/parent/Shared/Microsoft/Windows/Themes/SystemDropShadowChrome.cs,6d9c27d92a8128c1 + + Public Shared ReadOnly ColorProperty As DependencyProperty = DependencyProperty.Register("Color", GetType(Color), GetType(SystemDropShadowChrome), New FrameworkPropertyMetadata(Color.FromArgb(&H71, &H0, &H0, &H0), FrameworkPropertyMetadataOptions.AffectsRender, New PropertyChangedCallback(AddressOf ClearBrushes))) + Public Property Color As Color + Get + Return CType(GetValue(ColorProperty), Color) + End Get + Set(value As Color) + SetValue(ColorProperty, value) + End Set + End Property + + Public Shared ReadOnly CornerRadiusProperty As DependencyProperty = DependencyProperty.Register("CornerRadius", GetType(CornerRadius), GetType(SystemDropShadowChrome), New FrameworkPropertyMetadata(New CornerRadius(), FrameworkPropertyMetadataOptions.AffectsRender, New PropertyChangedCallback(AddressOf ClearBrushes)), New ValidateValueCallback(AddressOf IsCornerRadiusValid)) + Public Property CornerRadius As CornerRadius + Get + Return CType(GetValue(CornerRadiusProperty), CornerRadius) + End Get + Set(value As CornerRadius) + SetValue(CornerRadiusProperty, value) + End Set + End Property + Private Shared Function IsCornerRadiusValid(value As Object) As Boolean + Dim cr As CornerRadius = CType(value, CornerRadius) + Return Not (cr.TopLeft < 0.0 OrElse cr.TopRight < 0.0 OrElse cr.BottomLeft < 0.0 OrElse cr.BottomRight < 0.0 OrElse Double.IsNaN(cr.TopLeft) OrElse Double.IsNaN(cr.TopRight) OrElse Double.IsNaN(cr.BottomLeft) OrElse Double.IsNaN(cr.BottomRight) OrElse Double.IsInfinity(cr.TopLeft) OrElse Double.IsInfinity(cr.TopRight) OrElse Double.IsInfinity(cr.BottomLeft) OrElse Double.IsInfinity(cr.BottomRight)) + End Function + + Private Const ShadowDepth As Double = 5 + + Protected Overrides Sub OnRender(drawingContext As DrawingContext) + Dim cornerRadius As CornerRadius = Me.CornerRadius + Dim shadowBounds As New Rect(New Point(ShadowDepth, ShadowDepth), New Size(RenderSize.Width, RenderSize.Height)) + Dim color As Color = Me.Color + + If shadowBounds.Width > 0 AndAlso shadowBounds.Height > 0 AndAlso color.A > 0 Then + Dim centerWidth As Double = shadowBounds.Right - shadowBounds.Left - 2 * ShadowDepth + Dim centerHeight As Double = shadowBounds.Bottom - shadowBounds.Top - 2 * ShadowDepth + Dim maxRadius As Double = Math.Min(centerWidth * 0.5, centerHeight * 0.5) + cornerRadius.TopLeft = Math.Min(cornerRadius.TopLeft, maxRadius) + cornerRadius.TopRight = Math.Min(cornerRadius.TopRight, maxRadius) + cornerRadius.BottomLeft = Math.Min(cornerRadius.BottomLeft, maxRadius) + cornerRadius.BottomRight = Math.Min(cornerRadius.BottomRight, maxRadius) + Dim brushes As Brush() = GetBrushes(color, cornerRadius) + Dim centerTop As Double = shadowBounds.Top + ShadowDepth + Dim centerLeft As Double = shadowBounds.Left + ShadowDepth + Dim centerRight As Double = shadowBounds.Right - ShadowDepth + Dim centerBottom As Double = shadowBounds.Bottom - ShadowDepth + Dim guidelineSetX As Double() = New Double() {centerLeft, centerLeft + cornerRadius.TopLeft, centerRight - cornerRadius.TopRight, centerLeft + cornerRadius.BottomLeft, centerRight - cornerRadius.BottomRight, centerRight} + Dim guidelineSetY As Double() = New Double() {centerTop, centerTop + cornerRadius.TopLeft, centerTop + cornerRadius.TopRight, centerBottom - cornerRadius.BottomLeft, centerBottom - cornerRadius.BottomRight, centerBottom} + drawingContext.PushGuidelineSet(New GuidelineSet(guidelineSetX, guidelineSetY)) + cornerRadius.TopLeft += ShadowDepth + cornerRadius.TopRight += ShadowDepth + cornerRadius.BottomLeft += ShadowDepth + cornerRadius.BottomRight += ShadowDepth + Dim topLeft As New Rect(shadowBounds.Left, shadowBounds.Top, cornerRadius.TopLeft, cornerRadius.TopLeft) + drawingContext.DrawRectangle(brushes(Placement.TopLeft), Nothing, topLeft) + Dim topWidth As Double = guidelineSetX(2) - guidelineSetX(1) + + If topWidth > 0 Then + Dim top As New Rect(guidelineSetX(1), shadowBounds.Top, topWidth, ShadowDepth) + drawingContext.DrawRectangle(brushes(Placement.Top), Nothing, top) + End If + + Dim topRight As New Rect(guidelineSetX(2), shadowBounds.Top, cornerRadius.TopRight, cornerRadius.TopRight) + drawingContext.DrawRectangle(brushes(Placement.TopRight), Nothing, topRight) + Dim leftHeight As Double = guidelineSetY(3) - guidelineSetY(1) + + If leftHeight > 0 Then + Dim left As New Rect(shadowBounds.Left, guidelineSetY(1), ShadowDepth, leftHeight) + drawingContext.DrawRectangle(brushes(Placement.Left), Nothing, left) + End If + + Dim rightHeight As Double = guidelineSetY(4) - guidelineSetY(2) + + If rightHeight > 0 Then + Dim right As New Rect(guidelineSetX(5), guidelineSetY(2), ShadowDepth, rightHeight) + drawingContext.DrawRectangle(brushes(Placement.Right), Nothing, right) + End If + + Dim bottomLeft As New Rect(shadowBounds.Left, guidelineSetY(3), cornerRadius.BottomLeft, cornerRadius.BottomLeft) + drawingContext.DrawRectangle(brushes(Placement.BottomLeft), Nothing, bottomLeft) + Dim bottomWidth As Double = guidelineSetX(4) - guidelineSetX(3) + + If bottomWidth > 0 Then + Dim bottom As New Rect(guidelineSetX(3), guidelineSetY(5), bottomWidth, ShadowDepth) + drawingContext.DrawRectangle(brushes(Placement.Bottom), Nothing, bottom) + End If + + Dim bottomRight As New Rect(guidelineSetX(4), guidelineSetY(4), cornerRadius.BottomRight, cornerRadius.BottomRight) + drawingContext.DrawRectangle(brushes(Placement.BottomRight), Nothing, bottomRight) + + If cornerRadius.TopLeft = ShadowDepth AndAlso cornerRadius.TopLeft = cornerRadius.TopRight AndAlso cornerRadius.TopLeft = cornerRadius.BottomLeft AndAlso cornerRadius.TopLeft = cornerRadius.BottomRight Then + Dim center As New Rect(guidelineSetX(0), guidelineSetY(0), centerWidth, centerHeight) + drawingContext.DrawRectangle(brushes(Placement.Center), Nothing, center) + Else + Dim figure As New PathFigure() + + If cornerRadius.TopLeft > ShadowDepth Then + figure.StartPoint = New Point(guidelineSetX(1), guidelineSetY(0)) + figure.Segments.Add(New LineSegment(New Point(guidelineSetX(1), guidelineSetY(1)), True)) + figure.Segments.Add(New LineSegment(New Point(guidelineSetX(0), guidelineSetY(1)), True)) + Else + figure.StartPoint = New Point(guidelineSetX(0), guidelineSetY(0)) + End If + + If cornerRadius.BottomLeft > ShadowDepth Then + figure.Segments.Add(New LineSegment(New Point(guidelineSetX(0), guidelineSetY(3)), True)) + figure.Segments.Add(New LineSegment(New Point(guidelineSetX(3), guidelineSetY(3)), True)) + figure.Segments.Add(New LineSegment(New Point(guidelineSetX(3), guidelineSetY(5)), True)) + Else + figure.Segments.Add(New LineSegment(New Point(guidelineSetX(0), guidelineSetY(5)), True)) + End If + + If cornerRadius.BottomRight > ShadowDepth Then + figure.Segments.Add(New LineSegment(New Point(guidelineSetX(4), guidelineSetY(5)), True)) + figure.Segments.Add(New LineSegment(New Point(guidelineSetX(4), guidelineSetY(4)), True)) + figure.Segments.Add(New LineSegment(New Point(guidelineSetX(5), guidelineSetY(4)), True)) + Else + figure.Segments.Add(New LineSegment(New Point(guidelineSetX(5), guidelineSetY(5)), True)) + End If + + If cornerRadius.TopRight > ShadowDepth Then + figure.Segments.Add(New LineSegment(New Point(guidelineSetX(5), guidelineSetY(2)), True)) + figure.Segments.Add(New LineSegment(New Point(guidelineSetX(2), guidelineSetY(2)), True)) + figure.Segments.Add(New LineSegment(New Point(guidelineSetX(2), guidelineSetY(0)), True)) + Else + figure.Segments.Add(New LineSegment(New Point(guidelineSetX(5), guidelineSetY(0)), True)) + End If + + figure.IsClosed = True + figure.Freeze() + Dim geometry As New PathGeometry() + geometry.Figures.Add(figure) + geometry.Freeze() + drawingContext.DrawGeometry(brushes(Placement.Center), Nothing, geometry) + End If + + drawingContext.Pop() + End If + End Sub + + Private Enum Placement + TopLeft = 0 + Top = 1 + TopRight = 2 + Left = 3 + Center = 4 + Right = 5 + BottomLeft = 6 + Bottom = 7 + BottomRight = 8 + End Enum + Private Shared _commonBrushes As Brush() + Private Shared _commonCornerRadius As CornerRadius + Private Shared ReadOnly _resourceAccess As New Object() + Private _brushes As Brush() + + Private Shared Sub ClearBrushes(o As DependencyObject, e As DependencyPropertyChangedEventArgs) + CType(o, SystemDropShadowChrome)._brushes = Nothing + End Sub + Private Shared Function CreateStops(c As Color, cornerRadius As Double) As GradientStopCollection + Dim gradientScale As Double = 1 / (cornerRadius + ShadowDepth) + Dim gsc As New GradientStopCollection From { + New GradientStop(c, (0.5 + cornerRadius) * gradientScale) + } + Dim stopColor As Color = c + stopColor.A = CByte(0.74336 * c.A) + gsc.Add(New GradientStop(stopColor, (1.5 + cornerRadius) * gradientScale)) + stopColor.A = CByte(0.38053 * c.A) + gsc.Add(New GradientStop(stopColor, (2.5 + cornerRadius) * gradientScale)) + stopColor.A = CByte(0.12389 * c.A) + gsc.Add(New GradientStop(stopColor, (3.5 + cornerRadius) * gradientScale)) + stopColor.A = CByte(0.02654 * c.A) + gsc.Add(New GradientStop(stopColor, (4.5 + cornerRadius) * gradientScale)) + stopColor.A = 0 + gsc.Add(New GradientStop(stopColor, (5 + cornerRadius) * gradientScale)) + gsc.Freeze() + Return gsc + End Function + Private Shared Function CreateBrushes(c As Color, cornerRadius As CornerRadius) As Brush() + Dim brushes As Brush() = New Brush(8) {} + brushes(Placement.Center) = New SolidColorBrush(c) + brushes(Placement.Center).Freeze() + Dim sideStops As GradientStopCollection = CreateStops(c, 0) + Dim top As New LinearGradientBrush(sideStops, New Point(0, 1), New Point(0, 0)) + top.Freeze() + brushes(Placement.Top) = top + Dim left As New LinearGradientBrush(sideStops, New Point(1, 0), New Point(0, 0)) + left.Freeze() + brushes(Placement.Left) = left + Dim right As New LinearGradientBrush(sideStops, New Point(0, 0), New Point(1, 0)) + right.Freeze() + brushes(Placement.Right) = right + Dim bottom As New LinearGradientBrush(sideStops, New Point(0, 0), New Point(0, 1)) + bottom.Freeze() + brushes(Placement.Bottom) = bottom + Dim topLeftStops As GradientStopCollection + + If cornerRadius.TopLeft = 0 Then + topLeftStops = sideStops + Else + topLeftStops = CreateStops(c, cornerRadius.TopLeft) + End If + + Dim topLeft As New RadialGradientBrush(topLeftStops) With { + .RadiusX = 1, + .RadiusY = 1, + .Center = New Point(1, 1), + .GradientOrigin = New Point(1, 1) + } + topLeft.Freeze() + brushes(Placement.TopLeft) = topLeft + Dim topRightStops As GradientStopCollection + + If cornerRadius.TopRight = 0 Then + topRightStops = sideStops + ElseIf cornerRadius.TopRight = cornerRadius.TopLeft Then + topRightStops = topLeftStops + Else + topRightStops = CreateStops(c, cornerRadius.TopRight) + End If + + Dim topRight As New RadialGradientBrush(topRightStops) With { + .RadiusX = 1, + .RadiusY = 1, + .Center = New Point(0, 1), + .GradientOrigin = New Point(0, 1) + } + topRight.Freeze() + brushes(Placement.TopRight) = topRight + Dim bottomLeftStops As GradientStopCollection + + If cornerRadius.BottomLeft = 0 Then + bottomLeftStops = sideStops + ElseIf cornerRadius.BottomLeft = cornerRadius.TopLeft Then + bottomLeftStops = topLeftStops + ElseIf cornerRadius.BottomLeft = cornerRadius.TopRight Then + bottomLeftStops = topRightStops + Else + bottomLeftStops = CreateStops(c, cornerRadius.BottomLeft) + End If + + Dim bottomLeft As New RadialGradientBrush(bottomLeftStops) With { + .RadiusX = 1, + .RadiusY = 1, + .Center = New Point(1, 0), + .GradientOrigin = New Point(1, 0) + } + bottomLeft.Freeze() + brushes(Placement.BottomLeft) = bottomLeft + Dim bottomRightStops As GradientStopCollection + + If cornerRadius.BottomRight = 0 Then + bottomRightStops = sideStops + ElseIf cornerRadius.BottomRight = cornerRadius.TopLeft Then + bottomRightStops = topLeftStops + ElseIf cornerRadius.BottomRight = cornerRadius.TopRight Then + bottomRightStops = topRightStops + ElseIf cornerRadius.BottomRight = cornerRadius.BottomLeft Then + bottomRightStops = bottomLeftStops + Else + bottomRightStops = CreateStops(c, cornerRadius.BottomRight) + End If + + Dim bottomRight As New RadialGradientBrush(bottomRightStops) With { + .RadiusX = 1, + .RadiusY = 1, + .Center = New Point(0, 0), + .GradientOrigin = New Point(0, 0) + } + bottomRight.Freeze() + brushes(Placement.BottomRight) = bottomRight + Return brushes + End Function + Private Function GetBrushes(c As Color, cornerRadius As CornerRadius) As Brush() + If _commonBrushes Is Nothing Then + + SyncLock _resourceAccess + + If _commonBrushes Is Nothing Then + _commonBrushes = CreateBrushes(c, cornerRadius) + _commonCornerRadius = cornerRadius + End If + End SyncLock + End If + + If c = CType(_commonBrushes(Placement.Center), SolidColorBrush).Color AndAlso cornerRadius = _commonCornerRadius Then + _brushes = Nothing + Return _commonBrushes + ElseIf _brushes Is Nothing Then + _brushes = CreateBrushes(c, cornerRadius) + End If + + Return _brushes + End Function + +End Class diff --git a/Plain Craft Launcher 2/My Project/Resources.Designer.vb b/Plain Craft Launcher 2/My Project/Resources.Designer.vb index 02b1744..81e998d 100644 --- a/Plain Craft Launcher 2/My Project/Resources.Designer.vb +++ b/Plain Craft Launcher 2/My Project/Resources.Designer.vb @@ -1,4 +1,4 @@ -'------------------------------------------------------------------------------ +'------------------------------------------------------------------------------ ' ' 此代码由工具生成。 ' 运行时版本:4.0.30319.42000 @@ -11,61 +11,163 @@ Option Strict On Option Explicit On +Imports System - - _ -Partial Friend NotInheritable Class MySettings - Inherits Global.System.Configuration.ApplicationSettingsBase +Namespace My.Resources - Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings()),MySettings) - -#Region "My.Settings 自动保存功能" -#If _MyType = "WindowsForms" Then - Private Shared addedHandler As Boolean - - Private Shared addedHandlerLockObject As New Object - - _ - Private Shared Sub AutoSaveSettings(sender As Global.System.Object, e As Global.System.EventArgs) - If My.Application.SaveMySettingsOnExit Then - My.Settings.Save() - End If - End Sub -#End If -#End Region - - Public Shared ReadOnly Property [Default]() As MySettings - Get - -#If _MyType = "WindowsForms" Then - If Not addedHandler Then - SyncLock addedHandlerLockObject - If Not addedHandler Then - AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings - addedHandler = True - End If - End SyncLock - End If -#End If - Return defaultInstance - End Get - End Property -End Class - -Namespace My - - + ''' 一个强类型的资源类,用于查找本地化的字符串等。 + ''' + _ - Friend Module MySettingsProperty + Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute(), _ + Global.Microsoft.VisualBasic.HideModuleNameAttribute()> _ + Friend Module Resources - _ - Friend ReadOnly Property Settings() As Global.PCL.MySettings + Private resourceMan As Global.System.Resources.ResourceManager + + Private resourceCulture As Global.System.Globalization.CultureInfo + + ''' + ''' 返回此类使用的缓存的 ResourceManager 实例。 + ''' + _ + Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager Get - Return Global.PCL.MySettings.Default + If Object.ReferenceEquals(resourceMan, Nothing) Then + Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("PCL.Resources", GetType(Resources).Assembly) + resourceMan = temp + End If + Return resourceMan + End Get + End Property + + ''' + ''' 重写当前线程的 CurrentUICulture 属性,对 + ''' 使用此强类型资源类的所有资源查找执行重写。 + ''' + _ + Friend Property Culture() As Global.System.Globalization.CultureInfo + Get + Return resourceCulture + End Get + Set + resourceCulture = value + End Set + End Property + + ''' + ''' 查找 System.Byte[] 类型的本地化资源。 + ''' + Friend ReadOnly Property Custom() As Byte() + Get + Dim obj As Object = ResourceManager.GetObject("Custom", resourceCulture) + Return CType(obj,Byte()) + End Get + End Property + + ''' + ''' 查找 System.Byte[] 类型的本地化资源。 + ''' + Friend ReadOnly Property Dialogs() As Byte() + Get + Dim obj As Object = ResourceManager.GetObject("Dialogs", resourceCulture) + Return CType(obj,Byte()) + End Get + End Property + + ''' + ''' 查找 System.Byte[] 类型的本地化资源。 + ''' + Friend ReadOnly Property ForgeInstaller() As Byte() + Get + Dim obj As Object = ResourceManager.GetObject("ForgeInstaller", resourceCulture) + Return CType(obj,Byte()) + End Get + End Property + + ''' + ''' 查找 System.Byte[] 类型的本地化资源。 + ''' + Friend ReadOnly Property Help() As Byte() + Get + Dim obj As Object = ResourceManager.GetObject("Help", resourceCulture) + Return CType(obj,Byte()) + End Get + End Property + + ''' + ''' 查找 System.Byte[] 类型的本地化资源。 + ''' + Friend ReadOnly Property Imazen_WebP() As Byte() + Get + Dim obj As Object = ResourceManager.GetObject("Imazen_WebP", resourceCulture) + Return CType(obj,Byte()) + End Get + End Property + + ''' + ''' 查找 System.Byte[] 类型的本地化资源。 + ''' + Friend ReadOnly Property JavaWrapper() As Byte() + Get + Dim obj As Object = ResourceManager.GetObject("JavaWrapper", resourceCulture) + Return CType(obj,Byte()) + End Get + End Property + + ''' + ''' 查找 System.Byte[] 类型的本地化资源。 + ''' + Friend ReadOnly Property Json() As Byte() + Get + Dim obj As Object = ResourceManager.GetObject("Json", resourceCulture) + Return CType(obj,Byte()) + End Get + End Property + + ''' + ''' 查找 System.Byte[] 类型的本地化资源。 + ''' + Friend ReadOnly Property libwebp64() As Byte() + Get + Dim obj As Object = ResourceManager.GetObject("libwebp64", resourceCulture) + Return CType(obj,Byte()) + End Get + End Property + + ''' + ''' 查找 System.Byte[] 类型的本地化资源。 + ''' + Friend ReadOnly Property ModData() As Byte() + Get + Dim obj As Object = ResourceManager.GetObject("ModData", resourceCulture) + Return CType(obj,Byte()) + End Get + End Property + + ''' + ''' 查找 System.Byte[] 类型的本地化资源。 + ''' + Friend ReadOnly Property NAudio() As Byte() + Get + Dim obj As Object = ResourceManager.GetObject("NAudio", resourceCulture) + Return CType(obj,Byte()) + End Get + End Property + + ''' + ''' 查找 System.Byte[] 类型的本地化资源。 + ''' + Friend ReadOnly Property Transformer() As Byte() + Get + Dim obj As Object = ResourceManager.GetObject("Transformer", resourceCulture) + Return CType(obj,Byte()) End Get End Property End Module -End Namespace \ No newline at end of file +End Namespace diff --git a/Plain Craft Launcher 2/My Project/Settings.Designer.vb b/Plain Craft Launcher 2/My Project/Settings.Designer.vb index 502e28c..4fa2aee 100644 --- a/Plain Craft Launcher 2/My Project/Settings.Designer.vb +++ b/Plain Craft Launcher 2/My Project/Settings.Designer.vb @@ -12,16 +12,15 @@ Option Strict On Option Explicit On -Namespace My + + _ +Partial Friend NotInheritable Class MySettings + Inherits Global.System.Configuration.ApplicationSettingsBase + + Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings()),MySettings) - _ - Partial Friend NotInheritable Class MySettings - Inherits Global.System.Configuration.ApplicationSettingsBase - - Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings()),MySettings) - #Region "My.Settings 自动保存功能" #If _MyType = "WindowsForms" Then Private Shared addedHandler As Boolean @@ -36,10 +35,10 @@ Namespace My End Sub #End If #End Region - - Public Shared ReadOnly Property [Default]() As MySettings - Get - + + Public Shared ReadOnly Property [Default]() As MySettings + Get + #If _MyType = "WindowsForms" Then If Not addedHandler Then SyncLock addedHandlerLockObject @@ -50,11 +49,10 @@ Namespace My End SyncLock End If #End If - Return defaultInstance - End Get - End Property - End Class -End Namespace + Return defaultInstance + End Get + End Property +End Class Namespace My @@ -64,9 +62,9 @@ Namespace My Friend Module MySettingsProperty _ - Friend ReadOnly Property Settings() As Global.PCL.My.MySettings + Friend ReadOnly Property Settings() As Global.PCL.MySettings Get - Return Global.PCL.My.MySettings.Default + Return Global.PCL.MySettings.Default End Get End Property End Module diff --git a/Plain Craft Launcher 2/Resources/Language/en_US.xaml b/Plain Craft Launcher 2/Resources/Language/en_US.xaml new file mode 100644 index 0000000..f0714da --- /dev/null +++ b/Plain Craft Launcher 2/Resources/Language/en_US.xaml @@ -0,0 +1,10 @@ + + + More + Download + Launch + Setting + + diff --git a/Plain Craft Launcher 2/Resources/Language/zh_CN.xaml b/Plain Craft Launcher 2/Resources/Language/zh_CN.xaml new file mode 100644 index 0000000..a92d95b --- /dev/null +++ b/Plain Craft Launcher 2/Resources/Language/zh_CN.xaml @@ -0,0 +1,10 @@ + + + 更多 + 下载 + 启动 + 设置 + + From 7ecf306445ebf2a121c3bdaaa3e18151b5f771e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=84=E5=A5=9A=E6=A2=A6=E7=81=B5?= Date: Mon, 2 Jun 2025 13:44:17 +0800 Subject: [PATCH 6/9] =?UTF-8?q?chore:=20=E5=BC=84=E9=94=99=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plain Craft Launcher 2/App.config | 2 +- .../Reference/SystemDropShadowChrome.vb | 299 ------------------ .../Resources/Language/en_US.xaml | 10 - .../Resources/Language/zh_CN.xaml | 10 - 4 files changed, 1 insertion(+), 320 deletions(-) delete mode 100644 Plain Craft Launcher 2/Modules/Reference/SystemDropShadowChrome.vb delete mode 100644 Plain Craft Launcher 2/Resources/Language/en_US.xaml delete mode 100644 Plain Craft Launcher 2/Resources/Language/zh_CN.xaml diff --git a/Plain Craft Launcher 2/App.config b/Plain Craft Launcher 2/App.config index 8d134eb..d464350 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/Modules/Reference/SystemDropShadowChrome.vb b/Plain Craft Launcher 2/Modules/Reference/SystemDropShadowChrome.vb deleted file mode 100644 index 6e103cf..0000000 --- a/Plain Craft Launcher 2/Modules/Reference/SystemDropShadowChrome.vb +++ /dev/null @@ -1,299 +0,0 @@ -Public NotInheritable Class SystemDropShadowChrome - Inherits Decorator - - '源码来源于:https://referencesource.microsoft.com/#PresentationFramework.Aero/parent/Shared/Microsoft/Windows/Themes/SystemDropShadowChrome.cs,6d9c27d92a8128c1 - - Public Shared ReadOnly ColorProperty As DependencyProperty = DependencyProperty.Register("Color", GetType(Color), GetType(SystemDropShadowChrome), New FrameworkPropertyMetadata(Color.FromArgb(&H71, &H0, &H0, &H0), FrameworkPropertyMetadataOptions.AffectsRender, New PropertyChangedCallback(AddressOf ClearBrushes))) - Public Property Color As Color - Get - Return CType(GetValue(ColorProperty), Color) - End Get - Set(value As Color) - SetValue(ColorProperty, value) - End Set - End Property - - Public Shared ReadOnly CornerRadiusProperty As DependencyProperty = DependencyProperty.Register("CornerRadius", GetType(CornerRadius), GetType(SystemDropShadowChrome), New FrameworkPropertyMetadata(New CornerRadius(), FrameworkPropertyMetadataOptions.AffectsRender, New PropertyChangedCallback(AddressOf ClearBrushes)), New ValidateValueCallback(AddressOf IsCornerRadiusValid)) - Public Property CornerRadius As CornerRadius - Get - Return CType(GetValue(CornerRadiusProperty), CornerRadius) - End Get - Set(value As CornerRadius) - SetValue(CornerRadiusProperty, value) - End Set - End Property - Private Shared Function IsCornerRadiusValid(value As Object) As Boolean - Dim cr As CornerRadius = CType(value, CornerRadius) - Return Not (cr.TopLeft < 0.0 OrElse cr.TopRight < 0.0 OrElse cr.BottomLeft < 0.0 OrElse cr.BottomRight < 0.0 OrElse Double.IsNaN(cr.TopLeft) OrElse Double.IsNaN(cr.TopRight) OrElse Double.IsNaN(cr.BottomLeft) OrElse Double.IsNaN(cr.BottomRight) OrElse Double.IsInfinity(cr.TopLeft) OrElse Double.IsInfinity(cr.TopRight) OrElse Double.IsInfinity(cr.BottomLeft) OrElse Double.IsInfinity(cr.BottomRight)) - End Function - - Private Const ShadowDepth As Double = 5 - - Protected Overrides Sub OnRender(drawingContext As DrawingContext) - Dim cornerRadius As CornerRadius = Me.CornerRadius - Dim shadowBounds As New Rect(New Point(ShadowDepth, ShadowDepth), New Size(RenderSize.Width, RenderSize.Height)) - Dim color As Color = Me.Color - - If shadowBounds.Width > 0 AndAlso shadowBounds.Height > 0 AndAlso color.A > 0 Then - Dim centerWidth As Double = shadowBounds.Right - shadowBounds.Left - 2 * ShadowDepth - Dim centerHeight As Double = shadowBounds.Bottom - shadowBounds.Top - 2 * ShadowDepth - Dim maxRadius As Double = Math.Min(centerWidth * 0.5, centerHeight * 0.5) - cornerRadius.TopLeft = Math.Min(cornerRadius.TopLeft, maxRadius) - cornerRadius.TopRight = Math.Min(cornerRadius.TopRight, maxRadius) - cornerRadius.BottomLeft = Math.Min(cornerRadius.BottomLeft, maxRadius) - cornerRadius.BottomRight = Math.Min(cornerRadius.BottomRight, maxRadius) - Dim brushes As Brush() = GetBrushes(color, cornerRadius) - Dim centerTop As Double = shadowBounds.Top + ShadowDepth - Dim centerLeft As Double = shadowBounds.Left + ShadowDepth - Dim centerRight As Double = shadowBounds.Right - ShadowDepth - Dim centerBottom As Double = shadowBounds.Bottom - ShadowDepth - Dim guidelineSetX As Double() = New Double() {centerLeft, centerLeft + cornerRadius.TopLeft, centerRight - cornerRadius.TopRight, centerLeft + cornerRadius.BottomLeft, centerRight - cornerRadius.BottomRight, centerRight} - Dim guidelineSetY As Double() = New Double() {centerTop, centerTop + cornerRadius.TopLeft, centerTop + cornerRadius.TopRight, centerBottom - cornerRadius.BottomLeft, centerBottom - cornerRadius.BottomRight, centerBottom} - drawingContext.PushGuidelineSet(New GuidelineSet(guidelineSetX, guidelineSetY)) - cornerRadius.TopLeft += ShadowDepth - cornerRadius.TopRight += ShadowDepth - cornerRadius.BottomLeft += ShadowDepth - cornerRadius.BottomRight += ShadowDepth - Dim topLeft As New Rect(shadowBounds.Left, shadowBounds.Top, cornerRadius.TopLeft, cornerRadius.TopLeft) - drawingContext.DrawRectangle(brushes(Placement.TopLeft), Nothing, topLeft) - Dim topWidth As Double = guidelineSetX(2) - guidelineSetX(1) - - If topWidth > 0 Then - Dim top As New Rect(guidelineSetX(1), shadowBounds.Top, topWidth, ShadowDepth) - drawingContext.DrawRectangle(brushes(Placement.Top), Nothing, top) - End If - - Dim topRight As New Rect(guidelineSetX(2), shadowBounds.Top, cornerRadius.TopRight, cornerRadius.TopRight) - drawingContext.DrawRectangle(brushes(Placement.TopRight), Nothing, topRight) - Dim leftHeight As Double = guidelineSetY(3) - guidelineSetY(1) - - If leftHeight > 0 Then - Dim left As New Rect(shadowBounds.Left, guidelineSetY(1), ShadowDepth, leftHeight) - drawingContext.DrawRectangle(brushes(Placement.Left), Nothing, left) - End If - - Dim rightHeight As Double = guidelineSetY(4) - guidelineSetY(2) - - If rightHeight > 0 Then - Dim right As New Rect(guidelineSetX(5), guidelineSetY(2), ShadowDepth, rightHeight) - drawingContext.DrawRectangle(brushes(Placement.Right), Nothing, right) - End If - - Dim bottomLeft As New Rect(shadowBounds.Left, guidelineSetY(3), cornerRadius.BottomLeft, cornerRadius.BottomLeft) - drawingContext.DrawRectangle(brushes(Placement.BottomLeft), Nothing, bottomLeft) - Dim bottomWidth As Double = guidelineSetX(4) - guidelineSetX(3) - - If bottomWidth > 0 Then - Dim bottom As New Rect(guidelineSetX(3), guidelineSetY(5), bottomWidth, ShadowDepth) - drawingContext.DrawRectangle(brushes(Placement.Bottom), Nothing, bottom) - End If - - Dim bottomRight As New Rect(guidelineSetX(4), guidelineSetY(4), cornerRadius.BottomRight, cornerRadius.BottomRight) - drawingContext.DrawRectangle(brushes(Placement.BottomRight), Nothing, bottomRight) - - If cornerRadius.TopLeft = ShadowDepth AndAlso cornerRadius.TopLeft = cornerRadius.TopRight AndAlso cornerRadius.TopLeft = cornerRadius.BottomLeft AndAlso cornerRadius.TopLeft = cornerRadius.BottomRight Then - Dim center As New Rect(guidelineSetX(0), guidelineSetY(0), centerWidth, centerHeight) - drawingContext.DrawRectangle(brushes(Placement.Center), Nothing, center) - Else - Dim figure As New PathFigure() - - If cornerRadius.TopLeft > ShadowDepth Then - figure.StartPoint = New Point(guidelineSetX(1), guidelineSetY(0)) - figure.Segments.Add(New LineSegment(New Point(guidelineSetX(1), guidelineSetY(1)), True)) - figure.Segments.Add(New LineSegment(New Point(guidelineSetX(0), guidelineSetY(1)), True)) - Else - figure.StartPoint = New Point(guidelineSetX(0), guidelineSetY(0)) - End If - - If cornerRadius.BottomLeft > ShadowDepth Then - figure.Segments.Add(New LineSegment(New Point(guidelineSetX(0), guidelineSetY(3)), True)) - figure.Segments.Add(New LineSegment(New Point(guidelineSetX(3), guidelineSetY(3)), True)) - figure.Segments.Add(New LineSegment(New Point(guidelineSetX(3), guidelineSetY(5)), True)) - Else - figure.Segments.Add(New LineSegment(New Point(guidelineSetX(0), guidelineSetY(5)), True)) - End If - - If cornerRadius.BottomRight > ShadowDepth Then - figure.Segments.Add(New LineSegment(New Point(guidelineSetX(4), guidelineSetY(5)), True)) - figure.Segments.Add(New LineSegment(New Point(guidelineSetX(4), guidelineSetY(4)), True)) - figure.Segments.Add(New LineSegment(New Point(guidelineSetX(5), guidelineSetY(4)), True)) - Else - figure.Segments.Add(New LineSegment(New Point(guidelineSetX(5), guidelineSetY(5)), True)) - End If - - If cornerRadius.TopRight > ShadowDepth Then - figure.Segments.Add(New LineSegment(New Point(guidelineSetX(5), guidelineSetY(2)), True)) - figure.Segments.Add(New LineSegment(New Point(guidelineSetX(2), guidelineSetY(2)), True)) - figure.Segments.Add(New LineSegment(New Point(guidelineSetX(2), guidelineSetY(0)), True)) - Else - figure.Segments.Add(New LineSegment(New Point(guidelineSetX(5), guidelineSetY(0)), True)) - End If - - figure.IsClosed = True - figure.Freeze() - Dim geometry As New PathGeometry() - geometry.Figures.Add(figure) - geometry.Freeze() - drawingContext.DrawGeometry(brushes(Placement.Center), Nothing, geometry) - End If - - drawingContext.Pop() - End If - End Sub - - Private Enum Placement - TopLeft = 0 - Top = 1 - TopRight = 2 - Left = 3 - Center = 4 - Right = 5 - BottomLeft = 6 - Bottom = 7 - BottomRight = 8 - End Enum - Private Shared _commonBrushes As Brush() - Private Shared _commonCornerRadius As CornerRadius - Private Shared ReadOnly _resourceAccess As New Object() - Private _brushes As Brush() - - Private Shared Sub ClearBrushes(o As DependencyObject, e As DependencyPropertyChangedEventArgs) - CType(o, SystemDropShadowChrome)._brushes = Nothing - End Sub - Private Shared Function CreateStops(c As Color, cornerRadius As Double) As GradientStopCollection - Dim gradientScale As Double = 1 / (cornerRadius + ShadowDepth) - Dim gsc As New GradientStopCollection From { - New GradientStop(c, (0.5 + cornerRadius) * gradientScale) - } - Dim stopColor As Color = c - stopColor.A = CByte(0.74336 * c.A) - gsc.Add(New GradientStop(stopColor, (1.5 + cornerRadius) * gradientScale)) - stopColor.A = CByte(0.38053 * c.A) - gsc.Add(New GradientStop(stopColor, (2.5 + cornerRadius) * gradientScale)) - stopColor.A = CByte(0.12389 * c.A) - gsc.Add(New GradientStop(stopColor, (3.5 + cornerRadius) * gradientScale)) - stopColor.A = CByte(0.02654 * c.A) - gsc.Add(New GradientStop(stopColor, (4.5 + cornerRadius) * gradientScale)) - stopColor.A = 0 - gsc.Add(New GradientStop(stopColor, (5 + cornerRadius) * gradientScale)) - gsc.Freeze() - Return gsc - End Function - Private Shared Function CreateBrushes(c As Color, cornerRadius As CornerRadius) As Brush() - Dim brushes As Brush() = New Brush(8) {} - brushes(Placement.Center) = New SolidColorBrush(c) - brushes(Placement.Center).Freeze() - Dim sideStops As GradientStopCollection = CreateStops(c, 0) - Dim top As New LinearGradientBrush(sideStops, New Point(0, 1), New Point(0, 0)) - top.Freeze() - brushes(Placement.Top) = top - Dim left As New LinearGradientBrush(sideStops, New Point(1, 0), New Point(0, 0)) - left.Freeze() - brushes(Placement.Left) = left - Dim right As New LinearGradientBrush(sideStops, New Point(0, 0), New Point(1, 0)) - right.Freeze() - brushes(Placement.Right) = right - Dim bottom As New LinearGradientBrush(sideStops, New Point(0, 0), New Point(0, 1)) - bottom.Freeze() - brushes(Placement.Bottom) = bottom - Dim topLeftStops As GradientStopCollection - - If cornerRadius.TopLeft = 0 Then - topLeftStops = sideStops - Else - topLeftStops = CreateStops(c, cornerRadius.TopLeft) - End If - - Dim topLeft As New RadialGradientBrush(topLeftStops) With { - .RadiusX = 1, - .RadiusY = 1, - .Center = New Point(1, 1), - .GradientOrigin = New Point(1, 1) - } - topLeft.Freeze() - brushes(Placement.TopLeft) = topLeft - Dim topRightStops As GradientStopCollection - - If cornerRadius.TopRight = 0 Then - topRightStops = sideStops - ElseIf cornerRadius.TopRight = cornerRadius.TopLeft Then - topRightStops = topLeftStops - Else - topRightStops = CreateStops(c, cornerRadius.TopRight) - End If - - Dim topRight As New RadialGradientBrush(topRightStops) With { - .RadiusX = 1, - .RadiusY = 1, - .Center = New Point(0, 1), - .GradientOrigin = New Point(0, 1) - } - topRight.Freeze() - brushes(Placement.TopRight) = topRight - Dim bottomLeftStops As GradientStopCollection - - If cornerRadius.BottomLeft = 0 Then - bottomLeftStops = sideStops - ElseIf cornerRadius.BottomLeft = cornerRadius.TopLeft Then - bottomLeftStops = topLeftStops - ElseIf cornerRadius.BottomLeft = cornerRadius.TopRight Then - bottomLeftStops = topRightStops - Else - bottomLeftStops = CreateStops(c, cornerRadius.BottomLeft) - End If - - Dim bottomLeft As New RadialGradientBrush(bottomLeftStops) With { - .RadiusX = 1, - .RadiusY = 1, - .Center = New Point(1, 0), - .GradientOrigin = New Point(1, 0) - } - bottomLeft.Freeze() - brushes(Placement.BottomLeft) = bottomLeft - Dim bottomRightStops As GradientStopCollection - - If cornerRadius.BottomRight = 0 Then - bottomRightStops = sideStops - ElseIf cornerRadius.BottomRight = cornerRadius.TopLeft Then - bottomRightStops = topLeftStops - ElseIf cornerRadius.BottomRight = cornerRadius.TopRight Then - bottomRightStops = topRightStops - ElseIf cornerRadius.BottomRight = cornerRadius.BottomLeft Then - bottomRightStops = bottomLeftStops - Else - bottomRightStops = CreateStops(c, cornerRadius.BottomRight) - End If - - Dim bottomRight As New RadialGradientBrush(bottomRightStops) With { - .RadiusX = 1, - .RadiusY = 1, - .Center = New Point(0, 0), - .GradientOrigin = New Point(0, 0) - } - bottomRight.Freeze() - brushes(Placement.BottomRight) = bottomRight - Return brushes - End Function - Private Function GetBrushes(c As Color, cornerRadius As CornerRadius) As Brush() - If _commonBrushes Is Nothing Then - - SyncLock _resourceAccess - - If _commonBrushes Is Nothing Then - _commonBrushes = CreateBrushes(c, cornerRadius) - _commonCornerRadius = cornerRadius - End If - End SyncLock - End If - - If c = CType(_commonBrushes(Placement.Center), SolidColorBrush).Color AndAlso cornerRadius = _commonCornerRadius Then - _brushes = Nothing - Return _commonBrushes - ElseIf _brushes Is Nothing Then - _brushes = CreateBrushes(c, cornerRadius) - End If - - Return _brushes - End Function - -End Class diff --git a/Plain Craft Launcher 2/Resources/Language/en_US.xaml b/Plain Craft Launcher 2/Resources/Language/en_US.xaml deleted file mode 100644 index f0714da..0000000 --- a/Plain Craft Launcher 2/Resources/Language/en_US.xaml +++ /dev/null @@ -1,10 +0,0 @@ - - - More - Download - Launch - Setting - - diff --git a/Plain Craft Launcher 2/Resources/Language/zh_CN.xaml b/Plain Craft Launcher 2/Resources/Language/zh_CN.xaml deleted file mode 100644 index a92d95b..0000000 --- a/Plain Craft Launcher 2/Resources/Language/zh_CN.xaml +++ /dev/null @@ -1,10 +0,0 @@ - - - 更多 - 下载 - 启动 - 设置 - - From 6dbb5c98ccb0f8c97a9947575d6e6d7e568be47f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=84=E5=A5=9A=E6=A2=A6=E7=81=B5?= Date: Sat, 7 Jun 2025 14:18:00 +0800 Subject: [PATCH 7/9] =?UTF-8?q?fix:=20=E5=B0=91=E4=BA=86=E7=89=B9=E5=AE=9A?= =?UTF-8?q?=E6=A0=87=E5=A4=B4=E5=92=8C=E5=BC=82=E5=B8=B8=E6=8A=9B=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CO-Authored-By: baiyuexiao496 --- Plain Craft Launcher 2/Modules/Base/ModNet.vb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Plain Craft Launcher 2/Modules/Base/ModNet.vb b/Plain Craft Launcher 2/Modules/Base/ModNet.vb index 6e3ab5c..9a8f190 100644 --- a/Plain Craft Launcher 2/Modules/Base/ModNet.vb +++ b/Plain Craft Launcher 2/Modules/Base/ModNet.vb @@ -275,6 +275,8 @@ Retry: {"Accept", Accept} } 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 @@ -1250,7 +1252,7 @@ StartThread: Throw New Exception($"文件大小不足,获取结果为 {ContentLength},要求至少为 {Check.MinSize}。") End If If ContentLength <> Check.ActualSize AndAlso Check.ActualSize > 0 Then - 'Throw New Exception($"文件大小不一致,获取结果为 {ContentLength},要求必须为 {Check.ActualSize}。") + Throw New Exception($"文件大小不一致,获取结果为 {ContentLength},要求必须为 {Check.ActualSize}。") End If End If FileSize = ContentLength : IsUnknownSize = False @@ -1281,9 +1283,9 @@ NotSupportRange: 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}") + 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 From 65812b875481c57f056871c83baf27a2c3cea858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=84=E5=A5=9A=E6=A2=A6=E7=81=B5?= Date: Fri, 20 Jun 2025 18:38:28 +0800 Subject: [PATCH 8/9] =?UTF-8?q?chore:=20=E5=88=A0=E9=99=A4=E6=97=A0?= =?UTF-8?q?=E7=94=A8=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fbebe6a..5165cdb 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 From 83e34b84faed3b286eb20d8fd3352ae7bc08a4f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=84=E5=A5=9A=E6=A2=A6=E7=81=B5?= Date: Mon, 23 Jun 2025 20:45:34 +0800 Subject: [PATCH 9/9] Update Plain Craft Launcher 2/Modules/Base/ModNet.vb Co-authored-by: tangge233 <50769997+tangge233@users.noreply.github.com> --- Plain Craft Launcher 2/Modules/Base/ModNet.vb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plain Craft Launcher 2/Modules/Base/ModNet.vb b/Plain Craft Launcher 2/Modules/Base/ModNet.vb index 9a8f190..f7d1b3c 100644 --- a/Plain Craft Launcher 2/Modules/Base/ModNet.vb +++ b/Plain Craft Launcher 2/Modules/Base/ModNet.vb @@ -1212,7 +1212,7 @@ StartThread: HttpRequest = Request.GetRequestMessage(Info.Source.Url, RequireHeaderSign:=True) '可能出现结束减去起始等于或者小于 0 的情况,导致下载抛出 ArgumentOutOfRangeException - If Not Info.DownloadEnd - Info.DownloadEnd <= 0 Then HttpRequest.Headers.Range = New RangeHeaderValue(Info.DownloadStart, Info.DownloadEnd) + 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