Refactor(ModNet): 使用 HttpClient 代替过失函数 Feat(ModNet): 支持 GZip 等压缩格式响应的读取

CO-authored-by: qiuyue712 <qiuyue712@outlook.com>
This commit is contained in:
薄奚梦灵
2025-06-02 00:08:22 +08:00
co-authored by qiuyue712
parent d2e210320b
commit 5a94854809
6 changed files with 386 additions and 193 deletions
+12
View File
@@ -8,19 +8,31 @@ EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Beta|Any CPU = Beta|Any CPU Beta|Any CPU = Beta|Any CPU
Beta|x64 = Beta|x64
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
ReleaseUpdate|Any CPU = ReleaseUpdate|Any CPU ReleaseUpdate|Any CPU = ReleaseUpdate|Any CPU
ReleaseUpdate|x64 = ReleaseUpdate|x64
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Beta|Any CPU.ActiveCfg = BETA|Any CPU {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|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.ActiveCfg = Debug|Any CPU
{4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
{4499F2D2-18EF-4238-B1F0-7BD547AFF925}.Release|Any CPU.Build.0 = 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.ActiveCfg = ReleaseUpdate|Any CPU
{4499F2D2-18EF-4238-B1F0-7BD547AFF925}.ReleaseUpdate|Any CPU.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
+288 -141
View File
@@ -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 Module ModNet
Public Const NetDownloadEnd As String = ".PCLDownloading" Public Const NetDownloadEnd As String = ".PCLDownloading"
Public ReadOnly Client As New HttpClient() Public ReadOnly HttpProxy As New HttpWebProxyFactory()
Public ReadOnly ClientHandler As New HttpClientHandler() With {.AllowAutoRedirect = False} 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
''' <summary> ''' <summary>
''' 测试 Ping。失败则返回 -1。 ''' 测试 Ping。失败则返回 -1。
''' </summary> ''' </summary>
@@ -65,27 +273,21 @@ Retry:
End Select End Select
End Try End Try
End Function 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) Public Function NetGetCodeByClient(Url As String, Encoding As Encoding, Timeout As Integer, Accept As String, Optional UseBrowserUserAgent As Boolean = False) As String
Url = SecretCdnSign(Url)
Log("[Net] 获取客户端网络结果:" & Url & ",最大超时 " & Timeout) Log("[Net] 获取客户端网络结果:" & Url & ",最大超时 " & Timeout)
Try Try
SecretHeadersSign(Url, Client, UseBrowserUserAgent) Dim Headers As New Dictionary(Of String, String) From {
Client.DefaultRequestHeaders.Accept.Add(New Headers.MediaTypeWithQualityHeaderValue(Accept)) {"Accept", Accept}
Client.DefaultRequestHeaders.AcceptLanguage.Add(New Headers.StringWithQualityHeaderValue("en-US,en;q=0.5")) }
Client.DefaultRequestHeaders.Add("X-Requested-With", "XMLHttpRequest") Using Req As HttpRequestMessage = Request.GetRequestMessage(Url, Headers:=Headers, RequireHeaderSign:=True, RequireCdnSign:=True, UseBrowserUA:=UseBrowserUserAgent)
Dim Result = Await Client.GetAsync(Url) 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 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) Throw New WebException("获取结果失败," & ex.Message & "(" & Url & ")", ex)
End If
Finally
If Not IsNothing(HttpStream) Then HttpStream.Dispose()
If Not IsNothing(res) Then res.Dispose()
End Try End Try
End Function End Function
@@ -183,35 +385,23 @@ RequestFinished:
End Function 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) 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 线程执行了网络请求") If RunInUi() AndAlso Not Url.Contains("//127.") Then Throw New Exception("在 UI 线程执行了网络请求")
Url = SecretCdnSign(Url)
Log($"[Net] 获取网络结果:{Url},超时 {Timeout}ms{If(IsJson, ",要求 json", "")}") Log($"[Net] 获取网络结果:{Url},超时 {Timeout}ms{If(IsJson, ",要求 json", "")}")
Dim Request As HttpWebRequest = WebRequest.Create(Url)
Dim Result As New List(Of Byte)
Try Try
If Url.StartsWithF("https", True) Then Request.ProtocolVersion = HttpVersion.Version11 Dim Headers As New Dictionary(Of String, String) From {
Request.Timeout = Timeout {"Accept", If(Accept = "", "*/*", Accept)}
Request.Accept = Accept }
SecretHeadersSign(Url, Request, UseBrowserUserAgent) Using Req As HttpRequestMessage = Request.GetRequestMessage(Url, Headers:=Headers, UseBrowserUA:=UseBrowserUserAgent, RequireHeaderSign:=True, RequireCdnSign:=True)
Using res As HttpWebResponse = Request.GetResponse() Dim Resp As String = Request.SendRequest(Req, Encode:=Encode, Timeout:=Timeout)
Using HttpStream As Stream = res.GetResponseStream() Return If(IsJson, GetJson(Resp), Resp)
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
End Using End Using
Catch ex As ThreadInterruptedException Catch ex As ThreadInterruptedException
Throw Throw
Catch ex As Exception
If TypeOf ex Is WebException AndAlso CType(ex, WebException).Status = WebExceptionStatus.Timeout Then Catch ex As TaskCanceledException
Throw New TimeoutException($"获取结果失败({CType(ex, WebException).Status},{ex.Message},{Url})", ex) Throw New TimeoutException($"获取结果失败({ex.Message},{Url})", ex)
Else Catch ex As HttpRequestException
Throw New WebException($"获取结果失败({If(TypeOf ex Is WebException, CType(ex, WebException).Status & ",", "")}{ex.Message},{Url})", ex) Throw New WebException($"获取结果失败({ex.Message},{Url})", ex)
End If
Finally
Request.Abort()
End Try End Try
End Function End Function
@@ -265,7 +455,7 @@ RequestFinished:
'下载 '下载
Using Client As New WebClient Using Client As New WebClient
Try Try
SecretHeadersSign(Url, Client, UseBrowserUserAgent) 'SecretHeadersSign(Url, Client, UseBrowserUserAgent)
Client.DownloadFile(Url, LocalFile) Client.DownloadFile(Url, LocalFile)
Catch ex As Exception Catch ex As Exception
File.Delete(LocalFile) File.Delete(LocalFile)
@@ -314,7 +504,7 @@ RequestFinished:
''' <param name="Data">请求的内容。</param> ''' <param name="Data">请求的内容。</param>
''' <param name="ContentType">请求的套接字类型。</param> ''' <param name="ContentType">请求的套接字类型。</param>
''' <param name="DontRetryOnRefused">当返回 40x 时不重试。</param> ''' <param name="DontRetryOnRefused">当返回 40x 时不重试。</param>
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 RetryCount As Integer = 0
Dim RetryException As Exception = Nothing Dim RetryException As Exception = Nothing
Dim StartTime As Long = GetTimeTick() Dim StartTime As Long = GetTimeTick()
@@ -322,15 +512,15 @@ RequestFinished:
Retry: Retry:
Select Case RetryCount Select Case RetryCount
Case 0 '正常尝试 Case 0 '正常尝试
Return NetRequestOnce(Url, Method, Data, ContentType, 15000, Headers) Return NetRequestOnce(Url, Method, Data, ContentType, 15000, Headers, RequiredReturnRespObj:=RequireReturnRespObj)
Case 1 '慢速重试 Case 1 '慢速重试
Thread.Sleep(500) Thread.Sleep(500)
Return NetRequestOnce(Url, Method, Data, ContentType, 25000, Headers) Return NetRequestOnce(Url, Method, Data, ContentType, 25000, Headers, RequiredReturnRespObj:=RequireReturnRespObj)
Case Else '快速重试 Case Else '快速重试
If GetTimeTick() - StartTime > 5500 Then If GetTimeTick() - StartTime > 5500 Then
'若前两次加载耗费 5 秒以上,才进行重试 '若前两次加载耗费 5 秒以上,才进行重试
Thread.Sleep(500) Thread.Sleep(500)
Return NetRequestOnce(Url, Method, Data, ContentType, 4000, Headers) Return NetRequestOnce(Url, Method, Data, ContentType, 4000, Headers,RequiredReturnRespObj:=RequireReturnRespObj)
Else Else
Throw RetryException Throw RetryException
End If End If
@@ -397,79 +587,35 @@ RequestFinished:
''' <summary> ''' <summary>
''' 发送一次网络请求并获取返回内容。 ''' 发送一次网络请求并获取返回内容。
''' </summary> ''' </summary>
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 线程执行了网络请求") If RunInUi() AndAlso Not Url.Contains("//127.") Then Throw New Exception("在 UI 线程执行了网络请求")
Url = SecretCdnSign(Url)
If MakeLog Then Log("[Net] 发起网络请求(" & Method & "," & Url & "),最大超时 " & Timeout) If MakeLog Then Log("[Net] 发起网络请求(" & Method & "," & Url & "),最大超时 " & Timeout)
Dim DataStream As Stream = Nothing
Dim Resp As WebResponse = Nothing
Dim Req As HttpWebRequest
Try Try
Req = WebRequest.Create(Url) Dim ReqHeaders As New Dictionary(Of String, String)
Req.Method = Method If ContentType IsNot Nothing AndAlso Data IsNot Nothing Then
Dim SendData As Byte() ' 可能指定了 Content-Type 但是 Headers 是 Nothing
If TypeOf Data Is Byte() Then
SendData = Data
Else
SendData = New UTF8Encoding(False).GetBytes(Data.ToString)
End If
If Headers IsNot Nothing Then If Headers IsNot Nothing Then
For Each Pair In Headers If Not Headers.ContainsKey("Content-Type") Then Headers.Add("Content-Type", ContentType)
Req.Headers.Add(Pair.Key, Pair.Value) Else
Next If Not ReqHeaders.ContainsKey("Content-Type") Then ReqHeaders.Add("Content-Type", ContentType)
End If 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 End If
Resp = Req.GetResponse() Dim ReqData As Byte() = If(TypeOf Data Is String, Encoding.UTF8.GetBytes(Data), Data)
DataStream = Resp.GetResponseStream()
DataStream.WriteTimeout = Timeout
DataStream.ReadTimeout = Timeout Dim Req As HttpRequestMessage = Request.GetRequestMessage(Url, Method, If(Headers, ReqHeaders), If(Data IsNot Nothing, ReqData, Nothing), UseBrowserUserAgent, True, True)
Using Reader As New StreamReader(DataStream) Return Request.SendRequest(Req, If(RequiredReturnRespObj, False, True), Timeout:=Timeout, RequireReturnRespObj:=RequiredReturnRespObj)
Return Reader.ReadToEnd()
End Using
Catch ex As ThreadInterruptedException Catch ex As ThreadInterruptedException
Throw Throw
Catch ex As WebException Catch ex As TaskCanceledException
If ex.Status = WebExceptionStatus.Timeout Then Return New WebException($"连接服务器超时,请检查你的网络环境是否良好({ex.Message},{Url})", ex)
ex = New WebException($"连接服务器超时,请检查你的网络环境是否良好({ex.Message},{Url})", ex) Catch ex As HttpRequestException
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)
Else
ex = New ResponsedWebException($"服务器返回错误({ex.Status},{ex.Message},{Url}){vbCrLf}{Res}", Res, ex)
End If
End If
If MakeLog Then Log(ex, "NetRequestOnce 失败", LogLevel.Developer) If MakeLog Then Log(ex, "NetRequestOnce 失败", LogLevel.Developer)
Throw ex Throw New WebException(ex.Message, ex)
Catch ex As Exception Catch ex As Exception
ex = New WebException("网络请求失败(" & Url & ")", ex) ex = New WebException("网络请求失败(" & Url & ")", ex)
If MakeLog Then Log(ex, "NetRequestOnce 失败", LogLevel.Developer) If MakeLog Then Log(ex, "NetRequestOnce 失败", LogLevel.Developer)
Throw ex Throw ex
Finally
If DataStream IsNot Nothing Then DataStream.Dispose()
If Resp IsNot Nothing Then Resp.Dispose()
End Try End Try
End Function End Function
Public Class ResponsedWebException Public Class ResponsedWebException
@@ -818,6 +964,7 @@ RequestFinished:
Get Get
Return IsUnknownSize OrElse FileSize < FilePieceLimit Return IsUnknownSize OrElse FileSize < FilePieceLimit
End Get End Get
End Property End Property
''' <summary> ''' <summary>
''' 为不需要分割的小文件进行临时存储。 ''' 为不需要分割的小文件进行临时存储。
@@ -1055,8 +1202,9 @@ StartThread:
''' </summary> ''' </summary>
Private Sub Thread(Info As NetThread) Private Sub Thread(Info As NetThread)
If ModeDebug OrElse Info.DownloadStart = 0 Then Log("[Download] " & LocalName & " " & Info.Uuid & "#:开始,起始点 " & Info.DownloadStart & "," & Info.Source.Url) 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 ResultStream As Stream = Nothing
Dim HttpResponse As HttpResponseMessage
'部分下载源真的特别慢,并且只需要一个请求,例如 Ping 为 20s,如果增长太慢,就会造成类似 2.5s 5s 7.5s 10s 12.5s... 的极大延迟 '部分下载源真的特别慢,并且只需要一个请求,例如 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) Dim Timeout As Integer = Math.Min(Math.Max(ConnectAverage, 6000) * (1 + Info.Source.FailCount), 30000)
@@ -1064,18 +1212,16 @@ StartThread:
Try Try
Dim HttpDataCount As Integer = 0 Dim HttpDataCount As Integer = 0
If SourcesOnce.Contains(Info.Source) AndAlso Not Info.Equals(Info.Source.Thread) Then GoTo SourceBreak If SourcesOnce.Contains(Info.Source) AndAlso Not Info.Equals(Info.Source.Thread) Then GoTo SourceBreak
'请求头 HttpRequest = Request.GetRequestMessage(Info.Source.Url,
HttpRequest = WebRequest.Create(Info.Source.Url) RequireHeaderSign:=True)
If Info.Source.Url.StartsWithF("https", True) Then HttpRequest.ProtocolVersion = HttpVersion.Version11 '可能出现结束减去起始等于或者小于 0 的情况,导致下载抛出 ArgumentOutOfRangeException
'HttpRequest.Proxy = Nothing 'new WebProxy(Ip, Port) If Not Info.DownloadEnd - Info.DownloadEnd <= 0 Then HttpRequest.Headers.Range = New RangeHeaderValue(Info.DownloadStart, Info.DownloadEnd)
HttpRequest.Timeout = Timeout HttpResponse = Request.SendRequest(HttpRequest, False, Timeout:=Timeout, RequireReturnRespObj:=True)
HttpRequest.AddRange(Info.DownloadStart)
SecretHeadersSign(Info.Source.Url, HttpRequest, UseBrowserUserAgent)
Dim ContentLength As Long = 0 Dim ContentLength As Long = 0
Using HttpResponse As HttpWebResponse = HttpRequest.GetResponse() Using HttpResponse
If State = NetState.Error Then GoTo SourceBreak '快速中断 If State = NetState.Error Then GoTo SourceBreak '快速中断
If ModeDebug AndAlso HttpResponse.ResponseUri.OriginalString <> Info.Source.Url Then If ModeDebug AndAlso HttpResponse.RequestMessage.RequestUri.ToString <> Info.Source.Url Then
Log($"[Download] {LocalName} {Info.Uuid}#:重定向至 {HttpResponse.ResponseUri.OriginalString}") Log($"[Download] {LocalName} {Info.Uuid}#:重定向至 {HttpResponse.RequestMessage.RequestUri.ToString}")
End If End If
''从响应头获取文件名 ''从响应头获取文件名
'If Info.IsFirstThread Then 'If Info.IsFirstThread Then
@@ -1087,7 +1233,8 @@ StartThread:
' End If ' End If
'End If 'End If
'文件大小校验 '文件大小校验
ContentLength = HttpResponse.ContentLength ContentLength = HttpResponse.Content.Headers.ContentLength.GetValueOrDefault(-1)
If ContentLength = -1 Then If ContentLength = -1 Then
If FileSize > 1 Then If FileSize > 1 Then
If Info.DownloadStart = 0 Then If Info.DownloadStart = 0 Then
@@ -1108,7 +1255,7 @@ StartThread:
Throw New Exception($"文件大小不足,获取结果为 {ContentLength},要求至少为 {Check.MinSize}。") Throw New Exception($"文件大小不足,获取结果为 {ContentLength},要求至少为 {Check.MinSize}。")
End If End If
If ContentLength <> Check.ActualSize AndAlso Check.ActualSize > 0 Then 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
End If End If
FileSize = ContentLength : IsUnknownSize = False FileSize = ContentLength : IsUnknownSize = False
@@ -1136,9 +1283,10 @@ NotSupportRange:
SourcesOnce.Add(Info.Source) SourcesOnce.Add(Info.Source)
End If End If
End SyncLock End SyncLock
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}")
ElseIf Not FileSize - Info.DownloadStart = ContentLength Then 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 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 Info.State = NetState.Get
@@ -1154,8 +1302,7 @@ NotSupportRange:
ResultStream = New FileStream(Info.Temp, FileMode.Create, FileAccess.Write, FileShare.Read) ResultStream = New FileStream(Info.Temp, FileMode.Create, FileAccess.Write, FileShare.Read)
End If End If
'开始下载 '开始下载
Using HttpStream = HttpResponse.GetResponseStream() Using HttpStream = HttpResponse.Content.ReadAsStreamAsync().GetAwaiter().GetResult()
HttpStream.ReadTimeout = Timeout
If Setup.Get("SystemDebugDelay") Then Threading.Thread.Sleep(RandomInteger(50, 3000)) If Setup.Get("SystemDebugDelay") Then Threading.Thread.Sleep(RandomInteger(50, 3000))
Dim HttpData As Byte() = New Byte(16384) {} Dim HttpData As Byte() = New Byte(16384) {}
HttpDataCount = HttpStream.Read(HttpData, 0, 16384) HttpDataCount = HttpStream.Read(HttpData, 0, 16384)
@@ -1345,7 +1492,7 @@ Wrong:
If State < NetState.Merge Then If State < NetState.Merge Then
State = NetState.Merge State = NetState.Merge
Else Else
Return Exit Sub
End If End If
End SyncLock End SyncLock
Dim RetryCount As Integer = 0 Dim RetryCount As Integer = 0
@@ -1355,7 +1502,8 @@ Retry:
SyncLock LockChain SyncLock LockChain
'创建文件夹 '创建文件夹
If File.Exists(LocalPath) Then File.Delete(LocalPath) If File.Exists(LocalPath) Then File.Delete(LocalPath)
Directory.CreateDirectory(GetPathFromFullPath(LocalPath)) Dim Info As New FileInfo(LocalPath)
Info.Directory.Create()
'合并文件 '合并文件
If IsNoSplit Then If IsNoSplit Then
'仅有一个线程,从缓存中输出 '仅有一个线程,从缓存中输出
@@ -1434,7 +1582,7 @@ Retry:
''' </summary> ''' </summary>
Private Sub Fail(Optional RaiseEx As Exception = Nothing) Private Sub Fail(Optional RaiseEx As Exception = Nothing)
SyncLock LockState SyncLock LockState
If State >= NetState.Finish Then Return If State >= NetState.Finish Then Exit Sub
If RaiseEx IsNot Nothing Then Ex.Add(RaiseEx) If RaiseEx IsNot Nothing Then Ex.Add(RaiseEx)
'凉凉 '凉凉
State = NetState.Error State = NetState.Error
@@ -1450,10 +1598,10 @@ Retry:
Public Sub Abort(CausedByTask As LoaderDownload) Public Sub Abort(CausedByTask As LoaderDownload)
'从特定任务中移除,如果它还属于其他任务,则继续下载 '从特定任务中移除,如果它还属于其他任务,则继续下载
Tasks.Remove(CausedByTask) Tasks.Remove(CausedByTask)
If Tasks.Any Then Return If Tasks.Any Then Exit Sub
'确认中断 '确认中断
SyncLock LockState SyncLock LockState
If State >= NetState.Finish Then Return If State >= NetState.Finish Then Exit Sub
State = NetState.Error State = NetState.Error
End SyncLock End SyncLock
InterruptAndDelete() InterruptAndDelete()
@@ -1473,7 +1621,7 @@ Retry:
''' </summary> ''' </summary>
Public Sub Finish(Optional PrintLog As Boolean = True) Public Sub Finish(Optional PrintLog As Boolean = True)
SyncLock LockState SyncLock LockState
If State >= NetState.Finish Then Return If State >= NetState.Finish Then Exit Sub
State = NetState.Finish State = NetState.Finish
End SyncLock End SyncLock
SyncLock NetManager.LockRemain SyncLock NetManager.LockRemain
@@ -1600,7 +1748,7 @@ NextElement:
'输入检测 '输入检测
If Not Files.Any() Then If Not Files.Any() Then
OnFinish() OnFinish()
Return Exit Sub
End If End If
For Each File As NetFile In Files For Each File As NetFile In Files
If File Is Nothing Then Throw New ArgumentException("存在空文件请求!") If File Is Nothing Then Throw New ArgumentException("存在空文件请求!")
@@ -1657,14 +1805,13 @@ NextElement:
End Sub End Sub
Private Sub StartCopy(Files As List(Of NetFile), FolderList As List(Of String)) Private Sub StartCopy(Files As List(Of NetFile), FolderList As List(Of String))
Try Try
If ModeDebug Then Log($"[Download] 检查线程分配文件数:{Files.Count},线程名:{Thread.CurrentThread.Name}") If ModeDebug Then Log("[Download] 检查线程分配文件数:" & Files.Count & ",线程名:" & Thread.CurrentThread.Name)
'试图从已存在的 Minecraft 文件夹中寻找目标文件 '试图从已存在的 Minecraft 文件夹中寻找目标文件
Dim ExistFiles As New List(Of KeyValuePair(Of NetFile, String)) '{NetFile, Target As String} Dim ExistFiles As New List(Of KeyValuePair(Of NetFile, String)) '{NetFile, Target As String}
For Each File As NetFile In Files For Each File As NetFile In Files
Dim ExistFilePath As String = Nothing Dim ExistFilePath As String = Nothing
'判断是否有已存在的文件 '判断是否有已存在的文件
If File.Check IsNot Nothing AndAlso McFolderList IsNot Nothing AndAlso PathMcFolder IsNot Nothing AndAlso If File.Check IsNot Nothing AndAlso McFolderList IsNot Nothing AndAlso PathMcFolder IsNot Nothing AndAlso File.Check.CanUseExistsFile AndAlso File.LocalPath.StartsWithF(PathMcFolder) Then
File.Check.CanUseExistsFile AndAlso File.LocalPath.StartsWithF(PathMcFolder) Then
Dim Relative = File.LocalPath.Replace(PathMcFolder, "") Dim Relative = File.LocalPath.Replace(PathMcFolder, "")
For Each Folder In FolderList For Each Folder In FolderList
Dim Target = Folder & Relative Dim Target = Folder & Relative
@@ -1690,7 +1837,7 @@ NextElement:
For Each FileToken In ExistFiles For Each FileToken In ExistFiles
Dim File As NetFile = FileToken.Key Dim File As NetFile = FileToken.Key
SyncLock LockState SyncLock LockState
If File.State > NetState.WaitForCopy Then Return If File.State > NetState.WaitForCopy Then Exit Sub
End SyncLock End SyncLock
Dim LocalPath As String = FileToken.Value Dim LocalPath As String = FileToken.Value
Dim RetryCount As Integer = 0 Dim RetryCount As Integer = 0
@@ -1701,7 +1848,7 @@ Retry:
File.Finish(False) File.Finish(False)
Catch ex As Exception Catch ex As Exception
RetryCount += 1 RetryCount += 1
Log(ex, $"复制已存在的文件失败,重试第 {RetryCount} 次({LocalPath} -> {File.LocalPath})") Log(ex, String.Format("复制已存在的文件失败,重试第 {2} 次({0} -> {1})", LocalPath, File.LocalPath, RetryCount))
If RetryCount < 3 Then If RetryCount < 3 Then
Thread.Sleep(200) Thread.Sleep(200)
GoTo Retry GoTo Retry
@@ -1719,14 +1866,14 @@ Retry:
'要求全部文件完成 '要求全部文件完成
SyncLock FileRemainLock SyncLock FileRemainLock
FileRemain -= 1 FileRemain -= 1
If FileRemain > 0 Then Return If FileRemain > 0 Then Exit Sub
End SyncLock End SyncLock
OnFinish() OnFinish()
End Sub End Sub
Public Sub OnFinish() Public Sub OnFinish()
RaisePreviewFinish() RaisePreviewFinish()
SyncLock LockState SyncLock LockState
If State > LoadState.Loading Then Return If State > LoadState.Loading Then Exit Sub
State = LoadState.Finished State = LoadState.Finished
End SyncLock End SyncLock
End Sub End Sub
@@ -1739,7 +1886,7 @@ Retry:
End Sub End Sub
Public Sub OnFail(ExList As List(Of Exception)) Public Sub OnFail(ExList As List(Of Exception))
SyncLock LockState 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("未知错误!")} If ExList Is Nothing OrElse Not ExList.Any() Then ExList = New List(Of Exception) From {New Exception("未知错误!")}
'寻找第一个不是 404 的下载源 '寻找第一个不是 404 的下载源
Dim UsefulExs = ExList.Where(Function(e) Not e.Message.Contains("(404)")).ToList Dim UsefulExs = ExList.Where(Function(e) Not e.Message.Contains("(404)")).ToList
@@ -1768,7 +1915,7 @@ Retry:
End Sub End Sub
Public Overrides Sub Abort() Public Overrides Sub Abort()
SyncLock LockState SyncLock LockState
If State >= LoadState.Finished Then Return If State >= LoadState.Finished Then Exit Sub
State = LoadState.Aborted State = LoadState.Aborted
End SyncLock End SyncLock
Log("[Download] " & Name & " 已取消!") Log("[Download] " & Name & " 已取消!")
@@ -1885,7 +2032,7 @@ Retry:
''' 启动监控线程,用于新增下载线程。 ''' 启动监控线程,用于新增下载线程。
''' </summary> ''' </summary>
Private Sub StartManager() Private Sub StartManager()
If IsManagerStarted Then Return If IsManagerStarted Then Exit Sub
IsManagerStarted = True IsManagerStarted = True
Dim ThreadStarter = Dim ThreadStarter =
Sub(Id As Integer) '0 或 1 Sub(Id As Integer) '0 或 1
+8 -21
View File
@@ -1,7 +1,7 @@
'由于包含加解密等安全信息,本文件中的部分代码已被删除 '由于包含加解密等安全信息,本文件中的部分代码已被删除
Imports System.Security.Cryptography
Imports System.Net.Http Imports System.Net.Http
Imports System.Security.Cryptography
Friend Module ModSecret Friend Module ModSecret
@@ -79,32 +79,19 @@ Friend Module ModSecret
''' <summary> ''' <summary>
''' 设置 Headers 的 UA、Referer。 ''' 设置 Headers 的 UA、Referer。
''' </summary> ''' </summary>
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 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 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 Else
Client.DefaultRequestHeaders.Add("User-Agent", $"PCL2/{VersionStandardCode}") Req.Headers.Add("User-Agent", $"PCL2/{VersionStandardCode}")
End If End If
Client.DefaultRequestHeaders.Add("Referer", $"http://{VersionCode}.open.pcl2.server/") Req.Headers.Add("Referer", $"http://{VersionCode}.open.pcl2.server/")
If Url.Contains("api.curseforge.com") Then Client.DefaultRequestHeaders.Add("x-api-key", CurseForgeAPIKey) If Url.Contains("api.curseforge.com") Then Req.Headers.Add("x-api-key", CurseForgeAPIKey)
End Sub
''' <summary>
''' 设置 Headers 的 UA、Referer。
''' </summary>
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
End Sub End Sub
#End Region #End Region
#Region "字符串加解密" #Region "字符串加解密"
+5 -3
View File
@@ -12,9 +12,10 @@ Option Strict On
Option Explicit On Option Explicit On
Namespace My
<Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute(), _ <Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute(), _
Global.System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0"), _ Global.System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.13.0.0"), _
Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _ Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
Partial Friend NotInheritable Class MySettings Partial Friend NotInheritable Class MySettings
Inherits Global.System.Configuration.ApplicationSettingsBase Inherits Global.System.Configuration.ApplicationSettingsBase
@@ -53,6 +54,7 @@ Partial Friend NotInheritable Class MySettings
End Get End Get
End Property End Property
End Class End Class
End Namespace
Namespace My Namespace My
@@ -62,9 +64,9 @@ Namespace My
Friend Module MySettingsProperty Friend Module MySettingsProperty
<Global.System.ComponentModel.Design.HelpKeywordAttribute("My.Settings")> _ <Global.System.ComponentModel.Design.HelpKeywordAttribute("My.Settings")> _
Friend ReadOnly Property Settings() As Global.PCL.MySettings Friend ReadOnly Property Settings() As Global.PCL.My.MySettings
Get Get
Return Global.PCL.MySettings.Default Return Global.PCL.My.MySettings.Default
End Get End Get
End Property End Property
End Module End Module
@@ -139,6 +139,56 @@
<CodeAnalysisRuleSet>BasicDesignGuidelineRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>BasicDesignGuidelineRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DocumentationFile>Plain Craft Launcher 2.xml</DocumentationFile>
<WarningLevel>1</WarningLevel>
<NoWarn>41999,42016,42017,42018,42019,42020,42021,42022,42032,42036,42314</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>RELEASE</DefineConstants>
<DocumentationFile>Plain Craft Launcher 2.xml</DocumentationFile>
<Optimize>true</Optimize>
<WarningLevel>1</WarningLevel>
<NoWarn>41999,42016,42017,42018,42019,42020,42021,42022,42032,42036,42314</NoWarn>
<PlatformTarget>x64</PlatformTarget>
<CodeAnalysisRuleSet>BasicDesignGuidelineRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Snapshot|x64'">
<OutputPath>bin\x64\Snapshot\</OutputPath>
<DefineConstants>SNAPSHOT</DefineConstants>
<DocumentationFile>Plain Craft Launcher 2.xml</DocumentationFile>
<Optimize>true</Optimize>
<WarningLevel>1</WarningLevel>
<NoWarn>41999,42016,42017,42018,42019,42020,42021,42022,42032,42036,42314</NoWarn>
<PlatformTarget>x64</PlatformTarget>
<CodeAnalysisRuleSet>BasicDesignGuidelineRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'BETA|x64'">
<OutputPath>bin\x64\BETA\</OutputPath>
<DefineConstants>BETA</DefineConstants>
<DocumentationFile>Plain Craft Launcher 2.xml</DocumentationFile>
<Optimize>true</Optimize>
<WarningLevel>1</WarningLevel>
<NoWarn>41999,42016,42017,42018,42019,42020,42021,42022,42032,42036,42314</NoWarn>
<PlatformTarget>x64</PlatformTarget>
<CodeAnalysisRuleSet>BasicDesignGuidelineRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseUpdate|x64'">
<OutputPath>bin\x64\ReleaseUpdate\</OutputPath>
<DefineConstants>RELEASE,BY_UPDATE</DefineConstants>
<DocumentationFile>Plain Craft Launcher 2.xml</DocumentationFile>
<Optimize>true</Optimize>
<WarningLevel>1</WarningLevel>
<NoWarn>41999,42016,42017,42018,42019,42020,42021,42022,42032,42036,42314</NoWarn>
<PlatformTarget>x64</PlatformTarget>
<CodeAnalysisRuleSet>BasicDesignGuidelineRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Imazen.WebP, Version=10.0.1.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Imazen.WebP, Version=10.0.1.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
@@ -195,9 +245,6 @@
<Compile Include="Controls\MyIconTextButton.xaml.vb"> <Compile Include="Controls\MyIconTextButton.xaml.vb">
<DependentUpon>MyIconTextButton.xaml</DependentUpon> <DependentUpon>MyIconTextButton.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Modules\Minecraft\ModComp.vb" /> <Compile Include="Modules\Minecraft\ModComp.vb" />
<Compile Include="Modules\Minecraft\ModJava.vb" /> <Compile Include="Modules\Minecraft\ModJava.vb" />
<Compile Include="Modules\Minecraft\ModMod.vb" /> <Compile Include="Modules\Minecraft\ModMod.vb" />
@@ -501,7 +548,6 @@
<DependentUpon>FormMain.xaml</DependentUpon> <DependentUpon>FormMain.xaml</DependentUpon>
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Page Include="Pages\PageVersion\MyLocalModItem.xaml"> <Page Include="Pages\PageVersion\MyLocalModItem.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
@@ -949,7 +995,6 @@
<Resource Include="Images\Heads\MCBBS.png" /> <Resource Include="Images\Heads\MCBBS.png" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Resource Include="Images\Blocks\NeoForge.png" /> <Resource Include="Images\Blocks\NeoForge.png" />