refactor(net): 重构 CE 的部分网络库到 Core (#1387)
* chore(http): 修正以确保正确使用 Core 内的网络调用方式 * feat(http): 替换部分 http 实现 * feat: 完成部分 Stun Client * perf(binary): 换用 BinaryPrimitives 代替自己瞎搓的工具 * feat: 完成 Telemetry 的网络库迁移 * fix(style): 命名更改未同步至 CE * fix(telemetry): 未传递启动器的版本信息 * feat(http): 正版登录处换用 core 的 http 实现 --------- Co-authored-by: 任天天 <61044187+ruattd@users.noreply.github.com>
This commit is contained in:
@@ -13,7 +13,7 @@ public class DiffTest
|
||||
public async Task TestBsDiff()
|
||||
{
|
||||
var diff = new BsDiff();
|
||||
var res = await diff.Apply(
|
||||
var res = await diff.ApplyAsync(
|
||||
[
|
||||
73, 32, 97, 109, 32, 110, 111, 116, 32, 115, 117, 114, 101, 32, 104, 111, 119, 32, 99, 104, 111, 117,
|
||||
108,
|
||||
@@ -48,6 +48,6 @@ public class DiffTest
|
||||
if (string.IsNullOrEmpty(from) || string.IsNullOrEmpty(outFile) || string.IsNullOrEmpty(diffFile))
|
||||
return;
|
||||
var diff = new BsDiff();
|
||||
File.WriteAllBytes(outFile, await diff.Apply(File.ReadAllBytes(from), File.ReadAllBytes(diffFile)));
|
||||
File.WriteAllBytes(outFile, await diff.ApplyAsync(File.ReadAllBytes(from), File.ReadAllBytes(diffFile)));
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ namespace PCL.Test
|
||||
{
|
||||
// Java 搜索是否稳定
|
||||
var jas = new JavaManager();
|
||||
await jas.ScanJava();
|
||||
await jas.ScanJavaAsync();
|
||||
var firstScanedCount = jas.JavaList.Count;
|
||||
foreach (var ja in jas.JavaList)
|
||||
{
|
||||
@@ -25,11 +25,11 @@ namespace PCL.Test
|
||||
Assert.IsTrue(ja.Version.Major > 0, "Java version is not valid: " + ja.JavaFolder);
|
||||
Assert.IsTrue(!string.IsNullOrWhiteSpace(ja.JavaFolder));
|
||||
}
|
||||
await jas.ScanJava();
|
||||
await jas.ScanJavaAsync();
|
||||
var secondScanedCount = jas.JavaList.Count;
|
||||
Assert.IsTrue(firstScanedCount == secondScanedCount);
|
||||
// Java 搜索是否能够正确选择
|
||||
Assert.IsTrue(jas.JavaList.Count == 0 || (jas.JavaList.Count > 0 && (await jas.SelectSuitableJava(new Version(1, 8, 0), new Version(30, 0, 0))).Count > 0));
|
||||
Assert.IsTrue(jas.JavaList.Count == 0 || (jas.JavaList.Count > 0 && (await jas.SelectSuitableJavaAsync(new Version(1, 8, 0), new Version(30, 0, 0))).Count > 0));
|
||||
// Java 是否有重复
|
||||
Assert.IsFalse(jas.JavaList.GroupBy(x => x.JavawExePath).Any(x => x.Count() > 1));
|
||||
}
|
||||
|
||||
@@ -22,16 +22,44 @@
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<PlatformTarget>$(Platform)</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Beta' ">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'CI' ">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="fNbt" Version="1.0.0" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageReference Include="Microsoft.TestPlatform.AdapterUtilities" Version="17.14.1" />
|
||||
<PackageReference Include="Microsoft.TestPlatform.ObjectModel" Version="17.14.1" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.10.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.10.1" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.10.3" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.10.3" />
|
||||
<PackageReference Include="System.Text.Json" Version="9.0.8" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PCL.Core\PCL.Core.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="Net\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Remove="Net\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Remove="Net\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Net\**" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -26,19 +26,19 @@ public class WebServerTest
|
||||
|
||||
server.Route("/test", (path, _) => RoutedResponse.Text(path));
|
||||
Console.WriteLine("Test(/test): 200 OK (path relative to /test)");
|
||||
await server.StartResponseOnce();
|
||||
await server.StartResponseOnceAsync();
|
||||
|
||||
dynamic obj = new { a = 123, b = new { c = this, d = "text" } };
|
||||
server.Route("/json", () => RoutedResponse.Json(obj));
|
||||
Console.WriteLine("Test(/json): 200 OK (request JSON)");
|
||||
await server.StartResponseOnce();
|
||||
await server.StartResponseOnceAsync();
|
||||
|
||||
Console.WriteLine("Test(/any/path): 404 Not Found");
|
||||
await server.StartResponseOnce();
|
||||
await server.StartResponseOnceAsync();
|
||||
|
||||
server.Route("/", () => RoutedResponse.NoContent);
|
||||
Console.WriteLine("Test(/): 204 No Content");
|
||||
await server.StartResponseOnce();
|
||||
await server.StartResponseOnceAsync();
|
||||
|
||||
server.Dispose();
|
||||
Console.WriteLine("Test complete.");
|
||||
|
||||
@@ -19,6 +19,8 @@ Public Class Application
|
||||
#End If
|
||||
|
||||
Public Sub New()
|
||||
Basics.VersionName = VersionBaseName
|
||||
Basics.VersionNumber = VersionCode
|
||||
'注册生命周期事件
|
||||
Lifecycle.When(LifecycleState.Loaded, AddressOf Application_Startup)
|
||||
End Sub
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
Imports PCL.Core.Utils
|
||||
Imports PCL.Core.Net
|
||||
Imports PCL.Core.Utils
|
||||
|
||||
Public Class MyImage
|
||||
Inherits Image
|
||||
@@ -133,7 +134,7 @@ RetryStart:
|
||||
Directory.CreateDirectory(GetPathFromFullPath(TempPath)) '重新实现下载,以避免携带 Header(#5072)
|
||||
Using request As New Net.Http.HttpRequestMessage(Http.HttpMethod.Get, Url)
|
||||
Using fs As New FileStream(TempDownloadingPath, FileMode.Create)
|
||||
Using response = MyHttpClient.SendAsync(request).Result
|
||||
Using response = NetworkService.GetClient().SendAsync(request).Result
|
||||
response.EnsureSuccessStatusCode()
|
||||
Dim res = response.Content.ReadAsByteArrayAsync().Result
|
||||
fs.Write(res, 0, res.Length)
|
||||
|
||||
@@ -217,8 +217,12 @@ Public Class FormMain
|
||||
'遥测提示
|
||||
If Setup.IsUnset("SystemTelemetry") Then
|
||||
Select Case MyMsgBox("这是一项与 Steam 硬件调查类似的计划,参与调查可以帮助我们更好的进行规划和开发,且我们会不定期发布该调查的统计结果。" & vbCrLf &
|
||||
"如果选择参与调查,我们将会收集以下信息:" & vbCrLf &
|
||||
"启动器版本信息与识别码,使用的 Windows 系统版本与架构,已安装的物理内存大小,NAT 与 IPv6 支持情况,是否使用过官方版 PCL、HMCL 或 BakaXL" & vbCrLf & vbCrLf &
|
||||
"如果选择参与调查,我们将会收集以下信息:" & vbCrLf & vbCrLf &
|
||||
"- 启动器版本信息与识别码" & vbCrLf &
|
||||
"- Windows 系统版本与架构" & vbCrLf &
|
||||
"- 已安装的物理内存大小" & vbCrLf &
|
||||
"- NAT 与 IPv6 支持情况" & vbCrLf &
|
||||
"- 是否使用过官方版 PCL、HMCL 或 BakaXL" & vbCrLf & vbCrLf &
|
||||
"这些数据均不与你关联,我们也绝不会向第三方出售数据。" & vbCrLf &
|
||||
"如果不想参与该调查,可以选择拒绝,不会影响其他功能使用。" & vbCrLf &
|
||||
"你可以随时在启动器设置中调整这项设置。", "参与 PCL CE 软硬件调查", "同意", "拒绝")
|
||||
@@ -227,12 +231,9 @@ Public Class FormMain
|
||||
Case 2
|
||||
Setup.Set("SystemTelemetry", False)
|
||||
End Select
|
||||
ElseIf Setup.Get("SystemTelemetry") Then
|
||||
RunInNewThread(Sub() SendTelemetry())
|
||||
End If
|
||||
'启动加载器池
|
||||
Try
|
||||
' InitJava() ignore as JavaSerivce will InitJava automatically
|
||||
Thread.Sleep(100)
|
||||
DlClientListMojangLoader.Start(1) 'PCL 会同时根据这里的加载结果决定是否使用官方源进行下载
|
||||
RunCountSub()
|
||||
|
||||
@@ -4,8 +4,6 @@ Imports System.Runtime.InteropServices
|
||||
Imports System.Threading.Tasks
|
||||
Imports System
|
||||
Imports System.IO.Compression
|
||||
Imports CacheCow.Client
|
||||
Imports CacheCow.Common
|
||||
Imports PCL.Core.Net
|
||||
Imports PCL.Core.Utils
|
||||
Imports PCL.Core.Utils.Hash
|
||||
@@ -13,114 +11,6 @@ Imports PCL.Core.Utils.Hash
|
||||
Public Module ModNet
|
||||
Public Const NetDownloadEnd As String = ".PCLDownloading"
|
||||
|
||||
Public ReadOnly MyHttpClient As New HttpClient(New HttpClientHandler() With {
|
||||
.Proxy = HttpProxyManager.Instance,
|
||||
.MaxConnectionsPerServer = 256,
|
||||
.SslProtocols = System.Security.Authentication.SslProtocols.None,
|
||||
.AutomaticDecompression = DecompressionMethods.All,
|
||||
.AllowAutoRedirect = True,
|
||||
.UseCookies = False
|
||||
})
|
||||
|
||||
Public ReadOnly MyHttpCacheClient As HttpClient = ClientExtensions.CreateClient(New NetCacheStorage(IO.Path.Combine(PathTemp, "Cache", "Net")), New HttpClientHandler() With {
|
||||
.Proxy = HttpProxyManager.Instance,
|
||||
.SslProtocols = System.Security.Authentication.SslProtocols.None,
|
||||
.AutomaticDecompression = DecompressionMethods.All,
|
||||
.AllowAutoRedirect = True,
|
||||
.UseCookies = False
|
||||
})
|
||||
|
||||
Private Class NetCacheStorage
|
||||
Implements CacheCow.Common.ICacheStore
|
||||
Implements IDisposable
|
||||
|
||||
Private disposedValue As Boolean
|
||||
Private _storagePath As String
|
||||
Private _contentSerializer As New MessageContentHttpMessageSerializer()
|
||||
Sub New(storagePath As String)
|
||||
_storagePath = storagePath
|
||||
If Not Directory.Exists(storagePath) Then Directory.CreateDirectory(storagePath)
|
||||
End Sub
|
||||
|
||||
Public Async Function GetValueAsync(key As CacheKey) As Task(Of HttpResponseMessage) Implements ICacheStore.GetValueAsync
|
||||
Try
|
||||
Dim cacheFile As New FileInfo(IO.Path.Combine(_storagePath, GetCacheNameByKey(key)))
|
||||
If Not cacheFile.Exists Then Return Nothing
|
||||
Dim ms As New MemoryStream()
|
||||
Using fs = cacheFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read)
|
||||
Using decompress As New DeflateStream(fs, CompressionMode.Decompress)
|
||||
Await decompress.CopyToAsync(ms)
|
||||
End Using
|
||||
End Using
|
||||
ms.Seek(0, SeekOrigin.Begin)
|
||||
Return Await _contentSerializer.DeserializeToResponseAsync(ms)
|
||||
Catch ex As Exception
|
||||
Log(ex, $"[Net] 获取缓存资源({key.ResourceUri} : {key.HashBase64})出现异常")
|
||||
End Try
|
||||
Return Nothing
|
||||
End Function
|
||||
|
||||
Public Async Function AddOrUpdateAsync(key As CacheKey, response As HttpResponseMessage) As Task Implements ICacheStore.AddOrUpdateAsync
|
||||
Try
|
||||
Dim cacheFile As New FileInfo(IO.Path.Combine(_storagePath, GetCacheNameByKey(key)))
|
||||
Using fs = cacheFile.Open(FileMode.Create, FileAccess.Write, FileShare.None)
|
||||
fs.SetLength(0) '先清空之前的内容
|
||||
Using compress As New DeflateStream(fs, CompressionMode.Compress)
|
||||
Await _contentSerializer.SerializeAsync(response, compress)
|
||||
End Using
|
||||
End Using
|
||||
Catch ex As Exception
|
||||
Log(ex, $"[Net] 更新缓存资源({key.ResourceUri} : {key.HashBase64})出现异常")
|
||||
End Try
|
||||
End Function
|
||||
|
||||
Public Async Function TryRemoveAsync(key As CacheKey) As Task(Of Boolean) Implements ICacheStore.TryRemoveAsync
|
||||
Return Await Task.Run(Function()
|
||||
Try
|
||||
Dim cacheFile As New FileInfo(IO.Path.Combine(_storagePath, GetCacheNameByKey(key)))
|
||||
If Not cacheFile.Exists Then Return True
|
||||
cacheFile.Delete()
|
||||
Return True
|
||||
Catch ex As Exception
|
||||
Log(ex, $"[Net] 移除缓存资源({key.ResourceUri} : {key.HashBase64})出现异常")
|
||||
End Try
|
||||
Return False
|
||||
End Function)
|
||||
End Function
|
||||
|
||||
Public Async Function ClearAsync() As Task Implements ICacheStore.ClearAsync
|
||||
Await Task.Run(Sub()
|
||||
Try
|
||||
Dim dir As New DirectoryInfo(_storagePath)
|
||||
Dim cacheFiles = dir.EnumerateFiles()
|
||||
For Each cacheFile In cacheFiles
|
||||
cacheFile.Delete()
|
||||
Next
|
||||
Catch ex As Exception
|
||||
Log(ex, $"[Net] 清空缓存资源出现异常")
|
||||
End Try
|
||||
End Sub)
|
||||
End Function
|
||||
|
||||
Private Function GetCacheNameByKey(key As CacheKey)
|
||||
Return SHA512Provider.Instance.ComputeHash($"{key.HashBase64}{New Uri(key.ResourceUri).Host}")
|
||||
End Function
|
||||
|
||||
Protected Overridable Sub Dispose(disposing As Boolean)
|
||||
If Not disposedValue Then
|
||||
If disposing Then
|
||||
|
||||
End If
|
||||
disposedValue = True
|
||||
End If
|
||||
End Sub
|
||||
|
||||
Public Sub Dispose() Implements IDisposable.Dispose
|
||||
Dispose(disposing:=True)
|
||||
GC.SuppressFinalize(Me)
|
||||
End Sub
|
||||
End Class
|
||||
|
||||
''' <summary>
|
||||
''' 测试 Ping。失败则返回 -1。
|
||||
''' </summary>
|
||||
@@ -196,73 +86,6 @@ Public Module ModNet
|
||||
End If
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 以 HttpClient 获取网页源代码。会进行至多 45 秒 3 次的尝试,允许最长 30s 的超时。
|
||||
''' </summary>
|
||||
''' <param name="Url">网页的 Url。</param>
|
||||
''' <param name="Encoding">网页的编码,通常为 UTF-8。</param>
|
||||
Public Function NetGetCodeByClient(Url As String, Encoding As Encoding, Optional Accept As String = "application/json, text/javascript, */*; q=0.01", Optional UseBrowserUserAgent As Boolean = False) As String
|
||||
Dim RetryCount As Integer = 0
|
||||
Dim RetryException As Exception = Nothing
|
||||
Dim StartTime As Long = TimeUtils.GetTimeTick()
|
||||
While RetryCount <= 3
|
||||
RetryCount += 1
|
||||
Try
|
||||
Select Case RetryCount
|
||||
Case 0 '正常尝试
|
||||
Return NetGetCodeByClient(Url, Encoding, 10000, Accept, UseBrowserUserAgent)
|
||||
Case 1 '慢速重试
|
||||
Thread.Sleep(500)
|
||||
Return NetGetCodeByClient(Url, Encoding, 30000, Accept, UseBrowserUserAgent)
|
||||
Case Else '快速重试
|
||||
If TimeUtils.GetTimeTick() - StartTime > 5500 Then
|
||||
'若前两次加载耗费 5 秒以上,才进行重试
|
||||
Thread.Sleep(500)
|
||||
Return NetGetCodeByClient(Url, Encoding, 4000, Accept, UseBrowserUserAgent)
|
||||
Else
|
||||
Throw RetryException
|
||||
End If
|
||||
End Select
|
||||
Catch ex As Exception
|
||||
RetryException = ex
|
||||
End Try
|
||||
End While
|
||||
Throw RetryException
|
||||
End Function
|
||||
Public Function NetGetCodeByClient(Url As String, Encoding As Encoding, Timeout As Integer, Accept As String, Optional UseBrowserUserAgent As Boolean = False) As String
|
||||
Try
|
||||
Url = SecretCdnSign(Url)
|
||||
Log("[Net] 获取客户端网络结果:" & Url & ",最大超时 " & Timeout)
|
||||
Using cts As New CancellationTokenSource
|
||||
cts.CancelAfter(Timeout)
|
||||
Using request As New HttpRequestMessage(HttpMethod.Get, Url)
|
||||
SecretHeadersSign(Url, request, UseBrowserUserAgent)
|
||||
request.Headers.Accept.ParseAdd(Accept)
|
||||
request.Headers.AcceptLanguage.ParseAdd("en-US,en;q=0.5")
|
||||
request.Headers.Add("X-Requested-With", "XMLHttpRequest")
|
||||
Using response = MyHttpCacheClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cts.Token).Result
|
||||
EnsureSuccessStatusCode(response)
|
||||
Using responseStream As Stream = response.Content.ReadAsStreamAsync().Result
|
||||
If Encoding Is Nothing Then Encoding = Encoding.UTF8
|
||||
'读取流并转换为字符串
|
||||
Using reader As New StreamReader(responseStream, Encoding)
|
||||
Dim content As String = reader.ReadToEnd()
|
||||
If String.IsNullOrEmpty(content) Then Throw New WebException("获取结果失败,内容为空(" & Url & ")")
|
||||
Return content
|
||||
End Using
|
||||
End Using
|
||||
End Using
|
||||
End Using
|
||||
End Using
|
||||
Catch ex As TaskCanceledException
|
||||
Throw New TimeoutException("连接服务器超时(" & Url & ")", ex)
|
||||
Catch ex As HttpRequestFailedException
|
||||
Throw New HttpWebException("获取结果失败," & ex.Message & "(" & Url & ")", ex)
|
||||
Catch ex As Exception
|
||||
Throw New WebException("获取结果失败," & ex.Message & "(" & Url & ")", ex)
|
||||
End Try
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 以 WebRequest 获取网页源代码或 Json。会进行至多 45 秒 3 次的尝试,允许最长 30s 的超时。
|
||||
''' </summary>
|
||||
@@ -313,7 +136,7 @@ Public Module ModNet
|
||||
Using request As New HttpRequestMessage(HttpMethod.Get, Url)
|
||||
request.Headers.Accept.ParseAdd(Accept)
|
||||
SecretHeadersSign(Url, request, UseBrowserUserAgent)
|
||||
Using response = MyHttpCacheClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cts.Token).Result
|
||||
Using response = NetworkService.GetClient().SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cts.Token).Result
|
||||
EnsureSuccessStatusCode(response)
|
||||
If Encode Is Nothing Then Encode = Encoding.UTF8
|
||||
Using responseStream As Stream = response.Content.ReadAsStreamAsync().Result
|
||||
@@ -379,7 +202,7 @@ Public Module ModNet
|
||||
If File.Exists(LocalFile) Then File.Delete(LocalFile)
|
||||
Using request As New HttpRequestMessage(HttpMethod.Get, Url)
|
||||
SecretHeadersSign(Url, request, UseBrowserUserAgent)
|
||||
Using response As HttpResponseMessage = Await MyHttpCacheClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)
|
||||
Using response As HttpResponseMessage = Await NetworkService.GetClient().SendAsync(request, HttpCompletionOption.ResponseHeadersRead)
|
||||
EnsureSuccessStatusCode(response)
|
||||
Using httpStream As Stream = Await response.Content.ReadAsStreamAsync()
|
||||
Using fileStream As New FileStream(LocalFile, FileMode.Create)
|
||||
@@ -530,7 +353,7 @@ Public Module ModNet
|
||||
request.Headers.Add(Pair.Key, Pair.Value)
|
||||
Next
|
||||
End If
|
||||
Using response = MyHttpCacheClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cts.Token).Result
|
||||
Using response = NetworkService.GetClient().SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cts.Token).Result
|
||||
EnsureSuccessStatusCode(response)
|
||||
Using responseStream = response.Content.ReadAsStreamAsync().Result
|
||||
Using reader As New StreamReader(responseStream, Encoding.UTF8)
|
||||
@@ -1163,7 +986,7 @@ StartThread:
|
||||
If Not Info.IsFirstThread OrElse Info.DownloadStart <> 0 Then request.Headers.Range = New Headers.RangeHeaderValue(Info.DownloadStart, Nothing)
|
||||
Using cts As New CancellationTokenSource
|
||||
cts.CancelAfter(Timeout)
|
||||
Using response = MyHttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cts.Token).Result
|
||||
Using response = NetworkService.GetClient().SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cts.Token).Result
|
||||
EnsureSuccessStatusCode(response)
|
||||
If State = NetState.Error Then GoTo SourceBreak '快速中断
|
||||
Dim Redirected = response.RequestMessage.RequestUri.OriginalString
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
Imports PCL.Core.Utils
|
||||
Imports PCL.Core.Net
|
||||
Imports System.Net.Http
|
||||
Imports PCL.Core.Utils
|
||||
|
||||
Public Module ModDownload
|
||||
|
||||
@@ -414,7 +416,14 @@ Public Module ModDownload
|
||||
''' </summary>
|
||||
Public DlOptiFineListOfficialLoader As New LoaderTask(Of Integer, DlOptiFineListResult)("DlOptiFineList Official", AddressOf DlOptiFineListOfficialMain)
|
||||
Private Sub DlOptiFineListOfficialMain(Loader As LoaderTask(Of Integer, DlOptiFineListResult))
|
||||
Dim Result As String = NetGetCodeByClient("https://optifine.net/downloads", Encoding.Default)
|
||||
Dim Result As String = HttpRequestBuilder.
|
||||
Create("https://optifine.net/downloads", HttpMethod.Get).
|
||||
WithHeader("Accept", "application/json, text/javascript, */*; q=0.01").
|
||||
WithHeader("Accept-Language", "en-US,en;q=0.5").
|
||||
WithHeader("X-Requested-With", "XMLHttpRequest").
|
||||
SendAsync(True).
|
||||
Result.
|
||||
AsStringContent()
|
||||
If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
|
||||
Try
|
||||
'获取所有版本信息
|
||||
|
||||
@@ -118,11 +118,11 @@ Public Module ModJava
|
||||
Javas.CheckJavaAvailability()
|
||||
Dim reqMin = If(MinVersion, New Version(1, 0, 0))
|
||||
Dim reqMax = If(MaxVersion, New Version(999, 999, 999))
|
||||
Dim ret = Javas.SelectSuitableJava(reqMin, reqMax).Result.FirstOrDefault()
|
||||
Dim ret = Javas.SelectSuitableJavaAsync(reqMin, reqMax).Result.FirstOrDefault()
|
||||
If ret Is Nothing Then
|
||||
Log("[Java] 没有找到合适的 Java 开始尝试重新搜索后选择")
|
||||
Javas.ScanJava().GetAwaiter().GetResult()
|
||||
ret = Javas.SelectSuitableJava(reqMin, reqMax).Result.FirstOrDefault()
|
||||
Javas.ScanJavaAsync().GetAwaiter().GetResult()
|
||||
ret = Javas.SelectSuitableJavaAsync(reqMin, reqMax).Result.FirstOrDefault()
|
||||
End If
|
||||
Log($"[Java] 返回自动选择的 Java {If(ret IsNot Nothing, ret.ToString(), "无结果")}")
|
||||
Return ret
|
||||
@@ -216,7 +216,7 @@ Public Module ModJava
|
||||
Log($"[Java] 由于下载未完成,清理未下载完成的 Java 文件:{LastJavaBaseDir}", LogLevel.Debug)
|
||||
DeleteDirectory(LastJavaBaseDir)
|
||||
ElseIf NewState = LoadState.Finished Then
|
||||
Javas.ScanJava().GetAwaiter().GetResult()
|
||||
Javas.ScanJavaAsync().GetAwaiter().GetResult()
|
||||
LastJavaBaseDir = Nothing
|
||||
End If
|
||||
End Sub
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
|
||||
Imports System.IO.Compression
|
||||
Imports System.Net.Http
|
||||
Imports System.Text.Json
|
||||
Imports System.Text.Json.Nodes
|
||||
Imports PCL.Core.Minecraft
|
||||
Imports PCL.Core.Utils
|
||||
Imports PCL.Core.Utils.OS
|
||||
Imports PCL.Core.Net
|
||||
|
||||
Public Module ModLaunch
|
||||
|
||||
@@ -609,8 +612,13 @@ SkipLogin:
|
||||
'初始请求
|
||||
Retry:
|
||||
McLaunchLog("开始正版验证 Step 1/6(原始登录)")
|
||||
Dim PrepareJson As JObject = GetJson(NetRequestRetry("https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode", "POST",
|
||||
$"client_id={OAuthClientId}&tenant=/consumers&scope=XboxLive.signin%20offline_access", "application/x-www-form-urlencoded"))
|
||||
Dim PrepareJson As JObject
|
||||
Using response = HttpRequestBuilder.Create("https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode", HttpMethod.Post).
|
||||
WithContent(New Http.StringContent($"client_id={OAuthClientId}&tenant=/consumers&scope=XboxLive.signin%20offline_access"), "application/x-www-form-urlencoded").
|
||||
SendAsync(True).Result
|
||||
PrepareJson = GetJson(response.AsStringContent())
|
||||
End Using
|
||||
|
||||
McLaunchLog("网页登录地址:" & PrepareJson("verification_uri").ToString)
|
||||
|
||||
'弹窗
|
||||
@@ -634,18 +642,20 @@ Retry:
|
||||
End If
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 正版验证步骤 1,刷新登录:从 OAuth Code 或 OAuth RefreshToken 获取 {OAuth AccessToken, OAuth RefreshToken}
|
||||
''' 正版验证步骤 1,刷新登录:从 OAuth Code 或 OAuth RefreshToken 获取 {OAuth accessToken, OAuth RefreshToken}
|
||||
''' </summary>
|
||||
''' <param name="Code"></param>
|
||||
''' <returns></returns>
|
||||
Private Function MsLoginStep1Refresh(Code As String) As String()
|
||||
McLaunchLog("开始正版验证 Step 1/6(刷新登录)")
|
||||
If String.IsNullOrEmpty(Code) Then Throw New ArgumentException("传入的 Code 为空", NameOf(Code))
|
||||
Dim Result As String = Nothing
|
||||
Dim Result As String
|
||||
Try
|
||||
Result = NetRequestRetry("https://login.live.com/oauth20_token.srf", "POST",
|
||||
$"client_id={OAuthClientId}&refresh_token={Uri.EscapeDataString(Code)}&grant_type=refresh_token&scope=XboxLive.signin%20offline_access",
|
||||
"application/x-www-form-urlencoded", 2)
|
||||
Using response = HttpRequestBuilder.Create("https://login.live.com/oauth20_token.srf", HttpMethod.Post).
|
||||
WithContent($"client_id={OAuthClientId}&refresh_token={Uri.EscapeDataString(Code)}&grant_type=refresh_token&scope=XboxLive.signin%20offline_access", "application/x-www-form-urlencoded").
|
||||
SendAsync(True).Result
|
||||
Result = response.AsStringContent()
|
||||
End Using
|
||||
Catch ex As ThreadInterruptedException
|
||||
Log(ex, "加载线程已终止")
|
||||
Catch ex As Exception
|
||||
@@ -672,31 +682,43 @@ Retry:
|
||||
Return {AccessToken, RefreshToken}
|
||||
End Function
|
||||
|
||||
|
||||
Private Class XBLTokenRequestData
|
||||
Public Class PropertiesData
|
||||
Public Property AuthMethod As String
|
||||
Public Property SiteName As String
|
||||
Public Property RpsTicket As String
|
||||
End Class
|
||||
Public Property Properties As PropertiesData
|
||||
Public Property RelyingParty As String
|
||||
Public Property TokenType As String
|
||||
End Class
|
||||
|
||||
''' <summary>
|
||||
''' 正版验证步骤 2:从 OAuth AccessToken 获取 XBLToken
|
||||
''' 正版验证步骤 2:从 OAuth accessToken 获取 XBLToken
|
||||
''' </summary>
|
||||
''' <param name="AccessToken">OAuth AccessToken</param>
|
||||
''' <param name="accessToken">OAuth accessToken</param>
|
||||
''' <returns>XBLToken</returns>
|
||||
Private Function MsLoginStep2(AccessToken As String) As String
|
||||
Private Function MsLoginStep2(accessToken As String) As String
|
||||
ProfileLog("开始正版验证 Step 2/6: 获取 XBLToken")
|
||||
If String.IsNullOrEmpty(AccessToken) Then Throw New ArgumentException("传入的 AccessToken 为空", NameOf(AccessToken))
|
||||
Dim Request As String = New JObject(
|
||||
New JProperty("Properties", New JObject(
|
||||
New JProperty("AuthMethod", "RPS"),
|
||||
New JProperty("SiteName", "user.auth.xboxlive.com"),
|
||||
New JProperty("RpsTicket", If(AccessToken.StartsWith("d="), AccessToken, $"d={AccessToken}"))
|
||||
)),
|
||||
New JProperty("RelyingParty", "http://auth.xboxlive.com"),
|
||||
New JProperty("TokenType", "JWT")
|
||||
).ToString(Newtonsoft.Json.Formatting.None)
|
||||
Dim Result As String = Nothing
|
||||
If String.IsNullOrEmpty(accessToken) Then Throw New ArgumentException("传入的 AccessToken 为空", NameOf(accessToken))
|
||||
Dim requestData As New XBLTokenRequestData With {
|
||||
.Properties = New XBLTokenRequestData.PropertiesData With {
|
||||
.AuthMethod = "RPS",
|
||||
.SiteName = "user.auth.xboxlive.com",
|
||||
.RpsTicket = $"d={accessToken}"
|
||||
},
|
||||
.RelyingParty = "http://auth.xboxlive.com",
|
||||
.TokenType = "JWT"
|
||||
}
|
||||
Dim Result As String
|
||||
Try
|
||||
Result = NetRequestRetry(
|
||||
"https://user.auth.xboxlive.com/user/authenticate",
|
||||
"POST",
|
||||
Request,
|
||||
"application/json",
|
||||
False)
|
||||
Dim contentData = JsonSerializer.Serialize(requestData)
|
||||
Using response = HttpRequestBuilder.Create("https://user.auth.xboxlive.com/user/authenticate", HttpMethod.Post).
|
||||
WithContent(contentData, "application/json").
|
||||
SendAsync(True).Result
|
||||
Result = response.AsStringContent()
|
||||
End Using
|
||||
Catch ex As Exception
|
||||
ProfileLog("正版验证 Step 2/6 获取 XBLToken 失败:" & ex.ToString())
|
||||
Dim IsIgnore As Boolean = False
|
||||
@@ -706,7 +728,6 @@ Retry:
|
||||
End Sub)
|
||||
If IsIgnore Then
|
||||
Return "Ignore"
|
||||
Exit Function
|
||||
End If
|
||||
End Try
|
||||
|
||||
@@ -714,6 +735,17 @@ Retry:
|
||||
Dim XBLToken As String = ResultJson("Token").ToString
|
||||
Return XBLToken
|
||||
End Function
|
||||
|
||||
|
||||
Private Class XSTSTokenRequestData
|
||||
Public Class PropertiesData
|
||||
Public Property SandboxId As String
|
||||
Public Property UserTokens As List(Of String)
|
||||
End Class
|
||||
Public Property Properties As PropertiesData
|
||||
Public Property RelyingParty As String
|
||||
Public Property TokenType As String
|
||||
End Class
|
||||
''' <summary>
|
||||
''' 正版验证步骤 3:从 XBLToken 获取 {XSTSToken, UHS}
|
||||
''' </summary>
|
||||
@@ -721,23 +753,23 @@ Retry:
|
||||
Private Function MsLoginStep3(XBLToken As String) As String()
|
||||
ProfileLog("开始正版验证 Step 3/6: 获取 XSTSToken")
|
||||
If String.IsNullOrEmpty(XBLToken) Then Throw New ArgumentException("XBLToken 为空,无法获取数据", NameOf(XBLToken))
|
||||
Dim Request As String = New JObject(
|
||||
New JProperty("Properties", New JObject(
|
||||
New JProperty("SandboxId", "RETAIL"),
|
||||
New JProperty("UserTokens", New JArray(XBLToken))
|
||||
)),
|
||||
New JProperty("RelyingParty", "rp://api.minecraftservices.com/"),
|
||||
New JProperty("TokenType", "JWT")
|
||||
).ToString(Newtonsoft.Json.Formatting.None)
|
||||
Dim requestData As New XSTSTokenRequestData With {
|
||||
.Properties = New XSTSTokenRequestData.PropertiesData With {
|
||||
.SandboxId = "RETAIL",
|
||||
.UserTokens = {XBLToken}.ToList()
|
||||
},
|
||||
.RelyingParty = "rp://api.minecraftservices.com/",
|
||||
.TokenType = "JWT"
|
||||
}
|
||||
Dim Result As String
|
||||
Try
|
||||
Result = NetRequestRetry(
|
||||
"https://xsts.auth.xboxlive.com/xsts/authorize",
|
||||
"POST",
|
||||
Request,
|
||||
"application/json",
|
||||
False)
|
||||
Catch ex As WebException
|
||||
Dim contentData = JsonSerializer.Serialize(requestData)
|
||||
Using response = HttpRequestBuilder.Create("https://xsts.auth.xboxlive.com/xsts/authorize", HttpMethod.Post).
|
||||
WithContent(contentData, "application/json").
|
||||
SendAsync(True).Result
|
||||
Result = response.AsStringContent()
|
||||
End Using
|
||||
Catch ex As HttpRequestException
|
||||
'参考 https://github.com/PrismarineJS/prismarine-auth/blob/master/src/common/Constants.js
|
||||
If ex.Message.Contains("2148916227") Then
|
||||
MyMsgBox("该账号似乎已被微软封禁,无法登录。", "登录失败", "我知道了", IsWarn:=True)
|
||||
@@ -783,28 +815,30 @@ Retry:
|
||||
Return {XSTSToken, UHS}
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 正版验证步骤 4:从 {XSTSToken, UHS} 获取 Minecraft AccessToken
|
||||
''' 正版验证步骤 4:从 {XSTSToken, UHS} 获取 Minecraft accessToken
|
||||
''' </summary>
|
||||
''' <param name="Tokens">包含 XSTSToken 与 UHS 的字符串组</param>
|
||||
''' <returns>Minecraft AccessToken</returns>
|
||||
''' <returns>Minecraft accessToken</returns>
|
||||
Private Function MsLoginStep4(Tokens As String()) As String
|
||||
ProfileLog("开始正版验证 Step 4/6: 获取 Minecraft AccessToken")
|
||||
If Tokens.Length < 2 OrElse String.IsNullOrEmpty(Tokens.ElementAt(0)) OrElse String.IsNullOrEmpty(Tokens.ElementAt(1)) Then Throw New ArgumentException("传入的 XSTSToken 或者 UHS 错误", NameOf(Tokens))
|
||||
Dim Request As String = New JObject(New JProperty("identityToken", $"XBL3.0 x={Tokens(1)};{Tokens(0)}")).ToString(0)
|
||||
Dim requestData As New Dictionary(Of String, String) From {
|
||||
{"identityToken", $"XBL3.0 x={Tokens(1)};{Tokens(0)}"}
|
||||
}
|
||||
Dim Result As String
|
||||
Try
|
||||
Result = NetRequestRetry(
|
||||
"https://api.minecraftservices.com/authentication/login_with_xbox",
|
||||
"POST",
|
||||
Request,
|
||||
"application/json",
|
||||
False)
|
||||
Catch ex As PCL.ModNet.HttpWebException
|
||||
Dim contentData = JsonSerializer.Serialize(requestData)
|
||||
Using response = HttpRequestBuilder.Create("https://api.minecraftservices.com/authentication/login_with_xbox", HttpMethod.Post).
|
||||
WithContent(contentData, "application/json").
|
||||
SendAsync(True).Result
|
||||
Result = response.AsStringContent()
|
||||
End Using
|
||||
Catch ex As HttpRequestException
|
||||
Dim Message As String = ex.Message
|
||||
If CType(ex.StatusCode, Integer) = 429 Then
|
||||
If ex.StatusCode.Equals(HttpStatusCode.TooManyRequests) Then
|
||||
Log(ex, "正版验证 Step 4 汇报 429")
|
||||
Throw New Exception("$登录尝试太过频繁,请等待几分钟后再试!")
|
||||
ElseIf ex.StatusCode = HttpStatusCode.NotFound Then
|
||||
ElseIf ex.StatusCode = HttpStatusCode.Forbidden Then
|
||||
Log(ex, "正版验证 Step 4 汇报 403")
|
||||
Throw New Exception("$当前 IP 的登录尝试异常。" & vbCrLf & "如果你使用了 VPN 或加速器,请把它们关掉或更换节点后再试!")
|
||||
Else
|
||||
@@ -830,51 +864,48 @@ Retry:
|
||||
''' <summary>
|
||||
''' 正版验证步骤 5:验证微软账号是否持有 MC,这也会刷新 XGP
|
||||
''' </summary>
|
||||
''' <param name="AccessToken">Minecraft AccessToken</param>
|
||||
Private Sub MsLoginStep5(AccessToken As String)
|
||||
''' <param name="accessToken">Minecraft accessToken</param>
|
||||
Private Sub MsLoginStep5(accessToken As String)
|
||||
ProfileLog("开始正版验证 Step 5/6: 验证账户是否持有 MC")
|
||||
If String.IsNullOrEmpty(AccessToken) Then Throw New ArgumentException("传入的 AccessToken 为空", NameOf(AccessToken))
|
||||
Dim Result As String = NetRequestRetry(
|
||||
"https://api.minecraftservices.com/entitlements",
|
||||
"GET",
|
||||
Nothing,
|
||||
"application/json",
|
||||
False,
|
||||
New Dictionary(Of String, String) From {{"Authorization", $"Bearer {AccessToken}"}})
|
||||
If String.IsNullOrEmpty(accessToken) Then Throw New ArgumentException("传入的 AccessToken 为空", NameOf(accessToken))
|
||||
Dim result As String
|
||||
Try
|
||||
Dim ResultJson As JObject = GetJson(Result)
|
||||
Using response = HttpRequestBuilder.Create("https://api.minecraftservices.com/entitlements", HttpMethod.Get).
|
||||
WithBearerToken(accessToken).
|
||||
SendAsync(True).Result
|
||||
result = response.AsStringContent()
|
||||
End Using
|
||||
Dim ResultJson As JObject = GetJson(result)
|
||||
If Not (ResultJson.ContainsKey("items") AndAlso ResultJson("items").Any(Function(x) x("name")?.ToString() = "product_minecraft" OrElse x("name")?.ToString() = "game_minecraft")) Then
|
||||
Select Case MyMsgBox($"暂时无法获取到此账户信息,此账户可能没有购买 Minecraft Java Edition 或者账户的 Xbox Game Pass 已过期", "登录失败", "购买 Minecraft", "取消")
|
||||
Case 1
|
||||
OpenWebsite("https://www.xbox.com/zh-cn/games/store/minecraft-java-bedrock-edition-for-pc/9nxp44l49shj")
|
||||
End Select
|
||||
Throw New Exception("$$")
|
||||
End If
|
||||
Select Case MyMsgBox($"暂时无法获取到此账户信息,此账户可能没有购买 Minecraft Java Edition 或者账户的 Xbox Game Pass 已过期", "登录失败", "购买 Minecraft", "取消")
|
||||
Case 1
|
||||
OpenWebsite("https://www.xbox.com/zh-cn/games/store/minecraft-java-bedrock-edition-for-pc/9nxp44l49shj")
|
||||
End Select
|
||||
Throw New Exception("$$")
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "正版验证 Step 5 异常:" & Result)
|
||||
Log(ex, "正版验证 Step 5 异常:" & result)
|
||||
Throw
|
||||
End Try
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 正版验证步骤 6:从 Minecraft AccessToken 获取 {UUID, UserName, ProfileJson}
|
||||
''' 正版验证步骤 6:从 Minecraft accessToken 获取 {UUID, UserName, ProfileJson}
|
||||
''' </summary>
|
||||
''' <param name="AccessToken">Minecraft AccessToken</param>
|
||||
''' <param name="AccessToken">Minecraft accessToken</param>
|
||||
''' <returns>包含 UUID, UserName 和 ProfileJson 的字符串组</returns>
|
||||
Private Function MsLoginStep6(AccessToken As String) As String()
|
||||
ProfileLog("开始正版验证 Step 6/6: 获取玩家 ID 与 UUID 等相关信息")
|
||||
If String.IsNullOrEmpty(AccessToken) Then Throw New ArgumentException("传入的 AccessToken 为空", NameOf(AccessToken))
|
||||
Dim Result As String
|
||||
Try
|
||||
Result = NetRequestRetry(
|
||||
"https://api.minecraftservices.com/minecraft/profile",
|
||||
"GET",
|
||||
"",
|
||||
"application/json",
|
||||
False,
|
||||
New Dictionary(Of String, String) From {{"Authorization", $"Bearer {AccessToken}"}})
|
||||
Catch ex As PCL.ModNet.HttpWebException
|
||||
Using response = HttpRequestBuilder.Create("https://api.minecraftservices.com/minecraft/profile", HttpMethod.Get).
|
||||
WithBearerToken(AccessToken).
|
||||
SendAsync(True).Result
|
||||
Result = response.AsStringContent()
|
||||
End Using
|
||||
Catch ex As HttpRequestException
|
||||
Dim Message As String = ex.Message
|
||||
If CType(ex.StatusCode, Integer) = 429 Then '微软!我的 TooManyRequests 枚举呢?
|
||||
If ex.StatusCode.Equals(HttpStatusCode.TooManyRequests) Then
|
||||
Log(ex, "正版验证 Step 6 汇报 429")
|
||||
Throw New Exception("$登录尝试太过频繁,请等待几分钟后再试!")
|
||||
ElseIf ex.StatusCode = HttpStatusCode.NotFound Then
|
||||
|
||||
@@ -330,72 +330,6 @@ Public Module ModLink
|
||||
Dim SupportIPv6 As Boolean = Ips.Cast(Of Object)().Any(Function(Ip) Ip.contains(":"))
|
||||
Return {NatType, SupportIPv6}
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 进行网络测试,包括 IPv4 NAT 类型测试和 IPv6 支持情况测试
|
||||
''' </summary>
|
||||
''' <returns>NAT 类型 + IPv6 支持与否</returns>
|
||||
Public Function NetTest() As String()
|
||||
'申请通过防火墙以准确测试 NAT 类型
|
||||
Dim RetryTime As Integer = 0
|
||||
Try
|
||||
PortRetry:
|
||||
Dim TestTcpListener = TcpListener.Create(RandomUtils.NextInt(20000, 65000))
|
||||
TestTcpListener.Start()
|
||||
Thread.Sleep(200)
|
||||
TestTcpListener.Stop()
|
||||
Catch ex As Exception
|
||||
Log(ex, "[Link] 请求防火墙通过失败")
|
||||
If RetryTime >= 3 Then
|
||||
Log("[Link] 请求防火墙通过失败次数已达 3 次,不再重试")
|
||||
Exit Try
|
||||
End If
|
||||
GoTo PortRetry
|
||||
End Try
|
||||
'IPv4 NAT 测试
|
||||
Dim NATType As String
|
||||
Dim STUNServerDomain As String = "stun.miwifi.com" '指定 STUN 服务器
|
||||
Log("[STUN] 指定的 STUN 服务器: " + STUNServerDomain)
|
||||
Try
|
||||
Dim STUNServerIP As String = Dns.GetHostAddresses(STUNServerDomain)(0).ToString() '解析 STUN 服务器 IP
|
||||
Log("[STUN] 解析目标 STUN 服务器 IP: " + STUNServerIP)
|
||||
Dim STUNServerEndPoint As IPEndPoint = New IPEndPoint(IPAddress.Parse(STUNServerIP), 3478) '设置 IPEndPoint
|
||||
|
||||
STUNClient.ReceiveTimeout = 500 '设置超时
|
||||
Log("[STUN] 开始进行 NAT 测试")
|
||||
Dim STUNTestResult = STUNClient.Query(STUNServerEndPoint, STUNQueryType.ExactNAT, True) '进行 STUN 测试
|
||||
|
||||
NATType = STUNTestResult.NATType.ToString()
|
||||
Log("[STUN] 本地 NAT 类型: " + NATType)
|
||||
Catch ex As Exception
|
||||
Log(ex, "[STUN] 进行 NAT 测试失败", LogLevel.Normal)
|
||||
NATType = "TestFailed"
|
||||
End Try
|
||||
|
||||
'IPv6
|
||||
Dim IPv6Status As String = "Unsupported"
|
||||
Try
|
||||
For Each ip In NatDiscovery.GetIPAddresses()
|
||||
If ip.AddressFamily() = AddressFamily.InterNetworkV6 Then 'IPv6
|
||||
If ip.IsIPv6LinkLocal() OrElse ip.IsIPv6SiteLocal() OrElse ip.IsIPv6Teredo() OrElse ip.IsIPv4MappedToIPv6() Then
|
||||
Continue For
|
||||
ElseIf ip.IsPublic() Then
|
||||
Log("[IP] 检测到 IPv6 公网地址")
|
||||
IPv6Status = "Public"
|
||||
Exit For
|
||||
ElseIf ip.IsPrivate() AndAlso Not IPv6Status = "Supported" Then
|
||||
Log("[IP] 检测到 IPv6 支持")
|
||||
IPv6Status = "Supported"
|
||||
Continue For
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
Catch ex As Exception
|
||||
Log(ex, "[IP] 进行 IPv6 测试失败", LogLevel.Normal)
|
||||
IPv6Status = "Unknown"
|
||||
End Try
|
||||
|
||||
Return {NATType, IPv6Status}
|
||||
End Function
|
||||
#End Region
|
||||
|
||||
End Module
|
||||
|
||||
@@ -9,6 +9,7 @@ Imports PCL.Core.Utils
|
||||
Imports PCL.Core.Utils.Exts
|
||||
Imports PCL.Core.Utils.OS
|
||||
Imports PCL.Core.Utils.Secret
|
||||
Imports PCL.Core.Net
|
||||
|
||||
Friend Module ModSecret
|
||||
|
||||
@@ -141,9 +142,6 @@ PCL-Community 及其成员与龙腾猫跃无从属关系,且均不会为您的
|
||||
#End Region
|
||||
|
||||
#Region "网络鉴权"
|
||||
|
||||
|
||||
|
||||
Friend Function SecretCdnSign(UrlWithMark As String)
|
||||
If Not UrlWithMark.EndsWithF("{CDN}") Then Return UrlWithMark
|
||||
Return UrlWithMark.Replace("{CDN}", "").Replace(" ", "%20")
|
||||
@@ -163,7 +161,6 @@ PCL-Community 及其成员与龙腾猫跃无从属关系,且均不会为您的
|
||||
Client.Headers.Add("User-Agent", userAgent)
|
||||
|
||||
Client.Headers.Add("Referer", "http://" & VersionCode & ".ce.open.pcl2.server/")
|
||||
If Url.Contains("pcl2ce.pysio.online/post") AndAlso Not String.IsNullOrEmpty(TelemetryKey) Then Client.Headers.Add("Authorization", TelemetryKey)
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
@@ -938,42 +935,6 @@ PCL-Community 及其成员与龙腾猫跃无从属关系,且均不会为您的
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "遥测"
|
||||
''' <summary>
|
||||
''' 发送遥测数据,需要在非 UI 线程运行
|
||||
''' </summary>
|
||||
Public Sub SendTelemetry()
|
||||
If String.IsNullOrWhiteSpace(TelemetryKey) Then Exit Sub
|
||||
Dim NetResult = ModLink.NetTest()
|
||||
Dim Data = New JObject From {
|
||||
{"Tag", "Telemetry"},
|
||||
{"Id", UniqueAddress},
|
||||
{"OS", Environment.OSVersion.Version.Build},
|
||||
{"Is64Bit", Not Is32BitSystem},
|
||||
{"IsARM64", IsArm64System},
|
||||
{"Launcher", VersionCode},
|
||||
{"LauncherBranch", If(IsUpdBetaChannel, "Fast Ring", "Slow Ring")},
|
||||
{"UsedOfficialPCL", ReadReg("SystemEula", Nothing, "PCL") IsNot Nothing},
|
||||
{"UsedHMCL", Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\.hmcl")},
|
||||
{"UsedBakaXL", Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\BakaXL")},
|
||||
{"Memory", SystemMemorySize},
|
||||
{"NatType", NetResult(0)},
|
||||
{"IPv6Status", NetResult(1)}
|
||||
}
|
||||
Dim SendData = New JObject From {{"data", Data}}
|
||||
Try
|
||||
Dim Result As String = NetRequestRetry("https://pcl2ce.pysio.online/post", "POST", SendData.ToString(), "application/json")
|
||||
If Result.Contains("数据已成功保存") Then
|
||||
Log("[Telemetry] 软硬件调查数据已发送")
|
||||
Else
|
||||
Log("[Telemetry] 软硬件调查数据发送失败,原始返回内容: " + Result)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "[Telemetry] 软硬件调查数据发送失败", LogLevel.Normal)
|
||||
End Try
|
||||
End Sub
|
||||
#End Region
|
||||
|
||||
#Region "系统信息"
|
||||
Friend CPUName As String = Nothing
|
||||
''' <summary>
|
||||
|
||||
@@ -23,7 +23,7 @@ Public Module ModWebServer
|
||||
Async Function() As Task
|
||||
Log($"[WebServer] 服务端 '{name}' 已启动")
|
||||
Try
|
||||
Await server.StartResponse()
|
||||
Await server.StartResponseAsync()
|
||||
Catch ex As Exception
|
||||
Log(ex, $"[WebServer] 服务端 '{name}' 运行出错")
|
||||
End Try
|
||||
@@ -207,7 +207,7 @@ Public Module ModWebServer
|
||||
Dim code = parameters("code")
|
||||
Dim resultEx As Exception = Nothing
|
||||
Try
|
||||
NatayarkProfileManager.GetNaidDataSync(code).Wait()
|
||||
NatayarkProfileManager.GetNaidDataAsync(code).Wait()
|
||||
Catch ex As AggregateException
|
||||
resultEx = ex.InnerExceptions(0)
|
||||
End Try
|
||||
|
||||
@@ -137,7 +137,7 @@ Public Class UpdatesMinioModel '社区自己的更新系统格式
|
||||
loaders.Add(New LoaderTask(Of String, Integer)("应用文件", Sub()
|
||||
If patchUpdate Then
|
||||
Dim diff As New BsDiff()
|
||||
Dim newFile = diff.Apply(ReadFileBytes(ExePathWithName), ReadFileBytes(tempPath)).GetAwaiter().GetResult()
|
||||
Dim newFile = diff.ApplyAsync(ReadFileBytes(ExePathWithName), ReadFileBytes(tempPath)).GetAwaiter().GetResult()
|
||||
WriteFile(output, newFile)
|
||||
Else
|
||||
Using fs As New FileStream(tempPath, FileMode.Open, FileAccess.Read, FileShare.Read)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
Imports System.IO.Compression
|
||||
Imports PCL.Core.Minecraft
|
||||
Imports PCL.Core.Net
|
||||
Imports System.Net.Http
|
||||
Imports PCL.Core.UI
|
||||
|
||||
Public Module ModDownloadLib
|
||||
@@ -606,7 +608,14 @@ pause"
|
||||
'官方源
|
||||
Dim PageData As String
|
||||
Try
|
||||
PageData = NetGetCodeByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, New UTF8Encoding(False), 15000, "text/html", True)
|
||||
PageData = HttpRequestBuilder.
|
||||
Create("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, HttpMethod.Get).
|
||||
WithHeader("Accept", "text/html").
|
||||
WithHeader("Accept-Language", "en-US,en;q=0.5").
|
||||
WithHeader("X-Requested-With", "XMLHttpRequest").
|
||||
SendAsync(True).
|
||||
Result.
|
||||
AsStringContent()
|
||||
Task.Progress = 0.8
|
||||
Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0))
|
||||
Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources.Last)
|
||||
@@ -769,7 +778,14 @@ Retry:
|
||||
'官方源
|
||||
Dim PageData As String
|
||||
Try
|
||||
PageData = NetGetCodeByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, New UTF8Encoding(False), 15000, "text/html", True)
|
||||
PageData = HttpRequestBuilder.
|
||||
Create("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, HttpMethod.Get).
|
||||
WithHeader("Accept", "text/html").
|
||||
WithHeader("Accept-Language", "en-US,en;q=0.5").
|
||||
WithHeader("X-Requested-With", "XMLHttpRequest").
|
||||
SendAsync(True).
|
||||
Result.
|
||||
AsStringContent()
|
||||
Task.Progress = 0.8
|
||||
Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0))
|
||||
Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources.Last)
|
||||
|
||||
@@ -79,7 +79,7 @@ Public Class PageLoginAuth
|
||||
Dim serverUri As String = Nothing
|
||||
Dim serverName As String = Nothing
|
||||
Try
|
||||
serverUri = Await ApiLocation.TryRequest(serverUriInput)
|
||||
serverUri = Await ApiLocation.TryRequestAsync(serverUriInput)
|
||||
Dim response = Await HttpRequestBuilder.Create(serverUri, HttpMethod.Get).SendAsync()
|
||||
Dim responseText As String = Await response.AsStringAsync()
|
||||
serverName = Await Task.Run(Function() JObject.Parse(responseText)("meta")("serverName").ToString())
|
||||
|
||||
@@ -304,7 +304,7 @@ Public Class PageLinkLobby
|
||||
Log("[Link] 启动 EasyTier 轮询")
|
||||
IsWatcherStarted = True
|
||||
Dim retryCount = 0
|
||||
While ETInfoProvider.CheckETStatus().GetAwaiter().GetResult() = 0 AndAlso retryCount <= 15
|
||||
While ETInfoProvider.CheckETStatusAsync().GetAwaiter().GetResult() = 0 AndAlso retryCount <= 15
|
||||
retryCount += GetETInfo()
|
||||
If RequiresLogin AndAlso String.IsNullOrWhiteSpace(NaidProfile.AccessToken) Then
|
||||
Hint("请先登录 Natayark ID 再使用大厅!", HintType.Critical)
|
||||
|
||||
@@ -227,122 +227,101 @@
|
||||
EventType="打开网页" EventData="https://iconmonstr.com/" />
|
||||
<local:MyButton Grid.Row="9" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://iconmonstr.com/license/" />
|
||||
|
||||
<TextBlock Grid.Row="10" Grid.Column="0" Text="SixLabors.ImageSharp" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="10" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © Six Labors
Licensed under the Apache License 2.0." TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="11" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/SixLabors/ImageSharp" />
|
||||
<local:MyButton Grid.Row="11" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/SixLabors/ImageSharp/blob/main/LICENSE" />
|
||||
|
||||
<TextBlock Grid.Row="12" Grid.Column="0" Text="Iconfont" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="12" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © ALIMAMA MUX, powered by alimama THX." TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="13" Grid.Column="2" MinWidth="140" Text="查看原网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<TextBlock Grid.Row="10" Grid.Column="0" Text="Iconfont" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="10" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © ALIMAMA MUX, powered by alimama THX." TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="11" Grid.Column="2" MinWidth="140" Text="查看原网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://www.iconfont.cn/" />
|
||||
|
||||
<TextBlock Grid.Row="14" Grid.Column="0" Text="Fody" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="14" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © Simon Cropp" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="15" Grid.Column="2" MinWidth="140" Text="查看原网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<TextBlock Grid.Row="12" Grid.Column="0" Text="Fody" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="12" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © Simon Cropp" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="13" Grid.Column="2" MinWidth="140" Text="查看原网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/Fody/Fody" />
|
||||
<local:MyButton Grid.Row="15" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<local:MyButton Grid.Row="13" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/Fody/Fody/blob/master/License.txt" />
|
||||
|
||||
<TextBlock Grid.Row="16" Grid.Column="0" Text="Makaretu.Nat" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="16" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2018 Richard Schneider" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="17" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/richardschneider/net-nat" />
|
||||
<local:MyButton Grid.Row="17" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/richardschneider/net-nat/blob/master/LICENSE" />
|
||||
<TextBlock Grid.Row="14" Grid.Column="0" Text="Stun.Net" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="14" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © Bruce Wayne" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="15" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/HMBSbige/NatTypeTester" />
|
||||
<local:MyButton Grid.Row="15" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/HMBSbige/NatTypeTester/blob/master/LICENSE" />
|
||||
|
||||
<TextBlock Grid.Row="18" Grid.Column="0" Text="IPNetwork" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="18" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2015, lduchosal" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="19" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<TextBlock Grid.Row="16" Grid.Column="0" Text="IPNetwork" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="16" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2015, lduchosal" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="17" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/lduchosal/ipnetwork" />
|
||||
<local:MyButton Grid.Row="19" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<local:MyButton Grid.Row="17" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/lduchosal/ipnetwork/blob/master/LICENSE" />
|
||||
|
||||
<TextBlock Grid.Row="20" Grid.Column="0" Text="STUN" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="20" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2019 Moien007" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="21" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/moien007/stun" />
|
||||
<local:MyButton Grid.Row="21" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/moien007/STUN/blob/master/LICENSE" />
|
||||
|
||||
<TextBlock Grid.Row="22" Grid.Column="0" Text="Open.NAT" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="22" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2010-2011 Erik Davidson, 2012+ Matvei Stefarov
License under 3-Clause BSD" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="23" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/mstefarov/fNbt" />
|
||||
<local:MyButton Grid.Row="23" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://licenses.nuget.org/BSD-3-Clause" />
|
||||
|
||||
<TextBlock Grid.Row="24" Grid.Column="0" Text="TagLibSharp" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="24" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2006-2007 Brian Nickel.
Copyright © 2009-2020 Other contributors" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="25" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<TextBlock Grid.Row="18" Grid.Column="0" Text="TagLibSharp" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="18" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2006-2007 Brian Nickel.
Copyright © 2009-2020 Other contributors" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="19" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/mono/taglib-sharp" />
|
||||
<local:MyButton Grid.Row="25" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<local:MyButton Grid.Row="19" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/mono/taglib-sharp/blob/main/COPYING" />
|
||||
|
||||
<TextBlock Grid.Row="26" Grid.Column="0" Text="Microsoft.Identity.Client" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="26" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © Microsoft Corporation" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="27" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<TextBlock Grid.Row="20" Grid.Column="0" Text="Microsoft.Identity.Client" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="20" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © Microsoft Corporation" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="21" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://go.microsoft.com/fwlink/?linkid=844761" />
|
||||
<local:MyButton Grid.Row="27" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<local:MyButton Grid.Row="21" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://licenses.nuget.org/MIT" />
|
||||
|
||||
<TextBlock Grid.Row="28" Grid.Column="0" Text="Microsoft.Identity.Client.Broker" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="28" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © Microsoft Corporation" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="29" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<TextBlock Grid.Row="22" Grid.Column="0" Text="Microsoft.Identity.Client.Broker" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="22" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © Microsoft Corporation" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="23" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://go.microsoft.com/fwlink/?linkid=844761" />
|
||||
<local:MyButton Grid.Row="29" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<local:MyButton Grid.Row="23" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://licenses.nuget.org/MIT" />
|
||||
|
||||
<TextBlock Grid.Row="30" Grid.Column="0" Text="EntityFramework" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="30" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © Microsoft Corporation" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="31" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<TextBlock Grid.Row="24" Grid.Column="0" Text="EntityFramework" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="24" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © Microsoft Corporation" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="25" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/dotnet/efcore" />
|
||||
<local:MyButton Grid.Row="31" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<local:MyButton Grid.Row="25" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://licenses.nuget.org/Apache-2.0" />
|
||||
|
||||
<TextBlock Grid.Row="32" Grid.Column="0" Text="PCL.Core" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="32" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © PCL Community" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="33" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<TextBlock Grid.Row="26" Grid.Column="0" Text="PCL.Core" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="26" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © PCL Community" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="27" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/PCL-Community/PCL.Core" />
|
||||
<local:MyButton Grid.Row="33" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<local:MyButton Grid.Row="27" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/PCL-Community/PCL.Core/blob/main/LICENCE" />
|
||||
|
||||
<TextBlock Grid.Row="34" Grid.Column="0" Text="EleCho.WpfSuite" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="34" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © EleCho" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="35" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<TextBlock Grid.Row="28" Grid.Column="0" Text="EleCho.WpfSuite" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="28" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © EleCho" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="29" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/OrgEleCho/EleCho.WpfSuite" />
|
||||
<local:MyButton Grid.Row="35" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<local:MyButton Grid.Row="29" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/OrgEleCho/EleCho.WpfSuite/blob/master/LICENSE.txt" />
|
||||
|
||||
<TextBlock Grid.Row="36" Grid.Column="0" Text="LiteDB" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="36" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2014-2022 Mauricio David" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="37" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<TextBlock Grid.Row="30" Grid.Column="0" Text="LiteDB" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="30" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2014-2022 Mauricio David" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="31" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/litedb-org/LiteDB" />
|
||||
<local:MyButton Grid.Row="37" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<local:MyButton Grid.Row="31" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/litedb-org/LiteDB/blob/master/LICENSE" />
|
||||
|
||||
<TextBlock Grid.Row="38" Grid.Column="0" Text="Markdig.Wpf" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="38" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2016-2021 Nicolas Musset" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="39" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<TextBlock Grid.Row="32" Grid.Column="0" Text="Markdig.Wpf" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="32" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2016-2021 Nicolas Musset" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="33" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/Kryptos-FR/markdig.wpf" />
|
||||
<local:MyButton Grid.Row="39" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<local:MyButton Grid.Row="33" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/Kryptos-FR/markdig.wpf/blob/main/LICENSE.md" />
|
||||
|
||||
<TextBlock Grid.Row="40" Grid.Column="0" Text="CacheCow" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="40" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2013 Ali Kheyrollahi" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="41" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<TextBlock Grid.Row="34" Grid.Column="0" Text="CacheCow" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="34" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2013 Ali Kheyrollahi" TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="35" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/aliostad/CacheCow" />
|
||||
<local:MyButton Grid.Row="41" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<local:MyButton Grid.Row="35" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/aliostad/CacheCow/blob/master/LICENSE.txt" />
|
||||
|
||||
<TextBlock Grid.Row="42" Grid.Column="0" Text="Mesa Loader Windows" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="42" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2024 Glavo
Licensed under the Apache 2 License." TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="43" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<TextBlock Grid.Row="36" Grid.Column="0" Text="Mesa Loader Windows" TextWrapping="Wrap" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="36" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2024 Glavo
Licensed under the Apache 2 License." TextWrapping="Wrap" />
|
||||
<local:MyButton Grid.Row="37" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/HMCL-dev/mesa-loader-windows" />
|
||||
<local:MyButton Grid.Row="43" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
<local:MyButton Grid.Row="37" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
|
||||
EventType="打开网页" EventData="https://github.com/HMCL-dev/mesa-loader-windows/blob/main/LICENSE" />
|
||||
</Grid>
|
||||
</local:MyCard>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
Imports System.Text.RegularExpressions
|
||||
Imports PCL.Core.Net
|
||||
|
||||
Public Class PageOtherVote
|
||||
Public Class Vote
|
||||
@@ -27,7 +28,11 @@ Public Class PageOtherVote
|
||||
End Function
|
||||
|
||||
Public Sub VoteListGet(Task As LoaderTask(Of Integer, List(Of Vote)))
|
||||
Dim content = NetGetCodeByRequestRetry("https://github.com/Meloong-Git/PCL/discussions/categories/%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8?discussions_q=is%3Aopen+category%3A%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8+sort%3Atop", UseBrowserUserAgent:=True)
|
||||
Dim content As String
|
||||
Using response = HttpRequestBuilder.Create("https://github.com/Meloong-Git/PCL/discussions/categories/%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8?discussions_q=is%3Aopen+category%3A%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8+sort%3Atop", Http.HttpMethod.Get).
|
||||
SendAsync(True).Result
|
||||
content = response.AsStringContent()
|
||||
End Using
|
||||
If content Is Nothing Then Throw New Exception("空内容")
|
||||
|
||||
Dim pattern As String = "<div class=""d-flex flex-auto flex-items-start"">(.*?)<svg aria-hidden=""true"" height=""16"" viewBox=""0 0 16 16"" version=""1.1"" width=""16"" data-view-component=""true"" class=""octicon octicon-comment color-fg-muted mr-1"">"
|
||||
|
||||
@@ -15,7 +15,7 @@ Public Class PageSetupJava
|
||||
Return Javas.JavaList.Count
|
||||
End Function
|
||||
Private Sub Load_GetJavaList(loader As LoaderTask(Of Integer, List(Of Java)))
|
||||
Javas.ScanJava().GetAwaiter().GetResult()
|
||||
Javas.ScanJavaAsync().GetAwaiter().GetResult()
|
||||
loader.Output = Javas.JavaList
|
||||
End Sub
|
||||
|
||||
|
||||
@@ -66,10 +66,8 @@
|
||||
<PackageReference Include="LiteDB" Version="5.0.21" />
|
||||
<PackageReference Include="Markdig.Wpf" Version="0.5.0.1" />
|
||||
<PackageReference Include="IPNetwork2" Version="3.1.775" />
|
||||
<PackageReference Include="Makaretu.Nat" Version="0.2.0" />
|
||||
<PackageReference Include="NAudio" Version="2.2.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="STUN" Version="0.5.0" />
|
||||
<PackageReference Include="System.Management" Version="9.0.8" />
|
||||
<PackageReference Include="System.Private.Uri" Version="4.3.2" />
|
||||
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||
|
||||
Reference in New Issue
Block a user