diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index 486e53ed..e502435f 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -90,6 +90,8 @@ public class Global public const string SingboxFakeDNSTag = "fake_dns"; public const string SingboxEchDNSTag = "ech_dns"; + public const int Hysteria2DefaultHopInt = 10; + public static readonly List IEProxyProtocols = [ "{ip}:{http_port}", diff --git a/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs b/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs index 41ba1c7d..bfebedbe 100644 --- a/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs +++ b/v2rayN/ServiceLib/Models/ProtocolExtraItem.cs @@ -13,10 +13,10 @@ public class ProtocolExtraItem //public string? PluginArgs { get; set; } // hysteria2 - public string? UpMbps { get; set; } - public string? DownMbps { get; set; } + public int? UpMbps { get; set; } + public int? DownMbps { get; set; } public string? Ports { get; set; } - public string? HopInterval { get; set; } + public int? HopInterval { get; set; } // group profile public string? GroupType { get; set; } diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index fd3b2d9f..46de8983 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -2997,6 +2997,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Port hopping interval 的本地化字符串。 + /// + public static string TbHopInt7 { + get { + return ResourceManager.GetString("TbHopInt7", resourceCulture); + } + } + /// /// 查找类似 UUID(id) 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index 16fe442f..fdcffe9b 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1665,4 +1665,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used. + + Port hopping interval + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx index a114ac21..5db657ca 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fr.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx @@ -1662,4 +1662,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used. + + Port hopping interval + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 21e4e68d..9e5c9c4d 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1665,4 +1665,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used. + + Port hopping interval + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index 91095de4..dfd744a3 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1665,4 +1665,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used. + + Port hopping interval + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index adc9cc98..67cffea7 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1665,4 +1665,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used. + + Port hopping interval + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index b1675d48..f17495fd 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1662,4 +1662,7 @@ 当未选择或 "AsIs" 时,由远程服务器端 DNS 解析;否则,使用内部 DNS 模块解析。 + + 端口跳跃间隔 + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index 73a7dfe5..160340d1 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1662,4 +1662,7 @@ If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used. + + Port hopping interval + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index 61fe2f84..ccf62db2 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -146,10 +146,13 @@ public partial class CoreConfigSingboxService }; } - var extra = node.GetExtraItem(); - outbound.up_mbps = int.TryParse(extra?.UpMbps, out var upMbps) && upMbps >= 0 ? upMbps : (_config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null); - outbound.down_mbps = int.TryParse(extra?.DownMbps, out var downMbps) && downMbps >= 0 ? downMbps : (_config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null); - var ports = extra?.Ports?.IsNullOrEmpty() == false ? extra.Ports : null; + outbound.up_mbps = extraItem?.UpMbps is { } su and >= 0 + ? su + : _config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null; + outbound.down_mbps = extraItem?.DownMbps is { } sd and >= 0 + ? sd + : _config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null; + var ports = extraItem?.Ports?.IsNullOrEmpty() == false ? extraItem.Ports : null; if ((!ports.IsNullOrEmpty()) && (ports.Contains(':') || ports.Contains('-') || ports.Contains(','))) { outbound.server_port = null; @@ -162,7 +165,9 @@ public partial class CoreConfigSingboxService return port.Contains(':') ? port : $"{port}:{port}"; }) .ToList(); - outbound.hop_interval = _config.HysteriaItem.HopInterval > 0 ? $"{_config.HysteriaItem.HopInterval}s" : null; + outbound.hop_interval = extraItem?.HopInterval is { } hi and >= 5 + ? $"{hi}s" + : _config.HysteriaItem.HopInterval >= 5 ? $"{_config.HysteriaItem.HopInterval}s" : $"{Global.Hysteria2DefaultHopInt}s"; } break; diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs index 17fc7a09..3d42fb67 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs @@ -512,24 +512,31 @@ public partial class CoreConfigV2rayService case "hysteria": var extraItem = node.GetExtraItem(); var ports = extraItem?.Ports; + int? upMbps = extraItem?.UpMbps is { } su and >= 0 + ? su + : _config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null; + int? downMbps = extraItem?.DownMbps is { } sd and >= 0 + ? sd + : _config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null; + var hopInterval = extraItem?.HopInterval is { } hi and >= 5 + ? hi + : _config.HysteriaItem.HopInterval >= 5 ? _config.HysteriaItem.HopInterval : Global.Hysteria2DefaultHopInt; HysteriaUdpHop4Ray? udpHop = null; if (!ports.IsNullOrEmpty() && (ports.Contains(':') || ports.Contains('-') || ports.Contains(','))) { - udpHop = new() + udpHop = new HysteriaUdpHop4Ray { ports = ports.Replace(':', '-'), - interval = _config.HysteriaItem.HopInterval > 0 - ? _config.HysteriaItem.HopInterval - : null, + interval = hopInterval, }; } HysteriaSettings4Ray hysteriaSettings = new() { version = 2, auth = node.Id, - up = _config.HysteriaItem.UpMbps > 0 ? $"{_config.HysteriaItem.UpMbps}mbps" : null, - down = _config.HysteriaItem.DownMbps > 0 ? $"{_config.HysteriaItem.DownMbps}mbps" : null, + up = upMbps > 0 ? $"{upMbps}mbps" : null, + down = downMbps > 0 ? $"{downMbps}mbps" : null, udphop = udpHop, }; streamSettings.hysteriaSettings = hysteriaSettings; diff --git a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs index 589aa8df..2f629c79 100644 --- a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs @@ -23,6 +23,15 @@ public class AddServerViewModel : MyReactiveObject [Reactive] public string Ports { get; set; } + [Reactive] + public int UpMbps { get; set; } + + [Reactive] + public int DownMbps { get; set; } + + [Reactive] + public int HopInterval { get; set; } + [Reactive] public string Flow { get; set; } @@ -77,6 +86,9 @@ public class AddServerViewModel : MyReactiveObject Ports = extraItem?.Ports ?? string.Empty; AlterId = int.TryParse(extraItem?.AlterId, out var result) ? result : 0; Flow = extraItem?.Flow ?? string.Empty; + UpMbps = extraItem?.UpMbps ?? 0; + DownMbps = extraItem?.DownMbps ?? 0; + HopInterval = extraItem?.HopInterval ?? Global.Hysteria2DefaultHopInt; } private async Task SaveServerAsync() @@ -127,6 +139,9 @@ public class AddServerViewModel : MyReactiveObject extraItem.Ports = Ports; extraItem.AlterId = AlterId > 0 ? AlterId.ToString() : string.Empty; extraItem.Flow = Flow; + extraItem.UpMbps = UpMbps; + extraItem.DownMbps = DownMbps; + extraItem.HopInterval = HopInterval >= 5 ? HopInterval : Global.Hysteria2DefaultHopInt; SelectedSource.SetExtraItem(extraItem); if (await ConfigHandler.AddServer(_config, SelectedSource) == 0) diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml index 19c62868..356698fb 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml @@ -360,7 +360,7 @@ Grid.Row="2" ColumnDefinitions="300,Auto,Auto" IsVisible="False" - RowDefinitions="Auto,Auto,Auto,Auto"> + RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto"> + + + + + + + + + this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.HopInterval, v => v.txtHopInt7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.UpMbps, v => v.txtUpMbps7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.DownMbps, v => v.txtDownMbps7.Text).DisposeWith(disposables); break; case EConfigType.TUIC: diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml b/v2rayN/v2rayN/Views/AddServerWindow.xaml index 1f70489b..8c6b4a35 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml @@ -487,6 +487,8 @@ + + @@ -547,6 +549,47 @@ VerticalAlignment="Center" Style="{StaticResource ToolbarTextBlock}" Text="{x:Static resx:ResUI.TbPorts7Tips}" /> + + + + + + + + + vm.SelectedSource.Id, v => v.txtId7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.HopInterval, v => v.txtHopInt7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.UpMbps, v => v.txtUpMbps7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.DownMbps, v => v.txtDownMbps7.Text).DisposeWith(disposables); break; case EConfigType.TUIC: