Compare commits

...
17 Commits
Author SHA1 Message Date
Aigio Liu c749fad3be 🎨 加速二级代理描述 2025-03-24 16:29:02 +08:00
k1mlka 25afcb2b3f 🐛 ChangePropertyAction Content -> Value 2025-03-21 18:03:06 +08:00
Aigio Liu 45683c78a8 ⬆️ Pkg 2025-03-21 17:46:01 +08:00
k1mlka a477cd87cf 💄 ChangePropertyAction 限定值的颜色类型 2025-03-21 17:34:00 +08:00
Aigio Liu a2cf95f0c1 🐛 NATCheckCommand 2025-03-21 13:31:58 +08:00
Aigio Liu 88ddc7a320 🔖 3.0.0-rc.16 2025-03-21 10:04:27 +08:00
RMBGAME e5463f44d2 ⬆️ FluentAvalonia 2.3.0 2025-03-19 00:42:45 +08:00
Aigio Liu 816d1c9b0c 🐛 TestStunClient3489Async 2025-03-17 14:16:50 +08:00
Aigio Liu 69296f9587 ✨ IcuTestFail 2025-03-15 22:39:59 +08:00
Aigio Liu 6577ed9fa0 🚧 GenerateServerSideProxyToken cts 2025-03-13 00:10:19 +08:00
Aigio Liu a54e988441 🐛 https://github.com/BeyondDimension/SteamTools/issues/3590 2025-03-11 04:51:58 +08:00
Aigio Liu db508a04f0 🐛 GetActiveUserRecord 2025-03-10 00:50:23 +08:00
RMBGAME eb065d7c7e 🐛 部分选中的加速效果存在选择问题 2025-03-08 16:58:14 +08:00
RMBGAME 1b639291e4 ✨ Http3 2025-03-05 15:51:50 +08:00
RMBGAME c8810f7926 ⬆️ 反代 http3 支持 2025-03-04 17:17:29 +08:00
RMBGAME 073b5885c6 ⬆️ Avalonia 11.2.5 2025-03-03 14:04:47 +08:00
ff m ed6df52998 🐛 修复 没登陆无法跳转问题 2025-02-26 12:51:35 +08:00
22 changed files with 281 additions and 189 deletions
+1 -1
View File
@@ -21,7 +21,7 @@ public static partial class AssemblyInfo
/// <summary>
/// RC 版本号,范围 0~9,只增不减
/// </summary>
const string ver_for_rc = "15";
const string ver_for_rc = "16";
public const string FileVersion = $"{Version2}.{ver_for_rc}2{ver_for_preview}.0";
+33 -24
View File
@@ -295,33 +295,42 @@ sealed partial class Program : Startup
protected override ActiveUserRecordDTO GetActiveUserRecord()
{
#if !__MOBILE__ && !MAUI
var app = UI.App.Instance;
var window = app.GetFirstOrDefaultWindow();
var screens = window?.Screens;
#else
var mainDisplayInfo = DeviceDisplay.MainDisplayInfo;
var mainDisplayInfoH = mainDisplayInfo.Height.ToInt32(NumberToInt32Format.Ceiling);
var mainDisplayInfoW = mainDisplayInfo.Width.ToInt32(NumberToInt32Format.Ceiling);
#endif
var result = new ActiveUserRecordDTO
{
#if __MOBILE__ || MAUI
ScreenCount = 1,
PrimaryScreenPixelDensity = mainDisplayInfo.Density,
PrimaryScreenWidth = mainDisplayInfoW,
PrimaryScreenHeight = mainDisplayInfoH,
SumScreenWidth = mainDisplayInfoW,
SumScreenHeight = mainDisplayInfoH,
#else
ScreenCount = screens?.ScreenCount ?? default,
PrimaryScreenPixelDensity = screens?.Primary?.Scaling ?? default,
PrimaryScreenWidth = screens?.Primary?.Bounds.Width ?? default,
PrimaryScreenHeight = screens?.Primary?.Bounds.Height ?? default,
SumScreenWidth = screens?.All.Sum(x => x.Bounds.Width) ?? default,
SumScreenHeight = screens?.All.Sum(x => x.Bounds.Height) ?? default,
#endif
};
SetScreen(result);
static void SetScreen(ActiveUserRecordDTO m)
{
try
{
#if !__MOBILE__ && !MAUI
var app = UI.App.Instance;
var window = app.GetFirstOrDefaultWindow();
var screens = window?.Screens;
m.ScreenCount = screens?.ScreenCount ?? default;
m.PrimaryScreenPixelDensity = screens?.Primary?.Scaling ?? default;
m.PrimaryScreenWidth = screens?.Primary?.Bounds.Width ?? default;
m.PrimaryScreenHeight = screens?.Primary?.Bounds.Height ?? default;
m.SumScreenWidth = screens?.All?.Sum(x => x.Bounds.Width) ?? default;
m.SumScreenHeight = screens?.All?.Sum(x => x.Bounds.Height) ?? default;
#else
var mainDisplayInfo = DeviceDisplay.MainDisplayInfo;
var mainDisplayInfoH = mainDisplayInfo.Height.ToInt32(NumberToInt32Format.Ceiling);
var mainDisplayInfoW = mainDisplayInfo.Width.ToInt32(NumberToInt32Format.Ceiling);
m.ScreenCount = 1;
m.PrimaryScreenPixelDensity = mainDisplayInfo.Density;
m.PrimaryScreenWidth = mainDisplayInfoW;
m.PrimaryScreenHeight = mainDisplayInfoH;
m.SumScreenWidth = mainDisplayInfoW;
m.SumScreenHeight = mainDisplayInfoH;
#endif
}
catch
{
}
}
return result;
}
}
+1 -1
View File
@@ -45,7 +45,7 @@ public sealed partial class App : Application
{
if (ApplicationLifetime is ClassicDesktopStyleApplicationLifetime classicDesktopStyleApplicationLifetime)
{
window = classicDesktopStyleApplicationLifetime.Windows.FirstOrDefault(x => x != null);
window = classicDesktopStyleApplicationLifetime.Windows?.FirstOrDefault(x => x != null);
}
}
return window;
@@ -40,14 +40,6 @@
<Setter Property="FontFamily" Value="{Binding Source={x:Static ms:UISettings.FontName}, Path=Value, Mode=OneWay, Converter={StaticResource NameToFontFamilyConverter}}" />
</Style>
<Style Selector="ui|FontIcon">
<Setter Property="MinWidth" Value="16" />
</Style>
<Style Selector="ui|SymbolIcon">
<Setter Property="MinWidth" Value="16" />
</Style>
<!--<Style Selector="TextBlock">
<Setter Property="FontFamily" Value="{Binding Source={x:Static ms:UISettings.FontName}, Path=Value, Mode=OneWay, Converter={StaticResource NameToFontFamilyConverter}}" />
</Style>-->
@@ -35,6 +35,7 @@ public static class KestrelServerOptionsExtensions
options.Listen(IReverseProxyService.Constants.Instance.ProxyIp, httpProxyPort, listen =>
{
listen.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
var proxyMiddleware = options.ApplicationServices.GetRequiredService<HttpProxyMiddleware>();
var tunnelMiddleware = options.ApplicationServices.GetRequiredService<TunnelMiddleware>();
@@ -123,7 +124,7 @@ public static class KestrelServerOptionsExtensions
var httpsPort = IReverseProxyConfig.HttpsPort;
options.Listen(IReverseProxyService.Constants.Instance.ProxyIp, httpsPort, listen =>
{
listen.Protocols = HttpProtocols.Http1AndHttp2;
listen.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
listen.UseFlowAnalyze();
listen.UseTls();
});
@@ -36,7 +36,7 @@ sealed class ReverseProxyHttpClientHandler : DelegatingHandler
// 设置请求头 host,因为连接中已带端口号故 修改协议为 http
request.Headers.Host = uri.Host;
request.RequestUri = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttp }.Uri;
//request.RequestUri = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttp }.Uri;
if (domainConfig.Timeout != null)
{
@@ -61,6 +61,7 @@ sealed class ReverseProxyHttpClientHandler : DelegatingHandler
AutomaticDecompression = DecompressionMethods.None,
ConnectCallback = ConnectCallback,
EnableMultipleHttp2Connections = true,
EnableMultipleHttp3Connections = true,
RequestHeaderEncodingSelector = (_, _) => Encoding.UTF8,
ResponseHeaderEncodingSelector = (_, _) => Encoding.UTF8,
};
@@ -2,6 +2,7 @@
using Microsoft.AspNetCore.Http;
using Yarp.ReverseProxy.Forwarder;
using HttpVersion = System.Net.HttpVersion;
// ReSharper disable once CheckNamespace
namespace BD.WTTS.Services.Implementation;
@@ -30,6 +31,27 @@ sealed partial class HttpReverseProxyMiddleware
this.logger = logger;
}
static ArgumentOutOfRangeException GetUnknownHttpVersionException(string? actualValue, [CallerArgumentExpression(nameof(actualValue))] string? paramName = null) => new(
$"""
Version doesn't map to a known HTTP protocol. (Parameter '{paramName}')
Actual value was {actualValue}.
""");
static Version GetHttpVersion(string requestProtocol) =>
(requestProtocol != null && requestProtocol.Length >= 6) ? requestProtocol[5] switch
{
// 参考 Microsoft.AspNetCore.Http.HttpProtocol.GetHttpProtocol
'1' => requestProtocol.Length >= 8 ? requestProtocol[7] switch
{
'0' => HttpVersion.Version10,
'1' => HttpVersion.Version11,
_ => throw GetUnknownHttpVersionException(requestProtocol),
} : throw GetUnknownHttpVersionException(requestProtocol),
'2' => HttpVersion.Version20,
'3' => HttpVersion.Version30,
_ => throw GetUnknownHttpVersionException(requestProtocol),
} : throw GetUnknownHttpVersionException(requestProtocol);
/// <summary>
/// 处理请求
/// </summary>
@@ -60,12 +82,7 @@ sealed partial class HttpReverseProxyMiddleware
var destinationPrefix = GetDestinationPrefix(context.Request.Scheme, context.Request.Host, null);
var forwarderRequestConfig = new ForwarderRequestConfig()
{
Version = context.Request.Protocol switch
{
var protocol when protocol.StartsWith("HTTP/2") => System.Net.HttpVersion.Version20,
var protocol when protocol.StartsWith("HTTP/3") => System.Net.HttpVersion.Version30,
_ => System.Net.HttpVersion.Version11,
},
Version = GetHttpVersion(context.Request.Protocol),
};
var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient, forwarderRequestConfig, HttpTransformer.Empty);
if (error != ForwarderError.None)
@@ -119,19 +136,23 @@ sealed partial class HttpReverseProxyMiddleware
context.Request.Headers.UserAgent = domainConfig.UserAgent.Replace("${origin}", context.Request.Headers.UserAgent, StringComparison.OrdinalIgnoreCase);
}
var forwarderRequestConfig = new ForwarderRequestConfig()
{
Version = context.Request.Protocol switch
{
var protocol when protocol.StartsWith("HTTP/2") => System.Net.HttpVersion.Version20,
var protocol when protocol.StartsWith("HTTP/3") => System.Net.HttpVersion.Version30,
_ => System.Net.HttpVersion.Version11,
},
};
ForwarderRequestConfig forwarderRequestConfig;
if (domainConfig.IsServerSideProxy)
{
SetWattHeaders(context, reverseProxyConfig.Service.ServerSideProxyToken);
forwarderRequestConfig = new ForwarderRequestConfig()
{
Version = HttpVersion.Version30,
VersionPolicy = HttpVersionPolicy.RequestVersionOrLower
};
}
else
{
forwarderRequestConfig = new ForwarderRequestConfig()
{
Version = GetHttpVersion(context.Request.Protocol),
};
}
var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient, forwarderRequestConfig, HttpTransformer.Empty);
@@ -98,8 +98,58 @@ sealed partial class YarpReverseProxyServiceImpl : ReverseProxyServiceImpl, IRev
protected override Task<StartProxyResult> StartProxyImpl() => Task.FromResult(StartProxyCore());
readonly string[] allowedHosts = ["*",];
#if WINDOWS
/// <summary>
/// https://learn.microsoft.com/zh-cn/dotnet/core/extensions/globalization-icu#determine-if-your-app-is-using-icu
/// </summary>
/// <returns></returns>
static bool ICUMode()
{
SortVersion sortVersion = CultureInfo.InvariantCulture.CompareInfo.Version;
byte[] bytes = sortVersion.SortId.ToByteArray();
int version = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0];
return version != 0 && version == sortVersion.FullVersion;
}
static bool IcuTest(IEnumerable<string> incoming)
{
if (!OperatingSystem.IsWindowsVersionAtLeast(10, 0, 18362) && ICUMode())
{
// https://learn.microsoft.com/zh-cn/dotnet/core/extensions/globalization-icu#icu-on-windows
// 与 .NET 6 和 .NET 5 相比,.NET 7 及更高版本能够在早期 Windows 版本上加载 ICU
// 通常问题出在 1703 ~ 1903 之间的 Windows 版本
try
{
// https://github.com/dotnet/aspnetcore/blob/v9.0.2/src/Middleware/HostFiltering/src/MiddlewareConfigurationManager.cs#L67
foreach (var entry in incoming)
{
// Punycode. Http.Sys requires you to register Unicode hosts, but the headers contain punycode.
var host = new HostString(entry).ToUriComponent();
}
return true;
}
catch
{
return false;
}
}
else
{
return true;
}
}
#endif
StartProxyResult StartProxyCore()
{
#if WINDOWS
if (!IcuTest(allowedHosts))
{
return StartProxyResultCode.IcuTestFail;
}
#endif
// https://github.com/dotnetcore/FastGithub/blob/2.1.4/FastGithub/Program.cs#L29
try
{
@@ -113,13 +163,10 @@ sealed partial class YarpReverseProxyServiceImpl : ReverseProxyServiceImpl, IRev
builder.Logging.AddProvider(new LogConsoleService.Utf8StringLoggerProvider(AssemblyInfo.Accelerator));
builder.Services.Configure<HostFilteringOptions>(static o =>
builder.Services.Configure<HostFilteringOptions>(o =>
{
o.AllowEmptyHosts = true;
o.AllowedHosts = new List<string>
{
"*",
};
o.AllowedHosts = allowedHosts;
});
builder.Host.UseNLog();
@@ -341,6 +341,12 @@ public enum StartProxyResultCode : byte
/// 绑定端口错误
/// </summary>
BindPortError,
/// <summary>
/// 早期 Windows 版本且缺少 Windows 更新补丁,Icu 存在 Bug 导致,要么更新操作系统,要么更改配置使用 NLS 重启程序
/// <para>https://learn.microsoft.com/zh-cn/dotnet/core/extensions/globalization-icu#use-nls-instead-of-icu</para>
/// </summary>
IcuTestFail = 127, // 值与 Mobius 保持一致
}
/// <summary>
@@ -9,7 +9,7 @@ internal partial class NetworkTestService : INetworkTestService
#region 常量
// 默认测试地址
const string DEFAULT_TESTSERVER_HOSTNAME = "stun.syncthing.net";
internal const string DEFAULT_TESTSERVER_HOSTNAME = "stun.miwifi.com";
const int DEFAULT_PORT = 3478;
const int DEFAULT_TLSPORT = 5349;
@@ -202,8 +202,6 @@ internal partial class NetworkTestService : INetworkTestService
#region RFC3489
StunClient3489? _stunClient;
/// <summary>
/// 测试 STUN 客户端 基于 RFC3489
/// </summary>
@@ -217,19 +215,17 @@ internal partial class NetworkTestService : INetworkTestService
string? testServerHostName = DEFAULT_TESTSERVER_HOSTNAME,
int? testServerPort = DEFAULT_TESTSTUN3489_PORT,
IPEndPoint? localIPEndPoint = null,
bool force = false,
CancellationToken cancellationToken = default
)
{
testServerHostName ??= DEFAULT_TESTSERVER_HOSTNAME;
testServerPort ??= DEFAULT_TESTSTUN3489_PORT;
if (_stunClient == null || force)
_stunClient = await GetStunClient3489Async(testServerHostName, testServerPort.Value, localIPEndPoint);
using var stunClient = await GetStunClient3489Async(testServerHostName, testServerPort.Value, localIPEndPoint);
try
{
await _stunClient.QueryAsync(cancellationToken);
await stunClient.QueryAsync(cancellationToken);
}
catch (OperationCanceledException) { throw; }
catch (Exception ex)
@@ -238,7 +234,7 @@ internal partial class NetworkTestService : INetworkTestService
return null;
}
return _stunClient.State;
return stunClient.State;
}
private async ValueTask<StunClient3489> GetStunClient3489Async(string serverHostName, int port, IPEndPoint? localIPEndPoint = null)
@@ -289,7 +285,7 @@ internal partial class NetworkTestService : INetworkTestService
testServerHostName ??= DEFAULT_TESTSERVER_HOSTNAME;
testServerPort ??= DEFAULT_TESTSTUN5389_PORT;
IStunClient5389 client = await GetStunClient5389Async(protocol, testServerHostName, testServerPort.Value, localIPEndPoint);
using var client = await GetStunClient5389Async(protocol, testServerHostName, testServerPort.Value, localIPEndPoint);
if (client == null)
return null;
@@ -91,14 +91,12 @@ internal interface IStunTestService
/// <param name="testServerHostName">STUN 服务地址</param>
/// <param name="testServerPort">STUN 服务端口</param>
/// <param name="localIPEndPoint">本机IP</param>
/// <param name="force">刷新测试 Client</param>
/// <remarks></remarks>
/// <returns></returns>
Task<ClassicStunResult?> TestStunClient3489Async(
string? testServerHostName = default,
int? testServerPort = default,
IPEndPoint? localIPEndPoint = default,
bool force = false,
CancellationToken cancellationToken = default
);
@@ -629,45 +629,9 @@ public sealed partial class GameAcceleratorService
};
var install = Ioc.Get<IAcceleratorService>().XY_Install(GameAcceleratorSettings.WattAcceleratorDirPath.Value!);
td.Opened += (s, e) =>
td.Opened += async (s, e) =>
{
DownloadCallbackAsync(td, install).Wait();
//await foreach (var item in install)
//{
// if (item.HandleUI(out var content))
// {
// switch (content)
// {
// case < 100:
// Dispatcher.UIThread.Post(() => { td.Content = $"正在下载 {item.Content}%"; });
// td.SetProgressBarState(item.Content, TaskDialogProgressState.Normal);
// break;
// case 100:
// td.SetProgressBarState(item.Content, TaskDialogProgressState.Indeterminate);
// Dispatcher.UIThread.Post(() => { td.Content = $"下载完成,正在安装..."; });
// break;
// case (int)XunYouDownLoadCode.安装成功:
// //处理成功
// //Dispatcher.UIThread.Post(() => { td.Content = $"安装完成"; });
// Dispatcher.UIThread.Post(() => { td.Hide(TaskDialogStandardResult.OK); });
// td.Hide();
// break;
// case int n when n > 101 && n < (int)XunYouDownLoadCode.启动安装程序失败:
// //处理失败
// break;
// // Code 和进度重叠 递进 1000 XunYouInstallOrStartCode.默认 XunYouInstallOrStartCode.已安装
// case 1000:
// Dispatcher.UIThread.Post(() => { td.Content = $"默认"; });
// // XunYouInstallOrStartCode.默认
// break;
// case 1001:
// Dispatcher.UIThread.Post(() => { td.Content = $"已安装"; });
// // XunYouInstallOrStartCode.已安装
// Dispatcher.UIThread.Post(() => { td.Hide(TaskDialogStandardResult.OK); });
// break;
// }
// }
//}
await DownloadCallbackAsync(td, install);
};
//_ = Task.Run(() => { XunYouSDK.InstallAsync(progress, Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "WattAccelerator")); });
@@ -406,8 +406,10 @@ partial class ProxyService
if (!string.IsNullOrEmpty(proxyTokenCache))
return proxyTokenCache;
using CancellationTokenSource cts = new();
cts.CancelAfter(TimeSpan.FromSeconds(4.99D)); // 5 秒超时
var resp = await IMicroServiceClient.Instance.Account
.GenerateServerSideProxyToken();
.GenerateServerSideProxyToken(cts.Token);
if (resp != null && !string.IsNullOrEmpty(resp.Content?.AccessToken))
{
@@ -416,11 +418,14 @@ partial class ProxyService
return proxyTokenCache;
}
catch (OperationCanceledException)
{
}
catch (Exception ex)
{
ex.LogAndShowT();
return null;
}
return null;
}
/// <summary>
@@ -163,8 +163,35 @@ public sealed partial class ProxyService
return [];
var data = ProxyDomains.Items
.Where(x => x.Items != null)
.SelectMany(s => s.Items!.Where(w => w.ThreeStateEnable != false));
//return data.Concat(data.SelectMany(s => GetProxyDomainsItems(s)));
.SelectMany(s => s.Items!.Where(w => w.ThreeStateEnable != false))
.Select(item =>
{
//过滤部分选中的子项
if (item.Items.Any_Nullable())
{
return new AccelerateProjectDTO
{
Name = item.Name,
Port = item.Port,
MatchDomainNames = item.MatchDomainNames,
ForwardDomainNames = item.ForwardDomainNames,
IgnoreSSLCertVerification = item.IgnoreSSLCertVerification,
FakeServerName = item.FakeServerName,
ProxyType = item.ProxyType,
ListenDomainNames = item.ListenDomainNames,
Checked = item.Checked,
Id = item.Id,
Order = item.Order,
FakeUserAgent = item.FakeUserAgent,
Version = item.Version,
ThreeStateEnable = item.ThreeStateEnable,
// 递归过滤子项
Items = item.Items.Where(x => x.ThreeStateEnable != false).ToList()
};
}
return item;
});
return data;
}
@@ -21,16 +21,17 @@ public sealed partial class AcceleratorPageViewModel
{
// Create new ProxyEnableDomain for 加速服务 page
var enableGroupDomain = ProxyService.Current.ProxyDomainsList
.Where(list => list.ThreeStateEnable == true || list.ThreeStateEnable == null)
.Where(list => list.ThreeStateEnable != false)
.Select(list => new ProxyDomainGroupViewModel
{
Name = list.Name,
IconUrl = list.IconUrl ?? string.Empty,
EnableProxyDomainVMs = new(
list.Items!
.Where(i => i.ThreeStateEnable == true)
.Where(i => i.ThreeStateEnable != false)
.Select(i => new ProxyDomainViewModel(i.Name, i.ProxyType, "https://" + i.ListenDomainNames.Split(";")[0],
i.Items?
.Where(x => x.ThreeStateEnable != false)
.Select(c => new ProxyDomainViewModel(c.Name, c.ProxyType, "https://" + c.ListenDomainNames.Split(';')[0]))
.ToList()))
.ToList()),
@@ -31,10 +31,9 @@ public partial class NetworkCheckControlViewModel : ViewModelBase
public string[] STUNAddress { get; } =
[
"stun.syncthing.net",
NetworkTestService.DEFAULT_TESTSERVER_HOSTNAME,
"stun.hot-chilli.net",
"stun.fitauto.ru",
"stun.miwifi.com",
];
[ObservableAsProperty]
@@ -90,28 +89,7 @@ public partial class NetworkCheckControlViewModel : ViewModelBase
{
SelectedSTUNAddress = STUNAddress[0];
NATCheckCommand = ReactiveCommand.CreateFromTask(async () =>
{
var natCheckResult = await _networkTestService.TestStunClient3489Async(testServerHostName: SelectedSTUNAddress) ?? new ClassicStunResult { NatType = NatType.Unknown };
var (netSucc, _) = await _networkTestService.TestOpenUrlAsync("https://www.baidu.com");
var publicEndPoint = natCheckResult.PublicEndPoint?.Address.ToString() ?? "Unknown";
var localEndPoint = natCheckResult.LocalEndPoint?.Address.ToString() ?? "Unknown";
var (natLevel, natTypeTip) = natCheckResult.NatType switch
{
// Open
NatType.OpenInternet or NatType.FullCone => ("开放 NAT", "您可与在其网络上具有任意 NAT 类型的用户玩多人游戏和发起多人游戏。"),
// Moderate
NatType.RestrictedCone or NatType.PortRestrictedCone or NatType.SymmetricUdpFirewall => ("中等 NAT", "您可与一些用户玩多人游戏;但是,并且通常你将不会被选为比赛的主持人。"),
// Strict
NatType.Symmetric or NatType.UdpBlocked => ("严格 NAT", "您只能与具有开放 NAT 类型的用户玩多人游戏。您不能被选为比赛的主持人。"),
// Unknown
NatType.Unknown or NatType.UnsupportedServer or _ => ("不可用 NAT", "如果 NAT 不可用,您将无法使用群聊天或连接到某些 Xbox 游戏的多人游戏。"),
};
return new NATFetchResult(publicEndPoint, localEndPoint, natLevel, natTypeTip, netSucc);
});
NATCheckCommand = ReactiveCommand.CreateFromTask(NATCheckAsync);
NATCheckCommand
.IsExecuting
.ToPropertyEx(this, x => x.IsNATChecking);
@@ -145,36 +123,7 @@ public partial class NetworkCheckControlViewModel : ViewModelBase
var canDNSCheck = this.WhenAnyValue(x => x.DomainPendingTest)
.Select(domain => domain == string.Empty || DomainRegExp().IsMatch(domain));
DNSCheckCommand = ReactiveCommand.CreateFromTask(async () =>
{
var testDomain = DomainPendingTest == string.Empty ? DefaultTestDomain : DomainPendingTest;
try
{
long delayMs;
IPAddress[] address;
if (ProxySettings.UseDoh)
{
var configDoh = ProxySettings.CustomDohAddres2.Value ?? ProxySettingsWindowViewModel.DohAddress.FirstOrDefault() ?? string.Empty;
(delayMs, address) = await _networkTestService.TestDNSOverHttpsAsync(testDomain, configDoh);
}
else
{
var configDns = ProxySettings.ProxyMasterDns.Value ?? string.Empty;
(delayMs, address) = await _networkTestService.TestDNSAsync(testDomain, configDns, 53);
}
if (address.Length == 0)
throw new Exception("Parsing failed. Return empty ip address.");
DNSTestDelay = delayMs + "ms ";
DNSTestResult = string.Empty + address.FirstOrDefault();
}
catch (Exception ex)
{
Log.Error(nameof(AcceleratorPageViewModel), ex.ToString());
DNSTestDelay = string.Empty;
DNSTestResult = "error";
}
}, canDNSCheck);
DNSCheckCommand = ReactiveCommand.CreateFromTask(DNSCheckAsync, canDNSCheck);
DNSCheckCommand
.IsExecuting
.ToPropertyEx(this, x => x.IsDNSChecking);
@@ -199,4 +148,70 @@ public partial class NetworkCheckControlViewModel : ViewModelBase
IPv6CheckCommand.Execute().Subscribe();
}
async Task<NATFetchResult> NATCheckAsync()
{
try
{
return await NATCheckCoreAsync();
}
catch
{
return new("", "", "", "", false);
}
}
async Task<NATFetchResult> NATCheckCoreAsync()
{
var natCheckResult = await _networkTestService.TestStunClient3489Async(testServerHostName: SelectedSTUNAddress) ?? new ClassicStunResult { NatType = NatType.Unknown };
var (netSucc, _) = await _networkTestService.TestOpenUrlAsync("https://www.baidu.com");
var publicEndPoint = natCheckResult.PublicEndPoint?.Address.ToString() ?? "Unknown";
var localEndPoint = natCheckResult.LocalEndPoint?.Address.ToString() ?? "Unknown";
var (natLevel, natTypeTip) = natCheckResult.NatType switch
{
// Open
NatType.OpenInternet or NatType.FullCone => ("开放 NAT", "您可与在其网络上具有任意 NAT 类型的用户玩多人游戏和发起多人游戏。"),
// Moderate
NatType.RestrictedCone or NatType.PortRestrictedCone or NatType.SymmetricUdpFirewall => ("中等 NAT", "您可与一些用户玩多人游戏;但是,并且通常你将不会被选为比赛的主持人。"),
// Strict
NatType.Symmetric or NatType.UdpBlocked => ("严格 NAT", "您只能与具有开放 NAT 类型的用户玩多人游戏。您不能被选为比赛的主持人。"),
// Unknown
NatType.Unknown or NatType.UnsupportedServer or _ => ("不可用 NAT", "如果 NAT 不可用,您将无法使用群聊天或连接到某些 Xbox 游戏的多人游戏。"),
};
return new NATFetchResult(publicEndPoint, localEndPoint, natLevel, natTypeTip, netSucc);
}
async Task DNSCheckAsync()
{
var testDomain = DomainPendingTest == string.Empty ? DefaultTestDomain : DomainPendingTest;
try
{
long delayMs;
IPAddress[] address;
if (ProxySettings.UseDoh)
{
var configDoh = ProxySettings.CustomDohAddres2.Value ?? ProxySettingsWindowViewModel.DohAddress.FirstOrDefault() ?? string.Empty;
(delayMs, address) = await _networkTestService.TestDNSOverHttpsAsync(testDomain, configDoh);
}
else
{
var configDns = ProxySettings.ProxyMasterDns.Value ?? string.Empty;
(delayMs, address) = await _networkTestService.TestDNSAsync(testDomain, configDns, 53);
}
if (address.Length == 0)
throw new Exception("Parsing failed. Return empty ip address.");
DNSTestDelay = delayMs + "ms ";
DNSTestResult = string.Empty + address.FirstOrDefault();
}
catch (Exception ex)
{
Log.Error(nameof(AcceleratorPageViewModel), ex.ToString());
DNSTestDelay = string.Empty;
DNSTestResult = "error";
}
}
}
@@ -213,27 +213,31 @@
Binding="{Binding PingValue}"
ComparisonCondition="GreaterThan"
Value="120">
<ia:ChangePropertyAction
PropertyName="Fill"
TargetObject="PingEllipse"
Value="#FF4F58" />
<ia:ChangePropertyAction
PropertyName="Foreground"
TargetObject="PingText"
Value="#FF4F58" />
<ia:ChangePropertyAction PropertyName="Fill" TargetObject="PingEllipse">
<ia:ChangePropertyAction.Value>
<SolidColorBrush Color="#FF4F58" />
</ia:ChangePropertyAction.Value>
</ia:ChangePropertyAction>
<ia:ChangePropertyAction PropertyName="Foreground" TargetObject="PingText">
<ia:ChangePropertyAction.Value>
<SolidColorBrush Color="#FF4F58" />
</ia:ChangePropertyAction.Value>
</ia:ChangePropertyAction>
</ia:DataTriggerBehavior>
<ia:DataTriggerBehavior
Binding="{Binding PingValue}"
ComparisonCondition="LessThanOrEqual"
Value="120">
<ia:ChangePropertyAction
PropertyName="Fill"
TargetObject="PingEllipse"
Value="#47A42B" />
<ia:ChangePropertyAction
PropertyName="Foreground"
TargetObject="PingText"
Value="#47A42B" />
<ia:ChangePropertyAction PropertyName="Fill" TargetObject="PingEllipse">
<ia:ChangePropertyAction.Value>
<SolidColorBrush Color="#47A42B" />
</ia:ChangePropertyAction.Value>
</ia:ChangePropertyAction>
<ia:ChangePropertyAction PropertyName="Foreground" TargetObject="PingText">
<ia:ChangePropertyAction.Value>
<SolidColorBrush Color="#47A42B" />
</ia:ChangePropertyAction.Value>
</ia:ChangePropertyAction>
</ia:DataTriggerBehavior>
</i:Interaction.Behaviors>
<TextBlock
@@ -104,7 +104,7 @@
<Button Content="选择证书"/>
</DockPanel>-->
<ui:SettingsExpanderItem Description="*该选项勾选后会导致程序的加速完全不生效,请确认是配合二级代理实现仅需要脚本功能后再启用">
<ui:SettingsExpanderItem Description="*该选项勾选后会导致程序的加速失效,请确认是配合二级代理实现仅需要脚本功能后再启用">
<TextBlock Text="{Binding Path=Res.CommunityFix_ProxyOnlyOnScript, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:SettingsExpanderItem.Footer>
<ToggleSwitch Classes="LeftContent" IsChecked="{Binding Source={x:Static ms:ProxySettings.OnlyEnableProxyScript}, Path=Value, Mode=TwoWay}" />
@@ -114,7 +114,7 @@
<ui:SettingsExpander
Description="*改动后需重启加速才能生效"
Description="*请在有正确二级代理的情况下才考虑启用该选项,否则会导致程序的加速失效,改动后需重启加速才能生效"
Header="{Binding Path=Res.Settings_Proxy_TwoLevelAgent, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
IsExpanded="True">
<ui:SettingsExpander.Footer>
@@ -333,16 +333,18 @@ public sealed class UserService : ReactiveObject
if (token != null)
{
await Browser2.OpenAsync(string.Format(Constants.Urls.OfficialWebsite_Fast_Login_, token.AccessToken, HttpUtility.UrlEncode(token.ExpiresIn.ToString("R")), HttpUtility.UrlEncode(url)));
return;
}
return;
break;
case FastLoginWebChannel.WattGame:
var shopToken = await userManager.GetShopAuthTokenAsync();
if (shopToken != null)
{
var cookieMaxAge = (shopToken.ExpiresIn - DateTimeOffset.Now).TotalSeconds;
await Browser2.OpenAsync(string.Format(Constants.Urls.WattGame_Fast_Login_, shopToken.AccessToken, HttpUtility.UrlEncode(cookieMaxAge.ToString()), HttpUtility.UrlEncode(url)));
return;
}
return;
break;
}
await Browser2.OpenAsync(url.ToString());
}
@@ -137,12 +137,15 @@ partial class Startup // 全局异常处理
#endif
#if WINDOWS
var mbText =
if (isTerminating == true)
{
var mbText =
$"""
{string.Format(message, args)}
{ex}
""";
ShowApplicationCrash(mbText);
ShowApplicationCrash(mbText);
}
#endif
}