Add hysteria2 bandwidth and hop interval support

This commit is contained in:
DHR60 2026-01-17 19:05:50 +08:00
parent a6b12aa718
commit 1c2d086a71
17 changed files with 158 additions and 15 deletions

View file

@ -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<string> IEProxyProtocols =
[
"{ip}:{http_port}",

View file

@ -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; }

View file

@ -2997,6 +2997,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Port hopping interval 的本地化字符串。
/// </summary>
public static string TbHopInt7 {
get {
return ResourceManager.GetString("TbHopInt7", resourceCulture);
}
}
/// <summary>
/// 查找类似 UUID(id) 的本地化字符串。
/// </summary>

View file

@ -1665,4 +1665,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
</data>
<data name="TbHopInt7" xml:space="preserve">
<value>Port hopping interval</value>
</data>
</root>

View file

@ -1662,4 +1662,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
</data>
<data name="TbHopInt7" xml:space="preserve">
<value>Port hopping interval</value>
</data>
</root>

View file

@ -1665,4 +1665,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
</data>
<data name="TbHopInt7" xml:space="preserve">
<value>Port hopping interval</value>
</data>
</root>

View file

@ -1665,4 +1665,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
</data>
<data name="TbHopInt7" xml:space="preserve">
<value>Port hopping interval</value>
</data>
</root>

View file

@ -1665,4 +1665,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
</data>
<data name="TbHopInt7" xml:space="preserve">
<value>Port hopping interval</value>
</data>
</root>

View file

@ -1662,4 +1662,7 @@
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
<value>当未选择或 "AsIs" 时,由远程服务器端 DNS 解析;否则,使用内部 DNS 模块解析。</value>
</data>
<data name="TbHopInt7" xml:space="preserve">
<value>端口跳跃间隔</value>
</data>
</root>

View file

@ -1662,4 +1662,7 @@
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
</data>
<data name="TbHopInt7" xml:space="preserve">
<value>Port hopping interval</value>
</data>
</root>

View file

@ -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;

View file

@ -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;

View file

@ -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)

View file

@ -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">
<TextBlock
Grid.Row="1"
@ -407,6 +407,41 @@
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbPorts7Tips}" />
<TextBlock
Grid.Row="4"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbHopInt7}" />
<TextBox
x:Name="txtHopInt7"
Grid.Row="4"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="5"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsHysteriaBandwidth}" />
<StackPanel
Grid.Row="5"
Grid.Column="1"
Orientation="Horizontal">
<TextBox
x:Name="txtUpMbps7"
Width="90"
Margin="{StaticResource Margin4}"
Watermark="Up" />
<TextBox
x:Name="txtDownMbps7"
Width="90"
Margin="{StaticResource Margin4}"
Watermark="Down" />
</StackPanel>
</Grid>
<Grid
x:Name="gridTuic"

View file

@ -154,6 +154,9 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
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:

View file

@ -487,6 +487,8 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
@ -547,6 +549,47 @@
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbPorts7Tips}" />
<TextBlock
Grid.Row="4"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbHopInt7}" />
<TextBox
x:Name="txtHopInt7"
Grid.Row="4"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
Style="{StaticResource DefTextBox}" />
<TextBlock
Grid.Row="5"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbSettingsHysteriaBandwidth}" />
<StackPanel
Grid.Row="5"
Grid.Column="1"
Orientation="Horizontal">
<TextBox
x:Name="txtUpMbps7"
Width="90"
Margin="{StaticResource Margin4}"
materialDesign:HintAssist.Hint="Up"
Style="{StaticResource DefTextBox}" />
<TextBox
x:Name="txtDownMbps7"
Width="90"
Margin="{StaticResource Margin4}"
materialDesign:HintAssist.Hint="Down"
Style="{StaticResource DefTextBox}" />
</StackPanel>
</Grid>
<Grid
x:Name="gridTuic"

View file

@ -149,6 +149,9 @@ public partial class AddServerWindow
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: